class_name AuthorityEvidence extends RefCounted enum DiscoveryMethod { LATENT, REACHED, LINE_OF_SIGHT, TRUSTED_SOURCE, KNOWN_ILLEGAL, } var evidence_id: StringName = &"" var incident_id: StringName = &"" var source: StringName = &"" var severity := 0 var facility_id: StringName = &"" var location_cell := Vector3i.ZERO var created_at := 0 var expires_at := -1 var discovered_at := -1 var discovered_by: StringName = &"" var discovery_method: DiscoveryMethod = DiscoveryMethod.LATENT var revision := 0 func configure( p_evidence_id: StringName, p_source: StringName, p_severity: int, options: Dictionary = {} ) -> AuthorityEvidence: evidence_id = p_evidence_id incident_id = StringName(_value(options, &"incident_id", &"")) source = p_source severity = clampi(p_severity, 0, 100) facility_id = StringName(_value(options, &"facility_id", &"")) location_cell = _vector3i(_value(options, &"location_cell", Vector3i.ZERO)) created_at = maxi(int(_value(options, &"created_at", 0)), 0) expires_at = int(_value(options, &"expires_at", -1)) discovered_at = int(_value(options, &"discovered_at", -1)) discovered_by = StringName(_value(options, &"discovered_by", &"")) discovery_method = _method_from_value(_value(options, &"discovery_method", DiscoveryMethod.LATENT)) revision = maxi(int(_value(options, &"revision", 0)), 0) return self func is_discovered() -> bool: return discovered_at >= 0 func is_expired(absolute_minute: int) -> bool: return expires_at >= 0 and absolute_minute >= expires_at func mark_discovered( inspector_id: StringName, absolute_minute: int, method: DiscoveryMethod ) -> bool: if is_discovered() or inspector_id.is_empty() or absolute_minute < created_at: return false if method == DiscoveryMethod.LATENT: return false discovered_at = absolute_minute discovered_by = inspector_id discovery_method = method revision += 1 return true func is_valid() -> bool: if evidence_id.is_empty() or source.is_empty() or severity < 0 or severity > 100: return false if created_at < 0 or expires_at < -1 or revision < 0: return false if expires_at >= 0 and expires_at < created_at: return false if discovered_at < -1: return false if discovered_at >= 0: if discovered_by.is_empty() or discovery_method == DiscoveryMethod.LATENT: return false if discovered_at < created_at: return false else: if not discovered_by.is_empty() or discovery_method != DiscoveryMethod.LATENT: return false return true func create_snapshot() -> Dictionary: return { &"evidence_id": String(evidence_id), &"incident_id": String(incident_id), &"source": String(source), &"severity": severity, &"facility_id": String(facility_id), &"location_cell": [location_cell.x, location_cell.y, location_cell.z], &"created_at": created_at, &"expires_at": expires_at, &"discovered_at": discovered_at, &"discovered_by": String(discovered_by), &"discovery_method": DiscoveryMethod.keys()[discovery_method], &"revision": revision, } static func from_snapshot(snapshot: Dictionary) -> AuthorityEvidence: if not _snapshot_is_valid(snapshot): return null var evidence := AuthorityEvidence.new().configure( StringName(_value(snapshot, &"evidence_id", &"")), StringName(_value(snapshot, &"source", &"")), int(_value(snapshot, &"severity", -1)), snapshot ) return evidence if evidence.is_valid() else null static func _snapshot_is_valid(snapshot: Dictionary) -> bool: for key: StringName in [ &"evidence_id", &"incident_id", &"source", &"severity", &"facility_id", &"location_cell", &"created_at", &"expires_at", &"discovered_at", &"discovered_by", &"discovery_method", &"revision", ]: if not _has_key(snapshot, key): return false if StringName(_value(snapshot, &"evidence_id", &"")).is_empty() \ or StringName(_value(snapshot, &"source", &"")).is_empty(): return false if not _value(snapshot, &"location_cell", null) is Array \ or (_value(snapshot, &"location_cell", []) as Array).size() != 3: return false var severity_value := int(_value(snapshot, &"severity", -1)) if severity_value < 0 or severity_value > 100: return false if int(_value(snapshot, &"created_at", -1)) < 0 \ or int(_value(snapshot, &"expires_at", -2)) < -1 \ or int(_value(snapshot, &"discovered_at", -2)) < -1 \ or int(_value(snapshot, &"revision", -1)) < 0: return false return DiscoveryMethod.keys().has(String(_value(snapshot, &"discovery_method", ""))) static func _method_from_value(value: Variant) -> DiscoveryMethod: if value is String or value is StringName: var index := DiscoveryMethod.keys().find(String(value)) return (index as DiscoveryMethod) if index >= 0 else DiscoveryMethod.LATENT var number := int(value) return (number as DiscoveryMethod) if number >= 0 and number < DiscoveryMethod.size() else DiscoveryMethod.LATENT 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 _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))