feat: deliver emergent Jajce commitments
This commit is contained in:
@@ -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
|
||||
)
|
||||
Reference in New Issue
Block a user