From 38a7c06311d05110d98228b80f1ce9d787ade470 Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Thu, 26 Feb 2026 17:46:14 +0800 Subject: [PATCH] feat: Add PlayerMovementManager for grid-based movement, rotation, and player-to-player push mechanics. --- scripts/managers/player_movement_manager.gd | 49 ++++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/scripts/managers/player_movement_manager.gd b/scripts/managers/player_movement_manager.gd index 1e1777b..d482557 100644 --- a/scripts/managers/player_movement_manager.gd +++ b/scripts/managers/player_movement_manager.gd @@ -176,11 +176,27 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool: # 3. Knockback / Stagger # Push them away var pushed_to_pos = target_pos + direction - 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) and \ - not _is_position_blocked_by_physics(pushed_to_pos): + + # IMPROVED: Check if destination is valid and walkable to prevent being stuck on 'blocks' + var is_dest_valid = _can_push_to(pushed_to_pos) + + # DEFLECTION LOGIC: If direct path is blocked, try diagonal deflection + if not is_dest_valid: + var alts = [] + if direction.x != 0 and direction.y == 0: # Horizontal push -> try diagonal North/South + alts = [pushed_to_pos + Vector2i(0, 1), pushed_to_pos + Vector2i(0, -1)] + elif direction.y != 0 and direction.x == 0: # Vertical push -> try diagonal East/West + alts = [pushed_to_pos + Vector2i(1, 0), pushed_to_pos + Vector2i(-1, 0)] + elif direction.x != 0 and direction.y != 0: # Diagonal push -> try horizontal/vertical components + alts = [pushed_to_pos - Vector2i(direction.x, 0), pushed_to_pos - Vector2i(0, direction.y)] + + for alt in alts: + if _can_push_to(alt): + pushed_to_pos = alt + is_dest_valid = true + break + + if is_dest_valid: # Valid push var push_path = [Vector2(pushed_to_pos.x, pushed_to_pos.y)] if _can_rpc(): @@ -192,7 +208,7 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool: other_player.rpc("apply_stagger", 1.5) else: - # Wall/Blocked -> Stagger in place + # Wall/Blocked -> Stagger in place (Only if no alternatives found) if _can_rpc(): other_player.rpc("apply_stagger", 1.5) @@ -460,6 +476,27 @@ func _is_position_in_static_stand_area(pos: Vector2i) -> bool: return false +func _can_push_to(pos: Vector2i) -> bool: + """Helper to validate if a grid position is a safe landing spot for a push.""" + if not enhanced_gridmap or not enhanced_gridmap.is_position_valid(pos): + return false + + var cell_item = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y)) + # Must be walkable and NOT in non_walkable_items (to prevent getting stuck on walls/blocks) + if cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items: + return false + + if player.is_position_occupied(pos): + return false + + if _is_position_in_static_stand_area(pos): + return false + + if _is_position_blocked_by_physics(pos): + return false + + return true + func _is_position_blocked_by_physics(target_pos: Vector2i) -> bool: if not player.is_inside_tree(): return false