diff --git a/README.md b/README.md index dcd2ae7b..798643cc 100644 --- a/README.md +++ b/README.md @@ -69,4 +69,6 @@ Every time you add something to the GDExtension you need to add to the class ref 3. Write documentation in the style of Godot, see [Godot's Writing Documentation: Class Reference Guides](https://docs.godotengine.org/en/stable/contributing/documentation/index.html#class-reference-guides). 4. Build with `scons`. +Windows note: doctool may not work in powershell/vscode terminal use command prompt directly. + If you change anything that causes new behavior in the GDExtension you should also change the corresponding class reference documentation. diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation index 47be6033..4bc9c5b0 160000 --- a/extension/deps/openvic-simulation +++ b/extension/deps/openvic-simulation @@ -1 +1 @@ -Subproject commit 47be6033ef72fc86c946d24a462325b256dec3f7 +Subproject commit 4bc9c5b035d55ce1fce68e1e59734e3742a853e0 diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp index 2a5ae530..17cb3a97 100644 --- a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp +++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp @@ -1,30 +1,34 @@ #pragma once +#include +#include +#include +#include + #include +#include #include #include +#include #include +#include #include #include "openvic-extension/core/Convert.hpp" +#include "openvic-extension/utility/AsFloat.hpp" +#include "openvic-extension/utility/AsReferenceWrapper.hpp" #include "openvic-extension/utility/MapHelpers.hpp" -#include "openvic-simulation/core/template/Concepts.hpp" namespace OpenVic { - template - concept IsPieChartDistribution = ( - /* tsl::ordered_map, KeyType derived from HasIdentifierAndColour */ - specialization_of - /* IndexedFlatMap, KeyType derived from HasIdentifierAndColour */ - || specialization_of - ) - && has_get_identifier_and_colour>> - && ( - requires { static_cast(std::declval>()); } + template + concept IsPieChartKey = has_get_identifier_and_colour>; + template + concept IsPieChartValue = ( + requires { static_cast(std::declval()); } || ( - is_strongly_typed> - && requires { static_cast(type_safe::get(std::declval>())); } + is_strongly_typed + && requires { static_cast(type_safe::get(std::declval())); } ) ); @@ -56,52 +60,38 @@ namespace OpenVic { public: /* Generate slice data from a distribution of objects satisfying HasGetIdentifierAndGetColour, sorted by their weight. * The resulting Array of Dictionaries can be used as an argument for set_slices_array. */ - template + template static godot_pie_chart_data_t distribution_to_slices_array( - MapType const& distribution, + std::span> keys, + std::span> values, NodeTools::Functor< // return tooltip; args: key const*, identifier, weight, total weight - godot::String, std::remove_pointer_t> const*, godot::String const&, float, float + godot::String, std::reference_wrapper>>, godot::String const&, float, float > auto make_tooltip, godot::String const& identifier_suffix = {} ) { - using namespace godot; + assert(keys.size() == values.size()); + using key_t = std::reference_wrapper>>; + using entry_t = std::pair< + key_t, + float + >; - using key_type = std::remove_pointer_t>; - using entry_t = std::pair; - - std::vector sorted_distribution; - if constexpr (specialization_of) { - sorted_distribution.reserve(distribution.get_count()); - } else { - sorted_distribution.reserve(distribution.size()); - } + memory::FixedVector sorted_distribution { create_empty, keys.size() }; float total_weight = 0.0f; - - for (auto [key_ref_or_ptr, non_float_value] : distribution) { - key_type const* key_ptr; - if constexpr (std::same_as) { - key_ptr = key_ref_or_ptr; - } else { - key_ptr = &key_ref_or_ptr; - } - - float value; - if constexpr (is_strongly_typed) { - value = static_cast(type_safe::get(non_float_value)); - } else { - value = static_cast(non_float_value); - } + for (size_t i = 0; i < keys.size(); ++i) { + key_t key = as_reference_wrapper(keys[i]); + float value = as_float(values[i]); if (value > 0.0f) { - sorted_distribution.emplace_back(key_ptr, value); + sorted_distribution.emplace_back(key, value); total_weight += value; } else if (value < 0.0f) { spdlog::error_s( "Negative distribution value {} for key \"{}\"", - value, *key_ptr + value, key.get() ); } } @@ -114,24 +104,24 @@ namespace OpenVic { std::sort( sorted_distribution.begin(), sorted_distribution.end(), [](entry_t const& lhs, entry_t const& rhs) -> bool { - return lhs.first < rhs.first; + return lhs.first.get().get_identifier() < rhs.first.get().get_identifier(); } ); godot_pie_chart_data_t array; - ERR_FAIL_COND_V(array.resize(sorted_distribution.size()) != OK, {}); + ERR_FAIL_COND_V(array.resize(sorted_distribution.size()) != godot::OK, {}); for (size_t index = 0; index < array.size(); ++index) { auto const& [key, value] = sorted_distribution[index]; - String identifier = convert_to(key->get_identifier()); + godot::String identifier = convert_to(key.get().get_identifier()); identifier += identifier_suffix; - Dictionary sub_dict; + godot::Dictionary sub_dict; sub_dict[_slice_tooltip_key()] = make_tooltip(key, identifier, value, total_weight); sub_dict[_slice_identifier_key()] = std::move(identifier); - sub_dict[_slice_colour_key()] = convert_to(key->get_colour()); + sub_dict[_slice_colour_key()] = convert_to(key.get().get_colour()); sub_dict[_slice_weight_key()] = value; array[index] = std::move(sub_dict); @@ -139,6 +129,29 @@ namespace OpenVic { return array; } + // adapter for ordered maps + template + static godot_pie_chart_data_t distribution_to_slices_array( + MapType const& distribution, + NodeTools::Functor< + godot::String, std::reference_wrapper>>>, + godot::String const&, float, float + > auto make_tooltip, + godot::String const& identifier_suffix = {} + ) { + memory::FixedVector> keys { create_empty, distribution.size() }; + memory::FixedVector> values { create_empty, distribution.size() }; + for (auto const& [k,v] : distribution) { + keys.emplace_back(k); + values.emplace_back(v); + } + return distribution_to_slices_array, map_value_t>( + keys, values, + make_tooltip, + identifier_suffix + ); + } + protected: static void _bind_methods(); diff --git a/extension/src/openvic-extension/core/Convert.hpp b/extension/src/openvic-extension/core/Convert.hpp index 475ba19a..44b71549 100644 --- a/extension/src/openvic-extension/core/Convert.hpp +++ b/extension/src/openvic-extension/core/Convert.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -11,10 +12,11 @@ #include #include +#include #include +#include #include #include -#include namespace OpenVic { template diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp index 94818988..f4118a70 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.cpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp @@ -10,11 +10,13 @@ #include #include +#include +#include #include #include #include +#include #include -#include #include #include "openvic-extension/core/Convert.hpp" @@ -679,7 +681,7 @@ Error GameSingleton::_load_flag_sheet() { ERR_FAIL_COND_V(flag_images.size() != flag_sheet_count, FAILED); /* Calculate the width that will make the sheet as close to a square as possible (taking flag dimensions into account.) */ - flag_sheet_dims.x = (fixed_point_t { static_cast(flag_images.size()) } * flag_dims.y / flag_dims.x).sqrt().ceil(); + flag_sheet_dims.x = fp::sqrt(fixed_point_t { static_cast(flag_images.size()) } * flag_dims.y / flag_dims.x).ceil(); /* Calculated corresponding height (rounded up). */ flag_sheet_dims.y = (static_cast(flag_images.size()) + flag_sheet_dims.x - 1) / flag_sheet_dims.x; @@ -756,6 +758,8 @@ Error GameSingleton::load_defines_compatibility_mode(PackedStringArray const& mo err = FAILED; } + MenuSingleton::get_singleton()->initialise(); + return err; } diff --git a/extension/src/openvic-extension/singletons/MapItemSingleton.cpp b/extension/src/openvic-extension/singletons/MapItemSingleton.cpp index 1fe6fac0..38c584da 100644 --- a/extension/src/openvic-extension/singletons/MapItemSingleton.cpp +++ b/extension/src/openvic-extension/singletons/MapItemSingleton.cpp @@ -1,27 +1,30 @@ #include "MapItemSingleton.hpp" +#include + #include -#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "godot_cpp/core/error_macros.hpp" -#include "godot_cpp/variant/packed_int32_array.hpp" -#include "godot_cpp/variant/packed_vector2_array.hpp" -#include "godot_cpp/variant/typed_array.hpp" -#include "godot_cpp/variant/vector2.hpp" #include "openvic-extension/core/Convert.hpp" #include "openvic-extension/singletons/GameSingleton.hpp" #include "openvic-extension/core/Bind.hpp" #include "openvic-extension/utility/Utilities.hpp" -#include "openvic-simulation/country/CountryDefinition.hpp" -#include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/DefinitionManager.hpp" -#include "openvic-simulation/economy/BuildingType.hpp" -#include "openvic-simulation/interface/GFXObject.hpp" -#include "openvic-simulation/map/ProvinceDefinition.hpp" -#include "openvic-simulation/map/ProvinceInstance.hpp" -#include "openvic-simulation/map/State.hpp" -#include "openvic-simulation/types/Vector.hpp" using namespace godot; using namespace OpenVic; @@ -164,7 +167,9 @@ PackedVector2Array MapItemSingleton::get_capital_positions() const { PackedVector2Array billboard_pos {}; - billboard_pos.resize(country_instance_manager.get_country_instance_by_definition().get_count()); + billboard_pos.resize(type_safe::get( + country_instance_manager.get_country_instances().size() + )); int64_t index = 0; diff --git a/extension/src/openvic-extension/singletons/MapItemSingleton.hpp b/extension/src/openvic-extension/singletons/MapItemSingleton.hpp index e2ec3b4c..8163aedb 100644 --- a/extension/src/openvic-extension/singletons/MapItemSingleton.hpp +++ b/extension/src/openvic-extension/singletons/MapItemSingleton.hpp @@ -1,7 +1,10 @@ #pragma once +#include + #include #include + #include #include diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.cpp b/extension/src/openvic-extension/singletons/MenuSingleton.cpp index 3b3bf301..e6ad4f7c 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.cpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.cpp @@ -1,17 +1,23 @@ #include "MenuSingleton.hpp" #include -#include #include +#include + #include +#include #include #include #include #include +#include +#include #include +#include #include +#include #include "openvic-extension/classes/GFXPieChartTexture.hpp" #include "openvic-extension/classes/GUINode.hpp" @@ -20,15 +26,31 @@ #include "openvic-extension/singletons/GameSingleton.hpp" #include "openvic-extension/singletons/PlayerSingleton.hpp" #include "openvic-extension/core/Bind.hpp" +#include "openvic-extension/utility/EasyAccess.hpp" #include "openvic-extension/utility/Utilities.hpp" using namespace godot; using namespace OpenVic; +void MenuSingleton::initialise() { + population_menu.initialise(); +} + MenuSingleton::population_menu_t::population_menu_t() -: pop_type_sort_cache { decltype(pop_type_sort_cache)::create_empty() }, -province_sort_cache { decltype(province_sort_cache)::create_empty() }, -rebel_type_sort_cache { decltype(rebel_type_sort_cache)::create_empty() } {} + : workforce_distribution { create_empty }, + ideology_distribution { create_empty }, + pop_type_sort_cache { create_empty }, + province_sort_cache { create_empty }, + rebel_type_sort_cache { create_empty } {} + +void MenuSingleton::population_menu_t::population_menu_t::initialise() { + if (is_initialised()) { return; } + workforce_distribution = { get_pop_types().size(), fixed_point_t::_0 }; + ideology_distribution = { get_ideologies().size(), fixed_point_t::_0 }; + pop_type_sort_cache = { get_pop_types().size(), 0 }; + province_sort_cache = { get_province_definitions().size(), 0 }; + rebel_type_sort_cache = { get_rebel_types().size(), 0 }; +} StringName const& MenuSingleton::_signal_population_menu_province_list_changed() { static const StringName signal_population_menu_province_list_changed = "population_menu_province_list_changed"; @@ -178,7 +200,7 @@ String MenuSingleton::_make_mobilisation_impact_tooltip() const { PartyPolicyGroup const* war_policy_issue_group = issue_manager.get_party_policy_group_by_identifier("war_policy"); PartyPolicy const* war_policy_issue = war_policy_issue_group == nullptr ? nullptr - : country.get_ruling_party_untracked()->get_policies(*war_policy_issue_group); + : country.get_ruling_party_untracked()->get_policies()[war_policy_issue_group->index]; const String impact_string = Utilities::fixed_point_to_string_dp(country.get_mobilisation_impact() * 100, 1) + "%"; @@ -445,7 +467,7 @@ static TypedArray _make_buildings_dict_array( return buildings_array; } -Dictionary MenuSingleton::get_province_info_from_number(int32_t province_number) const { +Dictionary MenuSingleton::get_province_info_from_number(std::uint16_t province_number) const { GameSingleton const* game_singleton = GameSingleton::get_singleton(); ERR_FAIL_NULL_V(game_singleton, {}); InstanceManager const* instance_manager = game_singleton->get_instance_manager(); @@ -562,7 +584,8 @@ Dictionary MenuSingleton::get_province_info_from_number(int32_t province_number) std::optional const& owner_job = production_type.owner; if (owner_job.has_value()) { - PopType const& owner_pop_type = *owner_job->pop_type; + const pop_type_index_t owner_pop_type_index = owner_job->pop_type_index; + PopType const& owner_pop_type = get_pop_type(owner_pop_type_index); State const* state = province->get_state(); if (unlikely(state == nullptr)) { @@ -573,8 +596,9 @@ Dictionary MenuSingleton::get_province_info_from_number(int32_t province_number) } else { const fixed_point_t effect_value = state->get_total_population() == 0 ? fixed_point_t::_0 - : owner_job->effect_multiplier.mul_div( - state->get_population_by_type(owner_pop_type), + : fp::mul_div( + owner_job->effect_multiplier, + state->get_population_by_type()[owner_pop_type_index], state->get_total_population() ); @@ -611,7 +635,8 @@ Dictionary MenuSingleton::get_province_info_from_number(int32_t province_number) String amount_of_employees_by_pop_type; - for (auto const& [pop_type, employees_of_type] : rgo.get_employee_count_per_type_cache()) { + for (pop_type_index_t pop_type_index {}; pop_type_index < rgo.get_employee_count_per_type_cache().size(); ++pop_type_index) { + const pop_size_t employees_of_type = rgo.get_employee_count_per_type_cache()[pop_type_index]; if (employees_of_type <= 0) { continue; } @@ -619,6 +644,7 @@ Dictionary MenuSingleton::get_province_info_from_number(int32_t province_number) static const String amount_of_employees_by_pop_type_template_string = "\n -" + GUILabel::get_colour_marker() + "Y%s" + GUILabel::get_colour_marker() + "!:%d"; + PopType const& pop_type = get_pop_type(pop_type_index); amount_of_employees_by_pop_type += Utilities::format( amount_of_employees_by_pop_type_template_string, tr(convert_to(pop_type.get_identifier())), @@ -626,12 +652,12 @@ Dictionary MenuSingleton::get_province_info_from_number(int32_t province_number) ); for (Job const& job : production_type.get_jobs()) { - if (job.pop_type != nullptr && *job.pop_type != pop_type) { + if (job.pop_type_index != pop_type_index) { continue; } const fixed_point_t effect_multiplier = job.effect_multiplier; - fixed_point_t relative_to_workforce = fixed_point_t::from_fraction(employees_of_type, max_employee_count); + fixed_point_t relative_to_workforce = fp::from_fraction(employees_of_type, max_employee_count); const fixed_point_t effect_value = effect_multiplier == fixed_point_t::_1 ? relative_to_workforce : effect_multiplier * std::min(relative_to_workforce, job.amount); @@ -895,7 +921,10 @@ Dictionary MenuSingleton::get_province_info_from_number(int32_t province_number) ret[province_info_total_population_key] = type_safe::get(province->get_total_population()); const auto make_pie_chart_tooltip = [this]( - has_get_identifier_and_colour auto const* key, String const& identifier, float weight, float total_weight + auto const& key, + String const& identifier, + float weight, + float total_weight ) -> String { static const String format_key = "%d%% %s"; return Utilities::format( @@ -905,20 +934,28 @@ Dictionary MenuSingleton::get_province_info_from_number(int32_t province_number) ); }; - GFXPieChartTexture::godot_pie_chart_data_t pop_types = - GFXPieChartTexture::distribution_to_slices_array(province->get_population_by_type(), make_pie_chart_tooltip); + GFXPieChartTexture::godot_pie_chart_data_t pop_types = GFXPieChartTexture::distribution_to_slices_array( + get_pop_types(), + province->get_population_by_type(), + make_pie_chart_tooltip + ); if (!pop_types.is_empty()) { ret[province_info_pop_types_key] = std::move(pop_types); } - GFXPieChartTexture::godot_pie_chart_data_t ideologies = - GFXPieChartTexture::distribution_to_slices_array(province->get_supporter_equivalents_by_ideology(), make_pie_chart_tooltip); + GFXPieChartTexture::godot_pie_chart_data_t ideologies = GFXPieChartTexture::distribution_to_slices_array( + get_ideologies(), + province->get_supporter_equivalents_by_ideology(), + make_pie_chart_tooltip + ); if (!ideologies.is_empty()) { ret[province_info_pop_ideologies_key] = std::move(ideologies); } - GFXPieChartTexture::godot_pie_chart_data_t cultures = - GFXPieChartTexture::distribution_to_slices_array(province->get_population_by_culture(), make_pie_chart_tooltip); + GFXPieChartTexture::godot_pie_chart_data_t cultures = GFXPieChartTexture::distribution_to_slices_array( + province->get_population_by_culture(), + make_pie_chart_tooltip + ); if (!cultures.is_empty()) { ret[province_info_pop_cultures_key] = std::move(cultures); } @@ -951,11 +988,12 @@ int32_t MenuSingleton::get_province_building_count() const { GameSingleton const* game_singleton = GameSingleton::get_singleton(); ERR_FAIL_NULL_V(game_singleton, 0); - return game_singleton->get_definition_manager().get_economy_manager().get_building_type_manager() - .get_province_building_types().size(); + return type_safe::get( + game_singleton->get_definition_manager().get_economy_manager().get_building_type_manager().get_province_building_types().size() + ); } -String MenuSingleton::get_province_building_identifier(int32_t building_index) const { +String MenuSingleton::get_province_building_identifier(std::uint16_t building_index) const { GameSingleton const* game_singleton = GameSingleton::get_singleton(); ERR_FAIL_NULL_V(game_singleton, {}); @@ -1096,8 +1134,8 @@ Dictionary MenuSingleton::get_topbar_info() const { Utilities::fixed_point_to_string_dp(research_points, 2) ).replace( fraction_replace_key, Utilities::fixed_point_to_string_dp( - fixed_point_t::from_fraction( - 100 * country.get_population_by_type(*pop_type), + fp::from_fraction( + 100 * country.get_population_by_type()[pop_type->index], country.get_total_population() ), 2 ) @@ -1205,8 +1243,8 @@ Dictionary MenuSingleton::get_topbar_info() const { Utilities::fixed_point_to_string_dp(leadership_points, 2) ).replace( fraction_replace_key, Utilities::fixed_point_to_string_dp( - fixed_point_t::from_fraction( - 100 * country.get_population_by_type(*pop_type), + fp::from_fraction( + 100 * country.get_population_by_type()[pop_type->index], country.get_total_population() ), 2 ) @@ -1501,4 +1539,4 @@ Vector2 MenuSingleton::get_search_result_position(int32_t result_index) const { return game_singleton->normalise_map_position( std::visit(entry_visitor, search_panel.entry_cache[search_panel.result_indices[result_index]].target) ); -} +} \ No newline at end of file diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.hpp b/extension/src/openvic-extension/singletons/MenuSingleton.hpp index ae6a12b6..ef9fd052 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.hpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.hpp @@ -7,11 +7,16 @@ #include #include +#include + +#include #include #include #include -#include +#include +#include #include +#include #include #include "openvic-extension/classes/GFXPieChartTexture.hpp" @@ -36,6 +41,8 @@ namespace OpenVic { struct LeaderInstance; struct GUIScrollbar; + using province_number_t = decltype(std::declval().get_province_number()); + class MenuSingleton : public godot::Object { GDCLASS(MenuSingleton, godot::Object) @@ -87,23 +94,33 @@ namespace OpenVic { * - Nationality (Culture) * - Issues * - Vote */ - fixed_point_map_t workforce_distribution; + memory::FixedVector workforce_distribution; fixed_point_map_t religion_distribution; - fixed_point_map_t ideology_distribution; + memory::FixedVector ideology_distribution; fixed_point_map_t culture_distribution; fixed_point_map_t issue_distribution; fixed_point_map_t vote_distribution; PopSortKey sort_key = SORT_NONE; bool sort_descending = true; - IndexedFlatMap pop_type_sort_cache; + memory::FixedVector pop_type_sort_cache; ordered_map culture_sort_cache; ordered_map religion_sort_cache; - IndexedFlatMap province_sort_cache; - IndexedFlatMap rebel_type_sort_cache; + memory::FixedVector province_sort_cache; + memory::FixedVector rebel_type_sort_cache; std::vector> pops, filtered_pops; population_menu_t(); + + [[nodiscard]] constexpr bool is_initialised() const { + return !workforce_distribution.empty() + && !ideology_distribution.empty() + && !pop_type_sort_cache.empty() + && !province_sort_cache.empty() + && !rebel_type_sort_cache.empty(); + } + + void initialise(); }; enum TradeSettingBit { @@ -167,6 +184,7 @@ namespace OpenVic { public: static MenuSingleton* get_singleton(); + void initialise(); /* This should only be called AFTER GameSingleton has been initialised! */ MenuSingleton(); @@ -189,9 +207,9 @@ namespace OpenVic { /* PROVINCE OVERVIEW PANEL */ /* Get info to display in Province Overview Panel, packaged in a Dictionary using StringName constants as keys. */ - godot::Dictionary get_province_info_from_number(int32_t province_number) const; + godot::Dictionary get_province_info_from_number(std::uint16_t province_number) const; int32_t get_province_building_count() const; - godot::String get_province_building_identifier(int32_t building_index) const; + godot::String get_province_building_identifier(std::uint16_t building_index) const; int32_t get_slave_pop_icon_index() const; int32_t get_administrative_pop_icon_index() const; int32_t get_rgo_owner_pop_icon_index() const; @@ -223,10 +241,19 @@ namespace OpenVic { godot::Error _population_menu_sort_pops(); godot::Error population_menu_update_locale_sort_cache(); godot::Error population_menu_select_sort_key(PopSortKey sort_key); - template + + template + GFXPieChartTexture::godot_pie_chart_data_t generate_population_menu_pop_row_pie_chart_data( + std::span> keys, + std::span> values, + godot::String const& identifier_suffix = {} + ) const; + + template GFXPieChartTexture::godot_pie_chart_data_t generate_population_menu_pop_row_pie_chart_data( Container const& distribution, godot::String const& identifier_suffix = {} ) const; + godot::TypedArray get_population_menu_pop_rows(int32_t start, int32_t count) const; int32_t get_population_menu_pop_row_count() const; @@ -248,35 +275,11 @@ namespace OpenVic { ) const; godot::Dictionary get_trade_menu_tables_info() const; - static constexpr int32_t calculate_slider_value_from_trade_menu_stockpile_cutoff( + static int32_t calculate_slider_value_from_trade_menu_stockpile_cutoff( const fixed_point_t stockpile_cutoff, const int32_t max_slider_value - ) { - // Math.log(2)/Math.log(Math.exp(Math.log(2001)/2000)) = 182.37... - constexpr fixed_point_t DOUBLES_AFTER_STEPS = fixed_point_t::parse_raw(11952029); - int32_t times_halved = 0; - fixed_point_t copy_plus_one = stockpile_cutoff+1; - while (copy_plus_one >= 2) { - copy_plus_one /= 2; - times_halved++; - } - int32_t slider_value = times_halved * DOUBLES_AFTER_STEPS.truncate(); - while ( - calculate_trade_menu_stockpile_cutoff_amount_fp( - slider_value - ) < stockpile_cutoff - ) { - if (slider_value >= max_slider_value) { - return max_slider_value; - } - ++slider_value; - } - - return slider_value; - } - static constexpr fixed_point_t calculate_trade_menu_stockpile_cutoff_amount_fp(fixed_point_t value) { - return fixed_point_t::exp_2001(value / 2000) - fixed_point_t::_1; - } + ); + static fixed_point_t calculate_trade_menu_stockpile_cutoff_amount_fp(fixed_point_t value); static float calculate_trade_menu_stockpile_cutoff_amount(GUIScrollbar const* slider); /* MILITARY MENU */ diff --git a/extension/src/openvic-extension/singletons/ModelSingleton.cpp b/extension/src/openvic-extension/singletons/ModelSingleton.cpp index 0a470aa1..d85bf1ba 100644 --- a/extension/src/openvic-extension/singletons/ModelSingleton.cpp +++ b/extension/src/openvic-extension/singletons/ModelSingleton.cpp @@ -5,9 +5,9 @@ #include +#include #include #include -#include #include "openvic-extension/core/Convert.hpp" #include "openvic-extension/singletons/GameSingleton.hpp" diff --git a/extension/src/openvic-extension/singletons/PopulationMenu.cpp b/extension/src/openvic-extension/singletons/PopulationMenu.cpp index 0c714299..f3cb9e1c 100644 --- a/extension/src/openvic-extension/singletons/PopulationMenu.cpp +++ b/extension/src/openvic-extension/singletons/PopulationMenu.cpp @@ -3,6 +3,7 @@ #include #include +#include "openvic-simulation/politics/Ideology.hpp" #include #include @@ -19,6 +20,9 @@ #include "openvic-extension/classes/GUINode.hpp" #include "openvic-extension/core/Convert.hpp" #include "openvic-extension/singletons/GameSingleton.hpp" +#include "openvic-extension/utility/AsFloat.hpp" +#include "openvic-extension/utility/AsReferenceWrapper.hpp" +#include "openvic-extension/utility/EasyAccess.hpp" #include "openvic-extension/utility/MapHelpers.hpp" #include "openvic-extension/utility/Utilities.hpp" @@ -391,9 +395,21 @@ Error MenuSingleton::_population_menu_update_pops() { Error MenuSingleton::_population_menu_update_filtered_pops() { population_menu.filtered_pops.clear(); - population_menu.workforce_distribution.clear(); + fixed_point_t workforce_distribution_running_total = 0; + std::fill( + population_menu.workforce_distribution.begin(), + population_menu.workforce_distribution.end(), + 0 + ); + + fixed_point_t ideology_distribution_running_total = 0; + std::fill( + population_menu.ideology_distribution.begin(), + population_menu.ideology_distribution.end(), + 0 + ); + population_menu.religion_distribution.clear(); - population_menu.ideology_distribution.clear(); population_menu.culture_distribution.clear(); population_menu.issue_distribution.clear(); population_menu.vote_distribution.clear(); @@ -407,21 +423,44 @@ Error MenuSingleton::_population_menu_update_filtered_pops() { for (Pop const& pop : population_menu.filtered_pops) { const fixed_point_t pop_size = fixed_point_t(type_safe::get(pop.get_size())); - population_menu.workforce_distribution[&pop.get_type()] += pop_size; + population_menu.workforce_distribution[pop.get_type().index] += pop_size; + workforce_distribution_running_total += pop_size; population_menu.religion_distribution[&pop.religion] += pop_size; - for (auto const& [ideology, supporter_equivalents] : pop.get_supporter_equivalents_by_ideology()) { - if (supporter_equivalents > 0) { - population_menu.ideology_distribution[&ideology] += supporter_equivalents; + { + ideology_index_t ideology_index {}; + for (const fixed_point_t supporter_equivalents : pop.get_supporter_equivalents_by_ideology()) { + if (supporter_equivalents > 0) { + population_menu.ideology_distribution[ideology_index] += supporter_equivalents; + ideology_distribution_running_total += supporter_equivalents; + } + ++ideology_index; } } population_menu.culture_distribution[&pop.culture] += pop_size; - population_menu.issue_distribution += pop.get_supporter_equivalents_by_issue(); + { + party_policy_index_t party_policy_index {}; + for(const fixed_point_t party_policy_support : pop.get_supporter_equivalents_by_party_policy()) { + population_menu.issue_distribution[&get_party_policy(party_policy_index)]; + ++party_policy_index; + } + } + { + reform_index_t reform_index {}; + for(const fixed_point_t reform_support : pop.get_supporter_equivalents_by_reform()) { + population_menu.issue_distribution[&get_reform(reform_index)]; + ++reform_index; + } + } population_menu.vote_distribution += pop.get_vote_equivalents_by_party(); } - normalise_fixed_point_map(population_menu.workforce_distribution); + for (fixed_point_t& v : population_menu.workforce_distribution) { + v /= workforce_distribution_running_total; + } normalise_fixed_point_map(population_menu.religion_distribution); - normalise_fixed_point_map(population_menu.ideology_distribution); + for (fixed_point_t& v : population_menu.ideology_distribution) { + v /= ideology_distribution_running_total; + } normalise_fixed_point_map(population_menu.culture_distribution); normalise_fixed_point_map(population_menu.issue_distribution); normalise_fixed_point_map(population_menu.vote_distribution); @@ -429,14 +468,14 @@ Error MenuSingleton::_population_menu_update_filtered_pops() { return _population_menu_sort_pops(); } -template -static bool indexed_map_lookup_less_than(IndexedFlatMap const& map, T const& lhs, T const& rhs) { - return map.at(lhs) < map.at(rhs); +template +static bool span_lookup_less_than(TypedSpan span, T const& lhs, T const& rhs) { + return span[lhs.index] < span[rhs.index]; } -template -static bool indexed_map_lookup_less_than(IndexedFlatMap const& map, T const* lhs, T const* rhs) { - return lhs != nullptr && rhs != nullptr && map.at(*lhs) < map.at(*rhs); +template +static bool span_lookup_less_than(TypedSpan span, T const* lhs, T const* rhs) { + return lhs != nullptr && rhs != nullptr && span_lookup_less_than(span, *lhs, *rhs); } template @@ -449,17 +488,68 @@ static bool ordered_map_lookup_less_than(ordered_map const& ma return lhs != nullptr && rhs != nullptr && map.at(lhs) < map.at(rhs); } -template -static ordered_map copy_non_default_to_ordered_map(IndexedFlatMap const& indexed_flat_map) { - ordered_map result {}; - for (auto const& [key, value] : indexed_flat_map) { - if constexpr(!std::is_default_constructible_v) { - result[&key] = value; - } else if (value != ValueType()) { - result[&key] = value; +constexpr bool sort_by_ideologies_less_than( + TypedSpan lhs, + TypedSpan rhs +) { + assert(lhs.size() == rhs.size()); + ideology_index_t index_of_lhs_max {}, index_of_rhs_max {}; + fixed_point_t lhs_max = fixed_point_t::usable_min, rhs_max = fixed_point_t::usable_min; + for (ideology_index_t i {}; i < lhs.size(); ++i) { + const fixed_point_t lhs_v = lhs[i]; + if (lhs_v > lhs_max) { + index_of_lhs_max = i; + lhs_max = lhs_v; + } + const fixed_point_t rhs_v = rhs[i]; + if (rhs_v > rhs_max) { + index_of_rhs_max = i; + rhs_max = rhs_v; + } + } + + return index_of_lhs_max > index_of_rhs_max || rhs_max > lhs_max; +} + +constexpr bool sort_by_issues_less_than( + TypedSpan lhs_party_policies, + TypedSpan rhs_party_policies, + TypedSpan lhs_reforms, + TypedSpan rhs_reforms +) { + assert(lhs_party_policies.size() == rhs_party_policies.size()); + assert(lhs_reforms.size() == rhs_reforms.size()); + + size_t index_of_lhs_max = 0, index_of_rhs_max = 0; + fixed_point_t lhs_max = fixed_point_t::usable_min, rhs_max = fixed_point_t::usable_min; + for (party_policy_index_t i {}; i < lhs_party_policies.size(); ++i) { + const fixed_point_t lhs_v = lhs_party_policies[i]; + if (lhs_v > lhs_max) { + index_of_lhs_max = type_safe::get(i); + lhs_max = lhs_v; + } + const fixed_point_t rhs_v = rhs_party_policies[i]; + if (rhs_v > rhs_max) { + index_of_rhs_max = type_safe::get(i); + rhs_max = rhs_v; + } + } + + const size_t offset = type_safe::get(lhs_party_policies.size()); + for (reform_index_t i {}; i < lhs_reforms.size(); ++i) { + const fixed_point_t lhs_v = lhs_reforms[i]; + if (lhs_v > lhs_max) { + index_of_lhs_max = type_safe::get(i) + offset; + lhs_max = lhs_v; + } + const fixed_point_t rhs_v = rhs_reforms[i]; + if (rhs_v > rhs_max) { + index_of_rhs_max = type_safe::get(i) + offset; + rhs_max = rhs_v; } } - return result; + + return index_of_lhs_max > index_of_rhs_max || rhs_max > lhs_max; } MenuSingleton::sort_func_t MenuSingleton::_get_population_menu_sort_func(PopSortKey sort_key) const { @@ -470,7 +560,7 @@ MenuSingleton::sort_func_t MenuSingleton::_get_population_menu_sort_func(PopSort }; case SORT_TYPE: return [this](Pop const& a, Pop const& b) -> bool { - return indexed_map_lookup_less_than(population_menu.pop_type_sort_cache, a.get_type(), b.get_type()); + return span_lookup_less_than(population_menu.pop_type_sort_cache, a.get_type(), b.get_type()); }; case SORT_CULTURE: return [this](Pop const& a, Pop const& b) -> bool { @@ -482,7 +572,7 @@ MenuSingleton::sort_func_t MenuSingleton::_get_population_menu_sort_func(PopSort }; case SORT_LOCATION: return [this](Pop const& a, Pop const& b) -> bool { - return indexed_map_lookup_less_than(population_menu.province_sort_cache, a.get_location(), b.get_location()); + return span_lookup_less_than(population_menu.province_sort_cache, a.get_location(), b.get_location()); }; case SORT_MILITANCY: return [](Pop const& a, Pop const& b) -> bool { @@ -494,14 +584,17 @@ MenuSingleton::sort_func_t MenuSingleton::_get_population_menu_sort_func(PopSort }; case SORT_IDEOLOGY: return [](Pop const& a, Pop const& b) -> bool { - return sorted_fixed_map_less_than( - copy_non_default_to_ordered_map(a.get_supporter_equivalents_by_ideology()), - copy_non_default_to_ordered_map(b.get_supporter_equivalents_by_ideology()) + return sort_by_ideologies_less_than( + a.get_supporter_equivalents_by_ideology(), + b.get_supporter_equivalents_by_ideology() ); }; case SORT_ISSUES: return [](Pop const& a, Pop const& b) -> bool { - return sorted_fixed_map_less_than(a.get_supporter_equivalents_by_issue(), b.get_supporter_equivalents_by_issue()); + return sort_by_issues_less_than( + a.get_supporter_equivalents_by_party_policy(), b.get_supporter_equivalents_by_party_policy(), + a.get_supporter_equivalents_by_reform(), b.get_supporter_equivalents_by_reform() + ); }; case SORT_UNEMPLOYMENT: return [](Pop const& a, Pop const& b) -> bool { @@ -527,7 +620,7 @@ MenuSingleton::sort_func_t MenuSingleton::_get_population_menu_sort_func(PopSort return [this](Pop const& a, Pop const& b) -> bool { // TODO - include country adjective for [pan-]nationalist rebels // TODO - handle social/political reform movements - return indexed_map_lookup_less_than( + return span_lookup_less_than( population_menu.rebel_type_sort_cache, a.get_rebel_type(), b.get_rebel_type() ); }; @@ -548,11 +641,11 @@ MenuSingleton::sort_func_t MenuSingleton::_get_population_menu_sort_func(PopSort Error MenuSingleton::_population_menu_sort_pops() { if (population_menu.sort_key != SORT_NONE) { if ( - population_menu.pop_type_sort_cache.get_keys().empty() + population_menu.pop_type_sort_cache.empty() || population_menu.culture_sort_cache.empty() || population_menu.religion_sort_cache.empty() - || population_menu.province_sort_cache.get_keys().empty() - || population_menu.rebel_type_sort_cache.get_keys().empty() + || population_menu.province_sort_cache.empty() + || population_menu.rebel_type_sort_cache.empty() ) { ERR_FAIL_COND_V(population_menu_update_locale_sort_cache() != OK, FAILED); } @@ -580,15 +673,20 @@ Error MenuSingleton::population_menu_update_locale_sort_cache() { std::vector localised_items; std::vector sorted_items; - const auto generate_sort_cache_indexed = [this, &localised_items, &sorted_items]( - IndexedFlatMap& cache, std::span items + const auto generate_sort_cache_indexed = [ + this, + &localised_items, + &sorted_items + ]( + std::span cache, + std::span items ) { localised_items.resize(items.size()); sorted_items.resize(items.size()); for (size_t idx = 0; idx < items.size(); ++idx) { String identifier = convert_to(items[idx].get_identifier()); - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { identifier = GUINode::format_province_name(identifier); } localised_items[idx] = tr(identifier).to_lower(); @@ -601,15 +699,9 @@ Error MenuSingleton::population_menu_update_locale_sort_cache() { } ); - IndexedFlatMap sorted_cache { items }; for (size_t idx = 0; idx < sorted_items.size(); ++idx) { - sorted_cache.at_index( - static_cast::type>( - sorted_items[idx] - ) - ) = idx; + cache[sorted_items[idx]] = idx; } - cache = std::move(sorted_cache); }; const auto generate_sort_cache_ordered = [this, &localised_items, &sorted_items]( ordered_map& cache, std::span items @@ -639,9 +731,10 @@ Error MenuSingleton::population_menu_update_locale_sort_cache() { } }; - generate_sort_cache_indexed( + // .operator() for macos + generate_sort_cache_indexed.operator()( population_menu.pop_type_sort_cache, - { game_singleton->get_definition_manager().get_pop_manager().get_pop_types() } + get_pop_types() ); generate_sort_cache_ordered( population_menu.culture_sort_cache, @@ -651,13 +744,13 @@ Error MenuSingleton::population_menu_update_locale_sort_cache() { population_menu.religion_sort_cache, { game_singleton->get_definition_manager().get_pop_manager().get_religion_manager().get_religions() } ); - generate_sort_cache_indexed( + generate_sort_cache_indexed.operator()( population_menu.province_sort_cache, - { instance_manager->get_map_instance().get_province_instances() } + instance_manager->get_map_instance().get_province_instances() ); - generate_sort_cache_indexed( + generate_sort_cache_indexed.operator()( population_menu.rebel_type_sort_cache, - { game_singleton->get_definition_manager().get_politics_manager().get_rebel_manager().get_rebel_types() } + get_rebel_types() ); return OK; @@ -681,58 +774,70 @@ Error MenuSingleton::population_menu_select_sort_key(PopSortKey sort_key) { return _population_menu_sort_pops(); } -template +template GFXPieChartTexture::godot_pie_chart_data_t MenuSingleton::generate_population_menu_pop_row_pie_chart_data( - MapType const& distribution, String const& identifier_suffix + std::span> keys, + std::span> values, + String const& identifier_suffix ) const { - using key_type = std::remove_pointer_t>; + assert(keys.size() == values.size()); + using key_t = std::reference_wrapper>>; - ordered_map tooltips; - if constexpr (specialization_of) { - tooltips.reserve(distribution.get_count()); - } else { - tooltips.reserve(distribution.size()); - } + ordered_map tooltips; + tooltips.reserve(keys.size()); String full_tooltip = get_tooltip_separator().trim_suffix("\n"); float total_weight = 0.0f; - for (auto [key, weight] : distribution) { - total_weight += static_cast(weight); + for (const ValueType weight : values) { + total_weight += as_float(weight); } static const String pie_chart_tooltip_format_key = "%s: " + GUILabel::get_colour_marker() + "Y%s%%" + GUILabel::get_colour_marker() + "!"; - for (auto [key_ref_or_ptr, weight] : distribution) { + for (size_t i = 0; i < keys.size(); ++i) { + key_t key = as_reference_wrapper(keys[i]); + float weight = as_float(values[i]); if (weight > 0.0f) { - key_type const* key_ptr; - if constexpr (std::same_as) { - key_ptr = key_ref_or_ptr; - } else { - key_ptr = &key_ref_or_ptr; - } String tooltip = Utilities::format( pie_chart_tooltip_format_key, - tr(convert_to(key_ptr->get_identifier()) + identifier_suffix), - Utilities::float_to_string_dp(100.0f * static_cast(weight) / total_weight, 1) + tr(convert_to(key.get().get_identifier()) + identifier_suffix), + Utilities::float_to_string_dp(100.0f * weight / total_weight, 1) ); full_tooltip += "\n" + tooltip; - tooltips.emplace(key_ptr, std::move(tooltip)); + tooltips.emplace(key, std::move(tooltip)); } // No need to handle negative (invalid) weights here, GFXPieChartTexture::distribution_to_slices_array will // log errors for them and discard them without attempting to generate a tooltip. } - return GFXPieChartTexture::distribution_to_slices_array( - distribution, - [&tooltips, full_tooltip](key_type const* key, String const& identifier, float weight, float total_weight) -> String { + return GFXPieChartTexture::distribution_to_slices_array( + keys, values, + [&tooltips, full_tooltip](key_t key, String const& identifier, float weight, float total_weight) -> String { return tooltips.at(key) + full_tooltip; }, identifier_suffix ); } +// adapter for ordered maps +template +GFXPieChartTexture::godot_pie_chart_data_t MenuSingleton::generate_population_menu_pop_row_pie_chart_data( + MapType const& distribution, String const& identifier_suffix +) const { + memory::FixedVector> keys { create_empty, distribution.size() }; + memory::FixedVector> values { create_empty, distribution.size() }; + for (auto const& [k,v] : distribution) { + keys.emplace_back(k); + values.emplace_back(v); + } + return generate_population_menu_pop_row_pie_chart_data, map_value_t>( + keys, values, + identifier_suffix + ); +} + TypedArray MenuSingleton::get_population_menu_pop_rows(int32_t start, int32_t count) const { if (population_menu.filtered_pops.empty()) { return {}; @@ -804,6 +909,23 @@ TypedArray MenuSingleton::get_population_menu_pop_rows(int32_t start Pop const& pop = population_menu.filtered_pops[start + idx]; Dictionary pop_dict; + fixed_point_map_t supporter_equivalents_by_issue; + { + party_policy_index_t party_policy_index {}; + for(const fixed_point_t party_policy_support : pop.get_supporter_equivalents_by_party_policy()) { + supporter_equivalents_by_issue[&get_party_policy(party_policy_index)]; + ++party_policy_index; + } + } + { + reform_index_t reform_index {}; + for(const fixed_point_t reform_support : pop.get_supporter_equivalents_by_reform()) { + supporter_equivalents_by_issue[&get_reform(reform_index)]; + ++reform_index; + } + } + normalise_fixed_point_map(supporter_equivalents_by_issue); + pop_dict[pop_size_key] = type_safe::get(pop.get_size()); pop_dict[pop_type_icon_key] = pop.get_type().sprite; pop_dict[pop_culture_key] = convert_to(pop.culture.get_identifier()); @@ -811,9 +933,13 @@ TypedArray MenuSingleton::get_population_menu_pop_rows(int32_t start pop_dict[pop_location_key] = convert_to(pop.get_location().get_identifier()); pop_dict[pop_militancy_key] = static_cast(pop.get_militancy()); pop_dict[pop_consciousness_key] = static_cast(pop.get_consciousness()); - pop_dict[pop_ideology_key] = generate_population_menu_pop_row_pie_chart_data(pop.get_supporter_equivalents_by_ideology()); + pop_dict[pop_ideology_key] = generate_population_menu_pop_row_pie_chart_data( + get_ideologies(), + pop.get_supporter_equivalents_by_ideology() + ); pop_dict[pop_issues_key] = generate_population_menu_pop_row_pie_chart_data( - pop.get_supporter_equivalents_by_issue(), get_issue_identifier_suffix() + supporter_equivalents_by_issue, + get_issue_identifier_suffix() ); pop_dict[pop_unemployment_key] = static_cast(pop.get_unemployment_fraction()); pop_dict[pop_cash_key] = static_cast(pop.get_cash().get_copy_of_value()); @@ -961,7 +1087,7 @@ TypedArray MenuSingleton::get_population_menu_distribution_info() const { ERR_FAIL_COND_V(array.resize(population_menu_t::DISTRIBUTION_COUNT) != OK, {}); const auto make_pie_chart_tooltip = [this]( - has_get_identifier_and_colour auto const* key, String const& identifier, float weight, float total_weight + auto const& key, String const& identifier, float weight, float total_weight ) -> String { static const String format_key = GUILabel::get_colour_marker() + String { "Y%s" } + GUILabel::get_colour_marker() + "!: %s%%"; @@ -972,9 +1098,17 @@ TypedArray MenuSingleton::get_population_menu_distribution_info() const { ); }; - array[0] = GFXPieChartTexture::distribution_to_slices_array(population_menu.workforce_distribution, make_pie_chart_tooltip); + array[0] = GFXPieChartTexture::distribution_to_slices_array( + get_pop_types(), + population_menu.workforce_distribution, + make_pie_chart_tooltip + ); array[1] = GFXPieChartTexture::distribution_to_slices_array(population_menu.religion_distribution, make_pie_chart_tooltip); - array[2] = GFXPieChartTexture::distribution_to_slices_array(population_menu.ideology_distribution, make_pie_chart_tooltip); + array[2] = GFXPieChartTexture::distribution_to_slices_array( + get_ideologies(), + population_menu.ideology_distribution, + make_pie_chart_tooltip + ); array[3] = GFXPieChartTexture::distribution_to_slices_array(population_menu.culture_distribution, make_pie_chart_tooltip); array[4] = GFXPieChartTexture::distribution_to_slices_array( population_menu.issue_distribution, make_pie_chart_tooltip, get_issue_identifier_suffix() diff --git a/extension/src/openvic-extension/singletons/TradeMenu.cpp b/extension/src/openvic-extension/singletons/TradeMenu.cpp index 4a6aafe2..2447656f 100644 --- a/extension/src/openvic-extension/singletons/TradeMenu.cpp +++ b/extension/src/openvic-extension/singletons/TradeMenu.cpp @@ -4,6 +4,7 @@ #include +#include #include #include "openvic-extension/classes/GUILabel.hpp" @@ -208,27 +209,31 @@ Dictionary MenuSingleton::get_trade_menu_tables_info() const { PackedStringArray good_producers_tooltips; // TODO - replace test code with actual top producers CountryInstanceManager const& country_instance_manager = instance_manager->get_country_instance_manager(); + const size_t country_count = type_safe::get(country_instance_manager.get_country_instances().size()); for (GoodInstance const& good : good_instance_manager.get_good_instances()) { static const StringName top_producers_localisation_key = "TRADE_TOP_PRODUCERS"; String tooltip = tr(convert_to(good.get_identifier())) + get_tooltip_separator() + tr(top_producers_localisation_key); - for (size_t index = 0; index < 5; ++index) { - CountryInstance const* other_country_ptr = country_instance_manager.get_country_instance_by_index( + for (size_t index = 0; index < std::min(5, country_count); ++index) { + CountryInstance const& other_country = country_instance_manager.get_country_instance_by_index( country_index_t(index + 1) ); - ERR_CONTINUE(other_country_ptr == nullptr); - CountryInstance const& other_country = *other_country_ptr; - static const String top_producer_template_string = "\n" + GUILabel::get_flag_marker() + "%s %s: %s"; tooltip += Utilities::format( top_producer_template_string, convert_to(other_country.get_identifier()), Utilities::get_country_name(*this, other_country), - Utilities::fixed_point_to_string_dp(fixed_point_t(1000) / static_cast(index + 1), 2) + Utilities::fixed_point_to_string_dp( + fp::from_fraction( + 1000, + static_cast(index + 1) //for macos + ), + 2 + ) ); } @@ -332,6 +337,36 @@ Dictionary MenuSingleton::get_trade_menu_tables_info() const { return ret; } +int32_t MenuSingleton::calculate_slider_value_from_trade_menu_stockpile_cutoff( + const fixed_point_t stockpile_cutoff, + const int32_t max_slider_value +) { + // Math.log(2)/Math.log(Math.exp(Math.log(2001)/2000)) = 182.37... + constexpr fixed_point_t DOUBLES_AFTER_STEPS = fixed_point_t::parse_raw(11952029); + int32_t times_halved = 0; + fixed_point_t copy_plus_one = stockpile_cutoff+1; + while (copy_plus_one >= 2) { + copy_plus_one /= 2; + times_halved++; + } + int32_t slider_value = times_halved * DOUBLES_AFTER_STEPS.truncate(); + while ( + calculate_trade_menu_stockpile_cutoff_amount_fp( + slider_value + ) < stockpile_cutoff + ) { + if (slider_value >= max_slider_value) { + return max_slider_value; + } + ++slider_value; + } + + return slider_value; +} +fixed_point_t MenuSingleton::calculate_trade_menu_stockpile_cutoff_amount_fp(fixed_point_t value) { + return fixed_point_t::exp_2001(value / 2000) - fixed_point_t::_1; +} + float MenuSingleton::calculate_trade_menu_stockpile_cutoff_amount(GUIScrollbar const* slider) { ERR_FAIL_NULL_V(slider, 0.0f); diff --git a/extension/src/openvic-extension/utility/AsFloat.hpp b/extension/src/openvic-extension/utility/AsFloat.hpp new file mode 100644 index 00000000..6058d49b --- /dev/null +++ b/extension/src/openvic-extension/utility/AsFloat.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include + +namespace OpenVic { + template + static constexpr float as_float(T const& x) { + return static_cast(type_safe::get(x)); + } + + template + requires (!is_strongly_typed) + static constexpr float as_float(T const& x) { + return static_cast(x); + } +} \ No newline at end of file diff --git a/extension/src/openvic-extension/utility/AsReferenceWrapper.hpp b/extension/src/openvic-extension/utility/AsReferenceWrapper.hpp new file mode 100644 index 00000000..4dcf33a4 --- /dev/null +++ b/extension/src/openvic-extension/utility/AsReferenceWrapper.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace OpenVic { + template + static constexpr std::reference_wrapper> as_reference_wrapper(T const& x) { + return x; + } + template + static constexpr std::reference_wrapper> as_reference_wrapper(T const* const x) { + return *x; + } +} \ No newline at end of file diff --git a/extension/src/openvic-extension/utility/EasyAccess.hpp b/extension/src/openvic-extension/utility/EasyAccess.hpp new file mode 100644 index 00000000..cafc1aa1 --- /dev/null +++ b/extension/src/openvic-extension/utility/EasyAccess.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include + +#include "openvic-extension/singletons/GameSingleton.hpp" + +namespace OpenVic { + static TypedSpan get_ideologies() { + return GameSingleton::get_singleton()->get_definition_manager().get_politics_manager().get_ideology_manager().get_ideologies(); + } + static Ideology const& get_ideology(const ideology_index_t i) { + CRASH_BAD_INDEX_MSG(type_safe::get(i), type_safe::get(get_ideologies().size()), "get_ideology"); + return get_ideologies()[i]; + } + + static TypedSpan get_party_policies() { + return GameSingleton::get_singleton()->get_definition_manager().get_politics_manager().get_issue_manager().get_party_policies(); + } + static PartyPolicy const& get_party_policy(const party_policy_index_t i) { + CRASH_BAD_INDEX_MSG(type_safe::get(i), type_safe::get(get_party_policies().size()), "get_party_policy"); + return get_party_policies()[i]; + } + + static TypedSpan get_pop_types() { + return GameSingleton::get_singleton()->get_definition_manager().get_pop_manager().get_pop_types(); + } + static PopType const& get_pop_type(const pop_type_index_t i) { + CRASH_BAD_INDEX_MSG(type_safe::get(i), type_safe::get(get_pop_types().size()), "get_pop_types"); + return get_pop_types()[i]; + } + + static TypedSpan get_province_definitions() { + return GameSingleton::get_singleton()->get_definition_manager().get_map_definition().get_province_definitions(); + } + static ProvinceDefinition const& get_province_definition(const province_index_t i) { + CRASH_BAD_INDEX_MSG(type_safe::get(i), type_safe::get(get_province_definitions().size()), "get_province_definition"); + return get_province_definitions()[i]; + } + + static TypedSpan get_rebel_types() { + return GameSingleton::get_singleton()->get_definition_manager().get_politics_manager().get_rebel_manager().get_rebel_types(); + } + static RebelType const& get_rebel_type(const rebel_type_index_t i) { + CRASH_BAD_INDEX_MSG(type_safe::get(i), type_safe::get(get_rebel_types().size()), "get_rebel_type"); + return get_rebel_types()[i]; + } + + static TypedSpan get_reforms() { + return GameSingleton::get_singleton()->get_definition_manager().get_politics_manager().get_issue_manager().get_reforms(); + } + static Reform const& get_reform(const reform_index_t i) { + CRASH_BAD_INDEX_MSG(type_safe::get(i), type_safe::get(get_reforms().size()), "get_reform"); + return get_reforms()[i]; + } +} \ No newline at end of file diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp index c2ec1310..3611e21d 100644 --- a/extension/src/openvic-extension/utility/UITools.cpp +++ b/extension/src/openvic-extension/utility/UITools.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include "openvic-extension/classes/GUIButton.hpp" #include "openvic-extension/classes/GUIIcon.hpp" diff --git a/extension/src/openvic-extension/utility/Utilities.hpp b/extension/src/openvic-extension/utility/Utilities.hpp index 345fd18b..bf426381 100644 --- a/extension/src/openvic-extension/utility/Utilities.hpp +++ b/extension/src/openvic-extension/utility/Utilities.hpp @@ -1,14 +1,18 @@ #pragma once #include +#include #include #include #include #include +#include +#include #include #include +#include #include #define ERR(x) ((x) ? OK : FAILED) diff --git a/game/assets/localisation/locales/helpers.csv.import b/game/assets/localisation/locales/helpers.csv.import index 2a5dbb39..19dc3e40 100644 --- a/game/assets/localisation/locales/helpers.csv.import +++ b/game/assets/localisation/locales/helpers.csv.import @@ -15,3 +15,5 @@ dest_files=["res://assets/localisation/locales/helpers.en.translation", "res://a compress=false delimiter=1 +unescape_keys=false +unescape_translations=true diff --git a/game/project.godot b/game/project.godot index 7346aee7..30c6bbcc 100644 --- a/game/project.godot +++ b/game/project.godot @@ -42,7 +42,7 @@ SoundManager="*res://src/Autoload/SoundManager.gd" MusicManager="*res://src/Autoload/MusicManager/MusicManager.tscn" SaveManager="*res://src/Autoload/SaveManager.gd" CursorManager="*res://src/Autoload/CursorManager.gd" -Keychain="*uid://ba4bw6vqtoail" +Keychain="*uid://dgiia2xg7fsud" [display]