Spaces:
Runtime error
Runtime error
File size: 28,964 Bytes
c782fbf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | from __future__ import annotations
import json
import random
from dataclasses import dataclass
from pathlib import Path
from typing import Any
@dataclass(frozen=True)
class WorldTheme:
title: str
answer: str
foyer_label: str
foyer_description: str
shrine_label: str
shrine_description: str
workshop_label: str
workshop_description: str
courtyard_label: str
courtyard_description: str
gallery_label: str
gallery_description: str
entry_chest_label: str
entry_chest_description: str
iron_door_label: str
iron_door_description: str
ash_mural_label: str
ash_mural_description: str
ash_mural_text: str
iron_chest_label: str
iron_chest_description: str
stone_well_label: str
stone_well_description: str
water_plaque_label: str
water_plaque_description: str
water_plaque_text: str
cartographer_label: str
cartographer_description: str
faded_letter_label: str
faded_letter_description: str
faded_letter_text: str
stone_guardian_label: str
stone_guardian_description: str
brass_key_label: str
brass_key_description: str
torch_label: str
torch_description: str
torn_map_left_label: str
torn_map_left_description: str
torn_map_right_label: str
torn_map_right_description: str
full_map_label: str
full_map_description: str
lens_label: str
lens_description: str
initial_clue_text: str
river_clue_text: str
waterwarden_clue_text: str
_WORLD_THEMES: tuple[WorldTheme, ...] = (
WorldTheme(
title="The River Ward",
answer="mira",
foyer_label="Foyer",
foyer_description="A drafty entry hall with passages north, south, east, and west.",
shrine_label="Shrine",
shrine_description="An open shrine watched by a silent stone guardian.",
workshop_label="Workshop",
workshop_description="An ash-streaked workshop lit by a guttering lamp.",
courtyard_label="Courtyard",
courtyard_description="Rainwater gathers around a cracked stone well.",
gallery_label="Gallery",
gallery_description="Portraits of the wardens hang above a long dust-covered table.",
entry_chest_label="Entry Chest",
entry_chest_description="A squat travel chest sits beside the door.",
iron_door_label="Iron Door",
iron_door_description="A blackened iron door seals the workshop.",
ash_mural_label="Ash Mural",
ash_mural_description="An ash-dark mural is impossible to make out with the naked eye.",
ash_mural_text="The mural preserves one line: the betrayer's name begins with M.",
iron_chest_label="Iron Chest",
iron_chest_description="A soot-stained iron chest is tucked under a bench.",
stone_well_label="Stone Well",
stone_well_description="Etchings circle the well's rim, but they only align from the proper vantage.",
water_plaque_label="Water Plaque",
water_plaque_description="A bronze plaque slides out from the well masonry.",
water_plaque_text="The betrayer lived closest to the river gate.",
cartographer_label="Cartographer",
cartographer_description="The cartographer studies the walls and waits for a completed survey.",
faded_letter_label="Faded Letter",
faded_letter_description="A faded letter is still too blurred to decipher.",
faded_letter_text="Of the wardens, only Mira kept quarters beside the water.",
stone_guardian_label="Stone Guardian",
stone_guardian_description="The guardian asks for the betrayer's name once you are ready.",
brass_key_label="Brass Key",
brass_key_description="A brass key with soot in its teeth.",
torch_label="Torch",
torch_description="A pitch torch with a steady flame.",
torn_map_left_label="Torn Map Left",
torn_map_left_description="The left half of a survey map.",
torn_map_right_label="Torn Map Right",
torn_map_right_description="The right half of a survey map.",
full_map_label="Full Map",
full_map_description="A restored map of the ward.",
lens_label="Lens",
lens_description="A polished lens in a brass frame.",
initial_clue_text="The betrayer's name begins with M.",
river_clue_text="The betrayer lived closest to the river gate.",
waterwarden_clue_text="Of the wardens, only Mira kept quarters beside the water.",
),
WorldTheme(
title="The Ember Vault",
answer="vesna",
foyer_label="Receiving Hall",
foyer_description="A warm stone hall lined with soot and copper hooks.",
shrine_label="Crucible Shrine",
shrine_description="A brass sentinel stands before a furnace-bright altar.",
workshop_label="Forge Annex",
workshop_description="Bellows creak above benches powdered with black ash.",
courtyard_label="Quench Yard",
courtyard_description="A cracked basin gathers rain beside the old quench line.",
gallery_label="Ledger Hall",
gallery_description="Burned account books rest beneath portraits of furnace wardens.",
entry_chest_label="Courier Trunk",
entry_chest_description="A courier trunk waits under a soot-marked peg rail.",
iron_door_label="Furnace Door",
iron_door_description="A scorched iron door blocks the annex.",
ash_mural_label="Cinder Frieze",
ash_mural_description="A smoke-dark frieze only sharpens under moving flame.",
ash_mural_text="A surviving line says the betrayer's name begins with V.",
iron_chest_label="Coal Locker",
iron_chest_description="A riveted locker is wedged beneath a slagged bench.",
stone_well_label="Quench Basin",
stone_well_description="Marks on the basin align only when seen with the full survey.",
water_plaque_label="Cooling Plaque",
water_plaque_description="A brass plate rises from a seam in the basin stone.",
water_plaque_text="The betrayer worked closest to the quench trench.",
cartographer_label="Quartermaster",
cartographer_description="The quartermaster trades only for a complete furnace survey.",
faded_letter_label="Scorched Ledger",
faded_letter_description="Heat has blurred the ink into copper-colored streaks.",
faded_letter_text="Only Vesna kept the cooling ledgers beside the trench.",
stone_guardian_label="Brass Sentinel",
stone_guardian_description="The sentinel requests the betrayer's name when the case is ready.",
brass_key_label="Copper Key",
brass_key_description="A copper key with furnace grit packed in the cuts.",
torch_label="Coal Torch",
torch_description="A coal torch that burns with a steady orange core.",
torn_map_left_label="Smelter Map Left",
torn_map_left_description="The left half of a furnace survey.",
torn_map_right_label="Smelter Map Right",
torn_map_right_description="The right half of a furnace survey.",
full_map_label="Furnace Survey",
full_map_description="A restored survey of the ember vault.",
lens_label="Gauge Lens",
lens_description="A thick gauge lens set in a brass ring.",
initial_clue_text="The betrayer's name begins with V.",
river_clue_text="The betrayer worked closest to the quench trench.",
waterwarden_clue_text="Only Vesna kept the cooling ledgers beside the trench.",
),
WorldTheme(
title="The Astral Archive",
answer="selene",
foyer_label="Entry Rotunda",
foyer_description="A quiet rotunda opens toward stacked corridors and a dim observatory stair.",
shrine_label="Moon Chapel",
shrine_description="A silver warden stands beneath a ceiling of cold stars.",
workshop_label="Chart Room",
workshop_description="Tables of brass instruments glint in powdery moon dust.",
courtyard_label="Star Court",
courtyard_description="A dry fountain mirrors the constellations in chipped stone.",
gallery_label="Catalog Hall",
gallery_description="Glass cases hold the names of long-dead archivists.",
entry_chest_label="Porter's Case",
entry_chest_description="A leather case rests under the chart hooks.",
iron_door_label="Star Door",
iron_door_description="A ribbed iron door seals the chart room.",
ash_mural_label="Night Chart",
ash_mural_description="The chart is unreadable until lit from the proper angle.",
ash_mural_text="One surviving note says the betrayer's name begins with S.",
iron_chest_label="Index Chest",
iron_chest_description="A narrow chest sits below a shelf of cracked lenses.",
stone_well_label="Dry Fountain",
stone_well_description="Its star marks align only when the full survey is restored.",
water_plaque_label="Star Plaque",
water_plaque_description="A silver plaque slides free from the fountain rim.",
water_plaque_text="The betrayer slept nearest the eastern telescope.",
cartographer_label="Archivist",
cartographer_description="The archivist will trade for a complete celestial survey.",
faded_letter_label="Blurred Index",
faded_letter_description="The index script is too faint without magnification.",
faded_letter_text="Among the archivists, only Selene kept quarters by the east telescope.",
stone_guardian_label="Silver Warden",
stone_guardian_description="The warden will hear the accusation once you have evidence.",
brass_key_label="Star Key",
brass_key_description="A slim key engraved with a crescent notch.",
torch_label="Lamp Wand",
torch_description="A narrow lamp wand with a clean blue flame.",
torn_map_left_label="Celestial Map Left",
torn_map_left_description="The left half of a star survey.",
torn_map_right_label="Celestial Map Right",
torn_map_right_description="The right half of a star survey.",
full_map_label="Celestial Survey",
full_map_description="A restored survey of the astral archive.",
lens_label="Astrolabe Lens",
lens_description="A polished lens mounted in silver wire.",
initial_clue_text="The betrayer's name begins with S.",
river_clue_text="The betrayer slept nearest the eastern telescope.",
waterwarden_clue_text="Among the archivists, only Selene kept quarters by the east telescope.",
),
WorldTheme(
title="The Glass Conservatory",
answer="liora",
foyer_label="Gate House",
foyer_description="A humid gate house opens onto vine-choked passages.",
shrine_label="Bloom Shrine",
shrine_description="A mossy guardian waits among chipped planters.",
workshop_label="Potting Room",
workshop_description="Clay dust and root knives cover the worktables.",
courtyard_label="Glass Court",
courtyard_description="A cracked basin sits beneath panes webbed with ivy.",
gallery_label="Seed Gallery",
gallery_description="Pressed flowers hang beside records of vanished caretakers.",
entry_chest_label="Garden Chest",
entry_chest_description="A cedar chest is tucked beside the rain cloaks.",
iron_door_label="Greenhouse Door",
iron_door_description="A warped iron door blocks the potting room.",
ash_mural_label="Vine Panel",
ash_mural_description="The panel's scratches only read clearly under a steady flame.",
ash_mural_text="A scratched line says the betrayer's name begins with L.",
iron_chest_label="Tool Locker",
iron_chest_description="A damp locker crouches under a potting bench.",
stone_well_label="Ivy Basin",
stone_well_description="The etched rings align only when the full garden survey is in hand.",
water_plaque_label="Root Plaque",
water_plaque_description="A greened plaque slides from the basin wall.",
water_plaque_text="The betrayer tended the beds nearest the rain cistern.",
cartographer_label="Head Gardener",
cartographer_description="The gardener will barter only for a complete bed map.",
faded_letter_label="Watered Note",
faded_letter_description="The note is blurred by old rain and fertilizer.",
faded_letter_text="Only Liora kept the cistern ledgers beside the rain beds.",
stone_guardian_label="Moss Guardian",
stone_guardian_description="The guardian listens when you are ready to name the betrayer.",
brass_key_label="Trellis Key",
brass_key_description="A greened key shaped like a curling vine.",
torch_label="Glass Lantern",
torch_description="A glass-sided lantern with a bright white flame.",
torn_map_left_label="Bed Map Left",
torn_map_left_description="The left half of a conservatory plan.",
torn_map_right_label="Bed Map Right",
torn_map_right_description="The right half of a conservatory plan.",
full_map_label="Bed Survey",
full_map_description="A restored survey of the conservatory beds.",
lens_label="Prism Lens",
lens_description="A prism lens wrapped in tarnished copper.",
initial_clue_text="The betrayer's name begins with L.",
river_clue_text="The betrayer tended the beds nearest the rain cistern.",
waterwarden_clue_text="Only Liora kept the cistern ledgers beside the rain beds.",
),
WorldTheme(
title="The Salt Bastion",
answer="corin",
foyer_label="Watch Hall",
foyer_description="A salt-stung hall opens toward barracks, chapel, and the sea court.",
shrine_label="Tide Chapel",
shrine_description="A stone warden keeps watch over a shrine of ropes and shells.",
workshop_label="Signal Room",
workshop_description="Lantern hooks sway above benches dusted with salt ash.",
courtyard_label="Sea Court",
courtyard_description="A dry cistern sits beneath walls pitted by ocean wind.",
gallery_label="Roll Hall",
gallery_description="Roster boards hang beneath portraits of old coast captains.",
entry_chest_label="Harbor Chest",
entry_chest_description="A travel chest sits beside a rack of oilskins.",
iron_door_label="Beacon Door",
iron_door_description="A rusted iron door bars the signal room.",
ash_mural_label="Signal Board",
ash_mural_description="Salt haze hides the markings until a lamp is raised close.",
ash_mural_text="A surviving mark says the betrayer's name begins with C.",
iron_chest_label="Tar Locker",
iron_chest_description="A tar-black locker hides below a signal bench.",
stone_well_label="Dry Cistern",
stone_well_description="Its carved rings make sense only with the restored coast survey.",
water_plaque_label="Harbor Plaque",
water_plaque_description="A plaque rises from a crack in the cistern lip.",
water_plaque_text="The betrayer bunked nearest the harbor chain.",
cartographer_label="Harbor Clerk",
cartographer_description="The clerk trades only for a complete bastion survey.",
faded_letter_label="Salted Roll",
faded_letter_description="Salt has crusted over the roster names.",
faded_letter_text="Only Corin kept the harbor ledgers beside the chain gate.",
stone_guardian_label="Stone Warden",
stone_guardian_description="The warden asks for the betrayer's name when the proof is ready.",
brass_key_label="Anchor Key",
brass_key_description="A heavy key stamped with a worn anchor.",
torch_label="Signal Lamp",
torch_description="A shuttered lamp with a disciplined yellow flame.",
torn_map_left_label="Coast Map Left",
torn_map_left_description="The left half of a bastion survey.",
torn_map_right_label="Coast Map Right",
torn_map_right_description="The right half of a bastion survey.",
full_map_label="Coast Survey",
full_map_description="A restored survey of the salt bastion.",
lens_label="Captain's Lens",
lens_description="A salt-clear lens held in a bronze ring.",
initial_clue_text="The betrayer's name begins with C.",
river_clue_text="The betrayer bunked nearest the harbor chain.",
waterwarden_clue_text="Only Corin kept the harbor ledgers beside the chain gate.",
),
)
def sample_world_definition(seed: int | None = None, difficulty_target: float = 1.5) -> dict[str, Any]:
theme = _select_theme(seed)
return _build_world(theme, difficulty_target=difficulty_target)
def load_world(path: str) -> dict[str, Any]:
return json.loads(Path(path).read_text(encoding="utf-8"))
def _select_theme(seed: int | None) -> WorldTheme:
if seed is None:
return _WORLD_THEMES[0]
rng = random.Random(seed)
return _WORLD_THEMES[rng.randrange(len(_WORLD_THEMES))]
def _build_world(theme: WorldTheme, *, difficulty_target: float) -> dict[str, Any]:
return {
"meta": {
"title": theme.title,
"difficulty_target": difficulty_target,
"start_node_id": "foyer",
"win_condition": {
"type": "deduce",
"target_npc_id": "stone_guardian",
"answer_string": theme.answer,
},
},
"nodes": [
{"id": "foyer", "type": "location", "label": theme.foyer_label, "description": theme.foyer_description},
{"id": "shrine", "type": "location", "label": theme.shrine_label, "description": theme.shrine_description},
{"id": "workshop", "type": "location", "label": theme.workshop_label, "description": theme.workshop_description},
{"id": "courtyard", "type": "location", "label": theme.courtyard_label, "description": theme.courtyard_description},
{"id": "gallery", "type": "location", "label": theme.gallery_label, "description": theme.gallery_description},
{
"id": "entry_chest",
"type": "container",
"label": theme.entry_chest_label,
"description": theme.entry_chest_description,
"parent_id": "foyer",
"open": False,
"locked": False,
"lock_key_id": None,
},
{
"id": "iron_door",
"type": "door",
"label": theme.iron_door_label,
"description": theme.iron_door_description,
"open": False,
"locked": True,
"lock_key_id": "brass_key",
},
{
"id": "ash_mural",
"type": "readable",
"label": theme.ash_mural_label,
"description": theme.ash_mural_description,
"parent_id": "workshop",
"clue_id": "initial_clue",
"requires_item_id": "torch",
"consumes_item": False,
"text_content": theme.ash_mural_text,
},
{
"id": "iron_chest",
"type": "container",
"label": theme.iron_chest_label,
"description": theme.iron_chest_description,
"parent_id": "workshop",
"open": False,
"locked": False,
"lock_key_id": None,
},
{
"id": "stone_well",
"type": "fixture",
"label": theme.stone_well_label,
"description": theme.stone_well_description,
"parent_id": "courtyard",
"requires_item_id": "full_map",
"reveals_item_id": None,
"reveals_readable_id": "water_plaque",
"consumes_item": False,
},
{
"id": "water_plaque",
"type": "readable",
"label": theme.water_plaque_label,
"description": theme.water_plaque_description,
"parent_id": "courtyard",
"clue_id": "river_clue",
"requires_item_id": None,
"consumes_item": False,
"text_content": theme.water_plaque_text,
},
{
"id": "cartographer",
"type": "npc",
"label": theme.cartographer_label,
"description": theme.cartographer_description,
"parent_id": "gallery",
"requires_item_id": "full_map",
"gives_item_id": "lens",
"gives_clue_id": None,
},
{
"id": "faded_letter",
"type": "readable",
"label": theme.faded_letter_label,
"description": theme.faded_letter_description,
"parent_id": "gallery",
"clue_id": "waterwarden_clue",
"requires_item_id": "lens",
"consumes_item": False,
"text_content": theme.faded_letter_text,
},
{
"id": "stone_guardian",
"type": "npc",
"label": theme.stone_guardian_label,
"description": theme.stone_guardian_description,
"parent_id": "shrine",
"requires_item_id": None,
"gives_item_id": None,
"gives_clue_id": None,
},
],
"edges": [
{"id": "foyer_north", "from_node_id": "foyer", "to_node_id": "shrine", "direction": "north", "type": "passage", "required_item_id": None, "door_node_id": None},
{"id": "shrine_south", "from_node_id": "shrine", "to_node_id": "foyer", "direction": "south", "type": "passage", "required_item_id": None, "door_node_id": None},
{"id": "foyer_east", "from_node_id": "foyer", "to_node_id": "workshop", "direction": "east", "type": "locked_passage", "required_item_id": "brass_key", "door_node_id": "iron_door"},
{"id": "workshop_west", "from_node_id": "workshop", "to_node_id": "foyer", "direction": "west", "type": "locked_passage", "required_item_id": "brass_key", "door_node_id": "iron_door"},
{"id": "foyer_west", "from_node_id": "foyer", "to_node_id": "courtyard", "direction": "west", "type": "passage", "required_item_id": None, "door_node_id": None},
{"id": "courtyard_east", "from_node_id": "courtyard", "to_node_id": "foyer", "direction": "east", "type": "passage", "required_item_id": None, "door_node_id": None},
{"id": "foyer_south", "from_node_id": "foyer", "to_node_id": "gallery", "direction": "south", "type": "passage", "required_item_id": None, "door_node_id": None},
{"id": "gallery_north", "from_node_id": "gallery", "to_node_id": "foyer", "direction": "north", "type": "passage", "required_item_id": None, "door_node_id": None},
],
"items": [
{"id": "brass_key", "label": theme.brass_key_label, "description": theme.brass_key_description, "subtype": "key", "start_node_id": "entry_chest"},
{"id": "torch", "label": theme.torch_label, "description": theme.torch_description, "subtype": "puzzle", "start_node_id": "workshop"},
{"id": "torn_map_left", "label": theme.torn_map_left_label, "description": theme.torn_map_left_description, "subtype": "puzzle", "start_node_id": "iron_chest"},
{"id": "torn_map_right", "label": theme.torn_map_right_label, "description": theme.torn_map_right_description, "subtype": "puzzle", "start_node_id": "courtyard"},
{"id": "full_map", "label": theme.full_map_label, "description": theme.full_map_description, "subtype": "puzzle", "start_node_id": None},
{"id": "lens", "label": theme.lens_label, "description": theme.lens_description, "subtype": "puzzle", "start_node_id": None},
],
"clues": [
{"id": "initial_clue", "text": theme.initial_clue_text},
{"id": "river_clue", "text": theme.river_clue_text},
{"id": "waterwarden_clue", "text": theme.waterwarden_clue_text},
],
"recipes": [
{
"id": "restore_map",
"input_item_ids": ["torn_map_left", "torn_map_right"],
"output_item_id": "full_map",
}
],
"quest_chain": [
{"step_id": "open_entry_chest", "description": f"Open the {theme.entry_chest_label.lower()}.", "requires_step_ids": [], "action": "open(entry_chest)"},
{"step_id": "take_brass_key", "description": f"Take the {theme.brass_key_label.lower()}.", "requires_step_ids": ["open_entry_chest"], "action": "take(brass_key,entry_chest)"},
{"step_id": "unlock_workshop", "description": f"Unlock the {theme.iron_door_label.lower()}.", "requires_step_ids": ["take_brass_key"], "action": "unlock(iron_door,brass_key)"},
{"step_id": "open_workshop", "description": f"Open the {theme.iron_door_label.lower()}.", "requires_step_ids": ["unlock_workshop"], "action": "open(iron_door)"},
{"step_id": "go_workshop", "description": f"Enter the {theme.workshop_label.lower()}.", "requires_step_ids": ["open_workshop"], "action": "go(workshop)"},
{"step_id": "take_torch", "description": f"Take the {theme.torch_label.lower()}.", "requires_step_ids": ["go_workshop"], "action": "take(torch,workshop)"},
{"step_id": "use_torch_on_mural", "description": f"Use the {theme.torch_label.lower()} on the {theme.ash_mural_label.lower()}.", "requires_step_ids": ["take_torch"], "action": "use(torch,ash_mural)"},
{"step_id": "open_iron_chest", "description": f"Open the {theme.iron_chest_label.lower()}.", "requires_step_ids": ["go_workshop"], "action": "open(iron_chest)"},
{"step_id": "take_left_map", "description": f"Take the {theme.torn_map_left_label.lower()}.", "requires_step_ids": ["open_iron_chest"], "action": "take(torn_map_left,iron_chest)"},
{"step_id": "return_foyer", "description": f"Return to the {theme.foyer_label.lower()}.", "requires_step_ids": ["take_left_map"], "action": "go(foyer)"},
{"step_id": "go_courtyard", "description": f"Head to the {theme.courtyard_label.lower()}.", "requires_step_ids": ["return_foyer"], "action": "go(courtyard)"},
{"step_id": "take_right_map", "description": f"Take the {theme.torn_map_right_label.lower()}.", "requires_step_ids": ["go_courtyard"], "action": "take(torn_map_right,courtyard)"},
{"step_id": "combine_map", "description": f"Restore the {theme.full_map_label.lower()}.", "requires_step_ids": ["take_right_map"], "action": "combine(torn_map_left,torn_map_right)"},
{"step_id": "use_map_on_well", "description": f"Use the {theme.full_map_label.lower()} on the {theme.stone_well_label.lower()}.", "requires_step_ids": ["combine_map"], "action": "use(full_map,stone_well)"},
{"step_id": "read_plaque", "description": f"Read the {theme.water_plaque_label.lower()}.", "requires_step_ids": ["use_map_on_well"], "action": "read(water_plaque)"},
{"step_id": "go_foyer_again", "description": f"Go back to the {theme.foyer_label.lower()}.", "requires_step_ids": ["read_plaque"], "action": "go(foyer)"},
{"step_id": "go_gallery", "description": f"Head to the {theme.gallery_label.lower()}.", "requires_step_ids": ["go_foyer_again"], "action": "go(gallery)"},
{"step_id": "give_map", "description": f"Give the map to the {theme.cartographer_label.lower()}.", "requires_step_ids": ["go_gallery"], "action": "give(full_map,cartographer)"},
{"step_id": "use_lens_on_letter", "description": f"Use the {theme.lens_label.lower()} on the {theme.faded_letter_label.lower()}.", "requires_step_ids": ["give_map"], "action": "use(lens,faded_letter)"},
{"step_id": "return_foyer_final", "description": f"Return to the {theme.foyer_label.lower()} again.", "requires_step_ids": ["use_lens_on_letter"], "action": "go(foyer)"},
{"step_id": "go_shrine", "description": f"Go to the {theme.shrine_label.lower()}.", "requires_step_ids": ["return_foyer_final"], "action": "go(shrine)"},
{"step_id": "talk_guardian", "description": f"Speak to the {theme.stone_guardian_label.lower()}.", "requires_step_ids": ["go_shrine"], "action": "talk(stone_guardian)"},
{"step_id": "submit_answer", "description": "Submit the betrayer's name.", "requires_step_ids": ["talk_guardian"], "action": f'submit("{theme.answer}")'},
],
}
|