feat: deliver emergent Jajce commitments
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
class_name ConversationActStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
const MAX_TOPICS := 8
|
||||
|
||||
var data: Dictionary
|
||||
|
||||
|
||||
func _init(record_data: Dictionary = {}) -> void:
|
||||
data = record_data.duplicate(true)
|
||||
|
||||
|
||||
static func create(
|
||||
act_id: int,
|
||||
tick: int,
|
||||
speaker: WorldEntityRef,
|
||||
listener: WorldEntityRef,
|
||||
intent_id: StringName,
|
||||
topic_ids: Array[StringName] = []
|
||||
) -> ConversationActStateRecord:
|
||||
if (
|
||||
act_id < 0
|
||||
or tick < 0
|
||||
or speaker == null
|
||||
or listener == null
|
||||
or not speaker.is_valid()
|
||||
or not listener.is_valid()
|
||||
or speaker.equals(listener)
|
||||
or not ConversationIntentIds.is_supported(intent_id)
|
||||
or topic_ids.size() > MAX_TOPICS
|
||||
):
|
||||
return null
|
||||
var normalized_topics: Array[String] = []
|
||||
var seen: Dictionary = {}
|
||||
for topic_id in topic_ids:
|
||||
if topic_id.is_empty() or seen.has(topic_id):
|
||||
return null
|
||||
seen[topic_id] = true
|
||||
normalized_topics.append(String(topic_id))
|
||||
return (
|
||||
ConversationActStateRecord
|
||||
. new(
|
||||
{
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"act_id": act_id,
|
||||
"tick": tick,
|
||||
"speaker": speaker.to_dictionary(),
|
||||
"listener": listener.to_dictionary(),
|
||||
"intent_id": String(intent_id),
|
||||
"topic_ids": normalized_topics,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
static func from_dictionary(record_data: Dictionary) -> ConversationActStateRecord:
|
||||
if (
|
||||
int(record_data.get("schema_version", -1)) != SCHEMA_VERSION
|
||||
or not record_data.has_all(
|
||||
["act_id", "tick", "speaker", "listener", "intent_id", "topic_ids"]
|
||||
)
|
||||
or not record_data["speaker"] is Dictionary
|
||||
or not record_data["listener"] is Dictionary
|
||||
or not record_data["topic_ids"] is Array
|
||||
):
|
||||
return null
|
||||
var topics: Array[StringName] = []
|
||||
for topic in record_data["topic_ids"]:
|
||||
if not topic is String and not topic is StringName:
|
||||
return null
|
||||
topics.append(StringName(topic))
|
||||
return create(
|
||||
int(record_data["act_id"]),
|
||||
int(record_data["tick"]),
|
||||
WorldEntityRef.from_dictionary(record_data["speaker"]),
|
||||
WorldEntityRef.from_dictionary(record_data["listener"]),
|
||||
StringName(record_data["intent_id"]),
|
||||
topics
|
||||
)
|
||||
|
||||
|
||||
func get_act_id() -> int:
|
||||
return int(data["act_id"])
|
||||
|
||||
|
||||
func get_tick() -> int:
|
||||
return int(data["tick"])
|
||||
|
||||
|
||||
func get_speaker() -> WorldEntityRef:
|
||||
return WorldEntityRef.from_dictionary(data["speaker"])
|
||||
|
||||
|
||||
func get_listener() -> WorldEntityRef:
|
||||
return WorldEntityRef.from_dictionary(data["listener"])
|
||||
|
||||
|
||||
func get_intent_id() -> StringName:
|
||||
return StringName(data["intent_id"])
|
||||
|
||||
|
||||
func get_topic_ids() -> Array[StringName]:
|
||||
var result: Array[StringName] = []
|
||||
for topic in data["topic_ids"]:
|
||||
result.append(StringName(topic))
|
||||
return result
|
||||
|
||||
|
||||
func to_dictionary() -> Dictionary:
|
||||
return data.duplicate(true)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b4iruxxxc2g2j
|
||||
@@ -154,6 +154,16 @@ func set_tracking_preference(preference: StringName) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
func select_alternative(alternative_id: StringName) -> bool:
|
||||
if not is_active() or alternative_id.is_empty():
|
||||
return false
|
||||
var selected := get_selected_alternative_id()
|
||||
if not selected.is_empty() and selected != alternative_id:
|
||||
return false
|
||||
data["selected_alternative_id"] = String(alternative_id)
|
||||
return true
|
||||
|
||||
|
||||
func archive(closed_tick: int) -> bool:
|
||||
return close(STATUS_ARCHIVED, closed_tick)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ class_name SimulationStateRecord
|
||||
extends RefCounted
|
||||
|
||||
const SCHEMA_NAME := "the_steward.simulation"
|
||||
const SCHEMA_VERSION := 14
|
||||
const SCHEMA_VERSION := 15
|
||||
const LEGACY_SCHEMA_VERSION := 1
|
||||
const EVENT_LEGACY_SCHEMA_VERSION := 2
|
||||
const RELATIONSHIP_LEGACY_SCHEMA_VERSION := 3
|
||||
@@ -15,6 +15,7 @@ const ROUTINE_LEGACY_SCHEMA_VERSION := 10
|
||||
const PLAYER_RELATIONSHIP_SCHEMA_VERSION := 11
|
||||
const PLAYER_NEEDS_SCHEMA_VERSION := 12
|
||||
const CONFLICT_SCHEMA_VERSION := 13
|
||||
const PRE_EMERGENT_SCHEMA_VERSION := 14
|
||||
const PREVIOUS_SCHEMA_VERSION := 7
|
||||
|
||||
var simulation: Dictionary
|
||||
@@ -27,6 +28,10 @@ var economic_events: Array[EconomicEventRecord] = []
|
||||
var relationships: Array[RelationshipStateRecord] = []
|
||||
var event_knowledge: Array[KnownEventStateRecord] = []
|
||||
var opportunities: Array[OpportunityStateRecord] = []
|
||||
var situations: Array[SituationStateRecord] = []
|
||||
var quest_journal: Array[QuestJournalEntryStateRecord] = []
|
||||
var commitments: Array[CommitmentStateRecord] = []
|
||||
var conversation_history: Array[ConversationActStateRecord] = []
|
||||
var player: PlayerStateRecord
|
||||
var combatants: Array[CombatantStateRecord] = []
|
||||
var factions: Array[FactionStateRecord] = []
|
||||
@@ -58,6 +63,18 @@ func to_dictionary() -> Dictionary:
|
||||
var opportunity_data: Array[Dictionary] = []
|
||||
for opportunity_record in opportunities:
|
||||
opportunity_data.append(opportunity_record.to_dictionary())
|
||||
var situation_data: Array[Dictionary] = []
|
||||
for situation_record in situations:
|
||||
situation_data.append(situation_record.to_dictionary())
|
||||
var journal_data: Array[Dictionary] = []
|
||||
for journal_record in quest_journal:
|
||||
journal_data.append(journal_record.to_dictionary())
|
||||
var commitment_data: Array[Dictionary] = []
|
||||
for commitment_record in commitments:
|
||||
commitment_data.append(commitment_record.to_dictionary())
|
||||
var conversation_history_data: Array[Dictionary] = []
|
||||
for conversation_act in conversation_history:
|
||||
conversation_history_data.append(conversation_act.to_dictionary())
|
||||
var combatant_data: Array[Dictionary] = []
|
||||
for combatant_record in combatants:
|
||||
combatant_data.append(combatant_record.to_dictionary())
|
||||
@@ -78,6 +95,10 @@ func to_dictionary() -> Dictionary:
|
||||
"relationships": relationship_data,
|
||||
"event_knowledge": knowledge_data,
|
||||
"opportunities": opportunity_data,
|
||||
"situations": situation_data,
|
||||
"quest_journal": journal_data,
|
||||
"commitments": commitment_data,
|
||||
"conversation_history": conversation_history_data,
|
||||
"player": player.to_dictionary(),
|
||||
"combatants": combatant_data,
|
||||
"factions": faction_data
|
||||
@@ -115,6 +136,7 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
PLAYER_RELATIONSHIP_SCHEMA_VERSION,
|
||||
PLAYER_NEEDS_SCHEMA_VERSION,
|
||||
CONFLICT_SCHEMA_VERSION,
|
||||
PRE_EMERGENT_SCHEMA_VERSION,
|
||||
]
|
||||
):
|
||||
record_data = _migrate_legacy(record_data, version)
|
||||
@@ -134,6 +156,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
"relationships",
|
||||
"event_knowledge",
|
||||
"opportunities",
|
||||
"situations",
|
||||
"quest_journal",
|
||||
"commitments",
|
||||
"conversation_history",
|
||||
"player",
|
||||
"combatants",
|
||||
"factions",
|
||||
@@ -157,6 +183,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
"wander_random_streams",
|
||||
"next_event_id",
|
||||
"next_opportunity_id",
|
||||
"next_situation_id",
|
||||
"next_journal_entry_id",
|
||||
"next_commitment_id",
|
||||
"next_conversation_act_id",
|
||||
]
|
||||
)
|
||||
):
|
||||
@@ -166,6 +196,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
var saved_clock_accumulator := float(simulation_data["clock_accumulator"])
|
||||
var saved_clock_elapsed_ticks := int(simulation_data["clock_elapsed_ticks"])
|
||||
var saved_next_opportunity_id := int(simulation_data["next_opportunity_id"])
|
||||
var saved_next_situation_id := int(simulation_data["next_situation_id"])
|
||||
var saved_next_journal_entry_id := int(simulation_data["next_journal_entry_id"])
|
||||
var saved_next_commitment_id := int(simulation_data["next_commitment_id"])
|
||||
var saved_next_conversation_act_id := int(simulation_data["next_conversation_act_id"])
|
||||
if (
|
||||
not is_finite(saved_tick_interval)
|
||||
or saved_tick_interval <= 0.0
|
||||
@@ -174,6 +208,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
or saved_clock_accumulator < 0.0
|
||||
or saved_clock_elapsed_ticks < 0
|
||||
or saved_next_opportunity_id < 0
|
||||
or saved_next_situation_id < 0
|
||||
or saved_next_journal_entry_id < 0
|
||||
or saved_next_commitment_id < 0
|
||||
or saved_next_conversation_act_id < 0
|
||||
):
|
||||
return null
|
||||
if simulation_data.has("cycle_duration_seconds"):
|
||||
@@ -204,6 +242,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
var relationship_data = record_data["relationships"]
|
||||
var knowledge_data = record_data["event_knowledge"]
|
||||
var opportunity_data = record_data["opportunities"]
|
||||
var situation_data = record_data["situations"]
|
||||
var journal_data = record_data["quest_journal"]
|
||||
var commitment_data = record_data["commitments"]
|
||||
var conversation_history_data = record_data["conversation_history"]
|
||||
if (
|
||||
not npc_data is Array
|
||||
or not animal_data is Array
|
||||
@@ -213,6 +255,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
or not relationship_data is Array
|
||||
or not knowledge_data is Array
|
||||
or not opportunity_data is Array
|
||||
or not situation_data is Array
|
||||
or not journal_data is Array
|
||||
or not commitment_data is Array
|
||||
or not conversation_history_data is Array
|
||||
):
|
||||
return null
|
||||
|
||||
@@ -425,7 +471,10 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
return null
|
||||
var knower_id := known_event_record.get_knower_id()
|
||||
var known_event_id := known_event_record.get_event_id()
|
||||
if not npc_ids.has(knower_id) or not event_ids.has(known_event_id):
|
||||
if (
|
||||
(knower_id != SimulationIds.PLAYER_ACTOR_ID and not npc_ids.has(knower_id))
|
||||
or not event_ids.has(known_event_id)
|
||||
):
|
||||
return null
|
||||
var knowledge_key := "%d:%d" % [knower_id, known_event_id]
|
||||
if knowledge_keys.has(knowledge_key):
|
||||
@@ -448,29 +497,28 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
return null
|
||||
var observer_id := relationship_record.get_observer_id()
|
||||
var subject_id := relationship_record.get_subject_id()
|
||||
var is_player_observer := observer_id == SimulationIds.PLAYER_ACTOR_ID
|
||||
var is_player_subject := subject_id == SimulationIds.PLAYER_ACTOR_ID
|
||||
if not npc_ids.has(observer_id):
|
||||
if not is_player_observer and not npc_ids.has(observer_id):
|
||||
return null
|
||||
if not is_player_subject and not npc_ids.has(subject_id):
|
||||
return null
|
||||
var relationship_key := "%d:%d" % [observer_id, subject_id]
|
||||
if relationship_keys.has(relationship_key):
|
||||
return null
|
||||
var cause_event_id := relationship_record.get_last_trust_cause_event_id()
|
||||
if (
|
||||
cause_event_id != RelationshipStateRecord.NO_CAUSE_EVENT
|
||||
and not event_ids.has(cause_event_id)
|
||||
):
|
||||
return null
|
||||
if (
|
||||
cause_event_id != RelationshipStateRecord.NO_CAUSE_EVENT
|
||||
and not knowledge_keys.has("%d:%d" % [observer_id, cause_event_id])
|
||||
):
|
||||
return null
|
||||
if cause_event_id != RelationshipStateRecord.NO_CAUSE_EVENT:
|
||||
var cause_event := event_records_by_id[cause_event_id] as EconomicEventRecord
|
||||
if not _is_valid_relationship_cause(cause_event, subject_id, is_player_subject):
|
||||
for dimension in RelationshipStateRecord.DIMENSIONS:
|
||||
var cause_event_id := relationship_record.get_last_cause_event_id(dimension)
|
||||
if cause_event_id == RelationshipStateRecord.NO_CAUSE_EVENT:
|
||||
continue
|
||||
if (
|
||||
not event_ids.has(cause_event_id)
|
||||
or not knowledge_keys.has("%d:%d" % [observer_id, cause_event_id])
|
||||
):
|
||||
return null
|
||||
if dimension == RelationshipStateRecord.DIMENSION_TRUST:
|
||||
var cause_event := event_records_by_id[cause_event_id] as EconomicEventRecord
|
||||
if not _is_valid_relationship_cause(cause_event, subject_id, is_player_subject):
|
||||
return null
|
||||
relationship_keys[relationship_key] = true
|
||||
record.relationships.append(relationship_record)
|
||||
|
||||
@@ -515,6 +563,36 @@ static func from_dictionary(record_data: Dictionary) -> SimulationStateRecord:
|
||||
if int(record.simulation["next_opportunity_id"]) <= highest_opportunity_id:
|
||||
return null
|
||||
|
||||
var emergent_records: Variant = EmergentStateValidator.parse(
|
||||
situation_data, journal_data, commitment_data, simulation_data, npc_ids, event_ids
|
||||
)
|
||||
if emergent_records == null:
|
||||
return null
|
||||
record.situations.assign(emergent_records["situations"])
|
||||
record.quest_journal.assign(emergent_records["quest_journal"])
|
||||
record.commitments.assign(emergent_records["commitments"])
|
||||
var conversation_act_ids: Dictionary = {}
|
||||
var highest_conversation_act_id := -1
|
||||
for item in conversation_history_data:
|
||||
if not item is Dictionary:
|
||||
return null
|
||||
var conversation_act := ConversationActStateRecord.from_dictionary(item)
|
||||
if (
|
||||
conversation_act == null
|
||||
or conversation_act_ids.has(conversation_act.get_act_id())
|
||||
or conversation_act.get_tick() > saved_tick_count
|
||||
or not EmergentStateValidator.is_valid_actor(conversation_act.get_speaker(), npc_ids)
|
||||
or not EmergentStateValidator.is_valid_actor(conversation_act.get_listener(), npc_ids)
|
||||
):
|
||||
return null
|
||||
conversation_act_ids[conversation_act.get_act_id()] = true
|
||||
highest_conversation_act_id = maxi(
|
||||
highest_conversation_act_id, conversation_act.get_act_id()
|
||||
)
|
||||
record.conversation_history.append(conversation_act)
|
||||
if saved_next_conversation_act_id <= highest_conversation_act_id:
|
||||
return null
|
||||
|
||||
return record
|
||||
|
||||
|
||||
@@ -579,16 +657,21 @@ static func _is_valid_knowledge_provenance(
|
||||
SimulationIds.KNOWLEDGE_ACQUISITION_COMMUNICATED:
|
||||
if knower_id == actor_id:
|
||||
return false
|
||||
var source_npc_id := known_event.get_source_npc_id()
|
||||
if not npc_ids.has(source_npc_id):
|
||||
var source_actor_id := known_event.get_source_actor_id()
|
||||
if (
|
||||
source_actor_id != SimulationIds.PLAYER_ACTOR_ID
|
||||
and not npc_ids.has(source_actor_id)
|
||||
):
|
||||
return false
|
||||
var source_method := known_event.get_source_acquisition_method()
|
||||
if (
|
||||
(source_method == SimulationIds.KNOWLEDGE_ACQUISITION_PERFORMED)
|
||||
!= (source_npc_id == actor_id)
|
||||
!= (source_actor_id == actor_id)
|
||||
):
|
||||
return false
|
||||
return true
|
||||
if source_method not in KnownEventStateRecord.DIRECT_ACQUISITION_METHODS:
|
||||
return false
|
||||
return known_event.get_hop_count() == 1
|
||||
return false
|
||||
|
||||
|
||||
@@ -774,12 +857,13 @@ static func _is_valid_home_damaged_trigger(
|
||||
static func _is_valid_relationship_cause(
|
||||
cause_event: EconomicEventRecord, subject_id: int, is_player_subject: bool
|
||||
) -> bool:
|
||||
var event_type := StringName(cause_event.data["event_type"])
|
||||
if EmergentStateValidator.is_valid_commitment_relationship_cause(cause_event, subject_id):
|
||||
return true
|
||||
if not is_player_subject:
|
||||
return (
|
||||
int(cause_event.data["actor_id"]) == subject_id
|
||||
and (
|
||||
StringName(cause_event.data["event_type"]) == SimulationIds.EVENT_STORAGE_DEPOSITED
|
||||
)
|
||||
and (event_type == SimulationIds.EVENT_STORAGE_DEPOSITED)
|
||||
and StringName(cause_event.data["item_id"]) == SimulationIds.RESOURCE_FOOD
|
||||
and float(cause_event.data["amount"]) > 0.0
|
||||
)
|
||||
@@ -789,7 +873,7 @@ static func _is_valid_relationship_cause(
|
||||
return false
|
||||
if float(cause_event.data["amount"]) <= 0.0:
|
||||
return false
|
||||
if StringName(cause_event.data["event_type"]) == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||
if event_type == SimulationIds.EVENT_STORAGE_DEPOSITED:
|
||||
return (
|
||||
StringName(cause_event.data["source_id"]) == SimulationIds.PLAYER_INVENTORY_ID
|
||||
and (
|
||||
@@ -798,7 +882,7 @@ static func _is_valid_relationship_cause(
|
||||
)
|
||||
)
|
||||
return (
|
||||
StringName(cause_event.data["event_type"]) == SimulationIds.EVENT_RESOURCE_EXTRACTED
|
||||
event_type == SimulationIds.EVENT_RESOURCE_EXTRACTED
|
||||
and StringName(cause_event.data["destination_id"]) == SimulationIds.STORAGE_VILLAGE_PANTRY
|
||||
)
|
||||
|
||||
@@ -848,6 +932,16 @@ static func _is_valid_supply_resolution(
|
||||
static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary:
|
||||
var migrated := legacy_data.duplicate(true)
|
||||
migrated["schema_version"] = SCHEMA_VERSION
|
||||
migrated["situations"] = []
|
||||
migrated["quest_journal"] = []
|
||||
migrated["commitments"] = []
|
||||
migrated["conversation_history"] = []
|
||||
var emergent_simulation_data: Dictionary = migrated.get("simulation", {})
|
||||
emergent_simulation_data["next_situation_id"] = 0
|
||||
emergent_simulation_data["next_journal_entry_id"] = 0
|
||||
emergent_simulation_data["next_commitment_id"] = 0
|
||||
emergent_simulation_data["next_conversation_act_id"] = 0
|
||||
migrated["simulation"] = emergent_simulation_data
|
||||
if not migrated.has("player"):
|
||||
migrated["player"] = PlayerStateRecord.create_default().to_dictionary()
|
||||
if version < CONFLICT_SCHEMA_VERSION:
|
||||
@@ -927,6 +1021,7 @@ static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary
|
||||
PLAYER_RELATIONSHIP_SCHEMA_VERSION,
|
||||
PLAYER_NEEDS_SCHEMA_VERSION,
|
||||
CONFLICT_SCHEMA_VERSION,
|
||||
PRE_EMERGENT_SCHEMA_VERSION,
|
||||
]
|
||||
):
|
||||
migrated["opportunities"] = []
|
||||
|
||||
@@ -28,6 +28,10 @@ static func create(
|
||||
return null
|
||||
if trigger_event_id < NO_EVENT_ID:
|
||||
return null
|
||||
var normalized_context := context.duplicate(true)
|
||||
for integer_key in ["event_id", "opportunity_id"]:
|
||||
if normalized_context.has(integer_key):
|
||||
normalized_context[integer_key] = int(normalized_context[integer_key])
|
||||
return (
|
||||
SituationStateRecord
|
||||
. new(
|
||||
@@ -43,7 +47,7 @@ static func create(
|
||||
"resolution_event_id": NO_EVENT_ID,
|
||||
"closed_tick": -1,
|
||||
"close_reason": "",
|
||||
"context": context.duplicate(true),
|
||||
"context": normalized_context,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
class_name EmergentStateValidator
|
||||
extends RefCounted
|
||||
|
||||
|
||||
static func parse(
|
||||
situation_data: Array,
|
||||
journal_data: Array,
|
||||
commitment_data: Array,
|
||||
simulation_data: Dictionary,
|
||||
npc_ids: Dictionary,
|
||||
event_ids: Dictionary
|
||||
) -> Variant:
|
||||
var current_tick := int(simulation_data["tick_count"])
|
||||
var next_situation_id := int(simulation_data["next_situation_id"])
|
||||
var next_journal_entry_id := int(simulation_data["next_journal_entry_id"])
|
||||
var next_commitment_id := int(simulation_data["next_commitment_id"])
|
||||
var situation_system := SituationSystem.create_default()
|
||||
var situations_by_id: Dictionary = {}
|
||||
var situations: Array[SituationStateRecord] = []
|
||||
if not _parse_situations(
|
||||
situation_data,
|
||||
current_tick,
|
||||
next_situation_id,
|
||||
npc_ids,
|
||||
event_ids,
|
||||
situation_system,
|
||||
situations_by_id,
|
||||
situations
|
||||
):
|
||||
return null
|
||||
var journal: Array[QuestJournalEntryStateRecord] = []
|
||||
if not _parse_journal(
|
||||
journal_data,
|
||||
current_tick,
|
||||
next_journal_entry_id,
|
||||
situation_system,
|
||||
situations_by_id,
|
||||
journal
|
||||
):
|
||||
return null
|
||||
var commitments: Array[CommitmentStateRecord] = []
|
||||
if not _parse_commitments(
|
||||
commitment_data,
|
||||
current_tick,
|
||||
next_commitment_id,
|
||||
npc_ids,
|
||||
event_ids,
|
||||
situation_system,
|
||||
situations_by_id,
|
||||
commitments
|
||||
):
|
||||
return null
|
||||
return {
|
||||
"situations": situations,
|
||||
"quest_journal": journal,
|
||||
"commitments": commitments,
|
||||
}
|
||||
|
||||
|
||||
static func _parse_situations(
|
||||
serialized: Array,
|
||||
current_tick: int,
|
||||
next_id: int,
|
||||
npc_ids: Dictionary,
|
||||
event_ids: Dictionary,
|
||||
system: SituationSystem,
|
||||
by_id: Dictionary,
|
||||
result: Array[SituationStateRecord]
|
||||
) -> bool:
|
||||
var highest_id := -1
|
||||
for item in serialized:
|
||||
if not item is Dictionary:
|
||||
return false
|
||||
var record := SituationStateRecord.from_dictionary(item)
|
||||
if record == null:
|
||||
return false
|
||||
var situation_id := record.get_situation_id()
|
||||
var definition := system.get_definition(record.get_definition_id())
|
||||
if by_id.has(situation_id) or definition == null:
|
||||
return false
|
||||
if (
|
||||
record.get_created_tick() > current_tick
|
||||
or (not record.is_active() and record.get_closed_tick() > current_tick)
|
||||
):
|
||||
return false
|
||||
var trigger_event_id := record.get_trigger_event_id()
|
||||
if (
|
||||
trigger_event_id != SituationStateRecord.NO_EVENT_ID
|
||||
and not event_ids.has(trigger_event_id)
|
||||
):
|
||||
return false
|
||||
var resolution_event_id := record.get_resolution_event_id()
|
||||
if (
|
||||
resolution_event_id != SituationStateRecord.NO_EVENT_ID
|
||||
and (not event_ids.has(resolution_event_id) or resolution_event_id <= trigger_event_id)
|
||||
):
|
||||
return false
|
||||
var alternative_id := record.get_selected_alternative_id()
|
||||
if not alternative_id.is_empty() and definition.get_alternative(alternative_id) == null:
|
||||
return false
|
||||
var context := record.get_context()
|
||||
if context.has("interested_npc_id"):
|
||||
var interested_id_text := String(context["interested_npc_id"])
|
||||
if (
|
||||
not interested_id_text.is_valid_int()
|
||||
or not npc_ids.has(interested_id_text.to_int())
|
||||
):
|
||||
return false
|
||||
by_id[situation_id] = record
|
||||
highest_id = maxi(highest_id, situation_id)
|
||||
result.append(record)
|
||||
if next_id <= highest_id:
|
||||
return false
|
||||
return system.restore(result, next_id)
|
||||
|
||||
|
||||
static func _parse_journal(
|
||||
serialized: Array,
|
||||
current_tick: int,
|
||||
next_id: int,
|
||||
situation_system: SituationSystem,
|
||||
situations_by_id: Dictionary,
|
||||
result: Array[QuestJournalEntryStateRecord]
|
||||
) -> bool:
|
||||
var ids: Dictionary = {}
|
||||
var situation_ids: Dictionary = {}
|
||||
var highest_id := -1
|
||||
for item in serialized:
|
||||
if not item is Dictionary:
|
||||
return false
|
||||
var record := QuestJournalEntryStateRecord.from_dictionary(item)
|
||||
if record == null:
|
||||
return false
|
||||
var entry_id := record.get_entry_id()
|
||||
var situation := situations_by_id.get(record.get_situation_id()) as SituationStateRecord
|
||||
if (
|
||||
ids.has(entry_id)
|
||||
or situation_ids.has(record.get_situation_id())
|
||||
or situation == null
|
||||
or record.get_definition_id() != situation.get_definition_id()
|
||||
or record.get_discovered_tick() < situation.get_created_tick()
|
||||
or record.get_discovered_tick() > current_tick
|
||||
or (not record.is_active() and record.get_closed_tick() > current_tick)
|
||||
):
|
||||
return false
|
||||
var definition := situation_system.get_definition(record.get_definition_id())
|
||||
var alternative_id := record.get_selected_alternative_id()
|
||||
if (
|
||||
not alternative_id.is_empty()
|
||||
and (
|
||||
definition == null
|
||||
or definition.get_alternative(alternative_id) == null
|
||||
or situation.get_selected_alternative_id() != alternative_id
|
||||
)
|
||||
):
|
||||
return false
|
||||
ids[entry_id] = true
|
||||
situation_ids[record.get_situation_id()] = true
|
||||
highest_id = maxi(highest_id, entry_id)
|
||||
result.append(record)
|
||||
if next_id <= highest_id:
|
||||
return false
|
||||
return QuestJournalSystem.new().restore(result, next_id)
|
||||
|
||||
|
||||
static func _parse_commitments(
|
||||
serialized: Array,
|
||||
current_tick: int,
|
||||
next_id: int,
|
||||
npc_ids: Dictionary,
|
||||
event_ids: Dictionary,
|
||||
situation_system: SituationSystem,
|
||||
situations_by_id: Dictionary,
|
||||
result: Array[CommitmentStateRecord]
|
||||
) -> bool:
|
||||
var ids: Dictionary = {}
|
||||
var highest_id := -1
|
||||
for item in serialized:
|
||||
if not item is Dictionary:
|
||||
return false
|
||||
var record := CommitmentStateRecord.from_dictionary(item)
|
||||
if record == null:
|
||||
return false
|
||||
var commitment_id := record.get_commitment_id()
|
||||
var situation := situations_by_id.get(record.get_situation_id()) as SituationStateRecord
|
||||
if (
|
||||
ids.has(commitment_id)
|
||||
or situation == null
|
||||
or record.get_created_tick() < situation.get_created_tick()
|
||||
or record.get_created_tick() > current_tick
|
||||
or (not record.is_active() and record.get_closed_tick() > current_tick)
|
||||
or not is_valid_actor(record.get_debtor(), npc_ids)
|
||||
or not is_valid_actor(record.get_creditor(), npc_ids)
|
||||
):
|
||||
return false
|
||||
var definition := situation_system.get_definition(situation.get_definition_id())
|
||||
var alternative: SituationAlternativeDefinition
|
||||
if definition != null:
|
||||
alternative = definition.get_alternative(situation.get_selected_alternative_id())
|
||||
if alternative == null or alternative.commitment_terms != record.get_terms():
|
||||
return false
|
||||
var cause_event_id := record.get_cause_event_id()
|
||||
if (
|
||||
cause_event_id != CommitmentStateRecord.NO_EVENT_ID
|
||||
and not event_ids.has(cause_event_id)
|
||||
):
|
||||
return false
|
||||
ids[commitment_id] = true
|
||||
highest_id = maxi(highest_id, commitment_id)
|
||||
result.append(record)
|
||||
if next_id <= highest_id:
|
||||
return false
|
||||
return SocialCommitmentSystem.new().restore(result, next_id)
|
||||
|
||||
|
||||
static func is_valid_actor(entity: WorldEntityRef, npc_ids: Dictionary) -> bool:
|
||||
if entity == null or not entity.is_valid():
|
||||
return false
|
||||
if entity.get_entity_type() == WorldEventRecord.ENTITY_TYPE_PLAYER:
|
||||
return String(entity.get_entity_id()) == str(SimulationIds.PLAYER_ACTOR_ID)
|
||||
if entity.get_entity_type() != WorldEventRecord.ENTITY_TYPE_NPC:
|
||||
return false
|
||||
var entity_id_text := String(entity.get_entity_id())
|
||||
return entity_id_text.is_valid_int() and npc_ids.has(entity_id_text.to_int())
|
||||
|
||||
|
||||
static func is_valid_commitment_relationship_cause(
|
||||
cause_event: EconomicEventRecord, subject_id: int
|
||||
) -> bool:
|
||||
if cause_event == null:
|
||||
return false
|
||||
if (
|
||||
StringName(cause_event.data["event_type"])
|
||||
not in [
|
||||
SimulationIds.EVENT_COMMITMENT_FULFILLED,
|
||||
SimulationIds.EVENT_COMMITMENT_BROKEN,
|
||||
]
|
||||
):
|
||||
return false
|
||||
var debtor_data = cause_event.data.get("debtor")
|
||||
if not debtor_data is Dictionary:
|
||||
return false
|
||||
var debtor := WorldEntityRef.from_dictionary(debtor_data)
|
||||
if debtor == null:
|
||||
return false
|
||||
var expected_type := (
|
||||
WorldEventRecord.ENTITY_TYPE_PLAYER
|
||||
if subject_id == SimulationIds.PLAYER_ACTOR_ID
|
||||
else WorldEventRecord.ENTITY_TYPE_NPC
|
||||
)
|
||||
return (
|
||||
debtor.get_entity_type() == expected_type
|
||||
and String(debtor.get_entity_id()) == str(subject_id)
|
||||
and int(cause_event.data["actor_id"]) == subject_id
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bolivdac21erj
|
||||
Reference in New Issue
Block a user