diff options
51 files changed, 1477 insertions, 1484 deletions
diff --git a/deps/openvic-dataloader b/deps/openvic-dataloader -Subproject 0c5ecbc9b693a11548b55c2e0cc0c0a50a0f5f7 +Subproject efc11f571794acd64884680834c6636a03e8322 diff --git a/src/headless/main.cpp b/src/headless/main.cpp index 36a1329..0400302 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -74,7 +74,7 @@ static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) int main(int argc, char const* argv[]) { Logger::set_logger_funcs(); - char const* program_name = Logger::get_filename(argc > 0 ? argv[0] : nullptr, "<program>"); + char const* program_name = StringUtils::get_filename(argc > 0 ? argv[0] : nullptr, "<program>"); fs::path root; bool run_tests = false; int argn = 0; diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp index 261dea5..427fbb1 100644 --- a/src/openvic-simulation/GameManager.cpp +++ b/src/openvic-simulation/GameManager.cpp @@ -47,7 +47,7 @@ bool GameManager::setup() { return ret; } -Date const& GameManager::get_today() const { +Date GameManager::get_today() const { return today; } diff --git a/src/openvic-simulation/GameManager.hpp b/src/openvic-simulation/GameManager.hpp index 0b2a40c..9ee3442 100644 --- a/src/openvic-simulation/GameManager.hpp +++ b/src/openvic-simulation/GameManager.hpp @@ -51,7 +51,7 @@ namespace OpenVic { bool setup(); - Date const& get_today() const; + Date get_today() const; bool expand_building(Province::index_t province_index, std::string_view building_type_identifier); /* Hardcoded data for defining things for which parsing from files has diff --git a/src/openvic-simulation/Modifier.cpp b/src/openvic-simulation/Modifier.cpp index a42cc66..c3b7f6a 100644 --- a/src/openvic-simulation/Modifier.cpp +++ b/src/openvic-simulation/Modifier.cpp @@ -47,7 +47,7 @@ fixed_point_t ModifierValue::get_effect(ModifierEffect const* effect, bool* succ } bool ModifierValue::has_effect(ModifierEffect const* effect) const { - return values.find(effect) != values.end(); + return values.contains(effect); } ModifierValue& ModifierValue::operator+=(ModifierValue const& right) { @@ -96,7 +96,7 @@ Modifier const& ModifierInstance::get_modifier() const { return modifier; } -Date const& ModifierInstance::get_expiry_date() const { +Date ModifierInstance::get_expiry_date() const { return expiry_date; } @@ -107,7 +107,9 @@ bool ModifierManager::add_modifier_effect(std::string_view identifier, bool posi Logger::error("Invalid modifier effect identifier - empty!"); return false; } - return modifier_effects.add_item({ identifier, positive_good, format }); + return modifier_effects.add_item( + std::make_unique<ModifierEffect>(std::move(identifier), std::move(positive_good), std::move(format)) + ); } bool ModifierManager::add_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon) { @@ -150,8 +152,7 @@ bool ModifierManager::setup_modifier_effects() { ret &= add_modifier_effect("influence_modifier", true); ret &= add_modifier_effect("issue_change_speed", true); ret &= add_modifier_effect("land_organisation", true); - // weird, land_unit_start_experience = 15 would give a 15% boost - ret &= add_modifier_effect("land_unit_start_experience", true); + ret &= add_modifier_effect("land_unit_start_experience", true, PERCENTAGE_DECIMAL); ret &= add_modifier_effect("leadership_modifier", true); ret &= add_modifier_effect("loan_interest", false); ret &= add_modifier_effect("max_loan_modifier", true); @@ -172,8 +173,7 @@ bool ModifierManager::setup_modifier_effects() { ret &= add_modifier_effect("mobilisation_impact", false); ret &= add_modifier_effect("mobilisation_size", true); ret &= add_modifier_effect("naval_organisation", true); - // weird, naval_unit_start_experience = 15 would give a 15% boost - ret &= add_modifier_effect("naval_unit_start_experience", true); + ret &= add_modifier_effect("naval_unit_start_experience", true, PERCENTAGE_DECIMAL); ret &= add_modifier_effect("non_accepted_pop_consciousness_modifier", false, RAW_DECIMAL); ret &= add_modifier_effect("non_accepted_pop_militancy_modifier", false, RAW_DECIMAL); ret &= add_modifier_effect("org_regain", true); @@ -188,7 +188,9 @@ bool ModifierManager::setup_modifier_effects() { ret &= add_modifier_effect("research_points_modifier", true); ret &= add_modifier_effect("research_points_on_conquer", true); ret &= add_modifier_effect("rgo_output", true); + ret &= add_modifier_effect("RGO_output", true); ret &= add_modifier_effect("rgo_throughput", true); + ret &= add_modifier_effect("RGO_throughput", true); ret &= add_modifier_effect("rich_income_modifier", true); ret &= add_modifier_effect("rich_life_needs", true); ret &= add_modifier_effect("rich_everyday_needs", true); @@ -197,9 +199,10 @@ bool ModifierManager::setup_modifier_effects() { ret &= add_modifier_effect("ruling_party_support", true); ret &= add_modifier_effect("social_reform_desire", false); ret &= add_modifier_effect("supply_consumption", false); - // weird, naval_unit_start_experience = 15 would give a 15% boost - ret &= add_modifier_effect("unit_start_experience", true); + ret &= add_modifier_effect("tax_efficiency", true); + ret &= add_modifier_effect("unit_start_experience", true, PERCENTAGE_DECIMAL); ret &= add_modifier_effect("war_exhaustion", false); + // TODO: make technology group modifiers dynamic ret &= add_modifier_effect("army_tech_research_bonus", true); ret &= add_modifier_effect("commerce_tech_research_bonus", true); @@ -224,9 +227,13 @@ bool ModifierManager::setup_modifier_effects() { ret &= add_modifier_effect("pop_militancy_modifier", false, RAW_DECIMAL); ret &= add_modifier_effect("population_growth", true); ret &= add_modifier_effect("farm_rgo_eff", true); + ret &= add_modifier_effect("farm_RGO_eff", true); ret &= add_modifier_effect("farm_rgo_size", true); + ret &= add_modifier_effect("farm_RGO_size", true); ret &= add_modifier_effect("mine_rgo_eff", true); + ret &= add_modifier_effect("mine_RGO_eff", true); ret &= add_modifier_effect("mine_rgo_size", true); + ret &= add_modifier_effect("mine_RGO_size", true); ret &= add_modifier_effect("movement_cost", false); ret &= add_modifier_effect("supply_limit", true, RAW_DECIMAL); @@ -242,22 +249,6 @@ bool ModifierManager::setup_modifier_effects() { ret &= add_modifier_effect("reliability", true, RAW_DECIMAL); ret &= add_modifier_effect("speed", true); - /* These should be added automatically for each Building loaded (or at least - * non-factories), however currently we need modifier effects locked before we - * can load buildings, so some architectural changes will be needed. - */ - ret &= add_modifier_effect("max_fort", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("min_build_fort", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("max_naval_base", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("min_build_naval_base", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("max_railroad", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("min_build_railroad", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("max_university", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("min_build_university", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("max_bank", true, ModifierEffect::format_t::INT); - ret &= add_modifier_effect("min_build_bank", true, ModifierEffect::format_t::INT); - - modifier_effects.lock(); return ret; } @@ -269,7 +260,7 @@ key_value_callback_t ModifierManager::_modifier_effect_callback( ModifierEffect const* effect = get_modifier_effect_by_identifier(key); if (effect != nullptr) { if (effect_validator(*effect)) { - if (modifier.values.find(effect) == modifier.values.end()) { + if (!modifier.values.contains(effect)) { return expect_fixed_point(assign_variable_callback(modifier.values[effect]))(value); } else { Logger::error("Duplicate modifier effect: ", key); diff --git a/src/openvic-simulation/Modifier.hpp b/src/openvic-simulation/Modifier.hpp index 2a98782..fc37655 100644 --- a/src/openvic-simulation/Modifier.hpp +++ b/src/openvic-simulation/Modifier.hpp @@ -6,9 +6,14 @@ namespace OpenVic { struct ModifierManager; struct ModifierEffect : HasIdentifier { - friend struct ModifierManager; + enum class format_t { + PROPORTION_DECIMAL, /* An unscaled fraction/ratio, with 1 being "full"/"whole" */ + PERCENTAGE_DECIMAL, /* A fraction/ratio scaled so that 100 is "full"/"whole" */ + RAW_DECIMAL, /* A continuous quantity, e.g. attack strength */ + INT /* A discrete quantity, e.g. building count limit */ + }; - enum class format_t { RAW_DECIMAL, PERCENTAGE_DECIMAL, INT }; + friend std::unique_ptr<ModifierEffect> std::make_unique<ModifierEffect>(std::string_view&&, bool&&, format_t&&); private: /* If true, positive values will be green and negative values will be red. @@ -88,16 +93,19 @@ namespace OpenVic { public: Modifier const& get_modifier() const; - Date const& get_expiry_date() const; + Date get_expiry_date() const; }; template<typename Fn> concept ModifierEffectValidator = std::predicate<Fn, ModifierEffect const&>; struct ModifierManager { - + /* Some ModifierEffects are generated mid-load, such as max/min count modifiers for each building, so + * we can't lock it until loading is over. This means we can't rely on locking for pointer stability, + * so instead we use an IdentifierInstanceRegistry (using std::unique_ptr's under the hood). + */ private: - IdentifierRegistry<ModifierEffect> modifier_effects; + IdentifierInstanceRegistry<ModifierEffect> modifier_effects; IdentifierRegistry<Modifier> modifiers; /* effect_validator takes in ModifierEffect const& */ @@ -111,7 +119,7 @@ namespace OpenVic { bool add_modifier_effect( std::string_view identifier, bool province_good, - ModifierEffect::format_t format = ModifierEffect::format_t::PERCENTAGE_DECIMAL + ModifierEffect::format_t format = ModifierEffect::format_t::PROPORTION_DECIMAL ); IDENTIFIER_REGISTRY_ACCESSORS(modifier_effect) diff --git a/src/openvic-simulation/country/Country.cpp b/src/openvic-simulation/country/Country.cpp index fc867d3..6ad13ee 100644 --- a/src/openvic-simulation/country/Country.cpp +++ b/src/openvic-simulation/country/Country.cpp @@ -21,180 +21,222 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -std::string_view CountryParty::get_name() const { - return name; -} +CountryParty::CountryParty( + std::string_view new_identifier, Date new_start_date, Date new_end_date, Ideology const& new_ideology, + policy_map_t&& new_policies +) : HasIdentifier { new_identifier }, start_date { new_start_date }, end_date { new_end_date }, ideology { new_ideology }, + policies { std::move(new_policies) } {} -const Date& CountryParty::get_start_date() const { +Date CountryParty::get_start_date() const { return start_date; } -const Date& CountryParty::get_end_date() const { +Date CountryParty::get_end_date() const { return end_date; } -const Ideology& CountryParty::get_ideology() const { +Ideology const& CountryParty::get_ideology() const { return ideology; } -const std::vector<const Issue*>& CountryParty::get_policies() const { +CountryParty::policy_map_t const& CountryParty::get_policies() const { return policies; } -CountryParty::CountryParty( - std::string_view new_name, Date new_start_date, Date new_end_date, const Ideology& new_ideology, - std::vector<const Issue*>&& new_policies -) : name { new_name }, start_date { new_start_date }, end_date { new_end_date }, ideology { new_ideology }, - policies { std::move(new_policies) } {} - -std::string_view UnitNames::get_identifier() const { - return identifier; -} - -const std::vector<std::string>& UnitNames::get_names() const { - return names; -} - -UnitNames::UnitNames(std::string_view new_identifier, std::vector<std::string>&& new_names) - : identifier { new_identifier }, names { std::move(new_names) } {} +Country::Country( + std::string_view new_identifier, colour_t new_colour, GraphicalCultureType const& new_graphical_culture, + IdentifierRegistry<CountryParty>&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag, + government_colour_map_t&& new_alternative_colours +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, graphical_culture { new_graphical_culture }, + parties { std::move(new_parties) }, unit_names { std::move(new_unit_names) }, dynamic_tag { new_dynamic_tag }, + alternative_colours { std::move(new_alternative_colours) } {} -const GraphicalCultureType& Country::get_graphical_culture() const { +GraphicalCultureType const& Country::get_graphical_culture() const { return graphical_culture; } -const std::vector<CountryParty>& Country::get_parties() const { - return parties; -} - -const std::vector<UnitNames>& Country::get_unit_names() const { +Country::unit_names_map_t const& Country::get_unit_names() const { return unit_names; } -const std::map<const GovernmentType*, colour_t>& Country::get_alternative_colours() const { - return alternative_colours; -} - -const bool Country::is_dynamic_tag() const { +bool Country::is_dynamic_tag() const { return dynamic_tag; } -Country::Country( - std::string_view new_identifier, colour_t new_color, const GraphicalCultureType& new_graphical_culture, - std::vector<CountryParty>&& new_parties, std::vector<UnitNames>&& new_unit_names, bool new_dynamic_tag, - std::map<const GovernmentType*, colour_t>&& new_alternative_colours -) : HasIdentifierAndColour(new_identifier, new_color, false, false), graphical_culture { new_graphical_culture }, - parties { std::move(new_parties) }, unit_names { std::move(new_unit_names) }, dynamic_tag { new_dynamic_tag }, - alternative_colours { std::move(new_alternative_colours) } {} +Country::government_colour_map_t const& Country::get_alternative_colours() const { + return alternative_colours; +} CountryManager::CountryManager() : countries { "countries" } {} bool CountryManager::add_country( - std::string_view identifier, colour_t color, const GraphicalCultureType& graphical_culture, - std::vector<CountryParty>&& parties, std::vector<UnitNames>&& unit_names, bool dynamic_tag, - std::map<const GovernmentType*, colour_t>&& alternative_colours + std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, + IdentifierRegistry<CountryParty>&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag, + Country::government_colour_map_t&& alternative_colours ) { if (identifier.empty()) { + Logger::error("Invalid country identifier - empty!"); + return false; + } + if (!valid_basic_identifier(identifier)) { + Logger::error( + "Invalid country identifier: ", identifier, " (can only contain alphanumeric characters and underscores)" + ); + return false; + } + if (colour > MAX_COLOUR_RGB) { + Logger::error("Invalid country colour for ", identifier, ": ", colour_to_hex_string(colour)); + return false; + } + if (graphical_culture == nullptr) { + Logger::error("Null graphical culture for country ", identifier); return false; } return countries.add_item({ - identifier, color, graphical_culture, std::move(parties), std::move(unit_names), - dynamic_tag, std::move(alternative_colours) + identifier, colour, *graphical_culture, std::move(parties), std::move(unit_names), dynamic_tag, + std::move(alternative_colours) }); } -bool CountryManager::load_country_data_file( - GameManager& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root -) { - colour_t color; - const GraphicalCultureType* graphical_culture; - std::vector<CountryParty> country_parties; - std::vector<UnitNames> unit_names; - std::map<const GovernmentType*, colour_t> alternative_colours; - - bool ret = expect_dictionary_keys_and_default( - [&game_manager, &alternative_colours, &name](std::string_view key, ast::NodeCPtr value) -> bool { - const GovernmentType* colour_gov_type; - bool ret = game_manager.get_politics_manager().get_government_type_manager() - .expect_government_type_str(assign_variable_callback_pointer(colour_gov_type))(key); - - if (!ret) { - return false; - } - - colour_t alternative_colour; - ret &= expect_colour(assign_variable_callback(alternative_colour))(value); - - if (!ret) { - return false; - } - - return alternative_colours.emplace(std::move(colour_gov_type), std::move(alternative_colour)).second; - }, - "color", ONE_EXACTLY, expect_colour(assign_variable_callback(color)), - "graphical_culture", ONE_EXACTLY, expect_identifier_or_string( - [&game_manager, &graphical_culture, &name](std::string_view value) -> bool { - graphical_culture = game_manager.get_pop_manager().get_culture_manager() - .get_graphical_culture_type_by_identifier(value); - if (graphical_culture == nullptr) { - Logger::error("When loading country ", name, ", specified graphical culture ", value, - " is invalid!\nCheck that CultureManager has loaded before CountryManager."); - } - - return graphical_culture != nullptr; +bool CountryManager::load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root) { + static constexpr std::string_view common_dir = "common/"; + bool is_dynamic = false; + + const bool ret = expect_dictionary_reserve_length( + countries, + [this, &game_manager, &is_dynamic, &dataloader](std::string_view key, ast::NodeCPtr value) -> bool { + if (key == "dynamic_tags") { + return expect_bool([&is_dynamic](bool val) -> bool { + if (val == is_dynamic) { + Logger::warning("Redundant \"is_dynamic\", already ", val ? "true" : "false"); + } else { + if (is_dynamic) { + Logger::warning("Changing \"is_dynamic\" back to false"); + } + is_dynamic = val; + } + return true; + })(value); } - ), - "party", ZERO_OR_MORE, [&game_manager, &country_parties, &name](ast::NodeCPtr value) -> bool { - std::string_view party_name; - Date start_date, end_date; - const Ideology* ideology; - std::vector<const Issue*> policies; - - bool ret = expect_dictionary_keys_and_default( - [&game_manager, &policies](std::string_view key, ast::NodeCPtr value) -> bool { - const Issue* policy; - bool ret = expect_identifier_or_string( - game_manager.get_politics_manager().get_issue_manager().expect_issue_str( - assign_variable_callback_pointer(policy) - ) - )(value); - - if (ret && policy->get_group().get_identifier() == key) { - policies.push_back(policy); + if (expect_string( + [this, &game_manager, is_dynamic, &dataloader, &key](std::string_view filepath) -> bool { + if (load_country_data_file( + game_manager, key, is_dynamic, + Dataloader::parse_defines( + dataloader.lookup_file(StringUtils::append_string_views(common_dir, filepath)) + ).get_file_node() + )) { return true; } - + Logger::error("Failed to load country data file: ", filepath); return false; - }, - "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(party_name)), - "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start_date)), - "end_date", ONE_EXACTLY, expect_date(assign_variable_callback(end_date)), - "ideology", ONE_EXACTLY, expect_identifier_or_string(game_manager.get_politics_manager() - .get_ideology_manager().expect_ideology_str(assign_variable_callback_pointer(ideology))) - )(value); + } + )(value)) { + return true; + } + Logger::error("Failed to load country: ", key); + return false; + } + )(root); + lock_countries(); + return ret; +} - country_parties.push_back({ party_name, start_date, end_date, *ideology, std::move(policies) }); +node_callback_t CountryManager::load_country_party( + PoliticsManager const& politics_manager, IdentifierRegistry<CountryParty>& country_parties +) const { + return [&politics_manager, &country_parties](ast::NodeCPtr value) -> bool { + std::string_view party_name; + Date start_date, end_date; + Ideology const* ideology; + CountryParty::policy_map_t policies; + + bool ret = expect_dictionary_keys_and_default( + [&politics_manager, &policies, &party_name](std::string_view key, ast::NodeCPtr value) -> bool { + return politics_manager.get_issue_manager().expect_issue_group_str( + [&politics_manager, &policies, value, &party_name](IssueGroup const& group) -> bool { + if (policies.contains(&group)) { + Logger::error("Country party ", party_name, " has duplicate entry for ", group.get_identifier()); + return false; + } + return politics_manager.get_issue_manager().expect_issue_identifier( + [&policies, &group](Issue const& issue) -> bool { + if (&issue.get_group() == &group) { + policies.emplace(&group, &issue); + return true; + } + // TODO - change this back to error/false once TGC no longer has this issue + Logger::warning("Invalid policy ", issue.get_identifier(), ", group is ", + issue.get_group().get_identifier(), " when ", group.get_identifier(), " was expected"); + return true; + } + )(value); + } + )(key); + }, + "name", ONE_EXACTLY, expect_string(assign_variable_callback(party_name)), + "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start_date)), + "end_date", ONE_EXACTLY, expect_date(assign_variable_callback(end_date)), + "ideology", ONE_EXACTLY, + politics_manager.get_ideology_manager().expect_ideology_identifier(assign_variable_callback_pointer(ideology)) + )(value); + + ret &= country_parties.add_item( + { party_name, start_date, end_date, *ideology, std::move(policies) }, duplicate_warning_callback + ); + + return ret; + }; +} - return ret; +bool CountryManager::load_country_data_file( + GameManager const& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root +) { + colour_t colour; + GraphicalCultureType const* graphical_culture; + IdentifierRegistry<CountryParty> parties { "country parties" }; + Country::unit_names_map_t unit_names; + Country::government_colour_map_t alternative_colours; + bool ret = expect_dictionary_keys_and_default( + [&game_manager, &alternative_colours, &name](std::string_view key, ast::NodeCPtr value) -> bool { + return game_manager.get_politics_manager().get_government_type_manager().expect_government_type_str( + [&alternative_colours, &name, &value](GovernmentType const& government_type) -> bool { + if (alternative_colours.contains(&government_type)) { + Logger::error( + "Country ", name, " has duplicate entry for ", government_type.get_identifier(), + " alternative colour" + ); + return false; + } + return expect_colour([&alternative_colours, &government_type](colour_t colour) -> bool { + return alternative_colours.emplace(&government_type, std::move(colour)).second; + })(value); + } + )(key); }, - "unit_names", ZERO_OR_ONE, expect_dictionary([&unit_names](std::string_view key, ast::NodeCPtr value) -> bool { - std::vector<std::string> names; - - bool ret = expect_list(expect_identifier_or_string( - [&names](std::string_view value) -> bool { - names.push_back(std::string(value)); - return true; + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "graphical_culture", ONE_EXACTLY, + game_manager.get_pop_manager().get_culture_manager().expect_graphical_culture_type_identifier( + assign_variable_callback_pointer(graphical_culture) + ), + "party", ZERO_OR_MORE, load_country_party(game_manager.get_politics_manager(), parties), + "unit_names", ZERO_OR_ONE, + game_manager.get_military_manager().get_unit_manager().expect_unit_dictionary( + [&unit_names, &name](Unit const& unit, ast::NodeCPtr value) -> bool { + if (unit_names.contains(&unit)) { + Logger::error("Country ", name, " has duplicate entry for ", unit.get_identifier(), " name list"); + return false; + } + return name_list_callback([&unit_names, &unit](std::vector<std::string>&& list) -> bool { + return unit_names.emplace(&unit, std::move(list)).second; + })(value); } - ))(value); - - unit_names.push_back({ key, std::move(names) }); - - return ret; - }) + ) )(root); ret &= add_country( - name, color, *graphical_culture, std::move(country_parties), std::move(unit_names), is_dynamic, + name, colour, graphical_culture, std::move(parties), std::move(unit_names), is_dynamic, std::move(alternative_colours) ); return ret; diff --git a/src/openvic-simulation/country/Country.hpp b/src/openvic-simulation/country/Country.hpp index 66ce2e6..50cca04 100644 --- a/src/openvic-simulation/country/Country.hpp +++ b/src/openvic-simulation/country/Country.hpp @@ -5,17 +5,18 @@ #include <string> #include <string_view> #include <type_traits> -#include <unordered_map> #include <vector> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> #include "openvic-simulation/dataloader/Dataloader.hpp" #include "openvic-simulation/map/Province.hpp" +#include "openvic-simulation/military/Unit.hpp" #include "openvic-simulation/politics/Government.hpp" #include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/politics/Issue.hpp" #include "openvic-simulation/politics/NationalValue.hpp" +#include "openvic-simulation/politics/PoliticsManager.hpp" #include "openvic-simulation/pop/Culture.hpp" #include "openvic-simulation/pop/Religion.hpp" #include "openvic-simulation/types/Colour.hpp" @@ -26,81 +27,85 @@ namespace OpenVic { struct GameManager; struct CountryManager; - struct CountryParty { + struct CountryParty : HasIdentifier { friend struct CountryManager; + using policy_map_t = std::map<IssueGroup const*, Issue const*>; + private: - const std::string name; const Date start_date; const Date end_date; - const Ideology& ideology; - const std::vector<Issue const*> policies; + Ideology const& ideology; + const policy_map_t policies; CountryParty( - std::string_view new_name, Date new_start_date, Date new_end_date, const Ideology& new_ideology, - std::vector<const Issue*>&& new_policies + std::string_view new_identifier, Date new_start_date, Date new_end_date, Ideology const& new_ideology, + policy_map_t&& new_policies ); public: - std::string_view get_name() const; - const Date& get_start_date() const; - const Date& get_end_date() const; - const Ideology& get_ideology() const; - const std::vector<const Issue*>& get_policies() const; - }; - - struct UnitNames { - friend struct CountryManager; - - private: - const std::string identifier; - const std::vector<std::string> names; - - UnitNames(std::string_view new_identifier, std::vector<std::string>&& new_names); + CountryParty(CountryParty&&) = default; - public: - std::string_view get_identifier() const; - const std::vector<std::string>& get_names() const; + Date get_start_date() const; + Date get_end_date() const; + Ideology const& get_ideology() const; + policy_map_t const& get_policies() const; }; struct Country : HasIdentifierAndColour { friend struct CountryManager; + using unit_names_map_t = std::map<Unit const*, std::vector<std::string>>; + using government_colour_map_t = std::map<GovernmentType const*, colour_t>; + private: - const GraphicalCultureType& graphical_culture; - const std::vector<CountryParty> parties; - const std::vector<UnitNames> unit_names; + GraphicalCultureType const& graphical_culture; + /* Not const to allow elements to be moved, otherwise a copy is forced + * which causes a compile error as the copy constructor has been deleted. + */ + IdentifierRegistry<CountryParty> parties; + const unit_names_map_t unit_names; const bool dynamic_tag; - const std::map<const GovernmentType*, colour_t> alternative_colours; + const government_colour_map_t alternative_colours; Country( - std::string_view new_identifier, colour_t new_color, const GraphicalCultureType& new_graphical_culture, - std::vector<CountryParty>&& new_parties, std::vector<UnitNames>&& new_unit_names, const bool new_dynamic_tag, - std::map<const GovernmentType*, colour_t>&& new_alternative_colours + std::string_view new_identifier, colour_t new_colour, GraphicalCultureType const& new_graphical_culture, + IdentifierRegistry<CountryParty>&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag, + government_colour_map_t&& new_alternative_colours ); public: - const GraphicalCultureType& get_graphical_culture() const; - const std::vector<CountryParty>& get_parties() const; - const std::vector<UnitNames>& get_unit_names() const; - const bool is_dynamic_tag() const; - const std::map<const GovernmentType*, colour_t>& get_alternative_colours() const; + Country(Country&&) = default; + + IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(party, parties) + + GraphicalCultureType const& get_graphical_culture() const; + unit_names_map_t const& get_unit_names() const; + bool is_dynamic_tag() const; + government_colour_map_t const& get_alternative_colours() const; }; struct CountryManager { private: IdentifierRegistry<Country> countries; + NodeTools::node_callback_t load_country_party( + PoliticsManager const& politics_manager, IdentifierRegistry<CountryParty>& country_parties + ) const; + public: CountryManager(); bool add_country( - std::string_view identifier, colour_t color, const GraphicalCultureType& graphical_culture, - std::vector<CountryParty>&& parties, std::vector<UnitNames>&& unit_names, bool dynamic_tag, - std::map<const GovernmentType*, colour_t>&& alternative_colours + std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, + IdentifierRegistry<CountryParty>&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag, + Country::government_colour_map_t&& alternative_colours ); - IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries); + IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries) - bool load_country_data_file(GameManager& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root); + bool load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root); + bool load_country_data_file( + GameManager const& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root + ); }; } diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 5301aa1..6911de5 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -34,13 +34,22 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; using namespace ovdl; -// Windows and Mac by default act like case insensitive filesystems -static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) { -#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) +using StringUtils::append_string_views; + +#define FILESYSTEM_CASE_INSENSITIVE (defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))) +#define FILESYSTEM_NEEDS_FORWARD_SLASHES (!defined(_WIN32)) + +static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::string_view rhs) { constexpr auto ichar_equals = [](unsigned char l, unsigned char r) { return std::tolower(l) == std::tolower(r); }; return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), ichar_equals); +} + +// Windows and Mac by default act like case insensitive filesystems +static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) { +#if FILESYSTEM_CASE_INSENSITIVE + return path_equals_case_insensitive(lhs, rhs); #else return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); #endif @@ -69,13 +78,13 @@ static bool filename_equals(const is_filename auto& lhs, const is_filename auto& static fs::path _search_for_game_path(fs::path hint_path = {}) { // Apparently max amount of steam libraries is 8, if incorrect please correct it to the correct max amount - constexpr int max_amount_of_steam_libraries = 8; - constexpr std::string_view Victoria_2_folder = "Victoria 2"; - constexpr std::string_view v2_game_exe = "v2game.exe"; - constexpr std::string_view steamapps = "steamapps"; - constexpr std::string_view libraryfolders = "libraryfolders.vdf"; - constexpr std::string_view vic2_appmanifest = "appmanifest_42960.acf"; - constexpr std::string_view common_folder = "common"; + static constexpr int max_amount_of_steam_libraries = 8; + static constexpr std::string_view Victoria_2_folder = "Victoria 2"; + static constexpr std::string_view v2_game_exe = "v2game.exe"; + static constexpr std::string_view steamapps = "steamapps"; + static constexpr std::string_view libraryfolders = "libraryfolders.vdf"; + static constexpr std::string_view vic2_appmanifest = "appmanifest_42960.acf"; + static constexpr std::string_view common_folder = "common"; std::error_code error_code; @@ -359,37 +368,77 @@ bool Dataloader::set_roots(path_vector_t const& new_roots) { return ret; } -fs::path Dataloader::lookup_file(fs::path const& path) const { +fs::path Dataloader::lookup_file(std::string_view path, bool print_error) const { +#if FILESYSTEM_NEEDS_FORWARD_SLASHES + /* Back-slashes need to be converted into forward-slashes */ + const std::string forward_slash_path { StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path)) }; + path = forward_slash_path; +#endif + + const fs::path filepath { path }; + +#if FILESYSTEM_CASE_INSENSITIVE + /* Case-insensitive filesystem */ for (fs::path const& root : roots) { - const fs::path composed = root / path; + const fs::path composed = root / filepath; if (fs::is_regular_file(composed)) { return composed; } } - Logger::error("Lookup for ", path, " failed!"); - return {}; -} - -static bool contains_file_with_name(Dataloader::path_vector_t const& paths, fs::path const& name) { - for (fs::path const& path : paths) { - if (path.filename() == name) { - return true; +#else + /* Case-sensitive filesystem */ + const std::string_view filename = StringUtils::get_filename(path); + for (fs::path const& root : roots) { + const fs::path composed = root / filepath; + if (fs::is_regular_file(composed)) { + return composed; + } + std::error_code ec; + for (fs::directory_entry const& entry : fs::directory_iterator { composed.parent_path(), ec }) { + if (entry.is_regular_file()) { + const fs::path file = entry; + if (path_equals_case_insensitive(file.filename().string(), filename)) { + return file; + } + } } } - return false; +#endif + + if (print_error) { + Logger::error("Lookup for \"", path, "\" failed!"); + } + return {}; } -Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, fs::path const& extension) const { +template<typename _DirIterator, std::predicate<fs::path const&, fs::path const&> _Equiv> +Dataloader::path_vector_t Dataloader::_lookup_files_in_dir(std::string_view path, fs::path const& extension) const { +#if FILESYSTEM_NEEDS_FORWARD_SLASHES + /* Back-slashes need to be converted into forward-slashes */ + const std::string forward_slash_path { StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path)) }; + path = forward_slash_path; +#endif + const fs::path filepath { path }; + static constexpr _Equiv Equiv {}; path_vector_t ret; + size_t start_of_current_root_entries; for (fs::path const& root : roots) { - const fs::path composed = root / path; + start_of_current_root_entries = ret.size(); + const fs::path composed = root / filepath; std::error_code ec; - for (fs::directory_entry const& entry : fs::directory_iterator { composed, ec }) { + for (fs::directory_entry const& entry : _DirIterator { composed, ec }) { if (entry.is_regular_file()) { const fs::path file = entry; - if (extension.empty() || file.extension() == extension) { - if (!contains_file_with_name(ret, file.filename())) { + if ((extension.empty() || file.extension() == extension) && !Equiv(file, {})) { + size_t index = 0; + for (; index < ret.size() && !Equiv(file, ret[index]); ++index) {} + if (index >= ret.size()) { ret.push_back(file); + } else if (start_of_current_root_entries <= index) { + Logger::warning( + "Files in the same directory with conflicting names: ", ret[index], " (accepted) and ", file, + " (rejected)" + ); } } } @@ -398,10 +447,43 @@ Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, return ret; } -bool Dataloader::apply_to_files_in_dir(fs::path const& path, fs::path const& extension, callback_t<fs::path const&> callback) - const { +struct EquivFilename { + bool operator()(fs::path const& lhs, fs::path const& rhs) const { + return lhs.filename() == rhs.filename(); + } +}; + +Dataloader::path_vector_t Dataloader::lookup_files_in_dir(std::string_view path, fs::path const& extension) const { + return _lookup_files_in_dir<fs::directory_iterator, EquivFilename>(path, extension); +} + +Dataloader::path_vector_t Dataloader::lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const { + return _lookup_files_in_dir<fs::recursive_directory_iterator, EquivFilename>(path, extension); +} + +struct EquivBasicIdentifierPrefix { + bool operator()(fs::path const& lhs, fs::path const& rhs) const { + const std::string lhs_str = lhs.stem().string(); + const std::string rhs_str = rhs.stem().string(); + return extract_basic_identifier_prefix(lhs_str) == extract_basic_identifier_prefix(rhs_str); + } +}; + +Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir( + std::string_view path, fs::path const& extension +) const { + return _lookup_files_in_dir<fs::directory_iterator, EquivBasicIdentifierPrefix>(path, extension); +} + +Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir_recursive( + std::string_view path, fs::path const& extension +) const { + return _lookup_files_in_dir<fs::recursive_directory_iterator, EquivBasicIdentifierPrefix>(path, extension); +} + +bool Dataloader::apply_to_files(path_vector_t const& files, callback_t<fs::path const&> callback) const { bool ret = true; - for (fs::path const& file : lookup_files_in_dir(path, extension)) { + for (fs::path const& file : files) { if (!callback(file)) { Logger::error("Callback failed for file: ", file); ret = false; @@ -476,8 +558,9 @@ csv::Windows1252Parser Dataloader::parse_csv(fs::path const& path) { bool Dataloader::_load_pop_types( PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager ) const { - static const fs::path pop_type_directory = "poptypes"; - const bool ret = apply_to_files_in_dir(pop_type_directory, ".txt", + static constexpr std::string_view pop_type_directory = "poptypes"; + const bool ret = apply_to_files( + lookup_files_in_dir(pop_type_directory, ".txt"), [&pop_manager, &unit_manager, &good_manager](fs::path const& file) -> bool { return pop_manager.load_pop_type_file( file.stem().string(), unit_manager, good_manager, parse_defines(file).get_file_node() @@ -489,141 +572,95 @@ bool Dataloader::_load_pop_types( } bool Dataloader::_load_units(UnitManager& unit_manager, GoodManager const& good_manager) const { - static const fs::path units_directory = "units"; - const bool ret = apply_to_files_in_dir(units_directory, ".txt", + static constexpr std::string_view units_directory = "units"; + const bool ret = apply_to_files( + lookup_files_in_dir(units_directory, ".txt"), [&unit_manager, &good_manager](fs::path const& file) -> bool { return unit_manager.load_unit_file(good_manager, parse_defines(file).get_file_node()); - }); + } + ); unit_manager.lock_units(); return ret; } -bool Dataloader::_load_oobs(GameManager& game_manager) const { - static const fs::path oob_directory = "history/units"; - - /* used for countries with no defined initial OOB */ - game_manager.get_military_manager().get_deployment_manager().add_deployment( - "NULL", std::vector<Army>(), std::vector<Navy>(), std::vector<Leader>() - ); +bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_file_warnings) const { - bool ret = apply_to_files_in_dir(oob_directory, ".txt", [&game_manager](fs::path const& file) -> bool { - if (file.filename() == "v2dd2.txt") { - return true; /* dev diary just stuck in there for no reason whatsoever */ - } - return game_manager.get_military_manager().get_deployment_manager().load_oob_file( - game_manager, file.filename().string(), parse_defines(file).get_file_node() - ); - }); - - /* we also load OOBs in top level subdirectories, for other start dates etc */ - for (auto root : roots) { - const fs::path path = root / oob_directory; - std::error_code ec; - for (fs::directory_entry const& entry : fs::directory_iterator { path, ec }) { - if (entry.is_directory()) { - ret &= apply_to_files_in_dir(entry, ".txt", [&entry, &game_manager](fs::path const& file) -> bool { - return game_manager.get_military_manager().get_deployment_manager().load_oob_file( - game_manager, (entry.path().filename() / file.filename()).string(), parse_defines(file).get_file_node() - ); - }); + /* Country History */ + static constexpr std::string_view country_history_directory = "history/countries"; + bool ret = apply_to_files( + lookup_basic_indentifier_prefixed_files_in_dir(country_history_directory, ".txt"), + [this, &game_manager, unused_history_file_warnings](fs::path const& file) -> bool { + const std::string filename = file.stem().string(); + const std::string_view country_id = extract_basic_identifier_prefix(filename); + + Country const* country = game_manager.get_country_manager().get_country_by_identifier(country_id); + if (country == nullptr) { + if (unused_history_file_warnings) { + Logger::warning("Found history file for non-existent country: ", country_id); + } + return true; } - } - } - - game_manager.get_military_manager().get_deployment_manager().lock_deployments(); - return ret; -} - -bool Dataloader::_load_countries(GameManager& game_manager) const { - static const fs::path countries_file = "common/countries.txt"; - - bool is_dynamic = false; - bool ret = expect_dictionary([this, &game_manager, &is_dynamic](std::string_view key, ast::NodeCPtr value) -> bool { - if (key == "dynamic_tags") { - return expect_bool(assign_variable_callback(is_dynamic))(value); - } - - std::string_view data_path; - - if (!expect_string(assign_variable_callback(data_path))(value)) { - return false; + return game_manager.get_history_manager().get_country_manager().load_country_history_file( + game_manager, *this, *country, parse_defines(file).get_file_node() + ); } + ); + game_manager.get_history_manager().get_country_manager().lock_country_histories(); - return game_manager.get_country_manager().load_country_data_file( - game_manager, key, is_dynamic, parse_defines(lookup_file(countries_file.parent_path() / data_path)).get_file_node() - ); - })(parse_defines(lookup_file(countries_file)).get_file_node()); - game_manager.get_country_manager().lock_countries(); - - return ret; -} - -bool Dataloader::_load_history(GameManager& game_manager) const { - static const fs::path country_history_directory = "history/countries"; - static const fs::path province_history_directory = "history/provinces"; - - /* Country History */ - bool ret = apply_to_files_in_dir(country_history_directory, ".txt", [this, &game_manager](fs::path const& file) -> bool { - std::string tag = file.filename().string().substr(0, 3); - - if (!game_manager.get_country_manager().has_country_identifier(tag)) { - Logger::error("Error loading history for country ", tag, ": tag not defined!"); - return false; + { + DeploymentManager& deployment_manager = game_manager.get_military_manager().get_deployment_manager(); + deployment_manager.lock_deployments(); + if (deployment_manager.get_missing_oob_file_count() > 0) { + Logger::warning(deployment_manager.get_missing_oob_file_count(), " missing OOB files!"); } - - return game_manager.get_history_manager().get_country_manager().load_country_history_file( - game_manager, tag, parse_defines(lookup_file(file)).get_file_node() - ); - }); - game_manager.get_history_manager().get_country_manager().lock_country_histories(); + } /* Province History */ - for (auto root : roots) { - const fs::path path = root / province_history_directory; - std::error_code ec; - for (fs::directory_entry const& entry : fs::directory_iterator { path, ec }) { - if (entry.is_directory()) { - bool ret = apply_to_files_in_dir(entry, ".txt", [this, &game_manager](fs::path const& file) -> bool { - std::string province_id = file.filename().string(); - province_id = province_id.substr(0, province_id.find(" ")); - - if (!game_manager.get_map().has_province_identifier(province_id)) { - Logger::error("Error loading history for province ", province_id, ": province not defined!"); - return false; - } - - return game_manager.get_history_manager().get_province_manager().load_province_history_file( - game_manager, province_id, parse_defines(lookup_file(file)).get_file_node() - ); - }); + static constexpr std::string_view province_history_directory = "history/provinces"; + ret &= apply_to_files( + lookup_basic_indentifier_prefixed_files_in_dir_recursive(province_history_directory, ".txt"), + [this, &game_manager, unused_history_file_warnings](fs::path const& file) -> bool { + const std::string filename = file.stem().string(); + const std::string_view province_id = extract_basic_identifier_prefix(filename); + + Province const* province = game_manager.get_map().get_province_by_identifier(province_id); + if (province == nullptr) { + if (unused_history_file_warnings) { + Logger::warning("Found history file for non-existent province: ", province_id); + } + return true; } + + return game_manager.get_history_manager().get_province_manager().load_province_history_file( + game_manager, *province, parse_defines(file).get_file_node() + ); } - } - game_manager.get_history_manager().get_province_manager().lock_province_histories(); + ); + game_manager.get_history_manager().get_province_manager().lock_province_histories(game_manager.get_map(), false); return ret; } bool Dataloader::_load_map_dir(GameManager& game_manager) const { - static const fs::path map_directory = "map"; + static constexpr std::string_view map_directory = "map/"; Map& map = game_manager.get_map(); - static const fs::path defaults_filename = "default.map"; - static const std::string default_definitions = "definition.csv"; - static const std::string default_provinces = "provinces.bmp"; - static const std::string default_positions = "positions.txt"; - static const std::string default_terrain = "terrain.bmp"; - static const std::string default_rivers = "rivers.bmp"; - static const std::string default_terrain_definition = "terrain.txt"; - static const std::string default_tree_definition = "trees.txt"; - static const std::string default_continent = "continent.txt"; - static const std::string default_adjacencies = "adjacencies.csv"; - static const std::string default_region = "region.txt"; - static const std::string default_region_sea = "region_sea.txt"; - static const std::string default_province_flag_sprite = "province_flag_sprites"; - - const v2script::Parser parser = parse_defines(lookup_file(map_directory / defaults_filename)); + static constexpr std::string_view defaults_filename = "default.map"; + static constexpr std::string_view default_definitions = "definition.csv"; + static constexpr std::string_view default_provinces = "provinces.bmp"; + static constexpr std::string_view default_positions = "positions.txt"; + static constexpr std::string_view default_terrain = "terrain.bmp"; + static constexpr std::string_view default_rivers = "rivers.bmp"; + static constexpr std::string_view default_terrain_definition = "terrain.txt"; + static constexpr std::string_view default_tree_definition = "trees.txt"; + static constexpr std::string_view default_continent = "continent.txt"; + static constexpr std::string_view default_adjacencies = "adjacencies.csv"; + static constexpr std::string_view default_region = "region.txt"; + static constexpr std::string_view default_region_sea = "region_sea.txt"; + static constexpr std::string_view default_province_flag_sprite = "province_flag_sprites"; + + const v2script::Parser parser = parse_defines(lookup_file(append_string_views(map_directory, defaults_filename))); std::vector<std::string_view> water_province_identifiers; @@ -677,20 +714,20 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const { Logger::error("Failed to load map default file!"); } - if (!map.load_province_definitions(parse_csv(lookup_file(map_directory / definitions)).get_lines())) { + if (!map.load_province_definitions(parse_csv(lookup_file(append_string_views(map_directory, definitions))).get_lines())) { Logger::error("Failed to load province definitions file!"); ret = false; } if (!map.load_province_positions( game_manager.get_economy_manager().get_building_manager(), - parse_defines(lookup_file(map_directory / positions)).get_file_node() + parse_defines(lookup_file(append_string_views(map_directory, positions))).get_file_node() )) { Logger::error("Failed to load province positions file!"); ret = false; } - if (!map.load_region_file(parse_defines(lookup_file(map_directory / region)).get_file_node())) { + if (!map.load_region_file(parse_defines(lookup_file(append_string_views(map_directory, region))).get_file_node())) { Logger::error("Failed to load region file!"); ret = false; } @@ -701,18 +738,24 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const { } if (!map.get_terrain_type_manager().load_terrain_types( - game_manager.get_modifier_manager(), parse_defines(lookup_file(map_directory / terrain_definition)).get_file_node() + game_manager.get_modifier_manager(), + parse_defines(lookup_file(append_string_views(map_directory, terrain_definition))).get_file_node() )) { Logger::error("Failed to load terrain types!"); ret = false; } - if (!map.load_map_images(lookup_file(map_directory / provinces), lookup_file(map_directory / terrain), false)) { + if (!map.load_map_images( + lookup_file(append_string_views(map_directory, provinces)), + lookup_file(append_string_views(map_directory, terrain)), false + )) { Logger::error("Failed to load map images!"); ret = false; } - if (!map.generate_and_load_province_adjacencies(parse_csv(lookup_file(map_directory / adjacencies)).get_lines())) { + if (!map.generate_and_load_province_adjacencies( + parse_csv(lookup_file(append_string_views(map_directory, adjacencies))).get_lines() + )) { Logger::error("Failed to generate and load province adjacencies!"); ret = false; } @@ -721,19 +764,20 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const { } bool Dataloader::load_defines(GameManager& game_manager) const { - static const fs::path defines_file = "common/defines.lua"; - static const fs::path buildings_file = "common/buildings.txt"; - static const fs::path bookmark_file = "common/bookmarks.txt"; - static const fs::path culture_file = "common/cultures.txt"; - static const fs::path goods_file = "common/goods.txt"; - static const fs::path governments_file = "common/governments.txt"; - static const fs::path graphical_culture_type_file = "common/graphicalculturetype.txt"; - static const fs::path ideology_file = "common/ideologies.txt"; - static const fs::path issues_file = "common/issues.txt"; - static const fs::path national_values_file = "common/nationalvalues.txt"; - static const fs::path production_types_file = "common/production_types.txt"; - static const fs::path religion_file = "common/religion.txt"; - static const fs::path leader_traits_file = "common/traits.txt"; + static const std::string defines_file = "common/defines.lua"; + static const std::string buildings_file = "common/buildings.txt"; + static const std::string bookmark_file = "common/bookmarks.txt"; + static const std::string countries_file = "common/countries.txt"; + static const std::string culture_file = "common/cultures.txt"; + static const std::string goods_file = "common/goods.txt"; + static const std::string governments_file = "common/governments.txt"; + static const std::string graphical_culture_type_file = "common/graphicalculturetype.txt"; + static const std::string ideology_file = "common/ideologies.txt"; + static const std::string issues_file = "common/issues.txt"; + static const std::string national_values_file = "common/nationalvalues.txt"; + static const std::string production_types_file = "common/production_types.txt"; + static const std::string religion_file = "common/religion.txt"; + static const std::string leader_traits_file = "common/traits.txt"; bool ret = true; @@ -832,15 +876,13 @@ bool Dataloader::load_defines(GameManager& game_manager) const { Logger::error("Failed to load bookmarks!"); ret = false; } - if (!_load_oobs(game_manager)) { - Logger::error("Failed to load orders of battle!"); - ret = false; - } - if (!_load_countries(game_manager)) { + if (!game_manager.get_country_manager().load_countries( + game_manager, *this, parse_defines(lookup_file(countries_file)).get_file_node() + )) { Logger::error("Failed to load countries!"); ret = false; } - if (!_load_history(game_manager)) { + if (!_load_history(game_manager, false)) { Logger::error("Failed to load history!"); ret = false; } @@ -848,13 +890,17 @@ bool Dataloader::load_defines(GameManager& game_manager) const { return ret; } -bool Dataloader::load_pop_history(GameManager& game_manager, fs::path const& path) const { - return apply_to_files_in_dir(path, ".txt", [&game_manager](fs::path const& file) -> bool { - return game_manager.get_map() - .expect_province_dictionary([&game_manager](Province& province, ast::NodeCPtr value) -> bool { - return province.load_pop_list(game_manager.get_pop_manager(), value); - })(parse_defines(file).get_file_node()); - }); +bool Dataloader::load_pop_history(GameManager& game_manager, std::string_view path) const { + return apply_to_files( + lookup_files_in_dir(path, ".txt"), + [&game_manager](fs::path const& file) -> bool { + return game_manager.get_map().expect_province_dictionary( + [&game_manager](Province& province, ast::NodeCPtr value) -> bool { + return province.load_pop_list(game_manager.get_pop_manager(), value); + } + )(parse_defines(file).get_file_node()); + } + ); } static bool _load_localisation_file(Dataloader::localisation_callback_t callback, std::vector<csv::LineObject> const& lines) { @@ -874,8 +920,11 @@ static bool _load_localisation_file(Dataloader::localisation_callback_t callback return ret; } -bool Dataloader::load_localisation_files(localisation_callback_t callback, fs::path const& localisation_dir) const { - return apply_to_files_in_dir(localisation_dir, ".csv", [callback](fs::path path) -> bool { - return _load_localisation_file(callback, parse_csv(path).get_lines()); - }); +bool Dataloader::load_localisation_files(localisation_callback_t callback, std::string_view localisation_dir) const { + return apply_to_files( + lookup_files_in_dir(localisation_dir, ".csv"), + [callback](fs::path path) -> bool { + return _load_localisation_file(callback, parse_csv(path).get_lines()); + } + ); } diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp index 0bf3086..2123469 100644 --- a/src/openvic-simulation/dataloader/Dataloader.hpp +++ b/src/openvic-simulation/dataloader/Dataloader.hpp @@ -23,14 +23,16 @@ namespace OpenVic { private: path_vector_t roots; - bool _load_pop_types( - PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager - ) const; + bool _load_pop_types(PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager) const; bool _load_units(UnitManager& unit_manager, GoodManager const& good_manager) const; bool _load_map_dir(GameManager& game_manager) const; - bool _load_oobs(GameManager& game_manager) const; - bool _load_countries(GameManager& game_manager) const; - bool _load_history(GameManager& game_manager) const; + bool _load_history(GameManager& game_manager, bool unused_history_file_warnings) const; + + /* _DirIterator is fs::directory_iterator or fs::recursive_directory_iterator. + * _Equiv is an equivalence relation with respect to which every found file shall be unique. + * If a file is equivalent to the empty path then it is not included. */ + template<typename _DirIterator, std::predicate<fs::path const&, fs::path const&> _Equiv> + path_vector_t _lookup_files_in_dir(std::string_view path, fs::path const& extension) const; public: static ovdl::v2script::Parser parse_defines(fs::path const& path); @@ -67,39 +69,31 @@ namespace OpenVic { /* REQUIREMENTS: * DAT-24 */ - fs::path lookup_file(fs::path const& path) const; - path_vector_t lookup_files_in_dir(fs::path const& path, fs::path const& extension) const; - bool apply_to_files_in_dir( - fs::path const& path, fs::path const& extension, NodeTools::callback_t<fs::path const&> callback + fs::path lookup_file(std::string_view path, bool print_error = true) const; + path_vector_t lookup_files_in_dir(std::string_view path, fs::path const& extension) const; + path_vector_t lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const; + path_vector_t lookup_basic_indentifier_prefixed_files_in_dir(std::string_view path, fs::path const& extension) const; + path_vector_t lookup_basic_indentifier_prefixed_files_in_dir_recursive( + std::string_view path, fs::path const& extension ) const; + bool apply_to_files(path_vector_t const& files, NodeTools::callback_t<fs::path const&> callback) const; bool load_defines(GameManager& game_manager) const; - bool load_pop_history(GameManager& game_manager, fs::path const& path) const; + bool load_pop_history(GameManager& game_manager, std::string_view path) const; enum locale_t : size_t { - English, - French, - German, - Polish, - Spanish, - Italian, - Swedish, - Czech, - Hungarian, - Dutch, - Portugese, - Russian, - Finnish, - _LocaleCount + English, French, German, Polish, Spanish, Italian, Swedish, + Czech, Hungarian, Dutch, Portugese, Russian, Finnish, _LocaleCount + }; + static constexpr char const* locale_names[_LocaleCount] = { + "en_GB", "fr_FR", "de_DE", "pl_PL", "es_ES", "it_IT", "sv_SE", + "cs_CZ", "hu_HU", "nl_NL", "pt_PT", "ru_RU", "fi_FI" }; - static constexpr char const* locale_names[_LocaleCount] = { "en_GB", "fr_FR", "de_DE", "pl_PL", "es_ES", - "it_IT", "sv_SE", "cs_CZ", "hu_HU", "nl_NL", - "pt_PT", "ru_RU", "fi_FI" }; /* Args: key, locale, localisation */ using localisation_callback_t = NodeTools::callback_t<std::string_view, locale_t, std::string_view>; bool load_localisation_files( - localisation_callback_t callback, fs::path const& localisation_dir = "localisation" + localisation_callback_t callback, std::string_view localisation_dir = "localisation" ) const; private: @@ -111,6 +105,6 @@ namespace OpenVic { using hint_path_t = fs::path; using game_path_t = fs::path; - inline static std::unordered_map<hint_path_t, game_path_t, fshash> _cached_paths; + static inline std::unordered_map<hint_path_t, game_path_t, fshash> _cached_paths; }; } diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp index 2f93bc2..27d0f95 100644 --- a/src/openvic-simulation/dataloader/NodeTools.cpp +++ b/src/openvic-simulation/dataloader/NodeTools.cpp @@ -289,7 +289,7 @@ node_callback_t NodeTools::expect_dictionary(key_value_callback_t callback) { bool NodeTools::add_key_map_entry( key_map_t& key_map, std::string_view key, dictionary_entry_t::expected_count_t expected_count, node_callback_t callback ) { - if (key_map.find(key) == key_map.end()) { + if (!key_map.contains(key)) { key_map.emplace(key, dictionary_entry_t { expected_count, callback }); return true; } @@ -318,7 +318,12 @@ key_value_callback_t NodeTools::dictionary_keys_callback(key_map_t& key_map, key Logger::error("Invalid repeat of dictionary key: ", key); return false; } - return entry.callback(value); + if (entry.callback(value)) { + return true; + } else { + Logger::error("Callback failed for dictionary key: ", key); + return false; + } }; } diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index 798976f..a23fb4f 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -4,6 +4,7 @@ #include <cstdint> #include <functional> #include <map> +#include <optional> #include <set> #include <type_traits> @@ -66,7 +67,7 @@ namespace OpenVic { } node_callback_t expect_identifier(callback_t<std::string_view> callback); - node_callback_t expect_string(callback_t<std::string_view> callback, bool allow_empty = true); + node_callback_t expect_string(callback_t<std::string_view> callback, bool allow_empty = false); node_callback_t expect_identifier_or_string(callback_t<std::string_view> callback, bool allow_empty = false); node_callback_t expect_bool(callback_t<bool> callback); @@ -261,15 +262,8 @@ namespace OpenVic { }; } - template<std::integral T> - callback_t<T> assign_variable_callback_cast(auto& var) { - return [&var](T val) -> bool { - var = val; - return true; - }; - } - - template<std::signed_integral T> + template<typename T> + requires std::is_integral_v<T> || std::is_enum_v<T> callback_t<T> assign_variable_callback_cast(auto& var) { return [&var](T val) -> bool { var = val; @@ -317,5 +311,13 @@ namespace OpenVic { return true; }; } + + template<typename T> + Callback<T const&> auto assign_variable_callback_pointer(std::optional<T const*>& var) { + return [&var](T const& val) -> bool { + var = &val; + return true; + }; + } } } diff --git a/src/openvic-simulation/economy/Building.cpp b/src/openvic-simulation/economy/Building.cpp index a79741b..38a8863 100644 --- a/src/openvic-simulation/economy/Building.cpp +++ b/src/openvic-simulation/economy/Building.cpp @@ -129,11 +129,11 @@ ExpansionState BuildingInstance::get_expansion_state() const { return expansion_state; } -Date const& BuildingInstance::get_start_date() const { +Date BuildingInstance::get_start_date() const { return start; } -Date const& BuildingInstance::get_end_date() const { +Date BuildingInstance::get_end_date() const { return end; } @@ -153,7 +153,7 @@ bool BuildingInstance::expand() { /* REQUIREMENTS: * MAP-71, MAP-74, MAP-77 */ -void BuildingInstance::update_state(Date const& today) { +void BuildingInstance::update_state(Date today) { switch (expansion_state) { case ExpansionState::Preparing: start = today; @@ -166,7 +166,7 @@ void BuildingInstance::update_state(Date const& today) { } } -void BuildingInstance::tick(Date const& today) { +void BuildingInstance::tick(Date today) { if (expansion_state == ExpansionState::Preparing) { expansion_state = ExpansionState::Expanding; } @@ -207,8 +207,8 @@ bool BuildingManager::add_building(std::string_view identifier, BuildingType con } bool BuildingManager::load_buildings_file( - GoodManager const& good_manager, ProductionTypeManager const& production_type_manager, - ModifierManager const& modifier_manager, ast::NodeCPtr root + GoodManager const& good_manager, ProductionTypeManager const& production_type_manager, ModifierManager& modifier_manager, + ast::NodeCPtr root ) { bool ret = expect_dictionary_reserve_length(buildings, [this](std::string_view, ast::NodeCPtr value) -> bool { return expect_key("type", expect_identifier( @@ -281,6 +281,17 @@ bool BuildingManager::load_buildings_file( )(root); lock_buildings(); + for (Building const& building : buildings.get_items()) { + std::string max_modifier_prefix = "max_"; + std::string min_modifier_prefix = "min_build_"; + modifier_manager.add_modifier_effect( + max_modifier_prefix.append(building.get_identifier()), true, ModifierEffect::format_t::INT + ); + modifier_manager.add_modifier_effect( + min_modifier_prefix.append(building.get_identifier()), false, ModifierEffect::format_t::INT + ); + } + return ret; } diff --git a/src/openvic-simulation/economy/Building.hpp b/src/openvic-simulation/economy/Building.hpp index 89b6db8..e907e49 100644 --- a/src/openvic-simulation/economy/Building.hpp +++ b/src/openvic-simulation/economy/Building.hpp @@ -133,13 +133,13 @@ namespace OpenVic { level_t get_current_level() const; ExpansionState get_expansion_state() const; - Date const& get_start_date() const; - Date const& get_end_date() const; + Date get_start_date() const; + Date get_end_date() const; float get_expansion_progress() const; bool expand(); - void update_state(Date const& today); - void tick(Date const& today); + void update_state(Date today); + void tick(Date today); }; struct Province; @@ -162,7 +162,7 @@ namespace OpenVic { bool load_buildings_file( GoodManager const& good_manager, ProductionTypeManager const& production_type_manager, - ModifierManager const& modifier_manager, ast::NodeCPtr root + ModifierManager& modifier_manager, ast::NodeCPtr root ); bool generate_province_buildings(Province& province) const; diff --git a/src/openvic-simulation/economy/EconomyManager.hpp b/src/openvic-simulation/economy/EconomyManager.hpp index b7d45c9..7445614 100644 --- a/src/openvic-simulation/economy/EconomyManager.hpp +++ b/src/openvic-simulation/economy/EconomyManager.hpp @@ -20,7 +20,7 @@ namespace OpenVic { return production_type_manager.load_production_types_file(good_manager, pop_manager, root); } - inline bool load_buildings_file(ModifierManager const& modifier_manager, ast::NodeCPtr root) { + inline bool load_buildings_file(ModifierManager& modifier_manager, ast::NodeCPtr root) { return building_manager.load_buildings_file(good_manager, production_type_manager, modifier_manager, root); } }; diff --git a/src/openvic-simulation/economy/Good.cpp b/src/openvic-simulation/economy/Good.cpp index 11230d2..2b1d694 100644 --- a/src/openvic-simulation/economy/Good.cpp +++ b/src/openvic-simulation/economy/Good.cpp @@ -10,7 +10,7 @@ GoodCategory::GoodCategory(std::string_view new_identifier) : HasIdentifier { ne Good::Good( std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price, bool new_available_from_start, bool new_tradeable, bool new_money, bool new_overseas_penalty -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, category { new_category }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, category { new_category }, base_price { new_base_price }, available_from_start { new_available_from_start }, tradeable { new_tradeable }, money { new_money }, overseas_penalty { new_overseas_penalty } { assert(base_price > NULL_PRICE); diff --git a/src/openvic-simulation/history/Bookmark.cpp b/src/openvic-simulation/history/Bookmark.cpp index edfa064..92d8de5 100644 --- a/src/openvic-simulation/history/Bookmark.cpp +++ b/src/openvic-simulation/history/Bookmark.cpp @@ -25,7 +25,7 @@ std::string_view Bookmark::get_description() const { return description; } -Date const& Bookmark::get_date() const { +Date Bookmark::get_date() const { return date; } diff --git a/src/openvic-simulation/history/Bookmark.hpp b/src/openvic-simulation/history/Bookmark.hpp index 8f0075f..d5253fe 100644 --- a/src/openvic-simulation/history/Bookmark.hpp +++ b/src/openvic-simulation/history/Bookmark.hpp @@ -29,7 +29,7 @@ namespace OpenVic { std::string_view get_name() const; std::string_view get_description() const; - Date const& get_date() const; + Date get_date() const; uint32_t get_initial_camera_x() const; uint32_t get_initial_camera_y() const; }; @@ -42,9 +42,10 @@ namespace OpenVic { BookmarkManager(); bool add_bookmark( - std::string_view name, std::string_view description, Date date, uint32_t initial_camera_x, uint32_t initial_camera_y + std::string_view name, std::string_view description, Date date, uint32_t initial_camera_x, + uint32_t initial_camera_y ); - IDENTIFIER_REGISTRY_ACCESSORS(bookmark); + IDENTIFIER_REGISTRY_ACCESSORS(bookmark) bool load_bookmark_file(ast::NodeCPtr root); }; diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp index 2dfc171..04a5e09 100644 --- a/src/openvic-simulation/history/CountryHistory.cpp +++ b/src/openvic-simulation/history/CountryHistory.cpp @@ -5,154 +5,103 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -CountryHistory::CountryHistory( - Culture const* new_primary_culture, std::vector<Culture const*>&& new_accepted_cultures, Religion const* new_religion, - CountryParty const* new_ruling_party, Date new_last_election, std::map<Ideology const*, fixed_point_t>&& new_upper_house, - Province const* new_capital, GovernmentType const* new_government_type, fixed_point_t new_plurality, - NationalValue const* new_national_value, bool new_civilised, fixed_point_t new_prestige, - std::vector<Reform const*>&& new_reforms, Deployment const* new_inital_oob -) : primary_culture { new_primary_culture }, accepted_cultures { std::move(new_accepted_cultures) }, - religion { new_religion }, ruling_party { new_ruling_party }, last_election { new_last_election }, - upper_house { std::move(new_upper_house) }, capital { new_capital }, government_type { new_government_type }, - plurality { new_plurality }, national_value { new_national_value }, civilised { new_civilised }, - prestige { new_prestige }, reforms { std::move(new_reforms) }, inital_oob { new_inital_oob } {} +CountryHistoryEntry::CountryHistoryEntry(Country const& new_country, Date new_date) + : HistoryEntry { new_date }, country { new_country } {} -Culture const* CountryHistory::get_primary_culture() const { - return primary_culture; -} - -const std::vector<Culture const*>& CountryHistory::get_accepted_cultures() const { - return accepted_cultures; -} - -Religion const* CountryHistory::get_religion() const { - return religion; -} - -CountryParty const* CountryHistory::get_ruling_party() const { - return ruling_party; -} - -const Date CountryHistory::get_last_election() const { - return last_election; -} - -const std::map<Ideology const*, fixed_point_t>& CountryHistory::get_upper_house() const { - return upper_house; -} - -Province const* CountryHistory::get_capital() const { - return capital; -} - -GovernmentType const* CountryHistory::get_government_type() const { - return government_type; -} - -const fixed_point_t CountryHistory::get_plurality() const { - return plurality; -} - -NationalValue const* CountryHistory::get_national_value() const { - return national_value; -} - -const bool CountryHistory::is_civilised() const { - return civilised; -} - -const fixed_point_t CountryHistory::get_prestige() const { - return prestige; -} - -const std::vector<Reform const*>& CountryHistory::get_reforms() const { - return reforms; -} +CountryHistoryMap::CountryHistoryMap(Country const& new_country) : country { new_country } {} -Deployment const* CountryHistory::get_inital_oob() const { - return inital_oob; +std::unique_ptr<CountryHistoryEntry> CountryHistoryMap::_make_entry(Date date) const { + return std::unique_ptr<CountryHistoryEntry> { new CountryHistoryEntry { country, date } }; } -bool CountryHistoryManager::add_country_history_entry( - Country const* country, Date date, Culture const* primary_culture, std::vector<Culture const*>&& accepted_cultures, - Religion const* religion, CountryParty const* ruling_party, Date last_election, - std::map<Ideology const*, fixed_point_t>&& upper_house, Province const* capital, GovernmentType const* government_type, - fixed_point_t plurality, NationalValue const* national_value, bool civilised, fixed_point_t prestige, - std::vector<Reform const*>&& reforms, Deployment const* initial_oob, bool updated_accepted_cultures, - bool updated_upper_house, bool updated_reforms +bool CountryHistoryMap::_load_history_entry( + GameManager const& game_manager, Dataloader const& dataloader, DeploymentManager& deployment_manager, + CountryHistoryEntry& entry, ast::NodeCPtr root ) { - if (locked) { - Logger::error("Cannot add new history entry to country history registry: locked!"); - return false; - } - - /* combine duplicate histories, priority to current (defined later) */ - auto& country_registry = country_histories[country]; - const auto existing_entry = country_registry.find(date); - - if (existing_entry != country_registry.end()) { - if (primary_culture != nullptr) { - existing_entry->second.primary_culture = primary_culture; - } - if (updated_accepted_cultures) { - existing_entry->second.accepted_cultures = std::move(accepted_cultures); - } - if (religion != nullptr) { - existing_entry->second.religion = religion; - } - if (ruling_party != nullptr) { - existing_entry->second.ruling_party = ruling_party; - } - if (last_election != Date(0)) { - existing_entry->second.last_election = last_election; - } - if (updated_upper_house) { - existing_entry->second.upper_house = std::move(upper_house); - } - if (capital != nullptr) { - existing_entry->second.capital = capital; - } - if (government_type != nullptr) { - existing_entry->second.government_type = government_type; - } - if (plurality >= 0) { - existing_entry->second.plurality = plurality; - } - if (national_value != nullptr) { - existing_entry->second.national_value = national_value; - } - if (civilised) { - existing_entry->second.civilised = true; - } - if (prestige >= 0) { - existing_entry->second.prestige = prestige; - } - if (updated_reforms) { - existing_entry->second.reforms = std::move(reforms); - } - if (initial_oob != nullptr) { - existing_entry->second.inital_oob = initial_oob; - } - } else { - country_registry.emplace( date, - CountryHistory { - primary_culture, std::move(accepted_cultures), religion, ruling_party, last_election, - std::move(upper_house), capital, government_type, plurality, national_value, civilised, - prestige, std::move(reforms), initial_oob + PoliticsManager const& politics_manager = game_manager.get_politics_manager(); + IssueManager const& issue_manager = politics_manager.get_issue_manager(); + CultureManager const& culture_manager = game_manager.get_pop_manager().get_culture_manager(); + + return expect_dictionary_keys_and_default( + [this, &game_manager, &dataloader, &deployment_manager, &issue_manager, &entry]( + std::string_view key, ast::NodeCPtr value) -> bool { + ReformGroup const* reform_group = issue_manager.get_reform_group_by_identifier(key); + if (reform_group != nullptr) { + return issue_manager.expect_reform_identifier([&entry, reform_group](Reform const& reform) -> bool { + if (&reform.get_reform_group() != reform_group) { + Logger::warning( + "Listing ", reform.get_identifier(), " as belonging to the reform group ", + reform_group->get_identifier(), " when it actually belongs to ", + reform.get_reform_group().get_identifier(), " in history of ", entry.get_country().get_identifier() + ); + } + if (std::find(entry.reforms.begin(), entry.reforms.end(), &reform) != entry.reforms.end()) { + Logger::error( + "Redefinition of reform ", reform.get_identifier(), " in history of ", + entry.get_country().get_identifier() + ); + return false; + } + entry.reforms.push_back(&reform); + return true; + })(value); } - ); - } - return true; + // TODO: technologies & inventions + return _load_history_sub_entry_callback( + game_manager, dataloader, deployment_manager, entry.get_date(), value, key, value, key_value_success_callback + ); + }, + /* we have to use a lambda, assign_variable_callback_pointer + * apparently doesn't play nice with const & non-const accessors */ + // TODO - fix this issue (cause by provinces having non-const accessors) + "capital", ZERO_OR_ONE, + game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(entry.capital)), + "primary_culture", ZERO_OR_ONE, + culture_manager.expect_culture_identifier(assign_variable_callback_pointer(entry.primary_culture)), + "culture", ZERO_OR_MORE, culture_manager.expect_culture_identifier( + [&entry](Culture const& culture) -> bool { + entry.accepted_cultures.push_back(&culture); + return true; + } + ), + "religion", ZERO_OR_ONE, game_manager.get_pop_manager().get_religion_manager().expect_religion_identifier( + assign_variable_callback_pointer(entry.religion) + ), + "government", ZERO_OR_ONE, politics_manager.get_government_type_manager().expect_government_type_identifier( + assign_variable_callback_pointer(entry.government_type) + ), + "plurality", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.plurality)), + "nationalvalue", ZERO_OR_ONE, politics_manager.get_national_value_manager().expect_national_value_identifier( + assign_variable_callback_pointer(entry.national_value) + ), + "civilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(entry.civilised)), + "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.prestige)), + "ruling_party", ZERO_OR_ONE, country.expect_party_identifier(assign_variable_callback_pointer(entry.ruling_party)), + "last_election", ZERO_OR_ONE, expect_date(assign_variable_callback(entry.last_election)), + "upper_house", ZERO_OR_ONE, politics_manager.get_ideology_manager().expect_ideology_dictionary( + [&entry](Ideology const& ideology, ast::NodeCPtr value) -> bool { + return expect_fixed_point([&entry, &ideology](fixed_point_t val) -> bool { + entry.upper_house[&ideology] = val; + return true; + })(value); + } + ), + "oob", ZERO_OR_ONE, expect_identifier_or_string( + [&game_manager, &deployment_manager, &dataloader, &entry](std::string_view path) -> bool { + Deployment const* deployment = nullptr; + const bool ret = deployment_manager.load_oob_file(game_manager, dataloader, path, deployment, false); + if (deployment != nullptr) { + entry.inital_oob = deployment; + } + return ret; + } + ), + "schools", ZERO_OR_ONE, success_callback, // TODO: technology school + "foreign_investment", ZERO_OR_ONE, success_callback // TODO: foreign investment + )(root); } void CountryHistoryManager::lock_country_histories() { - for (const auto& entry : country_histories) { - if (entry.second.size() == 0) { - Logger::error( - "Attempted to lock country histories - country ", entry.first->get_identifier(), " has no history entries!" - ); - } - } Logger::info("Locked country history registry after registering ", country_histories.size(), " items"); locked = true; } @@ -161,214 +110,49 @@ bool CountryHistoryManager::is_locked() const { return locked; } -CountryHistory const* CountryHistoryManager::get_country_history(Country const* country, Date entry) const { - Date closest_entry; - auto country_registry = country_histories.find(country); - - if (country_registry == country_histories.end()) { - Logger::error("Attempted to access history of undefined country ", country->get_identifier()); +CountryHistoryMap const* CountryHistoryManager::get_country_history(Country const* country) const { + if (country == nullptr) { + Logger::error("Attempted to access history of null country"); return nullptr; } - - for (const auto& current : country_registry->second) { - if (current.first == entry) { - return ¤t.second; - } - if (current.first > entry) { - continue; - } - if (current.first > closest_entry && current.first < entry) { - closest_entry = current.first; - } - } - - auto entry_registry = country_registry->second.find(closest_entry); - if (entry_registry != country_registry->second.end()) { - return &entry_registry->second; + decltype(country_histories)::const_iterator country_registry = country_histories.find(country); + if (country_registry != country_histories.end()) { + return &country_registry->second; + } else { + Logger::error("Attempted to access history of country ", country->get_identifier(), " but none has been defined!"); + return nullptr; } - /* warned about lack of entries earlier, return nullptr */ - return nullptr; } -inline CountryHistory const* CountryHistoryManager::get_country_history(Country const* country, Bookmark const* entry) const { - return get_country_history(country, entry->get_date()); -} - -inline bool CountryHistoryManager::_load_country_history_entry( - GameManager& game_manager, std::string_view name, Date const& date, ast::NodeCPtr root +bool CountryHistoryManager::load_country_history_file( + GameManager& game_manager, Dataloader const& dataloader, Country const& country, ast::NodeCPtr root ) { - Province const* capital = nullptr; - Culture const* primary_culture = nullptr; - Religion const* religion = nullptr; - GovernmentType const* government_type = nullptr; - NationalValue const* national_value = nullptr; - CountryParty const* ruling_party = nullptr; - std::vector<Culture const*> accepted_cultures; - std::vector<Reform const*> reforms; - std::map<Ideology const*, fixed_point_t> upper_house; - fixed_point_t plurality = -1, prestige = -1; - bool civilised = false; - Date last_election = Date(0); - Deployment const* initial_oob = nullptr; - - bool updated_accepted_cultures = false, updated_upper_house = false, updated_reforms = false; - - bool ret = expect_dictionary_keys_and_default( - [this, &game_manager, &reforms, &updated_reforms, &name](std::string_view key, ast::NodeCPtr value) -> bool { - if (game_manager.get_politics_manager().get_issue_manager().has_reform_group_identifier(key)) { - updated_reforms = true; - - Reform const* reform; - - bool ret = game_manager.get_politics_manager().get_issue_manager() - .expect_reform_identifier(assign_variable_callback_pointer(reform))(value); - if (std::find(reforms.begin(), reforms.end(), reform) != reforms.end()) { - Logger::error("Redefinition of reform ", reform->get_identifier(), " in history of ", name); - return false; - } - - reforms.push_back(reform); - return ret; - } - // TODO: technologies & inventions - return true; - }, - /* we have to use a lambda, assign_variable_callback_pointer - * apparently doesn't play nice with const & non-const accessors */ - "capital", ZERO_OR_ONE, - game_manager.get_map().expect_province_identifier([&capital](Province const& province) -> bool { - capital = &province; - return true; - }), - "primary_culture", ZERO_OR_ONE, - game_manager.get_pop_manager().get_culture_manager().expect_culture_identifier( - assign_variable_callback_pointer(primary_culture) - ), - "culture", ZERO_OR_MORE, - game_manager.get_pop_manager().get_culture_manager().expect_culture_identifier( - [&game_manager, &accepted_cultures, &updated_accepted_cultures](Culture const& culture) -> bool { - updated_accepted_cultures = true; - accepted_cultures.push_back(&culture); - return true; - } - ), - "religion", ZERO_OR_ONE, - game_manager.get_pop_manager().get_religion_manager().expect_religion_identifier( - assign_variable_callback_pointer(religion) - ), - "government", ZERO_OR_ONE, - game_manager.get_politics_manager().get_government_type_manager().expect_government_type_identifier( - assign_variable_callback_pointer(government_type) - ), - "plurality", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(plurality)), - "nationalvalue", ZERO_OR_ONE, - game_manager.get_politics_manager().get_national_value_manager().expect_national_value_identifier( - assign_variable_callback_pointer(national_value) - ), - "civilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(civilised)), - "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(prestige)), - "ruling_party", ZERO_OR_ONE, - expect_identifier([this, &game_manager, &ruling_party, &name, &date](std::string_view identifier) -> bool { - const std::vector<CountryParty>* parties = - &game_manager.get_country_manager().get_country_by_identifier(name)->get_parties(); - for (auto& party : *parties) { - if (party.get_name() == identifier) { - if (party.get_start_date() <= date && date <= party.get_end_date()) { - ruling_party = &party; - return true; - } else { - if (party.get_start_date() > date) { - Logger::warning( - "Ruling party ", identifier, " of country ", name, " has invalid start date ", - party.get_start_date(), " for bookmark: ", date.to_string() - ); - } - if (party.get_end_date() < date) { - Logger::warning( - "Ruling party ", identifier, " of country ", name, " has invalid end date ", - party.get_end_date(), " for bookmark: ", date.to_string() - ); - } - ruling_party = &party; - return true; - } - } - } - Logger::error("Ruling party ", identifier, " of country ", name, " is not defined!"); - return false; - }), - "last_election", ZERO_OR_ONE, expect_date(assign_variable_callback(last_election)), - "upper_house", ZERO_OR_ONE, - game_manager.get_politics_manager().get_ideology_manager().expect_ideology_dictionary( - [&upper_house, &updated_upper_house](Ideology const& ideology, ast::NodeCPtr value) -> bool { - fixed_point_t popularity; - - updated_upper_house = true; - bool ret = expect_fixed_point(assign_variable_callback(popularity))(value); - upper_house.emplace(&ideology, popularity); - return ret; - } - ), - "oob", ZERO_OR_ONE, - [&game_manager, &initial_oob](ast::NodeCPtr node) -> bool { - std::string_view string; - expect_string(assign_variable_callback(string))(node); - - if (string.starts_with('/')) { - if (game_manager.get_military_manager().get_deployment_manager() - .has_deployment_identifier(string.substr(1))) { - initial_oob = game_manager.get_military_manager().get_deployment_manager() - .get_deployment_by_identifier(string.substr(1)); - return true; - } - } else { - if (game_manager.get_military_manager().get_deployment_manager().has_deployment_identifier(string)) { - initial_oob = - game_manager.get_military_manager().get_deployment_manager().get_deployment_by_identifier(string); - } - } - - initial_oob = - game_manager.get_military_manager().get_deployment_manager().get_deployment_by_identifier("NULL"); - return true; - }, - "schools", ZERO_OR_ONE, success_callback, // TODO: technology school - "foreign_investment", ZERO_OR_ONE, success_callback // TODO: foreign investment - )(root); - - ret &= add_country_history_entry( - game_manager.get_country_manager().get_country_by_identifier(name), date, primary_culture, std::move(accepted_cultures), - religion, ruling_party, last_election, std::move(upper_house), capital, government_type, plurality, national_value, - civilised, prestige, std::move(reforms), initial_oob, updated_accepted_cultures, updated_upper_house, updated_reforms - ); - return ret; -} + if (locked) { + Logger::error( + "Attempted to load country history file for ", country.get_identifier(), + " after country history registry was locked!" + ); + return false; + } -bool CountryHistoryManager::load_country_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root) { - if (game_manager.get_country_manager().get_country_by_identifier(name)->is_dynamic_tag()) { + if (country.is_dynamic_tag()) { return true; /* as far as I can tell dynamic countries are hardcoded, broken, and unused */ } - bool ret = _load_country_history_entry(game_manager, name, game_manager.get_define_manager().get_start_date(), root); - - ret &= expect_dictionary([this, &game_manager, &name](std::string_view key, ast::NodeCPtr value) -> bool { - bool is_date = false; - Date entry = Date().from_string(key, &is_date, true); - if (!is_date) { - return true; - } - - Date const& end_date = game_manager.get_define_manager().get_end_date(); - if (entry > end_date) { - Logger::error( - "History entry ", entry.to_string(), " of country ", name, " defined after defined end date ", - end_date.to_string() - ); + decltype(country_histories)::iterator it = country_histories.find(&country); + if (it == country_histories.end()) { + const std::pair<decltype(country_histories)::iterator, bool> result = + country_histories.emplace(&country, CountryHistoryMap { country }); + if (result.second) { + it = result.first; + } else { + Logger::error("Failed to create country history map for country ", country.get_identifier()); return false; } + } + CountryHistoryMap& country_history = it->second; - return _load_country_history_entry(game_manager, name, entry, value); - })(root); - - return ret; + return country_history._load_history_file( + game_manager, dataloader, game_manager.get_military_manager().get_deployment_manager(), root + ); } diff --git a/src/openvic-simulation/history/CountryHistory.hpp b/src/openvic-simulation/history/CountryHistory.hpp index 52d99df..ffb44c0 100644 --- a/src/openvic-simulation/history/CountryHistory.hpp +++ b/src/openvic-simulation/history/CountryHistory.hpp @@ -5,6 +5,7 @@ #include "openvic-simulation/country/Country.hpp" #include "openvic-simulation/history/Bookmark.hpp" +#include "openvic-simulation/history/HistoryMap.hpp" #include "openvic-simulation/map/Province.hpp" #include "openvic-simulation/military/Deployment.hpp" #include "openvic-simulation/politics/Government.hpp" @@ -17,86 +18,68 @@ #include "openvic-simulation/types/Date.hpp" namespace OpenVic { - struct CountryHistoryManager; + struct CountryHistoryMap; - struct CountryHistory { - friend struct CountryHistoryManager; + struct CountryHistoryEntry : HistoryEntry { + friend struct CountryHistoryMap; private: - Culture const* primary_culture; - std::vector<Culture const*> accepted_cultures; - Religion const* religion; - CountryParty const* ruling_party; - Date last_election; - std::map<Ideology const*, fixed_point_t> upper_house; - Province const* capital; - GovernmentType const* government_type; - fixed_point_t plurality; - NationalValue const* national_value; - bool civilised; - fixed_point_t prestige; - std::vector<Reform const*> reforms; - Deployment const* inital_oob; + Country const& PROPERTY(country); + + std::optional<Culture const*> PROPERTY(primary_culture); + std::vector<Culture const*> PROPERTY(accepted_cultures); + std::optional<Religion const*> PROPERTY(religion); + std::optional<CountryParty const*> PROPERTY(ruling_party); + std::optional<Date> PROPERTY(last_election); + decimal_map_t<Ideology const*> PROPERTY(upper_house); + std::optional<Province const*> PROPERTY(capital); + std::optional<GovernmentType const*> PROPERTY(government_type); + std::optional<fixed_point_t> PROPERTY(plurality); + std::optional<NationalValue const*> PROPERTY(national_value); + std::optional<bool> PROPERTY(civilised); + std::optional<fixed_point_t> PROPERTY(prestige); + std::vector<Reform const*> PROPERTY(reforms); + std::optional<Deployment const*> PROPERTY(inital_oob); // TODO: technologies, tech schools, and inventions when PR#51 merged // TODO: starting foreign investment - CountryHistory( - Culture const* new_primary_culture, std::vector<Culture const*>&& new_accepted_cultures, - Religion const* new_religion, CountryParty const* new_ruling_party, Date new_last_election, - std::map<Ideology const*, fixed_point_t>&& new_upper_house, Province const* new_capital, - GovernmentType const* new_government_type, fixed_point_t new_plurality, NationalValue const* new_national_value, - bool new_civilised, fixed_point_t new_prestige, std::vector<Reform const*>&& new_reforms, - Deployment const* new_inital_oob - ); + CountryHistoryEntry(Country const& new_country, Date new_date); + }; - public: - Culture const* get_primary_culture() const; - const std::vector<Culture const*>& get_accepted_cultures() const; - Religion const* get_religion() const; - CountryParty const* get_ruling_party() const; - const Date get_last_election() const; - const std::map<Ideology const*, fixed_point_t>& get_upper_house() const; - Province const* get_capital() const; - GovernmentType const* get_government_type() const; - const fixed_point_t get_plurality() const; - NationalValue const* get_national_value() const; - const bool is_civilised() const; - const fixed_point_t get_prestige() const; - const std::vector<Reform const*>& get_reforms() const; - Deployment const* get_inital_oob() const; + struct CountryHistoryManager; + + struct CountryHistoryMap : HistoryMap<CountryHistoryEntry, Dataloader const&, DeploymentManager&> { + friend struct CountryHistoryManager; + + private: + Country const& PROPERTY(country); + + protected: + CountryHistoryMap(Country const& new_country); + + std::unique_ptr<CountryHistoryEntry> _make_entry(Date date) const override; + bool _load_history_entry( + GameManager const& game_manager, Dataloader const& dataloader, DeploymentManager& deployment_manager, + CountryHistoryEntry& entry, ast::NodeCPtr root + ) override; }; struct CountryHistoryManager { private: - std::map<Country const*, std::map<Date, CountryHistory>> country_histories; + std::map<Country const*, CountryHistoryMap> country_histories; bool locked = false; - inline bool _load_country_history_entry( - GameManager& game_manager, std::string_view name, Date const& date, ast::NodeCPtr root - ); - public: - CountryHistoryManager() {} - - bool add_country_history_entry( - Country const* country, Date date, Culture const* primary_culture, std::vector<Culture const*>&& accepted_cultures, - Religion const* religion, CountryParty const* ruling_party, Date last_election, - std::map<Ideology const*, fixed_point_t>&& upper_house, Province const* capital, - GovernmentType const* government_type, fixed_point_t plurality, NationalValue const* national_value, bool civilised, - fixed_point_t prestige, std::vector<Reform const*>&& reforms, Deployment const* initial_oob, - bool updated_accepted_cultures, bool updated_upper_house, bool updated_reforms - ); + CountryHistoryManager() = default; void lock_country_histories(); bool is_locked() const; - /* Returns history of country at date, if date doesn't have an entry returns - * closest entry before date. Return can be nullptr if an error occurs. */ - CountryHistory const* get_country_history(Country const* country, Date entry) const; - /* Returns history of country at bookmark date. Return can be nullptr if an error occurs. */ - inline CountryHistory const* get_country_history(Country const* country, Bookmark const* entry) const; + CountryHistoryMap const* get_country_history(Country const* country) const; - bool load_country_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root); + bool load_country_history_file( + GameManager& game_manager, Dataloader const& dataloader, Country const& country, ast::NodeCPtr root + ); }; } // namespace OpenVic diff --git a/src/openvic-simulation/history/HistoryMap.cpp b/src/openvic-simulation/history/HistoryMap.cpp new file mode 100644 index 0000000..b669208 --- /dev/null +++ b/src/openvic-simulation/history/HistoryMap.cpp @@ -0,0 +1,15 @@ +#include "HistoryMap.hpp" + +#include "openvic-simulation/GameManager.hpp" + +using namespace OpenVic; + +HistoryEntry::HistoryEntry(Date new_date) : date { new_date } {} + +Date OpenVic::_get_start_date(GameManager const& game_manager) { + return game_manager.get_define_manager().get_start_date(); +} + +Date OpenVic::_get_end_date(GameManager const& game_manager) { + return game_manager.get_define_manager().get_end_date(); +} diff --git a/src/openvic-simulation/history/HistoryMap.hpp b/src/openvic-simulation/history/HistoryMap.hpp new file mode 100644 index 0000000..64d886d --- /dev/null +++ b/src/openvic-simulation/history/HistoryMap.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include <map> +#include <memory> + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/types/Date.hpp" + +namespace OpenVic { + + struct HistoryEntry { + private: + Date PROPERTY(date); + + protected: + HistoryEntry(Date new_date); + }; + + struct GameManager; + + /* Helper functions to avoid cyclic dependency issues */ + Date _get_start_date(GameManager const& game_manager); + Date _get_end_date(GameManager const& game_manager); + + template<std::derived_from<HistoryEntry> _Entry, typename... Args> + struct HistoryMap { + using entry_type = _Entry; + + private: + std::map<Date, std::unique_ptr<entry_type>> PROPERTY(entries); + + bool _try_load_history_entry(GameManager const& game_manager, Args... args, Date date, ast::NodeCPtr root) { + typename decltype(entries)::iterator it = entries.find(date); + if (it == entries.end()) { + const std::pair<typename decltype(entries)::iterator, bool> result = entries.emplace(date, _make_entry(date)); + if (result.second) { + it = result.first; + } else { + Logger::error("Failed to create history entry at date ", date); + return false; + } + } + return _load_history_entry(game_manager, args..., *it->second, root); + } + + protected: + HistoryMap() = default; + + virtual std::unique_ptr<entry_type> _make_entry(Date date) const = 0; + + virtual bool _load_history_entry( + GameManager const& game_manager, Args... args, entry_type& entry, ast::NodeCPtr root + ) = 0; + + bool _load_history_file(GameManager const& game_manager, Args... args, ast::NodeCPtr root) { + return _try_load_history_entry(game_manager, args..., _get_start_date(game_manager), root); + } + + bool _load_history_sub_entry_callback( + GameManager const& game_manager, Args... args, Date date, ast::NodeCPtr root, std::string_view key, + ast::NodeCPtr value, NodeTools::key_value_callback_t default_callback = NodeTools::key_value_invalid_callback + ) { + /* Date blocks (loaded into the corresponding HistoryEntry) */ + bool is_date = false; + const Date sub_date { Date::from_string(key, &is_date, true) }; + if (is_date) { + if (sub_date <= date) { + Logger::error("History entry ", sub_date, " defined before parent entry date ", date); + return false; + } + const Date end_date = _get_end_date(game_manager); + if (sub_date > end_date) { + Logger::error("History entry ", sub_date, " defined after end date ", end_date); + return false; + } + if (_try_load_history_entry(game_manager, args..., sub_date, value)) { + return true; + } else { + Logger::error("Failed to load history entry at date ", sub_date); + return false; + } + } + + return default_callback(key, value); + } + + public: + /* Returns history entry at specific date, if date doesn't have an entry returns nullptr. */ + entry_type const* get_entry(Date date) const { + typename decltype(entries)::const_iterator it = entries.find(date); + if (it != entries.end()) { + return it->second.get(); + } + return nullptr; + } + /* Returns history entries up to date as an ordered list of entries. */ + std::vector<entry_type const*> get_entries(Date end) const { + std::vector<entry_type const*> ret; + for (typename decltype(entries)::value_type const& entry : entries) { + if (entry.first <= end) { + ret.push_back(entry.second.get()); + } + } + std::sort(ret.begin(), ret.end(), [](entry_type const* lhs, entry_type const* rhs) -> bool { + return lhs->get_date() < rhs->get_date(); + }); + return ret; + } + }; +} diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp index c2d5451..141d256 100644 --- a/src/openvic-simulation/history/ProvinceHistory.cpp +++ b/src/openvic-simulation/history/ProvinceHistory.cpp @@ -6,132 +6,121 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -ProvinceHistory::ProvinceHistory( - Country const* new_owner, Country const* new_controller, uint8_t new_colonial, bool new_slave, - std::vector<Country const*>&& new_cores, Good const* new_rgo, uint8_t new_life_rating, TerrainType const* new_terrain_type, - std::map<Building const*, uint8_t>&& new_buildings, std::map<Ideology const*, uint8_t>&& new_party_loyalties -) : owner { new_owner }, controller { new_controller }, colonial { new_colonial }, slave { new_slave }, - cores { std::move(new_cores) }, rgo { new_rgo }, life_rating { new_life_rating }, terrain_type { new_terrain_type }, - buildings { std::move(new_buildings) }, party_loyalties { std::move(new_party_loyalties) } {} +ProvinceHistoryEntry::ProvinceHistoryEntry(Province const& new_province, Date new_date) + : HistoryEntry { new_date }, province { new_province } {} -Country const* ProvinceHistory::get_owner() const { - return owner; -} - -Country const* ProvinceHistory::get_controller() const { - return controller; -} - -uint8_t ProvinceHistory::get_colony_status() const { - return colonial; -} - -bool ProvinceHistory::is_slave() const { - return slave; -} +ProvinceHistoryMap::ProvinceHistoryMap(Province const& new_province) : province { new_province } {} -std::vector<Country const*> const& ProvinceHistory::get_cores() const { - return cores; +std::unique_ptr<ProvinceHistoryEntry> ProvinceHistoryMap::_make_entry(Date date) const { + return std::unique_ptr<ProvinceHistoryEntry> { new ProvinceHistoryEntry { province, date } }; } -bool ProvinceHistory::is_core_of(Country const* country) const { - return std::find(cores.begin(), cores.end(), country) != cores.end(); -} - -Good const* ProvinceHistory::get_rgo() const { - return rgo; -} - -uint8_t ProvinceHistory::get_life_rating() const { - return life_rating; -} - -TerrainType const* ProvinceHistory::get_terrain_type() const { - return terrain_type; -} +bool ProvinceHistoryMap::_load_history_entry( + GameManager const& game_manager, ProvinceHistoryEntry& entry, ast::NodeCPtr root +) { + BuildingManager const& building_manager = game_manager.get_economy_manager().get_building_manager(); + CountryManager const& country_manager = game_manager.get_country_manager(); + GoodManager const& good_manager = game_manager.get_economy_manager().get_good_manager(); + IdeologyManager const& ideology_manager = game_manager.get_politics_manager().get_ideology_manager(); + TerrainTypeManager const& terrain_type_manager = game_manager.get_map().get_terrain_type_manager(); + + using enum Province::colony_status_t; + static const string_map_t<Province::colony_status_t> colony_status_map { + { "0", STATE }, { "1", PROTECTORATE }, { "2", COLONY } + }; + + return expect_dictionary_keys_and_default( + [this, &game_manager, &building_manager, &entry]( + std::string_view key, ast::NodeCPtr value) -> bool { + // used for province buildings like forts or railroads + Building const* building = building_manager.get_building_by_identifier(key); + if (building != nullptr) { + return expect_uint<Building::level_t>([&entry, building](Building::level_t level) -> bool { + entry.buildings[building] = level; + return true; + })(value); + } -std::map<Building const*, uint8_t> const& ProvinceHistory::get_buildings() const { - return buildings; -} + return _load_history_sub_entry_callback(game_manager, entry.get_date(), value, key, value); + }, + "owner", ZERO_OR_ONE, + country_manager.expect_country_identifier(assign_variable_callback_pointer(entry.owner)), + "controller", ZERO_OR_ONE, + country_manager.expect_country_identifier(assign_variable_callback_pointer(entry.controller)), + "add_core", ZERO_OR_MORE, country_manager.expect_country_identifier( + [&entry](Country const& core) -> bool { + entry.add_cores.push_back(&core); + return true; + } + ), + "remove_core", ZERO_OR_MORE, country_manager.expect_country_identifier( + [&entry](Country const& core) -> bool { + entry.remove_cores.push_back(&core); + return true; + } + ), + "colonial", ZERO_OR_ONE, + expect_identifier(expect_mapped_string(colony_status_map, assign_variable_callback(entry.colonial))), + "colony", ZERO_OR_ONE, + expect_identifier(expect_mapped_string(colony_status_map, assign_variable_callback(entry.colonial))), + "is_slave", ZERO_OR_ONE, expect_bool(assign_variable_callback(entry.slave)), + "trade_goods", ZERO_OR_ONE, good_manager.expect_good_identifier(assign_variable_callback_pointer(entry.rgo)), + "life_rating", ZERO_OR_ONE, expect_uint<Province::life_rating_t>(assign_variable_callback(entry.life_rating)), + "terrain", ZERO_OR_ONE, terrain_type_manager.expect_terrain_type_identifier( + assign_variable_callback_pointer(entry.terrain_type) + ), + "party_loyalty", ZERO_OR_MORE, [&ideology_manager, &entry](ast::NodeCPtr node) -> bool { + Ideology const* ideology = nullptr; + fixed_point_t amount = 0; // percent I do believe + + const bool ret = expect_dictionary_keys( + "ideology", ONE_EXACTLY, ideology_manager.expect_ideology_identifier( + assign_variable_callback_pointer(ideology) + ), + "loyalty_value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(amount)) + )(node); + entry.party_loyalties[ideology] = amount; + return ret; + }, + "state_building", ZERO_OR_MORE, [&building_manager, &entry](ast::NodeCPtr node) -> bool { + Building const* building = nullptr; + uint8_t level = 0; -std::map<Ideology const*, uint8_t> const& ProvinceHistory::get_party_loyalties() const { - return party_loyalties; + const bool ret = expect_dictionary_keys( + "level", ONE_EXACTLY, expect_uint(assign_variable_callback(level)), + "building", ONE_EXACTLY, building_manager.expect_building_identifier( + assign_variable_callback_pointer(building) + ), + "upgrade", ZERO_OR_ONE, success_callback // doesn't appear to have an effect + )(node); + entry.buildings[building] = level; + return ret; + } + )(root); } -bool ProvinceHistoryManager::add_province_history_entry( - Province const* province, Date date, Country const* owner, Country const* controller, std::optional<uint8_t>&& colonial, - std::optional<bool>&& slave, std::vector<Country const*>&& cores, std::vector<Country const*>&& remove_cores, - Good const* rgo, std::optional<uint8_t>&& life_rating, TerrainType const* terrain_type, - std::optional<std::map<Building const*, uint8_t>>&& buildings, - std::optional<std::map<Ideology const*, uint8_t>>&& party_loyalties -) { - if (locked) { - Logger::error("Cannot add new history entry to province history registry: locked!"); - return false; +void ProvinceHistoryManager::lock_province_histories(Map const& map, bool detailed_errors) { + std::vector<bool> province_checklist(map.get_province_count()); + for (decltype(province_histories)::value_type const& entry : province_histories) { + province_checklist[entry.first->get_index() - 1] = true; } - /* combine duplicate histories, priority to current (defined later) */ - auto& province_registry = province_histories[province]; - const auto existing_entry = province_registry.find(date); - - if (existing_entry != province_registry.end()) { - if (owner != nullptr) { - existing_entry->second.owner = owner; - } - if (controller != nullptr) { - existing_entry->second.controller = controller; - } - if (rgo != nullptr) { - existing_entry->second.rgo = rgo; - } - if (terrain_type != nullptr) { - existing_entry->second.terrain_type = terrain_type; - } - if (colonial) { - existing_entry->second.colonial = *colonial; - } - if (slave) { - existing_entry->second.slave = *slave; - } - if (life_rating) { - existing_entry->second.life_rating = *life_rating; - } - if (buildings) { - existing_entry->second.buildings = std::move(*buildings); - } - if (party_loyalties) { - existing_entry->second.party_loyalties = std::move(*party_loyalties); - } - // province history cores are additive - existing_entry->second.cores.insert(existing_entry->second.cores.end(), cores.begin(), cores.end()); - for (const auto which : remove_cores) { - const auto core = std::find(cores.begin(), cores.end(), which); - if (core == cores.end()) { - Logger::error( - "In history of province ", province->get_identifier(), " tried to remove nonexistant core of country: ", - which->get_identifier(), " at date ", date.to_string() - ); - return false; + size_t missing = 0; + for (size_t idx = 0; idx < province_checklist.size(); ++idx) { + if (!province_checklist[idx]) { + Province const& province = *map.get_province_by_index(idx + 1); + if (!province.get_water()) { + if (detailed_errors) { + Logger::warning("Province history missing for province: ", province.get_identifier()); + } + missing++; } - existing_entry->second.cores.erase(core); } - } else { - province_registry.emplace(date, ProvinceHistory { - owner, controller, *colonial, *slave, std::move(cores), rgo, *life_rating, - terrain_type, std::move(*buildings), std::move(*party_loyalties) - }); } - return true; -} - -void ProvinceHistoryManager::lock_province_histories() { - for (auto const& entry : province_histories) { - if (entry.second.size() == 0) { - Logger::error( - "Attempted to lock province histories - province ", entry.first->get_identifier(), " has no history entries!" - ); - } + if (missing > 0) { + Logger::warning("Province history is missing for ", missing, " provinces"); } + Logger::info("Locked province history registry after registering ", province_histories.size(), " items"); locked = true; } @@ -140,190 +129,43 @@ bool ProvinceHistoryManager::is_locked() const { return locked; } -ProvinceHistory const* ProvinceHistoryManager::get_province_history(Province const* province, Date entry) const { - Date closest_entry; - auto province_registry = province_histories.find(province); - - if (province_registry == province_histories.end()) { - Logger::error("Attempted to access history of undefined province ", province->get_identifier()); +ProvinceHistoryMap const* ProvinceHistoryManager::get_province_history(Province const* province) const { + if (province == nullptr) { + Logger::error("Attempted to access history of null province"); return nullptr; } - - for (auto const& current : province_registry->second) { - if (current.first == entry) { - return ¤t.second; - } - if (current.first > entry) { - continue; - } - if (current.first > closest_entry && current.first < entry) { - closest_entry = current.first; - } - } - - auto entry_registry = province_registry->second.find(closest_entry); - if (entry_registry != province_registry->second.end()) { - return &entry_registry->second; + decltype(province_histories)::const_iterator province_registry = province_histories.find(province); + if (province_registry != province_histories.end()) { + return &province_registry->second; + } else { + Logger::error("Attempted to access history of province ", province->get_identifier(), " but none has been defined!"); + return nullptr; } - /* warned about lack of entries earlier, return nullptr */ - return nullptr; -} - -inline ProvinceHistory const* ProvinceHistoryManager::get_province_history( - Province const* province, Bookmark const* bookmark -) const { - return get_province_history(province, bookmark->get_date()); } -inline bool ProvinceHistoryManager::_load_province_history_entry( - GameManager& game_manager, std::string_view province, Date const& date, ast::NodeCPtr root +bool ProvinceHistoryManager::load_province_history_file( + GameManager const& game_manager, Province const& province, ast::NodeCPtr root ) { - Country const* owner = nullptr; - Country const* controller = nullptr; - std::vector<Country const*> cores; - std::vector<Country const*> remove_cores; - Good const* rgo; - std::optional<uint8_t> life_rating, colonial; - std::optional<bool> slave; - TerrainType const* terrain_type; - std::optional<std::map<Building const*, uint8_t>> buildings; - std::optional<std::map<Ideology const*, uint8_t>> party_loyalties; - - bool ret = expect_dictionary_keys_and_default( - [&game_manager, &buildings](std::string_view key, ast::NodeCPtr value) -> bool { - // used for province buildings like forts or railroads - if (game_manager.get_economy_manager().get_building_manager().has_building_identifier(key)) { - Building const* building; - uint8_t level; - - bool ret = game_manager.get_economy_manager().get_building_manager() - .expect_building_str(assign_variable_callback_pointer(building))(key); - ret &= expect_uint(assign_variable_callback(level))(value); - - if(!buildings.has_value()) - buildings = decltype(buildings)::value_type {}; - buildings->emplace(building, level); - return ret; - } - - bool is_date; - Date().from_string(key, &is_date, true); - if (is_date) { - return true; - } - - return key_value_invalid_callback(key, value); - }, - "owner", ZERO_OR_ONE, game_manager.get_country_manager() - .expect_country_identifier(assign_variable_callback_pointer(owner)), - "controller", ZERO_OR_ONE, game_manager.get_country_manager() - .expect_country_identifier(assign_variable_callback_pointer(controller)), - "add_core", ZERO_OR_MORE, [&game_manager, &cores](ast::NodeCPtr node) -> bool { - Country const* core; - - bool ret = game_manager.get_country_manager() - .expect_country_identifier(assign_variable_callback_pointer(core))(node); - cores.push_back(core); - return ret; - }, - "remove_core", ZERO_OR_MORE, [&game_manager, &remove_cores](ast::NodeCPtr node) -> bool { - Country const* remove; - - bool ret = game_manager.get_country_manager() - .expect_country_identifier(assign_variable_callback_pointer(remove))(node); - remove_cores.push_back(remove); - return ret; - }, - "colonial", ZERO_OR_ONE, expect_uint<uint8_t>([&colonial](uint8_t colony_level) -> bool { - colonial = colony_level; - - return true; - }), - "colony", ZERO_OR_ONE, expect_uint<uint8_t>([&colonial](uint8_t colony_level) -> bool { - colonial = colony_level; - - return true; - }), - "is_slave", ZERO_OR_ONE, expect_bool([&slave](bool is_slave) -> bool { - if (is_slave) { - slave = true; - } else { - slave = false; - } - - return true; - }), - "trade_goods", ZERO_OR_ONE, game_manager.get_economy_manager().get_good_manager() - .expect_good_identifier(assign_variable_callback_pointer(rgo)), - "life_rating", ZERO_OR_ONE, expect_uint<uint8_t>([&life_rating](uint8_t rating) -> bool { - life_rating = rating; - - return true; - }), - "terrain", ZERO_OR_ONE, game_manager.get_map().get_terrain_type_manager() - .expect_terrain_type_identifier(assign_variable_callback_pointer(terrain_type)), - "party_loyalty", ZERO_OR_MORE, [&game_manager, &party_loyalties](ast::NodeCPtr node) -> bool { - Ideology const* ideology; - uint8_t amount; // percent I do believe - - bool ret = expect_dictionary_keys( - "ideology", ONE_EXACTLY, game_manager.get_politics_manager().get_ideology_manager() - .expect_ideology_identifier(assign_variable_callback_pointer(ideology)), - "loyalty_value", ONE_EXACTLY, expect_uint(assign_variable_callback(amount)) - )(node); - if(!party_loyalties.has_value()) - party_loyalties = decltype(party_loyalties)::value_type {}; - party_loyalties->emplace(ideology, amount); - return ret; - }, - "state_building", ZERO_OR_MORE, [&game_manager, &buildings](ast::NodeCPtr node) -> bool { - Building const* building; - uint8_t level; - - bool ret = expect_dictionary_keys( - "level", ONE_EXACTLY, expect_uint(assign_variable_callback(level)), - "building", ONE_EXACTLY, game_manager.get_economy_manager().get_building_manager() - .expect_building_identifier(assign_variable_callback_pointer(building)), - "upgrade", ZERO_OR_ONE, success_callback // doesn't appear to have an effect - )(node); - if(!buildings.has_value()) - buildings = decltype(buildings)::value_type {}; - buildings->emplace(building, level); - return ret; - } - )(root); - - ret &= add_province_history_entry( - game_manager.get_map().get_province_by_identifier(province), date, owner, controller, std::move(colonial), std::move(slave), - std::move(cores), std::move(remove_cores), rgo, std::move(life_rating), terrain_type, std::move(buildings), - std::move(party_loyalties) - ); - return ret; -} - -bool ProvinceHistoryManager::load_province_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root) { - bool ret = _load_province_history_entry(game_manager, name, game_manager.get_define_manager().get_start_date(), root); - - ret &= expect_dictionary( - [this, &game_manager, &name](std::string_view key, ast::NodeCPtr value) -> bool { - bool is_date = false; - Date entry = Date().from_string(key, &is_date, true); - if (!is_date) { - return true; - } - - Date const& end_date = game_manager.get_define_manager().get_end_date(); - if (entry > end_date) { - Logger::error( - "History entry ", entry.to_string(), " of province ", name, " defined after defined end date ", - end_date.to_string() - ); - return false; - } + if (locked) { + Logger::error( + "Attempted to load province history file for ", province.get_identifier(), + " after province history registry was locked!" + ); + return false; + } - return _load_province_history_entry(game_manager, name, entry, value); + decltype(province_histories)::iterator it = province_histories.find(&province); + if (it == province_histories.end()) { + const std::pair<decltype(province_histories)::iterator, bool> result = + province_histories.emplace(&province, ProvinceHistoryMap { province }); + if (result.second) { + it = result.first; + } else { + Logger::error("Failed to create province history map for province ", province.get_identifier()); + return false; } - )(root); + } + ProvinceHistoryMap& province_history = it->second; - return ret; + return province_history._load_history_file(game_manager, root); } diff --git a/src/openvic-simulation/history/ProvinceHistory.hpp b/src/openvic-simulation/history/ProvinceHistory.hpp index 42dacbf..5a18723 100644 --- a/src/openvic-simulation/history/ProvinceHistory.hpp +++ b/src/openvic-simulation/history/ProvinceHistory.hpp @@ -8,79 +8,64 @@ #include "openvic-simulation/economy/Building.hpp" #include "openvic-simulation/economy/Good.hpp" #include "openvic-simulation/history/Bookmark.hpp" +#include "openvic-simulation/history/HistoryMap.hpp" #include "openvic-simulation/map/Province.hpp" #include "openvic-simulation/map/TerrainType.hpp" namespace OpenVic { + struct ProvinceHistoryMap; + + struct ProvinceHistoryEntry : HistoryEntry { + friend struct ProvinceHistoryMap; + + using building_level_map_t = std::map<Building const*, Building::level_t>; + + private: + Province const& PROPERTY(province); + + std::optional<Country const*> PROPERTY(owner); + std::optional<Country const*> PROPERTY(controller); + std::optional<Province::colony_status_t> PROPERTY(colonial); + std::optional<bool> PROPERTY(slave); + std::vector<Country const*> PROPERTY(add_cores); + std::vector<Country const*> PROPERTY(remove_cores); + std::optional<Good const*> PROPERTY(rgo); + std::optional<Province::life_rating_t> PROPERTY(life_rating); + std::optional<TerrainType const*> PROPERTY(terrain_type); + building_level_map_t PROPERTY(buildings); + decimal_map_t<Ideology const*> PROPERTY(party_loyalties); + + ProvinceHistoryEntry(Province const& new_province, Date new_date); + }; + struct ProvinceHistoryManager; - struct ProvinceHistory { + struct ProvinceHistoryMap : HistoryMap<ProvinceHistoryEntry> { friend struct ProvinceHistoryManager; private: - Country const* owner; - Country const* controller; - uint8_t colonial; - bool slave; - std::vector<Country const*> cores; // non-standard, maintains cores between entries - Good const* rgo; - uint8_t life_rating; - TerrainType const* terrain_type; - std::map<Building const*, uint8_t> buildings; - std::map<Ideology const*, uint8_t> party_loyalties; - - ProvinceHistory( - Country const* new_owner, Country const* new_controller, uint8_t new_colonial, bool new_slave, - std::vector<Country const*>&& new_cores, Good const* new_rgo, uint8_t new_life_rating, - TerrainType const* new_terrain_type, std::map<Building const*, uint8_t>&& new_buildings, - std::map<Ideology const*, uint8_t>&& new_party_loyalties - ); + Province const& PROPERTY(province); - public: - Country const* get_owner() const; - Country const* get_controller() const; - uint8_t get_colony_status() const; // 0 = state, 1 = protectorate, 2 = colony - bool is_slave() const; - std::vector<Country const*> const& get_cores() const; - bool is_core_of(Country const* country) const; - Good const* get_rgo() const; - uint8_t get_life_rating() const; - TerrainType const* get_terrain_type() const; - std::map<Building const*, uint8_t> const& get_buildings() const; - std::map<Ideology const*, uint8_t> const& get_party_loyalties() const; + protected: + ProvinceHistoryMap(Province const& new_province); + + std::unique_ptr<ProvinceHistoryEntry> _make_entry(Date date) const override; + bool _load_history_entry(GameManager const& game_manager, ProvinceHistoryEntry& entry, ast::NodeCPtr root) override; }; struct ProvinceHistoryManager { private: - std::map<Province const*, std::map<Date, ProvinceHistory>> province_histories; + std::map<Province const*, ProvinceHistoryMap> PROPERTY(province_histories); bool locked = false; - inline bool _load_province_history_entry( - GameManager& game_manager, std::string_view province, Date const& date, ast::NodeCPtr root - ); - public: - ProvinceHistoryManager() {} - - bool add_province_history_entry( - Province const* province, Date date, Country const* owner, Country const* controller, - std::optional<uint8_t>&& colonial, std::optional<bool>&& slave, - std::vector<Country const*>&& cores, // additive to existing entries - std::vector<Country const*>&& remove_cores, // existing cores that need to be removed - Good const* rgo, std::optional<uint8_t>&& life_rating, TerrainType const* terrain_type, - std::optional<std::map<Building const*, uint8_t>>&& buildings, - std::optional<std::map<Ideology const*, uint8_t>>&& party_loyalties - ); - - void lock_province_histories(); + ProvinceHistoryManager() = default; + + void lock_province_histories(Map const& map, bool detailed_errors); bool is_locked() const; - /* Returns history of province at date, if date doesn't have an entry returns closest entry before date. - * Return can be nullptr if an error occurs. */ - ProvinceHistory const* get_province_history(Province const* province, Date entry) const; - /* Returns history of province at bookmark date. Return can be nullptr if an error occurs. */ - inline ProvinceHistory const* get_province_history(Province const* province, Bookmark const* bookmark) const; + ProvinceHistoryMap const* get_province_history(Province const* province) const; - bool load_province_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root); + bool load_province_history_file(GameManager const& game_manager, Province const& province, ast::NodeCPtr root); }; } // namespace OpenVic diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp index 4df17bf..7e2213e 100644 --- a/src/openvic-simulation/map/Map.cpp +++ b/src/openvic-simulation/map/Map.cpp @@ -42,6 +42,12 @@ bool Map::add_province(std::string_view identifier, colour_t colour) { Logger::error("Invalid province identifier - empty!"); return false; } + if (!valid_basic_identifier(identifier)) { + Logger::error( + "Invalid province identifier: ", identifier, " (can only contain alphanumeric characters and underscores)" + ); + return false; + } if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) { Logger::error("Invalid province colour for ", identifier, ": ", colour_to_hex_string(colour)); return false; @@ -292,7 +298,7 @@ bool Map::setup(BuildingManager const& building_manager, PopManager const& pop_m return ret; } -void Map::update_state(Date const& today) { +void Map::update_state(Date today) { for (Province& province : provinces.get_items()) { province.update_state(today); } @@ -300,7 +306,7 @@ void Map::update_state(Date const& today) { update_total_map_population(); } -void Map::tick(Date const& today) { +void Map::tick(Date today) { for (Province& province : provinces.get_items()) { province.tick(today); } @@ -483,7 +489,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain uint8_t const* terrain_data = terrain_bmp.get_pixel_data().data(); std::vector<bool> province_checklist(provinces.size()); - std::vector<distribution_t> terrain_type_pixels_list(provinces.size()); + std::vector<decimal_map_t<TerrainType const*>> terrain_type_pixels_list(provinces.size()); bool ret = true; std::unordered_set<colour_t> unrecognised_province_colours; @@ -514,7 +520,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain goto set_terrain; } } - if (unrecognised_province_colours.find(province_colour) == unrecognised_province_colours.end()) { + if (!unrecognised_province_colours.contains(province_colour)) { unrecognised_province_colours.insert(province_colour); if (detailed_errors) { Logger::warning( diff --git a/src/openvic-simulation/map/Map.hpp b/src/openvic-simulation/map/Map.hpp index 61b7b48..d11ad8e 100644 --- a/src/openvic-simulation/map/Map.hpp +++ b/src/openvic-simulation/map/Map.hpp @@ -108,8 +108,8 @@ namespace OpenVic { void update_total_map_population(); Pop::pop_size_t get_total_map_population() const; - void update_state(Date const& today); - void tick(Date const& today); + void update_state(Date today); + void tick(Date today); bool load_province_definitions(std::vector<ovdl::csv::LineObject> const& lines); bool load_province_positions(BuildingManager const& building_manager, ast::NodeCPtr root); diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp index 225d4c0..77bf33f 100644 --- a/src/openvic-simulation/map/Province.cpp +++ b/src/openvic-simulation/map/Province.cpp @@ -5,7 +5,7 @@ using namespace OpenVic::NodeTools; Province::Province( std::string_view new_identifier, colour_t new_colour, index_t new_index -) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, index { new_index }, +) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, index { new_index }, buildings { "buildings", false } { assert(index != NULL_INDEX); } @@ -38,6 +38,10 @@ Province::life_rating_t Province::get_life_rating() const { return life_rating; } +Province::colony_status_t Province::get_colony_status() const { + return colony_status; +} + bool Province::load_positions(BuildingManager const& building_manager, ast::NodeCPtr root) { return expect_dictionary_keys( "text_position", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.text)), @@ -127,42 +131,32 @@ Pop::pop_size_t Province::get_total_population() const { return total_population; } -distribution_t const& Province::get_pop_type_distribution() const { - return pop_types; -} - -distribution_t const& Province::get_culture_distribution() const { - return cultures; -} - -distribution_t const& Province::get_religion_distribution() const { - return religions; -} - /* REQUIREMENTS: * MAP-65, MAP-68, MAP-70, MAP-234 */ void Province::update_pops() { total_population = 0; - pop_types.clear(); - cultures.clear(); - religions.clear(); + pop_type_distribution.clear(); + ideology_distribution.clear(); + culture_distribution.clear(); + religion_distribution.clear(); for (Pop const& pop : pops) { total_population += pop.get_size(); - pop_types[&pop.get_type()] += pop.get_size(); - cultures[&pop.get_culture()] += pop.get_size(); - religions[&pop.get_religion()] += pop.get_size(); + pop_type_distribution[&pop.get_type()] += pop.get_size(); + //ideology_distribution[&pop.get_???()] += pop.get_size(); + culture_distribution[&pop.get_culture()] += pop.get_size(); + religion_distribution[&pop.get_religion()] += pop.get_size(); } } -void Province::update_state(Date const& today) { +void Province::update_state(Date today) { for (BuildingInstance& building : buildings.get_items()) { building.update_state(today); } update_pops(); } -void Province::tick(Date const& today) { +void Province::tick(Date today) { for (BuildingInstance& building : buildings.get_items()) { building.tick(today); } diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp index 2704354..2fd15c9 100644 --- a/src/openvic-simulation/map/Province.hpp +++ b/src/openvic-simulation/map/Province.hpp @@ -3,6 +3,7 @@ #include <cassert> #include "openvic-simulation/economy/Building.hpp" +#include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/pop/Pop.hpp" namespace OpenVic { @@ -24,6 +25,8 @@ namespace OpenVic { using distance_t = uint16_t; using flags_t = uint16_t; + enum struct colony_status_t : int8_t { STATE, PROTECTORATE, COLONY }; + struct adjacency_t { friend struct Province; @@ -63,13 +66,17 @@ namespace OpenVic { Region* region = nullptr; bool on_map = false, has_region = false, water = false; life_rating_t life_rating = 0; + colony_status_t colony_status = colony_status_t::STATE; IdentifierRegistry<BuildingInstance> buildings; // TODO - change this into a factory-like structure Good const* rgo = nullptr; std::vector<Pop> pops; Pop::pop_size_t total_population; - distribution_t pop_types, cultures, religions; + decimal_map_t<PopType const*> PROPERTY(pop_type_distribution); + decimal_map_t<Ideology const*> PROPERTY(ideology_distribution); + decimal_map_t<Culture const*> PROPERTY(culture_distribution); + decimal_map_t<Religion const*> PROPERTY(religion_distribution); std::vector<adjacency_t> adjacencies; province_positions_t positions; @@ -90,6 +97,7 @@ namespace OpenVic { bool get_water() const; TerrainType const* get_terrain_type() const; life_rating_t get_life_rating() const; + colony_status_t get_colony_status() const; bool load_positions(BuildingManager const& building_manager, ast::NodeCPtr root); bool add_building(BuildingInstance&& building_instance); @@ -105,13 +113,10 @@ namespace OpenVic { size_t get_pop_count() const; std::vector<Pop> const& get_pops() const; Pop::pop_size_t get_total_population() const; - distribution_t const& get_pop_type_distribution() const; - distribution_t const& get_culture_distribution() const; - distribution_t const& get_religion_distribution() const; void update_pops(); - void update_state(Date const& today); - void tick(Date const& today); + void update_state(Date today); + void tick(Date today); bool is_adjacent_to(Province const* province); bool add_adjacency(Province const* province, distance_t distance, flags_t flags); diff --git a/src/openvic-simulation/map/TerrainType.cpp b/src/openvic-simulation/map/TerrainType.cpp index 1753246..796089e 100644 --- a/src/openvic-simulation/map/TerrainType.cpp +++ b/src/openvic-simulation/map/TerrainType.cpp @@ -7,7 +7,7 @@ using namespace OpenVic::NodeTools; TerrainType::TerrainType( std::string_view new_identifier, colour_t new_colour, ModifierValue&& new_modifier, bool new_is_water -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, modifier { std::move(new_modifier) }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, modifier { std::move(new_modifier) }, is_water { new_is_water } {} ModifierValue const& TerrainType::get_modifier() const { diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp index 5b3aa77..0530986 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -5,22 +5,28 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -Deployment::Deployment( - std::string_view new_path, std::vector<Army>&& new_armies, std::vector<Navy>&& new_navies, std::vector<Leader>&& new_leaders -) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) }, - leaders { std::move(new_leaders) } {} +Leader::Leader( + std::string_view new_name, Unit::type_t new_type, Date new_date, LeaderTrait const* new_personality, + LeaderTrait const* new_background, fixed_point_t new_prestige +) : name { new_name }, type { new_type }, date { new_date }, personality { new_personality }, background { new_background }, + prestige { new_prestige } {} -const std::vector<Army>& Deployment::get_armies() const { - return armies; -} +Regiment::Regiment(std::string_view new_name, Unit const* new_type, Province const* new_home) + : name { new_name }, type { new_type }, home { new_home } {} -const std::vector<Navy>& Deployment::get_navies() const { - return navies; -} +Ship::Ship(std::string_view new_name, Unit const* new_type) : name { new_name }, type { new_type } {} -const std::vector<Leader>& Deployment::get_leaders() const { - return leaders; -} +Army::Army(std::string_view new_name, Province const* new_location, std::vector<Regiment>&& new_regiments) + : name { new_name }, location { new_location }, regiments { std::move(new_regiments) } {} + +Navy::Navy(std::string_view new_name, Province const* new_location, std::vector<Ship>&& new_ships) + : name { new_name }, location { new_location }, ships { std::move(new_ships) } {} + +Deployment::Deployment( + std::string_view new_path, std::vector<Army>&& new_armies, std::vector<Navy>&& new_navies, + std::vector<Leader>&& new_leaders +) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) }, + leaders { std::move(new_leaders) } {} DeploymentManager::DeploymentManager() : deployments { "deployments" } {} @@ -31,117 +37,146 @@ bool DeploymentManager::add_deployment( Logger::error("Attemped to load order of battle with no path! Something is very wrong!"); return false; } - if (armies.empty() && navies.empty() && leaders.empty() && path != "NULL") { - Logger::warning("Loading redundant empty order of battle at ", path); - } - return deployments.add_item({ path, std::move(armies), std::move(navies), std::move(leaders) }); + return deployments.add_item( + std::make_unique<Deployment>(std::move(path), std::move(armies), std::move(navies), std::move(leaders)) + ); } -bool DeploymentManager::load_oob_file(GameManager& game_manager, std::string_view path, ast::NodeCPtr root) { +bool DeploymentManager::load_oob_file( + GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path, + Deployment const*& deployment, bool fail_on_missing +) { + deployment = get_deployment_by_identifier(history_path); + if (deployment != nullptr) { + return true; + } + if (missing_oob_files.contains(history_path)) { + return !fail_on_missing; + } + static constexpr std::string_view oob_directory = "history/units/"; + const fs::path lookedup_path = + dataloader.lookup_file(StringUtils::append_string_views(oob_directory, history_path), false); + if (lookedup_path.empty()) { + missing_oob_files.emplace(history_path); + if (fail_on_missing) { + Logger::warning("Could not find OOB file ", history_path, "!"); + return false; + } else { + return true; + } + } std::vector<Army> armies; std::vector<Navy> navies; std::vector<Leader> leaders; - bool ret = expect_dictionary_keys_and_default( key_value_success_callback, // TODO: load SOI information "leader", ZERO_OR_MORE, [&leaders, &game_manager](ast::NodeCPtr node) -> bool { - std::string_view name; - Unit::type_t type; - Date date; - LeaderTrait const* personality = nullptr; - LeaderTrait const* background = nullptr; - fixed_point_t prestige = 0; + std::string_view leader_name {}; + Unit::type_t leader_type = Unit::type_t::LAND; + Date leader_date {}; + LeaderTrait const* leader_personality = nullptr; + LeaderTrait const* leader_background = nullptr; + fixed_point_t leader_prestige = 0; bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false), - "date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(date))), - "type", ONE_EXACTLY, expect_identifier([&type](std::string_view leader_type) -> bool { - if (leader_type == "land") { - type = Unit::type_t::LAND; - } else { - type = Unit::type_t::NAVAL; - } - return true; - }), + "name", ONE_EXACTLY, expect_string(assign_variable_callback(leader_name)), + "date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(leader_date))), + "type", ONE_EXACTLY, expect_identifier(UnitManager::expect_type_str(assign_variable_callback(leader_type))), "personality", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager() - .expect_leader_trait_identifier(assign_variable_callback_pointer(personality)), + .expect_leader_trait_identifier(assign_variable_callback_pointer(leader_personality)), "background", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager() - .expect_leader_trait_identifier(assign_variable_callback_pointer(background)), - "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(prestige)), + .expect_leader_trait_identifier(assign_variable_callback_pointer(leader_background)), + "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(leader_prestige)), "picture", ZERO_OR_ONE, success_callback )(node); - if (!personality->is_personality_trait()) { - Logger::error("Leader ", name, " has personality ", personality->get_identifier(), - " which is not a personality trait!"); - return false; + if (!leader_personality->is_personality_trait()) { + Logger::error( + "Leader ", leader_name, " has personality ", leader_personality->get_identifier(), + " which is not a personality trait!" + ); + ret = false; } - if (!background->is_background_trait()) { - Logger::error("Leader ", name, " has background ", background->get_identifier(), - " which is not a background trait!"); - return false; + if (!leader_background->is_background_trait()) { + Logger::error( + "Leader ", leader_name, " has background ", leader_background->get_identifier(), + " which is not a background trait!" + ); + ret = false; } - - leaders.push_back(Leader{ std::string(name), type, date, personality, background, prestige }); + leaders.emplace_back( + leader_name, leader_type, leader_date, leader_personality, leader_background, leader_prestige + ); return ret; }, "army", ZERO_OR_MORE, [&armies, &game_manager](ast::NodeCPtr node) -> bool { - std::string_view name; - Province const* location = nullptr; - std::vector<Regiment> regiments; + std::string_view army_name {}; + Province const* army_location = nullptr; + std::vector<Regiment> army_regiments {}; - bool ret = expect_dictionary_keys( - /* another paradox gem, tested in game and they don't lead the army or even show up */ - "leader", ZERO_OR_MORE, success_callback, - "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false), + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(army_name)), "location", ONE_EXACTLY, - game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(location)), - "regiment", ONE_OR_MORE, [&game_manager, ®iments](ast::NodeCPtr node) -> bool { - Regiment regiment; - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(regiment.name), false), + game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(army_location)), + "regiment", ONE_OR_MORE, [&game_manager, &army_regiments](ast::NodeCPtr node) -> bool { + std::string_view regiment_name {}; + Unit const* regiment_type = nullptr; + Province const* regiment_home = nullptr; + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(regiment_name)), "type", ONE_EXACTLY, game_manager.get_military_manager().get_unit_manager() - .expect_unit_identifier(assign_variable_callback_pointer(regiment.type)), - "home", ONE_EXACTLY, game_manager.get_map() - .expect_province_identifier(assign_variable_callback_pointer(regiment.home)) + .expect_unit_identifier(assign_variable_callback_pointer(regiment_type)), + "home", ZERO_OR_ONE, game_manager.get_map() + .expect_province_identifier(assign_variable_callback_pointer(regiment_home)) )(node); - regiments.push_back(regiment); + if (regiment_home == nullptr) { + Logger::warning("Regiment ", regiment_name, " has no home province!"); + } + army_regiments.emplace_back(regiment_name, regiment_type, regiment_home); return ret; - } + }, + /* another paradox gem, tested in game and they don't lead the army or even show up */ + "leader", ZERO_OR_MORE, success_callback )(node); - armies.push_back(Army{ std::string(name), location, std::move(regiments) }); + armies.emplace_back(army_name, army_location, std::move(army_regiments)); return ret; }, "navy", ZERO_OR_MORE, [&navies, &game_manager](ast::NodeCPtr node) -> bool { - std::string_view name; - Province const* location = nullptr; - std::vector<Ship> ships; + std::string_view navy_name {}; + Province const* navy_location = nullptr; + std::vector<Ship> navy_ships {}; - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false), + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(navy_name)), "location", ONE_EXACTLY, - game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(location)), - "ship", ONE_OR_MORE, [&game_manager, &ships](ast::NodeCPtr node) -> bool { - Ship ship; - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(ship.name), false), + game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(navy_location)), + "ship", ONE_OR_MORE, [&game_manager, &navy_ships](ast::NodeCPtr node) -> bool { + std::string_view ship_name {}; + Unit const* ship_type = nullptr; + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(ship_name)), "type", ONE_EXACTLY, game_manager.get_military_manager().get_unit_manager() - .expect_unit_identifier(assign_variable_callback_pointer(ship.type)) + .expect_unit_identifier(assign_variable_callback_pointer(ship_type)) )(node); - ships.push_back(ship); + navy_ships.emplace_back(ship_name, ship_type); return ret; }, + /* another paradox gem, tested in game and they don't lead the army or even show up */ "leader", ZERO_OR_MORE, success_callback )(node); - navies.push_back(Navy{ std::string(name), location, std::move(ships) }); + navies.emplace_back(navy_name, navy_location, std::move(navy_ships)); return ret; } - )(root); - /* need to do this for platform compatibility of identifiers */ - std::string identifier = std::string { path }; - std::replace(identifier.begin(), identifier.end(), '\\', '/'); - ret &= add_deployment(identifier, std::move(armies), std::move(navies), std::move(leaders)); - + )(Dataloader::parse_defines(lookedup_path).get_file_node()); + ret &= add_deployment(history_path, std::move(armies), std::move(navies), std::move(leaders)); + deployment = get_deployment_by_identifier(history_path); + if (deployment == nullptr) { + ret = false; + } return ret; } + +size_t DeploymentManager::get_missing_oob_file_count() const { + return missing_oob_files.size(); +} diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp index 34cf82a..ead324e 100644 --- a/src/openvic-simulation/military/Deployment.hpp +++ b/src/openvic-simulation/military/Deployment.hpp @@ -15,46 +15,69 @@ namespace OpenVic { struct Leader { - std::string name; - const Unit::type_t type; - const Date date; - LeaderTrait const* personality; - LeaderTrait const* background; - fixed_point_t prestige; + private: + std::string PROPERTY(name); + Unit::type_t PROPERTY(type); + Date PROPERTY(date); + LeaderTrait const* PROPERTY(personality); + LeaderTrait const* PROPERTY(background); + fixed_point_t PROPERTY(prestige); + + public: + Leader( + std::string_view new_name, Unit::type_t new_type, Date new_date, LeaderTrait const* new_personality, + LeaderTrait const* new_background, fixed_point_t new_prestige + ); }; struct Regiment { - std::string name; - Unit const* type; - Province const* home; + private: + std::string PROPERTY(name); + Unit const* PROPERTY(type); + Province const* PROPERTY(home); + + public: + Regiment(std::string_view new_name, Unit const* new_type, Province const* new_home); }; struct Ship { - std::string name; - Unit const* type; + private: + std::string PROPERTY(name); + Unit const* PROPERTY(type); + + public: + Ship(std::string_view new_name, Unit const* new_type); }; struct Army { - std::string name; - Province const* location; - std::vector<Regiment> regiments; + private: + std::string PROPERTY(name); + Province const* PROPERTY(location); + std::vector<Regiment> PROPERTY(regiments); + + public: + Army(std::string_view new_name, Province const* new_location, std::vector<Regiment>&& new_regiments); }; struct Navy { - std::string name; - Province const* location; - std::vector<Ship> ships; - }; + private: + std::string PROPERTY(name); + Province const* PROPERTY(location); + std::vector<Ship> PROPERTY(ships); - struct DeploymentManager; + public: + Navy(std::string_view new_name, Province const* new_location, std::vector<Ship>&& new_ships); + }; struct Deployment : HasIdentifier { - friend struct DeploymentManager; + friend std::unique_ptr<Deployment> std::make_unique<Deployment>( + std::string_view&&, std::vector<OpenVic::Army>&&, std::vector<OpenVic::Navy>&&, std::vector<OpenVic::Leader>&& + ); private: - const std::vector<Army> armies; - const std::vector<Navy> navies; - const std::vector<Leader> leaders; + std::vector<Army> PROPERTY(armies); + std::vector<Navy> PROPERTY(navies); + std::vector<Leader> PROPERTY(leaders); Deployment( std::string_view new_path, std::vector<Army>&& new_armies, std::vector<Navy>&& new_navies, @@ -62,14 +85,13 @@ namespace OpenVic { ); public: - const std::vector<Army>& get_armies() const; - const std::vector<Navy>& get_navies() const; - const std::vector<Leader>& get_leaders() const; + Deployment(Deployment&&) = default; }; struct DeploymentManager { private: - IdentifierRegistry<Deployment> deployments; + IdentifierInstanceRegistry<Deployment> deployments; + string_set_t missing_oob_files; public: DeploymentManager(); @@ -77,8 +99,12 @@ namespace OpenVic { bool add_deployment( std::string_view path, std::vector<Army>&& armies, std::vector<Navy>&& navies, std::vector<Leader>&& leaders ); - IDENTIFIER_REGISTRY_ACCESSORS(deployment); + IDENTIFIER_REGISTRY_ACCESSORS(deployment) - bool load_oob_file(GameManager& game_manager, std::string_view path, ast::NodeCPtr root); + bool load_oob_file( + GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path, + Deployment const*& deployment, bool fail_on_missing + ); + size_t get_missing_oob_file_count() const; }; } // namespace OpenVic diff --git a/src/openvic-simulation/military/LeaderTrait.hpp b/src/openvic-simulation/military/LeaderTrait.hpp index e61a1fc..16b4201 100644 --- a/src/openvic-simulation/military/LeaderTrait.hpp +++ b/src/openvic-simulation/military/LeaderTrait.hpp @@ -46,7 +46,7 @@ namespace OpenVic { struct LeaderTraitManager { private: IdentifierRegistry<LeaderTrait> leader_traits; - inline static const string_set_t allowed_modifiers = { + static inline const string_set_t allowed_modifiers = { "attack", "defence", "morale", "organisation", "reconnaissance", "speed", "attrition", "experience", "reliability" }; diff --git a/src/openvic-simulation/military/Unit.cpp b/src/openvic-simulation/military/Unit.cpp index 1b5f2d2..5322a88 100644 --- a/src/openvic-simulation/military/Unit.cpp +++ b/src/openvic-simulation/military/Unit.cpp @@ -244,6 +244,12 @@ bool UnitManager::add_naval_unit(std::string_view identifier, UNIT_PARAMS, NAVY_ return units.add_item(NavalUnit { identifier, UNIT_ARGS, NAVY_ARGS }); } +callback_t<std::string_view> UnitManager::expect_type_str(Callback<Unit::type_t> auto callback) { + using enum Unit::type_t; + static const string_map_t<Unit::type_t> type_map = { { "land", LAND }, { "naval", NAVAL }, { "sea", NAVAL } }; + return expect_mapped_string(type_map, callback); +} + bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr root) { return expect_dictionary([this, &good_manager](std::string_view key, ast::NodeCPtr value) -> bool { Unit::type_t type; @@ -256,9 +262,7 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr fixed_point_t weighted_value = 0, supply_consumption = 0; Good::good_map_t build_cost, supply_cost; - using enum Unit::type_t; - static const string_map_t<Unit::type_t> type_map = { { "land", LAND }, { "naval", NAVAL } }; - bool ret = expect_key("type", expect_identifier(expect_mapped_string(type_map, assign_variable_callback(type))))(value); + bool ret = expect_key("type", expect_identifier(expect_type_str(assign_variable_callback(type))))(value); if (!ret) { Logger::error("Failed to read type for unit: ", key); @@ -288,9 +292,9 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr ); switch (type) { - case LAND: { + case Unit::type_t::LAND: { bool primary_culture = false; - std::string_view sprite_override, sprite_mount, sprite_mount_attach_node; + std::string_view sprite_override {}, sprite_mount {}, sprite_mount_attach_node {}; fixed_point_t reconnaissance = 0, attack = 0, defence = 0, discipline = 0, support = 0, maneuver = 0, siege = 0; ret &= add_key_map_entries(key_map, @@ -313,7 +317,7 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr return ret; } - case NAVAL: { + case Unit::type_t::NAVAL: { Unit::icon_t naval_icon = 0; bool sail = false, transport = false, capital = false, build_overseas = false; uint32_t min_port_level = 0; diff --git a/src/openvic-simulation/military/Unit.hpp b/src/openvic-simulation/military/Unit.hpp index acfc8b8..ab371e8 100644 --- a/src/openvic-simulation/military/Unit.hpp +++ b/src/openvic-simulation/military/Unit.hpp @@ -169,6 +169,8 @@ namespace OpenVic { bool add_naval_unit(std::string_view identifier, UNIT_PARAMS, NAVY_PARAMS); IDENTIFIER_REGISTRY_ACCESSORS(unit) + static NodeTools::callback_t<std::string_view> expect_type_str(NodeTools::Callback<Unit::type_t> auto callback); + bool load_unit_file(GoodManager const& good_manager, ast::NodeCPtr root); }; } diff --git a/src/openvic-simulation/misc/Define.cpp b/src/openvic-simulation/misc/Define.cpp index 5e6a3cb..c866b5d 100644 --- a/src/openvic-simulation/misc/Define.cpp +++ b/src/openvic-simulation/misc/Define.cpp @@ -36,11 +36,11 @@ bool DefineManager::add_define(std::string_view name, std::string&& value, Defin return defines.add_item({ name, std::move(value), type }, duplicate_warning_callback); } -const Date& DefineManager::get_start_date() const { +Date DefineManager::get_start_date() const { return *start_date; } -const Date& DefineManager::get_end_date() const { +Date DefineManager::get_end_date() const { return *end_date; } diff --git a/src/openvic-simulation/misc/Define.hpp b/src/openvic-simulation/misc/Define.hpp index 0db9716..be71f9d 100644 --- a/src/openvic-simulation/misc/Define.hpp +++ b/src/openvic-simulation/misc/Define.hpp @@ -15,8 +15,8 @@ namespace OpenVic { enum class Type : unsigned char { None, Country, Economy, Military, Diplomacy, Pops, Ai, Graphics }; private: - std::string HASID_PROPERTY(value); - Type HASID_PROPERTY(type); + const std::string PROPERTY(value); + const Type PROPERTY(type); Define(std::string_view new_identifier, std::string&& new_value, Type new_type); @@ -40,10 +40,10 @@ namespace OpenVic { bool add_define(std::string_view name, std::string&& value, Define::Type type); bool add_date_define(std::string_view name, Date date); - IDENTIFIER_REGISTRY_ACCESSORS(define); + IDENTIFIER_REGISTRY_ACCESSORS(define) - const Date& get_start_date() const; - const Date& get_end_date() const; + Date get_start_date() const; + Date get_end_date() const; bool load_defines_file(ast::NodeCPtr root); }; diff --git a/src/openvic-simulation/politics/Government.cpp b/src/openvic-simulation/politics/Government.cpp index 823284a..3b0f28d 100644 --- a/src/openvic-simulation/politics/Government.cpp +++ b/src/openvic-simulation/politics/Government.cpp @@ -85,7 +85,7 @@ bool GovernmentTypeManager::load_government_types_file(IdeologyManager const& id [this, &ideology_manager, &ideologies, government_type_identifier]( std::string_view key, ast::NodeCPtr value) -> bool { static const string_set_t reserved_keys = { "election", "duration", "appoint_ruling_party", "flagType" }; - if (reserved_keys.find(key) != reserved_keys.end()) { + if (reserved_keys.contains(key)) { return true; } Ideology const* ideology = ideology_manager.get_ideology_by_identifier(key); diff --git a/src/openvic-simulation/politics/Ideology.cpp b/src/openvic-simulation/politics/Ideology.cpp index acbd82f..148d6ac 100644 --- a/src/openvic-simulation/politics/Ideology.cpp +++ b/src/openvic-simulation/politics/Ideology.cpp @@ -8,7 +8,7 @@ IdeologyGroup::IdeologyGroup(std::string_view new_identifier) : HasIdentifier { Ideology::Ideology( std::string_view new_identifier, colour_t new_colour, IdeologyGroup const& new_group, bool new_uncivilised, bool new_can_reduce_militancy, Date new_spawn_date -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group }, uncivilised { new_uncivilised }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group }, uncivilised { new_uncivilised }, can_reduce_militancy { new_can_reduce_militancy }, spawn_date { new_spawn_date } {} IdeologyGroup const& Ideology::get_group() const { @@ -23,7 +23,7 @@ bool Ideology::get_can_reduce_militancy() const { return can_reduce_militancy; } -Date const& Ideology::get_spawn_date() const { +Date Ideology::get_spawn_date() const { return spawn_date; } diff --git a/src/openvic-simulation/politics/Ideology.hpp b/src/openvic-simulation/politics/Ideology.hpp index 046dbc9..bdf9f91 100644 --- a/src/openvic-simulation/politics/Ideology.hpp +++ b/src/openvic-simulation/politics/Ideology.hpp @@ -36,7 +36,7 @@ namespace OpenVic { IdeologyGroup const& get_group() const; bool is_uncivilised() const; bool get_can_reduce_militancy() const; - Date const& get_spawn_date() const; + Date get_spawn_date() const; }; struct IdeologyManager { diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp index e386930..47501e0 100644 --- a/src/openvic-simulation/pop/Culture.cpp +++ b/src/openvic-simulation/pop/Culture.cpp @@ -28,7 +28,7 @@ bool CultureGroup::get_is_overseas() const { Culture::Culture( std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, std::vector<std::string>&& new_first_names, std::vector<std::string>&& new_last_names -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group }, first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) } {} CultureGroup const& Culture::get_group() const { @@ -174,7 +174,7 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) { return false; } - static const std::string default_unit_graphical_culture_type_identifier = "Generic"; + static constexpr std::string_view default_unit_graphical_culture_type_identifier = "Generic"; GraphicalCultureType const* const default_unit_graphical_culture_type = get_graphical_culture_type_by_identifier(default_unit_graphical_culture_type_identifier); if (default_unit_graphical_culture_type == nullptr) { @@ -195,7 +195,7 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) { CultureGroup const* culture_group = get_culture_group_by_identifier(culture_group_key); return expect_dictionary([this, culture_group](std::string_view key, ast::NodeCPtr value) -> bool { static const string_set_t reserved_keys = { "leader", "unit", "union", "is_overseas" }; - if (reserved_keys.find(key) != reserved_keys.end()) { + if (reserved_keys.contains(key)) { return true; } return _load_culture(culture_group, key, value); diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index f63a704..0b61096 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -52,7 +52,7 @@ PopType::PopType( Good::good_map_t&& new_life_needs, Good::good_map_t&& new_everyday_needs, Good::good_map_t&& new_luxury_needs, rebel_units_t&& new_rebel_units, Pop::pop_size_t new_max_size, Pop::pop_size_t new_merge_max_size, bool new_state_capital_only, bool new_demote_migrant, bool new_is_artisan, bool new_is_slave -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, strata { new_strata }, sprite { new_sprite }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, strata { new_strata }, sprite { new_sprite }, life_needs { std::move(new_life_needs) }, everyday_needs { std::move(new_everyday_needs) }, luxury_needs { std::move(new_luxury_needs) }, rebel_units { std::move(new_rebel_units) }, max_size { new_max_size }, merge_max_size { new_merge_max_size }, state_capital_only { new_state_capital_only }, diff --git a/src/openvic-simulation/pop/Religion.cpp b/src/openvic-simulation/pop/Religion.cpp index 489984d..99915c3 100644 --- a/src/openvic-simulation/pop/Religion.cpp +++ b/src/openvic-simulation/pop/Religion.cpp @@ -9,7 +9,7 @@ ReligionGroup::ReligionGroup(std::string_view new_identifier) : HasIdentifier { Religion::Religion( std::string_view new_identifier, colour_t new_colour, ReligionGroup const& new_group, icon_t new_icon, bool new_pagan -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group }, icon { new_icon }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group }, icon { new_icon }, pagan { new_pagan } { assert(icon > 0); } diff --git a/src/openvic-simulation/types/Date.cpp b/src/openvic-simulation/types/Date.cpp index 8fbb859..c5eac41 100644 --- a/src/openvic-simulation/types/Date.cpp +++ b/src/openvic-simulation/types/Date.cpp @@ -214,7 +214,7 @@ Date::operator std::string() const { return to_string(); } -std::ostream& OpenVic::operator<<(std::ostream& out, Date const& date) { +std::ostream& OpenVic::operator<<(std::ostream& out, Date date) { return out << static_cast<int>(date.getYear()) << Date::SEPARATOR_CHARACTER << static_cast<int>(date.getMonth()) << Date::SEPARATOR_CHARACTER << static_cast<int>(date.getDay()); } diff --git a/src/openvic-simulation/types/Date.hpp b/src/openvic-simulation/types/Date.hpp index 0cc2587..b6e693c 100644 --- a/src/openvic-simulation/types/Date.hpp +++ b/src/openvic-simulation/types/Date.hpp @@ -4,9 +4,11 @@ #include <ostream> #include <string> +#include "openvic-simulation/utility/Getters.hpp" + namespace OpenVic { // A relative period between points in time, measured in days - struct Timespan { + struct Timespan : ReturnByValueProperty { using day_t = int64_t; private: @@ -44,7 +46,7 @@ namespace OpenVic { // Represents an in-game date // Note: Current implementation does not account for leap-years, or dates before Year 0 - struct Date { + struct Date : ReturnByValueProperty { using year_t = uint16_t; using month_t = uint8_t; using day_t = uint8_t; @@ -96,5 +98,5 @@ namespace OpenVic { static Date from_string(char const* str, size_t length, bool* successful = nullptr, bool quiet = false); static Date from_string(std::string_view str, bool* successful = nullptr, bool quiet = false); }; - std::ostream& operator<<(std::ostream& out, Date const& date); + std::ostream& operator<<(std::ostream& out, Date date); } diff --git a/src/openvic-simulation/types/IdentifierRegistry.cpp b/src/openvic-simulation/types/IdentifierRegistry.cpp index faddd75..3964b12 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.cpp +++ b/src/openvic-simulation/types/IdentifierRegistry.cpp @@ -8,10 +8,6 @@ HasIdentifier::HasIdentifier(std::string_view new_identifier) : identifier { new assert(!identifier.empty()); } -std::string_view HasIdentifier::get_identifier() const { - return identifier; -} - std::ostream& OpenVic::operator<<(std::ostream& stream, HasIdentifier const& obj) { return stream << obj.get_identifier(); } @@ -20,12 +16,8 @@ std::ostream& OpenVic::operator<<(std::ostream& stream, HasIdentifier const* obj return obj != nullptr ? stream << *obj : stream << "<NULL>"; } -HasColour::HasColour(colour_t const new_colour, bool can_be_null, bool can_have_alpha) : colour(new_colour) { - assert((can_be_null || colour != NULL_COLOUR) && colour <= (!can_have_alpha ? MAX_COLOUR_RGB : MAX_COLOUR_ARGB)); -} - -colour_t HasColour::get_colour() const { - return colour; +HasColour::HasColour(colour_t new_colour, bool cannot_be_null, bool can_have_alpha) : colour(new_colour) { + assert((!cannot_be_null || colour != NULL_COLOUR) && colour <= (!can_have_alpha ? MAX_COLOUR_RGB : MAX_COLOUR_ARGB)); } std::string HasColour::colour_to_hex_string() const { @@ -33,5 +25,5 @@ std::string HasColour::colour_to_hex_string() const { } HasIdentifierAndColour::HasIdentifierAndColour( - std::string_view new_identifier, const colour_t new_colour, bool can_be_null, bool can_have_alpha -) : HasIdentifier { new_identifier }, HasColour { new_colour, can_be_null, can_have_alpha } {} + std::string_view new_identifier, colour_t new_colour, bool cannot_be_null, bool can_have_alpha +) : HasIdentifier { new_identifier }, HasColour { new_colour, cannot_be_null, can_have_alpha } {} diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index bcb8e33..1121956 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -6,24 +6,31 @@ #include <vector> #include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Logger.hpp" -#define REF_GETTERS(var) \ - constexpr decltype(var)& get_##var() { \ - return var; \ - } \ - constexpr decltype(var) const& get_##var() const { \ - return var; \ +namespace OpenVic { + + constexpr bool valid_basic_identifier_char(char c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_'; + } + constexpr bool valid_basic_identifier(std::string_view identifier) { + return std::all_of(identifier.begin(), identifier.end(), valid_basic_identifier_char); + } + constexpr std::string_view extract_basic_identifier_prefix(std::string_view identifier) { + size_t len = 0; + while (len < identifier.size() && valid_basic_identifier_char(identifier[len])) { + ++len; + } + return { identifier.data(), len }; } -namespace OpenVic { /* - * Base class for objects with a non-empty string identifier, - * uniquely named instances of which can be entered into an - * IdentifierRegistry instance. + * Base class for objects with a non-empty string identifier. Uniquely named instances of a type derived from this class + * can be entered into an IdentifierRegistry instance. */ class HasIdentifier { - const std::string identifier; + const std::string PROPERTY(identifier); protected: HasIdentifier(std::string_view new_identifier); @@ -33,27 +40,6 @@ namespace OpenVic { HasIdentifier(HasIdentifier&&) = default; HasIdentifier& operator=(HasIdentifier const&) = delete; HasIdentifier& operator=(HasIdentifier&&) = delete; - - std::string_view get_identifier() const; - - template<typename T> - inline constexpr static decltype(auto) get_property(const T& property) { - if constexpr (std::same_as<T, std::string>) { - return std::string_view(property); - } else if constexpr (sizeof(T) <= sizeof(void*)) { - return T(property); - } else { - return property; - } - } - -#define HASID_PROPERTY(NAME) \ - const NAME; \ -public: \ - auto get_##NAME() const->decltype(get_property(NAME)) { \ - return get_property(NAME); \ - } \ -private: }; std::ostream& operator<<(std::ostream& stream, HasIdentifier const& obj); @@ -63,10 +49,10 @@ private: * Base class for objects with associated colour information. */ class HasColour { - const colour_t colour; + const colour_t PROPERTY(colour); protected: - HasColour(const colour_t new_colour, bool can_be_null, bool can_have_alpha); + HasColour(colour_t new_colour, bool cannot_be_null, bool can_have_alpha); public: HasColour(HasColour const&) = delete; @@ -74,19 +60,15 @@ private: HasColour& operator=(HasColour const&) = delete; HasColour& operator=(HasColour&&) = delete; - colour_t get_colour() const; std::string colour_to_hex_string() const; }; /* - * Base class for objects with a unique string identifier - * and associated colour information. + * Base class for objects with a unique string identifier and associated colour information. */ class HasIdentifierAndColour : public HasIdentifier, public HasColour { protected: - HasIdentifierAndColour( - std::string_view new_identifier, const colour_t new_colour, bool can_be_null, bool can_have_alpha - ); + HasIdentifierAndColour(std::string_view new_identifier, colour_t new_colour, bool cannot_be_null, bool can_have_alpha); public: HasIdentifierAndColour(HasIdentifierAndColour const&) = delete; @@ -111,8 +93,6 @@ private: } } - using distribution_t = decimal_map_t<HasIdentifierAndColour const*>; - /* Callbacks for trying to add duplicate keys via UniqueKeyRegistry::add_item */ static bool duplicate_fail_callback(std::string_view registry_name, std::string_view duplicate_identifier) { Logger::error( @@ -180,9 +160,10 @@ private: return duplicate_callback(name, new_identifier); } } - identifier_index_map.emplace(new_identifier, items.size()); - items.push_back(std::move(item)); - return true; + const std::pair<string_map_t<size_t>::iterator, bool> ret = + identifier_index_map.emplace(std::move(new_identifier), items.size()); + items.emplace_back(std::move(item)); + return ret.second && ret.first->second + 1 == items.size(); } void lock() { @@ -222,20 +203,20 @@ private: } } -#define GETTERS \ - value_type _const* get_item_by_identifier(std::string_view identifier) _const { \ +#define GETTERS(CONST) \ + value_type CONST* get_item_by_identifier(std::string_view identifier) CONST { \ const typename decltype(identifier_index_map)::const_iterator it = identifier_index_map.find(identifier); \ if (it != identifier_index_map.end()) { \ return GetPointer(items[it->second]); \ } \ return nullptr; \ } \ - value_type _const* get_item_by_index(size_t index) _const { \ + value_type CONST* get_item_by_index(size_t index) CONST { \ return index < items.size() ? &items[index] : nullptr; \ } \ - NodeTools::callback_t<std::string_view> expect_item_str(NodeTools::callback_t<value_type _const&> callback) _const { \ + NodeTools::callback_t<std::string_view> expect_item_str(NodeTools::callback_t<value_type CONST&> callback) CONST { \ return [this, callback](std::string_view identifier) -> bool { \ - value_type _const* item = get_item_by_identifier(identifier); \ + value_type CONST* item = get_item_by_identifier(identifier); \ if (item != nullptr) { \ return callback(*item); \ } \ @@ -243,22 +224,27 @@ private: return false; \ }; \ } \ - NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<value_type _const&> callback) _const { \ + NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t<value_type CONST&> callback) CONST { \ return NodeTools::expect_identifier(expect_item_str(callback)); \ } \ - NodeTools::node_callback_t expect_item_dictionary(NodeTools::callback_t<value_type _const&, ast::NodeCPtr> callback) \ - _const { \ + NodeTools::node_callback_t expect_item_dictionary( \ + NodeTools::callback_t<value_type CONST&, ast::NodeCPtr> callback \ + ) CONST { \ return NodeTools::expect_dictionary([this, callback](std::string_view key, ast::NodeCPtr value) -> bool { \ return expect_item_str(std::bind(callback, std::placeholders::_1, value))(key); \ }); \ } -#define _const - GETTERS -#undef _const -#define _const const - GETTERS -#undef _const +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4003) +#endif + GETTERS() + GETTERS(const) + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif #undef GETTERS @@ -280,8 +266,9 @@ private: return identifiers; } - NodeTools::node_callback_t expect_item_decimal_map(NodeTools::callback_t<decimal_map_t<value_type const*>&&> callback) - const { + NodeTools::node_callback_t expect_item_decimal_map( + NodeTools::callback_t<decimal_map_t<value_type const*>&&> callback + ) const { return [this, callback](ast::NodeCPtr node) -> bool { decimal_map_t<value_type const*> map; bool ret = expect_item_dictionary([&map](value_type const& key, ast::NodeCPtr value) -> bool { @@ -390,7 +377,9 @@ private: ) { \ return plural.expect_item_str(callback); \ } \ - NodeTools::node_callback_t expect_##singular##_identifier(NodeTools::callback_t<decltype(plural)::value_type&> callback) { \ + NodeTools::node_callback_t expect_##singular##_identifier( \ + NodeTools::callback_t<decltype(plural)::value_type&> callback \ + ) { \ return plural.expect_item_identifier(callback); \ } \ NodeTools::node_callback_t expect_##singular##_dictionary( \ diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp index 4bf5716..ba6790f 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp @@ -8,6 +8,7 @@ #include <sstream> #include <string_view> +#include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Logger.hpp" #include "openvic-simulation/utility/NumberUtils.hpp" #include "openvic-simulation/utility/StringUtils.hpp" @@ -15,7 +16,7 @@ #include "FixedPointLUT.hpp" namespace OpenVic { - struct fixed_point_t { + struct fixed_point_t : ReturnByValueProperty { static constexpr size_t SIZE = 8; static constexpr int32_t PRECISION = FPLUT::SIN_LUT_PRECISION; diff --git a/src/openvic-simulation/utility/Getters.hpp b/src/openvic-simulation/utility/Getters.hpp new file mode 100644 index 0000000..c8f2193 --- /dev/null +++ b/src/openvic-simulation/utility/Getters.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include <concepts> +#include <string> +#include <string_view> + +#define REF_GETTERS(var) \ + constexpr decltype(var)& get_##var() { \ + return var; \ + } \ + constexpr decltype(var) const& get_##var() const { \ + return var; \ + } + +namespace OpenVic { + struct ReturnByValueProperty {}; + + /* + * Template function used to choose the return type and provide the implementation for the + * for variable getters created using the PROPERTY macro. + */ + template<typename decl, typename T> + inline constexpr decltype(auto) _get_property(const T& property) { + if constexpr(std::is_reference_v<decl>) { + /* Return const reference */ + return property; + } else if constexpr (std::same_as<T, std::string>) { + /* Return std::string_view looking at std::string */ + return std::string_view { property }; + } else if constexpr ( + std::integral<T> || std::floating_point<T> || std::is_enum<T>::value || std::derived_from<T, ReturnByValueProperty> + ) { + /* Return value */ + return T { property }; + } else if constexpr(std::is_pointer<T>::value) { + /* Return const pointer */ + return static_cast<std::add_pointer_t<std::add_const_t<std::remove_pointer_t<T>>>>(property); + } else { + /* Return const reference */ + return property; + } + } + +/* + * Use this on a variable delcaration to generate a getter function. It assumes the variable is private and so + * sets the accessibility modifier state back to private after declaring the getter as public. + * Examples: + * int PROPERTY(x); // int x; int get_x() const; + * const std::string PROPERTY(name); // const std::string name; std::string_view get_name() const; + * std::vector<int> PROPERTY(sizes); // std::vector<int> sizes; std::vector<int> const& get_sizes() const; + * uint8_t const* PROPERTY(data); // uint8_t const* data; uint8_t const* get_data() const; + * colour_t* PROPERTY(pixels); // colour_t* pixels; colour_t const* get_pixels() const; + * CultureGroup const& PROPERTY(group);// CultureGroup const& group; CultureGroup const& get_group() const; + * Province& PROPERTY(province); // Province& province; Province const& get_province() const; + */ +#define PROPERTY(NAME) \ + NAME; \ +\ +public: \ + auto get_##NAME() const -> decltype(OpenVic::_get_property<decltype(NAME)>(NAME)) { \ + return OpenVic::_get_property<decltype(NAME)>(NAME); \ + } \ +\ +private: + +} diff --git a/src/openvic-simulation/utility/Logger.cpp b/src/openvic-simulation/utility/Logger.cpp deleted file mode 100644 index 5e25c98..0000000 --- a/src/openvic-simulation/utility/Logger.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "Logger.hpp" - -#include <iostream> - -using namespace OpenVic; - -void Logger::set_logger_funcs() { - Logger::set_info_func([](std::string&& str) { - std::cout << str; - }); - Logger::set_warning_func([](std::string&& str) { - std::cerr << str; - }); - Logger::set_error_func([](std::string&& str) { - std::cerr << str; - }); -} - -char const* Logger::get_filename(char const* filepath, char const* default_path) { - if (filepath == nullptr) { - return default_path; - } - char const* last_slash = filepath; - while (*filepath != '\0') { - if (*filepath == '\\' || *filepath == '/') { - last_slash = filepath + 1; - } - filepath++; - } - if (*last_slash == '\0') { - return default_path; - } - return last_slash; -} diff --git a/src/openvic-simulation/utility/Logger.hpp b/src/openvic-simulation/utility/Logger.hpp index a1c599d..20c7fdd 100644 --- a/src/openvic-simulation/utility/Logger.hpp +++ b/src/openvic-simulation/utility/Logger.hpp @@ -1,12 +1,16 @@ #pragma once #include <functional> +#include <iostream> #include <queue> #include <sstream> + #ifdef __cpp_lib_source_location #include <source_location> #endif +#include "openvic-simulation/utility/StringUtils.hpp" + namespace OpenVic { #ifndef __cpp_lib_source_location @@ -52,8 +56,17 @@ namespace OpenVic { #endif public: - static void set_logger_funcs(); - static char const* get_filename(char const* filepath, char const* default_path = nullptr); + static void set_logger_funcs() { + set_info_func([](std::string&& str) { + std::cout << "[INFO] " << str; + }); + set_warning_func([](std::string&& str) { + std::cerr << "[WARNING] " << str; + }); + set_error_func([](std::string&& str) { + std::cerr << "[ERROR] " << str; + }); + } private: struct log_channel_t { @@ -65,7 +78,7 @@ namespace OpenVic { struct log { log(log_channel_t& log_channel, Ts&&... ts, source_location const& location) { std::stringstream stream; - stream << "\n" << get_filename(location.file_name()) << "(" + stream << StringUtils::get_filename(location.file_name()) << "(" /* Function name removed to reduce clutter. It is already included * in Godot's print functions, so this was repeating it. */ //<< location.line() << ") `" << location.function_name() << "`: "; @@ -83,20 +96,21 @@ namespace OpenVic { }; #define LOG_FUNC(name) \ - private: \ - static inline log_channel_t name##_channel {}; \ - public: \ - static inline void set_##name##_func(log_func_t log_func) { \ - name##_channel.func = log_func; \ +private: \ + static inline log_channel_t name##_channel {}; \ +\ +public: \ + static inline void set_##name##_func(log_func_t log_func) { \ + name##_channel.func = log_func; \ + } \ + template<typename... Ts> \ + struct name { \ + name(Ts&&... ts, source_location const& location = source_location::current()) { \ + log<Ts...> { name##_channel, std::forward<Ts>(ts)..., location }; \ } \ - template<typename... Ts> \ - struct name { \ - name(Ts&&... ts, source_location const& location = source_location::current()) { \ - log<Ts...> { name##_channel, std::forward<Ts>(ts)..., location }; \ - } \ - }; \ - template<typename... Ts> \ - name(Ts&&...) -> name<Ts...>; + }; \ + template<typename... Ts> \ + name(Ts&&...) -> name<Ts...>; LOG_FUNC(info) LOG_FUNC(warning) diff --git a/src/openvic-simulation/utility/StringUtils.hpp b/src/openvic-simulation/utility/StringUtils.hpp index d968bf6..ede1d6b 100644 --- a/src/openvic-simulation/utility/StringUtils.hpp +++ b/src/openvic-simulation/utility/StringUtils.hpp @@ -1,4 +1,7 @@ +#pragma once + #include <cstdint> +#include <cstring> #include <limits> #include <string_view> @@ -90,7 +93,7 @@ namespace OpenVic::StringUtils { return result; } - constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { + inline constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { return string_to_uint64(str, str + length, successful, base); } @@ -135,11 +138,72 @@ namespace OpenVic::StringUtils { } } - constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { + inline constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { return string_to_int64(str, str + length, successful, base); } inline int64_t string_to_int64(std::string_view str, bool* successful = nullptr, int base = 10) { return string_to_int64(str.data(), str.length(), successful, base); } + + inline constexpr std::string_view get_filename(std::string_view path) { + size_t pos = path.size(); + while (pos > 0 && path[pos - 1] != '/' && path[pos - 1] != '\\') { + --pos; + } + path.remove_prefix(pos); + return path; + } + + inline constexpr char const* get_filename(char const* filepath, char const* default_path = nullptr) { + const std::string_view filename { get_filename(std::string_view { filepath }) }; + if (!filename.empty()) { + return filename.data(); + } + return default_path; + } + + inline std::string make_forward_slash_path(std::string_view path) { + std::string ret { path }; + std::replace(ret.begin(), ret.end(), '\\', '/'); + for (char& c : ret) { + if (c == '\\') { + c = '/'; + } + } + return ret; + } + + inline constexpr std::string_view remove_leading_slashes(std::string_view path) { + size_t count = 0; + while (count < path.size() && (path[count] == '/' || path[count] == '\\')) { + ++count; + } + path.remove_prefix(count); + return path; + } + + template<typename... Args> + requires (std::is_same_v<std::string_view, Args> && ...) + inline std::string _append_string_views(Args... args) { + std::string ret; + ret.reserve((args.size() + ...)); + (ret.append(args), ...); + return ret; + } + + template<typename... Args> + requires (std::is_convertible_v<Args, std::string_view> && ...) + inline std::string append_string_views(Args... args) { + return _append_string_views(std::string_view { args }...); + } + + inline constexpr std::string_view remove_extension(std::string_view path) { + size_t pos = path.size(); + while (pos > 0 && path[--pos] != '.') {} + if (path[pos] == '.') { + path.remove_suffix(path.size() - pos); + } + return path; + } } |