diff --git a/assets/lua/_meta.lua b/assets/lua/_meta.lua index 1745db3d36..dfbc829a0f 100644 --- a/assets/lua/_meta.lua +++ b/assets/lua/_meta.lua @@ -1,6 +1,5 @@ ---@meta -- META FILES, PROVIDES TYPING HINTS, DO NOT REQUIRE - alice = {} alice_ui = {} \ No newline at end of file diff --git a/assets/lua/custom_ffi.lua b/assets/lua/custom_ffi.lua index 2fc13832a0..852fa81786 100644 --- a/assets/lua/custom_ffi.lua +++ b/assets/lua/custom_ffi.lua @@ -18,61 +18,104 @@ ffi.cdef[[ bool alice_at_war(int32_t nation_a, int32_t nation_b); typedef struct { - int32_t good; - float amount; - } trade_good_container; + int32_t good; + float amount; + } trade_good_container; - typedef struct { - int32_t build_time; - int32_t default_organisation; - float maximum_speed; - float defence_or_hull; - float attack_or_gun_power; - float supply_consumption; - float support; - float siege_or_torpedo_attack; - float reconnaissance_or_fire_range; - float discipline_or_evasion; - float maneuver; - } unit_variable_stats; + typedef struct { + int32_t build_time; + int32_t default_organisation; + float maximum_speed; + float defence_or_hull; + float attack_or_gun_power; + float supply_consumption; + float support; + float siege_or_torpedo_attack; + float reconnaissance_or_fire_range; + float discipline_or_evasion; + float maneuver; + } unit_variable_stats; - unit_variable_stats* alice_get_unit_stats(int32_t unit_id); + unit_variable_stats* alice_get_unit_stats(int32_t unit_id); - void call_daily(const char function_name[]); - void remove_daily(const char function_name[]); + void call_daily(const char function_name[]); + void remove_daily(const char function_name[]); - void call_daily_battle(const char function_name[]); - void remove_daily_battle(const char function_name[]); + void call_daily_battle(const char function_name[]); + void remove_daily_battle(const char function_name[]); - void call_battle_end(const char function_name[]); - void remove_battle_end(const char function_name[]); + void call_battle_end(const char function_name[]); + void remove_battle_end(const char function_name[]); - void set_text(const char text[]); - int32_t local_player_nation(); + void set_text(const char text[]); + int32_t local_player_nation(); - void console_log(const char text[]); + void console_log(const char text[]); - int32_t lua_get_gamerule_id_by_name(const char gamerule_name[]); + int32_t lua_get_gamerule_id_by_name(const char gamerule_name[]); int32_t lua_get_gamerule_option_id_by_name(const char gamerule_option_name[]); bool lua_check_gamerule(int32_t gamerule_id, uint8_t opt_id); int32_t lua_get_active_gamerule_option(int32_t gamerule_id); - - - - + int32_t lua_create_text_key(const char key[]); + void lua_add_rgo_efficiency_commodity(int32_t target, int32_t commodity, float volume); + void lua_add_factory_efficiency_commodity(int32_t target, int32_t commodity, float volume); + void lua_add_factory_input_commodity(int32_t target, int32_t commodity, float volume); + void lua_add_factory_cost_commodity(int32_t target, int32_t commodity, float volume); ]] +TEXT = {} + +---comment +---@param key string +---@return text_key +function TEXT.create_key(key) + return ffi.C.lua_create_text_key(key) +end + +FACTORY_TYPE_DATA = {} +FACTORY_TYPE_DATA.COST = {} +FACTORY_TYPE_DATA.INPUT = {} +FACTORY_TYPE_DATA.EFFICIENCY = {} + +---@param target factory_type_id +---@param commodity commodity_id +---@param volume number +function FACTORY_TYPE_DATA.COST.add(target, commodity, volume) + ffi.C.lua_add_factory_cost_commodity(target, commodity, volume); +end +---@param target factory_type_id +---@param commodity commodity_id +---@param volume number +function FACTORY_TYPE_DATA.INPUT.add(target, commodity, volume) + ffi.C.lua_add_factory_input_commodity(target, commodity, volume); +end +---@param target factory_type_id +---@param commodity commodity_id +---@param volume number +function FACTORY_TYPE_DATA.EFFICIENCY.add(target, commodity, volume) + ffi.C.lua_add_factory_efficiency_commodity(target, commodity, volume); +end + +EFFICIENCY = {} +EFFICIENCY.RGO = {} +---comment +---@param target commodity_id +---@param commodity commodity_id +---@param volume number +function EFFICIENCY.RGO.add(target, commodity, volume) + ffi.C.lua_add_rgo_efficiency_commodity(target, commodity, volume); +end STATE = {} ---@return nation_id function STATE.player() - return ffi.C.local_player_nation() + return ffi.C.local_player_nation() end ---@param text string function STATE.set_text(text) - return ffi.C.set_text(text) + return ffi.C.set_text(text) end @@ -133,29 +176,29 @@ ON_ACTION = {} ---@param name string function ON_ACTION.add_daily_call(name) - ffi.C.call_daily(name) + ffi.C.call_daily(name) end ---@param name string function ON_ACTION.remove_daily_call(name) - ffi.C.remove_daily(name) + ffi.C.remove_daily(name) end ---@param name string function ON_ACTION.add_battle_call(name) - ffi.C.call_daily_battle(name) + ffi.C.call_daily_battle(name) end ---@param name string function ON_ACTION.remove_battle_call(name) - ffi.C.remove_daily_battle(name) + ffi.C.remove_daily_battle(name) end ---@param name string function ON_ACTION.add_battle_end_call(name) - ffi.C.call_battle_end(name) + ffi.C.call_battle_end(name) end ---@param name string function ON_ACTION.remove_battle_end_call(name) - ffi.C.remove_battle_end(name) + ffi.C.remove_battle_end(name) end ---@param name string function console_log(message) - ffi.C.console_log(message) + ffi.C.console_log(message) end diff --git a/assets/lua/game_scripts/scenario_load.lua b/assets/lua/game_scripts/scenario_load.lua new file mode 100644 index 0000000000..6a994ea287 --- /dev/null +++ b/assets/lua/game_scripts/scenario_load.lua @@ -0,0 +1,156 @@ +function LOAD_SCENARIO_DATA() + +end + +--[[ +Usage example: +(DO NOT FORGET TO DELETE ORIGINAL BUILDINGS SO YOU DON'T GET DUPLICATES!!!) + +---@enum COMMODITY_GROUP +COMMODITY_GROUP = { + MILITARY = 0, + RAW = 1, + INDUSTRY = 2, + CONSUMER = 3, + INDUSTRIAL_CONSUMER = 4 +} + +---comment +---@param name string +---@param icon number +---@param color number[] +---@return commodity_id +local function load_commodity(name, icon, color) + local item = COMMODITY.create() + local name_key = TEXT.create_key(name) + COMMODITY.set_name(item, name_key) + COMMODITY.set_commodity_group(item, COMMODITY_GROUP.CONSUMER) + COMMODITY.set_cost(item, 1) + COMMODITY.set_median_price(item, 1) + COMMODITY.set_is_available_from_start(item, true) + COMMODITY.set_is_local(item, false) + COMMODITY.set_overseas_penalty(item, false) + COMMODITY.set_uses_potentials(item, false) + COMMODITY.set_money_rgo(item, false) + COMMODITY.set_icon(item, icon) + -- ABGR + COMMODITY.set_color(item, color[3] * (2 ^ 16) + color[2] * (2 ^ 8) + color[1]) + return item +end +---@alias Container {[1] : commodity_id, [2] : number} + +---@param name string +---@param inputs Container[] +---@param efficiency_inputs Container[] +---@param output Container +---@param construction_cost Container[] +---@param construction_time number? +local function load_factory(name, inputs, efficiency_inputs, output, construction_cost, construction_time) + local item = FACTORY_TYPE.create() + local name_key = TEXT.create_key(name) + local description_key = TEXT.create_key(name .. "_desc") + FACTORY_TYPE.set_name(item, name_key) + FACTORY_TYPE.set_description(item, description_key) + if construction_time == nil then + FACTORY_TYPE.set_construction_time(item, 360) + else + FACTORY_TYPE.set_construction_time(item, construction_time) + end + FACTORY_TYPE.set_is_available_from_start(item, true) + FACTORY_TYPE.set_can_be_built_in_colonies(item, false) + FACTORY_TYPE.set_factory_tier(item, 0) + FACTORY_TYPE.set_is_coastal(item, false) + + for _, value in ipairs(construction_cost) do + FACTORY_TYPE_DATA.COST.add(item, value[1], value[2]) + end + for _, value in ipairs(inputs) do + FACTORY_TYPE_DATA.INPUT.add(item, value[1], value[2]) + end + for _, value in ipairs(efficiency_inputs) do + FACTORY_TYPE_DATA.EFFICIENCY.add(item, value[1], value[2]) + end + + FACTORY_TYPE.set_output(item, output[1]) + FACTORY_TYPE.set_output_amount(item, output[2]) + FACTORY_TYPE.set_base_workforce(item, 1000) +end + +---comment +---@param goods Container[] +---@return consumption_category_id +local function load_consumption_category(weight, depends_on, scales_with, goods) + local item = CONSUMPTION_CATEGORY.create() + if (depends_on) then + -- Consumption is discouraged unless pop is able to buy this category + CONSUMPTION_CATEGORY.set_buy_after(item, depends_on) + end + if (scales_with) then + -- Consumption is discouraged unless pop is able to buy this good from previously buy_after category + -- For example, it could be automobiles from transportation need which discourage consumption of fuel + CONSUMPTION_CATEGORY.set_scale_with(item, scales_with) + end + for index, value in ipairs(goods) do + CONSUMPTION_CATEGORY.set_weights(item, value[1], value[2]) + end + CONSUMPTION_CATEGORY.set_satisfaction_score(item, weight) + + -- Pops would prefer to buy goods with actual availability + CONSUMPTION_CATEGORY.set_availability_pressure(item, 1) + + -- When negative, pops will try to avoid buying expensive goods from this category + -- When positive, they will be encouraged to buy expensive goods rather than cheap + CONSUMPTION_CATEGORY.set_price_pressure(item, -10) + + -- Base pressure to buy goods from this category. + -- Set high to get more uniform weights. + CONSUMPTION_CATEGORY.set_base_pressure(item, 0.3) + return item +end + +---comment +---@param item consumption_category_id +local function set_as_luxury(item) + CONSUMPTION_CATEGORY.set_price_pressure(item, 1) + CONSUMPTION_CATEGORY.set_base_pressure(item, 1.0) +end + + +function LOAD_SCENARIO_DATA() + -- OBLIGATORY FOR NOW + local money = load_commodity("money", 0, {50, 50, 50}) + + -- Load your goods + + local raw_ammunition = load_commodity("raw_ammunition", 2, {100, 100, 100}) + local alloys = load_commodity("alloys", 3, {0, 100, 100}) + local super_alloys = load_commodity("super_alloys", 4, {100, 0, 100}) + local ammunition = load_commodity("ammunition", 1, {200, 200, 100}) + + -- Create convenience tables + local ammunition_RGO = {raw_ammunition} + local ammunition_needs_desc = {{ammunition, 1}} + local super_alloys_needs_desc = {{super_alloys, 1}} + + -- Set RGO data + for _, item in ipairs(ammunition_RGO) do + EFFICIENCY.RGO.add(item, fertilizer, 0.1) + EFFICIENCY.RGO.add(item, steel, 0.1) + EFFICIENCY.RGO.add(item, automobiles, 0.1) + COMMODITY.set_rgo_amount(item, 10) + COMMODITY.set_rgo_efficiency_inputs_are_defined_in_content(item, true) + end + + -- Create factories. + + local default_efficiency = {{alloys, 0.1}} + local default_cost = {{alloys, 100}, {super_alloys, 100}} + + load_factory("ammunition_factory", { {raw_ammunition, 2} }, default_efficiency, {ammunition, 1}, default_cost) + + -- Create categories of needs + + local basic_food = load_consumption_category(0.05, nil, nil, ammunition_needs_desc) + local cool_food = load_consumption_category(0.1, basic_food, nil, super_alloys_needs_desc) + set_as_luxury(cool_food) +--]] diff --git a/dependencies/DataContainerLuaGenerator/CMakeLists.txt b/dependencies/DataContainerLuaGenerator/CMakeLists.txt index f9ccbf476b..ff95164477 100644 --- a/dependencies/DataContainerLuaGenerator/CMakeLists.txt +++ b/dependencies/DataContainerLuaGenerator/CMakeLists.txt @@ -3,7 +3,7 @@ message(STATUS "Fetching DataContainerLua...") FetchContent_Declare( DataContainerLua GIT_REPOSITORY https://github.com/ineveraskedforthis/DataContainer-Lua - GIT_TAG main + GIT_TAG allow-creation GIT_SHALLOW 1 GIT_PROGRESS TRUE ) diff --git a/src/economy/economy.cpp b/src/economy/economy.cpp index 267d676001..f1f3f46b94 100644 --- a/src/economy/economy.cpp +++ b/src/economy/economy.cpp @@ -189,6 +189,12 @@ void initialize_needs_weights(sys::state& state, dcon::market_id n) { state.world.market_set_luxury_needs_weights(n, c, 0.001f); }); } + state.world.for_each_consumption_category([&](auto weights_id) { + state.world.for_each_commodity([&](dcon::commodity_id c) { + auto index = c.index() + weights_id.index() * state.world.commodity_size(); + state.world.market_set_local_consumption_weights(n, index, 0.01f); + }); + }); } // todo: make priority different per commodity @@ -2833,6 +2839,35 @@ void daily_update(sys::state& state, bool presimulation, float presimulation_sta } }); + // for new needs (much more elegant...) + state.world.execute_parallel_over_market([&](auto markets) { + state.world.for_each_consumption_category([&](auto cat_id) { + ve::fp_vector total {0.f}; + state.world.for_each_commodity([&](auto c) { + auto price = state.world.market_get_price(markets, c); + auto index = c.index() + cat_id.index() * state.world.commodity_size(); + auto consumption_weight = state.world.consumption_category_get_weights(cat_id, c); + auto w = state.world.market_get_local_consumption_weights(markets, index); +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, price* consumption_weight* w + ); +#endif + total = total + price * consumption_weight * w; + }); +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, total + ); +#endif + state.world.market_set_cost_per_consumption_category(markets, cat_id, total); + }); + }); + // PROFILE set_profile_point(state, "needs_costs"); @@ -2870,6 +2905,11 @@ void daily_update(sys::state& state, bool presimulation, float presimulation_sta state.world.market_set_luxury_needs_scale(ids, pt, 0.f); }); }); + state.world.execute_serial_over_market([&](auto ids) { + state.world.for_each_consumption_category([&](auto cat) { + state.world.market_set_demand_per_consumption_category(ids, cat, 0.f); + }); + }); break; } }); @@ -2908,6 +2948,10 @@ void daily_update(sys::state& state, bool presimulation, float presimulation_sta auto demand_everyday = state.world.pop_make_vectorizable_float_buffer(); auto demand_luxury = state.world.pop_make_vectorizable_float_buffer(); auto demand_paid_education = state.world.pop_make_vectorizable_float_buffer(); + std::vector> demand_consumption_category {}; + state.world.for_each_consumption_category([&](auto cat) { + demand_consumption_category.emplace_back(ve::vectorizable_buffer(state.world.pop_size()* state.world.consumption_category_size())); + }); auto satisfaction_from_subsistence = state.world.pop_make_vectorizable_float_buffer(); pops::update_consumption( @@ -2924,7 +2968,8 @@ void daily_update(sys::state& state, bool presimulation, float presimulation_sta demand_everyday, demand_luxury, demand_paid_education, - satisfaction_from_subsistence + satisfaction_from_subsistence, + demand_consumption_category ); set_profile_point(state, "pops_consumption"); @@ -3705,6 +3750,31 @@ void daily_update(sys::state& state, bool presimulation, float presimulation_sta }, pts); }); + + /* + Here we do a much more simple and self-explanatory computation. + */ + if(state.world.consumption_category_size() != 0) { + state.world.execute_parallel_over_market([&](auto ids) { + state.world.for_each_consumption_category([&](auto cat_id) { + ve::fp_vector total_demanded{ 0.f }; + ve::fp_vector total_bought{ 0.f }; + state.world.for_each_commodity([&](auto c) { + auto price = state.world.market_get_price(ids, c); + auto index = c.index() + cat_id.index() * state.world.commodity_size(); + auto consumption_weight = state.world.consumption_category_get_weights(cat_id, c); + auto w = state.world.market_get_local_consumption_weights(ids, index); + auto satisfied_demand = state.world.market_get_actual_probability_to_buy(ids, c); + total_demanded = total_demanded + price * consumption_weight * w; + total_bought = total_bought + satisfied_demand * price * consumption_weight * w; + }); + state.world.market_set_satisfied_demand_ratio_per_consumption_category(ids, cat_id, + ve::select(total_demanded == 0.f, 0.f, total_bought / total_demanded) + ); + }); + }); + } + sanity_check(state); // update needs satisfaction depending on actually available goods and services: @@ -3715,60 +3785,71 @@ void daily_update(sys::state& state, bool presimulation, float presimulation_sta auto local_market = state.world.state_instance_get_market_from_local_market(local_state); auto local_nation = state.world.province_get_nation_from_province_ownership(province); auto invalid = local_nation == dcon::nation_id {}; - auto pop_type = state.world.pop_get_poptype(ids); - auto ln_satisfaction = ve::apply([&](auto market, auto pt) { - return state.world.market_get_satisfied_ratio_of_max_life_needs(market, pt); - }, local_market, pop_type); - auto en_satisfaction = ve::apply([&](auto market, auto pt) { - return state.world.market_get_satisfied_ratio_of_max_everyday_needs(market, pt); - }, local_market, pop_type); - auto lx_satisfaction = ve::apply([&](auto market, auto pt) { - return state.world.market_get_satisfied_ratio_of_max_luxury_needs(market, pt); - }, local_market, pop_type); - - auto ln_spent = ve::apply([&](auto market, auto pt) { - return state.world.market_get_satisfied_ratio_of_demanded_life_needs(market, pt); - }, local_market, pop_type); - auto en_spent = ve::apply([&](auto market, auto pt) { - return state.world.market_get_satisfied_ratio_of_demanded_everyday_needs(market, pt); - }, local_market, pop_type); - auto lx_spent = ve::apply([&](auto market, auto pt) { - return state.world.market_get_satisfied_ratio_of_demanded_luxury_needs(market, pt); - }, local_market, pop_type); - - // return money which were not actually spent - { - auto ln_cost = ve::apply([&](auto market, auto pt) { - return state.world.market_get_life_needs_costs(market, pt); + if (state.world.consumption_category_size() == 0) { + auto ln_satisfaction = ve::apply([&](auto market, auto pt) { + return state.world.market_get_satisfied_ratio_of_max_life_needs(market, pt); }, local_market, pop_type); - auto en_cost = ve::apply([&](auto market, auto pt) { - return state.world.market_get_everyday_needs_costs(market, pt); + auto en_satisfaction = ve::apply([&](auto market, auto pt) { + return state.world.market_get_satisfied_ratio_of_max_everyday_needs(market, pt); }, local_market, pop_type); - auto lx_cost = ve::apply([&](auto market, auto pt) { - return state.world.market_get_luxury_needs_costs(market, pt); + auto lx_satisfaction = ve::apply([&](auto market, auto pt) { + return state.world.market_get_satisfied_ratio_of_max_luxury_needs(market, pt); }, local_market, pop_type); - auto pop_size = state.world.pop_get_size(ids); - auto unspent_money = - ln_cost * (1.f - ln_spent) * demand_life.get(ids) - + en_cost * (1.f - en_spent) * demand_everyday.get(ids) - + lx_cost * (1.f - lx_spent) * demand_luxury.get(ids); + auto ln_spent = ve::apply([&](auto market, auto pt) { + return state.world.market_get_satisfied_ratio_of_demanded_life_needs(market, pt); + }, local_market, pop_type); + auto en_spent = ve::apply([&](auto market, auto pt) { + return state.world.market_get_satisfied_ratio_of_demanded_everyday_needs(market, pt); + }, local_market, pop_type); + auto lx_spent = ve::apply([&](auto market, auto pt) { + return state.world.market_get_satisfied_ratio_of_demanded_luxury_needs(market, pt); + }, local_market, pop_type); - auto savings = state.world.pop_get_savings(ids); - state.world.pop_set_savings(ids, savings + unspent_money); - } + // return money which were not actually spent + { + auto ln_cost = ve::apply([&](auto market, auto pt) { + return state.world.market_get_life_needs_costs(market, pt); + }, local_market, pop_type); + auto en_cost = ve::apply([&](auto market, auto pt) { + return state.world.market_get_everyday_needs_costs(market, pt); + }, local_market, pop_type); + auto lx_cost = ve::apply([&](auto market, auto pt) { + return state.world.market_get_luxury_needs_costs(market, pt); + }, local_market, pop_type); + + auto pop_size = state.world.pop_get_size(ids); + auto unspent_money = + ln_cost * (1.f - ln_spent) * demand_life.get(ids) + + en_cost * (1.f - en_spent) * demand_everyday.get(ids) + + lx_cost * (1.f - lx_spent) * demand_luxury.get(ids); - { + auto savings = state.world.pop_get_savings(ids); + state.world.pop_set_savings(ids, savings + unspent_money); + } auto satisfaction = state.world.pop_get_satisfaction(ids); - auto satisfaction_gain = ve::min(1.f, potential_ratio_life.get(ids) * ln_satisfaction) + ve::min(1.f, potential_ratio_everyday.get(ids) * en_satisfaction) + ve::min(1.f, potential_ratio_luxury.get(ids) * lx_satisfaction); satisfaction = satisfaction * 0.999f + satisfaction_gain * 0.001f / 3.f; + state.world.pop_set_satisfaction(ids, satisfaction); + } else { + ve::fp_vector total_satisfaction_from_categories{ 0.f }; + state.world.for_each_consumption_category([&](auto cat_id) { + auto cost = state.world.market_get_cost_per_consumption_category(local_market, cat_id); + auto actually_bought = state.world.market_get_satisfied_demand_ratio_per_consumption_category(local_market, cat_id); + auto demanded = demand_consumption_category[cat_id.index()].get(ids); + auto refund = (1.f - actually_bought) * cost * demanded; + auto savings = state.world.pop_get_savings(ids); + state.world.pop_set_savings(ids, savings + refund); + total_satisfaction_from_categories = total_satisfaction_from_categories + actually_bought * demanded * state.world.consumption_category_get_satisfaction_score(cat_id); + }); + auto satisfaction = state.world.pop_get_satisfaction(ids); + satisfaction = satisfaction * 0.999f + total_satisfaction_from_categories * 0.001f; state.world.pop_set_satisfaction(ids, satisfaction); } @@ -4355,7 +4436,69 @@ void daily_update(sys::state& state, bool presimulation, float presimulation_sta rebalance_needs_weights(state, mid); }); + // rebalance new needs + if(state.world.consumption_category_size() > 0) { + state.world.execute_parallel_over_market([&](auto market) { + state.world.for_each_consumption_category([&](auto cat_id){ + auto price_w = state.world.consumption_category_get_price_pressure(cat_id); + auto availability_w = state.world.consumption_category_get_availability_pressure(cat_id); + auto base_w = state.world.consumption_category_get_base_pressure(cat_id); + auto depends_on = state.world.consumption_category_get_scale_with(cat_id); + + ve::fp_vector max_cost = {0.f}; + state.world.for_each_commodity([&](auto cid) { + auto price = state.world.market_get_price(market, cid); + auto consumption_weight = state.world.consumption_category_get_weights(cat_id, cid); + //if (consumption_weight == 0.f) return; + //total_cost = total_cost + price * consumption_weight; + max_cost = ve::max(max_cost, price * consumption_weight); + }); + //auto average_cost = total_cost / float(state.world.commodity_size()); + + ve::fp_vector total_weight = 0.f; + state.world.for_each_commodity([&](auto cid) { + auto index = cid.index() + cat_id.index() * state.world.commodity_size(); + auto consumption_weight = state.world.consumption_category_get_weights(cat_id, cid); + auto current = state.world.market_get_local_consumption_weights(market, index); + auto price = state.world.market_get_price(market, cid); + auto availability = state.world.market_get_expected_probability_to_buy(market, cid); + auto change = + base_w + + availability_w * availability + + price_w * price * consumption_weight / max_cost; + auto next = ve::select( + max_cost == 0.f || consumption_weight == 0.f, + 0.f, + ve::max(0.f, current + change * state.defines.alice_need_drift_speed) + ); +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, next + ); +#endif + state.world.market_set_local_consumption_weights(market, index, next); + total_weight = total_weight + next; + }); + + state.world.for_each_commodity([&](auto cid) { + auto index = cid.index() + cat_id.index() * state.world.commodity_size(); + auto current = state.world.market_get_local_consumption_weights(market, index); + auto next = ve::select(total_weight == 0.f, 0.01f, current / total_weight); +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, next + ); +#endif + state.world.market_set_local_consumption_weights(market, index, next); + }); + }); + }); + } set_profile_point(state, "need weights"); diff --git a/src/economy/economy_pops.cpp b/src/economy/economy_pops.cpp index a60c7b3c8e..adb3c5dc08 100644 --- a/src/economy/economy_pops.cpp +++ b/src/economy/economy_pops.cpp @@ -179,10 +179,18 @@ auto prepare_pop_budget_templated( VALUE housing_spending_ratio = 0.3f; VALUE everyday_spending_ratio = state.defines.alice_needs_ev_spend * (1.f - is_poor); VALUE luxury_spending_ratio = state.defines.alice_needs_lx_spend * (1.f - is_poor); + VALUE categories_spending_ratio = 0.0f; VALUE education_spending_ratio = (0.2f) * (1.f - is_poor); VALUE investment_ratio = adaptive_ve::max(investment_rate(state, ids), 0.0f); VALUE banking_ratio = adaptive_ve::max(bank_saving_rate(state, ids), 0.0f); + if(state.world.consumption_category_size() != 0) { + life_spending_ratio = 0.f; + everyday_spending_ratio = 0.f; + luxury_spending_ratio = 0.f; + categories_spending_ratio = 0.5f; + } + VALUE total_spending_ratio = life_spending_ratio + housing_spending_ratio @@ -210,6 +218,7 @@ auto prepare_pop_budget_templated( // set actual budgets + VALUE spend_on_categories = categories_spending_ratio * savings; VALUE spend_on_life_needs = life_spending_ratio * savings; VALUE spend_on_housing = housing_spending_ratio * savings; VALUE spend_on_everyday_needs = everyday_spending_ratio * savings; @@ -219,16 +228,57 @@ auto prepare_pop_budget_templated( VALUE spend_on_bank_savings = banking_ratio * savings; // upload data to structure - // here we do logic which can't be made uniform - VALUE satisfaction = state.world.pop_get_satisfaction(ids); + if(state.world.consumption_category_size() != 0) { + // all consumption goes first in this model + state.world.for_each_consumption_category([&](auto cat) { + auto base_cost = state.world.market_get_cost_per_consumption_category(markets, cat); + auto cost = base_cost * pop_size / state.defines.alice_needs_scaling_factor; + auto can_spend = adaptive_ve::min(2.f * cost, spend_on_categories); + auto depends_on = state.world.consumption_category_get_buy_after(cat).id; + auto scales_with = state.world.consumption_category_get_scale_with(cat).id; + + // avoid spending when we depend on not fulfilled category + if(depends_on && depends_on.index() < (int)result.per_consumption_category.size()) { + auto base_scale = result.per_consumption_category[depends_on.index()].satisfied_with_money_ratio; + base_scale = adaptive_ve::min(base_scale , 1.f); + if(scales_with) { + auto weight = state.world.market_get_local_consumption_weights(markets, scales_with.index() + depends_on.index() * state.world.commodity_size()); + // for example, if we don't buy cars for transportation, don't buy fuel + can_spend = can_spend * base_scale * weight; + } else { + // for example, avoid luxury when starving; + can_spend = can_spend * base_scale * base_scale; + } + } + +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, can_spend + ); +#endif + + result.per_consumption_category.emplace_back(vectorized_budget_position { + .required = cost, + .satisfied_with_money_ratio = adaptive_ve::select(cost == 0.f, 10.f, can_spend / cost), + .satisfied_for_free_ratio = 0.f, + .spent = can_spend, + .demand_scale = 1.f + }); + spend_on_categories = spend_on_categories - can_spend; + savings = savings - can_spend; + result.spent_total = result.spent_total + can_spend; + }); + } + VALUE satisfaction = state.world.pop_get_satisfaction(ids); // ########## // life needs // ########## - VALUE old_life = pop_demographics::get_life_needs(state, ids); VALUE subsistence = adjusted_subsistence_score(state, provs); BOOL_VALUE rgo_worker = state.world.pop_type_get_is_paid_rgo_worker(pop_type); subsistence = adaptive_ve::select(rgo_worker, subsistence, 0.f); @@ -460,7 +510,8 @@ void update_consumption( ve::vectorizable_buffer& demand_everyday, ve::vectorizable_buffer& demand_luxury, ve::vectorizable_buffer& demand_paid_education, - ve::vectorizable_buffer& subsistence_ratio + ve::vectorizable_buffer& subsistence_ratio, + std::vector>& demand_consumption_category ) { uint32_t total_commodities = state.world.commodity_size(); @@ -519,6 +570,22 @@ void update_consumption( to_bank.set(ids, data.bank_savings.spent); to_investments.set(ids, data.investments.spent); + state.world.for_each_consumption_category([&](auto cat) { + auto scale = data.per_consumption_category[cat.index()].demand_scale; + auto demanded_ratio = data.per_consumption_category[cat.index()].satisfied_with_money_ratio; +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, multiplier* scale* demanded_ratio + ); +#endif + demand_consumption_category[cat.index()].set( + ids, + multiplier * scale * demanded_ratio + ); + }); + // we do save savings here because a part of education is given for free // which leads to some part of wealth not being spent most of the time state.world.pop_set_savings(ids, ve::max(0.f, data.remaining_savings)); @@ -567,6 +634,19 @@ void update_consumption( state.world.market_set_life_needs_scale(m, pop_type, old_life + life); state.world.market_set_everyday_needs_scale(m, pop_type, old_everyday + everyday); state.world.market_set_luxury_needs_scale(m, pop_type, old_luxury + luxury); + + state.world.for_each_consumption_category([&](auto cat){ + auto old = state.world.market_get_demand_per_consumption_category(m, cat); + auto to_add = demand_consumption_category[cat.index()].get(pop); +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, old + to_add + ); +#endif + state.world.market_set_demand_per_consumption_category(m, cat, old + to_add); + }); }); }); }); @@ -675,7 +755,34 @@ void update_consumption( register_demand(state, ids, cid, demand_luxury); } } + // do the same for new weights + state.world.for_each_commodity([&](auto cid) { + ve::fp_vector total = 0.f; + state.world.for_each_consumption_category([&](auto consumption) { + auto index = cid.index() + consumption.index() * state.world.commodity_size(); + auto w = state.world.market_get_local_consumption_weights(ids, index); + auto scale = state.world.market_get_demand_per_consumption_category(ids, consumption); + auto base_amount = state.world.consumption_category_get_weights(consumption, cid); +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, scale* w * base_amount + ); +#endif + total = total + scale * w * base_amount; +#ifndef NDEBUG + ve::apply( + [](float amount) { + assert(std::isfinite(amount) && amount >= 0.f); + }, total + ); +#endif + }); + register_demand(state, ids, cid, total); + }); }); + } float estimate_artisan_income(sys::state const& state, dcon::province_id pid, dcon::pop_type_id ptid, float size) { @@ -1786,6 +1893,31 @@ float estimate_pop_demand_internal_luxury( * invention_factor; } +float estimate_pop_demand_internal_category( + sys::state const& state, dcon::market_id m, dcon::consumption_category_id cat, dcon::commodity_id c, dcon::pop_id pop, + pops::vectorized_pops_budget& budget +) { + auto total = budget.per_consumption_category[cat.index()].demand_scale + * budget.per_consumption_category[cat.index()].satisfied_with_money_ratio + * state.world.market_get_local_consumption_weights(m, c.index() + cat.index() * state.world.commodity_size()); + + auto pop_size = state.world.pop_get_size(pop); + return total * pop_size + / state.defines.alice_needs_scaling_factor; +} +float estimate_pop_spending_category(sys::state const& state, dcon::consumption_category_id cat, dcon::pop_id pop, dcon::commodity_id cid) { + auto pid = state.world.pop_get_province_from_pop_location(pop); + auto zone = state.world.province_get_state_membership(pid); + auto market = state.world.state_instance_get_market_from_local_market(zone); + auto budget = prepare_pop_budget(state, pop); + auto demand = estimate_pop_demand_internal_category( + state, market, cat, cid, pop, budget + ); + auto actually_bought = state.world.market_get_actual_probability_to_buy(market, cid); + auto cost = economy::price(state, market, cid); + return demand * actually_bought * cost; +} + float estimate_pop_spending_life(sys::state const& state, dcon::pop_id pop, dcon::commodity_id cid) { auto pid = state.world.pop_get_province_from_pop_location(pop); auto nation = state.world.province_get_nation_from_province_ownership(pid); @@ -1937,6 +2069,12 @@ float estimate_pops_consumption(sys::state const& state, dcon::commodity_id c, d auto consumption_luxury = pops::estimate_pop_demand_internal_luxury( state, c, pop, budget, luxury_mul, weight_luxury, invention_factor ); + state.world.for_each_consumption_category([&](auto cat) { + auto categories = pops::estimate_pop_demand_internal_category( + state, market, cat, c, pop, budget + ); + total = total + categories; + }); total += consumption_life + consumption_everyday + consumption_luxury; }); diff --git a/src/economy/economy_pops.hpp b/src/economy/economy_pops.hpp index 28b4a40cc5..d650e6f162 100644 --- a/src/economy/economy_pops.hpp +++ b/src/economy/economy_pops.hpp @@ -26,7 +26,8 @@ void update_consumption( ve::vectorizable_buffer& demand_everyday, ve::vectorizable_buffer& demand_luxury, ve::vectorizable_buffer& demand_paid_education, - ve::vectorizable_buffer& subsistence_ratio + ve::vectorizable_buffer& subsistence_ratio, + std::vector>& demand_consumption_category ); void update_income_artisans(sys::state& state); void update_income_national_subsidy(sys::state& state); @@ -59,6 +60,7 @@ float estimate_tax_spending(sys::state const& state, dcon::pop_id pop, float tax float estimate_pop_spending_life(sys::state const& state, dcon::pop_id pop, dcon::commodity_id cid); float estimate_pop_spending_everyday(sys::state const& state, dcon::pop_id pop, dcon::commodity_id cid); float estimate_pop_spending_luxury(sys::state const& state, dcon::pop_id pop, dcon::commodity_id cid); +float estimate_pop_spending_category(sys::state const& state, dcon::consumption_category_id cat, dcon::pop_id pop, dcon::commodity_id cid); } float estimate_pops_consumption(sys::state const& state, dcon::commodity_id c, dcon::province_id p); diff --git a/src/economy/economy_stats.cpp b/src/economy/economy_stats.cpp index b4ad6a2b0b..823755e495 100644 --- a/src/economy/economy_stats.cpp +++ b/src/economy/economy_stats.cpp @@ -55,21 +55,25 @@ void t_register_demand( ve::fp_vector amount //economy_reason reason ) { +#ifndef NDEBUG ve::apply( [](float amount) { assert(std::isfinite(amount) && amount >= 0.f); }, amount ); +#endif state.world.market_set_demand( s, commodity_type, state.world.market_get_demand(s, commodity_type) + amount ); +#ifndef NDEBUG ve::apply( [](float demand) { assert(std::isfinite(demand) && demand >= 0.f); }, state.world.market_get_demand(s, commodity_type) ); +#endif } void register_demand( @@ -1611,6 +1615,9 @@ nation_monetary_breakdown breakdown_nation_monetary_structure(sys::state& state, result.pops_spending_life += pops::estimate_pop_spending_life(state, pop, c); result.pops_spending_everyday += pops::estimate_pop_spending_everyday(state, pop, c); result.pops_spending_luxury += pops::estimate_pop_spending_luxury(state, pop, c); + state.world.for_each_consumption_category([&](auto cat) { + result.pops_spending_categories += pops::estimate_pop_spending_category(state, cat, pop, c); + }); }); auto budget = pops::prepare_pop_budget(state, pop); diff --git a/src/economy/economy_stats.hpp b/src/economy/economy_stats.hpp index 6fb03c7888..4d8adba7e2 100644 --- a/src/economy/economy_stats.hpp +++ b/src/economy/economy_stats.hpp @@ -450,6 +450,7 @@ struct nation_monetary_breakdown { float pops_spending_life = 0.f; float pops_spending_everyday = 0.f; float pops_spending_luxury = 0.f; + float pops_spending_categories = 0.f; float pops_spending_housing = 0.f; float pops_spending_education = 0.f; diff --git a/src/economy/economy_templates_pure.hpp b/src/economy/economy_templates_pure.hpp index 820ed06be4..f717c27c02 100644 --- a/src/economy/economy_templates_pure.hpp +++ b/src/economy/economy_templates_pure.hpp @@ -76,6 +76,7 @@ struct vectorized_budget_position { template struct vectorized_pops_budget { + std::vector> per_consumption_category{ }; vectorized_budget_position life_needs{}; vectorized_budget_position housing{ }; vectorized_budget_position everyday_needs{}; diff --git a/src/gamestate/dcon_generated.txt b/src/gamestate/dcon_generated.txt index 362c4ab3f6..03b27ab478 100644 --- a/src/gamestate/dcon_generated.txt +++ b/src/gamestate/dcon_generated.txt @@ -4435,6 +4435,51 @@ object { } } +object{ + name { consumption_category } + storage_type{ contiguous } + size{ 250 } + tag{ scenario } + + property { + name{ satisfaction_score } + type{float} + tag{scenario} + } + + property { + name{ weights } + type{ array{commodity_id}{float} } + tag{ scenario } + } + property { + name{ base_pressure } + type{ float } + tag{ scenario } + } + property { + name{ price_pressure } + type{ float } + tag{ scenario } + } + property { + name{ availability_pressure } + type{ float } + tag{ scenario } + } + property { + name{buy_after} + type{ consumption_category_id } + tag{ scenario } + } + property { + name{scale_with} + type{ commodity_id } + tag{ scenario } + } +} + + object{ name { market } storage_type{ erasable } @@ -4640,8 +4685,33 @@ object{ type{ float } tag{ save } } + + property { + name{ local_consumption_weights } + type{ array{int32_t}{float} } + tag{ save } + } + + property { + name{ demand_per_consumption_category } + type{ array{consumption_category_id}{float} } + tag{ save } + } + + property { + name{ cost_per_consumption_category } + type{ array{consumption_category_id}{float} } + tag{ save } + } + + property { + name{ satisfied_demand_ratio_per_consumption_category } + type{ array{consumption_category_id}{float} } + tag{ save } + } } + relationship { name{ local_market } storage_type{ compactable } diff --git a/src/gamestate/lua_alice_api.cpp b/src/gamestate/lua_alice_api.cpp index 0f5aa1c980..68ea75c51d 100644 --- a/src/gamestate/lua_alice_api.cpp +++ b/src/gamestate/lua_alice_api.cpp @@ -36,7 +36,68 @@ extern "C" { DCON_LUADLL_API uint8_t lua_get_gamerule_option_id_by_name(const char gamerule_option_name[]); DCON_LUADLL_API bool lua_check_gamerule(int32_t gamerule_id, uint8_t opt_id); DCON_LUADLL_API int32_t lua_get_active_gamerule_option(int32_t gamerule_id); + DCON_LUADLL_API int32_t lua_create_text_key(const char key[]); + DCON_LUADLL_API void lua_add_rgo_efficiency_commodity(int32_t target, int32_t commodity, float volume); + DCON_LUADLL_API void lua_add_factory_efficiency_commodity(int32_t target, int32_t commodity, float volume); + DCON_LUADLL_API void lua_add_factory_input_commodity(int32_t target, int32_t commodity, float volume); + DCON_LUADLL_API void lua_add_factory_cost_commodity(int32_t target, int32_t commodity, float volume); +} + +//dcon::text_key find_or_add_key(sys::state& state, std::string_view key, bool as_unicode); + +int32_t lua_create_text_key(const char key[]) { + std::string key_str { key }; + return text::find_or_add_key(*alice_state_ptr, key_str, true).index(); +} + +void lua_add_rgo_efficiency_commodity(int32_t target, int32_t commodity, float volume) { + auto t = dcon::commodity_id{ (dcon::commodity_id::value_base_t)target }; + auto cid = dcon::commodity_id{ (dcon::commodity_id::value_base_t)commodity}; + auto& efficiency = alice_state_ptr->world.commodity_get_rgo_efficiency_inputs(t); + for(uint8_t i = 0; i < efficiency.set_size; i++) { + if(!efficiency.commodity_type[i]) { + efficiency.commodity_type[i] = cid; + efficiency.commodity_amounts[i] = volume; + return; + } + } +} +void lua_add_factory_efficiency_commodity(int32_t target, int32_t commodity, float volume) { + auto t = dcon::factory_type_id{ (dcon::factory_type_id::value_base_t)target }; + auto cid = dcon::commodity_id{ (dcon::commodity_id::value_base_t)commodity }; + auto& efficiency = alice_state_ptr->world.factory_type_get_efficiency_inputs(t); + for(uint8_t i = 0; i < efficiency.set_size; i++) { + if(!efficiency.commodity_type[i]) { + efficiency.commodity_type[i] = cid; + efficiency.commodity_amounts[i] = volume; + return; + } + } +} +void lua_add_factory_input_commodity(int32_t target, int32_t commodity, float volume) { + auto t = dcon::factory_type_id{ (dcon::factory_type_id::value_base_t)target }; + auto cid = dcon::commodity_id{ (dcon::commodity_id::value_base_t)commodity }; + auto& cid_set = alice_state_ptr->world.factory_type_get_inputs(t); + for(uint8_t i = 0; i < cid_set.set_size; i++) { + if(!cid_set.commodity_type[i]) { + cid_set.commodity_type[i] = cid; + cid_set.commodity_amounts[i] = volume; + return; + } + } +} +void lua_add_factory_cost_commodity(int32_t target, int32_t commodity, float volume) { + auto t = dcon::factory_type_id{ (dcon::factory_type_id::value_base_t)target }; + auto cid = dcon::commodity_id{ (dcon::commodity_id::value_base_t)commodity }; + auto& cid_set = alice_state_ptr->world.factory_type_get_construction_costs(t); + for(uint8_t i = 0; i < cid_set.set_size; i++) { + if(!cid_set.commodity_type[i]) { + cid_set.commodity_type[i] = cid; + cid_set.commodity_amounts[i] = volume; + return; + } + } } void console_log(const char message[]) { diff --git a/src/gamestate/system_state.cpp b/src/gamestate/system_state.cpp index adc1d55ac5..43973fd708 100644 --- a/src/gamestate/system_state.cpp +++ b/src/gamestate/system_state.cpp @@ -1974,16 +1974,20 @@ void state::load_scenario_data(parsers::error_handler& err, sys::year_month_day int status; status = luaL_dostring(lua_game_loop_environment, lua_combined_script.c_str()); if(status) { + auto error_string = lua_tostring(lua_game_loop_environment, -1); + window::emit_error_message(error_string, true); #ifdef _WIN32 - OutputDebugStringA(lua_tostring(lua_game_loop_environment, -1)); + OutputDebugStringA(error_string); #endif lua_settop(lua_game_loop_environment, 0); std::abort(); } status = luaL_dostring(lua_game_loop_environment, lua_game_loop_script.c_str()); if(status) { + auto error_string = lua_tostring(lua_game_loop_environment, -1); + window::emit_error_message(error_string, true); #ifdef _WIN32 - OutputDebugStringA(lua_tostring(lua_game_loop_environment, -1)); + OutputDebugStringA(error_string); #endif lua_settop(lua_game_loop_environment, 0); std::abort(); @@ -1994,8 +1998,17 @@ void state::load_scenario_data(parsers::error_handler& err, sys::year_month_day err.accumulated_warnings += "update_administrative_efficiency function was overidden from LUA\n"; } + // Start with loading Lua scenario data. - + lua_getfield(lua_game_loop_environment, LUA_GLOBALSINDEX, "LOAD_SCENARIO_DATA"); + auto result = lua_pcall(lua_game_loop_environment, 0, 0, 0); + if(result) { + auto error_string = lua_tostring(lua_game_loop_environment, -1); + window::emit_error_message(error_string, true); + console_log(lua_tostring(lua_game_loop_environment, -1)); + lua_settop(lua_game_loop_environment, 0); + } + assert(lua_gettop(lua_game_loop_environment) == 0); //text::name_into_font_id(*this, "garamond_14"); ui::load_text_gui_definitions(*this, context.gfx_context, err); @@ -2126,7 +2139,10 @@ void state::load_scenario_data(parsers::error_handler& err, sys::year_month_day } } // read commodities from goods.txt - { + + if(world.commodity_size() == 0) { + // if lua haven't created any commodity + // FIRST: make sure that we have a money good if(world.commodity_size() == 0) { // create money @@ -2147,10 +2163,31 @@ void state::load_scenario_data(parsers::error_handler& err, sys::year_month_day err.fatal = true; err.accumulated_errors += "File common/goods.txt nor common/tradegoods.txt could be opened\n"; } + } else { + // register lua commodity names + world.for_each_commodity( [&](auto cid){ + auto name_id = world.commodity_get_name(cid); + auto name = to_string_view(name_id); + auto name_str = std::string(name); + context.map_of_commodity_names.insert_or_assign(name_str, cid); + }); } // read buildings.text // world.factory_type_resize_construction_costs(world.commodity_size()); + if(world.factory_type_size() == 0) { + context.lua_factories = false; + } else { + // register factory names + world.for_each_factory_type([&](auto cid) { + auto name_id = world.factory_type_get_name(cid); + auto name = to_string_view(name_id); + auto name_str = std::string (name); + context.map_of_factory_names.insert_or_assign(name_str, cid); + }); + context.lua_factories = true; + } + { auto buildings = open_file(common, NATIVE("buildings.txt")); if(buildings) { @@ -3251,6 +3288,11 @@ void state::load_scenario_data(parsers::error_handler& err, sys::year_month_day world.market_resize_everyday_needs_weights(world.commodity_size()); world.market_resize_luxury_needs_weights(world.commodity_size()); + world.market_resize_local_consumption_weights(world.commodity_size() * world.consumption_category_size()); + world.market_resize_demand_per_consumption_category(world.consumption_category_size()); + world.market_resize_cost_per_consumption_category(world.consumption_category_size()); + world.market_resize_satisfied_demand_ratio_per_consumption_category(world.consumption_category_size()); + world.province_resize_labor_price(economy::labor::total); world.province_resize_labor_supply(economy::labor::total); world.province_resize_labor_demand(economy::labor::total); diff --git a/src/gui/main_menu.cpp b/src/gui/main_menu.cpp index f7a125459e..23d793b33b 100644 --- a/src/gui/main_menu.cpp +++ b/src/gui/main_menu.cpp @@ -2812,9 +2812,9 @@ void main_menu_misc_auto_save_dropdown_t::on_update(sys::state& state) noexcept // BEGIN misc::auto_save_dropdown::update clear_list(); add_item(int32_t(sys::autosave_frequency::none), text::produce_simple_string(state, "auto_save_0")); - add_item(int32_t(sys::autosave_frequency::daily), text::produce_simple_string(state, "auto_save_1")); - add_item(int32_t(sys::autosave_frequency::monthly), text::produce_simple_string(state, "auto_save_2")); add_item(int32_t(sys::autosave_frequency::yearly), text::produce_simple_string(state, "auto_save_3")); + add_item(int32_t(sys::autosave_frequency::monthly), text::produce_simple_string(state, "auto_save_2")); + add_item(int32_t(sys::autosave_frequency::daily), text::produce_simple_string(state, "auto_save_1")); quiet_on_selection(state, int32_t(state.user_settings.autosaves)); // END } diff --git a/src/gui/pop_budget_details.cpp b/src/gui/pop_budget_details.cpp index 908a6870a5..fd37d4ca47 100644 --- a/src/gui/pop_budget_details.cpp +++ b/src/gui/pop_budget_details.cpp @@ -392,6 +392,15 @@ void pop_budget_details_main_needs_t::update(sys::state& state, layout_window_e add_consumption(cid, 2, cost, state.world.market_get_actual_probability_to_buy(market, cid)); } }); + + state.world.for_each_consumption_category([&](auto cat) { + state.world.for_each_commodity([&](dcon::commodity_id cid) { + auto cost = economy::pops::estimate_pop_spending_category(state, cat, main.for_pop, cid); + if(cost > 0.001f) { + add_consumption(cid, (uint8_t)(cat.index()) + 3, cost, state.world.market_get_actual_probability_to_buy(market, cid)); + } + }); + }); // END } measure_result pop_budget_details_main_needs_t::place_item(sys::state& state, ui::non_owning_container_base* destination, size_t index, int32_t x, int32_t y, bool first_in_section, bool& alternate) { @@ -526,6 +535,14 @@ void pop_budget_details_main_needs_s_value_t::on_update(sys::state& state) noexc total += cost; }); + // categories + state.world.for_each_consumption_category([&](dcon::consumption_category_id cat){ + state.world.for_each_commodity([&](dcon::commodity_id cid){ + auto cost = economy::pops::estimate_pop_spending_category(state, cat, main.for_pop, cid); + total += cost; + }); + }); + set_text(state, text::format_money(total)); // END } @@ -1915,6 +1932,8 @@ void pop_budget_details_consumption_name_t::on_update(sys::state& state) noexcep modifier = text::produce_simple_string(state, "alice_pop_details_en_short"); } else if(consumption.category == 2) { modifier = text::produce_simple_string(state, "alice_pop_details_lx_short"); + } else { + modifier = std::to_string(consumption.category - 3); } auto commodity_name = text::produce_simple_string(state, state.world.commodity_get_name(consumption.cid)); set_text(state, "(" + modifier + ")" + commodity_name); diff --git a/src/gui/pop_details.cpp b/src/gui/pop_details.cpp index 7a0690533f..a3041ae0b1 100644 --- a/src/gui/pop_details.cpp +++ b/src/gui/pop_details.cpp @@ -1509,12 +1509,24 @@ void pop_details_main_ln_list_t::update(sys::state& state, layout_window_elemen auto m = state.world.state_instance_get_market_from_local_market(s); values.clear(); - auto type = state.world.pop_get_poptype(main.for_pop); - for(auto c : state.world.in_commodity) { - auto v = state.world.pop_type_get_life_needs(type, c); - if(v > 0) { - add_needs_row(c.id, v, state.world.market_get_life_needs_weights(m, c)); + + if(state.world.consumption_category_size() == 0) { + auto type = state.world.pop_get_poptype(main.for_pop); + for(auto c : state.world.in_commodity) { + auto v = state.world.pop_type_get_life_needs(type, c); + if(v > 0) { + add_needs_row(c.id, v, state.world.market_get_life_needs_weights(m, c)); + } } + } else { + state.world.for_each_consumption_category([&](auto cat) { + for(auto c : state.world.in_commodity) { + auto v = state.world.consumption_category_get_weights(cat, c); + if(v > 0) { + add_needs_row(c.id, v, state.world.market_get_local_consumption_weights(m, c.id.index() + cat.index() * state.world.commodity_size())); + } + } + }); } // END { @@ -1651,13 +1663,14 @@ void pop_details_main_en_list_t::update(sys::state& state, layout_window_elemen auto l = state.world.pop_get_province_from_pop_location(main.for_pop); auto s = state.world.province_get_state_membership(l); auto m = state.world.state_instance_get_market_from_local_market(s); - - values.clear(); - auto type = state.world.pop_get_poptype(main.for_pop); - for(auto c : state.world.in_commodity) { - auto v = state.world.pop_type_get_everyday_needs(type, c); - if(v > 0) { - add_needs_row(c.id, v, state.world.market_get_everyday_needs_weights(m, c)); + if(state.world.consumption_category_size() == 0) { + values.clear(); + auto type = state.world.pop_get_poptype(main.for_pop); + for(auto c : state.world.in_commodity) { + auto v = state.world.pop_type_get_everyday_needs(type, c); + if(v > 0) { + add_needs_row(c.id, v, state.world.market_get_everyday_needs_weights(m, c)); + } } } // END @@ -1795,13 +1808,14 @@ void pop_details_main_lx_list_t::update(sys::state& state, layout_window_elemen auto l = state.world.pop_get_province_from_pop_location(main.for_pop); auto s = state.world.province_get_state_membership(l); auto m = state.world.state_instance_get_market_from_local_market(s); - - values.clear(); - auto type = state.world.pop_get_poptype(main.for_pop); - for(auto c : state.world.in_commodity) { - auto v = state.world.pop_type_get_luxury_needs(type, c); - if(v > 0) { - add_needs_row(c.id, v, state.world.market_get_luxury_needs_weights(m, c)); + if(state.world.consumption_category_size() == 0) { + values.clear(); + auto type = state.world.pop_get_poptype(main.for_pop); + for(auto c : state.world.in_commodity) { + auto v = state.world.pop_type_get_luxury_needs(type, c); + if(v > 0) { + add_needs_row(c.id, v, state.world.market_get_luxury_needs_weights(m, c)); + } } } // END diff --git a/src/map/map_borders.cpp b/src/map/map_borders.cpp index 368f0bce34..21efd73276 100644 --- a/src/map/map_borders.cpp +++ b/src/map/map_borders.cpp @@ -793,7 +793,7 @@ void display_data::make_borders(sys::state& state, std::vector& visited edge_to_fix = (int)border_edges.size() - 1; - auto& prev_prev = border_edges[edge_to_fix - 1]; + //auto& prev_prev = border_edges[edge_to_fix - 1]; assert(border_edges[edge_to_fix].offset > last_vertex_idx); } else { diff --git a/src/parsing/parsers_declarations.cpp b/src/parsing/parsers_declarations.cpp index 1418b0809b..3f5a493f93 100644 --- a/src/parsing/parsers_declarations.cpp +++ b/src/parsing/parsers_declarations.cpp @@ -675,6 +675,7 @@ void poptype_file::equivalent(association_type, std::string_view value, error_ha } void poptype_file::life_needs(commodity_array const& value, error_handler& err, int32_t line, poptype_context& context) { + //if (context.outer_context.state.world.consumption_category_size() > 0) return; context.outer_context.state.world.for_each_commodity([&](dcon::commodity_id cid) { if(cid.index() < value.data.ssize()) context.outer_context.state.world.pop_type_set_life_needs(context.id, cid, value.data[cid]); @@ -682,6 +683,7 @@ void poptype_file::life_needs(commodity_array const& value, error_handler& err, } void poptype_file::everyday_needs(commodity_array const& value, error_handler& err, int32_t line, poptype_context& context) { + //if(context.outer_context.state.world.consumption_category_size() > 0) return; context.outer_context.state.world.for_each_commodity([&](dcon::commodity_id cid) { if(cid.index() < value.data.ssize()) context.outer_context.state.world.pop_type_set_everyday_needs(context.id, cid, value.data[cid]); @@ -689,6 +691,7 @@ void poptype_file::everyday_needs(commodity_array const& value, error_handler& e } void poptype_file::luxury_needs(commodity_array const& value, error_handler& err, int32_t line, poptype_context& context) { + //if(context.outer_context.state.world.consumption_category_size() > 0) return; context.outer_context.state.world.for_each_commodity([&](dcon::commodity_id cid) { if(cid.index() < value.data.ssize()) context.outer_context.state.world.pop_type_set_luxury_needs(context.id, cid, value.data[cid]); diff --git a/src/parsing/parsers_declarations.hpp b/src/parsing/parsers_declarations.hpp index 10c8e44afe..d85ea7a620 100644 --- a/src/parsing/parsers_declarations.hpp +++ b/src/parsing/parsers_declarations.hpp @@ -412,6 +412,7 @@ struct scenario_building_context { ankerl::unordered_dense::map map_of_commodity_names; ankerl::unordered_dense::map map_of_production_types; ankerl::unordered_dense::map map_of_factory_names; + bool lua_factories; ankerl::unordered_dense::map map_of_ideologies; ankerl::unordered_dense::map map_of_ideology_groups; ankerl::unordered_dense::map map_of_ioptions;