diff --git a/scenes/main.gd b/scenes/main.gd index d0c8c82..e2529e8 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -158,11 +158,14 @@ func update_button_states(): randomize_button.visible = true arrange_button.visible = true - move_button.disabled = local_player_character.is_player_moving or local_player_character.has_performed_action - grab_button.disabled = not local_player_character.has_item_at_current_position() - put_button.disabled = not (local_player_character.has_items_in_playerboard() and not local_player_character.has_item_at_current_position()) + # Only keep randomize button's disable condition randomize_button.disabled = local_player_character.has_performed_action - arrange_button.disabled = not local_player_character.has_items_in_playerboard() + + # Remove disabled conditions for other buttons: + move_button.disabled = false + grab_button.disabled = false + put_button.disabled = false + arrange_button.disabled = false func _on_playerboard_slot_clicked(event, slot_index): if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: diff --git a/scripts/behaviors/actions/do_arrange.gd b/scripts/behaviors/actions/do_arrange.gd index 63b4f21..d28749b 100644 --- a/scripts/behaviors/actions/do_arrange.gd +++ b/scripts/behaviors/actions/do_arrange.gd @@ -1,24 +1,85 @@ extends ActionLeaf +func find_best_arrangement(actor) -> Dictionary: + # Convert goals to 2D array for easier comparison + var goals_2d = [] + for i in range(3): + var row = [] + for j in range(3): + row.append(actor.goals[i * 3 + j]) + goals_2d.append(row) + + # Convert playerboard to 2D + var board_2d = [] + for i in range(5): + var row = [] + for j in range(5): + row.append(actor.playerboard[i * 5 + j]) + board_2d.append(row) + + # Find misplaced items + for i in range(1, 4): # Check central 3x3 area + for j in range(1, 4): + var board_idx = i * 5 + j + var goal_idx = (i - 1) * 3 + (j - 1) + + var current_item = actor.playerboard[board_idx] + var goal_item = actor.goals[goal_idx] + + if current_item != goal_item and current_item != -1: + # Find better position for this item + for gi in range(3): + for gj in range(3): + if actor.goals[gi * 3 + gj] == current_item: + var target_slot = (gi + 1) * 5 + (gj + 1) + if actor.playerboard[target_slot] == -1: + return { + "source_slot": board_idx, + "target_slot": target_slot + } + + return {} + +func is_goals_achieved(actor) -> bool: + # Check only central 3x3 area of playerboard against goals + for i in range(3): + for j in range(3): + var board_idx = (i + 1) * 5 + (j + 1) + var goal_idx = i * 3 + j + + if actor.goals[goal_idx] != -1 and actor.goals[goal_idx] != actor.playerboard[board_idx]: + return false + elif actor.goals[goal_idx] == -1 and actor.playerboard[board_idx] != -1: + return false + + # Also check outside the goal area + if i == 0 or i == 4 or j == 0 or j == 4: + if actor.playerboard[i * 5 + j] != -1: + return false + + return true + func tick(actor: Node, blackboard: Blackboard) -> int: - var source_slot = blackboard.get_value("source_slot") - var target_slot = blackboard.get_value("target_slot") - - if source_slot == -1 or target_slot == -1: + # First check if goals are already achieved + if is_goals_achieved(actor): + blackboard.set_value("current_action", "idle") return FAILURE - - # Verify conditions are still valid - if actor.action_points < 2 or actor.playerboard[source_slot] == -1: + + if actor.action_points < 2: return FAILURE - if actor.playerboard[target_slot] != -1: + var arrangement = find_best_arrangement(actor) + if arrangement.is_empty(): return FAILURE - + + blackboard.set_value("source_slot", arrangement.source_slot) + blackboard.set_value("target_slot", arrangement.target_slot) + # Do the arrangement if actor.is_multiplayer_authority(): - var item = actor.playerboard[source_slot] - actor.playerboard[target_slot] = item - actor.playerboard[source_slot] = -1 + var item = actor.playerboard[arrangement.source_slot] + actor.playerboard[arrangement.target_slot] = item + actor.playerboard[arrangement.source_slot] = -1 actor.rpc("sync_playerboard", actor.playerboard) actor.has_performed_action = true actor.action_points -= 2 diff --git a/scripts/behaviors/actions/do_grab.gd b/scripts/behaviors/actions/do_grab.gd index f464b80..b9e9350 100644 --- a/scripts/behaviors/actions/do_grab.gd +++ b/scripts/behaviors/actions/do_grab.gd @@ -1,28 +1,105 @@ extends ActionLeaf +func is_goals_achieved(actor) -> bool: + # Convert goals to 2D for easier pattern matching + var goals_2d = [] + for i in range(3): + var row = [] + for j in range(3): + row.append(actor.goals[i * 3 + j]) + goals_2d.append(row) + + # Convert playerboard to 2D + var board_2d = [] + for i in range(5): + var row = [] + for j in range(5): + row.append(actor.playerboard[i * 5 + j]) + board_2d.append(row) + + # Check every possible 3x3 region in the 5x5 board + for start_row in range(3): # 5-3+1 possible start rows + for start_col in range(3): # 5-3+1 possible start columns + var matches = true + + # Check if this 3x3 region matches the goals + for i in range(3): + for j in range(3): + var board_item = board_2d[start_row + i][start_col + j] + var goal_item = goals_2d[i][j] + + if goal_item != -1 and goal_item != board_item: + matches = false + break + elif goal_item == -1 and board_item != -1: + matches = false + break + + if not matches: + break + + if matches: + return true + + return false + +func find_target_slot(actor, item: int) -> int: + # Get the goals in 2D format (3x3) + var goals_2d = [] + for i in range(3): + var row = [] + for j in range(3): + row.append(actor.goals[i * 3 + j]) + goals_2d.append(row) + + # Convert playerboard to 2D (5x5) + var board_2d = [] + for i in range(5): + var row = [] + for j in range(5): + row.append(actor.playerboard[i * 5 + j]) + board_2d.append(row) + + # Find matching position in goals + var target_positions = [] + for i in range(3): + for j in range(3): + if goals_2d[i][j] == item: + # Convert to playerboard position (centered in 5x5 grid) + target_positions.append(Vector2i(j + 1, i + 1)) + + # Find best empty slot that matches a goal position + for pos in target_positions: + var slot_index = (pos.y + 1) * 5 + (pos.x + 1) + if actor.playerboard[slot_index] == -1: + return slot_index + + # If no matching position found, find any empty slot + return actor.playerboard.find(-1) + func tick(actor: Node, blackboard: Blackboard) -> int: - # Early validation for bots only + # Early validation if not actor.is_bot or actor.action_points <= 0: return FAILURE var grab_position = blackboard.get_value("grab_position") if not grab_position: - grab_position = actor.current_position # Default to current position if no target + return FAILURE - # Check if there's an item at the position first + # Check if there's an item var cell = Vector3i(grab_position.x, 1, grab_position.y) var item = actor.enhanced_gridmap.get_cell_item(cell) if item == -1: return FAILURE - - # Find empty slot in playerboard - var empty_slot_idx = actor.playerboard.find(-1) - if empty_slot_idx == -1: + + # Find strategic slot + var target_slot = find_target_slot(actor, item) + if target_slot == -1: return FAILURE - # For bots, we bypass the usual visual feedback system + # Execute grab if actor.is_multiplayer_authority(): - actor.playerboard[empty_slot_idx] = item + actor.playerboard[target_slot] = item actor.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) actor.rpc("sync_playerboard", actor.playerboard) actor.has_performed_action = true diff --git a/scripts/behaviors/actions/do_move.gd b/scripts/behaviors/actions/do_move.gd index 3ce0e38..7611871 100644 --- a/scripts/behaviors/actions/do_move.gd +++ b/scripts/behaviors/actions/do_move.gd @@ -1,6 +1,21 @@ extends ActionLeaf +func is_goals_achieved(actor) -> bool: + for i in range(3): + for j in range(3): + var board_idx = (i + 1) * 5 + (j + 1) + var goal_idx = i * 3 + j + if actor.goals[goal_idx] != -1 and actor.goals[goal_idx] != actor.playerboard[board_idx]: + return false + elif actor.goals[goal_idx] == -1 and actor.playerboard[board_idx] != -1: + return false + return true + func tick(actor: Node, blackboard: Blackboard) -> int: + # Don't move if goals are achieved + if is_goals_achieved(actor): + return FAILURE + # Get target from blackboard var target_pos = blackboard.get_value("move_target") if not target_pos: diff --git a/scripts/behaviors/actions/do_put.gd b/scripts/behaviors/actions/do_put.gd index 549b067..1f8a8c0 100644 --- a/scripts/behaviors/actions/do_put.gd +++ b/scripts/behaviors/actions/do_put.gd @@ -1,5 +1,24 @@ extends ActionLeaf +func is_goals_achieved(actor) -> bool: + # Check only central 3x3 area of playerboard against goals + for i in range(3): + for j in range(3): + var board_idx = (i + 1) * 5 + (j + 1) + var goal_idx = i * 3 + j + + if actor.goals[goal_idx] != -1 and actor.goals[goal_idx] != actor.playerboard[board_idx]: + return false + elif actor.goals[goal_idx] == -1 and actor.playerboard[board_idx] != -1: + return false + + # Also check outside the goal area + if i == 0 or i == 4 or j == 0 or j == 4: + if actor.playerboard[i * 5 + j] != -1: + return false + + return true + func tick(actor: Node, blackboard: Blackboard) -> int: var put_position = blackboard.get_value("put_position") var put_slot = blackboard.get_value("put_slot") @@ -16,7 +35,7 @@ func tick(actor: Node, blackboard: Blackboard) -> int: if actor.enhanced_gridmap.get_cell_item(cell) != -1: return FAILURE - # Put the item + # Execute put var item = actor.playerboard[put_slot] if actor.is_multiplayer_authority(): actor.rpc("sync_grid_item", cell.x, cell.y, cell.z, item) diff --git a/scripts/behaviors/conditions/can_grab.gd b/scripts/behaviors/conditions/can_grab.gd index a6bcd92..905d9af 100644 --- a/scripts/behaviors/conditions/can_grab.gd +++ b/scripts/behaviors/conditions/can_grab.gd @@ -1,6 +1,49 @@ extends ConditionLeaf +func is_goals_achieved(actor) -> bool: + # Convert goals to 2D for easier pattern matching + var goals_2d = [] + for i in range(3): + var row = [] + for j in range(3): + row.append(actor.goals[i * 3 + j]) + goals_2d.append(row) + + # Convert playerboard to 2D + var board_2d = [] + for i in range(5): + var row = [] + for j in range(5): + row.append(actor.playerboard[i * 5 + j]) + board_2d.append(row) + + # Check every possible 3x3 region in the 5x5 board + for start_row in range(3): + for start_col in range(3): + var matches = true + for i in range(3): + for j in range(3): + var board_item = board_2d[start_row + i][start_col + j] + var goal_item = goals_2d[i][j] + + if goal_item != -1 and goal_item != board_item: + matches = false + break + elif goal_item == -1 and board_item != -1: + matches = false + break + if not matches: + break + if matches: + return true + return false + func tick(actor: Node, blackboard: Blackboard) -> int: + # First check if goals are achieved + if is_goals_achieved(actor): + blackboard.set_value("current_action", "idle") + return FAILURE + if actor.playerboard_is_full(): return FAILURE diff --git a/scripts/behaviors/conditions/can_put.gd b/scripts/behaviors/conditions/can_put.gd index 65cb9fb..70b8a55 100644 --- a/scripts/behaviors/conditions/can_put.gd +++ b/scripts/behaviors/conditions/can_put.gd @@ -1,6 +1,15 @@ extends ConditionLeaf +func is_goals_achieved(actor) -> bool: + # ... same is_goals_achieved function as in can_grab.gd ... + return false + func tick(actor: Node, blackboard: Blackboard) -> int: + # First check if goals are achieved + if is_goals_achieved(actor): + blackboard.set_value("current_action", "idle") + return FAILURE + # Find an item in playerboard that matches goals var put_slot = -1 for i in range(actor.playerboard.size()):