diff options
-rw-r--r-- | src/openvic-simulation/dataloader/Dataloader.cpp | 5 | ||||
-rw-r--r-- | src/openvic-simulation/misc/NewsArticle.cpp | 173 | ||||
-rw-r--r-- | src/openvic-simulation/misc/NewsArticle.hpp | 500 |
3 files changed, 677 insertions, 1 deletions
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 1be040f..8a6042c 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -292,7 +292,10 @@ bool Dataloader::_load_interface_files(UIManager& ui_manager) const { /* Nation management screens */ "country_production", "country_budget", "country_technology", "country_politics", "country_pops", "country_trade", - "country_diplomacy", "country_military" + "country_diplomacy", "country_military", + + /* News */ + "news" //news_window_default }; static constexpr std::string_view gui_file_extension = ".gui"; diff --git a/src/openvic-simulation/misc/NewsArticle.cpp b/src/openvic-simulation/misc/NewsArticle.cpp new file mode 100644 index 0000000..3030b9d --- /dev/null +++ b/src/openvic-simulation/misc/NewsArticle.cpp @@ -0,0 +1,173 @@ +#include "NewsArticle.hpp" + +#include <openvic-dataloader/v2script/Parser.hpp> +#include "openvic-simulation/dataloader/NodeTools.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + + +node_callback_t Typed::expect_typed_objects(length_callback_t length_callback, callback_t<std::unique_ptr<Typed>&&> callback) { + return expect_dictionary_keys_and_length( + length_callback, + //_expect_instance<Typed, Actor>(callback) + /*"news_priority", ZERO_OR_MORE, expect_dictionary_keys( + "case", ZERO_OR_MORE, success_callback + ),*/ + "news_priority", ZERO_OR_MORE, _expect_instance<Typed, NewsPriority>(callback), + "generator_selector", ZERO_OR_MORE, expect_dictionary_keys( + "case", ZERO_OR_MORE, success_callback, + "@2@", ZERO_OR_MORE, success_callback//expect_identifier_or_string() + ), + "generate_article", ZERO_OR_MORE, expect_dictionary_keys( + "size", ZERO_OR_MORE, success_callback, + "picture_case", ZERO_OR_MORE, success_callback, + "title_case", ZERO_OR_MORE, success_callback, + "description_case", ZERO_OR_MORE, success_callback + ), + "on_printing", ZERO_OR_MORE, expect_dictionary_keys( + "effect", ZERO_OR_MORE, success_callback + ) + ); + +} + + + +/* + Case(size_t new_index, fixed_point_t new_value, fixed_point_t priority_add, + std::string_view picture_path, ConditionScript&& new_trigger); +*/ + +Case::Case( size_t new_index, fixed_point_t new_value, +fixed_point_t new_priority_add, std::string_view picture_path, ConditionScript&& new_trigger) + : HasIdentifier { std::to_string(new_index) }, value { 0 }, priority_add { 0 }, + picture { }, trigger { std::move(new_trigger) } {} + +bool Case::parse_scripts(DefinitionManager const& definition_manager) { + return trigger.parse_script(true, definition_manager); +} + +bool Case::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) { + return add_key_map_entries(key_map, + "trigger", ZERO_OR_ONE, trigger.expect_script(), + "value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(value)), + "picture", ZERO_OR_ONE, expect_string(assign_variable_callback_string(picture)), + "priority_add", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(priority_add)) + ); +} + +//NewsPriority::NewsPriority() : priority_case { } {} + +bool NewsPriority::_fill_key_map(NodeTools::case_insensitive_key_map_t &key_map){ + return true; +} + +/* + EffectScript effect; + bool ret = expect_dictionary_keys( + "alert", ZERO_OR_ONE, expect_bool(assign_variable_callback(alert)), +... + "effect", ONE_EXACTLY, effect.expect_script(), +*/ + +/* + 0+ generator_selector + 1 string type + 1 string name = "default" + 0+ case = { value = int } + case = {trigger = { news_printing_count = 1 value = -999}} + can have '@2@' ?? if generator_selector is in a pattern +*/ + +/* +node_callback_t Object::expect_objects(length_callback_t length_callback, callback_t<std::unique_ptr<Object>&&> callback) { + return expect_dictionary_keys_and_length( + length_callback, + + "EMFXActorType", ZERO_OR_MORE, _expect_instance<Object, Actor>(callback), +*/ + +/* +"actorfile", ONE_EXACTLY, expect_string(assign_variable_callback_string(model_file)), + "scale", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(scale)), + + "attach", ZERO_OR_MORE, [this](ast::NodeCPtr node) -> bool { + std::string_view actor_name {}, attach_node {}; + Attachment::attach_id_t attach_id = 0; + + if (!expect_dictionary_keys<StringMapCaseInsensitive>( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(actor_name)), + "node", ONE_EXACTLY, expect_string(assign_variable_callback(attach_node)), + "attachId", ONE_EXACTLY, expect_uint(assign_variable_callback(attach_id)) + )(node)) { + Logger::error("Failed to load attachment for actor ", get_name()); + return false; + } + + attachments.push_back({ actor_name, attach_node, attach_id }); + return true; + }, +*/ + +/* +bool Actor::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) { + bool ret = Object::_fill_key_map(key_map); + + ret &= add_key_map_entries(key_map, + "actorfile", ONE_EXACTLY, +*/ + +/*bool SongChanceManager::load_songs_file(ast::NodeCPtr root) { + bool ret = true; + + ret &= expect_dictionary_reserve_length( + song_chances, + [this](std::string_view key, ast::NodeCPtr value) -> bool { + if (key != "song") { + Logger::error("Invalid song declaration ", key); + return false; + } + std::string_view name {}; + ConditionalWeight chance { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + + bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(name)), + "chance", ONE_EXACTLY, chance.expect_conditional_weight(ConditionalWeight::FACTOR) + )(value); + + ret &= song_chances.add_item({ song_chances.size(), name, std::move(chance) }); + return ret; + } + )(root); + + if (song_chances.size() == 0) { + Logger::error("No songs found in Songs.txt"); + return false; + } + + return ret; +} + +bool SongChanceManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + for (SongChance& songChance : song_chances.get_items()) { + ret &= songChance.parse_scripts(definition_manager); + } + return ret; +}*/ + + + +/* + "bonus", ZERO_OR_MORE, [&bonuses](ast::NodeCPtr bonus_node) -> bool { + ConditionScript trigger { scope_t::STATE, scope_t::NO_SCOPE, scope_t::NO_SCOPE }; + fixed_point_t bonus_value {}; + const bool ret = expect_dictionary_keys( + "trigger", ONE_EXACTLY, trigger.expect_script(), + "value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(bonus_value)) + )(bonus_node); + bonuses.emplace_back(std::move(trigger), bonus_value); + return ret; + }, +*/
\ No newline at end of file diff --git a/src/openvic-simulation/misc/NewsArticle.hpp b/src/openvic-simulation/misc/NewsArticle.hpp new file mode 100644 index 0000000..628ce40 --- /dev/null +++ b/src/openvic-simulation/misc/NewsArticle.hpp @@ -0,0 +1,500 @@ +#pragma once + +#include <cstddef> +#include <cstdint> +#include <vector> +#include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/types/OrderedContainers.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/interface/LoadBase.hpp" + +namespace OpenVic { + + /* + All from the /news folder + + For the @2@ syntax + + Consider AILoves + AILoves = { + "default" + "case = { value = 10 } case = { trigger = { news_printing_count = 1 } value = -999 }" + "AI_LOVES_OTHER_TITLE1 AI_LOVES_OTHER_TITLE2" + "AI_LOVES_OTHER_DESC1_LONG AI_LOVES_OTHER_DESC2_LONG" + "AI_LOVES_OTHER_DESC1_MEDIUM AI_LOVES_OTHER_DESC2_MEDIUM" + "AI_LOVES_OTHER_DESC1_SHORT AI_LOVES_OTHER_DESC2_SHORT" + } + @2@ in generator_selector pulls the case + @3@ pulls the titles + @4@ pulls the long description + and so on... + %1% seems to do the same buts allows for when we want the paste inside string quotes in addition to outside?? + + + The corresponding pattern has the name "AILoves", which seems to indicate it'll draw + from an identifier with name "AILoves" as seen above + + + EventGrammar handles a lot of this (esp. trigger and picture) + + */ + + enum class article_size_t { small, medium, large }; + static const string_map_t<article_size_t> article_size = { + { "small", article_size_t::small }, + { "medium", article_size_t::medium }, + { "large", article_size_t::large } + }; + + using identifier_int_map = deque_ordered_map<std::string,int32_t>; + using identifier_str_collection_map = deque_ordered_map<std::string,std::vector<std::vector<std::string>>>; + + struct NewsManager; + + class Typed : public Named<> { + protected: + Typed() = default; + + public: + Typed(Typed&&) = default; + virtual ~Typed() = default; + + OV_DETAIL_GET_BASE_TYPE(Typed) + OV_DETAIL_GET_TYPE + + static NodeTools::node_callback_t expect_typed_objects( + NodeTools::length_callback_t length_callback, NodeTools::callback_t<std::unique_ptr<Typed>&&> callback + ); + }; + + + + + /* + case can have + 0+ trigger = {} + 0+ value = int + 0+ priority_add = int + 0/1 picture + + more specifically, value, priority_add and picture can be thought of as effects, of which there must be one + having a trigger is optional though + */ + + struct Case : HasIdentifier { + //friend struct NewsManager; + + private: + ConditionScript PROPERTY(trigger); + //these always appear to be ints, but for consistency with other defines files... + fixed_point_t PROPERTY(value); + fixed_point_t PROPERTY(priority_add); + std::string PROPERTY(picture); + + Case(size_t new_index, fixed_point_t new_value, fixed_point_t new_priority_add, + std::string_view picture_path, ConditionScript&& new_trigger); + + bool parse_scripts(DefinitionManager const& definition_manager); + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map); + + //bool load_case(ast::NodeCPtr root); + + public: + Case(Case&&) = default; + }; + + + /* + TODO + ==== for these 3 (4?), the name and type properties will match === + 0+ generator_selector + 1 string type + 1 string name = "default" + 0+ case = { value = int } + case = {trigger = { news_printing_count = 1 value = -999}} + can have '@2@' ?? if generator_selector is in a pattern + */ + class GeneratorSelector final : public Typed { + friend std::unique_ptr<GeneratorSelector> std::make_unique<GeneratorSelector>(); + //public: + + private: + Case PROPERTY(generator_case); + + protected: + GeneratorSelector(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + GeneratorSelector(GeneratorSelector&&) = default; + virtual ~GeneratorSelector() = default; + + OV_DETAIL_GET_TYPE + + }; + + + /* + 0+ news_priority + 1 string type + 1 string name = "default" + 0+ case = { 0/1 trigger = {...} priority_add = int } + */ + class NewsPriority final : public Typed { + friend std::unique_ptr<NewsPriority> std::make_unique<NewsPriority>(); + //public: + + private: + Case PROPERTY(priority_case); + + protected: + NewsPriority(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + NewsPriority(NewsPriority&&) = default; + virtual ~NewsPriority() = default; + + OV_DETAIL_GET_TYPE + + }; + + /* + TODO + 0+ generate_article + 1 string type + 1 string name = "default" + 1 size = "small"|"medium"|"large" + 0+ picture_case = {...} + 1+ picture = "news/pic.dds" + 0/1? trigger = { or = { strings_eq = {...} string_eq = ... }} + 1+ title_case (guessing multiple of these means just pick one at random) + 0/1 trigger + 1+ text_add = { 1+ localization_str_identifier? } + 1+ description_case + 0/1 trigger + 1+ text_add = { 1+ localization_str_identifier? } + */ + class GenerateArticle final : public Typed { + friend std::unique_ptr<GenerateArticle> std::make_unique<GenerateArticle>(); + public: + class PictureCase { + friend class GenerateArticle; + + std::vector<std::string> PROPERTY(pictures); + ConditionScript PROPERTY(trigger); + + PictureCase(ConditionScript&& trigger_new, std::vector<std::string> pictures_new); + + public: + PictureCase(PictureCase&&) = default; + }; + + class TextCase { + friend class GenerateArticle; + + ConditionScript PROPERTY(trigger); + std::vector<std::string> PROPERTY(text); + + TextCase(ConditionScript&& trigger_new, std::vector<std::string> text_new); + + public: + TextCase(TextCase&&) = default; + }; + + + private: + article_size_t PROPERTY(size); + PictureCase PROPERTY(picture_case); + TextCase PROPERTY(title_case); + TextCase PROPERTY(description_case); + + protected: + GenerateArticle(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + GenerateArticle(GenerateArticle&&) = default; + virtual ~GenerateArticle() = default; + + OV_DETAIL_GET_TYPE + + }; + + /* + TODO + 0+ pattern + 1 string name **** not the same as the generator/priority/article name + 1 generator_selector + 1 news_priority + 1+ generate_article + + A Pattern object is not necessarily obtained from finding one in the defines, + the generator selector, news_priority, and article generators can be on their own in the + file, but are of course linked by sharing the type and name + + */ + class Pattern final : public Named<> { + friend std::unique_ptr<Pattern> std::make_unique<Pattern>(); + + private: + GeneratorSelector PROPERTY(selector); + NewsPriority PROPERTY(priority); + std::vector<GenerateArticle> PROPERTY(article_formats); + + protected: + Pattern(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + Pattern(Pattern&&) = default; + virtual ~Pattern() = default; + + OV_DETAIL_GET_TYPE + + }; + + + + /* + TODO + ===== scope things ==== + 0/1? on_printing + 1 string type + 1 string name = "default" + 1 effect + 0+ clear_news_scopes = {type = string limit = {...}} + 0/1+? set_news_flag = string >>> trigger can have has_news_flag = string + */ + class OnPrinting final : public Typed { + friend std::unique_ptr<OnPrinting> std::make_unique<OnPrinting>(); + //public: + + private: + EffectScript PROPERTY(effect); + + protected: + OnPrinting(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + OnPrinting(OnPrinting&&) = default; + virtual ~OnPrinting() = default; + + OV_DETAIL_GET_TYPE + + }; + + + + /* + TODO + the difference of on_collection might be that the player is involved? + 0/1 on_collection. desc says "what happens when scope is collected" + 1 string type + 1 effect + 0?+ clear_news_scopes = {type = string limit = { ... }} + */ + class OnCollection { + friend std::unique_ptr<OnCollection> std::make_unique<OnCollection>(); + //public: + + private: + std::string PROPERTY(type); + EffectScript PROPERTY(effect); + + protected: + OnCollection(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map); + + public: + OnCollection(OnCollection&&) = default; + virtual ~OnCollection() = default; + + //OV_DETAIL_GET_TYPE + + }; + + + /* + TODO + 1 style = {...} + 1 name = "default_style" + 1 trigger = {} + 1 gui_windows = "news_window_default" + 1+ article = { size = large|medium|small gui_window = "article_SIZE_INT"} + 1 article_limits = { IDENTIFIER = INT IDENTIFIER= INT ... } + 1 title_image = { ... } + 0?+ case + 0/1 trigger + 1 picture = "news/bla.dds" + */ + + class Style final : public Named<> { + friend std::unique_ptr<Style> std::make_unique<Style>(); + public: + class Article { + friend class Style; + std::string PROPERTY(gui_window); + article_size_t PROPERTY(size); + + Article(std::string_view new_gui_window, article_size_t new_size = article_size_t::small); + + public: + Article(Article&&) = default; + }; + + class TitleImage { + friend class Style; + std::vector<Case> PROPERTY(cases); + + TitleImage(std::vector<Case> new_cases); + + public: + TitleImage(TitleImage&&) = default; + }; + + private: + ConditionScript PROPERTY(trigger); + std::string PROPERTY(gui_window); + std::vector<Article> PROPERTY(articles); + TitleImage PROPERTY(title_image); + //using sfx_asset_map_t = deque_ordered_map<godot::StringName, godot::Ref<godot::AudioStreamWAV>>; + identifier_int_map PROPERTY(article_limits_map); + // map < IDENTIFIER, INT > + + protected: + Style(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + Style(Style&&) = default; + virtual ~Style() = default; + + OV_DETAIL_GET_TYPE + + }; + + + + struct NewsManager { + private: + //IdentifierRegistry<Pattern> IDENTIFIER_REGISTRY(patterns); + + /* + TODO + New GP has "default", "player_is_gp", "player_loss_gp" as identifier, fake names has "int", AI ones have "default" or "player" + 0+ identifier_for_pattern (ie. this could be any random name) + ex. dino_news_pattern1 or AiAfraidOf + these correspond to a pattern identifier (the ..._news_pattern will correspond to the name of pattern) + 1 unlabelled string identifier + 0/1 unlabelled string "case string" + 1 unlabelled string "TITLE_IDENTIFIER1 TITLE_IDENTIFIER2 ...." + 0/1? unlabelled string "LONG_DESC1 LONG_DESC2 ..." + 0/1? unlabelled string "MEDIUM_DESC ..." + 1? unlabelled string "SHORT_DESC ...." + */ + //>>>>>>> map < identifier, std::vector< std::vector<std::string> > > + identifier_str_collection_map news_str_collection;//PROPERTY(limits_map); + + + /* + TODO + news_layout.txt + 1 article_tensions = { IDENTIFIER = int IDENTIFIER = INT ... }, comments say default int is 1 + We should find the identifiers in the various components of a pattern (type property) + */ + // map < IDENTIFIER, INT > + identifier_int_map article_tensions;//PROPERTY(limits_map); + + //Songs.txt + public: + bool load_songs_file(ast::NodeCPtr root); + bool parse_scripts(DefinitionManager const& definition_manager); + }; + + + + + + + +/* + template<typename... Context> + class Named : public LoadBase<Context...> { + std::string PROPERTY(name); + + protected: + Named() = default; + + virtual bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, Context...) override { + using namespace OpenVic::NodeTools; + return add_key_map_entries(key_map, "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(name))); + } + + void _set_name(std::string_view new_name) { + if (!name.empty()) { + Logger::warning("Overriding scene name ", name, " with ", new_name); + } + name = new_name; + } + + public: + Named(Named&&) = default; + virtual ~Named() = default; + + OV_DETAIL_GET_TYPE + }; +*/ + + + /*struct NewsManager { + private: + IdentifierRegistry<Case> IDENTIFIER_REGISTRY(case); + //Songs.txt + public: + bool load_songs_file(ast::NodeCPtr root); + bool parse_scripts(DefinitionManager const& definition_manager); + };*/ + + +/* + class Actor final : public Object { + friend std::unique_ptr<Actor> std::make_unique<Actor>(); + + public: + struct attachment.... + private: + fixed_point_t PROPERTY(scale); + std::string PROPERTY(model_file); + std::optional<Animation> PROPERTY(idle_animation); + std::vector<Attachment> PROPERTY(attachments); + + bool _set_animation(std::string_view name, std::string_view file, fixed_point_t scroll_time); + + protected: + Actor(); + + bool _fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) override; + + public: + Actor(Actor&&) = default; + virtual ~Actor() = default; + + OV_DETAIL_GET_TYPE + }; +*/ + + +} + |