overhaul bot

This commit is contained in:
2026-01-03 03:40:20 +08:00
parent 7423e29443
commit cbbe95e108
185 changed files with 989 additions and 6514 deletions
+82 -78
View File
@@ -119,19 +119,28 @@ const AVAILABLE_CHARACTERS: Array[String] = ["Bob", "Masbro", "Gatot", "Oldpop"]
func _ready():
# Ensure name is set first (node name = authority ID for multiplayer identification)
name = str(get_multiplayer_authority())
# name = str(get_multiplayer_authority()) # CRITICAL FIX: Do NOT overwrite name. Bots have authority 1 but unique names (IDs).
# Look up player's display name from LobbyManager
var my_id = get_multiplayer_authority()
for player_data in LobbyManager.get_players():
if player_data.get("id") == my_id:
display_name = player_data.get("name", "Player")
break
if is_bot or is_in_group("Bots"):
# Bots get a unique name based on their Node Name (Bot ID)
var bot_id = name.to_int()
var bot_names = ["Bot", "Alpha", "Beta", "Gamma", "Delta"]
var name_idx = (bot_id - 1) % bot_names.size()
display_name = "%s %d" % ["Bot", bot_id]
else:
# Humans get name from Lobby
for player_data in LobbyManager.get_players():
if player_data.get("id") == my_id:
display_name = player_data.get("name", "Player")
break
if display_name.is_empty():
display_name = "Player %d" % my_id
$Name.text = display_name
# Sync name to other peers if this is our local player
# Sync name to other peers if this is our local player or a bot we own
if is_multiplayer_authority():
rpc("sync_display_name", display_name)
@@ -154,77 +163,58 @@ func _ready():
# Initialize character selection from LobbyManager
_setup_character()
# Early setup for bots
# Initialize playerboard for everyone
playerboard.resize(25)
playerboard.fill(-1)
# =========================================================================
# BOT-SPECIFIC SETUP - BotController handles bot AI, we just disable input
# =========================================================================
if is_bot == true or is_in_group("Bots"):
# Initialize behavior tree for bots
var behavior_tree = $BehaviorTree
# Disable all input processing for bots immediately
# Disable input processing for bots
set_process_input(false)
set_process_unhandled_input(false)
set_process(false)
set_physics_process(false)
# Disable visual highlights for bots
action_manager.highlighted_cells.clear()
if behavior_tree:
behavior_tree.enabled = is_multiplayer_authority()
behavior_tree.actor = self
rpc("sync_bot_status", true)
## Initialize bot-specific components
#if enhanced_gridmap:
#current_position = find_valid_starting_position()
#update_player_position(current_position)
# Spawn point handler
# Set initial position for bots
if enhanced_gridmap:
current_position = _find_random_spawn_position()
update_player_position(current_position)
spawn_point_selected = true
# Notify others about bot spawn position
rpc("notify_spawn_selected", current_position)
if is_multiplayer_authority():
rpc("notify_spawn_selected", current_position)
else:
# Human player initialization
if enhanced_gridmap:
enhanced_gridmap.initialize_astar()
enhanced_gridmap.set_diagonal_movement(use_diagonal_movement)
# Skip manual spawn selection if random spawn is enabled
# Host will assign positions via RPC
if not LobbyManager.get_randomize_spawn():
# Request current spawn positions before highlighting
request_spawn_positions_update()
highlight_available_spawn_points()
# Remove this line as goals are now managed by the host
#append_random_goals()
playerboard.resize(25)
playerboard.fill(-1)
return
# Assign bot character (deterministic based on ID to match lobby preview)
# Bot IDs start from 2 (host is 1)
# Lobby slots are 0-indexed in UI loop, but bots fill empty slots.
# Use name.to_int() because all bots have authority 1 (Server)
var bot_id_val = name.to_int()
var bot_characters = ["Bob", "Gatot", "Masbro", "Oldpop"]
# Map bot ID to character index. Bot 2 -> Index 1. Bot 3 -> Index 2.
# Formula: (bot_id - 1) % size
var char_index = (bot_id_val - 1) % bot_characters.size()
var bot_char_name = bot_characters[char_index]
set_character(bot_char_name)
if is_multiplayer_authority():
rpc("sync_character", bot_char_name)
# Sync bot status to network
if is_multiplayer_authority():
rpc("sync_bot_status", true)
return # Bot initialization complete - BotController handles AI
# Disable Beehave tree if this is not a bot
if not is_bot and has_node("BehaviorTree"):
$BehaviorTree.enabled = false
# Rest of initialization (only for human players)
# =========================================================================
# HUMAN PLAYER SETUP
# =========================================================================
if enhanced_gridmap:
enhanced_gridmap.initialize_astar()
enhanced_gridmap.set_diagonal_movement(use_diagonal_movement)
# Only set position if not using random spawn (host will assign via RPC)
# AND not already assigned
if not LobbyManager.get_randomize_spawn() and not spawn_point_selected:
current_position = find_valid_starting_position()
update_player_position(current_position)
#append_random_goals()
playerboard.resize(25)
playerboard.fill(-1)
# Ensure proper initial positioning (only if NOT using random spawn and not already positioned)
# When random spawn is enabled, the host assigns positions via set_spawn_position RPC
# Ensure proper initial positioning
if not LobbyManager.get_randomize_spawn() and not spawn_point_selected:
global_position = Vector3(
current_position.x * cell_size.x + cell_size.x * 0.5,
@@ -321,6 +311,11 @@ func sync_character(character_name: String) -> void:
func _setup_character() -> void:
"""Initialize character based on LobbyManager selection or defaults."""
# Bots self-assign characters based on ID in _ready()
# Skipping this for bots prevents race conditions or defaults overriding deterministic bot logic
if is_bot or is_in_group("Bots"):
return
var character_name = "Masbro" # Default
var player_authority_id = get_multiplayer_authority()
@@ -505,6 +500,29 @@ func receive_spawn_positions_update(occupied_positions: Array):
# Now highlight available positions
highlight_available_spawn_points()
@rpc("any_peer", "call_local", "reliable")
func sync_playerboard(new_playerboard: Array):
"""Sync playerboard data. Called by BotController or ActionManager."""
playerboard = new_playerboard.duplicate()
# Only update UI if this is the LOCAL HUMAN PLAYER
# Bots managed by server (ID 1) might sync here, but we must NOT update UI for them
var is_local = name == str(multiplayer.get_unique_id())
var is_bot_check = is_bot or is_in_group("Bots")
# DEBUG: Trace why UI updates might be happening
# print("[sync_playerboard] Player: %s (ID: %s), LocalID: %s, IsLocal: %s, IsBot: %s" % [name, get_multiplayer_authority(), multiplayer.get_unique_id(), is_local, is_bot_check])
if is_local and not is_bot_check:
var main = get_tree().get_root().get_node_or_null("Main")
if main and main.ui_manager:
main.ui_manager.update_playerboard_ui()
elif is_local and is_bot_check:
# This is a Bot running on the server (which is also the host)
# We must NOT update the Host's UI with the Bot's data
pass
@rpc("any_peer", "call_local")
func sync_bot_status(is_bot_status: bool):
is_bot = is_bot_status
@@ -513,18 +531,11 @@ func sync_bot_status(is_bot_status: bool):
set_process_input(false)
set_process_unhandled_input(false)
# Clear any existing highlights
action_manager.highlighted_cells.clear()
#clear_highlights()
#clear_playerboard_highlights()
# Clear any existing highlights (if action_manager is initialized)
if action_manager:
action_manager.highlighted_cells.clear()
var behavior_tree = get_node_or_null("BehaviorTree")
if behavior_tree:
behavior_tree.enabled = is_multiplayer_authority()
behavior_tree.actor = self
if not is_multiplayer_authority():
behavior_tree.set_physics_process(false)
behavior_tree.set_process(false)
# BotController already exists in scene and manages itself
@rpc("any_peer", "call_local", "reliable")
func sync_display_name(new_name: String) -> void:
@@ -1259,13 +1270,6 @@ func sync_second_lap_goals(new_goals: Array):
if race_manager:
race_manager.second_lap_goals = new_goals.duplicate()
@rpc("any_peer", "call_local")
func sync_playerboard(new_playerboard: Array):
playerboard = new_playerboard.duplicate()
var main = get_tree().get_root().get_node_or_null("Main")
if main:
main.rpc("sync_playerboard", get_multiplayer_authority(), playerboard)
action_manager.after_action_completed()
func _after_action_completed():
action_manager.after_action_completed()