diff --git a/scenes/main.gd b/scenes/main.gd index 22097eb..d0c8c82 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -110,21 +110,25 @@ func setup_playerboard_ui(): slot.get_child(2).hide() func set_action_state(new_state): - if not local_player_character or not local_player_character.is_multiplayer_authority() or current_action_state == new_state or local_player_character.action_points <= 0: + if not local_player_character or not local_player_character.is_multiplayer_authority(): + return + + if local_player_character.is_bot or local_player_character.is_in_group("Bots"): + current_action_state = new_state + return + + if current_action_state == new_state or local_player_character.action_points <= 0: return current_action_state = new_state - if local_player_character.is_bot == false: - local_player_character.clear_highlights() - local_player_character.clear_playerboard_highlights() + local_player_character.clear_highlights() + local_player_character.clear_playerboard_highlights() match new_state: ActionState.MOVING: - if local_player_character.is_bot == false: - local_player_character.highlight_movement_range() + local_player_character.highlight_movement_range() ActionState.GRABBING: - if local_player_character.is_bot == false: - local_player_character.highlight_adjacent_cells() + local_player_character.highlight_adjacent_cells() if local_player_character.has_item_at_current_position(): local_player_character.highlighted_cells.append(local_player_character.current_position) local_player_character.enhanced_gridmap.set_cell_item( @@ -132,15 +136,12 @@ func set_action_state(new_state): local_player_character.enhanced_gridmap.hover_item ) ActionState.PUTTING: - if local_player_character.is_bot == false: - local_player_character.highlight_occupied_playerboard_slots() + local_player_character.highlight_occupied_playerboard_slots() ActionState.RANDOMIZING: - if local_player_character.is_bot == false: - local_player_character.highlight_random_valid_cells() + local_player_character.highlight_random_valid_cells() ActionState.ARRANGING: show_arrangement_ui() - if local_player_character.is_bot == false: - local_player_character.highlight_occupied_playerboard_slots() + local_player_character.highlight_occupied_playerboard_slots() func update_button_states(): if not local_player_character or local_player_character.is_in_group("Bots"): @@ -388,12 +389,18 @@ func set_current_turn(player_id): return for player in get_tree().get_nodes_in_group("Players"): - if player.name == str(player_id): - player.is_my_turn = true + var is_current_turn = player.name == str(player_id) + player.is_my_turn = is_current_turn + + # Only reset state for human players + if is_current_turn and not (player.is_bot or player.is_in_group("Bots")): player.action_points = 2 player.has_moved_this_turn = false player.has_performed_action = false player.start_turn() + # Clear any existing highlights from other players + player.clear_highlights() + player.clear_playerboard_highlights() else: player.is_my_turn = false diff --git a/scenes/player.gd b/scenes/player.gd index bf4b947..ca6c3d6 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -59,9 +59,11 @@ func _ready(): # Early setup for bots if is_bot == true or is_in_group("Bots"): - # Set Input process to false for bots immediately + # Disable all input processing for bots immediately set_process_input(false) set_process_unhandled_input(false) + set_process(false) + set_physics_process(false) # Disable visual highlights for bots highlighted_cells.clear() @@ -119,9 +121,9 @@ func _physics_process(_delta): rpc("remote_set_position", global_position) func _unhandled_input(event): - if is_bot == true or is_in_group("Bots"): + # Early return if not authorized human player + if not is_multiplayer_authority() or is_bot or is_in_group("Bots"): set_process_unhandled_input(false) - set_process_input(false) return # Use get_node_or_null for safer node access @@ -283,13 +285,14 @@ func move_player_to_clicked_position(grid_position: Vector2i): break if valid_path: - path.pop_front() - rpc("start_movement_along_path", path) + # Pass clear_visual=false for bots to preserve player highlights + rpc("start_movement_along_path", path, not (is_bot or is_in_group("Bots"))) action_points -= 1 - if not is_bot == true: - clear_highlights() + # Only clear highlights if not a bot + if not (is_bot or is_in_group("Bots")): + clear_highlights() else: print("Path is blocked by other players") @@ -307,15 +310,24 @@ func start_movement_along_path(path: Array, clear_visual: bool = true): current_position = Vector2i(path[-1].x, path[-1].y) is_player_moving = false - if clear_visual == true: - enhanced_gridmap.clear_path_visualization() + var main = get_tree().get_root().get_node_or_null("Main") + + # Only clear visuals if this is a human player + if not (is_bot or is_in_group("Bots")): + if clear_visual: + enhanced_gridmap.clear_path_visualization() + + # Restore movement range highlights if it was the player's turn + if main and main.current_action_state == main.ActionState.MOVING and is_my_turn: + highlight_movement_range() has_moved_this_turn = path.size() <= movement_range - var main = get_tree().get_root().get_node_or_null("Main") - main.set_action_state(main.ActionState.NONE) + if main: + if not (is_bot or is_in_group("Bots")): + main.set_action_state(main.ActionState.NONE) - if main.turn_based_mode: + if main and main.turn_based_mode: end_turn() _after_action_completed() ) @@ -386,7 +398,48 @@ func append_random_goals(): if is_multiplayer_authority(): rpc("sync_goals", goals) +func bot_try_grab_item() -> bool: + if not enhanced_gridmap or action_points <= 0: + return false + + # First check current position + var current_cell = Vector3i(current_position.x, 1, current_position.y) + var item = enhanced_gridmap.get_cell_item(current_cell) + + if item != -1: + var empty_slot = playerboard.find(-1) + if empty_slot != -1: + if is_multiplayer_authority(): + playerboard[empty_slot] = item + rpc("sync_grid_item", current_cell.x, current_cell.y, current_cell.z, -1) + rpc("sync_playerboard", playerboard) + has_performed_action = true + action_points -= 1 + return true + + # Check adjacent cells if nothing at current position + var neighbors = enhanced_gridmap.get_neighbors(current_position, 0) + for neighbor in neighbors: + if neighbor.is_walkable: + var cell = Vector3i(neighbor.position.x, 1, neighbor.position.y) + item = enhanced_gridmap.get_cell_item(cell) + if item != -1: + var empty_slot = playerboard.find(-1) + if empty_slot != -1: + if is_multiplayer_authority(): + playerboard[empty_slot] = item + rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) + rpc("sync_playerboard", playerboard) + has_performed_action = true + action_points -= 1 + return true + + return false + func grab_item(grid_position: Vector2i = current_position) -> bool: + if is_bot: + return bot_try_grab_item() + if not enhanced_gridmap or action_points <= 0: return false @@ -406,36 +459,30 @@ func grab_item(grid_position: Vector2i = current_position) -> bool: if item == -1: return false - if is_in_group("Bots") or is_bot == true: + # Bot-specific grab logic moved to bot_grab_item RPC + if is_in_group("Bots") or is_bot: var empty_slot = playerboard.find(-1) if empty_slot == -1: return false - - if is_multiplayer_authority(): - playerboard[empty_slot] = item - rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) - rpc("sync_playerboard", playerboard) - - has_performed_action = true - consume_action_points(1) - clear_playerboard_highlights() - _after_action_completed() - return true - else: - var main = get_tree().get_root().get_node_or_null("Main") - if main: - selected_gridmap_position = grid_position - clear_highlights() - clear_playerboard_highlights() - for i in range(playerboard.size()): - if playerboard[i] == -1: - var slot = main.playerboard_ui.get_child(i) - if slot.get_child_count() > 0: - slot.get_child(0).show() - highlighted_cells.append(i) - return true + if is_multiplayer_authority(): + rpc("bot_grab_item", grid_position, empty_slot, cell.x, cell.y, cell.z) + return true + var main = get_tree().get_root().get_node_or_null("Main") + if main: + selected_gridmap_position = grid_position + clear_highlights() + clear_playerboard_highlights() + + for i in range(playerboard.size()): + if playerboard[i] == -1: + var slot = main.playerboard_ui.get_child(i) + if slot.get_child_count() > 0: + slot.get_child(0).show() + highlighted_cells.append(i) + return true + return false func put_item(grid_position: Vector2i = current_position) -> bool: @@ -637,36 +684,54 @@ func has_items_in_playerboard() -> bool: func playerboard_is_full() -> bool: return playerboard.find(-1) == -1 -func highlight_movement_range(): - if is_bot == true or is_in_group("Bots") or not is_multiplayer_authority(): +func highlight_cells_if_authorized(cells_to_highlight: Array): + if not is_multiplayer_authority() or is_bot or is_in_group("Bots"): return + + clear_highlights() + for cell in cells_to_highlight: + highlighted_cells.append(cell) + enhanced_gridmap.set_cell_item( + Vector3i(cell.x, 0, cell.y), + enhanced_gridmap.hover_item + ) +# Modify highlight functions to use the new authorized highlight method +func highlight_movement_range(): + if not is_multiplayer_authority() or is_bot or is_in_group("Bots"): + return + + var cells_to_highlight = [] for x in range(enhanced_gridmap.columns): for z in range(enhanced_gridmap.rows): var test_pos = Vector2i(x, z) if is_within_movement_range(test_pos): var cell_item = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) if cell_item != -1 and not (cell_item in enhanced_gridmap.non_walkable_items) and not is_position_occupied(test_pos): - highlighted_cells.append(test_pos) - enhanced_gridmap.set_cell_item(Vector3i(x, 0, z), enhanced_gridmap.hover_item) + cells_to_highlight.append(test_pos) + + highlight_cells_if_authorized(cells_to_highlight) func highlight_adjacent_cells(): - if is_bot == true or is_in_group("Bots") or not is_multiplayer_authority(): + if not is_multiplayer_authority() or is_bot or is_in_group("Bots"): return - + + var cells_to_highlight = [] + + # Add current position if item exists var current_cell = Vector3i(current_position.x, 1, current_position.y) if enhanced_gridmap.get_cell_item(current_cell) != -1: - highlighted_cells.append(current_position) - enhanced_gridmap.set_cell_item(Vector3i(current_position.x, 0, current_position.y), enhanced_gridmap.hover_item) + cells_to_highlight.append(current_position) + # Add valid neighbors var neighbors = enhanced_gridmap.get_neighbors(current_position, 0) for neighbor in neighbors: if neighbor.is_walkable: var cell_pos = neighbor.position if enhanced_gridmap.get_cell_item(Vector3i(cell_pos.x, 1, cell_pos.y)) != -1: - highlighted_cells.append(cell_pos) - enhanced_gridmap.set_cell_item(Vector3i(cell_pos.x, 0, cell_pos.y), enhanced_gridmap.hover_item) - + cells_to_highlight.append(cell_pos) + + highlight_cells_if_authorized(cells_to_highlight) func highlight_empty_adjacent_cells(): if is_bot == true or is_in_group("Bots") or not is_multiplayer_authority(): @@ -740,27 +805,41 @@ func highlight_occupied_playerboard_slots(): main.update_playerboard_ui() func clear_highlights(): - if is_bot == true or is_in_group("Bots") or not is_multiplayer_authority(): + # Never allow bots to clear highlights for human players + if is_bot or is_in_group("Bots"): return - if not enhanced_gridmap: + + if not enhanced_gridmap or not is_multiplayer_authority(): return + # Store the current action state before clearing + var main = get_tree().get_root().get_node_or_null("Main") + var current_state = main.current_action_state if main else null + for cell in highlighted_cells: if cell is Vector2i: enhanced_gridmap.set_cell_item(Vector3i(cell.x, 0, cell.y), enhanced_gridmap.normal_items[0]) highlighted_cells.clear() - 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) for child in slot.get_children(): child.hide() + # Restore highlights based on current action state + if main and current_state == main.ActionState.MOVING and is_my_turn: + highlight_movement_range() + func clear_playerboard_highlights(): - if is_bot == true or is_in_group("Bots") or not is_multiplayer_authority(): + # Never allow bots to clear highlights for human players + if is_bot or is_in_group("Bots"): return + + if not is_multiplayer_authority(): + return + 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()): @@ -920,3 +999,46 @@ func consume_action_points(points: int): rpc("display_message", "Action Points Reset!") _after_action_completed() + +@rpc("any_peer", "call_local") +func bot_grab_item(pos: Vector2i, slot: int, x: int, y: int, z: int): + if not (is_bot or is_in_group("Bots")): + return + + var cell = Vector3i(x, y, z) + var item = enhanced_gridmap.get_cell_item(cell) + + if item != -1: + playerboard[slot] = item + enhanced_gridmap.set_cell_item(cell, -1) + has_performed_action = true + action_points -= 1 + _after_action_completed() + +@rpc("any_peer", "call_local") +func bot_put_item(pos: Vector2i, slot: int, x: int, y: int, z: int): + if not (is_bot or is_in_group("Bots")): + return + + var cell = Vector3i(x, y, z) + var item = playerboard[slot] + + if enhanced_gridmap.get_cell_item(cell) == -1: + enhanced_gridmap.set_cell_item(cell, item) + playerboard[slot] = -1 + has_performed_action = true + action_points -= 1 + _after_action_completed() + +@rpc("any_peer", "call_local") +func bot_arrange_item(from_slot: int, to_slot: int): + if not (is_bot or is_in_group("Bots")) or action_points < 2: + return + + if playerboard[from_slot] != -1 and playerboard[to_slot] == -1: + var temp = playerboard[from_slot] + playerboard[from_slot] = -1 + playerboard[to_slot] = temp + has_performed_action = true + action_points -= 2 + _after_action_completed() diff --git a/scripts/behaviors/actions/do_grab.gd b/scripts/behaviors/actions/do_grab.gd index 64f32a8..f464b80 100644 --- a/scripts/behaviors/actions/do_grab.gd +++ b/scripts/behaviors/actions/do_grab.gd @@ -1,27 +1,32 @@ extends ActionLeaf func tick(actor: Node, blackboard: Blackboard) -> int: - var grab_position = blackboard.get_value("grab_position") - if not grab_position: - return FAILURE - - # Find empty slot in playerboard - var empty_slot = actor.playerboard.find(-1) - if empty_slot == -1: + # Early validation for bots only + if not actor.is_bot or actor.action_points <= 0: return FAILURE - # Get item at position + var grab_position = blackboard.get_value("grab_position") + if not grab_position: + grab_position = actor.current_position # Default to current position if no target + + # Check if there's an item at the position first var cell = Vector3i(grab_position.x, 1, grab_position.y) var item = actor.enhanced_gridmap.get_cell_item(cell) - if item == -1 or actor.action_points <= 0: + if item == -1: + return FAILURE + + # Find empty slot in playerboard + var empty_slot_idx = actor.playerboard.find(-1) + if empty_slot_idx == -1: return FAILURE - # Grab the item + # For bots, we bypass the usual visual feedback system if actor.is_multiplayer_authority(): - actor.playerboard[empty_slot] = item + actor.playerboard[empty_slot_idx] = item actor.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) actor.rpc("sync_playerboard", actor.playerboard) actor.has_performed_action = true actor.action_points -= 1 + blackboard.set_value("current_action", "idle") return SUCCESS diff --git a/scripts/bot_behavior.gd b/scripts/bot_behavior.gd index 813635b..991b40f 100644 --- a/scripts/bot_behavior.gd +++ b/scripts/bot_behavior.gd @@ -18,7 +18,11 @@ func _ready(): # Set this tree's actor actor = parent - # Wait a frame to ensure all nodes are ready - await get_tree().process_frame - enabled = parent.is_multiplayer_authority() and parent.is_bot + + # Set up blackboard with initial values + if blackboard: + blackboard.set_value("action_points", parent.action_points) + blackboard.set_value("current_action", "idle") + blackboard.set_value("grab_position", parent.current_position) # Default grab position + blackboard.set_value("move_target", null)