feat: add provenance-aware npc communication
This commit is contained in:
@@ -16,6 +16,7 @@ signal state_restored
|
||||
signal npc_decision_recorded(npc: SimNPC, decision: ActionSelectionResult)
|
||||
signal relationship_changed(relationship: RelationshipStateRecord, cause_event: EconomicEventRecord)
|
||||
signal event_knowledge_changed(knower_id: int, event: EconomicEventRecord)
|
||||
signal event_knowledge_transferred(speaker_id: int, listener_id: int, event: EconomicEventRecord)
|
||||
|
||||
var village := SimVillage.new()
|
||||
|
||||
@@ -52,6 +53,7 @@ var action_executor := ActionExecutionSystem.new()
|
||||
var target_resolver := ActionTargetResolver.new()
|
||||
var speed_index := 2
|
||||
const SPEED_LEVELS := [0.25, 0.5, 1.0, 2.0, 4.0, 10.0]
|
||||
const KNOWLEDGE_COMMUNICATION_RADIUS := 2.5
|
||||
signal speed_changed(multiplier: float)
|
||||
|
||||
const NPC_NAMES := ["Amina", "Tarik", "Jasmin", "Elma", "Mirza", "Lejla"]
|
||||
@@ -384,18 +386,24 @@ func notify_npc_arrived(npc_id: int) -> void:
|
||||
npc.has_travel_target = false
|
||||
npc.start_working()
|
||||
|
||||
var working_speakers: Array[SimNPC] = []
|
||||
if npc.target_id != &"":
|
||||
for other in npcs:
|
||||
if other.id == npc.id or other.is_dead:
|
||||
continue
|
||||
if other.target_id != npc.target_id:
|
||||
continue
|
||||
if (
|
||||
other.target_id == npc.target_id
|
||||
and (
|
||||
other.task_state
|
||||
in [SimNPC.TASK_STATE_TRAVELING, SimNPC.TASK_STATE_WORKING]
|
||||
)
|
||||
other.task_state
|
||||
in [SimNPC.TASK_STATE_TRAVELING, SimNPC.TASK_STATE_WORKING]
|
||||
):
|
||||
relationship_system.increase_shared_work_familiarity(npc.id, other.id)
|
||||
if other.task_state == SimNPC.TASK_STATE_WORKING:
|
||||
working_speakers.append(other)
|
||||
working_speakers.sort_custom(_sort_npcs_by_id)
|
||||
for speaker in working_speakers:
|
||||
if try_communicate_at_shared_activity(speaker.id, npc.id):
|
||||
break
|
||||
|
||||
if debug_logs:
|
||||
print(
|
||||
@@ -583,14 +591,81 @@ func _on_economic_event_recorded(event: EconomicEventRecord) -> void:
|
||||
event, npcs
|
||||
)
|
||||
for learned_record in learned_records:
|
||||
event_knowledge_changed.emit(learned_record.get_knower_id(), event)
|
||||
var knower_ids: Array[int] = event_knowledge_system.get_knowers(int(event.data["event_id"]))
|
||||
_apply_new_event_knowledge(learned_record, event)
|
||||
economic_event_recorded.emit(event)
|
||||
|
||||
|
||||
func try_communicate_at_shared_activity(speaker_id: int, listener_id: int) -> bool:
|
||||
var speaker := _find_npc_by_id(speaker_id)
|
||||
var listener := _find_npc_by_id(listener_id)
|
||||
if not _can_communicate_at_shared_activity(speaker, listener):
|
||||
return false
|
||||
var known_event_ids := event_knowledge_system.get_known_event_ids(speaker.id, 0)
|
||||
for index in range(known_event_ids.size() - 1, -1, -1):
|
||||
var event := event_log.get_by_id(known_event_ids[index])
|
||||
if event == null:
|
||||
continue
|
||||
var learned_record := event_knowledge_system.communicate_event(
|
||||
event, speaker.id, listener.id
|
||||
)
|
||||
if learned_record == null:
|
||||
continue
|
||||
_apply_new_event_knowledge(learned_record, event)
|
||||
event_knowledge_transferred.emit(speaker.id, listener.id, event)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _can_communicate_at_shared_activity(speaker: SimNPC, listener: SimNPC) -> bool:
|
||||
if speaker == null or listener == null or speaker.id == listener.id:
|
||||
return false
|
||||
if speaker.is_dead or listener.is_dead:
|
||||
return false
|
||||
if (
|
||||
speaker.task_state != SimNPC.TASK_STATE_WORKING
|
||||
or listener.task_state != SimNPC.TASK_STATE_WORKING
|
||||
):
|
||||
return false
|
||||
if speaker.target_id.is_empty() or speaker.target_id != listener.target_id:
|
||||
return false
|
||||
if storage_states.has(speaker.target_id):
|
||||
return false
|
||||
var speaker_action := SimulationDefinitions.get_action(speaker.current_task)
|
||||
var listener_action := SimulationDefinitions.get_action(listener.current_task)
|
||||
if (
|
||||
speaker_action == null
|
||||
or listener_action == null
|
||||
or speaker_action.target_type != SimulationIds.TARGET_ACTIVITY
|
||||
or listener_action.target_type != SimulationIds.TARGET_ACTIVITY
|
||||
):
|
||||
return false
|
||||
return (
|
||||
speaker.position.distance_squared_to(listener.position)
|
||||
<= KNOWLEDGE_COMMUNICATION_RADIUS * KNOWLEDGE_COMMUNICATION_RADIUS
|
||||
)
|
||||
|
||||
|
||||
func _apply_new_event_knowledge(
|
||||
known_event: KnownEventStateRecord, event: EconomicEventRecord
|
||||
) -> void:
|
||||
event_knowledge_changed.emit(known_event.get_knower_id(), event)
|
||||
var newly_informed_ids: Array[int] = [known_event.get_knower_id()]
|
||||
var changed_relationships: Array[RelationshipStateRecord] = relationship_system.apply_event(
|
||||
event, npcs, knower_ids
|
||||
event, npcs, newly_informed_ids
|
||||
)
|
||||
for relationship in changed_relationships:
|
||||
relationship_changed.emit(relationship, event)
|
||||
economic_event_recorded.emit(event)
|
||||
|
||||
|
||||
func _find_npc_by_id(npc_id: int) -> SimNPC:
|
||||
for npc in npcs:
|
||||
if npc.id == npc_id:
|
||||
return npc
|
||||
return null
|
||||
|
||||
|
||||
static func _sort_npcs_by_id(first: SimNPC, second: SimNPC) -> bool:
|
||||
return first.id < second.id
|
||||
|
||||
|
||||
func get_primary_relationship(npc_id: int) -> RelationshipStateRecord:
|
||||
@@ -616,6 +691,10 @@ func npc_knows_event(npc_id: int, event_id: int) -> bool:
|
||||
return event_knowledge_system.knows_event(npc_id, event_id)
|
||||
|
||||
|
||||
func get_known_event_record(npc_id: int, event_id: int) -> KnownEventStateRecord:
|
||||
return event_knowledge_system.get_record(npc_id, event_id)
|
||||
|
||||
|
||||
func get_current_speed() -> String:
|
||||
return "%.2fx" % SPEED_LEVELS[speed_index]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user