Preserve approved pixel tower prototype as development baseline
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
class_name AuthorityIncident
|
||||
extends RefCounted
|
||||
|
||||
enum State {
|
||||
OPEN,
|
||||
RESOLVED,
|
||||
DISMISSED,
|
||||
}
|
||||
|
||||
var incident_id: StringName = &""
|
||||
var incident_type: StringName = &""
|
||||
var created_at := 0
|
||||
var location_cell := Vector3i.ZERO
|
||||
var facility_id: StringName = &""
|
||||
var actor_ids: Array[StringName] = []
|
||||
var severity := 0
|
||||
var heat_delta := 0
|
||||
var detector_ids: Array[StringName] = []
|
||||
var evidence_ids: Array[StringName] = []
|
||||
var state: State = State.OPEN
|
||||
var resolution_id: StringName = &""
|
||||
var state_reason: StringName = &""
|
||||
var revision := 0
|
||||
|
||||
|
||||
func configure(
|
||||
p_incident_id: StringName,
|
||||
p_incident_type: StringName,
|
||||
p_created_at: int,
|
||||
options: Dictionary = {}
|
||||
) -> AuthorityIncident:
|
||||
incident_id = p_incident_id
|
||||
incident_type = p_incident_type
|
||||
created_at = maxi(p_created_at, 0)
|
||||
location_cell = _vector3i(_value(options, &"location_cell", Vector3i.ZERO))
|
||||
facility_id = StringName(_value(options, &"facility_id", &""))
|
||||
actor_ids = _sorted_unique_names(_value(options, &"actor_ids", []))
|
||||
severity = clampi(int(_value(options, &"severity", 0)), 0, 100)
|
||||
heat_delta = clampi(int(_value(options, &"heat_delta", severity / 5)), 0, 100)
|
||||
detector_ids = _sorted_unique_names(_value(options, &"detector_ids", []))
|
||||
evidence_ids = _sorted_unique_names(_value(options, &"evidence_ids", []))
|
||||
state = _state_from_value(_value(options, &"state", State.OPEN))
|
||||
resolution_id = StringName(_value(options, &"resolution_id", &""))
|
||||
state_reason = StringName(_value(options, &"state_reason", &""))
|
||||
revision = maxi(int(_value(options, &"revision", 0)), 0)
|
||||
return self
|
||||
|
||||
|
||||
func add_detector(detector_id: StringName) -> bool:
|
||||
if detector_id.is_empty() or detector_ids.has(detector_id):
|
||||
return false
|
||||
detector_ids.append(detector_id)
|
||||
detector_ids.sort_custom(_name_less)
|
||||
revision += 1
|
||||
return true
|
||||
|
||||
|
||||
func add_evidence(evidence_id: StringName) -> bool:
|
||||
if evidence_id.is_empty() or evidence_ids.has(evidence_id):
|
||||
return false
|
||||
evidence_ids.append(evidence_id)
|
||||
evidence_ids.sort_custom(_name_less)
|
||||
revision += 1
|
||||
return true
|
||||
|
||||
|
||||
func resolve(p_resolution_id: StringName, reason: StringName = &"") -> bool:
|
||||
if state != State.OPEN or p_resolution_id.is_empty():
|
||||
return false
|
||||
state = State.RESOLVED
|
||||
resolution_id = p_resolution_id
|
||||
state_reason = reason
|
||||
revision += 1
|
||||
return true
|
||||
|
||||
|
||||
func dismiss(reason: StringName) -> bool:
|
||||
if state != State.OPEN:
|
||||
return false
|
||||
state = State.DISMISSED
|
||||
state_reason = reason
|
||||
revision += 1
|
||||
return true
|
||||
|
||||
|
||||
func is_valid() -> bool:
|
||||
if incident_id.is_empty() or incident_type.is_empty() or created_at < 0:
|
||||
return false
|
||||
if severity < 0 or severity > 100 or heat_delta < 0 or heat_delta > 100 or revision < 0:
|
||||
return false
|
||||
if actor_ids != _sorted_unique_names(actor_ids):
|
||||
return false
|
||||
if detector_ids != _sorted_unique_names(detector_ids):
|
||||
return false
|
||||
if evidence_ids != _sorted_unique_names(evidence_ids):
|
||||
return false
|
||||
if state == State.RESOLVED and resolution_id.is_empty():
|
||||
return false
|
||||
if state != State.RESOLVED and not resolution_id.is_empty():
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func create_snapshot() -> Dictionary:
|
||||
return {
|
||||
&"incident_id": String(incident_id),
|
||||
&"incident_type": String(incident_type),
|
||||
&"created_at": created_at,
|
||||
&"location_cell": [location_cell.x, location_cell.y, location_cell.z],
|
||||
&"facility_id": String(facility_id),
|
||||
&"actor_ids": _names_to_strings(actor_ids),
|
||||
&"severity": severity,
|
||||
&"heat_delta": heat_delta,
|
||||
&"detector_ids": _names_to_strings(detector_ids),
|
||||
&"evidence_ids": _names_to_strings(evidence_ids),
|
||||
&"state": State.keys()[state],
|
||||
&"resolution_id": String(resolution_id),
|
||||
&"state_reason": String(state_reason),
|
||||
&"revision": revision,
|
||||
}
|
||||
|
||||
|
||||
static func from_snapshot(snapshot: Dictionary) -> AuthorityIncident:
|
||||
if not _snapshot_is_valid(snapshot):
|
||||
return null
|
||||
var incident := AuthorityIncident.new().configure(
|
||||
StringName(_value(snapshot, &"incident_id", &"")),
|
||||
StringName(_value(snapshot, &"incident_type", &"")),
|
||||
int(_value(snapshot, &"created_at", -1)),
|
||||
snapshot
|
||||
)
|
||||
return incident if incident.is_valid() else null
|
||||
|
||||
|
||||
static func _snapshot_is_valid(snapshot: Dictionary) -> bool:
|
||||
for key: StringName in [
|
||||
&"incident_id", &"incident_type", &"created_at", &"location_cell",
|
||||
&"facility_id", &"actor_ids", &"severity", &"heat_delta",
|
||||
&"detector_ids", &"evidence_ids", &"state", &"resolution_id",
|
||||
&"state_reason", &"revision",
|
||||
]:
|
||||
if not _has_key(snapshot, key):
|
||||
return false
|
||||
if StringName(_value(snapshot, &"incident_id", &"")).is_empty() \
|
||||
or StringName(_value(snapshot, &"incident_type", &"")).is_empty():
|
||||
return false
|
||||
if not _value(snapshot, &"location_cell", null) is Array \
|
||||
or (_value(snapshot, &"location_cell", []) as Array).size() != 3:
|
||||
return false
|
||||
if not _value(snapshot, &"actor_ids", null) is Array \
|
||||
or not _value(snapshot, &"detector_ids", null) is Array \
|
||||
or not _value(snapshot, &"evidence_ids", null) is Array:
|
||||
return false
|
||||
if not _name_array_is_canonical(_value(snapshot, &"actor_ids", [])) \
|
||||
or not _name_array_is_canonical(_value(snapshot, &"detector_ids", [])) \
|
||||
or not _name_array_is_canonical(_value(snapshot, &"evidence_ids", [])):
|
||||
return false
|
||||
var severity_value := int(_value(snapshot, &"severity", -1))
|
||||
var heat_value := int(_value(snapshot, &"heat_delta", -1))
|
||||
if int(_value(snapshot, &"created_at", -1)) < 0 or severity_value < 0 or severity_value > 100:
|
||||
return false
|
||||
if heat_value < 0 or heat_value > 100 or int(_value(snapshot, &"revision", -1)) < 0:
|
||||
return false
|
||||
return State.keys().has(String(_value(snapshot, &"state", "")))
|
||||
|
||||
|
||||
static func _state_from_value(value: Variant) -> State:
|
||||
if value is String or value is StringName:
|
||||
var index := State.keys().find(String(value))
|
||||
return (index as State) if index >= 0 else State.OPEN
|
||||
var number := int(value)
|
||||
return (number as State) if number >= 0 and number < State.size() else State.OPEN
|
||||
|
||||
|
||||
static func _vector3i(value: Variant) -> Vector3i:
|
||||
if value is Vector3i:
|
||||
return value
|
||||
if value is Array and value.size() >= 3:
|
||||
return Vector3i(int(value[0]), int(value[1]), int(value[2]))
|
||||
if value is Dictionary:
|
||||
return Vector3i(int(value.get(&"x", 0)), int(value.get(&"y", 0)), int(value.get(&"z", 0)))
|
||||
return Vector3i.ZERO
|
||||
|
||||
|
||||
static func _sorted_unique_names(values: Variant) -> Array[StringName]:
|
||||
var result: Array[StringName] = []
|
||||
if values is Array:
|
||||
for raw_value: Variant in values:
|
||||
var value := StringName(raw_value)
|
||||
if not value.is_empty() and not result.has(value):
|
||||
result.append(value)
|
||||
result.sort_custom(_name_less)
|
||||
return result
|
||||
|
||||
|
||||
static func _names_to_strings(values: Array[StringName]) -> Array[String]:
|
||||
var result: Array[String] = []
|
||||
for value: StringName in values:
|
||||
result.append(String(value))
|
||||
return result
|
||||
|
||||
|
||||
static func _name_less(a: StringName, b: StringName) -> bool:
|
||||
return String(a) < String(b)
|
||||
|
||||
|
||||
static func _value(source: Dictionary, key: StringName, fallback: Variant) -> Variant:
|
||||
if source.has(key):
|
||||
return source[key]
|
||||
return source.get(String(key), fallback)
|
||||
|
||||
|
||||
static func _has_key(source: Dictionary, key: StringName) -> bool:
|
||||
return source.has(key) or source.has(String(key))
|
||||
|
||||
|
||||
static func _name_array_is_canonical(value: Variant) -> bool:
|
||||
if not value is Array:
|
||||
return false
|
||||
var previous := ""
|
||||
for index: int in range(value.size()):
|
||||
var current := String(value[index])
|
||||
if current.is_empty() or (index > 0 and current <= previous):
|
||||
return false
|
||||
previous = current
|
||||
return true
|
||||
Reference in New Issue
Block a user