refactor: move resource authority into simulation
This commit is contained in:
+134
-37
@@ -14,6 +14,7 @@ var npcs: Array[SimNPC] = []
|
||||
var clock: SimulationClock
|
||||
var tick_count := 0
|
||||
var wander_random_sources := {}
|
||||
var resource_states: Dictionary = {}
|
||||
|
||||
var names := [
|
||||
"Amina", "Tarik", "Jasmin"
|
||||
@@ -28,11 +29,13 @@ var professions := [
|
||||
]
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("simulation_manager")
|
||||
clock = SimulationClock.new(tick_interval)
|
||||
village.debug_logs = debug_logs
|
||||
village.update_modifiers()
|
||||
village.update_priorities()
|
||||
generate_npcs()
|
||||
call_deferred("register_loaded_resource_nodes")
|
||||
if debug_logs:
|
||||
print("--- Simulation started ---")
|
||||
|
||||
@@ -110,13 +113,28 @@ func simulate_tick() -> void:
|
||||
var needs_immediate_food_after_gather := npc.should_eat_immediately_after_task()
|
||||
|
||||
if npc.target_id != &"":
|
||||
var node := ResourceNode.get_by_id(npc.target_id)
|
||||
if node != null and node.reserved_by == npc.id:
|
||||
var extracted := node.extract()
|
||||
var resource_state := get_resource_state(npc.target_id)
|
||||
if (
|
||||
resource_state != null
|
||||
and resource_state.get_reserved_by() == npc.id
|
||||
):
|
||||
var extracted := resource_state.extract()
|
||||
if extracted > 0:
|
||||
village.apply_resource_delta(node.resource_id, extracted)
|
||||
village.apply_resource_delta(
|
||||
resource_state.get_resource_id(),
|
||||
extracted
|
||||
)
|
||||
if debug_logs:
|
||||
print("[SimulationManager] ", npc.npc_name, " extracted ", extracted, " ", node.resource_id, " from ", node.node_id)
|
||||
print(
|
||||
"[SimulationManager] ",
|
||||
npc.npc_name,
|
||||
" extracted ",
|
||||
extracted,
|
||||
" ",
|
||||
resource_state.get_resource_id(),
|
||||
" from ",
|
||||
resource_state.get_node_id()
|
||||
)
|
||||
elif debug_logs:
|
||||
print("[SimulationManager] ", npc.npc_name, " could not complete ", completed_task, ": target reservation was invalid")
|
||||
release_npc_reservation(npc.id)
|
||||
@@ -185,9 +203,9 @@ func release_npc_reservation(npc_id: int) -> void:
|
||||
for npc in npcs:
|
||||
if npc.id == npc_id:
|
||||
if npc.target_id != &"":
|
||||
var node := ResourceNode.get_by_id(npc.target_id)
|
||||
if node != null:
|
||||
node.release(npc.id)
|
||||
var resource_state := get_resource_state(npc.target_id)
|
||||
if resource_state != null:
|
||||
resource_state.release(npc.id)
|
||||
npc.target_id = &""
|
||||
return
|
||||
|
||||
@@ -199,8 +217,12 @@ func notify_npc_arrived(npc_id: int) -> void:
|
||||
|
||||
if npc.task_state == SimNPC.TASK_STATE_TRAVELING:
|
||||
if npc.target_id != &"":
|
||||
var node := ResourceNode.get_by_id(npc.target_id)
|
||||
if node == null or not node.can_extract():
|
||||
var resource_state := get_resource_state(npc.target_id)
|
||||
if (
|
||||
resource_state == null
|
||||
or not resource_state.can_extract()
|
||||
or resource_state.get_reserved_by() != npc.id
|
||||
):
|
||||
if debug_logs:
|
||||
print("[SimulationManager] ", npc.npc_name, " arrived but target ", npc.target_id, " is unavailable, replanning")
|
||||
notify_npc_navigation_failed(npc.id)
|
||||
@@ -232,19 +254,107 @@ 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 harvest_resource_node(node: ResourceNode) -> float:
|
||||
if node == null or not node.can_player_use:
|
||||
return 0.0
|
||||
func register_loaded_resource_nodes() -> void:
|
||||
for node in ResourceNode.get_all():
|
||||
register_resource_node(node)
|
||||
|
||||
var extracted := node.extract()
|
||||
func register_resource_node(node: ResourceNode) -> bool:
|
||||
if node == null or node.node_id.is_empty():
|
||||
return false
|
||||
var resource_state := get_resource_state(node.node_id)
|
||||
if resource_state == null:
|
||||
resource_state = ResourceStateRecord.create_from_node(node)
|
||||
resource_states[node.node_id] = resource_state
|
||||
elif not resource_state.apply_definition(node):
|
||||
push_error(
|
||||
"SimulationManager: ResourceNode definition mismatch for '%s'"
|
||||
% node.node_id
|
||||
)
|
||||
return false
|
||||
return node.bind_state(resource_state)
|
||||
|
||||
func get_resource_state(node_id: StringName) -> ResourceStateRecord:
|
||||
return resource_states.get(node_id) as ResourceStateRecord
|
||||
|
||||
func reserve_resource(node_id: StringName, agent_id: int) -> bool:
|
||||
var resource_state := get_resource_state(node_id)
|
||||
return resource_state != null and resource_state.reserve(agent_id)
|
||||
|
||||
func release_resource(node_id: StringName, agent_id: int) -> void:
|
||||
var resource_state := get_resource_state(node_id)
|
||||
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
|
||||
) -> ResourceNode:
|
||||
var best: ResourceNode
|
||||
var best_distance := max_distance * max_distance
|
||||
for node in ResourceNode.get_all():
|
||||
var resource_state := get_resource_state(node.node_id)
|
||||
if (
|
||||
resource_state == null
|
||||
or not resource_state.can_player_use_resource()
|
||||
):
|
||||
continue
|
||||
var distance := from_position.distance_squared_to(
|
||||
node.interaction_point.global_position
|
||||
)
|
||||
if distance <= best_distance:
|
||||
best_distance = distance
|
||||
best = node
|
||||
return best
|
||||
|
||||
func harvest_resource_node(node: ResourceNode) -> float:
|
||||
if node == null:
|
||||
return 0.0
|
||||
var resource_state := get_resource_state(node.node_id)
|
||||
if resource_state == null or not resource_state.can_player_use_resource():
|
||||
return 0.0
|
||||
var extracted := resource_state.extract()
|
||||
if extracted <= 0.0:
|
||||
return 0.0
|
||||
|
||||
village.apply_resource_delta(node.resource_id, extracted)
|
||||
village.apply_resource_delta(resource_state.get_resource_id(), extracted)
|
||||
village_changed.emit(village)
|
||||
|
||||
if debug_logs:
|
||||
print("[SimulationManager] Player extracted ", extracted, " ", node.resource_id, " from ", node.node_id)
|
||||
print(
|
||||
"[SimulationManager] Player extracted ",
|
||||
extracted,
|
||||
" ",
|
||||
resource_state.get_resource_id(),
|
||||
" from ",
|
||||
resource_state.get_node_id()
|
||||
)
|
||||
|
||||
return extracted
|
||||
|
||||
@@ -336,17 +446,11 @@ func create_state_record() -> SimulationStateRecord:
|
||||
for npc in npcs:
|
||||
record.npcs.append(NPCStateRecord.capture(npc))
|
||||
|
||||
var resource_nodes := ResourceNode.get_all()
|
||||
resource_nodes.sort_custom(
|
||||
func(a: ResourceNode, b: ResourceNode) -> bool:
|
||||
return String(a.node_id) < String(b.node_id)
|
||||
)
|
||||
var captured_resource_ids := {}
|
||||
for node in resource_nodes:
|
||||
if captured_resource_ids.has(node.node_id):
|
||||
continue
|
||||
captured_resource_ids[node.node_id] = true
|
||||
record.resources.append(ResourceStateRecord.capture(node))
|
||||
var sorted_resource_ids: Array = resource_states.keys()
|
||||
sorted_resource_ids.sort()
|
||||
for resource_id in sorted_resource_ids:
|
||||
var resource_state: ResourceStateRecord = resource_states[resource_id]
|
||||
record.resources.append(resource_state)
|
||||
return record
|
||||
|
||||
func serialize_state() -> String:
|
||||
@@ -383,17 +487,10 @@ func restore_state(record: SimulationStateRecord) -> bool:
|
||||
source.state = String(stream_data["state"]).to_int()
|
||||
wander_random_sources[int(stream_data["npc_id"])] = source
|
||||
|
||||
resource_states.clear()
|
||||
for resource_record in record.resources:
|
||||
var node_id := StringName(resource_record.data["node_id"])
|
||||
var node := ResourceNode.get_by_id(node_id)
|
||||
if node == null:
|
||||
push_warning(
|
||||
"SimulationManager: resource state '%s' has no loaded presentation node"
|
||||
% node_id
|
||||
)
|
||||
continue
|
||||
if not resource_record.restore(node):
|
||||
return false
|
||||
resource_states[resource_record.get_node_id()] = resource_record
|
||||
register_loaded_resource_nodes()
|
||||
|
||||
village_changed.emit(village)
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user