feat: Implement the main game scene with new player functionality, Stop n Go and Portal Mode managers, a dynamic message bar, and pre-game countdown logic.
This commit is contained in:
+32
-7
@@ -807,15 +807,41 @@ func _assign_random_spawn_positions():
|
||||
print("Assigned spawn %s to player %s" % [assigned_pos, player.name])
|
||||
|
||||
func _assign_stop_n_go_spawn_positions(all_players: Array):
|
||||
"""Assigns spawns to the far left columns (Start Line) for Stop N Go mode."""
|
||||
"""Assigns random spawns on walkable arena tiles for Stop N Go mode."""
|
||||
# Sort players for deterministic assignment based on ID
|
||||
all_players.sort_custom(func(a, b): return a.name.to_int() < b.name.to_int())
|
||||
|
||||
var spawn_index = 0
|
||||
var enhanced_gridmap = $EnhancedGridMap
|
||||
if not enhanced_gridmap:
|
||||
return
|
||||
|
||||
# Collect all valid walkable positions (not obstacle, not void)
|
||||
var valid_positions: Array[Vector2i] = []
|
||||
for x in range(enhanced_gridmap.columns):
|
||||
for z in range(enhanced_gridmap.rows):
|
||||
var tile = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||
# Accept walkable (0), start (3), finish (3) — skip obstacles (4) and void (-1)
|
||||
if tile != -1 and tile != 4:
|
||||
valid_positions.append(Vector2i(x, z))
|
||||
|
||||
valid_positions.shuffle()
|
||||
|
||||
var used_positions: Array[Vector2i] = []
|
||||
|
||||
for player in all_players:
|
||||
# Use deterministic assignment from (0, 1) to (0, 8) to keep players separate
|
||||
# Start Line is Column 0. We use rows 1 to 8.
|
||||
var assigned_pos = Vector2i(0, spawn_index + 1)
|
||||
var assigned_pos = Vector2i(-1, -1)
|
||||
|
||||
# Find a random walkable position not occupied by another player
|
||||
for pos in valid_positions:
|
||||
if pos not in used_positions:
|
||||
assigned_pos = pos
|
||||
break
|
||||
|
||||
if assigned_pos == Vector2i(-1, -1):
|
||||
# Fallback: center of arena
|
||||
assigned_pos = Vector2i(int(enhanced_gridmap.columns / 2), int(enhanced_gridmap.rows / 2))
|
||||
|
||||
used_positions.append(assigned_pos)
|
||||
|
||||
# Ensure immediate sync
|
||||
player.position = player.grid_to_world(assigned_pos)
|
||||
@@ -825,8 +851,7 @@ func _assign_stop_n_go_spawn_positions(all_players: Array):
|
||||
if can_rpc():
|
||||
player.rpc("set_spawn_position", assigned_pos)
|
||||
|
||||
spawn_index += 1
|
||||
print("[StopNGo] Assigned fixed starting block %s to player %s" % [assigned_pos, player.name])
|
||||
print("[StopNGo] Assigned random spawn %s to player %s" % [assigned_pos, player.name])
|
||||
|
||||
func _assign_portal_mode_spawn_positions(all_players: Array):
|
||||
"""Assigns spawns to different quadrants for Tekton Doors mode."""
|
||||
|
||||
+19
-19
@@ -737,11 +737,11 @@ func apply_stagger(duration: float = 1.5):
|
||||
# If still immune, show immunity tint (Green?), otherwise White
|
||||
# UNLESS we are still stop-frozen (Cyan)
|
||||
if is_stop_frozen:
|
||||
_apply_tint_recursive(self, Color.CYAN)
|
||||
_apply_tint_recursive(self , Color.CYAN)
|
||||
elif immunity_timer > 0:
|
||||
_apply_tint_recursive(self, Color(0.5, 1.0, 0.5)) # Light Green for immunity
|
||||
_apply_tint_recursive(self , Color(0.5, 1.0, 0.5)) # Light Green for immunity
|
||||
else:
|
||||
_apply_tint_recursive(self, Color.WHITE) # Remove tint
|
||||
_apply_tint_recursive(self , Color.WHITE) # Remove tint
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func sync_stop_freeze(enabled: bool):
|
||||
@@ -753,16 +753,16 @@ func sync_stop_freeze(enabled: bool):
|
||||
is_stop_frozen = enabled
|
||||
|
||||
if enabled:
|
||||
_apply_tint_recursive(self, Color.CYAN)
|
||||
_apply_tint_recursive(self , Color.CYAN)
|
||||
print("[STOP n GO] Player %s FROZEN until GO phase" % name)
|
||||
else:
|
||||
# Restore appropriate tint
|
||||
if is_frozen:
|
||||
_apply_tint_recursive(self, Color.BLUE)
|
||||
_apply_tint_recursive(self , Color.BLUE)
|
||||
elif immunity_timer > 0:
|
||||
_apply_tint_recursive(self, Color(0.5, 1.0, 0.5))
|
||||
_apply_tint_recursive(self , Color(0.5, 1.0, 0.5))
|
||||
else:
|
||||
_apply_tint_recursive(self, Color.WHITE)
|
||||
_apply_tint_recursive(self , Color.WHITE)
|
||||
print("[STOP n GO] Player %s UNFROZEN" % name)
|
||||
|
||||
@rpc("any_peer", "call_local")
|
||||
@@ -905,7 +905,7 @@ func on_stop_phase_violation():
|
||||
var cell = Vector3i(pos.x, 1, pos.y)
|
||||
rpc("sync_grid_item", cell.x, cell.y, cell.z, item_id)
|
||||
|
||||
NotificationManager.send_message(self, "STOP VIOLATION! Tiles scattered!", NotificationManager.MessageType.WARNING)
|
||||
NotificationManager.send_message(self , "STOP VIOLATION! Tiles scattered!", NotificationManager.MessageType.WARNING)
|
||||
|
||||
func _find_multiple_drop_positions(count: int) -> Array:
|
||||
var positions = []
|
||||
@@ -1051,7 +1051,7 @@ func _process_remote_interpolation(_delta):
|
||||
# Fallback to simple lerp if not enough snapshots
|
||||
# Keep this very soft to smooth out transitions between tween and interpolation
|
||||
if global_position.distance_squared_to(target_visual_position) > 0.001:
|
||||
global_position = global_position.lerp(target_visual_position, _delta * 10.0)
|
||||
global_position = global_position.lerp(target_visual_position, _delta * 10.0)
|
||||
return
|
||||
|
||||
var render_time = Time.get_ticks_msec() - INTERPOLATION_OFFSET
|
||||
@@ -1063,7 +1063,7 @@ func _process_remote_interpolation(_delta):
|
||||
for i in range(1, snapshot_buffer.size()):
|
||||
if snapshot_buffer[i].time > render_time:
|
||||
newer = snapshot_buffer[i]
|
||||
older = snapshot_buffer[i-1]
|
||||
older = snapshot_buffer[i - 1]
|
||||
break
|
||||
|
||||
if newer:
|
||||
@@ -1257,7 +1257,7 @@ func _find_random_spawn_position() -> Vector2i:
|
||||
# We should check if it is NOT TILE_OBSTACLE.
|
||||
var item = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||
# Assuming 4 is obstacle, and -1 is void. 0 is walkable, 2 is safe zone.
|
||||
if item != -1 and item != 4:
|
||||
if item != -1 and item != 4:
|
||||
if not is_position_occupied(pos):
|
||||
available_positions.append(pos)
|
||||
|
||||
@@ -2164,13 +2164,13 @@ func sync_throw_tekton(target_pos: Vector2i):
|
||||
# 1. Stun nearby players (Radius 2?)
|
||||
# "if there's a player around that floor they will got stunned" -> "around that floor" implies radius
|
||||
var impact_center = target_pos
|
||||
var stun_radius = 1.5
|
||||
var stun_radius = 1.5
|
||||
|
||||
var players = get_tree().get_nodes_in_group("Players")
|
||||
print("[Throw] Checking stun impact at %s. Found %d players." % [impact_center, players.size()])
|
||||
|
||||
for p in players:
|
||||
if p == self: continue
|
||||
if p == self: continue
|
||||
|
||||
# Check distance
|
||||
var dist = Vector2(p.current_position.x, p.current_position.y).distance_to(Vector2(impact_center.x, impact_center.y))
|
||||
@@ -2184,7 +2184,7 @@ func sync_throw_tekton(target_pos: Vector2i):
|
||||
|
||||
# 2. Tekton drops tiles (Spawn tiles around) AND shrinks
|
||||
if tekton.has_method("on_thrown_landing"):
|
||||
tekton.on_thrown_landing(self, 2.0)
|
||||
tekton.on_thrown_landing(self , 2.0)
|
||||
else:
|
||||
# Fallback
|
||||
tekton.on_hit(self , 1.0)
|
||||
@@ -2218,7 +2218,7 @@ func sync_drop_tekton():
|
||||
|
||||
# Trigger landing effects (minimal scale)
|
||||
if tekton.has_method("on_thrown_landing"):
|
||||
tekton.on_thrown_landing(self, 1.0) # Minimal scale impact
|
||||
tekton.on_thrown_landing(self , 1.0) # Minimal scale impact
|
||||
|
||||
print("[Player %s] Dropped Tekton at %s" % [name, current_position])
|
||||
|
||||
@@ -2231,7 +2231,7 @@ func enter_attack_mode():
|
||||
|
||||
is_attack_mode = true
|
||||
is_knock_mode = false # Mutually exclusive
|
||||
NotificationManager.send_message(self, "Attack Mode ACTIVATED (Red)", NotificationManager.MessageType.POWERUP)
|
||||
NotificationManager.send_message(self , "Attack Mode ACTIVATED (Red)", NotificationManager.MessageType.POWERUP)
|
||||
update_active_player_indicator()
|
||||
|
||||
func enter_knock_mode():
|
||||
@@ -2239,7 +2239,7 @@ func enter_knock_mode():
|
||||
|
||||
is_knock_mode = true
|
||||
is_attack_mode = false # Mutually exclusive
|
||||
NotificationManager.send_message(self, "Knock Mode ACTIVATED (Yellow)", NotificationManager.MessageType.POWERUP)
|
||||
NotificationManager.send_message(self , "Knock Mode ACTIVATED (Yellow)", NotificationManager.MessageType.POWERUP)
|
||||
update_active_player_indicator()
|
||||
|
||||
func update_active_player_indicator():
|
||||
@@ -2276,7 +2276,7 @@ func knock_tekton():
|
||||
|
||||
# Reset Knock Mode after successful hit
|
||||
is_knock_mode = false
|
||||
NotificationManager.send_message(self, "Knock Successful!", NotificationManager.MessageType.POWERUP)
|
||||
NotificationManager.send_message(self , "Knock Successful!", NotificationManager.MessageType.POWERUP)
|
||||
update_active_player_indicator()
|
||||
else:
|
||||
# If we called knock_tekton but nothing was nearby, maybe we just enter the mode?
|
||||
@@ -2289,7 +2289,7 @@ func sync_knock_tekton(tekton_path: NodePath):
|
||||
if tekton:
|
||||
# Intensity 2.0 for knock (drops 200% tiles) + Shrink/Recover
|
||||
# Use on_thrown_landing to trigger shrink animation and floor freeze
|
||||
tekton.on_thrown_landing(self, 2.0)
|
||||
tekton.on_thrown_landing(self , 2.0)
|
||||
print("[Player %s] Knocked Tekton %s" % [name, tekton.name])
|
||||
|
||||
# Visual feedback (Juice)
|
||||
|
||||
Reference in New Issue
Block a user