From 5466f0bf366ce94e4f70bb0deba2f565b997d210 Mon Sep 17 00:00:00 2001 From: adtpdn Date: Mon, 29 Jun 2026 21:13:44 +0800 Subject: [PATCH] fix: empty out all gridmap layers under candy pump and use precise duration timer for projectile destruction --- scenes/candy_cannon.tscn | 21 +++--- .../controllers/candy_cannon_controller.gd | 12 ++-- scripts/managers/gauntlet_manager.gd | 64 ++++++++++--------- 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/scenes/candy_cannon.tscn b/scenes/candy_cannon.tscn index cbfad7d..a752ae3 100644 --- a/scenes/candy_cannon.tscn +++ b/scenes/candy_cannon.tscn @@ -1,6 +1,6 @@ -[gd_scene load_steps=10 format=3 uid="uid://ddy2r7xto80gq"] +[gd_scene format=3 uid="uid://ddy2r7xto80gq"] -[ext_resource type="Script" path="res://scripts/controllers/candy_cannon_controller.gd" id="1_canon"] +[ext_resource type="Script" uid="uid://du7cne5070ia0" path="res://scripts/controllers/candy_cannon_controller.gd" id="1_canon"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_base"] albedo_color = Color(0.15, 0.15, 0.2, 1) @@ -42,36 +42,35 @@ material = SubResource("StandardMaterial3D_pipe") inner_radius = 0.95 outer_radius = 1.15 rings = 32 -tube_segments = 12 -[node name="CandyCannon" type="Node3D"] +[node name="CandyCannon" type="Node3D" unique_id=1515964905] script = ExtResource("1_canon") -[node name="Base" type="MeshInstance3D" parent="."] +[node name="Base" type="MeshInstance3D" parent="." unique_id=867716100] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.25, 0) mesh = SubResource("CylinderMesh_base") -[node name="Tank" type="MeshInstance3D" parent="."] +[node name="Tank" type="MeshInstance3D" parent="." unique_id=2051786234] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.4, 0) mesh = SubResource("SphereMesh_tank") -[node name="Pipe" type="MeshInstance3D" parent="."] +[node name="Pipe" type="MeshInstance3D" parent="." unique_id=491265631] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.7, 0) mesh = SubResource("CylinderMesh_pipe") -[node name="Ring1" type="MeshInstance3D" parent="."] +[node name="Ring1" type="MeshInstance3D" parent="." unique_id=1607309179] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.4, 0) mesh = SubResource("TorusMesh_ring") -[node name="Ring2" type="MeshInstance3D" parent="."] +[node name="Ring2" type="MeshInstance3D" parent="." unique_id=850923213] transform = Transform3D(0.707107, 0.707107, 0, -0.707107, 0.707107, 0, 0, 0, 1, 0, 1.4, 0) mesh = SubResource("TorusMesh_ring") -[node name="Ring3" type="MeshInstance3D" parent="."] +[node name="Ring3" type="MeshInstance3D" parent="." unique_id=677024967] transform = Transform3D(0.707107, -0.707107, 0, 0.707107, 0.707107, 0, 0, 0, 1, 0, 1.4, 0) mesh = SubResource("TorusMesh_ring") -[node name="OmniLight3D" type="OmniLight3D" parent="."] +[node name="OmniLight3D" type="OmniLight3D" parent="." unique_id=1751121483] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8, 0) light_color = Color(1, 0.4, 0.8, 1) light_energy = 3.0 diff --git a/scripts/controllers/candy_cannon_controller.gd b/scripts/controllers/candy_cannon_controller.gd index 6fa0f6c..c6ca81f 100644 --- a/scripts/controllers/candy_cannon_controller.gd +++ b/scripts/controllers/candy_cannon_controller.gd @@ -115,17 +115,21 @@ func spawn_projectile(target_world_pos: Vector3, duration: float) -> void: var mid_y = max(global_position.y, target_world_pos.y) + 4.0 var tween_y = create_tween() + + # Important: Make sure both halves of the y-tween together equal `duration` tween_y.tween_property(projectile, "global_position:y", mid_y, duration / 2.0).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT) - tween_y.tween_property(projectile, "global_position:y", target_world_pos.y, duration / 2.0).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN).set_delay(duration / 2.0) + tween_y.tween_property(projectile, "global_position:y", target_world_pos.y, duration / 2.0).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) # Add some spin to the projectile var spin_tween = create_tween() spin_tween.set_loops() spin_tween.tween_property(projectile, "rotation", Vector3(PI*2, PI*2, PI*2), 0.5).as_relative() - tween.chain().tween_callback(func(): - spin_tween.kill() - projectile.queue_free() + # We need to wait for the X/Z tween to finish, but since it's parallel we can just use a separate timer or tween + # to kill the projectile exactly when duration is reached, ensuring it doesn't get killed early by X/Z finishing 1 frame earlier than Y + get_tree().create_timer(duration).timeout.connect(func(): + if is_instance_valid(spin_tween): spin_tween.kill() + if is_instance_valid(projectile): projectile.queue_free() ) func can_rpc() -> bool: diff --git a/scripts/managers/gauntlet_manager.gd b/scripts/managers/gauntlet_manager.gd index 7506a60..395bdb5 100644 --- a/scripts/managers/gauntlet_manager.gd +++ b/scripts/managers/gauntlet_manager.gd @@ -391,43 +391,47 @@ func sync_arena_setup() -> void: print("[Gauntlet] Client: Syncing Arena Setup (%dx%d)..." % [ARENA_COLUMNS, ARENA_ROWS]) _apply_arena_setup() -func _apply_arena_setup() -> void: - """Shared arena layout logic for host + clients.""" - if not gridmap: - gridmap = get_parent().get_node_or_null("EnhancedGridMap") - if not gridmap: - gridmap = get_node_or_null("/root/Main/EnhancedGridMap") - if not gridmap: return - - # Resize grid (bypass setters that wipe the map) - gridmap.set("columns", ARENA_COLUMNS) - gridmap.set("rows", ARENA_ROWS) - - # Clear all - gridmap.clear() - - # Build the 20x20 arena - for x in range(ARENA_COLUMNS): - for z in range(ARENA_ROWS): - var pos = Vector2i(x, z) - - # Center 3x3 block: NPC obstacle (Candy Pump) + func _apply_arena_setup() -> void: + """Shared arena layout logic for host + clients.""" + if not gridmap: + gridmap = get_parent().get_node_or_null("EnhancedGridMap") + if not gridmap: + gridmap = get_node_or_null("/root/Main/EnhancedGridMap") + if not gridmap: return + + # Resize grid (bypass setters that wipe the map) + gridmap.set("columns", ARENA_COLUMNS) + gridmap.set("rows", ARENA_ROWS) + + # Clear all + gridmap.clear() + + # Build the 20x20 arena + for x in range(ARENA_COLUMNS): + for z in range(ARENA_ROWS): + var pos = Vector2i(x, z) + + # Center 3x3 block: NPC obstacle (Candy Pump) if _is_npc_zone(pos): # Make the floor empty (-1) beneath the Candy Pump + # We need to clear all possible layers just in case gridmap.set_cell_item(Vector3i(x, 0, z), -1) gridmap.set_cell_item(Vector3i(x, 1, z), -1) + gridmap.set_cell_item(Vector3i(x, 2, z), -1) continue - - # Boundary walls: perimeter (row 0, row 19, col 0, col 19) - if x == 0 or x == ARENA_COLUMNS - 1 or z == 0 or z == ARENA_ROWS - 1: - # Also make border walls visually walkable floors instead of red blocks + + # Boundary walls: perimeter (row 0, row 19, col 0, col 19) + if x == 0 or x == ARENA_COLUMNS - 1 or z == 0 or z == ARENA_ROWS - 1: + # Also make border walls visually walkable floors instead of red blocks + gridmap.set_cell_item(Vector3i(x, 0, z), TILE_WALKABLE) + gridmap.set_cell_item(Vector3i(x, 1, z), -1) + gridmap.set_cell_item(Vector3i(x, 2, z), -1) + continue + + # Interior: walkable floor gridmap.set_cell_item(Vector3i(x, 0, z), TILE_WALKABLE) gridmap.set_cell_item(Vector3i(x, 1, z), -1) - continue - - # Interior: walkable floor - gridmap.set_cell_item(Vector3i(x, 0, z), TILE_WALKABLE) - gridmap.set_cell_item(Vector3i(x, 1, z), -1) + gridmap.set_cell_item(Vector3i(x, 2, z), -1) gridmap.diagonal_movement = true gridmap.update_grid_data()