263 lines
7.1 KiB
GDScript
263 lines
7.1 KiB
GDScript
class_name RelationshipStateRecord
|
|
extends RefCounted
|
|
|
|
const SCHEMA_VERSION := 2
|
|
const LEGACY_SCHEMA_VERSION := 1
|
|
const NO_CAUSE_EVENT := -1
|
|
const NEUTRAL_TRUST := 0.5
|
|
|
|
const DIMENSION_FAMILIARITY := &"familiarity"
|
|
const DIMENSION_TRUST := &"trust"
|
|
const DIMENSION_AFFECTION := &"affection"
|
|
const DIMENSION_RESPECT := &"respect"
|
|
const DIMENSION_FEAR := &"fear"
|
|
const DIMENSION_OBLIGATION := &"obligation"
|
|
const DIMENSION_HOSTILITY := &"hostility"
|
|
const DIMENSIONS := [
|
|
DIMENSION_FAMILIARITY,
|
|
DIMENSION_TRUST,
|
|
DIMENSION_AFFECTION,
|
|
DIMENSION_RESPECT,
|
|
DIMENSION_FEAR,
|
|
DIMENSION_OBLIGATION,
|
|
DIMENSION_HOSTILITY,
|
|
]
|
|
|
|
var data: Dictionary
|
|
|
|
|
|
func _init(record_data: Dictionary = {}) -> void:
|
|
data = record_data.duplicate(true)
|
|
|
|
|
|
static func create(
|
|
observer_id: int,
|
|
subject_id: int,
|
|
familiarity: float,
|
|
trust: float = NEUTRAL_TRUST,
|
|
last_trust_cause_event_id: int = NO_CAUSE_EVENT
|
|
) -> RelationshipStateRecord:
|
|
return (
|
|
RelationshipStateRecord
|
|
. new(
|
|
{
|
|
"schema_version": SCHEMA_VERSION,
|
|
"observer_id": observer_id,
|
|
"subject_id": subject_id,
|
|
"familiarity": clampf(familiarity, 0.0, 1.0),
|
|
"trust": clampf(trust, 0.0, 1.0),
|
|
"affection": 0.0,
|
|
"respect": 0.0,
|
|
"fear": 0.0,
|
|
"obligation": 0.0,
|
|
"hostility": 0.0,
|
|
"last_familiarity_cause_event_id": NO_CAUSE_EVENT,
|
|
"last_trust_cause_event_id": last_trust_cause_event_id,
|
|
"last_affection_cause_event_id": NO_CAUSE_EVENT,
|
|
"last_respect_cause_event_id": NO_CAUSE_EVENT,
|
|
"last_fear_cause_event_id": NO_CAUSE_EVENT,
|
|
"last_obligation_cause_event_id": NO_CAUSE_EVENT,
|
|
"last_hostility_cause_event_id": NO_CAUSE_EVENT,
|
|
}
|
|
)
|
|
)
|
|
|
|
|
|
static func from_dictionary(record_data: Dictionary) -> RelationshipStateRecord:
|
|
var version := int(record_data.get("schema_version", -1))
|
|
var normalized := record_data.duplicate(true)
|
|
if version == LEGACY_SCHEMA_VERSION:
|
|
normalized = _migrate_v1(record_data)
|
|
elif version != SCHEMA_VERSION:
|
|
return null
|
|
var required_fields := ["observer_id", "subject_id"]
|
|
for dimension in DIMENSIONS:
|
|
required_fields.append(String(dimension))
|
|
required_fields.append(_cause_key(dimension))
|
|
if not normalized.has_all(required_fields):
|
|
return null
|
|
var observer_id := int(normalized["observer_id"])
|
|
var subject_id := int(normalized["subject_id"])
|
|
if not is_valid_actor_id(observer_id) or not is_valid_actor_id(subject_id):
|
|
return null
|
|
if observer_id == subject_id:
|
|
return null
|
|
for dimension in DIMENSIONS:
|
|
var value := float(normalized[String(dimension)])
|
|
var cause_event_id := int(normalized[_cause_key(dimension)])
|
|
if not is_finite(value) or value < 0.0 or value > 1.0:
|
|
return null
|
|
if cause_event_id < NO_CAUSE_EVENT:
|
|
return null
|
|
return RelationshipStateRecord.new(normalized)
|
|
|
|
|
|
func get_observer_id() -> int:
|
|
return int(data["observer_id"])
|
|
|
|
|
|
func get_subject_id() -> int:
|
|
return int(data["subject_id"])
|
|
|
|
|
|
func get_familiarity() -> float:
|
|
return get_dimension(DIMENSION_FAMILIARITY)
|
|
|
|
|
|
func get_trust() -> float:
|
|
return get_dimension(DIMENSION_TRUST)
|
|
|
|
|
|
func get_affection() -> float:
|
|
return get_dimension(DIMENSION_AFFECTION)
|
|
|
|
|
|
func get_respect() -> float:
|
|
return get_dimension(DIMENSION_RESPECT)
|
|
|
|
|
|
func get_fear() -> float:
|
|
return get_dimension(DIMENSION_FEAR)
|
|
|
|
|
|
func get_obligation() -> float:
|
|
return get_dimension(DIMENSION_OBLIGATION)
|
|
|
|
|
|
func get_hostility() -> float:
|
|
return get_dimension(DIMENSION_HOSTILITY)
|
|
|
|
|
|
func get_dimension(dimension: StringName) -> float:
|
|
if not has_dimension(dimension):
|
|
return -1.0
|
|
return float(data[String(dimension)])
|
|
|
|
|
|
func get_last_familiarity_cause_event_id() -> int:
|
|
return get_last_cause_event_id(DIMENSION_FAMILIARITY)
|
|
|
|
|
|
func get_last_trust_cause_event_id() -> int:
|
|
return get_last_cause_event_id(DIMENSION_TRUST)
|
|
|
|
|
|
func get_last_affection_cause_event_id() -> int:
|
|
return get_last_cause_event_id(DIMENSION_AFFECTION)
|
|
|
|
|
|
func get_last_respect_cause_event_id() -> int:
|
|
return get_last_cause_event_id(DIMENSION_RESPECT)
|
|
|
|
|
|
func get_last_fear_cause_event_id() -> int:
|
|
return get_last_cause_event_id(DIMENSION_FEAR)
|
|
|
|
|
|
func get_last_obligation_cause_event_id() -> int:
|
|
return get_last_cause_event_id(DIMENSION_OBLIGATION)
|
|
|
|
|
|
func get_last_hostility_cause_event_id() -> int:
|
|
return get_last_cause_event_id(DIMENSION_HOSTILITY)
|
|
|
|
|
|
func get_last_cause_event_id(dimension: StringName) -> int:
|
|
if not has_dimension(dimension):
|
|
return NO_CAUSE_EVENT
|
|
return int(data[_cause_key(dimension)])
|
|
|
|
|
|
func increase_familiarity(amount: float) -> float:
|
|
return adjust_dimension(DIMENSION_FAMILIARITY, maxf(amount, 0.0), NO_CAUSE_EVENT)
|
|
|
|
|
|
func increase_familiarity_with_cause(amount: float, cause_event_id: int) -> float:
|
|
return adjust_dimension(DIMENSION_FAMILIARITY, maxf(amount, 0.0), cause_event_id)
|
|
|
|
|
|
func increase_trust(amount: float, cause_event_id: int) -> float:
|
|
return adjust_dimension(DIMENSION_TRUST, maxf(amount, 0.0), cause_event_id)
|
|
|
|
|
|
func adjust_affection(amount: float, cause_event_id: int) -> float:
|
|
return adjust_dimension(DIMENSION_AFFECTION, amount, cause_event_id)
|
|
|
|
|
|
func adjust_respect(amount: float, cause_event_id: int) -> float:
|
|
return adjust_dimension(DIMENSION_RESPECT, amount, cause_event_id)
|
|
|
|
|
|
func adjust_fear(amount: float, cause_event_id: int) -> float:
|
|
return adjust_dimension(DIMENSION_FEAR, amount, cause_event_id)
|
|
|
|
|
|
func adjust_obligation(amount: float, cause_event_id: int) -> float:
|
|
return adjust_dimension(DIMENSION_OBLIGATION, amount, cause_event_id)
|
|
|
|
|
|
func adjust_hostility(amount: float, cause_event_id: int) -> float:
|
|
return adjust_dimension(DIMENSION_HOSTILITY, amount, cause_event_id)
|
|
|
|
|
|
func adjust_dimension(dimension: StringName, amount: float, cause_event_id: int) -> float:
|
|
if not has_dimension(dimension) or not is_finite(amount) or cause_event_id < NO_CAUSE_EVENT:
|
|
return 0.0
|
|
var previous := get_dimension(dimension)
|
|
var current := clampf(previous + amount, 0.0, 1.0)
|
|
var applied := current - previous
|
|
if is_zero_approx(applied):
|
|
return 0.0
|
|
data[String(dimension)] = current
|
|
if cause_event_id != NO_CAUSE_EVENT:
|
|
data[_cause_key(dimension)] = cause_event_id
|
|
return applied
|
|
|
|
|
|
func get_salience() -> float:
|
|
var salience := get_familiarity()
|
|
salience = maxf(salience, absf(get_trust() - NEUTRAL_TRUST) * 2.0)
|
|
for dimension in [
|
|
DIMENSION_AFFECTION,
|
|
DIMENSION_RESPECT,
|
|
DIMENSION_FEAR,
|
|
DIMENSION_OBLIGATION,
|
|
DIMENSION_HOSTILITY,
|
|
]:
|
|
salience = maxf(salience, get_dimension(dimension))
|
|
return clampf(salience, 0.0, 1.0)
|
|
|
|
|
|
func to_dictionary() -> Dictionary:
|
|
return data.duplicate(true)
|
|
|
|
|
|
static func has_dimension(dimension: StringName) -> bool:
|
|
return dimension in DIMENSIONS
|
|
|
|
|
|
static func is_valid_actor_id(actor_id: int) -> bool:
|
|
return actor_id >= 0 or actor_id == SimulationIds.PLAYER_ACTOR_ID
|
|
|
|
|
|
static func _cause_key(dimension: StringName) -> String:
|
|
return "last_%s_cause_event_id" % String(dimension)
|
|
|
|
|
|
static func _migrate_v1(record_data: Dictionary) -> Dictionary:
|
|
var migrated := record_data.duplicate(true)
|
|
migrated["schema_version"] = SCHEMA_VERSION
|
|
for dimension in [
|
|
DIMENSION_AFFECTION,
|
|
DIMENSION_RESPECT,
|
|
DIMENSION_FEAR,
|
|
DIMENSION_OBLIGATION,
|
|
DIMENSION_HOSTILITY,
|
|
]:
|
|
migrated[String(dimension)] = 0.0
|
|
for dimension in DIMENSIONS:
|
|
var cause_key := _cause_key(dimension)
|
|
if not migrated.has(cause_key):
|
|
migrated[cause_key] = NO_CAUSE_EVENT
|
|
return migrated
|