New art assets, work on implementing more adventurer behavior and making them work better together, and steps towards completing the first quest loop.

This commit is contained in:
2025-07-31 08:44:26 -04:00
parent c0a2c058ba
commit 38a7ed85b0
66 changed files with 1112 additions and 658 deletions

View File

@@ -0,0 +1,103 @@
#*
#* use_guild_service.gd
#*
@tool
extends BTAction
## Moves the agent to the specified position, favoring horizontal movement. [br]
## Returns [code]SUCCESS[/code] when close to the target position (see [member tolerance]);
## otherwise returns [code]RUNNING[/code].
enum Phases {
ARRIVE,
QUEUE,
WAIT,
OBTAIN,
COMPLETE
}
var board : QuestBoard
var queue : GuildQueue
var wait_time_remaining : float = 0
var phase : Phases
func _generate_name() -> String:
return "Get a Quest"
func _enter() -> void:
var brd = Guild.hall.board
if !brd:
printerr("Get a Quest: Board not found!")
return
board = brd
queue = board.queue
phase = Phases.ARRIVE
queue.add_member(agent)
agent.approach(queue.get_last_position())
agent.navigation_finished.connect(_on_navigation_complete)
agent.navigation_failed.connect(_on_navigation_failed)
func _tick(delta: float) -> Status:
if board == null:
return FAILURE
match(phase):
Phases.ARRIVE:
if wait_time_remaining > 0:
wait_time_remaining -= delta
if wait_time_remaining <= 0:
agent.navigation_finished.connect(_on_navigation_complete)
agent.approach(queue.get_member_position(agent))
Phases.QUEUE:
pass
Phases.WAIT:
pass
Phases.OBTAIN:
if wait_time_remaining:
wait_time_remaining -= delta
if wait_time_remaining <= 0:
board.interaction_complete.connect(_on_interaction_complete)
board.interact(agent, "quest")
phase = Phases.OBTAIN
Phases.COMPLETE:
return SUCCESS
return RUNNING
func _on_navigation_complete() -> void:
agent.navigation_finished.disconnect(_on_navigation_complete)
agent.navigation_failed.disconnect(_on_navigation_failed)
queue.advanced.connect(_on_queue_advanced)
phase = Phases.QUEUE
func _on_navigation_failed() -> void:
wait_time_remaining = randf_range(.5, 2)
agent.navigation_finished.disconnect(_on_navigation_complete)
func get_quest():
phase = Phases.OBTAIN
wait_time_remaining = randf_range(2,5)
agent.show_speech_bubble("busy")
func wait():
wait_time_remaining = 1
phase = Phases.WAIT
func _on_queue_advanced() -> void:
if queue.front == agent:
queue.advanced.disconnect(_on_queue_advanced)
if board.busy:
wait()
else:
get_quest()
pass
func _on_interaction_complete() -> void:
board.interaction_complete.disconnect(_on_interaction_complete)
if agent.data.quest != null:
agent.show_speech_bubble("happy", 1.5)
else:
agent.show_speech_bubble("angry", 1.5)
queue.remove_member(agent)
phase = Phases.COMPLETE

View File

@@ -0,0 +1 @@
uid://ders6t7axct24

48
ai/tasks/actions/go_to.gd Normal file
View File

@@ -0,0 +1,48 @@
#*
#* go_to.gd
#*
@tool
extends BTAction
## Moves the agent to the specified position, favoring horizontal movement. [br]
## Returns [code]SUCCESS[/code] when close to the target position (see [member tolerance]);
## otherwise returns [code]RUNNING[/code].
## Blackboard variable that stores the target position (Vector2)
@export var target_position_var := &"pos"
var wait_time_remaining : float = 0
var goal_position : Vector2
var done : bool
func _generate_name() -> String:
return "Go to Position: %s" % [LimboUtility.decorate_var(target_position_var)]
func _enter() -> void:
done = false
goal_position = blackboard.get_var(target_position_var, Vector2.ZERO)
go_to(goal_position)
func _tick(delta: float) -> Status:
if done:
return SUCCESS
#If we were interrupted, wait a little bit and try again
if wait_time_remaining > 0:
wait_time_remaining -= delta
if wait_time_remaining <= 0:
go_to(goal_position)
return RUNNING
func go_to(pos : Vector2) -> void:
agent.navigation_finished.connect(_on_navigation_complete)
agent.navigation_failed.connect(_on_navigation_failed)
agent.approach(pos)
func _on_navigation_complete() -> void:
agent.navigation_finished.disconnect(_on_navigation_complete)
agent.navigation_failed.disconnect(_on_navigation_failed)
done = true
func _on_navigation_failed() -> void:
wait_time_remaining = randf_range(.5, 2)
agent.navigation_finished.disconnect(_on_navigation_complete)
agent.navigation_failed.disconnect(_on_navigation_failed)

View File

@@ -0,0 +1 @@
uid://h113xg55h4r8

View File

