Started implementing behaviors

pull/6/head
Yan Wittmann 2025-01-09 13:47:40 +01:00
parent b3e33b7cfa
commit bfca1ff23f
17 changed files with 204 additions and 59 deletions

View File

@ -1 +1 @@
{"width":320,"height":320,"layers":[{"name":"ground","cels":[{"image":"tilemaps\\tilemap_ground.png","frame":0}]},{"name":"objects","cels":[{"image":"tilemaps\\tilemap_objects.png","frame":0}]},{"name":"temperature","cels":[{"image":"tilemaps\\tilemap_temperature.png","frame":0}]},{"name":"player","cels":[{"image":"tilemaps\\tilemap_player.png","frame":0}]}],"frames":[{"duration":0.1}],"filename":"tilemaps.aseprite"}
{"layers":[{"cels":[{"frame":0,"image":"tilemaps\\tilemap_ground.png"}],"name":"ground"},{"cels":[{"frame":0,"image":"tilemaps\\tilemap_objects.png"}],"name":"objects"},{"cels":[{"frame":0,"image":"tilemaps\\tilemap_temperature.png"}],"name":"temperature"},{"cels":[{"frame":0,"image":"tilemaps\\tilemap_player.png"}],"name":"player"}],"height":320,"filename":"tilemaps.aseprite","frames":[{"duration":0.1}],"width":320}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 B

After

Width:  |  Height:  |  Size: 201 B

View File

@ -78,6 +78,7 @@ texture = ExtResource("3_xap0v")
2:1/0 = 0
1:1/0 = 0
0:1/0 = 0
3:1/0 = 0
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_i41cv"]
texture = ExtResource("4_f38wc")

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=10 format=4 uid="uid://b88asko1ugyd2"]
[gd_scene load_steps=13 format=4 uid="uid://b88asko1ugyd2"]
[ext_resource type="Script" path="res://scripts/global/GameManager.gd" id="1_eeg2d"]
[ext_resource type="Script" path="res://scripts/tilemap/World.gd" id="1_k0rw8"]
@ -9,6 +9,9 @@
[ext_resource type="Script" path="res://scripts/player/tree/BehaviorTree.gd" id="6_efs30"]
[ext_resource type="Script" path="res://scripts/player/tree/impl/base/TaskSelector.gd" id="7_1jajd"]
[ext_resource type="Script" path="res://scripts/player/tree/impl/testing/WalkUpToMouse.gd" id="8_s6mqc"]
[ext_resource type="Script" path="res://scripts/player/tree/impl/base/TaskSequence.gd" id="9_i67mw"]
[ext_resource type="Script" path="res://scripts/player/tree/impl/game/critical_survival/TaskCheckFoodCritical.gd" id="10_sf2pi"]
[ext_resource type="Script" path="res://scripts/player/tree/impl/base/GoToSuccess.gd" id="11_84a4v"]
[node name="Island-scene" type="Node2D"]
script = ExtResource("1_eeg2d")
@ -71,6 +74,18 @@ script = ExtResource("6_efs30")
[node name="sl_Root" type="Node" parent="PlayerManager/BehaviorTree"]
script = ExtResource("7_1jajd")
[node name="sl_CriticalSurvival" type="Node" parent="PlayerManager/BehaviorTree/sl_Root"]
script = ExtResource("7_1jajd")
[node name="sq_Food" type="Node" parent="PlayerManager/BehaviorTree/sl_Root/sl_CriticalSurvival"]
script = ExtResource("9_i67mw")
[node name="TaskCheckFoodCritical" type="Node" parent="PlayerManager/BehaviorTree/sl_Root/sl_CriticalSurvival/sq_Food"]
script = ExtResource("10_sf2pi")
[node name="GoToSuccess" type="Node" parent="PlayerManager/BehaviorTree/sl_Root/sl_CriticalSurvival/sq_Food"]
script = ExtResource("11_84a4v")
[node name="WalkUpToMouse" type="Node" parent="PlayerManager/BehaviorTree/sl_Root"]
script = ExtResource("8_s6mqc")

View File

@ -23,7 +23,9 @@ func _process(delta: float) -> void:
if Input.is_action_just_pressed("key_2"):
camera.go_to_zooming(Vector2(789.883972167969, 450.102813720703), 0.56015348434448)
if Input.is_action_just_pressed("key_9"):
world.camp_manager.campfire_light()
world.camp_manager.sleep_effect()
world.camp_manager.campfire_extinguish()
if Input.is_action_just_pressed("force_game_tick"):
player.game_tick()

View File

@ -29,4 +29,4 @@ func game_tick() -> void:
print("game_tick:")
populate_blackboard()
behavior_tree.internal_run(blackboard)
print(" ==> [active state=", blackboard["current_task"], "] [status=", behavior_tree.status, "]")
print(" ==> [active state=", blackboard["current_task"], "] [status=", behavior_tree.status, "] [blackboard=", blackboard, "]")

View File

