The FRONT Mana warp is guest-only: its connectors carry the "!staff" access, which path access checks treat as open to everyone except staff. A new 2x2 BACK service warp links only to other service warps for staff, with A07 values. Registry and palette now hold 25 definitions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
114 lines
5.3 KiB
GDScript
114 lines
5.3 KiB
GDScript
extends RefCounted
|
|
|
|
## A07/A08 Mana warp: every completed warp is linked to every other warp of the
|
|
## same kind by one path connector, so crossing is a single instant step. The
|
|
## FRONT guest warp excludes staff routes; the BACK service warp is staff-only.
|
|
## Connectors are derived from construction state; their operational state
|
|
## follows the reserve and the warps' own power. The Runtime charges fares.
|
|
const PathType := preload("res://scripts/world/path_service.gd")
|
|
const DEFINITION_ID := &"mana_warp"
|
|
const SERVICE_DEFINITION_ID := &"service_warp"
|
|
const DEFINITION_IDS: Array[StringName] = [DEFINITION_ID, SERVICE_DEFINITION_ID]
|
|
const ACCESS_BY_DEFINITION := {&"mana_warp": &"!staff", &"service_warp": &"staff"}
|
|
const PREFIX := "warp.connector:"
|
|
const MANA_PER_TRIP := 3
|
|
const PATH_COST := 400
|
|
|
|
|
|
static func is_warp_connector(connector_id: StringName) -> bool:
|
|
return String(connector_id).begins_with(PREFIX)
|
|
|
|
|
|
## Completed warps sorted by instance id: [{instance_id, origin}].
|
|
static func warps_from_facilities(facilities: Array) -> Array[Dictionary]:
|
|
var warps: Array[Dictionary] = []
|
|
for value: Variant in facilities:
|
|
if not value is Dictionary:
|
|
continue
|
|
var facility: Dictionary = value
|
|
var definition_id := StringName(str(facility.get(&"definition_id", "")))
|
|
if definition_id not in DEFINITION_IDS:
|
|
continue
|
|
var origin: Variant = facility.get(&"origin")
|
|
if not origin is Vector3i:
|
|
continue
|
|
warps.append({&"instance_id": StringName(str(facility.get(&"instance_id", ""))), &"origin": origin,
|
|
&"definition_id": definition_id})
|
|
warps.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
|
|
return String(a[&"instance_id"]) < String(b[&"instance_id"]))
|
|
return warps
|
|
|
|
|
|
static func expected_connectors(facilities: Array) -> Array[Dictionary]:
|
|
var warps := warps_from_facilities(facilities)
|
|
var result: Array[Dictionary] = []
|
|
for i: int in warps.size():
|
|
for j: int in range(i + 1, warps.size()):
|
|
if warps[i][&"definition_id"] != warps[j][&"definition_id"]:
|
|
continue
|
|
result.append({
|
|
&"access_id": ACCESS_BY_DEFINITION[warps[i][&"definition_id"]],
|
|
&"id": StringName("%s%s|%s" % [PREFIX, warps[i][&"instance_id"], warps[j][&"instance_id"]]),
|
|
&"from": warps[i][&"origin"],
|
|
&"to": warps[j][&"origin"],
|
|
&"endpoints": [warps[i][&"instance_id"], warps[j][&"instance_id"]],
|
|
})
|
|
return result
|
|
|
|
|
|
## Rebuilds warp connectors from construction state and applies availability.
|
|
## available: instance_id -> bool. Atomic: on failure the path is restored. The
|
|
## rollback snapshot is taken only when a change is actually needed.
|
|
static func synchronize(path: PathService, facilities: Array, available: Dictionary, fare_affordable: bool) -> Dictionary:
|
|
var expected := expected_connectors(facilities)
|
|
var expected_ids: Dictionary = {}
|
|
for connector: Dictionary in expected:
|
|
expected_ids[connector[&"id"]] = true
|
|
var before: Variant = null
|
|
for connector_id: StringName in path.get_connector_ids_with_prefix(PREFIX):
|
|
if expected_ids.has(connector_id):
|
|
continue
|
|
if before == null:
|
|
before = path.create_snapshot()
|
|
if not path.remove_connector(connector_id):
|
|
path.restore_snapshot(before)
|
|
return {&"ok": false, &"code": &"WARP_CONNECTOR_REMOVE_FAILED", &"reason": path.last_error}
|
|
for connector: Dictionary in expected:
|
|
var connector_id: StringName = connector[&"id"]
|
|
var existing := path.get_connector(connector_id)
|
|
var endpoints: Array = connector[&"endpoints"]
|
|
var usable := fare_affordable and bool(available.get(endpoints[0], false)) and bool(available.get(endpoints[1], false))
|
|
var state := PathType.CONNECTOR_OPERATIONAL if usable else PathType.CONNECTOR_OUTAGE
|
|
if not existing.is_empty() and existing[&"from"] == connector[&"from"] and existing[&"to"] == connector[&"to"] \
|
|
and existing[&"access_id"] == connector[&"access_id"] and existing[&"operational_state"] == state:
|
|
continue
|
|
if before == null:
|
|
before = path.create_snapshot()
|
|
if existing.is_empty() or existing[&"from"] != connector[&"from"] or existing[&"to"] != connector[&"to"] \
|
|
or existing[&"access_id"] != connector[&"access_id"]:
|
|
if not existing.is_empty():
|
|
path.remove_connector(connector_id)
|
|
if not path.configure_connector(connector_id, connector[&"from"], connector[&"to"], PATH_COST, true, connector[&"access_id"], true,
|
|
{&"movement_type": PathType.MOVEMENT_CONNECTOR}):
|
|
path.restore_snapshot(before)
|
|
return {&"ok": false, &"code": &"WARP_CONNECTOR_CONFIGURE_FAILED", &"reason": path.last_error}
|
|
if not path.set_connector_operational_state(connector_id, state):
|
|
path.restore_snapshot(before)
|
|
return {&"ok": false, &"code": &"WARP_CONNECTOR_STATE_FAILED", &"reason": path.last_error}
|
|
return {&"ok": true, &"connector_count": expected.size()}
|
|
|
|
|
|
## Saved paths must hold exactly the derived warp connectors.
|
|
static func connectors_match(path: PathService, facilities: Array) -> bool:
|
|
var expected := expected_connectors(facilities)
|
|
var ids := path.get_connector_ids_with_prefix(PREFIX)
|
|
if ids.size() != expected.size():
|
|
return false
|
|
for connector: Dictionary in expected:
|
|
var existing := path.get_connector(connector[&"id"])
|
|
if existing.is_empty() or existing[&"from"] != connector[&"from"] or existing[&"to"] != connector[&"to"] \
|
|
or int(existing[&"cost"]) != PATH_COST or StringName(existing[&"access_id"]) != connector[&"access_id"] \
|
|
or existing[&"movement_type"] != PathType.MOVEMENT_CONNECTOR:
|
|
return false
|
|
return true
|