diff --git a/scenes/main.gd b/scenes/main.gd index 87fd292..c0dcda7 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -232,11 +232,19 @@ func _on_join_pressed(): func _on_peer_connected(new_peer_id): if multiplayer.is_server(): - await get_tree().create_timer(1).timeout - rpc("add_newly_connected_player_character", new_peer_id) - rpc_id(new_peer_id, "add_previously_connected_player_characters", connected_peer_ids) + # Increase delay to ensure scene is ready + await get_tree().create_timer(1.5).timeout + + # Sync full state first rpc_id(new_peer_id, "sync_game_state", players, bots, game_started, turn_based_mode) + + # Then add players in correct order + for peer_id in connected_peer_ids: + rpc_id(new_peer_id, "add_player_character", peer_id) + + # Finally add the new player add_player_character(new_peer_id) + rpc("add_newly_connected_player_character", new_peer_id) replace_bot_with_player(new_peer_id) func _on_peer_disconnected(peer_id): @@ -270,23 +278,32 @@ func add_bot(bot_id): @rpc("call_local") func create_bot(bot_id): + # Ensure we're not duplicating bots + if has_node(str(bot_id)): + push_error("Bot already exists: " + str(bot_id)) + return + var bot_character = player_scene.instantiate() - bot_character.set_multiplayer_authority(1) + if not bot_character: + push_error("Failed to instantiate bot scene") + return + + bot_character.set_multiplayer_authority(1) # Server controls bots bot_character.name = str(bot_id) - bot_character.is_bot = true # Set bot flag - add_child(bot_character) + + # Add to scene tree + call_deferred("add_child", bot_character) + + # Add to groups after adding to scene tree bot_character.add_to_group("Players", true) bot_character.add_to_group("Bots", true) - # Get behavior tree reference - var behavior_tree = bot_character.get_node_or_null("BehaviorTree") - if behavior_tree: - behavior_tree.enabled = true - behavior_tree.actor = bot_character - if multiplayer.is_server(): bots.append(bot_id) players.append(bot_id) + # Sync bot status after a short delay to ensure node is ready + await get_tree().create_timer(0.1).timeout + bot_character.rpc("sync_bot_status", true) func replace_bot_with_player(player_id): if multiplayer.is_server() and bots.size() > 0: diff --git a/scenes/player.gd b/scenes/player.gd index dba46c9..d833a94 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -36,7 +36,6 @@ var rotation_speed: float = 10.0 rpc("display_message", "It's your turn!") @export var has_moved_this_turn = false -@onready var main_scene = get_tree().current_scene var highlighted_cells = [] @@ -44,40 +43,53 @@ func _ready(): name = str(get_multiplayer_authority()) $Name.text = str(name) - enhanced_gridmap = get_node(enhanced_gridmap_path) - - if main_scene: - enhanced_gridmap = main_scene.get_node("EnhancedGridMap") - else: + # More robust way to get the main scene + var main_scene = get_tree().get_root().get_node_or_null("Main") + if not main_scene: push_error("Main scene not found") - - if not enhanced_gridmap: - push_error("EnhancedGridMap node not found") return - enhanced_gridmap.initialize_astar() - enhanced_gridmap.set_diagonal_movement(use_diagonal_movement) + # Ensure proper initialization order + enhanced_gridmap = get_node(enhanced_gridmap_path) + if main_scene: + enhanced_gridmap = main_scene.get_node("EnhancedGridMap") - current_position = find_valid_starting_position() - update_player_position(current_position) + # Initialize behavior tree for bots + var behavior_tree = $BehaviorTree - # Set bot flag if in Bots group - if is_in_group("Bots"): - is_bot = true + + if is_in_group("Bots") and behavior_tree: + behavior_tree.enabled = true + behavior_tree.actor = self - # Only process input if not a bot and is authority - set_process_unhandled_input(not is_bot and is_multiplayer_authority()) + rpc("sync_bot_status", true) + + # Rest of initialization + if enhanced_gridmap: + enhanced_gridmap.initialize_astar() + enhanced_gridmap.set_diagonal_movement(use_diagonal_movement) + + current_position = find_valid_starting_position() + update_player_position(current_position) + + set_process_unhandled_input(not is_in_group("Bots") and is_multiplayer_authority()) append_random_goals() playerboard.resize(25) playerboard.fill(-1) - - # Enable behavior tree if bot + +@rpc("any_peer", "call_local") +func sync_bot_status(is_bot_status: bool): + is_bot = is_bot_status if is_bot: - var behavior_tree = $BehaviorTree + add_to_group("Bots", true) # Persistent group addition + var behavior_tree = get_node_or_null("BehaviorTree") if behavior_tree: behavior_tree.enabled = true behavior_tree.actor = self + if not is_multiplayer_authority(): + behavior_tree.set_physics_process(false) + behavior_tree.set_process(false) func _physics_process(_delta): if is_multiplayer_authority(): @@ -87,7 +99,11 @@ func _unhandled_input(event): #if is_in_group("Bots"): #return - var main = get_node("/root/Main/") + # Use get_node_or_null for safer node access + var main = get_tree().get_root().get_node_or_null("Main") + if not main: + return + if not is_multiplayer_authority() or (main.turn_based_mode and (not is_my_turn or is_player_moving)): return @@ -102,7 +118,8 @@ func _unhandled_input(event): func _on_slot_gui_input(event, slot_index, slot_ui) -> int: if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") + if main.current_action_state == main.ActionState.ARRANGING: if selected_playerboard_slot == -1: select_playerboard_slot(slot_index) @@ -120,8 +137,9 @@ func _on_slot_gui_input(event, slot_index, slot_ui) -> int: return -1 func handle_grid_click(grid_position: Vector2i): - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main: + push_error("Main node not found") return match main.current_action_state: @@ -210,7 +228,7 @@ func move_player_to_clicked_position(grid_position: Vector2i): if not is_multiplayer_authority() or is_player_moving or action_points <= 0: return - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main or main.current_action_state != main.ActionState.MOVING or not grid_position in highlighted_cells: return @@ -257,7 +275,7 @@ func start_movement_along_path(path: Array): enhanced_gridmap.clear_path_visualization() has_moved_this_turn = path.size() <= movement_range - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") main.set_action_state(main.ActionState.NONE) if main.turn_based_mode: @@ -367,7 +385,7 @@ func grab_item(grid_position: Vector2i = current_position) -> bool: _after_action_completed() return true else: - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if main: selected_gridmap_position = grid_position clear_highlights() @@ -414,14 +432,14 @@ func put_item(grid_position: Vector2i = current_position) -> bool: clear_playerboard_highlights() selected_playerboard_slot = -1 - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if main: main.set_action_state(main.ActionState.NONE) _after_action_completed() return true func handle_put_action(): - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main or action_points < 1: return @@ -437,7 +455,7 @@ func handle_put_action(): highlighted_cells.append(i) func handle_playerboard_slot_selected(slot_index: int): - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main: return @@ -467,7 +485,7 @@ func handle_playerboard_slot_selected(slot_index: int): # We also need to add handle_put_slot_selected: func handle_put_slot_selected(slot_index: int): - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main or main.current_action_state != main.ActionState.PUTTING: return @@ -483,7 +501,7 @@ func handle_put_slot_selected(slot_index: int): #var selected_item = playerboard[slot_index] #var adjacent_slots = get_adjacent_playerboard_slots(slot_index) # - #var main = get_node("/root/Main") + #var main = get_tree().get_root().get_node_or_null("Main") #if not main or not main.playerboard_ui: #return # @@ -528,7 +546,7 @@ func arrange_playerboard_item(slot_index: int): var selected_item = playerboard[slot_index] var adjacent_slots = get_adjacent_playerboard_slots(slot_index) - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main or not main.playerboard_ui: return @@ -558,7 +576,7 @@ func _on_slot_clicked(event: InputEvent, slot_index: int): if not event is InputEventMouseButton or not event.pressed or event.button_index != MOUSE_BUTTON_LEFT: return - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main or main.current_action_state != main.ActionState.ARRANGING: return @@ -760,7 +778,7 @@ func highlight_occupied_playerboard_slots(): #if is_in_group("Bots"): #return - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main or not main.playerboard_ui: return @@ -791,7 +809,7 @@ func clear_highlights(): highlighted_cells.clear() - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if main and main.playerboard_ui: for i in range(main.playerboard_ui.get_child_count()): var slot = main.playerboard_ui.get_child(i) @@ -799,7 +817,7 @@ func clear_highlights(): child.hide() func clear_playerboard_highlights(): - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if main and main.playerboard_ui: for i in range(main.playerboard_ui.get_child_count()): var slot = main.playerboard_ui.get_child(i) @@ -853,7 +871,7 @@ func can_move_to_target_playerboard_slot() -> bool: return adjacent_slots.has(targeted_playerboard_slot) func _update_playerboard_slot_visual(slot_index: int): - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main or not main.playerboard_ui: return @@ -867,7 +885,7 @@ func _update_playerboard_slot_visual(slot_index: int): slot.get_child(2).visible = selected_playerboard_slot != -1 and get_adjacent_playerboard_slots(selected_playerboard_slot).has(slot_index) func _highlight_adjacent_playerboard_slots(): - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main or not main.playerboard_ui: return @@ -902,14 +920,34 @@ func sync_playerboard(new_playerboard: Array): playerboard = new_playerboard _after_action_completed() +@rpc("any_peer", "call_local") +func sync_behavior_tree(enabled: bool): + var behavior_tree = $BehaviorTree + if behavior_tree: + behavior_tree.enabled = enabled + behavior_tree.actor = self + #behavior_tree.blackboard = blackboard + + ## Ensure blackboard has required values + #blackboard.set_value("action_points", action_points) + #blackboard.set_value("goals", goals) + #blackboard.set_value("playerboard", playerboard) + func _after_action_completed(): if multiplayer.get_unique_id() == get_multiplayer_authority(): - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if main: - if not main.turn_based_mode and action_points <= 0: - action_points = 2 + # Add this condition for bots + if not main.turn_based_mode and (action_points <= 0 or is_bot): + action_points = 20 # For bots in non-turn-based mode, this will keep refreshing has_performed_action = false has_moved_this_turn = false + + # Update blackboard after action points change + #var blackboard = $Blackboard + #if blackboard: + #blackboard.set_value("action_points", action_points) + main.update_button_states() main.update_playerboard_ui() @@ -917,11 +955,17 @@ func consume_action_points(points: int): if not is_instance_valid(self) or not is_multiplayer_authority(): return - action_points -= points - var main = get_node("/root/Main") + var main = get_tree().get_root().get_node_or_null("Main") if not main: return + # Don't consume points for bots in non-turn-based mode + if is_bot and not main.turn_based_mode: + _after_action_completed() + return + + action_points -= points + if action_points <= 0: if main.turn_based_mode: main.request_end_turn() diff --git a/scenes/player.tscn b/scenes/player.tscn index 969a5b7..c242829 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=13 format=3 uid="uid://1dbdbg3q5778"] +[gd_scene load_steps=25 format=3 uid="uid://1dbdbg3q5778"] [ext_resource type="Script" path="res://scenes/player.gd" id="1_qecr4"] [ext_resource type="PackedScene" uid="uid://ejeamn0pyey4" path="res://assets/characters/Bob.glb" id="2_3e0d5"] @@ -8,6 +8,18 @@ [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" path="res://scripts/bot_behavior.gd" id="8_1o2fn"] +[ext_resource type="Script" path="res://addons/beehave/nodes/composites/selector.gd" id="9_jspru"] +[ext_resource type="Script" path="res://addons/beehave/nodes/composites/sequence.gd" id="10_hv4ee"] +[ext_resource type="Script" path="res://scripts/behaviors/conditions/has_ap.gd" id="11_7fhpq"] +[ext_resource type="Script" path="res://scripts/behaviors/actions/do_arrange.gd" id="12_1ppih"] +[ext_resource type="Script" path="res://scripts/behaviors/conditions/can_arrange.gd" id="12_hr248"] +[ext_resource type="Script" path="res://scripts/behaviors/conditions/can_grab.gd" id="13_41jsv"] +[ext_resource type="Script" path="res://scripts/behaviors/actions/do_grab.gd" id="15_5h472"] +[ext_resource type="Script" path="res://scripts/behaviors/conditions/can_put.gd" id="16_ac2sy"] +[ext_resource type="Script" path="res://scripts/behaviors/actions/do_put.gd" id="17_e03nk"] +[ext_resource type="Script" path="res://scripts/behaviors/conditions/should_move.gd" id="18_2ghcp"] +[ext_resource type="Script" path="res://scripts/behaviors/actions/do_move.gd" id="19_dl4fn"] +[ext_resource type="Script" path="res://scripts/bot_blackboard.gd" id="20_24ja6"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xqgey"] albedo_color = Color(0.85, 0.085, 0.238, 1) @@ -87,7 +99,61 @@ autowrap_mode = 3 justification_flags = 171 width = 700.0 -[node name="BehaviorTree" type="Node" parent="."] +[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 = 60 -actor_node_path = NodePath("\"..\"") +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") diff --git a/scripts/behaviors/actions/do_move.gd b/scripts/behaviors/actions/do_move.gd index a32126f..ccfbc05 100644 --- a/scripts/behaviors/actions/do_move.gd +++ b/scripts/behaviors/actions/do_move.gd @@ -1,6 +1,7 @@ extends ActionLeaf func tick(actor: Node, blackboard: Blackboard) -> int: + # Get target from blackboard var target_pos = blackboard.get_value("move_target") if not target_pos: return FAILURE @@ -8,37 +9,18 @@ func tick(actor: Node, blackboard: Blackboard) -> int: if actor.action_points <= 0: return FAILURE - # Verify target is still valid - if not actor.is_within_movement_range(target_pos): - return FAILURE - - if actor.is_position_occupied(target_pos): - return FAILURE - - var cell_item = actor.enhanced_gridmap.get_cell_item(Vector3i(target_pos.x, 0, target_pos.y)) - if cell_item == -1 or cell_item in actor.enhanced_gridmap.non_walkable_items: - return FAILURE - - # Move to position - actor.rotate_towards_target(target_pos) - var path = actor.enhanced_gridmap.find_path(Vector2(actor.current_position), Vector2(target_pos)) - - if path.size() <= 1: - return FAILURE - - # Verify path is clear - var valid_path = true - for point in path.slice(1): - if actor.is_position_occupied(Vector2i(point.x, point.y)): - valid_path = false - break - - if valid_path: + # Execute movement + if actor.is_within_movement_range(target_pos): if actor.is_multiplayer_authority(): - path.pop_front() - actor.rpc("start_movement_along_path", path) - actor.action_points -= 1 - actor.clear_highlights() - return SUCCESS - + var path = actor.enhanced_gridmap.find_path( + Vector2(actor.current_position), + Vector2(target_pos) + ) + if path.size() > 1: + path.pop_front() + actor.rpc("start_movement_along_path", path) + actor.action_points -= 1 + blackboard.set_value("current_action", "moving") + return SUCCESS + return FAILURE diff --git a/scripts/behaviors/actions/grab_action.gd b/scripts/behaviors/actions/grab_action.gd deleted file mode 100644 index 954d1fb..0000000 --- a/scripts/behaviors/actions/grab_action.gd +++ /dev/null @@ -1,11 +0,0 @@ -class_name GrabAction extends ActionLeaf - -func tick(actor: Node, blackboard: Blackboard) -> int: - var grab_position = blackboard.get_value("grab_position") - if not grab_position: - return FAILURE - - if actor.grab_item(grab_position): - return SUCCESS - - return FAILURE diff --git a/scripts/behaviors/arrange_sequence.gd b/scripts/behaviors/arrange_sequence.gd deleted file mode 100644 index 9667a38..0000000 --- a/scripts/behaviors/arrange_sequence.gd +++ /dev/null @@ -1,5 +0,0 @@ -extends SequenceComposite - -func _ready(): - add_child(ArrangeCondition.new()) - add_child(ArrangeAction.new()) diff --git a/scripts/behaviors/conditions/arrange_action.gd b/scripts/behaviors/conditions/arrange_action.gd deleted file mode 100644 index 8e6818b..0000000 --- a/scripts/behaviors/conditions/arrange_action.gd +++ /dev/null @@ -1,22 +0,0 @@ -class_name ArrangeAction extends ActionLeaf - -func tick(actor: Node, blackboard: Blackboard) -> int: - var source_slot = blackboard.get_value("source_slot", -1) - var target_slot = blackboard.get_value("target_slot", -1) - - if source_slot == -1 or target_slot == -1: - return FAILURE - - if actor.action_points < 2: - return FAILURE - - var selected_item = actor.playerboard[source_slot] - actor.playerboard[target_slot] = selected_item - actor.playerboard[source_slot] = -1 - - if actor.is_multiplayer_authority(): - actor.rpc("sync_playerboard", actor.playerboard) - actor.action_points -= 2 - actor.has_performed_action = true - - return SUCCESS diff --git a/scripts/behaviors/conditions/arrange_condition.gd b/scripts/behaviors/conditions/arrange_condition.gd deleted file mode 100644 index b2e2cd9..0000000 --- a/scripts/behaviors/conditions/arrange_condition.gd +++ /dev/null @@ -1,18 +0,0 @@ -class_name ArrangeCondition extends ConditionLeaf - -func tick(actor: Node, blackboard: Blackboard) -> int: - # Check we have enough action points - if actor.action_points < 2: - return FAILURE - - # Check for items that can be arranged - for i in range(actor.playerboard.size()): - if actor.playerboard[i] != -1: - var neighbors = actor.get_adjacent_playerboard_slots(i) - for adj_slot in neighbors: - if actor.playerboard[adj_slot] == -1 and actor.playerboard[i] in actor.goals: - blackboard.set_value("source_slot", i) - blackboard.set_value("target_slot", adj_slot) - return SUCCESS - - return FAILURE diff --git a/scripts/behaviors/conditions/grab_condition.gd b/scripts/behaviors/conditions/grab_condition.gd deleted file mode 100644 index 834da45..0000000 --- a/scripts/behaviors/conditions/grab_condition.gd +++ /dev/null @@ -1,21 +0,0 @@ -class_name GrabCondition extends ConditionLeaf - -func tick(actor: Node, blackboard: Blackboard) -> int: - if actor.action_points < 1 or actor.playerboard_is_full(): - return FAILURE - - # Check current position first - var current_cell = Vector3i(actor.current_position.x, 1, actor.current_position.y) - if actor.enhanced_gridmap.get_cell_item(current_cell) in actor.goals: - blackboard.set_value("grab_position", actor.current_position) - return SUCCESS - - # Check adjacent cells - var neighbors = actor.enhanced_gridmap.get_neighbors(actor.current_position, 1) - for neighbor in neighbors: - var cell = Vector3i(neighbor.position.x, 1, neighbor.position.y) - if actor.enhanced_gridmap.get_cell_item(cell) in actor.goals: - blackboard.set_value("grab_position", neighbor.position) - return SUCCESS - - return FAILURE diff --git a/scripts/behaviors/conditions/has_ap.gd b/scripts/behaviors/conditions/has_ap.gd index dc4a30a..c950c90 100644 --- a/scripts/behaviors/conditions/has_ap.gd +++ b/scripts/behaviors/conditions/has_ap.gd @@ -1,6 +1,18 @@ extends ConditionLeaf func tick(actor: Node, blackboard: Blackboard) -> int: + var main = get_tree().get_root().get_node_or_null("Main") + if not main: + return FAILURE + + # Always return SUCCESS for bots in non-turn-based mode + if actor.is_bot and not main.turn_based_mode: + return SUCCESS + + # Update action points in blackboard + blackboard.set_value("action_points", actor.action_points) + + # Check if we have enough AP if actor.action_points >= 1: return SUCCESS return FAILURE diff --git a/scripts/behaviors/conditions/should_move.gd b/scripts/behaviors/conditions/should_move.gd index 86b69b7..98ac474 100644 --- a/scripts/behaviors/conditions/should_move.gd +++ b/scripts/behaviors/conditions/should_move.gd @@ -1,85 +1,27 @@ extends ConditionLeaf func tick(actor: Node, blackboard: Blackboard) -> int: - if actor.action_points <= 0: - return FAILURE - - var target_pos = find_best_move_position(actor) - if target_pos == Vector2i(-1, -1): - return FAILURE - - blackboard.set_value("move_target", target_pos) - return SUCCESS + # Find a valid movement target + var target_pos = find_valid_target(actor) + if target_pos: + # Store target in blackboard + blackboard.set_value("move_target", target_pos) + return SUCCESS + return FAILURE -func find_best_move_position(actor: Node) -> Vector2i: - # First priority: Move towards items that match our goals - var closest_goal_item = find_closest_goal_item(actor) - if closest_goal_item != Vector2i(-1, -1): - return get_position_towards(actor, closest_goal_item) - - # Second priority: Move towards empty cells if we have items - if actor.has_items_in_playerboard(): - var empty_pos = find_closest_empty_cell(actor) - if empty_pos != Vector2i(-1, -1): - return get_position_towards(actor, empty_pos) +func find_valid_target(actor: Node) -> Vector2i: + # Get random position in range + var valid_positions = [] - # Last resort: Random valid move - return actor.find_random_valid_position_in_range() - -func find_closest_goal_item(actor: Node) -> Vector2i: - var min_distance = 999999 - var closest_pos = Vector2i(-1, -1) - - for x in range(actor.enhanced_gridmap.columns): - for z in range(actor.enhanced_gridmap.rows): - var cell = Vector3i(x, 1, z) - var item = actor.enhanced_gridmap.get_cell_item(cell) - if item in actor.goals: - var dist = actor.current_position.distance_squared_to(Vector2i(x, z)) - if dist < min_distance: - min_distance = dist - closest_pos = Vector2i(x, z) - - return closest_pos - -func find_closest_empty_cell(actor: Node) -> Vector2i: - var min_distance = 999999 - var closest_pos = Vector2i(-1, -1) - - for x in range(actor.enhanced_gridmap.columns): - for z in range(actor.enhanced_gridmap.rows): - var cell = Vector3i(x, 1, z) - if actor.enhanced_gridmap.get_cell_item(cell) == -1: - var dist = actor.current_position.distance_squared_to(Vector2i(x, z)) - if dist < min_distance: - min_distance = dist - closest_pos = Vector2i(x, z) - - return closest_pos - -func get_position_towards(actor: Node, target: Vector2i) -> Vector2i: - # Find a valid position within movement range that's closest to target - var best_pos = Vector2i(-1, -1) - var min_distance = 999999 - - for x in range(actor.current_position.x - actor.movement_range, - actor.current_position.x + actor.movement_range + 1): - for z in range(actor.current_position.y - actor.movement_range, - actor.current_position.y + actor.movement_range + 1): + for x in range(max(0, actor.current_position.x - actor.movement_range), + min(actor.enhanced_gridmap.columns, actor.current_position.x + actor.movement_range + 1)): + for z in range(max(0, actor.current_position.y - actor.movement_range), + min(actor.enhanced_gridmap.rows, actor.current_position.y + actor.movement_range + 1)): var pos = Vector2i(x, z) - if not actor.is_within_movement_range(pos): - continue - - if actor.is_position_occupied(pos): - continue - - var cell_item = actor.enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) - if cell_item == -1 or cell_item in actor.enhanced_gridmap.non_walkable_items: - continue - - var dist = pos.distance_squared_to(target) - if dist < min_distance: - min_distance = dist - best_pos = pos + if pos != actor.current_position and actor.is_within_movement_range(pos): + if not actor.is_position_occupied(pos): + valid_positions.append(pos) - return best_pos + if valid_positions.size() > 0: + return valid_positions[randi() % valid_positions.size()] + return Vector2i(-1, -1) diff --git a/scripts/behaviors/sequences/grab_sequence.gd b/scripts/behaviors/sequences/grab_sequence.gd deleted file mode 100644 index f2a1ef6..0000000 --- a/scripts/behaviors/sequences/grab_sequence.gd +++ /dev/null @@ -1,5 +0,0 @@ -class_name GrabSequence extends SequenceComposite - -func _ready(): - add_child(GrabCondition.new()) - add_child(GrabAction.new()) diff --git a/scripts/behaviors/sequences/move_sequence.gd b/scripts/behaviors/sequences/move_sequence.gd deleted file mode 100644 index 2a9490f..0000000 --- a/scripts/behaviors/sequences/move_sequence.gd +++ /dev/null @@ -1,5 +0,0 @@ -class_name MoveSequence extends SequenceComposite - -func _ready(): - add_child(MoveCondition.new()) - add_child(MoveAction.new()) diff --git a/scripts/behaviors/sequences/put_sequence.gd b/scripts/behaviors/sequences/put_sequence.gd deleted file mode 100644 index 465e41e..0000000 --- a/scripts/behaviors/sequences/put_sequence.gd +++ /dev/null @@ -1,5 +0,0 @@ -class_name PutSequence extends SequenceComposite - -func _ready(): - add_child(PutCondition.new()) - add_child(PutAction.new()) diff --git a/scripts/bot_behavior.gd b/scripts/bot_behavior.gd index 2a08f97..2f6e1e6 100644 --- a/scripts/bot_behavior.gd +++ b/scripts/bot_behavior.gd @@ -1,125 +1,24 @@ -@tool extends BeehaveTree - +# In bot_behavior.gd func _ready(): if Engine.is_editor_hint(): return + # Get parent node safely + var parent = get_parent() + if not parent: + push_error("BehaviorTree: No parent node found") + return + # Only setup for bots - if not get_parent().is_in_group("Bots"): + if not parent.is_in_group("Bots"): queue_free() # Remove tree if not a bot return # Set this tree's actor - actor = get_parent() + actor = parent + + # Wait a frame to ensure all nodes are ready + await get_tree().process_frame + enabled = true - - # Create root selector - var selector = SelectorComposite.new() - add_child(selector) - - # Add sequences - selector.add_child(create_arrange_sequence()) - selector.add_child(create_grab_sequence()) - selector.add_child(create_put_sequence()) - selector.add_child(create_move_sequence()) - -func create_arrange_sequence() -> SequenceComposite: - var sequence = SequenceComposite.new() - sequence.name = "ArrangeSequence" - - # Has enough action points? - var check_ap = ConditionLeaf.new() - check_ap.name = "HasEnoughAP" - check_ap.set_script(preload("res://scripts/behaviors/conditions/has_ap.gd")) - - # Can arrange items? - var can_arrange = ConditionLeaf.new() - can_arrange.name = "CanArrange" - can_arrange.set_script(preload("res://scripts/behaviors/conditions/can_arrange.gd")) - - # Do arrange action - var do_arrange = ActionLeaf.new() - do_arrange.name = "DoArrange" - do_arrange.set_script(preload("res://scripts/behaviors/actions/do_arrange.gd")) - - sequence.add_child(check_ap) - sequence.add_child(can_arrange) - sequence.add_child(do_arrange) - - return sequence - -func create_grab_sequence() -> SequenceComposite: - var sequence = SequenceComposite.new() - sequence.name = "GrabSequence" - - # Has enough action points? - var check_ap = ConditionLeaf.new() - check_ap.name = "HasAP" - check_ap.set_script(preload("res://scripts/behaviors/conditions/has_ap.gd")) - - # Can grab item? - var can_grab = ConditionLeaf.new() - can_grab.name = "CanGrab" - can_grab.set_script(preload("res://scripts/behaviors/conditions/can_grab.gd")) - - # Do grab action - var do_grab = ActionLeaf.new() - do_grab.name = "DoGrab" - do_grab.set_script(preload("res://scripts/behaviors/actions/do_grab.gd")) - - sequence.add_child(check_ap) - sequence.add_child(can_grab) - sequence.add_child(do_grab) - - return sequence - -func create_put_sequence() -> SequenceComposite: - var sequence = SequenceComposite.new() - sequence.name = "PutSequence" - - # Has enough action points? - var check_ap = ConditionLeaf.new() - check_ap.name = "HasAP" - check_ap.set_script(preload("res://scripts/behaviors/conditions/has_ap.gd")) - - # Can put item? - var can_put = ConditionLeaf.new() - can_put.name = "CanPut" - can_put.set_script(preload("res://scripts/behaviors/conditions/can_put.gd")) - - # Do put action - var do_put = ActionLeaf.new() - do_put.name = "DoPut" - do_put.set_script(preload("res://scripts/behaviors/actions/do_put.gd")) - - sequence.add_child(check_ap) - sequence.add_child(can_put) - sequence.add_child(do_put) - - return sequence - -func create_move_sequence() -> SequenceComposite: - var sequence = SequenceComposite.new() - sequence.name = "MoveSequence" - - # Has enough action points? - var check_ap = ConditionLeaf.new() - check_ap.name = "HasAP" - check_ap.set_script(preload("res://scripts/behaviors/conditions/has_ap.gd")) - - # Should move? - var should_move = ConditionLeaf.new() - should_move.name = "ShouldMove" - should_move.set_script(preload("res://scripts/behaviors/conditions/should_move.gd")) - - # Do move action - var do_move = ActionLeaf.new() - do_move.name = "DoMove" - do_move.set_script(preload("res://scripts/behaviors/actions/do_move.gd")) - - sequence.add_child(check_ap) - sequence.add_child(should_move) - sequence.add_child(do_move) - - return sequence diff --git a/scripts/bot_blackboard.gd b/scripts/bot_blackboard.gd new file mode 100644 index 0000000..df69e78 --- /dev/null +++ b/scripts/bot_blackboard.gd @@ -0,0 +1,27 @@ +@tool +extends Blackboard + +# Default values when initializing blackboard +var default_data = { + "move_target": null, # Vector2i for movement target + "can_arrange": false, # Whether bot can arrange items + "can_grab": false, # Whether bot can grab items + "can_put": false, # Whether bot can put items + "should_move": false, # Whether bot should move + "action_points": 0, # Current action points + "current_action": "", # Current action being performed + "item_to_grab": null, # Item to grab + "grab_position": null, # Position to grab from + "put_position": null, # Position to put item + "arrange_from": -1, # Slot to arrange from + "arrange_to": -1 # Slot to arrange to +} + +func _ready(): + if Engine.is_editor_hint(): + return + + # Initialize with default values + for key in default_data: + if not has_value(key): + set_value(key, default_data[key])