diff --git a/assets/alice.gui b/assets/alice.gui index 21e936748..4d5a92367 100644 --- a/assets/alice.gui +++ b/assets/alice.gui @@ -5671,4 +5671,22 @@ guiTypes = { quadTextureSprite ="GFX_unit_upgrade_button" extends="factory_info" } + + iconType = { + name = "industry_size_icon" + spriteType = "GFX_budget_ind_sup_icon" + position = { x= 42 y = 233 } + Orientation = "UPPER_LEFT" + extends="diplomacy_country_facts" + } + + instantTextBoxType = { + name = "industry_size_text" + position = { 75 236 } + font = "vic_18_black" + text = " " + maxWidth = 40 + maxHeight = 20 + extends="diplomacy_country_facts" + } } diff --git a/assets/localisation/en-US/alice.csv b/assets/localisation/en-US/alice.csv index b6b173744..2aff10a4a 100644 --- a/assets/localisation/en-US/alice.csv +++ b/assets/localisation/en-US/alice.csv @@ -515,6 +515,11 @@ factory_stats_desired_income;Desired raw profit speed;$val$ kph shift_to_hold_open;Hold ?YSHIFT?! to keep this window open, ?YSHIFT-Right-click?! to build this in every state (but not expand) factory_upgrade_shortcuts;?YSHIFT-Left-click?W upgrade ?Gall?!S factories in a state\n?YSHIFT-Right-click?W upgrade ?Gall profitable?W factories in a state\n?YCTRL-Left-click?W upgrade ?Gall?W factories in a nation\n?YCTRL-Right-click?W upgrade ?Gall profitable?W factories in a nation\n?YCTRL-SHIFT-Left-click?W upgrade ?Gall profitable with full employment?W factories in a state\n?YCTRL-SHIFT-Right-click?W upgrade ?Gall profitable with full employment?W factories in a state +factory_consumer_count;Consumer factories: ?Y$val$?! +factory_heavy_count;Heavy industry factories: ?Y$val$?! +factory_military_count;Military factories: ?Y$val$?! +factory_processing_count;Processing factories: ?Y$val$?! +factory_industrial_and_consumer_count;Industrial and consumer goods factories: ?Y$val$?! blank_org_gain;organization gain plurality_change_reason;(based on the average consciousness) revanchism_reason;Based on the number of unowned cores (cores with your primary culture count for more) @@ -750,6 +755,7 @@ state_transfer_explain_3;We're not subjects state_transfer_explain_4;They're not subjects state_transfer_explain_5;We're at peace state_transfer_explain_6;We have more than 1 state +state_transfer_explain_7;Must be in a subject-overlord relationship unit_upgrade_desc;Changes units to different type. After swap, unit strength is reduced to 1% unit_upgrade_explain_1;Either land or sea units are selected unit_upgrade_explain_2;Unit type is unlocked @@ -1045,6 +1051,15 @@ et_on_po_accepted;The effects executed from accepting a peace offer with this wa alice_no_possible_units;No possible units can be built, and no units are in construction. alice_debt_spending;Enable/disable debt spending alice_loan_size;Our current maximum loan is $x$. Debt greater than that will cause us to go bankrupt +alice_loan_size_mod;Max loan free money modifier: ?G+$x$ +capitalists_savings_ratio;Capitalists saving ratio: ?G$x$?! +middle_class_savings_ratio;Middle class saving ratio: ?G$x$?! +farmers_savings_ratio;Farmers saving ratio: ?G$x$?! +landowners_savings_ratio;Landowners saving ratio: ?G$x$?! +capitalists_investment_ratio;Capitalists investment ratio: ?G$x$?! +middle_class_investment_ratio;Middle class investment ratio: ?G$x$?! +farmers_investment_ratio;Farmers investment ratio: ?G$x$?! +landowners_investment_ratio;Landowners investment ratio: ?G$x$?! alice_currently_bankrupt;We are bankrupt (until $x$) alice_no_loans_possible;We are currently unable to take loans alice_rf_demands_enforced_trigger;The rebels will only enforce their demands if: @@ -1087,6 +1102,12 @@ CANCEL_WARSUBSIDIES_WE_ACCEPT_LOG;$ACTOR$ stopped giving subsidies to us; CANCEL_WARSUBSIDIES_OTHER_ACCEPT_LOG;$ACTOR$ stopped giving subsidies to $RECIPIENT$; CANCEL_WARSUBSIDIES_DESC;Cancel monetary help to this country; CANCEL_WARSUBSIDIES_BUTTON;Cancel Subsidies; +production_refit_factory_tooltip_1;Refit ?Y$what$?! to a different production line +production_refit_factory_tooltip_2;Refit ?Y$what$?! into ?Y$name$?! +production_refit_factory_tooltip_3;Either can build factories +production_refit_factory_tooltip_4;Or old and new types match in inputs or outputs +factory_refit_cost;$what$: ?Y$val$?! (?Y$value$?!) +factory_refit_cost_total;Total: ?Y$value$?! alice_budget_scaled_16;Diplomatic interest: ?G$value$ alice_budget_scaled_17;Gold production: ?G$value$ alice_budget_scaled_net;Net revenue: ?Y$value$ @@ -1362,6 +1383,15 @@ alice_budget_tariffs_import;Import Duties alice_budget_tariffs_export;Export Duties alice_budget_overseas_maintanance;Colonial alice_budget_social_spending;Benefits +tb_suppression_points;Suppression points: +aristocrat_reinvestment;Aristocrats reinvestment rate +capitalist_reinvestment;Capitalists reinvestment rate +middle_class_reinvestment;Middle class reinvestment rate +farmers_reinvestment;Farmers reinvestment rate +aristocrat_savings;Aristocrats saving rate +capitalist_savings;Capitalists saving rate +middle_class_savings;Middle class saving rate +farmers_savings;Farmers saving rate alice_filter_all;All alice_filter_none;None alice_filter_noncolonial;Non Colonial diff --git a/docs/extensions.md b/docs/extensions.md index 2a68e2223..7f2238702 100644 --- a/docs/extensions.md +++ b/docs/extensions.md @@ -97,6 +97,7 @@ Additionally, triggers such as technology triggers no longer suffer from having } ``` - `diplo_points = ...` : Will add the number to the currently stored diplomatic points of the nation in scope. (Note: so you can use a negative number to subtract points. Diplomatic points can not be reduced to less than zero.) +- `suppression_points = ...` : Will add the number to the currently stored suppression points of the nation in scope. (Note: so you can use a negative number to subtract points. Suppression points can not be reduced to less than zero.) As for `build_xxx_in_capital`, the game doesn't allow custom defined buildings to be used in this mode as an effect. @@ -148,6 +149,7 @@ build_bank_in_capital = { } ``` - `diplo_points = ...` : This trigger condition is true if the nation in scope has saved diplomatic points greater than or equal to the given number. +- `suppression_points = ...` : This trigger condition is true if the nation in scope has saved suppression points greater than or equal to the given number. ### FROM bounce FROM bouncing is a technique where before, modders would do: @@ -510,6 +512,13 @@ Alice adds a handful of new defines: - `alice_privateinvestment_subject_transfer`: Percentage [0;100] of subjects' and overlord's private investment pool transferred daily when no useful projects are done. Overlord distributes money to subjects and subjects contribute to the overlord. Default: 2.0 - `alice_allow_revoke_subject_states`: Allows overlord to take subjects' states raising their militancy and giving separatism. Default: 0.0 + +**Crises and conferences:** +- `alice_crisis_necessary_base_win_ratio = 2.5f`: Strength Ratio at which AI submits to demands after 80 temperature +- `alice_crisis_necessary_base_fast_win_ratio = 3.5f`: Strength Ratio at which AI submits to demands before 80 temperature +- `alice_crisis_per_wg_ratio = 0.1f`: Added necessary ratio per every WG + + ### Support for reforms based on party issues In issues.txt you can add a `vote_modifiers = { ... }` section to any particular issue option within the party issues section. For example, one could go here: diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index 0f6d329ce..2a2fb2907 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -674,6 +674,26 @@ void update_influence_priorities(sys::state& state) { if(t.get_demographics(demographics::total) > state.defines.large_population_limit) continue; + auto natid = state.world.nation_get_identity_from_identity_holder(n.nation); + auto holdscores = false; + for(auto prov_owner : state.world.nation_get_province_ownership(t)) { + auto prov = prov_owner.get_province(); + + for(auto core : prov.get_core_as_province()) { + if(core.get_identity() == natid) { + holdscores = true; + break; + } + } + + if(holdscores) + break; + } + + if(holdscores) { + continue; // holds our cores + } + float weight = 0.0f; for(auto c : state.world.in_commodity) { @@ -2289,24 +2309,29 @@ crisis_str estimate_crisis_str(sys::state& state) { } } - auto necessary_atk_win_ratio = 1.55f; - auto necessary_def_win_ratio = 1.55f; - auto necessary_fast_win_ratio = 1.9f; + auto necessary_atk_win_ratio = state.defines.alice_crisis_necessary_base_win_ratio; + auto necessary_def_win_ratio = state.defines.alice_crisis_necessary_base_win_ratio; + auto necessary_fast_win_ratio = state.defines.alice_crisis_necessary_base_fast_win_ratio; + + if(!state.world.nation_get_is_civilized(state.crisis_defender)) { + necessary_atk_win_ratio += state.defines.alice_crisis_unciv_stubbornness; + necessary_fast_win_ratio += state.defines.alice_crisis_unciv_stubbornness; + } for(auto wg : state.crisis_attacker_wargoals) { if(!wg.cb) { break; } - necessary_atk_win_ratio += 0.10f; - necessary_fast_win_ratio += 0.1f; + necessary_atk_win_ratio += state.defines.alice_crisis_per_wg_ratio; + necessary_fast_win_ratio += state.defines.alice_crisis_per_wg_ratio; } for(auto wg : state.crisis_defender_wargoals) { if(!wg.cb) { break; } - necessary_def_win_ratio += 0.10f; - necessary_fast_win_ratio += 0.1f; + necessary_def_win_ratio += state.defines.alice_crisis_per_wg_ratio; + necessary_fast_win_ratio += state.defines.alice_crisis_per_wg_ratio; } auto def_victory = dtotal > atotal * necessary_def_win_ratio; @@ -2326,6 +2351,10 @@ bool will_join_crisis_with_offer(sys::state& state, dcon::nation_id n, sys::full auto offer_bits = state.world.cb_type_get_type_bits(offer.cb); if((offer_bits & (military::cb_flag::po_demand_state | military::cb_flag::po_annex)) != 0) return true; + + // GPs accept unequal treaties as crisis offer + if(state.world.nation_get_is_great_power(n) && (offer_bits & (military::cb_flag::po_unequal_treaty)) != 0) + return true; return false; } @@ -2423,8 +2452,23 @@ void update_crisis_leaders(sys::state& state) { if(state.crisis_temperature > 75.f || str_est.fast_victory) { // make peace offer auto any_victory = str_est.attacker_win || str_est.defender_win; auto defender_victory = str_est.defender_win; + + auto will_propose_peace = state.world.nation_get_is_player_controlled(state.primary_crisis_attacker) == false && any_victory; + + auto primary_wg = state.crisis_attacker_wargoals[0]; + + // Defender should never agree to full annex + if(state.crisis_defender == state.primary_crisis_defender) { + auto bits = state.world.cb_type_get_type_bits(primary_wg.cb); + if((bits & military::cb_flag::po_annex) != 0) + will_propose_peace = false; + } + + if(state.crisis_temperature <= 20.f) { + will_propose_peace = false; + } - if(state.world.nation_get_is_player_controlled(state.primary_crisis_attacker) == false && any_victory) { + if(will_propose_peace) { assert(command::can_start_crisis_peace_offer(state, state.primary_crisis_attacker, defender_victory)); command::execute_start_crisis_peace_offer(state, state.primary_crisis_attacker, defender_victory); auto pending = state.world.nation_get_peace_offer_from_pending_peace_offer(state.primary_crisis_attacker); @@ -2626,102 +2670,111 @@ void update_crisis_leaders(sys::state& state) { } bool will_accept_crisis_peace_offer(sys::state& state, dcon::nation_id to, bool is_concession, bool missing_wg) { - if(state.crisis_temperature < 50.0f) - return false; - auto str_est = estimate_crisis_str(state); - - if(to == state.primary_crisis_attacker) { - if(str_est.attacker < str_est.defender * 0.66f) - return true; - if(str_est.attacker < str_est.defender * 0.75f) - return is_concession; + auto primary_wg = state.crisis_attacker_wargoals[0]; - if(!is_concession) - return false; + // Defender should never agree to full annex + if(state.crisis_defender == state.primary_crisis_defender && to == state.crisis_defender) { + auto bits = state.world.cb_type_get_type_bits(primary_wg.cb); + if((bits & military::cb_flag::po_annex) != 0) + return false; + } - dcon::nation_id attacker = state.primary_crisis_attacker; + if(state.crisis_temperature <= 20.f) { + return false; + } - if(missing_wg) - return false; + auto str_est = estimate_crisis_str(state); - return true; - } else if(to == state.primary_crisis_defender) { - if(str_est.defender < str_est.attacker * 0.66f) - return true; - if(str_est.defender < str_est.attacker * 0.75f) - return is_concession; + if(state.crisis_temperature > 75.f || str_est.fast_victory) { + if(to == state.primary_crisis_attacker) { + if(is_concession) { + return true; + } + if(str_est.defender_win) { + return true; + } - if(!is_concession) return false; + } else if(to == state.primary_crisis_defender) { + if(is_concession) + return true; - if(missing_wg) - return false; + if(str_est.attacker_win) + return true; - return true; + return false; + } } return false; } bool will_accept_crisis_peace_offer(sys::state& state, dcon::nation_id to, dcon::peace_offer_id peace) { - if(state.crisis_temperature < 50.0f) + auto primary_wg = state.crisis_attacker_wargoals[0]; + + // Defender should never agree to full annex + if(state.crisis_defender == state.primary_crisis_defender && to == state.crisis_defender) { + auto bits = state.world.cb_type_get_type_bits(primary_wg.cb); + if((bits & military::cb_flag::po_annex) != 0) + return false; + } + + if(state.crisis_temperature <= 20.f) { return false; + } auto str_est = estimate_crisis_str(state); - if(to == state.primary_crisis_attacker) { - if(str_est.attacker < str_est.defender * 0.66f) - return true; - if(str_est.attacker < str_est.defender * 0.75f) - return state.world.peace_offer_get_is_concession(peace); + if(state.crisis_temperature > 75.f || str_est.fast_victory) { + if(to == state.primary_crisis_attacker) { + if(str_est.defender_win) + return true; - if(!state.world.peace_offer_get_is_concession(peace)) - return false; + if(!state.world.peace_offer_get_is_concession(peace)) + return false; - bool missing_wg = false; - for(auto swg : state.crisis_attacker_wargoals) { - bool found_wg = false; - for(auto item : state.world.peace_offer_get_peace_offer_item(peace)) { - auto wg = item.get_wargoal(); - if(wg.get_type() == swg.cb && wg.get_associated_state() == swg.state && wg.get_added_by() == swg.added_by && wg.get_target_nation() == swg.target_nation && - wg.get_associated_tag() == swg.wg_tag) { - found_wg = true; continue; + bool missing_wg = false; + for(auto swg : state.crisis_attacker_wargoals) { + bool found_wg = false; + for(auto item : state.world.peace_offer_get_peace_offer_item(peace)) { + auto wg = item.get_wargoal(); + if(wg.get_type() == swg.cb && wg.get_associated_state() == swg.state && wg.get_added_by() == swg.added_by && wg.get_target_nation() == swg.target_nation && + wg.get_associated_tag() == swg.wg_tag) { + found_wg = true; continue; + } } - } - if(!found_wg) { - return false; + if(!found_wg) { + return false; + } } - } - return true; - - } else if(to == state.primary_crisis_defender) { - if(str_est.defender < str_est.attacker * 0.66f) return true; - if(str_est.defender < str_est.attacker * 0.75f) - return state.world.peace_offer_get_is_concession(peace); - if(!state.world.peace_offer_get_is_concession(peace)) - return false; + } else if(to == state.primary_crisis_defender) { + if(str_est.attacker_win) + return true; + if(!state.world.peace_offer_get_is_concession(peace)) + return false; - bool missing_wg = false; - for(auto swg : state.crisis_defender_wargoals) { - bool found_wg = false; - for(auto item : state.world.peace_offer_get_peace_offer_item(peace)) { - auto wg = item.get_wargoal(); - if(wg.get_type() == swg.cb && wg.get_associated_state() == swg.state && wg.get_added_by() == swg.added_by && wg.get_target_nation() == swg.target_nation && - wg.get_associated_tag() == swg.wg_tag) { - found_wg = true; continue; + bool missing_wg = false; + for(auto swg : state.crisis_defender_wargoals) { + bool found_wg = false; + for(auto item : state.world.peace_offer_get_peace_offer_item(peace)) { + auto wg = item.get_wargoal(); + if(wg.get_type() == swg.cb && wg.get_associated_state() == swg.state && wg.get_added_by() == swg.added_by && wg.get_target_nation() == swg.target_nation && + wg.get_associated_tag() == swg.wg_tag) { + found_wg = true; continue; + } } - } - if(!found_wg) { - return false; + if(!found_wg) { + return false; + } } - } - return true; + return true; + } } return false; } diff --git a/src/economy/economy.cpp b/src/economy/economy.cpp index 3858dde6a..a8f2ef062 100644 --- a/src/economy/economy.cpp +++ b/src/economy/economy.cpp @@ -1056,96 +1056,101 @@ void initialize(sys::state& state) { } } - province::for_each_land_province(state, [&](dcon::province_id p) { - auto fp = fatten(state.world, p); - //max size of exploitable land: - auto max_rgo_size = std::ceil(4000.f / state.defines.alice_rgo_per_size_employment - * state.map_state.map_data.province_area[province::to_map_id(p)]); - // currently exploited land - float pop_amount = 0.0f; - for(auto pt : state.world.in_pop_type) { - if(pt == state.culture_definitions.slaves) { - pop_amount += state.world.province_get_demographics(p, demographics::to_key(state, state.culture_definitions.slaves)); - } else if(pt.get_is_paid_rgo_worker()) { - pop_amount += state.world.province_get_demographics(p, demographics::to_key(state, pt)); + if(state.defines.alice_rgo_generate_distribution) { + province::for_each_land_province(state, [&](dcon::province_id p) { + auto fp = fatten(state.world, p); + //max size of exploitable land: + auto max_rgo_size = std::ceil(4000.f / state.defines.alice_rgo_per_size_employment + * state.map_state.map_data.province_area[province::to_map_id(p)]); + // currently exploited land + float pop_amount = 0.0f; + for(auto pt : state.world.in_pop_type) { + if(pt == state.culture_definitions.slaves) { + pop_amount += state.world.province_get_demographics(p, demographics::to_key(state, state.culture_definitions.slaves)); + } else if(pt.get_is_paid_rgo_worker()) { + pop_amount += state.world.province_get_demographics(p, demographics::to_key(state, pt)); + } } - } - auto size_at_the_start_of_the_game = std::ceil(pop_amount / state.defines.alice_rgo_per_size_employment); - auto real_size = std::min(size_at_the_start_of_the_game * 1.5f, max_rgo_size); - assert(std::isfinite(real_size)); - fp.set_rgo_size(real_size); - - if(state.world.province_get_rgo_was_set_during_scenario_creation(p)) { - return; - } + auto size_at_the_start_of_the_game = std::ceil(pop_amount / state.defines.alice_rgo_per_size_employment); + auto real_size = std::min(size_at_the_start_of_the_game * 1.5f, max_rgo_size); + assert(std::isfinite(real_size)); + fp.set_rgo_size(real_size); - dcon::modifier_id climate = fp.get_climate(); - dcon::modifier_id terrain = fp.get_terrain(); - dcon::modifier_id continent = fp.get_continent(); + if(state.world.province_get_rgo_was_set_during_scenario_creation(p)) { + return; + } - dcon::commodity_id main_trade_good = state.world.province_get_rgo(p); - bool is_mine = state.world.commodity_get_is_mine(main_trade_good); + dcon::modifier_id climate = fp.get_climate(); + dcon::modifier_id terrain = fp.get_terrain(); + dcon::modifier_id continent = fp.get_continent(); - state.world.for_each_commodity([&](dcon::commodity_id c) { - fp.set_rgo_target_employment_per_good(c, 0.f); - }); + dcon::commodity_id main_trade_good = state.world.province_get_rgo(p); + bool is_mine = state.world.commodity_get_is_mine(main_trade_good); - static std::vector true_distribution; - true_distribution.resize(state.world.commodity_size()); + state.world.for_each_commodity([&](dcon::commodity_id c) { + fp.set_rgo_target_employment_per_good(c, 0.f); + }); - float total = 0.f; - state.world.for_each_commodity([&](dcon::commodity_id c) { - float climate_d = per_climate_distribution_buffer[climate.value][c.value]; - float terrain_d = per_terrain_distribution_buffer[terrain.value][c.value]; - float continent_d = per_continent_distribution_buffer[continent.value][c.value]; - float current = (climate_d + terrain_d) * (climate_d + terrain_d) * continent_d; - true_distribution[c.index()] = current; - total += current; - }); + static std::vector true_distribution; + true_distribution.resize(state.world.commodity_size()); - // remove continental restriction if failed: - if(total == 0.f) { + float total = 0.f; state.world.for_each_commodity([&](dcon::commodity_id c) { float climate_d = per_climate_distribution_buffer[climate.value][c.value]; float terrain_d = per_terrain_distribution_buffer[terrain.value][c.value]; - float current = (climate_d + terrain_d) * (climate_d + terrain_d); + float continent_d = per_continent_distribution_buffer[continent.value][c.value]; + float current = (climate_d + terrain_d) * (climate_d + terrain_d) * continent_d; true_distribution[c.index()] = current; total += current; }); - } - // make it into uniform distrubution on available goods then... - if(total == 0.f) { + // remove continental restriction if failed: + if(total == 0.f) { + state.world.for_each_commodity([&](dcon::commodity_id c) { + float climate_d = per_climate_distribution_buffer[climate.value][c.value]; + float terrain_d = per_terrain_distribution_buffer[terrain.value][c.value]; + float current = (climate_d + terrain_d) * (climate_d + terrain_d); + true_distribution[c.index()] = current; + total += current; + }); + } + + // make it into uniform distrubution on available goods then... + if(total == 0.f) { + state.world.for_each_commodity([&](dcon::commodity_id c) { + if(state.world.commodity_get_money_rgo(c)) { + return; + } + if(!state.world.commodity_get_is_available_from_start(c)) { + return; + } + float current = 1.f; + true_distribution[c.index()] = current; + total += current; + }); + } + state.world.for_each_commodity([&](dcon::commodity_id c) { - if(state.world.commodity_get_money_rgo(c)) { - return; - } - if(!state.world.commodity_get_is_available_from_start(c)) { - return; + assert(std::isfinite(total)); + // if everything had failed for some reason, then assume 0 distribution: main rgo is still active + if(total == 0.f) { + true_distribution[c.index()] = 0.f; + } else { + true_distribution[c.index()] /= total; } - float current = 1.f; - true_distribution[c.index()] = current; - total += current; }); - } - state.world.for_each_commodity([&](dcon::commodity_id c) { - assert(std::isfinite(total)); - // if everything had failed for some reason, then assume 0 distribution: main rgo is still active - if(total == 0.f) { - true_distribution[c.index()] = 0.f; - } else { - true_distribution[c.index()] /= total; - } - }); + // distribution of rgo land per good + state.world.for_each_commodity([&](dcon::commodity_id c) { + auto fc = fatten(state.world, c); + assert(std::isfinite(true_distribution[c.index()])); - // distribution of rgo land per good - state.world.for_each_commodity([&](dcon::commodity_id c) { - auto fc = fatten(state.world, c); - assert(std::isfinite(true_distribution[c.index()])); - state.world.province_get_rgo_max_size_per_good(fp, c) += real_size * true_distribution[c.index()]; + if(real_size * true_distribution[c.index()] > state.defines.alice_secondary_rgos_min_employment) { + state.world.province_get_rgo_max_size_per_good(fp, c) += real_size * true_distribution[c.index()]; + } + }); }); - }); + } state.world.for_each_nation([&](dcon::nation_id n) { auto fn = fatten(state.world, n); @@ -3853,13 +3858,26 @@ void update_pop_consumption( ve::mask_vector nation_allows_investment = is_civilised && allows_investment_mask; auto capitalists_mask = pop_type == state.culture_definitions.capitalists; - auto artisans_mask = pop_type == state.culture_definitions.artisans; + auto middle_class_investors_mask = pop_type == state.culture_definitions.artisans || pop_type == state.culture_definitions.secondary_factory_worker; + auto farmers_mask = pop_type == state.culture_definitions.farmers; auto landowners_mask = pop_type == state.culture_definitions.aristocrat; + auto capitalists_mod = state.world.nation_get_modifier_values(nations, sys::national_mod_offsets::capitalist_reinvestment); + auto middle_class_investors_mod = state.world.nation_get_modifier_values(nations, sys::national_mod_offsets::middle_class_reinvestment); + auto farmers_mod = state.world.nation_get_modifier_values(nations, sys::national_mod_offsets::farmers_reinvestment); + auto landowners_mod = state.world.nation_get_modifier_values(nations, sys::national_mod_offsets::aristocrat_reinvestment); + auto investment_ratio = - ve::select(capitalists_mask, state.defines.alice_invest_capitalist, zero) - + ve::select(landowners_mask, state.defines.alice_invest_aristocrat, zero) - + ve::select(artisans_mask, state.defines.alice_invest_aristocrat, zero); + ve::select(nation_allows_investment && capitalists_mask, capitalists_mod + state.defines.alice_invest_capitalist, zero) + + ve::select(nation_allows_investment && landowners_mask,landowners_mod + state.defines.alice_invest_aristocrat, zero) + + ve::select(nation_allows_investment && middle_class_investors_mask, middle_class_investors_mod + state.defines.alice_invest_middle_class, zero) + +ve::select(nation_allows_investment && farmers_mask, farmers_mod + state.defines.alice_invest_farmer, zero); + + investment_ratio = ve::max(investment_ratio, zero); + + ve::apply([&](float r) { + assert(r >= 0.f); + }, investment_ratio); auto investment = savings * investment_ratio; @@ -3877,38 +3895,34 @@ void update_pop_consumption( savings = savings - spend_on_everyday_needs; total_spendings = total_spendings + spend_on_everyday_needs; - //handle savings before luxury goods spending - /* - ve::fp_vector bank_to_pop_money_transfer { 0.f }; - auto enough_savings = savings > required_spendings_for_luxury_needs; - auto savings_for_transfer = required_spendings_for_luxury_needs - savings; - - auto enough_in_bank = state.world.nation_get_national_bank(nations) > ve::max(required_spendings_for_luxury_needs + 10000.f, savings_for_transfer); - bank_to_pop_money_transfer = ve::select( - enough_savings && nation_allows_investment && capitalists_mask, - bank_to_pop_money_transfer - savings_for_transfer * state.defines.alice_save_aristocrat, - bank_to_pop_money_transfer - ); - bank_to_pop_money_transfer = ve::select( - enough_savings && nation_allows_investment && landowners_mask, - bank_to_pop_money_transfer - savings_for_transfer * state.defines.alice_save_capitalist, - bank_to_pop_money_transfer - ); - bank_to_pop_money_transfer = ve::select( - !enough_savings && nation_allows_investment && enough_in_bank, - bank_to_pop_money_transfer + savings_for_transfer, - bank_to_pop_money_transfer - ); + //handle bank savings before luxury goods spending + // Note that farmers and middle_class don't do bank savings by default - that doens't mean they don't have savings. They don't use banks for savings without modifier (from tech, from example). + auto capitalists_savings_mod = state.world.nation_get_modifier_values(nations, sys::national_mod_offsets::capitalist_savings); + auto middle_class_savings_mod = state.world.nation_get_modifier_values(nations, sys::national_mod_offsets::middle_class_savings); + auto farmers_savings_mod = state.world.nation_get_modifier_values(nations, sys::national_mod_offsets::farmers_savings); + auto landowners_savings_mod = state.world.nation_get_modifier_values(nations, sys::national_mod_offsets::aristocrat_savings); + + auto saving_ratio = + ve::select(capitalists_mask, capitalists_savings_mod + state.defines.alice_save_capitalist, zero) + + ve::select(landowners_mask, landowners_savings_mod + state.defines.alice_save_aristocrat, zero) + + ve::select(middle_class_investors_mask, middle_class_savings_mod + state.defines.alice_save_middle_class, zero) + + ve::select(farmers_mask, farmers_savings_mod + state.defines.alice_save_farmer, zero); + + auto bank_deposits = savings * saving_ratio; + bank_deposits = ve::max(bank_deposits, zero); + + ve::apply([&](float r) { + assert(r >= 0.f); + }, bank_deposits); ve::apply( [&](float transfer, dcon::nation_id n) { - state.world.nation_get_national_bank(n) -= transfer; + state.world.nation_get_national_bank(n) += transfer; return 0; - }, bank_to_pop_money_transfer, nations + }, bank_deposits, nations ); - savings = savings + bank_to_pop_money_transfer; - */ + savings = savings - bank_deposits; // buy luxury needs @@ -3987,6 +4001,13 @@ void update_pop_consumption( float investment, auto pop_type ) { + assert(scale_life >= 0.0f); + assert(scale_everyday >= 0.0f); + assert(scale_luxury >= 0.0f); + assert(!isinf(scale_life)); + assert(!isinf(scale_everyday)); + assert(!isinf(scale_luxury)); + state.world.market_get_life_needs_scale(m, pop_type) += scale_life; state.world.market_get_everyday_needs_scale(m, pop_type) += scale_everyday; state.world.market_get_luxury_needs_scale(m, pop_type) += scale_luxury; diff --git a/src/gamestate/commands.cpp b/src/gamestate/commands.cpp index 80a77e86a..24d58911f 100644 --- a/src/gamestate/commands.cpp +++ b/src/gamestate/commands.cpp @@ -561,27 +561,6 @@ bool can_begin_factory_building_construction(sys::state& state, dcon::nation_id // We deliberately allow for duplicates to existing factories as this scenario is handled when construction is finished - // For refit factories must match in output good or inputs. - /* - auto output_1 = state.world.factory_type_get_output(type); - auto output_2 = state.world.factory_type_get_output(refit_target); - auto inputs_1 = state.world.factory_type_get_inputs(type); - auto inputs_2 = state.world.factory_type_get_inputs(refit_target); - auto inputs_match = true; - - for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) { - auto input_1 = inputs_1.commodity_type[i]; - auto input_2 = inputs_2.commodity_type[i]; - - if(input_1 != input_2) { - inputs_match = false; - break; - } - } - if(output_1 != output_2 && !inputs_match) { - return false; - } - */ } if(state.world.nation_get_is_civilized(source) == false) @@ -618,6 +597,30 @@ bool can_begin_factory_building_construction(sys::state& state, dcon::nation_id if(is_upgrade) { if((rules & issue_rule::expand_factory) == 0) return false; + } else if (refit_target) { + if((rules & issue_rule::build_factory) != 0) { + } + else { + // For capitalist economies, refit factories must match in output good or inputs. + auto output_1 = state.world.factory_type_get_output(type); + auto output_2 = state.world.factory_type_get_output(refit_target); + auto inputs_1 = state.world.factory_type_get_inputs(type); + auto inputs_2 = state.world.factory_type_get_inputs(refit_target); + auto inputs_match = true; + + for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) { + auto input_1 = inputs_1.commodity_type[i]; + auto input_2 = inputs_2.commodity_type[i]; + + if(input_1 != input_2) { + inputs_match = false; + break; + } + } + if(output_1 != output_2 && !inputs_match) { + return false; + } + } } else { if((rules & issue_rule::build_factory) == 0) return false; @@ -2751,6 +2754,13 @@ bool can_state_transfer(sys::state& state, dcon::nation_id asker, dcon::nation_i //auto ol2 = state.world.nation_get_overlord_as_subject(target); //if(state.world.overlord_get_ruler(ol2)) // return false; + + // Asker and target must be in a subject relation + if(state.defines.alice_state_transfer_limits) { + auto ol2 = state.world.nation_get_overlord_as_subject(target); + if(state.world.overlord_get_ruler(ol) != target && state.world.overlord_get_ruler(ol2) != asker) + return false; + } if(state.world.nation_get_is_at_war(asker) || state.world.nation_get_is_at_war(target)) return false; //Redundant, if we're at war already, we will return false: @@ -3004,7 +3014,7 @@ bool can_declare_war(sys::state& state, dcon::nation_id source, dcon::nation_id return false; auto source_ol_rel = state.world.nation_get_overlord_as_subject(source); - if(state.world.overlord_get_ruler(source_ol_rel) && state.world.overlord_get_ruler(source_ol_rel) != real_target) + if(state.world.overlord_get_ruler(source_ol_rel) && state.world.overlord_get_ruler(source_ol_rel) != real_target && state.defines.alice_allow_subjects_declare_wars == 0.0) return false; if(state.world.nation_get_in_sphere_of(real_target) == source) diff --git a/src/gamestate/modifiers.hpp b/src/gamestate/modifiers.hpp index 009ad51a9..8ba3fc98a 100644 --- a/src/gamestate/modifiers.hpp +++ b/src/gamestate/modifiers.hpp @@ -196,8 +196,16 @@ namespace sys { MOD_LIST_ELEMENT(123, diplomacy_tech_research_bonus, true, modifier_display_type::percent, "diplomacy_theory_tech_research_bonus") \ MOD_LIST_ELEMENT(124, flavor_tech_research_bonus, true, modifier_display_type::percent, "flavor_theory_tech_research_bonus") \ MOD_LIST_ELEMENT(125, seperatism, false, modifier_display_type::fp_two_places, "seperatism") \ + MOD_LIST_ELEMENT(126, aristocrat_reinvestment, true, modifier_display_type::percent, "aristocrat_reinvestment") \ + MOD_LIST_ELEMENT(127, capitalist_reinvestment, true, modifier_display_type::percent, "capitalist_reinvestment") \ + MOD_LIST_ELEMENT(128, middle_class_reinvestment, true, modifier_display_type::percent, "middle_class_reinvestment") \ + MOD_LIST_ELEMENT(129, farmers_reinvestment, true, modifier_display_type::percent, "farmers_reinvestment") \ + MOD_LIST_ELEMENT(130, aristocrat_savings, true, modifier_display_type::percent, "aristocrat_savings") \ + MOD_LIST_ELEMENT(131, capitalist_savings, true, modifier_display_type::percent, "capitalist_savings") \ + MOD_LIST_ELEMENT(132, middle_class_savings, true, modifier_display_type::percent, "middle_class_savings") \ + MOD_LIST_ELEMENT(133, farmers_savings, true, modifier_display_type::percent, "farmers_savings") \ -#define MOD_NAT_LIST_COUNT 126 +#define MOD_NAT_LIST_COUNT 134 namespace provincial_mod_offsets { #define MOD_LIST_ELEMENT(num, name, green_is_negative, display_type, locale_name) \ diff --git a/src/gui/budgetwindow.cpp b/src/gui/budgetwindow.cpp index 1e55812ab..abea61887 100644 --- a/src/gui/budgetwindow.cpp +++ b/src/gui/budgetwindow.cpp @@ -4245,6 +4245,29 @@ void budgetwindow_main_debt_enable_t::update_tooltip(sys::state& state, int32_t text::add_line(state, contents, "alice_debt_spending"); text::add_line_break_to_layout(state, contents); text::add_line(state, contents, "alice_loan_size", text::variable_type::x, text::fp_currency{ economy::max_loan(state, state.local_player_nation) }); + + auto mod = state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::max_loan_modifier); + text::add_line(state, contents, "alice_loan_size_mod", text::variable_type::x, text::fp_percentage_one_place{ mod }); + } + + text::add_line_break_to_layout(state, contents); + + auto capitalists_savings_ratio = state.defines.alice_save_capitalist + state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::capitalist_savings); + auto middle_class_savings_ratio = state.defines.alice_save_middle_class + state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::middle_class_savings); + auto farmers_savings_ratio = state.defines.alice_save_farmer + state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::farmers_savings); + auto landowners_savings_ratio = state.defines.alice_save_aristocrat + state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::aristocrat_savings); + + if(capitalists_savings_ratio > 0.f) { + text::add_line(state, contents, "capitalists_savings_ratio", text::variable_type::x, text::fp_percentage{ capitalists_savings_ratio }); + } + if(landowners_savings_ratio > 0.f) { + text::add_line(state, contents, "landowners_savings_ratio", text::variable_type::x, text::fp_percentage{ landowners_savings_ratio }); + } + if(middle_class_savings_ratio > 0.f) { + text::add_line(state, contents, "middle_class_savings_ratio", text::variable_type::x, text::fp_percentage{ middle_class_savings_ratio }); + } + if(farmers_savings_ratio > 0.f) { + text::add_line(state, contents, "farmers_savings_ratio", text::variable_type::x, text::fp_percentage{ farmers_savings_ratio }); } // END } diff --git a/src/gui/gui_effect_tooltips.cpp b/src/gui/gui_effect_tooltips.cpp index a1f66d484..8820ff729 100644 --- a/src/gui/gui_effect_tooltips.cpp +++ b/src/gui/gui_effect_tooltips.cpp @@ -2115,6 +2115,19 @@ uint32_t ef_diplo_points(EFFECT_DISPLAY_PARAMS) { } return 0; } +uint32_t ef_suppression_points(EFFECT_DISPLAY_PARAMS) { + auto amount = trigger::read_float_from_payload(tval + 1); + + { + auto box = text::open_layout_box(layout, indentation); + text::substitution_map m; + text::localised_format_box(ws, layout, box, "tb_suppression_points", m); + text::add_space_to_layout_box(ws, layout, box); + display_value(text::fp_one_place{ amount }, true, ws, layout, box); + text::close_layout_box(layout, box); + } + return 0; +} uint32_t ef_prestige(EFFECT_DISPLAY_PARAMS) { auto delta = trigger::read_float_from_payload(tval + 1); float change = delta; @@ -7294,7 +7307,8 @@ ef_reduce_pop_abs, //0x01BC ef_set_culture_pop, // 0x01BD ef_change_party_name, //EFFECT_BYTECODE_ELEMENT(0x01BE, change_party_name, 3) ef_change_party_position, //EFFECT_BYTECODE_ELEMENT(0x01BF, change_party_position, 2) -ef_diplo_points, //EFFECT_BYTECODE_ELEMENT(0x01C0, diplo_points, 2) +ef_diplo_points, //EFFECT_BYTECODE_ELEMENT(0x01C0, diplo_points, 2) +ef_suppression_points, // EFFECT_BYTECODE_ELEMENT(0x01C1, suppression_points, 2) // // SCOPES // diff --git a/src/gui/gui_topbar.hpp b/src/gui/gui_topbar.hpp index 796216411..e24323e26 100644 --- a/src/gui/gui_topbar.hpp +++ b/src/gui/gui_topbar.hpp @@ -408,10 +408,30 @@ class topbar_treasury_text : public multiline_text_element_base { { text::substitution_map sub{}; text::add_to_substitution_map(sub, text::variable_type::x, text::fp_currency{ state.ui_state.last_tick_investment_pool_change }); - auto box = text::open_layout_box(contents, 3); + auto box = text::open_layout_box(contents, 0); text::localised_format_box(state, contents, box, "investment_pool_income_1", sub); text::close_layout_box(contents, box); } + { + auto capitalists_investment_ratio = state.defines.alice_invest_capitalist + state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::capitalist_reinvestment); + auto middle_class_investment_ratio = state.defines.alice_invest_middle_class + state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::middle_class_reinvestment); + auto farmers_investment_ratio = state.defines.alice_invest_farmer + state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::farmers_reinvestment); + auto landowners_investment_ratio = state.defines.alice_invest_aristocrat + state.world.nation_get_modifier_values(state.local_player_nation, sys::national_mod_offsets::aristocrat_reinvestment); + + if(capitalists_investment_ratio > 0.f) { + text::add_line(state, contents, "capitalists_investment_ratio", text::variable_type::x, text::fp_percentage{ capitalists_investment_ratio }, 15); + } + if(landowners_investment_ratio > 0.f) { + text::add_line(state, contents, "landowners_investment_ratio", text::variable_type::x, text::fp_percentage{ landowners_investment_ratio }, 15); + } + if(middle_class_investment_ratio > 0.f) { + text::add_line(state, contents, "middle_class_investment_ratio", text::variable_type::x, text::fp_percentage{ middle_class_investment_ratio }, 15); + } + if(farmers_investment_ratio > 0.f) { + text::add_line(state, contents, "farmers_investment_ratio", text::variable_type::x, text::fp_percentage{ farmers_investment_ratio }, 15); + } + } + text::add_line_break_to_layout(state, contents); { for(auto n : state.world.in_nation) { auto rel = state.world.nation_get_overlord_as_subject(n); @@ -429,17 +449,18 @@ class topbar_treasury_text : public multiline_text_element_base { text::substitution_map sub{}; text::add_to_substitution_map(sub, text::variable_type::x, text::fp_currency{ amt }); text::add_to_substitution_map(sub, text::variable_type::country, n); - auto box = text::open_layout_box(contents, 3); + auto box = text::open_layout_box(contents, 15); text::localised_format_box(state, contents, box, "investment_pool_income_2", sub); text::close_layout_box(contents, box); } } } } + text::add_line_break_to_layout(state, contents); { text::substitution_map sub{}; text::add_to_substitution_map(sub, text::variable_type::x, text::fp_currency{ economy::estimate_investment_pool_daily_loss(state, state.local_player_nation) }); - auto box = text::open_layout_box(contents, 3); + auto box = text::open_layout_box(contents, 0); text::localised_format_box(state, contents, box, "investment_pool_spending_1", sub); text::close_layout_box(contents, box); } @@ -448,7 +469,7 @@ class topbar_treasury_text : public multiline_text_element_base { { text::substitution_map sub{}; text::add_to_substitution_map(sub, text::variable_type::x, text::fp_currency{ private_constr }); - auto box = text::open_layout_box(contents, 3); + auto box = text::open_layout_box(contents, 15); text::localised_format_box(state, contents, box, "investment_pool_spending_2", sub); text::close_layout_box(contents, box); } @@ -467,7 +488,7 @@ class topbar_treasury_text : public multiline_text_element_base { if(!overlord) { text::substitution_map sub{}; text::add_to_substitution_map(sub, text::variable_type::x, text::fp_currency{ amt }); - auto box = text::open_layout_box(contents, 3); + auto box = text::open_layout_box(contents, 15); text::localised_format_box(state, contents, box, "investment_pool_spending_3", sub); text::close_layout_box(contents, box); } diff --git a/src/gui/gui_trigger_tooltips.cpp b/src/gui/gui_trigger_tooltips.cpp index 043a2a504..dc5a6da75 100644 --- a/src/gui/gui_trigger_tooltips.cpp +++ b/src/gui/gui_trigger_tooltips.cpp @@ -2156,6 +2156,13 @@ void tf_diplo_points(TRIGGER_DISPLAY_PARAMS) { text::fp_one_place{ trigger::read_float_from_payload(tval + 1) }, ws, layout, box); text::close_layout_box(layout, box); } +void tf_suppression_points(TRIGGER_DISPLAY_PARAMS) { + auto box = text::open_layout_box(layout, indentation); + make_condition(tval, ws, layout, primary_slot, this_slot, from_slot, indentation, show_condition, box); + display_with_comparison(tval[0], text::produce_simple_string(ws, "tb_suppression_points"), + text::fp_one_place{ trigger::read_float_from_payload(tval + 1) }, ws, layout, box); + text::close_layout_box(layout, box); +} void tf_owns(TRIGGER_DISPLAY_PARAMS) { auto p = trigger::payload(tval[1]).prov_id; @@ -8451,6 +8458,7 @@ constexpr inline void (*trigger_functions[])(TRIGGER_DISPLAY_PARAMS) = { tf_party_name, //TRIGGER_BYTECODE_ELEMENT(0x02E2, party_name, 3) tf_party_position, //TRIGGER_BYTECODE_ELEMENT(0x02E3, party_position, 2) tf_diplo_points, //TRIGGER_BYTECODE_ELEMENT(0x02E4, tf_diplo_points, 2) + tf_suppression_points, //TRIGGER_BYTECODE_ELEMENT(0x02E5, tf_suppression_points, 2) // // scopes diff --git a/src/gui/gui_unit_grid_box.hpp b/src/gui/gui_unit_grid_box.hpp index 628a5ec2b..14d8892d1 100644 --- a/src/gui/gui_unit_grid_box.hpp +++ b/src/gui/gui_unit_grid_box.hpp @@ -433,13 +433,16 @@ inline outline_color to_color(sys::state& state, unit_var display_unit) { return outline_color::gray; } + auto subjects = nations::nation_get_subjects(state, state.local_player_nation); + auto is_subject = std::find(subjects.begin(), subjects.end(), controller) != subjects.end(); + if(selected && controller == state.local_player_nation) { return outline_color::gold; } else if(controller == state.local_player_nation) { return outline_color::blue; } else if(!controller || military::are_at_war(state, controller, state.local_player_nation)) { return outline_color::red; - } else if(military::are_allied_in_war(state, controller, state.local_player_nation)) { + } else if(military::are_allied_in_war(state, controller, state.local_player_nation) || is_subject) { return outline_color::cyan; } else { return outline_color::gray; diff --git a/src/gui/gui_unit_panel.hpp b/src/gui/gui_unit_panel.hpp index 4a7e3aaed..7f006fbf3 100644 --- a/src/gui/gui_unit_panel.hpp +++ b/src/gui/gui_unit_panel.hpp @@ -1848,6 +1848,42 @@ class unit_type_listbox_entry_label : public button_element_base { auto new_type = retrieve(state, parent); auto const& ut = state.military_definitions.unit_base_definitions[new_type]; + if(ut.is_land) { + if(state.world.nation_get_unit_stats(state.local_player_nation, new_type).reconnaissance_or_fire_range > 0) { + text::add_line(state, contents, "unit_recon", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).reconnaissance_or_fire_range, 2)); + } + if(state.world.nation_get_unit_stats(state.local_player_nation, new_type).siege_or_torpedo_attack > 0) { + text::add_line(state, contents, "unit_siege", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).siege_or_torpedo_attack, 2)); + } + + text::add_line(state, contents, "unit_attack", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).attack_or_gun_power, 2)); + text::add_line(state, contents, "unit_defence", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).defence_or_hull, 2)); + text::add_line(state, contents, "unit_discipline", text::variable_type::x, text::format_percentage(ut.discipline_or_evasion, 0)); + if(ut.support > 0) { + text::add_line(state, contents, "unit_support", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).support, 0)); + } + text::add_line(state, contents, "unit_maneuver", text::variable_type::x, text::format_float(ut.maneuver, 0)); + text::add_line(state, contents, "unit_max_speed", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).maximum_speed, 2)); + text::add_line(state, contents, "unit_supply_consumption", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).supply_consumption * 100, 0)); + text::add_line(state, contents, "unit_supply_load", text::variable_type::x, ut.supply_consumption_score); + } + else { + text::add_line(state, contents, "unit_max_speed", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).maximum_speed, 2)); + text::add_line(state, contents, "unit_attack", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).attack_or_gun_power, 2)); + if(state.world.nation_get_unit_stats(state.local_player_nation, new_type).siege_or_torpedo_attack > 0) { + text::add_line(state, contents, "unit_torpedo_attack", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).siege_or_torpedo_attack, 2)); + } + text::add_line(state, contents, "unit_hull", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).defence_or_hull, 2)); + text::add_line(state, contents, "unit_fire_range", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).reconnaissance_or_fire_range, 2)); + if(ut.discipline_or_evasion > 0) { + text::add_line(state, contents, "unit_evasion", text::variable_type::x, text::format_percentage(ut.discipline_or_evasion, 0)); + } + text::add_line(state, contents, "unit_supply_consumption", text::variable_type::x, text::format_float(state.world.nation_get_unit_stats(state.local_player_nation, new_type).supply_consumption * 100, 0)); + text::add_line(state, contents, "unit_supply_load", text::variable_type::x, ut.supply_consumption_score); + } + + text::add_line_break_to_layout(state, contents); + text::add_line_with_condition(state, contents, "unit_upgrade_explain_1", !state.selected_regiments[0] || !state.selected_ships[0]); text::add_line_with_condition(state, contents, "unit_upgrade_explain_2", ut.active || state.world.nation_get_active_unit(state.local_player_nation, new_type)); diff --git a/src/gui/topbar_subwindows/diplomacy_subwindows/gui_diplomacy_actions_window.hpp b/src/gui/topbar_subwindows/diplomacy_subwindows/gui_diplomacy_actions_window.hpp index ee9e07111..7d6036990 100644 --- a/src/gui/topbar_subwindows/diplomacy_subwindows/gui_diplomacy_actions_window.hpp +++ b/src/gui/topbar_subwindows/diplomacy_subwindows/gui_diplomacy_actions_window.hpp @@ -1980,6 +1980,15 @@ class diplomacy_action_state_transfer_button : public diplomacy_action_btn_logic text::add_line_with_condition(state, contents, "state_transfer_explain_3", !state.world.overlord_get_ruler(state.world.nation_get_overlord_as_subject(source))); text::add_line_with_condition(state, contents, "state_transfer_explain_5", !(state.world.nation_get_is_at_war(source) || state.world.nation_get_is_at_war(target))); text::add_line_with_condition(state, contents, "state_transfer_explain_6", state.world.nation_get_owned_state_count(source) > 1); + + // Asker and target must be in a subject relation + if(state.defines.alice_state_transfer_limits) { + auto ol = state.world.nation_get_overlord_as_subject(source); + auto ol2 = state.world.nation_get_overlord_as_subject(target); + + text::add_line_with_condition(state, contents, "state_transfer_explain_7", state.world.overlord_get_ruler(ol) == target || state.world.overlord_get_ruler(ol2) == source); + + } } }; diff --git a/src/gui/topbar_subwindows/gui_diplomacy_window.hpp b/src/gui/topbar_subwindows/gui_diplomacy_window.hpp index 44b3dac9c..aee8b551c 100644 --- a/src/gui/topbar_subwindows/gui_diplomacy_window.hpp +++ b/src/gui/topbar_subwindows/gui_diplomacy_window.hpp @@ -386,6 +386,70 @@ class diplomacy_war_exhaustion : public standard_nation_text { } }; +class diplomacy_industry_size : public standard_nation_text { +public: + std::string get_text(sys::state& state, dcon::nation_id nation_id) noexcept override { + auto fat_id = dcon::fatten(state.world, nation_id); + + auto res = 0; + for(auto p : fat_id.get_province_ownership_as_nation()) { + for(auto floc : p.get_province().get_factory_location_as_province()) { + res += floc.get_factory().get_level(); + } + } + return text::format_wholenum(res); + } + + tooltip_behavior has_tooltip(sys::state& state) noexcept override { + return tooltip_behavior::variable_tooltip; + } + + void update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept override { + auto n = retrieve(state, parent); + auto fat_id = dcon::fatten(state.world, n); + + auto consumer = 0; + auto heavy = 0; + auto mils = 0; + auto processing = 0; + auto indandconsumer = 0; + for(auto p : fat_id.get_province_ownership_as_nation()) { + for(auto floc : p.get_province().get_factory_location_as_province()) { + bool is_closed = economy::factory_total_desired_employment_score(state, floc.get_factory()) < economy::factory_closed_threshold; + if(is_closed) { + continue; + } + + auto output = floc.get_factory().get_building_type().get_output().get_commodity_group(); + if(output == (uint8_t)sys::commodity_group::military_goods) { + mils += floc.get_factory().get_level(); + } + else if(output == (uint8_t) sys::commodity_group::consumer_goods) { + consumer += floc.get_factory().get_level(); + } + else if(output == (uint8_t) sys::commodity_group::industrial_goods) { + heavy += floc.get_factory().get_level(); + } + else if(output == (uint8_t)sys::commodity_group::raw_material_goods) { + processing += floc.get_factory().get_level(); + } + else if(output == (uint8_t)sys::commodity_group::industrial_and_consumer_goods) { + indandconsumer += floc.get_factory().get_level(); + } + } + } + + text::add_line(state, contents, "factory_consumer_count", text::variable_type::val, consumer); + text::add_line(state, contents, "factory_heavy_count", text::variable_type::val, heavy); + text::add_line(state, contents, "factory_military_count", text::variable_type::val, mils); + text::add_line(state, contents, "factory_processing_count", text::variable_type::val, processing); + if(indandconsumer > 0) { + text::add_line(state, contents, "factory_industrial_and_consumer_count", text::variable_type::val, mils); + } + } +}; + + class diplomacy_country_select : public button_element_base { public: void on_update(sys::state& state) noexcept override { @@ -1228,7 +1292,7 @@ class diplomacy_country_facts : public window_element_base { std::array gp_elements; std::array non_gp_elements; - std::array war_elements; + std::array war_elements; public: void on_create(sys::state& state) noexcept override { @@ -1420,6 +1484,14 @@ class diplomacy_country_facts : public window_element_base { auto ptr = make_element_by_type(state, id); war_elements[20] = ptr.get(); return ptr; + } else if(name == "industry_size_text") { + auto ptr = make_element_by_type(state, id); + war_elements[21] = ptr.get(); + return ptr; + } else if(name == "industry_size_icon") { + auto ptr = make_element_by_type(state, id); + war_elements[22] = ptr.get(); + return ptr; } else if(name == "selected_military_icon") { return make_element_by_type(state, id); } else { diff --git a/src/gui/topbar_subwindows/production_subwindows/gui_factory_refit_window.hpp b/src/gui/topbar_subwindows/production_subwindows/gui_factory_refit_window.hpp index c42f4db97..e071e010e 100644 --- a/src/gui/topbar_subwindows/production_subwindows/gui_factory_refit_window.hpp +++ b/src/gui/topbar_subwindows/production_subwindows/gui_factory_refit_window.hpp @@ -95,8 +95,6 @@ class factory_refit_button : public button_element_base { bool is_activated = state.world.nation_get_active_building(n, type) == true || state.world.factory_type_get_is_available_from_start(type); text::add_line_with_condition(state, contents, "factory_upgrade_condition_3", is_activated); - auto rules = state.world.nation_get_combined_issue_rules(state.local_player_nation); - text::add_line_with_condition(state, contents, "factory_upgrade_condition_8", (rules & issue_rule::expand_factory) != 0); text::add_line_with_condition(state, contents, "factory_upgrade_condition_9", is_not_upgrading); text::add_line_with_condition(state, contents, "factory_upgrade_condition_10", fat.get_level() < 255); @@ -187,6 +185,27 @@ class factory_refit_type_listbox_entry_label : public button_element_base { text::add_line_with_condition(state, contents, "factory_upgrade_condition_9", is_not_upgrading); text::add_line_with_condition(state, contents, "factory_upgrade_condition_10", fat.get_level() < 255); + + text::add_line_with_condition(state, contents, "production_refit_factory_tooltip_3", (rules & issue_rule::build_factory) != 0, 5); + + // For capitalist economies, refit factories must match in output good or inputs. + auto output_1 = state.world.factory_type_get_output(type); + auto output_2 = state.world.factory_type_get_output(refit_target); + auto inputs_1 = state.world.factory_type_get_inputs(type); + auto inputs_2 = state.world.factory_type_get_inputs(refit_target); + auto inputs_match = true; + + for(uint32_t i = 0; i < economy::commodity_set::set_size; ++i) { + auto input_1 = inputs_1.commodity_type[i]; + auto input_2 = inputs_2.commodity_type[i]; + + if(input_1 != input_2) { + inputs_match = false; + break; + } + } + + text::add_line_with_condition(state, contents, "production_refit_factory_tooltip_4", output_1 == output_2 || inputs_match, 5); } }; diff --git a/src/military/military.cpp b/src/military/military.cpp index 77e2fc36a..8e9b7176b 100644 --- a/src/military/military.cpp +++ b/src/military/military.cpp @@ -4616,7 +4616,6 @@ void army_arrives_in_province(sys::state& state, dcon::army_id a, dcon::province } } - void add_navy_to_battle(sys::state& state, dcon::navy_id n, dcon::naval_battle_id b, war_role r) { assert(state.world.navy_is_valid(n)); bool battle_attacker = (r == war_role::attacker) == state.world.naval_battle_get_war_attacker_is_attacker(b); diff --git a/src/parsing/defines.hpp b/src/parsing/defines.hpp index 72229510e..049138794 100644 --- a/src/parsing/defines.hpp +++ b/src/parsing/defines.hpp @@ -686,8 +686,12 @@ LUA_DEFINES_LIST_ELEMENT(alice_rgo_production_scale_neg_delta, 0.001) \ LUA_DEFINES_LIST_ELEMENT(alice_invest_capitalist, 0.25) \ LUA_DEFINES_LIST_ELEMENT(alice_invest_aristocrat, 0.0125) \ - LUA_DEFINES_LIST_ELEMENT(alice_save_capitalist, 0.025) \ - LUA_DEFINES_LIST_ELEMENT(alice_save_aristocrat, 0.0125) \ + LUA_DEFINES_LIST_ELEMENT(alice_invest_middle_class, 0.0125) \ + LUA_DEFINES_LIST_ELEMENT(alice_invest_farmer, 0.00) \ + LUA_DEFINES_LIST_ELEMENT(alice_save_capitalist, 0.01) \ + LUA_DEFINES_LIST_ELEMENT(alice_save_aristocrat, 0.01) \ + LUA_DEFINES_LIST_ELEMENT(alice_save_middle_class, 0.0) \ + LUA_DEFINES_LIST_ELEMENT(alice_save_farmer, 0.0) \ LUA_DEFINES_LIST_ELEMENT(alice_needs_lf_spend, 0.05) \ LUA_DEFINES_LIST_ELEMENT(alice_needs_ev_spend, 0.45) \ LUA_DEFINES_LIST_ELEMENT(alice_needs_lx_spend, 0.50) \ @@ -704,6 +708,7 @@ LUA_DEFINES_LIST_ELEMENT(alice_overseas_mil, 0.035) \ LUA_DEFINES_LIST_ELEMENT(alice_militancy_decay, 0.015) \ LUA_DEFINES_LIST_ELEMENT(alice_rgo_per_size_employment, 40000.0) \ + LUA_DEFINES_LIST_ELEMENT(alice_rgo_generate_distribution, 1.0) \ LUA_DEFINES_LIST_ELEMENT(alice_eval_ai_mil_everyday, 0.0) \ LUA_DEFINES_LIST_ELEMENT(alice_allow_subjects_declare_wars, 0.0) \ LUA_DEFINES_LIST_ELEMENT(alice_research_points_on_conquer_base, 0.75) \ @@ -718,6 +723,12 @@ LUA_DEFINES_LIST_ELEMENT(alice_free_trade_agreement_years, 5.0) \ LUA_DEFINES_LIST_ELEMENT(alice_event_taken_auto_days, 30.0) \ LUA_DEFINES_LIST_ELEMENT(alice_message_expiration_days, 15.0) \ + LUA_DEFINES_LIST_ELEMENT(alice_crisis_necessary_base_win_ratio, 2.5) \ + LUA_DEFINES_LIST_ELEMENT(alice_crisis_necessary_base_fast_win_ratio, 3.5) \ + LUA_DEFINES_LIST_ELEMENT(alice_crisis_per_wg_ratio, 0.1) \ + LUA_DEFINES_LIST_ELEMENT(alice_crisis_unciv_stubbornness, 1.0) \ + LUA_DEFINES_LIST_ELEMENT(alice_state_transfer_limits, 0.0) \ + LUA_DEFINES_LIST_ELEMENT(alice_secondary_rgos_min_employment, 0.0) \ // scales the needs values so that they are needs per this many pops @@ -734,7 +745,7 @@ struct defines { LUA_DEFINES_LIST #undef LUA_DEFINES_LIST_ELEMENT - void assign_define(sys::state& state, int32_t line, std::string_view key, float v, parsers::error_handler& err); + void assign_define(sys::state& state, int32_t line, std::string_view key, float v, parsers::error_handler& err); void parse_line(sys::state& state, int32_t line, std::string_view data, parsers::error_handler& err); void parse_file(sys::state& state, std::string_view data, parsers::error_handler& err); }; diff --git a/src/parsing/effect_parser_defs.txt b/src/parsing/effect_parser_defs.txt index 5c733aec6..b19fe7f4f 100644 --- a/src/parsing/effect_parser_defs.txt +++ b/src/parsing/effect_parser_defs.txt @@ -192,6 +192,7 @@ effect_body change_controller value text member_fn infrastructure value int member_fn money value float member_fn + suppression_points value float member_fn prestige_factor value float member_fn leadership value int member_fn create_vassal value text member_fn diff --git a/src/parsing/effect_parsing.hpp b/src/parsing/effect_parsing.hpp index 1d4050831..3b17abddf 100644 --- a/src/parsing/effect_parsing.hpp +++ b/src/parsing/effect_parsing.hpp @@ -1714,6 +1714,17 @@ struct effect_body { return; } } + + void suppression_points(association_type t, float value, error_handler& err, int32_t line, effect_building_context& context) { + if(context.main_slot == trigger::slot_contents::nation) { + context.compiled_effect.push_back(uint16_t(effect::suppression_points)); + context.add_float_to_payload(value); + } else { + err.accumulated_errors += + "suppression_points effect used in an incorrect scope type " + slot_contents_to_string(context.main_slot) + " (" + err.file_name + ", line " + std::to_string(line) + ")\n"; + return; + } + } void leadership(association_type t, int32_t value, error_handler& err, int32_t line, effect_building_context& context) { if(context.main_slot == trigger::slot_contents::nation) { context.compiled_effect.push_back(uint16_t(effect::leadership)); diff --git a/src/parsing/parser_defs.txt b/src/parsing/parser_defs.txt index bacc2a19d..8f1cdb542 100644 --- a/src/parsing/parser_defs.txt +++ b/src/parsing/parser_defs.txt @@ -245,6 +245,16 @@ modifier_base diplomacy_tech_research_bonus value float member_fn population_tech_research_bonus value float member_fn flavor_tech_research_bonus value float member_fn + aristocrat_reinvestment value float member_fn + capitalist_reinvestment value float member_fn + middle_class_reinvestment value float member_fn + farmers_reinvestment value float member_fn + aristocrat_savings value float member_fn + capitalist_savings value float member_fn + middle_class_savings value float member_fn + farmers_savings value float member_fn + + int_vector #free value int member_fn diff --git a/src/parsing/parsers_declarations.hpp b/src/parsing/parsers_declarations.hpp index 5202db682..f35fbde2a 100644 --- a/src/parsing/parsers_declarations.hpp +++ b/src/parsing/parsers_declarations.hpp @@ -864,6 +864,14 @@ struct modifier_base { MOD_NAT_FUNCTION(population_tech_research_bonus) MOD_NAT_FUNCTION(flavor_tech_research_bonus) + MOD_NAT_FUNCTION(aristocrat_reinvestment) + MOD_NAT_FUNCTION(capitalist_reinvestment) + MOD_NAT_FUNCTION(middle_class_reinvestment) + MOD_NAT_FUNCTION(farmers_reinvestment) + MOD_NAT_FUNCTION(aristocrat_savings) + MOD_NAT_FUNCTION(capitalist_savings) + MOD_NAT_FUNCTION(middle_class_savings) + MOD_NAT_FUNCTION(farmers_savings) template void finish(T& context) { } diff --git a/src/parsing/trigger_parser_defs.txt b/src/parsing/trigger_parser_defs.txt index 8d9111b76..554fdfe22 100644 --- a/src/parsing/trigger_parser_defs.txt +++ b/src/parsing/trigger_parser_defs.txt @@ -92,6 +92,7 @@ trigger_body rich_tax value float member_fn state_id value int member_fn treasury value float member_fn (money) + suppression_points value float member_fn war_with value text member_fn civilized value bool member_fn continent value text member_fn diff --git a/src/parsing/trigger_parsing.hpp b/src/parsing/trigger_parsing.hpp index 3754930dd..8d87c60bb 100644 --- a/src/parsing/trigger_parsing.hpp +++ b/src/parsing/trigger_parsing.hpp @@ -3982,6 +3982,16 @@ struct trigger_body { } context.add_float_to_payload(value); } + void suppression_points(association_type a, float value, error_handler& err, int32_t line, trigger_building_context& context) { + if(context.main_slot == trigger::slot_contents::nation) { + context.compiled_trigger.push_back(uint16_t(trigger::suppression_points | association_to_trigger_code(a))); + } else { + err.accumulated_errors += "suppression_points trigger used in an incorrect scope type " + slot_contents_to_string(context.main_slot) + + "(" + err.file_name + ", line " + std::to_string(line) + ")\n"; + return; + } + context.add_float_to_payload(value); + } void lost_national(association_type a, float value, error_handler& err, int32_t line, trigger_building_context& context) { if(context.main_slot == trigger::slot_contents::nation) { context.compiled_trigger.push_back(uint16_t(trigger::lost_national | association_to_trigger_code(a))); diff --git a/src/scripting/effects.cpp b/src/scripting/effects.cpp index de6d25d5b..39f512180 100644 --- a/src/scripting/effects.cpp +++ b/src/scripting/effects.cpp @@ -1936,6 +1936,13 @@ uint32_t ef_treasury(EFFECT_PARAMTERS) { t = std::max(0.0f, t + amount); return 0; } +uint32_t ef_suppression_points(EFFECT_PARAMTERS) { + auto amount = trigger::read_float_from_payload(tval + 1); + assert(std::isfinite(amount)); + auto& t = ws.world.nation_get_suppression_points(trigger::to_nation(primary_slot)); + t = std::max(0.0f, t + amount); + return 0; +} uint32_t ef_war_exhaustion(EFFECT_PARAMTERS) { auto& war_x = ws.world.nation_get_war_exhaustion(trigger::to_nation(primary_slot)); auto amount = trigger::read_float_from_payload(tval + 1); diff --git a/src/scripting/fif_triggers.cpp b/src/scripting/fif_triggers.cpp index 0eeae1943..a794c4098 100644 --- a/src/scripting/fif_triggers.cpp +++ b/src/scripting/fif_triggers.cpp @@ -1913,6 +1913,9 @@ TRIGGER_FUNCTION(tf_money) { TRIGGER_FUNCTION(tf_money_province) { return "dup " + province_to_owner() + "0 >commodity_id @ " + std::to_string(read_float_from_payload(tval + 1)) + " " + compare_values(tval[0]); } +TRIGGER_FUNCTION(tf_suppression_points) { + return "dup suppression @ " + std::to_string(read_float_from_payload(tval + 1)) + " " + compare_values(tval[0]); +} TRIGGER_FUNCTION(tf_lost_national) { return "dup revanchism @ 1.0 swap - " + std::to_string(read_float_from_payload(tval + 1)) + " " + compare_values(tval[0]); } diff --git a/src/scripting/script_constants.hpp b/src/scripting/script_constants.hpp index 48f3c5857..2a2ca64b3 100644 --- a/src/scripting/script_constants.hpp +++ b/src/scripting/script_constants.hpp @@ -470,6 +470,8 @@ EFFECT_BYTECODE_ELEMENT(0x01BD, set_culture_pop, 1) \ EFFECT_BYTECODE_ELEMENT(0x01BE, change_party_name, 3) \ EFFECT_BYTECODE_ELEMENT(0x01BF, change_party_position, 2) \ EFFECT_BYTECODE_ELEMENT(0x01C0, diplo_points, 2) \ +EFFECT_BYTECODE_ELEMENT(0x01C1, suppression_points, 2) \ + #define EFFECT_BYTECODE_ELEMENT(code, name, arg) constexpr inline uint16_t name = code; EFFECT_BYTECODE_LIST @@ -477,7 +479,7 @@ EFFECT_BYTECODE_ELEMENT(0x01C0, diplo_points, 2) \ // invalid /* This value must be changed if more effects are added. */ -constexpr inline uint16_t first_scope_code = 0x01C1; +constexpr inline uint16_t first_scope_code = 0x01C2; // scopes constexpr inline uint16_t generic_scope = first_scope_code + 0x0000; // default grouping of effects (or hidden_tooltip) @@ -1379,13 +1381,15 @@ TRIGGER_BYTECODE_ELEMENT(0x02E0, has_national_focus_state, 1) \ TRIGGER_BYTECODE_ELEMENT(0x02E1, has_national_focus_province, 1) \ TRIGGER_BYTECODE_ELEMENT(0x02E2, party_name, 3) \ TRIGGER_BYTECODE_ELEMENT(0x02E3, party_position, 2) \ -TRIGGER_BYTECODE_ELEMENT(0x02E4, diplo_points, 2) +TRIGGER_BYTECODE_ELEMENT(0x02E4, diplo_points, 2) \ +TRIGGER_BYTECODE_ELEMENT(0x02E5, suppression_points, 2) \ + #define TRIGGER_BYTECODE_ELEMENT(code, name, arg) constexpr inline uint16_t name = code; TRIGGER_BYTECODE_LIST #undef TRIGGER_BYTECODE_ELEMENT -constexpr inline uint16_t first_scope_code = 0x02E5; +constexpr inline uint16_t first_scope_code = 0x02E6; // technology name -- payload 1 // ideology name -- 4 variants payload 2 diff --git a/src/scripting/triggers.cpp b/src/scripting/triggers.cpp index b53f4db02..dddb74e63 100644 --- a/src/scripting/triggers.cpp +++ b/src/scripting/triggers.cpp @@ -2817,6 +2817,10 @@ TRIGGER_FUNCTION(tf_money) { return compare_values(tval[0], ws.world.nation_get_stockpiles(to_nation(primary_slot), economy::money), read_float_from_payload(tval + 1)); } +TRIGGER_FUNCTION(tf_suppression_points) { + return compare_values(tval[0], ws.world.nation_get_suppression_points(to_nation(primary_slot)), + read_float_from_payload(tval + 1)); +} TRIGGER_FUNCTION(tf_money_province) { return compare_values(tval[0], ws.world.nation_get_stockpiles(ws.world.province_get_nation_from_province_ownership(to_prov(primary_slot)), economy::money), read_float_from_payload(tval + 1));