From b25fde2110a33c71f050421e726cb5adfec4b0c2 Mon Sep 17 00:00:00 2001 From: wvpm <24685035+wvpm@users.noreply.github.com> Date: Wed, 5 Feb 2025 21:16:21 +0100 Subject: [PATCH] Pop needs & spending --- src/openvic-simulation/InstanceManager.cpp | 3 +- .../country/CountryInstance.cpp | 24 +- .../country/CountryInstance.hpp | 4 + .../economy/GoodInstance.cpp | 6 +- .../economy/GoodInstance.hpp | 2 + .../economy/production/ArtisanalProducer.cpp | 224 ++++++++------- .../economy/production/ArtisanalProducer.hpp | 14 +- .../ArtisanalProducerFactoryPattern.cpp | 13 +- .../ArtisanalProducerFactoryPattern.hpp | 9 +- .../production/ResourceGatheringOperation.cpp | 19 +- .../production/ResourceGatheringOperation.hpp | 3 - src/openvic-simulation/map/MapInstance.cpp | 9 +- src/openvic-simulation/map/MapInstance.hpp | 2 + .../map/ProvinceInstance.cpp | 14 +- .../map/ProvinceInstance.hpp | 12 +- src/openvic-simulation/pop/Pop.cpp | 258 ++++++++++++++---- src/openvic-simulation/pop/Pop.hpp | 62 ++++- .../pop/PopValuesFromProvince.cpp | 53 ++++ .../pop/PopValuesFromProvince.hpp | 35 +++ 19 files changed, 563 insertions(+), 203 deletions(-) create mode 100644 src/openvic-simulation/pop/PopValuesFromProvince.cpp create mode 100644 src/openvic-simulation/pop/PopValuesFromProvince.hpp diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index 7ee59841..0a935409 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -16,7 +16,6 @@ InstanceManager::InstanceManager( good_instance_manager { new_definition_manager.get_economy_manager().get_good_definition_manager() }, market_instance { new_definition_manager.get_define_manager().get_country_defines(), good_instance_manager }, artisanal_producer_factory_pattern { - market_instance, new_definition_manager.get_modifier_manager().get_modifier_effect_cache(), new_definition_manager.get_economy_manager().get_production_type_manager() }, @@ -94,6 +93,7 @@ bool InstanceManager::setup() { definition_manager.get_economy_manager().get_building_type_manager(), market_instance, definition_manager.get_modifier_manager().get_modifier_effect_cache(), + definition_manager.get_define_manager().get_pops_defines(), definition_manager.get_pop_manager().get_stratas(), definition_manager.get_pop_manager().get_pop_types(), definition_manager.get_politics_manager().get_ideology_manager().get_ideologies() @@ -151,6 +151,7 @@ bool InstanceManager::load_bookmark(Bookmark const* new_bookmark) { country_instance_manager, // TODO - the following argument is for generating test pop attributes definition_manager.get_politics_manager().get_issue_manager(), + market_instance, artisanal_producer_factory_pattern ); diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 6b786408..b9fcb990 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -671,7 +671,15 @@ bool CountryInstance::modify_invention_unlock( return false; } + const bool invention_was_unlocked = unlock_level > 0; unlock_level += unlock_level_change; + if (invention_was_unlocked != (unlock_level > 0)) { + if (invention_was_unlocked) { + inventions_count--; + } else { + inventions_count++; + } + } bool ret = true; @@ -1389,14 +1397,24 @@ void CountryInstance::tick(InstanceManager& instance_manager) { //income_from_gold = country_defines.gold_to_cash_rate * sum (total_good_production * good.get_base_price()) for each good with is_money=true } + +void CountryInstance::report_pop_need_consumption(PopType const& pop_type, GoodDefinition const& good, const fixed_point_t quantity) { + //TODO +} +void CountryInstance::report_artisan_input_consumption(ProductionType const& production_type, GoodDefinition const& good, const fixed_point_t quantity) { + //TODO +} void CountryInstance::report_artisan_output(ProductionType const& production_type, const fixed_point_t quantity) { - //TODO record artisan output + //TODO +} +void CountryInstance::report_factory_input_consumption(ProductionType const& production_type, GoodDefinition const& good, const fixed_point_t quantity) { + //TODO } void CountryInstance::report_factory_output(ProductionType const& production_type, const fixed_point_t quantity) { - //TODO record factory output + //TODO } void CountryInstance::report_rgo_output(GoodDefinition const& good, const fixed_point_t quantity) { - //TODO record rgo output + //TODO } CountryInstance::good_data_t& CountryInstance::get_good_data(GoodInstance const& good_instance) { diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index 6631aebc..a370fadf 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -134,6 +134,7 @@ namespace OpenVic { /* Technology */ IndexedMap PROPERTY(technology_unlock_levels); IndexedMap PROPERTY(invention_unlock_levels); + int32_t PROPERTY(inventions_count); Technology const* PROPERTY(current_research, nullptr); fixed_point_t PROPERTY(invested_research_points); fixed_point_t PROPERTY(current_research_cost); @@ -518,7 +519,10 @@ namespace OpenVic { void update_gamestate(InstanceManager& instance_manager); void tick(InstanceManager& instance_manager); + void report_pop_need_consumption(PopType const& pop_type, GoodDefinition const& good, const fixed_point_t quantity); + void report_artisan_input_consumption(ProductionType const& production_type, GoodDefinition const& good, const fixed_point_t quantity); void report_artisan_output(ProductionType const& production_type, const fixed_point_t quantity); + void report_factory_input_consumption(ProductionType const& production_type, GoodDefinition const& good, const fixed_point_t quantity); void report_factory_output(ProductionType const& production_type, const fixed_point_t quantity); void report_rgo_output(GoodDefinition const& good, const fixed_point_t quantity); diff --git a/src/openvic-simulation/economy/GoodInstance.cpp b/src/openvic-simulation/economy/GoodInstance.cpp index ef933f82..cd68be3e 100644 --- a/src/openvic-simulation/economy/GoodInstance.cpp +++ b/src/openvic-simulation/economy/GoodInstance.cpp @@ -48,6 +48,8 @@ void GoodInstance::update_next_price_limits() { absolute_minimum_price, price - max_price_change ); + + price_inverse = fixed_point_t::_1() / price; } void GoodInstance::add_buy_up_to_order(GoodBuyUpToOrder&& buy_up_to_order) { @@ -195,7 +197,7 @@ void GoodInstance::execute_orders() { //sell below max_next_price if (game_rules_manager.get_use_optimal_pricing()) { //drop price while remaining_supply > 0 && new_price > min_next_price - while (remaining_supply > 0) { + while (remaining_supply > fixed_point_t::_0()) { const fixed_point_t possible_price = money_left_to_spend_sum / remaining_supply; if (possible_price >= new_price) { @@ -272,8 +274,6 @@ void GoodInstance::execute_orders() { price = new_price; update_next_price_limits(); } - - price = new_price; } void GoodInstance::record_price_history() { diff --git a/src/openvic-simulation/economy/GoodInstance.hpp b/src/openvic-simulation/economy/GoodInstance.hpp index 33c31cc8..dffb258f 100644 --- a/src/openvic-simulation/economy/GoodInstance.hpp +++ b/src/openvic-simulation/economy/GoodInstance.hpp @@ -29,6 +29,7 @@ namespace OpenVic { fixed_point_t absolute_maximum_price; fixed_point_t absolute_minimum_price; + //TODO get from pool instead of storing as field //only used inside execute_orders() std::vector quantity_bought_per_order; std::vector purchasing_power_per_order; @@ -38,6 +39,7 @@ namespace OpenVic { std::vector market_sell_orders; fixed_point_t PROPERTY(price); + fixed_point_t PROPERTY(price_inverse); fixed_point_t PROPERTY(price_change_yesterday); fixed_point_t PROPERTY(max_next_price); fixed_point_t PROPERTY(min_next_price); diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp index 5c3fa05a..bc68f178 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp @@ -1,10 +1,7 @@ #include "ArtisanalProducer.hpp" -#include "openvic-simulation/economy/GoodDefinition.hpp" +#include "openvic-simulation/economy/GoodInstance.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" -#include "openvic-simulation/economy/trading/BuyResult.hpp" -#include "openvic-simulation/economy/trading/MarketInstance.hpp" -#include "openvic-simulation/economy/trading/SellResult.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/modifier/ModifierEffectCache.hpp" #include "openvic-simulation/pop/Pop.hpp" @@ -12,19 +9,24 @@ using namespace OpenVic; ArtisanalProducer::ArtisanalProducer( - MarketInstance& new_market_instance, + GoodInstanceManager const& new_good_instance_manager, ModifierEffectCache const& new_modifier_effect_cache, GoodDefinition::good_definition_map_t&& new_stockpile, ProductionType const& new_production_type, fixed_point_t new_current_production -) : market_instance { new_market_instance }, +) : good_instance_manager { new_good_instance_manager }, modifier_effect_cache { new_modifier_effect_cache }, stockpile { std::move(new_stockpile) }, production_type { new_production_type }, current_production { new_current_production } - {} + { + max_quantity_to_buy_per_good.reserve(new_production_type.get_input_goods().size()); + } void ArtisanalProducer::artisan_tick(Pop& pop) { + max_quantity_to_buy_per_good.clear(); + + //TODO get from pool instead of create & destroy each time GoodDefinition::good_definition_map_t goods_to_buy_and_max_price { }; GoodDefinition::good_definition_map_t demand { }; @@ -32,119 +34,139 @@ void ArtisanalProducer::artisan_tick(Pop& pop) { fixed_point_t inputs_bought_fraction = fixed_point_t::_1(), inputs_bought_numerator= fixed_point_t::_1(), inputs_bought_denominator= fixed_point_t::_1(); - if (!production_type.get_input_goods().empty()) { - GoodInstanceManager const& good_instance_manager = market_instance.get_good_instance_manager(); + for (auto const& [input_good_ptr, base_desired_quantity] : production_type.get_input_goods()) { + const fixed_point_t desired_quantity = demand[input_good_ptr] = base_desired_quantity * pop.get_size() / production_type.get_base_workforce_size(); + if (desired_quantity == fixed_point_t::_0()) { + continue; + } + const fixed_point_t good_bought_fraction = stockpile[input_good_ptr] / desired_quantity; + if (good_bought_fraction < inputs_bought_fraction) { + inputs_bought_fraction = good_bought_fraction; + inputs_bought_numerator = stockpile[input_good_ptr]; + inputs_bought_denominator = desired_quantity; + } + GoodInstance const& good = good_instance_manager.get_good_instance_from_definition(*input_good_ptr); + goods_to_buy_and_max_price[input_good_ptr] = good.get_max_next_price(); + } + + //Produce output + fixed_point_t produce_left_to_sell = current_production = production_type.get_base_output_quantity() + * inputs_bought_numerator * pop.get_size() + / (inputs_bought_denominator * production_type.get_base_workforce_size()); + + CountryInstance* get_country_to_report_economy_nullable = pop.get_location()->get_country_to_report_economy(); + if (current_production > fixed_point_t::_0()) { + if (get_country_to_report_economy_nullable != nullptr) { + get_country_to_report_economy_nullable->report_artisan_output(production_type, current_production); + } + } + + if (inputs_bought_fraction > fixed_point_t::_0()) { for (auto const& [input_good_ptr, base_desired_quantity] : production_type.get_input_goods()) { - const fixed_point_t desired_quantity = demand[input_good_ptr] = base_desired_quantity * pop.get_size() / production_type.get_base_workforce_size(); - if (desired_quantity == fixed_point_t::_0()) { - continue; + const fixed_point_t desired_quantity = demand[input_good_ptr]; + fixed_point_t& good_stockpile = stockpile[input_good_ptr]; + + //Consume input good + fixed_point_t consumed_quantity = desired_quantity * inputs_bought_numerator / inputs_bought_denominator; + if (get_country_to_report_economy_nullable != nullptr) { + get_country_to_report_economy_nullable->report_artisan_input_consumption( + production_type, + *input_good_ptr, + consumed_quantity + ); } - const fixed_point_t good_bought_fraction = stockpile[input_good_ptr] / desired_quantity; - if (good_bought_fraction < inputs_bought_fraction) { - inputs_bought_fraction = good_bought_fraction; - inputs_bought_numerator = stockpile[input_good_ptr]; - inputs_bought_denominator = desired_quantity; + if (input_good_ptr == &production_type.get_output_good()) { + if (OV_unlikely(consumed_quantity > produce_left_to_sell)) { + consumed_quantity -= produce_left_to_sell; + produce_left_to_sell = fixed_point_t::_0(); + } else { + produce_left_to_sell -= consumed_quantity; + consumed_quantity = fixed_point_t::_0(); + } } - GoodInstance const& good = good_instance_manager.get_good_instance_from_definition(*input_good_ptr); - goods_to_buy_and_max_price[input_good_ptr] = good.get_max_next_price(); - } - if (inputs_bought_fraction > fixed_point_t::_0()) { - for (auto const& [input_good_ptr, base_desired_quantity] : production_type.get_input_goods()) { - const fixed_point_t desired_quantity = demand[input_good_ptr]; - fixed_point_t& good_stockpile = stockpile[input_good_ptr]; - //Consume input good - good_stockpile = std::max( - fixed_point_t::_0(), - good_stockpile - desired_quantity * inputs_bought_numerator / inputs_bought_denominator - ); + good_stockpile = std::max( + fixed_point_t::_0(), + good_stockpile - consumed_quantity + ); - if (good_stockpile >= desired_quantity) { - goods_to_buy_and_max_price.erase(input_good_ptr); - } + if (good_stockpile >= desired_quantity) { + goods_to_buy_and_max_price.erase(input_good_ptr); } } + } - const fixed_point_t total_cash_to_spend = pop.get_cash(); - if (total_cash_to_spend > 0 && !goods_to_buy_and_max_price.empty()) { - //Figure out the optimal amount of goods to buy based on their price, stockpiled quantiy & demand - fixed_point_t max_possible_satisfaction_numerator= fixed_point_t::_1(), - max_possible_satisfaction_denominator= fixed_point_t::_1(); - - bool at_or_below_optimum = false; - while (!at_or_below_optimum) { - at_or_below_optimum = true; - fixed_point_t total_demand_value = fixed_point_t::_0(); - fixed_point_t total_stockpile_value = fixed_point_t::_0(); - for (auto const& [input_good_ptr, max_price] : goods_to_buy_and_max_price) { - total_demand_value += max_price * demand[input_good_ptr]; - total_stockpile_value += max_price * stockpile[input_good_ptr]; - } + const fixed_point_t total_cash_to_spend = pop.get_cash(); + if (total_cash_to_spend > fixed_point_t::_0() && !goods_to_buy_and_max_price.empty()) { + //Figure out the optimal amount of goods to buy based on their price, stockpiled quantiy & demand + fixed_point_t max_possible_satisfaction_numerator= fixed_point_t::_1(), + max_possible_satisfaction_denominator= fixed_point_t::_1(); + + bool at_or_below_optimum = false; + while (!at_or_below_optimum) { + at_or_below_optimum = true; + fixed_point_t total_demand_value = fixed_point_t::_0(); + fixed_point_t total_stockpile_value = fixed_point_t::_0(); + for (auto const& [input_good_ptr, max_price] : goods_to_buy_and_max_price) { + total_demand_value += max_price * demand[input_good_ptr]; + total_stockpile_value += max_price * stockpile[input_good_ptr]; + } - if ( total_demand_value == fixed_point_t::_0()) { + if ( total_demand_value == fixed_point_t::_0()) { + max_possible_satisfaction_numerator = fixed_point_t::_1(); + max_possible_satisfaction_denominator = fixed_point_t::_1(); + } else { + max_possible_satisfaction_numerator = total_stockpile_value + total_cash_to_spend; + max_possible_satisfaction_denominator = total_demand_value; + if (max_possible_satisfaction_numerator > max_possible_satisfaction_denominator) { max_possible_satisfaction_numerator = fixed_point_t::_1(); max_possible_satisfaction_denominator = fixed_point_t::_1(); - } else { - max_possible_satisfaction_numerator = total_stockpile_value + total_cash_to_spend; - max_possible_satisfaction_denominator = total_demand_value; - if (max_possible_satisfaction_numerator > max_possible_satisfaction_denominator) { - max_possible_satisfaction_numerator = fixed_point_t::_1(); - max_possible_satisfaction_denominator = fixed_point_t::_1(); - } - } - - for (auto const& [input_good_ptr, max_price] : goods_to_buy_and_max_price) { - const fixed_point_t optimal_quantity = demand[input_good_ptr] * max_possible_satisfaction_numerator / max_possible_satisfaction_denominator; - if (stockpile[input_good_ptr] >= optimal_quantity) { - goods_to_buy_and_max_price.erase(input_good_ptr); - at_or_below_optimum = false; - } } } - //Place buy orders for each input - for (auto const& [input_good_ptr, max_price] : goods_to_buy_and_max_price) { - const fixed_point_t good_demand = demand[input_good_ptr]; - fixed_point_t& good_stockpile = stockpile[input_good_ptr]; - const fixed_point_t optimal_quantity = good_demand * max_possible_satisfaction_numerator / max_possible_satisfaction_denominator; - const fixed_point_t max_quantity_to_buy = good_demand - good_stockpile; - if (max_quantity_to_buy > 0) { - const fixed_point_t money_to_spend = optimal_quantity * max_price; - market_instance.place_buy_up_to_order({ - *input_good_ptr, - max_quantity_to_buy, - money_to_spend, - [this, &pop, &good_stockpile](const BuyResult buy_result) -> void { - if (buy_result.get_quantity_bought() > fixed_point_t::_0()) { - pop.add_artisan_inputs_expense(buy_result.get_money_spent()); - good_stockpile += buy_result.get_quantity_bought(); - } - } - }); + erase_if(goods_to_buy_and_max_price, [ + this, + &demand, + &at_or_below_optimum, + max_possible_satisfaction_numerator, + max_possible_satisfaction_denominator + ](auto const& pair)->bool { + GoodDefinition const* const input_good_ptr = pair.first; + const fixed_point_t optimal_quantity = demand[input_good_ptr] * max_possible_satisfaction_numerator / max_possible_satisfaction_denominator; + if (stockpile[input_good_ptr] < optimal_quantity) { + return false; } + at_or_below_optimum = false; + return true; + }); + } + + //Place buy orders for each input + for (auto const& [input_good_ptr, max_price] : goods_to_buy_and_max_price) { + const fixed_point_t good_demand = demand[input_good_ptr]; + fixed_point_t& good_stockpile = stockpile[input_good_ptr]; + const fixed_point_t optimal_quantity = good_demand * max_possible_satisfaction_numerator / max_possible_satisfaction_denominator; + const fixed_point_t max_quantity_to_buy = good_demand - good_stockpile; + if (max_quantity_to_buy > fixed_point_t::_0()) { + const fixed_point_t money_to_spend = optimal_quantity * max_price; + max_quantity_to_buy_per_good[input_good_ptr] = max_quantity_to_buy; + pop.artisanal_buy(*input_good_ptr, max_quantity_to_buy, money_to_spend); } } } - //Produce output & sell - current_production = production_type.get_base_output_quantity() - * inputs_bought_numerator * pop.get_size() - / (inputs_bought_denominator * production_type.get_base_workforce_size()); - - if (current_production > 0) { - CountryInstance* get_country_to_report_economy_nullable = pop.get_location()->get_country_to_report_economy(); - if (get_country_to_report_economy_nullable != nullptr) { - get_country_to_report_economy_nullable->report_artisan_output(production_type, current_production); - } + pop.artisanal_sell(produce_left_to_sell); +} - market_instance.place_market_sell_order({ - production_type.get_output_good(), - current_production, - [&pop](const SellResult sell_result) -> void { - if (sell_result.get_money_gained() > 0) { - pop.add_artisanal_income(sell_result.get_money_gained()); - } - } - }); +fixed_point_t ArtisanalProducer::add_to_stockpile(GoodDefinition const& good, const fixed_point_t quantity) { + if (OV_unlikely(quantity < fixed_point_t::_0())) { + Logger::error("Attempted to add negative quantity ",quantity," of ", good.get_identifier(), " to stockpile."); + return fixed_point_t::_0(); } + + fixed_point_t& max_quantity_to_buy = max_quantity_to_buy_per_good[&good]; + const fixed_point_t quantity_added_to_stockpile = std::min(quantity, max_quantity_to_buy); + stockpile[&good] += quantity_added_to_stockpile; + max_quantity_to_buy -= quantity_added_to_stockpile; + return quantity_added_to_stockpile; } \ No newline at end of file diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp index 2081bc63..d5d24039 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.hpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.hpp @@ -1,31 +1,37 @@ #pragma once -#include "openvic-simulation/economy/GoodInstance.hpp" +#include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { - struct MarketInstance; + struct GoodInstanceManager; struct ModifierEffectCache; struct Pop; struct ProductionType; struct ArtisanalProducer { private: - MarketInstance& market_instance; + GoodInstanceManager const& good_instance_manager; ModifierEffectCache const& modifier_effect_cache; GoodDefinition::good_definition_map_t stockpile; + + //only used during day tick (from artisan_tick() until MarketInstance.execute_orders()) + GoodDefinition::good_definition_map_t max_quantity_to_buy_per_good; + ProductionType const& PROPERTY(production_type); fixed_point_t PROPERTY(current_production); public: ArtisanalProducer( - MarketInstance& new_market_instance, + GoodInstanceManager const& new_good_instance_manager, ModifierEffectCache const& new_modifier_effect_cache, GoodDefinition::good_definition_map_t&& new_stockpile, ProductionType const& new_production_type, fixed_point_t new_current_production ); void artisan_tick(Pop& pop); + //adds to stockpile up to max_quantity_to_buy and returns quantity added to stockpile + fixed_point_t add_to_stockpile(GoodDefinition const& good, const fixed_point_t quantity); }; } diff --git a/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp index 4a27ffba..286d4539 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.cpp @@ -1,22 +1,21 @@ #include "ArtisanalProducerFactoryPattern.hpp" +#include "openvic-simulation/economy/GoodInstance.hpp" using namespace OpenVic; ArtisanalProducerFactoryPattern::ArtisanalProducerFactoryPattern( - MarketInstance& new_market_instance, ModifierEffectCache const& new_modifier_effect_cache, ProductionTypeManager const& new_production_type_manager ) : index { -1 }, unlocked_artisanal_production_types { }, - market_instance { new_market_instance }, modifier_effect_cache { new_modifier_effect_cache }, production_type_manager { new_production_type_manager } { } -std::unique_ptr ArtisanalProducerFactoryPattern::CreateNewArtisanalProducer() { +std::unique_ptr ArtisanalProducerFactoryPattern::CreateNewArtisanalProducer(GoodInstanceManager const& good_instance_manager) { //TODO update unlocked_artisanal_production_types when goods are unlocked if (index == -1) { - recalculate_unlocked_artisanal_production_types(); + recalculate_unlocked_artisanal_production_types(good_instance_manager); } if (OV_unlikely(unlocked_artisanal_production_types.size() == 0)) { @@ -29,7 +28,7 @@ std::unique_ptr ArtisanalProducerFactoryPattern::CreateNewArt ProductionType const* random_artisanal_production_type = unlocked_artisanal_production_types[index]; return std::make_unique( - market_instance, + good_instance_manager, modifier_effect_cache, GoodDefinition::good_definition_map_t{}, *random_artisanal_production_type, @@ -37,11 +36,11 @@ std::unique_ptr ArtisanalProducerFactoryPattern::CreateNewArt ); } -void ArtisanalProducerFactoryPattern::recalculate_unlocked_artisanal_production_types() { +void ArtisanalProducerFactoryPattern::recalculate_unlocked_artisanal_production_types(GoodInstanceManager const& good_instance_manager) { unlocked_artisanal_production_types.clear(); for (ProductionType const& production_type : production_type_manager.get_production_types()) { if (production_type.get_template_type() == ProductionType::template_type_t::ARTISAN) { - GoodInstance const& good_instance = market_instance.get_good_instance_manager().get_good_instance_from_definition( + GoodInstance const& good_instance = good_instance_manager.get_good_instance_from_definition( production_type.get_output_good() ); if (!good_instance.get_is_available()) { diff --git a/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp index 0ac95974..d9b8c8d8 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp @@ -5,29 +5,24 @@ #include "openvic-simulation/economy/production/ArtisanalProducer.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" -#include "openvic-simulation/economy/trading/MarketInstance.hpp" #include "openvic-simulation/modifier/ModifierEffectCache.hpp" namespace OpenVic { - struct Pop; - struct ArtisanalProducerFactoryPattern { private: int32_t index; std::vector unlocked_artisanal_production_types; - MarketInstance& market_instance; ModifierEffectCache const& modifier_effect_cache; ProductionTypeManager const& production_type_manager; - void recalculate_unlocked_artisanal_production_types(); + void recalculate_unlocked_artisanal_production_types(GoodInstanceManager const& good_instance_manager); public: ArtisanalProducerFactoryPattern( - MarketInstance& new_market_instance, ModifierEffectCache const& new_modifier_effect_cache, ProductionTypeManager const& new_production_type_manager ); - std::unique_ptr CreateNewArtisanalProducer(); + std::unique_ptr CreateNewArtisanalProducer(GoodInstanceManager const& good_instance_manager); }; } \ No newline at end of file diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp index 2636f33b..a01a0aa9 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp @@ -16,7 +16,6 @@ using namespace OpenVic; ResourceGatheringOperation::ResourceGatheringOperation( MarketInstance& new_market_instance, - ModifierEffectCache const& new_modifier_effect_cache, ProductionType const* new_production_type_nullable, fixed_point_t new_size_multiplier, fixed_point_t new_revenue_yesterday, @@ -25,7 +24,6 @@ ResourceGatheringOperation::ResourceGatheringOperation( std::vector&& new_employees, decltype(employee_count_per_type_cache)::keys_type const& pop_type_keys ) : market_instance { new_market_instance }, - modifier_effect_cache { new_modifier_effect_cache }, location_ptr { nullptr }, production_type_nullable { new_production_type_nullable }, revenue_yesterday { new_revenue_yesterday }, @@ -43,11 +41,9 @@ ResourceGatheringOperation::ResourceGatheringOperation( ResourceGatheringOperation::ResourceGatheringOperation( MarketInstance& new_market_instance, - ModifierEffectCache const& new_modifier_effect_cache, decltype(employee_count_per_type_cache)::keys_type const& pop_type_keys ) : ResourceGatheringOperation { new_market_instance, - new_modifier_effect_cache, nullptr, fixed_point_t::_0(), fixed_point_t::_0(), fixed_point_t::_0(), fixed_point_t::_0(), {}, pop_type_keys @@ -69,6 +65,7 @@ void ResourceGatheringOperation::initialise_rgo_size_multiplier() { } ProvinceInstance& location = *location_ptr; + ModifierEffectCache const& modifier_effect_cache = location.get_modifier_effect_cache(); ProductionType const& production_type = *production_type_nullable; std::vector const& jobs = production_type.get_jobs(); IndexedMap const& province_pop_type_distribution = location.get_pop_type_distribution(); @@ -103,6 +100,7 @@ fixed_point_t ResourceGatheringOperation::calculate_size_modifier() const { return fixed_point_t::_1(); } ProvinceInstance& location = *location_ptr; + ModifierEffectCache const& modifier_effect_cache = location.get_modifier_effect_cache(); ProductionType const& production_type = *production_type_nullable; fixed_point_t size_modifier = fixed_point_t::_1(); @@ -157,7 +155,7 @@ void ResourceGatheringOperation::rgo_tick() { } output_quantity_yesterday = produce(total_owner_count_in_state_cache); - if (output_quantity_yesterday > 0) { + if (output_quantity_yesterday > fixed_point_t::_0()) { CountryInstance* get_country_to_report_economy_nullable = location.get_country_to_report_economy(); if (get_country_to_report_economy_nullable != nullptr) { get_country_to_report_economy_nullable->report_rgo_output(production_type.get_output_good(), output_quantity_yesterday); @@ -237,6 +235,7 @@ fixed_point_t ResourceGatheringOperation::produce(const pop_size_t total_owner_c } ProvinceInstance& location = *location_ptr; + ModifierEffectCache const& modifier_effect_cache = location.get_modifier_effect_cache(); ProductionType const& production_type = *production_type_nullable; fixed_point_t throughput_multiplier = fixed_point_t::_1(); @@ -350,9 +349,13 @@ void ResourceGatheringOperation::pay_employees( total_owner_income_cache = 0; total_employee_income_cache = 0; - if (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()); } + if (revenue <= fixed_point_t::_0() || total_worker_count_in_province <= 0) { + if (revenue < fixed_point_t::_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; } diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp index f33601ac..760e08a3 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.hpp @@ -14,7 +14,6 @@ namespace OpenVic { struct ResourceGatheringOperation { private: MarketInstance& market_instance; - ModifierEffectCache const& modifier_effect_cache; ProvinceInstance* location_ptr; ProductionType const* PROPERTY_RW(production_type_nullable); fixed_point_t PROPERTY(revenue_yesterday); @@ -42,7 +41,6 @@ namespace OpenVic { public: ResourceGatheringOperation( MarketInstance& new_market_instance, - ModifierEffectCache const& new_modifier_effect_cache, ProductionType const* new_production_type_nullable, fixed_point_t new_size_multiplier, fixed_point_t new_revenue_yesterday, @@ -54,7 +52,6 @@ namespace OpenVic { ResourceGatheringOperation( MarketInstance& new_market_instance, - ModifierEffectCache const& new_modifier_effect_cache, decltype(employee_count_per_type_cache)::keys_type const& pop_type_keys ); diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp index 19414525..b03f7bcc 100644 --- a/src/openvic-simulation/map/MapInstance.cpp +++ b/src/openvic-simulation/map/MapInstance.cpp @@ -39,6 +39,7 @@ bool MapInstance::setup( BuildingTypeManager const& building_type_manager, MarketInstance& market_instance, ModifierEffectCache const& modifier_effect_cache, + PopsDefines const& pop_defines, decltype(ProvinceInstance::population_by_strata)::keys_type const& strata_keys, decltype(ProvinceInstance::pop_type_distribution)::keys_type const& pop_type_keys, decltype(ProvinceInstance::ideology_distribution)::keys_type const& ideology_keys @@ -60,6 +61,7 @@ bool MapInstance::setup( if (province_instances.add_item({ market_instance, modifier_effect_cache, + pop_defines, province, strata_keys, pop_type_keys, @@ -97,6 +99,7 @@ bool MapInstance::apply_history_to_provinces( const Date date, CountryInstanceManager& country_manager, IssueManager const& issue_manager, + MarketInstance& market_instance, ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern ) { bool ret = true; @@ -131,7 +134,11 @@ bool MapInstance::apply_history_to_provinces( if (pop_history_entry == nullptr) { Logger::warning("No pop history entry for province ", province.get_identifier(), " for date ", date); } else { - ret &= province.add_pop_vec(pop_history_entry->get_pops(), artisanal_producer_factory_pattern); + ret &= province.add_pop_vec( + pop_history_entry->get_pops(), + market_instance, + artisanal_producer_factory_pattern + ); province.setup_pop_test_values(issue_manager); } diff --git a/src/openvic-simulation/map/MapInstance.hpp b/src/openvic-simulation/map/MapInstance.hpp index 22f6cb9a..74d45623 100644 --- a/src/openvic-simulation/map/MapInstance.hpp +++ b/src/openvic-simulation/map/MapInstance.hpp @@ -47,6 +47,7 @@ namespace OpenVic { BuildingTypeManager const& building_type_manager, MarketInstance& market_instance, ModifierEffectCache const& modifier_effect_cache, + PopsDefines const& pop_defines, decltype(ProvinceInstance::population_by_strata)::keys_type const& strata_keys, decltype(ProvinceInstance::pop_type_distribution)::keys_type const& pop_type_keys, decltype(ProvinceInstance::ideology_distribution)::keys_type const& ideology_keys @@ -56,6 +57,7 @@ namespace OpenVic { const Date date, CountryInstanceManager& country_manager, IssueManager const& issue_manager, + MarketInstance& market_instance, ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern ); diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index 86829146..1544bf83 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -20,6 +20,7 @@ using namespace OpenVic; ProvinceInstance::ProvinceInstance( MarketInstance& new_market_instance, ModifierEffectCache const& new_modifier_effect_cache, + PopsDefines const& new_pop_defines, ProvinceDefinition const& new_province_definition, decltype(population_by_strata)::keys_type const& strata_keys, decltype(pop_type_distribution)::keys_type const& pop_type_keys, @@ -28,9 +29,11 @@ ProvinceInstance::ProvinceInstance( HasIndex { new_province_definition.get_index() }, FlagStrings { "province" }, province_definition { new_province_definition }, + modifier_effect_cache { new_modifier_effect_cache }, terrain_type { new_province_definition.get_default_terrain_type() }, - rgo { new_market_instance, new_modifier_effect_cache, pop_type_keys }, + rgo { new_market_instance, pop_type_keys }, buildings { "buildings", false }, + shared_pop_values { new_pop_defines, strata_keys }, population_by_strata { &strata_keys }, militancy_by_strata { &strata_keys }, life_needs_fulfilled_by_strata { &strata_keys }, @@ -191,13 +194,18 @@ bool ProvinceInstance::add_pop(Pop&& pop) { } } -bool ProvinceInstance::add_pop_vec(std::vector const& pop_vec, ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern) { +bool ProvinceInstance::add_pop_vec( + std::vector const& pop_vec, + MarketInstance& market_instance, + ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern +) { if (!province_definition.is_water()) { reserve_more(pops, pop_vec.size()); for (PopBase const& pop : pop_vec) { _add_pop(Pop { pop, *ideology_distribution.get_keys(), + market_instance, artisanal_producer_factory_pattern }); } @@ -402,6 +410,7 @@ void ProvinceInstance::update_gamestate(const Date today, DefineManager const& d } void ProvinceInstance::province_tick(const Date today) { + shared_pop_values.update(); try_parallel_for_each( pops.begin(), pops.end(), @@ -453,6 +462,7 @@ bool ProvinceInstance::setup(BuildingTypeManager const& building_type_manager) { } rgo.setup_location_ptr(*this); + shared_pop_values.set_province(this); bool ret = true; diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp index 51bd402f..f6ade3fa 100644 --- a/src/openvic-simulation/map/ProvinceInstance.hpp +++ b/src/openvic-simulation/map/ProvinceInstance.hpp @@ -6,11 +6,11 @@ #include "openvic-simulation/economy/BuildingInstance.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" #include "openvic-simulation/economy/production/ResourceGatheringOperation.hpp" -#include "openvic-simulation/economy/trading/MarketInstance.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/pop/PopValuesFromProvince.hpp" #include "openvic-simulation/types/FlagStrings.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" @@ -32,6 +32,7 @@ namespace OpenVic { struct IssueManager; struct CountryInstanceManager; struct ModifierEffectCache; + struct MarketInstance; template struct UnitInstanceGroup; @@ -65,6 +66,7 @@ namespace OpenVic { private: ProvinceDefinition const& PROPERTY(province_definition); + ModifierEffectCache const& PROPERTY(modifier_effect_cache); TerrainType const* PROPERTY(terrain_type); life_rating_t PROPERTY(life_rating, 0); @@ -94,6 +96,7 @@ namespace OpenVic { private: plf::colony PROPERTY(pops); // TODO - replace with a more easily vectorisable container? + PopValuesFromProvince PROPERTY(shared_pop_values); pop_size_t PROPERTY(total_population, 0); // TODO - population change (growth + migration), monthly totals + breakdown by source/destination fixed_point_t PROPERTY(average_literacy); @@ -118,6 +121,7 @@ namespace OpenVic { ProvinceInstance( MarketInstance& new_market_instance, ModifierEffectCache const& new_modifier_effect_cache, + PopsDefines const& new_pop_defines, ProvinceDefinition const& new_province_definition, decltype(population_by_strata)::keys_type const& strata_keys, decltype(pop_type_distribution)::keys_type const& pop_type_keys, @@ -198,7 +202,11 @@ namespace OpenVic { bool expand_building(size_t building_index); bool add_pop(Pop&& pop); - bool add_pop_vec(std::vector const& pop_vec, ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern); + bool add_pop_vec( + std::vector const& pop_vec, + MarketInstance& market_instance, + ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern + ); size_t get_pop_count() const; void update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache); diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index 09afa51e..9a44bd98 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -1,18 +1,21 @@ -#include "utility/Utility.hpp" #define KEEP_DO_FOR_ALL_TYPES_OF_INCOME #define KEEP_DO_FOR_ALL_TYPES_OF_EXPENSES +#define KEEP_DO_FOR_ALL_NEED_CATEGORIES #include "Pop.hpp" #undef KEEP_DO_FOR_ALL_TYPES_OF_INCOME #undef KEEP_DO_FOR_ALL_TYPES_OF_EXPENSES +#undef KEEP_DO_FOR_ALL_NEED_CATEGORIES -#include "openvic-simulation/DefinitionManager.hpp" -#include "openvic-simulation/economy/production/ArtisanalProducer.hpp" +#include "openvic-simulation/defines/Define.hpp" +#include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/economy/production/ArtisanalProducerFactoryPattern.hpp" +#include "openvic-simulation/economy/trading/MarketInstance.hpp" +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" using namespace OpenVic; -static constexpr fixed_point_t DEFAULT_POP_LITERACY = fixed_point_t::_0_10(); - 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 @@ -22,41 +25,20 @@ PopBase::PopBase( Pop::Pop( PopBase const& pop_base, decltype(ideology_distribution)::keys_type const& ideology_keys, + MarketInstance& new_market_instance, ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern ) : PopBase { pop_base }, + market_instance { new_market_instance }, artisanal_producer_nullable { type->get_is_artisan() - ? artisanal_producer_factory_pattern.CreateNewArtisanalProducer() + ? artisanal_producer_factory_pattern.CreateNewArtisanalProducer(market_instance.get_good_instance_manager()) : nullptr }, - location { nullptr }, - total_change { 0 }, - num_grown { 0 }, - num_promoted { 0 }, - num_demoted { 0 }, - num_migrated_internal { 0 }, - num_migrated_external { 0 }, - num_migrated_colonial { 0 }, - literacy { DEFAULT_POP_LITERACY }, ideology_distribution { &ideology_keys }, - issue_distribution {}, - vote_distribution { nullptr }, - unemployment { 0 }, - cash { 0 }, - income { 0 }, - expenses { 0 }, - savings { 0 }, - life_needs_fulfilled { 0 }, - everyday_needs_fulfilled { 0 }, - luxury_needs_fulfilled { 0 }, - #define INITALIZE_POP_MONEY_STORES(name)\ - name { 0 }, - - DO_FOR_ALL_TYPES_OF_POP_INCOME(INITALIZE_POP_MONEY_STORES) - DO_FOR_ALL_TYPES_OF_POP_EXPENSES(INITALIZE_POP_MONEY_STORES) - #undef INITALIZE_POP_MONEY_STORES - max_supported_regiments { 0 } {} + vote_distribution { nullptr } { + reserve_needs_fulfilled_goods(); + } void Pop::setup_pop_test_values(IssueManager const& issue_manager) { /* Returns +/- range% of size. */ @@ -123,9 +105,10 @@ void Pop::setup_pop_test_values(IssueManager const& issue_manager) { income = test_range(5); expenses = test_range(5); savings = test_range(15); - life_needs_fulfilled = test_range(); - everyday_needs_fulfilled = test_range(); - luxury_needs_fulfilled = test_range(); + life_needs_acquired_quantity = test_range(); + everyday_needs_acquired_quantity = test_range(); + luxury_needs_acquired_quantity = test_range(); + life_needs_desired_quantity = everyday_needs_desired_quantity = luxury_needs_desired_quantity = fixed_point_t::_1(); } bool Pop::convert_to_equivalent() { @@ -136,6 +119,7 @@ bool Pop::convert_to_equivalent() { } type = equivalent; + reserve_needs_fulfilled_goods(); return true; } @@ -227,19 +211,40 @@ std::stringstream Pop::get_pop_context_text() const { return pop_context; } +void Pop::reserve_needs_fulfilled_goods() { + PopType const& type_never_null = *type; + #define RESERVE_NEEDS(need_category)\ + need_category##_needs_fulfilled_goods.reserve(type_never_null.get_##need_category##_needs().size()); + + DO_FOR_ALL_NEED_CATEGORIES(RESERVE_NEEDS) + #undef RESERVE_NEEDS +} + +void Pop::fill_needs_fulfilled_goods_with_false() { + PopType const& type_never_null = *type; + #define FILL_WITH_FALSE(need_category)\ + need_category##_needs_fulfilled_goods.clear(); \ + for (auto [good, base_demand] : type_never_null.get_##need_category##_needs()) {\ + need_category##_needs_fulfilled_goods.emplace(good, false); \ + } + + DO_FOR_ALL_NEED_CATEGORIES(FILL_WITH_FALSE) + #undef FILL_WITH_FALSE +} + #define DEFINE_ADD_INCOME_FUNCTIONS(name)\ void Pop::add_##name(const fixed_point_t amount){\ - if (OV_unlikely(amount == 0)) { \ + if (OV_unlikely(amount == fixed_point_t::_0())) {\ Logger::warning("Adding ",#name, " of 0 to pop. Context", get_pop_context_text().str()); \ return; \ } \ - if (OV_unlikely(amount < 0)) { \ + if (OV_unlikely(amount < fixed_point_t::_0())) {\ Logger::error("Adding negative ", #name, " of ",amount," to pop. Context", get_pop_context_text().str()); \ return; \ } \ - name += amount;\ - income += amount;\ - cash += amount;\ + name += amount; \ + income += amount; \ + cash += amount; \ } DO_FOR_ALL_TYPES_OF_POP_INCOME(DEFINE_ADD_INCOME_FUNCTIONS) @@ -247,17 +252,17 @@ DO_FOR_ALL_TYPES_OF_POP_INCOME(DEFINE_ADD_INCOME_FUNCTIONS) #define DEFINE_ADD_EXPENSE_FUNCTIONS(name)\ void Pop::add_##name(const fixed_point_t amount){\ - if (OV_unlikely(amount == 0)) { \ + if (OV_unlikely(amount == fixed_point_t::_0())) {\ Logger::warning("Adding ",#name, " of 0 to pop. Context", get_pop_context_text().str()); \ return; \ } \ - name += amount;\ - expenses += amount;\ - if (OV_unlikely(expenses < 0)) { \ + name += amount; \ + expenses += amount; \ + if (OV_unlikely(expenses < fixed_point_t::_0())) {\ Logger::error("Total expenses became negative (",expenses,") after adding ", #name, " of ",amount," to pop. Context", get_pop_context_text().str()); \ } \ - cash -= amount;\ - if (OV_unlikely(cash < 0)) { \ + cash -= amount; \ + if (OV_unlikely(cash < fixed_point_t::_0())) {\ Logger::error("Total cash became negative (",cash,") after adding ", #name, " of ",amount," to pop. Context", get_pop_context_text().str()); \ } \ } @@ -273,10 +278,169 @@ void Pop::pop_tick() { #undef DO_FOR_ALL_TYPES_OF_POP_INCOME #undef SET_ALL_INCOME_TO_ZERO + cash_allocated_for_artisanal_spending = fixed_point_t::_0(); + max_quantity_to_buy_per_good.clear(); + money_to_spend_per_good.clear(); + fill_needs_fulfilled_goods_with_false(); if (artisanal_producer_nullable != nullptr) { + //execute artisan_tick before needs artisanal_producer_nullable->artisan_tick(*this); } + + PopType const& type_never_null = *type; + ProvinceInstance& location_never_null = *location; + PopValuesFromProvince const& shared_values = location_never_null.get_shared_pop_values(); + PopStrataValuesFromProvince const& shared_strata_values = shared_values.get_effects_per_strata()[type_never_null.get_strata()]; + PopsDefines const& defines = shared_values.get_defines(); + const fixed_point_t base_needs_scalar = ( + fixed_point_t::_1() + 2 * consciousness / defines.get_pdef_base_con() + ) * size; + constexpr int32_t size_denominator = 200000; + + CountryInstance* get_country_to_report_economy_nullable = location_never_null.get_country_to_report_economy(); + GoodInstanceManager const& good_instance_manager = market_instance.get_good_instance_manager(); + #define FILL_NEEDS(need_category)\ + need_category##_needs.clear(); \ + const fixed_point_t need_category##_needs_scalar = base_needs_scalar * shared_strata_values.get_shared_##need_category##_needs_scalar(); \ + fixed_point_t need_category##_needs_price_inverse_sum = fixed_point_t::_0(); \ + need_category##_needs_acquired_quantity = need_category##_needs_desired_quantity = fixed_point_t::_0(); \ + for (auto [good_definition, quantity] : type_never_null.get_##need_category##_needs()) { \ + fixed_point_t max_quantity_to_buy = quantity * need_category##_needs_scalar / size_denominator; \ + need_category##_needs_desired_quantity += max_quantity_to_buy; \ + if (artisanal_produce_left_to_sell > fixed_point_t::_0()\ + && &artisanal_producer_nullable->get_production_type().get_output_good() == good_definition\ + ) {\ + const fixed_point_t own_produce_consumed = std::min(artisanal_produce_left_to_sell, max_quantity_to_buy); \ + artisanal_produce_left_to_sell -= own_produce_consumed; \ + max_quantity_to_buy -= own_produce_consumed; \ + need_category##_needs_acquired_quantity += own_produce_consumed; \ + if (get_country_to_report_economy_nullable != nullptr) { \ + get_country_to_report_economy_nullable->report_pop_need_consumption(type_never_null, *good_definition, own_produce_consumed); \ + } \ + } \ + if (OV_likely(max_quantity_to_buy > 0)) {\ + GoodInstance const& good_instance = good_instance_manager.get_good_instance_from_definition(*good_definition); \ + need_category##_needs_price_inverse_sum += good_instance.get_price_inverse(); \ + need_category##_needs[good_definition] += max_quantity_to_buy; \ + max_quantity_to_buy_per_good[good_definition] += max_quantity_to_buy; \ + } \ + } + + DO_FOR_ALL_NEED_CATEGORIES(FILL_NEEDS) + #undef FILL_NEEDS + + fixed_point_t cash_left_to_spend = cash - cash_allocated_for_artisanal_spending; + + #define ALLOCATE_FOR_NEEDS(need_category)\ + if (cash_left_to_spend > fixed_point_t::_0()) {\ + GoodDefinition::good_definition_map_t money_to_spend_per_good_draft {}; \ + for (size_t i = 0; i < need_category##_needs.size(); i++) {\ + auto [good_definition, max_quantity_to_buy] = *need_category##_needs.nth(i); \ + GoodInstance const& good_instance = good_instance_manager.get_good_instance_from_definition(*good_definition); \ + const fixed_point_t max_money_to_spend = max_quantity_to_buy * good_instance.get_max_next_price(); \ + if (money_to_spend_per_good_draft[good_definition] >= max_money_to_spend) {\ + continue; \ + } \ + fixed_point_t cash_available_for_good = cash_left_to_spend * good_instance.get_price_inverse() / need_category##_needs_price_inverse_sum; \ + if (cash_available_for_good >= max_money_to_spend) {\ + cash_left_to_spend -= max_money_to_spend; \ + money_to_spend_per_good_draft[good_definition] = max_money_to_spend; \ + need_category##_needs_price_inverse_sum -= good_instance.get_price_inverse(); \ + i = -1; \ + } else {\ + money_to_spend_per_good_draft[good_definition] = cash_available_for_good; \ + } \ + } \ + money_to_spend_per_good += money_to_spend_per_good_draft; \ + } + + DO_FOR_ALL_NEED_CATEGORIES(ALLOCATE_FOR_NEEDS) + #undef ALLOCATE_FOR_NEEDS + + for (auto [good_definition, money_to_spend] : money_to_spend_per_good) { + const fixed_point_t max_quantity_to_buy = max_quantity_to_buy_per_good[good_definition]; + + if (OV_unlikely(max_quantity_to_buy <= fixed_point_t::_0())) { + Logger::error("Aborted placing buy order for ",*good_definition," of max_quantity_to_buy 0."); + continue; + } + + market_instance.place_buy_up_to_order({ + *good_definition, + max_quantity_to_buy, + money_to_spend, + [this, good_definition](const BuyResult buy_result) -> void { + fixed_point_t quantity_left_to_consume = buy_result.get_quantity_bought(); + if (artisanal_producer_nullable != nullptr) { + if (quantity_left_to_consume <= fixed_point_t::_0()) { + return; + } + const fixed_point_t quantity_added_to_stockpile = artisanal_producer_nullable->add_to_stockpile( + *good_definition, + quantity_left_to_consume + ); + + if (quantity_added_to_stockpile > fixed_point_t::_0()) { + quantity_left_to_consume -= quantity_added_to_stockpile; + const fixed_point_t expense = buy_result.get_money_spent() * quantity_added_to_stockpile / buy_result.get_quantity_bought(); + add_artisan_inputs_expense(expense); + } + } + + CountryInstance* get_country_to_report_economy_nullable = location->get_country_to_report_economy(); + #define CONSUME_NEED(need_category)\ + if (quantity_left_to_consume <= fixed_point_t::_0()) {\ + return; \ + } \ + const GoodDefinition::good_definition_map_t::const_iterator need_category##it = need_category##_needs.find(good_definition); \ + if (need_category##it != need_category##_needs.end()) {\ + const fixed_point_t desired_quantity = need_category##it->second; \ + fixed_point_t consumed_quantity; \ + if (quantity_left_to_consume >= desired_quantity) {\ + consumed_quantity = desired_quantity; \ + need_category##_needs_fulfilled_goods[good_definition] = true; \ + } else {\ + consumed_quantity = quantity_left_to_consume; \ + } \ + need_category##_needs_acquired_quantity += consumed_quantity; \ + quantity_left_to_consume -= consumed_quantity; \ + if (get_country_to_report_economy_nullable != nullptr) { \ + get_country_to_report_economy_nullable->report_pop_need_consumption(*type, *good_definition, consumed_quantity); \ + } \ + const fixed_point_t expense = buy_result.get_money_spent() * consumed_quantity / buy_result.get_quantity_bought(); \ + add_##need_category##_needs_expense(expense); \ + } + + DO_FOR_ALL_NEED_CATEGORIES(CONSUME_NEED) + #undef CONSUME_NEED + } + }); + } + + if (artisanal_produce_left_to_sell > fixed_point_t::_0()) { + market_instance.place_market_sell_order({ + artisanal_producer_nullable->get_production_type().get_output_good(), + artisanal_produce_left_to_sell, + [this](const SellResult sell_result) -> void { + if (sell_result.get_money_gained() > fixed_point_t::_0()) { + add_artisanal_income(sell_result.get_money_gained()); + } + } + }); + artisanal_produce_left_to_sell = fixed_point_t::_0(); + } +} + +void Pop::artisanal_buy(GoodDefinition const& good, const fixed_point_t max_quantity_to_buy, const fixed_point_t money_to_spend) { + cash_allocated_for_artisanal_spending += money_to_spend; + max_quantity_to_buy_per_good[&good] += max_quantity_to_buy; + money_to_spend_per_good[&good] += money_to_spend; +} + +void Pop::artisanal_sell(const fixed_point_t quantity) { + artisanal_produce_left_to_sell = quantity; } #undef DO_FOR_ALL_TYPES_OF_POP_INCOME -#undef DO_FOR_ALL_TYPES_OF_POP_EXPENSES \ No newline at end of file +#undef DO_FOR_ALL_TYPES_OF_POP_EXPENSES +#undef DO_FOR_ALL_NEED_CATEGORIES \ No newline at end of file diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp index e9982984..c6c3342a 100644 --- a/src/openvic-simulation/pop/Pop.hpp +++ b/src/openvic-simulation/pop/Pop.hpp @@ -7,8 +7,9 @@ #include "openvic-simulation/pop/PopType.hpp" namespace OpenVic { - struct DefineManager; struct CountryInstance; + struct DefineManager; + struct MarketInstance; struct ProvinceInstance; struct PopBase { @@ -49,6 +50,11 @@ namespace OpenVic { F(luxury_needs_expense)\ F(artisan_inputs_expense) + #define DO_FOR_ALL_NEED_CATEGORIES(F)\ + F(life)\ + F(everyday)\ + F(luxury) + #define DECLARE_POP_MONEY_STORES(money_type)\ fixed_point_t PROPERTY(money_type); @@ -64,19 +70,25 @@ namespace OpenVic { static constexpr pop_size_t MAX_SIZE = std::numeric_limits::max(); private: + MarketInstance& market_instance; std::unique_ptr artisanal_producer_nullable; - ProvinceInstance* PROPERTY_PTR(location); + fixed_point_t cash_allocated_for_artisanal_spending; + fixed_point_t artisanal_produce_left_to_sell; + GoodDefinition::good_definition_map_t max_quantity_to_buy_per_good; //TODO pool? + GoodDefinition::good_definition_map_t money_to_spend_per_good; //TODO pool? + ProvinceInstance* PROPERTY_PTR(location, nullptr); /* Last day's size change by source. */ - pop_size_t PROPERTY(total_change); - pop_size_t PROPERTY(num_grown); - pop_size_t PROPERTY(num_promoted); // TODO - detailed promotion/demotion info (what to) - pop_size_t PROPERTY(num_demoted); - pop_size_t PROPERTY(num_migrated_internal); // TODO - detailed migration info (where to) - pop_size_t PROPERTY(num_migrated_external); - pop_size_t PROPERTY(num_migrated_colonial); + pop_size_t PROPERTY(total_change, 0); + pop_size_t PROPERTY(num_grown, 0); + pop_size_t PROPERTY(num_promoted, 0); // TODO - detailed promotion/demotion info (what to) + pop_size_t PROPERTY(num_demoted, 0); + pop_size_t PROPERTY(num_migrated_internal, 0); // TODO - detailed migration info (where to) + pop_size_t PROPERTY(num_migrated_external, 0); + pop_size_t PROPERTY(num_migrated_colonial, 0); - fixed_point_t PROPERTY_RW(literacy); + static constexpr fixed_point_t DEFAULT_POP_LITERACY = fixed_point_t::_0_10(); + fixed_point_t PROPERTY_RW(literacy, DEFAULT_POP_LITERACY); // All of these should have a total size equal to the pop size, allowing the distributions from different pops to be // added together with automatic weighting based on their relative sizes. Similarly, the province, state and country @@ -90,23 +102,39 @@ namespace OpenVic { fixed_point_t PROPERTY(income); fixed_point_t PROPERTY(expenses); //positive value means POP paid for goods. This is displayed * -1 in UI. fixed_point_t PROPERTY(savings); - fixed_point_t PROPERTY(life_needs_fulfilled); - fixed_point_t PROPERTY(everyday_needs_fulfilled); - fixed_point_t PROPERTY(luxury_needs_fulfilled); + + #define NEED_MEMBERS(need_category)\ + fixed_point_t need_category##_needs_acquired_quantity, need_category##_needs_desired_quantity; \ + public: \ + constexpr fixed_point_t get_##need_category##_needs_fulfilled() const { \ + if (need_category##_needs_desired_quantity == fixed_point_t::_0()) { \ + return fixed_point_t::_1(); \ + } \ + return need_category##_needs_acquired_quantity / need_category##_needs_desired_quantity; \ + } \ + private: \ + GoodDefinition::good_definition_map_t need_category##_needs {}; \ + ordered_map PROPERTY(need_category##_needs_fulfilled_goods); + + DO_FOR_ALL_NEED_CATEGORIES(NEED_MEMBERS) + #undef NEED_MEMBERS DO_FOR_ALL_TYPES_OF_POP_INCOME(DECLARE_POP_MONEY_STORES); DO_FOR_ALL_TYPES_OF_POP_EXPENSES(DECLARE_POP_MONEY_STORES); #undef DECLARE_POP_MONEY_STORES - size_t PROPERTY(max_supported_regiments); + size_t PROPERTY(max_supported_regiments, 0); Pop( PopBase const& pop_base, decltype(ideology_distribution)::keys_type const& ideology_keys, + MarketInstance& new_market_instance, ArtisanalProducerFactoryPattern& artisanal_producer_factory_pattern ); std::stringstream get_pop_context_text() const; + void reserve_needs_fulfilled_goods(); + void fill_needs_fulfilled_goods_with_false(); public: Pop(Pop const&) = delete; @@ -135,6 +163,8 @@ namespace OpenVic { DO_FOR_ALL_TYPES_OF_POP_EXPENSES(DECLARE_POP_MONEY_STORE_FUNCTIONS) #undef DECLARE_POP_MONEY_STORE_FUNCTIONS void pop_tick(); + void artisanal_buy(GoodDefinition const& good, const fixed_point_t max_quantity_to_buy, const fixed_point_t money_to_spend); + void artisanal_sell(const fixed_point_t quantity); }; } #ifndef KEEP_DO_FOR_ALL_TYPES_OF_INCOME @@ -144,3 +174,7 @@ namespace OpenVic { #ifndef KEEP_DO_FOR_ALL_TYPES_OF_EXPENSES #undef DO_FOR_ALL_TYPES_OF_POP_EXPENSES #endif + +#ifndef KEEP_DO_FOR_ALL_NEED_CATEGORIES + #undef DO_FOR_ALL_NEED_CATEGORIES +#endif diff --git a/src/openvic-simulation/pop/PopValuesFromProvince.cpp b/src/openvic-simulation/pop/PopValuesFromProvince.cpp new file mode 100644 index 00000000..bec919c6 --- /dev/null +++ b/src/openvic-simulation/pop/PopValuesFromProvince.cpp @@ -0,0 +1,53 @@ +#include "PopValuesFromProvince.hpp" + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/defines/PopsDefines.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/utility/Utility.hpp" + +using namespace OpenVic; + +void PopStrataValuesFromProvince::update(PopValuesFromProvince const& parent, Strata const& strata) { + ProvinceInstance const& province = *parent.get_province(); + ModifierEffectCache const& modifier_effect_cache = province.get_modifier_effect_cache(); + ModifierEffectCache::strata_effects_t const& strata_effects = modifier_effect_cache.get_strata_effects()[strata]; + PopsDefines const& defines = parent.get_defines(); + fixed_point_t shared_base_needs_scalar = defines.get_base_goods_demand() + * province.get_modifier_effect_value(*modifier_effect_cache.get_goods_demand()); + + fixed_point_t invention_needs_scalar = fixed_point_t::_1(); + CountryInstance const* const owner_nullable = province.get_owner(); + if (owner_nullable != nullptr) { + CountryInstance const& owner = *owner_nullable; + shared_base_needs_scalar *= fixed_point_t::_1() + owner.get_plurality() / 100; + invention_needs_scalar += owner.get_inventions_count() * defines.get_invention_impact_on_demand(); + } + + fixed_point_t shared_life_needs_scalar = shared_base_needs_scalar + * (fixed_point_t::_1() + province.get_modifier_effect_value(*strata_effects.get_life_needs())); + fixed_point_t shared_everyday_needs_scalar = shared_base_needs_scalar + * invention_needs_scalar + * (fixed_point_t::_1() + province.get_modifier_effect_value(*strata_effects.get_everyday_needs())); + fixed_point_t shared_luxury_needs_scalar = shared_base_needs_scalar + * invention_needs_scalar + * (fixed_point_t::_1() + province.get_modifier_effect_value(*strata_effects.get_luxury_needs())); +} + +PopValuesFromProvince::PopValuesFromProvince( + PopsDefines const& new_defines, + decltype(effects_per_strata)::keys_type const& strata_keys +) : defines { new_defines }, + effects_per_strata { &strata_keys } + {} + +void PopValuesFromProvince::update() { + if (OV_unlikely(province == nullptr)) { + OpenVic::Logger::error("PopValuesFromProvince has no province. Check ProvinceInstance::setup correctly sets it."); + return; + } + + for (auto [strata, values] : effects_per_strata) { + values.update(*this, strata); + } +} \ No newline at end of file diff --git a/src/openvic-simulation/pop/PopValuesFromProvince.hpp b/src/openvic-simulation/pop/PopValuesFromProvince.hpp new file mode 100644 index 00000000..55f99974 --- /dev/null +++ b/src/openvic-simulation/pop/PopValuesFromProvince.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "openvic-simulation/pop/PopType.hpp" +#include "openvic-simulation/types/IndexedMap.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/utility/Getters.hpp" + +namespace OpenVic { + struct ProvinceInstance; + struct PopsDefines; + struct PopValuesFromProvince; + + struct PopStrataValuesFromProvince { + private: + fixed_point_t PROPERTY(shared_life_needs_scalar); + fixed_point_t PROPERTY(shared_everyday_needs_scalar); + fixed_point_t PROPERTY(shared_luxury_needs_scalar); + public: + void update(PopValuesFromProvince const& parent, Strata const& strata); + }; + + struct PopValuesFromProvince { + private: + PopsDefines const& PROPERTY(defines); + ProvinceInstance const* PROPERTY_RW(province, nullptr); + IndexedMap PROPERTY(effects_per_strata); + public: + PopValuesFromProvince( + PopsDefines const& new_defines, + decltype(effects_per_strata)::keys_type const& strata_keys + ); + + void update(); + }; +} \ No newline at end of file