feat: add provenance-aware npc communication

This commit is contained in:
Rijad Zuzo
2026-07-11 19:58:47 +02:00
parent 81409650df
commit 9a479ba182
18 changed files with 938 additions and 70 deletions
+112 -3
View File
@@ -2,11 +2,12 @@ class_name SimulationStateRecord
extends RefCounted
const SCHEMA_NAME := "the_steward.simulation"
const SCHEMA_VERSION := 5
const SCHEMA_VERSION := 6
const LEGACY_SCHEMA_VERSION := 1
const EVENT_LEGACY_SCHEMA_VERSION := 2
const RELATIONSHIP_LEGACY_SCHEMA_VERSION := 3
const PREVIOUS_SCHEMA_VERSION := 4
const KNOWLEDGE_LEGACY_SCHEMA_VERSION := 4
const PREVIOUS_SCHEMA_VERSION := 5
var simulation: Dictionary
var village: VillageStateRecord
@@ -74,6 +75,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
LEGACY_SCHEMA_VERSION,
EVENT_LEGACY_SCHEMA_VERSION,
RELATIONSHIP_LEGACY_SCHEMA_VERSION,
KNOWLEDGE_LEGACY_SCHEMA_VERSION,
PREVIOUS_SCHEMA_VERSION,
]
):
@@ -207,6 +209,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
return null
var knowledge_keys := {}
var knowledge_records_by_key := {}
for item in knowledge_data:
if not item is Dictionary:
return null
@@ -221,7 +224,13 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
if knowledge_keys.has(knowledge_key):
return null
knowledge_keys[knowledge_key] = true
knowledge_records_by_key[knowledge_key] = known_event_record
record.event_knowledge.append(known_event_record)
for known_event_record in record.event_knowledge:
if not _is_valid_knowledge_provenance(
known_event_record, npc_ids, event_records_by_id, knowledge_records_by_key
):
return null
var relationship_keys := {}
for item in relationship_data:
@@ -266,6 +275,54 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
return record
static func _is_valid_knowledge_provenance(
known_event: KnownEventStateRecord,
npc_ids: Dictionary,
event_records_by_id: Dictionary,
knowledge_records_by_key: Dictionary
) -> bool:
var event := event_records_by_id.get(known_event.get_event_id()) as EconomicEventRecord
if event == null:
return false
var method := known_event.get_acquisition_method()
var knower_id := known_event.get_knower_id()
var actor_id := int(event.data["actor_id"])
if method == SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY:
return true
if (
StringName(event.data["event_type"]) != SimulationIds.EVENT_STORAGE_DEPOSITED
or StringName(event.data["item_id"]) != SimulationIds.RESOURCE_FOOD
or float(event.data["amount"]) <= 0.0
):
return false
match method:
SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED:
return knower_id == actor_id
SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED:
return knower_id != actor_id
SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED:
if knower_id == actor_id:
return false
var source_npc_id := known_event.get_source_npc_id()
if not npc_ids.has(source_npc_id):
return false
var source_record := (
knowledge_records_by_key.get("%d:%d" % [source_npc_id, known_event.get_event_id()])
as KnownEventStateRecord
)
return (
source_record != null
and (
source_record.get_acquisition_method()
in [
SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED,
SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED,
]
)
)
return false
static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary:
var migrated := legacy_data.duplicate(true)
migrated["schema_version"] = SCHEMA_VERSION
@@ -301,7 +358,21 @@ static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary
in [LEGACY_SCHEMA_VERSION, EVENT_LEGACY_SCHEMA_VERSION, RELATIONSHIP_LEGACY_SCHEMA_VERSION]
):
migrated["relationships"] = _migrate_npc_familiarity(legacy_data.get("npcs", []))
migrated["event_knowledge"] = _migrate_relationship_causes(migrated.get("relationships", []))
if (
version
in [
LEGACY_SCHEMA_VERSION,
EVENT_LEGACY_SCHEMA_VERSION,
RELATIONSHIP_LEGACY_SCHEMA_VERSION,
KNOWLEDGE_LEGACY_SCHEMA_VERSION,
]
):
migrated["event_knowledge"] = _migrate_relationship_causes(
migrated.get("relationships", [])
)
migrated["event_knowledge"] = _migrate_known_event_provenance(
migrated.get("event_knowledge", []), migrated.get("economic_events", [])
)
return migrated
@@ -377,3 +448,41 @@ static func _knowledge_dictionary_sort(first: Dictionary, second: Dictionary) ->
if int(first["knower_id"]) != int(second["knower_id"]):
return int(first["knower_id"]) < int(second["knower_id"])
return int(first["event_id"]) < int(second["event_id"])
static func _migrate_known_event_provenance(knowledge_data: Variant, event_data: Variant) -> Array:
if not knowledge_data is Array:
return []
var actor_by_event_id := {}
if event_data is Array:
for event_item in event_data:
if not event_item is Dictionary:
continue
actor_by_event_id[int(event_item.get("event_id", -1))] = int(
event_item.get("actor_id", -1)
)
var migrated_knowledge: Array = []
var can_sort := true
for item in knowledge_data:
if not item is Dictionary:
migrated_knowledge.append(item)
can_sort = false
continue
if int(item.get("schema_version", -1)) == KnownEventStateRecord.SCHEMA_VERSION:
migrated_knowledge.append(item.duplicate(true))
continue
if int(item.get("schema_version", -1)) != 1 or not item.has_all(["knower_id", "event_id"]):
migrated_knowledge.append(item.duplicate(true))
can_sort = false
continue
var knower_id := int(item["knower_id"])
var event_id := int(item["event_id"])
var acquisition_method := SimulationIds.KNOWLEDGE_ACQUISITION_LEGACY
if int(actor_by_event_id.get(event_id, -2)) == knower_id:
acquisition_method = SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED
migrated_knowledge.append(
KnownEventStateRecord.create(knower_id, event_id, acquisition_method).to_dictionary()
)
if can_sort:
migrated_knowledge.sort_custom(_knowledge_dictionary_sort)
return migrated_knowledge