feat: add witnessed knowledge and wind ambience

This commit is contained in:
Rijad Zuzo
2026-07-11 11:27:40 +02:00
parent 2ed93de6d1
commit 81409650df
72 changed files with 3001 additions and 606 deletions
+178 -8
View File
@@ -14,6 +14,9 @@ func _run() -> void:
_test_legacy_resource_migration()
_test_legacy_world_storage_migration()
_test_previous_world_event_migration()
_test_previous_world_relationship_migration()
_test_previous_world_knowledge_migration()
_test_relationship_schema_rejection()
_test_schema_rejection()
if failures.is_empty():
@@ -157,8 +160,10 @@ func _test_legacy_resource_migration() -> void:
"Resource migration should preserve mutable authority"
)
_check(
is_equal_approx(migrated.get_safety_risk(), 0.0)
and is_equal_approx(migrated.get_comfort_distance(), 18.0),
(
is_equal_approx(migrated.get_safety_risk(), 0.0)
and is_equal_approx(migrated.get_comfort_distance(), 18.0)
),
"Resource migration should default discovery metadata"
)
@@ -190,9 +195,7 @@ func _test_legacy_world_storage_migration() -> void:
var migrated_storages := {}
for storage in migrated.storages:
migrated_storages[storage.get_storage_id()] = storage
var pantry: StorageStateRecord = migrated_storages.get(
SimulationIds.STORAGE_VILLAGE_PANTRY
)
var pantry: StorageStateRecord = migrated_storages.get(SimulationIds.STORAGE_VILLAGE_PANTRY)
var woodpile: StorageStateRecord = migrated_storages.get(
SimulationIds.STORAGE_VILLAGE_WOODPILE
)
@@ -200,8 +203,12 @@ func _test_legacy_world_storage_migration() -> void:
(
pantry != null
and woodpile != null
and is_equal_approx(pantry.get_amount(SimulationIds.RESOURCE_FOOD), manager.village.food)
and is_equal_approx(woodpile.get_amount(SimulationIds.RESOURCE_WOOD), manager.village.wood)
and is_equal_approx(
pantry.get_amount(SimulationIds.RESOURCE_FOOD), manager.village.food
)
and is_equal_approx(
woodpile.get_amount(SimulationIds.RESOURCE_WOOD), manager.village.wood
)
),
"World migration should preserve legacy village resources in their storage"
)
@@ -211,7 +218,7 @@ func _test_legacy_world_storage_migration() -> void:
func _test_previous_world_event_migration() -> void:
var manager := _create_manager(56)
var previous_data: Dictionary = manager.create_state_record().to_dictionary()
previous_data["schema_version"] = (SimulationStateRecord.PREVIOUS_SCHEMA_VERSION)
previous_data["schema_version"] = SimulationStateRecord.EVENT_LEGACY_SCHEMA_VERSION
previous_data.erase("economic_events")
previous_data["simulation"].erase("next_event_id")
var migrated := SimulationStateRecord.from_dictionary(previous_data)
@@ -224,6 +231,169 @@ func _test_previous_world_event_migration() -> void:
manager.free()
func _test_previous_world_relationship_migration() -> void:
var manager := _create_manager(57)
var previous_data: Dictionary = manager.create_state_record().to_dictionary()
previous_data["schema_version"] = SimulationStateRecord.RELATIONSHIP_LEGACY_SCHEMA_VERSION
previous_data.erase("relationships")
previous_data.erase("event_knowledge")
for npc_data in previous_data["npcs"]:
npc_data["schema_version"] = NPCStateRecord.RELATIONSHIP_LEGACY_SCHEMA_VERSION
npc_data["familiarity"] = []
previous_data["npcs"][0]["familiarity"] = [{"id": 1, "score": 0.75}]
var migrated := SimulationStateRecord.from_dictionary(previous_data)
_check(migrated != null, "World schema v3 should migrate NPC familiarity into relationships")
if migrated != null:
_check(
(
migrated.relationships.size() == 1
and migrated.relationships[0].get_observer_id() == 0
and migrated.relationships[0].get_subject_id() == 1
and is_equal_approx(migrated.relationships[0].get_familiarity(), 0.75)
and is_equal_approx(
migrated.relationships[0].get_trust(), RelationshipStateRecord.NEUTRAL_TRUST
)
),
"World v3 migration should preserve familiarity and initialize neutral trust"
)
_check(
not migrated.npcs[0].data.has("familiarity"),
"World v3 migration should remove obsolete NPC-local familiarity data"
)
manager.free()
func _test_previous_world_knowledge_migration() -> void:
var manager := _create_manager(59)
var previous_data: Dictionary = manager.create_state_record().to_dictionary()
previous_data["schema_version"] = SimulationStateRecord.PREVIOUS_SCHEMA_VERSION
previous_data.erase("event_knowledge")
var deposit_event := EconomicEventRecord.create(
0,
SimulationIds.EVENT_STORAGE_DEPOSITED,
12,
1,
SimulationIds.npc_inventory_id(1),
SimulationIds.STORAGE_VILLAGE_PANTRY,
SimulationIds.RESOURCE_FOOD,
2.0
)
previous_data["economic_events"] = [deposit_event.to_dictionary()]
previous_data["simulation"]["next_event_id"] = 1
previous_data["relationships"] = [
RelationshipStateRecord.create(0, 1, 0.5, 0.65, 0).to_dictionary()
]
var migrated := SimulationStateRecord.from_dictionary(previous_data)
_check(migrated != null, "World schema v4 should migrate causal facts into NPC knowledge")
if migrated != null:
_check(
(
migrated.event_knowledge.size() == 1
and migrated.event_knowledge[0].get_knower_id() == 0
and migrated.event_knowledge[0].get_event_id() == 0
),
"World v4 migration should preserve the fact implied by a relationship cause"
)
manager.free()
func _test_relationship_schema_rejection() -> void:
var manager := _create_manager(58)
var missing_cause_data: Dictionary = manager.create_state_record().to_dictionary()
missing_cause_data["relationships"] = [
RelationshipStateRecord.create(0, 1, 0.5, 0.65, 999).to_dictionary()
]
_check(
SimulationStateRecord.from_dictionary(missing_cause_data) == null,
"Relationship causes must reference an event in the same world record"
)
var duplicate_pair_data: Dictionary = manager.create_state_record().to_dictionary()
var relationship_data := RelationshipStateRecord.create(0, 1, 0.5).to_dictionary()
duplicate_pair_data["relationships"] = [relationship_data, relationship_data.duplicate(true)]
_check(
SimulationStateRecord.from_dictionary(duplicate_pair_data) == null,
"World state should reject duplicate directed relationship pairs"
)
var unknown_knowledge_data: Dictionary = manager.create_state_record().to_dictionary()
unknown_knowledge_data["event_knowledge"] = [
KnownEventStateRecord.create(0, 999).to_dictionary()
]
_check(
SimulationStateRecord.from_dictionary(unknown_knowledge_data) == null,
"Known-event records must reference an event in the same world record"
)
var duplicate_knowledge_data: Dictionary = manager.create_state_record().to_dictionary()
var event := EconomicEventRecord.create(
0,
SimulationIds.EVENT_STORAGE_DEPOSITED,
1,
0,
SimulationIds.npc_inventory_id(0),
SimulationIds.STORAGE_VILLAGE_PANTRY,
SimulationIds.RESOURCE_FOOD,
1.0
)
duplicate_knowledge_data["economic_events"] = [event.to_dictionary()]
duplicate_knowledge_data["simulation"]["next_event_id"] = 1
var known_event_data := KnownEventStateRecord.create(0, 0).to_dictionary()
duplicate_knowledge_data["event_knowledge"] = [
known_event_data, known_event_data.duplicate(true)
]
_check(
SimulationStateRecord.from_dictionary(duplicate_knowledge_data) == null,
"World state should reject duplicate NPC knowledge pairs"
)
var wrong_subject_cause_data: Dictionary = manager.create_state_record().to_dictionary()
var unrelated_deposit := EconomicEventRecord.create(
0,
SimulationIds.EVENT_STORAGE_DEPOSITED,
1,
2,
SimulationIds.npc_inventory_id(2),
SimulationIds.STORAGE_VILLAGE_PANTRY,
SimulationIds.RESOURCE_FOOD,
1.0
)
wrong_subject_cause_data["economic_events"] = [unrelated_deposit.to_dictionary()]
wrong_subject_cause_data["simulation"]["next_event_id"] = 1
wrong_subject_cause_data["event_knowledge"] = [
KnownEventStateRecord.create(0, 0).to_dictionary()
]
wrong_subject_cause_data["relationships"] = [
RelationshipStateRecord.create(0, 1, 0.5, 0.65, 0).to_dictionary()
]
_check(
SimulationStateRecord.from_dictionary(wrong_subject_cause_data) == null,
"A trust cause must be a known food deposit performed by the relationship subject"
)
var nonpositive_cause_data: Dictionary = manager.create_state_record().to_dictionary()
var nonpositive_deposit := EconomicEventRecord.create(
0,
SimulationIds.EVENT_STORAGE_DEPOSITED,
1,
1,
SimulationIds.npc_inventory_id(1),
SimulationIds.STORAGE_VILLAGE_PANTRY,
SimulationIds.RESOURCE_FOOD,
0.0
)
nonpositive_cause_data["economic_events"] = [nonpositive_deposit.to_dictionary()]
nonpositive_cause_data["simulation"]["next_event_id"] = 1
nonpositive_cause_data["event_knowledge"] = [KnownEventStateRecord.create(0, 0).to_dictionary()]
nonpositive_cause_data["relationships"] = [
RelationshipStateRecord.create(0, 1, 0.5, 0.65, 0).to_dictionary()
]
_check(
SimulationStateRecord.from_dictionary(nonpositive_cause_data) == null,
"A trust cause must describe a successful positive food deposit"
)
manager.free()
func _create_manager(seed_value: int) -> Node:
var manager: Node = load("res://simulation/SimulationManager.gd").new()
manager.simulation_seed = seed_value