overhaul bot
This commit is contained in:
+31
-1
@@ -452,7 +452,37 @@ func _update_player_slots() -> void:
|
||||
ready_label.add_theme_color_override("font_color",
|
||||
Color(0.4, 0.8, 0.4) if is_ready else Color(0.6, 0.6, 0.6))
|
||||
else:
|
||||
slot.visible = false
|
||||
# Empty slot - show as bot placeholder
|
||||
slot.visible = true
|
||||
|
||||
# Update name to show as bot
|
||||
var name_label = slot.get_node_or_null("PlayerName%d" % slot_num)
|
||||
if name_label:
|
||||
name_label.text = "🤖 Bot %d" % slot_num
|
||||
|
||||
# Use a character for bot preview
|
||||
var char_preview = slot.get_node_or_null("CharacterPreview%d" % slot_num)
|
||||
var bot_characters = ["Bob", "Gatot", "Masbro", "Oldpop"]
|
||||
var bot_char = bot_characters[(i) % bot_characters.size()]
|
||||
if char_preview and character_textures.has(bot_char):
|
||||
char_preview.texture = character_textures[bot_char]
|
||||
|
||||
# Hide character navigation for bots
|
||||
var char_nav = slot.get_node_or_null("CharacterNav%d" % slot_num)
|
||||
if char_nav:
|
||||
char_nav.visible = false
|
||||
|
||||
# Update character name label
|
||||
var char_name_label = slot.get_node_or_null("CharacterNameLabel%d" % slot_num)
|
||||
if char_name_label:
|
||||
char_name_label.text = bot_char
|
||||
char_name_label.visible = true
|
||||
|
||||
# Show ready status as waiting
|
||||
var ready_label = slot.get_node_or_null("ReadyStatus%d" % slot_num)
|
||||
if ready_label:
|
||||
ready_label.text = "WAITING..."
|
||||
ready_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.7))
|
||||
|
||||
func _update_status() -> void:
|
||||
var players = LobbyManager.get_players()
|
||||
|
||||
+13
-9
@@ -342,9 +342,10 @@ func _setup_host_game():
|
||||
call_deferred("_deferred_set_player_goals", peer_id, client_goals)
|
||||
player_index += 1
|
||||
|
||||
# Add bots (only if no lobby players connected)
|
||||
if GameStateManager.enable_bots and lobby_players.size() <= 1:
|
||||
for i in range(2, GameStateManager.max_players + 1):
|
||||
# Add bots to fill remaining slots (regardless of player count)
|
||||
if GameStateManager.enable_bots:
|
||||
var current_players = lobby_players.size()
|
||||
for i in range(current_players + 1, GameStateManager.max_players + 1):
|
||||
_add_bot(i)
|
||||
|
||||
_start_game()
|
||||
@@ -777,7 +778,8 @@ func update_all_players_boards():
|
||||
func verify_all_connections():
|
||||
if multiplayer.is_server():
|
||||
for peer_id in GameStateManager.players:
|
||||
if peer_id != 1:
|
||||
# Skip host (1) and bots (bots don't have real network connections)
|
||||
if peer_id != 1 and not peer_id in GameStateManager.bots:
|
||||
rpc_id(peer_id, "connection_verify", GameStateManager.players)
|
||||
|
||||
@rpc
|
||||
@@ -956,9 +958,10 @@ func _on_leaderboard_updated(sorted_scores: Array):
|
||||
var player_data = []
|
||||
for p in get_tree().get_nodes_in_group("Players"):
|
||||
player_data.append({
|
||||
"peer_id": p.get_multiplayer_authority(),
|
||||
# Use name.to_int() to correctly identify bots (Authority 1) vs Players
|
||||
"peer_id": p.name.to_int(),
|
||||
"name": p.display_name if not p.display_name.is_empty() else str(p.name),
|
||||
"score": goals_cycle_manager.get_player_score(p.get_multiplayer_authority()) if goals_cycle_manager else 0
|
||||
"score": goals_cycle_manager.get_player_score(p.name.to_int()) if goals_cycle_manager else 0
|
||||
})
|
||||
rpc("sync_leaderboard_data", player_data)
|
||||
|
||||
@@ -1148,9 +1151,10 @@ func request_leaderboard_sync():
|
||||
var player_data = []
|
||||
for p in get_tree().get_nodes_in_group("Players"):
|
||||
player_data.append({
|
||||
"peer_id": p.get_multiplayer_authority(),
|
||||
# Use name.to_int() for consistent ID
|
||||
"peer_id": p.name.to_int(),
|
||||
"name": p.display_name if not p.display_name.is_empty() else str(p.name),
|
||||
"score": goals_cycle_manager.get_player_score(p.get_multiplayer_authority()) if goals_cycle_manager else 0
|
||||
"score": goals_cycle_manager.get_player_score(p.name.to_int()) if goals_cycle_manager else 0
|
||||
})
|
||||
rpc_id(sender_id, "sync_leaderboard_data", player_data)
|
||||
|
||||
@@ -1209,7 +1213,7 @@ func _update_leaderboard_display():
|
||||
# Build scores array with all players
|
||||
var player_data = []
|
||||
for p in all_players:
|
||||
var peer_id = p.get_multiplayer_authority()
|
||||
var peer_id = p.name.to_int()
|
||||
var score = goals_cycle_manager.get_player_score(peer_id) if goals_cycle_manager else 0
|
||||
player_data.append({"peer_id": peer_id, "name": p.display_name if not p.display_name.is_empty() else str(p.name), "score": score})
|
||||
|
||||
|
||||
+82
-78
@@ -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()
|
||||
|
||||
+4
-72
@@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=25 format=3 uid="uid://1dbdbg3q5778"]
|
||||
[gd_scene load_steps=13 format=3 uid="uid://1dbdbg3q5778"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c78jcadupsdro" path="res://scenes/player.gd" id="1_qecr4"]
|
||||
[ext_resource type="PackedScene" uid="uid://ejeamn0pyey4" path="res://assets/characters/Bob.glb" id="2_3e0d5"]
|
||||
@@ -7,19 +7,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://d4cul3w3wem5w" path="res://assets/characters/Gatot.glb" id="4_3tlf6"]
|
||||
[ext_resource type="PackedScene" uid="uid://bmln7v6v5kvxg" path="res://assets/characters/Oldpop.glb" id="5_alfd1"]
|
||||
[ext_resource type="AnimationLibrary" uid="uid://c3pyopnwibckj" path="res://assets/characters/animations/animation-pack.res" id="6_5oq5w"]
|
||||
[ext_resource type="Script" uid="uid://6g75rh3nj2s6" path="res://scripts/bot_behavior.gd" id="8_1o2fn"]
|
||||
[ext_resource type="Script" uid="uid://dtctf507t71tj" path="res://addons/beehave/nodes/composites/selector.gd" id="9_jspru"]
|
||||
[ext_resource type="Script" uid="uid://t6e8rtdiqhg5" path="res://addons/beehave/nodes/composites/sequence.gd" id="10_hv4ee"]
|
||||
[ext_resource type="Script" uid="uid://b17qem72laaeb" path="res://scripts/behaviors/conditions/has_ap.gd" id="11_7fhpq"]
|
||||
[ext_resource type="Script" uid="uid://b4fxorcb1yq17" path="res://scripts/behaviors/actions/do_arrange.gd" id="12_1ppih"]
|
||||
[ext_resource type="Script" uid="uid://b25qg75d0xgkh" path="res://scripts/behaviors/conditions/can_arrange.gd" id="12_hr248"]
|
||||
[ext_resource type="Script" uid="uid://cui40g7qjf1y3" path="res://scripts/behaviors/conditions/can_grab.gd" id="13_41jsv"]
|
||||
[ext_resource type="Script" uid="uid://cuyorbwefmh0y" path="res://scripts/behaviors/actions/do_grab.gd" id="15_5h472"]
|
||||
[ext_resource type="Script" uid="uid://b7y30e5mxygj0" path="res://scripts/behaviors/conditions/can_put.gd" id="16_ac2sy"]
|
||||
[ext_resource type="Script" uid="uid://bdw5bwmr32h63" path="res://scripts/behaviors/actions/do_put.gd" id="17_e03nk"]
|
||||
[ext_resource type="Script" uid="uid://bc7jpc1bwy4dg" path="res://scripts/behaviors/conditions/should_move.gd" id="18_2ghcp"]
|
||||
[ext_resource type="Script" uid="uid://cireifbxafgf2" path="res://scripts/behaviors/actions/do_move.gd" id="19_dl4fn"]
|
||||
[ext_resource type="Script" uid="uid://d2cr28ak2s1rr" path="res://scripts/bot_blackboard.gd" id="20_24ja6"]
|
||||
[ext_resource type="Script" uid="uid://cwwwixc07jc86" path="res://scripts/bot_controller.gd" id="7_botctrl"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xqgey"]
|
||||
albedo_color = Color(0.85, 0.085, 0.238, 1)
|
||||
@@ -100,61 +88,5 @@ autowrap_mode = 3
|
||||
justification_flags = 171
|
||||
width = 700.0
|
||||
|
||||
[node name="Blackboard" type="Node" parent="."]
|
||||
script = ExtResource("20_24ja6")
|
||||
|
||||
[node name="BehaviorTree" type="Node" parent="." node_paths=PackedStringArray("blackboard")]
|
||||
script = ExtResource("8_1o2fn")
|
||||
tick_rate = 90
|
||||
blackboard = NodePath("../Blackboard")
|
||||
|
||||
[node name="Selector" type="Node" parent="BehaviorTree"]
|
||||
script = ExtResource("9_jspru")
|
||||
|
||||
[node name="GrabSequence" type="Node" parent="BehaviorTree/Selector"]
|
||||
script = ExtResource("10_hv4ee")
|
||||
|
||||
[node name="HasAP" type="Node" parent="BehaviorTree/Selector/GrabSequence"]
|
||||
script = ExtResource("11_7fhpq")
|
||||
|
||||
[node name="CanGrab" type="Node" parent="BehaviorTree/Selector/GrabSequence"]
|
||||
script = ExtResource("13_41jsv")
|
||||
|
||||
[node name="DoGrab" type="Node" parent="BehaviorTree/Selector/GrabSequence"]
|
||||
script = ExtResource("15_5h472")
|
||||
|
||||
[node name="MoveSequence" type="Node" parent="BehaviorTree/Selector"]
|
||||
script = ExtResource("10_hv4ee")
|
||||
|
||||
[node name="HasAP" type="Node" parent="BehaviorTree/Selector/MoveSequence"]
|
||||
script = ExtResource("11_7fhpq")
|
||||
|
||||
[node name="ShouldMove" type="Node" parent="BehaviorTree/Selector/MoveSequence"]
|
||||
script = ExtResource("18_2ghcp")
|
||||
|
||||
[node name="DoMove" type="Node" parent="BehaviorTree/Selector/MoveSequence"]
|
||||
script = ExtResource("19_dl4fn")
|
||||
|
||||
[node name="PutSequence" type="Node" parent="BehaviorTree/Selector"]
|
||||
script = ExtResource("10_hv4ee")
|
||||
|
||||
[node name="HasAP" type="Node" parent="BehaviorTree/Selector/PutSequence"]
|
||||
script = ExtResource("11_7fhpq")
|
||||
|
||||
[node name="CanPut" type="Node" parent="BehaviorTree/Selector/PutSequence"]
|
||||
script = ExtResource("16_ac2sy")
|
||||
|
||||
[node name="DoPut" type="Node" parent="BehaviorTree/Selector/PutSequence"]
|
||||
script = ExtResource("17_e03nk")
|
||||
|
||||
[node name="ArrangeSequence" type="Node" parent="BehaviorTree/Selector"]
|
||||
script = ExtResource("10_hv4ee")
|
||||
|
||||
[node name="HasEnoughAP" type="Node" parent="BehaviorTree/Selector/ArrangeSequence"]
|
||||
script = ExtResource("11_7fhpq")
|
||||
|
||||
[node name="CanArrange" type="Node" parent="BehaviorTree/Selector/ArrangeSequence"]
|
||||
script = ExtResource("12_hr248")
|
||||
|
||||
[node name="DoArrange" type="Node" parent="BehaviorTree/Selector/ArrangeSequence"]
|
||||
script = ExtResource("12_1ppih")
|
||||
[node name="BotController" type="Node" parent="."]
|
||||
script = ExtResource("7_botctrl")
|
||||
|
||||
Reference in New Issue
Block a user