refactor: separate action system responsibilities
This commit is contained in:
@@ -7,6 +7,8 @@ signal npc_task_changed(
|
||||
)
|
||||
signal village_changed(village: SimVillage)
|
||||
signal npc_died(npc: SimNPC)
|
||||
signal npc_target_requested(npc: SimNPC)
|
||||
signal npc_travel_requested(npc: SimNPC, target_position: Vector3)
|
||||
|
||||
var village := SimVillage.new()
|
||||
|
||||
@@ -14,11 +16,15 @@ var npcs: Array[SimNPC] = []
|
||||
@export var tick_interval := 1.2
|
||||
@export var simulation_seed: int = 1337
|
||||
@export var debug_logs := true
|
||||
@export var active_world_adapter: Node
|
||||
|
||||
var clock: SimulationClock
|
||||
var tick_count := 0
|
||||
var wander_random_sources := {}
|
||||
var resource_states: Dictionary = {}
|
||||
var action_selector := ActionSelectionSystem.new()
|
||||
var action_executor := ActionExecutionSystem.new()
|
||||
var target_resolver := ActionTargetResolver.new()
|
||||
|
||||
var names := [
|
||||
"Amina", "Tarik", "Jasmin"
|
||||
@@ -97,7 +103,24 @@ func simulate_tick() -> void:
|
||||
var was_complete := npc.task_complete
|
||||
var was_dead := npc.is_dead
|
||||
|
||||
npc.simulate_tick(village)
|
||||
action_executor.advance_npc(npc, village)
|
||||
if (
|
||||
not npc.is_dead
|
||||
and old_state in [
|
||||
SimNPC.TASK_STATE_IDLE,
|
||||
SimNPC.TASK_STATE_COMPLETE
|
||||
]
|
||||
and npc.task_state in [
|
||||
SimNPC.TASK_STATE_IDLE,
|
||||
SimNPC.TASK_STATE_COMPLETE
|
||||
]
|
||||
):
|
||||
var selection := action_selector.select_action(npc, village)
|
||||
if selection != null:
|
||||
npc.set_task(
|
||||
selection.action_id,
|
||||
selection.duration_override
|
||||
)
|
||||
|
||||
if old_task != npc.current_task and old_target != &"":
|
||||
release_npc_reservation(npc.id)
|
||||
@@ -113,6 +136,7 @@ func simulate_tick() -> void:
|
||||
|
||||
if old_task != npc.current_task and not npc.is_dead:
|
||||
npc_task_changed.emit(npc, old_task, npc.current_task)
|
||||
npc_target_requested.emit(npc)
|
||||
|
||||
if not was_complete and npc.task_complete and not npc.is_dead:
|
||||
var completed_task := npc.current_task
|
||||
@@ -162,6 +186,7 @@ func simulate_tick() -> void:
|
||||
if needs_immediate_food_after_gather and village.food > 0:
|
||||
npc.set_task(SimulationIds.ACTION_EAT, 1.0)
|
||||
npc_task_changed.emit(npc, completed_task, npc.current_task)
|
||||
npc_target_requested.emit(npc)
|
||||
|
||||
if debug_logs:
|
||||
print("[SimulationManager] ", npc.npc_name, " gathered food while starving and is going to eat immediately.")
|
||||
@@ -255,6 +280,7 @@ func notify_npc_navigation_failed(npc_id: int) -> void:
|
||||
npc.last_task = failed_task
|
||||
npc.set_task(SimulationIds.ACTION_WANDER, 2.0)
|
||||
npc_task_changed.emit(npc, failed_task, npc.current_task)
|
||||
npc_target_requested.emit(npc)
|
||||
|
||||
if debug_logs:
|
||||
print("[SimulationManager] ", npc.npc_name, " could not navigate for ", failed_task, " and is trying a wander target")
|
||||
@@ -263,6 +289,29 @@ func notify_npc_navigation_failed(npc_id: int) -> void:
|
||||
func notify_npc_target_unavailable(npc_id: int) -> void:
|
||||
notify_npc_navigation_failed(npc_id)
|
||||
|
||||
func resolve_npc_target(npc_id: int, origin: Vector3) -> bool:
|
||||
if active_world_adapter == null:
|
||||
push_error("SimulationManager: active_world_adapter is missing")
|
||||
return false
|
||||
for npc in npcs:
|
||||
if npc.id != npc_id:
|
||||
continue
|
||||
if npc.is_dead or npc.task_state != SimNPC.TASK_STATE_TRAVELING:
|
||||
return false
|
||||
var result := target_resolver.resolve(
|
||||
npc,
|
||||
origin,
|
||||
self,
|
||||
active_world_adapter
|
||||
)
|
||||
if result.is_empty():
|
||||
notify_npc_target_unavailable(npc.id)
|
||||
return false
|
||||
npc.target_id = StringName(result.get("target_id", ""))
|
||||
npc_travel_requested.emit(npc, result["position"])
|
||||
return true
|
||||
return false
|
||||
|
||||
func register_loaded_resource_nodes() -> void:
|
||||
for node in ResourceNode.get_all():
|
||||
register_resource_node(node)
|
||||
@@ -305,33 +354,6 @@ func release_resource(node_id: StringName, agent_id: int) -> void:
|
||||
if resource_state != null:
|
||||
resource_state.release(agent_id)
|
||||
|
||||
func acquire_resource_target(
|
||||
action_id: StringName,
|
||||
agent_id: int,
|
||||
from_position: Vector3
|
||||
) -> ResourceNode:
|
||||
var best: ResourceNode
|
||||
var best_distance := INF
|
||||
for node in ResourceNode.get_all():
|
||||
var resource_state := get_resource_state(node.node_id)
|
||||
if resource_state == null:
|
||||
continue
|
||||
if resource_state.get_action_id() != action_id:
|
||||
continue
|
||||
if not resource_state.can_npc_use():
|
||||
continue
|
||||
if not resource_state.is_available_for(agent_id):
|
||||
continue
|
||||
var distance := from_position.distance_squared_to(
|
||||
node.interaction_point.global_position
|
||||
)
|
||||
if distance < best_distance:
|
||||
best_distance = distance
|
||||
best = node
|
||||
if best == null or not reserve_resource(best.node_id, agent_id):
|
||||
return null
|
||||
return best
|
||||
|
||||
func find_resource_node_for_player(
|
||||
from_position: Vector3,
|
||||
max_distance: float
|
||||
|
||||
Reference in New Issue
Block a user