feat: add bounded npc memory retention
This commit is contained in:
@@ -2,6 +2,7 @@ class_name EventKnowledgeSystem
|
||||
extends RefCounted
|
||||
|
||||
const WITNESS_RADIUS := 10.0
|
||||
const MAX_RECENT_FACTS_PER_NPC := 3
|
||||
|
||||
var known_events: Dictionary = {}
|
||||
|
||||
@@ -14,7 +15,10 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
|
||||
if actor == null:
|
||||
return learned
|
||||
var event_id := int(event.data["event_id"])
|
||||
var actor_record := _remember(actor.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED)
|
||||
var acquired_tick := int(event.data["tick"])
|
||||
var actor_record := _remember(
|
||||
actor.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED, acquired_tick
|
||||
)
|
||||
if actor_record != null:
|
||||
learned.append(actor_record)
|
||||
var witness_radius_squared := WITNESS_RADIUS * WITNESS_RADIUS
|
||||
@@ -25,7 +29,7 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
|
||||
if npc.position.distance_squared_to(event_position) > witness_radius_squared:
|
||||
continue
|
||||
var witness_record := _remember(
|
||||
npc.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED
|
||||
npc.id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, acquired_tick
|
||||
)
|
||||
if witness_record != null:
|
||||
learned.append(witness_record)
|
||||
@@ -34,7 +38,7 @@ func observe_event(event: EconomicEventRecord, npcs: Array[SimNPC]) -> Array[Kno
|
||||
|
||||
|
||||
func communicate_event(
|
||||
event: EconomicEventRecord, speaker_id: int, listener_id: int
|
||||
event: EconomicEventRecord, speaker_id: int, listener_id: int, acquired_tick: int
|
||||
) -> KnownEventStateRecord:
|
||||
if (
|
||||
event == null
|
||||
@@ -56,7 +60,12 @@ func communicate_event(
|
||||
):
|
||||
return null
|
||||
return _remember(
|
||||
listener_id, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED, speaker_id
|
||||
listener_id,
|
||||
event_id,
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED,
|
||||
acquired_tick,
|
||||
speaker_id,
|
||||
speaker_record.get_acquisition_method()
|
||||
)
|
||||
|
||||
|
||||
@@ -78,11 +87,14 @@ func get_knowers(event_id: int) -> Array[int]:
|
||||
|
||||
|
||||
func get_known_event_ids(knower_id: int, max_count: int = 3) -> Array[int]:
|
||||
var event_ids: Array[int] = []
|
||||
var records: Array[KnownEventStateRecord] = []
|
||||
for record in known_events.values():
|
||||
if record.get_knower_id() == knower_id:
|
||||
event_ids.append(record.get_event_id())
|
||||
event_ids.sort()
|
||||
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())
|
||||
if max_count <= 0 or event_ids.size() <= max_count:
|
||||
return event_ids
|
||||
var recent_ids: Array[int] = []
|
||||
@@ -91,6 +103,67 @@ func get_known_event_ids(knower_id: int, max_count: int = 3) -> Array[int]:
|
||||
return recent_ids
|
||||
|
||||
|
||||
func get_communicable_event_ids(knower_id: int, lasting_event_ids: Array[int]) -> Array[int]:
|
||||
var ranked: Array[Dictionary] = []
|
||||
for record in known_events.values():
|
||||
if record.get_knower_id() != knower_id:
|
||||
continue
|
||||
if record.get_acquisition_method() not in KnownEventStateRecord.DIRECT_ACQUISITION_METHODS:
|
||||
continue
|
||||
(
|
||||
ranked
|
||||
. append(
|
||||
{
|
||||
"event_id": record.get_event_id(),
|
||||
"acquired_tick": record.get_acquired_tick(),
|
||||
"lasting": record.get_event_id() in lasting_event_ids,
|
||||
}
|
||||
)
|
||||
)
|
||||
ranked.sort_custom(_sort_communicable)
|
||||
var event_ids: Array[int] = []
|
||||
for item in ranked:
|
||||
event_ids.append(int(item["event_id"]))
|
||||
return event_ids
|
||||
|
||||
|
||||
func maintain_retention(
|
||||
current_tick: int,
|
||||
max_recent_age: int,
|
||||
lasting_records: Array[KnownEventStateRecord],
|
||||
expire_by_age: bool
|
||||
) -> Array[KnownEventStateRecord]:
|
||||
var lasting_keys := {}
|
||||
for record in lasting_records:
|
||||
lasting_keys[_key(record.get_knower_id(), record.get_event_id())] = true
|
||||
var removal_keys := {}
|
||||
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):
|
||||
continue
|
||||
if expire_by_age and current_tick - record.get_acquired_tick() >= max_recent_age:
|
||||
removal_keys[key] = true
|
||||
continue
|
||||
var recent: Array = recent_by_knower.get(record.get_knower_id(), [])
|
||||
recent.append(record)
|
||||
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
|
||||
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
|
||||
var forgotten: Array[KnownEventStateRecord] = []
|
||||
for record in get_all_sorted():
|
||||
var key := _key(record.get_knower_id(), record.get_event_id())
|
||||
if not removal_keys.has(key):
|
||||
continue
|
||||
known_events.erase(key)
|
||||
forgotten.append(record)
|
||||
return forgotten
|
||||
|
||||
|
||||
func get_all_sorted() -> Array[KnownEventStateRecord]:
|
||||
var records: Array[KnownEventStateRecord] = []
|
||||
for record in known_events.values():
|
||||
@@ -109,13 +182,20 @@ func _remember(
|
||||
knower_id: int,
|
||||
event_id: int,
|
||||
acquisition_method: StringName,
|
||||
source_npc_id: int = KnownEventStateRecord.NO_SOURCE_NPC_ID
|
||||
acquired_tick: int,
|
||||
source_npc_id: int = KnownEventStateRecord.NO_SOURCE_NPC_ID,
|
||||
source_acquisition_method: StringName = &""
|
||||
) -> KnownEventStateRecord:
|
||||
var key := _key(knower_id, event_id)
|
||||
if known_events.has(key):
|
||||
return null
|
||||
var record := KnownEventStateRecord.create(
|
||||
knower_id, event_id, acquisition_method, source_npc_id
|
||||
knower_id,
|
||||
event_id,
|
||||
acquisition_method,
|
||||
source_npc_id,
|
||||
acquired_tick,
|
||||
source_acquisition_method
|
||||
)
|
||||
known_events[key] = record
|
||||
return record
|
||||
@@ -144,3 +224,19 @@ static func _sort_records(first: KnownEventStateRecord, second: KnownEventStateR
|
||||
if first.get_knower_id() != second.get_knower_id():
|
||||
return first.get_knower_id() < second.get_knower_id()
|
||||
return first.get_event_id() < second.get_event_id()
|
||||
|
||||
|
||||
static func _sort_by_acquisition(
|
||||
first: KnownEventStateRecord, second: KnownEventStateRecord
|
||||
) -> bool:
|
||||
if first.get_acquired_tick() != second.get_acquired_tick():
|
||||
return first.get_acquired_tick() < second.get_acquired_tick()
|
||||
return first.get_event_id() < second.get_event_id()
|
||||
|
||||
|
||||
static func _sort_communicable(first: Dictionary, second: Dictionary) -> bool:
|
||||
if bool(first["lasting"]) != bool(second["lasting"]):
|
||||
return bool(first["lasting"])
|
||||
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"])
|
||||
|
||||
Reference in New Issue
Block a user