@ -2,56 +2,82 @@ class_name Task
extends Node
enum {FAILURE = -1, SUCCESS = 1, RUNNING = 0}
var status: int = FAILURE
var status: int = FAILURE
var status_reason: String = ""
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
func _ready() -> void:
for c in get_children():
if not c is Task:
push_error("Child is not a task: " + c.name + " in " + name)
return
for c in get_children():
if not c is Task:
push_error("Child is not a task: " + c.name + " in " + name)
return
func internal_run(p_blackboard: Dictionary) -> void:
p_blackboard["current_task"] = self
if status == RUNNING:
var running_child: Task = find_running_child()
if running_child != null:
running_child.internal_run(p_blackboard)
status = running_child.status
return
else:
run(p_blackboard)
else:
run(p_blackboard)
func internal_run(blackboard: Dictionary) -> void:
blackboard["current_task"] = self
if status == RUNNING:
var running_child: Task = find_running_child()
if running_child != null:
print(" -> ", human_readable(running_child.name))
running_child.internal_run(blackboard)
print(" <- ", human_readable(running_child.name))
status = running_child.status
return
else:
print(" -> ", human_readable())
run(blackboard)
print(" <- ", human_readable())
else:
print(" -> ", human_readable())
run(blackboard)
print(" <- ", human_readable())
func find_running_child() -> Task:
for c in get_children():
if c.status == RUNNING:
return c
return null
for c in get_children():
if c.status == RUNNING:
return c
return null
func run_child(p_blackboard: Dictionary, p_child: Task) -> void:
p_child.internal_run(p_blackboard)
if p_child.status != RUNNING:
status = RUNNING
func run_child(blackboard: Dictionary, p_child: Task) -> void:
p_child.internal_run(blackboard)
if p_child.status != RUNNING:
status = RUNNING
func run(p_blackboard: Dictionary) -> void:
pass
func run(blackboard: Dictionary) -> void:
pass
func cancel(p_blackboard: Dictionary):
pass
func cancel(blackboard: Dictionary):
pass
func get_first_child() -> Task:
if get_child_count() == 0:
push_error("Task does not have any children: " + name)
return null
return get_children()[0] as Task
if get_child_count() == 0:
push_error("Task does not have any children: " + name)
return null
return get_children()[0] as Task
func human_readable(addon: String = "") -> String:
var clear_status: String = "UNKNOWN"
if status == FAILURE:
clear_status = "FAILURE"
elif status == SUCCESS:
clear_status = "SUCCESS"
elif status == RUNNING:
clear_status = "RUNNING"
var ret: String = name;
if addon != "":
ret += " " + addon
if status_reason != "":
ret += " [" + clear_status + ", " + status_reason + "]"
else:
ret += " [" + clear_status + "]"
return ret

View File

@ -0,0 +1,26 @@
class_name GoToRunning
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
if not blackboard.has("path"):
status = FAILURE
status_reason = "blackboard did not have path"
return
var path: Array[Vector2i] = blackboard["path"]
if path.size() == 0:
status = FAILURE
status_reason = "path was empty"
return
if navigation.has_arrived(player.board_position, path):
status = SUCCESS
status_reason = "already arrived at destination"
return
player.walk_along(path)
status = RUNNING
status_reason = "walked along path, now at " + str(player.board_position)

View File

@ -0,0 +1,26 @@
class_name GoToSuccess
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
if not blackboard.has("path"):
status = FAILURE
status_reason = "blackboard did not have path"
return
var path: Array[Vector2i] = blackboard["path"]
if path.size() == 0:
status = FAILURE
status_reason = "path was empty"
return
if navigation.has_arrived(player.board_position, path):
status = SUCCESS
status_reason = "already arrived at destination"
return
player.walk_along(path)
status = SUCCESS
status_reason = "walked along path, now at " + str(player.board_position)

View File

@ -2,10 +2,9 @@ class_name TaskSelector
extends Task
func run(blackboard: Dictionary) -> void:
for c in self.get_children():
run_child(blackboard, c)
if c.status != FAILURE:
status = c.status
return
status = FAILURE
for c in self.get_children():
run_child(blackboard, c)
if c.status != FAILURE:
status = c.status
return
status = FAILURE

View File

@ -1,6 +1,30 @@
class_name TaskCheckFoodCritical
extends Task
func run(p_blackboard: Dictionary) -> void:
var world: World = p_blackboard["world"]
var player: PlayerManager = p_blackboard["player"]
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
var active_foods: Array[Vector2i] = world.tilemap_interactive.get_cells_by_type_collection(
tilemap_types.OBJECT_COLLECTION_BERRY_SOURCE, player.board_position, player.view_distance)
if len(active_foods) == 0:
status = FAILURE
status_reason = "No active foods found"
return
var closest_berry: Vector2i = navigation.manhattan_distance_closest(active_foods, player.board_position)
if closest_berry == tilemap_types.NO_TILE_FOUND:
status = FAILURE
status_reason = "No closest berry found"
return
var path: Array[Vector2i] = navigation.find_path_allow_neighbors(player.board_position, closest_berry, player.view_distance)
if path.size() > 0:
blackboard["path"] = path
status_reason = "Found path to closest berry"
status = SUCCESS
return
status = FAILURE
status_reason = "No path found to closest berry " + str(closest_berry)

