extends RefCounted const AID_HUNGER_THRESHOLD := 80.0 const TRUSTED_HELP_THRESHOLD := 0.6 const FOOD_AID_TRUST_GAIN := 0.15 const SHARED_WORK_FAMILIARITY_GAIN := 0.1 var relationships: Dictionary = {} func initialize_households(npcs: Array[SimNPC]) -> void: relationships.clear() for first_index in range(npcs.size()): for second_index in range(first_index + 1, npcs.size()): var first := npcs[first_index] var second := npcs[second_index] if first.home_position.distance_to(second.home_position) >= 4.0: continue _add(RelationshipStateRecord.create(first.id, second.id, 0.5)) _add(RelationshipStateRecord.create(second.id, first.id, 0.5)) func restore(records: Array[RelationshipStateRecord]) -> void: relationships.clear() for record in records: _add(record) func get_all_sorted() -> Array[RelationshipStateRecord]: var records: Array[RelationshipStateRecord] = [] for record in relationships.values(): records.append(record) records.sort_custom(_sort_relationships) return records func get_relationship(observer_id: int, subject_id: int) -> RelationshipStateRecord: return relationships.get(_key(observer_id, subject_id)) as RelationshipStateRecord func is_trusted_for_help(observer_id: int, subject_id: int) -> bool: var relationship := get_relationship(observer_id, subject_id) return ( relationship != null and relationship.get_familiarity() > 0.0 and relationship.get_trust() >= TRUSTED_HELP_THRESHOLD ) func increase_shared_work_familiarity(first_id: int, second_id: int) -> void: var first_to_second := _get_or_create(first_id, second_id) var second_to_first := _get_or_create(second_id, first_id) first_to_second.increase_familiarity(SHARED_WORK_FAMILIARITY_GAIN) second_to_first.increase_familiarity(SHARED_WORK_FAMILIARITY_GAIN) func apply_event( event: EconomicEventRecord, npcs: Array[SimNPC], knower_ids: Array[int] ) -> Array[RelationshipStateRecord]: var changed: Array[RelationshipStateRecord] = [] if StringName(event.data["event_type"]) != SimulationIds.EVENT_STORAGE_DEPOSITED: return changed if StringName(event.data["item_id"]) != SimulationIds.RESOURCE_FOOD: return changed var contributor_id := int(event.data["actor_id"]) var event_id := int(event.data["event_id"]) if contributor_id < 0 or event_id < 0 or float(event.data["amount"]) <= 0.0: return changed for observer in npcs: if observer.id == contributor_id or observer.is_dead: continue if observer.id not in knower_ids: continue if observer.hunger < AID_HUNGER_THRESHOLD: continue var relationship := get_relationship(observer.id, contributor_id) if relationship == null or relationship.get_familiarity() <= 0.0: continue if event_id <= relationship.get_last_trust_cause_event_id(): continue var applied := relationship.increase_trust(FOOD_AID_TRUST_GAIN, event_id) if applied <= 0.0: continue changed.append(relationship) return changed func get_primary_relationship(observer_id: int) -> RelationshipStateRecord: var best: RelationshipStateRecord for relationship in relationships.values(): if relationship.get_observer_id() != observer_id: continue if best == null or _is_stronger(relationship, best): best = relationship return best func get_most_familiar_living_subject(observer_id: int, npcs: Array[SimNPC]) -> SimNPC: var living_by_id := {} for npc in npcs: if not npc.is_dead: living_by_id[npc.id] = npc var best_relationship: RelationshipStateRecord for relationship in relationships.values(): if relationship.get_observer_id() != observer_id: continue if not living_by_id.has(relationship.get_subject_id()): continue if ( best_relationship == null or relationship.get_familiarity() > best_relationship.get_familiarity() or ( is_equal_approx(relationship.get_familiarity(), best_relationship.get_familiarity()) and relationship.get_subject_id() < best_relationship.get_subject_id() ) ): best_relationship = relationship if best_relationship == null: return null return living_by_id[best_relationship.get_subject_id()] as SimNPC func get_trusted_starving_subject(observer_id: int, npcs: Array[SimNPC]) -> SimNPC: var starving_by_id := {} for npc in npcs: if not npc.is_dead and npc.is_starving: starving_by_id[npc.id] = npc var best_relationship: RelationshipStateRecord for relationship in relationships.values(): if relationship.get_observer_id() != observer_id: continue if relationship.get_trust() < TRUSTED_HELP_THRESHOLD: continue if not starving_by_id.has(relationship.get_subject_id()): continue if ( best_relationship == null or relationship.get_trust() > best_relationship.get_trust() or ( is_equal_approx(relationship.get_trust(), best_relationship.get_trust()) and relationship.get_subject_id() < best_relationship.get_subject_id() ) ): best_relationship = relationship if best_relationship == null: return null return starving_by_id[best_relationship.get_subject_id()] as SimNPC func _get_or_create(observer_id: int, subject_id: int) -> RelationshipStateRecord: var relationship := get_relationship(observer_id, subject_id) if relationship != null: return relationship relationship = RelationshipStateRecord.create(observer_id, subject_id, 0.0) _add(relationship) return relationship func _add(relationship: RelationshipStateRecord) -> void: relationships[_key(relationship.get_observer_id(), relationship.get_subject_id())] = relationship static func _key(observer_id: int, subject_id: int) -> String: return "%d:%d" % [observer_id, subject_id] static func _sort_relationships( first: RelationshipStateRecord, second: RelationshipStateRecord ) -> bool: if first.get_observer_id() != second.get_observer_id(): return first.get_observer_id() < second.get_observer_id() return first.get_subject_id() < second.get_subject_id() static func _is_stronger( candidate: RelationshipStateRecord, current: RelationshipStateRecord ) -> bool: if not is_equal_approx(candidate.get_trust(), current.get_trust()): return candidate.get_trust() > current.get_trust() if not is_equal_approx(candidate.get_familiarity(), current.get_familiarity()): return candidate.get_familiarity() > current.get_familiarity() return candidate.get_subject_id() < current.get_subject_id()