extends GutTest const EventKnowledgeSystemScript := preload("res://simulation/knowledge/EventKnowledgeSystem.gd") const RelationshipSystemScript := preload("res://simulation/relationships/RelationshipSystem.gd") func test_known_event_v3_migrates_metadata_and_player_is_a_valid_knower() -> void: var legacy_data := { "schema_version": KnownEventStateRecord.LEGACY_SCHEMA_VERSION, "knower_id": 2, "event_id": 12, "acquisition_method": String(SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED), "source_npc_id": 1, "acquired_tick": 30, "source_acquisition_method": String(SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED), } var migrated := KnownEventStateRecord.from_dictionary(legacy_data) assert_not_null(migrated) assert_eq(int(migrated.to_dictionary()["schema_version"]), KnownEventStateRecord.SCHEMA_VERSION) assert_eq(migrated.get_source_actor_id(), 1) assert_eq(migrated.get_hop_count(), 1) assert_eq(migrated.get_confidence(), KnownEventStateRecord.DEFAULT_CONFIDENCE) assert_eq(migrated.get_salience(), KnownEventStateRecord.DEFAULT_SALIENCE) assert_false(migrated.is_pinned()) var player_record := KnownEventStateRecord.create( SimulationIds.PLAYER_ACTOR_ID, 13, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, KnownEventStateRecord.NO_SOURCE_NPC_ID, 31 ) assert_not_null(KnownEventStateRecord.from_dictionary(player_record.to_dictionary())) var invalid_knower_data := player_record.to_dictionary() invalid_knower_data["knower_id"] = KnownEventStateRecord.NO_SOURCE_ACTOR_ID assert_null(KnownEventStateRecord.from_dictionary(invalid_knower_data)) func test_direct_npc_knowledge_can_be_communicated_explicitly_to_player() -> void: var event := EconomicEventRecord.create( 21, SimulationIds.EVENT_STORAGE_DEPOSITED, 40, 0, SimulationIds.npc_inventory_id(0), SimulationIds.STORAGE_VILLAGE_PANTRY, SimulationIds.RESOURCE_FOOD, 2.0, Vector3.ZERO ) var speaker_record := KnownEventStateRecord.create( 1, int(event.data["event_id"]), SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, KnownEventStateRecord.NO_SOURCE_NPC_ID, 40 ) var knowledge_system := EventKnowledgeSystemScript.new() var records: Array[KnownEventStateRecord] = [speaker_record] knowledge_system.restore(records) assert_false( knowledge_system.knows_event(SimulationIds.PLAYER_ACTOR_ID, int(event.data["event_id"])) ) var communicated := knowledge_system.communicate_event_to_player(event, 1, 45) assert_not_null(communicated) assert_eq(communicated.get_knower_id(), SimulationIds.PLAYER_ACTOR_ID) assert_eq(communicated.get_source_actor_id(), 1) assert_eq(communicated.get_source_npc_id(), 1) assert_eq(communicated.get_hop_count(), 1) assert_true(is_equal_approx(communicated.get_confidence(), 0.9)) assert_eq(communicated.get_source_acquisition_method(), speaker_record.get_acquisition_method()) assert_null(knowledge_system.communicate_event(event, SimulationIds.PLAYER_ACTOR_ID, 2, 46)) func test_player_performed_world_event_is_known_without_a_witness() -> void: var event := EconomicEventRecord.create( 22, SimulationIds.EVENT_STORAGE_DEPOSITED, 41, SimulationIds.PLAYER_ACTOR_ID, SimulationIds.PLAYER_INVENTORY_ID, SimulationIds.STORAGE_VILLAGE_PANTRY, SimulationIds.RESOURCE_FOOD, 1.0, Vector3(1000.0, 0.0, 1000.0) ) var knowledge_system := EventKnowledgeSystemScript.new() var learned := knowledge_system.observe_event(event, []) assert_eq(learned.size(), 1) assert_eq(learned[0].get_knower_id(), SimulationIds.PLAYER_ACTOR_ID) assert_eq(learned[0].get_acquisition_method(), SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED) func test_pinned_knowledge_is_bounded_and_survives_ordinary_retention() -> void: var knowledge_system := EventKnowledgeSystemScript.new() var records: Array[KnownEventStateRecord] = [] for event_id in range(1, 6): records.append( KnownEventStateRecord.create( 0, event_id, SimulationIds.KNOWLEDGE_ACQUISITION_WITNESSED, KnownEventStateRecord.NO_SOURCE_NPC_ID, event_id ) ) knowledge_system.restore(records) assert_true(knowledge_system.pin_event(0, 1)) var no_lasting: Array[KnownEventStateRecord] = [] var forgotten := knowledge_system.maintain_retention(5, 100, no_lasting, false) assert_eq(forgotten.size(), 1) assert_eq(forgotten[0].get_event_id(), 2) assert_eq(knowledge_system.get_pinned_event_ids(0), [1]) assert_eq(knowledge_system.get_retained_event_ids(0, [], 0), [1, 5, 4, 3]) assert_true(knowledge_system.set_event_salience(0, 3, 2.0)) assert_eq(knowledge_system.get_record(0, 3).get_salience(), 1.0) assert_true(knowledge_system.set_event_confidence(0, 3, -1.0)) assert_eq(knowledge_system.get_record(0, 3).get_confidence(), 0.0) var pin_limit_system := EventKnowledgeSystemScript.new() var pin_limit_records: Array[KnownEventStateRecord] = [] for event_id in range(1, 6): pin_limit_records.append(KnownEventStateRecord.create(0, event_id)) pin_limit_system.restore(pin_limit_records) for event_id in range(1, EventKnowledgeSystemScript.MAX_PINNED_FACTS_PER_KNOWER + 1): assert_true(pin_limit_system.pin_event(0, event_id)) assert_false( pin_limit_system.pin_event(0, EventKnowledgeSystemScript.MAX_PINNED_FACTS_PER_KNOWER + 1) ) func test_relationship_v1_migrates_all_dimensions_and_exact_causes() -> void: var legacy_data := { "schema_version": RelationshipStateRecord.LEGACY_SCHEMA_VERSION, "observer_id": 1, "subject_id": 2, "familiarity": 0.4, "trust": 0.6, "last_trust_cause_event_id": 8, } var relationship := RelationshipStateRecord.from_dictionary(legacy_data) assert_not_null(relationship) assert_eq( int(relationship.to_dictionary()["schema_version"]), RelationshipStateRecord.SCHEMA_VERSION ) assert_eq(relationship.get_affection(), 0.0) assert_eq(relationship.get_respect(), 0.0) assert_eq(relationship.get_fear(), 0.0) assert_eq(relationship.get_obligation(), 0.0) assert_eq(relationship.get_hostility(), 0.0) assert_eq(relationship.get_last_trust_cause_event_id(), 8) for index in range(RelationshipStateRecord.DIMENSIONS.size()): var dimension: StringName = RelationshipStateRecord.DIMENSIONS[index] var cause_event_id := 20 + index assert_true(relationship.adjust_dimension(dimension, 0.1, cause_event_id) > 0.0) assert_eq(relationship.get_last_cause_event_id(dimension), cause_event_id) assert_true(relationship.adjust_hostility(2.0, 99) > 0.0) assert_eq(relationship.get_hostility(), 1.0) assert_eq(relationship.get_last_hostility_cause_event_id(), 99) assert_not_null(RelationshipStateRecord.from_dictionary(relationship.to_dictionary())) var player_observer := RelationshipStateRecord.create(SimulationIds.PLAYER_ACTOR_ID, 3, 0.2) assert_not_null(RelationshipStateRecord.from_dictionary(player_observer.to_dictionary())) func test_sparse_relationship_queries_are_deterministic_and_bounded() -> void: var relationship_system := RelationshipSystemScript.new() for subject_id in range(10): var relationship := relationship_system.get_or_create_relationship( SimulationIds.PLAYER_ACTOR_ID, subject_id, float(subject_id) / 10.0 ) assert_not_null(relationship) assert_true( ( relationship_system.adjust_relationship_dimension( SimulationIds.PLAYER_ACTOR_ID, 0, RelationshipStateRecord.DIMENSION_RESPECT, 1.0, 77 ) > 0.0 ) ) var sorted := relationship_system.get_relationships_for_observer(SimulationIds.PLAYER_ACTOR_ID) assert_eq(sorted.size(), 10) assert_eq(sorted[0].get_subject_id(), 0) assert_eq(sorted[9].get_subject_id(), 9) var salient := relationship_system.get_salient_relationships(SimulationIds.PLAYER_ACTOR_ID, 100) assert_eq(salient.size(), RelationshipSystemScript.MAX_BOUNDED_RELATIONSHIPS_PER_QUERY) assert_eq(salient[0].get_subject_id(), 0) assert_eq( relationship_system.get_relationship_cause_event_id( SimulationIds.PLAYER_ACTOR_ID, 0, RelationshipStateRecord.DIMENSION_RESPECT ), 77 )