feat: generalize social memory and relationships

This commit is contained in:
Rijad Zuzo
2026-08-12 20:23:17 +02:00
parent dd0fc7542c
commit ed7236257a
5 changed files with 726 additions and 71 deletions
+94 -8
View File
@@ -2,7 +2,10 @@ class_name EventKnowledgeSystem
extends RefCounted
const WITNESS_RADIUS := 10.0
const MAX_RECENT_FACTS_PER_NPC := 3
const MAX_RECENT_FACTS_PER_KNOWER := 3
const MAX_RECENT_FACTS_PER_NPC := MAX_RECENT_FACTS_PER_KNOWER
const MAX_PINNED_FACTS_PER_KNOWER := 4
const COMMUNICATED_CONFIDENCE_FACTOR := 0.9
var known_events: Dictionary = {}
@@ -18,7 +21,16 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
return learned
var event_id := int(event.data["event_id"])
var acquired_tick := int(event.data["tick"])
if actor != null:
if is_player_actor:
var player_record := _remember(
SimulationIds.PLAYER_ACTOR_ID,
event_id,
SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED,
acquired_tick
)
if player_record != null:
learned.append(player_record)
elif actor != null:
var actor_record := _remember(
actor.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED, acquired_tick
)
@@ -47,8 +59,11 @@ func communicate_event(
) -> KnownEventStateRecord:
if (
event == null
or not KnownEventStateRecord.is_valid_actor_id(speaker_id)
or not KnownEventStateRecord.is_valid_actor_id(listener_id)
or speaker_id == listener_id
or listener_id == int(event.data["actor_id"])
or acquired_tick < 0
or not is_knowable_event(event)
):
return null
@@ -70,10 +85,19 @@ func communicate_event(
SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED,
acquired_tick,
speaker_id,
speaker_record.get_acquisition_method()
speaker_record.get_acquisition_method(),
speaker_record.get_hop_count() + 1,
speaker_record.get_confidence() * COMMUNICATED_CONFIDENCE_FACTOR,
speaker_record.get_salience()
)
func communicate_event_to_player(
event: EconomicEventRecord, speaker_id: int, acquired_tick: int
) -> KnownEventStateRecord:
return communicate_event(event, speaker_id, SimulationIds.PLAYER_ACTOR_ID, acquired_tick)
func knows_event(knower_id: int, event_id: int) -> bool:
return known_events.has(_key(knower_id, event_id))
@@ -82,6 +106,37 @@ func get_record(knower_id: int, event_id: int) -> KnownEventStateRecord:
return known_events.get(_key(knower_id, event_id)) as KnownEventStateRecord
func pin_event(knower_id: int, event_id: int, pinned: bool = true) -> bool:
var record := get_record(knower_id, event_id)
if record == null or record.is_pinned() == pinned:
return false
if pinned and _get_pinned_count(knower_id) >= MAX_PINNED_FACTS_PER_KNOWER:
return false
return record.set_pinned(pinned)
func set_event_confidence(knower_id: int, event_id: int, confidence: float) -> bool:
var record := get_record(knower_id, event_id)
return record != null and record.set_confidence(confidence)
func set_event_salience(knower_id: int, event_id: int, salience: float) -> bool:
var record := get_record(knower_id, event_id)
return record != null and record.set_salience(salience)
func get_pinned_event_ids(knower_id: int) -> Array[int]:
var records: Array[KnownEventStateRecord] = []
for record in known_events.values():
if record.get_knower_id() == knower_id and record.is_pinned():
records.append(record)
records.sort_custom(_sort_by_acquisition)
var event_ids: Array[int] = []
for record in records:
event_ids.append(record.get_event_id())
return event_ids
func get_knowers(event_id: int) -> Array[int]:
var knower_ids: Array[int] = []
for record in known_events.values():
@@ -146,6 +201,9 @@ func _get_ranked_event_ids(
"event_id": record.get_event_id(),
"acquired_tick": record.get_acquired_tick(),
"lasting": record.get_event_id() in lasting_event_ids,
"pinned": record.is_pinned(),
"confidence": record.get_confidence(),
"salience": record.get_salience(),
}
)
)
@@ -171,7 +229,7 @@ func maintain_retention(
var recent_by_knower := {}
for record in known_events.values():
var key := _key(record.get_knower_id(), record.get_event_id())
if lasting_keys.has(key):
if lasting_keys.has(key) or record.is_pinned():
continue
if expire_by_age and current_tick - record.get_acquired_tick() >= max_recent_age:
removal_keys[key] = true
@@ -181,7 +239,7 @@ func maintain_retention(
recent_by_knower[record.get_knower_id()] = recent
for recent in recent_by_knower.values():
recent.sort_custom(_sort_by_acquisition)
var excess: int = recent.size() - MAX_RECENT_FACTS_PER_NPC
var excess: int = recent.size() - MAX_RECENT_FACTS_PER_KNOWER
for index in range(maxi(excess, 0)):
var record := recent[index] as KnownEventStateRecord
removal_keys[_key(record.get_knower_id(), record.get_event_id())] = true
@@ -214,24 +272,46 @@ func _remember(
event_id: int,
acquisition_method: StringName,
acquired_tick: int,
source_npc_id: int = KnownEventStateRecord.NO_SOURCE_NPC_ID,
source_acquisition_method: StringName = &""
source_actor_id: int = KnownEventStateRecord.NO_SOURCE_ACTOR_ID,
source_acquisition_method: StringName = &"",
hop_count: int = KnownEventStateRecord.AUTO_HOP_COUNT,
confidence: float = KnownEventStateRecord.DEFAULT_CONFIDENCE,
salience: float = KnownEventStateRecord.DEFAULT_SALIENCE,
pinned: bool = false
) -> KnownEventStateRecord:
if not KnownEventStateRecord.is_valid_actor_id(knower_id) or event_id < 0 or acquired_tick < 0:
return null
var key := _key(knower_id, event_id)
if known_events.has(key):
return null
var source_npc_id := (
source_actor_id if source_actor_id >= 0 else KnownEventStateRecord.NO_SOURCE_NPC_ID
)
var record := KnownEventStateRecord.create(
knower_id,
event_id,
acquisition_method,
source_npc_id,
acquired_tick,
source_acquisition_method
source_acquisition_method,
hop_count,
confidence,
salience,
source_actor_id,
pinned
)
known_events[key] = record
return record
func _get_pinned_count(knower_id: int) -> int:
var count := 0
for record in known_events.values():
if record.get_knower_id() == knower_id and record.is_pinned():
count += 1
return count
static func is_knowable_event(event: EconomicEventRecord) -> bool:
if event == null:
return false
@@ -308,8 +388,14 @@ static func _sort_by_acquisition(
static func _sort_by_importance(first: Dictionary, second: Dictionary) -> bool:
if bool(first["pinned"]) != bool(second["pinned"]):
return bool(first["pinned"])
if bool(first["lasting"]) != bool(second["lasting"]):
return bool(first["lasting"])
if not is_equal_approx(float(first["salience"]), float(second["salience"])):
return float(first["salience"]) > float(second["salience"])
if not is_equal_approx(float(first["confidence"]), float(second["confidence"])):
return float(first["confidence"]) > float(second["confidence"])
if int(first["acquired_tick"]) != int(second["acquired_tick"]):
return int(first["acquired_tick"]) > int(second["acquired_tick"])
return int(first["event_id"]) > int(second["event_id"])