diff --git a/extension/src/openvic-extension/classes/GUIButton.cpp b/extension/src/openvic-extension/classes/GUIButton.cpp index bd00e34b..f2766ed9 100644 --- a/extension/src/openvic-extension/classes/GUIButton.cpp +++ b/extension/src/openvic-extension/classes/GUIButton.cpp @@ -3,4 +3,14 @@ using namespace godot; using namespace OpenVic; -void GUIButton::_bind_methods() {} +GUI_TOOLTIP_IMPLEMENTATIONS(GUIButton) + +void GUIButton::_bind_methods() { + GUI_TOOLTIP_BIND_METHODS(GUIButton) +} + +void GUIButton::_notification(int what) { + _tooltip_notification(what); +} + +GUIButton::GUIButton() : tooltip_active { false } {} diff --git a/extension/src/openvic-extension/classes/GUIButton.hpp b/extension/src/openvic-extension/classes/GUIButton.hpp index 4f14fdaf..244b65a3 100644 --- a/extension/src/openvic-extension/classes/GUIButton.hpp +++ b/extension/src/openvic-extension/classes/GUIButton.hpp @@ -2,11 +2,20 @@ #include +#include "openvic-extension/classes/GUIHasTooltip.hpp" + namespace OpenVic { class GUIButton : public godot::Button { GDCLASS(GUIButton, godot::Button) + GUI_TOOLTIP_DEFINITIONS + protected: static void _bind_methods(); + + void _notification(int what); + + public: + GUIButton(); }; } diff --git a/extension/src/openvic-extension/classes/GUIHasTooltip.hpp b/extension/src/openvic-extension/classes/GUIHasTooltip.hpp new file mode 100644 index 00000000..d22dba8b --- /dev/null +++ b/extension/src/openvic-extension/classes/GUIHasTooltip.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "openvic-extension/singletons/MenuSingleton.hpp" +#include "openvic-extension/utility/ClassBindings.hpp" +#include "openvic-extension/utility/Utilities.hpp" + +/* To add tooltip functionality to a class: + * - the class must be derived from Control. + * - add GUI_TOOLTIP_DEFINITIONS to the class definition, bearing in mind that it leaves visibility as private. + * - add GUI_TOOLTIP_IMPLEMENTATIONS(CLASS) to the class' source file. + * - add GUI_TOOLTIP_BIND_METHODS(CLASS) to the class' _bind_methods implementation. + * - call _tooltip_notification from the class' _notification method. + * - initialise tooltip_active to false in the class' constructor. */ + +#define GUI_TOOLTIP_DEFINITIONS \ + public: \ + void set_tooltip_string_and_substitution_dict( \ + godot::String const& new_tooltip_string, godot::Dictionary const& new_tooltip_substitution_dict \ + ); \ + void set_tooltip_string(godot::String const& new_tooltip_string); \ + void set_tooltip_substitution_dict(godot::Dictionary const& new_tooltip_substitution_dict); \ + void clear_tooltip(); \ + private: \ + godot::String PROPERTY(tooltip_string); \ + godot::Dictionary PROPERTY(tooltip_substitution_dict); \ + bool PROPERTY_CUSTOM_PREFIX(tooltip_active, is); \ + void _tooltip_notification(int what); \ + void _set_tooltip_visibility(bool visible); + +#define GUI_TOOLTIP_IMPLEMENTATIONS(CLASS) \ + void CLASS::set_tooltip_string_and_substitution_dict( \ + String const& new_tooltip_string, Dictionary const& new_tooltip_substitution_dict \ + ) { \ + if (get_mouse_filter() == MOUSE_FILTER_IGNORE) { \ + UtilityFunctions::push_error("Tooltips won't work for \"", get_name(), "\" as it has MOUSE_FILTER_IGNORE"); \ + } \ + if (tooltip_string != new_tooltip_string || tooltip_substitution_dict != new_tooltip_substitution_dict) { \ + tooltip_string = new_tooltip_string; \ + tooltip_substitution_dict = new_tooltip_substitution_dict; \ + if (tooltip_active) { \ + _set_tooltip_visibility(!tooltip_string.is_empty()); \ + } \ + } \ + } \ + void CLASS::set_tooltip_string(String const& new_tooltip_string) { \ + if (get_mouse_filter() == MOUSE_FILTER_IGNORE) { \ + UtilityFunctions::push_error("Tooltips won't work for \"", get_name(), "\" as it has MOUSE_FILTER_IGNORE"); \ + } \ + if (tooltip_string != new_tooltip_string) { \ + tooltip_string = new_tooltip_string; \ + if (tooltip_active) { \ + _set_tooltip_visibility(!tooltip_string.is_empty()); \ + } \ + } \ + } \ + void CLASS::set_tooltip_substitution_dict(Dictionary const& new_tooltip_substitution_dict) { \ + if (get_mouse_filter() == MOUSE_FILTER_IGNORE) { \ + UtilityFunctions::push_error("Tooltips won't work for \"", get_name(), "\" as it has MOUSE_FILTER_IGNORE"); \ + } \ + if (tooltip_substitution_dict != new_tooltip_substitution_dict) { \ + tooltip_substitution_dict = new_tooltip_substitution_dict; \ + if (tooltip_active) { \ + _set_tooltip_visibility(!tooltip_string.is_empty()); \ + } \ + } \ + } \ + void CLASS::clear_tooltip() { \ + set_tooltip_string_and_substitution_dict({}, {}); \ + } \ + void CLASS::_tooltip_notification(int what) { \ + if (what == NOTIFICATION_MOUSE_ENTER || what == NOTIFICATION_MOUSE_EXIT) { \ + if (tooltip_active != (what == NOTIFICATION_MOUSE_ENTER)) { \ + tooltip_active = !tooltip_active; \ + if (!tooltip_string.is_empty()) { \ + _set_tooltip_visibility(tooltip_active); \ + } \ + } \ + } \ + } \ + void CLASS::_set_tooltip_visibility(bool visible) { \ + MenuSingleton* menu_singleton = MenuSingleton::get_singleton(); \ + ERR_FAIL_NULL(menu_singleton); \ + if (visible) { \ + using namespace OpenVic::Utilities::literals; \ + static const Vector2 offset { 0.0_real, 64.0_real }; \ + menu_singleton->show_tooltip(tooltip_string, tooltip_substitution_dict, get_global_position() + offset); \ + } else { \ + menu_singleton->hide_tooltip(); \ + } \ + } + +#define GUI_TOOLTIP_BIND_METHODS(CLASS) \ + OV_BIND_METHOD(CLASS::get_tooltip_string); \ + OV_BIND_METHOD(CLASS::set_tooltip_string, { "new_tooltip_string" }); \ + OV_BIND_METHOD(CLASS::get_tooltip_substitution_dict); \ + OV_BIND_METHOD(CLASS::set_tooltip_substitution_dict, { "new_tooltip_substitution_dict" }); \ + OV_BIND_METHOD( \ + CLASS::set_tooltip_string_and_substitution_dict, { "new_tooltip_string", "new_tooltip_substitution_dict" } \ + ); \ + OV_BIND_METHOD(CLASS::clear_tooltip); \ + OV_BIND_METHOD(CLASS::is_tooltip_active); \ + ADD_PROPERTY( \ + PropertyInfo(Variant::STRING, "tooltip_string", PROPERTY_HINT_MULTILINE_TEXT), \ + "set_tooltip_string", "get_tooltip_string" \ + ); \ + ADD_PROPERTY( \ + PropertyInfo(Variant::DICTIONARY, "tooltip_substitution_dict"), \ + "set_tooltip_substitution_dict", "get_tooltip_substitution_dict" \ + ); \ + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tooltip_active"), "", "is_tooltip_active"); diff --git a/extension/src/openvic-extension/classes/GUILabel.cpp b/extension/src/openvic-extension/classes/GUILabel.cpp index 9fd6b603..028ac336 100644 --- a/extension/src/openvic-extension/classes/GUILabel.cpp +++ b/extension/src/openvic-extension/classes/GUILabel.cpp @@ -14,7 +14,11 @@ using namespace OpenVic::Utilities::literals; static constexpr int32_t DEFAULT_FONT_SIZE = 16; +GUI_TOOLTIP_IMPLEMENTATIONS(GUILabel) + void GUILabel::_bind_methods() { + GUI_TOOLTIP_BIND_METHODS(GUILabel) + OV_BIND_METHOD(GUILabel::clear); OV_BIND_METHOD(GUILabel::get_gui_text_name); @@ -80,6 +84,8 @@ void GUILabel::_bind_methods() { } void GUILabel::_notification(int what) { + _tooltip_notification(what); + switch (what) { case NOTIFICATION_RESIZED: case NOTIFICATION_TRANSLATION_CHANGED: { @@ -146,7 +152,8 @@ void GUILabel::_notification(int what) { } GUILabel::GUILabel() - : gui_text { nullptr }, + : tooltip_active { false }, + gui_text { nullptr }, text {}, substitution_dict {}, horizontal_alignment { HORIZONTAL_ALIGNMENT_LEFT }, diff --git a/extension/src/openvic-extension/classes/GUILabel.hpp b/extension/src/openvic-extension/classes/GUILabel.hpp index e0982b22..320df7cd 100644 --- a/extension/src/openvic-extension/classes/GUILabel.hpp +++ b/extension/src/openvic-extension/classes/GUILabel.hpp @@ -8,11 +8,14 @@ #include #include "openvic-extension/classes/GFXSpriteTexture.hpp" +#include "openvic-extension/classes/GUIHasTooltip.hpp" namespace OpenVic { class GUILabel : public godot::Control { GDCLASS(GUILabel, godot::Control) + GUI_TOOLTIP_DEFINITIONS + using colour_instructions_t = std::vector>; GUI::Text const* PROPERTY(gui_text); diff --git a/extension/src/openvic-extension/classes/GUITextureRect.cpp b/extension/src/openvic-extension/classes/GUITextureRect.cpp index 13fd3bbb..fba9b193 100644 --- a/extension/src/openvic-extension/classes/GUITextureRect.cpp +++ b/extension/src/openvic-extension/classes/GUITextureRect.cpp @@ -3,4 +3,14 @@ using namespace godot; using namespace OpenVic; -void GUITextureRect::_bind_methods() {} +GUI_TOOLTIP_IMPLEMENTATIONS(GUITextureRect) + +void GUITextureRect::_bind_methods() { + GUI_TOOLTIP_BIND_METHODS(GUITextureRect) +} + +void GUITextureRect::_notification(int what) { + _tooltip_notification(what); +} + +GUITextureRect::GUITextureRect() : tooltip_active { false } {} diff --git a/extension/src/openvic-extension/classes/GUITextureRect.hpp b/extension/src/openvic-extension/classes/GUITextureRect.hpp index afcf4da7..6fc81237 100644 --- a/extension/src/openvic-extension/classes/GUITextureRect.hpp +++ b/extension/src/openvic-extension/classes/GUITextureRect.hpp @@ -2,11 +2,20 @@ #include +#include "openvic-extension/classes/GUIHasTooltip.hpp" + namespace OpenVic { class GUITextureRect : public godot::TextureRect { GDCLASS(GUITextureRect, godot::TextureRect) + GUI_TOOLTIP_DEFINITIONS + protected: static void _bind_methods(); + + void _notification(int what); + + public: + GUITextureRect(); }; } diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.cpp b/extension/src/openvic-extension/singletons/MenuSingleton.cpp index 7a5f47f3..970f1f1f 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.cpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.cpp @@ -30,6 +30,10 @@ StringName const& MenuSingleton::_signal_search_cache_changed() { static const StringName signal_search_cache_changed = "search_cache_changed"; return signal_search_cache_changed; } +StringName const& MenuSingleton::_signal_update_tooltip() { + static const StringName signal_update_tooltip = "update_tooltip"; + return signal_update_tooltip; +} String MenuSingleton::get_state_name(State const& state) const { StateSet const& state_set = state.get_state_set(); @@ -103,6 +107,15 @@ String MenuSingleton::get_country_adjective(CountryInstance const& country) cons } void MenuSingleton::_bind_methods() { + /* TOOLTIP */ + OV_BIND_METHOD(MenuSingleton::show_tooltip, { "text", "substitution_dict", "position" }); + OV_BIND_METHOD(MenuSingleton::hide_tooltip); + + ADD_SIGNAL(MethodInfo( + _signal_update_tooltip(), PropertyInfo(Variant::STRING, "text"), + PropertyInfo(Variant::DICTIONARY, "substitution_dict"), PropertyInfo(Variant::VECTOR2, "position") + )); + /* PROVINCE OVERVIEW PANEL */ OV_BIND_METHOD(MenuSingleton::get_province_info_from_index, { "index" }); OV_BIND_METHOD(MenuSingleton::get_province_building_count); @@ -205,6 +218,16 @@ MenuSingleton::~MenuSingleton() { singleton = nullptr; } +/* TOOLTIP */ + +void MenuSingleton::show_tooltip(String const& text, Dictionary const& substitution_dict, Vector2 const& position) { + emit_signal(_signal_update_tooltip(), text, substitution_dict, position); +} + +void MenuSingleton::hide_tooltip() { + show_tooltip({}, {}, {}); +} + /* PROVINCE OVERVIEW PANEL */ static TypedArray _make_buildings_dict_array( diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.hpp b/extension/src/openvic-extension/singletons/MenuSingleton.hpp index 190e3eaa..4cf9cb5d 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.hpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.hpp @@ -95,6 +95,10 @@ namespace OpenVic { 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(); + /* Emitted when the current tooltip changes. Arguments: text (godot::String), substitution_dict (godot::Dictionary), + * position (godot::Vector2). If text is empty then the tooltip will be hidden, otherwise the text will be shown at + * the given position. */ + static godot::StringName const& _signal_update_tooltip(); godot::String get_state_name(State const& state) const; godot::String get_country_name(CountryInstance const& country) const; @@ -110,6 +114,12 @@ namespace OpenVic { MenuSingleton(); ~MenuSingleton(); + /* TOOLTIP */ + void show_tooltip( + godot::String const& text, godot::Dictionary const& substitution_dict, godot::Vector2 const& position + ); + void hide_tooltip(); + /* 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_index(int32_t index) const; diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp index a0ac83ec..45da7981 100644 --- a/extension/src/openvic-extension/utility/UITools.cpp +++ b/extension/src/openvic-extension/utility/UITools.cpp @@ -279,6 +279,7 @@ static bool generate_icon(generate_gui_args_t&& args) { args.result = godot_progress_bar; } else if (icon.get_sprite()->is_type()) { + // TODO - add custom node supporting slice-based tooltip GUITextureRect* gui_texture_rect = nullptr; ret &= new_control(gui_texture_rect, icon, args.name); ERR_FAIL_NULL_V_MSG( diff --git a/game/src/Game/GameSession/GameSession.tscn b/game/src/Game/GameSession/GameSession.tscn index 018aad80..5925f3d8 100644 --- a/game/src/Game/GameSession/GameSession.tscn +++ b/game/src/Game/GameSession/GameSession.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=20 format=3 uid="uid://bgnupcshe1m7r"] +[gd_scene load_steps=21 format=3 uid="uid://bgnupcshe1m7r"] [ext_resource type="Script" path="res://src/Game/GameSession/GameSession.gd" id="1_eklvp"] [ext_resource type="PackedScene" uid="uid://cvl76duuym1wq" path="res://src/Game/MusicConductor/MusicPlayer.tscn" id="2_kt6aa"] @@ -19,6 +19,7 @@ [ext_resource type="Script" path="res://src/Game/GameSession/NationManagementScreen/TradeMenu.gd" id="10_mv1r6"] [ext_resource type="Script" path="res://src/Game/GameSession/NationManagementScreen/DiplomacyMenu.gd" id="11_fu7ys"] [ext_resource type="Script" path="res://src/Game/GameSession/NationManagementScreen/MilitaryMenu.gd" id="12_6h6nc"] +[ext_resource type="Script" path="res://src/Game/GameSession/Tooltip.gd" id="20_3306e"] [node name="GameSession" type="Control" node_paths=PackedStringArray("_model_manager", "_game_session_menu")] editor_description = "SS-102, UI-546" @@ -156,6 +157,11 @@ offset_left = -150.0 offset_right = 0.0 grow_horizontal = 0 +[node name="Tooltip" type="GUINode" parent="UICanvasLayer/UI"] +layout_mode = 1 +anchors_preset = 15 +script = ExtResource("20_3306e") + [connection signal="detailed_view_changed" from="MapView" to="ModelManager" method="set_visible"] [connection signal="map_view_camera_changed" from="MapView" to="UICanvasLayer/UI/MapControlPanel" method="_on_map_view_camera_changed"] [connection signal="game_session_menu_button_pressed" from="UICanvasLayer/UI/MapControlPanel" to="." method="_on_game_session_menu_button_pressed"] diff --git a/game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd b/game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd index 948403cc..70e755a7 100644 --- a/game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd +++ b/game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd @@ -38,6 +38,7 @@ var _pop_list_type_buttons : Array[GUIButton] var _pop_list_type_icons : Array[GFXSpriteTexture] var _pop_list_producing_icons : Array[GFXSpriteTexture] var _pop_list_culture_labels : Array[GUILabel] +var _pop_list_religion_texture_rects : Array[GUITextureRect] var _pop_list_religion_icons : Array[GFXSpriteTexture] var _pop_list_location_labels : Array[GUILabel] var _pop_list_militancy_labels : Array[GUILabel] @@ -57,6 +58,7 @@ var _pop_list_political_movement_texture_rects : Array[GUITextureRect] var _pop_list_political_movement_icons : Array[GFXSpriteTexture] var _pop_list_national_movement_texture_rects : Array[GUITextureRect] var _pop_list_national_movement_flags : Array[GFXMaskedFlagTexture] +var _pop_list_size_change_texture_rects : Array[GUITextureRect] var _pop_list_size_change_icons : Array[GFXSpriteTexture] var _pop_list_literacy_labels : Array[GUILabel] @@ -321,24 +323,42 @@ func _setup_pop_list() -> void: _pop_list_size_labels.push_back(GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_size"))) var pop_type_button : GUIButton = GUINode.get_gui_button_from_node(pop_row_panel.get_node(^"./pop_type")) - # TODO - open pop details menu on pop type button press _pop_list_type_buttons.push_back(pop_type_button) - - _pop_list_type_icons.push_back(GUINode.get_gfx_sprite_texture_from_node(pop_type_button)) + if pop_type_button: + # TODO - open pop details menu on pop type button press + _pop_list_type_icons.push_back(GUINode.get_gfx_sprite_texture_from_node(pop_type_button)) + else: + _pop_list_type_icons.push_back(null) _pop_list_producing_icons.push_back(GUINode.get_gfx_sprite_texture_from_node(pop_row_panel.get_node(^"./pop_producing_icon"))) var culture_label : GUILabel = GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_nation")) _pop_list_culture_labels.push_back(culture_label) - - _pop_list_religion_icons.push_back(GUINode.get_gfx_sprite_texture_from_node(pop_row_panel.get_node(^"./pop_religion"))) + if culture_label: + culture_label.set_mouse_filter(MOUSE_FILTER_PASS) + + var religion_texture_rect : GUITextureRect = GUINode.get_gui_texture_rect_from_node(pop_row_panel.get_node(^"./pop_religion")) + _pop_list_religion_texture_rects.push_back(religion_texture_rect) + if religion_texture_rect: + religion_texture_rect.set_mouse_filter(MOUSE_FILTER_PASS) + _pop_list_religion_icons.push_back(GUINode.get_gfx_sprite_texture_from_node(religion_texture_rect)) + else: + _pop_list_religion_icons.push_back(null) var location_label : GUILabel = GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_location")) _pop_list_location_labels.push_back(location_label) + if location_label: + location_label.set_mouse_filter(MOUSE_FILTER_PASS) - _pop_list_militancy_labels.push_back(GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_mil"))) + var militancy_label : GUILabel = GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_mil")) + _pop_list_militancy_labels.push_back(militancy_label) + if militancy_label: + militancy_label.set_mouse_filter(MOUSE_FILTER_PASS) - _pop_list_consciousness_labels.push_back(GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_con"))) + var consciousness_label : GUILabel = GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_con")) + _pop_list_consciousness_labels.push_back(consciousness_label) + if consciousness_label: + consciousness_label.set_mouse_filter(MOUSE_FILTER_PASS) _pop_list_ideology_charts.push_back(GUINode.get_gfx_pie_chart_texture_from_node(pop_row_panel.get_node(^"./pop_ideology"))) @@ -346,17 +366,20 @@ func _setup_pop_list() -> void: _pop_list_unemployment_progressbars.push_back(GUINode.get_progress_bar_from_node(pop_row_panel.get_node(^"./pop_unemployment_bar"))) - _pop_list_cash_labels.push_back(GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_cash"))) + var cash_label : GUILabel = GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_cash")) + _pop_list_cash_labels.push_back(cash_label) + if cash_label: + cash_label.set_mouse_filter(MOUSE_FILTER_PASS) var pop_list_life_needs_progressbar : TextureProgressBar = GUINode.get_progress_bar_from_node(pop_row_panel.get_node(^"./lifeneed_progress")) + _pop_list_life_needs_progressbars.push_back(pop_list_life_needs_progressbar) if pop_list_life_needs_progressbar: pop_list_life_needs_progressbar.position += Vector2(1, 0) - _pop_list_life_needs_progressbars.push_back(pop_list_life_needs_progressbar) var pop_list_everyday_needs_progressbar : TextureProgressBar = GUINode.get_progress_bar_from_node(pop_row_panel.get_node(^"./eveneed_progress")) + _pop_list_everyday_needs_progressbars.push_back(pop_list_everyday_needs_progressbar) if pop_list_everyday_needs_progressbar: pop_list_everyday_needs_progressbar.position += Vector2(1, 0) - _pop_list_everyday_needs_progressbars.push_back(pop_list_everyday_needs_progressbar) _pop_list_luxury_needs_progressbars.push_back(GUINode.get_progress_bar_from_node(pop_row_panel.get_node(^"./luxneed_progress"))) @@ -388,9 +411,18 @@ func _setup_pop_list() -> void: else: _pop_list_national_movement_flags.push_back(null) - _pop_list_size_change_icons.push_back(GUINode.get_gfx_sprite_texture_from_node(pop_row_panel.get_node(^"./growth_indicator"))) + var size_change_texture_rect : GUITextureRect = GUINode.get_gui_texture_rect_from_node(pop_row_panel.get_node(^"./growth_indicator")) + _pop_list_size_change_texture_rects.push_back(size_change_texture_rect) + if size_change_texture_rect: + size_change_texture_rect.set_mouse_filter(MOUSE_FILTER_PASS) + _pop_list_size_change_icons.push_back(GUINode.get_gfx_sprite_texture_from_node(size_change_texture_rect)) + else: + _pop_list_size_change_icons.push_back(null) - _pop_list_literacy_labels.push_back(GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_literacy"))) + var literacy_label : GUILabel = GUINode.get_gui_label_from_node(pop_row_panel.get_node(^"./pop_literacy")) + _pop_list_literacy_labels.push_back(literacy_label) + if literacy_label: + literacy_label.set_mouse_filter(MOUSE_FILTER_PASS) func _notification(what : int) -> void: match what: @@ -545,6 +577,10 @@ func _update_distributions(): var colour_icon_rect : GUITextureRect = GUINode.get_gui_texture_rect_from_node(child.get_node(^"./legend_color")) if colour_icon_rect: colour_icon_rect.set_modulate(distribution_row[slice_colour_key]) + colour_icon_rect.set_mouse_filter(MOUSE_FILTER_PASS) + colour_icon_rect.set_tooltip_string_and_substitution_dict("§Y$ID$§!: $PC$%", { + "ID": distribution_row[slice_identifier_key], "PC": GUINode.float_to_string_dp(distribution_row[slice_weight_key] * 100.0, 2) + }) var identifier_label : GUILabel = GUINode.get_gui_label_from_node(child.get_node(^"./legend_title")) if identifier_label: @@ -592,18 +628,29 @@ func _update_pop_list() -> void: if _pop_list_size_labels[index]: _pop_list_size_labels[index].set_text(GUINode.int_to_string_suffixed(pop_row[pop_size_key])) - if _pop_list_type_icons[index]: - _pop_list_type_icons[index].set_icon_index(pop_row[pop_type_icon_key]) + if _pop_list_type_buttons[index]: + # TODO - replace with actual poptype + _pop_list_type_buttons[index].set_tooltip_string("Pop Type #%d" % pop_row[pop_type_icon_key]) + if _pop_list_type_icons[index]: + _pop_list_type_icons[index].set_icon_index(pop_row[pop_type_icon_key]) if _pop_list_culture_labels[index]: _pop_list_culture_labels[index].set_text(pop_row[pop_culture_key]) - if _pop_list_religion_icons[index]: - _pop_list_religion_icons[index].set_icon_index(pop_row[pop_religion_icon_key]) + _pop_list_culture_labels[index].set_tooltip_string("NO_ASSIM_NOW") + if _pop_list_religion_texture_rects[index]: + # TODO - replace with actual religion + _pop_list_religion_texture_rects[index].set_tooltip_string("Religion #%d" % pop_row[pop_religion_icon_key]) + if _pop_list_religion_icons[index]: + _pop_list_religion_icons[index].set_icon_index(pop_row[pop_religion_icon_key]) if _pop_list_location_labels[index]: - _pop_list_location_labels[index].set_text(GUINode.format_province_name(pop_row.get(pop_location_key, ""))) + var province_name : String = GUINode.format_province_name(pop_row.get(pop_location_key, "")) + _pop_list_location_labels[index].set_text(province_name) + _pop_list_location_labels[index].set_tooltip_string(province_name) if _pop_list_militancy_labels[index]: _pop_list_militancy_labels[index].set_text(GUINode.float_to_string_dp(pop_row[pop_militancy_key], 2)) + _pop_list_militancy_labels[index].set_tooltip_string("POP_MIL_TOTAL") if _pop_list_consciousness_labels[index]: _pop_list_consciousness_labels[index].set_text(GUINode.float_to_string_dp(pop_row[pop_consciousness_key], 2)) + _pop_list_consciousness_labels[index].set_tooltip_string("POP_CON_TOTAL") if _pop_list_ideology_charts[index]: _pop_list_ideology_charts[index].set_slices_array(pop_row[pop_ideology_key]) if _pop_list_issues_charts[index]: @@ -611,7 +658,10 @@ func _update_pop_list() -> void: if _pop_list_unemployment_progressbars[index]: _pop_list_unemployment_progressbars[index].set_value_no_signal(pop_row[pop_unemployment_key]) if _pop_list_cash_labels[index]: - _pop_list_cash_labels[index].set_text(GUINode.float_to_string_dp(pop_row[pop_cash_key], 2)) + _pop_list_cash_labels[index].set_text("%s¤" % GUINode.float_to_string_dp(pop_row[pop_cash_key], 2)) + _pop_list_cash_labels[index].set_tooltip_string_and_substitution_dict("POP_DAILY_MONEY", { + "VAL": GUINode.float_to_string_dp(1.23, 2) + }) if _pop_list_life_needs_progressbars[index]: _pop_list_life_needs_progressbars[index].set_value_no_signal(pop_row[pop_life_needs_key]) if _pop_list_everyday_needs_progressbars[index]: @@ -635,10 +685,13 @@ func _update_pop_list() -> void: if _pop_list_national_movement_texture_rects[index]: _pop_list_national_movement_texture_rects[index].hide() - if _pop_list_size_change_icons[index]: - _pop_list_size_change_icons[index].set_icon_index(get_growth_icon_index(pop_row[pop_size_change_key])) + if _pop_list_size_change_texture_rects[index]: + _pop_list_size_change_texture_rects[index].set_tooltip_string("POPULATION_CHANGED_BY") + if _pop_list_size_change_icons[index]: + _pop_list_size_change_icons[index].set_icon_index(get_growth_icon_index(pop_row[pop_size_change_key])) if _pop_list_literacy_labels[index]: _pop_list_literacy_labels[index].set_text("%s%%" % GUINode.float_to_string_dp(pop_row[pop_literacy_key], 2)) + _pop_list_literacy_labels[index].set_tooltip_string("LIT_CHANGE") _pop_list_rows[index].show() else: diff --git a/game/src/Game/GameSession/Tooltip.gd b/game/src/Game/GameSession/Tooltip.gd new file mode 100644 index 00000000..c110e2ef --- /dev/null +++ b/game/src/Game/GameSession/Tooltip.gd @@ -0,0 +1,23 @@ +extends GUINode + +var _tooltip_label : GUILabel + +func _ready() -> void: + add_gui_element("core", "ToolTip") + + _tooltip_label = get_gui_label_from_nodepath(^"./ToolTip") + if _tooltip_label: + _tooltip_label.set_auto_adjust_to_content_size(true) + + MenuSingleton.update_tooltip.connect(update_tooltip) + + hide() + +func update_tooltip(text : String, substitution_dict : Dictionary, position : Vector2) -> void: + if text: + _tooltip_label.set_text(text) + _tooltip_label.set_substitution_dict(substitution_dict) + _tooltip_label.set_position(position) + show() + else: + hide() diff --git a/game/src/Game/GameSession/Topbar.gd b/game/src/Game/GameSession/Topbar.gd index 78ba9b2b..fc140ab0 100644 --- a/game/src/Game/GameSession/Topbar.gd +++ b/game/src/Game/GameSession/Topbar.gd @@ -1,6 +1,7 @@ extends GUINode # Country info +var _country_flag_button : GUIButton var _country_flag_texture : GFXMaskedFlagTexture var _country_flag_overlay_texture : GFXSpriteTexture var _country_name_label : GUILabel @@ -16,6 +17,8 @@ var _country_colonial_power_label : GUILabel # Time controls var _speed_up_button : GUIButton var _speed_down_button : GUIButton +var _pause_bg_button : GUIButton +var _speed_indicator_button : GUIButton var _speed_indicator_texture : GFXSpriteTexture var _date_label : GUILabel @@ -87,45 +90,63 @@ func _ready() -> void: ]) # Disables all consuming invisible panel - var topbar := get_panel_from_nodepath(^"./topbar") + var topbar : Panel = get_panel_from_nodepath(^"./topbar") if topbar: topbar.mouse_filter = Control.MOUSE_FILTER_IGNORE set_click_mask_from_nodepaths([^"./topbar/topbar_bg", ^"./topbar/topbar_paper"]) # Country info - var country_flag_button = get_gui_button_from_nodepath(^"./topbar/player_flag") - if country_flag_button: - country_flag_button.pressed.connect( + _country_flag_button = get_gui_button_from_nodepath(^"./topbar/player_flag") + if _country_flag_button: + _country_flag_button.pressed.connect( func() -> void: # TODO - open the diplomacy menu on the Wars tab Events.NationManagementScreens.open_nation_management_screen(NationManagement.Screen.DIPLOMACY) ) - _country_flag_texture = GUINode.get_gfx_masked_flag_texture_from_node(country_flag_button) + _country_flag_texture = GUINode.get_gfx_masked_flag_texture_from_node(_country_flag_button) _country_flag_overlay_texture = get_gfx_sprite_texture_from_nodepath(^"./topbar/topbar_flag_overlay") _country_name_label = get_gui_label_from_nodepath(^"./topbar/CountryName") _country_rank_label = get_gui_label_from_nodepath(^"./topbar/nation_totalrank") + if _country_rank_label: + _country_rank_label.set_mouse_filter(MOUSE_FILTER_PASS) _country_prestige_label = get_gui_label_from_nodepath(^"./topbar/country_prestige") + if _country_prestige_label: + _country_prestige_label.set_mouse_filter(MOUSE_FILTER_PASS) _country_prestige_rank_label = get_gui_label_from_nodepath(^"./topbar/selected_prestige_rank") + if _country_prestige_rank_label: + _country_prestige_rank_label.set_mouse_filter(MOUSE_FILTER_PASS) _country_industrial_power_label = get_gui_label_from_nodepath(^"./topbar/country_economic") + if _country_industrial_power_label: + _country_industrial_power_label.set_mouse_filter(MOUSE_FILTER_PASS) _country_industrial_power_rank_label = get_gui_label_from_nodepath(^"./topbar/selected_industry_rank") + if _country_industrial_power_rank_label: + _country_industrial_power_rank_label.set_mouse_filter(MOUSE_FILTER_PASS) _country_military_power_label = get_gui_label_from_nodepath(^"./topbar/country_military") + if _country_military_power_label: + _country_military_power_label.set_mouse_filter(MOUSE_FILTER_PASS) _country_military_power_rank_label = get_gui_label_from_nodepath(^"./topbar/selected_military_rank") + if _country_military_power_rank_label: + _country_military_power_rank_label.set_mouse_filter(MOUSE_FILTER_PASS) _country_colonial_power_label = get_gui_label_from_nodepath(^"./topbar/country_colonial_power") + if _country_colonial_power_label: + _country_colonial_power_label.set_mouse_filter(MOUSE_FILTER_PASS) # Time controls _speed_up_button = get_gui_button_from_nodepath(^"./topbar/button_speedup") if _speed_up_button: _speed_up_button.pressed.connect(_on_increase_speed_button_pressed) + _speed_up_button.set_tooltip_string("TOPBAR_INC_SPEED") _speed_down_button = get_gui_button_from_nodepath(^"./topbar/button_speeddown") if _speed_down_button: _speed_down_button.pressed.connect(_on_decrease_speed_button_pressed) - var pause_bg_button : GUIButton = get_gui_button_from_nodepath(^"./topbar/pause_bg") - if pause_bg_button: - pause_bg_button.pressed.connect(_on_play_pause_button_pressed) - var speed_indicator_button = get_gui_button_from_nodepath(^"./topbar/speed_indicator") - if speed_indicator_button: - speed_indicator_button.pressed.connect(_on_play_pause_button_pressed) - _speed_indicator_texture = GUINode.get_gfx_sprite_texture_from_node(speed_indicator_button) + _speed_down_button.set_tooltip_string("TOPBAR_DEC_SPEED") + _pause_bg_button = get_gui_button_from_nodepath(^"./topbar/pause_bg") + if _pause_bg_button: + _pause_bg_button.pressed.connect(_on_play_pause_button_pressed) + _speed_indicator_button = get_gui_button_from_nodepath(^"./topbar/speed_indicator") + if _speed_indicator_button: + _speed_indicator_button.pressed.connect(_on_play_pause_button_pressed) + _speed_indicator_texture = GUINode.get_gfx_sprite_texture_from_node(_speed_indicator_button) _date_label = get_gui_label_from_nodepath(^"./topbar/DateText") # Nation management screens @@ -145,6 +166,7 @@ func _ready() -> void: button.pressed.connect( Events.NationManagementScreens.toggle_nation_management_screen.bind(screen) ) + button.set_tooltip_string(tr("SHORTCUT") + "F3") var icon : GFXSpriteTexture = GUINode.get_gfx_sprite_texture_from_node(button) if icon: _nation_management_buttons[screen] = button @@ -165,10 +187,25 @@ func _ready() -> void: _budget_funds_label = get_gui_label_from_nodepath(^"./topbar/budget_funds") # Technology + var tech_button : GUIButton = _nation_management_buttons[NationManagement.Screen.TECHNOLOGY] _technology_progress_bar = get_progress_bar_from_nodepath(^"./topbar/topbar_tech_progress") + if _technology_progress_bar and tech_button: + _technology_progress_bar.reparent(tech_button) _technology_current_research_label = get_gui_label_from_nodepath(^"./topbar/tech_current_research") + if _technology_current_research_label: + _technology_current_research_label.set_mouse_filter(MOUSE_FILTER_PASS) + if tech_button: + _technology_current_research_label.reparent(tech_button) _technology_literacy_label = get_gui_label_from_nodepath(^"./topbar/tech_literacy_value") + if _technology_literacy_label: + _technology_literacy_label.set_mouse_filter(MOUSE_FILTER_PASS) + if tech_button: + _technology_literacy_label.reparent(tech_button) _technology_research_points_label = get_gui_label_from_nodepath(^"./topbar/topbar_researchpoints_value") + if _technology_research_points_label: + _technology_research_points_label.set_mouse_filter(MOUSE_FILTER_PASS) + if tech_button: + _technology_research_points_label.reparent(tech_button) # Politics _politics_party_icon = get_gui_texture_rect_from_nodepath(^"./topbar/politics_party_icon") @@ -249,27 +286,43 @@ func _notification(what : int) -> void: match what: NOTIFICATION_TRANSLATION_CHANGED: _update_info() + _update_speed_controls() func _update_info() -> void: # Placeholder data const player_country : String = "ENG" + const player_rank : int = 0 + + const RANK_NAMES : PackedStringArray = [ + "DIPLOMACY_GREATNATION_STATUS", + "DIPLOMACY_COLONIALNATION_STATUS", + "DIPLOMACY_CIVILIZEDNATION_STATUS", + "DIPLOMACY_UNCIVILIZEDNATION_STATUS" + ] ## Country info - if _country_flag_texture: - _country_flag_texture.set_flag_country_name(player_country) + if _country_flag_button: + _country_flag_button.set_tooltip_string_and_substitution_dict("PLAYER_COUNTRY_TOPBAR_RANK", { + "NAME": player_country, "RANK": RANK_NAMES[player_rank] + }) + if _country_flag_texture: + _country_flag_texture.set_flag_country_name(player_country) if _country_flag_overlay_texture: # 1 - Great Power # 2 - Secondary Power # 3 - Civilised # 4 - Uncivilised - _country_flag_overlay_texture.set_icon_index(1) + _country_flag_overlay_texture.set_icon_index(1 + player_rank) if _country_name_label: _country_name_label.set_text(player_country) if _country_rank_label: _country_rank_label.set_text(str(1)) + _country_rank_label.set_tooltip_string_and_substitution_dict("PLAYER_COUNTRY_TOPBAR_RANK", { + "NAME": player_country, "RANK": RANK_NAMES[player_rank] + }) if _country_prestige_label: _country_prestige_label.set_text(str(11)) @@ -285,9 +338,11 @@ func _update_info() -> void: if _country_military_power_label: _country_military_power_label.set_text(str(33)) + _country_military_power_label.set_tooltip_string("RANK_MILITARY") if _country_military_power_rank_label: _country_military_power_rank_label.set_text(str(3)) + _country_military_power_rank_label.set_tooltip_string("RANK_MILITARY") if _country_colonial_power_label: var available_colonial_power : int = 123 @@ -295,6 +350,7 @@ func _update_info() -> void: _country_colonial_power_label.set_text( "§%s%s§!/%s" % ["W" if available_colonial_power > 0 else "R", available_colonial_power, total_colonial_power] ) + _country_colonial_power_label.set_tooltip_string("COLONIAL_POINTS") ## Time control if _date_label: @@ -331,12 +387,19 @@ func _update_info() -> void: if _technology_current_research_label: # TODO - set current research or "unciv_nation" (in red) if uncivilised _technology_current_research_label.set_text("TB_TECH_NO_CURRENT") + _technology_current_research_label.set_tooltip_string("TECHNOLOGYVIEW_NO_RESEARCH_TOOLTIP") if _technology_literacy_label: - _technology_literacy_label.set_text("§Y%s§W%%" % GUINode.float_to_string_dp(80.0, 1)) + var literacy_float : float = 80.0 + var literacy_string : String = GUINode.float_to_string_dp(80.0, 1) + _technology_literacy_label.set_text("§Y%s§W%%" % literacy_string) + _technology_literacy_label.set_tooltip_string_and_substitution_dict("TOPBAR_AVG_LITERACY", { "AVG": literacy_string }) if _technology_research_points_label: _technology_research_points_label.set_text("§Y%s" % GUINode.float_to_string_dp(10.0, 2)) + _technology_research_points_label.set_tooltip_string_and_substitution_dict("TECH_DAILY_RESEARCHPOINTS_TOOLTIP", { + "POPTYPE": "Clergymen", "VALUE": GUINode.float_to_string_dp(1.42, 2) + }) ## Politics if _politics_party_icon: @@ -429,6 +492,9 @@ func _update_info() -> void: _military_leadership_points_label.set_text("§Y%d" % 15) func _update_speed_controls() -> void: + var paused : bool = MenuSingleton.is_paused() + var speed : int = MenuSingleton.get_speed() + # TODO - decide whether to disable these or not # (they don't appear to get disabled in the base game) #if _speed_up_button: @@ -437,10 +503,28 @@ func _update_speed_controls() -> void: #if _speed_down_button: # _speed_down_button.disabled = not MenuSingleton.can_decrease_speed() + if _pause_bg_button: + _pause_bg_button.set_tooltip_string("TOPBAR_DATE_IS_PAUSED" if paused else "TOPBAR_DATE") + + if _speed_indicator_button: + if paused: + _speed_indicator_button.set_tooltip_string("TOPBAR_PAUSE_INDICATOR") + else: + const SPEED_NAMES : PackedStringArray = [ + "SLOWEST_SPEED", + "SLOW_SPEED", + "NORMAL_SPEED", + "FAST_SPEED", + "FASTEST_SPEED" + ] + _speed_indicator_button.set_tooltip_string_and_substitution_dict( + "TOPBAR_SPEED_INDICATOR", { "SPEED": SPEED_NAMES[speed] if speed < SPEED_NAMES.size() else str(speed) } + ) + if _speed_indicator_texture: var index : int = 1 - if not MenuSingleton.is_paused(): - index += MenuSingleton.get_speed() + 1 + if not paused: + index += speed + 1 _speed_indicator_texture.set_icon_index(index) # REQUIREMENTS: diff --git a/game/src/Game/GameStart.tscn b/game/src/Game/GameStart.tscn index 3b3a41fb..f3678cfa 100644 --- a/game/src/Game/GameStart.tscn +++ b/game/src/Game/GameStart.tscn @@ -57,7 +57,7 @@ process_mode = 2 disable_3d = true title = "VIC2_DIR_DIALOG_TITLE" size = Vector2i(935, 159) -ok_button_text = "VIC2_DIR_DIALOG_SELECT" +ok_button_text = "Select Current Folder" cancel_button_text = "VIC2_DIR_DIALOG_CANCEL" mode_overrides_title = false file_mode = 2 diff --git a/game/src/Game/MusicConductor/MusicConductor.gd b/game/src/Game/MusicConductor/MusicConductor.gd index 7103aff1..0354b35c 100644 --- a/game/src/Game/MusicConductor/MusicConductor.gd +++ b/game/src/Game/MusicConductor/MusicConductor.gd @@ -67,7 +67,7 @@ func toggle_play_pause() -> void: func start_current_song() -> void: _audio_stream_player.stream = _available_songs[_selected_track].song_stream - _audio_stream_player.play() + #_audio_stream_player.play() song_started.emit(_selected_track) # REQUIREMENTS