191 lines
8.1 KiB
Python
191 lines
8.1 KiB
Python
from collections import deque
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class RoomSpec:
|
|
key: str
|
|
desc: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ExitSpec:
|
|
source: str
|
|
key: str
|
|
destination: str
|
|
aliases: tuple[str, ...] = ()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ObjectSpec:
|
|
key: str
|
|
location: str
|
|
desc: str
|
|
aliases: tuple[str, ...] = ()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CharacterSpec:
|
|
key: str
|
|
desc: str
|
|
starting_room: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TravelCommandSpec:
|
|
key: str
|
|
aliases: tuple[str, ...]
|
|
target_world: str
|
|
fallback_room: str
|
|
desc: str
|
|
|
|
|
|
ROOMS = (
|
|
RoomSpec(
|
|
"Limbo",
|
|
"The void between worlds. The air carries the pulse of three houses: Mac, VPS, and this one. "
|
|
"Everything begins here before it is given form.",
|
|
),
|
|
RoomSpec(
|
|
"Gatehouse",
|
|
"A stone guard tower at the edge of Bezalel's world. The walls are carved with runes of travel, "
|
|
"proof, and return. Every arrival is weighed before it is trusted.",
|
|
),
|
|
RoomSpec(
|
|
"Great Hall",
|
|
"A vast hall with a long working table. Maps of the three houses hang beside sketches, benchmarks, "
|
|
"and deployment notes. This is where the forge reports back to the house.",
|
|
),
|
|
RoomSpec(
|
|
"The Library of Bezalel",
|
|
"Shelves of technical manuals, Evennia code, test logs, and bridge schematics rise to the ceiling. "
|
|
"This room holds plans waiting to be made real.",
|
|
),
|
|
RoomSpec(
|
|
"The Observatory",
|
|
"A high chamber with telescopes pointing toward the Mac, the VPS, and the wider net. Screens glow with "
|
|
"status lights, latency traces, and long-range signals.",
|
|
),
|
|
RoomSpec(
|
|
"The Workshop",
|
|
"A forge and workbench share the same heat. Scattered here are half-finished bridges, patched harnesses, "
|
|
"and tools laid out for proof before pride.",
|
|
),
|
|
RoomSpec(
|
|
"The Server Room",
|
|
"Racks of humming servers line the walls. Fans push warm air through the chamber while status LEDs beat "
|
|
"like a mechanical heart. This is the pulse of Bezalel's house.",
|
|
),
|
|
RoomSpec(
|
|
"The Garden of Code",
|
|
"A quiet garden where ideas are left long enough to grow roots. Code-shaped leaves flutter in patterned wind, "
|
|
"and a stone path invites patient thought.",
|
|
),
|
|
RoomSpec(
|
|
"The Portal Room",
|
|
"Three shimmering doorways stand in a ring: one marked for the Mac house, one for the VPS, and one for the wider net. "
|
|
"The room hums like a bridge waiting for traffic.",
|
|
),
|
|
)
|
|
|
|
EXITS = (
|
|
ExitSpec("Limbo", "gatehouse", "Gatehouse", ("gate", "tower")),
|
|
ExitSpec("Gatehouse", "limbo", "Limbo", ("void", "back")),
|
|
ExitSpec("Gatehouse", "greathall", "Great Hall", ("hall", "great hall")),
|
|
ExitSpec("Great Hall", "gatehouse", "Gatehouse", ("gate", "tower")),
|
|
ExitSpec("Great Hall", "library", "The Library of Bezalel", ("books", "study")),
|
|
ExitSpec("The Library of Bezalel", "hall", "Great Hall", ("great hall", "back")),
|
|
ExitSpec("Great Hall", "observatory", "The Observatory", ("telescope", "tower top")),
|
|
ExitSpec("The Observatory", "hall", "Great Hall", ("great hall", "back")),
|
|
ExitSpec("Great Hall", "workshop", "The Workshop", ("forge", "bench")),
|
|
ExitSpec("The Workshop", "hall", "Great Hall", ("great hall", "back")),
|
|
ExitSpec("The Workshop", "serverroom", "The Server Room", ("servers", "server room")),
|
|
ExitSpec("The Server Room", "workshop", "The Workshop", ("forge", "bench")),
|
|
ExitSpec("The Workshop", "garden", "The Garden of Code", ("garden of code", "grove")),
|
|
ExitSpec("The Garden of Code", "workshop", "The Workshop", ("forge", "bench")),
|
|
ExitSpec("Gatehouse", "portalroom", "The Portal Room", ("portal", "portals")),
|
|
ExitSpec("The Portal Room", "gatehouse", "Gatehouse", ("gate", "back")),
|
|
)
|
|
|
|
OBJECTS = (
|
|
ObjectSpec("Threshold Ledger", "Gatehouse", "A heavy ledger where arrivals, departures, and field notes are recorded before the work begins."),
|
|
ObjectSpec("Three-House Map", "Great Hall", "A long map showing Mac, VPS, and remote edges in one continuous line of work."),
|
|
ObjectSpec("Bridge Schematics", "The Library of Bezalel", "Rolled plans describing world bridges, Evennia layouts, and deployment paths."),
|
|
ObjectSpec("Compiler Manuals", "The Library of Bezalel", "Manuals annotated in the margins with warnings against cleverness without proof."),
|
|
ObjectSpec("Tri-Axis Telescope", "The Observatory", "A brass telescope assembly that can be turned toward the Mac, the VPS, or the open net."),
|
|
ObjectSpec("Forge Anvil", "The Workshop", "Scarred metal used for turning rough plans into testable form."),
|
|
ObjectSpec("Bridge Workbench", "The Workshop", "A wide bench covered in harness patches, relay notes, and half-soldered bridge parts."),
|
|
ObjectSpec("Heartbeat Console", "The Server Room", "A monitoring console showing service health, latency, and the steady hum of the house."),
|
|
ObjectSpec("Server Racks", "The Server Room", "Stacked machines that keep the world awake even when no one is watching."),
|
|
ObjectSpec("Code Orchard", "The Garden of Code", "Trees with code-shaped leaves. Some branches bear elegant abstractions; others hold broken prototypes."),
|
|
ObjectSpec("Stone Bench", "The Garden of Code", "A place to sit long enough for a hard implementation problem to become clear."),
|
|
ObjectSpec("Mac Portal", "The Portal Room", "A silver doorway whose frame vibrates with the local sovereign house.", ("mac arch",)),
|
|
ObjectSpec("VPS Portal", "The Portal Room", "A cobalt doorway tuned toward the testbed VPS house.", ("vps arch",)),
|
|
ObjectSpec("Net Portal", "The Portal Room", "A pale doorway pointed toward the wider net and every uncertain edge beyond it.", ("net arch", "network arch")),
|
|
)
|
|
|
|
CHARACTERS = (
|
|
CharacterSpec("Timmy", "The Builder's first creation. Quiet, observant, already measuring the room before he speaks.", "Gatehouse"),
|
|
CharacterSpec("Bezalel", "The forge-and-testbed wizard. Scarred hands, steady gaze, the habit of proving things before trusting them.", "The Workshop"),
|
|
CharacterSpec("Marcus", "An old man with kind eyes. He walks like someone who has already survived the night once.", "The Garden of Code"),
|
|
CharacterSpec("Kimi", "The deep scholar of context and meaning. He carries long memory like a lamp.", "The Library of Bezalel"),
|
|
)
|
|
|
|
PORTAL_COMMANDS = (
|
|
TravelCommandSpec(
|
|
"mac",
|
|
("macbook", "local"),
|
|
"Mac house",
|
|
"Limbo",
|
|
"Align with the sovereign local house. Until live cross-world transport is wired, the command resolves into Limbo — the threshold between houses.",
|
|
),
|
|
TravelCommandSpec(
|
|
"vps",
|
|
("testbed", "house"),
|
|
"VPS house",
|
|
"Limbo",
|
|
"Step toward the forge VPS. For now the command lands in Limbo, preserving the inter-world threshold until real linking is live.",
|
|
),
|
|
TravelCommandSpec(
|
|
"net",
|
|
("network", "wider-net"),
|
|
"Wider net",
|
|
"Limbo",
|
|
"Face the open network. The command currently routes through Limbo so the direction exists before the final bridge does.",
|
|
),
|
|
)
|
|
|
|
|
|
def room_keys() -> tuple[str, ...]:
|
|
return tuple(room.key for room in ROOMS)
|
|
|
|
|
|
def character_keys() -> tuple[str, ...]:
|
|
return tuple(character.key for character in CHARACTERS)
|
|
|
|
|
|
def portal_command_keys() -> tuple[str, ...]:
|
|
return tuple(command.key for command in PORTAL_COMMANDS)
|
|
|
|
|
|
def grouped_exits() -> dict[str, tuple[ExitSpec, ...]]:
|
|
grouped: dict[str, list[ExitSpec]] = {}
|
|
for exit_spec in EXITS:
|
|
grouped.setdefault(exit_spec.source, []).append(exit_spec)
|
|
return {key: tuple(value) for key, value in grouped.items()}
|
|
|
|
|
|
def reachable_rooms_from(start: str) -> set[str]:
|
|
seen: set[str] = set()
|
|
queue: deque[str] = deque([start])
|
|
exits_by_room = grouped_exits()
|
|
while queue:
|
|
current = queue.popleft()
|
|
if current in seen:
|
|
continue
|
|
seen.add(current)
|
|
for exit_spec in exits_by_room.get(current, ()):
|
|
if exit_spec.destination not in seen:
|
|
queue.append(exit_spec.destination)
|
|
return seen
|