1
0
Fork 0

Added exploration behavior

Yan Wittmann 2025-01-12 14:37:40 +01:00
parent 268f2c0ce1
commit 57db80e685
7 changed files with 198 additions and 27 deletions

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,9 @@ enum Event {
PLAYER_DROPPED_ITEM, PLAYER_DROPPED_ITEM,
PLAYER_USED_ITEM, PLAYER_USED_ITEM,
GAME_STATE_WIN, GAME_STATE_WIN,
NEW_EXPLORATION_GOAL,
EXPLORATION_GOAL_REACHED,
TEMPERATURE_COLD,
}; };
# #
static var events: Array[TrackedEvent] = [] static var events: Array[TrackedEvent] = []
@ -74,6 +77,12 @@ static func populate_visual_log_create_label(event: TrackedEvent, container: Con
text = "Boat complete" text = "Boat complete"
elif event_id == Event.GAME_STATE_WIN: elif event_id == Event.GAME_STATE_WIN:
text = "Game won" text = "Game won"
elif event_id == Event.NEW_EXPLORATION_GOAL:
text = "New exploration goal " + str(params["goal"])
elif event_id == Event.EXPLORATION_GOAL_REACHED:
text = "Exploration goal reached " + str(params["goal"])
elif event_id == Event.TEMPERATURE_COLD:
text = "Temperature is cold: -" + str(params["temperature"])
else: else:
text = "Something happened..." text = "Something happened..."

View File

@ -0,0 +1,104 @@
class_name TaskPlannedExploration
extends Task
var last_goals: Array[Vector2i] = []
var current_goal: Vector2i = tilemap_types.NO_TILE_FOUND
func run(blackboard: Dictionary) -> void:
var world: World = blackboard["world"]
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
# check if player distance is < 2 to the current goal
if current_goal != tilemap_types.NO_TILE_FOUND:
if TilemapNavigation.manhattan_distance(player.board_position, current_goal) < 3:
EventsTracker.track(EventsTracker.Event.EXPLORATION_GOAL_REACHED, {"goal": current_goal})
current_goal = tilemap_types.NO_TILE_FOUND
if current_goal == tilemap_types.NO_TILE_FOUND:
find_new_goal(world, player, navigation)
if current_goal != tilemap_types.NO_TILE_FOUND:
EventsTracker.track(EventsTracker.Event.NEW_EXPLORATION_GOAL, {"goal": current_goal})
if current_goal == tilemap_types.NO_TILE_FOUND:
status = Task.FAILURE
status_reason = "No goal found"
return
StepVisualization.add_circle_tileset(current_goal, 2, StepVisualization.CircleType.GOAL)
StepVisualization.add_line_tileset(player.board_position, current_goal, StepVisualization.LineType.SEARCH_SELECTED)
var path: Array[Vector2i] = navigation.cached_path_allow_neighbors(blackboard, "exploration_goal", current_goal)
if path.size() == 0:
status = Task.FAILURE
status_reason = "No path found"
return
blackboard["path"] = path
status = Task.SUCCESS
status_reason = "goal: " + str(current_goal)
func find_new_goal(world: World, player: PlayerManager, navigation: TilemapNavigation) -> void:
if last_goals.size() == 0:
last_goals.append(world.camp_manager.camp)
# perform search for a new goal X times, pick the one that is furthest away from the last goal
var best_goal: Vector2i = tilemap_types.NO_TILE_FOUND
var best_distance: float = 0
for i in range(4):
var goal_consideration: Vector2i = determine_an_interesting_goal(world)
if goal_consideration == tilemap_types.NO_TILE_FOUND:
continue
StepVisualization.add_circle_tileset(goal_consideration, 2, StepVisualization.CircleType.GOAL_CONSIDERATION)
StepVisualization.add_line_tileset(player.board_position, goal_consideration, StepVisualization.LineType.SEARCH_BASE)
var distance: float = TilemapNavigation.manhattan_distance(goal_consideration, navigation.manhattan_distance_closest(last_goals, goal_consideration))
if distance > best_distance:
best_goal = goal_consideration
best_distance = distance
if best_goal != tilemap_types.NO_TILE_FOUND:
last_goals.append(best_goal)
current_goal = best_goal
func determine_an_interesting_goal(world: World) -> Vector2i:
# starting from the camp position (world.camp_manager.camp),
# pick a random direction (360 degrees, random on x and y then normalize),
# then step in that direction until the last walkable tile is found
# (if not walkable, check every step for the next 10 tiles again and only stop if none of them are walkable)
# then, pick a random walkable tile in the area around that last walkable tile,
# and check if the player can get there using the navigation system.
var camp_position: Vector2 = Vector2(world.camp_manager.camp)
var direction: Vector2 = Vector2(randf() * 2 - 1, randf() * 2 - 1).normalized()
var last_walkable: Vector2i = Vector2i(0, 0)
var iterations_no_walkable: int = 0
for i in range(5000):
var check_position: Vector2i = camp_position + (direction * i).floor()
if not world.is_walkable(check_position):
iterations_no_walkable += 1
else:
iterations_no_walkable = 0
last_walkable = check_position
if iterations_no_walkable > 10:
break
if last_walkable == Vector2i(0, 0):
return tilemap_types.NO_TILE_FOUND
var picked_goal: Vector2i = Vector2i(0, 0)
for i in range(10):
var check_position: Vector2i = last_walkable + Vector2i(randi_range(-10, 10), randi_range(-10, 10))
if world.is_walkable(check_position):
picked_goal = check_position
break
return picked_goal

View File

@ -0,0 +1,14 @@
class_name TaskRandomWalking
extends Task
func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"]
var navigation: TilemapNavigation = blackboard["navigation"]
var direction: Vector2i = navigation.walking_directions[randi() % navigation.walking_directions.size()]
var target: Vector2i = player.board_position + direction
player.walk_towards(target)
status = SUCCESS
status_reason = "Walking towards " + str(target)

View File

@ -1,13 +1,27 @@
class_name TaskCheckTemperatureCold class_name TaskCheckTemperatureCold
extends Task extends Task
var last_temperature: float = 0
func run(blackboard: Dictionary) -> void: func run(blackboard: Dictionary) -> void:
var player: PlayerManager = blackboard["player"] var player: PlayerManager = blackboard["player"]
if player.get_current_temperature() > 0: var current_temperature: int = player.get_current_temperature()
var temperature_changed: bool = current_temperature != last_temperature
var temperature_cold: bool = current_temperature > 0
if temperature_changed and temperature_cold:
EventsTracker.track(EventsTracker.Event.TEMPERATURE_COLD, {"temperature": current_temperature})
if temperature_changed:
last_temperature = current_temperature
if temperature_cold:
status = SUCCESS status = SUCCESS
status_reason = "cold: " + str(player.get_current_temperature()) status_reason = "cold: " + str(current_temperature)
return return
status = FAILURE status = FAILURE
status_reason = "not cold: " + str(player.get_current_temperature()) status_reason = "not cold: " + str(current_temperature)

View File

@ -5,7 +5,7 @@ static var game_manager: GameManager
static var world: World static var world: World
# #
enum LineType { SEARCH_BASE, SEARCH_SELECTED, SEARCH_FAILED } enum LineType { SEARCH_BASE, SEARCH_SELECTED, SEARCH_FAILED }
enum CircleType { PLAYER_VIEW } enum CircleType { PLAYER_VIEW, GOAL_CONSIDERATION, GOAL }
# #
# Dictionary[Array[Vector2i], LineType] ([from, to], line_type) # Dictionary[Array[Vector2i], LineType] ([from, to], line_type)
static var draw_lines: Dictionary = {} static var draw_lines: Dictionary = {}
@ -70,3 +70,7 @@ func _draw() -> void:
if circle_type == CircleType.PLAYER_VIEW: if circle_type == CircleType.PLAYER_VIEW:
draw_circle(center, radius, Color("green"), false, 2, true) draw_circle(center, radius, Color("green"), false, 2, true)
elif circle_type == CircleType.GOAL_CONSIDERATION:
draw_circle(center, radius, Color("yellow"), false, 2, true)
elif circle_type == CircleType.GOAL:
draw_circle(center, radius, Color("red"), false, 2, true)

View File

@ -43,7 +43,7 @@ func setup() -> void:
else: else:
push_error("No boat leave location found on tilemap") push_error("No boat leave location found on tilemap")
print("CampManager: camp=", camp, " campfire=", campfire) print("CampManager: camp=", camp, " campfire=", campfire, " boat_build_location=", boat_build_location, " boat_leave_location=", boat_leave_location)
tilemap_interactive.set_cell(campfire, tilemap_types.OBJECT_I_FIREPIT_OFF) tilemap_interactive.set_cell(campfire, tilemap_types.OBJECT_I_FIREPIT_OFF)