diff options
author | wvpm <24685035+wvpm@users.noreply.github.com> | 2024-10-30 22:08:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-30 22:08:36 +0100 |
commit | 5d8451172fefa93f6f53583fa6b8723f1dd3598e (patch) | |
tree | 187df50130475006b404c5ab57f0fa679173ad04 /src | |
parent | 48e4e92682db99239e928a67e6677cdd2c53a375 (diff) | |
parent | 0d861669d9e1f5d487c810ae01be50f142790f1e (diff) |
Merge pull request #206 from OpenVicProject/prepare_for_rgo
Prepare for rgo
Diffstat (limited to 'src')
28 files changed, 712 insertions, 143 deletions
diff --git a/src/headless/main.cpp b/src/headless/main.cpp index 21e9f7c..a50b4ba 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -1,7 +1,10 @@ -#include <cstring> - +#include <openvic-simulation/country/CountryInstance.hpp> #include <openvic-simulation/dataloader/Dataloader.hpp> +#include <openvic-simulation/economy/GoodDefinition.hpp> +#include <openvic-simulation/economy/production/ProductionType.hpp> +#include <openvic-simulation/economy/production/ResourceGatheringOperation.hpp> #include <openvic-simulation/GameManager.hpp> +#include <openvic-simulation/pop/Pop.hpp> #include <openvic-simulation/testing/Testing.hpp> #include <openvic-simulation/utility/Logger.hpp> @@ -18,6 +21,40 @@ static void print_help(std::ostream& stream, char const* program_name) { << "(Paths with spaces need to be enclosed in \"quotes\").\n"; } +static void print_rgo(ProvinceInstance const& province) { + ResourceGatheringOperation const& rgo = province.get_rgo(); + ProductionType const* const production_type_nullable = rgo.get_production_type_nullable(); + if (production_type_nullable == nullptr) { + Logger::error( + "\n ", province.get_identifier(), + " - production_type: nullptr" + ); + } else { + ProductionType const& production_type = *production_type_nullable; + GoodDefinition const& output_good = production_type.get_output_good(); + std::string text = StringUtils::append_string_views( + "\n\t", province.get_identifier(), + " - good: ", output_good.get_identifier(), + ", production_type: ", production_type.get_identifier(), + ", size_multiplier: ", rgo.get_size_multiplier().to_string(3), + ", output_quantity_yesterday: ", rgo.get_output_quantity_yesterday().to_string(3), + ", revenue_yesterday: ", rgo.get_revenue_yesterday().to_string(3), + ", total owner income: ", rgo.get_total_owner_income_cache().to_string(3), + ", total employee income: ", rgo.get_total_employee_income_cache().to_string(3), + "\n\temployees:" + ); + + auto const& employee_count_per_type_cache=rgo.get_employee_count_per_type_cache(); + for (PopType const& pop_type : *employee_count_per_type_cache.get_keys()) { + const Pop::pop_size_t employees_of_type = employee_count_per_type_cache[pop_type]; + if (employees_of_type > 0) { + text += StringUtils::append_string_views("\n\t\t", std::to_string(employees_of_type), " ", pop_type.get_identifier()); + } + } + Logger::info("", text); + } +} + static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) { bool ret = true; @@ -72,9 +109,21 @@ static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) CountryInstanceManager const& country_instance_manager = game_manager.get_instance_manager()->get_country_instance_manager(); - print_ranking_list("Great Powers", country_instance_manager.get_great_powers()); + std::vector<CountryInstance*> const& great_powers = country_instance_manager.get_great_powers(); + print_ranking_list("Great Powers", great_powers); print_ranking_list("Secondary Powers", country_instance_manager.get_secondary_powers()); print_ranking_list("All countries", country_instance_manager.get_total_ranking()); + + Logger::info("===== RGO test... ====="); + for (size_t i = 0; i < std::min<size_t>(3, great_powers.size()); ++i) { + CountryInstance const& great_power = *great_powers[i]; + ProvinceInstance const* const capital_province = great_power.get_capital(); + if (capital_province == nullptr) { + Logger::warning(great_power.get_identifier(), " has no capital ProvinceInstance set."); + } else { + print_rgo(*capital_province); + } + } } else { Logger::error("Instance manager not available!"); ret = false; diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index eaa0692..670cbfc 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -38,32 +38,7 @@ void InstanceManager::update_gamestate() { currently_updating_gamestate = true; Logger::info("Update: ", today); - - if constexpr (ProvinceInstance::ADD_OWNER_CONTRIBUTION) { - // Calculate local province modifier sums first, then national country modifier sums, then loop over owned provinces - // adding their contributions to the owner country's modifier sum and loop over them again to add the country's total - // (including province contributions) to the provinces' modifier sum. This results in every country and province - // having a full copy of all the modifiers affecting them in their modifier sum. - map_instance.update_modifier_sums( - today, definition_manager.get_modifier_manager().get_static_modifier_cache() - ); - country_instance_manager.update_modifier_sums( - today, definition_manager.get_modifier_manager().get_static_modifier_cache() - ); - } else { - // Calculate national country modifier sums first, then local province modifier sums, adding province contributions - // to owner countries' modifier sums if each province has an owner. This results in every country having a full copy - // of all the modifiers affecting them in their modifier sum, but provinces only having their directly/locally applied - // modifiers in their modifier sum, hence requiring both province and owner country modifier effect values to be looked - // up and added together to get the full effect on the province. - country_instance_manager.update_modifier_sums( - today, definition_manager.get_modifier_manager().get_static_modifier_cache() - ); - map_instance.update_modifier_sums( - today, definition_manager.get_modifier_manager().get_static_modifier_cache() - ); - } - + update_modifier_sums(); // Update gamestate... map_instance.update_gamestate(today, definition_manager.get_define_manager()); country_instance_manager.update_gamestate( @@ -163,6 +138,11 @@ bool InstanceManager::load_bookmark(Bookmark const* new_bookmark) { map_instance, definition_manager.get_pop_manager().get_pop_types() ); + if (ret) { + update_modifier_sums(); + map_instance.initialise_for_new_game(definition_manager.get_modifier_manager().get_modifier_effect_cache()); + } + return ret; } @@ -204,3 +184,30 @@ bool InstanceManager::expand_selected_province_building(size_t building_index) { } return province->expand_building(building_index); } + +void InstanceManager::update_modifier_sums() { + if constexpr (ProvinceInstance::ADD_OWNER_CONTRIBUTION) { + // Calculate local province modifier sums first, then national country modifier sums, then loop over owned provinces + // adding their contributions to the owner country's modifier sum and loop over them again to add the country's total + // (including province contributions) to the provinces' modifier sum. This results in every country and province + // having a full copy of all the modifiers affecting them in their modifier sum. + map_instance.update_modifier_sums( + today, definition_manager.get_modifier_manager().get_static_modifier_cache() + ); + country_instance_manager.update_modifier_sums( + today, definition_manager.get_modifier_manager().get_static_modifier_cache() + ); + } else { + // Calculate national country modifier sums first, then local province modifier sums, adding province contributions + // to owner countries' modifier sums if each province has an owner. This results in every country having a full copy + // of all the modifiers affecting them in their modifier sum, but provinces only having their directly/locally applied + // modifiers in their modifier sum, hence requiring both province and owner country modifier effect values to be looked + // up and added together to get the full effect on the province. + country_instance_manager.update_modifier_sums( + today, definition_manager.get_modifier_manager().get_static_modifier_cache() + ); + map_instance.update_modifier_sums( + today, definition_manager.get_modifier_manager().get_static_modifier_cache() + ); + } +}
\ No newline at end of file diff --git a/src/openvic-simulation/InstanceManager.hpp b/src/openvic-simulation/InstanceManager.hpp index cfb5447..05ae412 100644 --- a/src/openvic-simulation/InstanceManager.hpp +++ b/src/openvic-simulation/InstanceManager.hpp @@ -33,6 +33,7 @@ namespace OpenVic { bool PROPERTY_CUSTOM_PREFIX(game_instance_setup, is); bool PROPERTY_CUSTOM_PREFIX(game_session_started, is); + void update_modifier_sums(); public: inline constexpr bool is_bookmark_loaded() const { return bookmark != nullptr; diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index ea30c0d..6e01649 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -139,7 +139,7 @@ namespace OpenVic { // TODO - population change over last 30 days fixed_point_t PROPERTY(national_consciousness); fixed_point_t PROPERTY(national_militancy); - IndexedMap<PopType, fixed_point_t> PROPERTY(pop_type_distribution); + IndexedMap<PopType, Pop::pop_size_t> PROPERTY(pop_type_distribution); size_t PROPERTY(national_focus_capacity) // TODO - national foci diff --git a/src/openvic-simulation/dataloader/Vic2PathSearch.cpp b/src/openvic-simulation/dataloader/Vic2PathSearch.cpp index a23d0ce..a1a125d 100644 --- a/src/openvic-simulation/dataloader/Vic2PathSearch.cpp +++ b/src/openvic-simulation/dataloader/Vic2PathSearch.cpp @@ -206,7 +206,7 @@ static fs::path _search_for_game_path(fs::path hint_path = {}) { } return empty_fail_result_callback("Could not parse VDF at '", current_path, "'."); } - std::optional current_node = *(parser.get_key_values()); + std::optional current_node = *parser.get_key_values(); // check "libraryfolders" list auto it = current_node.value().find("libraryfolders"); diff --git a/src/openvic-simulation/economy/GoodDefinition.cpp b/src/openvic-simulation/economy/GoodDefinition.cpp index 7ad5a87..ba5aff8 100644 --- a/src/openvic-simulation/economy/GoodDefinition.cpp +++ b/src/openvic-simulation/economy/GoodDefinition.cpp @@ -13,18 +13,18 @@ GoodDefinition::GoodDefinition( index_t new_index, GoodCategory const& new_category, fixed_point_t new_base_price, - bool new_available_from_start, - bool new_tradeable, - bool new_money, - bool new_overseas_penalty + bool new_is_available_from_start, + bool new_is_tradeable, + bool new_is_money, + bool new_counters_overseas_penalty ) : HasIdentifierAndColour { new_identifier, new_colour, false }, HasIndex { new_index }, 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 } {} + is_available_from_start { new_is_available_from_start }, + is_tradeable { new_is_tradeable }, + is_money { new_is_money }, + counters_overseas_penalty { new_counters_overseas_penalty } {} bool GoodDefinitionManager::add_good_category(std::string_view identifier) { if (identifier.empty()) { @@ -36,7 +36,7 @@ bool GoodDefinitionManager::add_good_category(std::string_view identifier) { bool GoodDefinitionManager::add_good_definition( std::string_view identifier, colour_t colour, GoodCategory const& category, fixed_point_t base_price, - bool available_from_start, bool tradeable, bool money, bool overseas_penalty + bool is_available_from_start, bool is_tradeable, bool is_money, bool has_overseas_penalty ) { if (identifier.empty()) { Logger::error("Invalid good identifier - empty!"); @@ -47,8 +47,8 @@ bool GoodDefinitionManager::add_good_definition( return false; } return good_definitions.add_item({ - identifier, colour, get_good_definition_count(), category, base_price, available_from_start, - tradeable, money, overseas_penalty + identifier, colour, get_good_definition_count(), category, base_price, is_available_from_start, + is_tradeable, is_money, has_overseas_penalty }); } @@ -68,19 +68,19 @@ bool GoodDefinitionManager::load_goods_file(ast::NodeCPtr root) { return expect_dictionary([this, &good_category](std::string_view key, ast::NodeCPtr value) -> bool { colour_t colour = colour_t::null(); fixed_point_t base_price; - bool available_from_start = true, tradeable = true; - bool money = false, overseas_penalty = false; + bool is_available_from_start = true, is_tradeable = true; + bool is_money = false, has_overseas_penalty = false; bool ret = expect_dictionary_keys( "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), "cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(base_price)), - "available_from_start", ZERO_OR_ONE, expect_bool(assign_variable_callback(available_from_start)), - "tradeable", ZERO_OR_ONE, expect_bool(assign_variable_callback(tradeable)), - "money", ZERO_OR_ONE, expect_bool(assign_variable_callback(money)), - "overseas_penalty", ZERO_OR_ONE, expect_bool(assign_variable_callback(overseas_penalty)) + "available_from_start", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_available_from_start)), + "tradeable", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_tradeable)), + "money", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_money)), + "overseas_penalty", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_overseas_penalty)) )(value); ret &= add_good_definition( - key, colour, good_category, base_price, available_from_start, tradeable, money, overseas_penalty + key, colour, good_category, base_price, is_available_from_start, is_tradeable, is_money, has_overseas_penalty ); return ret; })(good_category_value); diff --git a/src/openvic-simulation/economy/GoodDefinition.hpp b/src/openvic-simulation/economy/GoodDefinition.hpp index 823c1a3..7cb87cc 100644 --- a/src/openvic-simulation/economy/GoodDefinition.hpp +++ b/src/openvic-simulation/economy/GoodDefinition.hpp @@ -35,15 +35,15 @@ namespace OpenVic { private: GoodCategory const& PROPERTY(category); const fixed_point_t PROPERTY(base_price); - const bool PROPERTY_CUSTOM_PREFIX(available_from_start, is); - const bool PROPERTY_CUSTOM_PREFIX(tradeable, is); - const bool PROPERTY(money); - const bool PROPERTY(overseas_penalty); + const bool PROPERTY(is_available_from_start); + const bool PROPERTY(is_tradeable); + const bool PROPERTY(is_money); + const bool PROPERTY(counters_overseas_penalty); GoodDefinition( std::string_view new_identifier, colour_t new_colour, index_t new_index, GoodCategory const& new_category, - fixed_point_t new_base_price, bool new_available_from_start, bool new_tradeable, bool new_money, - bool new_overseas_penalty + fixed_point_t new_base_price, bool new_is_available_from_start, bool new_is_tradeable, bool new_is_money, + bool new_counters_overseas_penalty ); public: @@ -62,7 +62,7 @@ namespace OpenVic { bool add_good_definition( std::string_view identifier, colour_t colour, GoodCategory const& category, fixed_point_t base_price, - bool available_from_start, bool tradeable, bool money, bool overseas_penalty + bool is_available_from_start, bool is_tradeable, bool is_money, bool has_overseas_penalty ); bool load_goods_file(ast::NodeCPtr root); diff --git a/src/openvic-simulation/economy/GoodInstance.cpp b/src/openvic-simulation/economy/GoodInstance.cpp index 6fe3a2f..ac081c9 100644 --- a/src/openvic-simulation/economy/GoodInstance.cpp +++ b/src/openvic-simulation/economy/GoodInstance.cpp @@ -4,7 +4,7 @@ using namespace OpenVic; GoodInstance::GoodInstance(GoodDefinition const& new_good_definition) : HasIdentifierAndColour { new_good_definition }, good_definition { new_good_definition }, - price { new_good_definition.get_base_price() }, available { new_good_definition.is_available_from_start() } {} + price { new_good_definition.get_base_price() }, is_available { new_good_definition.get_is_available_from_start() } {} bool GoodInstanceManager::setup(GoodDefinitionManager const& good_definition_manager) { if (good_instances_are_locked()) { diff --git a/src/openvic-simulation/economy/GoodInstance.hpp b/src/openvic-simulation/economy/GoodInstance.hpp index 20370ef..74ec939 100644 --- a/src/openvic-simulation/economy/GoodInstance.hpp +++ b/src/openvic-simulation/economy/GoodInstance.hpp @@ -14,7 +14,7 @@ namespace OpenVic { private: GoodDefinition const& PROPERTY(good_definition); fixed_point_t PROPERTY(price); - bool PROPERTY(available); + bool PROPERTY(is_available); // TODO - supply, demand, actual bought GoodInstance(GoodDefinition const& new_good_definition); diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp index e5c0b80..65aa3fa 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp @@ -6,7 +6,7 @@ #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { - class ArtisanalProducer final { + struct ArtisanalProducer { private: ProductionType const& PROPERTY(production_type); GoodDefinition::good_definition_map_t PROPERTY(stockpile); diff --git a/src/openvic-simulation/economy/production/Employee.cpp b/src/openvic-simulation/economy/production/Employee.cpp new file mode 100644 index 0000000..569299a --- /dev/null +++ b/src/openvic-simulation/economy/production/Employee.cpp @@ -0,0 +1,8 @@ +#include "Employee.hpp" + +using namespace OpenVic; + +Employee::Employee(Pop& new_pop, const Pop::pop_size_t new_size) + : pop { new_pop }, + size { new_size } + {}
\ No newline at end of file diff --git a/src/openvic-simulation/economy/production/Employee.hpp b/src/openvic-simulation/economy/production/Employee.hpp new file mode 100644 index 0000000..8a09c31 --- /dev/null +++ b/src/openvic-simulation/economy/production/Employee.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "openvic-simulation/pop/Pop.hpp" + +namespace OpenVic { + struct Employee { + private: + Pop::pop_size_t PROPERTY_RW(size); + public: + Pop& pop; + Employee(Pop& new_pop, const Pop::pop_size_t new_size); + }; +}
\ No newline at end of file diff --git a/src/openvic-simulation/economy/production/FactoryProducer.hpp b/src/openvic-simulation/economy/production/FactoryProducer.hpp index 54ddfb8..9e660ba 100644 --- a/src/openvic-simulation/economy/production/FactoryProducer.hpp +++ b/src/openvic-simulation/economy/production/FactoryProducer.hpp @@ -8,7 +8,7 @@ #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { - class FactoryProducer final { + struct FactoryProducer { private: static constexpr uint8_t DAYS_OF_HISTORY = 7; using daily_profit_history_t = std::array<fixed_point_t, DAYS_OF_HISTORY>; diff --git a/src/openvic-simulation/economy/production/ProductionType.cpp b/src/openvic-simulation/economy/production/ProductionType.cpp index 7c4dfef..c5db641 100644 --- a/src/openvic-simulation/economy/production/ProductionType.cpp +++ b/src/openvic-simulation/economy/production/ProductionType.cpp @@ -9,33 +9,33 @@ Job::Job( PopType const* new_pop_type, effect_t new_effect_type, fixed_point_t new_effect_multiplier, - fixed_point_t new_desired_workforce_share + fixed_point_t new_amount ) : pop_type { new_pop_type }, effect_type { new_effect_type }, effect_multiplier { new_effect_multiplier }, - desired_workforce_share { new_desired_workforce_share } {} + amount { new_amount } {} ProductionType::ProductionType( - std::string_view new_identifier, - std::optional<Job> new_owner, + const std::string_view new_identifier, + const std::optional<Job> new_owner, std::vector<Job>&& new_jobs, - template_type_t new_template_type, - Pop::pop_size_t new_base_workforce_size, + const template_type_t new_template_type, + const Pop::pop_size_t new_base_workforce_size, GoodDefinition::good_definition_map_t&& new_input_goods, - GoodDefinition const* new_output_goods, - fixed_point_t new_base_output_quantity, + GoodDefinition const& new_output_good, + const fixed_point_t new_base_output_quantity, std::vector<bonus_t>&& new_bonuses, GoodDefinition::good_definition_map_t&& new_maintenance_requirements, - bool new_is_coastal, - bool new_is_farm, - bool new_is_mine + const bool new_is_coastal, + const bool new_is_farm, + const 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 }, + output_good { new_output_good }, base_output_quantity { new_base_output_quantity }, bonuses { std::move(new_bonuses) }, maintenance_requirements { std::move(new_maintenance_requirements) }, @@ -51,7 +51,9 @@ bool ProductionType::parse_scripts(DefinitionManager const& definition_manager) return ret; } -ProductionTypeManager::ProductionTypeManager() : rgo_owner_sprite { 0 } {} +ProductionTypeManager::ProductionTypeManager() : + good_to_rgo_production_type { nullptr }, + rgo_owner_sprite { 0 } {} node_callback_t ProductionTypeManager::_expect_job( GoodDefinitionManager const& good_definition_manager, PopManager const& pop_manager, callback_t<Job&&> callback @@ -92,19 +94,19 @@ node_callback_t ProductionTypeManager::_expect_job_list( } bool ProductionTypeManager::add_production_type( - std::string_view identifier, + const 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, + const ProductionType::template_type_t template_type, + const Pop::pop_size_t base_workforce_size, GoodDefinition::good_definition_map_t&& input_goods, - GoodDefinition const* output_goods, - fixed_point_t base_output_quantity, + GoodDefinition const* const output_good, + const fixed_point_t base_output_quantity, std::vector<ProductionType::bonus_t>&& bonuses, GoodDefinition::good_definition_map_t&& maintenance_requirements, - bool is_coastal, - bool is_farm, - bool is_mine + const bool is_coastal, + const bool is_farm, + const bool is_mine ) { if (identifier.empty()) { Logger::error("Invalid production type identifier - empty!"); @@ -121,7 +123,7 @@ bool ProductionTypeManager::add_production_type( return false; } - if (output_goods == nullptr) { + if (output_good == nullptr) { Logger::error("Output good for production type ", identifier, " was null!"); return false; } @@ -167,13 +169,25 @@ bool ProductionTypeManager::add_production_type( } const bool ret = production_types.add_item({ - identifier, owner, std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_goods, + identifier, owner, std::move(jobs), template_type, base_workforce_size, std::move(input_goods), *output_good, base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine }); + + if (ret && (template_type == RGO)) { + ProductionType const& production_type = production_types.get_items().back(); + ProductionType const*& current_rgo_pt = good_to_rgo_production_type[*output_good]; + if (current_rgo_pt == nullptr || (is_farm && !current_rgo_pt->is_farm())) { + // first rgo pt or farms are preferred (over mines) in V2 + current_rgo_pt = &production_type; + } + //else ignore, we already have an rgo pt + } + 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(); } + return ret; } @@ -224,7 +238,10 @@ bool ProductionTypeManager::load_production_types_file( } )(parser.get_file_node()); + /* Pass #3: actually load production types */ + good_to_rgo_production_type.set_keys(&good_definition_manager.get_good_definitions()); + reserve_more_production_types(expected_types); ret &= expect_dictionary( [this, &good_definition_manager, &pop_manager, &template_target_map, &template_node_map]( @@ -238,7 +255,7 @@ bool ProductionTypeManager::load_production_types_file( std::optional<Job> owner; std::vector<Job> jobs; ProductionType::template_type_t template_type { FACTORY }; - GoodDefinition const* output_goods = nullptr; + GoodDefinition const* output_good = nullptr; Pop::pop_size_t base_workforce_size = 0; GoodDefinition::good_definition_map_t input_goods, maintenance_requirements; fixed_point_t base_output_quantity = 0; @@ -271,7 +288,7 @@ bool ProductionTypeManager::load_production_types_file( "input_goods", ZERO_OR_ONE, good_definition_manager.expect_good_definition_decimal_map(move_variable_callback(input_goods)), "output_goods", ZERO_OR_ONE, - good_definition_manager.expect_good_definition_identifier(assign_variable_callback_pointer(output_goods)), + good_definition_manager.expect_good_definition_identifier(assign_variable_callback_pointer(output_good)), "value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(base_output_quantity)), "efficiency", ZERO_OR_ONE, good_definition_manager.expect_good_definition_decimal_map( move_variable_callback(maintenance_requirements) @@ -300,9 +317,10 @@ bool ProductionTypeManager::load_production_types_file( ret &= parse_node(node); ret &= add_production_type( - key, owner, std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_goods, + key, owner, std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_good, base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine ); + return ret; } )(parser.get_file_node()); diff --git a/src/openvic-simulation/economy/production/ProductionType.hpp b/src/openvic-simulation/economy/production/ProductionType.hpp index 5394938..f9b1778 100644 --- a/src/openvic-simulation/economy/production/ProductionType.hpp +++ b/src/openvic-simulation/economy/production/ProductionType.hpp @@ -20,13 +20,13 @@ namespace OpenVic { PopType const* PROPERTY(pop_type); effect_t PROPERTY(effect_type); fixed_point_t PROPERTY(effect_multiplier); - fixed_point_t PROPERTY(desired_workforce_share); + fixed_point_t PROPERTY(amount); Job( PopType const* new_pop_type, effect_t new_effect_type, fixed_point_t new_effect_multiplier, - fixed_point_t new_desired_workforce_share + fixed_point_t new_amount ); public: @@ -47,7 +47,7 @@ namespace OpenVic { const Pop::pop_size_t PROPERTY(base_workforce_size); GoodDefinition::good_definition_map_t PROPERTY(input_goods); - GoodDefinition const* PROPERTY(output_goods); + GoodDefinition const& PROPERTY(output_good); const fixed_point_t PROPERTY(base_output_quantity); std::vector<bonus_t> PROPERTY(bonuses); @@ -58,19 +58,19 @@ namespace OpenVic { const bool PROPERTY_CUSTOM_PREFIX(mine, is); ProductionType( - std::string_view new_identifier, - std::optional<Job> new_owner, + const std::string_view new_identifier, + const std::optional<Job> new_owner, std::vector<Job>&& new_jobs, - template_type_t new_template_type, - Pop::pop_size_t new_base_workforce_size, + const template_type_t new_template_type, + const Pop::pop_size_t new_base_workforce_size, GoodDefinition::good_definition_map_t&& new_input_goods, - GoodDefinition const* new_output_goods, - fixed_point_t new_base_output_quantity, + GoodDefinition const& new_output_good, + const fixed_point_t new_base_output_quantity, std::vector<bonus_t>&& new_bonuses, GoodDefinition::good_definition_map_t&& new_maintenance_requirements, - bool new_is_coastal, - bool new_is_farm, - bool new_is_mine + const bool new_is_coastal, + const bool new_is_farm, + const bool new_is_mine ); bool parse_scripts(DefinitionManager const& definition_manager); @@ -83,6 +83,7 @@ namespace OpenVic { private: IdentifierRegistry<ProductionType> IDENTIFIER_REGISTRY(production_type); PopType::sprite_t PROPERTY(rgo_owner_sprite); + IndexedMap<GoodDefinition, ProductionType const*> PROPERTY(good_to_rgo_production_type); NodeTools::node_callback_t _expect_job( GoodDefinitionManager const& good_definition_manager, PopManager const& pop_manager, @@ -97,19 +98,19 @@ namespace OpenVic { ProductionTypeManager(); bool add_production_type( - std::string_view identifier, + const std::string_view identifier, std::optional<Job> owner, - std::vector<Job>&& employees, - ProductionType::template_type_t template_type, - Pop::pop_size_t workforce, + std::vector<Job>&& jobs, + const ProductionType::template_type_t template_type, + const Pop::pop_size_t base_workforce_size, GoodDefinition::good_definition_map_t&& input_goods, - GoodDefinition const* output_goods, - fixed_point_t value, + GoodDefinition const* const output_good, + const fixed_point_t base_output_quantity, std::vector<ProductionType::bonus_t>&& bonuses, GoodDefinition::good_definition_map_t&& maintenance_requirements, - bool coastal, - bool farm, - bool mine + const bool is_coastal, + const bool is_farm, + const bool is_mine ); bool load_production_types_file( diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp index 9bf6f49..df91645 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp @@ -1,21 +1,343 @@ #include "ResourceGatheringOperation.hpp" +#include <vector> + +#include "openvic-simulation/economy/production/Employee.hpp" +#include "openvic-simulation/economy/production/ProductionType.hpp" +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/map/State.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" +#include "openvic-simulation/pop/Pop.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/utility/Logger.hpp" + using namespace OpenVic; ResourceGatheringOperation::ResourceGatheringOperation( - ProductionType const& new_production_type, + ProductionType const* new_production_type_nullable, fixed_point_t new_size_multiplier, fixed_point_t new_revenue_yesterday, fixed_point_t new_output_quantity_yesterday, fixed_point_t new_unsold_quantity_yesterday, - ordered_map<Pop*, Pop::pop_size_t>&& new_employees -) : production_type { new_production_type }, + std::vector<Employee>&& new_employees, + decltype(employee_count_per_type_cache)::keys_t const& pop_type_keys +) : production_type_nullable { new_production_type_nullable }, revenue_yesterday { new_revenue_yesterday }, output_quantity_yesterday { new_output_quantity_yesterday }, unsold_quantity_yesterday { new_unsold_quantity_yesterday }, size_multiplier { new_size_multiplier }, - employees { std::move(new_employees) } {} + employees { std::move(new_employees) }, + max_employee_count_cache { 0 }, + total_employees_count_cache { 0 }, + total_paid_employees_count_cache { 0 }, + total_owner_income_cache { }, + total_employee_income_cache { }, + employee_count_per_type_cache { &pop_type_keys } +{ } -ResourceGatheringOperation::ResourceGatheringOperation( - ProductionType const& new_production_type, fixed_point_t new_size_multiplier -) : ResourceGatheringOperation { new_production_type, new_size_multiplier, 0, 0, 0, {} } {} +ResourceGatheringOperation::ResourceGatheringOperation(decltype(employee_count_per_type_cache)::keys_t const& pop_type_keys) : ResourceGatheringOperation { + nullptr, fixed_point_t::_0(), + fixed_point_t::_0(), fixed_point_t::_0(), + fixed_point_t::_0(), {}, pop_type_keys +} {} + +void ResourceGatheringOperation::initialise_for_new_game(ProvinceInstance& location, ModifierEffectCache const& modifier_effect_cache) { + if (production_type_nullable == nullptr) { + output_quantity_yesterday = 0; + revenue_yesterday = 0; + return; + } + + ProductionType const& production_type = *production_type_nullable; + const fixed_point_t size_modifier = calculate_size_modifier(location, modifier_effect_cache); + const Pop::pop_size_t total_worker_count_in_province = update_size_and_return_total_worker_count(location, modifier_effect_cache, size_modifier); + hire(location, total_worker_count_in_province); + Pop::pop_size_t total_owner_count_in_state_cache = 0; + std::vector<Pop*> owner_pops_cache {}; + output_quantity_yesterday = produce(location, owner_pops_cache, total_owner_count_in_state_cache, modifier_effect_cache, size_modifier); + revenue_yesterday = output_quantity_yesterday * production_type.get_output_good().get_base_price(); //TODO sell on market + pay_employees(location, revenue_yesterday, total_worker_count_in_province, owner_pops_cache, total_owner_count_in_state_cache); +} + +Pop::pop_size_t ResourceGatheringOperation::update_size_and_return_total_worker_count( + ProvinceInstance& location, + ModifierEffectCache const& modifier_effect_cache, + const fixed_point_t size_modifier +) { + if (production_type_nullable == nullptr) { + size_multiplier = fixed_point_t::_0(); + max_employee_count_cache = fixed_point_t::_0(); + return fixed_point_t::_0(); + } + + Pop::pop_size_t total_worker_count_in_province = 0; //not counting equivalents + ProductionType const& production_type = *production_type_nullable; + std::vector<Job> const& jobs = production_type.get_jobs(); + //can't use pop_type_distribution as it is not filled correctly yet (possibly due to equivalent pop type conversion) + for (Pop const& pop : location.get_pops()){ + PopType const* pop_type = pop.get_type(); + for(Job const& job : jobs) { + if (job.get_pop_type() == pop_type) { + total_worker_count_in_province += pop.get_size(); + break; + } + } + } + + const fixed_point_t base_workforce_size = production_type.get_base_workforce_size(); + if (size_modifier == fixed_point_t::_0()) { + size_multiplier = 0; + } else { + size_multiplier = ((total_worker_count_in_province / (size_modifier * base_workforce_size)).ceil() * fixed_point_t::_1_50()).floor(); + } + max_employee_count_cache = (size_modifier * size_multiplier * base_workforce_size).floor(); + return total_worker_count_in_province; +} + +fixed_point_t ResourceGatheringOperation::calculate_size_modifier(ProvinceInstance const& location, ModifierEffectCache const& modifier_effect_cache) const { + if (production_type_nullable == nullptr) { + return fixed_point_t::_1(); + } + + ProductionType const& production_type = *production_type_nullable; + + fixed_point_t size_modifier = fixed_point_t::_1(); + if (production_type.is_farm()) { + size_modifier += location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_farm_rgo_size_global()) + + location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_farm_rgo_size_local()); + } + if (production_type.is_mine()) { + size_modifier += location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_mine_rgo_size_global()) + + location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_mine_rgo_size_local()); + } + auto const& good_effects = modifier_effect_cache.get_good_effects()[production_type.get_output_good()]; + size_modifier += location.get_modifier_effect_value_nullcheck(good_effects.get_rgo_size()); + return size_modifier > fixed_point_t::_0() ? size_modifier : fixed_point_t::_0(); +} + +void ResourceGatheringOperation::hire(ProvinceInstance& location, Pop::pop_size_t available_worker_count) { + total_employees_count_cache = 0; + total_paid_employees_count_cache=0; + if (production_type_nullable == nullptr) { + employees.clear(); + employee_count_per_type_cache.fill(fixed_point_t::_0()); + return; + } + + ProductionType const& production_type = *production_type_nullable; + if (max_employee_count_cache <= 0) { return; } + if (available_worker_count <= 0) { return; } + + fixed_point_t proportion_to_hire; + if (max_employee_count_cache >= available_worker_count) { + //hire everyone + proportion_to_hire = fixed_point_t::_1(); + } else { + //hire all pops proportionally + const fixed_point_t max_worker_count_real = max_employee_count_cache, available_worker_count_real = available_worker_count; + proportion_to_hire = max_worker_count_real / available_worker_count_real; + } + + std::vector<Job> const& jobs = production_type.get_jobs(); + for (Pop& pop : location.get_mutable_pops()){ + PopType const& pop_type = *pop.get_type(); + for(Job const& job : jobs) { + if (job.get_pop_type() == &pop_type) { + const Pop::pop_size_t pop_size_to_hire = static_cast<Pop::pop_size_t>((proportion_to_hire * pop.get_size()).floor()); + employee_count_per_type_cache[pop_type] += pop_size_to_hire; + employees.emplace_back(pop, pop_size_to_hire); + total_employees_count_cache += pop_size_to_hire; + if (!pop_type.get_is_slave()) { + total_paid_employees_count_cache += pop_size_to_hire; + } + break; + } + } + } +} + +fixed_point_t ResourceGatheringOperation::produce( + ProvinceInstance& location, + std::vector<Pop*>& owner_pops_cache, + Pop::pop_size_t& total_owner_count_in_state_cache, + ModifierEffectCache const& modifier_effect_cache, + const fixed_point_t size_modifier +) { + if (size_modifier == fixed_point_t::_0()){ + return fixed_point_t::_0(); + } + + total_owner_count_in_state_cache = 0; + owner_pops_cache = {}; + if (production_type_nullable == nullptr || max_employee_count_cache <= 0) { + return fixed_point_t::_0(); + } + + ProductionType const& production_type = *production_type_nullable; + fixed_point_t throughput_multiplier = fixed_point_t::_1(); + fixed_point_t output_multilpier = fixed_point_t::_1(); + + std::optional<Job> const& owner = production_type.get_owner(); + if (owner.has_value()) { + Job const& owner_job = owner.value(); + PopType const* owner_job_pop_type_nullable = owner_job.get_pop_type(); + if (owner_job_pop_type_nullable == nullptr) { + Logger::error("Owner job for ", production_type.get_identifier(), " has nullptr as pop_type."); + return fixed_point_t::_0(); + } + PopType const& owner_pop_type = *owner_job_pop_type_nullable; + State const* state_nullable = location.get_state(); + if (state_nullable == nullptr) { + Logger::error("Province ", location.get_identifier(), " has no state."); + return fixed_point_t::_0(); + } + State const& state = *state_nullable; + Pop::pop_size_t state_population = 0; //state.get_total_population() is not filled yet + std::vector<ProvinceInstance*> const& provinces_in_state = state.get_provinces(); + for (ProvinceInstance* const province_nullable : provinces_in_state) { + if (province_nullable == nullptr) { + Logger::error("State ", state.get_identifier(), " has nullptr in provinces."); + return fixed_point_t::_0(); + } + ProvinceInstance& province = *province_nullable; + for (Pop& pop : province.get_mutable_pops()){ + state_population += pop.get_size(); + if (&owner_pop_type == pop.get_type()) { + owner_pops_cache.push_back(&pop); + total_owner_count_in_state_cache += pop.get_size(); + } + } + } + + if (total_owner_count_in_state_cache > 0) { + switch (owner_job.get_effect_type()) { + case Job::effect_t::OUTPUT: + output_multilpier += owner_job.get_effect_multiplier() * total_owner_count_in_state_cache / state_population; + break; + case Job::effect_t::THROUGHPUT: + throughput_multiplier += owner_job.get_effect_multiplier() * total_owner_count_in_state_cache / state_population; + break; + default: + Logger::error("Invalid job effect in RGO ",production_type.get_identifier()); + break; + } + } + } + + throughput_multiplier += location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_rgo_throughput()) + +location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_local_rgo_throughput()); + output_multilpier += location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_rgo_output()) + +location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_local_rgo_output()); + + if (production_type.is_farm()) { + throughput_multiplier += location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_farm_rgo_throughput_global()); + output_multilpier += location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_farm_rgo_output_global()) + + location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_farm_rgo_output_local()); + } + if (production_type.is_mine()) { + throughput_multiplier += location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_mine_rgo_throughput_global()); + output_multilpier += location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_mine_rgo_output_global()) + + location.get_modifier_effect_value_nullcheck(modifier_effect_cache.get_mine_rgo_output_local()); + } + auto const& good_effects = modifier_effect_cache.get_good_effects()[production_type.get_output_good()]; + throughput_multiplier += location.get_modifier_effect_value_nullcheck(good_effects.get_rgo_goods_throughput()); + output_multilpier += location.get_modifier_effect_value_nullcheck(good_effects.get_rgo_goods_output()); + + fixed_point_t throughput_from_workers = fixed_point_t::_0(); + fixed_point_t output_from_workers = fixed_point_t::_1(); + for (PopType const& pop_type : *employee_count_per_type_cache.get_keys()) { + const Pop::pop_size_t employees_of_type = employee_count_per_type_cache[pop_type]; + + for(Job const& job : production_type.get_jobs()) { + if (job.get_pop_type() != &pop_type) { + continue; + } + + fixed_point_t const effect_multiplier = job.get_effect_multiplier(); + fixed_point_t relative_to_workforce = fixed_point_t::parse(employees_of_type) / fixed_point_t::parse(max_employee_count_cache); + fixed_point_t const amount = job.get_amount(); + if (effect_multiplier != fixed_point_t::_1() && relative_to_workforce > amount) { + relative_to_workforce = amount; + } + switch (job.get_effect_type()) { + case Job::effect_t::OUTPUT: + output_from_workers += effect_multiplier * relative_to_workforce; + break; + case Job::effect_t::THROUGHPUT: + throughput_from_workers += effect_multiplier * relative_to_workforce; + break; + default: + Logger::error("Invalid job effect in RGO ",production_type.get_identifier()); + break; + } + } + } + + //if province is overseas multiply by (1 + overseas penalty) + + return production_type.get_base_output_quantity() + * size_modifier * size_multiplier + * throughput_multiplier * throughput_from_workers + * output_multilpier * output_from_workers; +} + +void ResourceGatheringOperation::pay_employees( + ProvinceInstance& location, + const fixed_point_t revenue, + const Pop::pop_size_t total_worker_count_in_province, + std::vector<Pop*>& owner_pops_cache, + const Pop::pop_size_t total_owner_count_in_state_cache +) { + total_owner_income_cache = 0; + total_employee_income_cache = 0; + if (production_type_nullable == nullptr || revenue <= 0 || total_worker_count_in_province <= 0) { + if (revenue < 0) { Logger::error("Negative revenue for province ", location.get_identifier()); } + if (total_worker_count_in_province < 0) { Logger::error("Negative total worker count for province ", location.get_identifier()); } + return; + } + + ProductionType const& production_type = *production_type_nullable; + + fixed_point_t revenue_left = revenue; + if (total_owner_count_in_state_cache > 0) { + Job const& owner_job = production_type.get_owner().value(); + PopType const* owner_job_pop_type_nullable = owner_job.get_pop_type(); + + fixed_point_t owner_share = (fixed_point_t::_2() * total_owner_count_in_state_cache / total_worker_count_in_province); + constexpr fixed_point_t upper_limit = fixed_point_t::_0_50(); + if (owner_share > upper_limit) { + owner_share = upper_limit; + } + + for(Pop* owner_pop_nullable : owner_pops_cache) { + Pop& owner_pop = *owner_pop_nullable; + const fixed_point_t income_for_this_pop = revenue_left * owner_share * owner_pop.get_size() / total_owner_count_in_state_cache; + owner_pop.add_rgo_owner_income(income_for_this_pop); + total_owner_income_cache += income_for_this_pop; + } + revenue_left *= (fixed_point_t::_1() - owner_share); + } + + if (total_paid_employees_count_cache > 0) { + for (Employee& employee : employees) { + Pop& employee_pop = employee.pop; + PopType const* employee_pop_type_nullable = employee_pop.get_type(); + if (employee_pop_type_nullable == nullptr) { + Logger::error("employee has nullptr pop_type."); + return; + } + PopType const& employee_pop_type = *employee_pop_type_nullable; + if (employee_pop_type.get_is_slave()) { + continue; + } + + const Pop::pop_size_t employee_size = employee.get_size(); + const fixed_point_t income_for_this_pop = revenue_left * employee_size / total_paid_employees_count_cache; + employee_pop.add_rgo_worker_income(income_for_this_pop); + total_employee_income_cache += income_for_this_pop; + } + } else { + //scenario slaves only + //Money is removed from system in Victoria 2. + } +}
\ No newline at end of file diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp index d71c569..a15e87d 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp @@ -1,25 +1,64 @@ #pragma once +#include "openvic-simulation/economy/production/Employee.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" +#include "openvic-simulation/pop/Pop.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { - class ResourceGatheringOperation final { + struct ResourceGatheringOperation { private: - ProductionType const& PROPERTY(production_type); + ProductionType const* PROPERTY_RW(production_type_nullable); fixed_point_t PROPERTY(revenue_yesterday); fixed_point_t PROPERTY(output_quantity_yesterday); fixed_point_t PROPERTY(unsold_quantity_yesterday); - fixed_point_t PROPERTY(size_multiplier); - ordered_map<Pop*, Pop::pop_size_t> PROPERTY(employees); + fixed_point_t PROPERTY_RW(size_multiplier); + std::vector<Employee> PROPERTY(employees); + Pop::pop_size_t PROPERTY(max_employee_count_cache); + Pop::pop_size_t PROPERTY(total_employees_count_cache); + Pop::pop_size_t PROPERTY(total_paid_employees_count_cache); + fixed_point_t PROPERTY(total_owner_income_cache); + fixed_point_t PROPERTY(total_employee_income_cache); + IndexedMap<PopType, Pop::pop_size_t> PROPERTY(employee_count_per_type_cache); + + Pop::pop_size_t update_size_and_return_total_worker_count( + ProvinceInstance& location, + ModifierEffectCache const& modifier_effect_cache, + const fixed_point_t size_modifier + ); + fixed_point_t calculate_size_modifier(ProvinceInstance const& location, ModifierEffectCache const& modifier_effect_cache) const; + void hire(ProvinceInstance& location, const Pop::pop_size_t available_worker_count); + fixed_point_t produce( + ProvinceInstance& location, + std::vector<Pop*>& owner_pops_cache, + Pop::pop_size_t& total_owner_count_in_state_cache, + ModifierEffectCache const& modifier_effect_cache, + const fixed_point_t size_modifier + ); + void pay_employees( + ProvinceInstance& location, + const fixed_point_t revenue, + const Pop::pop_size_t total_worker_count_in_province, + std::vector<Pop*>& owner_pops_cache, + const Pop::pop_size_t total_owner_count_in_state_cache + ); public: ResourceGatheringOperation( - ProductionType const& new_production_type, fixed_point_t new_size_multiplier, fixed_point_t new_revenue_yesterday, - fixed_point_t new_output_quantity_yesterday, fixed_point_t new_unsold_quantity_yesterday, - ordered_map<Pop*, Pop::pop_size_t>&& new_employees + ProductionType const* new_production_type_nullable, + fixed_point_t new_size_multiplier, + fixed_point_t new_revenue_yesterday, + fixed_point_t new_output_quantity_yesterday, + fixed_point_t new_unsold_quantity_yesterday, + std::vector<Employee>&& new_employees, + decltype(employee_count_per_type_cache)::keys_t const& pop_type_keys ); - ResourceGatheringOperation(ProductionType const& new_production_type, fixed_point_t new_size_multiplier); + ResourceGatheringOperation(decltype(employee_count_per_type_cache)::keys_t const& pop_type_keys); + constexpr bool is_valid() const { + return production_type_nullable != nullptr; + } + void initialise_for_new_game(ProvinceInstance& location, ModifierEffectCache const& modifier_effect_cache); }; } diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp index 60fcbe4..bc92f48 100644 --- a/src/openvic-simulation/history/ProvinceHistory.cpp +++ b/src/openvic-simulation/history/ProvinceHistory.cpp @@ -1,7 +1,9 @@ #include "ProvinceHistory.hpp" #include "openvic-simulation/DefinitionManager.hpp" +#include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" +#include "openvic-simulation/utility/Logger.hpp" using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -57,6 +59,9 @@ bool ProvinceHistoryMap::_load_history_entry( }; }; + constexpr bool allow_empty_true = true; + constexpr bool do_warn = true; + return expect_dictionary_keys_and_default( [this, &definition_manager, &building_type_manager, &entry]( std::string_view key, ast::NodeCPtr value) -> bool { @@ -98,7 +103,20 @@ bool ProvinceHistoryMap::_load_history_entry( 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_definition_manager.expect_good_definition_identifier(assign_variable_callback_pointer_opt(entry.rgo)), + good_definition_manager.expect_good_definition_identifier_or_string( + [&definition_manager, &entry](GoodDefinition const& rgo_good) ->bool { + entry.rgo_production_type_nullable = definition_manager.get_economy_manager().get_production_type_manager().get_good_to_rgo_production_type()[rgo_good]; + if (entry.rgo_production_type_nullable == nullptr) { + Logger::error(entry.province.get_identifier(), " has trade_goods ", rgo_good.get_identifier(), " which has no rgo production type defined."); + //we expect the good to have an rgo production type + //Victoria 2 treats this as null, but clearly the modder wanted there to be a good + return false; + } + return true; + }, + allow_empty_true, //could be explicitly setting trade_goods to null + do_warn //could be typo in good identifier + ), "life_rating", ZERO_OR_ONE, expect_uint<ProvinceInstance::life_rating_t>(assign_variable_callback(entry.life_rating)), "terrain", ZERO_OR_ONE, terrain_type_manager.expect_terrain_type_identifier( assign_variable_callback_pointer_opt(entry.terrain_type) diff --git a/src/openvic-simulation/history/ProvinceHistory.hpp b/src/openvic-simulation/history/ProvinceHistory.hpp index 99ea2af..f44fc81 100644 --- a/src/openvic-simulation/history/ProvinceHistory.hpp +++ b/src/openvic-simulation/history/ProvinceHistory.hpp @@ -32,7 +32,7 @@ namespace OpenVic { std::optional<ProvinceInstance::colony_status_t> PROPERTY(colonial); std::optional<bool> PROPERTY(slave); ordered_map<CountryDefinition const*, bool> PROPERTY(cores); - std::optional<GoodDefinition const*> PROPERTY(rgo); + std::optional<ProductionType const*> PROPERTY(rgo_production_type_nullable); std::optional<ProvinceInstance::life_rating_t> PROPERTY(life_rating); std::optional<TerrainType const*> PROPERTY(terrain_type); ordered_map<BuildingType const*, BuildingType::level_t> PROPERTY(province_buildings); diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp index 47ae40e..4a4a1e5 100644 --- a/src/openvic-simulation/map/MapInstance.cpp +++ b/src/openvic-simulation/map/MapInstance.cpp @@ -93,6 +93,7 @@ bool MapInstance::apply_history_to_provinces( if (history_map != nullptr) { ProvinceHistoryEntry const* pop_history_entry = nullptr; + ProductionType const* rgo_production_type_nullable = nullptr; for (auto const& [entry_date, entry] : history_map->get_entries()) { if(entry_date > date) { @@ -101,6 +102,10 @@ bool MapInstance::apply_history_to_provinces( } } else { province.apply_history_to_province(*entry, country_manager); + std::optional<ProductionType const*> const& rgo_production_type_nullable_optional = entry->get_rgo_production_type_nullable(); + if (rgo_production_type_nullable_optional.has_value()) { + rgo_production_type_nullable = rgo_production_type_nullable_optional.value(); + } } if (!entry->get_pops().empty()) { @@ -114,6 +119,8 @@ bool MapInstance::apply_history_to_provinces( province.add_pop_vec(pop_history_entry->get_pops()); province.setup_pop_test_values(issue_manager); } + + ret&=province.set_rgo_production_type_nullable(rgo_production_type_nullable); } } } @@ -153,3 +160,9 @@ void MapInstance::tick(Date today) { province.tick(today); } } + +void MapInstance::initialise_for_new_game(ModifierEffectCache const& modifier_effect_cache){ + for (ProvinceInstance& province : province_instances.get_items()) { + province.initialise_for_new_game(modifier_effect_cache); + } +}
\ No newline at end of file diff --git a/src/openvic-simulation/map/MapInstance.hpp b/src/openvic-simulation/map/MapInstance.hpp index e7c623e..a580c55 100644 --- a/src/openvic-simulation/map/MapInstance.hpp +++ b/src/openvic-simulation/map/MapInstance.hpp @@ -55,5 +55,6 @@ namespace OpenVic { void update_modifier_sums(Date today, StaticModifierCache const& static_modifier_cache); void update_gamestate(Date today, DefineManager const& define_manager); void tick(Date today); + void initialise_for_new_game(ModifierEffectCache const& modifier_effect_cache); }; } diff --git a/src/openvic-simulation/map/Mapmode.cpp b/src/openvic-simulation/map/Mapmode.cpp index f951a05..88eb2eb 100644 --- a/src/openvic-simulation/map/Mapmode.cpp +++ b/src/openvic-simulation/map/Mapmode.cpp @@ -179,7 +179,7 @@ bool MapmodeManager::setup_mapmodes() { "mapmode_terrain_type", get_colour_mapmode(&ProvinceInstance::get_terrain_type) }, { - "mapmode_rgo", get_colour_mapmode(&ProvinceInstance::get_rgo) + "mapmode_rgo", get_colour_mapmode(&ProvinceInstance::get_rgo_good) }, { "mapmode_infrastructure", diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index cbb23bd..64a104b 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -1,6 +1,8 @@ #include "ProvinceInstance.hpp" #include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/economy/production/ProductionType.hpp" +#include "openvic-simulation/economy/production/ResourceGatheringOperation.hpp" #include "openvic-simulation/history/ProvinceHistory.hpp" #include "openvic-simulation/map/Crime.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" @@ -10,6 +12,8 @@ #include "openvic-simulation/misc/Define.hpp" #include "openvic-simulation/modifier/StaticModifierCache.hpp" #include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/pop/Pop.hpp" +#include "openvic-simulation/utility/Logger.hpp" using namespace OpenVic; @@ -29,7 +33,7 @@ ProvinceInstance::ProvinceInstance( event_modifiers {}, slave { false }, crime { nullptr }, - rgo { nullptr }, + rgo { pop_type_keys }, buildings { "buildings", false }, armies {}, navies {}, @@ -41,6 +45,25 @@ ProvinceInstance::ProvinceInstance( religion_distribution {}, max_supported_regiments { 0 } {} +GoodDefinition const* ProvinceInstance::get_rgo_good() const { + if (!rgo.is_valid()) { return nullptr; } + return &(rgo.get_production_type_nullable()->get_output_good()); +} +bool ProvinceInstance::set_rgo_production_type_nullable(ProductionType const* rgo_production_type_nullable) { + bool is_valid_operation = true; + if (rgo_production_type_nullable != nullptr) { + ProductionType const& rgo_production_type = *rgo_production_type_nullable; + if (rgo_production_type.get_template_type() != ProductionType::template_type_t::RGO) { + Logger::error("Tried setting province ", get_identifier(), " rgo to ", rgo_production_type.get_identifier(), " which is not of template_type RGO."); + is_valid_operation = false; + } + is_valid_operation&=convert_rgo_worker_pops_to_equivalent(rgo_production_type); + } + + rgo.set_production_type_nullable(rgo_production_type_nullable); + return is_valid_operation; +} + bool ProvinceInstance::set_owner(CountryInstance* new_owner) { bool ret = true; @@ -176,7 +199,7 @@ void ProvinceInstance::_update_pops(DefineManager const& define_manager) { average_consciousness += pop.get_consciousness(); average_militancy += pop.get_militancy(); - pop_type_distribution[pop.get_type()] += pop.get_size(); + pop_type_distribution[*pop.get_type()] += pop.get_size(); ideology_distribution += pop.get_ideologies(); culture_distribution[&pop.get_culture()] += pop.get_size(); religion_distribution[&pop.get_religion()] += pop.get_size(); @@ -311,6 +334,24 @@ std::vector<ModifierSum::modifier_entry_t> ProvinceInstance::get_contributing_mo } } +bool ProvinceInstance::convert_rgo_worker_pops_to_equivalent(ProductionType const& production_type) { + bool is_valid_operation = true; + std::vector<Job> const& jobs = production_type.get_jobs(); + for(Pop& pop : pops) { + for(Job const& job : jobs) { + PopType const* const job_pop_type = job.get_pop_type(); + PopType const* old_pop_type = pop.get_type(); + if (job_pop_type != old_pop_type) { + PopType const* const equivalent = old_pop_type->get_equivalent(); + if (job_pop_type == equivalent) { + is_valid_operation&=pop.convert_to_equivalent(); + } + } + } + } + return is_valid_operation; +} + void ProvinceInstance::update_gamestate(Date today, DefineManager const& define_manager) { for (BuildingInstance& building : buildings.get_items()) { building.update_gamestate(today); @@ -405,7 +446,7 @@ bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const& ent ret &= remove_core(country_manager.get_country_instance_from_definition(*country)); } } - set_optional(rgo, entry.get_rgo()); + set_optional(life_rating, entry.get_life_rating()); set_optional(terrain_type, entry.get_terrain_type()); for (auto const& [building, level] : entry.get_province_buildings()) { @@ -425,8 +466,16 @@ bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const& ent return ret; } +void ProvinceInstance::initialise_for_new_game(ModifierEffectCache const& modifier_effect_cache) { + rgo.initialise_for_new_game(*this, modifier_effect_cache); +} + void ProvinceInstance::setup_pop_test_values(IssueManager const& issue_manager) { for (Pop& pop : pops) { pop.setup_pop_test_values(issue_manager); } } + +plf::colony<Pop>& ProvinceInstance::get_mutable_pops() { + return pops; +}
\ No newline at end of file diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp index e7c0326..ba800a9 100644 --- a/src/openvic-simulation/map/ProvinceInstance.hpp +++ b/src/openvic-simulation/map/ProvinceInstance.hpp @@ -3,11 +3,12 @@ #include <plf_colony.h> #include "openvic-simulation/economy/BuildingInstance.hpp" +#include "openvic-simulation/economy/production/ProductionType.hpp" +#include "openvic-simulation/economy/production/ResourceGatheringOperation.hpp" #include "openvic-simulation/military/UnitInstance.hpp" #include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/modifier/ModifierSum.hpp" #include "openvic-simulation/pop/Pop.hpp" -#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" @@ -81,8 +82,7 @@ namespace OpenVic { bool PROPERTY(slave); Crime const* PROPERTY_RW(crime); - // TODO - change this into a factory-like structure - GoodDefinition const* PROPERTY(rgo); + ResourceGatheringOperation PROPERTY(rgo); IdentifierRegistry<BuildingInstance> IDENTIFIER_REGISTRY(building); ordered_set<ArmyInstance*> PROPERTY(armies); ordered_set<NavyInstance*> PROPERTY(navies); @@ -95,7 +95,7 @@ namespace OpenVic { fixed_point_t PROPERTY(average_literacy); fixed_point_t PROPERTY(average_consciousness); fixed_point_t PROPERTY(average_militancy); - IndexedMap<PopType, fixed_point_t> PROPERTY(pop_type_distribution); + IndexedMap<PopType, Pop::pop_size_t> PROPERTY(pop_type_distribution); IndexedMap<Ideology, fixed_point_t> PROPERTY(ideology_distribution); fixed_point_map_t<Culture const*> PROPERTY(culture_distribution); fixed_point_map_t<Religion const*> PROPERTY(religion_distribution); @@ -108,6 +108,7 @@ namespace OpenVic { void _add_pop(Pop&& pop); void _update_pops(DefineManager const& define_manager); + bool convert_rgo_worker_pops_to_equivalent(ProductionType const& production_type); public: ProvinceInstance(ProvinceInstance&&) = default; @@ -123,6 +124,9 @@ namespace OpenVic { return controller; } + GoodDefinition const* get_rgo_good() const; + bool set_rgo_production_type_nullable(ProductionType const* rgo_production_type_nullable); + bool set_owner(CountryInstance* new_owner); bool set_controller(CountryInstance* new_controller); bool add_core(CountryInstance& new_core); @@ -155,6 +159,9 @@ namespace OpenVic { bool setup(BuildingTypeManager const& building_type_manager); bool apply_history_to_province(ProvinceHistoryEntry const& entry, CountryInstanceManager& country_manager); + void initialise_for_new_game(ModifierEffectCache const& modifier_effect_cache); + void setup_pop_test_values(IssueManager const& issue_manager); + plf::colony<Pop>& get_mutable_pops(); }; } diff --git a/src/openvic-simulation/map/State.cpp b/src/openvic-simulation/map/State.cpp index 619f97b..31ea05f 100644 --- a/src/openvic-simulation/map/State.cpp +++ b/src/openvic-simulation/map/State.cpp @@ -66,16 +66,19 @@ void State::update_gamestate() { const int32_t potential_workforce_in_state = 0; // sum of worker pops, regardless of employment const int32_t potential_employment_in_state = 0; // sum of (factory level * production method base_workforce_size) - fixed_point_t unclamped_score_per_factory = fixed_point_t::_0(); - - if (potential_employment_in_state > 0) { - unclamped_score_per_factory = (fixed_point_t { potential_workforce_in_state } / fixed_point_t::_100()).floor() * 400 / potential_employment_in_state; + fixed_point_t workforce_scalar; + constexpr fixed_point_t min_workforce_scalar = fixed_point_t::_0_20(); + constexpr fixed_point_t max_workforce_scalar = fixed_point_t::_4(); + if (potential_employment_in_state <= 0) { + workforce_scalar = min_workforce_scalar; } else { - industrial_power = total_factory_levels_in_state * std::clamp( - unclamped_score_per_factory, - fixed_point_t::_0_20(), fixed_point_t::_4() + workforce_scalar = std::clamp( + (fixed_point_t { potential_workforce_in_state } / 100).floor() * 400 / potential_employment_in_state, + min_workforce_scalar, max_workforce_scalar ); } + + industrial_power = total_factory_levels_in_state * workforce_scalar; } /* Whether two provinces in the same region should be grouped into the same state or not. diff --git a/src/openvic-simulation/map/State.hpp b/src/openvic-simulation/map/State.hpp index 596206a..a39eea6 100644 --- a/src/openvic-simulation/map/State.hpp +++ b/src/openvic-simulation/map/State.hpp @@ -29,7 +29,7 @@ namespace OpenVic { fixed_point_t PROPERTY(average_literacy); fixed_point_t PROPERTY(average_consciousness); fixed_point_t PROPERTY(average_militancy); - IndexedMap<PopType, fixed_point_t> PROPERTY(pop_type_distribution); + IndexedMap<PopType, Pop::pop_size_t> PROPERTY(pop_type_distribution); fixed_point_t PROPERTY(industrial_power); diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index 4b81e5d..0f0de69 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -9,6 +9,7 @@ #include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/politics/Issue.hpp" #include "openvic-simulation/politics/Rebel.hpp" +#include "openvic-simulation/utility/Logger.hpp" #include "openvic-simulation/utility/TslHelper.hpp" using namespace OpenVic; @@ -19,7 +20,7 @@ using enum PopType::income_type_t; PopBase::PopBase( 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 }, militancy { new_militancy }, +) : type { &new_type }, culture { new_culture }, religion { new_religion }, size { new_size }, militancy { new_militancy }, consciousness { new_consciousness }, rebel_type { new_rebel_type } {} Pop::Pop(PopBase const& pop_base, decltype(ideologies)::keys_t const& ideology_keys) @@ -115,6 +116,17 @@ void Pop::setup_pop_test_values(IssueManager const& issue_manager) { luxury_needs_fulfilled = test_range(); } +bool Pop::convert_to_equivalent() { + PopType const* const equivalent = get_type()->get_equivalent(); + if (equivalent == nullptr) { + Logger::error("Tried to convert pop of type ", get_type()->get_identifier(), " to equivalent, but there is no equivalent."); + return false; + } + + type = equivalent; + return true; +} + void Pop::set_location(ProvinceInstance const& new_location) { if (location != &new_location) { location = &new_location; @@ -131,7 +143,7 @@ void Pop::set_location(ProvinceInstance const& new_location) { void Pop::update_gamestate( DefineManager const& define_manager, CountryInstance const* owner, fixed_point_t const& pop_size_per_regiment_multiplier ) { - if (type.get_can_be_recruited()) { + if (type->get_can_be_recruited()) { if ( size < define_manager.get_min_pop_size_for_regiment() || owner == nullptr || !RegimentType::allowed_cultures_check_culture_in_country(owner->get_allowed_regiment_cultures(), culture, *owner) @@ -145,6 +157,10 @@ void Pop::update_gamestate( } } +//TODO store income +void Pop::add_rgo_owner_income(const fixed_point_t income) {} +void Pop::add_rgo_worker_income(const fixed_point_t income) {} + Strata::Strata(std::string_view new_identifier) : HasIdentifier { new_identifier } {} PopType::PopType( diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp index 59a7794..88c397a 100644 --- a/src/openvic-simulation/pop/Pop.hpp +++ b/src/openvic-simulation/pop/Pop.hpp @@ -35,7 +35,7 @@ namespace OpenVic { using pop_size_t = int32_t; protected: - PopType const& PROPERTY_ACCESS(type, protected); + PopType const* PROPERTY_ACCESS(type, protected); Culture const& PROPERTY_ACCESS(culture, protected); Religion const& PROPERTY_ACCESS(religion, protected); pop_size_t PROPERTY_ACCESS(size, protected); @@ -95,6 +95,7 @@ namespace OpenVic { Pop& operator=(Pop&&) = delete; void setup_pop_test_values(IssueManager const& issue_manager); + bool convert_to_equivalent(); void set_location(ProvinceInstance const& new_location); @@ -102,6 +103,9 @@ namespace OpenVic { DefineManager const& define_manager, CountryInstance const* owner, fixed_point_t const& pop_size_per_regiment_multiplier ); + + void add_rgo_owner_income(const fixed_point_t income); + void add_rgo_worker_income(const fixed_point_t income); }; struct Strata : HasIdentifier { |