From 489e31fb39d77fd3ca4cef9aa8eaef5ca566c86a Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Fri, 13 Feb 2026 16:02:23 +0800 Subject: [PATCH] feat: Add `PlayerMovementManager` to centralize player movement logic and implement a 'SUPER PUSH' attack mechanic. --- scripts/managers/player_movement_manager.gd | 71 +++++++++++++++------ scripts/static_tekton_stand.gd | 2 + 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/scripts/managers/player_movement_manager.gd b/scripts/managers/player_movement_manager.gd index 5e106ed..44da21a 100644 --- a/scripts/managers/player_movement_manager.gd +++ b/scripts/managers/player_movement_manager.gd @@ -78,25 +78,8 @@ func simple_move_to(grid_position: Vector2i) -> bool: return false # PHYSICS CHECK: Ensure no static obstacles (like Stands) are blocking the path - # GridMap logic handles cells, but Objects/Bodies might be placed on top (like StaticTektonStand) - var space_state = player.get_world_3d().direct_space_state - # RAYCAST HEIGHT: 0.3 (Center of the 0.6m tall stand) - # Check from CENTER using +0.5 - var from = Vector3(player.current_position.x + 0.5, 0.3, player.current_position.y + 0.5) - var to = Vector3(grid_position.x + 0.5, 0.3, grid_position.y + 0.5) - - # Check center of target tile - var query = PhysicsRayQueryParameters3D.create(from, to) - query.collide_with_areas = false - query.collide_with_bodies = true - # query.collision_mask = 1 # Default mask usually covers static bodies - - var result = space_state.intersect_ray(query) - if result: - # If we hit something static that isn't ourselves - if result.collider != player: - print("Movement Blocked by Physics Body: ", result.collider.name) - return false + if _is_position_blocked_by_physics(grid_position): + return false if player.is_position_occupied(grid_position): var push_dir = grid_position - player.current_position @@ -158,9 +141,11 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool: # 3. Knockback / Stagger # Push them away var pushed_to_pos = target_pos + direction - if enhanced_gridmap.is_position_valid(pushed_to_pos) and \ + if not _is_position_in_static_stand_area(pushed_to_pos) and \ + enhanced_gridmap.is_position_valid(pushed_to_pos) and \ enhanced_gridmap.get_cell_item(Vector3i(pushed_to_pos.x, 0, pushed_to_pos.y)) != -1 and \ - not player.is_position_occupied(pushed_to_pos): + not player.is_position_occupied(pushed_to_pos) and \ + not _is_position_blocked_by_physics(pushed_to_pos): # Valid push var push_path = [Vector2(pushed_to_pos.x, pushed_to_pos.y)] other_player.rpc("start_movement_along_path", push_path, false) @@ -404,3 +389,47 @@ func highlight_adjacent_cells(): cells_to_highlight.append(cell_pos) player.highlight_cells_if_authorized(cells_to_highlight) + +func _is_position_in_static_stand_area(pos: Vector2i) -> bool: + # Check against all known Static Tekton Stands (3x3 areas) + var stands = player.get_tree().get_nodes_in_group("StaticTektonStands") + print("[Debug] Checking Push Prevention for %s. Found %d stands." % [pos, stands.size()]) + + for stand in stands: + if not enhanced_gridmap: continue + + # Convert world to grid. Use global_position just to be safe. + var local_pos = enhanced_gridmap.to_local(stand.global_position) + var stand_grid_pos = enhanced_gridmap.local_to_map(local_pos) + + # Stand is centered, so key check is 3x3 around it + var center = Vector2i(stand_grid_pos.x, stand_grid_pos.z) + + # Check if pos is right on top of stand (distance 0) or adjacent (distance 1) + # Chebyshev distance <= 1 means 3x3 square + if abs(pos.x - center.x) <= 1 and abs(pos.y - center.y) <= 1: + print(" - BLOCKED by Stand at %s (Center: %s)" % [stand.name, center]) + return true + + return false + +func _is_position_blocked_by_physics(target_pos: Vector2i) -> bool: + if not player.is_inside_tree(): return false + + var space_state = player.get_world_3d().direct_space_state + var center_x = target_pos.x + 0.5 + var center_z = target_pos.y + 0.5 + var from = Vector3(center_x, 1.0, center_z) + var to = Vector3(center_x, 0.1, center_z) + + var query = PhysicsRayQueryParameters3D.create(from, to) + query.collide_with_areas = false + query.collide_with_bodies = true + + var result = space_state.intersect_ray(query) + if result: + if result.collider != player: + # print("Movement Blocked by Physics Body: ", result.collider.name) + return true + + return false diff --git a/scripts/static_tekton_stand.gd b/scripts/static_tekton_stand.gd index 87293f8..a643fca 100644 --- a/scripts/static_tekton_stand.gd +++ b/scripts/static_tekton_stand.gd @@ -10,6 +10,8 @@ extends StaticBody3D _update_mesh_from_index() func _ready(): + add_to_group("StaticTektonStands") + print("Static Stand Ready: ", name) if multiplayer.is_server(): # Only randomize if not already set (Main.gd sets it now) if shape_index == -1: