diff options
Diffstat (limited to 'extension/src/openvic-extension/singletons')
6 files changed, 212 insertions, 7 deletions
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp index c1a811e..33d70da 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.cpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp @@ -170,7 +170,7 @@ float GameSingleton::get_map_aspect_ratio() const { return static_cast<float>(get_map_width()) / static_cast<float>(get_map_height()); } -Vector2 GameSingleton::map_position_to_world_coords(fvec2_t const& position) const { +Vector2 GameSingleton::normalise_map_position(fvec2_t const& position) const { return Utilities::to_godot_fvec2(position) / get_map_dims(); } diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp index b29e588..e7f12bd 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.hpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp @@ -86,7 +86,7 @@ namespace OpenVic { int32_t get_map_height() const; godot::Vector2i get_map_dims() const; float get_map_aspect_ratio() const; - godot::Vector2 map_position_to_world_coords(fvec2_t const& position) const; + godot::Vector2 normalise_map_position(fvec2_t const& position) const; /* The cosmetic terrain textures stored in a Texture2DArray. */ godot::Ref<godot::Texture2DArray> get_terrain_texture() const; diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.cpp b/extension/src/openvic-extension/singletons/MenuSingleton.cpp index 1c3b6ec..885915c 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.cpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.cpp @@ -28,6 +28,10 @@ StringName const& MenuSingleton::_signal_population_menu_pops_changed() { static const StringName signal_population_menu_pops_changed = "population_menu_pops_changed"; return signal_population_menu_pops_changed; } +StringName const& MenuSingleton::_signal_search_cache_changed() { + static const StringName signal_search_cache_changed = "search_cache_changed"; + return signal_search_cache_changed; +} String MenuSingleton::get_state_name(State const& state) const { StateSet const& state_set = state.get_state_set(); @@ -59,14 +63,47 @@ String MenuSingleton::get_state_name(State const& state) const { if (owned && split) { // COUNTRY STATE/CAPITAL - return tr(std_view_to_godot_string(StringUtils::append_string_views(state.get_owner()->get_identifier(), "_ADJ"))) - + " " + name; + return get_country_adjective(*state.get_owner()) + " " + name; } // STATE/CAPITAL return name; } +String MenuSingleton::get_country_name(CountryInstance const& country) const { + if (country.get_government_type() != nullptr && !country.get_government_type()->get_identifier().empty()) { + const String government_name_key = std_to_godot_string(StringUtils::append_string_views( + country.get_identifier(), "_", country.get_government_type()->get_identifier() + )); + + String government_name = tr(government_name_key); + + if (government_name != government_name_key) { + return government_name; + } + } + + return tr(std_view_to_godot_string(country.get_identifier())); +} + +String MenuSingleton::get_country_adjective(CountryInstance const& country) const { + static constexpr std::string_view adjective = "_ADJ"; + + if (country.get_government_type() != nullptr && !country.get_government_type()->get_identifier().empty()) { + const String government_adjective_key = std_to_godot_string(StringUtils::append_string_views( + country.get_identifier(), "_", country.get_government_type()->get_identifier(), adjective + )); + + String government_adjective = tr(government_adjective_key); + + if (government_adjective != government_adjective_key) { + return government_adjective; + } + } + + return tr(std_to_godot_string(StringUtils::append_string_views(country.get_identifier(), adjective))); +} + void MenuSingleton::_bind_methods() { /* PROVINCE OVERVIEW PANEL */ OV_BIND_METHOD(MenuSingleton::get_province_info_from_index, { "index" }); @@ -142,6 +179,15 @@ void MenuSingleton::_bind_methods() { BIND_ENUM_CONSTANT(SORT_REBEL_FACTION); BIND_ENUM_CONSTANT(SORT_SIZE_CHANGE); BIND_ENUM_CONSTANT(SORT_LITERACY); + + /* Find/Search Panel */ + OV_BIND_METHOD(MenuSingleton::generate_search_cache); + OV_BIND_METHOD(MenuSingleton::update_search_results, { "text" }); + OV_BIND_METHOD(MenuSingleton::get_search_result_rows, { "start", "count" }); + OV_BIND_METHOD(MenuSingleton::get_search_result_row_count); + OV_BIND_METHOD(MenuSingleton::get_search_result_position, { "result_index" }); + + ADD_SIGNAL(MethodInfo(_signal_search_cache_changed())); } MenuSingleton* MenuSingleton::get_singleton() { @@ -442,3 +488,139 @@ String MenuSingleton::get_longform_date() const { return Utilities::date_to_formatted_string(instance_manager->get_today()); } + +Error MenuSingleton::generate_search_cache() { + GameSingleton const* game_singleton = GameSingleton::get_singleton(); + ERR_FAIL_NULL_V(game_singleton, FAILED); + InstanceManager const* instance_manager = game_singleton->get_instance_manager(); + ERR_FAIL_NULL_V(instance_manager, FAILED); + + search_panel.entry_cache.clear(); + + std::vector<ProvinceInstance> const& provinces = instance_manager->get_map_instance().get_province_instances(); + std::vector<StateSet> const& state_sets = instance_manager->get_map_instance().get_state_manager().get_state_sets(); + std::vector<CountryInstance> const& countries = instance_manager->get_country_instance_manager().get_country_instances(); + + // TODO - reserve actual state count rather than state set count (maybe use a vector of pointers to all states?) + search_panel.entry_cache.reserve(provinces.size() + state_sets.size() + countries.size()); + + for (ProvinceInstance const& province : provinces) { + String identifier = std_view_to_godot_string(province.get_identifier()); + String display_name = tr(GUINode::format_province_name(identifier)); + String search_name = display_name.to_lower(); + + search_panel.entry_cache.push_back({ + &province, std::move(display_name), std::move(search_name), identifier.to_lower() + }); + } + + for (StateSet const& state_set : state_sets) { + for (State const& state : state_set.get_states()) { + String display_name = get_state_name(state); + String search_name = display_name.to_lower(); + + search_panel.entry_cache.push_back({ + // TODO - include state identifier? (region and/or split?) + &state, std::move(display_name), std::move(search_name), {} + }); + } + } + + for (CountryInstance const& country : countries) { + // TODO - replace with a proper "exists" check + if (country.get_capital() != nullptr) { + String display_name = get_country_name(country); + String search_name = display_name.to_lower(); + + search_panel.entry_cache.push_back({ + &country, std::move(display_name), std::move(search_name), + std_view_to_godot_string(country.get_identifier()).to_lower() + }); + } + } + + std::sort(search_panel.entry_cache.begin(), search_panel.entry_cache.end(), [](auto const& a, auto const& b) -> bool { + return a.search_name < b.search_name; + }); + + emit_signal(_signal_search_cache_changed()); + + return OK; +} + +void MenuSingleton::update_search_results(godot::String const& text) { + // Sanatise input + const String search_text = text.strip_edges().to_lower(); + + search_panel.result_indices.clear(); + + if (!search_text.is_empty()) { + // Search through cache + for (size_t idx = 0; idx < search_panel.entry_cache.size(); ++idx) { + search_panel_t::entry_t const& entry = search_panel.entry_cache[idx]; + + if (entry.search_name.begins_with(search_text) || entry.identifier == search_text) { + search_panel.result_indices.push_back(idx); + } + } + } +} + +PackedStringArray MenuSingleton::get_search_result_rows(int32_t start, int32_t count) const { + if (search_panel.result_indices.empty()) { + return {}; + } + + ERR_FAIL_INDEX_V_MSG( + start, search_panel.result_indices.size(), {}, + vformat("Invalid start for search panel result rows: %d", start) + ); + ERR_FAIL_COND_V_MSG(count <= 0, {}, vformat("Invalid count for search panel result rows: %d", count)); + + if (start + count > search_panel.result_indices.size()) { + UtilityFunctions::push_warning( + "Requested search result rows beyond the end of the result indices (", start, " + ", count, " > ", + static_cast<int64_t>(search_panel.result_indices.size()), "), limiting to ", + static_cast<int64_t>(search_panel.result_indices.size() - start), " rows." + ); + count = search_panel.result_indices.size() - start; + } + + PackedStringArray results; + results.resize(count); + + for (size_t idx = 0; idx < count; ++idx) { + results[idx] = search_panel.entry_cache[search_panel.result_indices[start + idx]].display_name; + } + + return results; +} + +int32_t MenuSingleton::get_search_result_row_count() const { + return search_panel.result_indices.size(); +} + +Vector2 MenuSingleton::get_search_result_position(int32_t result_index) const { + ERR_FAIL_INDEX_V(result_index, search_panel.result_indices.size(), {}); + + GameSingleton const* game_singleton = GameSingleton::get_singleton(); + ERR_FAIL_NULL_V(game_singleton, {}); + + struct entry_visitor_t { + fvec2_t operator()(ProvinceInstance const* province) { + return province->get_province_definition().get_centre(); + } + + fvec2_t operator()(State const* state) { + return (*this)(state->get_capital()); + } + + fvec2_t operator()(CountryInstance const* country) { + return (*this)(country->get_capital()); + } + } entry_visitor; + + return game_singleton->normalise_map_position( + std::visit(entry_visitor, search_panel.entry_cache[search_panel.result_indices[result_index]].target) + ); +} diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.hpp b/extension/src/openvic-extension/singletons/MenuSingleton.hpp index 54891bc..190e3ea 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.hpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.hpp @@ -73,8 +73,18 @@ namespace OpenVic { std::vector<Pop const*> pops, filtered_pops; }; + struct search_panel_t { + struct entry_t { + std::variant<ProvinceInstance const*, State const*, CountryInstance const*> target; + godot::String display_name, search_name, identifier; + }; + std::vector<entry_t> entry_cache; + std::vector<size_t> result_indices; + }; + private: population_menu_t population_menu; + search_panel_t search_panel; /* Emitted when the number of visible province list rows changes (list generated or state entry expanded).*/ static godot::StringName const& _signal_population_menu_province_list_changed(); @@ -83,8 +93,12 @@ namespace OpenVic { static godot::StringName const& _signal_population_menu_province_list_selected_changed(); /* Emitted when the selected/filtered collection of pops changes. */ static godot::StringName const& _signal_population_menu_pops_changed(); + /* Emitted when the collection of possible search results changes. */ + static godot::StringName const& _signal_search_cache_changed(); godot::String get_state_name(State const& state) const; + godot::String get_country_name(CountryInstance const& country) const; + godot::String get_country_adjective(CountryInstance const& country) const; protected: static void _bind_methods(); @@ -145,6 +159,15 @@ namespace OpenVic { godot::PackedStringArray get_population_menu_distribution_setup_info() const; /* Array of GFXPieChartTexture::godot_pie_chart_data_t. */ godot::TypedArray<godot::Array> get_population_menu_distribution_info() const; + + /* Find/Search Panel */ + // TODO - update on country government type change and state creation/destruction + // (which automatically includes country creation/destruction) + godot::Error generate_search_cache(); + void update_search_results(godot::String const& text); + godot::PackedStringArray get_search_result_rows(int32_t start, int32_t count) const; + int32_t get_search_result_row_count() const; + godot::Vector2 get_search_result_position(int32_t result_index) const; }; } diff --git a/extension/src/openvic-extension/singletons/ModelSingleton.cpp b/extension/src/openvic-extension/singletons/ModelSingleton.cpp index 5fb9cf8..0e90a00 100644 --- a/extension/src/openvic-extension/singletons/ModelSingleton.cpp +++ b/extension/src/openvic-extension/singletons/ModelSingleton.cpp @@ -295,7 +295,7 @@ bool ModelSingleton::add_unit_dict( } dict[position_key] = - game_singleton->map_position_to_world_coords(unit.get_position()->get_province_definition().get_unit_position()); + game_singleton->normalise_map_position(unit.get_position()->get_province_definition().get_unit_position()); if (display_unit_type->get_unit_category() != UnitType::unit_category_t::INFANTRY) { dict[rotation_key] = -0.25f * std::numbers::pi_v<float>; @@ -433,7 +433,7 @@ bool ModelSingleton::add_building_dict( dict[model_key] = make_model_dict(*actor); - dict[position_key] = game_singleton->map_position_to_world_coords( + dict[position_key] = game_singleton->normalise_map_position( position_ptr != nullptr ? *position_ptr : province_definition.get_centre() ); diff --git a/extension/src/openvic-extension/singletons/PopulationMenu.cpp b/extension/src/openvic-extension/singletons/PopulationMenu.cpp index df5b6b1..65987ad 100644 --- a/extension/src/openvic-extension/singletons/PopulationMenu.cpp +++ b/extension/src/openvic-extension/singletons/PopulationMenu.cpp @@ -110,7 +110,7 @@ TypedArray<Dictionary> MenuSingleton::get_population_menu_province_list_rows(int country_dict[type_key] = population_menu_t::LIST_ENTRY_COUNTRY; country_dict[index_key] = index; - country_dict[name_key] = std_view_to_godot_string(country_entry.country.get_identifier()); + country_dict[name_key] = menu_singleton.get_country_name(country_entry.country); country_dict[size_key] = country_entry.country.get_total_population(); country_dict[change_key] = 0; country_dict[selected_key] = country_entry.selected; |