diff --git a/project/assets/tilemap/tilemaps.aseprite b/project/assets/tilemap/tilemaps.aseprite index 6287ad6..81f69c6 100644 Binary files a/project/assets/tilemap/tilemaps.aseprite and b/project/assets/tilemap/tilemaps.aseprite differ diff --git a/project/assets/tilemap/tilemaps/sprite.json b/project/assets/tilemap/tilemaps/sprite.json index 23734c9..d5b7165 100644 --- a/project/assets/tilemap/tilemaps/sprite.json +++ b/project/assets/tilemap/tilemaps/sprite.json @@ -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"} \ No newline at end of file +{"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} \ No newline at end of file diff --git a/project/assets/tilemap/tilemaps/tilemap_temperature.png b/project/assets/tilemap/tilemaps/tilemap_temperature.png index e779996..8688551 100644 Binary files a/project/assets/tilemap/tilemaps/tilemap_temperature.png and b/project/assets/tilemap/tilemaps/tilemap_temperature.png differ diff --git a/project/assets/tilemap/tileset.tres b/project/assets/tilemap/tileset.tres index 47ad8de..f5a7103 100644 --- a/project/assets/tilemap/tileset.tres +++ b/project/assets/tilemap/tileset.tres @@ -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") diff --git a/project/main-scenes/island.tscn b/project/main-scenes/island.tscn index db4bcfa..6dd6483 100644 --- a/project/main-scenes/island.tscn +++ b/project/main-scenes/island.tscn @@ -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") diff --git a/project/scripts/global/GameManager.gd b/project/scripts/global/GameManager.gd index edd90fc..08014b7 100644 --- a/project/scripts/global/GameManager.gd +++ b/project/scripts/global/GameManager.gd @@ -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() diff --git a/project/scripts/player/tree/BehaviorTree.gd b/project/scripts/player/tree/BehaviorTree.gd index e1a7f8d..56945af 100644 --- a/project/scripts/player/tree/BehaviorTree.gd +++ b/project/scripts/player/tree/BehaviorTree.gd @@ -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, "]") diff --git a/project/scripts/player/tree/Task.gd b/project/scripts/player/tree/Task.gd index 8593f24..7ed7455 100644 --- a/project/scripts/player/tree/Task.gd +++ b/project/scripts/player/tree/Task.gd @@ -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 +func internal_run(blackboard: Dictionary) -> void: + 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) - + 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 diff --git a/project/scripts/player/tree/impl/base/GoToRunning.gd b/project/scripts/player/tree/impl/base/GoToRunning.gd new file mode 100644 index 0000000..29c2e85 --- /dev/null +++ b/project/scripts/player/tree/impl/base/GoToRunning.gd @@ -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) diff --git a/project/scripts/player/tree/impl/base/GoToSuccess.gd b/project/scripts/player/tree/impl/base/GoToSuccess.gd new file mode 100644 index 0000000..221d821 --- /dev/null +++ b/project/scripts/player/tree/impl/base/GoToSuccess.gd @@ -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) diff --git a/project/scripts/player/tree/impl/base/TaskSelector.gd b/project/scripts/player/tree/impl/base/TaskSelector.gd index 5fca651..2cdd883 100644 --- a/project/scripts/player/tree/impl/base/TaskSelector.gd +++ b/project/scripts/player/tree/impl/base/TaskSelector.gd @@ -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 diff --git a/project/scripts/player/tree/impl/game/critical_survival/TaskCheckFoodCritical.gd b/project/scripts/player/tree/impl/game/critical_survival/TaskCheckFoodCritical.gd index 3c657d5..9d16af7 100644 --- a/project/scripts/player/tree/impl/game/critical_survival/TaskCheckFoodCritical.gd +++ b/project/scripts/player/tree/impl/game/critical_survival/TaskCheckFoodCritical.gd @@ -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) diff --git a/project/scripts/player/tree/impl/testing/WalkToMouse.gd b/project/scripts/player/tree/impl/testing/WalkToMouse.gd index 9dd44c8..1a466c4 100644 --- a/project/scripts/player/tree/impl/testing/WalkToMouse.gd +++ b/project/scripts/player/tree/impl/testing/WalkToMouse.gd @@ -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) diff --git a/project/scripts/player/tree/impl/testing/WalkUpToMouse.gd b/project/scripts/player/tree/impl/testing/WalkUpToMouse.gd index 35f4c56..251db88 100644 --- a/project/scripts/player/tree/impl/testing/WalkUpToMouse.gd +++ b/project/scripts/player/tree/impl/testing/WalkUpToMouse.gd @@ -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) diff --git a/project/scripts/tilemap/TileMapLayerAccess.gd b/project/scripts/tilemap/TileMapLayerAccess.gd index ca1dab2..42f42fa 100644 --- a/project/scripts/tilemap/TileMapLayerAccess.gd +++ b/project/scripts/tilemap/TileMapLayerAccess.gd @@ -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 diff --git a/project/scripts/tilemap/TileMapTileTypes.gd b/project/scripts/tilemap/TileMapTileTypes.gd index 43902fc..1b2346a 100644 --- a/project/scripts/tilemap/TileMapTileTypes.gd +++ b/project/scripts/tilemap/TileMapTileTypes.gd @@ -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) # diff --git a/project/scripts/tilemap/TilemapNavigation.gd b/project/scripts/tilemap/TilemapNavigation.gd index 9e50c2b..5f90687 100644 --- a/project/scripts/tilemap/TilemapNavigation.gd +++ b/project/scripts/tilemap/TilemapNavigation.gd @@ -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: