update bot experimental

This commit is contained in:
2025-01-28 16:34:46 +08:00
parent 5a9092adfc
commit f3d720d91d
16 changed files with 272 additions and 375 deletions
+14 -32
View File
@@ -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
-11
View File
@@ -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
-5
View File
@@ -1,5 +0,0 @@
extends SequenceComposite
func _ready():
add_child(ArrangeCondition.new())
add_child(ArrangeAction.new())
@@ -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
@@ -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
@@ -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
+12
View File
@@ -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
+20 -78
View File
@@ -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)
@@ -1,5 +0,0 @@
class_name GrabSequence extends SequenceComposite
func _ready():
add_child(GrabCondition.new())
add_child(GrabAction.new())
@@ -1,5 +0,0 @@
class_name MoveSequence extends SequenceComposite
func _ready():
add_child(MoveCondition.new())
add_child(MoveAction.new())
@@ -1,5 +0,0 @@
class_name PutSequence extends SequenceComposite
func _ready():
add_child(PutCondition.new())
add_child(PutAction.new())
+13 -114
View File
@@ -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
+27
View File
@@ -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])