233 lines
7.1 KiB
GDScript
233 lines
7.1 KiB
GDScript
class_name ServiceInstance
|
|
extends RefCounted
|
|
|
|
enum State {
|
|
ACTIVE,
|
|
PAUSED,
|
|
WAITING_FOR_STAFF,
|
|
COMPLETED,
|
|
FAILED,
|
|
CANCELLED,
|
|
}
|
|
|
|
const NO_DEADLINE := -1
|
|
|
|
var service_id: StringName = &""
|
|
var request_id: StringName = &""
|
|
var facility_id: StringName = &""
|
|
var started_at := 0
|
|
var finish_at := 1
|
|
var guest_ids: Array[StringName] = []
|
|
var staff_ids: Array[StringName] = []
|
|
var quality_snapshot := 0
|
|
var price_snapshot := 0
|
|
var policy_snapshot: Dictionary = {}
|
|
var inventory_reservations: Array[StringName] = []
|
|
var interruption_policy: Dictionary = {}
|
|
var duration_minutes := 1
|
|
var elapsed_minutes := 0
|
|
var state: State = State.ACTIVE
|
|
var interruption_reason: StringName = &""
|
|
var interrupted_at := -1
|
|
var staff_grace_deadline := NO_DEADLINE
|
|
var completed_at := -1
|
|
var failure_reason: StringName = &""
|
|
|
|
|
|
func configure(
|
|
p_service_id: StringName,
|
|
p_request_id: StringName,
|
|
p_facility_id: StringName,
|
|
p_started_at: int,
|
|
p_duration_minutes: int,
|
|
p_guest_ids: Array[StringName],
|
|
p_staff_ids: Array[StringName],
|
|
options: Dictionary = {}
|
|
) -> ServiceInstance:
|
|
service_id = p_service_id
|
|
request_id = p_request_id
|
|
facility_id = p_facility_id
|
|
started_at = maxi(p_started_at, 0)
|
|
duration_minutes = maxi(p_duration_minutes, 1)
|
|
finish_at = started_at + duration_minutes
|
|
guest_ids.assign(p_guest_ids)
|
|
staff_ids.assign(p_staff_ids)
|
|
quality_snapshot = int(_value(options, &"quality_snapshot", 0))
|
|
price_snapshot = maxi(int(_value(options, &"price_snapshot", 0)), 0)
|
|
policy_snapshot = _dictionary_copy(_value(options, &"policy_snapshot", {}))
|
|
inventory_reservations = _string_name_array(
|
|
_value(options, &"inventory_reservations", [])
|
|
)
|
|
interruption_policy = _dictionary_copy(_value(options, &"interruption_policy", {}))
|
|
return self
|
|
|
|
|
|
func is_valid() -> bool:
|
|
return not service_id.is_empty() \
|
|
and not request_id.is_empty() \
|
|
and not facility_id.is_empty() \
|
|
and not guest_ids.is_empty() \
|
|
and duration_minutes > 0 \
|
|
and elapsed_minutes >= 0 \
|
|
and elapsed_minutes <= duration_minutes
|
|
|
|
|
|
func is_terminal() -> bool:
|
|
return state in [State.COMPLETED, State.FAILED, State.CANCELLED]
|
|
|
|
|
|
func can_progress() -> bool:
|
|
return state == State.ACTIVE and elapsed_minutes < duration_minutes
|
|
|
|
|
|
func advance_one_minute() -> bool:
|
|
if not can_progress():
|
|
return false
|
|
elapsed_minutes = mini(elapsed_minutes + 1, duration_minutes)
|
|
return elapsed_minutes >= duration_minutes
|
|
|
|
|
|
func pause(reason: StringName, absolute_minute: int) -> bool:
|
|
if state != State.ACTIVE:
|
|
return false
|
|
state = State.PAUSED
|
|
interruption_reason = reason
|
|
interrupted_at = absolute_minute
|
|
return true
|
|
|
|
|
|
func wait_for_staff(reason: StringName, absolute_minute: int, grace_minutes := 5) -> bool:
|
|
if state not in [State.ACTIVE, State.PAUSED]:
|
|
return false
|
|
state = State.WAITING_FOR_STAFF
|
|
interruption_reason = reason
|
|
interrupted_at = absolute_minute
|
|
staff_grace_deadline = absolute_minute + maxi(grace_minutes, 0)
|
|
return true
|
|
|
|
|
|
func resume() -> bool:
|
|
if state not in [State.PAUSED, State.WAITING_FOR_STAFF]:
|
|
return false
|
|
state = State.ACTIVE
|
|
interruption_reason = &""
|
|
interrupted_at = -1
|
|
staff_grace_deadline = NO_DEADLINE
|
|
return true
|
|
|
|
|
|
func mark_completed(absolute_minute: int) -> void:
|
|
elapsed_minutes = duration_minutes
|
|
state = State.COMPLETED
|
|
completed_at = absolute_minute
|
|
interruption_reason = &""
|
|
failure_reason = &""
|
|
staff_grace_deadline = NO_DEADLINE
|
|
|
|
|
|
func mark_failed(reason: StringName, absolute_minute: int) -> void:
|
|
state = State.FAILED
|
|
failure_reason = reason
|
|
completed_at = absolute_minute
|
|
staff_grace_deadline = NO_DEADLINE
|
|
|
|
|
|
func mark_cancelled(reason: StringName, absolute_minute: int) -> void:
|
|
state = State.CANCELLED
|
|
failure_reason = reason
|
|
completed_at = absolute_minute
|
|
staff_grace_deadline = NO_DEADLINE
|
|
|
|
|
|
func remaining_minutes() -> int:
|
|
return maxi(duration_minutes - elapsed_minutes, 0)
|
|
|
|
|
|
func create_snapshot() -> Dictionary:
|
|
return {
|
|
&"service_id": String(service_id),
|
|
&"request_id": String(request_id),
|
|
&"facility_id": String(facility_id),
|
|
&"started_at": started_at,
|
|
&"finish_at": finish_at,
|
|
&"guest_ids": _names_to_strings(guest_ids),
|
|
&"staff_ids": _names_to_strings(staff_ids),
|
|
&"quality_snapshot": quality_snapshot,
|
|
&"price_snapshot": price_snapshot,
|
|
&"policy_snapshot": policy_snapshot.duplicate(true),
|
|
&"inventory_reservations": _names_to_strings(inventory_reservations),
|
|
&"interruption_policy": interruption_policy.duplicate(true),
|
|
&"duration_minutes": duration_minutes,
|
|
&"elapsed_minutes": elapsed_minutes,
|
|
&"state": State.keys()[state],
|
|
&"interruption_reason": String(interruption_reason),
|
|
&"interrupted_at": interrupted_at,
|
|
&"staff_grace_deadline": staff_grace_deadline,
|
|
&"completed_at": completed_at,
|
|
&"failure_reason": String(failure_reason),
|
|
}
|
|
|
|
|
|
static func from_snapshot(snapshot: Dictionary) -> ServiceInstance:
|
|
if not snapshot.get(&"guest_ids", null) is Array:
|
|
return null
|
|
if not snapshot.get(&"staff_ids", null) is Array:
|
|
return null
|
|
if not snapshot.get(&"policy_snapshot", null) is Dictionary:
|
|
return null
|
|
if not snapshot.get(&"inventory_reservations", null) is Array:
|
|
return null
|
|
if not snapshot.get(&"interruption_policy", null) is Dictionary:
|
|
return null
|
|
var service := ServiceInstance.new().configure(
|
|
StringName(snapshot.get(&"service_id", "")),
|
|
StringName(snapshot.get(&"request_id", "")),
|
|
StringName(snapshot.get(&"facility_id", "")),
|
|
int(snapshot.get(&"started_at", 0)),
|
|
int(snapshot.get(&"duration_minutes", 1)),
|
|
_string_name_array(snapshot.get(&"guest_ids", [])),
|
|
_string_name_array(snapshot.get(&"staff_ids", [])),
|
|
{
|
|
&"quality_snapshot": snapshot.get(&"quality_snapshot", 0),
|
|
&"price_snapshot": snapshot.get(&"price_snapshot", 0),
|
|
&"policy_snapshot": snapshot.get(&"policy_snapshot", {}),
|
|
&"inventory_reservations": snapshot.get(&"inventory_reservations", []),
|
|
&"interruption_policy": snapshot.get(&"interruption_policy", {}),
|
|
}
|
|
)
|
|
var state_index := State.keys().find(String(snapshot.get(&"state", "ACTIVE")))
|
|
if state_index < 0:
|
|
return null
|
|
service.state = state_index as State
|
|
service.finish_at = int(snapshot.get(&"finish_at", service.started_at + service.duration_minutes))
|
|
service.elapsed_minutes = int(snapshot.get(&"elapsed_minutes", 0))
|
|
service.interruption_reason = StringName(snapshot.get(&"interruption_reason", ""))
|
|
service.interrupted_at = int(snapshot.get(&"interrupted_at", -1))
|
|
service.staff_grace_deadline = int(snapshot.get(&"staff_grace_deadline", NO_DEADLINE))
|
|
service.completed_at = int(snapshot.get(&"completed_at", -1))
|
|
service.failure_reason = StringName(snapshot.get(&"failure_reason", ""))
|
|
return service if service.is_valid() else null
|
|
|
|
|
|
static func _value(source: Dictionary, key: StringName, fallback: Variant) -> Variant:
|
|
return source.get(key, source.get(String(key), fallback))
|
|
|
|
|
|
static func _dictionary_copy(source: Variant) -> Dictionary:
|
|
return source.duplicate(true) if source is Dictionary else {}
|
|
|
|
|
|
static func _string_name_array(source: Variant) -> Array[StringName]:
|
|
var values: Array[StringName] = []
|
|
if source is Array:
|
|
for value: Variant in source:
|
|
values.append(StringName(value))
|
|
return values
|
|
|
|
|
|
static func _names_to_strings(source: Array[StringName]) -> Array[String]:
|
|
var values: Array[String] = []
|
|
for value: StringName in source:
|
|
values.append(String(value))
|
|
return values
|