From 6472e47edfa2f094838c8c335de9f27f68a49a8d Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Fri, 13 Feb 2026 13:26:22 +0800 Subject: [PATCH] feat: Introduce special tile power-up system, managing inventory, levels, cooldowns, and targeted effects like area freeze and block floor. --- scripts/managers/special_tiles_manager.gd | 31 ++++++++++++++++++++++- scripts/static_tekton_controller.gd | 5 ++++ scripts/tekton.gd | 29 +++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index 38eb535..5099d21 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -380,6 +380,11 @@ func _execute_block_floor(target_pos: Vector2i): for n in neighbors: var pos = n.position + + # PHYSICS CHECK + if _is_position_blocked_by_stand(pos): + continue + var block_pos = Vector3i(pos.x, 0, pos.y) # Check current item first @@ -437,6 +442,10 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true): for x in range(-radius, radius + 1): for y in range(-radius, radius + 1): var pos = center + Vector2i(x, y) + + if _is_position_blocked_by_stand(pos): + continue + if enhanced_gridmap.is_position_valid(pos): # Random chance to spawn ANYTHING at this spot (keep density reasonable) if rng.randf() > 0.5: continue @@ -640,4 +649,24 @@ func _create_unfreeze_timer(target_player: Node3D, duration: float): # Reset visuals if target_player.has_method("sync_modulate"): target_player.rpc("sync_modulate", Color.WHITE) - NotificationManager.send_message(target_player, NotificationManager.MESSAGES.UNFROZEN, NotificationManager.MessageType.NORMAL) + NotificationManager.send_message(target_player, "You are no longer frozen!", NotificationManager.MessageType.NORMAL) + +func _is_position_blocked_by_stand(target_pos: Vector2i) -> bool: + if not player: return false + + # Raycast check for Static Tekton Stand + # Check CENTER of tile (x+0.5) at LOW height (0.3) + var from = Vector3(target_pos.x + 0.5, 0.3, target_pos.y + 0.5) + Vector3.UP * 2.0 + var to = Vector3(target_pos.x + 0.5, 0.3, target_pos.y + 0.5) + Vector3.DOWN * 2.0 + + var query = PhysicsRayQueryParameters3D.create(from, to) + query.collide_with_areas = false + query.collide_with_bodies = true + + var space_state = player.get_world_3d().direct_space_state + var result = space_state.intersect_ray(query) + + if result: + if result.collider != player: + return true + return false diff --git a/scripts/static_tekton_controller.gd b/scripts/static_tekton_controller.gd index 695586f..102ccf1 100644 --- a/scripts/static_tekton_controller.gd +++ b/scripts/static_tekton_controller.gd @@ -107,6 +107,11 @@ func _find_empty_tile() -> Vector2i: # Check range logic (Euclidean or Manhattan?) if Vector2(x, y).distance_to(Vector2(center.x, center.y)) > throw_range: continue + # EXPLICITLY EXCLUDE 3x3 BASE AREA (Tekton Stand) + # The stand occupies [center-1, center+1]. We must NOT spawn here. + if abs(x - center.x) <= 1 and abs(y - center.y) <= 1: + continue + # Check if empty (Layer 1 has no item) if enhanced_gridmap.get_cell_item(Vector3i(x, 1, y)) == -1: # Also check floor exist? (Layer 0) diff --git a/scripts/tekton.gd b/scripts/tekton.gd index 01b9117..7ded067 100644 --- a/scripts/tekton.gd +++ b/scripts/tekton.gd @@ -283,6 +283,10 @@ func temporarily_change_floor(center: Vector2i, radius: int, new_id: int, durati for x in range(-radius, radius + 1): for y in range(-radius, radius + 1): var pos = center + Vector2i(x, y) + + if _is_position_blocked_by_stand(pos): + continue + if enhanced_gridmap.is_position_valid(pos): var cell_3d = Vector3i(pos.x, 0, pos.y) var original = enhanced_gridmap.get_cell_item(cell_3d) @@ -355,6 +359,10 @@ func spawn_tiles_around(count: int = 4): # Maybe avoid center. if x == 0 and y == 0: continue + # PHYSICS CHECK: Block spawning on Static Stands + if _is_position_blocked_by_stand(pos): + continue + if enhanced_gridmap.is_position_valid(pos): # 50% chance to spawn something if rng.randf() > 0.5: continue @@ -425,3 +433,24 @@ func play_animation(anim_name: String): print_tree_pretty() else: print("[Tekton] Animation '%s' NOT FOUND in AnimationPlayer!" % anim_name) + +func _is_position_blocked_by_stand(target_pos: Vector2i) -> bool: + # Raycast check for Static Tekton Stand (Layer 1, Body) + # Check CENTER of tile (x+0.5) at LOW height (0.3) + var from = Vector3(target_pos.x + 0.5, 0.3, target_pos.y + 0.5) + Vector3.UP * 2.0 + var to = Vector3(target_pos.x + 0.5, 0.3, target_pos.y + 0.5) + Vector3.DOWN * 2.0 + + # We perform a vertical raycast through the potential stand volume + var query = PhysicsRayQueryParameters3D.create(from, to) + query.collide_with_areas = false + query.collide_with_bodies = true + + var space_state = get_world_3d().direct_space_state + var result = space_state.intersect_ray(query) + + if result: + # If we hit something not ourselves/player, assume it's a stand/blocker + if result.collider != self: + return true + + return false