feat: Introduce PlayerMovementManager to manage player movement, rotation, attack mode push mechanics, and grid-based collision detection.

This commit is contained in:
Yogi Wiguna
2026-02-20 17:54:58 +08:00
parent e90cbfe246
commit 0e4d69f7b9
8 changed files with 193 additions and 61 deletions
+31 -18
View File
@@ -27,6 +27,7 @@ const TILE_OBSTACLE = 4 # Wall
var hud_layer: CanvasLayer
var phase_label: Label
var mission_label: Label
var red_tint_overlay: ColorRect
func _ready():
set_process(false)
@@ -37,6 +38,13 @@ func _setup_hud():
hud_layer.visible = false
add_child(hud_layer)
# Full-screen red tint overlay (below everything else in this layer, but above game)
red_tint_overlay = ColorRect.new()
red_tint_overlay.color = Color(1.0, 0.0, 0.0, 0.25) # Transparent red
red_tint_overlay.set_anchors_preset(Control.PRESET_FULL_RECT) # Cover whole screen
red_tint_overlay.visible = false # Hidden initially
hud_layer.add_child(red_tint_overlay)
var vbox = VBoxContainer.new()
vbox.set_anchors_preset(Control.PRESET_TOP_RIGHT)
vbox.offset_right = -20
@@ -90,6 +98,10 @@ func _update_hud_visuals():
if phase_label:
phase_label.text = "PHASE: %s (%.0fs)" % [phase_name, max(0, phase_timer)]
phase_label.add_theme_color_override("font_color", Color.GREEN if current_phase == Phase.GO else Color.RED)
# Toggle Red Screen Tint
if red_tint_overlay:
red_tint_overlay.visible = (current_phase == Phase.STOP)
var my_id = multiplayer.get_unique_id()
if mission_label and player_missions.has(my_id):
@@ -120,7 +132,7 @@ func start_game_mode():
if multiplayer.is_server():
activate_client_side() # Server also needs local processing
_setup_arena()
# _setup_arena() # REMOVED: Already explicitly called in main.gd _setup_host_game to prepare floor before spawns!
_assign_missions()
_start_phase(Phase.GO)
else:
@@ -133,11 +145,15 @@ func _start_phase(phase: Phase):
phase_timer = GO_DURATION if phase == Phase.GO else STOP_DURATION
var phase_name = "GO" if phase == Phase.GO else "STOP"
if multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
if multiplayer.get_peers().size() > 0:
rpc("sync_phase", phase_name, phase_timer)
if can_rpc():
rpc("sync_phase", phase_name, phase_timer)
emit_signal("phase_changed", phase_name, phase_timer)
func can_rpc() -> bool:
if not multiplayer.has_multiplayer_peer() or multiplayer.multiplayer_peer.get_connection_status() != MultiplayerPeer.CONNECTION_CONNECTED:
return false
return true
@rpc("authority", "call_local", "reliable")
func sync_phase(phase_name: String, duration: float):
if not is_active:
@@ -152,9 +168,8 @@ func _setup_arena():
print("[StopNGo] Setting up 22x10 Arena with Randomized Obstacles...")
# Explicitly sync dimensions and clear grid on all clients
if multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
if multiplayer.get_peers().size() > 0:
rpc("sync_arena_setup")
if can_rpc():
rpc("sync_arena_setup")
# Apply locally for Server (RPC is call_remote)
_apply_arena_setup()
@@ -169,9 +184,9 @@ func _apply_arena_setup():
var gridmap = get_node("/root/Main/EnhancedGridMap")
if not gridmap: return
# Set Size for Stop n Go
gridmap.columns = 22
gridmap.rows = 10
# Set Size for Stop n Go explicitly, bypassing setters that wipe the map
gridmap.set("columns", 22)
gridmap.set("rows", 10)
# Clear existing items on all layers
gridmap.clear()
@@ -232,7 +247,7 @@ func _apply_arena_setup():
# Sync the WHOLE grid to all clients to ensure size and stripes are correct
var main = get_node("/root/Main")
if main and multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
if main and can_rpc():
# Gather all floor 0 and floor 1 data
var floor0_data = gridmap.get_floor_data(0)
var floor1_data = gridmap.get_floor_data(1)
@@ -272,9 +287,8 @@ func _assign_missions():
}
idx += 1
if multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
if multiplayer.get_peers().size() > 0:
rpc("sync_missions", player_missions)
if can_rpc():
rpc("sync_missions", player_missions)
@rpc("authority", "call_local", "reliable")
func sync_missions(missions: Dictionary):
@@ -306,7 +320,7 @@ func _penalize_player(player_id: int):
if player_node:
# Don't reset mission progress!
# Just Drop All Tiles (which already exist on player)
if multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
if can_rpc():
player_node.rpc("drop_all_tiles")
# Also send message
@@ -331,9 +345,8 @@ func update_mission_progress(player_id: int, tile_id: int):
if player_node:
NotificationManager.send_message(player_node, "Mission Complete! Reach the Finish!", NotificationManager.MessageType.GOAL)
if multiplayer.is_server() and multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
if multiplayer.get_peers().size() > 0:
rpc("sync_mission_progress", player_id, mission["current"])
if multiplayer.is_server() and can_rpc():
rpc("sync_mission_progress", player_id, mission["current"])
@rpc("any_peer", "call_local", "reliable")
func sync_mission_progress(player_id: int, current: int):