feat: add player standing and quest generation from village needs
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
extends Node
|
||||
|
||||
const SimulationEventLogScript := preload("res://simulation/events/SimulationEventLog.gd")
|
||||
const SimulationEventRecorderScript := preload("res://simulation/events/SimulationEventRecorder.gd")
|
||||
const VillageEconomyScript := preload("res://simulation/economy/VillageEconomy.gd")
|
||||
const AnimalCareSystemScript := preload("res://simulation/animals/animal_care_system.gd")
|
||||
const NpcTickDebugLog := preload("res://simulation/debug/npc_tick_debug_log.gd")
|
||||
@@ -23,6 +24,10 @@ signal event_knowledge_forgotten(knower_id: int, event: EconomicEventRecord)
|
||||
signal opportunity_opened(opportunity: OpportunityStateRecord)
|
||||
signal opportunity_resolved(opportunity: OpportunityStateRecord, cause_event: EconomicEventRecord)
|
||||
signal opportunity_invalidated(opportunity: OpportunityStateRecord)
|
||||
signal player_quest_opened(quest: PlayerQuestRecord)
|
||||
signal player_quest_completed(quest: PlayerQuestRecord)
|
||||
signal player_quest_expired(quest: PlayerQuestRecord)
|
||||
signal player_standing_changed(standing: PlayerStandingRecord)
|
||||
|
||||
var village := SimVillage.new()
|
||||
|
||||
@@ -39,11 +44,13 @@ var tick_count := 0
|
||||
var wander_random_sources := {}
|
||||
var resource_states: Dictionary = {}
|
||||
var event_log := SimulationEventLogScript.new()
|
||||
var event_recorder := SimulationEventRecorderScript.new()
|
||||
var economy := VillageEconomyScript.new()
|
||||
var animal_care := AnimalCareSystemScript.new()
|
||||
var relationship_system := RelationshipSystemScript.new()
|
||||
var event_knowledge_system := EventKnowledgeSystemScript.new()
|
||||
var opportunity_system := VillageOpportunitySystem.new()
|
||||
var player_quest_system := PlayerQuestSystem.new()
|
||||
var storage_states: Dictionary:
|
||||
get:
|
||||
return economy.storage_states
|
||||
@@ -71,11 +78,13 @@ const NPC_NAMES := ["Amina", "Tarik", "Jasmin", "Elma", "Mirza", "Lejla"]
|
||||
func _ready() -> void:
|
||||
add_to_group("simulation_manager")
|
||||
event_log.event_recorded.connect(_on_economic_event_recorded)
|
||||
event_recorder.configure(event_log, Callable(self, "get_tick_count_for_recording"))
|
||||
event_recorder.set_npcs(npcs)
|
||||
economy.inventory_changed.connect(_on_economy_inventory_changed)
|
||||
economy.economic_event_requested.connect(_record_economic_event)
|
||||
economy.narrative_event_requested.connect(record_narrative_event)
|
||||
animal_care.economic_event_requested.connect(_record_economic_event_at)
|
||||
animal_care.narrative_event_requested.connect(record_narrative_event)
|
||||
economy.economic_event_requested.connect(event_recorder.record_economic)
|
||||
economy.narrative_event_requested.connect(event_recorder.record_narrative)
|
||||
animal_care.economic_event_requested.connect(event_recorder.record_economic_at)
|
||||
animal_care.narrative_event_requested.connect(event_recorder.record_narrative)
|
||||
action_selector.relationship_system = relationship_system
|
||||
var definition_errors := SimulationDefinitions.validate()
|
||||
if not definition_errors.is_empty():
|
||||
@@ -175,6 +184,9 @@ func simulate_tick() -> void:
|
||||
)
|
||||
if invalidated != null:
|
||||
opportunity_invalidated.emit(invalidated)
|
||||
var expired_quest := player_quest_system.on_opportunity_invalidated(invalidated, tick_count)
|
||||
if expired_quest != null:
|
||||
player_quest_expired.emit(expired_quest)
|
||||
if tick_count % get_knowledge_review_interval() == 0:
|
||||
_maintain_event_knowledge(true)
|
||||
if debug_logs:
|
||||
@@ -239,7 +251,7 @@ func _select_action_if_idle(npc: SimNPC, previous_state: StringName) -> void:
|
||||
var display_name := (
|
||||
definition.display_name if definition != null else String(selection.action_id)
|
||||
)
|
||||
record_narrative_event(SimulationIds.EVENT_TASK_STARTED, npc.id, &"", display_name)
|
||||
event_recorder.record_narrative(SimulationIds.EVENT_TASK_STARTED, npc.id, &"", display_name)
|
||||
|
||||
|
||||
func _handle_npc_death(npc: SimNPC, previous_task: StringName, previous_target: StringName) -> void:
|
||||
@@ -247,7 +259,7 @@ func _handle_npc_death(npc: SimNPC, previous_task: StringName, previous_target:
|
||||
release_npc_reservation(npc.id)
|
||||
npc_died.emit(npc)
|
||||
npc_task_changed.emit(npc, previous_task, npc.current_task)
|
||||
record_narrative_event(SimulationIds.EVENT_NPC_DIED, npc.id)
|
||||
event_recorder.record_narrative(SimulationIds.EVENT_NPC_DIED, npc.id)
|
||||
_notify_mourning(npc)
|
||||
if debug_logs:
|
||||
print("[SimulationManager] NPC died: ", npc.npc_name)
|
||||
@@ -293,7 +305,7 @@ func _apply_action_completion(
|
||||
SimulationIds.ACTION_SLEEP:
|
||||
npc.energy = minf(npc.energy + 40.0, 100.0)
|
||||
npc.position = npc.home_position
|
||||
record_narrative_event(SimulationIds.EVENT_NPC_SLEPT, npc.id)
|
||||
event_recorder.record_narrative(SimulationIds.EVENT_NPC_SLEPT, npc.id)
|
||||
SimulationIds.ACTION_FEED_ANIMAL:
|
||||
pass
|
||||
SimulationIds.ACTION_PATROL, SimulationIds.ACTION_STUDY, SimulationIds.ACTION_REST, SimulationIds.ACTION_WANDER:
|
||||
@@ -320,7 +332,7 @@ func _complete_resource_gather(npc: SimNPC, completed_task: StringName) -> void:
|
||||
var resource_id := resource_state.get_resource_id()
|
||||
npc.add_inventory(resource_id, extracted)
|
||||
npc_inventory_changed.emit(npc, resource_id, npc.get_inventory_amount(resource_id))
|
||||
_record_economic_event(
|
||||
event_recorder.record_economic(
|
||||
SimulationIds.EVENT_RESOURCE_EXTRACTED,
|
||||
npc.id,
|
||||
resource_state.get_node_id(),
|
||||
@@ -329,7 +341,7 @@ func _complete_resource_gather(npc: SimNPC, completed_task: StringName) -> void:
|
||||
extracted
|
||||
)
|
||||
if resource_state.get_amount_remaining() <= 0.0:
|
||||
record_narrative_event(
|
||||
event_recorder.record_narrative(
|
||||
SimulationIds.EVENT_RESOURCE_DEPLETED, npc.id, resource_state.get_node_id()
|
||||
)
|
||||
if debug_logs:
|
||||
@@ -540,93 +552,8 @@ func _notify_mourning(dead_npc: SimNPC) -> void:
|
||||
print("[SimulationManager] ", mourner.npc_name, " is mourning ", dead_npc.npc_name)
|
||||
|
||||
|
||||
func _record_economic_event(
|
||||
event_type: StringName,
|
||||
actor_id: int,
|
||||
source_id: StringName,
|
||||
destination_id: StringName,
|
||||
item_id: StringName,
|
||||
amount: float
|
||||
) -> void:
|
||||
_record_economic_event_at(
|
||||
event_type,
|
||||
actor_id,
|
||||
source_id,
|
||||
destination_id,
|
||||
item_id,
|
||||
amount,
|
||||
_get_event_world_position(actor_id, source_id, destination_id)
|
||||
)
|
||||
|
||||
|
||||
func _record_economic_event_at(
|
||||
event_type: StringName,
|
||||
actor_id: int,
|
||||
source_id: StringName,
|
||||
destination_id: StringName,
|
||||
item_id: StringName,
|
||||
amount: float,
|
||||
world_position: Vector3
|
||||
) -> void:
|
||||
event_log.record_economic(
|
||||
tick_count, event_type, actor_id, source_id, destination_id, item_id, amount, world_position
|
||||
)
|
||||
|
||||
|
||||
func record_narrative_event(
|
||||
event_type: StringName,
|
||||
actor_id: int,
|
||||
source_id: StringName = &"",
|
||||
action_display: String = "",
|
||||
action_id: StringName = &"",
|
||||
item_id: StringName = &"",
|
||||
required_amount: float = 0.0
|
||||
) -> void:
|
||||
_record_narrative_event_at(
|
||||
event_type,
|
||||
actor_id,
|
||||
source_id,
|
||||
action_display,
|
||||
_get_event_world_position(actor_id, source_id, &""),
|
||||
action_id,
|
||||
item_id,
|
||||
required_amount
|
||||
)
|
||||
|
||||
|
||||
func _record_narrative_event_at(
|
||||
event_type: StringName,
|
||||
actor_id: int,
|
||||
source_id: StringName,
|
||||
action_display: String,
|
||||
world_position: Vector3,
|
||||
action_id: StringName = &"",
|
||||
item_id: StringName = &"",
|
||||
required_amount: float = 0.0
|
||||
) -> void:
|
||||
event_log.record_narrative(
|
||||
tick_count,
|
||||
event_type,
|
||||
actor_id,
|
||||
source_id,
|
||||
action_display,
|
||||
world_position,
|
||||
action_id,
|
||||
item_id,
|
||||
required_amount
|
||||
)
|
||||
|
||||
|
||||
func _get_event_world_position(
|
||||
actor_id: int, source_id: StringName, destination_id: StringName
|
||||
) -> Vector3:
|
||||
for npc in npcs:
|
||||
if npc.id != actor_id:
|
||||
continue
|
||||
if not npc.target_id.is_empty() and npc.target_id in [source_id, destination_id]:
|
||||
return npc.travel_target_position
|
||||
return npc.position
|
||||
return Vector3.ZERO
|
||||
func get_tick_count_for_recording() -> int:
|
||||
return tick_count
|
||||
|
||||
|
||||
func get_npc_events(npc_id: int, max_count: int = 8) -> Array[EconomicEventRecord]:
|
||||
@@ -729,8 +656,21 @@ func _update_opportunity_from_event(event: EconomicEventRecord) -> void:
|
||||
return
|
||||
if changed.get_status() == OpportunityStateRecord.STATUS_OPEN:
|
||||
opportunity_opened.emit(changed)
|
||||
var quest := player_quest_system.consider_opportunity_opened(
|
||||
changed, get_active_opportunity_player_response(), tick_count
|
||||
)
|
||||
if quest != null:
|
||||
player_quest_opened.emit(quest)
|
||||
return
|
||||
opportunity_resolved.emit(changed, event)
|
||||
var resolution := player_quest_system.on_opportunity_resolved(changed, event, tick_count)
|
||||
if resolution.has("quest"):
|
||||
var quest: PlayerQuestRecord = resolution["quest"]
|
||||
if resolution["completed"]:
|
||||
player_quest_completed.emit(quest)
|
||||
player_standing_changed.emit(player_quest_system.standing)
|
||||
else:
|
||||
player_quest_expired.emit(quest)
|
||||
_maintain_event_knowledge(false)
|
||||
|
||||
|
||||
@@ -816,6 +756,22 @@ func get_active_opportunity() -> OpportunityStateRecord:
|
||||
return opportunity_system.get_open_opportunity()
|
||||
|
||||
|
||||
func get_active_player_quest() -> PlayerQuestRecord:
|
||||
return player_quest_system.get_active_quest()
|
||||
|
||||
|
||||
func get_player_quests() -> Array[PlayerQuestRecord]:
|
||||
return player_quest_system.get_all_sorted()
|
||||
|
||||
|
||||
func get_latest_player_quest_for_requester(requester_npc_id: int) -> PlayerQuestRecord:
|
||||
return player_quest_system.get_latest_for_requester(requester_npc_id)
|
||||
|
||||
|
||||
func get_player_standing() -> PlayerStandingRecord:
|
||||
return player_quest_system.standing
|
||||
|
||||
|
||||
func get_active_opportunity_helper() -> OpportunityHelperResult:
|
||||
var active := get_active_opportunity()
|
||||
if active == null:
|
||||
@@ -1015,7 +971,7 @@ func harvest_resource_node(node: ResourceNode) -> float:
|
||||
if node.interaction_point != null
|
||||
else node.global_position
|
||||
)
|
||||
_record_economic_event_at(
|
||||
event_recorder.record_economic_at(
|
||||
SimulationIds.EVENT_RESOURCE_EXTRACTED,
|
||||
-1,
|
||||
resource_state.get_node_id(),
|
||||
@@ -1033,7 +989,7 @@ func harvest_resource_node(node: ResourceNode) -> float:
|
||||
event_position
|
||||
)
|
||||
if resource_state.get_amount_remaining() <= 0.0:
|
||||
_record_narrative_event_at(
|
||||
event_recorder.record_narrative_at(
|
||||
SimulationIds.EVENT_RESOURCE_DEPLETED,
|
||||
-1,
|
||||
resource_state.get_node_id(),
|
||||
@@ -1101,6 +1057,7 @@ func create_state_record() -> SimulationStateRecord:
|
||||
"wander_random_streams": wander_streams,
|
||||
"next_event_id": next_event_id,
|
||||
"next_opportunity_id": opportunity_system.next_opportunity_id,
|
||||
"next_quest_id": player_quest_system.next_quest_id,
|
||||
"cycle_duration_seconds": clock.cycle_duration_seconds
|
||||
}
|
||||
record.village = VillageStateRecord.capture(village)
|
||||
@@ -1125,6 +1082,9 @@ func create_state_record() -> SimulationStateRecord:
|
||||
record.event_knowledge.append(known_event)
|
||||
for opportunity in opportunity_system.get_all_sorted():
|
||||
record.opportunities.append(opportunity)
|
||||
record.player_standing = player_quest_system.standing
|
||||
for quest in player_quest_system.get_all_sorted():
|
||||
record.player_quests.append(quest)
|
||||
return record
|
||||
|
||||
|
||||
@@ -1162,6 +1122,9 @@ func restore_state(record: SimulationStateRecord) -> bool:
|
||||
relationship_system.restore(record.relationships)
|
||||
event_knowledge_system.restore(record.event_knowledge)
|
||||
opportunity_system.restore(record.opportunities, int(record.simulation["next_opportunity_id"]))
|
||||
player_quest_system.restore(
|
||||
record.player_quests, int(record.simulation.get("next_quest_id", 0)), record.player_standing
|
||||
)
|
||||
_maintain_event_knowledge(tick_count % get_knowledge_review_interval() == 0)
|
||||
wander_random_sources.clear()
|
||||
var wander_streams: Array = record.simulation["wander_random_streams"]
|
||||
|
||||
Reference in New Issue
Block a user