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/assets/pop_budget_details.aui b/assets/pop_budget_details.aui index 6cc88ae16c..f78f28dbbd 100644 Binary files a/assets/pop_budget_details.aui and b/assets/pop_budget_details.aui differ 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..64dd3272a7 100644 --- a/src/gamestate/dcon_generated.txt +++ b/src/gamestate/dcon_generated.txt @@ -4435,6 +4435,57 @@ object { } } +object{ + name { consumption_category } + storage_type{ contiguous } + size{ 250 } + tag{ scenario } + + property { + name{ name } + type{text_key} + 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 +4691,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..58d62398a9 100644 --- a/src/gui/pop_budget_details.cpp +++ b/src/gui/pop_budget_details.cpp @@ -1,8 +1,7 @@ +// BEGIN prelude +// END + namespace alice_ui { -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wswitch" -#endif struct pop_budget_details_main_unemployment_value_t; struct pop_budget_details_main_pension_value_t; struct pop_budget_details_main_rgo_value_t; @@ -26,6 +25,8 @@ struct pop_budget_details_consumption_name_t; struct pop_budget_details_consumption_satisfaction_t; struct pop_budget_details_consumption_cost_t; struct pop_budget_details_consumption_t; +struct pop_budget_details_consumption_category_name_t; +struct pop_budget_details_consumption_category_t; struct pop_budget_details_main_unemployment_value_t : public alice_ui::template_label { // BEGIN main::unemployment_value::variables // END @@ -102,7 +103,11 @@ struct pop_budget_details_main_needs_t : public layout_generator { std::vector> consumption_pool; int32_t consumption_pool_used = 0; void add_consumption( dcon::commodity_id cid, uint8_t category, float cost_val, float satisfaction_val); - std::vector> values; + struct consumption_category_option { dcon::consumption_category_id cat; }; + std::vector> consumption_category_pool; + int32_t consumption_category_pool_used = 0; + void add_consumption_category( dcon::consumption_category_id cat); + std::vector> values; void on_create(sys::state& state, layout_window_element* container); void update(sys::state& state, layout_window_element* container); measure_result 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) override; @@ -149,6 +154,11 @@ struct pop_budget_details_consumption_cost_t : public alice_ui::template_label { // END void on_update(sys::state& state) noexcept override; }; +struct pop_budget_details_consumption_category_name_t : public alice_ui::template_label { +// BEGIN consumption_category::name::variables +// END + void on_update(sys::state& state) noexcept override; +}; struct pop_budget_details_main_t : public layout_window_element { // BEGIN main::variables // END @@ -159,11 +169,6 @@ struct pop_budget_details_main_t : public layout_window_element { std::unique_ptr header_spending; std::unique_ptr fancy_bar_1; std::unique_ptr fancy_bar_2; - std::unique_ptr fancy_bar_3; - std::unique_ptr fancy_bar_4; - std::unique_ptr fancy_bar_5; - std::unique_ptr fancy_bar_6; - std::unique_ptr fancy_bar_7; std::unique_ptr fancy_bar_8; std::unique_ptr unemployment_label; std::unique_ptr pension_label; @@ -308,6 +313,27 @@ struct pop_budget_details_consumption_t : public layout_window_element { } }; std::unique_ptr make_pop_budget_details_consumption(sys::state& state); +struct pop_budget_details_consumption_category_t : public layout_window_element { +// BEGIN consumption_category::variables +// END + dcon::consumption_category_id cat; + ankerl::unordered_dense::map> scripted_elements; + std::unique_ptr fancy_block; + std::unique_ptr name; + std::vector> gui_inserts; + void create_layout_level(sys::state& state, layout_level& lvl, char const* ldata, size_t sz); + void on_create(sys::state& state) noexcept override; + ui::message_result on_lbutton_down(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override; + ui::message_result on_rbutton_down(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override; + void on_update(sys::state& state) noexcept override; + void* get_by_name(sys::state& state, std::string_view name_parameter) noexcept override { + if(name_parameter == "cat") { + return (void*)(&cat); + } + return nullptr; + } +}; +std::unique_ptr make_pop_budget_details_consumption_category(sys::state& state); void pop_budget_details_main_wage_per_labor_t::add_wage_income(int32_t labor_type_index, float ratio, float wage) { values.emplace_back(wage_income_option{labor_type_index, ratio, wage}); } @@ -344,7 +370,8 @@ measure_result pop_budget_details_main_wage_per_labor_t::place_item(sys::state& wage_income_pool_used++; } alternate = !alternate; - return measure_result{ wage_income_pool[0]->base_data.size.x, wage_income_pool[0]->base_data.size.y + 0, measure_result::special::none}; + bool stick_to_next = false; + return measure_result{ wage_income_pool[0]->base_data.size.x, wage_income_pool[0]->base_data.size.y + 0, stick_to_next ? measure_result::special::no_break : measure_result::special::none}; } return measure_result{0,0,measure_result::special::none}; } @@ -354,6 +381,9 @@ void pop_budget_details_main_wage_per_labor_t::reset_pools() { void pop_budget_details_main_needs_t::add_consumption(dcon::commodity_id cid, uint8_t category, float cost_val, float satisfaction_val) { values.emplace_back(consumption_option{cid, category, cost_val, satisfaction_val}); } +void pop_budget_details_main_needs_t::add_consumption_category(dcon::consumption_category_id cat) { + values.emplace_back(consumption_category_option{cat}); +} void pop_budget_details_main_needs_t::on_create(sys::state& state, layout_window_element* parent) { pop_budget_details_main_t& main = *((pop_budget_details_main_t*)(parent)); // BEGIN main::needs::on_create @@ -392,6 +422,16 @@ 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) { + add_consumption_category(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) { @@ -413,12 +453,30 @@ measure_result pop_budget_details_main_needs_t::place_item(sys::state& state, u consumption_pool_used++; } alternate = !alternate; - return measure_result{ consumption_pool[0]->base_data.size.x, consumption_pool[0]->base_data.size.y + 0, measure_result::special::none}; + bool stick_to_next = false; + return measure_result{ consumption_pool[0]->base_data.size.x, consumption_pool[0]->base_data.size.y + 0, stick_to_next ? measure_result::special::no_break : measure_result::special::none}; + } + if(std::holds_alternative(values[index])) { + if(consumption_category_pool.empty()) consumption_category_pool.emplace_back(make_pop_budget_details_consumption_category(state)); + if(destination) { + if(consumption_category_pool.size() <= size_t(consumption_category_pool_used)) consumption_category_pool.emplace_back(make_pop_budget_details_consumption_category(state)); + consumption_category_pool[consumption_category_pool_used]->base_data.position.x = int16_t(x); + consumption_category_pool[consumption_category_pool_used]->base_data.position.y = int16_t(y); + consumption_category_pool[consumption_category_pool_used]->parent = destination; + destination->children.push_back(consumption_category_pool[consumption_category_pool_used].get()); + ((pop_budget_details_consumption_category_t*)(consumption_category_pool[consumption_category_pool_used].get()))->cat = std::get(values[index]).cat; + consumption_category_pool[consumption_category_pool_used]->impl_on_update(state); + consumption_category_pool_used++; + } + alternate = true; + bool stick_to_next = false; + return measure_result{ consumption_category_pool[0]->base_data.size.x, consumption_category_pool[0]->base_data.size.y + 0, stick_to_next ? measure_result::special::no_break : measure_result::special::none}; } return measure_result{0,0,measure_result::special::none}; } void pop_budget_details_main_needs_t::reset_pools() { consumption_pool_used = 0; + consumption_category_pool_used = 0; } void pop_budget_details_main_unemployment_value_t::on_update(sys::state& state) noexcept { pop_budget_details_main_t& main = *((pop_budget_details_main_t*)(parent)); @@ -526,6 +584,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 } @@ -580,13 +646,15 @@ void pop_budget_details_main_t::create_layout_level(sys::state& state, layout_le buffer.read(temp.texture); lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::control: + case layout_item_types::control2: { layout_control temp; std::string_view cname = buffer.read(); buffer.read(temp.abs_x); buffer.read(temp.abs_y); buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); temp.ptr = nullptr; if(cname == "header") { temp.ptr = header.get(); @@ -603,21 +671,6 @@ void pop_budget_details_main_t::create_layout_level(sys::state& state, layout_le if(cname == "fancy_bar_2") { temp.ptr = fancy_bar_2.get(); } else - if(cname == "fancy_bar_3") { - temp.ptr = fancy_bar_3.get(); - } else - if(cname == "fancy_bar_4") { - temp.ptr = fancy_bar_4.get(); - } else - if(cname == "fancy_bar_5") { - temp.ptr = fancy_bar_5.get(); - } else - if(cname == "fancy_bar_6") { - temp.ptr = fancy_bar_6.get(); - } else - if(cname == "fancy_bar_7") { - temp.ptr = fancy_bar_7.get(); - } else if(cname == "fancy_bar_8") { temp.ptr = fancy_bar_8.get(); } else @@ -705,13 +758,15 @@ void pop_budget_details_main_t::create_layout_level(sys::state& state, layout_le } lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::window: + case layout_item_types::window2: { layout_window temp; std::string_view cname = buffer.read(); buffer.read(temp.abs_x); buffer.read(temp.abs_y); buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); if(cname == "main") { temp.ptr = make_pop_budget_details_main(state); } @@ -724,6 +779,9 @@ void pop_budget_details_main_t::create_layout_level(sys::state& state, layout_le if(cname == "consumption") { temp.ptr = make_pop_budget_details_consumption(state); } + if(cname == "consumption_category") { + temp.ptr = make_pop_budget_details_consumption_category(state); + } lvl.contents.emplace_back(std::move(temp)); } break; case layout_item_types::glue: @@ -733,7 +791,7 @@ void pop_budget_details_main_t::create_layout_level(sys::state& state, layout_le buffer.read(temp.amount); lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::generator: + case layout_item_types::generator2: { generator_instance temp; std::string_view cname = buffer.read(); @@ -856,86 +914,6 @@ void pop_budget_details_main_t::on_create(sys::state& state) noexcept { children.push_back(cptr); pending_children.pop_back(); continue; } else - if(child_data.name == "fancy_bar_3") { - fancy_bar_3 = std::make_unique(); - fancy_bar_3->parent = this; - auto cptr = fancy_bar_3.get(); - cptr->base_data.position.x = child_data.x_pos; - cptr->base_data.position.y = child_data.y_pos; - cptr->base_data.size.x = child_data.x_size; - cptr->base_data.size.y = child_data.y_size; - cptr->template_id = child_data.template_id; - if(child_data.tooltip_text_key.length() > 0) - cptr->default_tooltip = state.lookup_key(child_data.tooltip_text_key); - cptr->parent = this; - cptr->on_create(state); - children.push_back(cptr); - pending_children.pop_back(); continue; - } else - if(child_data.name == "fancy_bar_4") { - fancy_bar_4 = std::make_unique(); - fancy_bar_4->parent = this; - auto cptr = fancy_bar_4.get(); - cptr->base_data.position.x = child_data.x_pos; - cptr->base_data.position.y = child_data.y_pos; - cptr->base_data.size.x = child_data.x_size; - cptr->base_data.size.y = child_data.y_size; - cptr->template_id = child_data.template_id; - if(child_data.tooltip_text_key.length() > 0) - cptr->default_tooltip = state.lookup_key(child_data.tooltip_text_key); - cptr->parent = this; - cptr->on_create(state); - children.push_back(cptr); - pending_children.pop_back(); continue; - } else - if(child_data.name == "fancy_bar_5") { - fancy_bar_5 = std::make_unique(); - fancy_bar_5->parent = this; - auto cptr = fancy_bar_5.get(); - cptr->base_data.position.x = child_data.x_pos; - cptr->base_data.position.y = child_data.y_pos; - cptr->base_data.size.x = child_data.x_size; - cptr->base_data.size.y = child_data.y_size; - cptr->template_id = child_data.template_id; - if(child_data.tooltip_text_key.length() > 0) - cptr->default_tooltip = state.lookup_key(child_data.tooltip_text_key); - cptr->parent = this; - cptr->on_create(state); - children.push_back(cptr); - pending_children.pop_back(); continue; - } else - if(child_data.name == "fancy_bar_6") { - fancy_bar_6 = std::make_unique(); - fancy_bar_6->parent = this; - auto cptr = fancy_bar_6.get(); - cptr->base_data.position.x = child_data.x_pos; - cptr->base_data.position.y = child_data.y_pos; - cptr->base_data.size.x = child_data.x_size; - cptr->base_data.size.y = child_data.y_size; - cptr->template_id = child_data.template_id; - if(child_data.tooltip_text_key.length() > 0) - cptr->default_tooltip = state.lookup_key(child_data.tooltip_text_key); - cptr->parent = this; - cptr->on_create(state); - children.push_back(cptr); - pending_children.pop_back(); continue; - } else - if(child_data.name == "fancy_bar_7") { - fancy_bar_7 = std::make_unique(); - fancy_bar_7->parent = this; - auto cptr = fancy_bar_7.get(); - cptr->base_data.position.x = child_data.x_pos; - cptr->base_data.position.y = child_data.y_pos; - cptr->base_data.size.x = child_data.x_size; - cptr->base_data.size.y = child_data.y_size; - cptr->template_id = child_data.template_id; - if(child_data.tooltip_text_key.length() > 0) - cptr->default_tooltip = state.lookup_key(child_data.tooltip_text_key); - cptr->parent = this; - cptr->on_create(state); - children.push_back(cptr); - pending_children.pop_back(); continue; - } else if(child_data.name == "fancy_bar_8") { fancy_bar_8 = std::make_unique(); fancy_bar_8->parent = this; @@ -1511,13 +1489,15 @@ void pop_budget_details_wage_income_t::create_layout_level(sys::state& state, la buffer.read(temp.texture); lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::control: + case layout_item_types::control2: { layout_control temp; std::string_view cname = buffer.read(); buffer.read(temp.abs_x); buffer.read(temp.abs_y); buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); temp.ptr = nullptr; if(cname == "labor_type") { temp.ptr = labor_type.get(); @@ -1537,13 +1517,15 @@ void pop_budget_details_wage_income_t::create_layout_level(sys::state& state, la } lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::window: + case layout_item_types::window2: { layout_window temp; std::string_view cname = buffer.read(); buffer.read(temp.abs_x); buffer.read(temp.abs_y); buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); if(cname == "main") { temp.ptr = make_pop_budget_details_main(state); } @@ -1556,6 +1538,9 @@ void pop_budget_details_wage_income_t::create_layout_level(sys::state& state, la if(cname == "consumption") { temp.ptr = make_pop_budget_details_consumption(state); } + if(cname == "consumption_category") { + temp.ptr = make_pop_budget_details_consumption_category(state); + } lvl.contents.emplace_back(std::move(temp)); } break; case layout_item_types::glue: @@ -1565,7 +1550,7 @@ void pop_budget_details_wage_income_t::create_layout_level(sys::state& state, la buffer.read(temp.amount); lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::generator: + case layout_item_types::generator2: { generator_instance temp; std::string_view cname = buffer.read(); @@ -1750,13 +1735,15 @@ void pop_budget_details_production_income_t::create_layout_level(sys::state& sta buffer.read(temp.texture); lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::control: + case layout_item_types::control2: { layout_control temp; std::string_view cname = buffer.read(); buffer.read(temp.abs_x); buffer.read(temp.abs_y); buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); temp.ptr = nullptr; if(cname == "commodity") { temp.ptr = commodity.get(); @@ -1773,13 +1760,15 @@ void pop_budget_details_production_income_t::create_layout_level(sys::state& sta } lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::window: + case layout_item_types::window2: { layout_window temp; std::string_view cname = buffer.read(); buffer.read(temp.abs_x); buffer.read(temp.abs_y); buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); if(cname == "main") { temp.ptr = make_pop_budget_details_main(state); } @@ -1792,6 +1781,9 @@ void pop_budget_details_production_income_t::create_layout_level(sys::state& sta if(cname == "consumption") { temp.ptr = make_pop_budget_details_consumption(state); } + if(cname == "consumption_category") { + temp.ptr = make_pop_budget_details_consumption_category(state); + } lvl.contents.emplace_back(std::move(temp)); } break; case layout_item_types::glue: @@ -1801,7 +1793,7 @@ void pop_budget_details_production_income_t::create_layout_level(sys::state& sta buffer.read(temp.amount); lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::generator: + case layout_item_types::generator2: { generator_instance temp; std::string_view cname = buffer.read(); @@ -1915,9 +1907,15 @@ 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 = ""; + } + auto commodity_name = text::produce_simple_string(state, state.world.commodity_get_name(consumption.cid)); + if(modifier.size() > 0) { + set_text(state, "(" + modifier + ")" + commodity_name); + } else { + set_text(state, commodity_name); } - auto commodity_name = text::produce_simple_string(state, state.world.commodity_get_name(consumption.cid)); - set_text(state, "(" + modifier + ")" + commodity_name); // END } void pop_budget_details_consumption_satisfaction_t::on_update(sys::state& state) noexcept { @@ -1986,13 +1984,15 @@ void pop_budget_details_consumption_t::create_layout_level(sys::state& state, la buffer.read(temp.texture); lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::control: + case layout_item_types::control2: { layout_control temp; std::string_view cname = buffer.read(); buffer.read(temp.abs_x); buffer.read(temp.abs_y); buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); temp.ptr = nullptr; if(cname == "name") { temp.ptr = name.get(); @@ -2012,13 +2012,15 @@ void pop_budget_details_consumption_t::create_layout_level(sys::state& state, la } lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::window: + case layout_item_types::window2: { layout_window temp; std::string_view cname = buffer.read(); buffer.read(temp.abs_x); buffer.read(temp.abs_y); buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); if(cname == "main") { temp.ptr = make_pop_budget_details_main(state); } @@ -2031,6 +2033,9 @@ void pop_budget_details_consumption_t::create_layout_level(sys::state& state, la if(cname == "consumption") { temp.ptr = make_pop_budget_details_consumption(state); } + if(cname == "consumption_category") { + temp.ptr = make_pop_budget_details_consumption_category(state); + } lvl.contents.emplace_back(std::move(temp)); } break; case layout_item_types::glue: @@ -2040,7 +2045,7 @@ void pop_budget_details_consumption_t::create_layout_level(sys::state& state, la buffer.read(temp.amount); lvl.contents.emplace_back(std::move(temp)); } break; - case layout_item_types::generator: + case layout_item_types::generator2: { generator_instance temp; std::string_view cname = buffer.read(); @@ -2161,9 +2166,225 @@ std::unique_ptr make_pop_budget_details_consumption(sys::state ptr->on_create(state); return ptr; } - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif +void pop_budget_details_consumption_category_name_t::on_update(sys::state& state) noexcept { + pop_budget_details_consumption_category_t& consumption_category = *((pop_budget_details_consumption_category_t*)(parent)); + pop_budget_details_main_t& main = *((pop_budget_details_main_t*)(parent->parent)); +// BEGIN consumption_category::name::update + auto name = state.world.consumption_category_get_name(consumption_category.cat); + if(name) { + set_text(state, text::produce_simple_string(state, name)); + } else { + set_text(state, std::to_string(name.value)); + } +// END +} +ui::message_result pop_budget_details_consumption_category_t::on_lbutton_down(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept { + return ui::message_result::consumed; +} +ui::message_result pop_budget_details_consumption_category_t::on_rbutton_down(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept { + return ui::message_result::consumed; +} +void pop_budget_details_consumption_category_t::on_update(sys::state& state) noexcept { + pop_budget_details_main_t& main = *((pop_budget_details_main_t*)(parent->parent)); +// BEGIN consumption_category::update +// END + remake_layout(state, true); +} +void pop_budget_details_consumption_category_t::create_layout_level(sys::state& state, layout_level& lvl, char const* ldata, size_t sz) { + serialization::in_buffer buffer(ldata, sz); + buffer.read(lvl.size_x); + buffer.read(lvl.size_y); + buffer.read(lvl.margin_top); + buffer.read(lvl.margin_bottom); + buffer.read(lvl.margin_left); + buffer.read(lvl.margin_right); + buffer.read(lvl.line_alignment); + buffer.read(lvl.line_internal_alignment); + buffer.read(lvl.type); + buffer.read(lvl.page_animation); + buffer.read(lvl.interline_spacing); + buffer.read(lvl.paged); + if(lvl.paged) { + lvl.page_controls = std::make_unique(); + lvl.page_controls->for_layout = &lvl; + lvl.page_controls->parent = this; + lvl.page_controls->base_data.size.x = int16_t(grid_size * 10); + lvl.page_controls->base_data.size.y = int16_t(grid_size * 2); + } + auto expansion_section = buffer.read_section(); + if(expansion_section) + expansion_section.read(lvl.template_id); + if(lvl.template_id == -1 && window_template != -1) + lvl.template_id = int16_t(state.ui_templates.window_t[window_template].layout_region_definition); + while(buffer) { + layout_item_types t; + buffer.read(t); + switch(t) { + case layout_item_types::texture_layer: + { + texture_layer temp; + buffer.read(temp.texture_type); + buffer.read(temp.texture); + lvl.contents.emplace_back(std::move(temp)); + } break; + case layout_item_types::control2: + { + layout_control temp; + std::string_view cname = buffer.read(); + buffer.read(temp.abs_x); + buffer.read(temp.abs_y); + buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); + temp.ptr = nullptr; + if(cname == "fancy_block") { + temp.ptr = fancy_block.get(); + } else + if(cname == "name") { + temp.ptr = name.get(); + } else + { + std::string str_cname {cname}; + auto found = scripted_elements.find(str_cname); + if (found != scripted_elements.end()) { + temp.ptr = found->second.get(); + } + } + lvl.contents.emplace_back(std::move(temp)); + } break; + case layout_item_types::window2: + { + layout_window temp; + std::string_view cname = buffer.read(); + buffer.read(temp.abs_x); + buffer.read(temp.abs_y); + buffer.read(temp.absolute_position); + buffer.read(temp.fill_x); + buffer.read(temp.fill_y); + if(cname == "main") { + temp.ptr = make_pop_budget_details_main(state); + } + if(cname == "wage_income") { + temp.ptr = make_pop_budget_details_wage_income(state); + } + if(cname == "production_income") { + temp.ptr = make_pop_budget_details_production_income(state); + } + if(cname == "consumption") { + temp.ptr = make_pop_budget_details_consumption(state); + } + if(cname == "consumption_category") { + temp.ptr = make_pop_budget_details_consumption_category(state); + } + lvl.contents.emplace_back(std::move(temp)); + } break; + case layout_item_types::glue: + { + layout_glue temp; + buffer.read(temp.type); + buffer.read(temp.amount); + lvl.contents.emplace_back(std::move(temp)); + } break; + case layout_item_types::generator2: + { + generator_instance temp; + std::string_view cname = buffer.read(); + auto gen_details = buffer.read_section(); // ignored + lvl.contents.emplace_back(std::move(temp)); + } break; + case layout_item_types::layout: + { + sub_layout temp; + temp.layout = std::make_unique(); + auto layout_section = buffer.read_section(); + create_layout_level(state, *temp.layout, layout_section.view_data() + layout_section.view_read_position(), layout_section.view_size() - layout_section.view_read_position()); + lvl.contents.emplace_back(std::move(temp)); + } break; + } + } +} +void pop_budget_details_consumption_category_t::on_create(sys::state& state) noexcept { + auto window_bytes = state.ui_state.new_ui_windows.find(std::string("pop_budget_details::consumption_category")); + if(window_bytes == state.ui_state.new_ui_windows.end()) std::abort(); + std::vector pending_children; + auto win_data = read_window_bytes(window_bytes->second.data, window_bytes->second.size, pending_children); + base_data.position.x = win_data.x_pos; + base_data.position.y = win_data.y_pos; + base_data.size.x = win_data.x_size; + base_data.size.y = win_data.y_size; + base_data.flags = uint8_t(win_data.orientation); + layout_window_element::initialize_template(state, win_data.template_id, win_data.grid_size, win_data.auto_close_button); + while(!pending_children.empty()) { + auto child_data = read_child_bytes(pending_children.back().data, pending_children.back().size); + if(child_data.name == "fancy_block") { + fancy_block = std::make_unique(); + fancy_block->parent = this; + auto cptr = fancy_block.get(); + cptr->base_data.position.x = child_data.x_pos; + cptr->base_data.position.y = child_data.y_pos; + cptr->base_data.size.x = child_data.x_size; + cptr->base_data.size.y = child_data.y_size; + cptr->template_id = child_data.template_id; + if(child_data.tooltip_text_key.length() > 0) + cptr->default_tooltip = state.lookup_key(child_data.tooltip_text_key); + cptr->parent = this; + cptr->on_create(state); + children.push_back(cptr); + pending_children.pop_back(); continue; + } else + if(child_data.name == "name") { + name = std::make_unique(); + name->parent = this; + auto cptr = name.get(); + cptr->base_data.position.x = child_data.x_pos; + cptr->base_data.position.y = child_data.y_pos; + cptr->base_data.size.x = child_data.x_size; + cptr->base_data.size.y = child_data.y_size; + cptr->template_id = child_data.template_id; + if(child_data.text_key.length() > 0) + cptr->default_text = state.lookup_key(child_data.text_key); + if(child_data.tooltip_text_key.length() > 0) + cptr->default_tooltip = state.lookup_key(child_data.tooltip_text_key); + cptr->parent = this; + cptr->on_create(state); + children.push_back(cptr); + pending_children.pop_back(); continue; + } else + if (child_data.is_lua) { + std::string str_name {child_data.name}; + scripted_elements[str_name] = std::make_unique(); + auto cptr = scripted_elements[str_name].get(); + cptr->base_data.position.x = child_data.x_pos; + cptr->base_data.position.y = child_data.y_pos; + cptr->base_data.size.x = child_data.x_size; + cptr->base_data.size.y = child_data.y_size; + cptr->texture_key = child_data.texture; + cptr->text_scale = child_data.text_scale; + cptr->text_is_header = (child_data.text_type == aui_text_type::header); + cptr->text_alignment = child_data.text_alignment; + cptr->text_color = child_data.text_color; + cptr->on_update_lname = child_data.text_key; + if(child_data.tooltip_text_key.length() > 0) { + cptr->tooltip_key = state.lookup_key(child_data.tooltip_text_key); + } + cptr->parent = this; + cptr->on_create(state); + children.push_back(cptr); + pending_children.pop_back(); continue; + } + pending_children.pop_back(); + } + page_left_texture_key = win_data.page_left_texture; + page_right_texture_key = win_data.page_right_texture; + page_text_color = win_data.page_text_color; + create_layout_level(state, layout, win_data.layout_data, win_data.layout_data_size); +// BEGIN consumption_category::create +// END +} +std::unique_ptr make_pop_budget_details_consumption_category(sys::state& state) { + auto ptr = std::make_unique(); + ptr->on_create(state); + return ptr; +} // LOST-CODE } 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;