Preserve approved pixel tower prototype as development baseline
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
class_name ConstructionJob
|
||||
extends RefCounted
|
||||
|
||||
enum State {
|
||||
PLANNED,
|
||||
BUILDING,
|
||||
COMPLETED,
|
||||
CANCELLED,
|
||||
BLOCKED,
|
||||
}
|
||||
|
||||
const MAX_PROGRESS := 10_000
|
||||
const NORMAL_WORK_RATE := 10_000
|
||||
|
||||
var job_id: StringName = &""
|
||||
var definition_id: StringName = &""
|
||||
var origin := Vector3i.ZERO
|
||||
var rotation := 0
|
||||
var layer := 0
|
||||
var reserved_cells: Array[Vector3i] = []
|
||||
var progress := 0
|
||||
var state: State = State.PLANNED
|
||||
var state_reason := ""
|
||||
var assigned_builder_ids: Array[StringName] = []
|
||||
var construction_cost: Dictionary = {}
|
||||
var build_minutes := 60
|
||||
var work_units_done := 0
|
||||
var created_at := 0
|
||||
var started_at := -1
|
||||
var completed_at := -1
|
||||
var cancelled_at := -1
|
||||
var facility_instance_id: StringName = &""
|
||||
|
||||
|
||||
func configure(
|
||||
p_job_id: StringName,
|
||||
p_definition_id: StringName,
|
||||
p_origin: Vector3i,
|
||||
p_rotation: int,
|
||||
p_layer: int,
|
||||
p_reserved_cells: Array[Vector3i],
|
||||
p_construction_cost: Dictionary,
|
||||
p_build_minutes: int,
|
||||
p_created_at: int
|
||||
) -> ConstructionJob:
|
||||
job_id = p_job_id
|
||||
definition_id = p_definition_id
|
||||
origin = p_origin
|
||||
rotation = _normalize_rotation(p_rotation)
|
||||
layer = p_layer
|
||||
reserved_cells.assign(p_reserved_cells)
|
||||
construction_cost = _normalize_cost(p_construction_cost)
|
||||
build_minutes = maxi(p_build_minutes, 1)
|
||||
created_at = maxi(p_created_at, 0)
|
||||
return self
|
||||
|
||||
|
||||
func advance_one_minute(work_rate_basis_points: int = NORMAL_WORK_RATE, absolute_minute: int = -1) -> bool:
|
||||
if state == State.COMPLETED or state == State.CANCELLED:
|
||||
return false
|
||||
if progress >= MAX_PROGRESS:
|
||||
return true
|
||||
if work_rate_basis_points <= 0:
|
||||
mark_blocked("No effective construction work was supplied.")
|
||||
return false
|
||||
if started_at < 0:
|
||||
started_at = absolute_minute
|
||||
state = State.BUILDING
|
||||
state_reason = ""
|
||||
work_units_done += work_rate_basis_points
|
||||
progress = mini(MAX_PROGRESS, work_units_done / build_minutes)
|
||||
return progress >= MAX_PROGRESS
|
||||
|
||||
|
||||
func mark_blocked(reason: String) -> void:
|
||||
if state == State.COMPLETED or state == State.CANCELLED:
|
||||
return
|
||||
state = State.BLOCKED
|
||||
state_reason = reason
|
||||
|
||||
|
||||
func mark_completed(instance_id: StringName, absolute_minute: int = -1) -> void:
|
||||
progress = MAX_PROGRESS
|
||||
state = State.COMPLETED
|
||||
state_reason = ""
|
||||
facility_instance_id = instance_id
|
||||
completed_at = absolute_minute
|
||||
|
||||
|
||||
func mark_cancelled(absolute_minute: int = -1) -> void:
|
||||
state = State.CANCELLED
|
||||
state_reason = ""
|
||||
cancelled_at = absolute_minute
|
||||
|
||||
|
||||
func is_terminal() -> bool:
|
||||
return state == State.COMPLETED or state == State.CANCELLED
|
||||
|
||||
|
||||
func refundable_cost() -> Dictionary:
|
||||
var refund: Dictionary = {}
|
||||
var remaining_basis_points := maxi(MAX_PROGRESS - progress, 0)
|
||||
for resource_name: Variant in construction_cost:
|
||||
var amount := int(construction_cost[resource_name])
|
||||
refund[StringName(resource_name)] = amount * remaining_basis_points / MAX_PROGRESS
|
||||
return refund
|
||||
|
||||
|
||||
func create_snapshot() -> Dictionary:
|
||||
return {
|
||||
&"job_id": String(job_id),
|
||||
&"definition_id": String(definition_id),
|
||||
&"origin": [origin.x, origin.y, origin.z],
|
||||
&"rotation": rotation,
|
||||
&"layer": layer,
|
||||
&"reserved_cells": _serialize_cells(reserved_cells),
|
||||
&"progress": progress,
|
||||
&"state": State.keys()[state],
|
||||
&"state_reason": state_reason,
|
||||
&"assigned_builder_ids": _string_names_to_strings(assigned_builder_ids),
|
||||
&"construction_cost": construction_cost.duplicate(true),
|
||||
&"build_minutes": build_minutes,
|
||||
&"work_units_done": work_units_done,
|
||||
&"created_at": created_at,
|
||||
&"started_at": started_at,
|
||||
&"completed_at": completed_at,
|
||||
&"cancelled_at": cancelled_at,
|
||||
&"facility_instance_id": String(facility_instance_id),
|
||||
}
|
||||
|
||||
|
||||
static func from_snapshot(snapshot: Dictionary) -> ConstructionJob:
|
||||
if not _is_valid_snapshot(snapshot):
|
||||
return null
|
||||
var job := ConstructionJob.new()
|
||||
job.job_id = StringName(snapshot.get(&"job_id", ""))
|
||||
job.definition_id = StringName(snapshot.get(&"definition_id", ""))
|
||||
job.origin = _parse_cell(snapshot.get(&"origin", []))
|
||||
job.rotation = _normalize_rotation(int(snapshot.get(&"rotation", 0)))
|
||||
job.layer = int(snapshot.get(&"layer", job.origin.z))
|
||||
job.reserved_cells = _parse_cells(snapshot.get(&"reserved_cells", []))
|
||||
job.progress = clampi(int(snapshot.get(&"progress", 0)), 0, MAX_PROGRESS)
|
||||
var state_index := State.keys().find(String(snapshot.get(&"state", "PLANNED")))
|
||||
if state_index < 0:
|
||||
return null
|
||||
job.state = state_index as State
|
||||
job.state_reason = String(snapshot.get(&"state_reason", ""))
|
||||
var saved_builders: Variant = snapshot.get(&"assigned_builder_ids", [])
|
||||
if saved_builders is Array:
|
||||
for builder_id: Variant in saved_builders:
|
||||
job.assigned_builder_ids.append(StringName(builder_id))
|
||||
job.construction_cost = _normalize_cost(snapshot.get(&"construction_cost", {}))
|
||||
job.build_minutes = maxi(int(snapshot.get(&"build_minutes", 60)), 1)
|
||||
job.work_units_done = maxi(int(snapshot.get(&"work_units_done", 0)), 0)
|
||||
job.created_at = maxi(int(snapshot.get(&"created_at", 0)), 0)
|
||||
job.started_at = int(snapshot.get(&"started_at", -1))
|
||||
job.completed_at = int(snapshot.get(&"completed_at", -1))
|
||||
job.cancelled_at = int(snapshot.get(&"cancelled_at", -1))
|
||||
job.facility_instance_id = StringName(snapshot.get(&"facility_instance_id", ""))
|
||||
return job
|
||||
|
||||
|
||||
static func _is_valid_snapshot(snapshot: Dictionary) -> bool:
|
||||
if String(snapshot.get(&"job_id", "")).is_empty():
|
||||
return false
|
||||
if String(snapshot.get(&"definition_id", "")).is_empty():
|
||||
return false
|
||||
if not snapshot.get(&"origin", null) is Array:
|
||||
return false
|
||||
if not snapshot.get(&"reserved_cells", null) is Array:
|
||||
return false
|
||||
if not snapshot.get(&"construction_cost", null) is Dictionary:
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
static func _normalize_cost(source: Variant) -> Dictionary:
|
||||
var normalized: Dictionary = {}
|
||||
if not source is Dictionary:
|
||||
return normalized
|
||||
for resource_name: Variant in source:
|
||||
normalized[StringName(resource_name)] = maxi(int(source[resource_name]), 0)
|
||||
return normalized
|
||||
|
||||
|
||||
static func _normalize_rotation(value: int) -> int:
|
||||
var normalized := value % 360
|
||||
if normalized < 0:
|
||||
normalized += 360
|
||||
return (normalized / 90) * 90
|
||||
|
||||
|
||||
static func _parse_cell(source: Variant) -> Vector3i:
|
||||
if not source is Array or source.size() != 3:
|
||||
return Vector3i.ZERO
|
||||
return Vector3i(int(source[0]), int(source[1]), int(source[2]))
|
||||
|
||||
|
||||
static func _parse_cells(source: Variant) -> Array[Vector3i]:
|
||||
var cells: Array[Vector3i] = []
|
||||
if not source is Array:
|
||||
return cells
|
||||
for value: Variant in source:
|
||||
if value is Array and value.size() == 3:
|
||||
cells.append(_parse_cell(value))
|
||||
return cells
|
||||
|
||||
|
||||
static func _serialize_cells(source: Array[Vector3i]) -> Array[Array]:
|
||||
var serialized: Array[Array] = []
|
||||
for cell: Vector3i in source:
|
||||
serialized.append([cell.x, cell.y, cell.z])
|
||||
return serialized
|
||||
|
||||
|
||||
static func _string_names_to_strings(source: Array[StringName]) -> Array[String]:
|
||||
var serialized: Array[String] = []
|
||||
for value: StringName in source:
|
||||
serialized.append(String(value))
|
||||
return serialized
|
||||
Reference in New Issue
Block a user