From 62007ab1d69b9785407c11a90d9df8c02015c438 Mon Sep 17 00:00:00 2001 From: BrickPi Date: Mon, 4 Nov 2024 23:54:42 -0600 Subject: [PATCH] Add Technology Folders and Areas --- .../singletons/MenuSingleton.cpp | 192 +++++++++++++----- .../singletons/MenuSingleton.hpp | 10 +- .../NationManagementScreen/TechnologyMenu.gd | 166 ++++++++++++++- 3 files changed, 303 insertions(+), 65 deletions(-) diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.cpp b/extension/src/openvic-extension/singletons/MenuSingleton.cpp index c9847e14..3bb4351d 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.cpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.cpp @@ -17,8 +17,12 @@ #include "openvic-extension/singletons/GameSingleton.hpp" #include "openvic-extension/utility/ClassBindings.hpp" #include "openvic-extension/utility/Utilities.hpp" +#include "godot_cpp/core/error_macros.hpp" #include "godot_cpp/variant/array.hpp" #include "godot_cpp/variant/dictionary.hpp" +#include "godot_cpp/variant/packed_float32_array.hpp" +#include "godot_cpp/variant/packed_int32_array.hpp" +#include "godot_cpp/variant/packed_string_array.hpp" #include "godot_cpp/variant/string.hpp" #include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/research/Technology.hpp" @@ -132,47 +136,55 @@ String MenuSingleton::make_modifier_effects_tooltip(ModifierValue const& modifie result += "\n"; } - result += tr(Utilities::std_to_godot_string(effect->get_localisation_key())); + make_modifier_effect_tooltip(*effect, value); + } - static const String post_name_text = ": " + GUILabel::get_colour_marker(); - result += post_name_text; + return result; +} - if (value == 0) { - result += "Y"; - } else if (effect->is_positive_good() == value > 0) { - result += "G"; - } else { - result += "R"; - } +String MenuSingleton::make_modifier_effect_tooltip(ModifierEffect const& effect, fixed_point_t value) const { + String result; - if (value >= 0) { - result += "+"; - } + result += tr(Utilities::std_to_godot_string(effect.get_localisation_key())); - static constexpr int32_t DECIMAL_PLACES = 2; - - using enum ModifierEffect::format_t; - - switch (effect->get_format()) { - case PROPORTION_DECIMAL: - result += GUINode::float_to_string_dp((value * 100).to_float(), DECIMAL_PLACES) + "%"; - break; - case PERCENTAGE_DECIMAL: - result += GUINode::float_to_string_dp(value.to_float(), DECIMAL_PLACES) + "%"; - break; - case INT: - result += String::num_int64(value.to_int64_t()); - break; - case RAW_DECIMAL: [[fallthrough]]; - default: // Use raw decimal as fallback format - result += GUINode::float_to_string_dp(value.to_float(), DECIMAL_PLACES); - break; - } + static const String post_name_text = ": " + GUILabel::get_colour_marker(); + result += post_name_text; + + if (value == 0) { + result += "Y"; + } else if (effect.is_positive_good() == value > 0) { + result += "G"; + } else { + result += "R"; + } - static const String end_text = GUILabel::get_colour_marker() + String { "!" }; - result += end_text; + if (value >= 0) { + result += "+"; } + static constexpr int32_t DECIMAL_PLACES = 2; + + using enum ModifierEffect::format_t; + + switch (effect.get_format()) { + case PROPORTION_DECIMAL: + result += GUINode::float_to_string_dp((value * 100).to_float(), DECIMAL_PLACES) + "%"; + break; + case PERCENTAGE_DECIMAL: + result += GUINode::float_to_string_dp(value.to_float(), DECIMAL_PLACES) + "%"; + break; + case INT: + result += String::num_int64(value.to_int64_t()); + break; + case RAW_DECIMAL: [[fallthrough]]; + default: // Use raw decimal as fallback format + result += GUINode::float_to_string_dp(value.to_float(), DECIMAL_PLACES); + break; + } + + static const String end_text = GUILabel::get_colour_marker() + String { "!" }; + result += end_text; + return result; } @@ -307,6 +319,7 @@ void MenuSingleton::_bind_methods() { ADD_SIGNAL(MethodInfo(_signal_search_cache_changed())); /* TECHNOLOGY MENU */ + OV_BIND_METHOD(MenuSingleton::get_technology_menu_defines); OV_BIND_METHOD(MenuSingleton::get_technology_menu_info); } @@ -1027,32 +1040,81 @@ Vector2 MenuSingleton::get_search_result_position(int32_t result_index) const { } /* TECHNOLOGY MENU */ -godot::Dictionary MenuSingleton::get_technology_menu_info() const { +godot::Dictionary MenuSingleton::get_technology_menu_defines() const { GameSingleton const* game_singleton = GameSingleton::get_singleton(); ERR_FAIL_NULL_V(game_singleton, {}); static const StringName tech_folders_key = "tech_folders"; + static const StringName tech_areas_key = "tech_areas"; + static const StringName technologies_key = "technologies"; + static const StringName folder_tech_count_key = "folder_tech_count"; + + Dictionary ret; + + std::vector const& tech_folders = game_singleton->get_definition_manager().get_research_manager().get_technology_manager().get_technology_folders(); + + PackedStringArray tech_folder_identifiers {}; + Array tech_area_identifiers {}; + Array tech_identifiers {}; + PackedInt32Array folder_tech_count {}; + for (TechnologyFolder const& folder : tech_folders) { + tech_folder_identifiers.push_back(Utilities::std_to_godot_string(folder.get_identifier())); + int32_t num_in_folder = 0; + + PackedStringArray folder_areas {}; + Array tech_folder_nested_array {}; // tech_identifiers has three levels of nested arrays :P + for (TechnologyArea const* area : folder.get_technology_areas()) { + folder_areas.push_back(Utilities::std_to_godot_string(area->get_identifier())); + + PackedStringArray area_technologies {}; + for (Technology const* tech : area->get_technologies()) { + area_technologies.push_back(Utilities::std_to_godot_string(tech->get_identifier())); + num_in_folder++; + } + tech_folder_nested_array.push_back(std::move(area_technologies)); + } + tech_area_identifiers.push_back(std::move(folder_areas)); + tech_identifiers.push_back(std::move(tech_folder_nested_array)); + folder_tech_count.push_back(num_in_folder); + } + ret[tech_folders_key] = std::move(tech_folder_identifiers); + ret[tech_areas_key] = std::move(tech_area_identifiers); + ret[technologies_key] = std::move(tech_identifiers); + ret[folder_tech_count_key] = std::move(folder_tech_count); + + return ret; +} + +godot::Dictionary MenuSingleton::get_technology_menu_info() const { + GameSingleton const* game_singleton = GameSingleton::get_singleton(); + ERR_FAIL_NULL_V(game_singleton, {}); + static const StringName tech_school_key = "tech_school"; - static const StringName tech_school_mod_names = "tech_school_mod_names"; static const StringName tech_school_mod_values = "tech_school_mod_values"; static const StringName tech_school_mod_icons = "tech_school_mod_icons"; + static const StringName tech_school_mod_tt = "tech_school_mod_tt"; - Dictionary ret; + static const StringName current_research_tech = "current_research_tech"; + static const StringName current_research_cat = "current_research_cat"; - std::vector tech_folder_identifiers = game_singleton->get_definition_manager().get_research_manager().get_technology_manager().get_technology_folder_identifiers(); - Array tech_folders {}; - for (auto folder : tech_folder_identifiers) { - tech_folders.push_back(Utilities::std_to_godot_string(folder)); - } - ret[tech_folders_key] = tech_folders; + static const StringName researched_technologies_key = "researched_technologies"; + + Dictionary ret; const CountryInstance* country = game_singleton->get_viewed_country(); if (country == nullptr) { ret[tech_school_key] = String("traditional_academic"); - ret[tech_school_mod_names] = Array {}; - ret[tech_school_mod_values] = Array {}; + ret[tech_school_mod_values] = PackedFloat32Array {}; + ret[tech_school_mod_icons] = PackedInt32Array {}; + ret[tech_school_mod_tt] = PackedStringArray {}; + ret[current_research_tech] = ""; + ret[current_research_cat] = ""; + ret[researched_technologies_key] = PackedStringArray {}; return ret; } + + std::vector tech_folder_identifiers = game_singleton->get_definition_manager().get_research_manager().get_technology_manager().get_technology_folder_identifiers(); + ret[tech_school_key] = Utilities::std_to_godot_string(country->get_tech_school() == nullptr ? "traditional_academic" : country->get_tech_school()->get_identifier()); static const auto bonus_suffix = "_research_bonus"; @@ -1062,7 +1124,7 @@ godot::Dictionary MenuSingleton::get_technology_menu_info() const { return std::find(tech_folder_identifiers.begin(), tech_folder_identifiers.end(), tempA) < std::find(tech_folder_identifiers.begin(), tech_folder_identifiers.end(), tempB); }; - std::vector> school_modifiers; + std::vector> school_modifiers {}; if (country->get_tech_school() != nullptr) { for (auto effect : country->get_tech_school()->get_values()) { if (!effect.first->get_identifier().starts_with("unciv")) { @@ -1071,22 +1133,42 @@ godot::Dictionary MenuSingleton::get_technology_menu_info() const { } } } + if (country->get_tech_school()->get_effect_count() > 0) { + std::sort(school_modifiers.begin(), school_modifiers.end(), compareFolders); + } } - std::sort(school_modifiers.begin(), school_modifiers.end(), compareFolders); - Array school_modifier_names {}; - Array school_modifier_values {}; - Array school_modifier_icons {}; + PackedFloat32Array school_modifier_values {}; + PackedInt32Array school_modifier_icons {}; + PackedStringArray school_modifier_tt {}; for (auto modifier : school_modifiers) { - school_modifier_names.push_back(Utilities::std_to_godot_string(modifier.first)); + int32_t folder_id = std::find(tech_folder_identifiers.begin(), tech_folder_identifiers.end(), modifier.first.substr(0, modifier.first.find(bonus_suffix))) - tech_folder_identifiers.begin(); + school_modifier_values.push_back(modifier.second.to_float()); - school_modifier_icons.push_back(1 + std::find(tech_folder_identifiers.begin(), tech_folder_identifiers.end(), modifier.first.substr(0, modifier.first.find(bonus_suffix))) - tech_folder_identifiers.begin()); + school_modifier_icons.push_back(1 + folder_id); + school_modifier_tt.push_back(make_modifier_effect_tooltip(**game_singleton->get_definition_manager().get_modifier_manager().get_modifier_effect_cache().get_research_bonus_effects().get_item_by_key(*game_singleton->get_definition_manager().get_research_manager().get_technology_manager().get_technology_folder_by_index(folder_id)), modifier.second)); } - ret[tech_school_mod_names] = school_modifier_names; - ret[tech_school_mod_values] = school_modifier_values; - ret[tech_school_mod_icons] = school_modifier_icons; + ret[tech_school_mod_values] = std::move(school_modifier_values); + ret[tech_school_mod_icons] = std::move(school_modifier_icons); + ret[tech_school_mod_tt] = std::move(school_modifier_tt); + + Technology const* current_research = country->get_current_research(); + if (current_research != nullptr) { + ret[current_research_tech] = Utilities::std_to_godot_string(current_research->get_identifier()); + ret[current_research_cat] = tr(Utilities::std_to_godot_string(current_research->get_area().get_folder().get_identifier())) + ", " + tr(Utilities::std_to_godot_string(current_research->get_area().get_identifier())); + } else { + ret[current_research_tech] = String(""); + ret[current_research_cat] = String(""); + } + + PackedStringArray researched_technologies {}; + for (Technology const& tech : game_singleton->get_definition_manager().get_research_manager().get_technology_manager().get_technologies()) { + if (country->is_technology_unlocked(tech)) + researched_technologies.push_back(Utilities::std_to_godot_string(tech.get_identifier())); + } + ret[researched_technologies_key] = std::move(researched_technologies); return ret; } \ 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 2c1bef4d..9081a187 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.hpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.hpp @@ -1,15 +1,15 @@ #pragma once -<<<<<<< HEAD -======= ->>>>>>> 05f564d (tech schools) #include #include +#include #include #include #include -#include "godot_cpp/variant/dictionary.hpp" +#include +#include +#include namespace OpenVic { struct CountryInstance; @@ -127,6 +127,7 @@ namespace OpenVic { godot::String get_country_adjective(CountryInstance const& country) const; godot::String make_modifier_effects_tooltip(ModifierValue const& modifier) const; + godot::String make_modifier_effect_tooltip(ModifierEffect const& effect, fixed_point_t value) const; godot::String make_rules_tooltip(RuleSet const& rules) const; protected: @@ -206,6 +207,7 @@ namespace OpenVic { godot::TypedArray get_population_menu_distribution_info() const; /* TECHNOLOGY MENU */ + godot::Dictionary get_technology_menu_defines() const; godot::Dictionary get_technology_menu_info() const; /* Find/Search Panel */ diff --git a/game/src/Game/GameSession/NationManagementScreen/TechnologyMenu.gd b/game/src/Game/GameSession/NationManagementScreen/TechnologyMenu.gd index 22df08fc..2af73201 100644 --- a/game/src/Game/GameSession/NationManagementScreen/TechnologyMenu.gd +++ b/game/src/Game/GameSession/NationManagementScreen/TechnologyMenu.gd @@ -4,8 +4,22 @@ var _active : bool = false const _screen : NationManagement.Screen = NationManagement.Screen.TECHNOLOGY +var _tech_defines : Dictionary = MenuSingleton.get_technology_menu_defines() +var _tech_folders : PackedStringArray +var _tech_areas : Array +var _technologies : Array +var _folder_tech_counts : PackedInt32Array + +var _tech_folder_buttons : Array +var _tech_folder_progressbars : Array +var _tech_folder_number_discovered_labels : Array +var _selected_folder = 0 + var _tech_school : GUILabel var _tech_school_modifiers : GUIOverlappingElementsBox +var _current_research_label : GUILabel +var _current_research_cat_label : GUILabel +var _current_research_progressbar : GUIProgressBar func _ready() -> void: GameSingleton.gamestate_updated.connect(_update_info) @@ -21,6 +35,101 @@ func _ready() -> void: if _tech_school_modifiers: _tech_school_modifiers.set_gui_child_element_name("country_technology", "school_icon_window") + _current_research_label = get_gui_label_from_nodepath(^"./country_technology/research_progress_name") + _current_research_cat_label = get_gui_label_from_nodepath(^"./country_technology/research_progress_category") + _current_research_progressbar = get_gui_progress_bar_from_nodepath(^"./country_technology/research_progress") + + _tech_folders = _tech_defines.get("tech_folders") + _tech_areas = _tech_defines.get("tech_areas") + _technologies = _tech_defines.get("technologies") + _folder_tech_counts = _tech_defines.get("folder_tech_count") + for i in range(_tech_folders.size()): + add_gui_element("country_technology", "folder_window") + + var folder_node = get_node(^"./folder_window") + var root_node = get_node(^"./country_technology") + + folder_node.reparent(root_node) + folder_node.name = _tech_folders[i] + "_folder" + + var pos = GUINode.get_gui_position("country_technology", "folder_offset") + pos.x += folder_node.get_size().x * i + folder_node.set_position(pos) + + var icon = GUINode.get_gui_icon_from_node(folder_node.get_node(^"./folder_icon")) + if icon: + icon.set_icon_index(i+1) + + var title = GUINode.get_gui_label_from_node(folder_node.get_node(^"./folder_category")) + if title: + title.set_text(tr(_tech_folders[i])) + + var button = GUINode.get_gui_icon_button_from_node(folder_node.get_node(^"./folder_button")) + var button_tooltip: String = tr("TECHNOLOGYVIEW_SHOW_FOLDER_TOOLTIP") + var button_dict: Dictionary = { "FOLDER" : _tech_folders[i] } + if button: + if i == 0: + button.set_icon_index(2) + button.pressed.connect( # change selected technology area + func() -> void: + _tech_folder_buttons[_selected_folder].set_icon_index(1) + for x in range(_tech_areas[_selected_folder].size()): + root_node.get_node("./" + _tech_areas[_selected_folder][x]).visible = false + _selected_folder = i + button.set_icon_index(2) + for x in range(_tech_areas[_selected_folder].size()): + root_node.get_node("./" + _tech_areas[_selected_folder][x]).visible = true + ) + button.set_tooltip_string_and_substitution_dict(button_tooltip, button_dict) + _tech_folder_buttons.push_back(button) + + var progressbar = GUINode.get_gui_progress_bar_from_node(folder_node.get_node(^"./folder_progress")) + if progressbar: + progressbar.mouse_filter = Control.MOUSE_FILTER_IGNORE + _tech_folder_progressbars.push_back(progressbar) + + var discovered = GUINode.get_gui_label_from_node(folder_node.get_node(^"folder_number_discovered")) + if discovered: + _tech_folder_number_discovered_labels.push_back(discovered) + + # areas + var folder_areas : PackedStringArray = _tech_areas[i] + for area_index in range(folder_areas.size()): + add_gui_element("country_technology", "tech_group") + + var area_node = get_node(^"./tech_group") + + area_node.reparent(root_node) + area_node.name = folder_areas[area_index] + if i != 0: + area_node.set_visible(false) + + pos = GUINode.get_gui_position("country_technology", "tech_group_offset") + pos.x += area_node.get_size().x * area_index + area_node.set_position(pos) + + var area_title = GUINode.get_gui_label_from_node(area_node.get_node(^"./group_name")) + if area_title: + area_title.set_text(tr(folder_areas[area_index])) + + # technologies + var area_technologies : PackedStringArray = _technologies[i][area_index] + for tech_index in range(area_technologies.size()): + add_gui_element("country_technology", "tech_window") + + var tech_node = get_node(^"./tech_window") + + tech_node.reparent(area_node) + tech_node.name = area_technologies[tech_index] + + pos = GUINode.get_gui_position("country_technology", "tech_offset") + pos.y += tech_node.get_size().y * tech_index + tech_node.set_position(pos) + + var tech_name = GUINode.get_gui_label_from_node(tech_node.get_node(^"./tech_name")) + if tech_name: + tech_name.set_text(tr(area_technologies[tech_index])) + var close_button : GUIIconButton = get_gui_icon_button_from_nodepath(^"./country_technology/close_button") if close_button: close_button.pressed.connect(Events.NationManagementScreens.close_nation_management_screen.bind(_screen)) @@ -39,18 +148,63 @@ func _on_update_active_nation_management_screen(active_screen : NationManagement func _update_info() -> void: if _active: var info : Dictionary = MenuSingleton.get_technology_menu_info() + if _tech_school: _tech_school.set_text(info.get("tech_school")) if _tech_school_modifiers: - var mod_names : Array = info.get("tech_school_mod_names") - var mod_values : Array = info.get("tech_school_mod_values") - var mod_icons : Array = info.get("tech_school_mod_icons") - var mod_count = mod_names.size() + var mod_values : PackedFloat32Array = info.get("tech_school_mod_values") + var mod_icons : PackedInt32Array = info.get("tech_school_mod_icons") + var mod_tooltips : PackedStringArray = info.get("tech_school_mod_tt") + var mod_count = mod_values.size() _tech_school_modifiers.set_child_count(mod_count) for i in range(mod_count): - get_gui_icon_from_nodepath("./country_technology/school_bonus_icons/school_icon_window_{x}/main_icon".format({"x": i})).set_icon_index(mod_icons[i]) - get_gui_icon_from_nodepath("./country_technology/school_bonus_icons/school_icon_window_{x}/plusminus_icon".format({"x": i})).set_icon_index(2 if mod_values[i] > 0 else 1) + var main_icon = get_gui_icon_from_nodepath("./country_technology/school_bonus_icons/school_icon_window_{x}/main_icon".format({"x": i})) + main_icon.set_icon_index(mod_icons[i]) + main_icon.mouse_filter = Control.MOUSE_FILTER_PASS + main_icon.set_tooltip_string(mod_tooltips[i]) + var plusminus_icon = get_gui_icon_from_nodepath("./country_technology/school_bonus_icons/school_icon_window_{x}/plusminus_icon".format({"x": i})) + plusminus_icon.set_icon_index(2 if mod_values[i] > 0 else 1) + plusminus_icon.mouse_filter = Control.MOUSE_FILTER_PASS + plusminus_icon.set_tooltip_string(mod_tooltips[i]) + + var current_research : String = info.get("current_research_tech") + if _current_research_label: + if current_research != "": + _current_research_label.set_text(tr(current_research)) + else: + _current_research_label.set_text(tr("TECHNOLOGYVIEW_NO_RESEARCH")) + + if _current_research_cat_label: + _current_research_cat_label.set_text(info.get("current_research_cat")) + + if _current_research_progressbar: + if current_research != "": + _current_research_progressbar.set_tooltip_string_and_substitution_dict(tr("TECHNOLOGYVIEW_RESEARCH_TOOLTIP"), {"TECH": tr(current_research), "DATE": MenuSingleton.get_longform_date()}) + else: + _current_research_progressbar.set_tooltip_string(tr("TECHNOLOGYVIEW_NO_RESEARCH_TOOLTIP")) + + var researched_techs : PackedStringArray = info.get("researched_technologies") + for ix in range(_technologies.size()): + var folder_number_discovered = 0 + for iy in range(_technologies[ix].size()): + for iz in range(_technologies[ix][iy].size()): + var tech_identifier = _technologies[ix][iy][iz] + var tech = get_gui_icon_button_from_nodepath("./country_technology/{y}/{z}/start_research".format({"y":_tech_areas[ix][iy], "z":tech_identifier})) + if tech: + if (researched_techs.has(tech_identifier)): + tech.set_icon_index(2) + folder_number_discovered += 1 + elif current_research == tech_identifier: + tech.set_icon_index(1) + else: + tech.set_icon_index(4) + var label: GUILabel = _tech_folder_number_discovered_labels[ix] + if label: + label.set_text("{r}/{a}".format({"r":folder_number_discovered,"a":_folder_tech_counts[ix]})) + var progbar: GUIProgressBar = _tech_folder_progressbars[ix] + if progbar: + progbar.value = float(folder_number_discovered) / float(_folder_tech_counts[ix]) show() else: