feat: Implement Stop N Go game mode with phase management, mission objectives, and visual HUD elements.
This commit is contained in:
+18
-12
@@ -195,6 +195,9 @@ func _run_ai_tick():
|
||||
print("[BotController] Action Taken: Put")
|
||||
return
|
||||
|
||||
if not is_instance_valid(actor):
|
||||
return
|
||||
|
||||
var goals_achv = _is_goals_achieved()
|
||||
|
||||
if actor.action_points > 1: # Only print if they have multi-AP
|
||||
@@ -256,7 +259,7 @@ func _try_use_powerup() -> bool:
|
||||
NotificationManager.send_message(actor, NotificationManager.MESSAGES.USED_SPECIAL_POWER, NotificationManager.MessageType.POWERUP)
|
||||
|
||||
await _wait_with_variance(action_delay)
|
||||
if not is_instance_valid(self): return true # Early exit if deleted
|
||||
if not is_instance_valid(actor) or not is_instance_valid(self): return true # Early exit if deleted
|
||||
|
||||
_is_processing_action = false
|
||||
_current_action = "idle"
|
||||
@@ -301,7 +304,7 @@ func _try_attack_chase() -> bool:
|
||||
_is_processing_action = true
|
||||
_current_action = "attacking"
|
||||
await _wait_with_variance(action_delay) # Shorter delay for attacks? perhaos
|
||||
if not is_instance_valid(self): return true
|
||||
if not is_instance_valid(self) or not is_instance_valid(actor): return true
|
||||
_is_processing_action = false
|
||||
_current_action = "idle"
|
||||
return true
|
||||
@@ -365,7 +368,7 @@ func _try_grab() -> bool:
|
||||
|
||||
# Wait for animation
|
||||
await _wait_with_variance(action_delay)
|
||||
if not is_instance_valid(self): return true
|
||||
if not is_instance_valid(self) or not is_instance_valid(actor): return true
|
||||
_is_processing_action = false
|
||||
_current_action = "idle"
|
||||
return true
|
||||
@@ -466,17 +469,19 @@ func _try_move() -> bool:
|
||||
var max_wait_time = 2.0
|
||||
var elapsed = 0.0
|
||||
|
||||
while actor.is_player_moving and is_instance_valid(self):
|
||||
while is_instance_valid(actor) and actor.is_player_moving and is_instance_valid(self):
|
||||
await get_tree().process_frame
|
||||
elapsed += get_process_delta_time()
|
||||
if elapsed > max_wait_time:
|
||||
print("[BotController] %s movement TIMEOUT after %.1fs" % [actor.name, elapsed])
|
||||
if is_instance_valid(actor):
|
||||
print("[BotController] %s movement TIMEOUT after %.1fs" % [actor.name, elapsed])
|
||||
break
|
||||
|
||||
if not is_instance_valid(self): return true
|
||||
if not is_instance_valid(self) or not is_instance_valid(actor): return true
|
||||
_is_processing_action = false
|
||||
_current_action = "idle"
|
||||
print("[BotController] %s move finished. New Pos: %s" % [actor.name, actor.current_position])
|
||||
if is_instance_valid(actor):
|
||||
print("[BotController] %s move finished. New Pos: %s" % [actor.name, actor.current_position])
|
||||
return true
|
||||
else:
|
||||
print("[BotController] %s simple_move_to BLOCKED (others). Trying unstuck move." % actor.name)
|
||||
@@ -515,15 +520,16 @@ func _try_unstuck_move() -> bool:
|
||||
# Proper wait for movement completion
|
||||
var max_wait = 1.5
|
||||
var elapsed = 0.0
|
||||
while actor.is_player_moving and is_instance_valid(self):
|
||||
while is_instance_valid(actor) and actor.is_player_moving and is_instance_valid(self):
|
||||
await get_tree().process_frame
|
||||
elapsed += get_process_delta_time()
|
||||
if elapsed > max_wait: break
|
||||
|
||||
if not is_instance_valid(self): return true
|
||||
if not is_instance_valid(self) or not is_instance_valid(actor): return true
|
||||
_is_processing_action = false
|
||||
_current_action = "idle"
|
||||
print("[BotController] %s Unstuck move finished at %s" % [actor.name, actor.current_position])
|
||||
if is_instance_valid(actor):
|
||||
print("[BotController] %s Unstuck move finished at %s" % [actor.name, actor.current_position])
|
||||
return true
|
||||
|
||||
return false
|
||||
@@ -580,7 +586,7 @@ func _try_put(high_priority: bool = false) -> bool:
|
||||
print("[BotController] %s put unneeded tile %d at %s (Panic: %s)" % [actor.name, item, put_position, is_panic])
|
||||
|
||||
await _wait_with_variance(action_delay)
|
||||
if not is_instance_valid(self): return true
|
||||
if not is_instance_valid(actor) or not is_instance_valid(self): return true
|
||||
_is_processing_action = false
|
||||
_current_action = "idle"
|
||||
return true
|
||||
@@ -631,7 +637,7 @@ func _try_arrange() -> bool:
|
||||
print("[BotController] %s arranged slot %d -> %d" % [actor.name, arrangement.source_slot, arrangement.target_slot])
|
||||
|
||||
await _wait_with_variance(action_delay)
|
||||
if not is_instance_valid(self): return true
|
||||
if not is_instance_valid(actor) or not is_instance_valid(self): return true
|
||||
_is_processing_action = false
|
||||
_current_action = "idle"
|
||||
return true
|
||||
|
||||
@@ -120,6 +120,9 @@ func refresh_room_list() -> void:
|
||||
|
||||
func set_ready(is_ready: bool) -> void:
|
||||
"""Set local player's ready state."""
|
||||
if not multiplayer.has_multiplayer_peer():
|
||||
return
|
||||
|
||||
var my_id = multiplayer.get_unique_id()
|
||||
|
||||
# Update local state
|
||||
@@ -248,6 +251,10 @@ func set_character(character_name: String) -> void:
|
||||
return
|
||||
|
||||
local_character_index = idx
|
||||
|
||||
if not multiplayer.has_multiplayer_peer():
|
||||
return
|
||||
|
||||
var my_id = multiplayer.get_unique_id()
|
||||
|
||||
# Update local player data
|
||||
@@ -289,6 +296,11 @@ func sync_character(player_id: int, character_name: String) -> void:
|
||||
func set_player_name(new_name: String) -> void:
|
||||
"""Set local player's name. Syncs to all peers."""
|
||||
local_player_name = new_name
|
||||
|
||||
if not multiplayer.has_multiplayer_peer():
|
||||
emit_signal("player_list_changed")
|
||||
return
|
||||
|
||||
var my_id = multiplayer.get_unique_id()
|
||||
|
||||
# Update local player data
|
||||
@@ -412,6 +424,9 @@ func _on_match_joined(match_id: String) -> void:
|
||||
var short_id = match_id.substr(0, 8) if match_id.length() > 8 else match_id
|
||||
current_room["room_name"] = short_id
|
||||
|
||||
if not multiplayer.has_multiplayer_peer():
|
||||
return
|
||||
|
||||
# Add self to player list
|
||||
var my_id = multiplayer.get_unique_id()
|
||||
var my_data = {
|
||||
|
||||
@@ -11,7 +11,7 @@ enum Phase {GO, STOP}
|
||||
|
||||
const GO_DURATION: float = 8.0
|
||||
const STOP_DURATION: float = 4.0
|
||||
const REQUIRED_GOALS: int = 8
|
||||
const REQUIRED_GOALS: int = 1
|
||||
|
||||
var current_phase: Phase = Phase.GO
|
||||
var phase_timer: float = GO_DURATION
|
||||
|
||||
@@ -75,6 +75,10 @@ func connect_to_nakama_async(email: String = "", password: String = "") -> bool:
|
||||
|
||||
# 3. Initialize Multiplayer Bridge
|
||||
# This links Nakama's socket to Godot's High-Level Multiplayer API
|
||||
if bridge:
|
||||
bridge.leave()
|
||||
bridge = null
|
||||
|
||||
bridge = NakamaMultiplayerBridge.new(socket)
|
||||
|
||||
# Connect bridge signals
|
||||
@@ -89,6 +93,26 @@ func connect_to_nakama_async(email: String = "", password: String = "") -> bool:
|
||||
emit_signal("connected_to_nakama")
|
||||
return true
|
||||
|
||||
func cleanup():
|
||||
"""Properly shutdown the Nakama connection and reset the multiplayer peer."""
|
||||
print("[NakamaManager] Full cleanup starting...")
|
||||
|
||||
if bridge:
|
||||
bridge.leave()
|
||||
bridge = null
|
||||
|
||||
if socket:
|
||||
socket.close()
|
||||
socket = null
|
||||
|
||||
current_match_id = ""
|
||||
|
||||
# Reset Godot's multiplayer peer
|
||||
if multiplayer.get_multiplayer_peer():
|
||||
multiplayer.set_multiplayer_peer(null)
|
||||
|
||||
print("[NakamaManager] Cleanup complete.")
|
||||
|
||||
# --- Match Management ---
|
||||
|
||||
func host_game():
|
||||
@@ -172,5 +196,4 @@ func list_matches_async() -> Array:
|
||||
return rooms
|
||||
|
||||
func _exit_tree():
|
||||
if socket:
|
||||
socket.close()
|
||||
cleanup()
|
||||
|
||||
Reference in New Issue
Block a user