diff options
Diffstat (limited to 'game/src/Game/GameSession')
16 files changed, 1192 insertions, 0 deletions
diff --git a/game/src/Game/GameSession/GameSession.gd b/game/src/Game/GameSession/GameSession.gd new file mode 100644 index 0000000..5bb1c2f --- /dev/null +++ b/game/src/Game/GameSession/GameSession.gd @@ -0,0 +1,16 @@ +extends Control + +@export var _game_session_menu : Control + +func _ready(): + Events.Options.load_settings_from_file() + if GameSingleton.setup_game() != OK: + push_error("Failed to setup game") + +func _process(delta : float): + GameSingleton.try_tick() + +# REQUIREMENTS: +# * SS-42 +func _on_game_session_menu_button_pressed() -> void: + _game_session_menu.visible = !_game_session_menu.visible diff --git a/game/src/Game/GameSession/GameSession.tscn b/game/src/Game/GameSession/GameSession.tscn new file mode 100644 index 0000000..188ccde --- /dev/null +++ b/game/src/Game/GameSession/GameSession.tscn @@ -0,0 +1,95 @@ +[gd_scene load_steps=10 format=3 uid="uid://bgnupcshe1m7r"] + +[ext_resource type="Script" path="res://src/GameSession/GameSession.gd" id="1_eklvp"] +[ext_resource type="PackedScene" uid="uid://cvl76duuym1wq" path="res://src/MusicConductor/MusicPlayer.tscn" id="2_kt6aa"] +[ext_resource type="PackedScene" uid="uid://g524p8lr574w" path="res://src/GameSession/MapControlPanel/MapControlPanel.tscn" id="3_afh6d"] +[ext_resource type="PackedScene" uid="uid://dvdynl6eir40o" path="res://src/GameSession/GameSessionMenu.tscn" id="3_bvmqh"] +[ext_resource type="PackedScene" uid="uid://dkehmdnuxih2r" path="res://src/GameSession/MapView.tscn" id="4_xkg5j"] +[ext_resource type="PackedScene" uid="uid://byq323jbel48u" path="res://src/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn" id="5_osjnn"] +[ext_resource type="PackedScene" uid="uid://cnbfxjy1m6wja" path="res://src/OptionMenu/OptionsMenu.tscn" id="6_p5mnx"] +[ext_resource type="PackedScene" uid="uid://dd8k3p7r3huwc" path="res://src/GameSession/GameSpeedPanel.tscn" id="7_myy4q"] +[ext_resource type="PackedScene" uid="uid://d3g6wbvwflmyk" path="res://src/SaveLoadMenu/SaveLoadMenu.tscn" id="8_4g7ko"] + +[node name="GameSession" type="Control" node_paths=PackedStringArray("_game_session_menu")] +editor_description = "SS-102, UI-546" +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_eklvp") +_game_session_menu = NodePath("GameSessionMenu") + +[node name="MapView" parent="." instance=ExtResource("4_xkg5j")] + +[node name="MapControlPanel" parent="." instance=ExtResource("3_afh6d")] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 0 +grow_vertical = 0 + +[node name="ProvinceOverviewPanel" parent="." instance=ExtResource("5_osjnn")] +layout_mode = 1 + +[node name="GameSpeedPanel" parent="." instance=ExtResource("7_myy4q")] +layout_mode = 0 +offset_right = 302.0 +offset_bottom = 31.0 + +[node name="GameSessionMenu" parent="." instance=ExtResource("3_bvmqh")] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -159.0 +offset_top = -165.0 +offset_right = 159.0 +offset_bottom = 165.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="OptionsMenu" parent="." instance=ExtResource("6_p5mnx")] +visible = false +layout_mode = 1 + +[node name="SaveLoadMenu" parent="." instance=ExtResource("8_4g7ko")] +visible = false +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -640.0 +offset_right = 640.0 + +[node name="MusicPlayer" parent="." instance=ExtResource("2_kt6aa")] +layout_mode = 1 +anchors_preset = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -150.0 +offset_right = 0.0 +grow_horizontal = 0 + +[connection signal="map_view_camera_changed" from="MapView" to="MapControlPanel" method="_on_map_view_camera_changed"] +[connection signal="game_session_menu_button_pressed" from="MapControlPanel" to="." method="_on_game_session_menu_button_pressed"] +[connection signal="minimap_clicked" from="MapControlPanel" to="MapView" method="_on_minimap_clicked"] +[connection signal="mouse_entered" from="MapControlPanel" to="MapView" method="_on_mouse_exited_viewport"] +[connection signal="mouse_exited" from="MapControlPanel" to="MapView" method="_on_mouse_entered_viewport"] +[connection signal="zoom_in_button_pressed" from="MapControlPanel" to="MapView" method="zoom_in"] +[connection signal="zoom_out_button_pressed" from="MapControlPanel" to="MapView" method="zoom_out"] +[connection signal="mouse_entered" from="ProvinceOverviewPanel" to="MapView" method="_on_mouse_exited_viewport"] +[connection signal="mouse_exited" from="ProvinceOverviewPanel" to="MapView" method="_on_mouse_entered_viewport"] +[connection signal="load_button_pressed" from="GameSessionMenu" to="SaveLoadMenu" method="show_for_load"] +[connection signal="options_button_pressed" from="GameSessionMenu" to="OptionsMenu" method="show"] +[connection signal="save_button_pressed" from="GameSessionMenu" to="SaveLoadMenu" method="show_for_save"] +[connection signal="back_button_pressed" from="OptionsMenu" to="MapView" method="enable_processing"] +[connection signal="back_button_pressed" from="OptionsMenu" to="OptionsMenu" method="hide"] diff --git a/game/src/Game/GameSession/GameSessionMenu.gd b/game/src/Game/GameSession/GameSessionMenu.gd new file mode 100644 index 0000000..23ef2ef --- /dev/null +++ b/game/src/Game/GameSession/GameSessionMenu.gd @@ -0,0 +1,80 @@ +extends PanelContainer + +@export var _main_menu_scene : PackedScene + +@export var _main_menu_dialog : AcceptDialog +@export var _quit_dialog : AcceptDialog + +var _main_menu_save_button : Button +var _main_menu_save_separator : Control +var _quit_save_button : Button +var _quit_save_separator : Control + +signal save_button_pressed +signal load_button_pressed +signal options_button_pressed + +func _ready() -> void: + _main_menu_save_button = _main_menu_dialog.add_button("DIALOG_SAVE_AND_RESIGN", true, &"save_and_main_menu") + _quit_save_button = _quit_dialog.add_button("DIALOG_SAVE_AND_QUIT", true, &"save_and_quit") + + # Neccessary to center the save buttons and preserve the reference to the separator elements + var dialog_hbox : HBoxContainer = _main_menu_dialog.get_child(2, true) + var index := _main_menu_save_button.get_index(true) + dialog_hbox.move_child(_main_menu_save_button, _main_menu_dialog.get_ok_button().get_index(true)) + dialog_hbox.move_child(_main_menu_dialog.get_ok_button(), index) + _main_menu_save_separator = dialog_hbox.get_child(_main_menu_save_button.get_index(true) - 1) + + dialog_hbox = _quit_dialog.get_child(2, true) + index = _quit_save_button.get_index(true) + dialog_hbox.move_child(_quit_save_button, _quit_dialog.get_ok_button().get_index(true)) + dialog_hbox.move_child(_quit_dialog.get_ok_button(), index) + _quit_save_separator = dialog_hbox.get_child(_quit_save_button.get_index(true) - 1) + +func hide_save_dialog_button() -> void: + _main_menu_save_button.hide() + _main_menu_save_separator.hide() + _quit_save_button.hide() + _quit_save_separator.hide() + +func show_save_dialog_button() -> void: + _main_menu_save_button.show() + _main_menu_save_separator.show() + _quit_save_button.show() + _quit_save_separator.show() + +# REQUIREMENTS: +# * SS-47 +# * UIFUN-69 +func _on_main_menu_confirmed() -> void: + SaveManager.current_session_tag = "" + SaveManager.current_save = null + get_tree().change_scene_to_packed(_main_menu_scene) + +# REQUIREMENTS: +# * SS-48 +# * UIFUN-70 +func _on_quit_confirmed() -> void: + get_tree().quit() + +# REQUIREMENTS: +# * SS-7, SS-46 +# * UIFUN-11 +func _on_options_button_pressed() -> void: + options_button_pressed.emit() + +func _on_main_menu_dialog_custom_action(action) -> void: + match action: + &"save_and_main_menu": + _on_main_menu_confirmed() + +func _on_quit_dialog_custom_action(action : StringName) -> void: + match action: + &"save_and_quit": + _on_quit_confirmed() + +func _on_save_button_pressed(): + save_button_pressed.emit() + +func _on_load_button_pressed(): + load_button_pressed.emit() diff --git a/game/src/Game/GameSession/GameSessionMenu.tscn b/game/src/Game/GameSession/GameSessionMenu.tscn new file mode 100644 index 0000000..025ef3b --- /dev/null +++ b/game/src/Game/GameSession/GameSessionMenu.tscn @@ -0,0 +1,91 @@ +[gd_scene load_steps=4 format=3 uid="uid://dvdynl6eir40o"] + +[ext_resource type="Theme" uid="uid://cqrfmjt5yeti7" path="res://theme/game_session_menu.tres" id="1_2onog"] +[ext_resource type="Script" path="res://src/GameSession/GameSessionMenu.gd" id="1_usq6o"] +[ext_resource type="PackedScene" uid="uid://o4u142w4qkln" path="res://src/GameMenu.tscn" id="2_xi6a4"] + +[node name="GameSessionMenu" type="PanelContainer" node_paths=PackedStringArray("_main_menu_dialog", "_quit_dialog")] +process_mode = 3 +editor_description = "UI-68" +theme = ExtResource("1_2onog") +theme_type_variation = &"SessionPanel" +script = ExtResource("1_usq6o") +_main_menu_scene = ExtResource("2_xi6a4") +_main_menu_dialog = NodePath("MainMenuDialog") +_quit_dialog = NodePath("QuitDialog") + +[node name="ButtonListMargin" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="ButtonList" type="VBoxContainer" parent="ButtonListMargin"] +layout_mode = 2 + +[node name="SaveButton" type="Button" parent="ButtonListMargin/ButtonList"] +editor_description = "UI-69" +layout_mode = 2 +theme_type_variation = &"SessionButton" +text = "GAMESESSIONMENU_SAVE" + +[node name="LoadButton" type="Button" parent="ButtonListMargin/ButtonList"] +editor_description = "UI-70" +layout_mode = 2 +theme_type_variation = &"SessionButton" +text = "GAMESESSIONMENU_LOAD" + +[node name="OptionsButton" type="Button" parent="ButtonListMargin/ButtonList"] +editor_description = "UI-10" +layout_mode = 2 +theme_type_variation = &"SessionButton" +text = "GAMESESSIONMENU_OPTIONS" + +[node name="MainMenuButton" type="Button" parent="ButtonListMargin/ButtonList"] +editor_description = "UI-71" +layout_mode = 2 +theme_type_variation = &"SessionButton" +text = "GAMESESSIONMENU_MAINMENU" + +[node name="QuitButton" type="Button" parent="ButtonListMargin/ButtonList"] +editor_description = "UI-72" +layout_mode = 2 +theme_type_variation = &"SessionButton" +text = "GAMESESSIONMENU_QUIT" + +[node name="CloseSeparator" type="HSeparator" parent="ButtonListMargin/ButtonList"] +layout_mode = 2 +theme_type_variation = &"SessionSeparator" + +[node name="CloseButton" type="Button" parent="ButtonListMargin/ButtonList"] +editor_description = "SS-64, UI-80, UIFUN-79" +layout_mode = 2 +theme_type_variation = &"SessionButton" +text = "GAMESESSIONMENU_CLOSE" + +[node name="MainMenuDialog" type="ConfirmationDialog" parent="."] +disable_3d = true +title = "GAMESESSIONMENU_MAINMENU_DIALOG_TITLE" +size = Vector2i(384, 100) +ok_button_text = "DIALOG_OK" +dialog_text = "GAMESESSIONMENU_MAINMENU_DIALOG_TEXT" +cancel_button_text = "DIALOG_CANCEL" + +[node name="QuitDialog" type="ConfirmationDialog" parent="."] +disable_3d = true +title = "GAMESESSIONMENU_QUIT_DIALOG_TITLE" +ok_button_text = "DIALOG_OK" +dialog_text = "GAMESESSIONMENU_QUIT_DIALOG_TEXT" +cancel_button_text = "DIALOG_CANCEL" + +[connection signal="pressed" from="ButtonListMargin/ButtonList/SaveButton" to="." method="_on_save_button_pressed"] +[connection signal="pressed" from="ButtonListMargin/ButtonList/LoadButton" to="." method="_on_load_button_pressed"] +[connection signal="pressed" from="ButtonListMargin/ButtonList/OptionsButton" to="." method="_on_options_button_pressed"] +[connection signal="pressed" from="ButtonListMargin/ButtonList/MainMenuButton" to="MainMenuDialog" method="popup_centered"] +[connection signal="pressed" from="ButtonListMargin/ButtonList/QuitButton" to="QuitDialog" method="popup_centered"] +[connection signal="pressed" from="ButtonListMargin/ButtonList/CloseButton" to="." method="hide"] +[connection signal="confirmed" from="MainMenuDialog" to="." method="_on_main_menu_confirmed"] +[connection signal="custom_action" from="MainMenuDialog" to="." method="_on_main_menu_dialog_custom_action"] +[connection signal="confirmed" from="QuitDialog" to="." method="_on_quit_confirmed"] +[connection signal="custom_action" from="QuitDialog" to="." method="_on_quit_dialog_custom_action"] diff --git a/game/src/Game/GameSession/GameSpeedPanel.gd b/game/src/Game/GameSession/GameSpeedPanel.gd new file mode 100644 index 0000000..80708b1 --- /dev/null +++ b/game/src/Game/GameSession/GameSpeedPanel.gd @@ -0,0 +1,37 @@ +extends PanelContainer + +#UI-74 UI-75 UI-76 UI-77 + +@export var _longform_date_button : Button +@export var _play_pause_display_button : Button +@export var _decrease_speed_button : Button +@export var _increase_speed_button : Button + +func _ready(): + GameSingleton.state_updated.connect(_update_buttons) + _update_buttons() + +func _update_buttons(): + _play_pause_display_button.text = "⏸ " if GameSingleton.is_paused() else "▶" + + _increase_speed_button.disabled = not GameSingleton.can_increase_speed() + _decrease_speed_button.disabled = not GameSingleton.can_decrease_speed() + + _longform_date_button.text = GameSingleton.get_longform_date() + + +func _on_decrease_speed_button_pressed(): + GameSingleton.decrease_speed() + _update_buttons() + +func _on_increase_speed_button_pressed(): + GameSingleton.increase_speed() + _update_buttons() + +func _on_play_pause_display_button_pressed(): + GameSingleton.toggle_paused() + _update_buttons() + +func _on_longform_date_label_pressed(): + GameSingleton.toggle_paused() + _update_buttons() diff --git a/game/src/Game/GameSession/GameSpeedPanel.tscn b/game/src/Game/GameSession/GameSpeedPanel.tscn new file mode 100644 index 0000000..8a37565 --- /dev/null +++ b/game/src/Game/GameSession/GameSpeedPanel.tscn @@ -0,0 +1,38 @@ +[gd_scene load_steps=2 format=3 uid="uid://dd8k3p7r3huwc"] + +[ext_resource type="Script" path="res://src/GameSession/GameSpeedPanel.gd" id="1_pfs8t"] + +[node name="GameSpeedPanel" type="PanelContainer" node_paths=PackedStringArray("_longform_date_button", "_play_pause_display_button", "_decrease_speed_button", "_increase_speed_button")] +script = ExtResource("1_pfs8t") +_longform_date_button = NodePath("ButtonList/LongformDateButton") +_play_pause_display_button = NodePath("ButtonList/PlayPauseDisplayButton") +_decrease_speed_button = NodePath("ButtonList/DecreaseSpeedButton") +_increase_speed_button = NodePath("ButtonList/IncreaseSpeedButton") + +[node name="ButtonList" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="LongformDateButton" type="Button" parent="ButtonList"] +custom_minimum_size = Vector2(200, 0) +layout_mode = 2 +text = "LONGFORM DATE" + +[node name="PlayPauseDisplayButton" type="Button" parent="ButtonList"] +custom_minimum_size = Vector2(30, 0) +layout_mode = 2 +text = "⏸ " + +[node name="DecreaseSpeedButton" type="Button" parent="ButtonList"] +custom_minimum_size = Vector2(30, 0) +layout_mode = 2 +text = "-" + +[node name="IncreaseSpeedButton" type="Button" parent="ButtonList"] +custom_minimum_size = Vector2(30, 0) +layout_mode = 2 +text = "+" + +[connection signal="pressed" from="ButtonList/LongformDateButton" to="." method="_on_longform_date_label_pressed"] +[connection signal="pressed" from="ButtonList/PlayPauseDisplayButton" to="." method="_on_play_pause_display_button_pressed"] +[connection signal="pressed" from="ButtonList/DecreaseSpeedButton" to="." method="_on_decrease_speed_button_pressed"] +[connection signal="pressed" from="ButtonList/IncreaseSpeedButton" to="." method="_on_increase_speed_button_pressed"] diff --git a/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd new file mode 100644 index 0000000..0cef057 --- /dev/null +++ b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd @@ -0,0 +1,57 @@ +extends PanelContainer + +signal game_session_menu_button_pressed +signal map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2) +signal minimap_clicked(pos_clicked : Vector2) +signal zoom_in_button_pressed +signal zoom_out_button_pressed + +@export var _mapmodes_grid : GridContainer + +var _mapmode_button_group : ButtonGroup + +# REQUIREMENTS: +# * UI-550, UI-552, UI-554, UI-561 +func _add_mapmode_button(identifier : String) -> void: + var button := Button.new() + button.text = identifier + button.tooltip_text = identifier + button.toggle_mode = true + button.button_group = _mapmode_button_group + button.mouse_filter = MOUSE_FILTER_PASS + _mapmodes_grid.add_child(button) + if _mapmode_button_group.get_pressed_button() == null: + button.button_pressed = true + +func _ready(): + _mapmode_button_group = ButtonGroup.new() + _mapmode_button_group.pressed.connect(_mapmode_pressed) + for index in GameSingleton.get_mapmode_count(): + _add_mapmode_button(GameSingleton.get_mapmode_identifier(index)) + +# REQUIREMENTS: +# * UIFUN-10 +func _on_game_session_menu_button_pressed() -> void: + game_session_menu_button_pressed.emit() + +# REQUIREMENTS: +# * SS-76 +# * UIFUN-129, UIFUN-131, UIFUN-133 +func _mapmode_pressed(button : BaseButton) -> void: + GameSingleton.set_mapmode(button.tooltip_text) + +func _on_map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2) -> void: + map_view_camera_changed.emit(near_left, far_left, far_right, near_right) + +func _on_minimap_clicked(pos_clicked : Vector2) -> void: + minimap_clicked.emit(pos_clicked) + +# REQUIREMENTS: +# * UIFUN-269 +func _on_zoom_in_button_pressed() -> void: + zoom_in_button_pressed.emit() + +# REQUIREMENTS: +# * UIFUN-270 +func _on_zoom_out_button_pressed() -> void: + zoom_out_button_pressed.emit() diff --git a/game/src/Game/GameSession/MapControlPanel/MapControlPanel.tscn b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.tscn new file mode 100644 index 0000000..bd4a010 --- /dev/null +++ b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.tscn @@ -0,0 +1,107 @@ +[gd_scene load_steps=7 format=3 uid="uid://g524p8lr574w"] + +[ext_resource type="Script" path="res://src/GameSession/MapControlPanel/MapControlPanel.gd" id="1_ign64"] +[ext_resource type="Shader" path="res://src/GameSession/MapControlPanel/Minimap.gdshader" id="2_rinsg"] +[ext_resource type="Script" path="res://src/GameSession/MapControlPanel/Minimap.gd" id="3_s4dml"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_bhuqb"] +shader = ExtResource("2_rinsg") +shader_parameter/province_shape_subdivisions = null +shader_parameter/selected_index = null + +[sub_resource type="InputEventAction" id="InputEventAction_5nck3"] +action = &"ui_cancel" + +[sub_resource type="Shortcut" id="Shortcut_fc1tk"] +events = [SubResource("InputEventAction_5nck3")] + +[node name="MapControlPanel" type="PanelContainer" node_paths=PackedStringArray("_mapmodes_grid")] +editor_description = "SS-103, UI-548" +mouse_filter = 1 +script = ExtResource("1_ign64") +_mapmodes_grid = NodePath("MapPanelMargin/MapPanelList/MapDisplayList/MapmodesGrid") + +[node name="MapPanelMargin" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="MapPanelList" type="HBoxContainer" parent="MapPanelMargin"] +layout_mode = 2 +theme_override_constants/separation = 6 +alignment = 1 + +[node name="MapDisplayList" type="VBoxContainer" parent="MapPanelMargin/MapPanelList"] +layout_mode = 2 +alignment = 1 + +[node name="MapmodesGrid" type="GridContainer" parent="MapPanelMargin/MapPanelList/MapDisplayList"] +editor_description = "UI-750" +layout_mode = 2 +columns = 11 + +[node name="Minimap" type="PanelContainer" parent="MapPanelMargin/MapPanelList/MapDisplayList"] +editor_description = "UI-549" +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +mouse_filter = 1 + +[node name="MinimapTexture" type="ColorRect" parent="MapPanelMargin/MapPanelList/MapDisplayList/Minimap"] +editor_description = "UI-751, FS-338" +material = SubResource("ShaderMaterial_bhuqb") +layout_mode = 2 +color = Color(0.921569, 0.835294, 0.701961, 1) + +[node name="ViewportQuad" type="Control" parent="MapPanelMargin/MapPanelList/MapDisplayList/Minimap" node_paths=PackedStringArray("_minimap_texture")] +layout_mode = 2 +mouse_filter = 1 +script = ExtResource("3_s4dml") +_minimap_texture = NodePath("../MinimapTexture") + +[node name="AuxiliaryPanel" type="VBoxContainer" parent="MapPanelMargin/MapPanelList"] +editor_description = "UI-761" +layout_mode = 2 + +[node name="GameSessionMenuButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel"] +editor_description = "UI-9" +layout_mode = 2 +mouse_filter = 1 +shortcut = SubResource("Shortcut_fc1tk") +text = "ESC" + +[node name="LedgerButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel"] +editor_description = "UI-860" +layout_mode = 2 +mouse_filter = 1 +text = "L" + +[node name="FindButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel"] +editor_description = "UI-861" +layout_mode = 2 +mouse_filter = 1 +text = "F" + +[node name="ZoomButtonsContainer" type="HBoxContainer" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel"] +layout_mode = 2 +alignment = 1 + +[node name="ZoomInButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel/ZoomButtonsContainer"] +editor_description = "UI-862" +layout_mode = 2 +mouse_filter = 1 +text = "+" + +[node name="ZoomOutButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel/ZoomButtonsContainer"] +editor_description = "UI-863" +layout_mode = 2 +mouse_filter = 1 +text = "-" + +[connection signal="map_view_camera_changed" from="." to="MapPanelMargin/MapPanelList/MapDisplayList/Minimap/ViewportQuad" method="_on_map_view_camera_changed"] +[connection signal="minimap_clicked" from="MapPanelMargin/MapPanelList/MapDisplayList/Minimap/ViewportQuad" to="." method="_on_minimap_clicked"] +[connection signal="pressed" from="MapPanelMargin/MapPanelList/AuxiliaryPanel/GameSessionMenuButton" to="." method="_on_game_session_menu_button_pressed"] +[connection signal="pressed" from="MapPanelMargin/MapPanelList/AuxiliaryPanel/ZoomButtonsContainer/ZoomInButton" to="." method="_on_zoom_in_button_pressed"] +[connection signal="pressed" from="MapPanelMargin/MapPanelList/AuxiliaryPanel/ZoomButtonsContainer/ZoomOutButton" to="." method="_on_zoom_out_button_pressed"] diff --git a/game/src/Game/GameSession/MapControlPanel/Minimap.gd b/game/src/Game/GameSession/MapControlPanel/Minimap.gd new file mode 100644 index 0000000..1f9b75e --- /dev/null +++ b/game/src/Game/GameSession/MapControlPanel/Minimap.gd @@ -0,0 +1,105 @@ +extends Control + +signal minimap_clicked(pos_clicked : Vector2) + +const _action_click : StringName = &"map_click" + +@export var _minimap_texture : Control +var _minimap_shader : ShaderMaterial + +var _viewport_points : PackedVector2Array + +func _ready(): + _minimap_texture.custom_minimum_size = Vector2(GameSingleton.get_aspect_ratio(), 1.0) * 150 + var minimap_material := _minimap_texture.get_material() + if Events.ShaderManager.set_up_shader(minimap_material, false) != OK: + push_error("Failed to set up minimap shader") + else: + _minimap_shader = minimap_material + GameSingleton.province_selected.connect(_on_province_selected) + +func _on_province_selected(index : int) -> void: + if _minimap_shader != null: + _minimap_shader.set_shader_parameter(Events.ShaderManager.param_selected_index, index) + +# REQUIREMENTS +# * SS-80 +# * UI-752 +func _draw() -> void: + if _viewport_points.size() > 1: + draw_multiline(_viewport_points, Color.WHITE, -1) + +# REQUIREMENTS +# * SS-81 +# * UIFUN-127 +func _unhandled_input(event : InputEvent): + if event is InputEventMouse and Input.is_action_pressed(_action_click): + var pos_clicked := get_local_mouse_position() / size - Vector2(0.5, 0.5) + if abs(pos_clicked.x) < 0.5 and abs(pos_clicked.y) < 0.5: + minimap_clicked.emit(pos_clicked) + +# Returns the point on the line going through p and q with the specific x coord +func _intersect_x(p : Vector2, q : Vector2, x : float) -> Vector2: + if p.x == q.x: + return Vector2(x, 0.5 * (p.y + q.y)) + var t := (x - q.x) / (p.x - q.x) + return q + t * (p - q) + +# Returns the point on the line going through p and q with the specific y coord +func _intersect_y(p : Vector2, q : Vector2, y : float) -> Vector2: + if p.y == q.y: + return Vector2(0.5 * (p.x + q.x), y) + var t := (y - q.y) / (p.y - q.y) + return q + t * (p - q) + +const _one_x := Vector2(1, 0) + +func _add_line_looped_over_x(left : Vector2, right : Vector2) -> void: + if left.x < 0: + if right.x < 0: + _viewport_points.push_back(left + _one_x) + _viewport_points.push_back(right + _one_x) + else: + var mid_point := _intersect_x(left, right, 0) + _viewport_points.push_back(mid_point) + _viewport_points.push_back(right) + mid_point.x = 1 + _viewport_points.push_back(left + _one_x) + _viewport_points.push_back(mid_point) + elif right.x > 1: + if left.x > 1: + _viewport_points.push_back(left - _one_x) + _viewport_points.push_back(right - _one_x) + else: + var mid_point := _intersect_x(left, right, 1) + _viewport_points.push_back(left) + _viewport_points.push_back(mid_point) + mid_point.x = 0 + _viewport_points.push_back(mid_point) + _viewport_points.push_back(right - _one_x) + else: + _viewport_points.push_back(left) + _viewport_points.push_back(right) + +# This can break if the viewport is rotated too far! +func _on_map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2) -> void: + # Bound far y coords + if far_left.y < 0: + far_left = _intersect_y(near_left, far_left, 0) + if far_right.y < 0: + far_right = _intersect_y(near_right, far_right, 0) + # Bound near y coords + if near_left.y > 1: + near_left = _intersect_y(near_left, far_left, 1) + if near_right.y > 1: + near_right = _intersect_y(near_right, far_right, 1) + + _viewport_points.clear() + _add_line_looped_over_x(near_left, near_right) + _add_line_looped_over_x(far_left, far_right) + _add_line_looped_over_x(far_left, near_left) + _add_line_looped_over_x(near_right, far_right) + + for i in _viewport_points.size(): + _viewport_points[i] *= size + queue_redraw() diff --git a/game/src/Game/GameSession/MapControlPanel/Minimap.gdshader b/game/src/Game/GameSession/MapControlPanel/Minimap.gdshader new file mode 100644 index 0000000..608abe2 --- /dev/null +++ b/game/src/Game/GameSession/MapControlPanel/Minimap.gdshader @@ -0,0 +1,18 @@ +shader_type canvas_item; + +#include "res://src/GameSession/ProvinceIndexSampler.gdshaderinc" + +// Index of the currently selected province +uniform uint selected_index; + +const vec3 land_colour = vec3(0.5); +const vec3 selected_colour = vec3(1.0, 1.0, 0.0); + +void fragment() { + uvec3 data = read_uvec3(UV); + uint index = uvec2_to_uint(data.rg); + float is_land = float(data.b != 0u); + float is_selected = float(index == selected_index); + COLOR.rgb = mix(COLOR.rgb, land_colour, is_land); + COLOR.rgb = mix(COLOR.rgb, selected_colour, is_selected); +} diff --git a/game/src/Game/GameSession/MapView.gd b/game/src/Game/GameSession/MapView.gd new file mode 100644 index 0000000..ced8bb7 --- /dev/null +++ b/game/src/Game/GameSession/MapView.gd @@ -0,0 +1,242 @@ +extends Node3D + +signal map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2) + +const _action_north : StringName = &"map_north" +const _action_east : StringName = &"map_east" +const _action_south : StringName = &"map_south" +const _action_west : StringName = &"map_west" +const _action_zoom_in : StringName = &"map_zoom_in" +const _action_zoom_out : StringName = &"map_zoom_out" +const _action_drag : StringName = &"map_drag" +const _action_click : StringName = &"map_click" + +@export var _camera : Camera3D + +@export var _cardinal_move_speed : float = 1.0 +@export var _edge_move_threshold: float = 0.01 +@export var _edge_move_speed: float = 2.5 +var _drag_anchor : Vector2 +var _drag_active : bool = false + +var _mouse_over_viewport : bool = true + +@export var _zoom_target_min : float = 0.05 +@export var _zoom_target_max : float = 5.0 +@export var _zoom_target_step : float = 0.1 +@export var _zoom_epsilon : float = _zoom_target_step * 0.1 +@export var _zoom_speed : float = 5.0 +@export var _zoom_target : float = 1.0: + get: return _zoom_target + set(v): _zoom_target = clamp(v, _zoom_target_min, _zoom_target_max) + +@export var _map_mesh_instance : MeshInstance3D +var _map_mesh : MapMesh +var _map_shader_material : ShaderMaterial +var _map_mesh_corner : Vector2 +var _map_mesh_dims : Vector2 + +var _mouse_pos_viewport : Vector2 = Vector2(0.5, 0.5) +var _mouse_pos_map : Vector2 = Vector2(0.5, 0.5) +var _viewport_dims : Vector2 = Vector2(1, 1) + +# ??? Strange Godot/GDExtension Bug ??? +# Upon first opening a clone of this repo with the Godot Editor, +# if GameSingleton.get_province_index_image is called before MapMesh +# is referenced in the script below, then the editor will crash due +# to a failed HashMap lookup. I'm not sure if this is a bug in the +# editor, GDExtension, my own extension, or a combination of them. +# This was an absolute pain to track down. --- hop311 +func _ready(): + if _camera == null: + push_error("MapView's _camera variable hasn't been set!") + return + _zoom_target = _camera.position.y + if _map_mesh_instance == null: + push_error("MapView's _map_mesh variable hasn't been set!") + return + + # Shader Material + var map_material := _map_mesh_instance.get_active_material(0) + if Events.ShaderManager.set_up_shader(map_material, true) != OK: + push_error("Failed to set up map shader") + return + _map_shader_material = map_material + + if not _map_mesh_instance.mesh is MapMesh: + push_error("Invalid map mesh class: ", _map_mesh_instance.mesh.get_class(), "(expected MapMesh)") + return + _map_mesh = _map_mesh_instance.mesh + + # Set map mesh size and get bounds + const pixels_per_terrain_tile : float = 32.0 + _map_shader_material.set_shader_parameter(Events.ShaderManager.param_terrain_tile_factor, + float(GameSingleton.get_height()) / pixels_per_terrain_tile) + var map_mesh_aabb := _map_mesh.get_core_aabb() * _map_mesh_instance.transform + _map_mesh_corner = Vector2( + min(map_mesh_aabb.position.x, map_mesh_aabb.end.x), + min(map_mesh_aabb.position.z, map_mesh_aabb.end.z) + ) + _map_mesh_dims = abs(Vector2( + map_mesh_aabb.position.x - map_mesh_aabb.end.x, + map_mesh_aabb.position.z - map_mesh_aabb.end.z + )) + + GameSingleton.province_selected.connect(_on_province_selected) + +func _notification(what : int): + match what: + NOTIFICATION_WM_MOUSE_ENTER: # Mouse inside window + _on_mouse_entered_viewport() + NOTIFICATION_WM_MOUSE_EXIT: # Mouse out of window + _on_mouse_exited_viewport() + +func _world_to_map_coords(pos : Vector3) -> Vector2: + return (Vector2(pos.x, pos.z) - _map_mesh_corner) / _map_mesh_dims + +func _viewport_to_map_coords(pos_viewport : Vector2) -> Vector2: + var ray_origin := _camera.project_ray_origin(pos_viewport) + var ray_normal := _camera.project_ray_normal(pos_viewport) + # Plane with normal (0,1,0) facing upwards, at a distance 0 from the origin + var intersection = Plane(0, 1, 0, 0).intersects_ray(ray_origin, ray_normal) + if typeof(intersection) == TYPE_VECTOR3: + return _world_to_map_coords(intersection as Vector3) + else: + # Normals parallel to the xz-plane could cause null intersections, + # but the camera's orientation should prevent such normals + push_error("Invalid intersection: ", intersection) + return Vector2(0.5, 0.5) + +func zoom_in() -> void: + _zoom_target -= _zoom_target_step + +func zoom_out() -> void: + _zoom_target += _zoom_target_step + +func _on_province_selected(index : int) -> void: + _map_shader_material.set_shader_parameter(Events.ShaderManager.param_selected_index, index) + +# REQUIREMENTS +# * SS-31 +func _unhandled_input(event : InputEvent): + if _mouse_over_viewport and event.is_action_pressed(_action_click): + # Check if the mouse is outside of bounds + if _map_mesh.is_valid_uv_coord(_mouse_pos_map): + GameSingleton.set_selected_province(GameSingleton.get_province_index_from_uv_coords(_mouse_pos_map)) + else: + print("Clicked outside the map!") + elif event.is_action_pressed(_action_drag): + if _drag_active: + push_warning("Drag being activated while already active!") + _drag_active = true + _drag_anchor = _mouse_pos_map + elif event.is_action_released(_action_drag): + if not _drag_active: + push_warning("Drag being deactivated while already not active!") + _drag_active = false + elif event.is_action_pressed(_action_zoom_in, true): + zoom_in() + elif event.is_action_pressed(_action_zoom_out, true): + zoom_out() + +func _physics_process(delta : float): + _mouse_pos_viewport = get_viewport().get_mouse_position() + _viewport_dims = Vector2(Resolution.get_current_resolution()) + # Process movement + _movement_process(delta) + # Keep within map bounds + _clamp_over_map() + # Process zooming + _zoom_process(delta) + # Orient based on height + _update_orientation() + # Update viewport on minimap + _update_minimap_viewport() + # Calculate where the mouse lies on the map + _update_mouse_map_position() + +# REQUIREMENTS +# * UIFUN-124 +func _movement_process(delta : float) -> void: + var direction : Vector2 + if _drag_active: + direction = (_drag_anchor - _mouse_pos_map) * _map_mesh_dims + else: + direction = _edge_scrolling_vector() + _cardinal_movement_vector() + # Scale movement speed with height + direction *= _camera.position.y * delta + _camera.position += Vector3(direction.x, 0, direction.y) + +# REQUIREMENTS +# * UIFUN-125 +func _edge_scrolling_vector() -> Vector2: + if not _mouse_over_viewport: + return Vector2() + var mouse_vector := _mouse_pos_viewport * GuiScale.get_current_guiscale() / _viewport_dims - Vector2(0.5, 0.5) + if abs(mouse_vector.x) < 0.5 - _edge_move_threshold and abs(mouse_vector.y) < 0.5 - _edge_move_threshold: + mouse_vector *= 0 + return mouse_vector * _edge_move_speed + +# REQUIREMENTS +# * SS-75 +func _cardinal_movement_vector() -> Vector2: + var move := Vector2( + float(Input.is_action_pressed(_action_east)) - float(Input.is_action_pressed(_action_west)), + float(Input.is_action_pressed(_action_south)) - float(Input.is_action_pressed(_action_north)) + ) + return move * _cardinal_move_speed + +func _clamp_over_map() -> void: + _camera.position.x = _map_mesh_corner.x + fposmod(_camera.position.x - _map_mesh_corner.x, _map_mesh_dims.x) + _camera.position.z = clamp(_camera.position.z, _map_mesh_corner.y, _map_mesh_corner.y + _map_mesh_dims.y) + +# REQUIREMENTS +# * SS-74 +# * UIFUN-123 +func _zoom_process(delta : float) -> void: + var height := _camera.position.y + var zoom := _zoom_target - height + height += zoom * _zoom_speed * delta + var new_zoom := _zoom_target - height + # Set to target if height is within _zoom_epsilon of it or has overshot past it + if abs(new_zoom) < _zoom_epsilon or sign(zoom) != sign(new_zoom): + height = _zoom_target + _camera.position.y = height + +func _update_orientation() -> void: + var dir := Vector3(0, -1, -exp(-_camera.position.y - 1)) + _camera.look_at(_camera.position + dir) + +func _update_minimap_viewport() -> void: + var near_left := _viewport_to_map_coords(Vector2(0, _viewport_dims.y)) + var far_left := _viewport_to_map_coords(Vector2(0, 0)) + var far_right := _viewport_to_map_coords(Vector2(_viewport_dims.x, 0)) + var near_right := _viewport_to_map_coords(_viewport_dims) + map_view_camera_changed.emit(near_left, far_left, far_right, near_right) + +func _update_mouse_map_position() -> void: + _mouse_pos_map = _viewport_to_map_coords(_mouse_pos_viewport) + var hover_index := GameSingleton.get_province_index_from_uv_coords(_mouse_pos_map) + if _mouse_over_viewport: + _map_shader_material.set_shader_parameter(Events.ShaderManager.param_hover_index, hover_index) + +func _on_mouse_entered_viewport(): + _mouse_over_viewport = true + +func _on_mouse_exited_viewport(): + _mouse_over_viewport = false + _map_shader_material.set_shader_parameter(Events.ShaderManager.param_hover_index, 0) + +func _on_minimap_clicked(pos_clicked : Vector2): + pos_clicked *= _map_mesh_dims + _camera.position.x = pos_clicked.x + _camera.position.z = pos_clicked.y + _clamp_over_map() + +func enable_processing() -> void: + set_process_unhandled_input(true) + set_physics_process(true) + +func disable_processing() -> void: + set_process_unhandled_input(false) + set_physics_process(false) diff --git a/game/src/Game/GameSession/MapView.tscn b/game/src/Game/GameSession/MapView.tscn new file mode 100644 index 0000000..fa6ffcd --- /dev/null +++ b/game/src/Game/GameSession/MapView.tscn @@ -0,0 +1,30 @@ +[gd_scene load_steps=5 format=3 uid="uid://dkehmdnuxih2r"] + +[ext_resource type="Script" path="res://src/GameSession/MapView.gd" id="1_exccw"] +[ext_resource type="Shader" path="res://src/GameSession/TerrainMap.gdshader" id="1_upocn"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_tayeg"] +render_priority = 0 +shader = ExtResource("1_upocn") +shader_parameter/province_shape_subdivisions = null +shader_parameter/hover_index = null +shader_parameter/selected_index = null +shader_parameter/terrain_tile_factor = null + +[sub_resource type="MapMesh" id="MapMesh_3gtsd"] + +[node name="MapView" type="Node3D" node_paths=PackedStringArray("_camera", "_map_mesh_instance")] +editor_description = "SS-73" +script = ExtResource("1_exccw") +_camera = NodePath("MapCamera") +_map_mesh_instance = NodePath("MapMeshInstance") + +[node name="MapCamera" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0.25, 1.5, -2.75) +near = 0.01 + +[node name="MapMeshInstance" type="MeshInstance3D" parent="."] +editor_description = "FS-343" +transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0) +material_override = SubResource("ShaderMaterial_tayeg") +mesh = SubResource("MapMesh_3gtsd") diff --git a/game/src/Game/GameSession/ProvinceIndexSampler.gdshaderinc b/game/src/Game/GameSession/ProvinceIndexSampler.gdshaderinc new file mode 100644 index 0000000..65f73d8 --- /dev/null +++ b/game/src/Game/GameSession/ProvinceIndexSampler.gdshaderinc @@ -0,0 +1,18 @@ + +// Province shape texture +uniform sampler2DArray province_shape_tex : repeat_enable, filter_nearest; +// Province shape subdivisions +uniform vec2 province_shape_subdivisions; + +uvec3 vec3_to_uvec3(vec3 v) { + return uvec3(v * 255.0); +} +uvec3 read_uvec3(vec2 uv) { + uv *= province_shape_subdivisions; + vec2 subdivision_coords = mod(floor(uv), province_shape_subdivisions); + float idx = subdivision_coords.x + subdivision_coords.y * province_shape_subdivisions.x; + return vec3_to_uvec3(texture(province_shape_tex, vec3(uv, idx)).rgb); +} +uint uvec2_to_uint(uvec2 v) { + return (v.y << 8u) | v.x; +} diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd new file mode 100644 index 0000000..67060bf --- /dev/null +++ b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd @@ -0,0 +1,122 @@ +extends PanelContainer + +@export var _province_name_label : Label +@export var _region_name_label : Label +@export var _life_rating_bar : ProgressBar +@export var _rgo_icon_texture_rect : TextureRect +@export var _rgo_name_label : Label +@export var _buildings_container : Container + +const _missing_suffix : String = "_MISSING" + +var _selected_index : int: + get: return _selected_index + set(v): + _selected_index = v + update_info() +var _province_info : Dictionary + +func _ready(): + GameSingleton.province_selected.connect(_on_province_selected) + GameSingleton.state_updated.connect(update_info) + update_info() + +enum { CANNOT_EXPAND, CAN_EXPAND, PREPARING, EXPANDING } + +func _expand_building(building_identifier : String) -> void: + if GameSingleton.expand_building(_selected_index, building_identifier) != OK: + push_error("Failed to expand ", building_identifier, " in province #", _selected_index); + +# Each building row contains: +# level - Level Label +# name - Name Label +# button - Expansion Button +# progress_bar - Expansion ProgressBar +var _building_rows : Array[Dictionary] + +# REQUIREMENTS: +# * UI-183, UI-185, UI-186, UI-765, UI-187, UI-188, UI-189 +# * UI-191, UI-193, UI-194, UI-766, UI-195, UI-196, UI-197 +# * UI-199, UI-201, UI-202, UI-767, UI-203, UI-204, UI-205 +func _add_building_row() -> void: + var row : Dictionary + + row.level = Label.new() + row.level.text = "X" + _buildings_container.add_child(row.level) + + row.name = Label.new() + row.name.text = GameSingleton.get_building_info_building_key() + _missing_suffix + _buildings_container.add_child(row.name) + + row.button = Button.new() + row.button.text = "EXPAND_PROVINCE_BUILDING" + row.button.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.button.mouse_filter = Control.MOUSE_FILTER_PASS + row.button.pressed.connect(func(): _expand_building(row.name.text)) + _buildings_container.add_child(row.button) + + row.progress_bar = ProgressBar.new() + row.progress_bar.max_value = 1 + row.progress_bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.progress_bar.mouse_filter = Control.MOUSE_FILTER_PASS + _buildings_container.add_child(row.progress_bar) + + _building_rows.append(row) + _set_building_row(_building_rows.size() - 1, {}) + +func _set_building_row(index : int, building : Dictionary) -> void: + if index < 0 or index > _building_rows.size(): + push_error("Invalid building row index: ", index, " (max ", _building_rows.size(), ")") + return + if index == _building_rows.size(): _add_building_row() + var row := _building_rows[index] + if building.is_empty(): + row.level.visible = false + row.name.visible = false + row.progress_bar.visible = false + row.button.visible = false + return + row.level.text = str(building.get(GameSingleton.get_building_info_level_key(), 0)) + row.level.visible = true + row.name.text = building.get(GameSingleton.get_building_info_building_key(), + GameSingleton.get_building_info_building_key() + _missing_suffix) + row.name.visible = true + + var expansion_state : int = building.get(GameSingleton.get_building_info_expansion_state_key(), + CANNOT_EXPAND) + var show_progress_bar := expansion_state == PREPARING or expansion_state == EXPANDING + row.progress_bar.value = building.get(GameSingleton.get_building_info_expansion_progress_key(), 0) + row.progress_bar.visible = show_progress_bar + row.button.disabled = expansion_state != CAN_EXPAND + row.button.visible = not show_progress_bar + +func update_info() -> void: + _province_info = GameSingleton.get_province_info_from_index(_selected_index) + if _province_info: + _province_name_label.text = _province_info.get(GameSingleton.get_province_info_province_key(), + GameSingleton.get_province_info_province_key() + _missing_suffix) + _region_name_label.text = _province_info.get(GameSingleton.get_province_info_region_key(), + GameSingleton.get_province_info_region_key() + _missing_suffix) + + _life_rating_bar.value = _province_info.get(GameSingleton.get_province_info_life_rating_key(), 0) + _life_rating_bar.tooltip_text = tr("LIFE_RATING_TOOLTIP").format({ "life_rating": _life_rating_bar.value }) + + _rgo_name_label.text = _province_info.get(GameSingleton.get_province_info_rgo_key(), + GameSingleton.get_province_info_rgo_key() + _missing_suffix) + _rgo_icon_texture_rect.texture = GameSingleton.get_good_icon_texture(_rgo_name_label.text) + + var buildings : Array = _province_info.get(GameSingleton.get_province_info_buildings_key(), []) + for i in max(buildings.size(), _building_rows.size()): + _set_building_row(i, buildings[i] if i < buildings.size() else {}) + + show() + else: + hide() + mouse_exited.emit() + +func _on_province_selected(index : int) -> void: + _selected_index = index + +func _on_close_button_pressed() -> void: + GameSingleton.set_selected_province(0) diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn new file mode 100644 index 0000000..a233db0 --- /dev/null +++ b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn @@ -0,0 +1,86 @@ +[gd_scene load_steps=2 format=3 uid="uid://byq323jbel48u"] + +[ext_resource type="Script" path="res://src/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd" id="1_3n8k5"] + +[node name="ProvinceOverviewPanel" type="PanelContainer" node_paths=PackedStringArray("_province_name_label", "_region_name_label", "_life_rating_bar", "_rgo_icon_texture_rect", "_rgo_name_label", "_buildings_container")] +editor_description = "UI-56" +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_top = -300.0 +offset_right = 200.0 +grow_vertical = 0 +mouse_filter = 1 +script = ExtResource("1_3n8k5") +_province_name_label = NodePath("PanelList/TopBarList/NameList/ProvinceName") +_region_name_label = NodePath("PanelList/TopBarList/NameList/RegionName") +_life_rating_bar = NodePath("PanelList/TopBarList/NameList/LifeRatingBar") +_rgo_icon_texture_rect = NodePath("PanelList/InteractList/RGOInfo/RGOIcon") +_rgo_name_label = NodePath("PanelList/InteractList/RGOInfo/RGOName") +_buildings_container = NodePath("PanelList/InteractList/BuildingsContainer") + +[node name="PanelList" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="TopBarList" type="HBoxContainer" parent="PanelList"] +layout_mode = 2 + +[node name="NameList" type="VBoxContainer" parent="PanelList/TopBarList"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 0 + +[node name="ProvinceName" type="Label" parent="PanelList/TopBarList/NameList"] +editor_description = "UI-57" +layout_mode = 2 +text = "province_MISSING" +vertical_alignment = 1 + +[node name="RegionName" type="Label" parent="PanelList/TopBarList/NameList"] +editor_description = "UI-58" +layout_mode = 2 +text = "region_MISSING" +vertical_alignment = 1 + +[node name="LifeRatingBar" type="ProgressBar" parent="PanelList/TopBarList/NameList"] +editor_description = "UI-62" +layout_mode = 2 +mouse_filter = 1 + +[node name="CloseButton" type="Button" parent="PanelList/TopBarList"] +custom_minimum_size = Vector2(30, 30) +layout_mode = 2 +size_flags_vertical = 0 +mouse_filter = 1 +text = "X" + +[node name="InteractList" type="VBoxContainer" parent="PanelList"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="HSeparator" type="HSeparator" parent="PanelList/InteractList"] +layout_mode = 2 +mouse_filter = 1 + +[node name="RGOInfo" type="HBoxContainer" parent="PanelList/InteractList"] +editor_description = "UI-112" +layout_mode = 2 + +[node name="RGOIcon" type="TextureRect" parent="PanelList/InteractList/RGOInfo"] +editor_description = "UI-100" +layout_mode = 2 + +[node name="RGOName" type="Label" parent="PanelList/InteractList/RGOInfo"] +layout_mode = 2 +text = "rgo_MISSING" +vertical_alignment = 1 + +[node name="HSeparator2" type="HSeparator" parent="PanelList/InteractList"] +layout_mode = 2 +mouse_filter = 1 + +[node name="BuildingsContainer" type="GridContainer" parent="PanelList/InteractList"] +layout_mode = 2 +columns = 3 + +[connection signal="pressed" from="PanelList/TopBarList/CloseButton" to="." method="_on_close_button_pressed"] diff --git a/game/src/Game/GameSession/TerrainMap.gdshader b/game/src/Game/GameSession/TerrainMap.gdshader new file mode 100644 index 0000000..88e7019 --- /dev/null +++ b/game/src/Game/GameSession/TerrainMap.gdshader @@ -0,0 +1,50 @@ +shader_type spatial; + +render_mode unshaded; + +#include "res://src/GameSession/ProvinceIndexSampler.gdshaderinc" + +// Province colour texture +uniform sampler2D province_colour_tex: source_color, repeat_enable, filter_nearest; +// Index of the mouse over the map mesh +uniform uint hover_index; +// Index of the currently selected province +uniform uint selected_index; +// Cosmetic terrain textures +uniform sampler2DArray terrain_tex: source_color, repeat_enable, filter_linear; +// The number of times the terrain textures should tile vertically +uniform float terrain_tile_factor; + +const vec3 highlight_colour = vec3(1.0); + +vec3 get_terrain_colour(vec2 uv, vec2 corner, vec2 half_pixel_size, vec2 terrain_uv) { + uvec3 province_data = read_uvec3(fma(corner, half_pixel_size, uv)); + vec4 province_colour = texelFetch(province_colour_tex, ivec2(province_data.rg), 0); + vec3 terrain_colour = texture(terrain_tex, vec3(terrain_uv, float(province_data.b))).rgb; + vec3 mixed_colour = mix(terrain_colour, province_colour.rgb, province_colour.a); + uint index = uvec2_to_uint(province_data.rg); + float mix_val = 0.1 * (float(index == hover_index) + float(index == selected_index)); + return mix(mixed_colour, highlight_colour, mix_val); +} + +vec3 mix_terrain_colour(vec2 uv) { + vec2 map_size = vec2(textureSize(province_shape_tex, 0).xy) * province_shape_subdivisions; + vec2 pixel_offset = fract(fma(uv, map_size, vec2(0.5))); + vec2 half_pixel_size = 0.49 / map_size; + + vec2 terrain_uv = uv; + terrain_uv.x *= map_size.x / map_size.y; + terrain_uv *= terrain_tile_factor; + + return mix( + mix(get_terrain_colour(uv, vec2(-1, -1), half_pixel_size, terrain_uv), + get_terrain_colour(uv, vec2(+1, -1), half_pixel_size, terrain_uv), pixel_offset.x), + mix(get_terrain_colour(uv, vec2(-1, +1), half_pixel_size, terrain_uv), + get_terrain_colour(uv, vec2(+1, +1), half_pixel_size, terrain_uv), pixel_offset.x), + pixel_offset.y); +} + +void fragment() { + vec3 terrain_colour = mix_terrain_colour(UV); + ALBEDO = terrain_colour; +} |