From f91f64dd4c1b801c043789eadfb8b1ba50a93862 Mon Sep 17 00:00:00 2001 From: Nemrav <> Date: Sun, 29 Sep 2024 15:48:59 -0300 Subject: news objects initial --- src/openvic-simulation/dataloader/Dataloader.cpp | 5 +- src/openvic-simulation/misc/NewsArticle.cpp | 173 ++++++++ src/openvic-simulation/misc/NewsArticle.hpp | 500 +++++++++++++++++++++++ 3 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 src/openvic-simulation/misc/NewsArticle.cpp create mode 100644 src/openvic-simulation/misc/NewsArticle.hpp (limited to 'src') 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 +#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&&> callback) { + return expect_dictionary_keys_and_length( + length_callback, + //_expect_instance(callback) + /*"news_priority", ZERO_OR_MORE, expect_dictionary_keys( + "case", ZERO_OR_MORE, success_callback + ),*/ + "news_priority", ZERO_OR_MORE, _expect_instance(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&&> callback) { + return expect_dictionary_keys_and_length( + length_callback, + + "EMFXActorType", ZERO_OR_MORE, _expect_instance(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( + "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 +#include +#include +#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 = { + { "small", article_size_t::small }, + { "medium", article_size_t::medium }, + { "large", article_size_t::large } + }; + + using identifier_int_map = deque_ordered_map; + using identifier_str_collection_map = deque_ordered_map>>; + + 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&&> 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 std::make_unique(); + //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 std::make_unique(); + //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 std::make_unique(); + public: + class PictureCase { + friend class GenerateArticle; + + std::vector PROPERTY(pictures); + ConditionScript PROPERTY(trigger); + + PictureCase(ConditionScript&& trigger_new, std::vector pictures_new); + + public: + PictureCase(PictureCase&&) = default; + }; + + class TextCase { + friend class GenerateArticle; + + ConditionScript PROPERTY(trigger); + std::vector PROPERTY(text); + + TextCase(ConditionScript&& trigger_new, std::vector 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 std::make_unique(); + + private: + GeneratorSelector PROPERTY(selector); + NewsPriority PROPERTY(priority); + std::vector 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 std::make_unique(); + //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 std::make_unique(); + //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