Files
gamedev-the-steward/simulation/relationships/RelationshipSystem.gd
T
2026-07-16 23:51:08 +02:00

185 lines
6.3 KiB
GDScript

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(
observer_id: int, npcs: Array[SimNPC], population_view: SimulationPopulationView = null
) -> SimNPC:
var effective_view := population_view
if effective_view == null:
effective_view = SimulationPopulationView.new(npcs)
var best_relationship: RelationshipStateRecord
for relationship in relationships.values():
if relationship.get_observer_id() != observer_id:
continue
if effective_view.get_living(relationship.get_subject_id()) == null:
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 effective_view.get_living(best_relationship.get_subject_id())
func get_trusted_starving_subject(
observer_id: int, npcs: Array, population_view: SimulationPopulationView = null
) -> SimNPC:
var effective_view := population_view
if effective_view == null:
effective_view = SimulationPopulationView.new(npcs)
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 effective_view.get_starving(relationship.get_subject_id()) == null:
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 effective_view.get_starving(best_relationship.get_subject_id())
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()