class_name SituationStateRecord extends RefCounted const SCHEMA_VERSION := 1 const NO_EVENT_ID := -1 const STATUS_ACTIVE := &"active" const STATUS_RESOLVED := &"resolved" const STATUS_EXPIRED := &"expired" const STATUS_SUPERSEDED := &"superseded" const VALID_STATUSES := [STATUS_ACTIVE, STATUS_RESOLVED, STATUS_EXPIRED, STATUS_SUPERSEDED] var data: Dictionary func _init(record_data: Dictionary = {}) -> void: data = record_data.duplicate(true) static func create( situation_id: int, definition_id: StringName, dedupe_key: String, created_tick: int, trigger_event_id: int = NO_EVENT_ID, context: Dictionary = {} ) -> SituationStateRecord: if situation_id < 0 or definition_id.is_empty() or dedupe_key.is_empty() or created_tick < 0: 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( { "schema_version": SCHEMA_VERSION, "situation_id": situation_id, "definition_id": String(definition_id), "dedupe_key": dedupe_key, "status": String(STATUS_ACTIVE), "created_tick": created_tick, "trigger_event_id": trigger_event_id, "selected_alternative_id": "", "resolution_event_id": NO_EVENT_ID, "closed_tick": -1, "close_reason": "", "context": normalized_context, } ) ) static func from_dictionary(record_data: Dictionary) -> SituationStateRecord: if int(record_data.get("schema_version", -1)) != SCHEMA_VERSION: return null if not ( record_data . has_all( [ "situation_id", "definition_id", "dedupe_key", "status", "created_tick", "trigger_event_id", "selected_alternative_id", "resolution_event_id", "closed_tick", "close_reason", "context", ] ) ): return null var situation_id := int(record_data["situation_id"]) var definition_id := StringName(record_data["definition_id"]) var dedupe_key := String(record_data["dedupe_key"]) var status := StringName(record_data["status"]) var created_tick := int(record_data["created_tick"]) var trigger_event_id := int(record_data["trigger_event_id"]) var selected_alternative_id := StringName(record_data["selected_alternative_id"]) var resolution_event_id := int(record_data["resolution_event_id"]) var closed_tick := int(record_data["closed_tick"]) var close_reason := StringName(record_data["close_reason"]) if ( situation_id < 0 or definition_id.is_empty() or dedupe_key.is_empty() or status not in VALID_STATUSES or created_tick < 0 or trigger_event_id < NO_EVENT_ID or not record_data["context"] is Dictionary ): return null if status == STATUS_ACTIVE: if resolution_event_id != NO_EVENT_ID or closed_tick != -1 or not close_reason.is_empty(): return null else: if closed_tick < created_tick or close_reason.is_empty(): return null if status != STATUS_RESOLVED and resolution_event_id != NO_EVENT_ID: return null var record := create( situation_id, definition_id, dedupe_key, created_tick, trigger_event_id, record_data["context"] ) if not selected_alternative_id.is_empty(): record.select_alternative(selected_alternative_id) if status != STATUS_ACTIVE: record.close(status, closed_tick, close_reason, resolution_event_id) return record static func from_opportunity( opportunity: OpportunityStateRecord, mapped_definition_id: StringName = &"" ) -> SituationStateRecord: if opportunity == null: return null var context := { "opportunity_id": opportunity.get_opportunity_id(), "interested_npc_id": opportunity.get_interested_npc_id(), "target_id": String(opportunity.get_target_id()), "resource_id": String(opportunity.get_resource_id()), "target_amount": opportunity.get_target_amount(), } var dedupe_key := "opportunity:%d" % opportunity.get_opportunity_id() var definition_id := mapped_definition_id if definition_id.is_empty(): definition_id = opportunity.get_opportunity_type() var record := create( opportunity.get_opportunity_id(), definition_id, dedupe_key, opportunity.get_created_tick(), opportunity.get_trigger_event_id(), context ) if opportunity.get_status() == OpportunityStateRecord.STATUS_RESOLVED: record.close( STATUS_RESOLVED, opportunity.get_closed_tick(), &"opportunity_resolved", opportunity.get_resolution_event_id() ) elif opportunity.get_status() == OpportunityStateRecord.STATUS_INVALIDATED: record.close( STATUS_EXPIRED, opportunity.get_closed_tick(), opportunity.get_invalidation_reason() ) return record func get_situation_id() -> int: return int(data["situation_id"]) func get_definition_id() -> StringName: return StringName(data["definition_id"]) func get_dedupe_key() -> String: return String(data["dedupe_key"]) func get_status() -> StringName: return StringName(data["status"]) func get_created_tick() -> int: return int(data["created_tick"]) func get_trigger_event_id() -> int: return int(data["trigger_event_id"]) func get_selected_alternative_id() -> StringName: return StringName(data["selected_alternative_id"]) func get_resolution_event_id() -> int: return int(data["resolution_event_id"]) func get_closed_tick() -> int: return int(data["closed_tick"]) func get_close_reason() -> StringName: return StringName(data["close_reason"]) func get_context() -> Dictionary: return data["context"].duplicate(true) func is_active() -> bool: return get_status() == STATUS_ACTIVE 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 close( status: StringName, closed_tick: int, reason: StringName, resolution_event_id: int = NO_EVENT_ID ) -> bool: if ( not is_active() or status not in [STATUS_RESOLVED, STATUS_EXPIRED, STATUS_SUPERSEDED] or closed_tick < get_created_tick() or reason.is_empty() ): return false if status != STATUS_RESOLVED and resolution_event_id != NO_EVENT_ID: return false data["status"] = String(status) data["resolution_event_id"] = resolution_event_id data["closed_tick"] = closed_tick data["close_reason"] = String(reason) return true func to_dictionary() -> Dictionary: return data.duplicate(true)