diff options
author | hop311 <hop3114@gmail.com> | 2024-01-17 23:24:39 +0100 |
---|---|---|
committer | hop311 <hop3114@gmail.com> | 2024-01-18 23:29:40 +0100 |
commit | e33a330129364b4bd632b2fd531a996b8c57cefb (patch) | |
tree | 88365260addebee9d3ac798d484e02ab18f56692 | |
parent | b5783116f67c9f7aa9d8d9653e6bda0227f67cd2 (diff) |
Parse missing variables, Logger counting, misc cleanup
25 files changed, 745 insertions, 276 deletions
diff --git a/src/headless/main.cpp b/src/headless/main.cpp index 7f33314..a6c4914 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -131,9 +131,10 @@ int main(int argc, char const* argv[]) { return -1; } } - Dataloader::path_vector_t roots = { root }; + Dataloader::path_vector_t roots { root }; while (argn < argc) { - roots.emplace_back(root / argv[argn++]); + static const fs::path mod_directory = "mod"; + roots.emplace_back(root / mod_directory / argv[argn++]); } std::cout << "!!! HEADLESS SIMULATION START !!!" << std::endl; @@ -144,5 +145,8 @@ int main(int argc, char const* argv[]) { std::cout << "\nLoad returned: " << (ret ? "SUCCESS" : "FAILURE") << std::endl; + std::cout << "\nLogger Summary: Info = " << Logger::get_info_count() << ", Warning = " << Logger::get_warning_count() + << ", Error = " << Logger::get_error_count() << std::endl; + return ret ? 0 : -1; } diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp index 50ec2b2..6435f05 100644 --- a/src/openvic-simulation/GameManager.cpp +++ b/src/openvic-simulation/GameManager.cpp @@ -83,13 +83,24 @@ bool GameManager::expand_selected_province_building(size_t building_index) { return province->expand_building(building_index); } -static constexpr colour_argb_t::value_type ALPHA_VALUE = colour_argb_t::colour_traits::alpha_from_float(0.7f); +static constexpr colour_argb_t::value_type ALPHA_VALUE = colour_argb_t::max_value; +/* White default colour, used in mapmodes including political, revolt risk and party loyaly. */ +static constexpr colour_argb_t DEFAULT_COLOUR_WHITE = (0xFFFFFF_argb).with_alpha(ALPHA_VALUE); +/* Grey default colour, used in mapmodes including diplomatic, administrative and colonial, recruitment, + * national focus, RGO, population density, sphere of influence, ranking and migration. */ +static constexpr colour_argb_t DEFAULT_COLOUR_GREY = (0x7F7F7F_argb).with_alpha(ALPHA_VALUE); template<IsColour ColourT = colour_t, std::derived_from<_HasColour<ColourT>> T> static constexpr auto get_colour_mapmode(T const*(Province::*get_item)() const) { return [get_item](Map const& map, Province const& province) -> Mapmode::base_stripe_t { T const* item = (province.*get_item)(); - return item != nullptr ? colour_argb_t { item->get_colour(), ALPHA_VALUE } : colour_argb_t::null(); + if (item != nullptr) { + return colour_argb_t { item->get_colour(), ALPHA_VALUE }; + } else if (!province.is_water()) { + return DEFAULT_COLOUR_WHITE; + } else { + return colour_argb_t::null(); + } }; } @@ -133,6 +144,7 @@ bool GameManager::load_hardcoded_defines() { "mapmode_political", get_colour_mapmode(&Province::get_owner) }, { + /* TEST MAPMODE, TO BE REMOVED */ "mapmode_province", [](Map const&, Province const& province) -> Mapmode::base_stripe_t { return colour_argb_t { province.get_colour(), ALPHA_VALUE }; @@ -142,6 +154,7 @@ bool GameManager::load_hardcoded_defines() { "mapmode_region", get_colour_mapmode(&Province::get_region) }, { + /* TEST MAPMODE, TO BE REMOVED */ "mapmode_index", [](Map const& map, Province const& province) -> Mapmode::base_stripe_t { const colour_argb_t::value_type f = @@ -150,6 +163,7 @@ bool GameManager::load_hardcoded_defines() { } }, { + /* Non-vanilla mapmode, still of use in game. */ "mapmode_terrain_type", get_colour_mapmode(&Province::get_terrain_type) }, { @@ -191,9 +205,11 @@ bool GameManager::load_hardcoded_defines() { "mapmode_culture", shaded_mapmode(&Province::get_culture_distribution) }, { + /* Non-vanilla mapmode, still of use in game. */ "mapmode_religion", shaded_mapmode(&Province::get_religion_distribution) }, { + /* TEST MAPMODE, TO BE REMOVED */ "mapmode_adjacencies", [](Map const& map, Province const& province) -> Mapmode::base_stripe_t { Province const* selected_province = map.get_selected_province(); if (selected_province != nullptr) { @@ -228,7 +244,13 @@ bool GameManager::load_hardcoded_defines() { }, { "mapmode_port", [](Map const& map, Province const& province) -> Mapmode::base_stripe_t { - return province.has_port() ? (0xFFFFFF_argb).with_alpha(ALPHA_VALUE) : colour_argb_t::null(); + if (province.has_port()) { + return (0xFFFFFF_argb).with_alpha(ALPHA_VALUE); + } else if (!province.is_water()) { + return (0x333333_argb).with_alpha(ALPHA_VALUE); + } else { + return colour_argb_t::null(); + } } } }; diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 0078ed2..b5a32d3 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -791,12 +791,6 @@ bool Dataloader::load_defines(GameManager& game_manager) { Logger::error("Failed to load graphical culture types!"); ret = false; } - if (!game_manager.get_pop_manager().get_culture_manager().load_culture_file( - parse_defines(lookup_file(culture_file)).get_file_node() - )) { - Logger::error("Failed to load cultures!"); - ret = false; - } if (!game_manager.get_pop_manager().get_religion_manager().load_religion_file( parse_defines(lookup_file(religion_file)).get_file_node() )) { @@ -928,6 +922,12 @@ bool Dataloader::load_defines(GameManager& game_manager) { Logger::error("Failed to load countries!"); ret = false; } + if (!game_manager.get_pop_manager().get_culture_manager().load_culture_file( + game_manager.get_country_manager(), parse_defines(lookup_file(culture_file)).get_file_node() + )) { + Logger::error("Failed to load cultures!"); + ret = false; + } if (!_load_decisions(game_manager)) { Logger::error("Failde to load decisions!"); ret = false; diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index b3fce16..4b33c6d 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -356,33 +356,46 @@ namespace OpenVic { }; } + template<typename... Args> + bool warn_or_error(bool warn, Args... args) { + if (warn) { + Logger::warning(args...); + return true; + } else { + Logger::error(args...); + return false; + } + } + template<typename T, typename U, typename...SetArgs> - Callback<T> auto set_callback(tsl::ordered_set<U, SetArgs...>& set) { - return [&set](T val) -> bool { - if (!set.emplace(std::move(val)).second) { - Logger::warning("Duplicate set entry: \"", val, "\""); + Callback<T> auto set_callback(tsl::ordered_set<U, SetArgs...>& set, bool warn = false) { + return [&set, warn](T val) -> bool { + if (set.emplace(std::move(val)).second) { + return true; } - return true; + return warn_or_error(warn, "Duplicate set entry: \"", val, "\""); }; } template<std::derived_from<HasIdentifier> T, typename...SetArgs> - Callback<T const&> auto set_callback_pointer(tsl::ordered_set<T const*, SetArgs...>& set) { - return [&set](T const& val) -> bool { - if (!set.emplace(&val).second) { - Logger::warning("Duplicate set entry: \"", &val, "\""); + Callback<T const&> auto set_callback_pointer(tsl::ordered_set<T const*, SetArgs...>& set, bool warn = false) { + return [&set, warn](T const& val) -> bool { + if (set.emplace(&val).second) { + return true; } - return true; + return warn_or_error(warn, "Duplicate set entry: \"", &val, "\""); }; } template<std::derived_from<HasIdentifier> Key, typename Value, typename... MapArgs> - Callback<Value> auto map_callback(tsl::ordered_map<Key const*, Value, MapArgs...>& map, Key const* key) { - return [&map, key](Value value) -> bool { - if (!map.emplace(key, std::move(value)).second) { - Logger::warning("Duplicate map entry with key: \"", key, "\""); + Callback<Value> auto map_callback( + tsl::ordered_map<Key const*, Value, MapArgs...>& map, Key const* key, bool warn = false + ) { + return [&map, key, warn](Value value) -> bool { + if (map.emplace(key, std::move(value)).second) { + return true; } - return true; + return warn_or_error(warn, "Duplicate map entry with key: \"", key, "\""); }; } } diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp index 7254510..8f659c6 100644 --- a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp +++ b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp @@ -57,7 +57,6 @@ namespace OpenVic { using allowed_to_cancel_func = FunctionRef<bool(const Argument&)>; - static bool allowed_to_cancel_default(const Argument& argument) { return true; } diff --git a/src/openvic-simulation/economy/BuildingType.cpp b/src/openvic-simulation/economy/BuildingType.cpp index c9ed410..e06beea 100644 --- a/src/openvic-simulation/economy/BuildingType.cpp +++ b/src/openvic-simulation/economy/BuildingType.cpp @@ -97,13 +97,13 @@ bool BuildingTypeManager::load_buildings_file( lock_building_types(); for (BuildingType const& building_type : building_types.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_type.get_identifier()), true, ModifierEffect::format_t::INT + static constexpr std::string_view max_prefix = "max_"; + static constexpr std::string_view min_prefix = "min_build_"; + ret &= modifier_manager.add_modifier_effect( + StringUtils::append_string_views(max_prefix, building_type.get_identifier()), true, ModifierEffect::format_t::INT ); - modifier_manager.add_modifier_effect( - min_modifier_prefix.append(building_type.get_identifier()), false, ModifierEffect::format_t::INT + ret &= modifier_manager.add_modifier_effect( + StringUtils::append_string_views(min_prefix, building_type.get_identifier()), false, ModifierEffect::format_t::INT ); if (building_type.is_in_province()) { diff --git a/src/openvic-simulation/economy/ProductionType.cpp b/src/openvic-simulation/economy/ProductionType.cpp index 61ec27f..86f3c13 100644 --- a/src/openvic-simulation/economy/ProductionType.cpp +++ b/src/openvic-simulation/economy/ProductionType.cpp @@ -4,23 +4,30 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; Job::Job( - PopType const* const new_pop_type, effect_t new_effect_type, fixed_point_t new_effect_multiplier, + PopType const* new_pop_type, effect_t new_effect_type, fixed_point_t new_effect_multiplier, fixed_point_t new_desired_workforce_share -) - : pop_type { new_pop_type }, effect_type { new_effect_type }, effect_multiplier { new_effect_multiplier }, - desired_workforce_share { new_desired_workforce_share } {} +) : pop_type { new_pop_type }, effect_type { new_effect_type }, effect_multiplier { new_effect_multiplier }, + desired_workforce_share { new_desired_workforce_share } {} ProductionType::ProductionType( - std::string_view new_identifier, Job new_owner, std::vector<Job> new_jobs, template_type_t new_template_type, - Pop::pop_size_t new_base_workforce_size, Good::good_map_t&& new_input_goods, Good const* const new_output_goods, - fixed_point_t new_base_output_quantity, std::vector<bonus_t>&& new_bonuses, Good::good_map_t&& new_maintenance_requirements, - bool new_is_coastal, bool new_is_farm, bool new_is_mine -) - : HasIdentifier { new_identifier }, owner { new_owner }, jobs { new_jobs }, template_type { new_template_type }, - base_workforce_size { new_base_workforce_size }, input_goods { std::move(new_input_goods) }, - output_goods { new_output_goods }, base_output_quantity { new_base_output_quantity }, bonuses { std::move(new_bonuses) }, - maintenance_requirements { std::move(new_maintenance_requirements) }, coastal { new_is_coastal }, farm { new_is_farm }, - mine { new_is_mine } {} + std::string_view new_identifier, + std::optional<Job> new_owner, + std::vector<Job>&& new_jobs, + template_type_t new_template_type, + Pop::pop_size_t new_base_workforce_size, + Good::good_map_t&& new_input_goods, + Good const* new_output_goods, + fixed_point_t new_base_output_quantity, + std::vector<bonus_t>&& new_bonuses, + Good::good_map_t&& new_maintenance_requirements, + bool new_is_coastal, + bool new_is_farm, + bool new_is_mine +) : HasIdentifier { new_identifier }, owner { new_owner }, jobs { std::move(new_jobs) }, template_type { new_template_type }, + base_workforce_size { new_base_workforce_size }, input_goods { std::move(new_input_goods) }, + output_goods { new_output_goods }, base_output_quantity { new_base_output_quantity }, bonuses { std::move(new_bonuses) }, + maintenance_requirements { std::move(new_maintenance_requirements) }, coastal { new_is_coastal }, farm { new_is_farm }, + mine { new_is_mine } {} bool ProductionType::parse_scripts(GameManager const& game_manager) { bool ret = true; @@ -33,10 +40,9 @@ bool ProductionType::parse_scripts(GameManager const& game_manager) { ProductionTypeManager::ProductionTypeManager() : rgo_owner_sprite { 0 } {} node_callback_t ProductionTypeManager::_expect_job( - GoodManager const& good_manager, PopManager const& pop_manager, callback_t<Job&&> cb + GoodManager const& good_manager, PopManager const& pop_manager, callback_t<Job&&> callback ) { - - return [this, &good_manager, &pop_manager, cb](ast::NodeCPtr node) -> bool { + return [this, &good_manager, &pop_manager, callback](ast::NodeCPtr node) -> bool { using enum Job::effect_t; std::string_view pop_type {}; @@ -54,27 +60,36 @@ node_callback_t ProductionTypeManager::_expect_job( "amount", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(desired_workforce_share)) )(node); - PopType const* const found_pop_type = pop_manager.get_pop_type_by_identifier(pop_type); - return res & cb({ found_pop_type, effect_type, effect_multiplier, desired_workforce_share }); + PopType const* found_pop_type = pop_manager.get_pop_type_by_identifier(pop_type); + return res & callback({ found_pop_type, effect_type, effect_multiplier, desired_workforce_share }); }; } node_callback_t ProductionTypeManager::_expect_job_list( - GoodManager const& good_manager, PopManager const& pop_manager, callback_t<std::vector<Job>&&> cb + GoodManager const& good_manager, PopManager const& pop_manager, callback_t<std::vector<Job>&&> callback ) { - return [this, &good_manager, &pop_manager, cb](ast::NodeCPtr node) -> bool { + return [this, &good_manager, &pop_manager, callback](ast::NodeCPtr node) -> bool { std::vector<Job> jobs; bool ret = expect_list(_expect_job(good_manager, pop_manager, vector_callback(jobs)))(node); - ret &= cb(std::move(jobs)); + ret &= callback(std::move(jobs)); return ret; }; } bool ProductionTypeManager::add_production_type( - std::string_view identifier, Job owner, std::vector<Job> jobs, ProductionType::template_type_t template_type, - Pop::pop_size_t base_workforce_size, Good::good_map_t&& input_goods, Good const* const output_goods, - fixed_point_t base_output_quantity, std::vector<ProductionType::bonus_t>&& bonuses, - Good::good_map_t&& maintenance_requirements, bool is_coastal, bool is_farm, bool is_mine + std::string_view identifier, + std::optional<Job> owner, + std::vector<Job>&& jobs, + ProductionType::template_type_t template_type, + Pop::pop_size_t base_workforce_size, + Good::good_map_t&& input_goods, + Good const* output_goods, + fixed_point_t base_output_quantity, + std::vector<ProductionType::bonus_t>&& bonuses, + Good::good_map_t&& maintenance_requirements, + bool is_coastal, + bool is_farm, + bool is_mine ) { if (identifier.empty()) { Logger::error("Invalid production type identifier - empty!"); @@ -96,13 +111,30 @@ bool ProductionTypeManager::add_production_type( return false; } - if (template_type == ProductionType::template_type_t::ARTISAN) { - if (owner.get_pop_type() != nullptr || !jobs.empty()) { - Logger::warning("Artisanal production types don't use owner and employees. Effects are ignored."); + using enum ProductionType::template_type_t; + + if (template_type == ARTISAN) { + if (owner.has_value()) { + Logger::warning( + "Artisanal production type ", identifier, " should not have an owner - it is being ignored." + ); + owner.reset(); + } + + if (!jobs.empty()) { + Logger::warning( + "Artisanal production type ", identifier, " should not have employees - ", jobs.size(), " are being ignored." + ); + jobs.clear(); } } else { - if (owner.get_pop_type() == nullptr) { - Logger::error("Production type ", identifier, " lacks owner or has an invalid pop type."); + if (!owner.has_value()) { + Logger::error("Production type ", identifier, " is missing an owner."); + return false; + } + + if (owner->get_pop_type() == nullptr) { + Logger::error("Production type ", identifier, " owner has an invalid pop type."); return false; } @@ -120,12 +152,12 @@ bool ProductionTypeManager::add_production_type( } const bool ret = production_types.add_item({ - identifier, owner, jobs, template_type, base_workforce_size, std::move(input_goods), - output_goods, base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine + identifier, owner, std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_goods, + base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine }); - if (rgo_owner_sprite <= 0 && ret && template_type == ProductionType::template_type_t::RGO && owner.get_pop_type() != nullptr) { + if (rgo_owner_sprite <= 0 && ret && template_type == RGO && owner.has_value() && owner->get_pop_type() != nullptr) { /* Set rgo owner sprite to that of the first RGO owner we find. */ - rgo_owner_sprite = owner.get_pop_type()->get_sprite(); + rgo_owner_sprite = owner->get_pop_type()->get_sprite(); } return ret; } @@ -135,7 +167,7 @@ bool ProductionTypeManager::load_production_types_file( ) { size_t expected_types = 0; - // pass 1: find and store template identifiers + /* Pass #1: find and store template identifiers */ ordered_set<std::string_view> templates; ordered_map<std::string_view, std::string_view> template_target_map; bool ret = expect_dictionary( @@ -159,7 +191,7 @@ bool ProductionTypeManager::load_production_types_file( } )(root); - // pass 2: create and populate the template map + /* Pass #2: create and populate the template map */ ordered_map<std::string_view, ast::NodeCPtr> template_node_map; ret &= expect_dictionary( [this, &expected_types, &templates, &template_node_map](std::string_view key, ast::NodeCPtr value) -> bool { @@ -171,7 +203,7 @@ bool ProductionTypeManager::load_production_types_file( } )(root); - // pass 3: actually load production types + /* Pass #3: actually load production types */ production_types.reserve(production_types.size() + expected_types); ret &= expect_dictionary( [this, &good_manager, &pop_manager, &template_target_map, &template_node_map]( @@ -182,13 +214,13 @@ bool ProductionTypeManager::load_production_types_file( return true; } - Job owner {}; + std::optional<Job> owner; std::vector<Job> jobs; ProductionType::template_type_t template_type { FACTORY }; Good const* output_goods = nullptr; - Pop::pop_size_t base_workforce_size = 0; // 0 is a meaningless value -> unset + Pop::pop_size_t base_workforce_size = 0; Good::good_map_t input_goods, maintenance_requirements; - fixed_point_t base_output_quantity = 0; // 0 is a meaningless value -> unset + fixed_point_t base_output_quantity = 0; std::vector<ProductionType::bonus_t> bonuses; bool is_coastal = false, is_farm = false, is_mine = false; @@ -198,8 +230,8 @@ bool ProductionTypeManager::load_production_types_file( { "factory", FACTORY }, { "rgo", RGO }, { "artisan", ARTISAN } }; - const node_callback_t parse_node = expect_dictionary_keys( - "template", ZERO_OR_ONE, success_callback, + const auto parse_node = expect_dictionary_keys( + "template", ZERO_OR_ONE, success_callback, /* Already parsed using expect_key in Pass #1 above. */ "bonus", ZERO_OR_MORE, [&bonuses](ast::NodeCPtr bonus_node) -> bool { ConditionScript trigger { scope_t::STATE, scope_t::NO_SCOPE, scope_t::NO_SCOPE }; fixed_point_t bonus_value {}; @@ -223,7 +255,7 @@ bool ProductionTypeManager::load_production_types_file( "mine", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_mine)) ); - // apply template first + /* Check if this ProductionType has a template, and if so parse it. */ { const typename decltype(template_target_map)::const_iterator target_it = template_target_map.find(key); if (target_it != template_target_map.end()) { @@ -238,11 +270,12 @@ bool ProductionTypeManager::load_production_types_file( } } + /* Parse the ProductionType's own entries, over those of its template if necessary. */ ret &= parse_node(node); ret &= add_production_type( - key, owner, jobs, template_type, base_workforce_size, std::move(input_goods), output_goods, base_output_quantity, std::move(bonuses), - std::move(maintenance_requirements), is_coastal, is_farm, is_mine + key, owner, std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_goods, + base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine ); return ret; } diff --git a/src/openvic-simulation/economy/ProductionType.hpp b/src/openvic-simulation/economy/ProductionType.hpp index cc7d44b..3c0bf3f 100644 --- a/src/openvic-simulation/economy/ProductionType.hpp +++ b/src/openvic-simulation/economy/ProductionType.hpp @@ -21,7 +21,9 @@ namespace OpenVic { fixed_point_t PROPERTY(desired_workforce_share); Job( - PopType const* new_pop_type, effect_t new_effect_type, fixed_point_t new_effect_multiplier, + PopType const* new_pop_type, + effect_t new_effect_type, + fixed_point_t new_effect_multiplier, fixed_point_t new_desired_workforce_share ); @@ -37,7 +39,7 @@ namespace OpenVic { using bonus_t = std::pair<ConditionScript, fixed_point_t>; private: - const Job PROPERTY(owner); + const std::optional<Job> PROPERTY(owner); std::vector<Job> PROPERTY(jobs); const template_type_t PROPERTY(template_type); const Pop::pop_size_t PROPERTY(base_workforce_size); @@ -54,13 +56,23 @@ namespace OpenVic { const bool PROPERTY_CUSTOM_PREFIX(mine, is); ProductionType( - std::string_view new_identifier, Job new_owner, std::vector<Job> new_jobs, template_type_t new_template_type, - Pop::pop_size_t new_base_workforce_size, Good::good_map_t&& new_input_goods, Good const* new_output_goods, - fixed_point_t new_base_output_quantity, std::vector<bonus_t>&& new_bonuses, Good::good_map_t&& new_efficiency, bool new_is_coastal, - bool new_is_farm, bool new_is_mine + std::string_view new_identifier, + std::optional<Job> new_owner, + std::vector<Job>&& new_jobs, + template_type_t new_template_type, + Pop::pop_size_t new_base_workforce_size, + Good::good_map_t&& new_input_goods, + Good const* new_output_goods, + fixed_point_t new_base_output_quantity, + std::vector<bonus_t>&& new_bonuses, + Good::good_map_t&& new_maintenance_requirements, + bool new_is_coastal, + bool new_is_farm, + bool new_is_mine ); bool parse_scripts(GameManager const& game_manager); + public: ProductionType(ProductionType&&) = default; }; @@ -71,19 +83,29 @@ namespace OpenVic { PopType::sprite_t PROPERTY(rgo_owner_sprite); NodeTools::node_callback_t _expect_job( - GoodManager const& good_manager, PopManager const& pop_manager, NodeTools::callback_t<Job&&> cb + GoodManager const& good_manager, PopManager const& pop_manager, NodeTools::callback_t<Job&&> callback ); NodeTools::node_callback_t _expect_job_list( - GoodManager const& good_manager, PopManager const& pop_manager, NodeTools::callback_t<std::vector<Job>&&> cb + GoodManager const& good_manager, PopManager const& pop_manager, NodeTools::callback_t<std::vector<Job>&&> callback ); public: ProductionTypeManager(); bool add_production_type( - std::string_view identifier, Job owner, std::vector<Job> employees, ProductionType::template_type_t template_type, - Pop::pop_size_t workforce, Good::good_map_t&& input_goods, Good const* output_goods, fixed_point_t value, - std::vector<ProductionType::bonus_t>&& bonuses, Good::good_map_t&& efficiency, bool coastal, bool farm, bool mine + std::string_view identifier, + std::optional<Job> owner, + std::vector<Job>&& employees, + ProductionType::template_type_t template_type, + Pop::pop_size_t workforce, + Good::good_map_t&& input_goods, + Good const* output_goods, + fixed_point_t value, + std::vector<ProductionType::bonus_t>&& bonuses, + Good::good_map_t&& maintenance_requirements, + bool coastal, + bool farm, + bool mine ); bool load_production_types_file(GoodManager const& good_manager, PopManager const& pop_manager, ast::NodeCPtr root); diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp index 4117450..fd174a3 100644 --- a/src/openvic-simulation/history/ProvinceHistory.cpp +++ b/src/openvic-simulation/history/ProvinceHistory.cpp @@ -36,7 +36,11 @@ bool ProvinceHistoryMap::_load_history_entry( BuildingType const* building_type = building_type_manager.get_building_type_by_identifier(key); if (building_type != nullptr) { if (building_type->is_in_province()) { - return expect_uint<BuildingType::level_t>(map_callback(entry.province_buildings, building_type))(value); + return expect_uint<BuildingType::level_t>( + /* This is set to warn to prevent vanilla from always having errors because + * of a duplicate railroad entry in the 1861.1.1 history of Manchester (278). */ + map_callback(entry.province_buildings, building_type, true) + )(value); } else { Logger::error( "Attempted to add state building \"", building_type, "\" at top scope of province history for ", @@ -68,7 +72,7 @@ bool ProvinceHistoryMap::_load_history_entry( ), "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 + fixed_point_t amount = 0; /* PERCENTAGE_DECIMAL */ bool ret = expect_dictionary_keys( "ideology", ONE_EXACTLY, ideology_manager.expect_ideology_identifier( @@ -90,7 +94,7 @@ bool ProvinceHistoryMap::_load_history_entry( "building", ONE_EXACTLY, building_type_manager.expect_building_type_identifier( assign_variable_callback_pointer(building_type) ), - "upgrade", ZERO_OR_ONE, success_callback // doesn't appear to have an effect + "upgrade", ZERO_OR_ONE, success_callback /* Doesn't appear to have an effect */ )(node); if (building_type != nullptr) { if (!building_type->is_in_province()) { diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp index da860ad..d4a092b 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -7,9 +7,9 @@ using namespace OpenVic::NodeTools; 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 + LeaderTrait const* new_background, fixed_point_t new_prestige, std::string_view new_picture ) : name { new_name }, type { new_type }, date { new_date }, personality { new_personality }, background { new_background }, - prestige { new_prestige } {} + prestige { new_prestige }, picture { new_picture } {} Regiment::Regiment(std::string_view new_name, Unit const* new_type, Province const* new_home) : name { new_name }, type { new_type }, home { new_home } {} @@ -76,9 +76,10 @@ bool DeploymentManager::load_oob_file( LeaderTrait const* leader_personality = nullptr; LeaderTrait const* leader_background = nullptr; fixed_point_t leader_prestige = 0; + std::string_view picture {}; bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(leader_name)), + "name", ONE_EXACTLY, expect_identifier_or_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() @@ -86,7 +87,7 @@ bool DeploymentManager::load_oob_file( "background", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager() .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 + "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(picture)) )(node); if (!leader_personality->is_personality_trait()) { @@ -104,7 +105,7 @@ bool DeploymentManager::load_oob_file( ret = false; } leaders.emplace_back( - leader_name, leader_type, leader_date, leader_personality, leader_background, leader_prestige + leader_name, leader_type, leader_date, leader_personality, leader_background, leader_prestige, picture ); return ret; }, @@ -134,7 +135,7 @@ bool DeploymentManager::load_oob_file( 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 */ + /* 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.emplace_back(army_name, army_location, std::move(army_regiments)); @@ -160,7 +161,7 @@ bool DeploymentManager::load_oob_file( 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 */ + /* 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.emplace_back(navy_name, navy_location, std::move(navy_ships)); diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp index 2aef314..a952823 100644 --- a/src/openvic-simulation/military/Deployment.hpp +++ b/src/openvic-simulation/military/Deployment.hpp @@ -1,6 +1,5 @@ #pragma once -#include <filesystem> #include <string> #include <string_view> #include <vector> @@ -22,11 +21,12 @@ namespace OpenVic { LeaderTrait const* PROPERTY(personality); LeaderTrait const* PROPERTY(background); fixed_point_t PROPERTY(prestige); + std::string PROPERTY(picture); 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 + LeaderTrait const* new_background, fixed_point_t new_prestige, std::string_view new_picture ); }; diff --git a/src/openvic-simulation/military/Unit.cpp b/src/openvic-simulation/military/Unit.cpp index 9981c86..d0f6435 100644 --- a/src/openvic-simulation/military/Unit.cpp +++ b/src/openvic-simulation/military/Unit.cpp @@ -104,10 +104,10 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr } key_map_t key_map; - // shared + /* Shared dictionary entries */ ret &= add_key_map_entries(key_map, "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), - "type", ONE_EXACTLY, success_callback, + "type", ONE_EXACTLY, success_callback, /* Already loaded above using expect_key */ "sprite", ONE_EXACTLY, expect_identifier(assign_variable_callback(sprite)), "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(active)), "unit_type", ONE_EXACTLY, expect_identifier(assign_variable_callback(unit_type)), diff --git a/src/openvic-simulation/misc/Event.cpp b/src/openvic-simulation/misc/Event.cpp index d6750c1..1796799 100644 --- a/src/openvic-simulation/misc/Event.cpp +++ b/src/openvic-simulation/misc/Event.cpp @@ -169,7 +169,7 @@ bool EventManager::load_event_file(IssueManager const& issue_manager, ast::NodeC }; bool ret = expect_dictionary_keys_and_default( - key_value_success_callback, + key_value_success_callback, /* Option effects, passed to the EffectScript below */ "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(name)), "ai_chance", ZERO_OR_ONE, ai_chance.expect_conditional_weight(ConditionalWeight::FACTOR) )(node); diff --git a/src/openvic-simulation/misc/Modifier.cpp b/src/openvic-simulation/misc/Modifier.cpp index 6f6deda..e73d0e3 100644 --- a/src/openvic-simulation/misc/Modifier.cpp +++ b/src/openvic-simulation/misc/Modifier.cpp @@ -366,10 +366,10 @@ bool ModifierManager::parse_scripts(GameManager const& game_manager) { key_value_callback_t ModifierManager::_modifier_effect_callback( ModifierValue& modifier, key_value_callback_t default_callback, ModifierEffectValidator auto effect_validator ) const { - const auto add_modifier_cb = [this, &modifier, - effect_validator](ModifierEffect const* effect, ast::NodeCPtr value) -> bool { + const auto add_modifier_cb = [this, &modifier, effect_validator]( + ModifierEffect const* effect, ast::NodeCPtr value + ) -> bool { if (effect_validator(*effect)) { - static const case_insensitive_string_set_t no_effect_modifiers { "boost_strongest_party", "poor_savings_modifier", "local_artisan_input", "local_artisan_throughput", "local_artisan_output", "artisan_input", "artisan_throughput", "artisan_output", @@ -385,8 +385,9 @@ key_value_callback_t ModifierManager::_modifier_effect_callback( } }; - const auto add_flattened_modifier_cb = - [this, add_modifier_cb](std::string_view prefix, std::string_view key, ast::NodeCPtr value) -> bool { + const auto add_flattened_modifier_cb = [this, add_modifier_cb]( + std::string_view prefix, std::string_view key, ast::NodeCPtr value + ) -> bool { const std::string flat_identifier = get_flat_identifier(prefix, key); ModifierEffect const* effect = get_modifier_effect_by_identifier(flat_identifier); if (effect != nullptr) { @@ -397,7 +398,9 @@ key_value_callback_t ModifierManager::_modifier_effect_callback( } }; - return [this, default_callback, add_modifier_cb, add_flattened_modifier_cb](std::string_view key, ast::NodeCPtr value) -> bool { + return [this, default_callback, add_modifier_cb, add_flattened_modifier_cb]( + std::string_view key, ast::NodeCPtr value + ) -> bool { ModifierEffect const* effect = get_modifier_effect_by_identifier(key); if (effect != nullptr && value->is_type<ast::IdentifierNode>()) { return add_modifier_cb(effect, value); @@ -437,6 +440,7 @@ node_callback_t ModifierManager::expect_validated_modifier_value_and_default( return ret; }; } + node_callback_t ModifierManager::expect_validated_modifier_value( callback_t<ModifierValue&&> modifier_callback, ModifierEffectValidator auto effect_validator ) const { diff --git a/src/openvic-simulation/politics/Rebel.cpp b/src/openvic-simulation/politics/Rebel.cpp index 4bf5ecb..2f39bee 100644 --- a/src/openvic-simulation/politics/Rebel.cpp +++ b/src/openvic-simulation/politics/Rebel.cpp @@ -4,7 +4,6 @@ #include "openvic-simulation/misc/Modifier.hpp" - using namespace OpenVic; using namespace OpenVic::NodeTools; diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp index 2cd6005..53273f5 100644 --- a/src/openvic-simulation/pop/Culture.cpp +++ b/src/openvic-simulation/pop/Culture.cpp @@ -1,5 +1,6 @@ #include "Culture.hpp" +#include "openvic-simulation/country/Country.hpp" #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/types/Colour.hpp" @@ -10,15 +11,16 @@ GraphicalCultureType::GraphicalCultureType(std::string_view new_identifier) : Ha CultureGroup::CultureGroup( std::string_view new_identifier, std::string_view new_leader, GraphicalCultureType const& new_unit_graphical_culture_type, - bool new_is_overseas + bool new_is_overseas, Country const* new_union_country ) : HasIdentifier { new_identifier }, leader { new_leader }, unit_graphical_culture_type { new_unit_graphical_culture_type }, - is_overseas { new_is_overseas } {} + is_overseas { new_is_overseas }, union_country { new_union_country } {} Culture::Culture( - std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, - name_list_t&& new_first_names, name_list_t&& new_last_names + std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, name_list_t&& new_first_names, + name_list_t&& new_last_names, fixed_point_t new_radicalism, Country const* new_primary_country ) : HasIdentifierAndColour { new_identifier, new_colour, false }, group { new_group }, - first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) } {} + first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) }, radicalism { new_radicalism }, + primary_country { new_primary_country } {} bool CultureManager::add_graphical_culture_type(std::string_view identifier) { if (identifier.empty()) { @@ -29,7 +31,8 @@ bool CultureManager::add_graphical_culture_type(std::string_view identifier) { } bool CultureManager::add_culture_group( - std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, bool is_overseas + std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, bool is_overseas, + Country const* union_country ) { if (!graphical_culture_types.is_locked()) { Logger::error("Cannot register culture groups until graphical culture types are locked!"); @@ -47,12 +50,12 @@ bool CultureManager::add_culture_group( Logger::error("Null graphical culture type for ", identifier); return false; } - return culture_groups.add_item({ identifier, leader, *graphical_culture_type, is_overseas }); + return culture_groups.add_item({ identifier, leader, *graphical_culture_type, is_overseas, union_country }); } bool CultureManager::add_culture( std::string_view identifier, colour_t colour, CultureGroup const& group, name_list_t&& first_names, - name_list_t&& last_names + name_list_t&& last_names, fixed_point_t radicalism, Country const* primary_country ) { if (!culture_groups.is_locked()) { Logger::error("Cannot register cultures until culture groups are locked!"); @@ -62,7 +65,12 @@ bool CultureManager::add_culture( Logger::error("Invalid culture identifier - empty!"); return false; } - return cultures.add_item({ identifier, colour, group, std::move(first_names), std::move(last_names) }); + + // TODO - check radicalism range + + return cultures.add_item({ + identifier, colour, group, std::move(first_names), std::move(last_names), radicalism, primary_country + }); } bool CultureManager::load_graphical_culture_type_file(ast::NodeCPtr root) { @@ -74,41 +82,46 @@ bool CultureManager::load_graphical_culture_type_file(ast::NodeCPtr root) { } bool CultureManager::_load_culture_group( - size_t& total_expected_cultures, GraphicalCultureType const* default_unit_graphical_culture_type, - std::string_view culture_group_key, ast::NodeCPtr culture_group_node + CountryManager const& country_manager, size_t& total_expected_cultures, + GraphicalCultureType const* default_unit_graphical_culture_type, std::string_view culture_group_key, + ast::NodeCPtr culture_group_node ) { - - std::string_view leader; + std::string_view leader {}; GraphicalCultureType const* unit_graphical_culture_type = default_unit_graphical_culture_type; bool is_overseas = true; + Country const* union_country = nullptr; bool ret = expect_dictionary_keys_and_default( increment_callback(total_expected_cultures), "leader", ONE_EXACTLY, expect_identifier(assign_variable_callback(leader)), "unit", ZERO_OR_ONE, expect_graphical_culture_type_identifier(assign_variable_callback_pointer(unit_graphical_culture_type)), - "union", ZERO_OR_ONE, success_callback, + "union", ZERO_OR_ONE, country_manager.expect_country_identifier(assign_variable_callback_pointer(union_country)), "is_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_overseas)) )(culture_group_node); - ret &= add_culture_group(culture_group_key, leader, unit_graphical_culture_type, is_overseas); + ret &= add_culture_group(culture_group_key, leader, unit_graphical_culture_type, is_overseas, union_country); return ret; } bool CultureManager::_load_culture( - CultureGroup const& culture_group, std::string_view culture_key, ast::NodeCPtr culture_node + CountryManager const& country_manager, CultureGroup const& culture_group, std::string_view culture_key, + ast::NodeCPtr culture_node ) { - colour_t colour = colour_t::null(); - name_list_t first_names, last_names; + name_list_t first_names {}, last_names {}; + fixed_point_t radicalism = 0; + Country const* primary_country = nullptr; bool ret = expect_dictionary_keys( "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), "first_names", ONE_EXACTLY, name_list_callback(move_variable_callback(first_names)), "last_names", ONE_EXACTLY, name_list_callback(move_variable_callback(last_names)), - "radicalism", ZERO_OR_ONE, success_callback, - "primary", ZERO_OR_ONE, success_callback + "radicalism", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(radicalism)), + "primary", ZERO_OR_ONE, country_manager.expect_country_identifier(assign_variable_callback_pointer(primary_country)) )(culture_node); - ret &= add_culture(culture_key, colour, culture_group, std::move(first_names), std::move(last_names)); + ret &= add_culture( + culture_key, colour, culture_group, std::move(first_names), std::move(last_names), radicalism, primary_country + ); return ret; } @@ -132,7 +145,7 @@ bool CultureManager::_load_culture( * POP-267, POP-268, POP-269, POP-270, POP-271, POP-272, POP-273, POP-274, POP-275, POP-276, POP-277, POP-278, POP-279, * POP-280, POP-281, POP-282, POP-283, POP-284 */ -bool CultureManager::load_culture_file(ast::NodeCPtr root) { +bool CultureManager::load_culture_file(CountryManager const& country_manager, ast::NodeCPtr root) { if (!graphical_culture_types.is_locked()) { Logger::error("Cannot load culture groups until graphical culture types are locked!"); return false; @@ -147,23 +160,30 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) { size_t total_expected_cultures = 0; bool ret = expect_dictionary_reserve_length(culture_groups, - [this, default_unit_graphical_culture_type, &total_expected_cultures]( - std::string_view key, ast::NodeCPtr value) -> bool { - return _load_culture_group(total_expected_cultures, default_unit_graphical_culture_type, key, value); + [this, &country_manager, default_unit_graphical_culture_type, &total_expected_cultures]( + std::string_view key, ast::NodeCPtr value + ) -> bool { + return _load_culture_group( + country_manager, total_expected_cultures, default_unit_graphical_culture_type, key, value + ); } )(root); lock_culture_groups(); cultures.reserve(cultures.size() + total_expected_cultures); - ret &= expect_culture_group_dictionary([this](CultureGroup const& culture_group, ast::NodeCPtr culture_group_value) -> bool { - 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.contains(key)) { - return true; - } - return _load_culture(culture_group, key, value); - })(culture_group_value); - })(root); + ret &= expect_culture_group_dictionary( + [this, &country_manager](CultureGroup const& culture_group, ast::NodeCPtr culture_group_value) -> bool { + return expect_dictionary( + [this, &country_manager, &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.contains(key)) { + return true; + } + return _load_culture(country_manager, culture_group, key, value); + } + )(culture_group_value); + } + )(root); lock_cultures(); return ret; } diff --git a/src/openvic-simulation/pop/Culture.hpp b/src/openvic-simulation/pop/Culture.hpp index c8dfe7a..8807123 100644 --- a/src/openvic-simulation/pop/Culture.hpp +++ b/src/openvic-simulation/pop/Culture.hpp @@ -5,6 +5,8 @@ namespace OpenVic { struct CultureManager; + struct Country; + struct CountryManager; struct GraphicalCultureType : HasIdentifier { friend struct CultureManager; @@ -20,15 +22,14 @@ namespace OpenVic { friend struct CultureManager; private: - const std::string PROPERTY(leader); + std::string PROPERTY(leader); GraphicalCultureType const& PROPERTY(unit_graphical_culture_type); - const bool PROPERTY(is_overseas); - - // TODO - union tag + bool PROPERTY(is_overseas); + Country const* PROPERTY(union_country); CultureGroup( std::string_view new_identifier, std::string_view new_leader, - GraphicalCultureType const& new_unit_graphical_culture_type, bool new_is_overseas + GraphicalCultureType const& new_unit_graphical_culture_type, bool new_is_overseas, Country const* new_union_country ); public: @@ -40,14 +41,14 @@ namespace OpenVic { private: CultureGroup const& PROPERTY(group); - const name_list_t PROPERTY(first_names); - const name_list_t PROPERTY(last_names); - - // TODO - radicalism, primary tag + name_list_t PROPERTY(first_names); + name_list_t PROPERTY(last_names); + fixed_point_t PROPERTY(radicalism); + Country const* PROPERTY(primary_country); Culture( - std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, - name_list_t&& new_first_names, name_list_t&& new_last_names + std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, name_list_t&& new_first_names, + name_list_t&& new_last_names, fixed_point_t new_radicalism, Country const* new_primary_country ); public: @@ -61,25 +62,29 @@ namespace OpenVic { IdentifierRegistry<Culture> IDENTIFIER_REGISTRY(culture); bool _load_culture_group( - size_t& total_expected_cultures, GraphicalCultureType const* default_unit_graphical_culture_type, - std::string_view culture_group_key, ast::NodeCPtr culture_group_node + CountryManager const& country_manager, size_t& total_expected_cultures, + GraphicalCultureType const* default_unit_graphical_culture_type, std::string_view culture_group_key, + ast::NodeCPtr culture_group_node + ); + bool _load_culture( + CountryManager const& country_manager, CultureGroup const& culture_group, std::string_view culture_key, + ast::NodeCPtr node ); - bool _load_culture(CultureGroup const& culture_group, std::string_view culture_key, ast::NodeCPtr node); public: bool add_graphical_culture_type(std::string_view identifier); bool add_culture_group( std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, - bool is_overseas + bool is_overseas, Country const* union_country ); bool add_culture( std::string_view identifier, colour_t colour, CultureGroup const& group, name_list_t&& first_names, - name_list_t&& last_names + name_list_t&& last_names, fixed_point_t radicalism, Country const* primary_country ); bool load_graphical_culture_type_file(ast::NodeCPtr root); - bool load_culture_file(ast::NodeCPtr root); + bool load_culture_file(CountryManager const& country_manager, ast::NodeCPtr root); }; } diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index 5afcb18..d422038 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -1,5 +1,6 @@ #include "Pop.hpp" +#include "openvic-simulation/military/Unit.hpp" #include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/politics/Issue.hpp" #include "openvic-simulation/politics/Rebel.hpp" @@ -8,12 +9,28 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; +using enum PopType::income_type_t; + Pop::Pop( - PopType const& new_type, Culture const& new_culture, Religion const& new_religion, pop_size_t new_size, - fixed_point_t new_militancy, fixed_point_t new_consciousness, RebelType const* new_rebel_type -) : type { new_type }, culture { new_culture }, religion { new_religion }, size { new_size }, num_grown { 0 }, - num_promoted { 0 }, num_demoted { 0 }, num_migrated_internal { 0 }, num_migrated_external { 0 }, - num_migrated_colonial { 0 }, militancy { new_militancy }, consciousness { new_consciousness }, + PopType const& new_type, + Culture const& new_culture, + Religion const& new_religion, + pop_size_t new_size, + fixed_point_t new_militancy, + fixed_point_t new_consciousness, + RebelType const* new_rebel_type +) : type { new_type }, + culture { new_culture }, + religion { new_religion }, + size { new_size }, + num_grown { 0 }, + num_promoted { 0 }, + num_demoted { 0 }, + num_migrated_internal { 0 }, + num_migrated_external { 0 }, + num_migrated_colonial { 0 }, + militancy { new_militancy }, + consciousness { new_consciousness }, rebel_type { new_rebel_type } { assert(size > 0); } @@ -21,25 +38,75 @@ Pop::Pop( Strata::Strata(std::string_view new_identifier) : HasIdentifier { new_identifier } {} PopType::PopType( - std::string_view new_identifier, colour_t new_colour, Strata const& new_strata, sprite_t new_sprite, - 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_allowed_to_vote, bool new_is_slave, - bool new_can_be_recruited, bool new_can_reduce_consciousness, bool new_administrative_efficiency, bool new_can_build, - bool new_factory, bool new_can_work_factory, bool new_unemployment, ConditionalWeight&& new_country_migration_target, - ConditionalWeight&& new_migration_target, poptype_weight_map_t&& new_promote_to, ideology_weight_map_t&& new_ideologies, + std::string_view new_identifier, + colour_t new_colour, + Strata const& new_strata, + sprite_t new_sprite, + Good::good_map_t&& new_life_needs, + Good::good_map_t&& new_everyday_needs, + Good::good_map_t&& new_luxury_needs, + income_type_t new_life_needs_income_types, + income_type_t new_everyday_needs_income_types, + income_type_t new_luxury_needs_income_types, + 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_allowed_to_vote, + bool new_is_slave, + bool new_can_be_recruited, + bool new_can_reduce_consciousness, + bool new_administrative_efficiency, + bool new_can_invest, + bool new_factory, + bool new_can_work_factory, + bool new_unemployment, + fixed_point_t new_research_points, + fixed_point_t new_leadership_points, + fixed_point_t new_research_leadership_optimum, + fixed_point_t new_state_administration_multiplier, + PopType const* new_equivalent, + ConditionalWeight&& new_country_migration_target, + ConditionalWeight&& new_migration_target, + poptype_weight_map_t&& new_promote_to, + ideology_weight_map_t&& new_ideologies, issue_weight_map_t&& new_issues -) : HasIdentifierAndColour { new_identifier, new_colour, 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 }, - demote_migrant { new_demote_migrant }, is_artisan { new_is_artisan }, allowed_to_vote { new_allowed_to_vote }, - is_slave { new_is_slave }, can_be_recruited { new_can_be_recruited }, - can_reduce_consciousness { new_can_reduce_consciousness }, administrative_efficiency { new_administrative_efficiency }, - can_build { new_can_build }, factory { new_factory }, can_work_factory { new_can_work_factory }, - unemployment { new_unemployment }, country_migration_target { std::move(new_country_migration_target) }, - migration_target { std::move(new_migration_target) }, promote_to { std::move(new_promote_to) }, - ideologies { std::move(new_ideologies) }, issues { std::move(new_issues) } { +) : HasIdentifierAndColour { new_identifier, new_colour, 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) }, + life_needs_income_types { std::move(new_life_needs_income_types) }, + everyday_needs_income_types { std::move(new_everyday_needs_income_types) }, + luxury_needs_income_types { std::move(new_luxury_needs_income_types) }, + 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 }, + demote_migrant { new_demote_migrant }, + is_artisan { new_is_artisan }, + allowed_to_vote { new_allowed_to_vote }, + is_slave { new_is_slave }, + can_be_recruited { new_can_be_recruited }, + can_reduce_consciousness { new_can_reduce_consciousness }, + administrative_efficiency { new_administrative_efficiency }, + can_invest { new_can_invest }, + factory { new_factory }, + can_work_factory { new_can_work_factory }, + unemployment { new_unemployment }, + research_points { new_research_points }, + leadership_points { new_leadership_points }, + research_leadership_optimum { new_research_leadership_optimum }, + state_administration_multiplier { new_state_administration_multiplier }, + equivalent { new_equivalent }, + country_migration_target { std::move(new_country_migration_target) }, + migration_target { std::move(new_migration_target) }, + promote_to { std::move(new_promote_to) }, + ideologies { std::move(new_ideologies) }, + issues { std::move(new_issues) } { assert(sprite > 0); assert(max_size >= 0); assert(merge_max_size >= 0); @@ -80,13 +147,41 @@ bool PopManager::add_strata(std::string_view identifier) { } bool PopManager::add_pop_type( - std::string_view identifier, colour_t colour, Strata const* strata, PopType::sprite_t sprite, - Good::good_map_t&& life_needs, Good::good_map_t&& everyday_needs, Good::good_map_t&& luxury_needs, - PopType::rebel_units_t&& rebel_units, Pop::pop_size_t max_size, Pop::pop_size_t merge_max_size, bool state_capital_only, - bool demote_migrant, bool is_artisan, bool allowed_to_vote, bool is_slave, bool can_be_recruited, - bool can_reduce_consciousness, bool administrative_efficiency, bool can_build, bool factory, bool can_work_factory, - bool unemployment, ConditionalWeight&& country_migration_target, ConditionalWeight&& migration_target, - ast::NodeCPtr promote_to_node, PopType::ideology_weight_map_t&& ideologies, ast::NodeCPtr issues_node + std::string_view identifier, + colour_t colour, + Strata const* strata, + PopType::sprite_t sprite, + Good::good_map_t&& life_needs, + Good::good_map_t&& everyday_needs, + Good::good_map_t&& luxury_needs, + PopType::income_type_t life_needs_income_types, + PopType::income_type_t everyday_needs_income_types, + PopType::income_type_t luxury_needs_income_types, + PopType::rebel_units_t&& rebel_units, + Pop::pop_size_t max_size, + Pop::pop_size_t merge_max_size, + bool state_capital_only, + bool demote_migrant, + bool is_artisan, + bool allowed_to_vote, + bool is_slave, + bool can_be_recruited, + bool can_reduce_consciousness, + bool administrative_efficiency, + bool can_invest, + bool factory, + bool can_work_factory, + bool unemployment, + fixed_point_t research_points, + fixed_point_t leadership_points, + fixed_point_t research_leadership_optimum, + fixed_point_t state_administration_multiplier, + ast::NodeCPtr equivalent, + ConditionalWeight&& country_migration_target, + ConditionalWeight&& migration_target, + ast::NodeCPtr promote_to_node, + PopType::ideology_weight_map_t&& ideologies, + ast::NodeCPtr issues_node ) { if (identifier.empty()) { Logger::error("Invalid pop type identifier - empty!"); @@ -97,27 +192,76 @@ bool PopManager::add_pop_type( return false; } if (sprite <= 0) { - Logger::error("Invalid pop type sprite index for ", identifier, ": ", sprite); + Logger::error("Invalid pop type sprite index for ", identifier, ": ", sprite, " (must be positive)"); + return false; + } + if (max_size <= 0) { + Logger::error("Invalid pop type max size for ", identifier, ": ", max_size, " (must be positive)"); return false; } - if (max_size < 0) { - Logger::error("Invalid pop type max size for ", identifier, ": ", max_size); + if (merge_max_size <= 0) { + Logger::error("Invalid pop type merge max size for ", identifier, ": ", merge_max_size, " (must be positive)"); return false; } - if (merge_max_size < 0) { - Logger::error("Invalid pop type merge max size for ", identifier, ": ", merge_max_size); + + if (research_leadership_optimum < 0) { + Logger::error( + "Invalid pop type research/leadership optimum for ", identifier, ": ", research_leadership_optimum, + " (cannot be negative)" + ); return false; } + if ((research_points != 0 || leadership_points != 0) != (research_leadership_optimum > 0)) { + Logger::error( + "Invalid pop type research/leadership points and optimum for ", identifier, ": research = ", research_points, + ", leadership = ", leadership_points, ", optimum = ", research_leadership_optimum, + " (optimum is positive if and only if at least one of research and leadership is non-zero)" + ); + return false; + } + const bool ret = pop_types.add_item({ - identifier, colour, *strata, sprite, std::move(life_needs), std::move(everyday_needs), - std::move(luxury_needs), std::move(rebel_units), max_size, merge_max_size, state_capital_only, - demote_migrant, is_artisan, allowed_to_vote, is_slave, can_be_recruited, can_reduce_consciousness, - administrative_efficiency, can_build, factory, can_work_factory, unemployment, std::move(country_migration_target), - std::move(migration_target), {}, std::move(ideologies), {} + identifier, + colour, + *strata, + sprite, + std::move(life_needs), + std::move(everyday_needs), + std::move(luxury_needs), + life_needs_income_types, + everyday_needs_income_types, + luxury_needs_income_types, + std::move(rebel_units), + max_size, + merge_max_size, + state_capital_only, + demote_migrant, + is_artisan, + allowed_to_vote, + is_slave, + can_be_recruited, + can_reduce_consciousness, + administrative_efficiency, + can_invest, + factory, + can_work_factory, + unemployment, + research_points, + leadership_points, + research_leadership_optimum, + state_administration_multiplier, + nullptr, + std::move(country_migration_target), + std::move(migration_target), + {}, + std::move(ideologies), + {} }); + if (ret) { - delayed_parse_promote_to_and_issues_nodes.emplace_back(promote_to_node, issues_node); + delayed_parse_nodes.emplace_back(equivalent, promote_to_node, issues_node); } + if (slave_sprite <= 0 && ret && is_slave) { /* Set slave sprite to that of the first is_slave pop type we find. */ slave_sprite = sprite; @@ -132,6 +276,29 @@ bool PopManager::add_pop_type( void PopManager::reserve_pop_types(size_t count) { stratas.reserve(stratas.size() + count); pop_types.reserve(pop_types.size() + count); + delayed_parse_nodes.reserve(delayed_parse_nodes.size() + count); +} + +static NodeCallback auto expect_needs_income(PopType::income_type_t& types) { + static const string_map_t<PopType::income_type_t> income_type_map { + { "administration", ADMINISTRATION }, + { "education", EDUCATION }, + { "military", MILITARY }, + { "reforms", REFORMS } + }; + return expect_dictionary_keys( + "type", ONE_OR_MORE, expect_identifier(expect_mapped_string(income_type_map, + [&types](PopType::income_type_t type) -> bool { + if (!share_income_type(types, type)) { + types |= type; + return true; + } + Logger::error("Duplicate income type ", type, " in pop type income types!"); + return false; + } + )), + "weight", ZERO_OR_ONE, success_callback /* Has no effect in game */ + ); } /* REQUIREMENTS: @@ -145,11 +312,16 @@ bool PopManager::load_pop_type_file( Strata const* strata = nullptr; PopType::sprite_t sprite = 0; Good::good_map_t life_needs, everyday_needs, luxury_needs; + PopType::income_type_t life_needs_income_types = NO_INCOME_TYPE, everyday_needs_income_types = NO_INCOME_TYPE, + luxury_needs_income_types = NO_INCOME_TYPE; PopType::rebel_units_t rebel_units; + Pop::pop_size_t max_size = Pop::MAX_SIZE, merge_max_size = Pop::MAX_SIZE; bool state_capital_only = false, demote_migrant = false, is_artisan = false, allowed_to_vote = true, is_slave = false, - can_be_recruited = false, can_reduce_consciousness = false, administrative_efficiency = false, can_build = false, + can_be_recruited = false, can_reduce_consciousness = false, administrative_efficiency = false, can_invest = false, factory = false, can_work_factory = false, unemployment = false; - Pop::pop_size_t max_size = 0, merge_max_size = 0; + fixed_point_t research_points = 0, leadership_points = 0, research_leadership_optimum = 0, + state_administration_multiplier = 0; + ast::NodeCPtr equivalent = nullptr; ConditionalWeight country_migration_target { scope_t::COUNTRY, scope_t::POP, scope_t::NO_SCOPE }; ConditionalWeight migration_target { scope_t::PROVINCE, scope_t::POP, scope_t::NO_SCOPE }; ast::NodeCPtr promote_to_node = nullptr; @@ -176,18 +348,18 @@ bool PopManager::load_pop_type_file( } ), "state_capital_only", ZERO_OR_ONE, expect_bool(assign_variable_callback(state_capital_only)), - "research_points", ZERO_OR_ONE, success_callback, // TODO - research points generation - "research_optimum", ZERO_OR_ONE, success_callback, // TODO - bonus research points generation + "research_points", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(research_points)), + "research_optimum", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(research_leadership_optimum)), "rebel", ZERO_OR_ONE, unit_manager.expect_unit_decimal_map(move_variable_callback(rebel_units)), - "equivalent", ZERO_OR_ONE, success_callback, // TODO - worker convertability - "leadership", ZERO_OR_ONE, success_callback, // TODO - leadership points generation + "equivalent", ZERO_OR_ONE, assign_variable_callback(equivalent), + "leadership", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(leadership_points)), "allowed_to_vote", ZERO_OR_ONE, expect_bool(assign_variable_callback(allowed_to_vote)), "is_slave", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_slave)), "can_be_recruited", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_be_recruited)), "can_reduce_consciousness", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_reduce_consciousness)), - "life_needs_income", ZERO_OR_ONE, success_callback, // TODO - incomes from national budget - "everyday_needs_income", ZERO_OR_ONE, success_callback, - "luxury_needs_income", ZERO_OR_ONE, success_callback, + "life_needs_income", ZERO_OR_ONE, expect_needs_income(life_needs_income_types), + "everyday_needs_income", ZERO_OR_ONE, expect_needs_income(everyday_needs_income_types), + "luxury_needs_income", ZERO_OR_ONE, expect_needs_income(luxury_needs_income_types), "luxury_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(luxury_needs)), "everyday_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(everyday_needs)), "life_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(life_needs)), @@ -206,9 +378,9 @@ bool PopManager::load_pop_type_file( "issues", ZERO_OR_ONE, assign_variable_callback(issues_node), "demote_migrant", ZERO_OR_ONE, expect_bool(assign_variable_callback(demote_migrant)), "administrative_efficiency", ZERO_OR_ONE, expect_bool(assign_variable_callback(administrative_efficiency)), - "tax_eff", ZERO_OR_ONE, success_callback, // TODO - tax collection modifier - "can_build", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_build)), - "factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(factory)), + "tax_eff", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(state_administration_multiplier)), + "can_build", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_invest)), + "factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(factory)), // TODO - work out what this does "workplace_input", ZERO_OR_ONE, success_callback, // TODO - work out what these do "workplace_output", ZERO_OR_ONE, success_callback, "starter_share", ZERO_OR_ONE, success_callback, @@ -217,10 +389,40 @@ bool PopManager::load_pop_type_file( )(root); ret &= add_pop_type( - filestem, colour, strata, sprite, std::move(life_needs), std::move(everyday_needs), std::move(luxury_needs), - std::move(rebel_units), max_size, merge_max_size, state_capital_only, demote_migrant, is_artisan, allowed_to_vote, - is_slave, can_be_recruited, can_reduce_consciousness, administrative_efficiency, can_build, factory, can_work_factory, - unemployment, std::move(country_migration_target), std::move(migration_target), promote_to_node, std::move(ideologies), + filestem, + colour, + strata, + sprite, + std::move(life_needs), + std::move(everyday_needs), + std::move(luxury_needs), + life_needs_income_types, + everyday_needs_income_types, + luxury_needs_income_types, + std::move(rebel_units), + max_size, + merge_max_size, + state_capital_only, + demote_migrant, + is_artisan, + allowed_to_vote, + is_slave, + can_be_recruited, + can_reduce_consciousness, + administrative_efficiency, + can_invest, + factory, + can_work_factory, + unemployment, + research_points, + leadership_points, + research_leadership_optimum, + state_administration_multiplier, + equivalent, + std::move(country_migration_target), + std::move(migration_target), + promote_to_node, + std::move(ideologies), issues_node ); return ret; @@ -228,9 +430,15 @@ bool PopManager::load_pop_type_file( bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_manager) { bool ret = true; - for (size_t index = 0; index < delayed_parse_promote_to_and_issues_nodes.size(); ++index) { - const auto [promote_to_node, issues_node] = delayed_parse_promote_to_and_issues_nodes[index]; + for (size_t index = 0; index < delayed_parse_nodes.size(); ++index) { + const auto [equivalent, promote_to_node, issues_node] = delayed_parse_nodes[index]; PopType* pop_type = pop_types.get_item_by_index(index); + if (equivalent != nullptr && !expect_pop_type_identifier( + assign_variable_callback_pointer(pop_type->equivalent) + )(equivalent)) { + Logger::error("Errors parsing equivalent pop type for pop type ", pop_type, "!"); + ret = false; + } if (promote_to_node != nullptr && !expect_pop_type_dictionary_reserve_length( pop_type->promote_to, [pop_type](PopType const& type, ast::NodeCPtr node) -> bool { @@ -244,7 +452,7 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana return ret; } )(promote_to_node)) { - Logger::error("Errors parsing pop type ", pop_type, " promotion weights!"); + Logger::error("Errors parsing promotion weights for pop type ", pop_type, "!"); ret = false; } if (issues_node != nullptr && !expect_dictionary_reserve_length( @@ -264,11 +472,11 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana return ret; } )(issues_node)) { - Logger::error("Errors parsing pop type ", pop_type, " issue weights!"); + Logger::error("Errors parsing issue weights for pop type ", pop_type, "!"); ret = false; } } - delayed_parse_promote_to_and_issues_nodes.clear(); + delayed_parse_nodes.clear(); return ret; } diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp index ffef6ea..5a4cebf 100644 --- a/src/openvic-simulation/pop/Pop.hpp +++ b/src/openvic-simulation/pop/Pop.hpp @@ -1,16 +1,21 @@ #pragma once +#include <limits> +#include <ostream> + #include "openvic-simulation/economy/Good.hpp" -#include "openvic-simulation/military/Unit.hpp" #include "openvic-simulation/pop/Culture.hpp" #include "openvic-simulation/pop/Religion.hpp" #include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/types/EnumBitfield.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" namespace OpenVic { struct PopManager; struct PopType; + struct Unit; + struct UnitManager; struct RebelType; struct RebelManager; struct Ideology; @@ -26,6 +31,8 @@ namespace OpenVic { using pop_size_t = int64_t; + static constexpr pop_size_t MAX_SIZE = std::numeric_limits<pop_size_t>::max(); + private: PopType const& PROPERTY(type); Culture const& PROPERTY(culture); @@ -46,8 +53,13 @@ namespace OpenVic { RebelType const* PROPERTY(rebel_type); Pop( - PopType const& new_type, Culture const& new_culture, Religion const& new_religion, pop_size_t new_size, - fixed_point_t new_militancy, fixed_point_t new_consciousness, RebelType const* new_rebel_type + PopType const& new_type, + Culture const& new_culture, + Religion const& new_religion, + pop_size_t new_size, + fixed_point_t new_militancy, + fixed_point_t new_consciousness, + RebelType const* new_rebel_type ); public: @@ -73,6 +85,16 @@ namespace OpenVic { struct PopType : HasIdentifierAndColour { friend struct PopManager; + /* This is a bitfield - PopTypes can have up to one of each income source for each need category. */ + enum struct income_type_t : uint8_t { + NO_INCOME_TYPE = 0, + ADMINISTRATION = 1 << 0, + EDUCATION = 1 << 1, + MILITARY = 1 << 2, + REFORMS = 1 << 3, + MAX_INCOME_TYPE = (1 << 4) - 1 + }; + using sprite_t = uint8_t; using rebel_units_t = fixed_point_map_t<Unit const*>; using poptype_weight_map_t = ordered_map<PopType const*, ConditionalWeight>; @@ -85,6 +107,9 @@ namespace OpenVic { Good::good_map_t PROPERTY(life_needs); Good::good_map_t PROPERTY(everyday_needs); Good::good_map_t PROPERTY(luxury_needs); + income_type_t PROPERTY(life_needs_income_types); + income_type_t PROPERTY(everyday_needs_income_types); + income_type_t PROPERTY(luxury_needs_income_types); rebel_units_t PROPERTY(rebel_units); const Pop::pop_size_t PROPERTY(max_size); const Pop::pop_size_t PROPERTY(merge_max_size); @@ -96,10 +121,15 @@ namespace OpenVic { const bool PROPERTY(can_be_recruited); const bool PROPERTY(can_reduce_consciousness); const bool PROPERTY(administrative_efficiency); - const bool PROPERTY(can_build); + const bool PROPERTY(can_invest); const bool PROPERTY(factory); const bool PROPERTY(can_work_factory); const bool PROPERTY(unemployment); + const fixed_point_t PROPERTY(research_points); + const fixed_point_t PROPERTY(leadership_points); + const fixed_point_t PROPERTY(research_leadership_optimum); + const fixed_point_t PROPERTY(state_administration_multiplier); + PopType const* PROPERTY(equivalent); ConditionalWeight PROPERTY(country_migration_target); /* Scope - country, THIS - pop */ ConditionalWeight PROPERTY(migration_target); /* Scope - province, THIS - pop */ @@ -108,14 +138,41 @@ namespace OpenVic { issue_weight_map_t PROPERTY(issues); /* Scope - province, THIS - country (?) */ PopType( - std::string_view new_identifier, colour_t new_colour, Strata const& new_strata, sprite_t new_sprite, - 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_allowed_to_vote, - bool new_is_slave, bool new_can_be_recruited, bool new_can_reduce_consciousness, - bool new_administrative_efficiency, bool new_can_build, bool new_factory, bool new_can_work_factory, - bool new_unemployment, ConditionalWeight&& new_country_migration_target, ConditionalWeight&& new_migration_target, - poptype_weight_map_t&& new_promote_to, ideology_weight_map_t&& new_ideologies, issue_weight_map_t&& new_issues + std::string_view new_identifier, + colour_t new_colour, + Strata const& new_strata, + sprite_t new_sprite, + Good::good_map_t&& new_life_needs, + Good::good_map_t&& new_everyday_needs, + Good::good_map_t&& new_luxury_needs, + income_type_t new_life_needs_income_types, + income_type_t new_everyday_needs_income_types, + income_type_t new_luxury_needs_income_types, + 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_allowed_to_vote, + bool new_is_slave, + bool new_can_be_recruited, + bool new_can_reduce_consciousness, + bool new_administrative_efficiency, + bool new_can_invest, + bool new_factory, + bool new_can_work_factory, + bool new_unemployment, + fixed_point_t new_research_points, + fixed_point_t new_leadership_points, + fixed_point_t new_research_leadership_optimum, + fixed_point_t new_state_administration_multiplier, + PopType const* new_equivalent, + ConditionalWeight&& new_country_migration_target, + ConditionalWeight&& new_migration_target, + poptype_weight_map_t&& new_promote_to, + ideology_weight_map_t&& new_ideologies, + issue_weight_map_t&& new_issues ); bool parse_scripts(GameManager const& game_manager); @@ -124,6 +181,40 @@ namespace OpenVic { PopType(PopType&&) = default; }; + template<> struct enable_bitfield<PopType::income_type_t> : std::true_type {}; + + /* This returns true if at least one income type is shared by both arguments. */ + constexpr inline bool share_income_type(PopType::income_type_t lhs, PopType::income_type_t rhs) { + return (lhs & rhs) != PopType::income_type_t::NO_INCOME_TYPE; + } + + inline std::ostream& operator<<(std::ostream& stream, PopType::income_type_t income_type) { + using enum PopType::income_type_t; + if (income_type == NO_INCOME_TYPE) { + return stream << "[NO_INCOME_TYPE]"; + } + bool type_found = false; + stream << '['; +#define BUILD_STRING(entry) \ + if (share_income_type(income_type, entry)) { \ + if (type_found) { \ + stream << " | "; \ + } else { \ + type_found = true; \ + } \ + stream << #entry; \ + } + BUILD_STRING(ADMINISTRATION); + BUILD_STRING(EDUCATION); + BUILD_STRING(MILITARY); + BUILD_STRING(REFORMS); +#undef BUILD_STRING + if (!type_found) { + stream << "INVALID INCOME TYPE"; + } + return stream << ']'; + } + struct Province; struct PopManager { @@ -131,11 +222,12 @@ namespace OpenVic { /* Using strata/stratas instead of stratum/strata to avoid confusion. */ IdentifierRegistry<Strata> IDENTIFIER_REGISTRY(strata); IdentifierRegistry<PopType> IDENTIFIER_REGISTRY(pop_type); - /* promote_to can't be parsed until after all PopTypes are registered, and issues requires Issues to be loaded, - * which themselves depend on pop strata. To get around this, the nodes for these variables are stored here and - * parsed after both PopTypes and Issues. The nodes will remain valid as PopType files' Parser objects are cached - * to preserve their condition script nodes until all other defines are loaded and the scripts can be parsed. */ - std::vector<std::pair<ast::NodeCPtr, ast::NodeCPtr>> delayed_parse_promote_to_and_issues_nodes; + /* equivalent and promote_to can't be parsed until after all PopTypes are registered, and issues requires Issues + * to be loaded, which themselves depend on pop strata. To get around this, the nodes for these variables are stored + * here and parsed after both PopTypes and Issues. The nodes will remain valid as PopType files' Parser objects are + * cached to preserve their condition script nodes until all other defines are loaded and the scripts can be parsed. + * Entries contain: (equivalent, promote_to, issues) */ + std::vector<std::tuple<ast::NodeCPtr, ast::NodeCPtr, ast::NodeCPtr>> delayed_parse_nodes; ConditionalWeight PROPERTY(promotion_chance); ConditionalWeight PROPERTY(demotion_chance); @@ -157,13 +249,40 @@ namespace OpenVic { bool add_strata(std::string_view identifier); bool add_pop_type( - std::string_view identifier, colour_t new_colour, Strata const* strata, PopType::sprite_t sprite, - Good::good_map_t&& life_needs, Good::good_map_t&& everyday_needs, Good::good_map_t&& luxury_needs, - PopType::rebel_units_t&& rebel_units, Pop::pop_size_t max_size, Pop::pop_size_t merge_max_size, - bool state_capital_only, bool demote_migrant, bool is_artisan, bool allowed_to_vote, bool is_slave, - bool can_be_recruited, bool can_reduce_consciousness, bool administrative_efficiency, bool can_build, bool factory, - bool can_work_factory, bool unemployment, ConditionalWeight&& country_migration_target, - ConditionalWeight&& migration_target, ast::NodeCPtr promote_to_node, PopType::ideology_weight_map_t&& ideologies, + std::string_view identifier, + colour_t new_colour, + Strata const* strata, + PopType::sprite_t sprite, + Good::good_map_t&& life_needs, + Good::good_map_t&& everyday_needs, + Good::good_map_t&& luxury_needs, + PopType::income_type_t life_needs_income_types, + PopType::income_type_t everyday_needs_income_types, + PopType::income_type_t luxury_needs_income_types, + PopType::rebel_units_t&& rebel_units, + Pop::pop_size_t max_size, + Pop::pop_size_t merge_max_size, + bool state_capital_only, + bool demote_migrant, + bool is_artisan, + bool allowed_to_vote, + bool is_slave, + bool can_be_recruited, + bool can_reduce_consciousness, + bool administrative_efficiency, + bool can_invest, + bool factory, + bool can_work_factory, + bool unemployment, + fixed_point_t research_points, + fixed_point_t leadership_points, + fixed_point_t research_leadership_optimum, + fixed_point_t state_administration_multiplier, + ast::NodeCPtr equivalent, + ConditionalWeight&& country_migration_target, + ConditionalWeight&& migration_target, + ast::NodeCPtr promote_to_node, + PopType::ideology_weight_map_t&& ideologies, ast::NodeCPtr issues_node ); diff --git a/src/openvic-simulation/research/Invention.cpp b/src/openvic-simulation/research/Invention.cpp index 92bf059..792dcba 100644 --- a/src/openvic-simulation/research/Invention.cpp +++ b/src/openvic-simulation/research/Invention.cpp @@ -2,6 +2,7 @@ #include "openvic-simulation/economy/BuildingType.hpp" #include "openvic-simulation/map/Crime.hpp" +#include "openvic-simulation/military/Unit.hpp" using namespace OpenVic; using namespace OpenVic::NodeTools; diff --git a/src/openvic-simulation/types/Colour.hpp b/src/openvic-simulation/types/Colour.hpp index 38c7b1e..06a6b36 100644 --- a/src/openvic-simulation/types/Colour.hpp +++ b/src/openvic-simulation/types/Colour.hpp @@ -147,15 +147,19 @@ namespace OpenVic { static constexpr basic_colour_t from_integer(integer_type integer) { if constexpr (colour_traits::has_alpha) { - return { colour_traits::red_from_integer(integer), colour_traits::green_from_integer(integer), - colour_traits::blue_from_integer(integer), colour_traits::alpha_from_integer(integer) }; + return { + colour_traits::red_from_integer(integer), colour_traits::green_from_integer(integer), + colour_traits::blue_from_integer(integer), colour_traits::alpha_from_integer(integer) + }; } else { assert( colour_traits::alpha_from_integer(integer) == colour_traits::null || colour_traits::alpha_from_integer(integer) == max_value ); - return { colour_traits::red_from_integer(integer), colour_traits::green_from_integer(integer), - colour_traits::blue_from_integer(integer) }; + return { + colour_traits::red_from_integer(integer), colour_traits::green_from_integer(integer), + colour_traits::blue_from_integer(integer) + }; } } @@ -163,8 +167,10 @@ namespace OpenVic { from_floats(float r, float g, float b, float a = colour_traits::alpha_to_float(max_value)) requires(colour_traits::has_alpha) { - return { colour_traits::red_from_float(r), colour_traits::green_from_float(g), colour_traits::blue_from_float(b), - colour_traits::alpha_from_float(a) }; + return { + colour_traits::red_from_float(r), colour_traits::green_from_float(g), colour_traits::blue_from_float(b), + colour_traits::alpha_from_float(a) + }; } static constexpr basic_colour_t from_floats(float r, float g, float b) @@ -192,13 +198,19 @@ namespace OpenVic { : red(r), green(g), blue(b) {} template<typename _ColourTraits> - requires(_ColourTraits::has_alpha && std::same_as<typename _ColourTraits::value_type, value_type> && std::same_as<typename _ColourTraits::integer_type, integer_type>) + requires( + _ColourTraits::has_alpha && std::same_as<typename _ColourTraits::value_type, value_type> && + std::same_as<typename _ColourTraits::integer_type, integer_type> + ) explicit constexpr basic_colour_t(basic_colour_t<value_type, integer_type, _ColourTraits> const& colour) requires(colour_traits::has_alpha) : basic_colour_t { colour.red, colour.green, colour.blue, colour.alpha } {} template<typename _ColourTraits> - requires(!_ColourTraits::has_alpha && std::same_as<typename _ColourTraits::value_type, value_type> && std::same_as<typename _ColourTraits::integer_type, integer_type>) + requires( + !_ColourTraits::has_alpha && std::same_as<typename _ColourTraits::value_type, value_type> && + std::same_as<typename _ColourTraits::integer_type, integer_type> + ) explicit constexpr basic_colour_t( basic_colour_t<value_type, integer_type, _ColourTraits> const& colour, value_type a = max_value ) @@ -206,7 +218,10 @@ namespace OpenVic { : basic_colour_t { colour.red, colour.green, colour.blue, a } {} template<typename _ColourTraits> - requires(std::same_as<typename _ColourTraits::value_type, value_type> && std::same_as<typename _ColourTraits::integer_type, integer_type>) + requires( + std::same_as<typename _ColourTraits::value_type, value_type> && + std::same_as<typename _ColourTraits::integer_type, integer_type> + ) explicit constexpr basic_colour_t(basic_colour_t<value_type, integer_type, _ColourTraits> const& colour) requires(!colour_traits::has_alpha) : basic_colour_t { colour.red, colour.green, colour.blue } {} diff --git a/src/openvic-simulation/types/FunctionRef.hpp b/src/openvic-simulation/types/FunctionRef.hpp index 1ca5bb7..88f6284 100644 --- a/src/openvic-simulation/types/FunctionRef.hpp +++ b/src/openvic-simulation/types/FunctionRef.hpp @@ -39,8 +39,8 @@ namespace OpenVic { template<bool bNoExcept2, typename Func, typename Ret2, typename... Args2> struct make_type_erased_function_ptr final { type_erased_function_ptr<bNoExcept2, Ret2, Args2...> operator()() const& noexcept { - return [](AnyRef anyref, - Args2... args) noexcept(bNoExcept2) -> Ret2 { // implicit cast of stateless lambda to function pointer + // implicit cast of stateless lambda to function pointer + return [](AnyRef anyref, Args2... args) noexcept(bNoExcept2) -> Ret2 { return std::invoke( anyref.template get_ref<Func>(), std::forward<Args2>(args)... ); // MAYTHROW unless bNoExcept @@ -51,8 +51,8 @@ namespace OpenVic { template<bool bNoExcept2, typename Func, typename... Args2> struct make_type_erased_function_ptr<bNoExcept2, Func, void, Args2...> final { type_erased_function_ptr<bNoExcept2, void, Args2...> operator()() const& noexcept { - return [](AnyRef anyref, - Args2... args) noexcept(bNoExcept2) { // implicit cast of stateless lambda to function pointer + // implicit cast of stateless lambda to function pointer + return [](AnyRef anyref, Args2... args) noexcept(bNoExcept2) { std::invoke(anyref.template get_ref<Func>(), std::forward<Args2>(args)...); // MAYTHROW unless bNoExcept }; } @@ -69,7 +69,6 @@ namespace OpenVic { return m_pfuncTypeErased(m_anyref, std::forward<Args>(args)...); // MAYTHROW unless bNoExcept } - template<typename Derived, typename Base> struct decayed_derived_from : std::bool_constant<std::derived_from<typename std::decay<Derived>::type, Base>> { static_assert(std::same_as<std::decay_t<Base>, Base>); @@ -77,10 +76,10 @@ namespace OpenVic { template<typename Func> requires(!decayed_derived_from<Func, FunctionRefBase>::value) && - std::invocable<std::remove_reference_t<Func>&, Args...> && - (std::convertible_to< - decltype(std::invoke(std::declval<std::remove_reference_t<Func>&>(), std::declval<Args>()...)), Ret> || - std::is_void<Ret>::value) + std::invocable<std::remove_reference_t<Func>&, Args...> && + (std::convertible_to< + decltype(std::invoke(std::declval<std::remove_reference_t<Func>&>(), std::declval<Args>()...)), Ret + > || std::is_void<Ret>::value) FunctionRefBase(Func&& func) noexcept : m_pfuncTypeErased(make_type_erased_function_ptr<bNoExcept, std::remove_reference_t<Func>, Ret, Args...> {}()), m_anyref(as_lvalue(func)) { diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index bbaf52c..251632b 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -232,7 +232,7 @@ namespace OpenVic { } } - constexpr static NodeTools::KeyValueCallback auto key_value_invalid_callback(std::string_view name) { + static constexpr NodeTools::KeyValueCallback auto key_value_invalid_callback(std::string_view name) { return [name](std::string_view key, ast::NodeCPtr) { Logger::error("Invalid ", name, ": ", key); return false; @@ -258,13 +258,7 @@ namespace OpenVic { if (item != nullptr) { \ return callback(*item); \ } \ - if (!warn) { \ - Logger::error("Invalid ", name, ": ", identifier); \ - return false; \ - } else { \ - Logger::warning("Invalid ", name, ": ", identifier); \ - return true; \ - } \ + return NodeTools::warn_or_error(warn, "Invalid ", name, ": ", identifier); \ }; \ } \ constexpr NodeTools::NodeCallback auto expect_item_identifier( \ diff --git a/src/openvic-simulation/utility/Logger.hpp b/src/openvic-simulation/utility/Logger.hpp index 20c7fdd..53decb3 100644 --- a/src/openvic-simulation/utility/Logger.hpp +++ b/src/openvic-simulation/utility/Logger.hpp @@ -72,6 +72,7 @@ namespace OpenVic { struct log_channel_t { log_func_t func; log_queue_t queue; + size_t message_count; }; template<typename... Ts> @@ -90,6 +91,8 @@ namespace OpenVic { do { log_channel.func(std::move(log_channel.queue.front())); log_channel.queue.pop(); + /* Only count printed messages, so that message_count matches what is seen in the console. */ + log_channel.message_count++; } while (!log_channel.queue.empty()); } } @@ -103,6 +106,9 @@ public: \ static inline void set_##name##_func(log_func_t log_func) { \ name##_channel.func = log_func; \ } \ + static inline size_t get_##name##_count() { \ + return name##_channel.message_count; \ + } \ template<typename... Ts> \ struct name { \ name(Ts&&... ts, source_location const& location = source_location::current()) { \ diff --git a/src/openvic-simulation/utility/StringUtils.hpp b/src/openvic-simulation/utility/StringUtils.hpp index c5a0b71..570f296 100644 --- a/src/openvic-simulation/utility/StringUtils.hpp +++ b/src/openvic-simulation/utility/StringUtils.hpp @@ -1,10 +1,11 @@ #pragma once +#include <algorithm> #include <cctype> -#include <cstdint> #include <cstring> #include <limits> #include <string_view> +#include <type_traits> namespace OpenVic::StringUtils { /* The constexpr function 'string_to_uint64' will convert a string into a uint64_t integer value. |