diff --git a/CHANGELOG_DRAFT.md b/CHANGELOG_DRAFT.md index a7eb53b..e6ca632 100644 --- a/CHANGELOG_DRAFT.md +++ b/CHANGELOG_DRAFT.md @@ -1,3 +1,18 @@ +## [2.3.2] — 2026-05-12 +- Integrated Mailbox UI into lobby with CanvasLayer overlay (renders above 3D viewport). +- Redesigned `mailbox_panel.tscn` to 3-column layout: scrollable mail list | content area | reward slots. +- Auto read-all and auto claim-all triggered on mailbox open — no "NEW" tags remain after viewing. +- Fixed `MailboxBtn` text glitch — removed text override, replaced with `MailBadge` Label overlay in top-right corner of button. +- Fixed `mark_as_read()` in `MailManager` — now persists `read_ids` to server via new `save_mail_state` RPC. +- Admin Panel: Announcement tab `Target User` field now accepts username, display name, or user_id with **Find** button for resolution. +- Admin Panel: New **Mail Manager** tab listing all global and personal sent mails with Type, Title, Sender, Start, Expires, Status columns. +- Admin Panel: Edit dialog for mails uses `date_picker.tscn` for end date and supports reassigning recipient (username lookup + move). +- Admin Panel: **End Now** button sets `end_date` to current timestamp; **Delete** permanently removes mail from server storage. +- Server: Added `admin_list_mail` RPC — paginates all `inbox/personal` storage objects across all users via `storageList`. +- Server: Added `admin_update_mail` RPC — extract-then-reinsert pattern supports field edits and cross-user mail movement. +- Server: Added `admin_delete_mail_server` RPC — hard-removes mail from global config or personal inbox storage. +- Server: Added `save_mail_state` RPC — merges client `read_ids` into server `inbox/state` without clobbering claimed/deleted IDs. + ## [2.3.1] — 2026-05-11 - Integrated DM tab system directly into the lobby chatbox — DMs now open as closeable tabs inside ChatPanel instead of a fullscreen overlay. - Removed static DMTabBtn; DM tabs are dynamically created per friend using a scene-local DMTabTemplate with (X) close button. diff --git a/assets/data/version.json b/assets/data/version.json index f8828e2..817fd3a 100644 --- a/assets/data/version.json +++ b/assets/data/version.json @@ -1,7 +1,27 @@ { - "latest_version": "2.3.1", + "latest_version": "2.3.2", "minimum_app_version": "2.1.0", "releases": [ + { + "version": "2.3.2", + "date": "2026-05-12", + "pck_url": "https://raw.githubusercontent.com/adtpdn/tekton-updates/main/latest/patch.pck", + "pck_size": 0, + "changelog": [ + "Integrated Mailbox UI into lobby with CanvasLayer overlay", + "Redesigned mailbox_panel.tscn to 3-column layout (mail list, content, rewards)", + "Added auto read-all and auto claim-all on mailbox open", + "Fixed MailboxBtn text glitch — replaced text override with MailBadge Label overlay", + "Admin Panel: Target user field now accepts username or user_id with Find button", + "Admin Panel: New Mail Manager tab to view, edit, end, and delete sent announcements", + "Admin Panel: Edit dialog uses DatePicker for end date and supports recipient reassignment", + "Server: Added admin_list_mail, admin_update_mail, admin_delete_mail_server RPCs", + "Server: admin_list_mail scans all users personal inboxes via storageList", + "Server: admin_update_mail supports moving mail between users and global", + "Server: Added save_mail_state RPC to persist read_ids to server", + "Fixed mark_as_read — now persists to Nakama inbox state instead of local-only pass" + ] + }, { "version": "2.3.1", "date": "2026-05-11", diff --git a/assets/graphics/gui/mailbox/btn_back.png b/assets/graphics/gui/mailbox/btn_back.png new file mode 100644 index 0000000..a282146 Binary files /dev/null and b/assets/graphics/gui/mailbox/btn_back.png differ diff --git a/assets/graphics/gui/mailbox/btn_back.png.import b/assets/graphics/gui/mailbox/btn_back.png.import new file mode 100644 index 0000000..f37b42b --- /dev/null +++ b/assets/graphics/gui/mailbox/btn_back.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mw0hjqg4u58a" +path="res://.godot/imported/btn_back.png-766f18f6c608c9d18d187505f5ac889b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/gui/mailbox/btn_back.png" +dest_files=["res://.godot/imported/btn_back.png-766f18f6c608c9d18d187505f5ac889b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/graphics/gui/mailbox/btn_delete.png b/assets/graphics/gui/mailbox/btn_delete.png new file mode 100644 index 0000000..d06fcc2 Binary files /dev/null and b/assets/graphics/gui/mailbox/btn_delete.png differ diff --git a/assets/graphics/gui/mailbox/btn_delete.png.import b/assets/graphics/gui/mailbox/btn_delete.png.import new file mode 100644 index 0000000..ebd05ac --- /dev/null +++ b/assets/graphics/gui/mailbox/btn_delete.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://byvqbe1m2dr41" +path="res://.godot/imported/btn_delete.png-cf3f17cefdd7f374d94d8379a1c9f478.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/gui/mailbox/btn_delete.png" +dest_files=["res://.godot/imported/btn_delete.png-cf3f17cefdd7f374d94d8379a1c9f478.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/graphics/gui/mailbox/mailbox_icon.png b/assets/graphics/gui/mailbox/mailbox_icon.png new file mode 100644 index 0000000..fc3b547 Binary files /dev/null and b/assets/graphics/gui/mailbox/mailbox_icon.png differ diff --git a/assets/graphics/gui/mailbox/mailbox_icon.png.import b/assets/graphics/gui/mailbox/mailbox_icon.png.import new file mode 100644 index 0000000..2379333 --- /dev/null +++ b/assets/graphics/gui/mailbox/mailbox_icon.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cs5lke40qve7y" +path="res://.godot/imported/mailbox_icon.png-c85e0603097425f2befc209c5030293f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/gui/mailbox/mailbox_icon.png" +dest_files=["res://.godot/imported/mailbox_icon.png-c85e0603097425f2befc209c5030293f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/graphics/gui/mailbox/mailbox_node_01.png b/assets/graphics/gui/mailbox/mailbox_node_01.png new file mode 100644 index 0000000..96d962a Binary files /dev/null and b/assets/graphics/gui/mailbox/mailbox_node_01.png differ diff --git a/assets/graphics/gui/mailbox/mailbox_node_01.png.import b/assets/graphics/gui/mailbox/mailbox_node_01.png.import new file mode 100644 index 0000000..e4a2cdf --- /dev/null +++ b/assets/graphics/gui/mailbox/mailbox_node_01.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bc14cddtp0gvq" +path="res://.godot/imported/mailbox_node_01.png-a4c2ade2737ef5ed9de0d64d83793bb5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/gui/mailbox/mailbox_node_01.png" +dest_files=["res://.godot/imported/mailbox_node_01.png-a4c2ade2737ef5ed9de0d64d83793bb5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/graphics/gui/mailbox/mailbox_node_02.png b/assets/graphics/gui/mailbox/mailbox_node_02.png new file mode 100644 index 0000000..25d2066 Binary files /dev/null and b/assets/graphics/gui/mailbox/mailbox_node_02.png differ diff --git a/assets/graphics/gui/mailbox/mailbox_node_02.png.import b/assets/graphics/gui/mailbox/mailbox_node_02.png.import new file mode 100644 index 0000000..9097ce9 --- /dev/null +++ b/assets/graphics/gui/mailbox/mailbox_node_02.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cosjvp4msuv34" +path="res://.godot/imported/mailbox_node_02.png-c71d25c932b515f6569f65ba769a206c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/gui/mailbox/mailbox_node_02.png" +dest_files=["res://.godot/imported/mailbox_node_02.png-c71d25c932b515f6569f65ba769a206c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/graphics/gui/mailbox/read_all.png b/assets/graphics/gui/mailbox/read_all.png new file mode 100644 index 0000000..1cda915 Binary files /dev/null and b/assets/graphics/gui/mailbox/read_all.png differ diff --git a/assets/graphics/gui/mailbox/read_all.png.import b/assets/graphics/gui/mailbox/read_all.png.import new file mode 100644 index 0000000..c2cb270 --- /dev/null +++ b/assets/graphics/gui/mailbox/read_all.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://exkx1hado3wi" +path="res://.godot/imported/read_all.png-3faf33804509dc0f18651b7ee0cbaf4b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/graphics/gui/mailbox/read_all.png" +dest_files=["res://.godot/imported/read_all.png-3faf33804509dc0f18651b7ee0cbaf4b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/models/meshes/lobby.tscn b/assets/models/meshes/lobby.tscn deleted file mode 100644 index 641b1cb..0000000 --- a/assets/models/meshes/lobby.tscn +++ /dev/null @@ -1,286 +0,0 @@ -[gd_scene format=3 uid="uid://xpalyawdgnyl"] - -[ext_resource type="Script" uid="uid://b5q6yekyk0tld" path="res://scenes/lobby.gd" id="1_lp6xi"] -[ext_resource type="Theme" uid="uid://da337sh5qxi0s" path="res://assets/themes/ui_theme.tres" id="2_theme"] - -[node name="Lobby" type="Control" unique_id=1681755368] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme = ExtResource("2_theme") -script = ExtResource("1_lp6xi") - -[node name="Background" type="ColorRect" parent="." unique_id=610186638] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -color = Color(0.12, 0.1, 0.08, 1) - -[node name="MainMenuPanel" type="PanelContainer" parent="." unique_id=749972967] -layout_mode = 1 -anchors_preset = 8 -anchor_left = 0.5 -anchor_top = 0.5 -anchor_right = 0.5 -anchor_bottom = 0.5 -offset_left = -220.0 -offset_top = -240.0 -offset_right = 220.0 -offset_bottom = 240.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="MainMenuPanel" unique_id=172832102] -layout_mode = 2 -theme_override_constants/separation = 18 - -[node name="TitleContainer" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=1254704944] -layout_mode = 2 -theme_override_constants/separation = 4 - -[node name="Title" type="Label" parent="MainMenuPanel/VBoxContainer/TitleContainer" unique_id=1313646849] -layout_mode = 2 -theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1) -theme_override_font_sizes/font_size = 44 -text = "TEKTON DASH" -horizontal_alignment = 1 -vertical_alignment = 1 - -[node name="Subtitle" type="Label" parent="MainMenuPanel/VBoxContainer/TitleContainer" unique_id=981494139] -layout_mode = 2 -theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1) -theme_override_font_sizes/font_size = 12 -text = "ARMAGEDDON VERSION" -horizontal_alignment = 1 - -[node name="Separator" type="HSeparator" parent="MainMenuPanel/VBoxContainer" unique_id=1861761683] -layout_mode = 2 - -[node name="InputSection" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=113453689] -layout_mode = 2 -theme_override_constants/separation = 10 - -[node name="PlayerNameLabel" type="Label" parent="MainMenuPanel/VBoxContainer/InputSection" unique_id=2007198770] -layout_mode = 2 -theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1) -theme_override_font_sizes/font_size = 13 -text = "YOUR NAME" - -[node name="PlayerNameInput" type="LineEdit" parent="MainMenuPanel/VBoxContainer/InputSection" unique_id=543986981] -custom_minimum_size = Vector2(0, 44) -layout_mode = 2 -text = "Player" -placeholder_text = "Enter your name..." - -[node name="ButtonSection" type="VBoxContainer" parent="MainMenuPanel/VBoxContainer" unique_id=357245135] -layout_mode = 2 -theme_override_constants/separation = 12 - -[node name="CreateRoomBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=1297504653] -custom_minimum_size = Vector2(0, 48) -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "CREATE ROOM" - -[node name="BrowseRoomsBtn" type="Button" parent="MainMenuPanel/VBoxContainer/ButtonSection" unique_id=1825906901] -custom_minimum_size = Vector2(0, 48) -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "BROWSE ROOMS" - -[node name="RoomListPanel" type="PanelContainer" parent="." unique_id=1969054574] -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 = -300.0 -offset_top = -260.0 -offset_right = 300.0 -offset_bottom = 260.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="RoomListPanel" unique_id=726092140] -layout_mode = 2 -theme_override_constants/separation = 14 - -[node name="Header" type="Label" parent="RoomListPanel/VBoxContainer" unique_id=1272860526] -layout_mode = 2 -theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1) -theme_override_font_sizes/font_size = 28 -text = "SERVER BROWSER" -horizontal_alignment = 1 - -[node name="HSeparator" type="HSeparator" parent="RoomListPanel/VBoxContainer" unique_id=38843595] -layout_mode = 2 - -[node name="MatchIdLabel" type="Label" parent="RoomListPanel/VBoxContainer" unique_id=316758674] -layout_mode = 2 -theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1) -theme_override_font_sizes/font_size = 12 -text = "DIRECT CONNECT (MATCH ID)" - -[node name="MatchIdInput" type="LineEdit" parent="RoomListPanel/VBoxContainer" unique_id=159753310] -custom_minimum_size = Vector2(0, 44) -layout_mode = 2 -placeholder_text = "Paste match ID here..." - -[node name="RoomListLabel" type="Label" parent="RoomListPanel/VBoxContainer" unique_id=867057060] -layout_mode = 2 -theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1) -theme_override_font_sizes/font_size = 12 -text = "AVAILABLE SERVERS" - -[node name="RoomList" type="ItemList" parent="RoomListPanel/VBoxContainer" unique_id=203590990] -custom_minimum_size = Vector2(0, 200) -layout_mode = 2 -allow_reselect = true - -[node name="ButtonContainer" type="HBoxContainer" parent="RoomListPanel/VBoxContainer" unique_id=178401080] -layout_mode = 2 -theme_override_constants/separation = 14 -alignment = 1 - -[node name="RefreshBtn" type="Button" parent="RoomListPanel/VBoxContainer/ButtonContainer" unique_id=1068680772] -custom_minimum_size = Vector2(110, 44) -layout_mode = 2 -text = "REFRESH" - -[node name="JoinBtn" type="Button" parent="RoomListPanel/VBoxContainer/ButtonContainer" unique_id=822083530] -custom_minimum_size = Vector2(130, 44) -layout_mode = 2 -text = "JOIN SERVER" - -[node name="BackBtn" type="Button" parent="RoomListPanel/VBoxContainer/ButtonContainer" unique_id=1873303918] -custom_minimum_size = Vector2(110, 44) -layout_mode = 2 -text = "BACK" - -[node name="LobbyPanel" type="PanelContainer" parent="." unique_id=1955208668] -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 = -300.0 -offset_top = -260.0 -offset_right = 300.0 -offset_bottom = 260.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="VBoxContainer" type="VBoxContainer" parent="LobbyPanel" unique_id=1443407103] -layout_mode = 2 -theme_override_constants/separation = 14 - -[node name="RoomNameHeader" type="Label" parent="LobbyPanel/VBoxContainer" unique_id=2019479226] -layout_mode = 2 -theme_override_colors/font_color = Color(0.647, 0.996, 0.224, 1) -theme_override_font_sizes/font_size = 26 -text = "ROOM: " -horizontal_alignment = 1 - -[node name="MatchIdContainer" type="HBoxContainer" parent="LobbyPanel/VBoxContainer" unique_id=280307543] -layout_mode = 2 -alignment = 1 - -[node name="MatchIdDisplay" type="Label" parent="LobbyPanel/VBoxContainer/MatchIdContainer" unique_id=1883524532] -layout_mode = 2 -theme_override_colors/font_color = Color(0.5, 0.5, 0.55, 1) -theme_override_font_sizes/font_size = 10 -text = "Match ID: " - -[node name="CopyIdBtn" type="Button" parent="LobbyPanel/VBoxContainer/MatchIdContainer" unique_id=1770583860] -custom_minimum_size = Vector2(80, 32) -layout_mode = 2 -theme_override_font_sizes/font_size = 10 -text = "COPY" - -[node name="HSeparator" type="HSeparator" parent="LobbyPanel/VBoxContainer" unique_id=346787128] -layout_mode = 2 - -[node name="PlayersLabel" type="Label" parent="LobbyPanel/VBoxContainer" unique_id=1266635921] -layout_mode = 2 -theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1) -theme_override_font_sizes/font_size = 13 -text = "PLAYERS" - -[node name="PlayerList" type="ItemList" parent="LobbyPanel/VBoxContainer" unique_id=1481128408] -custom_minimum_size = Vector2(0, 160) -layout_mode = 2 -allow_reselect = true - -[node name="StatusLabel" type="Label" parent="LobbyPanel/VBoxContainer" unique_id=1750308910] -layout_mode = 2 -theme_override_colors/font_color = Color(0.992, 0.796, 0.047, 1) -theme_override_font_sizes/font_size = 14 -text = "Waiting for players..." -horizontal_alignment = 1 - -[node name="ButtonContainer" type="HBoxContainer" parent="LobbyPanel/VBoxContainer" unique_id=941148439] -layout_mode = 2 -theme_override_constants/separation = 14 -alignment = 1 - -[node name="ReadyBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer" unique_id=386730307] -custom_minimum_size = Vector2(110, 48) -layout_mode = 2 -toggle_mode = true -text = "READY" - -[node name="StartGameBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer" unique_id=1510805318] -custom_minimum_size = Vector2(140, 48) -layout_mode = 2 -disabled = true -text = "START GAME" - -[node name="LeaveBtn" type="Button" parent="LobbyPanel/VBoxContainer/ButtonContainer" unique_id=1773320572] -custom_minimum_size = Vector2(110, 48) -layout_mode = 2 -text = "LEAVE" - -[node name="StatusBar" type="PanelContainer" parent="." unique_id=547557114] -layout_mode = 1 -anchors_preset = 12 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 24.0 -offset_top = -72.0 -offset_right = -24.0 -offset_bottom = -24.0 -grow_horizontal = 2 -grow_vertical = 0 - -[node name="ConnectionStatus" type="Label" parent="StatusBar" unique_id=1282790307] -layout_mode = 2 -theme_override_colors/font_color = Color(0.69, 0.529, 0.357, 1) -theme_override_font_sizes/font_size = 12 -text = "NOT CONNECTED" -horizontal_alignment = 1 - -[node name="VersionLabel" type="Label" parent="." unique_id=1619858099] -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -130.0 -offset_top = 20.0 -offset_right = -20.0 -offset_bottom = 40.0 -grow_horizontal = 0 -theme_override_colors/font_color = Color(0.6, 0.6, 0.6, 0.6) -theme_override_font_sizes/font_size = 11 -text = "v0.1.0 ALPHA" -horizontal_alignment = 2 diff --git a/export_presets.cfg b/export_presets.cfg index b0e0a12..c8761db 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -42,8 +42,8 @@ application/modify_resources=false application/icon="" application/console_wrapper_icon="" application/icon_interpolation=4 -application/file_version="2.3" -application/product_version="2.3" +application/file_version="2.3.1" +application/product_version="2.3.1" application/company_name="DanchieGo" application/product_name="Tekton Armageddon" application/file_description="" @@ -565,8 +565,8 @@ codesign/digest_algorithm=1 codesign/identity_type=0 application/modify_resources=false application/console_wrapper_icon="" -application/file_version="2.3" -application/product_version="2.3" +application/file_version="2.3.1" +application/product_version="2.3.1" application/company_name="DanchieGo" application/product_name="Tekton Armageddon" application/file_description="" diff --git a/project.godot b/project.godot index 7cd383e..df5f358 100644 --- a/project.godot +++ b/project.godot @@ -15,7 +15,7 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true [application] config/name="Tekton Dash Armageddon" -config/version="2.3.1" +config/version="2.3.2" run/main_scene="res://scenes/ui/boot_screen.tscn" config/features=PackedStringArray("4.6", "Forward Plus") boot_splash/bg_color=Color(0.16470589, 0.6745098, 0.9372549, 1) diff --git a/scenes/lobby.gd b/scenes/lobby.gd index 18e9b85..5d34bc0 100644 --- a/scenes/lobby.gd +++ b/scenes/lobby.gd @@ -30,6 +30,7 @@ extends Control @onready var shop_btn = %CartBtn @onready var top_right_profile_btn = %ProfileBtn @onready var mailbox_btn = get_node_or_null("%MailboxBtn") +@onready var mail_badge = get_node_or_null("%MailBadge") @onready var banner1_btn = get_node_or_null("%Banner1") @onready var ticket_btn = get_node_or_null("%TicketBtn") @onready var mailbox_panel = get_node_or_null("MailboxPanel") @@ -869,28 +870,37 @@ func _on_profile_btn_pressed() -> void: main_menu_panel.hide() profile_panel_instance.show_panel() -func _on_mailbox_pressed() -> void: - if mailbox_panel: - mailbox_panel.show_panel() - if main_menu_panel: - main_menu_panel.hide() - - # Connect the closed signal to reshow main menu if not connected - if not mailbox_panel.closed.is_connected(_on_mailbox_closed): - mailbox_panel.closed.connect(_on_mailbox_closed) +var _mailbox_panel_instance: Control -func _on_mailbox_closed() -> void: - if main_menu_panel: - main_menu_panel.show() +func _on_mailbox_pressed() -> void: + if not _mailbox_panel_instance: + var scene = load("res://scenes/ui/mailbox_panel.tscn") + if scene: + _mailbox_panel_instance = scene.instantiate() + _mailbox_panel_instance.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + + var cl := CanvasLayer.new() + cl.layer = 100 + cl.name = "MailboxCanvasLayer" + add_child(cl) + cl.add_child(_mailbox_panel_instance) + + if _mailbox_panel_instance.has_signal("closed"): + _mailbox_panel_instance.closed.connect(func(): + _mailbox_panel_instance.get_parent().queue_free() + _mailbox_panel_instance = null + ) + + if _mailbox_panel_instance: + _mailbox_panel_instance.show_panel() func _on_mail_unread_count_changed(count: int) -> void: - if mailbox_btn: + if mail_badge: if count > 0: - mailbox_btn.text = "MAIL (%d)" % count - mailbox_btn.add_theme_color_override("font_color", Color.YELLOW) + mail_badge.text = str(count) if count < 100 else "99+" + mail_badge.visible = true else: - mailbox_btn.text = "MAIL" - mailbox_btn.remove_theme_color_override("font_color") + mail_badge.visible = false func _on_logout_pressed() -> void: AuthManager.logout() diff --git a/scenes/lobby.tscn b/scenes/lobby.tscn index 6d7c256..7e39c8a 100644 --- a/scenes/lobby.tscn +++ b/scenes/lobby.tscn @@ -31,6 +31,7 @@ [ext_resource type="Texture2D" uid="uid://dv782w5t0xlcc" path="res://assets/graphics/gui/lobby/friends.png" id="22_1x1aw"] [ext_resource type="Texture2D" uid="uid://cpy5lppf3ro02" path="res://assets/graphics/gui/play/selection_play1.png" id="22_kn4i6"] [ext_resource type="Texture2D" uid="uid://b0ovmvcm8rt2n" path="res://assets/graphics/gui/play/selection_room0.png" id="23_3jc85"] +[ext_resource type="Texture2D" uid="uid://cs5lke40qve7y" path="res://assets/graphics/gui/mailbox/mailbox_icon.png" id="23_835bk"] [ext_resource type="Texture2D" uid="uid://bqcxrfu2jlplr" path="res://assets/graphics/gui/lobby/settings.png" id="23_twy5w"] [ext_resource type="Texture2D" uid="uid://3p0sabd1og31" path="res://assets/graphics/gui/play/selection_room1.png" id="24_jhtcy"] [ext_resource type="Texture2D" uid="uid://bpco6lch7homj" path="res://assets/graphics/gui/play/bg.png" id="25_iwv4c"] @@ -113,6 +114,12 @@ bg_color = Color(0.6, 0.6, 0.6, 0) [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_dfnwm"] +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_835bk"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ucbax"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_1x1aw"] + [sub_resource type="StyleBoxTexture" id="StyleBoxTexture_50md7"] texture = ExtResource("25_iwv4c") @@ -480,73 +487,6 @@ layout_mode = 2 custom_minimum_size = Vector2(0, 8) layout_mode = 2 -[node name="CurrencyRow" type="VBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol" unique_id=59384589] -visible = false -layout_mode = 2 -theme_override_constants/separation = 8 - -[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow" unique_id=1807032398] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="StarPanel" type="PanelContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer" unique_id=509438] -layout_mode = 2 -size_flags_horizontal = 3 -theme = ExtResource("2_theme") - -[node name="Margin" type="MarginContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer/StarPanel" unique_id=609485] -layout_mode = 2 -theme_override_constants/margin_left = 12 -theme_override_constants/margin_top = 4 -theme_override_constants/margin_right = 12 -theme_override_constants/margin_bottom = 4 - -[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer/StarPanel/Margin" unique_id=69485094] -layout_mode = 2 -theme_override_constants/separation = 12 - -[node name="Icon" type="Label" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer/StarPanel/Margin/HBoxContainer" unique_id=958438] -layout_mode = 2 -theme_override_colors/font_color = Color(0.9, 0.7, 0.3, 1) -theme_override_font_sizes/font_size = 18 -text = "✦" - -[node name="Control" type="Control" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer" unique_id=255421565] -custom_minimum_size = Vector2(180, 0) -layout_direction = 3 -layout_mode = 2 - -[node name="HBoxContainer2" type="HBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow" unique_id=1399423386] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="GoldPanel" type="PanelContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2" unique_id=9458940] -layout_mode = 2 -size_flags_horizontal = 3 -theme = ExtResource("2_theme") - -[node name="Margin" type="MarginContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2/GoldPanel" unique_id=94589] -layout_mode = 2 -theme_override_constants/margin_left = 12 -theme_override_constants/margin_top = 4 -theme_override_constants/margin_right = 12 -theme_override_constants/margin_bottom = 4 - -[node name="HBoxContainer" type="HBoxContainer" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2/GoldPanel/Margin" unique_id=9458094] -layout_mode = 2 -theme_override_constants/separation = 12 - -[node name="Icon" type="Label" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2/GoldPanel/Margin/HBoxContainer" unique_id=9485] -layout_mode = 2 -theme_override_colors/font_color = Color(0.8, 0.6, 0.2, 1) -theme_override_font_sizes/font_size = 18 -text = "▤" - -[node name="Control" type="Control" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol/CurrencyRow/HBoxContainer2" unique_id=320833893] -custom_minimum_size = Vector2(180, 0) -layout_direction = 3 -layout_mode = 2 - [node name="SpacerMiddle" type="Control" parent="MainMenuPanel/MainMargin/MainHBox/LeftCol" unique_id=984509] layout_mode = 2 size_flags_vertical = 3 @@ -764,6 +704,44 @@ grow_vertical = 2 texture = ExtResource("21_ucbax") expand_mode = 2 +[node name="MailboxBtn" type="Button" parent="MainMenuPanel/MainMargin/MainHBox/RightCol/TopRightPanel" unique_id=64042311] +unique_name_in_owner = true +custom_minimum_size = Vector2(61, 61) +layout_mode = 2 +theme_override_fonts/font = ExtResource("5_pc087") +theme_override_styles/normal = SubResource("StyleBoxEmpty_835bk") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_ucbax") +theme_override_styles/hover = SubResource("StyleBoxEmpty_1x1aw") + +[node name="TextureRect" type="TextureRect" parent="MainMenuPanel/MainMargin/MainHBox/RightCol/TopRightPanel/MailboxBtn" unique_id=109558991] +custom_minimum_size = Vector2(32, 32) +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +texture = ExtResource("23_835bk") +expand_mode = 3 + +[node name="MailBadge" type="Label" parent="MainMenuPanel/MainMargin/MainHBox/RightCol/TopRightPanel/MailboxBtn"] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchor_left = 1.0 +anchor_right = 1.0 +offset_left = -18.0 +offset_top = -2.0 +offset_right = 2.0 +offset_bottom = 16.0 +grow_horizontal = 0 +grow_vertical = 1 +theme_override_colors/font_color = Color(1, 1, 1, 1) +theme_override_font_sizes/font_size = 10 +horizontal_alignment = 1 +vertical_alignment = 1 +text = "" + [node name="SocialBtn" type="Button" parent="MainMenuPanel/MainMargin/MainHBox/RightCol/TopRightPanel" unique_id=82719328] unique_name_in_owner = true custom_minimum_size = Vector2(61, 61) @@ -824,7 +802,7 @@ unique_name_in_owner = true custom_minimum_size = Vector2(0, 120) layout_mode = 2 size_flags_horizontal = 3 -theme = ExtResource("2_theme") +theme = ExtResource("14_2630d") theme_override_fonts/font = ExtResource("5_pc087") theme_override_font_sizes/font_size = 30 disabled = true @@ -845,7 +823,7 @@ unique_name_in_owner = true custom_minimum_size = Vector2(0, 120) layout_mode = 2 size_flags_horizontal = 3 -theme = ExtResource("2_theme") +theme = ExtResource("14_2630d") theme_override_fonts/font = ExtResource("5_pc087") theme_override_font_sizes/font_size = 30 disabled = true @@ -866,7 +844,7 @@ unique_name_in_owner = true custom_minimum_size = Vector2(0, 120) layout_mode = 2 size_flags_horizontal = 3 -theme = ExtResource("2_theme") +theme = ExtResource("14_2630d") theme_override_fonts/font = ExtResource("5_pc087") theme_override_font_sizes/font_size = 30 disabled = true diff --git a/scenes/ui/admin_panel.tscn b/scenes/ui/admin_panel.tscn index bf70925..e7e73b5 100644 --- a/scenes/ui/admin_panel.tscn +++ b/scenes/ui/admin_panel.tscn @@ -271,17 +271,29 @@ metadata/_tab_index = 3 [node name="TargetHBox" type="HBoxContainer" parent="Margin/VBox/Tabs/Announcements"] layout_mode = 2 -theme_override_constants/separation = 12 +theme_override_constants/separation = 8 [node name="Label" type="Label" parent="Margin/VBox/Tabs/Announcements/TargetHBox"] layout_mode = 2 -text = "Target User ID:" +text = "Target:" [node name="TargetUserEdit" type="LineEdit" parent="Margin/VBox/Tabs/Announcements/TargetHBox"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -placeholder_text = "Leave empty to send to ALL users (Global)" +placeholder_text = "Username or User ID (empty = ALL)" + +[node name="FindUserBtn" type="Button" parent="Margin/VBox/Tabs/Announcements/TargetHBox"] +unique_name_in_owner = true +custom_minimum_size = Vector2(80, 0) +layout_mode = 2 +text = "Find" + +[node name="ResolvedIdLabel" type="Label" parent="Margin/VBox/Tabs/Announcements/TargetHBox"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(0.4, 0.9, 0.4, 1) +text = "" [node name="TitleEdit" type="LineEdit" parent="Margin/VBox/Tabs/Announcements"] unique_name_in_owner = true @@ -376,6 +388,54 @@ custom_minimum_size = Vector2(0, 40) layout_mode = 2 text = "SEND ANNOUNCEMENT" +[node name="Mail Manager" type="VBoxContainer" parent="Margin/VBox/Tabs"] +visible = false +layout_mode = 2 +theme_override_constants/separation = 8 +metadata/_tab_index = 4 + +[node name="MailTree" type="Tree" parent="Margin/VBox/Tabs/Mail Manager"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +columns = 6 +column_titles_visible = true +allow_reselect = true +hide_root = true +select_mode = 1 + +[node name="MailActionBar" type="HBoxContainer" parent="Margin/VBox/Tabs/Mail Manager"] +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="RefreshMailBtn" type="Button" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 36) +layout_mode = 2 +text = "Refresh" + +[node name="Spacer" type="Control" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="EditMailBtn" type="Button" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 36) +layout_mode = 2 +text = "Edit" + +[node name="EndMailBtn" type="Button" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"] +unique_name_in_owner = true +custom_minimum_size = Vector2(120, 36) +layout_mode = 2 +text = "End Now" + +[node name="DeleteMailServerBtn" type="Button" parent="Margin/VBox/Tabs/Mail Manager/MailActionBar"] +unique_name_in_owner = true +custom_minimum_size = Vector2(120, 36) +layout_mode = 2 +text = "Delete" + [node name="StatusLabel" type="Label" parent="Margin/VBox"] unique_name_in_owner = true layout_mode = 2 diff --git a/scenes/ui/mailbox_panel.tscn b/scenes/ui/mailbox_panel.tscn index 32f856f..0fb0b75 100644 --- a/scenes/ui/mailbox_panel.tscn +++ b/scenes/ui/mailbox_panel.tscn @@ -1,6 +1,39 @@ -[gd_scene format=3 uid="uid://cb5cbbxyxx"] +[gd_scene format=3 uid="uid://5e1bfpagcpps"] -[ext_resource type="Script" uid="uid://df7xxyyzz" path="res://scripts/ui/mailbox_panel.gd" id="1_a"] +[ext_resource type="Script" uid="uid://b5fema68m6b2s" path="res://scripts/ui/mailbox_panel.gd" id="1_a"] +[ext_resource type="Theme" uid="uid://cxab3xxy00" path="res://assets/themes/GUI_Tekton.tres" id="1_wi8mn"] +[ext_resource type="Texture2D" uid="uid://dfmailbox" path="res://assets/graphics/gui/mainmenu/mailbox.png" id="tex_mailbox"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_middle"] +bg_color = Color(0.02, 0.04, 0.08, 1) +border_width_left = 3 +border_width_top = 3 +border_width_right = 3 +border_width_bottom = 3 +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 12 +corner_radius_top_right = 12 +corner_radius_bottom_right = 12 +corner_radius_bottom_left = 12 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_slot"] +bg_color = Color(0.1, 0.3, 0.6, 0.6) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mailbtn"] +bg_color = Color(0.2, 0.4, 0.6, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0, 0, 0, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 [node name="MailboxPanel" type="Panel"] anchors_preset = 15 @@ -8,6 +41,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +theme = ExtResource("1_wi8mn") script = ExtResource("1_a") [node name="BG" type="ColorRect" parent="."] @@ -17,7 +51,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -color = Color(0.08, 0.15, 0.22, 1) +color = Color(0.12, 0.4, 0.9, 1) [node name="Margin" type="MarginContainer" parent="."] layout_mode = 1 @@ -26,45 +60,51 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_constants/margin_left = 24 -theme_override_constants/margin_top = 24 -theme_override_constants/margin_right = 24 -theme_override_constants/margin_bottom = 24 +theme_override_constants/margin_left = 40 +theme_override_constants/margin_top = 40 +theme_override_constants/margin_right = 40 +theme_override_constants/margin_bottom = 40 -[node name="HBox" type="HBoxContainer" parent="Margin"] +[node name="VBox" type="VBoxContainer" parent="Margin"] layout_mode = 2 -theme_override_constants/separation = 24 +theme_override_constants/separation = 20 -[node name="LeftCol" type="VBoxContainer" parent="Margin/HBox"] -custom_minimum_size = Vector2(300, 0) -layout_mode = 2 -theme_override_constants/separation = 12 - -[node name="HeaderHBox" type="HBoxContainer" parent="Margin/HBox/LeftCol"] +[node name="HeaderHBox" type="HBoxContainer" parent="Margin/VBox"] layout_mode = 2 -[node name="Icon" type="TextureRect" parent="Margin/HBox/LeftCol/HeaderHBox"] -custom_minimum_size = Vector2(32, 32) +[node name="Icon" type="TextureRect" parent="Margin/VBox/HeaderHBox"] +custom_minimum_size = Vector2(48, 48) layout_mode = 2 +texture = ExtResource("tex_mailbox") expand_mode = 1 stretch_mode = 5 -[node name="Label" type="Label" parent="Margin/HBox/LeftCol/HeaderHBox"] +[node name="Label" type="Label" parent="Margin/VBox/HeaderHBox"] layout_mode = 2 +theme_override_font_sizes/font_size = 42 text = "MAILBOX" -theme_override_font_sizes/font_size = 24 -[node name="Scroll" type="ScrollContainer" parent="Margin/HBox/LeftCol"] +[node name="HBox" type="HBoxContainer" parent="Margin/VBox"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/separation = 20 + +[node name="LeftCol" type="VBoxContainer" parent="Margin/VBox/HBox"] +custom_minimum_size = Vector2(350, 0) +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="Scroll" type="ScrollContainer" parent="Margin/VBox/HBox/LeftCol"] layout_mode = 2 size_flags_vertical = 3 -[node name="MailListVBox" type="VBoxContainer" parent="Margin/HBox/LeftCol/Scroll"] +[node name="MailListVBox" type="VBoxContainer" parent="Margin/VBox/HBox/LeftCol/Scroll"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 -theme_override_constants/separation = 8 +theme_override_constants/separation = 12 -[node name="EmptyStateLbl" type="Label" parent="Margin/HBox/LeftCol/Scroll"] +[node name="EmptyStateLbl" type="Label" parent="Margin/VBox/HBox/LeftCol/Scroll"] unique_name_in_owner = true visible = false layout_mode = 2 @@ -73,102 +113,170 @@ size_flags_vertical = 6 text = "No mails found." horizontal_alignment = 1 -[node name="CloseBtn" type="Button" parent="Margin/HBox/LeftCol"] -unique_name_in_owner = true -custom_minimum_size = Vector2(100, 40) -layout_mode = 2 -size_flags_horizontal = 0 -text = "BACK" - -[node name="MailBtnTemplate" type="Button" parent="Margin/HBox/LeftCol"] -unique_name_in_owner = true -visible = false -custom_minimum_size = Vector2(0, 80) -layout_mode = 2 -toggle_mode = true - -[node name="VBox" type="VBoxContainer" parent="Margin/HBox/LeftCol/MailBtnTemplate"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_constants/margin_left = 8 -theme_override_constants/margin_right = 8 - -[node name="Title" type="Label" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox"] -layout_mode = 2 -theme_override_font_sizes/font_size = 18 -text = "No Title" - -[node name="HBox" type="HBoxContainer" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox"] -layout_mode = 2 - -[node name="DateLbl" type="Label" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox/HBox"] -layout_mode = 2 -theme_override_colors/font_color = Color(0.745098, 0.745098, 0.745098, 1) -theme_override_font_sizes/font_size = 12 - -[node name="Spacer" type="Control" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox/HBox"] +[node name="MiddleCol" type="PanelContainer" parent="Margin/VBox/HBox"] layout_mode = 2 size_flags_horizontal = 3 +theme_override_styles/panel = SubResource("StyleBoxFlat_middle") -[node name="StatusLbl" type="Label" parent="Margin/HBox/LeftCol/MailBtnTemplate/VBox/HBox"] +[node name="Margin" type="MarginContainer" parent="Margin/VBox/HBox/MiddleCol"] layout_mode = 2 -theme_override_font_sizes/font_size = 12 +theme_override_constants/margin_left = 24 +theme_override_constants/margin_top = 24 +theme_override_constants/margin_right = 24 +theme_override_constants/margin_bottom = 24 -[node name="RightCol" type="VBoxContainer" parent="Margin/HBox"] +[node name="VBox" type="VBoxContainer" parent="Margin/VBox/HBox/MiddleCol/Margin"] layout_mode = 2 -size_flags_horizontal = 3 +theme_override_constants/separation = 16 -[node name="MailTitleLbl" type="Label" parent="Margin/HBox/RightCol"] +[node name="MailTitleLbl" type="Label" parent="Margin/VBox/HBox/MiddleCol/Margin/VBox"] unique_name_in_owner = true layout_mode = 2 -theme_override_font_sizes/font_size = 28 -text = "WELCOME TO TEKTONIA" +theme_override_font_sizes/font_size = 32 +text = "Message Title" -[node name="Sep" type="HSeparator" parent="Margin/HBox/RightCol"] +[node name="Sep" type="HSeparator" parent="Margin/VBox/HBox/MiddleCol/Margin/VBox"] layout_mode = 2 -[node name="MailContentText" type="RichTextLabel" parent="Margin/HBox/RightCol"] +[node name="MailContentText" type="RichTextLabel" parent="Margin/VBox/HBox/MiddleCol/Margin/VBox"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 theme_override_font_sizes/normal_font_size = 18 text = "Lorem ipsum..." -[node name="FooterHBox" type="HBoxContainer" parent="Margin/HBox/RightCol"] +[node name="RightCol" type="VBoxContainer" parent="Margin/VBox/HBox"] +custom_minimum_size = Vector2(250, 0) +layout_mode = 2 +theme_override_constants/separation = 16 + +[node name="DynamicRewardsContainer" type="VBoxContainer" parent="Margin/VBox/HBox/RightCol"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_constants/separation = 16 + +[node name="FooterHBox" type="HBoxContainer" parent="Margin/VBox"] layout_mode = 2 -[node name="SenderLbl" type="Label" parent="Margin/HBox/RightCol/FooterHBox"] +[node name="CloseBtn" type="Button" parent="Margin/VBox/FooterHBox"] unique_name_in_owner = true +custom_minimum_size = Vector2(120, 50) +layout_mode = 2 +theme_override_colors/font_color = Color(1, 1, 1, 1) +text = "< BACK" + +[node name="ReadAllBtn" type="Button" parent="Margin/VBox/FooterHBox"] +unique_name_in_owner = true +custom_minimum_size = Vector2(160, 50) +layout_mode = 2 +text = "READ ALL" + +[node name="Spacer" type="Control" parent="Margin/VBox/FooterHBox"] layout_mode = 2 size_flags_horizontal = 3 -text = "SENDER: -TEKTON DEV TEAM" -[node name="DynamicRewardsContainer" type="HBoxContainer" parent="Margin/HBox/RightCol/FooterHBox"] +[node name="ActionBtn" type="Button" parent="Margin/VBox/FooterHBox"] unique_name_in_owner = true +custom_minimum_size = Vector2(180, 50) +layout_mode = 2 +theme_override_colors/font_color = Color(1, 1, 1, 1) +text = "DELETE" + +[node name="Templates" type="Control" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 0 + +[node name="MailBtnTemplate" type="Button" parent="Templates"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 100) +layout_mode = 0 +toggle_mode = true +theme_override_styles/normal = SubResource("StyleBoxFlat_mailbtn") + +[node name="Margin" type="MarginContainer" parent="Templates/MailBtnTemplate"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 12 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 12 + +[node name="VBox" type="VBoxContainer" parent="Templates/MailBtnTemplate/Margin"] +layout_mode = 2 + +[node name="Title" type="Label" parent="Templates/MailBtnTemplate/Margin/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 22 +text = "Message Title" +text_overrun_behavior = 3 + +[node name="Spacer" type="Control" parent="Templates/MailBtnTemplate/Margin/VBox"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="HBox" type="HBoxContainer" parent="Templates/MailBtnTemplate/Margin/VBox"] +layout_mode = 2 + +[node name="DateLbl" type="Label" parent="Templates/MailBtnTemplate/Margin/VBox/HBox"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.9, 0.9, 0.9, 1) +theme_override_font_sizes/font_size = 14 + +[node name="Spacer" type="Control" parent="Templates/MailBtnTemplate/Margin/VBox/HBox"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="StatusLbl" type="Label" parent="Templates/MailBtnTemplate/Margin/VBox/HBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 14 + +[node name="RewardHBoxTemplate" type="PanelContainer" parent="Templates"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 80) +layout_mode = 0 +theme_override_styles/panel = SubResource("StyleBoxFlat_slot") + +[node name="Margin" type="MarginContainer" parent="Templates/RewardHBoxTemplate"] +layout_mode = 2 +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 12 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 12 + +[node name="HBox" type="HBoxContainer" parent="Templates/RewardHBoxTemplate/Margin"] layout_mode = 2 theme_override_constants/separation = 12 -[node name="RewardHBoxTemplate" type="HBoxContainer" parent="Margin/HBox/RightCol/FooterHBox"] -unique_name_in_owner = true -visible = false +[node name="IconBg" type="ColorRect" parent="Templates/RewardHBoxTemplate/Margin/HBox"] +custom_minimum_size = Vector2(56, 56) layout_mode = 2 +color = Color(0, 0, 0, 1) -[node name="Icon" type="TextureRect" parent="Margin/HBox/RightCol/FooterHBox/RewardHBoxTemplate"] -custom_minimum_size = Vector2(32, 32) -layout_mode = 2 +[node name="Icon" type="TextureRect" parent="Templates/RewardHBoxTemplate/Margin/HBox/IconBg"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +expand_mode = 1 +stretch_mode = 5 -[node name="AmountLbl" type="Label" parent="Margin/HBox/RightCol/FooterHBox/RewardHBoxTemplate"] +[node name="VBox" type="VBoxContainer" parent="Templates/RewardHBoxTemplate/Margin/HBox"] layout_mode = 2 -text = "100" +alignment = 1 -[node name="ActionBtn" type="Button" parent="Margin/HBox/RightCol/FooterHBox"] -unique_name_in_owner = true -custom_minimum_size = Vector2(120, 48) +[node name="TypeLbl" type="Label" parent="Templates/RewardHBoxTemplate/Margin/HBox/VBox"] layout_mode = 2 -theme_override_colors/font_color = Color(1, 0.2, 0.2, 1) -text = "DELETE" +theme_override_font_sizes/font_size = 12 +text = "ITEM NAME" + +[node name="AmountLbl" type="Label" parent="Templates/RewardHBoxTemplate/Margin/HBox/VBox"] +layout_mode = 2 +theme_override_font_sizes/font_size = 16 +text = "x00000" diff --git a/scenes/ui/settings_menu.tscn b/scenes/ui/settings_menu.tscn index 280796c..59a7fc7 100644 --- a/scenes/ui/settings_menu.tscn +++ b/scenes/ui/settings_menu.tscn @@ -1,53 +1,16 @@ [gd_scene format=3 uid="uid://b1two2tvv5prx"] [ext_resource type="Script" uid="uid://cdege6m8u5cp" path="res://scripts/ui/settings_menu.gd" id="1_script"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bg"] -bg_color = Color(0.05, 0.05, 0.08, 0.85) -border_width_left = 2 -border_width_top = 2 -border_width_right = 2 -border_width_bottom = 2 -border_color = Color(0.2, 0.8, 1, 0.5) -corner_radius_top_left = 20 -corner_radius_top_right = 20 -corner_radius_bottom_right = 20 -corner_radius_bottom_left = 20 -shadow_color = Color(0, 0, 0, 0.5) -shadow_size = 20 +[ext_resource type="Theme" uid="uid://cxab3xxy00" path="res://assets/themes/GUI_Tekton.tres" id="2_theme"] [sub_resource type="LabelSettings" id="LabelSettings_title"] font_size = 32 font_color = Color(0.2, 0.9, 1, 1) -outline_size = 8 -outline_color = Color(0, 0, 0, 1) - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tab_bg"] -content_margin_left = 20.0 -content_margin_top = 20.0 -content_margin_right = 20.0 -content_margin_bottom = 20.0 -bg_color = Color(0.1, 0.1, 0.15, 0.6) -corner_radius_top_left = 10 -corner_radius_top_right = 10 -corner_radius_bottom_right = 10 -corner_radius_bottom_left = 10 [sub_resource type="LabelSettings" id="LabelSettings_heading"] font_size = 24 font_color = Color(0.9, 0.9, 0.9, 1) -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_section_header"] -content_margin_left = 15.0 -content_margin_top = 5.0 -content_margin_right = 15.0 -content_margin_bottom = 5.0 -bg_color = Color(0.2, 0.5, 0.8, 0.3) -corner_radius_top_left = 5 -corner_radius_top_right = 5 -corner_radius_bottom_right = 5 -corner_radius_bottom_left = 5 - [sub_resource type="LabelSettings" id="LabelSettings_section"] font_size = 18 font_color = Color(0.4, 0.9, 1, 1) @@ -79,47 +42,41 @@ offset_right = 400.0 offset_bottom = 300.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_bg") +theme = ExtResource("2_theme") [node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer" unique_id=1608766484] layout_mode = 2 -theme_override_constants/separation = 20 [node name="Header" type="HBoxContainer" parent="PanelContainer/VBoxContainer" unique_id=1476625293] custom_minimum_size = Vector2(0, 80) layout_mode = 2 -theme_override_constants/separation = 20 + +[node name="PanelContainer" type="Control" parent="PanelContainer/VBoxContainer/Header" unique_id=1523545144] +custom_minimum_size = Vector2(30, 0) +layout_mode = 2 [node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer/Header" unique_id=22122987] layout_mode = 2 size_flags_horizontal = 3 -theme_override_constants/margin_left = 30 [node name="Title" type="Label" parent="PanelContainer/VBoxContainer/Header/MarginContainer" unique_id=1831070364] layout_mode = 2 -text = "SYSTEM SETTINGS" +text = "SETTINGS" label_settings = SubResource("LabelSettings_title") [node name="CloseButton" type="Button" parent="PanelContainer/VBoxContainer/Header" unique_id=1345773906] custom_minimum_size = Vector2(60, 60) layout_mode = 2 size_flags_vertical = 4 -theme_override_font_sizes/font_size = 32 text = "X" flat = true [node name="ContentSection" type="MarginContainer" parent="PanelContainer/VBoxContainer" unique_id=1702280876] layout_mode = 2 size_flags_vertical = 3 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 0 -theme_override_constants/margin_right = 20 -theme_override_constants/margin_bottom = 30 [node name="TabContainer" type="TabContainer" parent="PanelContainer/VBoxContainer/ContentSection" unique_id=1795678842] layout_mode = 2 -theme_override_constants/side_margin = 20 -theme_override_styles/panel = SubResource("StyleBoxFlat_tab_bg") current_tab = 0 [node name="Video" type="ScrollContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer" unique_id=964480166] @@ -129,7 +86,6 @@ metadata/_tab_index = 0 [node name="VBox" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Video" unique_id=1271648419] layout_mode = 2 size_flags_horizontal = 3 -theme_override_constants/separation = 15 [node name="Fullscreen" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Video/VBox" unique_id=683482011] layout_mode = 2 @@ -216,7 +172,6 @@ layout_mode = 2 [node name="Audio" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer" unique_id=1585166620] visible = false layout_mode = 2 -theme_override_constants/separation = 20 metadata/_tab_index = 1 [node name="Master" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Audio" unique_id=2083739941] @@ -272,7 +227,6 @@ metadata/_tab_index = 2 [node name="VBox" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls" unique_id=1592525164] layout_mode = 2 size_flags_horizontal = 3 -theme_override_constants/separation = 15 [node name="ControllerToggleContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000001] layout_mode = 2 @@ -289,7 +243,6 @@ layout_mode = 2 [node name="MovementSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000004] layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_section_header") [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/MovementSection" unique_id=100000005] layout_mode = 2 @@ -298,8 +251,6 @@ label_settings = SubResource("LabelSettings_section") [node name="MoveGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000006] layout_mode = 2 -theme_override_constants/h_separation = 20 -theme_override_constants/v_separation = 10 columns = 3 [node name="ColH1" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/MoveGrid" unique_id=393496035] @@ -391,7 +342,6 @@ text = "Right" [node name="PowerUpSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000015] layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_section_header") [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/PowerUpSection" unique_id=100000016] layout_mode = 2 @@ -400,8 +350,6 @@ label_settings = SubResource("LabelSettings_section") [node name="PowerUpGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000017] layout_mode = 2 -theme_override_constants/h_separation = 20 -theme_override_constants/v_separation = 10 columns = 3 [node name="UsePowerupLabel" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/PowerUpGrid" unique_id=100000118] @@ -424,7 +372,6 @@ text = "N/A" [node name="PowerBarSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000026] layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_section_header") [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/PowerBarSection" unique_id=100000027] layout_mode = 2 @@ -433,8 +380,6 @@ label_settings = SubResource("LabelSettings_section") [node name="PowerBarGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000028] layout_mode = 2 -theme_override_constants/h_separation = 20 -theme_override_constants/v_separation = 10 columns = 3 [node name="AttackLabel" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/PowerBarGrid" unique_id=100000029] @@ -475,7 +420,6 @@ text = "N/A" [node name="OtherSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000033] layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_section_header") [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/OtherSection" unique_id=100000034] layout_mode = 2 @@ -484,8 +428,6 @@ label_settings = SubResource("LabelSettings_section") [node name="OtherGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox" unique_id=100000035] layout_mode = 2 -theme_override_constants/h_separation = 20 -theme_override_constants/v_separation = 10 columns = 3 [node name="GrabLabel" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controls/VBox/OtherGrid" unique_id=100000036] @@ -514,11 +456,9 @@ metadata/_tab_index = 3 [node name="VBox" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller" unique_id=300000002] layout_mode = 2 size_flags_horizontal = 3 -theme_override_constants/separation = 15 [node name="CtrlInfoSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller/VBox" unique_id=300000003] layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_section_header") [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller/VBox/CtrlInfoSection" unique_id=300000004] layout_mode = 2 @@ -527,8 +467,6 @@ label_settings = SubResource("LabelSettings_section") [node name="CtrlGrid" type="GridContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller/VBox" unique_id=300000005] layout_mode = 2 -theme_override_constants/h_separation = 20 -theme_override_constants/v_separation = 10 columns = 2 [node name="ColH1" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/Controller/VBox/CtrlGrid" unique_id=300000006] @@ -606,11 +544,9 @@ metadata/_tab_index = 4 [node name="VBox" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput" unique_id=200000002] layout_mode = 2 size_flags_horizontal = 3 -theme_override_constants/separation = 20 [node name="JoystickSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox" unique_id=200000003] layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_section_header") [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox/JoystickSection" unique_id=200000004] layout_mode = 2 @@ -619,7 +555,6 @@ label_settings = SubResource("LabelSettings_section") [node name="JoystickSizeContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox" unique_id=200000005] layout_mode = 2 -theme_override_constants/separation = 8 [node name="JoystickSizeHeader" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox/JoystickSizeContainer" unique_id=200000006] layout_mode = 2 @@ -662,7 +597,6 @@ button_pressed = true [node name="ButtonsSection" type="PanelContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox" unique_id=200000013] layout_mode = 2 -theme_override_styles/panel = SubResource("StyleBoxFlat_section_header") [node name="Label" type="Label" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox/ButtonsSection" unique_id=200000014] layout_mode = 2 @@ -671,7 +605,6 @@ label_settings = SubResource("LabelSettings_section") [node name="ButtonOpacityContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox" unique_id=200000015] layout_mode = 2 -theme_override_constants/separation = 8 [node name="ButtonOpacityHeader" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ContentSection/TabContainer/TouchInput/VBox/ButtonOpacityContainer" unique_id=200000016] layout_mode = 2 diff --git a/scenes/ui/social_panel.tscn b/scenes/ui/social_panel.tscn index efffea9..b746f50 100644 --- a/scenes/ui/social_panel.tscn +++ b/scenes/ui/social_panel.tscn @@ -104,6 +104,8 @@ size_flags_horizontal = 3 theme_override_styles/normal = SubResource("StyleBoxFlat_tab_inactive") theme_override_styles/hover = SubResource("StyleBoxFlat_tab_inactive") theme_override_styles/pressed = SubResource("StyleBoxFlat_tab_active") +toggle_mode = true +button_pressed = true text = "FIND FRIEND" [node name="FriendsTabBtn" type="Button" parent="Panel/Margin/VBox/TabBar" unique_id=1723617099] @@ -111,9 +113,10 @@ unique_name_in_owner = true custom_minimum_size = Vector2(0, 40) layout_mode = 2 size_flags_horizontal = 3 -theme_override_styles/normal = SubResource("StyleBoxFlat_tab_active") +theme_override_styles/normal = SubResource("StyleBoxFlat_tab_inactive") theme_override_styles/hover = SubResource("StyleBoxFlat_tab_inactive") theme_override_styles/pressed = SubResource("StyleBoxFlat_tab_active") +toggle_mode = true text = "FRIENDS" [node name="HSep1" type="HSeparator" parent="Panel/Margin/VBox" unique_id=549111536] diff --git a/scripts/managers/mail_manager.gd b/scripts/managers/mail_manager.gd index 6dc1739..7d3a1bc 100644 --- a/scripts/managers/mail_manager.gd +++ b/scripts/managers/mail_manager.gd @@ -90,7 +90,52 @@ func delete_mail(mail_id: String) -> bool: func mark_as_read(mail_id: String) -> void: if mail_id in read_ids: return - # Since there's no specific RPC for just marking as read, we can just do a claim with 0 rewards if needed, - # or add an RPC for it. For now we just let claim or delete handle the server-side state. - # Let's add an empty claim or just handle it purely local until claimed/deleted if it has no rewards. - pass + read_ids.append(mail_id) + _update_unread_count() + # Persist read state to server via delete_mail RPC pattern (just saves state) + _save_inbox_state() + +func _save_inbox_state() -> void: + if not NakamaManager.session: return + var state_payload = { + "claimed_ids": claimed_ids, + "deleted_ids": [], + "read_ids": read_ids + } + # We use storage write via a lightweight RPC or direct storage + var r = await NakamaManager.client.rpc_async( + NakamaManager.session, "save_mail_state", + JSON.stringify(state_payload) + ) + if r.is_exception(): + push_warning("[MailManager] Could not save mail state: " + r.get_exception().message) + +func read_all_and_claim_all() -> void: + """Mark all mails as read and claim all unclaimed rewards.""" + if mails.is_empty(): return + + # Mark all as read + for mail in mails: + var mid = mail.get("id", "") + if mid not in read_ids: + read_ids.append(mid) + + # Claim all unclaimed rewards + var to_claim: Array = [] + for mail in mails: + var mid = mail.get("id", "") + if mid in claimed_ids: continue + var rewards = mail.get("rewards", []) + var has_rewards = false + if typeof(rewards) == TYPE_DICTIONARY: + has_rewards = rewards.get("star", 0) > 0 or rewards.get("gold", 0) > 0 + elif typeof(rewards) == TYPE_ARRAY: + has_rewards = rewards.size() > 0 + if has_rewards: + to_claim.append(mid) + + for mid in to_claim: + await claim_reward(mid) + + _update_unread_count() + mail_updated.emit() diff --git a/scripts/ui/admin_panel.gd b/scripts/ui/admin_panel.gd index ebd2ce0..a21668c 100644 --- a/scripts/ui/admin_panel.gd +++ b/scripts/ui/admin_panel.gd @@ -38,6 +38,8 @@ var _current_dr_month: String = "" # Tab: Announcements @onready var target_user_edit := %TargetUserEdit as LineEdit +@onready var find_user_btn := %FindUserBtn as Button +@onready var resolved_id_label := %ResolvedIdLabel as Label @onready var title_edit := %TitleEdit as LineEdit @onready var content_edit := %ContentEdit as TextEdit @onready var start_date_edit := %StartDatePicker as Button @@ -47,6 +49,18 @@ var _current_dr_month: String = "" @onready var reward_row_template := %RewardRowTemplate as HBoxContainer @onready var send_mail_btn := %SendMailBtn as Button +var _resolved_user_id: String = "" + +# Tab: Mail Manager +@onready var mail_tree := %MailTree as Tree +@onready var refresh_mail_btn := %RefreshMailBtn as Button +@onready var edit_mail_btn := %EditMailBtn as Button +@onready var end_mail_btn := %EndMailBtn as Button +@onready var delete_mail_server_btn := %DeleteMailServerBtn as Button + +var _mail_root: TreeItem +var _all_server_mails: Array = [] + const MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] # -- Data -- @@ -140,6 +154,19 @@ func _setup_columns() -> void: lb_tree.set_column_custom_minimum_width(5, 60) _lb_root = lb_tree.create_item() + # Mail Manager + mail_tree.set_column_title(0, "Type") + mail_tree.set_column_title(1, "Title") + mail_tree.set_column_title(2, "Sender") + mail_tree.set_column_title(3, "Start") + mail_tree.set_column_title(4, "Expires") + mail_tree.set_column_title(5, "Status") + mail_tree.set_column_custom_minimum_width(0, 100) + mail_tree.set_column_expand(0, false) + mail_tree.set_column_custom_minimum_width(5, 80) + mail_tree.set_column_expand(5, false) + _mail_root = mail_tree.create_item() + func _connect_signals() -> void: close_btn.pressed.connect(_on_close) refresh_btn.pressed.connect(_on_refresh) @@ -166,6 +193,16 @@ func _connect_signals() -> void: # Announcement actions send_mail_btn.pressed.connect(_on_send_mail) add_reward_btn.pressed.connect(_on_add_reward_pressed) + find_user_btn.pressed.connect(_on_find_user) + target_user_edit.text_changed.connect(func(_t): _resolved_user_id = ""; resolved_id_label.text = "") + + # Mail Manager actions + refresh_mail_btn.pressed.connect(func(): await _load_mail()) + mail_tree.item_selected.connect(_on_mail_item_selected) + edit_mail_btn.pressed.connect(_on_edit_mail_pressed) + end_mail_btn.pressed.connect(_on_end_mail_pressed) + delete_mail_server_btn.pressed.connect(_on_delete_mail_server_pressed) + _update_mail_action_btns(null) # ============================================================================= # Core Panel Logic @@ -189,6 +226,8 @@ func _on_tab_changed(tab_index: int) -> void: await _load_leaderboard() elif tab_index == 2: await _load_daily_rewards_config() + elif tab_index == 4: + await _load_mail() # ============================================================================= # RPC Helper @@ -649,8 +688,60 @@ func _on_add_reward_pressed() -> void: var remove_btn = row.get_node("RemoveBtn") as Button remove_btn.pressed.connect(func(): row.queue_free()) +func _on_find_user() -> void: + var input = target_user_edit.text.strip_edges() + if input.is_empty(): + _resolved_user_id = "" + resolved_id_label.text = "(Global — all users)" + return + + _set_status("Looking up user...") + var uid = await _resolve_target_user_id(input) + if uid.is_empty(): + resolved_id_label.text = "NOT FOUND" + resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_ERR) + _set_status("User not found: " + input, CLR_STATUS_ERR) + else: + _resolved_user_id = uid + resolved_id_label.text = "ID: " + uid.substr(0, 12) + "..." + resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_OK) + _set_status("Found user: " + uid, CLR_STATUS_OK) + +func _resolve_target_user_id(input: String) -> String: + """Resolve a username, display_name, or user_id to a user_id. + Returns empty string if not found.""" + if input.is_empty(): + return "" + + # If it looks like a UUID already, return as-is + if input.length() >= 32 and "-" in input: + return input + + # Search in cached all_users first + for u in all_users: + var uname: String = u.get("username", "") + var dname: String = u.get("display_name", "") + var uid: String = u.get("user_id", "") + if uname.to_lower() == input.to_lower() or dname.to_lower() == input.to_lower(): + return uid + + # Cache miss — fetch from server + var res := await _rpc("admin_list_users", {}) + if res.has("error"): + return "" + all_users = res.get("users", []) + + for u in all_users: + var uname: String = u.get("username", "") + var dname: String = u.get("display_name", "") + var uid: String = u.get("user_id", "") + if uname.to_lower() == input.to_lower() or dname.to_lower() == input.to_lower(): + return uid + + return "" + func _on_send_mail() -> void: - var target = target_user_edit.text.strip_edges() + var input = target_user_edit.text.strip_edges() var title = title_edit.text.strip_edges() var content = content_edit.text.strip_edges() var start_date = start_date_edit.get_date_iso() @@ -659,6 +750,21 @@ func _on_send_mail() -> void: if title.is_empty() or content.is_empty(): _set_status("Title and content cannot be empty", CLR_STATUS_ERR) return + + # Resolve target + var target_uid := "" + if not input.is_empty(): + if not _resolved_user_id.is_empty(): + target_uid = _resolved_user_id + else: + _set_status("Resolving user...") + target_uid = await _resolve_target_user_id(input) + if target_uid.is_empty(): + _set_status("User not found: " + input + ". Use Find button.", CLR_STATUS_ERR) + return + _resolved_user_id = target_uid + resolved_id_label.text = "ID: " + target_uid.substr(0, 12) + "..." + resolved_id_label.add_theme_color_override("font_color", CLR_STATUS_OK) var rewards_arr = [] for child in rewards_list.get_children(): @@ -679,7 +785,7 @@ func _on_send_mail() -> void: _set_status("Sending mail...") var payload = { - "target_user_id": target, + "target_user_id": target_uid, "title": title, "content": content, "start_date": start_date, @@ -694,6 +800,9 @@ func _on_send_mail() -> void: _set_status("Failed to send mail: " + res.get("error", "Unknown"), CLR_STATUS_ERR) else: _set_status("Mail sent successfully!", CLR_STATUS_OK) + target_user_edit.text = "" + _resolved_user_id = "" + resolved_id_label.text = "" title_edit.text = "" content_edit.text = "" if start_date_edit.has_method("clear_date"): @@ -702,3 +811,257 @@ func _on_send_mail() -> void: end_date_edit.clear_date() for child in rewards_list.get_children(): if child.visible: child.queue_free() + +# ============================================================================= +# TAB 5: MAIL MANAGER +# ============================================================================= +func _load_mail() -> void: + _clear_tree(mail_tree, _mail_root) + _set_status("Loading mails...") + + var res := await _rpc("admin_list_mail", {}) + if res.has("error"): + _set_status("Failed: " + str(res.error), CLR_STATUS_ERR) + return + + _all_server_mails = res.get("mails", []) + count_label.text = "%d mails" % _all_server_mails.size() + + var now_str = Time.get_datetime_string_from_system(true) + + for mail in _all_server_mails: + var item := _mail_root.create_child() + var mail_type: String = mail.get("type", "global") + var mail_title: String = mail.get("title", "No Title") + var sender: String = mail.get("sender", "SYSTEM") + var start_date: String = mail.get("start_date", "") + var expiry: String = mail.get("expiry_date", "") + + # Type column + item.set_text(0, mail_type.to_upper()) + if mail_type == "personal": + item.set_custom_color(0, CLR_ADMIN) + else: + item.set_custom_color(0, CLR_MOD) + + # Title + item.set_text(1, mail_title) + + # Sender + item.set_text(2, sender) + + # Start date + item.set_text(3, start_date.substr(0, 10) if start_date.length() >= 10 else start_date) + + # Expiry + var expiry_short = expiry.substr(0, 10) if expiry.length() >= 10 else expiry + item.set_text(4, expiry_short) + + # Status + var end_date: String = mail.get("end_date", "") + var status := "ACTIVE" + if not end_date.is_empty() and now_str > end_date: + status = "ENDED" + item.set_custom_color(5, CLR_BTN_DEL) + elif not expiry.is_empty() and now_str > expiry: + status = "EXPIRED" + item.set_custom_color(5, CLR_BTN_DEL) + else: + item.set_custom_color(5, CLR_STATUS_OK) + item.set_text(5, status) + + item.set_metadata(0, mail) + + _update_mail_action_btns(null) + _set_status("") + +func _on_mail_item_selected() -> void: + var item = mail_tree.get_selected() + _update_mail_action_btns(item) + +func _update_mail_action_btns(item) -> void: + var has_sel = item != null + edit_mail_btn.disabled = not has_sel + end_mail_btn.disabled = not has_sel + delete_mail_server_btn.disabled = not has_sel + +func _get_selected_mail() -> Dictionary: + var item = mail_tree.get_selected() + if item: + return item.get_metadata(0) + return {} + +func _on_edit_mail_pressed() -> void: + var mail = _get_selected_mail() + if mail.is_empty(): return + _show_edit_mail_dialog(mail) + +func _show_edit_mail_dialog(mail: Dictionary) -> void: + var mail_id: String = mail.get("id", "") + var mail_type: String = mail.get("type", "global") + var target_uid: String = mail.get("target_user_id", "") + + var dialog := AcceptDialog.new() + dialog.title = "Edit Mail: " + mail.get("title", "") + dialog.min_size = Vector2i(480, 360) + var vbox := VBoxContainer.new() + vbox.add_theme_constant_override("separation", 10) + + var id_lbl := Label.new() + id_lbl.text = "ID: " + mail_id + " | Type: " + mail_type + id_lbl.add_theme_color_override("font_color", CLR_DIM) + vbox.add_child(id_lbl) + + var grid := GridContainer.new() + grid.columns = 2 + grid.add_theme_constant_override("h_separation", 8) + grid.add_theme_constant_override("v_separation", 8) + + var title_lbl := Label.new(); title_lbl.text = "Title:"; grid.add_child(title_lbl) + var title_input := LineEdit.new(); title_input.text = mail.get("title", ""); title_input.custom_minimum_size.x = 300; grid.add_child(title_input) + + var content_lbl := Label.new(); content_lbl.text = "Content:"; grid.add_child(content_lbl) + var content_input := TextEdit.new(); content_input.text = mail.get("content", ""); content_input.custom_minimum_size = Vector2(300, 100); grid.add_child(content_input) + + var end_lbl := Label.new(); end_lbl.text = "End Date:"; grid.add_child(end_lbl) + var end_picker: Button = load("res://scenes/ui/date_picker.tscn").instantiate() + end_picker.custom_minimum_size.x = 200 + grid.add_child(end_picker) + # Pre-populate if existing end_date + var existing_end: String = mail.get("end_date", "") + if not existing_end.is_empty(): + var parts = existing_end.substr(0, 10).split("-") + if parts.size() == 3: + end_picker.current_date = {"year": int(parts[0]), "month": int(parts[1]), "day": int(parts[2])} + end_picker.view_date = end_picker.current_date.duplicate() + end_picker.text = existing_end.substr(0, 10) + + # Recipient row + var recip_lbl := Label.new(); recip_lbl.text = "Recipient:"; grid.add_child(recip_lbl) + var recip_hbox := HBoxContainer.new() + recip_hbox.add_theme_constant_override("separation", 6) + var recip_input := LineEdit.new() + recip_input.custom_minimum_size.x = 180 + recip_input.placeholder_text = "Username or ID (empty = global)" + if mail_type == "personal" and not target_uid.is_empty(): + recip_input.text = target_uid.substr(0, 12) + "..." + recip_input.tooltip_text = target_uid + recip_hbox.add_child(recip_input) + var recip_find_btn := Button.new() + recip_find_btn.text = "Find" + recip_find_btn.custom_minimum_size.x = 60 + recip_hbox.add_child(recip_find_btn) + var recip_resolved_lbl := Label.new() + recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK) + recip_hbox.add_child(recip_resolved_lbl) + grid.add_child(recip_hbox) + + var _edit_resolved_uid := target_uid + recip_find_btn.pressed.connect(func(): + var inp = recip_input.text.strip_edges() + if inp.is_empty(): + _edit_resolved_uid = "" + recip_resolved_lbl.text = "(Global)" + recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK) + return + var uid = await _resolve_target_user_id(inp) + if uid.is_empty(): + recip_resolved_lbl.text = "NOT FOUND" + recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_ERR) + else: + _edit_resolved_uid = uid + recip_resolved_lbl.text = uid.substr(0, 12) + "..." + recip_resolved_lbl.add_theme_color_override("font_color", CLR_STATUS_OK) + ) + + vbox.add_child(grid) + + var save_btn := Button.new() + save_btn.text = "Save Changes" + save_btn.custom_minimum_size.y = 40 + vbox.add_child(save_btn) + + dialog.add_child(vbox) + add_child(dialog) + dialog.popup_centered() + + save_btn.pressed.connect(func(): + # If user typed something but didn't click Find, auto-resolve + var recip_text = recip_input.text.strip_edges() + var final_target = _edit_resolved_uid + if not recip_text.is_empty() and _edit_resolved_uid == target_uid: + # Text changed but not resolved yet + var resolved = await _resolve_target_user_id(recip_text) + if not resolved.is_empty(): + final_target = resolved + elif recip_text.is_empty(): + final_target = "" + + _set_status("Updating mail...") + var payload = { + "mail_id": mail_id, + "type": mail_type, + "target_user_id": target_uid, + "new_target_user_id": final_target, + "title": title_input.text, + "content": content_input.text, + "end_date": end_picker.get_date_iso() + } + var res = await _rpc("admin_update_mail", payload) + if res.has("success"): + _set_status("Mail updated!", CLR_STATUS_OK) + await _load_mail() + dialog.queue_free() + ) + +func _on_end_mail_pressed() -> void: + var mail = _get_selected_mail() + if mail.is_empty(): return + + var mail_id: String = mail.get("id", "") + var mail_type: String = mail.get("type", "global") + var target_uid: String = mail.get("target_user_id", "") + + var confirm := ConfirmationDialog.new() + confirm.title = "End Mail Now?" + confirm.dialog_text = "Set end_date to NOW for:\n" + mail.get("title", "Unknown") + add_child(confirm) + confirm.popup_centered() + confirm.confirmed.connect(func(): + var now_iso = Time.get_datetime_string_from_system(true) + var res = await _rpc("admin_update_mail", { + "mail_id": mail_id, + "type": mail_type, + "target_user_id": target_uid, + "end_date": now_iso + }) + if res.has("success"): + _set_status("Mail ended", CLR_STATUS_OK) + await _load_mail() + confirm.queue_free() + ) + +func _on_delete_mail_server_pressed() -> void: + var mail = _get_selected_mail() + if mail.is_empty(): return + + var mail_id: String = mail.get("id", "") + var mail_type: String = mail.get("type", "global") + var target_uid: String = mail.get("target_user_id", "") + + var confirm := ConfirmationDialog.new() + confirm.title = "PERMANENTLY Delete Mail?" + confirm.dialog_text = "Remove from server storage:\n" + mail.get("title", "Unknown") + "\n\nThis cannot be undone!" + add_child(confirm) + confirm.popup_centered() + confirm.confirmed.connect(func(): + var res = await _rpc("admin_delete_mail_server", { + "mail_id": mail_id, + "type": mail_type, + "target_user_id": target_uid + }) + if res.has("success"): + _set_status("Mail deleted from server", CLR_STATUS_OK) + await _load_mail() + confirm.queue_free() + ) diff --git a/scripts/ui/mailbox_panel.gd b/scripts/ui/mailbox_panel.gd index 60369a5..a0f920b 100644 --- a/scripts/ui/mailbox_panel.gd +++ b/scripts/ui/mailbox_panel.gd @@ -6,11 +6,13 @@ signal closed @onready var mail_list_vbox := %MailListVBox as VBoxContainer @onready var mail_title_lbl := %MailTitleLbl as Label @onready var mail_content_text := %MailContentText as RichTextLabel -@onready var sender_lbl := %SenderLbl as Label -@onready var dynamic_rewards_container := %DynamicRewardsContainer as HBoxContainer -@onready var reward_hbox_template := %RewardHBoxTemplate as HBoxContainer +@onready var sender_lbl = get_node_or_null("%SenderLbl") +@onready var dynamic_rewards_container := %DynamicRewardsContainer as VBoxContainer +@onready var reward_hbox_template = %RewardHBoxTemplate @onready var action_btn := %ActionBtn as Button +@onready var read_all_btn := %ReadAllBtn as Button @onready var empty_state_lbl := %EmptyStateLbl as Label +@onready var mail_btn_template := %MailBtnTemplate as Button var _current_mail: Dictionary = {} @@ -18,6 +20,7 @@ func _ready() -> void: visible = false close_btn.pressed.connect(hide_panel) action_btn.pressed.connect(_on_action_pressed) + read_all_btn.pressed.connect(_on_read_all_pressed) if MailManager: MailManager.mail_updated.connect(_refresh_ui) @@ -25,6 +28,7 @@ func show_panel() -> void: visible = true _clear_details() if MailManager: + await MailManager.read_all_and_claim_all() MailManager.fetch_mails() _refresh_ui() @@ -57,16 +61,15 @@ func _refresh_ui() -> void: if _current_mail.is_empty() and mails.size() > 0: _on_mail_selected(mails[0]) -@onready var mail_btn_template := %MailBtnTemplate as Button - func _create_mail_button(mail: Dictionary) -> Button: var btn = mail_btn_template.duplicate() btn.visible = true - var title = btn.get_node("VBox/Title") as Label - title.text = mail.get("title", "No Title") + var title_lbl = btn.get_node("Margin/VBox/Title") as Label + if title_lbl: + title_lbl.text = mail.get("title", "No Title") - var date_lbl = btn.get_node("VBox/HBox/DateLbl") as Label + var date_lbl = btn.get_node("Margin/VBox/HBox/DateLbl") as Label var date_str = mail.get("date", "") var expiry_str = mail.get("expiry_date", "") @@ -85,19 +88,21 @@ func _create_mail_button(mail: Dictionary) -> Button: else: label_text += " (Expired)" - date_lbl.text = label_text + if date_lbl: + date_lbl.text = label_text - var status_lbl = btn.get_node("VBox/HBox/StatusLbl") as Label + var status_lbl = btn.get_node("Margin/VBox/HBox/StatusLbl") as Label var mail_id = mail.get("id", "") - if mail_id in MailManager.claimed_ids: - status_lbl.text = "CLAIMED" - status_lbl.add_theme_color_override("font_color", Color.GREEN) - elif mail_id in MailManager.read_ids: - status_lbl.text = "READ" - status_lbl.add_theme_color_override("font_color", Color.GRAY) - else: - status_lbl.text = "NEW" - status_lbl.add_theme_color_override("font_color", Color.YELLOW) + if status_lbl: + if mail_id in MailManager.claimed_ids: + status_lbl.text = "CLAIMED" + status_lbl.add_theme_color_override("font_color", Color.GREEN) + elif mail_id in MailManager.read_ids: + status_lbl.text = "READ" + status_lbl.add_theme_color_override("font_color", Color.GRAY) + else: + status_lbl.text = "NEW" + status_lbl.add_theme_color_override("font_color", Color.YELLOW) return btn @@ -113,7 +118,7 @@ func _on_mail_selected(mail: Dictionary) -> void: func _clear_details() -> void: mail_title_lbl.text = "" mail_content_text.text = "" - sender_lbl.text = "" + if sender_lbl: sender_lbl.text = "" for child in dynamic_rewards_container.get_children(): if child.visible: child.queue_free() action_btn.hide() @@ -121,7 +126,7 @@ func _clear_details() -> void: func _update_details(mail: Dictionary) -> void: mail_title_lbl.text = mail.get("title", "No Title") mail_content_text.text = mail.get("content", "") - sender_lbl.text = "SENDER:\n" + mail.get("sender", "SYSTEM") + if sender_lbl: sender_lbl.text = "SENDER:\n" + mail.get("sender", "SYSTEM") for child in dynamic_rewards_container.get_children(): if child.visible: child.queue_free() @@ -141,16 +146,29 @@ func _update_details(mail: Dictionary) -> void: var row = reward_hbox_template.duplicate() row.visible = true dynamic_rewards_container.add_child(row) - var amt_lbl = row.get_node("AmountLbl") as Label + var amt_lbl = row.get_node("Margin/HBox/VBox/AmountLbl") as Label + var type_lbl = row.get_node("Margin/HBox/VBox/TypeLbl") as Label var t = r.get("type", "star") var amt = r.get("amount", 0) - var id = r.get("id", "") + var rid = r.get("id", "") - if t == "star" or t == "gold": - amt_lbl.text = str(amt) + " " + t.to_upper() - else: - amt_lbl.text = str(amt) + " " + id + if amt_lbl: amt_lbl.text = "x" + str(amt) + if type_lbl: type_lbl.text = t.to_upper() if (t == "star" or t == "gold") else rid.to_upper() + + # Fill empty slots up to 4 + var added = rewards.size() + while added < 4: + added += 1 + var empty_row = reward_hbox_template.duplicate() + empty_row.visible = true + dynamic_rewards_container.add_child(empty_row) + var icon_bg = empty_row.get_node_or_null("Margin/HBox/IconBg") + if icon_bg: icon_bg.color = Color(0, 0, 0, 0) + var t_lbl = empty_row.get_node_or_null("Margin/HBox/VBox/TypeLbl") + if t_lbl: t_lbl.text = "" + var a_lbl = empty_row.get_node_or_null("Margin/HBox/VBox/AmountLbl") + if a_lbl: a_lbl.text = "" action_btn.show() var mail_id = mail.get("id", "") @@ -178,3 +196,11 @@ func _on_action_pressed() -> void: if ok: _current_mail = {} _clear_details() + +func _on_read_all_pressed() -> void: + if not MailManager: return + for mail in MailManager.mails: + var mid = mail.get("id", "") + if mid not in MailManager.read_ids: + MailManager.mark_as_read(mid) + _refresh_ui() diff --git a/scripts/ui/social_panel.gd b/scripts/ui/social_panel.gd index 19c789a..fbbee3c 100644 --- a/scripts/ui/social_panel.gd +++ b/scripts/ui/social_panel.gd @@ -83,6 +83,11 @@ func _show_tab(tab: String) -> void: _friends_view.visible = tab == "friends" _dm_view.visible = tab == "dm" + _search_tab_btn.set_pressed_no_signal(tab == "search") + _friends_tab_btn.set_pressed_no_signal(tab == "friends") + if _dm_tab_btn: + _dm_tab_btn.set_pressed_no_signal(tab == "dm") + # Auto-load search results on first open if tab == "search" and _search_results_list.get_child_count() == 0: _on_search_pressed() diff --git a/server/nakama/tekton_admin.js b/server/nakama/tekton_admin.js index cc67b2a..27ce563 100644 --- a/server/nakama/tekton_admin.js +++ b/server/nakama/tekton_admin.js @@ -52,9 +52,13 @@ function InitModule(ctx, logger, nk, initializer) { // Inbox System RPCs initializer.registerRpc("admin_send_mail", rpcAdminSendMail); + initializer.registerRpc("admin_list_mail", rpcAdminListMail); + initializer.registerRpc("admin_update_mail", rpcAdminUpdateMail); + initializer.registerRpc("admin_delete_mail_server", rpcAdminDeleteMailServer); initializer.registerRpc("get_mail", rpcGetMail); initializer.registerRpc("claim_mail_reward", rpcClaimMailReward); initializer.registerRpc("delete_mail", rpcDeleteMail); + initializer.registerRpc("save_mail_state", rpcSaveMailState); // Steam auth hooks initializer.registerAfterAuthenticateSteam(afterAuthenticateSteam); @@ -1712,3 +1716,229 @@ function rpcDeleteMail(ctx, logger, nk, payload) { return JSON.stringify({ success: true, deleted_ids: state.deleted_ids }); } + +function rpcSaveMailState(ctx, logger, nk, payload) { + if (!ctx.userId) throw new Error("Not authenticated"); + var request = JSON.parse(payload || "{}"); + + // Load existing state to merge (don't clobber claimed_ids from client) + var stateObjs = nk.storageRead([{ collection: "inbox", key: "state", userId: ctx.userId }]); + var state = { claimed_ids: [], deleted_ids: [], read_ids: [] }; + if (stateObjs && stateObjs.length > 0) { + var val = stateObjs[0].value; + state.claimed_ids = val.claimed_ids || []; + state.deleted_ids = val.deleted_ids || []; + state.read_ids = val.read_ids || []; + } + + // Merge read_ids from client (add any new ones) + var newReadIds = request.read_ids || []; + for (var i = 0; i < newReadIds.length; i++) { + if (state.read_ids.indexOf(newReadIds[i]) === -1) { + state.read_ids.push(newReadIds[i]); + } + } + + nk.storageWrite([{ + collection: "inbox", + key: "state", + userId: ctx.userId, + value: state, + permissionRead: 1, + permissionWrite: 0 + }]); + + return JSON.stringify({ success: true }); +} + +// ============================================================================= +// Admin Mail Management RPCs +// ============================================================================= + +function rpcAdminListMail(ctx, logger, nk, payload) { + requireAdmin(ctx, nk); + + // --- Global mails --- + var globalObjs = nk.storageRead([{ collection: "config", key: "global_mail", userId: "00000000-0000-0000-0000-000000000000" }]); + var globalMails = (globalObjs && globalObjs.length > 0) ? (globalObjs[0].value.mails || []) : []; + for (var i = 0; i < globalMails.length; i++) { + globalMails[i].type = "global"; + } + + // --- Personal mails: scan ALL users' inbox/personal objects --- + var personalMails = []; + var cursor = null; + try { + do { + // storageList(userId, collection, limit, cursor) + // Using empty string userId means list across all users for this collection + var listResult = nk.storageList("", "inbox", 100, cursor); + var objects = listResult.objects || []; + for (var j = 0; j < objects.length; j++) { + var obj = objects[j]; + if (obj.key !== "personal") continue; + var ownerUserId = obj.userId; + var mails = obj.value.mails || []; + for (var k = 0; k < mails.length; k++) { + var m = mails[k]; + m.type = "personal"; + m.target_user_id = ownerUserId; + personalMails.push(m); + } + } + cursor = listResult.cursor || null; + } while (cursor); + } catch (e) { + logger.warn("admin_list_mail: could not list personal inboxes: " + e); + } + + var allMails = globalMails.concat(personalMails); + // Sort newest first + allMails.sort(function(a, b) { + return (b.date || "").localeCompare(a.date || ""); + }); + + return JSON.stringify({ mails: allMails }); +} + +function rpcAdminUpdateMail(ctx, logger, nk, payload) { + requireAdmin(ctx, nk); + var request = JSON.parse(payload || "{}"); + var mailId = request.mail_id; + if (!mailId) throw new Error("mail_id required"); + + var isGlobal = request.type !== "personal"; + var targetUserId = request.target_user_id || ""; + var newTargetUserId = request.new_target_user_id; + var hasNewTarget = (newTargetUserId !== undefined && newTargetUserId !== null); + + // Step 1: Find and extract the mail object from its current location + var mailObj = null; + + if (isGlobal) { + var globalObjs = nk.storageRead([{ collection: "config", key: "global_mail", userId: "00000000-0000-0000-0000-000000000000" }]); + var globalMails = (globalObjs && globalObjs.length > 0) ? (globalObjs[0].value.mails || []) : []; + for (var i = 0; i < globalMails.length; i++) { + if (globalMails[i].id === mailId) { + mailObj = globalMails.splice(i, 1)[0]; + break; + } + } + if (!mailObj) throw new Error("Mail not found in global"); + // Write back without this mail (it may move) + nk.storageWrite([{ + collection: "config", + key: "global_mail", + userId: "00000000-0000-0000-0000-000000000000", + value: { mails: globalMails }, + permissionRead: 2, + permissionWrite: 0 + }]); + } else { + if (!targetUserId) throw new Error("target_user_id required for personal mail"); + var pObjs = nk.storageRead([{ collection: "inbox", key: "personal", userId: targetUserId }]); + var personalMails = (pObjs && pObjs.length > 0) ? (pObjs[0].value.mails || []) : []; + for (var j = 0; j < personalMails.length; j++) { + if (personalMails[j].id === mailId) { + mailObj = personalMails.splice(j, 1)[0]; + break; + } + } + if (!mailObj) throw new Error("Mail not found in personal inbox"); + // Write back without this mail + nk.storageWrite([{ + collection: "inbox", + key: "personal", + userId: targetUserId, + value: { mails: personalMails }, + permissionRead: 1, + permissionWrite: 0 + }]); + } + + // Step 2: Apply field updates to the mail object + if (request.title !== undefined) mailObj.title = request.title; + if (request.content !== undefined) mailObj.content = request.content; + if (request.end_date !== undefined) mailObj.end_date = request.end_date; + if (request.expiry_date !== undefined) mailObj.expiry_date = request.expiry_date; + + // Step 3: Determine destination + var destUserId = hasNewTarget ? newTargetUserId : (isGlobal ? "" : targetUserId); + + if (destUserId === "") { + // Write to global + mailObj.type = "global"; + var gObjs = nk.storageRead([{ collection: "config", key: "global_mail", userId: "00000000-0000-0000-0000-000000000000" }]); + var gMails = (gObjs && gObjs.length > 0) ? (gObjs[0].value.mails || []) : []; + gMails.push(mailObj); + nk.storageWrite([{ + collection: "config", + key: "global_mail", + userId: "00000000-0000-0000-0000-000000000000", + value: { mails: gMails }, + permissionRead: 2, + permissionWrite: 0 + }]); + } else { + // Write to personal inbox of destUserId + mailObj.type = "personal"; + var dObjs = nk.storageRead([{ collection: "inbox", key: "personal", userId: destUserId }]); + var dMails = (dObjs && dObjs.length > 0) ? (dObjs[0].value.mails || []) : []; + dMails.push(mailObj); + nk.storageWrite([{ + collection: "inbox", + key: "personal", + userId: destUserId, + value: { mails: dMails }, + permissionRead: 1, + permissionWrite: 0 + }]); + } + + logger.info("Admin updated mail " + mailId + " by " + ctx.userId + (hasNewTarget ? " (moved to " + destUserId + ")" : "")); + return JSON.stringify({ success: true }); +} + +function rpcAdminDeleteMailServer(ctx, logger, nk, payload) { + requireAdmin(ctx, nk); + var request = JSON.parse(payload || "{}"); + var mailId = request.mail_id; + if (!mailId) throw new Error("mail_id required"); + + var isGlobal = request.type !== "personal"; + var targetUserId = request.target_user_id || ""; + + if (isGlobal) { + var globalObjs = nk.storageRead([{ collection: "config", key: "global_mail", userId: "00000000-0000-0000-0000-000000000000" }]); + var globalMails = (globalObjs && globalObjs.length > 0) ? (globalObjs[0].value.mails || []) : []; + var before = globalMails.length; + globalMails = globalMails.filter(function(m) { return m.id !== mailId; }); + if (globalMails.length === before) throw new Error("Mail not found"); + nk.storageWrite([{ + collection: "config", + key: "global_mail", + userId: "00000000-0000-0000-0000-000000000000", + value: { mails: globalMails }, + permissionRead: 2, + permissionWrite: 0 + }]); + } else { + if (!targetUserId) throw new Error("target_user_id required for personal mail"); + var pObjs = nk.storageRead([{ collection: "inbox", key: "personal", userId: targetUserId }]); + var personalMails = (pObjs && pObjs.length > 0) ? (pObjs[0].value.mails || []) : []; + var pBefore = personalMails.length; + personalMails = personalMails.filter(function(m) { return m.id !== mailId; }); + if (personalMails.length === pBefore) throw new Error("Mail not found"); + nk.storageWrite([{ + collection: "inbox", + key: "personal", + userId: targetUserId, + value: { mails: personalMails }, + permissionRead: 1, + permissionWrite: 0 + }]); + } + + logger.info("Admin deleted mail " + mailId + " from server by " + ctx.userId); + return JSON.stringify({ success: true }); +}