From 02d13d9ff5b4a6b6f2a1d3f97eeb75c0e95a02e3 Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Tue, 3 Feb 2026 14:48:41 +0800 Subject: [PATCH] feat: Add main game scene with core logic, manager initialization, UI setup, and an in-game message bar. --- .../enhanced_gridmap/meshlibrary/default.tres | 62 +--- .../touch_control/attack_mode.png.import | 9 +- .../touch_control/freeze_area.png.import | 9 +- .../touch_control/put_tile.png.import | 9 +- .../touch_control/spawn_tile.png.import | 9 +- .../graphics/touch_control/speed.png.import | 9 +- .../touch_control/take_tile.png.import | 9 +- assets/graphics/touch_control/wall.png.import | 9 +- assets/models/tiles/tile_ghost.tres | 5 - scenes/main.gd | 6 + scenes/main.tscn | 114 ++++---- scripts/managers/special_tiles_manager.gd | 13 +- scripts/ui/powerup_inventory_ui.gd | 273 +++++++++--------- 13 files changed, 257 insertions(+), 279 deletions(-) diff --git a/addons/enhanced_gridmap/meshlibrary/default.tres b/addons/enhanced_gridmap/meshlibrary/default.tres index ca68458..f0bf1d9 100644 --- a/addons/enhanced_gridmap/meshlibrary/default.tres +++ b/addons/enhanced_gridmap/meshlibrary/default.tres @@ -6,51 +6,11 @@ [ext_resource type="ArrayMesh" uid="uid://brevl3ab0tdqe" path="res://assets/models/tiles/tile_wall.tres" id="4_8v5xv"] [ext_resource type="ArrayMesh" uid="uid://b5ta7tcw0iscd" path="res://assets/models/tiles/tile_coin.tres" id="4_76xkl"] [ext_resource type="ArrayMesh" uid="uid://d4himvyb81in8" path="res://assets/models/meshes/non-walkable.res" id="4_sx8rm"] -[ext_resource type="ArrayMesh" uid="uid://dr80txgr61irt" path="res://assets/models/tiles/tile_diamond.tres" id="5_sx8rm"] +[ext_resource type="ArrayMesh" uid="uid://dr80txgr61irt" path="res://assets/models/tiles/tile_area_freeze.tres" id="5_sx8rm"] [ext_resource type="ArrayMesh" uid="uid://bqvqj3fhf5x51" path="res://assets/models/tiles/tile_ghost.tres" id="6_r32il"] [ext_resource type="ArrayMesh" uid="uid://cv4bedhida00g" path="res://assets/models/tiles/tile_star.tres" id="7_p5epg"] - -[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_ghs0t"] -load_path = "res://.godot/imported/tile_coin_holo.png-6a443a1d36bcd3bf79f0210c252c3d26.s3tc.ctex" - -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_5pajh"] -resource_name = "boost" -transparency = 1 -cull_mode = 2 -albedo_color = Color(0.91, 0.91, 0.91, 0.45098) -albedo_texture = SubResource("CompressedTexture2D_ghs0t") - -[sub_resource type="ArrayMesh" id="ArrayMesh_ghs0t"] -_surfaces = [{ -"aabb": AABB(-0.282176, -0.000324821, -0.282176, 0.564351, 0.050792, 0.564351), -"format": 34896613377, -"index_count": 36, -"index_data": PackedByteArray("BwAEAAUABwAGAAQABQACAAMABQAEAAIAAAAEAAYAAAACAAQABQABAAcABQADAAEAAgABAAMAAgAAAAEAAQAGAAcAAQAAAAYA"), -"name": "boost", -"primitive": 3, -"uv_scale": Vector4(0, 0, 0, 0), -"vertex_count": 8, -"vertex_data": PackedByteArray("AAD/////AAAAAP7/AAAAAP///////wAA///+/wAAAAD//wAA//8AAP//AAAAAAAAAAAAAP//AAAAAAAAAAAAAA==") -}] -blend_shape_mode = 0 - -[sub_resource type="ArrayMesh" id="ArrayMesh_cg50n"] -resource_name = "tile_diamond" -_surfaces = [{ -"aabb": AABB(-0.282176, -0.000324821, -0.282176, 0.564351, 0.050792, 0.564351), -"attribute_data": PackedByteArray("sPss5W0hot47+9DosPv6720hXCE7+6/6sPvl75Heot6r9qfksPu0+pHeXCHM5KfkSvvl78X6sfqr9kDkSvu0+sX6tuTM5EDkSvss5c7ksfrU+tDoSvv6787ktuTU+q/6"), -"format": 34896613399, -"index_count": 36, -"index_data": PackedByteArray("FgANABAAFgATAA0AEQAIAAsAEQAOAAgAAAAMABIAAAAGAAwADwADABUADwAJAAMABwAEAAoABwABAAQABQAUABcABQACABQA"), -"material": SubResource("StandardMaterial3D_5pajh"), -"name": "tile_coin_holo", -"primitive": 3, -"uv_scale": Vector4(0, 0, 0, 0), -"vertex_count": 24, -"vertex_data": PackedByteArray("AAD//////78AAP//////vwAA/////6oqAAD+/wAAAAAAAP7/AAD/vwAA/v8AAKoq/////////7//////////v//////////////+/wAAAAD///7/AAD/v////v8AAP////8AAP///7///wAA////P///AAD///////8AAAAAAAD//wAAAAD/P///AAAAAP//AAAAAP///78AAAAA////PwAAAAD//6oqAAAAAAAAAAAAAAAAAAD/PwAAAAAAAKoq/////////39U1VTV/7//v////39U1VTV/////////3//v/9//7//v////3//v/9//////wAA/3//v/9//7//vwAA/3//v/9//////wAA/39U1VTV/7//vwAA/39U1VTV") -}] -blend_shape_mode = 0 -shadow_mesh = SubResource("ArrayMesh_ghs0t") +[ext_resource type="ArrayMesh" uid="uid://gpnl4cjrivor" path="res://assets/models/tiles/tile_speed.tres" id="7_sx8rm"] +[ext_resource type="ArrayMesh" uid="uid://dr80txgr61irt" path="res://assets/models/tiles/tile_diamond.tres" id="10_r32il"] [sub_resource type="CompressedTexture2D" id="CompressedTexture2D_5d0gc"] load_path = "res://.godot/imported/tile_heart.png-deeef50755ca225f028608dfd16900e6.s3tc.ctex" @@ -150,7 +110,7 @@ item/7/shapes = [] item/7/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/7/navigation_layers = 1 item/8/name = "tile_diamond" -item/8/mesh = ExtResource("5_sx8rm") +item/8/mesh = ExtResource("10_r32il") item/8/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/8/mesh_cast_shadow = 1 item/8/shapes = [] @@ -170,29 +130,29 @@ item/10/mesh_cast_shadow = 1 item/10/shapes = [] item/10/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/10/navigation_layers = 1 -item/11/name = "tile_heart_holo" -item/11/mesh = ExtResource("4_8v5xv") +item/11/name = "tile_speed" +item/11/mesh = ExtResource("7_sx8rm") item/11/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/11/mesh_cast_shadow = 1 item/11/shapes = [] item/11/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/11/navigation_layers = 1 -item/12/name = "tile_diamond_holo" +item/12/name = "tile_area_freeze" item/12/mesh = ExtResource("5_sx8rm") item/12/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/12/mesh_cast_shadow = 1 item/12/shapes = [] item/12/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/12/navigation_layers = 1 -item/13/name = "tile_star_holo" -item/13/mesh = ExtResource("6_r32il") +item/13/name = "tile_wall" +item/13/mesh = ExtResource("4_8v5xv") item/13/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/13/mesh_cast_shadow = 1 item/13/shapes = [] item/13/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/13/navigation_layers = 1 -item/14/name = "tile_coin_holo" -item/14/mesh = SubResource("ArrayMesh_cg50n") +item/14/name = "tile_ghost" +item/14/mesh = ExtResource("6_r32il") item/14/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) item/14/mesh_cast_shadow = 1 item/14/shapes = [] diff --git a/assets/graphics/touch_control/attack_mode.png.import b/assets/graphics/touch_control/attack_mode.png.import index 2882e84..5b2639e 100644 --- a/assets/graphics/touch_control/attack_mode.png.import +++ b/assets/graphics/touch_control/attack_mode.png.import @@ -3,19 +3,20 @@ importer="texture" type="CompressedTexture2D" uid="uid://umw3e8nfe3vr" -path="res://.godot/imported/attack_mode.png-c8d2e720b153f717981c069694b99c1d.ctex" +path.s3tc="res://.godot/imported/attack_mode.png-c8d2e720b153f717981c069694b99c1d.s3tc.ctex" metadata={ -"vram_texture": false +"imported_formats": ["s3tc_bptc"], +"vram_texture": true } [deps] source_file="res://assets/graphics/touch_control/attack_mode.png" -dest_files=["res://.godot/imported/attack_mode.png-c8d2e720b153f717981c069694b99c1d.ctex"] +dest_files=["res://.godot/imported/attack_mode.png-c8d2e720b153f717981c069694b99c1d.s3tc.ctex"] [params] -compress/mode=0 +compress/mode=2 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 diff --git a/assets/graphics/touch_control/freeze_area.png.import b/assets/graphics/touch_control/freeze_area.png.import index 86853bd..7b85adc 100644 --- a/assets/graphics/touch_control/freeze_area.png.import +++ b/assets/graphics/touch_control/freeze_area.png.import @@ -3,19 +3,20 @@ importer="texture" type="CompressedTexture2D" uid="uid://3up2su2e0lfa" -path="res://.godot/imported/freeze_area.png-637e16813f4e334856ce1077dd0a8f60.ctex" +path.s3tc="res://.godot/imported/freeze_area.png-637e16813f4e334856ce1077dd0a8f60.s3tc.ctex" metadata={ -"vram_texture": false +"imported_formats": ["s3tc_bptc"], +"vram_texture": true } [deps] source_file="res://assets/graphics/touch_control/freeze_area.png" -dest_files=["res://.godot/imported/freeze_area.png-637e16813f4e334856ce1077dd0a8f60.ctex"] +dest_files=["res://.godot/imported/freeze_area.png-637e16813f4e334856ce1077dd0a8f60.s3tc.ctex"] [params] -compress/mode=0 +compress/mode=2 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 diff --git a/assets/graphics/touch_control/put_tile.png.import b/assets/graphics/touch_control/put_tile.png.import index fe9fb0d..6f7d26a 100644 --- a/assets/graphics/touch_control/put_tile.png.import +++ b/assets/graphics/touch_control/put_tile.png.import @@ -3,19 +3,20 @@ importer="texture" type="CompressedTexture2D" uid="uid://pwxo4lb87yi" -path="res://.godot/imported/put_tile.png-076fc15f3cb4549d9803338227d28dc3.ctex" +path.s3tc="res://.godot/imported/put_tile.png-076fc15f3cb4549d9803338227d28dc3.s3tc.ctex" metadata={ -"vram_texture": false +"imported_formats": ["s3tc_bptc"], +"vram_texture": true } [deps] source_file="res://assets/graphics/touch_control/put_tile.png" -dest_files=["res://.godot/imported/put_tile.png-076fc15f3cb4549d9803338227d28dc3.ctex"] +dest_files=["res://.godot/imported/put_tile.png-076fc15f3cb4549d9803338227d28dc3.s3tc.ctex"] [params] -compress/mode=0 +compress/mode=2 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 diff --git a/assets/graphics/touch_control/spawn_tile.png.import b/assets/graphics/touch_control/spawn_tile.png.import index 9782427..d4756ba 100644 --- a/assets/graphics/touch_control/spawn_tile.png.import +++ b/assets/graphics/touch_control/spawn_tile.png.import @@ -3,19 +3,20 @@ importer="texture" type="CompressedTexture2D" uid="uid://ckhdyxnho6sjp" -path="res://.godot/imported/spawn_tile.png-1538ef0a9fcda66388ef4cde6070b0fa.ctex" +path.s3tc="res://.godot/imported/spawn_tile.png-1538ef0a9fcda66388ef4cde6070b0fa.s3tc.ctex" metadata={ -"vram_texture": false +"imported_formats": ["s3tc_bptc"], +"vram_texture": true } [deps] source_file="res://assets/graphics/touch_control/spawn_tile.png" -dest_files=["res://.godot/imported/spawn_tile.png-1538ef0a9fcda66388ef4cde6070b0fa.ctex"] +dest_files=["res://.godot/imported/spawn_tile.png-1538ef0a9fcda66388ef4cde6070b0fa.s3tc.ctex"] [params] -compress/mode=0 +compress/mode=2 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 diff --git a/assets/graphics/touch_control/speed.png.import b/assets/graphics/touch_control/speed.png.import index c1bd1b2..747e920 100644 --- a/assets/graphics/touch_control/speed.png.import +++ b/assets/graphics/touch_control/speed.png.import @@ -3,19 +3,20 @@ importer="texture" type="CompressedTexture2D" uid="uid://bsgqrjx2ity4c" -path="res://.godot/imported/speed.png-b1b011b3242f4e45ee38a2ab2d8e9378.ctex" +path.s3tc="res://.godot/imported/speed.png-b1b011b3242f4e45ee38a2ab2d8e9378.s3tc.ctex" metadata={ -"vram_texture": false +"imported_formats": ["s3tc_bptc"], +"vram_texture": true } [deps] source_file="res://assets/graphics/touch_control/speed.png" -dest_files=["res://.godot/imported/speed.png-b1b011b3242f4e45ee38a2ab2d8e9378.ctex"] +dest_files=["res://.godot/imported/speed.png-b1b011b3242f4e45ee38a2ab2d8e9378.s3tc.ctex"] [params] -compress/mode=0 +compress/mode=2 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 diff --git a/assets/graphics/touch_control/take_tile.png.import b/assets/graphics/touch_control/take_tile.png.import index 9780c81..69cb703 100644 --- a/assets/graphics/touch_control/take_tile.png.import +++ b/assets/graphics/touch_control/take_tile.png.import @@ -3,19 +3,20 @@ importer="texture" type="CompressedTexture2D" uid="uid://ba80xnybpixw2" -path="res://.godot/imported/take_tile.png-e2d53446f322555ce2a2ebc189d0edb9.ctex" +path.s3tc="res://.godot/imported/take_tile.png-e2d53446f322555ce2a2ebc189d0edb9.s3tc.ctex" metadata={ -"vram_texture": false +"imported_formats": ["s3tc_bptc"], +"vram_texture": true } [deps] source_file="res://assets/graphics/touch_control/take_tile.png" -dest_files=["res://.godot/imported/take_tile.png-e2d53446f322555ce2a2ebc189d0edb9.ctex"] +dest_files=["res://.godot/imported/take_tile.png-e2d53446f322555ce2a2ebc189d0edb9.s3tc.ctex"] [params] -compress/mode=0 +compress/mode=2 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 diff --git a/assets/graphics/touch_control/wall.png.import b/assets/graphics/touch_control/wall.png.import index a37eb84..8b4de4a 100644 --- a/assets/graphics/touch_control/wall.png.import +++ b/assets/graphics/touch_control/wall.png.import @@ -3,19 +3,20 @@ importer="texture" type="CompressedTexture2D" uid="uid://cupfmb5m15kmf" -path="res://.godot/imported/wall.png-3792c1a2a09d1bc9ce89d91015b9b816.ctex" +path.s3tc="res://.godot/imported/wall.png-3792c1a2a09d1bc9ce89d91015b9b816.s3tc.ctex" metadata={ -"vram_texture": false +"imported_formats": ["s3tc_bptc"], +"vram_texture": true } [deps] source_file="res://assets/graphics/touch_control/wall.png" -dest_files=["res://.godot/imported/wall.png-3792c1a2a09d1bc9ce89d91015b9b816.ctex"] +dest_files=["res://.godot/imported/wall.png-3792c1a2a09d1bc9ce89d91015b9b816.s3tc.ctex"] [params] -compress/mode=0 +compress/mode=2 compress/high_quality=false compress/lossy_quality=0.7 compress/uastc_level=0 diff --git a/assets/models/tiles/tile_ghost.tres b/assets/models/tiles/tile_ghost.tres index a758eb0..d1886f9 100644 --- a/assets/models/tiles/tile_ghost.tres +++ b/assets/models/tiles/tile_ghost.tres @@ -1,14 +1,9 @@ [gd_resource type="ArrayMesh" format=4 uid="uid://bqvqj3fhf5x51"] -[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_7uoqg"] -load_path = "res://.godot/imported/tile_star_holo.png-4aade3ef0db71672f9bd2143ab924c6e.s3tc.ctex" - [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ev6sk"] resource_name = "boost" -transparency = 1 cull_mode = 2 albedo_color = Color(0.91, 0.91, 0.91, 0.45098) -albedo_texture = SubResource("CompressedTexture2D_7uoqg") [sub_resource type="ArrayMesh" id="ArrayMesh_w3aij"] _surfaces = [{ diff --git a/scenes/main.gd b/scenes/main.gd index 998461e..11621a8 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -459,6 +459,12 @@ func _setup_client_game(): touch_controls.set_player(player_character) ui_manager.update_button_states() print("Client: Configured local player ", my_id) + + # ALWAYS setup PowerUpUI when we have the local player, just in case + var powerup_ui = get_node_or_null("PowerUpInventoryUI") + if powerup_ui: + powerup_ui.setup(player_character) + print("Client: PowerUpInventoryUI setup forced for ", my_id) # Wait shorter time for host to be ready, then request full sync to correct positions/state await get_tree().create_timer(1.0).timeout diff --git a/scenes/main.tscn b/scenes/main.tscn index a89d0d8..c35314c 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -22,9 +22,13 @@ [ext_resource type="StyleBox" uid="uid://d3ruc8gytoovx" path="res://assets/styles/ribbon_selected_gui.tres" id="18_u5x6e"] [ext_resource type="StyleBox" uid="uid://cdhnwvcklbyl8" path="res://assets/styles/ribbon_hovered_gui.tres" id="19_w1rqq"] [ext_resource type="StyleBox" uid="uid://3yog1weaqhxb" path="res://assets/styles/ribbon_unselected_gui.tres" id="20_q6bc1"] +[ext_resource type="Texture2D" uid="uid://ba80xnybpixw2" path="res://assets/graphics/touch_control/take_tile.png" id="25_qkpxi"] [ext_resource type="Texture2D" uid="uid://bsgqrjx2ity4c" path="res://assets/graphics/touch_control/speed.png" id="26_2f3dj"] +[ext_resource type="Texture2D" uid="uid://pwxo4lb87yi" path="res://assets/graphics/touch_control/put_tile.png" id="26_5q0nq"] +[ext_resource type="Texture2D" uid="uid://umw3e8nfe3vr" path="res://assets/graphics/touch_control/attack_mode.png" id="27_dgi5k"] [ext_resource type="Texture2D" uid="uid://cupfmb5m15kmf" path="res://assets/graphics/touch_control/wall.png" id="27_yq6so"] [ext_resource type="Texture2D" uid="uid://3up2su2e0lfa" path="res://assets/graphics/touch_control/freeze_area.png" id="28_fv21b"] +[ext_resource type="Texture2D" uid="uid://ckhdyxnho6sjp" path="res://assets/graphics/touch_control/spawn_tile.png" id="28_j8jky"] [ext_resource type="Script" uid="uid://86ikh0wuqk7v" path="res://scripts/ui/powerup_inventory_ui.gd" id="powerup_ui_script"] [ext_resource type="Script" uid="uid://b54tfa0n6kogi" path="res://scripts/managers/touch_controls.gd" id="touch_manager"] [ext_resource type="Script" uid="uid://djiml4sh61dc1" path="res://scripts/ui/virtual_joystick.gd" id="virtual_joystick"] @@ -9718,7 +9722,7 @@ anchor_bottom = 1.0 offset_top = 318.0 grow_horizontal = 2 grow_vertical = 2 -mouse_filter = 1 +mouse_filter = 2 [node name="VirtualJoystick" type="Control" parent="TouchControls/TouchControls" unique_id=1983608919] layout_mode = 1 @@ -9745,7 +9749,10 @@ offset_right = -233.0 offset_bottom = -179.0 grow_horizontal = 0 grow_vertical = 0 -text = "👋" +focus_mode = 0 +icon = ExtResource("25_qkpxi") +flat = true +expand_icon = true [node name="PutBtn" type="Button" parent="TouchControls/TouchControls" unique_id=1027790362] layout_mode = 1 @@ -9760,7 +9767,10 @@ offset_right = -153.0 offset_bottom = -99.0 grow_horizontal = 0 grow_vertical = 0 -text = "📦" +focus_mode = 0 +icon = ExtResource("26_5q0nq") +flat = true +expand_icon = true [node name="SpecialBtn" type="Button" parent="TouchControls/TouchControls" unique_id=1380511463] layout_mode = 1 @@ -9775,7 +9785,10 @@ offset_right = -153.0 offset_bottom = -253.0 grow_horizontal = 0 grow_vertical = 0 -text = "⚡" +focus_mode = 0 +icon = ExtResource("27_dgi5k") +flat = true +expand_icon = true [node name="SpawnBoostBtn" type="Button" parent="TouchControls/TouchControls" unique_id=1566173505] layout_mode = 1 @@ -9790,7 +9803,10 @@ offset_right = -73.0 offset_bottom = -171.0 grow_horizontal = 0 grow_vertical = 0 -text = "🚀" +icon = ExtResource("28_j8jky") +flat = true +icon_alignment = 1 +expand_icon = true [node name="SettingsBtn" type="Button" parent="TouchControls/TouchControls" unique_id=1964422444] layout_mode = 1 @@ -9827,72 +9843,48 @@ layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 -offset_right = 212.0 -offset_bottom = 50.0 +offset_top = 2.0 +offset_right = 237.07463 +offset_bottom = 58.0 grow_horizontal = 2 grow_vertical = 2 -theme_override_constants/separation = 15 +theme_override_constants/separation = 10 -[node name="CoinIcon" type="TextureRect" parent="PowerUpInventoryUI/HBoxContainer" unique_id=341904386] +[node name="SpeedBtn" type="Button" parent="PowerUpInventoryUI/HBoxContainer" unique_id=1549270030] layout_mode = 2 -texture = ExtResource("26_2f3dj") -expand_mode = 2 +size_flags_horizontal = 3 +focus_mode = 0 +icon = ExtResource("26_2f3dj") +flat = true +icon_alignment = 1 +expand_icon = true -[node name="SelectRect" type="TextureRect" parent="PowerUpInventoryUI/HBoxContainer/CoinIcon" unique_id=961231613] -visible = false -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -texture = ExtResource("8_8meci") - -[node name="HeartIcon" type="TextureRect" parent="PowerUpInventoryUI/HBoxContainer" unique_id=61328923] +[node name="WallBtn" type="Button" parent="PowerUpInventoryUI/HBoxContainer" unique_id=863365575] layout_mode = 2 -texture = ExtResource("27_yq6so") -expand_mode = 2 +size_flags_horizontal = 3 +focus_mode = 0 +icon = ExtResource("27_yq6so") +flat = true +icon_alignment = 1 +expand_icon = true -[node name="SelectRect" type="TextureRect" parent="PowerUpInventoryUI/HBoxContainer/HeartIcon" unique_id=772732397] -visible = false -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -texture = ExtResource("8_8meci") - -[node name="DiamondIcon" type="TextureRect" parent="PowerUpInventoryUI/HBoxContainer" unique_id=1830849071] +[node name="FreezeAreaBtn" type="Button" parent="PowerUpInventoryUI/HBoxContainer" unique_id=1087493560] layout_mode = 2 -texture = ExtResource("28_fv21b") -expand_mode = 2 +size_flags_horizontal = 3 +focus_mode = 0 +icon = ExtResource("28_fv21b") +flat = true +icon_alignment = 1 +expand_icon = true -[node name="SelectRect" type="TextureRect" parent="PowerUpInventoryUI/HBoxContainer/DiamondIcon" unique_id=439518389] -visible = false -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -texture = ExtResource("8_8meci") - -[node name="StarIcon" type="TextureRect" parent="PowerUpInventoryUI/HBoxContainer" unique_id=1468665065] -visible = false +[node name="GhostBtn" type="Button" parent="PowerUpInventoryUI/HBoxContainer" unique_id=2041811828] layout_mode = 2 -texture = ExtResource("9_i0gbs") -expand_mode = 2 - -[node name="SelectRect" type="TextureRect" parent="PowerUpInventoryUI/HBoxContainer/StarIcon" unique_id=1393132163] -visible = false -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -texture = ExtResource("8_8meci") +size_flags_horizontal = 3 +focus_mode = 0 +icon = ExtResource("27_dgi5k") +flat = true +icon_alignment = 1 +expand_icon = true [connection signal="pressed" from="Menu/Host" to="." method="_on_host_pressed"] [connection signal="pressed" from="Menu/Join" to="." method="_on_join_pressed"] diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index 5ebc54e..eebeec4 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -269,10 +269,17 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true): for y in range(-radius, radius + 1): var pos = center + Vector2i(x, y) if enhanced_gridmap.is_position_valid(pos): - # Random chance - if rng.randf() > 0.4: continue + # Random chance to spawn ANYTHING at this spot (keep density reasonable) + if rng.randf() > 0.5: continue + + var item_id: int + # 70% Chance for PowerUp (11-14) + if rng.randf() < 0.7: + item_id = rng.randi_range(11, 14) + else: + # 30% Chance for Normal Tile (7-10) + item_id = rng.randi_range(7, 10) - var item_id = rng.randi_range(11, 14) # 11-14 are the new powerups var cell = Vector3i(pos.x, 1, pos.y) if player.is_multiplayer_authority(): diff --git a/scripts/ui/powerup_inventory_ui.gd b/scripts/ui/powerup_inventory_ui.gd index e811b6f..6bea140 100644 --- a/scripts/ui/powerup_inventory_ui.gd +++ b/scripts/ui/powerup_inventory_ui.gd @@ -3,8 +3,7 @@ extends Control # PowerUpInventoryUI - Displays stored powerups and handles selection # UI References -var icon_containers: Dictionary = {} # { EffectEnum: TextureRect } -var selection_indicators: Dictionary = {} # { EffectEnum: Control } +var icon_containers: Dictionary = {} # { EffectEnum: Button } # Local State var selected_effect: int = -1 @@ -13,159 +12,171 @@ var special_manager_ref: Node = null # Reference to SpecialTilesManager signal effect_selected(effect: int) func _ready(): - # Wait for children to be ready - await get_tree().process_frame - + print("[PowerUpUI] _ready called") # Map Effect Enum to UI Nodes (Assumes specific names in main.tscn) - # Default structure: HBoxContainer > CoinIcon, HeartIcon, etc. + # We try to get them immediately. If they are children, they should be accessible. var container = get_node_or_null("HBoxContainer") if not container: - print("PowerUpUI: Container not found") + print("[PowerUpUI] ERROR: HBoxContainer not found in ", get_path()) return - # ID 11 = Faster (Coin Icon?) User said: "Coin : random between two" originally, - # but now "11 (Faster)". Let's use CoinIcon for Speed? Or Star? - # User Request: "CoinIcon, HeartIcon, DiamondIcon and StarIcon" - # Let's map: - # 11 (Faster) -> CoinIcon - # 12 (Freeze) -> DiamondIcon - # 13 (Block) -> HeartIcon - # 14 (Invisible) -> StarIcon + # Mapping based on User Request + # 11: FASTER_SPEED (0) -> SpeedBtn + # 12: AREA_FREEZE (1) -> FreezeAreaBtn + # 13: BLOCK_FLOOR (2) -> WallBtn + # 14: INVISIBLE_MODE (3) -> GhostBtn - _setup_icon(11, container.get_node_or_null("CoinIcon")) - _setup_icon(12, container.get_node_or_null("DiamondIcon")) - _setup_icon(13, container.get_node_or_null("HeartIcon")) - _setup_icon(14, container.get_node_or_null("StarIcon")) + # We use 0, 1, 2, 3 to match SpecialTilesManager.SpecialEffect enum + _setup_btn(0, container.get_node_or_null("SpeedBtn")) + _setup_btn(1, container.get_node_or_null("FreezeAreaBtn")) + _setup_btn(2, container.get_node_or_null("WallBtn")) + _setup_btn(3, container.get_node_or_null("GhostBtn")) - # Note: SpecialEffect enum values from SpecialTilesManager: - # BURN_TILES = 0 - # SPAWN_TILES = 1 (we map Coin to 0, merged) - # FREEZE_PLAYER = 2 - # BLOCK_FLOOR = 3 - # INVISIBLE_MODE = 4 + print("[PowerUpUI] UI Initialization Complete. Mapped %d buttons." % icon_containers.size()) -func _setup_icon(effect_id: int, node: Control): - if not node: return +func _setup_btn(effect_id: int, btn: Button): + if not btn: + print("[PowerUpUI] Warning: Button for effect %d is null" % effect_id) + return - icon_containers[effect_id] = node + icon_containers[effect_id] = btn - # Assume node has specific children for selection state? - # "SelectRect" child? - var select_rect = node.get_node_or_null("SelectRect") - if select_rect: - selection_indicators[effect_id] = select_rect - select_rect.visible = false + # Start DISABLED + btn.disabled = true + btn.modulate = Color(0.5, 0.5, 0.5, 0.5) # Grayed out + btn.focus_mode = Control.FOCUS_NONE + + # Add Level Label + if not btn.has_node("LevelLabel"): + var lvl_lbl = Label.new() + lvl_lbl.name = "LevelLabel" + lvl_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE + lvl_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + lvl_lbl.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM + lvl_lbl.set_anchors_preset(Control.PRESET_FULL_RECT) + lvl_lbl.add_theme_font_size_override("font_size", 16) + lvl_lbl.add_theme_color_override("font_outline_color", Color.BLACK) + lvl_lbl.add_theme_constant_override("outline_size", 4) + lvl_lbl.text = "" # Hidden initially + btn.add_child(lvl_lbl) - # Add Cooldown Label if missing - if not node.has_node("CooldownLabel"): - var lbl = Label.new() - lbl.name = "CooldownLabel" - lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE # Ensure input passes to icon - # Style the label - lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - lbl.set_anchors_preset(Control.PRESET_FULL_RECT) # Cover icon - lbl.add_theme_color_override("font_shadow_color", Color.BLACK) - lbl.add_theme_constant_override("shadow_offset_x", 1) - lbl.add_theme_constant_override("shadow_offset_y", 1) - lbl.add_theme_font_size_override("font_size", 20) # Big text - lbl.text = "" - lbl.hide() - node.add_child(lbl) - - # Connect click event - if not node.gui_input.is_connected(_on_icon_input): - node.gui_input.connect(_on_icon_input.bind(effect_id)) + # Add Cooldown Label + if not btn.has_node("CooldownLabel"): + var cd_lbl = Label.new() + cd_lbl.name = "CooldownLabel" + cd_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE + cd_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + cd_lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + cd_lbl.set_anchors_preset(Control.PRESET_FULL_RECT) + cd_lbl.add_theme_font_size_override("font_size", 20) + cd_lbl.add_theme_color_override("font_outline_color", Color.BLACK) + cd_lbl.add_theme_constant_override("outline_size", 4) + cd_lbl.text = "" + btn.add_child(cd_lbl) -func _on_icon_input(event, effect_id: int): - if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: - print("[PowerUpUI] Clicked Icon %d. Manager: %s" % [effect_id, special_manager_ref]) - # User Request: "Click the CoinIcon for activate" - # Instead of just selecting, we ACTIVATE it immediately. - if special_manager_ref: - special_manager_ref.activate_effect(effect_id) - else: - print("[PowerUpUI] ERROR: SpecialManagerRef is null!") - - # Visual feedback (still useful) - select_effect(effect_id) - - # Auto-deselect after short delay? - # For now, keep selection highlight as feedback of "last used" or "active"? - # Or just flash it? - - # If it's a "selection for target" ability (like Block Floor on Target?), - # we might need selection state first, then clicking target. - # But user said "CoinIcon ... activate faster speed". Speed is self-cast. - # Freeze is 3x3 self-centered. - # Invisible is self. - # Block is "Wall Block"... maybe checks target? - # SpecialTilesManager.activate_effect defaults target to self if null. - # So direct activation is safe for IsInstant effects. + # Connect click + if not btn.pressed.is_connected(_on_btn_pressed): + btn.pressed.connect(_on_btn_pressed.bind(effect_id)) + +func _on_btn_pressed(effect_id: int): + print("[PowerUpUI] Clicked Button %d" % effect_id) + if special_manager_ref: + special_manager_ref.activate_effect(effect_id) + else: + print("[PowerUpUI] ERROR: special_manager_ref is null during click") func setup(player_node): + print("[PowerUpUI] Setup called for player: ", player_node.name) + var special_manager = player_node.get_node_or_null("SpecialTilesManager") if special_manager: - special_manager_ref = special_manager # Store reference for activation logic - if not special_manager.is_connected("inventory_updated", _on_inventory_updated): - special_manager.connect("inventory_updated", _on_inventory_updated) - if not special_manager.is_connected("cooldown_updated", _on_cooldown_updated): - special_manager.connect("cooldown_updated", _on_cooldown_updated) - - _on_inventory_updated(special_manager.inventory) + _connect_special_manager(special_manager) + else: + print("[PowerUpUI] SpecialTilesManager not found on %s. Waiting for it..." % player_node.name) + if not player_node.child_entered_tree.is_connected(_on_player_child_entered): + player_node.child_entered_tree.connect(_on_player_child_entered.bind(player_node)) + +func _on_player_child_entered(node: Node, player_node: Node): + if node.name == "SpecialTilesManager": + print("[PowerUpUI] SpecialTilesManager appeared on %s!" % player_node.name) + _connect_special_manager(node) + # Disconnect to avoid checking every child forever + if player_node.child_entered_tree.is_connected(_on_player_child_entered): + player_node.child_entered_tree.disconnect(_on_player_child_entered) + +func _connect_special_manager(special_manager): + special_manager_ref = special_manager + print("[PowerUpUI] Connected to SpecialTilesManager") + + # Connect signals if not already connected + if not special_manager.is_connected("powerup_unlocked", _on_powerup_unlocked): + special_manager.connect("powerup_unlocked", _on_powerup_unlocked) + + if not special_manager.is_connected("cooldown_updated", _on_cooldown_updated): + special_manager.connect("cooldown_updated", _on_cooldown_updated) + + if not special_manager.is_connected("inventory_updated", _on_inventory_updated): + special_manager.connect("inventory_updated", _on_inventory_updated) + + # Initial State Sync + if icon_containers.is_empty(): + print("[PowerUpUI] Warning: Icon containers empty during setup. Attempting _ready logic now...") + var container = get_node_or_null("HBoxContainer") + if container: + # Fix: Use correct IDs 0-3 here too + _setup_btn(0, container.get_node_or_null("SpeedBtn")) + _setup_btn(1, container.get_node_or_null("FreezeAreaBtn")) + _setup_btn(2, container.get_node_or_null("WallBtn")) + _setup_btn(3, container.get_node_or_null("GhostBtn")) + + # Sync Inventory + _on_inventory_updated(special_manager.inventory) + + # Sync Levels for owned items + for effect in special_manager.inventory: + if special_manager.inventory[effect]: + var lvl = special_manager.powerup_levels.get(effect, 1) + _on_powerup_unlocked(effect, lvl) + +func _on_powerup_unlocked(effect: int, level: int): + # Enable button and set level + if icon_containers.has(effect): + var btn = icon_containers[effect] + print("[PowerUpUI] Enabling button for Effect %d (Node: %s)" % [effect, btn.name]) + btn.disabled = false + btn.modulate = Color.WHITE # Restore color + btn.visible = true # Ensure visible + + # Update Level + var lvl_lbl = btn.get_node_or_null("LevelLabel") + if lvl_lbl: + lvl_lbl.text = "Lvl %d" % level + else: + print("[PowerUpUI] ERROR: Unlocked Effect %d but no UI button found! Keys: %s" % [effect, icon_containers.keys()]) func _on_cooldown_updated(effect: int, time_left: float, max_time: float): if icon_containers.has(effect): - var node = icon_containers[effect] - var lbl = node.get_node_or_null("CooldownLabel") - if lbl: + var btn = icon_containers[effect] + var cd_lbl = btn.get_node_or_null("CooldownLabel") + if cd_lbl: if time_left > 0: - lbl.text = "%.1fs" % time_left - lbl.show() - node.modulate = Color(0.5, 0.5, 0.5, 1.0) # Dimmed + cd_lbl.text = "%.1f" % time_left + btn.disabled = true + btn.modulate = Color(0.7, 0.7, 0.7, 0.8) else: - lbl.hide() - # Check inventory to restore proper modulate - var has_item = false # Need ref to inventory... or rely on next inventory update? - # Inventory update might not fire when cooldown ends. - # Let's restore bright if we have it? - # We don't have inventory reference here easily without storing it. - node.modulate = Color.WHITE # Assume we have it if we used it? - # Or better, just restore to WHITE and let inventory logic handle "not owned" graying. - # But wait, logic in _on_inventory_updated sets GRAY if not owned. - # If we set WHITE here, we might un-gray an unowned item? - # The only way cooldown runs is if we ACTIVATED it, so we HAD it. - # And we treat it as infinite consumable now. So we still have it. - pass - - # Better modulate handling: - # If cooldown > 0, DIM. - # If cooldown == 0, check owned state? - # For now, simplistic: if cooldown ends, set white. - if time_left <= 0: - node.modulate = Color.WHITE + cd_lbl.text = "" + # Re-enable if we own it + if special_manager_ref and special_manager_ref.inventory.get(effect, false): + btn.disabled = false + btn.modulate = Color.WHITE func _on_inventory_updated(inventory: Dictionary): - # Update UI icons (Dimmed vs Lit) + # Update UI icons (Dimmed vs Lit) and Enablement + print("[PowerUpUI] Inventory Updated: ", inventory) for effect in icon_containers: if inventory.has(effect): var has_item = inventory[effect] - icon_containers[effect].modulate = Color.WHITE if has_item else Color(0.3, 0.3, 0.3, 0.5) + var btn = icon_containers[effect] - if not has_item and selected_effect == effect: - deselect() - -func select_effect(effect: int): - # Check if we own it first? The UI click handler should check. - selected_effect = effect - emit_signal("effect_selected", effect) - _update_selection_visuals() - -func deselect(): - selected_effect = -1 - emit_signal("effect_selected", -1) - _update_selection_visuals() - -func _update_selection_visuals(): - for effect in selection_indicators: - selection_indicators[effect].visible = (effect == selected_effect) + btn.modulate = Color.WHITE if has_item else Color(0.5, 0.5, 0.5, 0.5) + btn.disabled = !has_item