Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
This commit is contained in:
@@ -145,6 +145,12 @@ func set_operational_status(
|
||||
var related := StringName(raw_related)
|
||||
if related == instance_id or (not related.is_empty() and not has_instance(related)):
|
||||
return _invalid("Related facility is missing or refers to itself.")
|
||||
if instance.requires_operating_kitchen() and not related.is_empty():
|
||||
var kitchen := get_instance(related)
|
||||
if kitchen.definition_id != &"kitchen" or kitchen.origin.z != WorldService.LAYER_BACK:
|
||||
return _invalid("Dining facilities require one existing BACK kitchen.", {&"reason": &"INVALID_RELATED_KITCHEN"})
|
||||
if not _required_related_placement_matches(instance, kitchen):
|
||||
return _invalid("The BACK kitchen must lie inside this FRONT dining facility's XY footprint.", {&"reason": &"INVALID_RELATED_KITCHEN_PLACEMENT"})
|
||||
var seen := {instance_id: true}
|
||||
var cursor := related
|
||||
while not cursor.is_empty() and has_instance(cursor):
|
||||
@@ -253,7 +259,7 @@ func create_request(
|
||||
)
|
||||
|
||||
|
||||
func enqueue_request(request: Variant, absolute_minute: int = -1) -> CommandResult:
|
||||
func enqueue_request(request: Variant, absolute_minute: int = -1, commit_clock: bool = true) -> CommandResult:
|
||||
if request == null or not request is ServiceRequest:
|
||||
return _invalid("Service request is missing or has an invalid type.")
|
||||
var queued_request: ServiceRequest = request
|
||||
@@ -269,7 +275,11 @@ func enqueue_request(request: Variant, absolute_minute: int = -1) -> CommandResu
|
||||
"Facility queue is full.",
|
||||
{&"instance_id": instance.instance_id, &"queue_capacity": instance.queue_capacity}
|
||||
)
|
||||
var minute := _resolve_minute(absolute_minute)
|
||||
# Queue registration can precede the Clock increment in APPLY_COMMANDS.
|
||||
# Preserve the request effective time without claiming participant advancement.
|
||||
if not commit_clock and absolute_minute != current_minute + 1:
|
||||
return _invalid("Deferred queue registration must target the next uncommitted minute.")
|
||||
var minute := _resolve_minute(absolute_minute) if commit_clock else absolute_minute
|
||||
queued_request.enqueued_at = minute
|
||||
_requests[queued_request.request_id] = queued_request
|
||||
_known_request_ids[queued_request.request_id] = true
|
||||
@@ -342,6 +352,76 @@ func cancel_request(request_id: StringName, reason: StringName = &"GUEST_LEFT")
|
||||
return _not_found("Queued service request", request_id)
|
||||
|
||||
|
||||
func _required_related_kind_matches(instance: FacilityInstance) -> bool:
|
||||
if not instance.requires_operating_kitchen():
|
||||
return true
|
||||
var kitchen := get_instance(instance.related_facility_id)
|
||||
return kitchen != null and kitchen.definition_id == &"kitchen" and kitchen.origin.z == WorldService.LAYER_BACK
|
||||
|
||||
|
||||
# Original 2.2 and 8.3 put a BACK kitchen behind the FRONT dining venue.
|
||||
# XY containment is Root's operational interpretation, not a width/origin-x
|
||||
# equality rule. Use actual occupied cells; do not synthesize missing geometry,
|
||||
# rewrite old IDs, or persist this derived predicate.
|
||||
func _required_related_placement_matches(instance: FacilityInstance, proposed_kitchen: FacilityInstance = null) -> bool:
|
||||
if not instance.requires_operating_kitchen():
|
||||
return true
|
||||
var kitchen: FacilityInstance = proposed_kitchen if proposed_kitchen != null else get_instance(instance.related_facility_id)
|
||||
if kitchen == null or kitchen.definition_id != &"kitchen" \
|
||||
or instance.origin.z != WorldService.LAYER_FRONT or kitchen.origin.z != WorldService.LAYER_BACK \
|
||||
or instance.occupied_cells.is_empty() or kitchen.occupied_cells.is_empty():
|
||||
return false
|
||||
var front_xy: Dictionary = {}
|
||||
for cell: Vector3i in instance.occupied_cells:
|
||||
if cell.z != WorldService.LAYER_FRONT:
|
||||
return false
|
||||
front_xy[Vector2i(cell.x, cell.y)] = true
|
||||
for cell: Vector3i in kitchen.occupied_cells:
|
||||
if cell.z != WorldService.LAYER_BACK or not front_xy.has(Vector2i(cell.x, cell.y)):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
# Read-only derived eligibility. Legacy cached state/IDs remain untouched until
|
||||
# actual gameplay evaluates a facility or advances one minute.
|
||||
func get_required_kitchen_report(instance_id: StringName) -> Dictionary:
|
||||
var instance := get_instance(instance_id)
|
||||
if instance == null:
|
||||
return {}
|
||||
var required := instance.requires_operating_kitchen()
|
||||
var related := instance.related_facility_id
|
||||
var kitchen := get_instance(related)
|
||||
var valid_kind := _required_related_kind_matches(instance)
|
||||
var valid_placement := _required_related_placement_matches(instance)
|
||||
var operating := not required or (valid_placement and _restored_dependency_operates(related, {}))
|
||||
var reason: StringName = &""
|
||||
if required:
|
||||
if related.is_empty(): reason = &"KITCHEN_NOT_LINKED"
|
||||
elif kitchen == null: reason = &"KITCHEN_REMOVED"
|
||||
elif not valid_kind: reason = &"INVALID_RELATED_KITCHEN"
|
||||
elif not valid_placement: reason = &"INVALID_RELATED_KITCHEN_PLACEMENT"
|
||||
elif not kitchen.suspension_reason.is_empty(): reason = &"KITCHEN_SUSPENDED"
|
||||
elif not operating: reason = &"KITCHEN_INACTIVE"
|
||||
return {&"required": required, &"related_facility_id": related, &"exists": kitchen != null, &"valid_kind": valid_kind, &"operating": operating, &"reason": reason}
|
||||
|
||||
|
||||
# Read-only eligibility of a proposed link. Does not select a kitchen, alter an
|
||||
# existing related ID, evaluate state, advance time, or clear persistent suspension.
|
||||
func get_kitchen_placement_report(instance_id: StringName, kitchen_id: StringName) -> Dictionary:
|
||||
var instance := get_instance(instance_id)
|
||||
var kitchen := get_instance(kitchen_id)
|
||||
var required := instance != null and instance.requires_operating_kitchen()
|
||||
var valid_kind := kitchen != null and kitchen.definition_id == &"kitchen" and kitchen.origin.z == WorldService.LAYER_BACK
|
||||
var valid_placement := required and valid_kind and _required_related_placement_matches(instance, kitchen)
|
||||
var reason: StringName = &""
|
||||
if instance == null: reason = &"FACILITY_NOT_FOUND"
|
||||
elif not required: reason = &"KITCHEN_NOT_REQUIRED"
|
||||
elif kitchen == null: reason = &"KITCHEN_REMOVED"
|
||||
elif not valid_kind: reason = &"INVALID_RELATED_KITCHEN"
|
||||
elif not valid_placement: reason = &"INVALID_RELATED_KITCHEN_PLACEMENT"
|
||||
return {&"facility_id": instance_id, &"kitchen_id": kitchen_id, &"required": required, &"exists": kitchen != null, &"valid_kind": valid_kind, &"valid_placement": valid_placement, &"reason": reason}
|
||||
|
||||
|
||||
func refresh_operating_dependencies(minute: int) -> void:
|
||||
var resolved: Dictionary = {}
|
||||
for instance_id: StringName in _sorted_keys(_instances):
|
||||
@@ -357,7 +437,7 @@ func _resolve_operating_dependency(id: StringName, minute: int, resolved: Dictio
|
||||
var instance := get_instance(id)
|
||||
instance.apply_pending_policy(minute)
|
||||
var related := instance.related_facility_id
|
||||
instance.related_facility_operating = related.is_empty() or _resolve_operating_dependency(related, minute, resolved, visiting)
|
||||
instance.related_facility_operating = _required_related_placement_matches(instance) and (related.is_empty() or _resolve_operating_dependency(related, minute, resolved, visiting))
|
||||
var old_state := instance.state
|
||||
instance.evaluate_state(minute)
|
||||
_emit_state_change(instance, old_state)
|
||||
@@ -686,7 +766,7 @@ func restore_snapshot(snapshot: Dictionary) -> bool:
|
||||
_next_service_sequence = restored_next_sequence
|
||||
current_minute = restored_minute
|
||||
for instance: FacilityInstance in _instances.values():
|
||||
instance.related_facility_operating = _restored_dependency_operates(instance.related_facility_id, {})
|
||||
instance.related_facility_operating = _required_related_placement_matches(instance) and _restored_dependency_operates(instance.related_facility_id, {})
|
||||
instance.operation_state = FacilityInstanceType.OperationState.SUSPENDED if not instance.suspension_reason.is_empty() else (FacilityInstanceType.OperationState.OPERATING if instance.state in [FacilityInstanceType.State.OPEN, FacilityInstanceType.State.DEGRADED] and instance.related_facility_operating else FacilityInstanceType.OperationState.INACTIVE)
|
||||
last_error = ""
|
||||
return true
|
||||
@@ -699,7 +779,7 @@ func _restored_dependency_operates(id: StringName, seen: Dictionary) -> bool:
|
||||
return false
|
||||
seen[id] = true
|
||||
var instance := get_instance(id)
|
||||
return instance.suspension_reason.is_empty() and instance.state in [FacilityInstanceType.State.OPEN, FacilityInstanceType.State.DEGRADED] and _restored_dependency_operates(instance.related_facility_id, seen)
|
||||
return _required_related_placement_matches(instance) and instance.suspension_reason.is_empty() and instance.state in [FacilityInstanceType.State.OPEN, FacilityInstanceType.State.DEGRADED] and _restored_dependency_operates(instance.related_facility_id, seen)
|
||||
|
||||
|
||||
func _saved_policy_is_valid(policy: Dictionary) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user