Files
TheTree_mock/scripts/transport/warp_service.gd
T
TakeshiandClaude Opus 5.5 0cfaec1626 P14-02: Add buildable Mana warp (A07)
2x2 FRONT warp at 3000G/30 materials/360 min, 15G/day and 1 Mana upkeep.
Every completed warp pair is linked by one path connector, so crossing is a
single step; a 3-Mana fare per rider is charged once in the step's tick.
Warps close while either end is unpowered, the reserve is empty or below one
fare, and blocked arrivals offer no step. Saves validate the derived links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:45:28 +09:00

104 lines
4.7 KiB
GDScript

extends RefCounted
## A07 Mana warp: every completed warp is linked to every other warp by one
## path connector, so crossing is a single instant step. Connectors are derived
## from construction state; their operational state follows the reserve and the
## warps' own power. Fares are charged by the Runtime when an actor steps.
const PathType := preload("res://scripts/world/path_service.gd")
const DEFINITION_ID := &"mana_warp"
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
if StringName(str(facility.get(&"definition_id", ""))) != DEFINITION_ID:
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})
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()):
result.append({
&"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[&"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"]:
if not existing.is_empty():
path.remove_connector(connector_id)
if not path.configure_connector(connector_id, connector[&"from"], connector[&"to"], PATH_COST, true, &"", 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"]) != &"" \
or existing[&"movement_type"] != PathType.MOVEMENT_CONNECTOR:
return false
return true