feat: generate world-backed situations and commitments
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
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()]
|
||||
Reference in New Issue
Block a user