@@ -0,0 +1,111 @@
#*
#* use_guild_service.gd
#*
@tool
extends BTAction
## Moves the agent to the specified position, favoring horizontal movement. [br]
## Returns [code]SUCCESS[/code] when close to the target position (see [member tolerance]);
## otherwise returns [code]RUNNING[/code].
enum Phases {
ARRIVE,
QUEUE,
WAIT,
SERVICE,
COMPLETE
}
## Blackboard variable that stores the target position (Vector2)
@export var employee_name : String = ""
## Variable that stores desired speed (float)
@export var service_name : String = ""
var employee : GuildEmployee
var queue : GuildQueue
var wait_time_remaining : float = 0
var phase : Phases
func _generate_name() -> String:
return "Use Guild Service (%s) - %s" % [
employee_name,
service_name
]
func _enter() -> void:
var emp = Guild.hall.employees.get(employee_name)
if !emp:
printerr("Use Guild Service (%s) - %s, '%s' not found!", employee_name, service_name, employee_name)
return
employee = emp
queue = employee.queue
phase = Phases.ARRIVE
queue.add_member(agent)
agent.approach(queue.get_last_position())
agent.navigation_finished.connect(_on_navigation_complete)
agent.navigation_failed.connect(_on_navigation_failed)
func _tick(delta: float) -> Status:
if employee == null:
return FAILURE
match(phase):
Phases.ARRIVE:
if wait_time_remaining > 0:
wait_time_remaining -= delta
if wait_time_remaining <= 0:
agent.navigation_finished.connect(_on_navigation_complete)
agent.approach(queue.get_member_position(agent))
Phases.QUEUE:
pass
Phases.WAIT:
pass
Phases.SERVICE:
if wait_time_remaining > 0:
wait_time_remaining -= delta
if wait_time_remaining <= 0:
employee.service_provided.connect(_on_service_complete)
employee.interact(agent, service_name)
Phases.COMPLETE:
return SUCCESS
return RUNNING
func _on_navigation_complete() -> void:
agent.navigation_finished.disconnect(_on_navigation_complete)
agent.navigation_failed.disconnect(_on_navigation_failed)
queue.advanced.connect(_on_queue_advanced)
phase = Phases.QUEUE
func _on_navigation_failed() -> void:
wait_time_remaining = randf_range(.5, 2)
agent.navigation_finished.disconnect(_on_navigation_complete)
func use_service():
phase = Phases.SERVICE
wait_time_remaining = randf_range(2,5)
#TODO: Make them both do the talking emoji
agent.show_speech_bubble("talk")
employee.show_speech_bubble("talk")
func wait():
wait_time_remaining = 1
phase = Phases.WAIT
func _on_queue_advanced() -> void:
if queue.front == agent:
queue.advanced.disconnect(_on_queue_advanced)
if employee.busy:
wait()
else:
use_service()
pass
func _on_service_complete() -> void:
employee.service_provided.disconnect(_on_service_complete)
agent.show_speech_bubble("")
employee.show_speech_bubble("")
queue.remove_member(agent)
phase = Phases.COMPLETE

View File

@@ -0,0 +1 @@
uid://bsq5dxul0uto

View File

@@ -0,0 +1,50 @@
#*
#* go_to.gd
#*
@tool
extends BTAction
## Moves the agent to the specified position, favoring horizontal movement. [br]
## Returns [code]SUCCESS[/code] when close to the target position (see [member tolerance]);
## otherwise returns [code]RUNNING[/code].
var wait_time_remaining : float = 0
var goal_position : Vector2
var retries : int
var done : bool
func _generate_name() -> String:
return "Wander to New Position"
func _enter() -> void:
done = false
retries = 0
goal_position = NavigationServer2D.region_get_random_point(Guild.hall.nav_region.get_rid(),1,false)
go_to(goal_position)
func _tick(delta: float) -> Status:
if done:
return SUCCESS
#If we were interrupted, wait a little bit and try again
if wait_time_remaining > 0:
wait_time_remaining -= delta
if wait_time_remaining <= 0:
go_to(goal_position)
return RUNNING
func go_to(pos : Vector2) -> void:
agent.navigation_finished.connect(_on_navigation_complete)
agent.navigation_failed.connect(_on_navigation_failed)
agent.approach(pos)
func _on_navigation_complete() -> void:
agent.navigation_finished.disconnect(_on_navigation_complete)
agent.navigation_failed.disconnect(_on_navigation_failed)
done = true
func _on_navigation_failed() -> void:
wait_time_remaining = randf_range(.5, 2)
retries += 1
if retries >= 3:
done = true
agent.navigation_finished.disconnect(_on_navigation_complete)
agent.navigation_failed.disconnect(_on_navigation_failed)

View File

@@ -0,0 +1 @@
uid://767b4fdlrgr

View File

@@ -0,0 +1,7 @@
extends BTCondition
var invert : bool
func _tick(delta: float) -> Status:
if agent.data and ((agent.data.quest == null) == invert):
return SUCCESS
else:
return FAILURE

View File

@@ -0,0 +1 @@
uid://xom38ohdwfms

View File

@@ -0,0 +1,7 @@
extends BTCondition
func _tick(delta: float) -> Status:
if agent.data and !Guild.has_guild_member(agent.data):
return SUCCESS
else:
return FAILURE

View File

@@ -0,0 +1 @@
uid://bcbfnm21rtkuo

View File

@@ -0,0 +1,22 @@
@tool
extends BTDecorator
var prev_busy : bool
func _get_task_icon():
return load("res://ai/icons/stopwatch.png")
func _enter() -> void:
if agent.get("busy") != null:
prev_busy = agent.busy
agent.busy = true
func _exit() -> void:
if agent.get("busy") != null:
agent.busy = prev_busy
# Called to generate a display name for the task (requires @tool).
func _generate_name() -> String:
return "Busy"

View File

@@ -0,0 +1 @@
uid://b2vuw12mttm40