190 lines
5.6 KiB
GDScript
190 lines
5.6 KiB
GDScript
class_name SocialCommitmentSystem
|
|
extends RefCounted
|
|
|
|
const REASON_FACT_FULFILLED := &"world_fact_fulfilled"
|
|
const REASON_DEADLINE_MISSED := &"deadline_missed"
|
|
const REASON_SITUATION_EXPIRED := &"situation_expired"
|
|
const REASON_SITUATION_SUPERSEDED := &"situation_superseded"
|
|
|
|
var _commitments_by_id: Dictionary = {}
|
|
var _active_contract_keys: Dictionary = {}
|
|
var _next_commitment_id := 0
|
|
|
|
|
|
func create_for_alternative(
|
|
situation: SituationStateRecord,
|
|
definition: SituationDefinition,
|
|
alternative_id: StringName,
|
|
debtor: WorldEntityRef,
|
|
creditor: WorldEntityRef,
|
|
current_tick: int,
|
|
deadline_tick: int = -1
|
|
) -> CommitmentStateRecord:
|
|
if (
|
|
situation == null
|
|
or definition == null
|
|
or not situation.is_active()
|
|
or situation.get_definition_id() != definition.situation_definition_id
|
|
or current_tick < situation.get_created_tick()
|
|
):
|
|
return null
|
|
var alternative := definition.get_alternative(alternative_id)
|
|
if alternative == null or alternative.commitment_terms.is_empty():
|
|
return null
|
|
var contract_key := _contract_key(situation.get_situation_id(), debtor, creditor)
|
|
if contract_key.is_empty() or _active_contract_keys.has(contract_key):
|
|
return null
|
|
if not situation.select_alternative(alternative_id):
|
|
return null
|
|
var commitment := CommitmentStateRecord.create(
|
|
_next_commitment_id,
|
|
situation.get_situation_id(),
|
|
debtor,
|
|
creditor,
|
|
alternative.commitment_terms,
|
|
current_tick,
|
|
deadline_tick
|
|
)
|
|
if commitment == null:
|
|
return null
|
|
_commitments_by_id[_next_commitment_id] = commitment
|
|
_active_contract_keys[contract_key] = _next_commitment_id
|
|
_next_commitment_id += 1
|
|
return commitment
|
|
|
|
|
|
func maintain(
|
|
current_tick: int,
|
|
situation_system: SituationSystem,
|
|
world_events: WorldEventStore,
|
|
state_facts: Dictionary
|
|
) -> Array[CommitmentStateRecord]:
|
|
var closed: Array[CommitmentStateRecord] = []
|
|
if situation_system == null:
|
|
return closed
|
|
for commitment in get_active_sorted():
|
|
var situation := situation_system.get_by_id(commitment.get_situation_id())
|
|
if situation == null:
|
|
continue
|
|
var progress := situation_system.derive_progress(
|
|
situation, world_events, state_facts, situation.get_selected_alternative_id()
|
|
)
|
|
if progress.resolved:
|
|
if _close(
|
|
commitment,
|
|
CommitmentStateRecord.STATUS_FULFILLED,
|
|
current_tick,
|
|
REASON_FACT_FULFILLED,
|
|
progress.matched_event_id
|
|
):
|
|
closed.append(commitment)
|
|
continue
|
|
if situation.get_status() == SituationStateRecord.STATUS_SUPERSEDED:
|
|
if _close(
|
|
commitment,
|
|
CommitmentStateRecord.STATUS_SUPERSEDED,
|
|
current_tick,
|
|
REASON_SITUATION_SUPERSEDED
|
|
):
|
|
closed.append(commitment)
|
|
continue
|
|
if situation.get_status() == SituationStateRecord.STATUS_EXPIRED:
|
|
if _close(
|
|
commitment,
|
|
CommitmentStateRecord.STATUS_BROKEN,
|
|
current_tick,
|
|
REASON_SITUATION_EXPIRED
|
|
):
|
|
closed.append(commitment)
|
|
continue
|
|
var deadline := commitment.get_deadline_tick()
|
|
if deadline >= 0 and current_tick > deadline:
|
|
if _close(
|
|
commitment,
|
|
CommitmentStateRecord.STATUS_BROKEN,
|
|
current_tick,
|
|
REASON_DEADLINE_MISSED
|
|
):
|
|
closed.append(commitment)
|
|
return closed
|
|
|
|
|
|
func release(commitment_id: int, current_tick: int, reason: StringName) -> bool:
|
|
var commitment := get_by_id(commitment_id)
|
|
return _close(commitment, CommitmentStateRecord.STATUS_RELEASED, current_tick, reason)
|
|
|
|
|
|
func supersede(commitment_id: int, current_tick: int, reason: StringName) -> bool:
|
|
var commitment := get_by_id(commitment_id)
|
|
return _close(commitment, CommitmentStateRecord.STATUS_SUPERSEDED, current_tick, reason)
|
|
|
|
|
|
func restore(records: Array[CommitmentStateRecord], restored_next_id: int) -> bool:
|
|
var by_id: Dictionary = {}
|
|
var active_keys: Dictionary = {}
|
|
var next_id := maxi(restored_next_id, 0)
|
|
for record in records:
|
|
if record == null or by_id.has(record.get_commitment_id()):
|
|
return false
|
|
if record.is_active():
|
|
var key := _contract_key(
|
|
record.get_situation_id(), record.get_debtor(), record.get_creditor()
|
|
)
|
|
if key.is_empty() or active_keys.has(key):
|
|
return false
|
|
active_keys[key] = record.get_commitment_id()
|
|
by_id[record.get_commitment_id()] = record
|
|
next_id = maxi(next_id, record.get_commitment_id() + 1)
|
|
_commitments_by_id = by_id
|
|
_active_contract_keys = active_keys
|
|
_next_commitment_id = next_id
|
|
return true
|
|
|
|
|
|
func get_by_id(commitment_id: int) -> CommitmentStateRecord:
|
|
return _commitments_by_id.get(commitment_id) as CommitmentStateRecord
|
|
|
|
|
|
func get_all_sorted() -> Array[CommitmentStateRecord]:
|
|
var records: Array[CommitmentStateRecord] = []
|
|
for record in _commitments_by_id.values():
|
|
records.append(record)
|
|
records.sort_custom(
|
|
func(first: CommitmentStateRecord, second: CommitmentStateRecord) -> bool:
|
|
return first.get_commitment_id() < second.get_commitment_id()
|
|
)
|
|
return records
|
|
|
|
|
|
func get_active_sorted() -> Array[CommitmentStateRecord]:
|
|
var records: Array[CommitmentStateRecord] = []
|
|
for record in get_all_sorted():
|
|
if record.is_active():
|
|
records.append(record)
|
|
return records
|
|
|
|
|
|
func _close(
|
|
commitment: CommitmentStateRecord,
|
|
status: StringName,
|
|
current_tick: int,
|
|
reason: StringName,
|
|
cause_event_id: int = CommitmentStateRecord.NO_EVENT_ID
|
|
) -> bool:
|
|
if commitment == null or not commitment.close(status, current_tick, reason, cause_event_id):
|
|
return false
|
|
_active_contract_keys.erase(
|
|
_contract_key(
|
|
commitment.get_situation_id(), commitment.get_debtor(), commitment.get_creditor()
|
|
)
|
|
)
|
|
return true
|
|
|
|
|
|
static func _contract_key(
|
|
situation_id: int, debtor: WorldEntityRef, creditor: WorldEntityRef
|
|
) -> String:
|
|
if situation_id < 0 or debtor == null or creditor == null:
|
|
return ""
|
|
return "%d|%s|%s" % [situation_id, debtor.index_key(), creditor.index_key()]
|