feat: Introduce PlayerMovementManager to centralize grid-based player and bot movement, including multiplayer synchronization and push interactions.

This commit is contained in:
Yogi Wiguna
2026-03-05 15:32:45 +08:00
parent fb6d9df5db
commit aa26e9f2a4
3 changed files with 93 additions and 13 deletions
+53 -4
View File
@@ -282,7 +282,35 @@ func _try_attack_chase() -> bool:
# No victim found? Just behave normally (grab tiles etc)
return false
# Pathfind to victim
# 1. Adjacency Check: If already touching or on same tile, attack directly!
var dist_manhattan = abs(victim.current_position.x - actor.current_position.x) + abs(victim.current_position.y - actor.current_position.y)
if dist_manhattan <= 1:
print("[BotController] %s is close to %s (Dist: %d). Attacking!" % [actor.name, victim.name, dist_manhattan])
var push_dir = victim.current_position - actor.current_position
if push_dir == Vector2i.ZERO:
# If overlapping, use actor's last move direction or fallback
push_dir = actor.movement_manager.last_move_direction if actor.movement_manager else Vector2i(1, 0)
# Trigger push logic directly
var push_success = actor.movement_manager.try_push(victim.current_position, push_dir)
if not push_success:
# If attack failed (e.g. Safe Zone in Stop n Go), don't just loop!
if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO):
if victim.current_position.x in [6, 7, 8, 14, 15, 16]: # Safe Zone Columns
print("[BotController] %s target is in Safe Zone. Moving to find better angle." % actor.name)
await _try_unstuck_move()
_is_processing_action = true
_current_action = "attacking"
await _wait_with_variance(action_delay)
if not is_instance_valid(self) or not is_instance_valid(actor): return true
_is_processing_action = false
_current_action = "idle"
return true
# 2. Pathfind to victim if not adjacent
var path = enhanced_gridmap.find_path(
Vector2(actor.current_position),
Vector2(victim.current_position),
@@ -305,15 +333,28 @@ func _try_attack_chase() -> bool:
# print("[BotController] %s attack blocked by boundary (Not late game yet)." % actor.name)
return false
# Move to next step (If occupied by victim, movement_manager will trigger PUSH)
# Move to next step
# note: simple_move_to will call try_push if next_step is occupied
if actor.movement_manager.simple_move_to(next_step):
_is_processing_action = true
_current_action = "attacking"
await _wait_with_variance(action_delay) # Shorter delay for attacks? perhaos
await _wait_with_variance(action_delay)
if not is_instance_valid(self) or not is_instance_valid(actor): return true
_is_processing_action = false
_current_action = "idle"
return true
else:
# If move failed, it might be because simple_move_to called try_push and returned false
# We check if the target is occupied. If it is, and we are in attack mode,
# we assume an attack attempt was made.
if actor.is_position_occupied(next_step):
_is_processing_action = true
_current_action = "attacking"
await _wait_with_variance(action_delay)
if not is_instance_valid(self) or not is_instance_valid(actor): return true
_is_processing_action = false
_current_action = "idle"
return true
return false
@@ -450,7 +491,15 @@ func _try_move() -> bool:
var unstuck = await _try_unstuck_move()
return unstuck
# Execute SINGLE STEP movement using player manager
# 1. Check if next step is blocked by another player/bot
# If we are NOT in attack mode, we should avoid bumping into others if possible
if not actor.get("is_attack_mode") and actor.is_position_occupied(next_step):
# Try to find a detour? For now, just try an unstuck move to get out of the way
print("[BotController] %s path blocked by %s. Detouring." % [actor.name, next_step])
var unstuck = await _try_unstuck_move()
return unstuck
# 2. Execute movement
if actor.movement_manager.simple_move_to(next_step):
_is_processing_action = true
_current_action = "moving"