View File

@ -1,10 +1,10 @@
class_name WalkToMouse
extends Task
func run(p_blackboard: Dictionary) -> void:
var world: World = p_blackboard["world"]
var player: PlayerManager = p_blackboard["player"]
var tilemap_navigation: TilemapNavigation = p_blackboard["navigation"]
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var tilemap_navigation: TilemapNavigation = blackboard["navigation"]
world.tilemap_temperature.clear_cells()
var path: Array[Vector2i] = tilemap_navigation.find_path(player.board_position, world.tilemap_mouse_position(), player.view_distance)

View File

@ -1,10 +1,10 @@
class_name WalkUpToMouse
extends Task
func run(p_blackboard: Dictionary) -> void:
var world: World = p_blackboard["world"]
var player: PlayerManager = p_blackboard["player"]
var tilemap_navigation: TilemapNavigation = p_blackboard["navigation"]
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var tilemap_navigation: TilemapNavigation = blackboard["navigation"]
world.tilemap_temperature.clear_cells()
var path: Array[Vector2i] = tilemap_navigation.find_path_allow_neighbors(player.board_position, world.tilemap_mouse_position(), player.view_distance)

View File

@ -13,10 +13,19 @@ func get_cells_by_type(atlas_coords: Vector2i) -> Array[Vector2i]:
return tilemap.get_used_cells_by_id(sid, atlas_coords)
func get_cells_by_type_collection(atlas_coords: Array[Vector2i]) -> Array[Vector2i]:
func get_cells_by_type_collection(
atlas_coords: Array[Vector2i],
center: Vector2i = Vector2i(-1, -1), max_distance: int = 99999999
) -> Array[Vector2i]:
var tiles_with_type: Array[Vector2i] = []
for coords in atlas_coords:
tiles_with_type.append_array(get_cells_by_type(coords))
if max_distance < 99999999:
var filtered_tiles: Array[Vector2i] = []
for tile in tiles_with_type:
if TilemapNavigation.manhattan_distance(center, tile) <= max_distance:
filtered_tiles.append(tile)
return filtered_tiles
return tiles_with_type

View File

@ -58,6 +58,7 @@ const TEMPERATURE_COLD_1: Vector2i = Vector2i(0, 0)
const TEMPERATURE_COLD_2: Vector2i = Vector2i(1, 0)
#
const NAVIGATION_CHECKED: Vector2i = Vector2i(0, 1)
const NAVIGATION_TARGET: Vector2i = Vector2i(3, 1)
const NAVIGATION_CHOSEN: Vector2i = Vector2i(1, 1)
const NAVIGATION_FAILED: Vector2i = Vector2i(2, 1)
#

View File

@ -20,7 +20,6 @@ func game_tick_start() -> void:
func game_tick_end() -> void:
world.tilemap_nav_vis.clear_cells()
# use tilemap_types.NAVIGATION_CHECKED, tilemap_types.NAVIGATION_FAILED and tilemap_types.NAVIGATION_CHOSEN
for path in found_paths.values():
for pos in path:
world.tilemap_nav_vis.set_cell(pos, tilemap_types.NAVIGATION_CHECKED)
@ -28,16 +27,29 @@ func game_tick_end() -> void:
world.tilemap_nav_vis.set_cell(pos, tilemap_types.NAVIGATION_FAILED)
for pos in chosen_path:
world.tilemap_nav_vis.set_cell(pos, tilemap_types.NAVIGATION_CHOSEN)
# mark last in chosen path as NAVIGATION_TARGET
if chosen_path.size() > 0:
world.tilemap_nav_vis.set_cell(chosen_path[chosen_path.size() - 1], tilemap_types.NAVIGATION_TARGET)
func is_within_radius(position: Vector2i, center: Vector2i, radius: int) -> bool:
return manhattan_distance(position, center) <= radius
return TilemapNavigation.manhattan_distance(position, center) <= radius
func manhattan_distance(a: Vector2i, b: Vector2i) -> int:
static func manhattan_distance(a: Vector2i, b: Vector2i) -> int:
return abs(a.x - b.x) + abs(a.y - b.y)
func manhattan_distance_closest(options: Array[Vector2i], target: Vector2i) -> Vector2i:
var closest: Vector2i = tilemap_types.NO_TILE_FOUND
var shortest: int = 9999999999
for option in options:
var distance: int = manhattan_distance(option, target)
if distance < shortest:
closest = option
shortest = distance
return closest
var walking_directions: Array[Vector2i] = [Vector2i(0, -1), Vector2i(0, 1), Vector2i(-1, 0), Vector2i(1, 0)]
var f_score: Dictionary = {}
@ -59,6 +71,10 @@ func find_path_allow_neighbors(start_position: Vector2i, end_position: Vector2i,
return []
func has_arrived(position: Vector2i, path: Array[Vector2i]) -> bool:
return path.size() > 0 and path[path.size() - 1] == position
func find_path(start_position: Vector2i, end_position: Vector2i, max_radius: int = -1) -> Array[Vector2i]:
var path: Array[Vector2i] = _find_path_internal(start_position, end_position, max_radius)
if path.size() > 0: