class_name ConstructionService extends RefCounted signal job_created(job: ConstructionJob) signal job_changed(job: ConstructionJob) signal facility_completed(instance: Dictionary) signal facility_demolished(instance_id: StringName, refund: Dictionary) const PlacementReportType = preload("res://scripts/construction/placement_report.gd") const ConstructionJobType = preload("res://scripts/construction/construction_job.gd") const CommandResultType = preload("res://scripts/core/command_result.gd") const SNAPSHOT_VERSION := 2 const LEGACY_SNAPSHOT_VERSION := 1 const DEFAULT_BUILD_MINUTES := 60 const DEMOLITION_MATERIAL_REFUND_BASIS_POINTS := 2_500 const MAX_PROGRESS := 10_000 const MAX_WORK_RATE := 1_000_000 const MAX_WORK_UNITS := 9_000_000_000_000_000 const MAX_BUILD_MINUTES := 10_000_000 const MAX_JOBS := 20_000 const MAX_FACILITIES := 20_000 const MAX_SEQUENCE := 99_999_999 const DEFERRED_JOB_CHANGED: StringName = &"JOB_CHANGED" const DEFERRED_FACILITY_COMPLETED: StringName = &"FACILITY_COMPLETED" var default_build_minutes := DEFAULT_BUILD_MINUTES var current_minute := 0 var _world: Variant var _resource_state: Variant var _jobs: Dictionary = {} var _facilities: Dictionary = {} var _next_job_sequence := 1 var _next_facility_sequence := 1 var _last_progress_minutes: Dictionary = {} var _mutation_in_progress := false var _quarantined := false var _quarantine_reason := "" var _deferred_notifications: Array[Dictionary] = [] var _notification_staging_active := false var _notification_commit_active := false func _init(world: Variant = null, resource_state: Variant = null) -> void: _world = world _resource_state = resource_state func configure(world: Variant, resource_state: Variant) -> ConstructionService: _world = world _resource_state = resource_state return self func validate_placement( definition: Variant, origin: Vector3i, rotation: int = 0 ) -> PlacementReport: var report := PlacementReportType.new() if not _definition_is_valid(definition): report.add_error( PlacementReportType.ERROR_INVALID_DEFINITION, "The facility definition is missing or invalid." ) return report.finalize() var footprint: Vector2i = _definition_value(definition, &"footprint", Vector2i.ZERO) var expected_zone := int(_definition_value(definition, &"zone", 0)) var expected_layer := int(_definition_value(definition, &"layer", origin.z)) report.total_cost = _normalize_cost(_definition_value(definition, &"construction_cost", {})) report.build_time = _definition_build_minutes(definition) var missing_world_methods := _missing_methods( _world, [&"cells_for_footprint", &"is_in_bounds", &"get_zone", &"get_occupant", &"is_excavated", &"validate_support"] ) if not missing_world_methods.is_empty(): report.add_error( PlacementReportType.ERROR_MISSING_DEPENDENCY, "World placement services are unavailable.", [], {&"methods": missing_world_methods} ) if _resource_state == null: report.add_error( PlacementReportType.ERROR_MISSING_DEPENDENCY, "The resource state is unavailable." ) if missing_world_methods.is_empty(): report.cells = _world_cells_for_footprint( origin, footprint, _normalize_rotation(rotation), expected_layer ) if report.cells.is_empty(): report.add_error( PlacementReportType.ERROR_INVALID_DEFINITION, "The footprint produced no world cells." ) _validate_cell_constraints(report, origin, expected_zone, expected_layer) _validate_support(report, definition) _validate_cost(report) return report.finalize() func start_construction( definition: Variant, origin: Vector3i, rotation: int = 0, build_minutes: int = 0, created_at: int = -1 ) -> CommandResult: if not _mutation_allowed(): return _unavailable_result() if _jobs.size() >= MAX_JOBS or _next_job_sequence > MAX_SEQUENCE: return _failure("CONSTRUCTION_LIMIT", "Construction job capacity has been reached.") if build_minutes > MAX_BUILD_MINUTES: return _failure("INVALID_BUILD_TIME", "Construction build time exceeds the supported limit.") _mutation_in_progress = true var report := validate_placement(definition, origin, rotation) if not report.can_build: _mutation_in_progress = false return _failure("INVALID_PLACEMENT", "Facility placement is invalid.", { &"placement_report": report.to_dictionary(), }) var job_id := StringName("construction_%08d" % _next_job_sequence) var resource_snapshot: Variant = _capture_resource_state() var world_snapshot := _capture_world_state(report.cells) if not _spend_cost(report.total_cost): var payment_rolled_back := _restore_resource_state(resource_snapshot) _mutation_in_progress = false if not payment_rolled_back: _quarantine("Construction payment failed after mutating resources and could not be rolled back.") return _failure("PAYMENT_FAILED", "Construction resources changed before the command was applied.", { &"cost": report.total_cost.duplicate(true), &"rolled_back": payment_rolled_back, &"rollback_failed": not payment_rolled_back, }) if not _world_occupy(report.cells, job_id, &"construction"): var world_rolled_back := _restore_world_state(world_snapshot) var resources_rolled_back := _restore_resource_state(resource_snapshot) var rolled_back := world_rolled_back and resources_rolled_back _mutation_in_progress = false if not rolled_back: _quarantine("Construction reservation failed and transaction rollback was incomplete.") return _failure("RESERVATION_FAILED", "World reservation failed.", { &"job_id": job_id, &"rolled_back": rolled_back, &"rollback_failed": not rolled_back, &"world_rolled_back": world_rolled_back, &"resources_rolled_back": resources_rolled_back, }) var effective_minutes := build_minutes if build_minutes > 0 else report.build_time var effective_created_at := current_minute if created_at < 0 else created_at var definition_id := StringName(_definition_value(definition, &"id", &"")) var expected_layer := int(_definition_value(definition, &"layer", origin.z)) var job := ConstructionJobType.new().configure( job_id, definition_id, origin, rotation, expected_layer, report.cells, report.total_cost, effective_minutes, effective_created_at ) _jobs[job_id] = job _last_progress_minutes[job_id] = -1 _next_job_sequence += 1 _mutation_in_progress = false job_created.emit(job) return CommandResultType.succeeded(0, { &"job_id": job_id, &"placement_report": report.to_dictionary(), }) func get_job(job_id: StringName) -> ConstructionJob: return _jobs.get(job_id) as ConstructionJob func get_jobs() -> Array[ConstructionJob]: var result: Array[ConstructionJob] = [] var ids := _sorted_string_name_keys(_jobs) for job_id: StringName in ids: result.append(_jobs[job_id]) return result func get_facility(instance_id: StringName) -> Dictionary: if not _facilities.has(instance_id): return {} return (_facilities[instance_id] as Dictionary).duplicate(true) func get_facilities() -> Array[Dictionary]: var result: Array[Dictionary] = [] var ids := _sorted_string_name_keys(_facilities) for instance_id: StringName in ids: result.append((_facilities[instance_id] as Dictionary).duplicate(true)) return result func assign_builders(job_id: StringName, builder_ids: Array[StringName]) -> CommandResult: if not _mutation_allowed(): return _unavailable_result() var job := get_job(job_id) if job == null: return _not_found("Construction job", job_id) if job.is_terminal(): return _invalid_state("A terminal construction job cannot receive builders.", job_id) job.assigned_builder_ids.assign(builder_ids) _emit_job_changed(job) return CommandResultType.succeeded(0, {&"job_id": job_id}) func advance_one_minute( absolute_minute: int = -1, work_rates: Dictionary = {}, defer_notifications := false ) -> Array[CommandResult]: if _notification_staging_active or _notification_commit_active: return [_notification_unavailable_result(&"NOTIFICATION_REENTRY", "Construction notification dispatch is already active.")] if has_deferred_notifications(): return [_notification_unavailable_result( &"DEFERRED_NOTIFICATIONS_PENDING", "Deferred construction notifications must be committed or discarded before another advance." )] if not _mutation_allowed(): return [_unavailable_result()] _notification_staging_active = defer_notifications if absolute_minute < 0: current_minute += 1 else: current_minute = maxi(absolute_minute, current_minute) var results: Array[CommandResult] = [] for job_id: StringName in _sorted_string_name_keys(_jobs): var job: ConstructionJob = _jobs[job_id] if job.is_terminal(): continue var raw_rate: Variant = work_rates.get(job_id, ConstructionJobType.NORMAL_WORK_RATE) if not raw_rate is int: results.append(_failure("INVALID_WORK_RATE", "Construction work rate must be an integer.")) continue results.append(progress_job(job_id, int(raw_rate), current_minute)) _notification_staging_active = false return results func has_deferred_notifications() -> bool: return not _deferred_notifications.is_empty() func commit_deferred_notifications() -> bool: if _notification_staging_active or _notification_commit_active or _mutation_in_progress: return false if _deferred_notifications.is_empty(): return true _notification_commit_active = true var prepared: Array[Dictionary] = [] for notification: Dictionary in _deferred_notifications: var kind: StringName = notification.get(&"kind", &"") match kind: DEFERRED_JOB_CHANGED: var saved_job: Variant = notification.get(&"payload", null) if not saved_job is Dictionary: _notification_commit_active = false return false var job := ConstructionJobType.from_snapshot(saved_job.duplicate(true)) if job == null: _notification_commit_active = false return false prepared.append({&"kind": kind, &"payload": job}) DEFERRED_FACILITY_COMPLETED: var saved_instance: Variant = notification.get(&"payload", null) if not saved_instance is Dictionary: _notification_commit_active = false return false prepared.append({&"kind": kind, &"payload": saved_instance.duplicate(true)}) _: _notification_commit_active = false return false _deferred_notifications.clear() for notification: Dictionary in prepared: match StringName(notification[&"kind"]): DEFERRED_JOB_CHANGED: job_changed.emit(notification[&"payload"]) DEFERRED_FACILITY_COMPLETED: facility_completed.emit((notification[&"payload"] as Dictionary).duplicate(true)) _notification_commit_active = false return true func discard_deferred_notifications() -> bool: if _notification_staging_active or _notification_commit_active or _mutation_in_progress: return false _deferred_notifications.clear() return true func _emit_job_changed(job: ConstructionJob) -> void: if _notification_staging_active: _deferred_notifications.append({ &"kind": DEFERRED_JOB_CHANGED, &"payload": job.create_snapshot().duplicate(true), }) return job_changed.emit(job) func _emit_facility_completed(instance: Dictionary) -> void: var payload := instance.duplicate(true) if _notification_staging_active: _deferred_notifications.append({ &"kind": DEFERRED_FACILITY_COMPLETED, &"payload": payload, }) return facility_completed.emit(payload) func progress_job( job_id: StringName, work_rate_basis_points: int = ConstructionJob.NORMAL_WORK_RATE, absolute_minute: int = -1 ) -> CommandResult: if not _mutation_allowed(): return _unavailable_result() var job := get_job(job_id) if job == null: return _not_found("Construction job", job_id) if job.is_terminal(): return _invalid_state("A completed or cancelled job cannot progress.", job_id) var minute := current_minute if absolute_minute < 0 else absolute_minute if minute < 0: return _failure("INVALID_MINUTE", "Construction progress minute cannot be negative.") if work_rate_basis_points <= 0 or work_rate_basis_points > MAX_WORK_RATE: return _failure("INVALID_WORK_RATE", "Construction work rate is outside the supported range.") if minute <= int(_last_progress_minutes.get(job_id, -1)): var replay := CommandResultType.succeeded(0, { &"job_id": job_id, &"progress": job.progress, &"state": ConstructionJobType.State.keys()[job.state], }) replay.is_replay = true return replay if job.work_units_done > MAX_WORK_UNITS - work_rate_basis_points: return _failure("WORK_LIMIT", "Construction work accumulator limit was reached.") _mutation_in_progress = true var ready_to_complete := job.advance_one_minute(work_rate_basis_points, minute) _last_progress_minutes[job_id] = minute if ready_to_complete: var completed := _complete_job(job, minute) _mutation_in_progress = false return completed _mutation_in_progress = false _emit_job_changed(job) return CommandResultType.succeeded(0, { &"job_id": job_id, &"progress": job.progress, &"state": ConstructionJobType.State.keys()[job.state], }) func cancel_job(job_id: StringName, absolute_minute: int = -1) -> CommandResult: if not _mutation_allowed(): return _unavailable_result() var job := get_job(job_id) if job == null: return _not_found("Construction job", job_id) if job.is_terminal(): return _invalid_state("A completed or cancelled job cannot be cancelled.", job_id) var minute := current_minute if absolute_minute < 0 else absolute_minute if minute < 0: return _failure("INVALID_MINUTE", "Construction cancellation minute cannot be negative.") var refund := job.refundable_cost() var resource_snapshot: Variant = _capture_resource_state() var world_snapshot := _capture_world_state(job.reserved_cells) _mutation_in_progress = true if not _world_release(job.reserved_cells, job.job_id): var release_rolled_back := _restore_world_state(world_snapshot) _mutation_in_progress = false if not release_rolled_back: _quarantine("Construction cancellation release failed and world state could not be restored.") return _failure("RELEASE_FAILED", "The construction reservation could not be released.", { &"job_id": job_id, &"rolled_back": release_rolled_back, &"rollback_failed": not release_rolled_back, }) if not _refund_cost(refund): var world_rolled_back := _restore_world_state(world_snapshot) var resources_rolled_back := _restore_resource_state(resource_snapshot) var rolled_back := world_rolled_back and resources_rolled_back _mutation_in_progress = false if not rolled_back: _quarantine("Construction cancellation refund failed and rollback was incomplete.") return _failure("REFUND_FAILED", "The cancellation refund failed.", { &"job_id": job_id, &"rolled_back": rolled_back, &"rollback_failed": not rolled_back, &"world_rolled_back": world_rolled_back, &"resources_rolled_back": resources_rolled_back, }) job.mark_cancelled(minute) _mutation_in_progress = false _emit_job_changed(job) return CommandResultType.succeeded(0, { &"job_id": job_id, &"refund": refund, &"progress": job.progress, }) func demolish_facility( instance_id: StringName, material_refund_basis_points: int = DEMOLITION_MATERIAL_REFUND_BASIS_POINTS ) -> CommandResult: if not _mutation_allowed(): return _unavailable_result() if not _facilities.has(instance_id): return _not_found("Facility instance", instance_id) var instance: Dictionary = _facilities[instance_id] var cells: Array[Vector3i] = [] cells.assign(instance.get(&"occupied_cells", [])) var refund: Dictionary = {} var construction_cost: Dictionary = instance.get(&"construction_cost", {}) var materials := int(construction_cost.get(&"materials", 0)) if materials > 0: refund[&"materials"] = materials * clampi(material_refund_basis_points, 0, 10_000) / 10_000 var resource_snapshot: Variant = _capture_resource_state() var world_snapshot := _capture_world_state(cells) _mutation_in_progress = true if not _world_release(cells, instance_id): var release_rolled_back := _restore_world_state(world_snapshot) _mutation_in_progress = false if not release_rolled_back: _quarantine("Facility demolition release failed and world state could not be restored.") return _failure("RELEASE_FAILED", "The facility occupancy could not be released.", { &"instance_id": instance_id, &"rolled_back": release_rolled_back, &"rollback_failed": not release_rolled_back, }) if not _refund_cost(refund): var world_rolled_back := _restore_world_state(world_snapshot) var resources_rolled_back := _restore_resource_state(resource_snapshot) var rolled_back := world_rolled_back and resources_rolled_back _mutation_in_progress = false if not rolled_back: _quarantine("Facility demolition refund failed and rollback was incomplete.") return _failure("REFUND_FAILED", "The demolition refund failed.", { &"instance_id": instance_id, &"rolled_back": rolled_back, &"rollback_failed": not rolled_back, &"world_rolled_back": world_rolled_back, &"resources_rolled_back": resources_rolled_back, }) _facilities.erase(instance_id) # A completed construction job owns its live facility. Once demolition has # committed, retire both records so snapshots cannot retain an orphaned # COMPLETED job. Sequence counters and emitted domain events retain history. var source_job_id := StringName(instance.get(&"source_job_id", &"")) _jobs.erase(source_job_id) _last_progress_minutes.erase(source_job_id) _mutation_in_progress = false facility_demolished.emit(instance_id, refund) return CommandResultType.succeeded(0, { &"instance_id": instance_id, &"refund": refund, }) func create_snapshot() -> Dictionary: if _notification_staging_active or _notification_commit_active or has_deferred_notifications(): return {} var saved_jobs: Array[Dictionary] = [] for job_id: StringName in _sorted_string_name_keys(_jobs): var job: ConstructionJob = _jobs[job_id] var saved_job := job.create_snapshot() saved_job[&"last_progress_at"] = int(_last_progress_minutes.get(job_id, -1)) saved_jobs.append(saved_job) var saved_facilities: Array[Dictionary] = [] for instance_id: StringName in _sorted_string_name_keys(_facilities): saved_facilities.append(_serialize_facility(_facilities[instance_id])) return { &"schema_version": SNAPSHOT_VERSION, &"default_build_minutes": default_build_minutes, &"current_minute": current_minute, &"next_job_sequence": _next_job_sequence, &"next_facility_sequence": _next_facility_sequence, &"jobs": saved_jobs, &"facilities": saved_facilities, &"quarantined": _quarantined, &"quarantine_reason": _quarantine_reason, } func restore_snapshot(snapshot: Dictionary) -> bool: if _mutation_in_progress or _notification_staging_active \ or _notification_commit_active or has_deferred_notifications(): return false var parsed := _parse_snapshot(snapshot) if not bool(parsed.get(&"ok", false)): return false if not _validate_restored_state(parsed): return false _jobs = parsed[&"jobs"] _facilities = parsed[&"facilities"] _last_progress_minutes = parsed[&"last_progress_minutes"] default_build_minutes = parsed[&"default_build_minutes"] current_minute = parsed[&"current_minute"] _next_job_sequence = parsed[&"next_job_sequence"] _next_facility_sequence = parsed[&"next_facility_sequence"] _quarantined = parsed[&"quarantined"] _quarantine_reason = parsed[&"quarantine_reason"] return true func _parse_snapshot(snapshot: Dictionary) -> Dictionary: var raw_version: Variant = snapshot.get(&"schema_version", null) if not raw_version is int or int(raw_version) not in [LEGACY_SNAPSHOT_VERSION, SNAPSHOT_VERSION]: return {&"ok": false} var version := int(raw_version) var top_level_keys: Array[StringName] = [ &"schema_version", &"default_build_minutes", &"current_minute", &"next_job_sequence", &"next_facility_sequence", &"jobs", &"facilities", ] if version == SNAPSHOT_VERSION: top_level_keys.append_array([&"quarantined", &"quarantine_reason"]) if not _has_exact_keys(snapshot, top_level_keys): return {&"ok": false} for counter_key: StringName in [ &"default_build_minutes", &"current_minute", &"next_job_sequence", &"next_facility_sequence", ]: if not snapshot[counter_key] is int: return {&"ok": false} var restored_default := int(snapshot[&"default_build_minutes"]) var restored_minute := int(snapshot[&"current_minute"]) var job_sequence := int(snapshot[&"next_job_sequence"]) var facility_sequence := int(snapshot[&"next_facility_sequence"]) if ( restored_default < 1 or restored_default > MAX_BUILD_MINUTES or restored_minute < 0 or job_sequence < 1 or job_sequence > MAX_SEQUENCE + 1 or facility_sequence < 1 or facility_sequence > MAX_SEQUENCE + 1 ): return {&"ok": false} var saved_jobs: Variant = snapshot[&"jobs"] var saved_facilities: Variant = snapshot[&"facilities"] if ( not saved_jobs is Array or not saved_facilities is Array or saved_jobs.size() > MAX_JOBS or saved_facilities.size() > MAX_FACILITIES ): return {&"ok": false} var quarantined := false var quarantine_reason := "" if version == SNAPSHOT_VERSION: if not snapshot[&"quarantined"] is bool or not snapshot[&"quarantine_reason"] is String: return {&"ok": false} quarantined = snapshot[&"quarantined"] quarantine_reason = snapshot[&"quarantine_reason"] if quarantined == quarantine_reason.is_empty(): return {&"ok": false} var restored_jobs: Dictionary = {} var progress_minutes: Dictionary = {} for job_data: Variant in saved_jobs: if not job_data is Dictionary or not _validate_saved_job(job_data, version): return {&"ok": false} var construction_data: Dictionary = job_data.duplicate(true) construction_data.erase(&"last_progress_at") var job := ConstructionJobType.from_snapshot(construction_data) if job == null or restored_jobs.has(job.job_id): return {&"ok": false} restored_jobs[job.job_id] = job var last_progress := -1 if version == SNAPSHOT_VERSION: last_progress = int(job_data[&"last_progress_at"]) elif job.work_units_done > 0: last_progress = maxi(job.started_at, restored_minute) progress_minutes[job.job_id] = last_progress var restored_facilities: Dictionary = {} for facility_data: Variant in saved_facilities: if not facility_data is Dictionary or not _validate_saved_facility(facility_data): return {&"ok": false} var instance := _deserialize_facility(facility_data) var instance_id: StringName = instance[&"instance_id"] if restored_facilities.has(instance_id): return {&"ok": false} restored_facilities[instance_id] = instance return { &"ok": true, &"jobs": restored_jobs, &"facilities": restored_facilities, &"last_progress_minutes": progress_minutes, &"default_build_minutes": restored_default, &"current_minute": restored_minute, &"next_job_sequence": job_sequence, &"next_facility_sequence": facility_sequence, &"quarantined": quarantined, &"quarantine_reason": quarantine_reason, } func _validate_saved_job(saved: Dictionary, version: int) -> bool: var keys: Array[StringName] = [ &"job_id", &"definition_id", &"origin", &"rotation", &"layer", &"reserved_cells", &"progress", &"state", &"state_reason", &"assigned_builder_ids", &"construction_cost", &"build_minutes", &"work_units_done", &"created_at", &"started_at", &"completed_at", &"cancelled_at", &"facility_instance_id", ] if version == SNAPSHOT_VERSION: keys.append(&"last_progress_at") if not _has_exact_keys(saved, keys): return false for text_key: StringName in [ &"job_id", &"definition_id", &"state", &"state_reason", &"facility_instance_id", ]: if not saved[text_key] is String: return false if String(saved[&"job_id"]).is_empty() or String(saved[&"definition_id"]).is_empty(): return false if _parse_saved_cell(saved[&"origin"]) == null: return false var cells: Variant = _parse_saved_cells(saved[&"reserved_cells"]) if cells == null or cells.is_empty(): return false var origin: Vector3i = _parse_saved_cell(saved[&"origin"]) for integer_key: StringName in [ &"rotation", &"layer", &"progress", &"build_minutes", &"work_units_done", &"created_at", &"started_at", &"completed_at", &"cancelled_at", ]: if not saved[integer_key] is int: return false if version == SNAPSHOT_VERSION and not saved[&"last_progress_at"] is int: return false if int(saved[&"rotation"]) not in [0, 90, 180, 270] or int(saved[&"layer"]) not in [0, 1]: return false if origin.z != int(saved[&"layer"]) or not cells.has(origin): return false for cell: Vector3i in cells: if cell.z != int(saved[&"layer"]): return false if not _valid_cost(saved[&"construction_cost"]): return false if not saved[&"assigned_builder_ids"] is Array: return false var builder_set: Dictionary = {} for builder_id: Variant in saved[&"assigned_builder_ids"]: if not builder_id is String or String(builder_id).is_empty() or builder_set.has(builder_id): return false builder_set[builder_id] = true var progress := int(saved[&"progress"]) var build_minutes := int(saved[&"build_minutes"]) var work_done := int(saved[&"work_units_done"]) if ( progress < 0 or progress > MAX_PROGRESS or build_minutes < 1 or build_minutes > MAX_BUILD_MINUTES or work_done < 0 or work_done > MAX_WORK_UNITS or progress != mini(MAX_PROGRESS, work_done / build_minutes) or int(saved[&"created_at"]) < 0 or int(saved[&"started_at"]) < -1 or int(saved[&"completed_at"]) < -1 or int(saved[&"cancelled_at"]) < -1 ): return false if work_done == 0 and int(saved[&"started_at"]) != -1: return false if work_done > 0 and int(saved[&"started_at"]) < 0: return false if version == SNAPSHOT_VERSION: var last_progress := int(saved[&"last_progress_at"]) if last_progress < -1: return false if work_done == 0 and last_progress != -1: return false if work_done > 0 and last_progress < int(saved[&"started_at"]): return false var state := String(saved[&"state"]) if state not in ConstructionJobType.State.keys(): return false var facility_id := String(saved[&"facility_instance_id"]) match state: "COMPLETED": if progress != MAX_PROGRESS or int(saved[&"completed_at"]) < 0 or int(saved[&"cancelled_at"]) != -1 or facility_id.is_empty() or not String(saved[&"state_reason"]).is_empty(): return false "CANCELLED": if int(saved[&"cancelled_at"]) < 0 or int(saved[&"completed_at"]) != -1 or not facility_id.is_empty() or not String(saved[&"state_reason"]).is_empty(): return false "PLANNED": if work_done != 0 or progress != 0 or int(saved[&"started_at"]) != -1 or not String(saved[&"state_reason"]).is_empty(): return false "BUILDING": if work_done <= 0 or progress >= MAX_PROGRESS or not String(saved[&"state_reason"]).is_empty(): return false "BLOCKED": if String(saved[&"state_reason"]).is_empty(): return false _: if int(saved[&"completed_at"]) != -1 or int(saved[&"cancelled_at"]) != -1 or not facility_id.is_empty(): return false if state not in ["COMPLETED", "CANCELLED"] and ( int(saved[&"completed_at"]) != -1 or int(saved[&"cancelled_at"]) != -1 or not facility_id.is_empty() ): return false return true func _validate_saved_facility(saved: Dictionary) -> bool: var keys: Array[StringName] = [ &"instance_id", &"definition_id", &"origin", &"rotation", &"layer", &"occupied_cells", &"construction_cost", &"source_job_id", &"state", &"completed_at", ] if not _has_exact_keys(saved, keys): return false for text_key: StringName in [&"instance_id", &"definition_id", &"source_job_id", &"state"]: if not saved[text_key] is String or String(saved[text_key]).is_empty(): return false if String(saved[&"state"]) != "ACTIVE" or _parse_saved_cell(saved[&"origin"]) == null: return false var cells: Variant = _parse_saved_cells(saved[&"occupied_cells"]) if cells == null or cells.is_empty(): return false var origin: Vector3i = _parse_saved_cell(saved[&"origin"]) for integer_key: StringName in [&"rotation", &"layer", &"completed_at"]: if not saved[integer_key] is int: return false if ( int(saved[&"rotation"]) not in [0, 90, 180, 270] or int(saved[&"layer"]) not in [0, 1] or int(saved[&"completed_at"]) < 0 ): return false if origin.z != int(saved[&"layer"]) or not cells.has(origin): return false for cell: Vector3i in cells: if cell.z != int(saved[&"layer"]): return false return _valid_cost(saved[&"construction_cost"]) func _validate_restored_state(parsed: Dictionary) -> bool: var restored_jobs: Dictionary = parsed[&"jobs"] var restored_facilities: Dictionary = parsed[&"facilities"] var progress_minutes: Dictionary = parsed[&"last_progress_minutes"] if ( (not restored_jobs.is_empty() or not restored_facilities.is_empty()) and _missing_methods(_world, [&"is_in_bounds", &"get_occupant"]).size() > 0 ): return false var job_high_water := 0 var facility_high_water := 0 for job_id: Variant in restored_jobs: if not job_id is StringName or not progress_minutes.has(job_id): return false var job: ConstructionJob = restored_jobs[job_id] if not _strict_bool_call(_world, &"is_in_bounds", [job.origin]): return false for cell: Vector3i in job.reserved_cells: if not _strict_bool_call(_world, &"is_in_bounds", [cell]): return false var sequence := _sequence_from_id(String(job_id), "construction_") if sequence < 1: return false job_high_water = maxi(job_high_water, sequence) if job.state == ConstructionJobType.State.COMPLETED: if not restored_facilities.has(job.facility_instance_id): return false elif job.state != ConstructionJobType.State.CANCELLED: for cell: Vector3i in job.reserved_cells: if _world_occupant(cell) != job.job_id: return false for instance_id: Variant in restored_facilities: if not instance_id is StringName: return false var sequence := _sequence_from_id(String(instance_id), "facility_") if sequence < 1: return false facility_high_water = maxi(facility_high_water, sequence) var instance: Dictionary = restored_facilities[instance_id] if not _strict_bool_call(_world, &"is_in_bounds", [instance[&"origin"]]): return false var source_id: StringName = instance[&"source_job_id"] if not restored_jobs.has(source_id): return false var source_job: ConstructionJob = restored_jobs[source_id] if ( source_job.state != ConstructionJobType.State.COMPLETED or source_job.facility_instance_id != instance_id or source_job.definition_id != instance[&"definition_id"] or source_job.origin != instance[&"origin"] or source_job.rotation != int(instance[&"rotation"]) or source_job.layer != int(instance[&"layer"]) or source_job.reserved_cells != instance[&"occupied_cells"] or source_job.construction_cost != instance[&"construction_cost"] or source_job.completed_at != int(instance[&"completed_at"]) ): return false for cell: Vector3i in instance[&"occupied_cells"]: if _world_occupant(cell) != instance_id: return false return ( job_high_water < int(parsed[&"next_job_sequence"]) and facility_high_water < int(parsed[&"next_facility_sequence"]) ) static func _parse_saved_cell(value: Variant) -> Variant: if not value is Array or value.size() != 3: return null if not value[0] is int or not value[1] is int or not value[2] is int: return null return Vector3i(int(value[0]), int(value[1]), int(value[2])) static func _parse_saved_cells(value: Variant) -> Variant: if not value is Array: return null var result: Array[Vector3i] = [] var seen: Dictionary = {} for raw_cell: Variant in value: var cell: Variant = _parse_saved_cell(raw_cell) if cell == null or seen.has(cell): return null seen[cell] = true result.append(cell) return result static func _sequence_from_id(value: String, prefix: String) -> int: if not value.begins_with(prefix) or value.length() != prefix.length() + 8: return -1 var suffix := value.substr(prefix.length()) if not suffix.is_valid_int(): return -1 return int(suffix) func _validate_cell_constraints( report: PlacementReport, origin: Vector3i, expected_zone: int, expected_layer: int ) -> void: if origin.z != expected_layer: report.layer_mismatch_cells.append(origin) if expected_zone == 2 and origin.y != 0: report.zone_mismatch_cells.append(origin) for cell: Vector3i in report.cells: var in_bounds := _strict_bool_call(_world, &"is_in_bounds", [cell]) if not in_bounds: report.out_of_bounds_cells.append(cell) if cell.z != expected_layer and not report.layer_mismatch_cells.has(cell): report.layer_mismatch_cells.append(cell) if not in_bounds: continue var zone_mismatch := false if expected_zone == 2: zone_mismatch = cell.y < 0 elif expected_zone != 0: zone_mismatch = not _zone_matches(expected_zone, _world.call(&"get_zone", cell)) if zone_mismatch and not report.zone_mismatch_cells.has(cell): report.zone_mismatch_cells.append(cell) var occupant: Variant = _world.call(&"get_occupant", cell) if not _occupant_is_empty(occupant): report.occupied_cells.append(cell) if cell.y < 0 and not _strict_bool_call(_world, &"is_excavated", [cell]): report.unexcavated_cells.append(cell) if not report.out_of_bounds_cells.is_empty(): report.add_error( PlacementReportType.ERROR_OUT_OF_BOUNDS, "One or more footprint cells are outside the current map bounds.", report.out_of_bounds_cells ) if not report.zone_mismatch_cells.is_empty(): report.add_error( PlacementReportType.ERROR_ZONE_MISMATCH, "The facility is not permitted in one or more map zones.", report.zone_mismatch_cells, {&"expected_zone": expected_zone} ) if not report.layer_mismatch_cells.is_empty(): report.add_error( PlacementReportType.ERROR_LAYER_MISMATCH, "The footprint layer differs from the facility definition.", report.layer_mismatch_cells, {&"expected_layer": expected_layer} ) if not report.occupied_cells.is_empty(): report.add_error( PlacementReportType.ERROR_OVERLAP, "One or more footprint cells are already occupied or reserved.", report.occupied_cells ) if not report.unexcavated_cells.is_empty(): report.add_error( PlacementReportType.ERROR_NOT_EXCAVATED, "Underground facilities require every footprint cell to be excavated first.", report.unexcavated_cells ) func _validate_support(report: PlacementReport, definition: Variant) -> void: if report.cells.is_empty(): return var weight_per_cell := maxi(int(_definition_value(definition, &"weight_per_cell", 1)), 1) var support_result: Variant = _call_by_supported_arity( _world, &"validate_support", [report.cells, weight_per_cell] ) var supported := true var reason := "The structure does not have sufficient support." if support_result is bool: supported = support_result if not supported: report.unsupported_cells.assign(report.cells) elif support_result is Array: for cell: Variant in support_result: if cell is Vector3i: report.unsupported_cells.append(cell) supported = report.unsupported_cells.is_empty() elif support_result is Dictionary: var raw_ok: Variant = support_result.get(&"ok", support_result.get("ok", null)) supported = raw_ok if raw_ok is bool else false reason = String(support_result.get(&"reason", support_result.get("reason", reason))) var unsupported: Variant = support_result.get( &"unsupported_cells", support_result.get("unsupported_cells", []) ) if unsupported is Array: for cell: Variant in unsupported: if cell is Vector3i: report.unsupported_cells.append(cell) if not supported and report.unsupported_cells.is_empty(): report.unsupported_cells.assign(report.cells) elif support_result is Object and support_result.has_method(&"is_success"): var raw_success: Variant = support_result.call(&"is_success") supported = raw_success if raw_success is bool else false if not supported: report.unsupported_cells.assign(report.cells) if not supported: report.add_error( PlacementReportType.ERROR_UNSUPPORTED, reason, report.unsupported_cells ) func _validate_cost(report: PlacementReport) -> void: if _resource_state == null: return if _can_afford(report.total_cost): return for resource_name: Variant in report.total_cost: var required := int(report.total_cost[resource_name]) var available := _resource_amount(StringName(resource_name)) if available < required: report.missing_resources[StringName(resource_name)] = required - available report.add_error( PlacementReportType.ERROR_INSUFFICIENT_RESOURCES, "Available resources do not cover the complete construction cost.", [], {&"missing": report.missing_resources.duplicate(true)} ) func _complete_job(job: ConstructionJob, absolute_minute: int) -> CommandResult: if _facilities.size() >= MAX_FACILITIES or _next_facility_sequence > MAX_SEQUENCE: job.mark_blocked("Facility capacity has been reached.") _emit_job_changed(job) return _failure("FACILITY_LIMIT", job.state_reason, {&"job_id": job.job_id}) var instance_id := StringName("facility_%08d" % _next_facility_sequence) var world_snapshot := _capture_world_state(job.reserved_cells) if not _world_release(job.reserved_cells, job.job_id): var release_rolled_back := _restore_world_state(world_snapshot) job.mark_blocked("The construction reservation could not be released for completion.") if not release_rolled_back: _quarantine("Construction completion release failed and world state could not be restored.") _emit_job_changed(job) return _failure("RELEASE_FAILED", job.state_reason, { &"job_id": job.job_id, &"rolled_back": release_rolled_back, &"rollback_failed": not release_rolled_back, }) if not _world_occupy(job.reserved_cells, instance_id, &"facility"): var rolled_back := _restore_world_state(world_snapshot) job.mark_blocked("Facility occupancy failed during construction completion.") if not rolled_back: _quarantine("Facility occupancy failed and the construction reservation could not be restored.") _emit_job_changed(job) return _failure("FACILITY_OCCUPANCY_FAILED", job.state_reason, { &"job_id": job.job_id, &"rolled_back": rolled_back, &"rollback_failed": not rolled_back, }) var instance := { &"instance_id": instance_id, &"definition_id": job.definition_id, &"origin": job.origin, &"rotation": job.rotation, &"layer": job.layer, &"occupied_cells": job.reserved_cells.duplicate(), &"construction_cost": job.construction_cost.duplicate(true), &"source_job_id": job.job_id, &"state": &"ACTIVE", &"completed_at": absolute_minute, } _facilities[instance_id] = instance _next_facility_sequence += 1 job.mark_completed(instance_id, absolute_minute) _emit_job_changed(job) _emit_facility_completed(instance) return CommandResultType.succeeded(0, { &"job_id": job.job_id, &"instance_id": instance_id, &"progress": job.progress, }) func _world_cells_for_footprint( origin: Vector3i, footprint: Vector2i, rotation: int, layer: int ) -> Array[Vector3i]: var argument_count := _method_argument_count(_world, &"cells_for_footprint") var returned: Variant if argument_count <= 2: var rotated_footprint := footprint if rotation == 90 or rotation == 270: rotated_footprint = Vector2i(footprint.y, footprint.x) returned = _world.callv(&"cells_for_footprint", [origin, rotated_footprint]) else: returned = _call_by_supported_arity( _world, &"cells_for_footprint", [origin, footprint, rotation, layer] ) var cells: Array[Vector3i] = [] var seen: Dictionary = {} if not returned is Array or returned.size() != footprint.x * footprint.y: return cells for cell: Variant in returned: if not cell is Vector3i or seen.has(cell): return [] seen[cell] = true cells.append(cell) return cells func _world_occupy(cells: Array[Vector3i], occupant_id: StringName, kind: StringName) -> bool: if _world == null or not _world.has_method(&"occupy"): return false var argument_count := _method_argument_count(_world, &"occupy") var arguments: Array = [occupant_id, cells, kind] var returned: Variant = _world.callv(&"occupy", arguments.slice(0, argument_count)) return _operation_succeeded(returned) func _world_release(cells: Array[Vector3i], occupant_id: StringName) -> bool: if _world == null or not _world.has_method(&"release"): return false var argument_count := _method_argument_count(_world, &"release") var arguments: Array = [occupant_id] if argument_count == 1 else [occupant_id, cells] var returned: Variant = _world.callv(&"release", arguments.slice(0, argument_count)) return _operation_succeeded(returned) func _definition_build_minutes(definition: Variant) -> int: var configured := int(_definition_value(definition, &"build_minutes", 0)) return configured if configured > 0 else maxi(default_build_minutes, 1) func _definition_is_valid(definition: Variant) -> bool: if definition == null: return false if definition is Object and definition.has_method(&"is_valid"): var valid_result: Variant = definition.call(&"is_valid") if not valid_result is bool or not valid_result: return false var raw_id: Variant = _definition_value(definition, &"id", null) var footprint: Variant = _definition_value(definition, &"footprint", null) var cost: Variant = _definition_value(definition, &"construction_cost", null) var minutes: Variant = _definition_value(definition, &"build_minutes", default_build_minutes) return ( (raw_id is String or raw_id is StringName) and not String(raw_id).is_empty() and footprint is Vector2i and footprint.x > 0 and footprint.y > 0 and _valid_cost(cost) and minutes is int and int(minutes) > 0 and int(minutes) <= MAX_BUILD_MINUTES ) func _definition_value(source: Variant, property_name: StringName, fallback: Variant) -> Variant: if source is Dictionary: return source.get(property_name, source.get(String(property_name), fallback)) if source == null or not source.has_method(&"get_property_list"): return fallback for property: Dictionary in source.get_property_list(): if StringName(property.get(&"name", &"")) == property_name: return source.get(property_name) return fallback func _can_afford(cost: Dictionary) -> bool: if _resource_state == null: return false if _resource_state.has_method(&"can_afford"): return _operation_succeeded(_resource_state.call(&"can_afford", cost)) for resource_name: Variant in cost: if _resource_amount(StringName(resource_name)) < int(cost[resource_name]): return false return true func _spend_cost(cost: Dictionary) -> bool: if _resource_state == null: return false if _resource_state.has_method(&"spend"): return _operation_succeeded(_resource_state.call(&"spend", cost)) if not _can_afford(cost): return false var deltas: Dictionary = {} for resource_name: Variant in cost: deltas[StringName(resource_name)] = -int(cost[resource_name]) if _resource_state.has_method(&"apply_resource_deltas"): return _operation_succeeded(_resource_state.call(&"apply_resource_deltas", deltas)) return false func _refund_cost(refund: Dictionary) -> bool: if refund.is_empty(): return true if _resource_state == null: return false if _resource_state.has_method(&"apply_resource_deltas"): return _operation_succeeded(_resource_state.call(&"apply_resource_deltas", refund)) if _resource_state.has_method(&"add_resource"): for resource_name: Variant in refund: var returned: Variant = _resource_state.call( &"add_resource", StringName(resource_name), int(refund[resource_name]) ) if not returned is int and not (returned is bool and returned): return false return true return false func _resource_amount(resource_name: StringName) -> int: if _resource_state == null: return 0 if _resource_state.has_method(&"get_resource"): var returned: Variant = _resource_state.call(&"get_resource", resource_name) return returned if returned is int else 0 var resources: Variant = _definition_value(_resource_state, &"resources", {}) if resources is Dictionary: var amount: Variant = resources.get(resource_name, resources.get(String(resource_name), 0)) return amount if amount is int else 0 return 0 func _capture_resource_state() -> Variant: var captured := { &"amounts": { &"gold": _resource_amount(&"gold"), &"materials": _resource_amount(&"materials"), }, &"has_native": false, &"native_snapshot": null, } if _resource_state != null and _resource_state.has_method(&"create_snapshot"): captured[&"has_native"] = true captured[&"native_snapshot"] = _resource_state.call(&"create_snapshot") return captured func _restore_resource_state(snapshot: Variant) -> bool: if not snapshot is Dictionary or _resource_state == null: return false var expected: Variant = snapshot.get(&"amounts", null) if not expected is Dictionary: return false if bool(snapshot.get(&"has_native", false)) and _resource_state.has_method(&"restore_snapshot"): _operation_succeeded(_resource_state.call(&"restore_snapshot", snapshot.get(&"native_snapshot", null))) elif _resource_state.has_method(&"apply_resource_deltas"): var deltas: Dictionary = {} for resource_name: StringName in [&"gold", &"materials"]: var expected_amount: Variant = expected.get(resource_name, null) if not expected_amount is int: return false deltas[resource_name] = int(expected_amount) - _resource_amount(resource_name) _operation_succeeded(_resource_state.call(&"apply_resource_deltas", deltas)) else: return false return _resource_amounts_match(expected) func _capture_world_state(cells: Array[Vector3i]) -> Dictionary: var occupants: Array[Dictionary] = [] for cell: Vector3i in cells: var occupant := _world_occupant(cell) var kind := &"" if _world != null and _world.has_method(&"get_occupant_kind"): var raw_kind: Variant = _world.call(&"get_occupant_kind", cell) if raw_kind is String or raw_kind is StringName: kind = StringName(raw_kind) if kind.is_empty() and not occupant.is_empty(): kind = &"construction" if String(occupant).begins_with("construction_") else &"facility" occupants.append({&"cell": cell, &"occupant_id": occupant, &"kind": kind}) var captured := { &"occupants": occupants, &"has_native": false, &"native_snapshot": null, } if _world != null and _world.has_method(&"create_snapshot"): captured[&"has_native"] = true captured[&"native_snapshot"] = _world.call(&"create_snapshot") return captured func _restore_world_state(snapshot: Variant) -> bool: if not snapshot is Dictionary or _world == null: return false var occupants: Variant = snapshot.get(&"occupants", null) if not occupants is Array: return false if bool(snapshot.get(&"has_native", false)) and _world.has_method(&"restore_snapshot"): _operation_succeeded(_world.call(&"restore_snapshot", snapshot.get(&"native_snapshot", null))) else: var cells: Array[Vector3i] = [] var ids_to_release: Dictionary = {} for entry: Variant in occupants: if not entry is Dictionary or not entry.get(&"cell", null) is Vector3i: return false var cell: Vector3i = entry[&"cell"] cells.append(cell) var current_id := _world_occupant(cell) if not current_id.is_empty(): ids_to_release[current_id] = true for occupant_id: Variant in ids_to_release: _world_release(cells, StringName(occupant_id)) var groups: Dictionary = {} for entry: Dictionary in occupants: var expected_id := StringName(entry[&"occupant_id"]) if expected_id.is_empty(): continue if not groups.has(expected_id): groups[expected_id] = {&"cells": [], &"kind": StringName(entry[&"kind"])} groups[expected_id][&"cells"].append(entry[&"cell"]) for expected_id: Variant in groups: var restored_cells: Array[Vector3i] = [] restored_cells.assign(groups[expected_id][&"cells"]) _world_occupy(restored_cells, StringName(expected_id), groups[expected_id][&"kind"]) return _world_state_matches(occupants) func _world_state_matches(occupants: Variant) -> bool: if not occupants is Array: return false for entry: Variant in occupants: if not entry is Dictionary or not entry.get(&"cell", null) is Vector3i: return false if _world_occupant(entry[&"cell"]) != StringName(entry.get(&"occupant_id", &"")): return false return true func _world_occupant(cell: Vector3i) -> StringName: if _world == null or not _world.has_method(&"get_occupant"): return &"" var returned: Variant = _world.call(&"get_occupant", cell) if returned is String or returned is StringName: return StringName(returned) return &"" func _resource_amounts_match(expected: Dictionary) -> bool: for resource_name: StringName in [&"gold", &"materials"]: var amount: Variant = expected.get(resource_name, null) if not amount is int or _resource_amount(resource_name) != int(amount): return false return true func _serialize_facility(instance: Dictionary) -> Dictionary: var serialized := instance.duplicate(true) serialized[&"instance_id"] = String(instance.get(&"instance_id", &"")) serialized[&"definition_id"] = String(instance.get(&"definition_id", &"")) serialized[&"source_job_id"] = String(instance.get(&"source_job_id", &"")) serialized[&"state"] = String(instance.get(&"state", &"ACTIVE")) var origin: Vector3i = instance.get(&"origin", Vector3i.ZERO) serialized[&"origin"] = [origin.x, origin.y, origin.z] var saved_cells: Array[Array] = [] for cell: Vector3i in instance.get(&"occupied_cells", []): saved_cells.append([cell.x, cell.y, cell.z]) serialized[&"occupied_cells"] = saved_cells return serialized func _deserialize_facility(saved: Dictionary) -> Dictionary: var instance := saved.duplicate(true) instance[&"instance_id"] = StringName(saved.get(&"instance_id", "")) instance[&"definition_id"] = StringName(saved.get(&"definition_id", "")) instance[&"source_job_id"] = StringName(saved.get(&"source_job_id", "")) instance[&"state"] = StringName(saved.get(&"state", "ACTIVE")) var origin: Variant = saved.get(&"origin", []) if origin is Array and origin.size() == 3: instance[&"origin"] = Vector3i(int(origin[0]), int(origin[1]), int(origin[2])) else: instance[&"origin"] = Vector3i.ZERO var cells: Array[Vector3i] = [] var saved_cells: Variant = saved.get(&"occupied_cells", []) if saved_cells is Array: for cell: Variant in saved_cells: if cell is Array and cell.size() == 3: cells.append(Vector3i(int(cell[0]), int(cell[1]), int(cell[2]))) instance[&"occupied_cells"] = cells instance[&"construction_cost"] = _normalize_cost(saved.get(&"construction_cost", {})) return instance func _not_found(entity_name: String, entity_id: StringName) -> CommandResult: return CommandResultType.failed( CommandResultType.Code.NOT_FOUND, 0, "%s '%s' was not found." % [entity_name, entity_id], {&"id": entity_id} ) func _invalid_state(message: String, entity_id: StringName) -> CommandResult: return CommandResultType.failed( CommandResultType.Code.VALIDATION_FAILED, 0, message, {&"id": entity_id} ) func _failure(code: StringName, message: String, details: Dictionary = {}) -> CommandResult: var merged := details.duplicate(true) merged[&"code"] = code return CommandResultType.failed( CommandResultType.Code.TRANSACTION_APPLY_FAILED, 0, message, merged ) func _notification_unavailable_result(code: StringName, message: String) -> CommandResult: return _failure(code, message) func is_quarantined() -> bool: return _quarantined func get_quarantine_reason() -> String: return _quarantine_reason func _mutation_allowed() -> bool: return ( not _mutation_in_progress and not _notification_commit_active and not _quarantined and (_notification_staging_active or not has_deferred_notifications()) ) func _unavailable_result() -> CommandResult: var code: StringName = &"SERVICE_QUARANTINED" if _quarantined else &"REENTRANT_MUTATION" var message := ( "Construction service is quarantined: %s" % _quarantine_reason if _quarantined else "A reentrant construction mutation was rejected." ) return _failure(code, message, {&"quarantine_reason": _quarantine_reason}) func _quarantine(reason: String) -> void: _quarantined = true _quarantine_reason = reason static func _normalize_cost(source: Variant) -> Dictionary: var normalized: Dictionary = {} if not _valid_cost(source): return normalized for resource_name: StringName in [&"gold", &"materials"]: normalized[resource_name] = int(source[resource_name]) return normalized static func _valid_cost(source: Variant) -> bool: if not source is Dictionary or source.size() != 2: return false for resource_name: StringName in [&"gold", &"materials"]: if not source.has(resource_name): return false var amount: Variant = source[resource_name] if not amount is int or int(amount) < 0: return false return true static func _has_exact_keys(source: Dictionary, expected: Array) -> bool: if source.size() != expected.size(): return false for key: Variant in expected: if not source.has(key): return false return true static func _normalize_rotation(value: int) -> int: var normalized := value % 360 if normalized < 0: normalized += 360 return (normalized / 90) * 90 static func _zone_matches(expected_zone: int, actual_zone: Variant) -> bool: if actual_zone is int: return int(actual_zone) == expected_zone var normalized := String(actual_zone).to_upper() match expected_zone: 1: return normalized == "ABOVE_GROUND" 2: return normalized == "BOUNDARY" 3: return normalized == "UNDERGROUND" return false static func _occupant_is_empty(occupant: Variant) -> bool: if occupant == null: return true match typeof(occupant): TYPE_BOOL: return not bool(occupant) TYPE_INT, TYPE_FLOAT: return float(occupant) == 0.0 TYPE_STRING, TYPE_STRING_NAME: return String(occupant).is_empty() return false static func _operation_succeeded(returned: Variant) -> bool: if returned is bool: return returned if returned is Object and returned.has_method(&"is_success"): var result: Variant = returned.call(&"is_success") return result if result is bool else false return false static func _strict_bool_call(target: Variant, method_name: StringName, arguments: Array) -> bool: if target == null or not target.has_method(method_name): return false var returned: Variant = target.callv(method_name, arguments) return returned if returned is bool else false static func _missing_methods(target: Variant, methods: Array[StringName]) -> Array[String]: var missing: Array[String] = [] if target == null: for method_name: StringName in methods: missing.append(String(method_name)) return missing for method_name: StringName in methods: if not target.has_method(method_name): missing.append(String(method_name)) return missing static func _call_by_supported_arity(target: Variant, method_name: StringName, arguments: Array) -> Variant: if target == null or not target.has_method(method_name): return null var argument_count := mini(arguments.size(), _method_argument_count(target, method_name)) return target.callv(method_name, arguments.slice(0, argument_count)) static func _method_argument_count(target: Variant, method_name: StringName) -> int: if target == null or not target.has_method(method_name): return 0 for method: Dictionary in target.get_method_list(): if StringName(method.get(&"name", &"")) != method_name: continue var declared_arguments: Variant = method.get(&"args", []) if declared_arguments is Array: return declared_arguments.size() return 0 static func _sorted_string_name_keys(source: Dictionary) -> Array[StringName]: var as_strings: Array[String] = [] for key: Variant in source: as_strings.append(String(key)) as_strings.sort() var result: Array[StringName] = [] for value: String in as_strings: result.append(StringName(value)) return result