From 064084fde6da4446d85c5b018d0a59d3ae1bdc2e Mon Sep 17 00:00:00 2001 From: Caleb LeNoir Date: Fri, 30 Oct 2026 13:06:39 -0400 Subject: [PATCH 1/3] Let a floor fall in a game when its countdown runs out, and the player on it with it A floor whose row has minutes falls in a game that many story minutes after the game first stood on it (playthrough_floors.arrived_at). The engine asks before a line is read and again once the line's time is spent: every reached floor whose deadline has passed gets fell_at (the deadline), and a player standing on one is harmed to zero and the game ends. A wait across the deadline is played, never refused, and is fatal. A fallen floor is gone in that game only: cast_in holds nobody on it, Game::exits and the room's exits leave out every way onto it, and a move that names such a way, or the floor, is refused in the engine's words: "{floor} has fallen. There is no way back onto it." On the live path no reader is asked. The world's rows are untouched, so another game finds the floor whole and runs its own countdown. The glance gains floor (name, falls_at, minutes_left and the countdown line "{floor} falls in {h} hours {m} minutes." / "{floor} falls in {m} minutes.") and collapse ("{floor} gave way at {time}, and {fullname} fell with it.", which the unchanged death statement follows). The engine-alone report notes the collapse. No request carries any of it. Migration 20261031120000 adds floors.minutes and playthrough_floors.fell_at, applied to every world dump as Rails' SQLite adapter applies it. store::OPTIONAL_COLUMNS holds both, so the game at its current pin still opens and plays; SCHEMA_VERSION and shape.txt do not move. No golden or records file moved, and the fight simulator's reference fights are unchanged. New world a-floor-that-falls (the game's loader at server 132b475, then the migration and minutes set) and scripts waiting-out-a-floor and a-floor-falls-behind-you, in PASSING with goldens and records. --- README.md | 4 +- parity/PASSING | 2 + parity/goldens/a-floor-falls-behind-you.json | 1011 + parity/goldens/waiting-out-a-floor.json | 524 + parity/records/a-floor-falls-behind-you.json | 18007 ++++++++++++++++ parity/records/waiting-out-a-floor.json | 4065 ++++ parity/scripts/a-floor-falls-behind-you.yml | 156 + parity/scripts/waiting-out-a-floor.yml | 92 + parity/worlds/a-bell-nobody-has-rung.sql | 5 +- parity/worlds/a-chamber-of-one-room.sql | 5 +- .../worlds/a-clerk-with-somewhere-to-be.sql | 5 +- parity/worlds/a-conversation-at-the-gate.sql | 5 +- parity/worlds/a-crowded-cellar.sql | 5 +- parity/worlds/a-deeper-floor.sql | 9 +- parity/worlds/a-dose-beyond-two-doors.sql | 5 +- parity/worlds/a-floor-that-falls.sql | 669 + parity/worlds/a-friend-at-the-gate.sql | 5 +- parity/worlds/a-guarded-stair.sql | 9 +- parity/worlds/a-key-for-the-other-door.sql | 5 +- parity/worlds/a-lock-in-the-moving-city.sql | 5 +- parity/worlds/a-lodge-inside-the-gate.sql | 5 +- parity/worlds/a-turn-at-the-gate.sql | 5 +- parity/worlds/a-window-over-the-yard.sql | 5 +- parity/worlds/a-writ-on-the-copying-desk.sql | 5 +- .../worlds/a-written-mill-beside-a-stub.sql | 5 +- parity/worlds/a-yard-before-the-winter.sql | 5 +- parity/worlds/the-cloakroom-at-the-ferry.sql | 5 +- parity/worlds/the-furnished-rooms.sql | 5 +- parity/worlds/the-green-at-saltmarsh.sql | 5 +- parity/worlds/the-iron-gate-descends.sql | 5 +- parity/worlds/the-landing-stair.sql | 5 +- parity/worlds/the-lunar-cartographer.sql | 5 +- parity/worlds/the-pottery-on-mill-lane.sql | 5 +- parity/worlds/the-quay-house.sql | 5 +- parity/worlds/the-rook-wood-gate.sql | 5 +- parity/worlds/the-ropewalk-at-saltmarsh.sql | 5 +- parity/worlds/the-salt-assizes.sql | 5 +- parity/worlds/the-unfinished-workshop.sql | 5 +- parity/worlds/the-unrecorded-hour.sql | 5 +- parity/worlds/the-weighing-house.sql | 5 +- parity/worlds/three-floors-that-raise-you.sql | 11 +- parity/worlds/two-clerks-at-one-counter.sql | 5 +- parity/worlds/two-floors-and-a-key.sql | 9 +- src/cascade.rs | 1 + src/glance.rs | 27 + src/grammar.rs | 28 + src/moment.rs | 9 + src/playthrough.rs | 100 +- src/refusal.rs | 6 + src/room.rs | 21 + src/store.rs | 23 +- src/turn.rs | 98 +- src/turn/told.rs | 16 + tests/engine.rs | 40 + tests/floor_falls.rs | 436 + tests/vectors/lines.rs | 1 + 56 files changed, 25434 insertions(+), 90 deletions(-) create mode 100644 parity/goldens/a-floor-falls-behind-you.json create mode 100644 parity/goldens/waiting-out-a-floor.json create mode 100644 parity/records/a-floor-falls-behind-you.json create mode 100644 parity/records/waiting-out-a-floor.json create mode 100644 parity/scripts/a-floor-falls-behind-you.yml create mode 100644 parity/scripts/waiting-out-a-floor.yml create mode 100644 parity/worlds/a-floor-that-falls.sql create mode 100644 tests/floor_falls.rs diff --git a/README.md b/README.md index 6fff151..c3c3ee0 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ rule rewrites them here, and the game vendors them at the commit it pins. | `cascade` | `Playthrough::Classifier::Cascade`, `::State`, `::Request`, `SystemOneAgent::Answers` | the System One request for a line, and recorded answers composed into an intent or escalated. It sends nothing | | `classifier` | `Playthrough::Classifier#classify` | a line read over a room's records: System One first where it is on, then the classifier model's call, through whatever answers the two (`classifier::Reader`) | | `records` | the rows a builder reads | every row of every table, handed in as values; a query is a filter over a list | -| `playthrough` | `Playthrough`'s readers, `Playthrough::Vitals::Condition`, `Playthrough::Toll#to_s` | who is in a room, how much is left of a body, who is fighting the party; the floors a game has reached (`playthrough_floors`, written by `turn` the first time the game stands on each), the level its player has risen to on them (on a floor whose row says `levels_up`, one past the level the game had, healed to full there and then; nobody else, and no world row, is ever raised), the hit points a body can take in this game now or when a blow or toll landed, and what the engine says to the player of a floor that raised a level, which no model is told | +| `playthrough` | `Playthrough`'s readers, `Playthrough::Vitals::Condition`, `Playthrough::Toll#to_s` | who is in a room, how much is left of a body, who is fighting the party; the floors a game has reached (`playthrough_floors`, written by `turn` the first time the game stands on each), the level its player has risen to on them (on a floor whose row says `levels_up`, one past the level the game had, healed to full there and then; nobody else, and no world row, is ever raised), the hit points a body can take in this game now or when a blow or toll landed, and what the engine says to the player of a floor that raised a level, which no model is told; and a floor's countdown (`floors.minutes` from the game's arrival): when it falls in this game, which `turn` writes (`playthrough_floors.fell_at`) before a line is read and once its time is spent, killing a player who stands on it; that nobody on a fallen floor is alive in this game and no way out leads onto it, while the world's rows and every other game keep it whole; and what the engine says to the player of the time left and of a floor that gave way under them, which no model is told | | `ledger` | `Playthrough::Ledger` | what one person saw happen in one game, inside both of its bounds | | `memory` | `Playthrough::Memory` | which earlier exchanges come back into a prompt, ranked with Ruby's float arithmetic | | `plan` | `Location::Plan` | a room's size, storey and ways out, said in sentences | @@ -75,7 +75,7 @@ rule rewrites them here, and the game vendors them at the commit it pins. | `reach` | none: it was written here | what is within arm's length in a laid-out room (your cell and the eight around it), the nearest free cell beside a thing or person you walk up to, and where a step back of two paces puts you or that there is no room behind you, with no die; and the doctor's warning for a place the party could stand in with no layout (`Engine::layout_findings`), which changes nothing. No act reads it yet and nothing a model reads names it | | `layout` | none: it was written here | a layout for a room that stands on its own (no parent): a box at the origin of its own plane, sized from its sort's band (`data/location/size.yml`, `Roll::ROOM_SIZE` on the room's row), written as the room is first written; which side each of its ways out leaves by (`Roll::WAY_OUT_SIDE` on the doorway's row, worked out on every read and never kept) and the cell beside it, which a throw at that way out is measured to; and every such room of a story with no layout yet, laid out with a spot for each of the world's own loose things and people in it (`Engine::lay_out_rooms`, which the game runs after it pulls). A storey is compared only within one place (`plan::storey`). Nothing a model reads names any of it | | `revisit` | none: it was written here | the arrival diff: what a game saw of a room when it last left it, kept by the move that walked out, and on a walk back what has changed among it, in a closed list in the engine's words (a thing gone, held by somebody there, moved or broken; a person gone, come or dead; a way out gone or new; an event of the world in the room), kept by the move that walked back. Every game keeps both; a narrated game's arrival writer is told the list under "Since You Were Last Here" and that nothing else changed | -| `glance` | `Playthrough::Glance`, `Playthrough::Availability`, `Playthrough::SlashMenu` | what a front end's panels show between turns: the room, its ways out, who and what is here, which verbs are open at what (each target one the turn plays, each closed verb closed in the refusal's words), the slash menu and the next beat | +| `glance` | `Playthrough::Glance`, `Playthrough::Availability`, `Playthrough::SlashMenu` | what a front end's panels show between turns: the room, its ways out, who and what is here, which verbs are open at what (each target one the turn plays, each closed verb closed in the refusal's words), the slash menu and the next beat; and the floor the player stands on with when it falls (`floor`), and the sentence for a floor that gave way under them (`collapse`) | | `facts`, `prompt_version` | `Scene::Narrator#prompt_for`, `Playthrough::Turn`'s `_fact` builders, `Playthrough::PromptVersion::Scaffold` | what a turn hands the narrator as already done, and that scaffold rendered against fixed placeholders for the game's prompt version to digest | | `command` | `Playthrough::Command`, `Playthrough::Command::Journal` | a submitted line and its token, the order lines were accepted in, and the receipts a turn writes as it goes | | `turn::Turn` | `Playthrough::Turn#play` with a request token, `Playthrough::Classifier#classify`, `InteractionAgent`, `Scene::Narrator`, `Scene::Generator`, `Location::Generator#realize!` with `Item::Kit#furnish!`, `Item::Registry`, `Character::Registry`, `Location::RoomName` and `Location::Interior.lay_out!`, `Quest::Binder`, `Quest::Deadline`, `Scene::Ending`, `Item::Inscriber` | a submitted line read, refused or played through the models, told in prose or in the engine's own words, and answered by the world | diff --git a/parity/PASSING b/parity/PASSING index 7c3052c..ececa6d 100644 --- a/parity/PASSING +++ b/parity/PASSING @@ -18,6 +18,7 @@ a-desk-and-what-lies-on-it a-drawer-searched-once a-fight-that-kills-the-player a-fight-the-player-wins +a-floor-falls-behind-you a-floor-laid-out-to-its-count a-floor-raises-a-level-once a-foe-demands-and-still-strikes @@ -93,6 +94,7 @@ truncated-character-prose-keeps-the-person two-players-of-one-arc two-readers-of-one-typed-line unfinished-realization-resumes-on-entry +waiting-out-a-floor walking-in-off-the-quay leaving-the-room-you-stand-in a-plain-take-is-told-in-the-engines-own-words diff --git a/parity/goldens/a-floor-falls-behind-you.json b/parity/goldens/a-floor-falls-behind-you.json new file mode 100644 index 0000000..e984976 --- /dev/null +++ b/parity/goldens/a-floor-falls-behind-you.json @@ -0,0 +1,1011 @@ +{ + "script": "a-floor-falls-behind-you", + "story": "A Floor That Falls", + "steps": [ + { + "step": "step 1 onto-the-first-floor", + "player": "first", + "typed": "/move The First Floor", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "stub" + }, + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": "move -> The First Floor", + "resolved_by": "grammar", + "note": "into-the-first-floor: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 1.5, + "shown": null + } + }, + { + "step": "step 2 to-the-way-down", + "player": "first", + "typed": "/move The First Floor room 2", + "dump": { + "location": { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + { + "id": 1000000003, + "name": "The Second Floor", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000016, + "name": "ledge" + }, + { + "id": 1000000019, + "name": "old rope end" + }, + { + "id": 1000000017, + "name": "pebble" + }, + { + "id": 1000000013, + "name": "rock shelf" + }, + { + "id": 1000000014, + "name": "rubble" + }, + { + "id": 1000000018, + "name": "rusted hinge" + }, + { + "id": 1000000015, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [ + { + "id": 1000000002, + "name": "Wenna Cole" + } + ], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": { + "Wenna Cole": 6 + }, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": "move -> The First Floor room 2", + "resolved_by": "grammar", + "note": "first-floor-room-2: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 1.0, + "shown": null + } + }, + { + "step": "step 3 down-onto-the-second-floor", + "player": "first", + "typed": "/move The Second Floor", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": "move -> The Second Floor", + "resolved_by": "grammar", + "note": "into-the-second-floor: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 1.5, + "shown": null + } + }, + { + "step": "step 4 wait-1", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-1: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 5 wait-2", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-2: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 6 wait-3", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-3: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 7 wait-4", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-4: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 8 wait-5", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-5: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 9 the-first-floor-falls-above-her", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-6: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 10 there-is-no-way-back-up", + "player": "first", + "typed": "/move The First Floor room 2", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": false, + "change": null, + "refused": true, + "offers": "The First Floor has fallen. There is no way back onto it.", + "understood": null, + "resolved_by": "grammar", + "note": null, + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 0.0, + "shown": null + } + }, + { + "step": "step 11 nor-onto-the-floor-by-its-name", + "player": "first", + "typed": "/move The First Floor", + "dump": { + "location": { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000007, + "name": "The Second Floor room 2", + "detail": "stub" + } + ], + "here": [ + { + "id": 1000000029, + "name": "bat skull" + }, + { + "id": 1000000030, + "name": "candle stub" + }, + { + "id": 1000000026, + "name": "rock shelf" + }, + { + "id": 1000000027, + "name": "rubble" + }, + { + "id": 1000000028, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": false, + "change": null, + "refused": true, + "offers": "The First Floor has fallen. There is no way back onto it.", + "understood": null, + "resolved_by": "grammar", + "note": null, + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 0.0, + "shown": null + } + }, + { + "step": "step 12 a-second-game-finds-the-first-floor-whole (second)", + "player": "second", + "typed": "/move The First Floor", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000032, + "name": "pool" + }, + { + "id": 1000000031, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "moved: The Landing -> The First Floor room 1", + "refused": false, + "offers": null, + "understood": "move -> The First Floor room 1", + "resolved_by": "grammar", + "note": null, + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 0.0, + "shown": null + } + }, + { + "step": "step 13 and-wenna-is-alive-at-its-stair (second)", + "player": "second", + "typed": "/move The First Floor room 2", + "dump": { + "location": { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + { + "id": 1000000006, + "name": "The Second Floor room 1", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000036, + "name": "ledge" + }, + { + "id": 1000000039, + "name": "old rope end" + }, + { + "id": 1000000037, + "name": "pebble" + }, + { + "id": 1000000033, + "name": "rock shelf" + }, + { + "id": 1000000034, + "name": "rubble" + }, + { + "id": 1000000038, + "name": "rusted hinge" + }, + { + "id": 1000000035, + "name": "stalagmite" + } + ], + "carrying": [], + "present": [ + { + "id": 1000000002, + "name": "Wenna Cole" + } + ], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": { + "Wenna Cole": 6 + }, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "moved: The First Floor room 1 -> The First Floor room 2", + "refused": false, + "offers": null, + "understood": "move -> The First Floor room 2", + "resolved_by": "grammar", + "note": null, + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 0.0, + "shown": null + } + } + ] +} diff --git a/parity/goldens/waiting-out-a-floor.json b/parity/goldens/waiting-out-a-floor.json new file mode 100644 index 0000000..1d544ef --- /dev/null +++ b/parity/goldens/waiting-out-a-floor.json @@ -0,0 +1,524 @@ +{ + "script": "waiting-out-a-floor", + "story": "A Floor That Falls", + "steps": [ + { + "step": "step 1 onto-the-first-floor", + "player": "first", + "typed": "/move The First Floor", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "stub" + }, + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": "move -> The First Floor", + "resolved_by": "grammar", + "note": "into-the-first-floor: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 1.5, + "shown": null + } + }, + { + "step": "step 2 wait-1", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "stub" + }, + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-1: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 3 wait-2", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "stub" + }, + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-2: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 4 wait-3", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "stub" + }, + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-3: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 5 wait-4", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "stub" + }, + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-4: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 6 wait-5", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000005, + "name": "The First Floor room 2", + "detail": "stub" + }, + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 18, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": false, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-5: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 7 wait-6", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 0, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": true, + "changed": true, + "change": "The browser turn completed.", + "refused": false, + "offers": null, + "understood": null, + "resolved_by": "grammar", + "note": "wait-6: completed", + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 5.0, + "shown": null + } + }, + { + "step": "step 8 nothing-after", + "player": "first", + "typed": "/wait", + "dump": { + "location": { + "id": 1000000004, + "name": "The First Floor room 1", + "detail": "realized" + }, + "storey": 0, + "exits": [ + { + "id": 1000000001, + "name": "The Landing", + "detail": "realized" + } + ], + "here": [ + { + "id": 1000000004, + "name": "pool" + }, + { + "id": 1000000003, + "name": "rock shelf" + } + ], + "carrying": [], + "present": [], + "foes": [], + "inscription": [], + "hp": 0, + "hp_of": {}, + "abilities": { + "strength": 14, + "dexterity": 12, + "will": 12 + }, + "dead": true, + "changed": false, + "change": null, + "refused": true, + "offers": "Odile is dead, and this playthrough is over. Nothing you type can change it. Start a new playthrough to play this world again.", + "understood": null, + "resolved_by": null, + "note": null, + "drifts": 0, + "blows": 0, + "hazards": 0, + "quest": {}, + "volitions": 0, + "acts": 0, + "ending": "none", + "ending_words": null, + "scheduled": 0, + "fired": 0, + "elapsed_minutes": 0.0, + "shown": null + } + } + ] +} diff --git a/parity/records/a-floor-falls-behind-you.json b/parity/records/a-floor-falls-behind-you.json new file mode 100644 index 0000000..560e8a6 --- /dev/null +++ b/parity/records/a-floor-falls-behind-you.json @@ -0,0 +1,18007 @@ +{ + "script": "a-floor-falls-behind-you", + "steps": [ + { + "step": "step 1 onto-the-first-floor", + "requests": [ + { + "purpose": "location", + "system": "You build the rooms of a text adventure one at a time. You write places a\nplayer can stand in and walk out of: concrete, specific, and consistent\nwith the world they belong to. Every exit you name is somewhere the player\ncould actually go.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\ngeographies: A dungeon cut down into black stone under a lantern-lit landing.\nraces: Delvers\ncivilizations: Delvers who sell what they bring up.\n\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\npreface: The floors of this dungeon do not stay up for long.\nsummary: Odile goes down before the first floor gives way, or does not.\n\n\n## The Place\nname: The First Floor\nteaser: The first level of the dungeon, cracked through, with a stair somewhere that goes further down.\n\n## Instructions\nWrite this place out in full. It is a BUILDING -- somewhere with rooms\ninside it that a player walks into and moves around in.\n- The description is what the player reads as they come in. Address them as \"you\"\n- Describe what is here now, not the history -- the history is the lore\n- Stay consistent with the universe and with the teaser above\n- Do NOT describe the floor plan: how many rooms there are, where the stairs\n are and which door leads where are the game's to decide, out of the answers\n below, and it will tell you room by room as the player reaches them\n- Do not name anybody standing here and do not list anything lying here.\n People and things belong to the rooms, and each room is written as it is\n reached\n- Respect the stated length of each field\n\n## What Kind Of Building This Is\nThe game lays the inside out itself -- every room, every door, every stair\n-- from the answers to these, and then writes each room as the player\nreaches it. You are choosing what KIND of place this is, not drawing it.\n- Answer for the place you have just described and for the story it stands in\n- Every one of them can be left out, and the quietest answer is usually the\n right one: one floor, nothing underneath, nothing dangerous, nothing that\n hurts you\n- A HAZARD is not atmosphere. It takes hit points off everybody who walks\n through those rooms, every turn in some cases, so pick one only for a\n place that really is flooded, unlit, silent or airless\n- A GRADIENT is only worth saying when the place itself makes it true: a\n cellar that gets worse the further down you go, a tower that gets worse\n the higher you climb\n", + "schema": { + "name": "Location::PlaceSchema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player sees, hears and smells standing in this place right now. Describe THIS place only -- not what neighbours it, not what is visible out of a window or across the way, because the world around it can move. Second person. One paragraph, 4 to 6 sentences.", + "maxLength": 1200 + }, + "lore": { + "type": "string", + "description": "What this place is, who made it and what happened here. Written for the game engine rather than the player. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "place_kind": { + "type": "string", + "enum": [ + "house", + "farmhouse", + "mansion", + "apartment block", + "inn", + "shop", + "office building", + "guildhall", + "workshop", + "smithy", + "mill", + "factory", + "warehouse", + "barn", + "temple", + "library", + "school", + "hospital", + "theatre", + "bathhouse", + "barracks", + "fortress", + "jail", + "tower", + "lighthouse", + "warren", + "ruin", + "spaceship", + "space station" + ], + "description": "What sort of place with rooms this is -- a building, a ship, a station -- as the game will lay out and furnish its rooms: pick the closest. The game decides which rooms it has and what stands in each; you describe it from outside and at its way in." + }, + "parameters": { + "type": "object", + "properties": { + "storeys_above": { + "type": "string", + "enum": [ + "ground floor only", + "one storey up", + "two storeys up" + ], + "description": "How far up it goes, counting the ground floor. Most buildings are one floor." + }, + "storeys_below": { + "type": "string", + "enum": [ + "none", + "a cellar", + "two levels down", + "deep" + ], + "description": "How far down it goes below the ground floor. Most buildings have nothing under them." + }, + "danger": { + "type": "string", + "enum": [ + "safe", + "uneasy", + "dangerous" + ], + "description": "How likely a room of it is to hold something that means the player harm. Most buildings are safe." + }, + "gradient": { + "type": "string", + "enum": [ + "the same throughout", + "worse the deeper you go", + "worse the higher you climb" + ], + "description": "Whether that gets worse in one direction. Only say so when the place itself makes it true -- a cellar that gets worse the further down you go, a tower that gets worse the higher you climb." + }, + "hazard": { + "type": "string", + "enum": [ + "none", + "flooded", + "unlit", + "silent", + "airless" + ], + "description": "What standing in its rooms does to a person, if anything. NONE is the right answer for almost every building: this is not atmosphere, it is a place that takes hit points off whoever walks through it." + } + }, + "required": [], + "additionalProperties": false, + "description": "What kind of building this is. Every field is optional; leave one out and the game takes the quietest option. Answer for the place you have just described." + } + }, + "required": [ + "description", + "lore" + ], + "additionalProperties": false, + "strict": true + } + } + }, + { + "purpose": "location", + "system": "You build the rooms of a text adventure one at a time. You write places a\nplayer can stand in and walk out of: concrete, specific, and consistent\nwith the world they belong to. Every exit you name is somewhere the player\ncould actually go.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\ngeographies: A dungeon cut down into black stone under a lantern-lit landing.\nraces: Delvers\ncivilizations: Delvers who sell what they bring up.\n\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\npreface: The floors of this dungeon do not stay up for long.\nsummary: Odile goes down before the first floor gives way, or does not.\n\n\n## The Place\nname: The First Floor room 1\nteaser: A room inside The First Floor, 9 by 6 paces on storey 0.\n## Where This Room Is\nThe game's own records of this room, already decided and not yours to\nchange. Write the room around them: do not contradict a measurement, and\ndo not give it a way out this list does not have.\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The First Floor room 2; a way out to The Landing, which is outside The First Floor. Those are every way out of this room.\n## Already Here, Decided By The Game\nThe game's own records of what is in this room, already decided and not yours\nto change, and it will tell the player so. Write the room around them, and do\nnot add another piece of furniture or fixed thing a player could reach for.\nFixed in place: rock shelf, pool.\nDo not list any of them again as a thing lying here.\n## Instructions\nWrite this place out in full.\n- The description is what the player reads on arrival. Address them as \"you\"\n- Describe what is here now, not the history -- the history is the lore\n- Stay consistent with the universe and with the teaser above\n- Respect the stated length of each field\n- NAME THIS ROOM. It is one room inside The First Floor. The player reads the room and\n the place together -- \"the of The First Floor\" -- so name the ROOM only,\n and never put the place's own name into it. A short noun phrase carrying the\n article English wants on it, 2 to 4 words: \"the counting room\", \"the cold\n store\", \"the harbourmaster's office\". Name it for what the floor plan above\n says this room is and for what you have just described standing in it. Never\n a comma in it\n\n## What Is Lying Here\nList AT MOST 3 portable things a player could pick up and carry away, besides the ones Already Here above: those are written already, and a thing named again is not a new one.\n- Nothing is the right answer for most rooms. An empty list is a complete answer\n- Only loose, portable things. Not the door, not the floor, not the machinery\n bolted to it -- something a person could put in a pocket or under an arm\n- Each one must be consistent with the description you just wrote, and worth\n the player noticing\n- Pick its use_kind from the supplied physical profiles. Ordinary is the\n default. Food and drink can be consumed without healing; healing is only\n a restorative dose this world permits. Firestarters burn combustible\n objects, levers pry jammed passages, lockpicks try locks, and a key only\n opens a passage explicitly matched to it. Mark combustible only when an\n ordinary firestarter can destroy the object. The engine owns the effects\n and their amounts; its description cannot grant extra powers\n- If a thing has WRITING on it -- a note, a letter, a handbill, a label, a\n docket, a page, a sign -- mark it readable and WRITE OUT WHAT IS WRITTEN\n ON IT, exactly as it appears on the thing. The words themselves, not a\n description of them, and short enough to finish -- a few words, a line,\n or a few short lines. The game keeps those words and a player reading it\n twice reads the same ones\n- Never name it after a person or after a place. Already spoken for in this story, so do not reuse: Odile, Wenna Cole\n\n## Who Is Here\nWrite NOBODY into this place. There is nobody here, and the description\nshould read like somewhere nobody is standing.\n", + "schema": { + "name": "Location::DetailSchema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player sees, hears and smells standing in this place right now. Describe THIS place only -- not what neighbours it, not what is visible out of a window or across the way, because the world around it can move. Second person. One paragraph, 4 to 6 sentences.", + "maxLength": 1200 + }, + "lore": { + "type": "string", + "description": "What this place is, who made it and what happened here. Written for the game engine rather than the player. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "name": { + "type": "string", + "description": "What this room is called -- ONLY when the instructions above ask you to name it. Leave this out entirely otherwise. A short noun phrase a player would type to walk into it, carrying the article English wants on it: \"the counting room\". Never the name of the building it is in, never a name this story has already given to a room, a person or a thing, and never a comma.", + "maxLength": 60 + }, + "items": { + "type": "array", + "description": "Portable things lying loose in this place that a player could pick up and carry away. Empty is the right answer for most rooms.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "What the thing is called, as a player would type it to pick it up. A short noun phrase, 1 to 4 words, lower case unless it is a proper name. Never the name of a person or of a place.", + "maxLength": 60 + }, + "description": { + "type": "string", + "description": "What it is and what state it is in, consistent with the description of the room you just wrote. One or two sentences.", + "maxLength": 400 + }, + "use_kind": { + "type": "string", + "enum": [ + "ordinary", + "food", + "drink", + "healing", + "firestarter", + "lever", + "lockpick", + "key" + ], + "description": "The engine's physical profile: ordinary unless it is food, a drink, a healing dose, a firestarter, a prying lever, lockpicks, or a key. Choose healing only for an actual restorative dose allowed by this world. Profiles supply behavior from code, never from the description." + }, + "combustible": { + "type": "boolean", + "description": "True only if this portable thing can be destroyed by an ordinary carried firestarter: dry paper, cloth or wood. False for stone, metal, liquids, or anything uncertain." + }, + "readable": { + "type": "boolean", + "description": "True only if this thing has WRITING on it that a player could read: a note, a letter, a label, a docket, a page, a sign, an inscription. False for everything else, which is most things." + }, + "inscription": { + "type": "string", + "description": "The words written on it, exactly as they appear, and only when `readable` is true. Write what is actually on the thing -- what a player would read off it -- not a description of it. A few words, a line, or a few short lines; well under the limit, and finished rather than trailing off. Leave this out entirely when nothing is written on it.", + "maxLength": 400 + } + }, + "required": [ + "name", + "description", + "use_kind", + "combustible", + "readable" + ], + "additionalProperties": false + }, + "maxItems": 3 + }, + "people": { + "type": "array", + "description": "People who are in this place right now.", + "items": { + "type": "object", + "properties": { + "fullname": { + "type": "string", + "description": "Their full name, as a player would type it to speak to them. 2 or 3 words.", + "maxLength": 60 + }, + "nickname": { + "type": "string", + "description": "What they are called to their face. 1 or 2 words.", + "maxLength": 30 + }, + "appearance": { + "type": "string", + "description": "What somebody walking in sees of them, consistent with the room you just wrote and with the race and age you were given for them. One or two sentences.", + "maxLength": 300 + }, + "personality": { + "type": "string", + "description": "How they behave and how they treat a stranger. One or two sentences.", + "maxLength": 300 + }, + "backstory": { + "type": "string", + "description": "The life history that brought them here, the repeated choices it taught them, and why today's presence presses on an enduring aim. Third person, by name. Two or three sentences.", + "maxLength": 450 + }, + "likes": { + "type": "string", + "description": "A comma separated list of 2 or 3 things they enjoy.", + "maxLength": 160 + }, + "dislikes": { + "type": "string", + "description": "A comma separated list of 2 or 3 things they cannot stand.", + "maxLength": 160 + }, + "fears": { + "type": "string", + "description": "A comma separated list of 1 or 2 things they are afraid of.", + "maxLength": 160 + }, + "conscious_desire": { + "type": "string", + "description": "The future, standing, relationship, legacy, place or way of life they knowingly organize their choices around and would name if asked what they want from their life. Make it specific to this person and world. Do not substitute the clue, deadline, delivery, payment or room they are dealing with today. One sentence, third person, by name.", + "maxLength": 180 + }, + "unconscious_desire": { + "type": "string", + "description": "The deeper reward their choices have pursued for years and they would deny: the recognition, absolution, dependence, belonging, power or intimacy beneath the conscious account. It explains a repeated pattern in the backstory and pulls at an angle to the conscious desire, not merely today's hidden motive. One sentence, third person, by name.", + "maxLength": 180 + }, + "recognized_need": { + "type": "string", + "description": "The duty, oath, debt, craft, family burden or bodily discipline they believe they must keep over years whether they want to or not. It predates today's assignment, survives it, and can repeatedly obstruct the conscious desire. One sentence, third person, by name.", + "maxLength": 180 + }, + "unrecognized_need": { + "type": "string", + "description": "The enduring change, truth or relationship their life requires and their repeated pattern prevents them from seeing. A reader can infer it from the backstory. It cannot be completed by one confession, realization or errand: it demands a new way of choosing across the story. If they never move toward it, pursuit of the conscious desire ruins them. It must not be the conscious desire said twice. One sentence, third person, by name.", + "maxLength": 180 + }, + "desire_pursuit": { + "type": "string", + "enum": [ + "keep", + "obtain", + "reach", + "attend", + "avoid", + "withhold", + "offer" + ], + "description": "Which listed ENGINE ACT could repeatedly advance or protect the CONSCIOUS DESIRE in ordinary rooms. The label is the next-step expression of the larger desire, not its timescale." + }, + "need_pursuit": { + "type": "string", + "enum": [ + "keep", + "obtain", + "reach", + "attend", + "avoid", + "withhold", + "offer" + ], + "description": "Which listed ENGINE ACT could repeatedly move them toward the UNRECOGNIZED NEED in ordinary rooms. It may match desire_pursuit, but a different label should reflect a real conflict rather than manufactured variety." + } + }, + "required": [ + "fullname", + "nickname", + "appearance", + "personality", + "backstory", + "likes", + "dislikes", + "fears", + "conscious_desire", + "unconscious_desire", + "recognized_need", + "unrecognized_need", + "desire_pursuit", + "need_pursuit" + ], + "additionalProperties": false + }, + "maxItems": 3 + } + }, + "required": [ + "description", + "lore" + ], + "additionalProperties": false, + "strict": true + } + } + }, + { + "purpose": "arrival", + "system": "You narrate a text adventure. You write the moment a player walks into a\nplace: what reaches them first, in the second person and the present\ntense. You never break character, never offer a numbered menu, and never\nmention that you are a narrator.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\nsummary: Odile goes down before the first floor gives way, or does not.\n\n## The Place\nname: The First Floor room 1\ndescription: A low chamber at the foot of the stair, its floor split down the middle.\nlore: Delvers do not rest here long.\nways out: The First Floor room 2, The Landing\n\n## Who Is Here\nOdile (Odile), Delvers -- the player, the one arriving\n\n## Just Before This\nThe player has come from The Landing. Odile reaches the landing.\n\n## Instructions\n- The player has never been here. This is the first time they have set\n foot in it\n- Narrate discovery: what catches them on the way in, before they\n have made sense of the rest\n- Address the player as \"you\", in the present tense\n- One paragraph. Do not re-describe the place item by item -- the\n description above is already what is here, and your job is the moment\n of coming into it\n- Anyone listed above is here; write them as already present, not as\n arriving. Do not add a person who is not on that list\n- Do not name a way out that is not on the list above\n- Respect the stated length of each field\n\n## Current State On Arrival\nThe place description is the world's original account. These current records\ntake precedence over its claims about people, items and wounds. Narrate the\nrecorded crossing result as part of this arrival.\nYou are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing.\n", + "schema": { + "name": "Scene::Schema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player experiences as they arrive here, right now. Second person, present tense. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "summary": { + "type": "string", + "description": "What happened in this moment, for the game engine rather than the player. Third person. One sentence.", + "maxLength": 200 + } + }, + "required": [ + "description", + "summary" + ], + "additionalProperties": false, + "strict": true + } + } + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 2 to-the-way-down", + "requests": [ + { + "purpose": "location", + "system": "You build the rooms of a text adventure one at a time. You write places a\nplayer can stand in and walk out of: concrete, specific, and consistent\nwith the world they belong to. Every exit you name is somewhere the player\ncould actually go.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\ngeographies: A dungeon cut down into black stone under a lantern-lit landing.\nraces: Delvers\ncivilizations: Delvers who sell what they bring up.\n\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\npreface: The floors of this dungeon do not stay up for long.\nsummary: Odile goes down before the first floor gives way, or does not.\n\n\n## The Place\nname: The First Floor room 2\nteaser: A room inside The First Floor, 3 by 6 paces on storey 0.\n## Where This Room Is\nThe game's own records of this room, already decided and not yours to\nchange. Write the room around them: do not contradict a measurement, and\ndo not give it a way out this list does not have.\nThis room is 3 by 6 paces -- about 5 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the west wall, to The First Floor room 1; a way out to The Second Floor, which is outside The First Floor. Those are every way out of this room.\n## Already Here, Decided By The Game\nThe game's own records of what is in this room, already decided and not yours\nto change, and it will tell the player so. Write the room around them, and do\nnot add another piece of furniture or fixed thing a player could reach for.\nFixed in place: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nOn the rock shelf: pebble.\nIn the rubble: rusted hinge.\nOn the ledge: old rope end.\nDo not list any of them again as a thing lying here.\n## Already Standing Here, Decided By The Game\nThe game's own records of who is in this room, already decided and not yours\nto change. Write the room with them in it, as they are, and do not write any\nof them again as a new person.\n Wenna Cole, Delvers, about 41, female: A patched cloak and a sack of candles.\n## Instructions\nWrite this place out in full.\n- The description is what the player reads on arrival. Address them as \"you\"\n- Describe what is here now, not the history -- the history is the lore\n- Stay consistent with the universe and with the teaser above\n- Respect the stated length of each field\n- NAME THIS ROOM. It is one room inside The First Floor. The player reads the room and\n the place together -- \"the of The First Floor\" -- so name the ROOM only,\n and never put the place's own name into it. A short noun phrase carrying the\n article English wants on it, 2 to 4 words: \"the counting room\", \"the cold\n store\", \"the harbourmaster's office\". Name it for what the floor plan above\n says this room is and for what you have just described standing in it. Never\n a comma in it\n\n## What Is Lying Here\nList AT MOST 3 portable things a player could pick up and carry away, besides the ones Already Here above: those are written already, and a thing named again is not a new one.\n- Nothing is the right answer for most rooms. An empty list is a complete answer\n- Only loose, portable things. Not the door, not the floor, not the machinery\n bolted to it -- something a person could put in a pocket or under an arm\n- Each one must be consistent with the description you just wrote, and worth\n the player noticing\n- Pick its use_kind from the supplied physical profiles. Ordinary is the\n default. Food and drink can be consumed without healing; healing is only\n a restorative dose this world permits. Firestarters burn combustible\n objects, levers pry jammed passages, lockpicks try locks, and a key only\n opens a passage explicitly matched to it. Mark combustible only when an\n ordinary firestarter can destroy the object. The engine owns the effects\n and their amounts; its description cannot grant extra powers\n- If a thing has WRITING on it -- a note, a letter, a handbill, a label, a\n docket, a page, a sign -- mark it readable and WRITE OUT WHAT IS WRITTEN\n ON IT, exactly as it appears on the thing. The words themselves, not a\n description of them, and short enough to finish -- a few words, a line,\n or a few short lines. The game keeps those words and a player reading it\n twice reads the same ones\n- Never name it after a person or after a place. Already spoken for in this story, so do not reuse: Odile, Wenna Cole\n\n## Who Is Here\nWrite NOBODY new into this place. The people Already Standing Here above are\neveryone who is here.\n", + "schema": { + "name": "Location::DetailSchema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player sees, hears and smells standing in this place right now. Describe THIS place only -- not what neighbours it, not what is visible out of a window or across the way, because the world around it can move. Second person. One paragraph, 4 to 6 sentences.", + "maxLength": 1200 + }, + "lore": { + "type": "string", + "description": "What this place is, who made it and what happened here. Written for the game engine rather than the player. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "name": { + "type": "string", + "description": "What this room is called -- ONLY when the instructions above ask you to name it. Leave this out entirely otherwise. A short noun phrase a player would type to walk into it, carrying the article English wants on it: \"the counting room\". Never the name of the building it is in, never a name this story has already given to a room, a person or a thing, and never a comma.", + "maxLength": 60 + }, + "items": { + "type": "array", + "description": "Portable things lying loose in this place that a player could pick up and carry away. Empty is the right answer for most rooms.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "What the thing is called, as a player would type it to pick it up. A short noun phrase, 1 to 4 words, lower case unless it is a proper name. Never the name of a person or of a place.", + "maxLength": 60 + }, + "description": { + "type": "string", + "description": "What it is and what state it is in, consistent with the description of the room you just wrote. One or two sentences.", + "maxLength": 400 + }, + "use_kind": { + "type": "string", + "enum": [ + "ordinary", + "food", + "drink", + "healing", + "firestarter", + "lever", + "lockpick", + "key" + ], + "description": "The engine's physical profile: ordinary unless it is food, a drink, a healing dose, a firestarter, a prying lever, lockpicks, or a key. Choose healing only for an actual restorative dose allowed by this world. Profiles supply behavior from code, never from the description." + }, + "combustible": { + "type": "boolean", + "description": "True only if this portable thing can be destroyed by an ordinary carried firestarter: dry paper, cloth or wood. False for stone, metal, liquids, or anything uncertain." + }, + "readable": { + "type": "boolean", + "description": "True only if this thing has WRITING on it that a player could read: a note, a letter, a label, a docket, a page, a sign, an inscription. False for everything else, which is most things." + }, + "inscription": { + "type": "string", + "description": "The words written on it, exactly as they appear, and only when `readable` is true. Write what is actually on the thing -- what a player would read off it -- not a description of it. A few words, a line, or a few short lines; well under the limit, and finished rather than trailing off. Leave this out entirely when nothing is written on it.", + "maxLength": 400 + } + }, + "required": [ + "name", + "description", + "use_kind", + "combustible", + "readable" + ], + "additionalProperties": false + }, + "maxItems": 3 + }, + "people": { + "type": "array", + "description": "People who are in this place right now.", + "items": { + "type": "object", + "properties": { + "fullname": { + "type": "string", + "description": "Their full name, as a player would type it to speak to them. 2 or 3 words.", + "maxLength": 60 + }, + "nickname": { + "type": "string", + "description": "What they are called to their face. 1 or 2 words.", + "maxLength": 30 + }, + "appearance": { + "type": "string", + "description": "What somebody walking in sees of them, consistent with the room you just wrote and with the race and age you were given for them. One or two sentences.", + "maxLength": 300 + }, + "personality": { + "type": "string", + "description": "How they behave and how they treat a stranger. One or two sentences.", + "maxLength": 300 + }, + "backstory": { + "type": "string", + "description": "The life history that brought them here, the repeated choices it taught them, and why today's presence presses on an enduring aim. Third person, by name. Two or three sentences.", + "maxLength": 450 + }, + "likes": { + "type": "string", + "description": "A comma separated list of 2 or 3 things they enjoy.", + "maxLength": 160 + }, + "dislikes": { + "type": "string", + "description": "A comma separated list of 2 or 3 things they cannot stand.", + "maxLength": 160 + }, + "fears": { + "type": "string", + "description": "A comma separated list of 1 or 2 things they are afraid of.", + "maxLength": 160 + }, + "conscious_desire": { + "type": "string", + "description": "The future, standing, relationship, legacy, place or way of life they knowingly organize their choices around and would name if asked what they want from their life. Make it specific to this person and world. Do not substitute the clue, deadline, delivery, payment or room they are dealing with today. One sentence, third person, by name.", + "maxLength": 180 + }, + "unconscious_desire": { + "type": "string", + "description": "The deeper reward their choices have pursued for years and they would deny: the recognition, absolution, dependence, belonging, power or intimacy beneath the conscious account. It explains a repeated pattern in the backstory and pulls at an angle to the conscious desire, not merely today's hidden motive. One sentence, third person, by name.", + "maxLength": 180 + }, + "recognized_need": { + "type": "string", + "description": "The duty, oath, debt, craft, family burden or bodily discipline they believe they must keep over years whether they want to or not. It predates today's assignment, survives it, and can repeatedly obstruct the conscious desire. One sentence, third person, by name.", + "maxLength": 180 + }, + "unrecognized_need": { + "type": "string", + "description": "The enduring change, truth or relationship their life requires and their repeated pattern prevents them from seeing. A reader can infer it from the backstory. It cannot be completed by one confession, realization or errand: it demands a new way of choosing across the story. If they never move toward it, pursuit of the conscious desire ruins them. It must not be the conscious desire said twice. One sentence, third person, by name.", + "maxLength": 180 + }, + "desire_pursuit": { + "type": "string", + "enum": [ + "keep", + "obtain", + "reach", + "attend", + "avoid", + "withhold", + "offer" + ], + "description": "Which listed ENGINE ACT could repeatedly advance or protect the CONSCIOUS DESIRE in ordinary rooms. The label is the next-step expression of the larger desire, not its timescale." + }, + "need_pursuit": { + "type": "string", + "enum": [ + "keep", + "obtain", + "reach", + "attend", + "avoid", + "withhold", + "offer" + ], + "description": "Which listed ENGINE ACT could repeatedly move them toward the UNRECOGNIZED NEED in ordinary rooms. It may match desire_pursuit, but a different label should reflect a real conflict rather than manufactured variety." + } + }, + "required": [ + "fullname", + "nickname", + "appearance", + "personality", + "backstory", + "likes", + "dislikes", + "fears", + "conscious_desire", + "unconscious_desire", + "recognized_need", + "unrecognized_need", + "desire_pursuit", + "need_pursuit" + ], + "additionalProperties": false + }, + "maxItems": 3 + } + }, + "required": [ + "description", + "lore" + ], + "additionalProperties": false, + "strict": true + } + } + }, + { + "purpose": "arrival", + "system": "You narrate a text adventure. You write the moment a player walks into a\nplace: what reaches them first, in the second person and the present\ntense. You never break character, never offer a numbered menu, and never\nmention that you are a narrator.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\nsummary: Odile goes down before the first floor gives way, or does not.\n\n## The Place\nname: The First Floor room 2\ndescription: A bare chamber with a stair cut down into its floor.\nlore: The stair down is older than the rest.\nways out: The First Floor room 1, The Second Floor\n\n## Who Is Here\nOdile (Odile), Delvers -- the player, the one arriving\nWenna Cole (Wenna), Delvers\n\n## Just Before This\nThe player has come from The First Floor room 1. You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\n\n## Instructions\n- The player has never been here. This is the first time they have set\n foot in it\n- Narrate discovery: what catches them on the way in, before they\n have made sense of the rest\n- Address the player as \"you\", in the present tense\n- One paragraph. Do not re-describe the place item by item -- the\n description above is already what is here, and your job is the moment\n of coming into it\n- Anyone listed above is here; write them as already present, not as\n arriving. Do not add a person who is not on that list\n- Do not name a way out that is not on the list above\n- Respect the stated length of each field\n\n## Current State On Arrival\nThe place description is the world's original account. These current records\ntake precedence over its claims about people, items and wounds. Narrate the\nrecorded crossing result as part of this arrival.\nYou are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing.\n", + "schema": { + "name": "Scene::Schema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player experiences as they arrive here, right now. Second person, present tense. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "summary": { + "type": "string", + "description": "What happened in this moment, for the game engine rather than the player. Third person. One sentence.", + "maxLength": 200 + } + }, + "required": [ + "description", + "summary" + ], + "additionalProperties": false, + "strict": true + } + } + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 3 down-onto-the-second-floor", + "requests": [ + { + "purpose": "location", + "system": "You build the rooms of a text adventure one at a time. You write places a\nplayer can stand in and walk out of: concrete, specific, and consistent\nwith the world they belong to. Every exit you name is somewhere the player\ncould actually go.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\ngeographies: A dungeon cut down into black stone under a lantern-lit landing.\nraces: Delvers\ncivilizations: Delvers who sell what they bring up.\n\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\npreface: The floors of this dungeon do not stay up for long.\nsummary: Odile goes down before the first floor gives way, or does not.\n\n\n## The Place\nname: The Second Floor\nteaser: The second level of the dungeon, where the stair ends.\n\n## Instructions\nWrite this place out in full. It is a BUILDING -- somewhere with rooms\ninside it that a player walks into and moves around in.\n- The description is what the player reads as they come in. Address them as \"you\"\n- Describe what is here now, not the history -- the history is the lore\n- Stay consistent with the universe and with the teaser above\n- Do NOT describe the floor plan: how many rooms there are, where the stairs\n are and which door leads where are the game's to decide, out of the answers\n below, and it will tell you room by room as the player reaches them\n- Do not name anybody standing here and do not list anything lying here.\n People and things belong to the rooms, and each room is written as it is\n reached\n- Respect the stated length of each field\n\n## What Kind Of Building This Is\nThe game lays the inside out itself -- every room, every door, every stair\n-- from the answers to these, and then writes each room as the player\nreaches it. You are choosing what KIND of place this is, not drawing it.\n- Answer for the place you have just described and for the story it stands in\n- Every one of them can be left out, and the quietest answer is usually the\n right one: one floor, nothing underneath, nothing dangerous, nothing that\n hurts you\n- A HAZARD is not atmosphere. It takes hit points off everybody who walks\n through those rooms, every turn in some cases, so pick one only for a\n place that really is flooded, unlit, silent or airless\n- A GRADIENT is only worth saying when the place itself makes it true: a\n cellar that gets worse the further down you go, a tower that gets worse\n the higher you climb\n", + "schema": { + "name": "Location::PlaceSchema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player sees, hears and smells standing in this place right now. Describe THIS place only -- not what neighbours it, not what is visible out of a window or across the way, because the world around it can move. Second person. One paragraph, 4 to 6 sentences.", + "maxLength": 1200 + }, + "lore": { + "type": "string", + "description": "What this place is, who made it and what happened here. Written for the game engine rather than the player. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "place_kind": { + "type": "string", + "enum": [ + "house", + "farmhouse", + "mansion", + "apartment block", + "inn", + "shop", + "office building", + "guildhall", + "workshop", + "smithy", + "mill", + "factory", + "warehouse", + "barn", + "temple", + "library", + "school", + "hospital", + "theatre", + "bathhouse", + "barracks", + "fortress", + "jail", + "tower", + "lighthouse", + "warren", + "ruin", + "spaceship", + "space station" + ], + "description": "What sort of place with rooms this is -- a building, a ship, a station -- as the game will lay out and furnish its rooms: pick the closest. The game decides which rooms it has and what stands in each; you describe it from outside and at its way in." + }, + "parameters": { + "type": "object", + "properties": { + "storeys_above": { + "type": "string", + "enum": [ + "ground floor only", + "one storey up", + "two storeys up" + ], + "description": "How far up it goes, counting the ground floor. Most buildings are one floor." + }, + "storeys_below": { + "type": "string", + "enum": [ + "none", + "a cellar", + "two levels down", + "deep" + ], + "description": "How far down it goes below the ground floor. Most buildings have nothing under them." + }, + "danger": { + "type": "string", + "enum": [ + "safe", + "uneasy", + "dangerous" + ], + "description": "How likely a room of it is to hold something that means the player harm. Most buildings are safe." + }, + "gradient": { + "type": "string", + "enum": [ + "the same throughout", + "worse the deeper you go", + "worse the higher you climb" + ], + "description": "Whether that gets worse in one direction. Only say so when the place itself makes it true -- a cellar that gets worse the further down you go, a tower that gets worse the higher you climb." + }, + "hazard": { + "type": "string", + "enum": [ + "none", + "flooded", + "unlit", + "silent", + "airless" + ], + "description": "What standing in its rooms does to a person, if anything. NONE is the right answer for almost every building: this is not atmosphere, it is a place that takes hit points off whoever walks through it." + } + }, + "required": [], + "additionalProperties": false, + "description": "What kind of building this is. Every field is optional; leave one out and the game takes the quietest option. Answer for the place you have just described." + } + }, + "required": [ + "description", + "lore" + ], + "additionalProperties": false, + "strict": true + } + } + }, + { + "purpose": "location", + "system": "You build the rooms of a text adventure one at a time. You write places a\nplayer can stand in and walk out of: concrete, specific, and consistent\nwith the world they belong to. Every exit you name is somewhere the player\ncould actually go.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\ngeographies: A dungeon cut down into black stone under a lantern-lit landing.\nraces: Delvers\ncivilizations: Delvers who sell what they bring up.\n\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\npreface: The floors of this dungeon do not stay up for long.\nsummary: Odile goes down before the first floor gives way, or does not.\n\n\n## The Place\nname: The Second Floor room 1\nteaser: A room inside The Second Floor, 9 by 6 paces on storey 0.\n## Where This Room Is\nThe game's own records of this room, already decided and not yours to\nchange. Write the room around them: do not contradict a measurement, and\ndo not give it a way out this list does not have.\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The Second Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The Second Floor room 2; a way out to The First Floor room 2, which is outside The Second Floor. Those are every way out of this room.\n## Already Here, Decided By The Game\nThe game's own records of what is in this room, already decided and not yours\nto change, and it will tell the player so. Write the room around them, and do\nnot add another piece of furniture or fixed thing a player could reach for.\nFixed in place: rock shelf, rubble, stalagmite.\nOn the rock shelf: bat skull, candle stub.\nDo not list any of them again as a thing lying here.\n## Instructions\nWrite this place out in full.\n- The description is what the player reads on arrival. Address them as \"you\"\n- Describe what is here now, not the history -- the history is the lore\n- Stay consistent with the universe and with the teaser above\n- Respect the stated length of each field\n- NAME THIS ROOM. It is one room inside The Second Floor. The player reads the room and\n the place together -- \"the of The Second Floor\" -- so name the ROOM only,\n and never put the place's own name into it. A short noun phrase carrying the\n article English wants on it, 2 to 4 words: \"the counting room\", \"the cold\n store\", \"the harbourmaster's office\". Name it for what the floor plan above\n says this room is and for what you have just described standing in it. Never\n a comma in it\n\n## What Is Lying Here\nList AT MOST 3 portable things a player could pick up and carry away, besides the ones Already Here above: those are written already, and a thing named again is not a new one.\n- Nothing is the right answer for most rooms. An empty list is a complete answer\n- Only loose, portable things. Not the door, not the floor, not the machinery\n bolted to it -- something a person could put in a pocket or under an arm\n- Each one must be consistent with the description you just wrote, and worth\n the player noticing\n- Pick its use_kind from the supplied physical profiles. Ordinary is the\n default. Food and drink can be consumed without healing; healing is only\n a restorative dose this world permits. Firestarters burn combustible\n objects, levers pry jammed passages, lockpicks try locks, and a key only\n opens a passage explicitly matched to it. Mark combustible only when an\n ordinary firestarter can destroy the object. The engine owns the effects\n and their amounts; its description cannot grant extra powers\n- If a thing has WRITING on it -- a note, a letter, a handbill, a label, a\n docket, a page, a sign -- mark it readable and WRITE OUT WHAT IS WRITTEN\n ON IT, exactly as it appears on the thing. The words themselves, not a\n description of them, and short enough to finish -- a few words, a line,\n or a few short lines. The game keeps those words and a player reading it\n twice reads the same ones\n- Never name it after a person or after a place. Already spoken for in this story, so do not reuse: Odile, Wenna Cole\n\n## Who Is Here\nWrite NOBODY into this place. There is nobody here, and the description\nshould read like somewhere nobody is standing.\n", + "schema": { + "name": "Location::DetailSchema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player sees, hears and smells standing in this place right now. Describe THIS place only -- not what neighbours it, not what is visible out of a window or across the way, because the world around it can move. Second person. One paragraph, 4 to 6 sentences.", + "maxLength": 1200 + }, + "lore": { + "type": "string", + "description": "What this place is, who made it and what happened here. Written for the game engine rather than the player. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "name": { + "type": "string", + "description": "What this room is called -- ONLY when the instructions above ask you to name it. Leave this out entirely otherwise. A short noun phrase a player would type to walk into it, carrying the article English wants on it: \"the counting room\". Never the name of the building it is in, never a name this story has already given to a room, a person or a thing, and never a comma.", + "maxLength": 60 + }, + "items": { + "type": "array", + "description": "Portable things lying loose in this place that a player could pick up and carry away. Empty is the right answer for most rooms.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "What the thing is called, as a player would type it to pick it up. A short noun phrase, 1 to 4 words, lower case unless it is a proper name. Never the name of a person or of a place.", + "maxLength": 60 + }, + "description": { + "type": "string", + "description": "What it is and what state it is in, consistent with the description of the room you just wrote. One or two sentences.", + "maxLength": 400 + }, + "use_kind": { + "type": "string", + "enum": [ + "ordinary", + "food", + "drink", + "healing", + "firestarter", + "lever", + "lockpick", + "key" + ], + "description": "The engine's physical profile: ordinary unless it is food, a drink, a healing dose, a firestarter, a prying lever, lockpicks, or a key. Choose healing only for an actual restorative dose allowed by this world. Profiles supply behavior from code, never from the description." + }, + "combustible": { + "type": "boolean", + "description": "True only if this portable thing can be destroyed by an ordinary carried firestarter: dry paper, cloth or wood. False for stone, metal, liquids, or anything uncertain." + }, + "readable": { + "type": "boolean", + "description": "True only if this thing has WRITING on it that a player could read: a note, a letter, a label, a docket, a page, a sign, an inscription. False for everything else, which is most things." + }, + "inscription": { + "type": "string", + "description": "The words written on it, exactly as they appear, and only when `readable` is true. Write what is actually on the thing -- what a player would read off it -- not a description of it. A few words, a line, or a few short lines; well under the limit, and finished rather than trailing off. Leave this out entirely when nothing is written on it.", + "maxLength": 400 + } + }, + "required": [ + "name", + "description", + "use_kind", + "combustible", + "readable" + ], + "additionalProperties": false + }, + "maxItems": 3 + }, + "people": { + "type": "array", + "description": "People who are in this place right now.", + "items": { + "type": "object", + "properties": { + "fullname": { + "type": "string", + "description": "Their full name, as a player would type it to speak to them. 2 or 3 words.", + "maxLength": 60 + }, + "nickname": { + "type": "string", + "description": "What they are called to their face. 1 or 2 words.", + "maxLength": 30 + }, + "appearance": { + "type": "string", + "description": "What somebody walking in sees of them, consistent with the room you just wrote and with the race and age you were given for them. One or two sentences.", + "maxLength": 300 + }, + "personality": { + "type": "string", + "description": "How they behave and how they treat a stranger. One or two sentences.", + "maxLength": 300 + }, + "backstory": { + "type": "string", + "description": "The life history that brought them here, the repeated choices it taught them, and why today's presence presses on an enduring aim. Third person, by name. Two or three sentences.", + "maxLength": 450 + }, + "likes": { + "type": "string", + "description": "A comma separated list of 2 or 3 things they enjoy.", + "maxLength": 160 + }, + "dislikes": { + "type": "string", + "description": "A comma separated list of 2 or 3 things they cannot stand.", + "maxLength": 160 + }, + "fears": { + "type": "string", + "description": "A comma separated list of 1 or 2 things they are afraid of.", + "maxLength": 160 + }, + "conscious_desire": { + "type": "string", + "description": "The future, standing, relationship, legacy, place or way of life they knowingly organize their choices around and would name if asked what they want from their life. Make it specific to this person and world. Do not substitute the clue, deadline, delivery, payment or room they are dealing with today. One sentence, third person, by name.", + "maxLength": 180 + }, + "unconscious_desire": { + "type": "string", + "description": "The deeper reward their choices have pursued for years and they would deny: the recognition, absolution, dependence, belonging, power or intimacy beneath the conscious account. It explains a repeated pattern in the backstory and pulls at an angle to the conscious desire, not merely today's hidden motive. One sentence, third person, by name.", + "maxLength": 180 + }, + "recognized_need": { + "type": "string", + "description": "The duty, oath, debt, craft, family burden or bodily discipline they believe they must keep over years whether they want to or not. It predates today's assignment, survives it, and can repeatedly obstruct the conscious desire. One sentence, third person, by name.", + "maxLength": 180 + }, + "unrecognized_need": { + "type": "string", + "description": "The enduring change, truth or relationship their life requires and their repeated pattern prevents them from seeing. A reader can infer it from the backstory. It cannot be completed by one confession, realization or errand: it demands a new way of choosing across the story. If they never move toward it, pursuit of the conscious desire ruins them. It must not be the conscious desire said twice. One sentence, third person, by name.", + "maxLength": 180 + }, + "desire_pursuit": { + "type": "string", + "enum": [ + "keep", + "obtain", + "reach", + "attend", + "avoid", + "withhold", + "offer" + ], + "description": "Which listed ENGINE ACT could repeatedly advance or protect the CONSCIOUS DESIRE in ordinary rooms. The label is the next-step expression of the larger desire, not its timescale." + }, + "need_pursuit": { + "type": "string", + "enum": [ + "keep", + "obtain", + "reach", + "attend", + "avoid", + "withhold", + "offer" + ], + "description": "Which listed ENGINE ACT could repeatedly move them toward the UNRECOGNIZED NEED in ordinary rooms. It may match desire_pursuit, but a different label should reflect a real conflict rather than manufactured variety." + } + }, + "required": [ + "fullname", + "nickname", + "appearance", + "personality", + "backstory", + "likes", + "dislikes", + "fears", + "conscious_desire", + "unconscious_desire", + "recognized_need", + "unrecognized_need", + "desire_pursuit", + "need_pursuit" + ], + "additionalProperties": false + }, + "maxItems": 3 + } + }, + "required": [ + "description", + "lore" + ], + "additionalProperties": false, + "strict": true + } + } + }, + { + "purpose": "arrival", + "system": "You narrate a text adventure. You write the moment a player walks into a\nplace: what reaches them first, in the second person and the present\ntense. You never break character, never offer a numbered menu, and never\nmention that you are a narrator.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\nsummary: Odile goes down before the first floor gives way, or does not.\n\n## The Place\nname: The Second Floor room 1\ndescription: The foot of the stair, cold and quiet.\nlore: The stair comes down here from the floor above.\nways out: The Second Floor room 2, The First Floor room 2\n\n## Who Is Here\nOdile (Odile), Delvers -- the player, the one arriving\n\n## Just Before This\nThe player has come from The First Floor room 2. You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.\n\n## Instructions\n- The player has never been here. This is the first time they have set\n foot in it\n- Narrate discovery: what catches them on the way in, before they\n have made sense of the rest\n- Address the player as \"you\", in the present tense\n- One paragraph. Do not re-describe the place item by item -- the\n description above is already what is here, and your job is the moment\n of coming into it\n- Anyone listed above is here; write them as already present, not as\n arriving. Do not add a person who is not on that list\n- Do not name a way out that is not on the list above\n- Respect the stated length of each field\n\n## Current State On Arrival\nThe place description is the world's original account. These current records\ntake precedence over its claims about people, items and wounds. Narrate the\nrecorded crossing result as part of this arrival.\nYou are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing.\n", + "schema": { + "name": "Scene::Schema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player experiences as they arrive here, right now. Second person, present tense. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "summary": { + "type": "string", + "description": "What happened in this moment, for the game engine rather than the player. Third person. One sentence.", + "maxLength": 200 + } + }, + "required": [ + "description", + "summary" + ], + "additionalProperties": false, + "strict": true + } + } + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:04:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 4 wait-1", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The Second Floor room 1: The foot of the stair, cold and quiet.\n\nWays out of here: The First Floor room 2, The Second Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The Second Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The Second Floor room 2; a way out to The First Floor room 2, which is outside The Second Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\n\nLying here, and takeable: bat skull, candle stub.\n\nThe player is carrying: nothing.\n\nWhat just happened: You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.\n\nEarlier, in order:\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\nYou arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:09:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 5 wait-2", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The Second Floor room 1: The foot of the stair, cold and quiet.\n\nWays out of here: The First Floor room 2, The Second Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The Second Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The Second Floor room 2; a way out to The First Floor room 2, which is outside The Second Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\n\nLying here, and takeable: bat skull, candle stub.\n\nThe player is carrying: nothing.\n\nWhat just happened: You stand at the foot of the stair and listen.\n\nEarlier, in order:\n(1 earlier turn left out)\nOdile reaches the landing.\nYou arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.\nYou arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:14:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 6 wait-3", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The Second Floor room 1: The foot of the stair, cold and quiet.\n\nWays out of here: The First Floor room 2, The Second Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The Second Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The Second Floor room 2; a way out to The First Floor room 2, which is outside The Second Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\n\nLying here, and takeable: bat skull, candle stub.\n\nThe player is carrying: nothing.\n\nWhat just happened: Grit trickles down the stair from above.\n\nEarlier, in order:\n(1 earlier turn left out)\nOdile reaches the landing.\nYou arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.\nYou arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.\nYou stand at the foot of the stair and listen.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:19:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000006, + "description": "Something above you groans.", + "story_timestamp": "2026-10-31 08:19:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 7 wait-4", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The Second Floor room 1: The foot of the stair, cold and quiet.\n\nWays out of here: The First Floor room 2, The Second Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The Second Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The Second Floor room 2; a way out to The First Floor room 2, which is outside The Second Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\n\nLying here, and takeable: bat skull, candle stub.\n\nThe player is carrying: nothing.\n\nWhat just happened: Something above you groans.\n\nEarlier, in order:\n(1 earlier turn left out)\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\nYou arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.\nYou stand at the foot of the stair and listen.\nGrit trickles down the stair from above.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:24:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000006, + "description": "Something above you groans.", + "story_timestamp": "2026-10-31 08:19:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000007, + "description": "Dust drifts down the stair.", + "story_timestamp": "2026-10-31 08:24:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 8 wait-5", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The Second Floor room 1: The foot of the stair, cold and quiet.\n\nWays out of here: The First Floor room 2, The Second Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The Second Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The Second Floor room 2; a way out to The First Floor room 2, which is outside The Second Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\n\nLying here, and takeable: bat skull, candle stub.\n\nThe player is carrying: nothing.\n\nWhat just happened: Dust drifts down the stair.\n\nEarlier, in order:\n(1 earlier turn left out)\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\nYou arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.\nYou stand at the foot of the stair and listen.\nGrit trickles down the stair from above.\nSomething above you groans.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:29:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000006, + "description": "Something above you groans.", + "story_timestamp": "2026-10-31 08:19:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000007, + "description": "Dust drifts down the stair.", + "story_timestamp": "2026-10-31 08:24:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000009, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000008, + "description": "The groaning above grows louder.", + "story_timestamp": "2026-10-31 08:29:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + }, + { + "character_id": 1000000001, + "scene_id": 1000000009 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 9 the-first-floor-falls-above-her", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The Second Floor room 1: The foot of the stair, cold and quiet.\n\nWays out of here: The First Floor room 2, The Second Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The Second Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The Second Floor room 2; a way out to The First Floor room 2, which is outside The Second Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\n\nLying here, and takeable: bat skull, candle stub.\n\nThe player is carrying: nothing.\n\nWhat just happened: The groaning above grows louder.\n\nEarlier, in order:\n(1 earlier turn left out)\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\nYou arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.\nYou stand at the foot of the stair and listen.\nGrit trickles down the stair from above.\nSomething above you groans.\nDust drifts down the stair.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:34:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000006, + "description": "Something above you groans.", + "story_timestamp": "2026-10-31 08:19:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000007, + "description": "Dust drifts down the stair.", + "story_timestamp": "2026-10-31 08:24:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000009, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000008, + "description": "The groaning above grows louder.", + "story_timestamp": "2026-10-31 08:29:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000010, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000009, + "description": "A roar comes down the stair, and then nothing.", + "story_timestamp": "2026-10-31 08:34:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + }, + { + "character_id": 1000000001, + "scene_id": 1000000009 + }, + { + "character_id": 1000000001, + "scene_id": 1000000010 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 10 there-is-no-way-back-up", + "requests": [], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:34:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000006, + "description": "Something above you groans.", + "story_timestamp": "2026-10-31 08:19:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000007, + "description": "Dust drifts down the stair.", + "story_timestamp": "2026-10-31 08:24:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000009, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000008, + "description": "The groaning above grows louder.", + "story_timestamp": "2026-10-31 08:29:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000010, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000009, + "description": "A roar comes down the stair, and then nothing.", + "story_timestamp": "2026-10-31 08:34:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + }, + { + "character_id": 1000000001, + "scene_id": 1000000009 + }, + { + "character_id": 1000000001, + "scene_id": 1000000010 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 11 nor-onto-the-floor-by-its-name", + "requests": [], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:34:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000006, + "description": "Something above you groans.", + "story_timestamp": "2026-10-31 08:19:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000007, + "description": "Dust drifts down the stair.", + "story_timestamp": "2026-10-31 08:24:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000009, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000008, + "description": "The groaning above grows louder.", + "story_timestamp": "2026-10-31 08:29:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000010, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000009, + "description": "A roar comes down the stair, and then nothing.", + "story_timestamp": "2026-10-31 08:34:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + }, + { + "character_id": 1000000001, + "scene_id": 1000000009 + }, + { + "character_id": 1000000001, + "scene_id": 1000000010 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 12 a-second-game-finds-the-first-floor-whole (second)", + "requests": [], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:34:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000031, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000032, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000006, + "description": "Something above you groans.", + "story_timestamp": "2026-10-31 08:19:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000007, + "description": "Dust drifts down the stair.", + "story_timestamp": "2026-10-31 08:24:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000009, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000008, + "description": "The groaning above grows louder.", + "story_timestamp": "2026-10-31 08:29:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000010, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000009, + "description": "A roar comes down the stair, and then nothing.", + "story_timestamp": "2026-10-31 08:34:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + }, + { + "character_id": 1000000001, + "scene_id": 1000000009 + }, + { + "character_id": 1000000001, + "scene_id": 1000000010 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 13 and-wenna-is-alive-at-its-stair (second)", + "requests": [], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000003, + "story_id": 1000000001, + "name": "The Second Floor", + "description": "A colder level, the air still and the dark close.", + "lore": "Few delvers come this far.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The second level of the dungeon, where the stair ends.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": "A bare chamber with a stair cut down into its floor.", + "lore": "The stair down is older than the rest.", + "last_protagonist_visit": "2026-10-31 08:02:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "name": "The Second Floor room 1", + "description": "The foot of the stair, cold and quiet.", + "lore": "The stair comes down here from the floor above.", + "last_protagonist_visit": "2026-10-31 08:34:00", + "parent_location_id": 1000000003, + "detail_level": "realized", + "teaser": "A room inside The Second Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "name": "The Second Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000003, + "detail_level": "stub", + "teaser": "A room inside The Second Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000011, + "location_id": 1000000006, + "connected_location_id": 1000000007, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000012, + "location_id": 1000000007, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000013, + "location_id": 1000000006, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000014, + "location_id": 1000000005, + "connected_location_id": 1000000006, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000005, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000006, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000007, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000008, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000009, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000005, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000010, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000006, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000011, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000008, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000012, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000007, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000013, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000014, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000015, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000016, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000017, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000013, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000018, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000014, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000019, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000016, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000020, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000015, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000021, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000022, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000023, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000024, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000025, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000021, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000026, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000021, + "bulk": "immovable", + "x": 5, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000027, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000022, + "bulk": "immovable", + "x": 1, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000028, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000023, + "bulk": "immovable", + "x": 0, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000029, + "character_id": null, + "name": "bat skull", + "description": "The bat skull, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000024, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/bat skull", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000030, + "character_id": null, + "name": "candle stub", + "description": "The candle stub, on the rock shelf.", + "properties": null, + "location_id": 1000000006, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000025, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000026, + "how": "on", + "kit_key": "cave/rock shelf/candle stub", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000031, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000032, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000033, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000005, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000034, + "character_id": null, + "name": "rubble", + "description": "The rubble, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000006, + "bulk": "immovable", + "x": 9, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "hollow", + "within_id": null, + "how": null, + "kit_key": "cave/rubble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000035, + "character_id": null, + "name": "stalagmite", + "description": "The stalagmite, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000007, + "bulk": "immovable", + "x": 11, + "y": 5, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/stalagmite", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000036, + "character_id": null, + "name": "ledge", + "description": "The ledge, fixed in place.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000008, + "bulk": "immovable", + "x": 11, + "y": 2, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/ledge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000037, + "character_id": null, + "name": "pebble", + "description": "The pebble, on the rock shelf.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000009, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000033, + "how": "on", + "kit_key": "cave/rock shelf/pebble", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000038, + "character_id": null, + "name": "rusted hinge", + "description": "The rusted hinge, in the rubble.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000010, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000034, + "how": "in", + "kit_key": "cave/rubble/rusted hinge", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "metal", + "wear": null, + "worn": 0 + }, + { + "id": 1000000039, + "character_id": null, + "name": "old rope end", + "description": "The old rope end, on the ledge.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000011, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 1, + "disposition": "intact", + "fragility": "sturdy", + "tier": "portable", + "holds": null, + "within_id": 1000000036, + "how": "on", + "kit_key": "cave/ledge/old rope end", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "rope", + "wear": null, + "worn": 0 + }, + { + "id": 1000000040, + "character_id": null, + "name": "stalagmite tip", + "description": "The stalagmite tip, attached to the stalagmite.", + "properties": null, + "location_id": 1000000005, + "playthrough_id": 1000000002, + "readable": 0, + "inscription": null, + "template_id": 1000000012, + "bulk": "handy", + "x": null, + "y": null, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "brittle", + "tier": "part", + "holds": null, + "within_id": 1000000035, + "how": "attached", + "kit_key": "cave/stalagmite/attached/stalagmite tip", + "noticed_at": null, + "searched_at": null, + "hold": "stout", + "material": "stone", + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000005, + "previous_scene_id": 1000000002, + "description": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:02:30", + "summary": "You arrive at The First Floor room 2. You are unhurt. Also here: Wenna Cole. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite, ledge. Attached to the stalagmite, and could be broken off: stalagmite tip. Lying here: pebble, rusted hinge, old rope end. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor room 2", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000005, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nAlso here: Wenna Cole. Nobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite, ledge.\nAttached to the stalagmite, and could be broken off: stalagmite tip.\nLying here: pebble, rusted hinge, old rope end.\nYou are carrying: nothing." + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000003, + "description": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:04:00", + "summary": "You arrive at The Second Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, rubble, stalagmite. Lying here: bat skull, candle stub. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The Second Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000003, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, rubble, stalagmite.\nLying here: bat skull, candle stub.\nYou are carrying: nothing." + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000004, + "description": "You stand at the foot of the stair and listen.", + "story_timestamp": "2026-10-31 08:09:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000005, + "description": "Grit trickles down the stair from above.", + "story_timestamp": "2026-10-31 08:14:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000006, + "description": "Something above you groans.", + "story_timestamp": "2026-10-31 08:19:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000007, + "description": "Dust drifts down the stair.", + "story_timestamp": "2026-10-31 08:24:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000009, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000008, + "description": "The groaning above grows louder.", + "story_timestamp": "2026-10-31 08:29:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000010, + "story_id": 1000000001, + "location_id": 1000000006, + "previous_scene_id": 1000000009, + "description": "A roar comes down the stair, and then nothing.", + "story_timestamp": "2026-10-31 08:34:00", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000002, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + }, + { + "character_id": 1000000001, + "scene_id": 1000000009 + }, + { + "character_id": 1000000001, + "scene_id": 1000000010 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + } + ] +} diff --git a/parity/records/waiting-out-a-floor.json b/parity/records/waiting-out-a-floor.json new file mode 100644 index 0000000..19e53c7 --- /dev/null +++ b/parity/records/waiting-out-a-floor.json @@ -0,0 +1,4065 @@ +{ + "script": "waiting-out-a-floor", + "steps": [ + { + "step": "step 1 onto-the-first-floor", + "requests": [ + { + "purpose": "location", + "system": "You build the rooms of a text adventure one at a time. You write places a\nplayer can stand in and walk out of: concrete, specific, and consistent\nwith the world they belong to. Every exit you name is somewhere the player\ncould actually go.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\ngeographies: A dungeon cut down into black stone under a lantern-lit landing.\nraces: Delvers\ncivilizations: Delvers who sell what they bring up.\n\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\npreface: The floors of this dungeon do not stay up for long.\nsummary: Odile goes down before the first floor gives way, or does not.\n\n\n## The Place\nname: The First Floor\nteaser: The first level of the dungeon, cracked through, with a stair somewhere that goes further down.\n\n## Instructions\nWrite this place out in full. It is a BUILDING -- somewhere with rooms\ninside it that a player walks into and moves around in.\n- The description is what the player reads as they come in. Address them as \"you\"\n- Describe what is here now, not the history -- the history is the lore\n- Stay consistent with the universe and with the teaser above\n- Do NOT describe the floor plan: how many rooms there are, where the stairs\n are and which door leads where are the game's to decide, out of the answers\n below, and it will tell you room by room as the player reaches them\n- Do not name anybody standing here and do not list anything lying here.\n People and things belong to the rooms, and each room is written as it is\n reached\n- Respect the stated length of each field\n\n## What Kind Of Building This Is\nThe game lays the inside out itself -- every room, every door, every stair\n-- from the answers to these, and then writes each room as the player\nreaches it. You are choosing what KIND of place this is, not drawing it.\n- Answer for the place you have just described and for the story it stands in\n- Every one of them can be left out, and the quietest answer is usually the\n right one: one floor, nothing underneath, nothing dangerous, nothing that\n hurts you\n- A HAZARD is not atmosphere. It takes hit points off everybody who walks\n through those rooms, every turn in some cases, so pick one only for a\n place that really is flooded, unlit, silent or airless\n- A GRADIENT is only worth saying when the place itself makes it true: a\n cellar that gets worse the further down you go, a tower that gets worse\n the higher you climb\n", + "schema": { + "name": "Location::PlaceSchema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player sees, hears and smells standing in this place right now. Describe THIS place only -- not what neighbours it, not what is visible out of a window or across the way, because the world around it can move. Second person. One paragraph, 4 to 6 sentences.", + "maxLength": 1200 + }, + "lore": { + "type": "string", + "description": "What this place is, who made it and what happened here. Written for the game engine rather than the player. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "place_kind": { + "type": "string", + "enum": [ + "house", + "farmhouse", + "mansion", + "apartment block", + "inn", + "shop", + "office building", + "guildhall", + "workshop", + "smithy", + "mill", + "factory", + "warehouse", + "barn", + "temple", + "library", + "school", + "hospital", + "theatre", + "bathhouse", + "barracks", + "fortress", + "jail", + "tower", + "lighthouse", + "warren", + "ruin", + "spaceship", + "space station" + ], + "description": "What sort of place with rooms this is -- a building, a ship, a station -- as the game will lay out and furnish its rooms: pick the closest. The game decides which rooms it has and what stands in each; you describe it from outside and at its way in." + }, + "parameters": { + "type": "object", + "properties": { + "storeys_above": { + "type": "string", + "enum": [ + "ground floor only", + "one storey up", + "two storeys up" + ], + "description": "How far up it goes, counting the ground floor. Most buildings are one floor." + }, + "storeys_below": { + "type": "string", + "enum": [ + "none", + "a cellar", + "two levels down", + "deep" + ], + "description": "How far down it goes below the ground floor. Most buildings have nothing under them." + }, + "danger": { + "type": "string", + "enum": [ + "safe", + "uneasy", + "dangerous" + ], + "description": "How likely a room of it is to hold something that means the player harm. Most buildings are safe." + }, + "gradient": { + "type": "string", + "enum": [ + "the same throughout", + "worse the deeper you go", + "worse the higher you climb" + ], + "description": "Whether that gets worse in one direction. Only say so when the place itself makes it true -- a cellar that gets worse the further down you go, a tower that gets worse the higher you climb." + }, + "hazard": { + "type": "string", + "enum": [ + "none", + "flooded", + "unlit", + "silent", + "airless" + ], + "description": "What standing in its rooms does to a person, if anything. NONE is the right answer for almost every building: this is not atmosphere, it is a place that takes hit points off whoever walks through it." + } + }, + "required": [], + "additionalProperties": false, + "description": "What kind of building this is. Every field is optional; leave one out and the game takes the quietest option. Answer for the place you have just described." + } + }, + "required": [ + "description", + "lore" + ], + "additionalProperties": false, + "strict": true + } + } + }, + { + "purpose": "location", + "system": "You build the rooms of a text adventure one at a time. You write places a\nplayer can stand in and walk out of: concrete, specific, and consistent\nwith the world they belong to. Every exit you name is somewhere the player\ncould actually go.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\ngeographies: A dungeon cut down into black stone under a lantern-lit landing.\nraces: Delvers\ncivilizations: Delvers who sell what they bring up.\n\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\npreface: The floors of this dungeon do not stay up for long.\nsummary: Odile goes down before the first floor gives way, or does not.\n\n\n## The Place\nname: The First Floor room 1\nteaser: A room inside The First Floor, 9 by 6 paces on storey 0.\n## Where This Room Is\nThe game's own records of this room, already decided and not yours to\nchange. Write the room around them: do not contradict a measurement, and\ndo not give it a way out this list does not have.\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The First Floor room 2; a way out to The Landing, which is outside The First Floor. Those are every way out of this room.\n## Already Here, Decided By The Game\nThe game's own records of what is in this room, already decided and not yours\nto change, and it will tell the player so. Write the room around them, and do\nnot add another piece of furniture or fixed thing a player could reach for.\nFixed in place: rock shelf, pool.\nDo not list any of them again as a thing lying here.\n## Instructions\nWrite this place out in full.\n- The description is what the player reads on arrival. Address them as \"you\"\n- Describe what is here now, not the history -- the history is the lore\n- Stay consistent with the universe and with the teaser above\n- Respect the stated length of each field\n- NAME THIS ROOM. It is one room inside The First Floor. The player reads the room and\n the place together -- \"the of The First Floor\" -- so name the ROOM only,\n and never put the place's own name into it. A short noun phrase carrying the\n article English wants on it, 2 to 4 words: \"the counting room\", \"the cold\n store\", \"the harbourmaster's office\". Name it for what the floor plan above\n says this room is and for what you have just described standing in it. Never\n a comma in it\n\n## What Is Lying Here\nList AT MOST 3 portable things a player could pick up and carry away, besides the ones Already Here above: those are written already, and a thing named again is not a new one.\n- Nothing is the right answer for most rooms. An empty list is a complete answer\n- Only loose, portable things. Not the door, not the floor, not the machinery\n bolted to it -- something a person could put in a pocket or under an arm\n- Each one must be consistent with the description you just wrote, and worth\n the player noticing\n- Pick its use_kind from the supplied physical profiles. Ordinary is the\n default. Food and drink can be consumed without healing; healing is only\n a restorative dose this world permits. Firestarters burn combustible\n objects, levers pry jammed passages, lockpicks try locks, and a key only\n opens a passage explicitly matched to it. Mark combustible only when an\n ordinary firestarter can destroy the object. The engine owns the effects\n and their amounts; its description cannot grant extra powers\n- If a thing has WRITING on it -- a note, a letter, a handbill, a label, a\n docket, a page, a sign -- mark it readable and WRITE OUT WHAT IS WRITTEN\n ON IT, exactly as it appears on the thing. The words themselves, not a\n description of them, and short enough to finish -- a few words, a line,\n or a few short lines. The game keeps those words and a player reading it\n twice reads the same ones\n- Never name it after a person or after a place. Already spoken for in this story, so do not reuse: Odile, Wenna Cole\n\n## Who Is Here\nWrite NOBODY into this place. There is nobody here, and the description\nshould read like somewhere nobody is standing.\n", + "schema": { + "name": "Location::DetailSchema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player sees, hears and smells standing in this place right now. Describe THIS place only -- not what neighbours it, not what is visible out of a window or across the way, because the world around it can move. Second person. One paragraph, 4 to 6 sentences.", + "maxLength": 1200 + }, + "lore": { + "type": "string", + "description": "What this place is, who made it and what happened here. Written for the game engine rather than the player. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "name": { + "type": "string", + "description": "What this room is called -- ONLY when the instructions above ask you to name it. Leave this out entirely otherwise. A short noun phrase a player would type to walk into it, carrying the article English wants on it: \"the counting room\". Never the name of the building it is in, never a name this story has already given to a room, a person or a thing, and never a comma.", + "maxLength": 60 + }, + "items": { + "type": "array", + "description": "Portable things lying loose in this place that a player could pick up and carry away. Empty is the right answer for most rooms.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "What the thing is called, as a player would type it to pick it up. A short noun phrase, 1 to 4 words, lower case unless it is a proper name. Never the name of a person or of a place.", + "maxLength": 60 + }, + "description": { + "type": "string", + "description": "What it is and what state it is in, consistent with the description of the room you just wrote. One or two sentences.", + "maxLength": 400 + }, + "use_kind": { + "type": "string", + "enum": [ + "ordinary", + "food", + "drink", + "healing", + "firestarter", + "lever", + "lockpick", + "key" + ], + "description": "The engine's physical profile: ordinary unless it is food, a drink, a healing dose, a firestarter, a prying lever, lockpicks, or a key. Choose healing only for an actual restorative dose allowed by this world. Profiles supply behavior from code, never from the description." + }, + "combustible": { + "type": "boolean", + "description": "True only if this portable thing can be destroyed by an ordinary carried firestarter: dry paper, cloth or wood. False for stone, metal, liquids, or anything uncertain." + }, + "readable": { + "type": "boolean", + "description": "True only if this thing has WRITING on it that a player could read: a note, a letter, a label, a docket, a page, a sign, an inscription. False for everything else, which is most things." + }, + "inscription": { + "type": "string", + "description": "The words written on it, exactly as they appear, and only when `readable` is true. Write what is actually on the thing -- what a player would read off it -- not a description of it. A few words, a line, or a few short lines; well under the limit, and finished rather than trailing off. Leave this out entirely when nothing is written on it.", + "maxLength": 400 + } + }, + "required": [ + "name", + "description", + "use_kind", + "combustible", + "readable" + ], + "additionalProperties": false + }, + "maxItems": 3 + }, + "people": { + "type": "array", + "description": "People who are in this place right now.", + "items": { + "type": "object", + "properties": { + "fullname": { + "type": "string", + "description": "Their full name, as a player would type it to speak to them. 2 or 3 words.", + "maxLength": 60 + }, + "nickname": { + "type": "string", + "description": "What they are called to their face. 1 or 2 words.", + "maxLength": 30 + }, + "appearance": { + "type": "string", + "description": "What somebody walking in sees of them, consistent with the room you just wrote and with the race and age you were given for them. One or two sentences.", + "maxLength": 300 + }, + "personality": { + "type": "string", + "description": "How they behave and how they treat a stranger. One or two sentences.", + "maxLength": 300 + }, + "backstory": { + "type": "string", + "description": "The life history that brought them here, the repeated choices it taught them, and why today's presence presses on an enduring aim. Third person, by name. Two or three sentences.", + "maxLength": 450 + }, + "likes": { + "type": "string", + "description": "A comma separated list of 2 or 3 things they enjoy.", + "maxLength": 160 + }, + "dislikes": { + "type": "string", + "description": "A comma separated list of 2 or 3 things they cannot stand.", + "maxLength": 160 + }, + "fears": { + "type": "string", + "description": "A comma separated list of 1 or 2 things they are afraid of.", + "maxLength": 160 + }, + "conscious_desire": { + "type": "string", + "description": "The future, standing, relationship, legacy, place or way of life they knowingly organize their choices around and would name if asked what they want from their life. Make it specific to this person and world. Do not substitute the clue, deadline, delivery, payment or room they are dealing with today. One sentence, third person, by name.", + "maxLength": 180 + }, + "unconscious_desire": { + "type": "string", + "description": "The deeper reward their choices have pursued for years and they would deny: the recognition, absolution, dependence, belonging, power or intimacy beneath the conscious account. It explains a repeated pattern in the backstory and pulls at an angle to the conscious desire, not merely today's hidden motive. One sentence, third person, by name.", + "maxLength": 180 + }, + "recognized_need": { + "type": "string", + "description": "The duty, oath, debt, craft, family burden or bodily discipline they believe they must keep over years whether they want to or not. It predates today's assignment, survives it, and can repeatedly obstruct the conscious desire. One sentence, third person, by name.", + "maxLength": 180 + }, + "unrecognized_need": { + "type": "string", + "description": "The enduring change, truth or relationship their life requires and their repeated pattern prevents them from seeing. A reader can infer it from the backstory. It cannot be completed by one confession, realization or errand: it demands a new way of choosing across the story. If they never move toward it, pursuit of the conscious desire ruins them. It must not be the conscious desire said twice. One sentence, third person, by name.", + "maxLength": 180 + }, + "desire_pursuit": { + "type": "string", + "enum": [ + "keep", + "obtain", + "reach", + "attend", + "avoid", + "withhold", + "offer" + ], + "description": "Which listed ENGINE ACT could repeatedly advance or protect the CONSCIOUS DESIRE in ordinary rooms. The label is the next-step expression of the larger desire, not its timescale." + }, + "need_pursuit": { + "type": "string", + "enum": [ + "keep", + "obtain", + "reach", + "attend", + "avoid", + "withhold", + "offer" + ], + "description": "Which listed ENGINE ACT could repeatedly move them toward the UNRECOGNIZED NEED in ordinary rooms. It may match desire_pursuit, but a different label should reflect a real conflict rather than manufactured variety." + } + }, + "required": [ + "fullname", + "nickname", + "appearance", + "personality", + "backstory", + "likes", + "dislikes", + "fears", + "conscious_desire", + "unconscious_desire", + "recognized_need", + "unrecognized_need", + "desire_pursuit", + "need_pursuit" + ], + "additionalProperties": false + }, + "maxItems": 3 + } + }, + "required": [ + "description", + "lore" + ], + "additionalProperties": false, + "strict": true + } + } + }, + { + "purpose": "arrival", + "system": "You narrate a text adventure. You write the moment a player walks into a\nplace: what reaches them first, in the second person and the present\ntense. You never break character, never offer a numbered menu, and never\nmention that you are a narrator.\n\nDO NOT INCLUDE EMOJIS IN YOUR RESPONSE.\n", + "user": "## Universe Details\nphysics: Ordinary physics.\ntechnology: Lanterns, iron keys and hand tools.\n\n## Story Details\ntitle: A Floor That Falls (engine sweep)\ngenre: dungeon crawl\nsummary: Odile goes down before the first floor gives way, or does not.\n\n## The Place\nname: The First Floor room 1\ndescription: A low chamber at the foot of the stair, its floor split down the middle.\nlore: Delvers do not rest here long.\nways out: The First Floor room 2, The Landing\n\n## Who Is Here\nOdile (Odile), Delvers -- the player, the one arriving\n\n## Just Before This\nThe player has come from The Landing. Odile reaches the landing.\n\n## Instructions\n- The player has never been here. This is the first time they have set\n foot in it\n- Narrate discovery: what catches them on the way in, before they\n have made sense of the rest\n- Address the player as \"you\", in the present tense\n- One paragraph. Do not re-describe the place item by item -- the\n description above is already what is here, and your job is the moment\n of coming into it\n- Anyone listed above is here; write them as already present, not as\n arriving. Do not add a person who is not on that list\n- Do not name a way out that is not on the list above\n- Respect the stated length of each field\n\n## Current State On Arrival\nThe place description is the world's original account. These current records\ntake precedence over its claims about people, items and wounds. Narrate the\nrecorded crossing result as part of this arrival.\nYou are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing.\n", + "schema": { + "name": "Scene::Schema", + "description": null, + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "What the player experiences as they arrive here, right now. Second person, present tense. One paragraph, 3 to 5 sentences.", + "maxLength": 900 + }, + "summary": { + "type": "string", + "description": "What happened in this moment, for the game engine rather than the player. Third person. One sentence.", + "maxLength": 200 + } + }, + "required": [ + "description", + "summary" + ], + "additionalProperties": false, + "strict": true + } + } + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:01:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 2 wait-1", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The First Floor room 1: A low chamber at the foot of the stair, its floor split down the middle.\n\nWays out of here: The Landing, The First Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The First Floor room 2; a way out to The Landing, which is outside The First Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, pool.\n\nLying here, and takeable: nothing.\n\nThe player is carrying: nothing.\n\nWhat just happened: You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\n\nEarlier, in order:\nOdile reaches the landing.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:06:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000002, + "description": "You stand still and listen to the stone settle.", + "story_timestamp": "2026-10-31 08:06:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 3 wait-2", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The First Floor room 1: A low chamber at the foot of the stair, its floor split down the middle.\n\nWays out of here: The Landing, The First Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The First Floor room 2; a way out to The Landing, which is outside The First Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, pool.\n\nLying here, and takeable: nothing.\n\nThe player is carrying: nothing.\n\nWhat just happened: You stand still and listen to the stone settle.\n\nEarlier, in order:\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:11:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000002, + "description": "You stand still and listen to the stone settle.", + "story_timestamp": "2026-10-31 08:06:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000003, + "description": "Grit trickles from a crack overhead.", + "story_timestamp": "2026-10-31 08:11:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 4 wait-3", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The First Floor room 1: A low chamber at the foot of the stair, its floor split down the middle.\n\nWays out of here: The Landing, The First Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The First Floor room 2; a way out to The Landing, which is outside The First Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, pool.\n\nLying here, and takeable: nothing.\n\nThe player is carrying: nothing.\n\nWhat just happened: Grit trickles from a crack overhead.\n\nEarlier, in order:\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\nYou stand still and listen to the stone settle.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:16:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000002, + "description": "You stand still and listen to the stone settle.", + "story_timestamp": "2026-10-31 08:06:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000003, + "description": "Grit trickles from a crack overhead.", + "story_timestamp": "2026-10-31 08:11:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000004, + "description": "The crack in the floor is wider than it was.", + "story_timestamp": "2026-10-31 08:16:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 5 wait-4", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The First Floor room 1: A low chamber at the foot of the stair, its floor split down the middle.\n\nWays out of here: The Landing, The First Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The First Floor room 2; a way out to The Landing, which is outside The First Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, pool.\n\nLying here, and takeable: nothing.\n\nThe player is carrying: nothing.\n\nWhat just happened: The crack in the floor is wider than it was.\n\nEarlier, in order:\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\nYou stand still and listen to the stone settle.\nGrit trickles from a crack overhead.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:21:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000002, + "description": "You stand still and listen to the stone settle.", + "story_timestamp": "2026-10-31 08:06:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000003, + "description": "Grit trickles from a crack overhead.", + "story_timestamp": "2026-10-31 08:11:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000004, + "description": "The crack in the floor is wider than it was.", + "story_timestamp": "2026-10-31 08:16:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000005, + "description": "Somewhere further in, something heavy drops.", + "story_timestamp": "2026-10-31 08:21:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 6 wait-5", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The First Floor room 1: A low chamber at the foot of the stair, its floor split down the middle.\n\nWays out of here: The Landing, The First Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The First Floor room 2; a way out to The Landing, which is outside The First Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, pool.\n\nLying here, and takeable: nothing.\n\nThe player is carrying: nothing.\n\nWhat just happened: Somewhere further in, something heavy drops.\n\nEarlier, in order:\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\nYou stand still and listen to the stone settle.\nGrit trickles from a crack overhead.\nThe crack in the floor is wider than it was.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:26:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000002, + "description": "You stand still and listen to the stone settle.", + "story_timestamp": "2026-10-31 08:06:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000003, + "description": "Grit trickles from a crack overhead.", + "story_timestamp": "2026-10-31 08:11:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000004, + "description": "The crack in the floor is wider than it was.", + "story_timestamp": "2026-10-31 08:16:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000005, + "description": "Somewhere further in, something heavy drops.", + "story_timestamp": "2026-10-31 08:21:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000006, + "description": "The floor shudders under your boots.", + "story_timestamp": "2026-10-31 08:26:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 7 wait-6", + "requests": [ + { + "purpose": "narration", + "system": "You are the narrator of a text adventure. Write in the second person,\npresent tense, addressing the player as \"you\". Describe what happens in\nresponse to what they did, in one or two short paragraphs of prose.\n\nNever break character, never offer the player a numbered menu, and never\nmention that you are an AI or a narrator. The ways out of here, the people\npresent and what the player carries are listed in the prompt: do not add a\nway out, a person or a possession that is not on those lists. If the\nplayer tries something impossible, narrate the failure rather than\nrefusing.\n", + "user": "Story: A Floor That Falls (engine sweep) (dungeon crawl)\n\nPremise: Odile goes down before the first floor gives way, or does not.\n\nThe player is in The First Floor room 1: A low chamber at the foot of the stair, its floor split down the middle.\n\nWays out of here: The Landing, The First Floor room 2. There are no others.\n\nThis room is 9 by 6 paces -- about 14 by 9 metres. It is on storey 0 of The First Floor, which is 12 by 6 paces across; storey 0 is the ground floor. Its ways out: a door in the east wall, to The First Floor room 2; a way out to The Landing, which is outside The First Floor. Those are every way out of this room.\n\nThe player is Odile.\n\nOdile is unhurt.\n\nNobody else is here.\n\nFixed here, and not takeable: rock shelf, pool.\n\nLying here, and takeable: nothing.\n\nThe player is carrying: nothing.\n\nWhat just happened: The floor shudders under your boots.\n\nEarlier, in order:\nOdile reaches the landing.\nYou arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.\nYou stand still and listen to the stone settle.\nGrit trickles from a crack overhead.\nThe crack in the floor is wider than it was.\nSomewhere further in, something heavy drops.\n\n\nThe player types: wait\n\nNarrate what happens.\n", + "schema": null + } + ], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:31:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000002, + "description": "You stand still and listen to the stone settle.", + "story_timestamp": "2026-10-31 08:06:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000003, + "description": "Grit trickles from a crack overhead.", + "story_timestamp": "2026-10-31 08:11:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000004, + "description": "The crack in the floor is wider than it was.", + "story_timestamp": "2026-10-31 08:16:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000005, + "description": "Somewhere further in, something heavy drops.", + "story_timestamp": "2026-10-31 08:21:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000006, + "description": "The floor shudders under your boots.", + "story_timestamp": "2026-10-31 08:26:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000007, + "description": "The floor tilts.", + "story_timestamp": "2026-10-31 08:31:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + }, + { + "step": "step 8 nothing-after", + "requests": [], + "written": { + "locations": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "name": "The First Floor", + "description": "A level of black stone passages, cracked across every few paces.", + "lore": "The first floor was cut by people nobody remembers.", + "last_protagonist_visit": null, + "parent_location_id": null, + "detail_level": "realized", + "teaser": "The first level of the dungeon, cracked through, with a stair somewhere that goes further down.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": null, + "y": null, + "z": null, + "width": 12, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "warren", + "density": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "name": "The First Floor room 1", + "description": "A low chamber at the foot of the stair, its floor split down the middle.", + "lore": "Delvers do not rest here long.", + "last_protagonist_visit": "2026-10-31 08:31:30", + "parent_location_id": 1000000002, + "detail_level": "realized", + "teaser": "A room inside The First Floor, 9 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 0, + "y": 0, + "z": 0, + "width": 9, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "name": "The First Floor room 2", + "description": null, + "lore": null, + "last_protagonist_visit": null, + "parent_location_id": 1000000002, + "detail_level": "stub", + "teaser": "A room inside The First Floor, 3 by 6 paces on storey 0.", + "mobile": 0, + "danger": "safe", + "hazard": null, + "hazard_die": null, + "x": 9, + "y": 0, + "z": 0, + "width": 3, + "depth": 6, + "population": null, + "generation_checkpoint": null, + "surface": null, + "kind": "cave", + "density": null + } + ], + "removed": [] + }, + "location_connections": { + "rows": [ + { + "id": 1000000005, + "location_id": 1000000004, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000006, + "location_id": 1000000005, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "walking", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000007, + "location_id": 1000000004, + "connected_location_id": 1000000001, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000008, + "location_id": 1000000001, + "connected_location_id": 1000000004, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000009, + "location_id": 1000000005, + "connected_location_id": 1000000003, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + }, + { + "id": 1000000010, + "location_id": 1000000003, + "connected_location_id": 1000000005, + "distance": "adjacent", + "time_to_travel": "about a minute", + "travel_method": "taking stairs", + "hazard": null, + "hazard_die": null, + "barrier": "open", + "key_template_id": null + } + ], + "removed": [ + 1000000001, + 1000000002, + 1000000003, + 1000000004 + ] + }, + "characters": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "fullname": "Wenna Cole", + "nickname": "Wenna", + "age": 41, + "sex": "female", + "personality": "Cheerful and talkative.", + "appearance": "A patched cloak and a sack of candles.", + "likes": "Company.", + "dislikes": "Silence.", + "fears": "Being left in the dark.", + "backstory": "She sells candles to delvers at the head of the stair down, and has never seen a floor fall.", + "is_companion": 0, + "race_id": 1000000001, + "is_protagonist": 0, + "location_id": 1000000005, + "deliberately_absent": 0, + "level": 1, + "hit_die": 6, + "strength": 8, + "dexterity": 10, + "will": 10, + "hostile": 0, + "x": 9, + "y": 3, + "conscious_desire": null, + "unconscious_desire": null, + "recognized_need": null, + "unrecognized_need": null, + "desire_pursuit": null, + "need_pursuit": null, + "posted_to_id": 1000000002 + } + ], + "removed": [] + }, + "items": { + "rows": [ + { + "id": 1000000001, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000002, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": null, + "readable": 0, + "inscription": null, + "template_id": null, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + }, + { + "id": 1000000003, + "character_id": null, + "name": "rock shelf", + "description": "The rock shelf, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000001, + "bulk": "immovable", + "x": 2, + "y": 4, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "top", + "within_id": null, + "how": null, + "kit_key": "cave/rock shelf", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": "stone", + "wear": null, + "worn": 0 + }, + { + "id": 1000000004, + "character_id": null, + "name": "pool", + "description": "The pool, fixed in place.", + "properties": null, + "location_id": 1000000004, + "playthrough_id": 1000000001, + "readable": 0, + "inscription": null, + "template_id": 1000000002, + "bulk": "immovable", + "x": 4, + "y": 0, + "use_kind": "ordinary", + "combustible": 0, + "disposition": "intact", + "fragility": "sturdy", + "tier": "fixture", + "holds": "nothing", + "within_id": null, + "how": null, + "kit_key": "cave/pool", + "noticed_at": null, + "searched_at": null, + "hold": null, + "material": null, + "wear": null, + "worn": 0 + } + ], + "removed": [] + }, + "quest_steps": { + "rows": [], + "removed": [] + }, + "scenes": { + "rows": [ + { + "id": 1000000002, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000001, + "description": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "story_timestamp": "2026-10-31 08:01:30", + "summary": "You arrive at The First Floor room 1. You are unhurt. Nobody else is alive here. Fixed here, and not takeable: rock shelf, pool. Lying here: nothing. You are carrying: nothing.", + "is_opening": 0, + "typed": "move The First Floor", + "resolved_action": "move", + "acted_on_type": "Location", + "acted_on_id": 1000000002, + "resolved_by": "grammar", + "engine_fallback": 1, + "engine_fact": "You are unhurt.\nNobody else is alive here.\nFixed here, and not takeable: rock shelf, pool.\nLying here: nothing.\nYou are carrying: nothing." + }, + { + "id": 1000000003, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000002, + "description": "You stand still and listen to the stone settle.", + "story_timestamp": "2026-10-31 08:06:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000004, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000003, + "description": "Grit trickles from a crack overhead.", + "story_timestamp": "2026-10-31 08:11:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000005, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000004, + "description": "The crack in the floor is wider than it was.", + "story_timestamp": "2026-10-31 08:16:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000006, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000005, + "description": "Somewhere further in, something heavy drops.", + "story_timestamp": "2026-10-31 08:21:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000007, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000006, + "description": "The floor shudders under your boots.", + "story_timestamp": "2026-10-31 08:26:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + }, + { + "id": 1000000008, + "story_id": 1000000001, + "location_id": 1000000004, + "previous_scene_id": 1000000007, + "description": "The floor tilts.", + "story_timestamp": "2026-10-31 08:31:30", + "summary": null, + "is_opening": 0, + "typed": "wait", + "resolved_action": "wait", + "acted_on_type": null, + "acted_on_id": null, + "resolved_by": "grammar", + "engine_fallback": 0, + "engine_fact": null + } + ], + "removed": [] + }, + "characters_scenes": { + "rows": [ + { + "character_id": 1000000001, + "scene_id": 1000000002 + }, + { + "character_id": 1000000001, + "scene_id": 1000000003 + }, + { + "character_id": 1000000001, + "scene_id": 1000000004 + }, + { + "character_id": 1000000001, + "scene_id": 1000000005 + }, + { + "character_id": 1000000001, + "scene_id": 1000000006 + }, + { + "character_id": 1000000001, + "scene_id": 1000000007 + }, + { + "character_id": 1000000001, + "scene_id": 1000000008 + } + ], + "removed": [] + }, + "interactions": { + "rows": [], + "removed": [] + }, + "playthrough_endings": { + "rows": [], + "removed": [] + }, + "playthrough_beats": { + "rows": [], + "removed": [] + }, + "playthrough_npc_states": { + "rows": [], + "removed": [] + }, + "world_events": { + "rows": [], + "removed": [] + } + } + } + ] +} diff --git a/parity/scripts/a-floor-falls-behind-you.yml b/parity/scripts/a-floor-falls-behind-you.yml new file mode 100644 index 0000000..41a7619 --- /dev/null +++ b/parity/scripts/a-floor-falls-behind-you.yml @@ -0,0 +1,156 @@ +--- +story: A Floor That Falls +steps: +- id: onto-the-first-floor + type: "/move The First Floor" + browser: + token: into-the-first-floor + realizes: The First Floor + replies: + - purpose: location + content: + description: A level of black stone passages, cracked across every few paces. + lore: The first floor was cut by people nobody remembers. + place_kind: warren + - purpose: location + content: + description: A low chamber at the foot of the stair, its floor split down + the middle. + lore: Delvers do not rest here long. + items: [] + people: [] + - purpose: arrival + unavailable: true + expect: + location: The First Floor room 1 (realized) + dead: false +- id: to-the-way-down + type: "/move The First Floor room 2" + browser: + token: first-floor-room-2 + realizes: The First Floor room 2 + replies: + - purpose: location + content: + description: A bare chamber with a stair cut down into its floor. + lore: The stair down is older than the rest. + items: [] + people: [] + - purpose: arrival + unavailable: true + expect: + location: The First Floor room 2 (realized) + present: + - Wenna Cole + exits_include: + - The Second Floor (stub) +- id: down-onto-the-second-floor + type: "/move The Second Floor" + browser: + token: into-the-second-floor + realizes: The Second Floor + replies: + - purpose: location + content: + description: A colder level, the air still and the dark close. + lore: Few delvers come this far. + place_kind: warren + - purpose: location + content: + description: The foot of the stair, cold and quiet. + lore: The stair comes down here from the floor above. + items: [] + people: [] + - purpose: arrival + unavailable: true + expect: + location: The Second Floor room 1 (realized) + exits_include: + - The First Floor room 2 (realized) +- id: wait-1 + type: "/wait" + browser: + token: wait-1 + replies: + - purpose: narration + content: You stand at the foot of the stair and listen. + expect: + elapsed_minutes: 5.0 + exits_include: + - The First Floor room 2 (realized) +- id: wait-2 + type: "/wait" + browser: + token: wait-2 + replies: + - purpose: narration + content: Grit trickles down the stair from above. + expect: + elapsed_minutes: 5.0 +- id: wait-3 + type: "/wait" + browser: + token: wait-3 + replies: + - purpose: narration + content: Something above you groans. + expect: + elapsed_minutes: 5.0 +- id: wait-4 + type: "/wait" + browser: + token: wait-4 + replies: + - purpose: narration + content: Dust drifts down the stair. + expect: + elapsed_minutes: 5.0 +- id: wait-5 + type: "/wait" + browser: + token: wait-5 + replies: + - purpose: narration + content: The groaning above grows louder. + expect: + elapsed_minutes: 5.0 + exits_include: + - The First Floor room 2 (realized) +- id: the-first-floor-falls-above-her + type: "/wait" + browser: + token: wait-6 + replies: + - purpose: narration + content: A roar comes down the stair, and then nothing. + expect: + elapsed_minutes: 5.0 + dead: false + exits_exclude: + - The First Floor room 2 +- id: there-is-no-way-back-up + type: "/move The First Floor room 2" + expect: + refused: true + offers: The First Floor has fallen. There is no way back onto it. + location: The Second Floor room 1 (realized) +- id: nor-onto-the-floor-by-its-name + type: "/move The First Floor" + expect: + refused: true + offers: The First Floor has fallen. There is no way back onto it. +- id: a-second-game-finds-the-first-floor-whole + player: second + type: "/move The First Floor" + expect: + location: The First Floor room 1 (realized) + dead: false +- id: and-wenna-is-alive-at-its-stair + player: second + type: "/move The First Floor room 2" + expect: + location: The First Floor room 2 (realized) + present: + - Wenna Cole + exits_include: + - The Second Floor room 1 (realized) diff --git a/parity/scripts/waiting-out-a-floor.yml b/parity/scripts/waiting-out-a-floor.yml new file mode 100644 index 0000000..2be6f89 --- /dev/null +++ b/parity/scripts/waiting-out-a-floor.yml @@ -0,0 +1,92 @@ +--- +story: A Floor That Falls +steps: +- id: onto-the-first-floor + type: "/move The First Floor" + browser: + token: into-the-first-floor + realizes: The First Floor + replies: + - purpose: location + content: + description: A level of black stone passages, cracked across every few paces. + lore: The first floor was cut by people nobody remembers. + place_kind: warren + - purpose: location + content: + description: A low chamber at the foot of the stair, its floor split down + the middle. + lore: Delvers do not rest here long. + items: [] + people: [] + - purpose: arrival + unavailable: true + expect: + location: The First Floor room 1 (realized) + dead: false +- id: wait-1 + type: "/wait" + browser: + token: wait-1 + replies: + - purpose: narration + content: You stand still and listen to the stone settle. + expect: + elapsed_minutes: 5.0 + dead: false +- id: wait-2 + type: "/wait" + browser: + token: wait-2 + replies: + - purpose: narration + content: Grit trickles from a crack overhead. + expect: + elapsed_minutes: 5.0 + dead: false +- id: wait-3 + type: "/wait" + browser: + token: wait-3 + replies: + - purpose: narration + content: The crack in the floor is wider than it was. + expect: + elapsed_minutes: 5.0 + dead: false +- id: wait-4 + type: "/wait" + browser: + token: wait-4 + replies: + - purpose: narration + content: Somewhere further in, something heavy drops. + expect: + elapsed_minutes: 5.0 + dead: false +- id: wait-5 + type: "/wait" + browser: + token: wait-5 + replies: + - purpose: narration + content: The floor shudders under your boots. + expect: + elapsed_minutes: 5.0 + dead: false +- id: wait-6 + type: "/wait" + browser: + token: wait-6 + replies: + - purpose: narration + content: The floor tilts. + expect: + elapsed_minutes: 5.0 + dead: true +- id: nothing-after + type: "/wait" + expect: + refused: true + dead: true + offers: is dead, and this playthrough is over diff --git a/parity/worlds/a-bell-nobody-has-rung.sql b/parity/worlds/a-bell-nobody-has-rung.sql index 29ed0de..6930ef6 100644 --- a/parity/worlds/a-bell-nobody-has-rung.sql +++ b/parity/worlds/a-bell-nobody-has-rung.sql @@ -407,6 +407,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -417,7 +418,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -439,7 +440,7 @@ FOREIGN KEY ("posted_to_id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,27,'A mended wool cloak.','A bell-founder''s daughter from the valley.',NULL,'2026-10-02 13:25:58.087091',0,NULL,12,'Silence kept for its own sake.','Heights.','Ilse',8,0,0,1,5,'Clear tones.',NULL,NULL,'Ilse','Curious and stubborn.',1000000001,NULL,'female',1000000001,11,NULL,NULL,'2026-10-02 13:25:58.087091',13,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,61,'A grey apron and a ring of keys.','The sexton, who has swept the hall for forty years.',NULL,'2026-10-02 13:25:58.123801',0,NULL,10,'Visitors who touch things.','The bell tower stair.','Oda',6,0,0,0,3,'A swept floor.',1000000001,NULL,'Oda','Patient and dry.',1000000001,NULL,'female',1000000001,9,NULL,NULL,'2026-10-02 13:25:58.123801',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-chamber-of-one-room.sql b/parity/worlds/a-chamber-of-one-room.sql index 261926c..1964ab6 100644 --- a/parity/worlds/a-chamber-of-one-room.sql +++ b/parity/worlds/a-chamber-of-one-room.sql @@ -396,6 +396,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -406,7 +407,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -427,7 +428,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,30,'A patched jumpsuit.','An engineer on her third run aboard.',NULL,'2026-10-02 13:25:38.114411',0,NULL,12,'Guesswork.','A breach she cannot seal.','Kael',8,0,0,1,10,'A clean fault log.',NULL,NULL,'Kael','Careful and dry.',1000000001,NULL,'female',1000000001,12,NULL,NULL,'2026-10-02 13:25:38.114411',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-clerk-with-somewhere-to-be.sql b/parity/worlds/a-clerk-with-somewhere-to-be.sql index df2594d..69336aa 100644 --- a/parity/worlds/a-clerk-with-somewhere-to-be.sql +++ b/parity/worlds/a-clerk-with-somewhere-to-be.sql @@ -398,6 +398,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -408,7 +409,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -430,7 +431,7 @@ FOREIGN KEY ("posted_to_id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,34,'A wet coat and a folded paper.','A tenant who has come for a receipt and will not leave without one.','Wick wants a dated receipt for the quarter''s rent, in his hand, before the office closes.','2026-10-02 13:25:43.372798',0,'obtain',12,'Being asked to come back tomorrow.','Paying twice for the same thing.','Wick',8,0,0,1,10,'A receipt with a date on it.',NULL,'offer','Wick','Patient and stubborn in equal measure.',1000000001,'Wick needs to keep every receipt he has ever been given, because it is the only argument he has.','male',1000000001,12,'Wick wants somebody behind that counter to know his name without being told it.','Wick needs to say out loud that he cannot pay next quarter, to somebody who can do something about it.','2026-10-02 13:25:43.372798',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,41,'An office coat and a ring of keys she keeps turning over.','A clerk of works who is due on the landing and has been due there for an hour.','Bell wants to be up on the landing where the survey is waiting, before the hour goes and the whole morning goes with it.','2026-10-02 13:25:43.419441',0,'reach',12,'Being caught on the stairs.','Being the one who held everybody up.','Bell',8,0,0,0,10,'A morning that runs to time.',1000000001,'attend','Bell','Brisk, decent, and already thinking about the next room.',1000000001,'Bell needs to answer whoever is at the counter first, because that is what the counter is for.','female',1000000001,12,'Bell wants one person in this building to say the delay was never hers.','Bell needs to stand still long enough to hear what the person at the counter actually came to say.','2026-10-02 13:25:43.419441',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-conversation-at-the-gate.sql b/parity/worlds/a-conversation-at-the-gate.sql index ca04ab9..fdc6433 100644 --- a/parity/worlds/a-conversation-at-the-gate.sql +++ b/parity/worlds/a-conversation-at-the-gate.sql @@ -398,6 +398,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -408,7 +409,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -430,7 +431,7 @@ FOREIGN KEY ("posted_to_id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,30,'A travel-worn coat.','A trader returning to the market.',NULL,'2026-10-02 13:26:11.419682',0,NULL,12,'Needless arguments.','Losing a friend.','Cal',8,0,0,1,10,'A fair bargain.',NULL,NULL,'Cal','Patient and practical.',1000000001,NULL,'male',1000000001,12,NULL,NULL,'2026-10-02 13:26:11.419682',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,32,'A blue coat and a wooden staff.','A gatekeeper who misunderstood Cal''s arrival.',NULL,'2026-10-02 13:26:11.466046',0,NULL,12,'Deception.','Betraying a trust.','Maren',8,1,0,0,10,'Clear agreements.',1000000001,NULL,'Maren','Willing to listen and keep a promise.',1000000001,NULL,'female',1000000001,12,NULL,NULL,'2026-10-02 13:26:11.466046',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-crowded-cellar.sql b/parity/worlds/a-crowded-cellar.sql index e46e0a9..95c9a61 100644 --- a/parity/worlds/a-crowded-cellar.sql +++ b/parity/worlds/a-crowded-cellar.sql @@ -416,10 +416,11 @@ INSERT INTO schema_migrations VALUES('20250822184649'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-30 10:43:42.317995','2026-10-30 10:43:42.317999'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','ebdda9bf12dc2d5493a426a2e64de0ed551652b9','2026-10-30 10:43:42.324377','2026-10-30 10:43:42.324380'); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -451,7 +452,7 @@ INSERT INTO characters VALUES(1000000009,1000000001,'Wick Dunmore','Wick',62,'ma INSERT INTO characters VALUES(1000000010,1000000001,'Yara Holt','Yara',31,'female','Restless.','Riding boots.','Speed.','Waiting.','A lost letter.','A courier resting between runs.',0,'2026-10-30 10:43:43.874924','2026-10-30 10:43:43.874924',1000000001,0,1000000004,0,3,8,12,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000011,1000000001,'Zeb Marrow','Zeb',29,'male','Easygoing.','A green waistcoat.','Applause.','Silence.','A broken string.','A fiddler hired for the evening.',0,'2026-10-30 10:43:43.878653','2026-10-30 10:43:43.878653',1000000001,0,1000000004,0,3,8,12,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000012,1000000001,'Ash Penrose','Ash',34,'trans man','Precise.','Ink on his fingers.','Straight lines.','Muddle.','Getting lost.','A surveyor laid up by the rain.',0,'2026-10-30 10:43:43.882211','2026-10-30 10:43:43.882211',1000000001,0,1000000004,0,3,8,12,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-deeper-floor.sql b/parity/worlds/a-deeper-floor.sql index 45594b1..d25fec1 100644 --- a/parity/worlds/a-deeper-floor.sql +++ b/parity/worlds/a-deeper-floor.sql @@ -41,15 +41,15 @@ FOREIGN KEY ("player_id") FOREIGN KEY ("ruby_llm_model_id") REFERENCES "ruby_llm_models" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_8a0268155a" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_8a0268155a" FOREIGN KEY ("down_location_id") REFERENCES "locations" ("id") ON DELETE SET NULL, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE); -INSERT INTO floors VALUES(1000000001,1000000002,1,'uneasy','none',NULL,'2026-10-30 13:44:46.699801','2026-10-30 13:44:46.726626',NULL,0); -INSERT INTO floors VALUES(1000000002,1000000003,1,'uneasy','none',NULL,'2026-10-30 13:44:46.717364','2026-10-30 13:44:46.730299',4,0); +INSERT INTO floors VALUES(1000000001,1000000002,1,'uneasy','none',NULL,'2026-10-30 13:44:46.699801','2026-10-30 13:44:46.726626',NULL,0,NULL); +INSERT INTO floors VALUES(1000000002,1000000003,1,'uneasy','none',NULL,'2026-10-30 13:44:46.717364','2026-10-30 13:44:46.730299',4,0,NULL); CREATE TABLE IF NOT EXISTS "interactions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "character_id" integer NOT NULL, "scene_id" integer, "location_id" integer, "pre_thought" text, "pre_feeling" text, "action" text, "post_feeling" text, "post_thought" text, "inner_resolution" text, "summary" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "user_input" text, "engine_action" varchar, "action_status" varchar, "action_fact" text, CONSTRAINT "fk_rails_294d4ae9f8" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") @@ -431,10 +431,11 @@ INSERT INTO schema_migrations VALUES('20250822184651'); INSERT INTO schema_migrations VALUES('20250822184650'); INSERT INTO schema_migrations VALUES('20250822184649'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-30 13:44:43.769467','2026-10-30 13:44:43.769470'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','997d2947cb01ed87e8df6e9bddf8aab22ba21a27','2026-10-30 13:44:43.773358','2026-10-30 13:44:43.773359'); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-dose-beyond-two-doors.sql b/parity/worlds/a-dose-beyond-two-doors.sql index 2e5ed0a..6aaf016 100644 --- a/parity/worlds/a-dose-beyond-two-doors.sql +++ b/parity/worlds/a-dose-beyond-two-doors.sql @@ -406,6 +406,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -416,7 +417,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -437,7 +438,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,30,'A travel-worn coat.','A trader returning for supplies.',NULL,'2026-10-02 13:26:09.790338',0,NULL,12,'Waste.','Losing needed supplies.','Cal',8,0,0,1,3,'Well-kept tools.',NULL,NULL,'Cal','Patient and practical.',1000000001,NULL,'male',1000000001,3,NULL,NULL,'2026-10-02 13:26:09.790338',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-floor-that-falls.sql b/parity/worlds/a-floor-that-falls.sql new file mode 100644 index 0000000..8841fea --- /dev/null +++ b/parity/worlds/a-floor-that-falls.sql @@ -0,0 +1,669 @@ +PRAGMA foreign_keys=OFF; +BEGIN TRANSACTION; +CREATE TABLE IF NOT EXISTS "characters_scenes" ("character_id" integer NOT NULL, "scene_id" integer NOT NULL); +INSERT INTO characters_scenes VALUES(1000000001,1000000001); +CREATE TABLE IF NOT EXISTS "lab_exits_vantages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "world" varchar NOT NULL, "name" varchar NOT NULL, "teaser" text NOT NULL, "reached_from" varchar, "danger" varchar, "absent" text, "expects_inside_quantifier" varchar, "expects_population" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); +CREATE TABLE IF NOT EXISTS "lab_realization_kinds" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "world" varchar NOT NULL, "name" varchar NOT NULL, "teaser" text NOT NULL, "reached_from" varchar, "inside" varchar, "danger" varchar, "population" varchar, "expects_inside" text, "expects_population" text, "expects_storeys_above" text, "expects_storeys_below" text, "expects_danger" text, "expects_gradient" text, "expects_hazard" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); +CREATE TABLE IF NOT EXISTS "players" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "token_digest" varchar NOT NULL, "monthly_limit_usd" decimal(10,4) DEFAULT 1.0 NOT NULL, "revoked_at" datetime(6), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); +CREATE TABLE IF NOT EXISTS "playthrough_visits" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); +CREATE TABLE IF NOT EXISTS "refused_lines" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "typed" text NOT NULL, "state" json NOT NULL, "answers" json, "reading" json NOT NULL, "act_confidence" float, "thing_confidence" float, "refusal" json NOT NULL, "place" varchar, "weights" varchar, "playthrough_id" integer, "location_id" integer, "status" varchar DEFAULT 'new' NOT NULL, "label" json, "labelled_at" datetime(6), "note" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); +CREATE TABLE IF NOT EXISTS "ruby_llm_batches" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "provider_batch_id" varchar NOT NULL, "provider" varchar NOT NULL, "status" varchar NOT NULL, "raw_status" varchar, "completed" boolean DEFAULT FALSE NOT NULL, "chat_type" varchar, "batch_protocol" varchar, "chat_ids" json DEFAULT '[]', "request_counts" json, "reported_cost" json, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); +CREATE TABLE IF NOT EXISTS "ruby_llm_models" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "model_id" varchar NOT NULL, "name" varchar NOT NULL, "provider" varchar NOT NULL, "family" varchar, "model_created_at" datetime(6), "context_window" integer, "max_output_tokens" integer, "knowledge_cutoff" date, "modalities" json DEFAULT '{}', "capabilities" json DEFAULT '[]', "pricing" json DEFAULT '{}', "metadata" json DEFAULT '{}', "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "unlisted_at" datetime(6)); +CREATE TABLE IF NOT EXISTS "ruby_llm_tool_calls" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "message_id" integer NOT NULL, "tool_call_id" varchar NOT NULL, "name" varchar NOT NULL, "arguments" json DEFAULT '{}', "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "message_type" varchar NOT NULL, "result_type" varchar, "result_id" integer, "thought_signature" text, "approval" varchar, "remote" boolean DEFAULT FALSE NOT NULL); +CREATE TABLE IF NOT EXISTS "ruby_llm_usages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "chat_type" varchar NOT NULL, "chat_id" integer NOT NULL, "message_type" varchar, "message_id" integer, "operation" varchar NOT NULL, "provider" varchar NOT NULL, "model" varchar NOT NULL, "status" varchar NOT NULL, "input_tokens" integer, "output_tokens" integer, "cache_read_tokens" integer, "cache_write_tokens" integer, "thinking_tokens" integer, "input_cost" decimal(16,10), "output_cost" decimal(16,10), "cache_read_cost" decimal(16,10), "cache_write_cost" decimal(16,10), "thinking_cost" decimal(16,10), "total_cost" decimal(16,10), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "latency_ms" integer, CONSTRAINT chk_rails_71abd85d6e CHECK (operation IN ('chat', 'embedding', 'moderation', 'image', 'speech', 'transcription', 'ocr', 'rerank')), CONSTRAINT chk_rails_f50895962a CHECK (status IN ('pending', 'succeeded', 'failed', 'cancelled'))); +CREATE TABLE IF NOT EXISTS "ruby_llm_v2_backfills" ("task" varchar NOT NULL, "last_id" integer, "completed" boolean DEFAULT FALSE NOT NULL); +CREATE TABLE IF NOT EXISTS "universes" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "physics" text, "technology" text, "weapons" text, "civilizations" text, "geographies" text, "history" text, "economics" text, "politics" text, "religion" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "gravity" varchar, "carrying" varchar); +INSERT INTO universes VALUES(1000000001,'Ordinary physics.','Lanterns, iron keys and hand tools.','Short blades and cudgels.','Delvers who sell what they bring up.','A dungeon cut down into black stone under a lantern-lit landing.','The dungeon was cut long ago by people nobody remembers.','Coin for what comes up from below.','Nobody governs the dungeon.','Delvers leave a coin on the landing for luck.','2026-10-30 16:58:16.173191','2026-10-30 16:58:16.173191',NULL,NULL); +CREATE TABLE IF NOT EXISTS "characters" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "story_id" integer NOT NULL, "fullname" varchar, "nickname" varchar, "age" integer, "sex" varchar, "personality" text, "appearance" text, "likes" text, "dislikes" text, "fears" text, "backstory" text, "is_companion" boolean, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "race_id" integer NOT NULL, "is_protagonist" boolean DEFAULT FALSE NOT NULL, "location_id" integer, "deliberately_absent" boolean DEFAULT FALSE NOT NULL, "level" integer, "hit_die" integer, "strength" integer, "dexterity" integer, "will" integer, "hostile" boolean DEFAULT FALSE NOT NULL, "x" integer, "y" integer, "conscious_desire" text, "unconscious_desire" text, "recognized_need" text, "unrecognized_need" text, "desire_pursuit" varchar, "need_pursuit" varchar, "posted_to_id" integer, CONSTRAINT "fk_rails_56a213cbe2" +FOREIGN KEY ("race_id") + REFERENCES "races" ("id") +, CONSTRAINT "fk_rails_0b4b445641" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_4c54fc506e" +FOREIGN KEY ("posted_to_id") + REFERENCES "locations" ("id") + ON DELETE SET NULL, CONSTRAINT "fk_rails_6608c248e8" +FOREIGN KEY ("story_id") + REFERENCES "stories" ("id") +); +INSERT INTO characters VALUES(1000000001,1000000001,'Odile','Odile',29,'female','Careful and stubborn.','A lantern on a pole and a short blade.','A clear way back.','Doors she cannot open.','The floor giving way under her.','A delver who has never been below the first floor.',0,'2026-10-30 16:58:16.385184','2026-10-30 16:58:16.385184',1000000001,1,NULL,0,3,8,14,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO characters VALUES(1000000002,1000000001,'Wenna Cole','Wenna',41,'female','Cheerful and talkative.','A patched cloak and a sack of candles.','Company.','Silence.','Being left in the dark.','She sells candles to delvers at the head of the stair down, and has never seen a floor fall.',0,'2026-10-30 16:58:16.411049','2026-10-30 16:58:16.411049',1000000001,0,NULL,1,1,6,8,10,10,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1000000002); +CREATE TABLE IF NOT EXISTS "chats" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "model_id_string" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "ruby_llm_model_id" integer, "purpose" varchar, "playthrough_id" integer, "character_id" integer, "cancelled" boolean DEFAULT FALSE NOT NULL, "player_id" integer, CONSTRAINT "fk_rails_415520c982" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_5c87bd0780" +FOREIGN KEY ("character_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_ea73c47d5b" +FOREIGN KEY ("player_id") + REFERENCES "players" ("id") +, CONSTRAINT "fk_rails_d8a12df187" +FOREIGN KEY ("ruby_llm_model_id") + REFERENCES "ruby_llm_models" ("id") +); +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_8a0268155a" +FOREIGN KEY ("down_location_id") + REFERENCES "locations" ("id") + ON DELETE SET NULL, CONSTRAINT "fk_rails_ce8b005cb2" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") + ON DELETE CASCADE); +INSERT INTO floors VALUES(1000000001,1000000002,2,'safe','none',1000000003,'2026-10-30 16:58:16.247440','2026-10-30 16:58:16.277807',NULL,0,30); +INSERT INTO floors VALUES(1000000002,1000000003,2,'safe','none',NULL,'2026-10-30 16:58:16.265273','2026-10-30 16:58:16.282347',NULL,0,NULL); +CREATE TABLE IF NOT EXISTS "interactions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "character_id" integer NOT NULL, "scene_id" integer, "location_id" integer, "pre_thought" text, "pre_feeling" text, "action" text, "post_feeling" text, "post_thought" text, "inner_resolution" text, "summary" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "user_input" text, "engine_action" varchar, "action_status" varchar, "action_fact" text, CONSTRAINT "fk_rails_294d4ae9f8" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_6b73a2cd17" +FOREIGN KEY ("character_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_b73583a008" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "items" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "character_id" integer, "name" varchar, "description" text, "properties" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "location_id" integer, "playthrough_id" integer, "readable" boolean DEFAULT FALSE NOT NULL, "inscription" text, "template_id" integer, "bulk" varchar DEFAULT 'handy' NOT NULL, "x" integer, "y" integer, "use_kind" varchar DEFAULT 'ordinary' NOT NULL, "combustible" boolean DEFAULT FALSE NOT NULL, "disposition" varchar DEFAULT 'intact' NOT NULL, "fragility" varchar DEFAULT 'sturdy' NOT NULL, "tier" varchar DEFAULT 'portable' NOT NULL, "holds" varchar, "within_id" integer, "how" varchar, "kit_key" varchar, "noticed_at" datetime(6), "searched_at" datetime(6), "hold" varchar, "material" varchar, "wear" varchar, "worn" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_e8ed83a2e6" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_35423c7ef8" +FOREIGN KEY ("character_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_5248e92099" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +); +CREATE TABLE IF NOT EXISTS "lab_exits_judgements" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "vantage_id" integer NOT NULL, "name" varchar NOT NULL, "name_key" varchar NOT NULL, "expects_inside" text, "expects_population" text, "verdict" varchar, "aspects" text, "note" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_cbf2b734ae" +FOREIGN KEY ("vantage_id") + REFERENCES "lab_exits_vantages" ("id") +); +CREATE TABLE IF NOT EXISTS "lab_exits_samples" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "vantage_id" integer NOT NULL, "row" json DEFAULT '{}' NOT NULL, "verdict" varchar, "aspects" text, "note" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_3cc36a20a9" +FOREIGN KEY ("vantage_id") + REFERENCES "lab_exits_vantages" ("id") +); +CREATE TABLE IF NOT EXISTS "lab_realization_samples" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "kind_id" integer NOT NULL, "row" json DEFAULT '{}' NOT NULL, "verdict" varchar, "aspects" text, "note" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_7bb6f06ff4" +FOREIGN KEY ("kind_id") + REFERENCES "lab_realization_kinds" ("id") +); +CREATE TABLE IF NOT EXISTS "location_connections" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "connected_location_id" integer NOT NULL, "distance" text, "time_to_travel" text, "travel_method" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "hazard" varchar, "hazard_die" integer, "barrier" varchar DEFAULT 'open' NOT NULL, "key_template_id" integer, CONSTRAINT "fk_rails_99792c7943" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_34dc49debe" +FOREIGN KEY ("key_template_id") + REFERENCES "items" ("id") + ON DELETE SET NULL, CONSTRAINT "fk_rails_37a61eef17" +FOREIGN KEY ("connected_location_id") + REFERENCES "locations" ("id") +); +INSERT INTO location_connections VALUES(1000000001,1000000001,1000000002,'adjacent','about a minute','taking stairs','2026-10-30 16:58:16.473881','2026-10-30 16:58:16.473881',NULL,NULL,'open',NULL); +INSERT INTO location_connections VALUES(1000000002,1000000002,1000000001,'adjacent','about a minute','taking stairs','2026-10-30 16:58:16.476405','2026-10-30 16:58:16.476405',NULL,NULL,'open',NULL); +INSERT INTO location_connections VALUES(1000000003,1000000002,1000000003,'adjacent','about a minute','taking stairs','2026-10-30 16:58:16.477946','2026-10-30 16:58:16.477946',NULL,NULL,'open',NULL); +INSERT INTO location_connections VALUES(1000000004,1000000003,1000000002,'adjacent','about a minute','taking stairs','2026-10-30 16:58:16.479047','2026-10-30 16:58:16.479047',NULL,NULL,'open',NULL); +CREATE TABLE IF NOT EXISTS "locations" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "story_id" integer NOT NULL, "name" varchar, "description" text, "lore" text, "last_protagonist_visit" datetime(6), "parent_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "detail_level" varchar DEFAULT 'stub' NOT NULL, "teaser" text, "mobile" boolean DEFAULT FALSE NOT NULL, "danger" varchar DEFAULT 'safe' NOT NULL, "hazard" varchar, "hazard_die" integer, "x" integer, "y" integer, "z" integer, "width" integer, "depth" integer, "population" varchar, "generation_checkpoint" json, "surface" varchar, "kind" varchar, "density" varchar, CONSTRAINT "fk_rails_5bc98acf09" +FOREIGN KEY ("parent_location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_fedd9b21a0" +FOREIGN KEY ("story_id") + REFERENCES "stories" ("id") +); +INSERT INTO locations VALUES(1000000001,1000000001,'The Landing','A square landing of black stone lit by one lantern, a stair going down from it.','Delvers gather here before they go down.',NULL,NULL,'2026-10-30 16:58:16.219716','2026-10-30 16:58:16.219716','realized','A lantern-lit landing.',0,'safe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,'nobody',NULL,NULL,NULL,NULL); +INSERT INTO locations VALUES(1000000002,1000000001,'The First Floor',NULL,NULL,NULL,NULL,'2026-10-30 16:58:16.240052','2026-10-30 16:58:16.240052','stub','The first level of the dungeon, cracked through, with a stair somewhere that goes further down.',0,'safe',NULL,NULL,NULL,NULL,NULL,12,6,NULL,NULL,NULL,'warren',NULL); +INSERT INTO locations VALUES(1000000003,1000000001,'The Second Floor',NULL,NULL,NULL,NULL,'2026-10-30 16:58:16.259504','2026-10-30 16:58:16.259504','stub','The second level of the dungeon, where the stair ends.',0,'safe',NULL,NULL,NULL,NULL,NULL,12,6,NULL,NULL,NULL,'warren',NULL); +CREATE TABLE IF NOT EXISTS "locations_world_events" ("location_id" integer NOT NULL, "world_event_id" integer NOT NULL, CONSTRAINT "fk_rails_861146f35c" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_16317bfdf9" +FOREIGN KEY ("world_event_id") + REFERENCES "world_events" ("id") +); +CREATE TABLE IF NOT EXISTS "messages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "chat_id" integer NOT NULL, "role" varchar, "content" text, "model_id_string" varchar, "input_tokens" integer, "output_tokens" integer, "tool_call_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "model_id" integer, "scene_id" integer, "content_raw" json, "cache_until_here" boolean DEFAULT FALSE NOT NULL, "finish_reason" varchar, "citations" json, "server_tool_calls" json, "raw_content" json, "raw_reasoning" json, CONSTRAINT "fk_rails_c02b47ad97" +FOREIGN KEY ("model_id") + REFERENCES "ruby_llm_models" ("id") +, CONSTRAINT "fk_rails_0f670de7ba" +FOREIGN KEY ("chat_id") + REFERENCES "chats" ("id") +, CONSTRAINT "fk_rails_5c4d747b35" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_beats" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "quest_step_id" integer NOT NULL, "reached_at" datetime(6) NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_d479d5ffac" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_c536cc5a9f" +FOREIGN KEY ("quest_step_id") + REFERENCES "quest_steps" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_blows" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "attacker_id" integer NOT NULL, "target_id" integer NOT NULL, "location_id" integer NOT NULL, "damage" integer NOT NULL, "hp_after" integer NOT NULL, "round" integer NOT NULL, "sequence" integer NOT NULL, "story_timestamp" datetime(6) NOT NULL, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_f0e8a189c5" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_9a2b0679db" +FOREIGN KEY ("target_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_d02f38aa6d" +FOREIGN KEY ("attacker_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_104e085e3d" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_83e55c02a1" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_commands" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "request_token" varchar NOT NULL, "command" text NOT NULL, "status" varchar DEFAULT 'pending' NOT NULL, "result_scene_id" integer, "refusal" json DEFAULT '{}' NOT NULL, "error_kind" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "journal" json DEFAULT '{}' NOT NULL, CONSTRAINT "fk_rails_53f23eb49f" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_69c7d16a1a" +FOREIGN KEY ("result_scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_drifts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "scene_id" integer, "location_id" integer, "action" varchar NOT NULL, "command" text NOT NULL, "offered" text, "story_timestamp" datetime(6), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_018f29e0ac" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_094a605342" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_45044856a9" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_due_endings" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "quest_outcome_id" integer NOT NULL, "due_at" datetime(6) NOT NULL, "declined_at" datetime(6), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_c471335e2f" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_b8e95530cf" +FOREIGN KEY ("quest_outcome_id") + REFERENCES "quest_outcomes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_endings" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "quest_outcome_id" integer NOT NULL, "reached_at" datetime(6) NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_762b78023d" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_2220d324b0" +FOREIGN KEY ("quest_outcome_id") + REFERENCES "quest_outcomes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_feedbacks" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "scene_id" integer NOT NULL, "verdict" varchar NOT NULL, "note" text, "prose_model" varchar, "prose_purpose" varchar, "prose_models" text, "answering_models" text, "input_tokens" integer, "output_tokens" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "prose_prompt_digest" varchar, CONSTRAINT "fk_rails_99ee3b49ea" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_247709935a" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_f82b72b677" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_b4a8914069" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_npc_states" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "character_id" integer NOT NULL, "location_id" integer, "following" boolean DEFAULT FALSE NOT NULL, "ceasefire" boolean DEFAULT FALSE NOT NULL, "peace_after_blow_id" integer DEFAULT 0 NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "noticed_at" datetime(6), CONSTRAINT "fk_rails_ef100d7841" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_71a135780d" +FOREIGN KEY ("character_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_984efad0e9" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_overreaches" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "scene_id" integer, "location_id" integer, "action" varchar NOT NULL, "command" text NOT NULL, "acted" text NOT NULL, "unacted" text NOT NULL, "story_timestamp" datetime(6), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_b903d1bc16" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_9aac75595a" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_879cc0f78c" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_paragraphs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "scene_id" integer NOT NULL, "author" varchar DEFAULT 'player' NOT NULL, "text" text NOT NULL, "audit" json, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "consent" varchar DEFAULT 'none' NOT NULL, "requests" json, "prompt_digest" varchar, CONSTRAINT "fk_rails_db3983d32c" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_52ef8100a0" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_passages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_connection_id" integer NOT NULL, "opened_by_item_id" integer, "means" varchar NOT NULL, "opened_at" datetime(6) NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_7bdf6a743a" +FOREIGN KEY ("location_connection_id") + REFERENCES "location_connections" ("id") + ON DELETE CASCADE, CONSTRAINT "fk_rails_75c2355411" +FOREIGN KEY ("opened_by_item_id") + REFERENCES "items" ("id") + ON DELETE SET NULL, CONSTRAINT "fk_rails_995c25e2fd" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") + ON DELETE CASCADE); +CREATE TABLE IF NOT EXISTS "playthrough_tolls" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "character_id" integer NOT NULL, "location_id" integer NOT NULL, "location_connection_id" integer, "hazard" varchar NOT NULL, "saved" boolean DEFAULT FALSE NOT NULL, "damage" integer NOT NULL, "hp_after" integer NOT NULL, "sequence" integer NOT NULL, "story_timestamp" datetime(6) NOT NULL, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_83dfd307ab" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_443984de3a" +FOREIGN KEY ("location_connection_id") + REFERENCES "location_connections" ("id") +, CONSTRAINT "fk_rails_8a2212da5c" +FOREIGN KEY ("character_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_6ec4904a86" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_af4dd03665" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_turn_events" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_command_id" integer NOT NULL, "sequence" integer NOT NULL, "kind" varchar NOT NULL, "data" json DEFAULT '{}' NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_c7a6770f24" +FOREIGN KEY ("playthrough_command_id") + REFERENCES "playthrough_commands" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_vitals" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "character_id" integer NOT NULL, "hp_current" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "provoked_at" datetime(6), CONSTRAINT "fk_rails_03cf4b1b8e" +FOREIGN KEY ("character_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_9956f79aa5" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +); +CREATE TABLE IF NOT EXISTS "playthrough_volitions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "character_id" integer NOT NULL, "location_id" integer NOT NULL, "scene_id" integer, "chosen" varchar NOT NULL, "status" varchar NOT NULL, "fact" text NOT NULL, "serves" varchar NOT NULL, "round" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "decided_by" varchar, "system_one_error" varchar, CONSTRAINT "fk_rails_55e6f9e865" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +, CONSTRAINT "fk_rails_0c408b6253" +FOREIGN KEY ("character_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_1824dea9f6" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_a054701564" +FOREIGN KEY ("scene_id") + REFERENCES "scenes" ("id") +); +CREATE TABLE IF NOT EXISTS "playthroughs" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "story_id" integer NOT NULL, "character_id" integer, "current_location_id" integer, "current_scene_id" integer, "token" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "ended_at" datetime(6), "player_id" integer, "mode" varchar DEFAULT 'narrated' NOT NULL, CONSTRAINT "fk_rails_9b48509224" +FOREIGN KEY ("current_scene_id") + REFERENCES "scenes" ("id") +, CONSTRAINT "fk_rails_3a7e48fa36" +FOREIGN KEY ("current_location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_5b54078c4b" +FOREIGN KEY ("character_id") + REFERENCES "characters" ("id") +, CONSTRAINT "fk_rails_ab75586f4b" +FOREIGN KEY ("player_id") + REFERENCES "players" ("id") +, CONSTRAINT "fk_rails_3d6d3a9c5a" +FOREIGN KEY ("story_id") + REFERENCES "stories" ("id") +); +CREATE TABLE IF NOT EXISTS "quest_outcomes" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "quest_id" integer NOT NULL, "name" varchar NOT NULL, "summary" text NOT NULL, "is_default" boolean DEFAULT FALSE NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "condition" varchar, "minutes" integer, "ramification_summary" text, "ramification_minutes" integer, "step_position" integer, "character_id" integer, CONSTRAINT "fk_rails_acf8ece7c5" +FOREIGN KEY ("quest_id") + REFERENCES "quests" ("id") +); +CREATE TABLE IF NOT EXISTS "quest_steps" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "quest_id" integer NOT NULL, "position" integer NOT NULL, "summary" text NOT NULL, "trigger_kind" varchar NOT NULL, "target_name" varchar, "teaser" text, "target_type" varchar, "target_id" integer, "minutes" integer, "bound_at" datetime(6), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "companion_id" integer, CONSTRAINT "fk_rails_ba1603d17b" +FOREIGN KEY ("quest_id") + REFERENCES "quests" ("id") +); +CREATE TABLE IF NOT EXISTS "quests" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "story_id" integer NOT NULL, "parent_quest_id" integer, "title" varchar NOT NULL, "premise" text NOT NULL, "status" varchar DEFAULT 'open' NOT NULL, "contributes" boolean DEFAULT TRUE NOT NULL, "origin" varchar DEFAULT 'seeded' NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_a64954ae79" +FOREIGN KEY ("parent_quest_id") + REFERENCES "quests" ("id") +, CONSTRAINT "fk_rails_fa77fc496b" +FOREIGN KEY ("story_id") + REFERENCES "stories" ("id") +); +CREATE TABLE IF NOT EXISTS "races" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "universe_id" integer NOT NULL, "name" varchar NOT NULL, "description" text NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monstrous" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_c25ac61605" +FOREIGN KEY ("universe_id") + REFERENCES "universes" ("id") +); +INSERT INTO races VALUES(1000000001,1000000001,'Delvers','People who go down into the dungeon for a living.','2026-10-30 16:58:16.176470','2026-10-30 16:58:16.176470',0); +CREATE TABLE IF NOT EXISTS "relay_receipts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "player_id" integer NOT NULL, "route" varchar NOT NULL, "model" varchar NOT NULL, "stream" boolean DEFAULT FALSE NOT NULL, "status" varchar DEFAULT 'open' NOT NULL, "reserved_usd" decimal(12,6) NOT NULL, "cost_usd" decimal(12,6), "cost_source" varchar, "input_tokens" integer, "output_tokens" integer, "upstream_status" integer, "finished_at" datetime(6), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_deae27bd71" +FOREIGN KEY ("player_id") + REFERENCES "players" ("id") +); +CREATE TABLE IF NOT EXISTS "scenes" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "story_id" integer NOT NULL, "location_id" integer NOT NULL, "previous_scene_id" integer, "description" text, "story_timestamp" datetime(6), "summary" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "is_opening" boolean DEFAULT FALSE NOT NULL, "typed" text, "resolved_action" varchar, "acted_on_type" varchar, "acted_on_id" integer, "resolved_by" varchar, "engine_fallback" boolean DEFAULT FALSE NOT NULL, "engine_fact" text, CONSTRAINT "fk_rails_5cc24f985a" +FOREIGN KEY ("previous_scene_id") + REFERENCES "scenes" ("id") +, CONSTRAINT "fk_rails_abfe1c0369" +FOREIGN KEY ("location_id") + REFERENCES "locations" ("id") +, CONSTRAINT "fk_rails_38ea512481" +FOREIGN KEY ("story_id") + REFERENCES "stories" ("id") +); +INSERT INTO scenes VALUES(1000000001,1000000001,1000000001,NULL,'You stand on the lantern-lit landing above the first floor.','2026-10-31 08:00:00','Odile reaches the landing.','2026-10-30 16:58:16.570870','2026-10-30 16:58:16.570870',1,NULL,NULL,NULL,NULL,NULL,0,NULL); +CREATE TABLE IF NOT EXISTS "stories" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar, "genre" varchar, "preface" text, "summary" text, "start_time" datetime(6), "universe_id" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "generation_snapshot" text, "people_limit" integer, "things_limit" integer, CONSTRAINT "fk_rails_2a912ea846" +FOREIGN KEY ("universe_id") + REFERENCES "universes" ("id") +); +INSERT INTO stories VALUES(1000000001,'A Floor That Falls (engine sweep)','dungeon crawl','The floors of this dungeon do not stay up for long.','Odile goes down before the first floor gives way, or does not.','2026-10-31 08:00:00',1000000001,'2026-10-30 16:58:16.196351','2026-10-30 16:58:16.196351',NULL,NULL,NULL); +CREATE TABLE IF NOT EXISTS "system_one_receipts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "player_id" integer, "playthrough_id" integer, "purpose" varchar, "transport" varchar, "cost_usd" decimal(12,6) NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "cost_source" varchar DEFAULT 'estimated' NOT NULL, "latency_ms" integer, CONSTRAINT "fk_rails_4ae941c854" +FOREIGN KEY ("player_id") + REFERENCES "players" ("id") +, CONSTRAINT "fk_rails_75847e1556" +FOREIGN KEY ("playthrough_id") + REFERENCES "playthroughs" ("id") +); +CREATE TABLE IF NOT EXISTS "world_events" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "world_mechanic_id" integer, "story_id" integer NOT NULL, "occurred_at" datetime(6) NOT NULL, "summary" text NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "source" varchar NOT NULL, "playthrough_id" integer, "scheduled_for" datetime(6), "fired_at" datetime(6), CONSTRAINT "fk_rails_cafc0959e8" +FOREIGN KEY ("story_id") + REFERENCES "stories" ("id") +, CONSTRAINT "fk_rails_efc6cdec27" +FOREIGN KEY ("world_mechanic_id") + REFERENCES "world_mechanics" ("id") +); +CREATE TABLE IF NOT EXISTS "world_mechanics" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "story_id" integer NOT NULL, "name" varchar NOT NULL, "kind" varchar NOT NULL, "cadence" varchar NOT NULL, "description" text, "last_run_at" datetime(6), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_d7328bbbe5" +FOREIGN KEY ("story_id") + REFERENCES "stories" ("id") +); +CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY); +INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261030230000'); +INSERT INTO schema_migrations VALUES('20261030200000'); +INSERT INTO schema_migrations VALUES('20261030120000'); +INSERT INTO schema_migrations VALUES('20261029154235'); +INSERT INTO schema_migrations VALUES('20261027120117'); +INSERT INTO schema_migrations VALUES('20261025230000'); +INSERT INTO schema_migrations VALUES('20261025200000'); +INSERT INTO schema_migrations VALUES('20261025120000'); +INSERT INTO schema_migrations VALUES('20261024120000'); +INSERT INTO schema_migrations VALUES('20261015213000'); +INSERT INTO schema_migrations VALUES('20261015200000'); +INSERT INTO schema_migrations VALUES('20261015180000'); +INSERT INTO schema_migrations VALUES('20261015120000'); +INSERT INTO schema_migrations VALUES('20261010220000'); +INSERT INTO schema_migrations VALUES('20261010180000'); +INSERT INTO schema_migrations VALUES('20261010120000'); +INSERT INTO schema_migrations VALUES('20260929023507'); +INSERT INTO schema_migrations VALUES('20260928124453'); +INSERT INTO schema_migrations VALUES('20260928102823'); +INSERT INTO schema_migrations VALUES('20260928035434'); +INSERT INTO schema_migrations VALUES('20260928035429'); +INSERT INTO schema_migrations VALUES('20260928031026'); +INSERT INTO schema_migrations VALUES('20260928025950'); +INSERT INTO schema_migrations VALUES('20260928020741'); +INSERT INTO schema_migrations VALUES('20260927152056'); +INSERT INTO schema_migrations VALUES('20260927031841'); +INSERT INTO schema_migrations VALUES('20260927031839'); +INSERT INTO schema_migrations VALUES('20260927031837'); +INSERT INTO schema_migrations VALUES('20260927031835'); +INSERT INTO schema_migrations VALUES('20260927031834'); +INSERT INTO schema_migrations VALUES('20260926195041'); +INSERT INTO schema_migrations VALUES('20260922141619'); +INSERT INTO schema_migrations VALUES('20260922141618'); +INSERT INTO schema_migrations VALUES('20260922141617'); +INSERT INTO schema_migrations VALUES('20260919202606'); +INSERT INTO schema_migrations VALUES('20260919202605'); +INSERT INTO schema_migrations VALUES('20260917133823'); +INSERT INTO schema_migrations VALUES('20260910041334'); +INSERT INTO schema_migrations VALUES('20260910025040'); +INSERT INTO schema_migrations VALUES('20260909030346'); +INSERT INTO schema_migrations VALUES('20260909023028'); +INSERT INTO schema_migrations VALUES('20260909023014'); +INSERT INTO schema_migrations VALUES('20260908203558'); +INSERT INTO schema_migrations VALUES('20260908155546'); +INSERT INTO schema_migrations VALUES('20260908120000'); +INSERT INTO schema_migrations VALUES('20260907150000'); +INSERT INTO schema_migrations VALUES('20260907140000'); +INSERT INTO schema_migrations VALUES('20260907120000'); +INSERT INTO schema_migrations VALUES('20260906130000'); +INSERT INTO schema_migrations VALUES('20260906120000'); +INSERT INTO schema_migrations VALUES('20260905150000'); +INSERT INTO schema_migrations VALUES('20260905140000'); +INSERT INTO schema_migrations VALUES('20260905130000'); +INSERT INTO schema_migrations VALUES('20260905120000'); +INSERT INTO schema_migrations VALUES('20260905110000'); +INSERT INTO schema_migrations VALUES('20260905100001'); +INSERT INTO schema_migrations VALUES('20260905100000'); +INSERT INTO schema_migrations VALUES('20260905090100'); +INSERT INTO schema_migrations VALUES('20260905090000'); +INSERT INTO schema_migrations VALUES('20260904190000'); +INSERT INTO schema_migrations VALUES('20260904180000'); +INSERT INTO schema_migrations VALUES('20260904170000'); +INSERT INTO schema_migrations VALUES('20260904160000'); +INSERT INTO schema_migrations VALUES('20260904120000'); +INSERT INTO schema_migrations VALUES('20260903170000'); +INSERT INTO schema_migrations VALUES('20260903120000'); +INSERT INTO schema_migrations VALUES('20260902120000'); +INSERT INTO schema_migrations VALUES('20260901170000'); +INSERT INTO schema_migrations VALUES('20260901160000'); +INSERT INTO schema_migrations VALUES('20260901120000'); +INSERT INTO schema_migrations VALUES('20260831140000'); +INSERT INTO schema_migrations VALUES('20260831130000'); +INSERT INTO schema_migrations VALUES('20260831120000'); +INSERT INTO schema_migrations VALUES('20260831000000'); +INSERT INTO schema_migrations VALUES('20260830220946'); +INSERT INTO schema_migrations VALUES('20260830220945'); +INSERT INTO schema_migrations VALUES('20260830210000'); +INSERT INTO schema_migrations VALUES('20260830200001'); +INSERT INTO schema_migrations VALUES('20260830200000'); +INSERT INTO schema_migrations VALUES('20260830192215'); +INSERT INTO schema_migrations VALUES('20260830192214'); +INSERT INTO schema_migrations VALUES('20250824193519'); +INSERT INTO schema_migrations VALUES('20250823024100'); +INSERT INTO schema_migrations VALUES('20250823024050'); +INSERT INTO schema_migrations VALUES('20250823024037'); +INSERT INTO schema_migrations VALUES('20250823024029'); +INSERT INTO schema_migrations VALUES('20250823024021'); +INSERT INTO schema_migrations VALUES('20250823024013'); +INSERT INTO schema_migrations VALUES('20250823023950'); +INSERT INTO schema_migrations VALUES('20250823023942'); +INSERT INTO schema_migrations VALUES('20250823023925'); +INSERT INTO schema_migrations VALUES('20250822184651'); +INSERT INTO schema_migrations VALUES('20250822184650'); +INSERT INTO schema_migrations VALUES('20250822184649'); +INSERT INTO schema_migrations VALUES('20261031120000'); +CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); +INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-30 16:58:14.956642','2026-10-30 16:58:14.956646'); +INSERT INTO ar_internal_metadata VALUES('schema_sha1','5b41acc531745dba36c583b40f12d94bcf2d8eb7','2026-10-30 16:58:14.961749','2026-10-30 16:58:14.961751'); +PRAGMA writable_schema=ON; +CREATE TABLE IF NOT EXISTS sqlite_sequence(name,seq); +DELETE FROM sqlite_sequence; +INSERT INTO sqlite_sequence VALUES('lab_exits_vantages',1000000000); +INSERT INTO sqlite_sequence VALUES('lab_realization_kinds',1000000000); +INSERT INTO sqlite_sequence VALUES('players',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_visits',1000000000); +INSERT INTO sqlite_sequence VALUES('refused_lines',1000000000); +INSERT INTO sqlite_sequence VALUES('ruby_llm_batches',1000000000); +INSERT INTO sqlite_sequence VALUES('ruby_llm_models',1000000000); +INSERT INTO sqlite_sequence VALUES('ruby_llm_tool_calls',1000000000); +INSERT INTO sqlite_sequence VALUES('ruby_llm_usages',1000000000); +INSERT INTO sqlite_sequence VALUES('universes',1000000001); +INSERT INTO sqlite_sequence VALUES('characters',1000000002); +INSERT INTO sqlite_sequence VALUES('chats',1000000000); +INSERT INTO sqlite_sequence VALUES('floors',1000000002); +INSERT INTO sqlite_sequence VALUES('interactions',1000000000); +INSERT INTO sqlite_sequence VALUES('items',1000000000); +INSERT INTO sqlite_sequence VALUES('lab_exits_judgements',1000000000); +INSERT INTO sqlite_sequence VALUES('lab_exits_samples',1000000000); +INSERT INTO sqlite_sequence VALUES('lab_realization_samples',1000000000); +INSERT INTO sqlite_sequence VALUES('location_connections',1000000004); +INSERT INTO sqlite_sequence VALUES('locations',1000000003); +INSERT INTO sqlite_sequence VALUES('messages',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_beats',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_blows',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_commands',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_drifts',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_due_endings',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_endings',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_feedbacks',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_floors',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_npc_states',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_overreaches',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_paragraphs',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_passages',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_tolls',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_turn_events',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_vitals',1000000000); +INSERT INTO sqlite_sequence VALUES('playthrough_volitions',1000000000); +INSERT INTO sqlite_sequence VALUES('playthroughs',1000000000); +INSERT INTO sqlite_sequence VALUES('quest_outcomes',1000000000); +INSERT INTO sqlite_sequence VALUES('quest_steps',1000000000); +INSERT INTO sqlite_sequence VALUES('quests',1000000000); +INSERT INTO sqlite_sequence VALUES('races',1000000001); +INSERT INTO sqlite_sequence VALUES('relay_receipts',1000000000); +INSERT INTO sqlite_sequence VALUES('scenes',1000000001); +INSERT INTO sqlite_sequence VALUES('stories',1000000001); +INSERT INTO sqlite_sequence VALUES('system_one_receipts',1000000000); +INSERT INTO sqlite_sequence VALUES('world_events',1000000000); +INSERT INTO sqlite_sequence VALUES('world_mechanics',1000000000); +CREATE INDEX "index_characters_scenes_on_character_id_and_scene_id" ON "characters_scenes" ("character_id", "scene_id"); +CREATE INDEX "index_characters_scenes_on_scene_id_and_character_id" ON "characters_scenes" ("scene_id", "character_id"); +CREATE UNIQUE INDEX "index_players_on_name" ON "players" ("name"); +CREATE UNIQUE INDEX "index_players_on_token_digest" ON "players" ("token_digest"); +CREATE INDEX "index_playthrough_visits_on_location_id" ON "playthrough_visits" ("location_id"); +CREATE UNIQUE INDEX "index_playthrough_visits_on_playthrough_and_location" ON "playthrough_visits" ("playthrough_id", "location_id"); +CREATE INDEX "index_playthrough_visits_on_playthrough_id" ON "playthrough_visits" ("playthrough_id"); +CREATE INDEX "index_refused_lines_on_status" ON "refused_lines" ("status"); +CREATE UNIQUE INDEX "index_ruby_llm_batches_on_provider_and_provider_batch_id" ON "ruby_llm_batches" ("provider", "provider_batch_id"); +CREATE INDEX "index_ruby_llm_batches_on_status" ON "ruby_llm_batches" ("status"); +CREATE INDEX "index_ruby_llm_models_on_family" ON "ruby_llm_models" ("family"); +CREATE UNIQUE INDEX "index_ruby_llm_models_on_provider_and_model_id" ON "ruby_llm_models" ("provider", "model_id"); +CREATE INDEX "index_ruby_llm_models_on_provider" ON "ruby_llm_models" ("provider"); +CREATE INDEX "index_ruby_llm_tool_calls_on_message_type_and_message_id" ON "ruby_llm_tool_calls" ("message_type", "message_id"); +CREATE INDEX "index_ruby_llm_tool_calls_on_name" ON "ruby_llm_tool_calls" ("name"); +CREATE INDEX "index_ruby_llm_tool_calls_on_result_type_and_result_id" ON "ruby_llm_tool_calls" ("result_type", "result_id"); +CREATE UNIQUE INDEX "index_ruby_llm_tool_calls_on_tool_call_id" ON "ruby_llm_tool_calls" ("tool_call_id"); +CREATE INDEX "index_ruby_llm_usages_on_chat_type_and_chat_id" ON "ruby_llm_usages" ("chat_type", "chat_id"); +CREATE INDEX "index_ruby_llm_usages_on_message_type_and_message_id" ON "ruby_llm_usages" ("message_type", "message_id"); +CREATE INDEX "index_ruby_llm_usages_on_status" ON "ruby_llm_usages" ("status"); +CREATE UNIQUE INDEX "index_ruby_llm_v2_backfills_on_task" ON "ruby_llm_v2_backfills" ("task"); +CREATE UNIQUE INDEX "index_characters_on_story_id_and_lower_fullname" ON "characters" (story_id, LOWER(fullname)); +CREATE INDEX "index_characters_on_location_id_and_hostile" ON "characters" ("location_id", "hostile"); +CREATE INDEX "index_characters_on_location_id_and_id" ON "characters" ("location_id", "id"); +CREATE INDEX "index_characters_on_location_id" ON "characters" ("location_id"); +CREATE INDEX "index_characters_on_posted_to_id" ON "characters" ("posted_to_id"); +CREATE INDEX "index_characters_on_race_id" ON "characters" ("race_id"); +CREATE INDEX "index_characters_on_story_id_and_is_protagonist" ON "characters" ("story_id", "is_protagonist"); +CREATE INDEX "index_characters_on_story_id" ON "characters" ("story_id"); +CREATE INDEX "index_chats_on_character_id" ON "chats" ("character_id"); +CREATE INDEX "index_chats_on_player_id" ON "chats" ("player_id"); +CREATE INDEX "index_chats_on_conversation_key" ON "chats" ("playthrough_id", "character_id", "purpose"); +CREATE INDEX "index_chats_on_playthrough_id" ON "chats" ("playthrough_id"); +CREATE INDEX "index_chats_on_ruby_llm_model_id" ON "chats" ("ruby_llm_model_id"); +CREATE INDEX "index_floors_on_down_location_id" ON "floors" ("down_location_id"); +CREATE UNIQUE INDEX "index_floors_on_location_id" ON "floors" ("location_id"); +CREATE INDEX "index_interactions_on_character_id" ON "interactions" ("character_id"); +CREATE INDEX "index_interactions_on_location_id" ON "interactions" ("location_id"); +CREATE INDEX "index_interactions_on_scene_id" ON "interactions" ("scene_id"); +CREATE INDEX "index_items_on_character_id" ON "items" ("character_id"); +CREATE INDEX "index_items_on_location_id_and_character_id" ON "items" ("location_id", "character_id"); +CREATE INDEX "index_items_on_location_id" ON "items" ("location_id"); +CREATE INDEX "index_items_on_playthrough_id_and_id" ON "items" ("playthrough_id", "id"); +CREATE INDEX "index_items_on_playthrough_id_and_template_id" ON "items" ("playthrough_id", "template_id"); +CREATE INDEX "index_items_on_playthrough_id" ON "items" ("playthrough_id"); +CREATE INDEX "index_items_on_template_id" ON "items" ("template_id"); +CREATE INDEX "index_items_on_within_id" ON "items" ("within_id"); +CREATE UNIQUE INDEX "index_lab_exits_judgements_on_place" ON "lab_exits_judgements" ("vantage_id", "name_key"); +CREATE INDEX "index_lab_exits_judgements_on_vantage_id" ON "lab_exits_judgements" ("vantage_id"); +CREATE INDEX "index_lab_exits_samples_on_vantage_id" ON "lab_exits_samples" ("vantage_id"); +CREATE INDEX "index_lab_realization_samples_on_kind_id" ON "lab_realization_samples" ("kind_id"); +CREATE INDEX "index_location_connections_on_connected_location_id" ON "location_connections" ("connected_location_id"); +CREATE INDEX "index_location_connections_on_key_template_id" ON "location_connections" ("key_template_id"); +CREATE UNIQUE INDEX "idx_on_location_id_connected_location_id_a0efda2bf6" ON "location_connections" ("location_id", "connected_location_id"); +CREATE INDEX "index_location_connections_on_location_id" ON "location_connections" ("location_id"); +CREATE UNIQUE INDEX "index_locations_on_story_id_and_lower_name" ON "locations" (story_id, lower(name)); +CREATE INDEX "index_locations_on_parent_location_id" ON "locations" ("parent_location_id"); +CREATE INDEX "index_locations_on_story_id_and_detail_level" ON "locations" ("story_id", "detail_level"); +CREATE INDEX "index_locations_on_story_id" ON "locations" ("story_id"); +CREATE INDEX "index_locations_world_events_on_location_id" ON "locations_world_events" ("location_id"); +CREATE UNIQUE INDEX "index_locations_world_events_on_world_event_id_and_location_id" ON "locations_world_events" ("world_event_id", "location_id"); +CREATE INDEX "index_locations_world_events_on_world_event_id" ON "locations_world_events" ("world_event_id"); +CREATE INDEX "index_messages_on_chat_id" ON "messages" ("chat_id"); +CREATE INDEX "index_messages_on_model_id" ON "messages" ("model_id"); +CREATE INDEX "index_messages_on_scene_id" ON "messages" ("scene_id"); +CREATE INDEX "index_messages_on_tool_call_id" ON "messages" ("tool_call_id"); +CREATE UNIQUE INDEX "index_playthrough_beats_on_playthrough_id_and_quest_step_id" ON "playthrough_beats" ("playthrough_id", "quest_step_id"); +CREATE INDEX "index_playthrough_beats_on_playthrough_id" ON "playthrough_beats" ("playthrough_id"); +CREATE INDEX "index_playthrough_beats_on_quest_step_id" ON "playthrough_beats" ("quest_step_id"); +CREATE INDEX "index_playthrough_blows_on_attacker_id" ON "playthrough_blows" ("attacker_id"); +CREATE INDEX "index_playthrough_blows_on_location_id" ON "playthrough_blows" ("location_id"); +CREATE INDEX "index_playthrough_blows_on_playthrough_and_scene" ON "playthrough_blows" ("playthrough_id", "scene_id", "id"); +CREATE INDEX "index_playthrough_blows_on_playthrough_id" ON "playthrough_blows" ("playthrough_id"); +CREATE INDEX "index_playthrough_blows_on_scene_id" ON "playthrough_blows" ("scene_id"); +CREATE INDEX "index_playthrough_blows_on_target_id" ON "playthrough_blows" ("target_id"); +CREATE UNIQUE INDEX "index_playthrough_commands_on_submission" ON "playthrough_commands" ("playthrough_id", "request_token", "command"); +CREATE INDEX "index_playthrough_commands_on_playthrough_id" ON "playthrough_commands" ("playthrough_id"); +CREATE INDEX "index_playthrough_commands_on_result_scene_id" ON "playthrough_commands" ("result_scene_id"); +CREATE INDEX "index_playthrough_drifts_on_action" ON "playthrough_drifts" ("action"); +CREATE INDEX "index_playthrough_drifts_on_location_id" ON "playthrough_drifts" ("location_id"); +CREATE INDEX "index_playthrough_drifts_on_playthrough_id_and_story_timestamp" ON "playthrough_drifts" ("playthrough_id", "story_timestamp"); +CREATE INDEX "index_playthrough_drifts_on_playthrough_id" ON "playthrough_drifts" ("playthrough_id"); +CREATE INDEX "index_playthrough_drifts_on_scene_id" ON "playthrough_drifts" ("scene_id"); +CREATE UNIQUE INDEX "index_playthrough_due_endings_on_playthrough_id" ON "playthrough_due_endings" ("playthrough_id"); +CREATE INDEX "index_playthrough_due_endings_on_quest_outcome_id" ON "playthrough_due_endings" ("quest_outcome_id"); +CREATE UNIQUE INDEX "idx_on_playthrough_id_quest_outcome_id_7ea31b4171" ON "playthrough_endings" ("playthrough_id", "quest_outcome_id"); +CREATE INDEX "index_playthrough_endings_on_playthrough_id" ON "playthrough_endings" ("playthrough_id"); +CREATE INDEX "index_playthrough_endings_on_quest_outcome_id" ON "playthrough_endings" ("quest_outcome_id"); +CREATE UNIQUE INDEX "index_playthrough_feedbacks_on_playthrough_id_and_scene_id" ON "playthrough_feedbacks" ("playthrough_id", "scene_id"); +CREATE INDEX "index_playthrough_feedbacks_on_playthrough_id" ON "playthrough_feedbacks" ("playthrough_id"); +CREATE INDEX "index_playthrough_feedbacks_on_prose_model" ON "playthrough_feedbacks" ("prose_model"); +CREATE INDEX "index_playthrough_feedbacks_on_prose_prompt_digest" ON "playthrough_feedbacks" ("prose_prompt_digest"); +CREATE INDEX "index_playthrough_feedbacks_on_scene_id" ON "playthrough_feedbacks" ("scene_id"); +CREATE INDEX "index_playthrough_feedbacks_on_verdict" ON "playthrough_feedbacks" ("verdict"); +CREATE INDEX "index_playthrough_floors_on_location_id" ON "playthrough_floors" ("location_id"); +CREATE UNIQUE INDEX "index_playthrough_floors_on_playthrough_and_location" ON "playthrough_floors" ("playthrough_id", "location_id"); +CREATE INDEX "index_playthrough_floors_on_playthrough_id" ON "playthrough_floors" ("playthrough_id"); +CREATE INDEX "index_playthrough_floors_on_scene_id" ON "playthrough_floors" ("scene_id"); +CREATE INDEX "index_playthrough_npc_states_on_character_id" ON "playthrough_npc_states" ("character_id"); +CREATE INDEX "index_playthrough_npc_states_on_location_id" ON "playthrough_npc_states" ("location_id"); +CREATE UNIQUE INDEX "idx_on_playthrough_id_character_id_008ae03862" ON "playthrough_npc_states" ("playthrough_id", "character_id"); +CREATE INDEX "index_playthrough_npc_states_on_playthrough_id" ON "playthrough_npc_states" ("playthrough_id"); +CREATE INDEX "index_playthrough_overreaches_on_action" ON "playthrough_overreaches" ("action"); +CREATE INDEX "index_playthrough_overreaches_on_location_id" ON "playthrough_overreaches" ("location_id"); +CREATE INDEX "idx_on_playthrough_id_story_timestamp_b54cd36315" ON "playthrough_overreaches" ("playthrough_id", "story_timestamp"); +CREATE INDEX "index_playthrough_overreaches_on_playthrough_id" ON "playthrough_overreaches" ("playthrough_id"); +CREATE INDEX "index_playthrough_overreaches_on_scene_id" ON "playthrough_overreaches" ("scene_id"); +CREATE INDEX "index_playthrough_paragraphs_on_playthrough_id" ON "playthrough_paragraphs" ("playthrough_id"); +CREATE UNIQUE INDEX "index_playthrough_paragraphs_on_scene_id_and_author" ON "playthrough_paragraphs" ("scene_id", "author"); +CREATE INDEX "index_playthrough_passages_on_location_connection_id" ON "playthrough_passages" ("location_connection_id"); +CREATE INDEX "index_playthrough_passages_on_opened_by_item_id" ON "playthrough_passages" ("opened_by_item_id"); +CREATE UNIQUE INDEX "idx_on_playthrough_id_location_connection_id_6d3a29d910" ON "playthrough_passages" ("playthrough_id", "location_connection_id"); +CREATE INDEX "index_playthrough_passages_on_playthrough_id" ON "playthrough_passages" ("playthrough_id"); +CREATE INDEX "index_playthrough_tolls_on_character_id" ON "playthrough_tolls" ("character_id"); +CREATE INDEX "index_playthrough_tolls_on_location_connection_id" ON "playthrough_tolls" ("location_connection_id"); +CREATE INDEX "index_playthrough_tolls_on_location_id" ON "playthrough_tolls" ("location_id"); +CREATE INDEX "index_playthrough_tolls_on_playthrough_and_scene" ON "playthrough_tolls" ("playthrough_id", "scene_id", "id"); +CREATE INDEX "index_playthrough_tolls_on_playthrough_id" ON "playthrough_tolls" ("playthrough_id"); +CREATE INDEX "index_playthrough_tolls_on_scene_id" ON "playthrough_tolls" ("scene_id"); +CREATE UNIQUE INDEX "index_playthrough_turn_events_on_command_and_sequence" ON "playthrough_turn_events" ("playthrough_command_id", "sequence"); +CREATE INDEX "index_playthrough_turn_events_on_playthrough_command_id" ON "playthrough_turn_events" ("playthrough_command_id"); +CREATE INDEX "index_playthrough_vitals_on_character_id" ON "playthrough_vitals" ("character_id"); +CREATE UNIQUE INDEX "index_playthrough_vitals_on_playthrough_and_character" ON "playthrough_vitals" ("playthrough_id", "character_id"); +CREATE INDEX "index_playthrough_vitals_on_playthrough_id" ON "playthrough_vitals" ("playthrough_id"); +CREATE INDEX "index_playthrough_volitions_on_character_id" ON "playthrough_volitions" ("character_id"); +CREATE INDEX "index_playthrough_volitions_on_location_id" ON "playthrough_volitions" ("location_id"); +CREATE INDEX "index_playthrough_volitions_on_playthrough_and_scene" ON "playthrough_volitions" ("playthrough_id", "scene_id", "id"); +CREATE INDEX "index_playthrough_volitions_on_playthrough_id" ON "playthrough_volitions" ("playthrough_id"); +CREATE INDEX "index_playthrough_volitions_on_scene_id" ON "playthrough_volitions" ("scene_id"); +CREATE INDEX "index_playthroughs_on_character_id" ON "playthroughs" ("character_id"); +CREATE INDEX "index_playthroughs_on_current_location_id" ON "playthroughs" ("current_location_id"); +CREATE INDEX "index_playthroughs_on_current_scene_id" ON "playthroughs" ("current_scene_id"); +CREATE INDEX "index_playthroughs_on_player_id" ON "playthroughs" ("player_id"); +CREATE INDEX "index_playthroughs_on_story_id" ON "playthroughs" ("story_id"); +CREATE UNIQUE INDEX "index_playthroughs_on_token" ON "playthroughs" ("token"); +CREATE INDEX "index_quest_outcomes_on_character_id" ON "quest_outcomes" ("character_id"); +CREATE UNIQUE INDEX "index_quest_outcomes_on_quest_id_and_name" ON "quest_outcomes" ("quest_id", "name"); +CREATE INDEX "index_quest_outcomes_on_quest_id" ON "quest_outcomes" ("quest_id"); +CREATE INDEX "index_quest_steps_on_companion_id" ON "quest_steps" ("companion_id"); +CREATE UNIQUE INDEX "index_quest_steps_on_quest_id_and_position" ON "quest_steps" ("quest_id", "position"); +CREATE INDEX "index_quest_steps_on_quest_id" ON "quest_steps" ("quest_id"); +CREATE INDEX "index_quest_steps_on_target" ON "quest_steps" ("target_type", "target_id"); +CREATE INDEX "index_quests_on_parent_quest_id" ON "quests" ("parent_quest_id"); +CREATE UNIQUE INDEX "index_quests_on_story_id_and_title" ON "quests" ("story_id", "title"); +CREATE INDEX "index_quests_on_story_id" ON "quests" ("story_id"); +CREATE UNIQUE INDEX "index_races_on_universe_id_and_name" ON "races" ("universe_id", "name"); +CREATE INDEX "index_races_on_universe_id" ON "races" ("universe_id"); +CREATE INDEX "index_relay_receipts_on_player_id_and_created_at" ON "relay_receipts" ("player_id", "created_at"); +CREATE INDEX "index_relay_receipts_on_player_id_and_status" ON "relay_receipts" ("player_id", "status"); +CREATE INDEX "index_relay_receipts_on_player_id" ON "relay_receipts" ("player_id"); +CREATE INDEX "index_scenes_on_acted_on" ON "scenes" ("acted_on_type", "acted_on_id"); +CREATE INDEX "index_scenes_on_location_id" ON "scenes" ("location_id"); +CREATE INDEX "index_scenes_on_previous_scene_id" ON "scenes" ("previous_scene_id"); +CREATE INDEX "index_scenes_on_story_id_and_is_opening" ON "scenes" ("story_id", "is_opening"); +CREATE INDEX "index_scenes_on_story_id_and_resolved_action" ON "scenes" ("story_id", "resolved_action"); +CREATE INDEX "index_scenes_on_story_id_and_story_timestamp" ON "scenes" ("story_id", "story_timestamp"); +CREATE INDEX "index_scenes_on_story_id" ON "scenes" ("story_id"); +CREATE INDEX "index_stories_on_universe_id" ON "stories" ("universe_id"); +CREATE INDEX "index_system_one_receipts_on_player_id_and_created_at" ON "system_one_receipts" ("player_id", "created_at"); +CREATE INDEX "index_system_one_receipts_on_player_id" ON "system_one_receipts" ("player_id"); +CREATE INDEX "index_system_one_receipts_on_playthrough_id" ON "system_one_receipts" ("playthrough_id"); +CREATE INDEX "index_world_events_on_playthrough_id" ON "world_events" ("playthrough_id"); +CREATE INDEX "index_world_events_on_story_id_and_occurred_at" ON "world_events" ("story_id", "occurred_at"); +CREATE INDEX "index_world_events_on_story_id_and_scheduled_for" ON "world_events" ("story_id", "scheduled_for"); +CREATE INDEX "index_world_events_on_story_id" ON "world_events" ("story_id"); +CREATE INDEX "index_world_events_on_world_mechanic_id" ON "world_events" ("world_mechanic_id"); +CREATE UNIQUE INDEX "index_world_mechanics_on_story_id_and_name" ON "world_mechanics" ("story_id", "name"); +CREATE INDEX "index_world_mechanics_on_story_id" ON "world_mechanics" ("story_id"); +PRAGMA writable_schema=OFF; +COMMIT; diff --git a/parity/worlds/a-friend-at-the-gate.sql b/parity/worlds/a-friend-at-the-gate.sql index 37e1d86..3b381a4 100644 --- a/parity/worlds/a-friend-at-the-gate.sql +++ b/parity/worlds/a-friend-at-the-gate.sql @@ -412,10 +412,11 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-27 12:10:16.651328','2026-10-27 12:10:16.651334'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','deec051e019741f5fc4e8fe09fff00a4e1d2f04d','2026-10-27 12:10:16.662468','2026-10-27 12:10:16.662473'); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -437,7 +438,7 @@ FOREIGN KEY ("posted_to_id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,1000000001,'Cal','Cal',30,'male','Patient and practical.','A travel-worn coat.','A fair bargain.','Needless arguments.','Losing a friend.','A trader returning to the market.',0,'2026-10-27 12:13:21.151410','2026-10-27 12:13:21.151410',1000000001,1,NULL,0,10,8,12,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,1000000001,'Maren','Maren',32,'female','Willing to listen and keep a promise.','A blue coat and a wooden staff.','Clear agreements.','Deception.','Betraying a trust.','A gatekeeper who knows the old way through the courtyard.',0,'2026-10-27 12:13:21.207835','2026-10-27 12:13:21.207835',1000000001,0,1000000001,0,10,8,12,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-guarded-stair.sql b/parity/worlds/a-guarded-stair.sql index 62d4f83..f0eae2a 100644 --- a/parity/worlds/a-guarded-stair.sql +++ b/parity/worlds/a-guarded-stair.sql @@ -42,15 +42,15 @@ FOREIGN KEY ("player_id") FOREIGN KEY ("ruby_llm_model_id") REFERENCES "ruby_llm_models" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_8a0268155a" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_8a0268155a" FOREIGN KEY ("down_location_id") REFERENCES "locations" ("id") ON DELETE SET NULL, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE); -INSERT INTO floors VALUES(1000000001,1000000002,3,'uneasy','none',1000000003,'2026-10-30 13:44:45.087077','2026-10-30 13:44:45.144689',NULL,0); -INSERT INTO floors VALUES(1000000002,1000000003,2,'dangerous','unlit',NULL,'2026-10-30 13:44:45.103852','2026-10-30 13:44:45.149215',NULL,0); +INSERT INTO floors VALUES(1000000001,1000000002,3,'uneasy','none',1000000003,'2026-10-30 13:44:45.087077','2026-10-30 13:44:45.144689',NULL,0,NULL); +INSERT INTO floors VALUES(1000000002,1000000003,2,'dangerous','unlit',NULL,'2026-10-30 13:44:45.103852','2026-10-30 13:44:45.149215',NULL,0,NULL); CREATE TABLE IF NOT EXISTS "interactions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "character_id" integer NOT NULL, "scene_id" integer, "location_id" integer, "pre_thought" text, "pre_feeling" text, "action" text, "post_feeling" text, "post_thought" text, "inner_resolution" text, "summary" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "user_input" text, "engine_action" varchar, "action_status" varchar, "action_fact" text, CONSTRAINT "fk_rails_294d4ae9f8" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") @@ -434,10 +434,11 @@ INSERT INTO schema_migrations VALUES('20250822184651'); INSERT INTO schema_migrations VALUES('20250822184650'); INSERT INTO schema_migrations VALUES('20250822184649'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-30 13:44:43.769467','2026-10-30 13:44:43.769470'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','997d2947cb01ed87e8df6e9bddf8aab22ba21a27','2026-10-30 13:44:43.773358','2026-10-30 13:44:43.773359'); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-key-for-the-other-door.sql b/parity/worlds/a-key-for-the-other-door.sql index 908fe45..6895d8b 100644 --- a/parity/worlds/a-key-for-the-other-door.sql +++ b/parity/worlds/a-key-for-the-other-door.sql @@ -405,6 +405,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -415,7 +416,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -436,7 +437,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,30,'A travel-worn coat.','A trader looking for stores.',NULL,'2026-10-02 13:25:54.417562',0,NULL,12,'Waste.','Being shut in.','Mar',8,0,0,1,3,'Well-kept tools.',NULL,NULL,'Mar','Patient and practical.',1000000001,NULL,'female',1000000001,10,NULL,NULL,'2026-10-02 13:25:54.417562',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-lock-in-the-moving-city.sql b/parity/worlds/a-lock-in-the-moving-city.sql index 0db2e31..fa27d6e 100644 --- a/parity/worlds/a-lock-in-the-moving-city.sql +++ b/parity/worlds/a-lock-in-the-moving-city.sql @@ -404,6 +404,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -414,7 +415,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -435,7 +436,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,30,'A travel-worn coat.','A trader returning for supplies.',NULL,'2026-10-02 13:25:59.775380',0,NULL,12,'Waste.','Losing needed supplies.','Cal',8,0,0,1,3,'Well-kept tools.',NULL,NULL,'Cal','Patient and practical.',1000000001,NULL,'male',1000000001,12,NULL,NULL,'2026-10-02 13:25:59.775380',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-lodge-inside-the-gate.sql b/parity/worlds/a-lodge-inside-the-gate.sql index 89629dc..eb991ee 100644 --- a/parity/worlds/a-lodge-inside-the-gate.sql +++ b/parity/worlds/a-lodge-inside-the-gate.sql @@ -404,6 +404,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -414,7 +415,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -438,7 +439,7 @@ INSERT INTO characters VALUES(1000000001,31,'A drover''s cloak and mud to the kn INSERT INTO characters VALUES(1000000002,44,'A porter''s sash and a ring of lodge keys.','The head porter, who has met every traveller through this gate for twenty years.','Mabry wants to see every traveller in and settled before they reach the market.','2026-10-02 13:25:50.949246',0,'attend',12,'A cold lodge.','Letting the wrong person through.','Mabry Quill',8,0,0,0,10,'A traveller with news.',1000000002,'keep','Mabry','Warm and nosy.',1000000001,'Mabry needs to keep the lodge book.','female',1000000001,12,'Mabry wants to be thanked.','Mabry needs to let someone pass without questions.','2026-10-02 13:25:50.949246',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000003,23,'A sash too big for him and ink on one thumb.','The under-porter, who took the post to be left alone and has not been.','Osric wants to be anywhere but the lodge when a traveller comes in.','2026-10-02 13:25:50.954473',0,'avoid',12,'Being looked at.','Being asked a question he cannot answer.','Osric Venn',8,0,0,0,10,'The back stair when nobody is on it.',1000000002,'withhold','Osric','Shy and restless.',1000000001,'Osric needs to answer the door.','male',1000000001,12,'Osric wants to be good at the post.','Osric needs to stay in the room.','2026-10-02 13:25:50.954473',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000004,57,'A keeper''s coat and an iron-shod staff.','The toll keeper, who has decided every traveller is trying to cheat the bar.','Grist wants the booth to himself and every token in his box.','2026-10-02 13:25:50.969712',0,'keep',12,'Travellers who come into the booth.','Being short at the count.','Grist Harrow',8,1,0,0,10,'A full toll box.',1000000003,'withhold','Grist','Suspicious and quick with a staff.',1000000001,'Grist needs to keep the count true.','male',1000000001,12,'Grist wants to be trusted with the gate.','Grist needs to let one traveller through for nothing.','2026-10-02 13:25:50.969712',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-turn-at-the-gate.sql b/parity/worlds/a-turn-at-the-gate.sql index 974bd00..f7d9fc5 100644 --- a/parity/worlds/a-turn-at-the-gate.sql +++ b/parity/worlds/a-turn-at-the-gate.sql @@ -402,6 +402,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -412,7 +413,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -434,7 +435,7 @@ FOREIGN KEY ("posted_to_id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,30,'A travel-worn coat.','A trader returning to the market.',NULL,'2026-10-02 13:26:08.098372',0,NULL,12,'Needless arguments.','Losing a friend.','Cal',8,0,0,1,10,'A fair bargain.',NULL,NULL,'Cal','Patient and practical.',1000000001,NULL,'male',1000000001,12,NULL,NULL,'2026-10-02 13:26:08.098372',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,32,'A blue coat and a wooden staff.','A gatekeeper who misunderstood Cal''s arrival.',NULL,'2026-10-02 13:26:08.139887',0,NULL,12,'Deception.','Betraying a trust.','Maren',8,1,0,0,10,'Clear agreements.',1000000001,NULL,'Maren','Willing to listen and keep a promise.',1000000001,NULL,'female',1000000001,12,NULL,NULL,'2026-10-02 13:26:08.139887',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-window-over-the-yard.sql b/parity/worlds/a-window-over-the-yard.sql index f68cf4f..02e67c6 100644 --- a/parity/worlds/a-window-over-the-yard.sql +++ b/parity/worlds/a-window-over-the-yard.sql @@ -405,6 +405,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -415,7 +416,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -436,7 +437,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,29,'A work coat two sizes too big.','Runs the farm since her father''s knees gave out, and keeps the accounts in her head.',NULL,'2026-10-02 13:25:52.726724',0,NULL,11,'A door bolted from the wrong side','The farm going to her cousin','Wenna Halloway',8,0,0,1,3,'A job finished before dark',1000000001,NULL,'Wenna','Practical, and slow to be frightened.',1000000001,NULL,'female',1000000001,11,NULL,NULL,'2026-10-02 13:25:52.726724',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-writ-on-the-copying-desk.sql b/parity/worlds/a-writ-on-the-copying-desk.sql index 72f929e..e5c750c 100644 --- a/parity/worlds/a-writ-on-the-copying-desk.sql +++ b/parity/worlds/a-writ-on-the-copying-desk.sql @@ -414,10 +414,11 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-26 02:09:45.289336','2026-10-26 02:09:45.289340'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','9fcff787fff0ed60123ee84a65c7f80738f353f3','2026-10-26 02:09:45.295415','2026-10-26 02:09:45.295421'); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -438,7 +439,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,29,'Ink on both cuffs.','A copyist who has been asked to run an errand instead of copying.',NULL,'2026-10-26 02:10:11.603319',0,NULL,12,'Errands.','Losing a deed.','Tamsin Rook',8,0,0,1,3,'A clean page.',NULL,NULL,'Tamsin','Careful, and tired of being careful.',1000000001,NULL,'female',1000000001,15,NULL,NULL,'2026-10-26 02:10:11.603319',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-written-mill-beside-a-stub.sql b/parity/worlds/a-written-mill-beside-a-stub.sql index 5227249..2d346b1 100644 --- a/parity/worlds/a-written-mill-beside-a-stub.sql +++ b/parity/worlds/a-written-mill-beside-a-stub.sql @@ -399,6 +399,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -409,7 +410,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -430,7 +431,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,30,'A travel-worn coat.','A trader returning to the market.',NULL,'2026-10-02 13:26:04.567073',0,NULL,12,'Needless arguments.','Losing a friend.','Cal',8,0,0,1,10,'A fair bargain.',NULL,NULL,'Cal','Patient and practical.',1000000001,NULL,'male',1000000001,12,NULL,NULL,'2026-10-02 13:26:04.567073',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/a-yard-before-the-winter.sql b/parity/worlds/a-yard-before-the-winter.sql index 2dbb204..f1cc3bf 100644 --- a/parity/worlds/a-yard-before-the-winter.sql +++ b/parity/worlds/a-yard-before-the-winter.sql @@ -413,6 +413,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -423,7 +424,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -444,7 +445,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,34,'A salt-stained coat.','A salt-carter with no road to cart on.',NULL,'2026-10-02 13:25:41.524363',0,NULL,11,'Waiting.','A hungry winter.','Tamsin',8,0,0,1,4,'An open road.',NULL,NULL,'Tamsin','Blunt and tireless.',1000000001,NULL,'female',1000000001,13,NULL,NULL,'2026-10-02 13:25:41.524363',10,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-cloakroom-at-the-ferry.sql b/parity/worlds/the-cloakroom-at-the-ferry.sql index b3cf1ed..dc995f0 100644 --- a/parity/worlds/the-cloakroom-at-the-ferry.sql +++ b/parity/worlds/the-cloakroom-at-the-ferry.sql @@ -412,10 +412,11 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-25 22:48:20.183741','2026-10-25 22:48:20.183743'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','1ce6b3b7eea61111c69aac54baf282539c772990','2026-10-25 22:48:20.187552','2026-10-25 22:48:20.187553'); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -437,7 +438,7 @@ FOREIGN KEY ("posted_to_id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,31,'Wind-reddened cheeks and ink on her fingers.','A surveyor waiting on the morning crossing.',NULL,'2026-10-25 22:48:32.982956',0,NULL,12,'Waiting.','A missed crossing.','Wren Calloway',8,0,0,1,3,'A dry bench.',NULL,NULL,'Wren','Patient and cold.',1000000001,NULL,'female',1000000001,12,NULL,NULL,'2026-10-25 22:48:32.982956',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,62,'A white beard and a tarred cap.','The ferryman, who has worked the chain for forty years.',NULL,'2026-10-25 22:48:33.060368',0,NULL,8,'Passengers who talk.','The chain parting.','Abel Moss',6,0,0,0,1,'A slack tide.',1000000001,NULL,'Abel','Gruff and slow.',1000000001,NULL,'male',1000000001,8,NULL,NULL,'2026-10-25 22:48:33.060368',10,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-furnished-rooms.sql b/parity/worlds/the-furnished-rooms.sql index cb27269..a04a0e1 100644 --- a/parity/worlds/the-furnished-rooms.sql +++ b/parity/worlds/the-furnished-rooms.sql @@ -414,6 +414,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -424,7 +425,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -445,7 +446,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,34,'An ink-stained coat.','A ward auditor sent to look over the clerk''s rooms.',NULL,'2026-10-02 13:25:45.301225',0,NULL,12,'Loose paper.','A lost record.','Mira Vell',8,0,0,1,3,'A tidy ledger.',NULL,NULL,'Mira','Careful and dry.',1000000001,NULL,'female',1000000001,10,NULL,NULL,'2026-10-02 13:25:45.301225',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-green-at-saltmarsh.sql b/parity/worlds/the-green-at-saltmarsh.sql index 6d3877f..ad22c66 100644 --- a/parity/worlds/the-green-at-saltmarsh.sql +++ b/parity/worlds/the-green-at-saltmarsh.sql @@ -404,6 +404,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -414,7 +415,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -437,7 +438,7 @@ FOREIGN KEY ("posted_to_id") INSERT INTO characters VALUES(1000000001,27,'Tar to the wrists, and a rope burn across one palm.','Walks the ropewalk twice a day, and can tell a bad strand by the feel of it.',NULL,'2026-10-02 13:26:03.006411',0,NULL,11,'Being hurried on a long lay','The walk closing for good','Jory Pask',8,0,0,1,2,'A cable laid without a kink',NULL,NULL,'Jory','Steady, and slow to say what he has noticed.',1000000001,NULL,'male',1000000001,18,NULL,NULL,'2026-10-02 13:26:03.006411',10,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,61,'Bent at the shoulders, with a net across his knees.','Laid rope on the walk for forty years, and mends nets on the green now.',NULL,'2026-10-02 13:26:03.102878',0,NULL,12,'New rope','Being asked to walk the ropewalk again','Wat Coyle',6,0,0,0,1,'A fine day for mending',1000000001,NULL,'Wat','Talkative, and certain the old ways were better.',1000000001,NULL,'male',1000000001,11,NULL,NULL,'2026-10-02 13:26:03.102878',10,10,27,NULL); INSERT INTO characters VALUES(1000000003,38,'Ink on one cuff, tar on the other.','Keeps the ropewalk''s book, and signs for every fathom that leaves it.',NULL,'2026-10-02 13:26:03.111748',0,NULL,13,'Rope sold off the book','The parted cable being traced to her signature','Hester Vane',6,0,0,0,1,'A book that balances',1000000002,NULL,'Hester','Exact, and short with anybody who is not.',1000000001,NULL,'female',1000000001,10,NULL,NULL,'2026-10-02 13:26:03.111748',12,18,1,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-iron-gate-descends.sql b/parity/worlds/the-iron-gate-descends.sql index d0a6450..a5f886c 100644 --- a/parity/worlds/the-iron-gate-descends.sql +++ b/parity/worlds/the-iron-gate-descends.sql @@ -463,6 +463,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -473,7 +474,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -497,7 +498,7 @@ INSERT INTO characters VALUES(1000000001,107,'Towering over most, Veythra’s gr INSERT INTO characters VALUES(1000000002,54,'A tall Trollkin woman with weathered, grayish skin and deep-set, amber eyes. She wears a patched cloak of stitched hides and carries a notched axe at her belt. Her hair, streaked with white, is tied back in a loose braid.','Gorva was once part of a nomadic band, but age and injury left her behind. She now drifts between towns like Hollow’s End, trading odd jobs for coin or shelter. She knows the paths through the black pines better than most, and she has a grudging respect for the Iron Council’s reach.',NULL,'2026-10-02 13:26:06.347579',0,NULL,14,'fools, the Church, wasted time','fire, deep water','Gorva the Wanderer',6,0,0,0,1,'quiet, strong drink, sharp blades',1000000007,NULL,'Gorva','Gorva speaks little but watches everything, her voice a low rumble when she does. She treats strangers with cautious curiosity, though her patience wears thin quickly.',1000000003,NULL,'female',1000000001,10,NULL,NULL,'2026-10-02 13:26:06.347579',6,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000003,41,'A Veythi woman with dark, braided hair streaked with silver, her sharp features framed by a high-collared coat of river-worn leather. Her hands are calloused from years of hauling nets and ropes, and her eyes flicker with the restlessness of someone used to the water.','Lyssa was born on the barges of the Veythi but left the river life after a falling-out with her kin. Now she trades in Hollow’s End, smuggling goods between the town and the Rust Market hidden in the river’s mist. She knows the Iron Council’s patrols and the goblin raids better than anyone.',NULL,'2026-10-02 13:26:06.351914',0,NULL,7,'the Church, betrayal, cold nights','being trapped, the Spire’s shadow','Lyssa of the Mist',10,0,0,0,1,'bargaining, river stories, fine cloth',1000000007,NULL,'Lyssa','Lyssa is quick with a joke and quicker with a deal, her voice smooth as the river’s current. She sizes up strangers with a practiced eye, always weighing what they might be worth.',1000000004,NULL,'trans woman',1000000001,10,NULL,NULL,'2026-10-02 13:26:06.351914',9,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000004,19,'A tall Durnish youth gone gaunt, his ashen skin greyer still for a season without sun, and his dark hair cropped to the scalp where the goblins took it for their own uses. What is left of a doublet of Council blue hangs off him, rotted at the seams and stiff with old blood at the collar. His wrists carry two bright rings of scar from the chain, and the third finger of his left hand is scarred around the base where the signet was pulled over the knuckle. His eyes have adjusted to the fungi’s glow and water in any brighter light.','Aurel Durn is the last living son of the Iron Council’s ruling line, and until the spring he had never been further from the Obsidian Throne than the smelters at the Spire’s foot. He was taken on the north road to Hollow’s End with eleven men-at-arms around him, none of whom outlived the ambush; the Blackfang came out of the black pines without a sound, which is how the clan hunts, and cut the horses first. Somewhere outside the iron gate they wrenched the signet from his left hand, and he understood, watching it go into the mud, that they meant to be believed rather than paid. He has been in the dark since. The clan keeps him whole and keeps him fed on fungus-mash because a corpse buys nothing, and they have told him plainly what he is worth: the sacred obsidian the Durnish took in the War of the Spire, weighed out and returned. He counts the dimming of the fungi to keep the days, and he has counted enough of them to know the arithmetic the Church will do. Rescue is not what he expects. Being burned with the caves is.',NULL,'2026-10-02 13:26:06.357747',0,NULL,10,'fungus-mash, the Church’s arithmetic, being spoken to as a price, pity, torchlight held close to his face','the Council burning the caves with him inside them, the Silent Maw’s hunger, losing his count of the hours, hearing his own name used as a sum','Prince Aurel Durn',6,0,0,0,1,'the twelve-hour dimming of the fungi, which is his only clock, clean water, the sound of iron being worked, his mother’s salt-and-iron psalm, being told the truth about numbers',1000000014,NULL,'Aurel','Aurel is courteous in a way that has stopped costing him anything, and he has learned the goblin habit of silence well enough to make Durnish visitors uneasy. He listens first, at length, before he says a word, and what he says is usually a price or a warning. He does not beg and he does not thank; he bargains, even from the floor of a cell, because bargaining is the only office left to him and he was raised to hold one.',1000000001,NULL,'male',1000000001,8,NULL,NULL,'2026-10-02 13:26:06.357747',16,10,7,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-landing-stair.sql b/parity/worlds/the-landing-stair.sql index 493399c..20c7160 100644 --- a/parity/worlds/the-landing-stair.sql +++ b/parity/worlds/the-landing-stair.sql @@ -409,6 +409,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -419,7 +420,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -440,7 +441,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,38,'Salt at the cuff, whatever she is wearing.','Kept the quay''s books for eleven years without once being asked to.',NULL,'2026-10-02 13:26:13.280276',0,NULL,13,'A door found open','Being the last one to know','Nell Cawsand',8,0,0,1,3,'A tide that comes in on the table',1000000001,NULL,'Nell','Unhurried, and counts things twice.',1000000001,NULL,'female',1000000001,11,NULL,NULL,'2026-10-02 13:26:13.280276',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-lunar-cartographer.sql b/parity/worlds/the-lunar-cartographer.sql index 705c5bb..ded7fbb 100644 --- a/parity/worlds/the-lunar-cartographer.sql +++ b/parity/worlds/the-lunar-cartographer.sql @@ -435,10 +435,11 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-29 02:06:59.131845','2026-10-29 02:06:59.131848'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','9ea7d9a7496841168065535777683ed7c64300e0','2026-10-29 02:06:59.136646','2026-10-29 02:06:59.136649'); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -461,7 +462,7 @@ FOREIGN KEY ("posted_to_id") INSERT INTO characters VALUES(1000000001,1000000001,'Isbet Marrow','Iz',34,'female','Methodical to the point of rudeness. She would rather finish a bearing than finish a sentence, and she trusts a measurement she took herself over anything a Sovereign puts in writing. Dry, patient, slow to anger, and incapable of forgetting a debt in either direction.','Weathered dark skin, a nomad''s squint, and charcoal on the side of every finger. Her hair is cropped short and going grey at one temple only.','Fresh bearings, strong tea, the hour before the city moves','Sovereign paperwork, being told a street has always been there','The memory loss. She has started writing down what she did yesterday.','Born in a Scorch caravan that made its living selling bearings and salvaged road-signs to a city which rearranged itself out from under them every night. She walked into Nocturnis at nineteen with a stolen theodolite and a talent nobody in the caravan had a word for: she can look at a street and tell where it has been. The Lunar Sovereigns hired her four years ago to map the nightly rearrangements, pay her badly, and deny in public that the survey exists at all. She has been behind on rent for most of that time.',0,'2026-10-29 02:09:13.143141','2026-10-29 02:09:13.143141',1000000004,1,NULL,0,3,8,11,14,13,0,NULL,NULL,'Isbet Marrow wants to reach a complete map of the city''s nightly rearrangements.','Isbet Marrow wants to keep the one measurement that proves the Sovereigns are lying.','Isbet Marrow needs to keep her survey notes in order across every night.','Isbet Marrow needs to reach the memory of what she has begun to lose.','reach','keep',NULL); INSERT INTO characters VALUES(1000000002,1000000001,'Grenn Ollivar','Old Grenn',61,'male','Loud, aggrieved, and fundamentally decent under about two inches of grievance. He threatens eviction roughly weekly and has carried one out once in thirty years. He would very much like to be asked about the house.','Heavyset, pale even for a Sovereign, silver hair gone thin on top, and a cough he blames on the plaster dust. He carries a ledger under one arm at all hours, including the hours when nobody is awake to owe him anything.','His own foundations, being right about the plaster, a settled ledger','The Sovereign''s Circle, tenants who talk to the walls','That the house will crack after all, and that his family was right','A Sovereign family''s third son, bought out of his inheritance at thirty for a sum that covered exactly one boarding house in the Larkspur Quarter. He has spent the thirty years since laying his foundation stones loose so the building survives the shifting, and reminding anyone who will hold still that he owns it outright. He does not go up to the third floor after dark.',0,'2026-10-29 02:09:13.198081','2026-10-29 02:09:13.231377',1000000002,0,1000000001,0,1,6,9,7,12,0,0,3,'Grenn Ollivar wants to obtain the overdue rent before the Bell of Saint Aravel rings again.','Grenn Ollivar wants to reach someone who will ask him about the foundations.','Grenn Ollivar needs to keep his boarding-house ledger settled.','Grenn Ollivar needs to reach the third floor after dark and inspect his house.','obtain','reach',NULL); INSERT INTO characters VALUES(1000000003,1000000001,'Marek Sollen','the Ringer',44,'male','Nothing decides anything up there any more. What is left is habit and a terrible steadiness: he climbs, he waits, he pulls, and anything in the chamber that still answers to a name is something he goes for without hurrying and without stopping.','A big man gone grey all the way through, skin and coat and eyes the same lamp-lit nothing, standing straighter than a living man stands. His hands are still a ringer''s hands. He does not blink and he does not look away.','The hour. The rope. The weight coming back up the line','Anything that says a name out loud in his tower','Nothing at all, and that is the whole of what is wrong with him','He rang the eleven o''clock for twenty-nine years, which meant twenty-nine years of standing in the one tower the city cannot move while the rest of it went past underneath him. Nobody told him what that does. The parish stopped seeing him some while before it stopped hearing him, and by then he had already forgotten his sister''s name, then his own, then what the rope was for -- though he still pulls it, on the hour, more or less.',0,'2026-10-29 02:09:13.201303','2026-10-29 02:09:13.271925',1000000003,0,1000000007,0,1,10,14,8,3,1,3,7,'Marek Sollen wants to keep the bell ringing at the appointed hour.','Marek Sollen wants to avoid every name spoken in the tower.','Marek Sollen needs to attend the rope until the city hears its hour.','Marek Sollen needs to reach the person he was before the bell took his name.','keep','avoid',NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-pottery-on-mill-lane.sql b/parity/worlds/the-pottery-on-mill-lane.sql index 757b36b..7dce20e 100644 --- a/parity/worlds/the-pottery-on-mill-lane.sql +++ b/parity/worlds/the-pottery-on-mill-lane.sql @@ -413,6 +413,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -423,7 +424,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -444,7 +445,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,34,'Clay to the elbows, and an apron gone grey with it.','Throws every pot the pottery sells, and keeps the kiln book in her own hand.',NULL,'2026-10-02 13:26:01.444968',0,NULL,12,'Somebody touching the kiln without asking','The pottery going to her brother','Ada Hollin',8,0,0,1,3,'A clean firing',NULL,NULL,'Ada','Quick, and quicker to lose her temper with a cracked pot.',1000000001,NULL,'female',1000000001,16,NULL,NULL,'2026-10-02 13:26:01.444968',11,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-quay-house.sql b/parity/worlds/the-quay-house.sql index 14dc1c1..a4c40ff 100644 --- a/parity/worlds/the-quay-house.sql +++ b/parity/worlds/the-quay-house.sql @@ -448,6 +448,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -458,7 +459,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -479,7 +480,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,38,'Salt at the cuff, whatever she is wearing.','Kept the quay''s books for eleven years without once being asked to.',NULL,'2026-10-02 13:25:36.472221',0,NULL,13,'A door found open','Being the last one to know','Nell Cawsand',8,0,0,1,3,'A tide that comes in on the table',1000000001,NULL,'Nell','Unhurried, and counts things twice.',1000000001,NULL,'female',1000000001,11,NULL,NULL,'2026-10-02 13:25:36.472221',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-rook-wood-gate.sql b/parity/worlds/the-rook-wood-gate.sql index 3197101..bca348f 100644 --- a/parity/worlds/the-rook-wood-gate.sql +++ b/parity/worlds/the-rook-wood-gate.sql @@ -396,6 +396,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-15 21:43:40.193939','2026-10-15 21:43:40.193941'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','62d31b9fa38811bdb1f557cc2a6f6d523066e2aa','2026-10-15 21:43:40.198165','2026-10-15 21:43:40.198167'); @@ -406,7 +407,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -427,7 +428,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,29,'A canvas apron.','A ward porter sent out for kindling.',NULL,'2026-10-15 21:45:55.786953',0,NULL,11,'Wet boots.','The wood after dark.','Tamsin Rook',8,0,0,1,2,'A dry log.',NULL,NULL,'Tamsin','Patient and plain.',1000000001,NULL,'female',1000000001,10,NULL,NULL,'2026-10-15 21:45:55.786953',10,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-ropewalk-at-saltmarsh.sql b/parity/worlds/the-ropewalk-at-saltmarsh.sql index 95a27f6..3edf066 100644 --- a/parity/worlds/the-ropewalk-at-saltmarsh.sql +++ b/parity/worlds/the-ropewalk-at-saltmarsh.sql @@ -404,6 +404,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -414,7 +415,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -437,7 +438,7 @@ FOREIGN KEY ("posted_to_id") INSERT INTO characters VALUES(1000000001,27,'Tar to the wrists, and a rope burn across one palm.','Walks the ropewalk twice a day, and can tell a bad strand by the feel of it.',NULL,'2026-10-02 13:26:03.006411',0,NULL,11,'Being hurried on a long lay','The walk closing for good','Jory Pask',8,0,0,1,2,'A cable laid without a kink',NULL,NULL,'Jory','Steady, and slow to say what he has noticed.',1000000001,NULL,'male',1000000001,18,NULL,NULL,'2026-10-02 13:26:03.006411',10,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,61,'Bent at the shoulders, with a net across his knees.','Laid rope on the walk for forty years, and mends nets on the green now.',NULL,'2026-10-02 13:26:03.102878',0,NULL,12,'New rope','Being asked to walk the ropewalk again','Wat Coyle',6,0,0,0,1,'A fine day for mending',1000000001,NULL,'Wat','Talkative, and certain the old ways were better.',1000000001,NULL,'male',1000000001,11,NULL,NULL,'2026-10-02 13:26:03.102878',10,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000003,38,'Ink on one cuff, tar on the other.','Keeps the ropewalk''s book, and signs for every fathom that leaves it.',NULL,'2026-10-02 13:26:03.111748',0,NULL,13,'Rope sold off the book','The parted cable being traced to her signature','Hester Vane',6,0,0,0,1,'A book that balances',1000000002,NULL,'Hester','Exact, and short with anybody who is not.',1000000001,NULL,'female',1000000001,10,NULL,NULL,'2026-10-02 13:26:03.111748',12,18,1,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-salt-assizes.sql b/parity/worlds/the-salt-assizes.sql index 2975131..8c1629a 100644 --- a/parity/worlds/the-salt-assizes.sql +++ b/parity/worlds/the-salt-assizes.sql @@ -425,10 +425,11 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-29 02:06:59.131845','2026-10-29 02:06:59.131848'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','9ea7d9a7496841168065535777683ed7c64300e0','2026-10-29 02:06:59.136646','2026-10-29 02:06:59.136649'); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -451,7 +452,7 @@ FOREIGN KEY ("posted_to_id") INSERT INTO characters VALUES(1000000001,1000000001,'Coraith Vell','Vell',44,'female','Exact, literal and slow to speak, with the Keeper''s inability to give a figure she is not sure of. She does not argue; she states the water and lets people arrange themselves around it. Under pressure she gets quieter and more precise, which people mistake for calm and is closer to fury.','Tall, salt-weathered a decade early, with cropped grey hair and a permanent squint to seaward. Deaf in the left ear, which faces the bell. Chalk is worn into the creases of her right hand so deeply that it does not wash out.','A slate that agrees with the water, the ten minutes before a turn, the sound of a court rising on time','A figure given without a source, being asked to round, anyone standing between her and the flats','Ringing a court up and finding afterwards that the water had not turned','Sworn Bell-Keeper at seven, as her mother and her mother''s mother were, and keeper of this causeway alone since she was twenty-five. She has rung nineteen years of Assizes up and down and has ended two hearings early, one of which cost a man his acquittal and is the only thing she has ever apologised for. She writes the slate herself every night, in a still room, on a dry board, and has never had cause to check it in the morning.',0,'2026-10-29 02:09:15.196954','2026-10-29 02:09:15.196954',1000000001,1,NULL,0,3,8,13,10,14,0,NULL,NULL,'Coraith Vell wants to keep the tide-slate true through the day''s high water.','Coraith Vell wants to reach the source of the hand that altered her figure.','Coraith Vell needs to attend the bell and ring the court on time.','Coraith Vell needs to reach the flats and measure the water herself.','keep','reach',NULL); INSERT INTO characters VALUES(1000000002,1000000001,'Ammon Brace','Justicar Brace',58,'male','Courteous, unhurried, genuinely fair-minded within the law and entirely unable to see outside it. He will hear anybody out. He wants the hearing to happen because a hearing not held is a man held without one, and he will not notice that the fastest way to have it is to trust a number nobody sourced.','Neat, dry and out of place: gown, bands, town shoes ruined by the causeway, and a face that has been polite in bad weather for thirty years. He keeps his hands folded because they are cold.','A court that sits when it says it will, a witness who answers the question, being given a figure','Adjournments, the smell of the flats, being asked to wait without a reason','Signing off on a delay that turns out to have been a sentence','Appointed at thirty-one and moved every season since, through forty parishes he could recite and not one he could find a friend in. He has heard eleven wrecking cases on this coast and returned nine convictions, and he is fairly sure that is the correct proportion. He has never in his life known when the water would turn, and he has spent thirty years being told.',0,'2026-10-29 02:09:15.273680','2026-10-29 02:09:15.330754',1000000002,0,1000000001,0,1,6,8,9,16,0,0,3,'Ammon Brace wants to reach the hearing before the tide closes the causeway.','Ammon Brace wants to obtain certainty that the law has not failed the accused.','Ammon Brace needs to attend every witness until the hearing is complete.','Ammon Brace needs to reach a judgment that accounts for the water beyond the law.','reach','attend',NULL); INSERT INTO characters VALUES(1000000003,1000000001,'Neb Halloran','Neb',33,'male','Loud, funny and shrewd, and getting quieter the higher the water gets. He bargains out of habit even when he has nothing, and he tells the truth in fragments, testing what each one buys before he parts with the next.','Broad, sunburnt over salt-burn, with a wrecker''s hands and a rope-scar around the left wrist that predates the chain by ten years. Shirt, no coat; they took the coat.','A tide that comes in slow, being called by his name, an argument he can win','Being counted with the marsh, questions about his cousins, the sound of the bell','Drowning at the post while the court is still deciding whether to hear him','Third-generation salvager out of the shore villages, chained at the tide post since the ebb for the wrecking of the *Marianne Dowe* -- a job everybody on this coast agrees took six men and which only he has been charged with. He knows all six names. He has said none of them, because five of them are his cousins and the sixth pays for his mother''s roof.',0,'2026-10-29 02:09:15.295749','2026-10-29 02:09:15.351873',1000000004,0,1000000002,0,1,10,15,13,9,0,3,3,'Neb Halloran wants to obtain a hearing before the water reaches the tide post.','Neb Halloran wants to avoid naming the five cousins who paid for his mother''s roof.','Neb Halloran needs to keep his mother''s roof secure through the trial.','Neb Halloran needs to reach the truth without leaving his cousins to drown in his place.','obtain','avoid',NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-unfinished-workshop.sql b/parity/worlds/the-unfinished-workshop.sql index dd6b845..e36af92 100644 --- a/parity/worlds/the-unfinished-workshop.sql +++ b/parity/worlds/the-unfinished-workshop.sql @@ -396,6 +396,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -406,7 +407,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -427,7 +428,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,30,'A travel-worn coat.','A trader returning to the market.',NULL,'2026-10-02 13:25:34.627193',0,NULL,12,'Needless arguments.','Losing a friend.','Cal',8,0,0,1,10,'A fair bargain.',NULL,NULL,'Cal','Patient and practical.',1000000001,NULL,'male',1000000001,12,NULL,NULL,'2026-10-02 13:25:34.627193',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-unrecorded-hour.sql b/parity/worlds/the-unrecorded-hour.sql index dfbe59d..78e872b 100644 --- a/parity/worlds/the-unrecorded-hour.sql +++ b/parity/worlds/the-unrecorded-hour.sql @@ -427,10 +427,11 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-29 02:06:59.131845','2026-10-29 02:06:59.131848'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','9ea7d9a7496841168065535777683ed7c64300e0','2026-10-29 02:06:59.136646','2026-10-29 02:06:59.136649'); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -453,7 +454,7 @@ FOREIGN KEY ("posted_to_id") INSERT INTO characters VALUES(1000000001,1000000001,'Odile Vance','Vance',41,'female','Precise, dry, and unhurried in a way that reads as calm and is actually stubbornness. She does not raise her voice or her hopes. She will not sign something she has not read, which has cost her every promotion she was ever considered for, and she considers this a fair trade she would make again.','Narrow, upright, greying at the front, with ink worn permanently into the first two fingers of her right hand.','A ledger that balances, the smell of a fresh cylinder of wax, rain on the window','Amendments in another hand, being told a query is still open','Signing her name to something that turns out to have closed a file','Fourth-generation Copyists'' Line, apprenticed at eleven, posted to Ward Office 12 at thirty and never moved because she asked not to be. She is the clerk other clerks send a queried entry to. Twice she has found an amendment in an incoming ledger that should not have been possible and twice she has filed a query about it and heard nothing back, which she has told herself is the system working slowly and has stopped believing.',0,'2026-10-29 02:09:17.181556','2026-10-29 02:09:17.181556',1000000001,1,NULL,0,3,8,9,11,15,0,NULL,NULL,'Odile Vance wants to reconcile the missing hour in Ward Office 12''s daybook.','Odile Vance wants to obtain the truth behind the impossible amendment.','Odile Vance needs to keep every query open until its source is recorded.','Odile Vance needs to reach the sealed registry and read what it contains.','keep','obtain',NULL); INSERT INTO characters VALUES(1000000002,1000000001,'Perrin Lasco','Perrin',29,'male','Quick, funny, and careful in a way people mistake for nerves. He asks one question too many and then apologizes for it. Being forgettable has taught him to be useful fast, and to write everything down twice.','Slight, dark-haired, with a face that will not stay in your memory for an hour after he leaves the room -- an effect people find restful and then cannot account for. He wears a copy-room apron over ordinary clothes and keeps a second pencil behind his ear he never uses.','An index that catches something, other people''s strong tea, the last hour of a shift','Being asked to repeat his name, doors that lock from one side','Being closed quietly, on a Tuesday, with nobody able to say what was lost','Third generation Marginalia; his grandmother''s file was closed in the Great Amendment and the damage came down the family with the name. Hired into Ward Office 12 four years ago for the entries nobody wanted their hand on, and good at it. He sat at the desk across from Vance, drank tea she made too strong, and kept a private index of every amendment that came through the ward -- because his people keep their own archive, and because he had begun to see a pattern in whose files got touched.',0,'2026-10-29 02:09:17.224554','2026-10-29 02:09:17.224554',1000000003,0,NULL,1,1,6,10,14,8,0,NULL,NULL,'Perrin Lasco wants to place his private index in the hands of a clerk whose file remains open.','Perrin Lasco wants to obtain a name that will remember him after his file closes.','Perrin Lasco needs to keep two copies of every amendment.','Perrin Lasco needs to reach a person who will hear his own name and not use him.','keep','withhold',NULL); INSERT INTO characters VALUES(1000000003,1000000001,'Halkett Rowe','Sub-Inspector Rowe',52,'male','Courteous, patient, genuinely likeable, and entirely unable to imagine that the institution which made him could be the thing doing harm. He listens well. He will hear you out, agree with you, and file the writ anyway, and he will be sorry about it in a way that costs him nothing.','Tall, unhurried, immaculate; the sharply consistent good looks of a family nobody has ever had to remember with effort. Grey coat, Inspectorate seal on a chain, and shoes you hear coming up the hallway a long time before he arrives.','A ward office that has nothing to hide, being met at the door, punctuality','An open query, a clerk who reads the file out loud to him','That the Sealed Room has a file on him, and that it is thicker than it should be','Ledger-Kept, four hundred years of unbroken record behind him, and a career built on being the man the Inspectorate sends when a ward office needs to be reminded that it is audited. He has signed eleven closures. He believes, sincerely, that each was procedurally correct, and he has stopped reading the supporting files before he signs because reading them was making the work harder to do.',0,'2026-10-29 02:09:17.230546','2026-10-29 02:09:17.284680',1000000002,0,1000000001,0,1,8,11,9,13,0,3,7,'Halkett Rowe wants to close Ward Office 12 with every query answered and the file clean.','Halkett Rowe wants to obtain the Registry''s absolution for his eleven closures.','Halkett Rowe needs to keep his appointment at the next ward on time.','Halkett Rowe needs to reach one supporting file and read it before he signs.','keep','obtain',NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/the-weighing-house.sql b/parity/worlds/the-weighing-house.sql index bd80c78..e894e90 100644 --- a/parity/worlds/the-weighing-house.sql +++ b/parity/worlds/the-weighing-house.sql @@ -413,10 +413,11 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-25 22:57:23.355205','2026-10-25 22:57:23.355207'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','9fcff787fff0ed60123ee84a65c7f80738f353f3','2026-10-25 22:57:23.358867','2026-10-25 22:57:23.358868'); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -438,7 +439,7 @@ FOREIGN KEY ("posted_to_id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,14,'Ink on her fingers and a satchel strap with no satchel.','The warden''s runner, sent with the tally and never with anything heavy.',NULL,'2026-10-25 22:57:24.717037',0,NULL,13,'Being told something is too heavy','Dropping the warden''s slate','Nell Fisk',6,0,0,1,1,'A tally that comes out even',NULL,NULL,'Nell','Quick, and certain she can carry more than she can.',1000000001,NULL,'female',1000000001,5,NULL,NULL,'2026-10-25 22:57:24.717037',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,41,'Shoulders like a door, and a weigh ticket tucked in his cap.','Has carried the market''s loads on the same yoke for twenty years.',NULL,'2026-10-25 22:57:24.767089',0,NULL,10,'A yoke set down in the wet','His back going','Bram Hale',8,0,0,0,2,'An even load',1000000001,NULL,'Bram','Slow to speak, and kind about other people''s arms.',1000000001,NULL,'male',1000000001,16,NULL,NULL,'2026-10-25 22:57:24.767089',10,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/three-floors-that-raise-you.sql b/parity/worlds/three-floors-that-raise-you.sql index b407c36..2858e28 100644 --- a/parity/worlds/three-floors-that-raise-you.sql +++ b/parity/worlds/three-floors-that-raise-you.sql @@ -41,16 +41,16 @@ FOREIGN KEY ("player_id") FOREIGN KEY ("ruby_llm_model_id") REFERENCES "ruby_llm_models" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_8a0268155a" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_8a0268155a" FOREIGN KEY ("down_location_id") REFERENCES "locations" ("id") ON DELETE SET NULL, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE); -INSERT INTO floors VALUES(1000000001,1000000002,2,'safe','none',1000000003,'2026-10-30 16:09:38.312105','2026-10-30 16:09:38.358184',NULL,1); -INSERT INTO floors VALUES(1000000002,1000000003,2,'safe','none',1000000004,'2026-10-30 16:09:38.332180','2026-10-30 16:09:38.368358',NULL,1); -INSERT INTO floors VALUES(1000000003,1000000004,2,'safe','none',NULL,'2026-10-30 16:09:38.346863','2026-10-30 16:09:38.374301',NULL,1); +INSERT INTO floors VALUES(1000000001,1000000002,2,'safe','none',1000000003,'2026-10-30 16:09:38.312105','2026-10-30 16:09:38.358184',NULL,1,NULL); +INSERT INTO floors VALUES(1000000002,1000000003,2,'safe','none',1000000004,'2026-10-30 16:09:38.332180','2026-10-30 16:09:38.368358',NULL,1,NULL); +INSERT INTO floors VALUES(1000000003,1000000004,2,'safe','none',NULL,'2026-10-30 16:09:38.346863','2026-10-30 16:09:38.374301',NULL,1,NULL); CREATE TABLE IF NOT EXISTS "interactions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "character_id" integer NOT NULL, "scene_id" integer, "location_id" integer, "pre_thought" text, "pre_feeling" text, "action" text, "post_feeling" text, "post_thought" text, "inner_resolution" text, "summary" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "user_input" text, "engine_action" varchar, "action_status" varchar, "action_fact" text, CONSTRAINT "fk_rails_294d4ae9f8" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") @@ -188,7 +188,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("scene_id") REFERENCES "scenes" ("id") ); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" @@ -445,6 +445,7 @@ INSERT INTO schema_migrations VALUES('20250823023925'); INSERT INTO schema_migrations VALUES('20250822184651'); INSERT INTO schema_migrations VALUES('20250822184650'); INSERT INTO schema_migrations VALUES('20250822184649'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-30 16:09:36.934157','2026-10-30 16:09:36.934160'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','5b41acc531745dba36c583b40f12d94bcf2d8eb7','2026-10-30 16:09:36.938862','2026-10-30 16:09:36.938864'); diff --git a/parity/worlds/two-clerks-at-one-counter.sql b/parity/worlds/two-clerks-at-one-counter.sql index a6176d6..777c687 100644 --- a/parity/worlds/two-clerks-at-one-counter.sql +++ b/parity/worlds/two-clerks-at-one-counter.sql @@ -400,6 +400,7 @@ INSERT INTO schema_migrations VALUES('20261030120000'); INSERT INTO schema_migrations VALUES('20261030200000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-02 13:25:33.143313','2026-10-02 13:25:33.143316'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','072abc2bfb80d111a1808df270e9210911f5e81d','2026-10-02 13:25:33.148224','2026-10-02 13:25:33.148228'); @@ -410,7 +411,7 @@ FOREIGN KEY ("playthrough_id") FOREIGN KEY ("quest_outcome_id") REFERENCES "quest_outcomes" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_ce8b005cb2" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE, CONSTRAINT "fk_rails_8a0268155a" @@ -433,7 +434,7 @@ FOREIGN KEY ("posted_to_id") INSERT INTO characters VALUES(1000000001,38,'A carter''s coat with straw on the shoulders.','A carter who pawned his late father''s watch and has come to get it back.','Ansel wants his father''s watch back across the counter before noon.','2026-10-02 13:25:56.043954',0,'obtain',12,'Being kept waiting at a counter.','Finding the watch already sold.','Ansel Mott',8,0,0,1,10,'A fair count.',NULL,'offer','Ansel','Quiet and determined.',1000000001,'Ansel needs to keep hold of the ticket, because it is the only proof he has.','male',1000000001,12,'Ansel wants somebody to tell him the pledge was not a betrayal.','Ansel needs to say out loud why he pawned it.','2026-10-02 13:25:56.043954',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000002,29,'Ink on her cuffs and a pencil behind one ear.','The pawnbroker''s niece, who keeps the ticket book and wants every stub back in it.','Tamsin wants every pawn ticket back in the book, starting with the one in Ansel''s hand.','2026-10-02 13:25:56.157492',0,'obtain',12,'A stub that walks out of the shop.','Her uncle finding the book short.','Tamsin Gale',8,0,0,0,10,'A ticket book with no gaps in it.',1000000001,'attend','Tamsin','Quick, friendly and acquisitive.',1000000001,'Tamsin needs to keep the book square.','female',1000000001,12,'Tamsin wants to be trusted with the till.','Tamsin needs to let one pledge go without a fight.','2026-10-02 13:25:56.157492',12,NULL,NULL,NULL); INSERT INTO characters VALUES(1000000003,52,'A leather apron and a loupe on a cord.','The pawnbroker''s partner, who would rather close early than haggle.','Rowe wants the counter to himself and the door shut.','2026-10-02 13:25:56.174444',0,'keep',12,'Customers who lean on the counter.','Being made a fool of by a pledge.','Rowe Harker',8,0,0,0,10,'An empty shop at closing time.',1000000001,'withhold','Rowe','Gruff and territorial.',1000000001,'Rowe needs to keep the shop''s reputation.','male',1000000001,12,'Rowe wants somebody to ask his advice.','Rowe needs to admit the watch is already sold.','2026-10-02 13:25:56.174444',12,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/parity/worlds/two-floors-and-a-key.sql b/parity/worlds/two-floors-and-a-key.sql index 76abd0f..67aab57 100644 --- a/parity/worlds/two-floors-and-a-key.sql +++ b/parity/worlds/two-floors-and-a-key.sql @@ -27,15 +27,15 @@ FOREIGN KEY ("player_id") FOREIGN KEY ("ruby_llm_model_id") REFERENCES "ruby_llm_models" ("id") ); -CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, CONSTRAINT "fk_rails_8a0268155a" +CREATE TABLE IF NOT EXISTS "floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "location_id" integer NOT NULL, "rooms" integer NOT NULL, "danger" varchar, "hazard" varchar, "down_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "monster_level" integer, "levels_up" boolean DEFAULT FALSE NOT NULL, "minutes" integer, CONSTRAINT "fk_rails_8a0268155a" FOREIGN KEY ("down_location_id") REFERENCES "locations" ("id") ON DELETE SET NULL, CONSTRAINT "fk_rails_ce8b005cb2" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE); -INSERT INTO floors VALUES(1000000001,1000000002,4,'uneasy','none',1000000003,'2026-10-30 12:22:18.418505','2026-10-30 12:22:18.452529',NULL,0); -INSERT INTO floors VALUES(1000000002,1000000003,2,'dangerous','unlit',NULL,'2026-10-30 12:22:18.431886','2026-10-30 12:22:18.466493',NULL,0); +INSERT INTO floors VALUES(1000000001,1000000002,4,'uneasy','none',1000000003,'2026-10-30 12:22:18.418505','2026-10-30 12:22:18.452529',NULL,0,NULL); +INSERT INTO floors VALUES(1000000002,1000000003,2,'dangerous','unlit',NULL,'2026-10-30 12:22:18.431886','2026-10-30 12:22:18.466493',NULL,0,NULL); CREATE TABLE IF NOT EXISTS "interactions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "character_id" integer NOT NULL, "scene_id" integer, "location_id" integer, "pre_thought" text, "pre_feeling" text, "action" text, "post_feeling" text, "post_thought" text, "inner_resolution" text, "summary" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "user_input" text, "engine_action" varchar, "action_status" varchar, "action_fact" text, CONSTRAINT "fk_rails_294d4ae9f8" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") @@ -422,6 +422,7 @@ INSERT INTO schema_migrations VALUES('20250822184650'); INSERT INTO schema_migrations VALUES('20250822184649'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261031090000'); +INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-30 12:22:17.036817','2026-10-30 12:22:17.036819'); INSERT INTO ar_internal_metadata VALUES('schema_sha1','79cd55df42fd029083ae3c2121533177a39731eb','2026-10-30 12:22:17.042088','2026-10-30 12:22:17.042090'); @@ -439,7 +440,7 @@ FOREIGN KEY ("posted_to_id") REFERENCES "locations" ("id") ON DELETE SET NULL); INSERT INTO characters VALUES(1000000001,1000000001,'Odile','Odile',29,'female','Careful and stubborn.','A lantern on a pole and a coil of rope.','A clear way back.','Doors she cannot open.','The dark below the first floor.','A delver who has never been below the first floor.',0,'2026-10-30 12:22:18.511394','2026-10-30 12:22:18.511394',1000000001,1,NULL,0,3,8,12,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); -CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_340e8cdc38" +CREATE TABLE IF NOT EXISTS "playthrough_floors" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "playthrough_id" integer NOT NULL, "location_id" integer NOT NULL, "arrived_at" datetime(6) NOT NULL, "level" integer, "scene_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fell_at" datetime(6), CONSTRAINT "fk_rails_340e8cdc38" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") , CONSTRAINT "fk_rails_f82b72b677" diff --git a/src/cascade.rs b/src/cascade.rs index 5b0f4aa..59bb883 100644 --- a/src/cascade.rs +++ b/src/cascade.rs @@ -869,6 +869,7 @@ mod tests { edge: 100, key: None, }], + fallen: Vec::new(), cast, lying: Vec::new(), carried: Vec::new(), diff --git a/src/glance.rs b/src/glance.rs index c159388..792e6d1 100644 --- a/src/glance.rs +++ b/src/glance.rs @@ -200,6 +200,19 @@ impl EndingOffer { } } +/// The floor the player stands on, while it stands and has a countdown +/// (`floors.minutes`). No model is told any of it. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Floor { + pub name: String, + /// Story time it gives way in this game, in seconds since the epoch. + pub falls_at: i64, + /// Whole minutes until then, a part of a minute counted as one. + pub minutes_left: i64, + /// The engine's line for it: "The First Floor falls in 25 minutes." + pub countdown: String, +} + /// Everything a front end reads between turns. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Glance { @@ -223,6 +236,13 @@ pub struct Glance { pub next_beat: Option, /// Story time now, in seconds since the epoch. pub story_time: Option, + /// The floor the player stands on and when it falls, while it stands. + pub floor: Option, + /// For a game that ended with the player on a floor that gave way, the + /// engine's sentence for it, which the death statement (`ended`) + /// follows: "The First Floor gave way at 8:25 am, and Odile fell with + /// it." + pub collapse: Option, /// Every goal is met and the ending decided: `/end` ends the story on /// it, whether or not it is still offered. pub ending_due: bool, @@ -354,6 +374,13 @@ impl Glance { sheet: player.map(|who| sheet(&game, who)).unwrap_or_default(), next_beat: crate::moment::Moment::new(game).next_beat(), story_time: game.story_time(), + floor: game.countdown().map(|countdown| Floor { + countdown: countdown.in_words(), + name: countdown.name, + falls_at: countdown.falls_at, + minutes_left: countdown.minutes_left, + }), + collapse: game.collapse_to_s(), ending_due: !over && mechanics.due_ending().is_some(), ending_offer: ending_offer(mechanics, &game), over, diff --git a/src/grammar.rs b/src/grammar.rs index 0c94b82..02ceebe 100644 --- a/src/grammar.rs +++ b/src/grammar.rs @@ -749,6 +749,9 @@ impl<'a> Grammar<'a> { } let found = self.resolve(&exits, argument); if !found.found() { + if let Some(fact) = self.fallen_way(argument) { + return Reading::refused(fact); + } return Reading::refused(cannot_find("way out", argument, &found, &exits)); } intent_reading(Intent { @@ -928,6 +931,31 @@ impl<'a> Grammar<'a> { self.resolve(&self.room.items_carried(), &argument).record } + /// The engine's words for a line whose name, read past its verb the way + /// [`Grammar::parse`] reads it, lands on a way onto a floor that has + /// fallen in this game (or on the floor), and so on no way out. + pub fn fallen_named(&self, command: &str) -> Option { + let text = normalize_line(&unslashed(command)); + let argument = match self.verb_for(&text) { + Some(verb) => ruby_strip(chars_after(&text, verb.chars().count())).to_string(), + None => text, + }; + self.fallen_way(&argument) + } + + fn fallen_way(&self, argument: &str) -> Option { + let found = self.resolve(&self.room.fallen_here(), argument); + let place = found + .record + .or_else(|| found.candidates.into_iter().next())?; + let fallen = self + .room + .fallen + .iter() + .find(|fallen| Some(fallen.place.id) == place.id())?; + Some(crate::refusal::fallen(&fallen.floor)) + } + /// What a line that opens by leaving ([`cascade::leaving`]) comes to in /// this room, or `None` where it names something other than the room /// the player stands in, which is the reader's to read. A line opening diff --git a/src/moment.rs b/src/moment.rs index 77104d7..0b84126 100644 --- a/src/moment.rs +++ b/src/moment.rs @@ -817,3 +817,12 @@ pub fn time_of_day(at: i64) -> String { }; format!("{twelve} {}", if hour < 12 { "am" } else { "pm" }) } + +/// `strftime("%-l:%M %P")` in UTC: [`time_of_day`] to the minute, "8:25 am". +pub fn clock_time(at: i64) -> String { + let (hour, rest) = time_of_day(at) + .split_once(' ') + .map(|(hour, rest)| (hour.to_string(), rest.to_string())) + .unwrap_or_default(); + format!("{hour}:{:02} {rest}", at.rem_euclid(3_600) / 60) +} diff --git a/src/playthrough.rs b/src/playthrough.rs index f484b2d..8d1ebee 100644 --- a/src/playthrough.rs +++ b/src/playthrough.rs @@ -102,6 +102,33 @@ impl Condition { } } +/// A floor the player stands on that falls in this game, and when. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Countdown { + /// The floor's name. + pub name: String, + /// Story time it gives way, in seconds since the epoch. + pub falls_at: i64, + /// Whole minutes until then, a part of a minute counted as one. + pub minutes_left: i64, +} + +impl Countdown { + /// What the engine says of it, to the player only: "The First Floor + /// falls in 1 hour 5 minutes.", and under an hour "The First Floor + /// falls in 25 minutes." + pub fn in_words(&self) -> String { + let (hours, minutes) = (self.minutes_left / 60, self.minutes_left % 60); + let unit = |n: i64, word: &str| format!("{n} {word}{}", if n == 1 { "" } else { "s" }); + let left = if hours == 0 { + unit(minutes, "minute") + } else { + format!("{} {}", unit(hours, "hour"), unit(minutes, "minute")) + }; + format!("{} falls in {left}.", self.name) + } +} + /// How a game is told (`playthroughs.mode`), chosen when it starts and kept /// on it. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -328,6 +355,70 @@ impl<'a> Game<'a> { )) } + /// This game's row for the floor a room is on (the floor itself, or a + /// room laid out inside it), or none for a room on no floor and a floor + /// this game has not reached. + pub fn reached(&self, location: &Row) -> Option<&'a Row> { + let place = int(floor_of(self.records, location)?, "location_id"); + self.floors_reached() + .into_iter() + .find(|row| int(row, "location_id") == place) + } + + /// The story time a floor this game has reached gives way in it: its + /// countdown (`floors.minutes`) from the time this game first stood on + /// it. None for a floor with no countdown. + pub fn falls_at(&self, reached: &Row) -> Option { + let place = int(reached, "location_id"); + let floor = self.records.first(crate::store::FLOORS, |floor| { + int(floor, "location_id") == place + })?; + Some(int(reached, "arrived_at")? + int(floor, "minutes")? * 60) + } + + /// Whether a room is on a floor that has fallen in this game + /// (`playthrough_floors.fell_at`): nobody on it is alive in this game, + /// and no way out leads onto it. The world's rows are as they were, and + /// every other game sees the floor whole. + pub fn fallen(&self, location: &Row) -> bool { + self.reached(location) + .is_some_and(|row| int(row, "fell_at").is_some()) + } + + /// The floor the player stands on and how long it has left, while it + /// stands: none off a floor, on a floor with no countdown, and once it + /// has fallen. + pub fn countdown(&self) -> Option { + let here = self.current_location()?; + let row = self.reached(here)?; + if int(row, "fell_at").is_some() { + return None; + } + let falls_at = self.falls_at(row)?; + let left = (falls_at - self.story_time()?).max(0); + Some(Countdown { + name: string(self.location(int(row, "location_id")?), "name").to_string(), + falls_at, + minutes_left: (left + 59).div_euclid(60), + }) + } + + /// What the engine says of a floor that gave way under the player, in + /// its own words and to the player only: "The First Floor gave way at + /// 8:25 am, and Odile fell with it." None unless the game ended with the + /// player on a fallen floor; the death statement follows it. + pub fn collapse_to_s(&self) -> Option { + int(self.row, "ended_at")?; + let row = self.reached(self.current_location()?)?; + let fell = int(row, "fell_at")?; + Some(format!( + "{} gave way at {}, and {} fell with it.", + string(self.location(int(row, "location_id")?), "name"), + crate::moment::clock_time(fell), + string(self.protagonist()?, "fullname") + )) + } + /// `#vitals_for`: none for somebody with no stat block. pub fn vitals_for(&self, character: &Row) -> Option { let max = self.max_hp(character)?; @@ -428,9 +519,10 @@ impl<'a> Game<'a> { people } - /// `#cast_in`: the living in a room. + /// `#cast_in`: the living in a room. Nobody on a floor that has fallen + /// in this game is alive in it. pub fn cast_in(&self, location: Option<&Row>) -> Vec<&'a Row> { - let Some(location) = location else { + let Some(location) = location.filter(|room| !self.fallen(room)) else { return Vec::new(); }; self.characters_located_in(location) @@ -559,7 +651,8 @@ impl<'a> Game<'a> { self.items(|item| int(item, "character_id") == Some(who)) } - /// `#exits`: the rooms the current room's doorways lead to, by id. + /// `#exits`: the rooms the current room's doorways lead to, by id, less + /// any on a floor that has fallen in this game. pub fn exits(&self) -> Vec<&'a Row> { let Some(here) = self.current_location() else { return Vec::new(); @@ -572,6 +665,7 @@ impl<'a> Game<'a> { }) .iter() .map(|edge| self.location(int(edge, "connected_location_id").expect("a far end"))) + .filter(|room| !self.fallen(room)) .collect(); rooms.sort_by_key(|room| id(room)); rooms diff --git a/src/refusal.rs b/src/refusal.rs index 797c882..1759844 100644 --- a/src/refusal.rs +++ b/src/refusal.rs @@ -593,6 +593,12 @@ fn present_name(character: Option<&Person>) -> Option<&str> { .filter(|name| !crate::text::is_blank(name)) } +/// What a line naming a floor that has fallen in this game, or a way onto +/// it, is told. +pub fn fallen(floor: &str) -> String { + format!("{floor} has fallen. There is no way back onto it.") +} + /// What a dead player is told. pub fn death_sentence(character: Option<&Person>) -> String { let who = present_name(character).unwrap_or("You"); diff --git a/src/room.rs b/src/room.rs index 88b4ed2..3e6b0a9 100644 --- a/src/room.rs +++ b/src/room.rs @@ -198,6 +198,16 @@ pub struct Exit { pub key: Option, } +/// A way out onto a floor that has fallen in this game, or that floor +/// itself: never offered, and a line that names it is refused in the +/// engine's words ([`crate::refusal::fallen`]). +#[derive(Clone, Debug)] +pub struct Fallen { + pub place: Place, + /// The fallen floor's name. + pub floor: String, +} + /// One physical attempt the room offers, built from this game's own things /// and doorways. A reader picks one; it never supplies an effect. #[derive(Clone, Debug)] @@ -445,6 +455,9 @@ pub struct Room { /// Where the playthrough stands, or `None` for nowhere. pub here: Option, pub exits: Vec, + /// The ways out of here onto a floor that has fallen in this game, and + /// each such floor, kept out of [`Room::exits`]. + pub fallen: Vec, /// Who is standing here, the player not among them. pub cast: Vec, pub lying: Vec, @@ -472,6 +485,14 @@ impl Room { .collect() } + /// The ways out onto a fallen floor, as records a line could name. + pub fn fallen_here(&self) -> Vec { + self.fallen + .iter() + .map(|fallen| Record::Place(fallen.place.clone())) + .collect() + } + /// Who the player can speak to or strike. pub fn characters_here(&self) -> Vec { if self.here.is_none() { diff --git a/src/store.rs b/src/store.rs index 83bdf1e..0d7b8cd 100644 --- a/src/store.rs +++ b/src/store.rs @@ -95,10 +95,12 @@ pub const OPTIONAL: &[&str] = &[REFUSED_LINES]; /// read as null, and so as the ordinary limits, in a database from before /// them; a floor's monster level and the floor a person is posted to /// (migration 20261030230000), which read as null, and so as nobody born -/// stronger and nobody posted; and whether a floor raises a level -/// (migration 20261031090000), which reads as false. [`shape`] leaves them -/// out, so a database opens with or without them; one declared any other -/// way is still a difference. +/// stronger and nobody posted; whether a floor raises a level (migration +/// 20261031090000), which reads as false; and a floor's countdown and when +/// it fell in a game (migration 20261031120000), which read as null, and so +/// as a floor that never falls. [`shape`] leaves them out, so a database +/// opens with or without them; one declared any other way is still a +/// difference. pub const OPTIONAL_COLUMNS: &[&str] = &[ "column stories.people_limit integer pk=0", "column stories.things_limit integer pk=0", @@ -107,6 +109,8 @@ pub const OPTIONAL_COLUMNS: &[&str] = &[ "foreign key characters.posted_to_id -> locations.id on update NO ACTION on delete SET NULL", "index characters (posted_to_id)", "column floors.levels_up boolean not null default FALSE pk=0", + "column floors.minutes integer pk=0", + "column playthrough_floors.fell_at datetime(6) pk=0", ]; /// A floor's own layout numbers, one row per place laid out as a floor @@ -116,7 +120,8 @@ pub const FLOORS: &str = "floors"; /// The floors one game has reached, one row per game and floor, written the /// first time the game stands on it (migration 20261031090000): when it /// arrived, and on a floor that raises a level, the level it raised the -/// player to. +/// player to; and on a floor with a countdown, when it fell in that game +/// (`fell_at`, migration 20261031120000). pub const PLAYTHROUGH_FLOORS: &str = "playthrough_floors"; /// Tables the engine loads only where the database has them: a database @@ -452,6 +457,14 @@ impl Store { self.columns.contains_key(table) } + /// Whether the database has this column of a table it has: one of + /// [`OPTIONAL_COLUMNS`] only where a migration added it. + pub fn has_column(&self, table: &str, column: &str) -> bool { + self.columns + .get(table) + .is_some_and(|columns| columns.iter().any(|found| found.name == column)) + } + fn columns(&self, table: &str) -> Result<&[Column], Error> { self.columns.get(table).map(Vec::as_slice).ok_or_else(|| { Error::Database(if OPTIONAL.contains(&table) { diff --git a/src/turn.rs b/src/turn.rs index 3cf0700..8644580 100644 --- a/src/turn.rs +++ b/src/turn.rs @@ -22,7 +22,7 @@ use crate::playthrough::{stat_block, Condition, Game}; use crate::records::{flag, id, int, string, text, Records, Row}; use crate::refusal::Refusal; use crate::roll::{self, Seed}; -use crate::room::{Choice, Exit, Person, Place, Record, Room, Thing, REMOVE, WAIT, WEAR}; +use crate::room::{Choice, Exit, Fallen, Person, Place, Record, Room, Thing, REMOVE, WAIT, WEAR}; use crate::spot; use crate::store::{self, Store}; use crate::text::to_sentence; @@ -455,6 +455,12 @@ impl<'s> Mechanics<'s> { self.put_off_ending()?; self.catch_up_world()?; + if self.floors_fall()? { + return Ok(Report { + note: self.collapse_note(), + ..Report::refuse(self.over_refusal(command).reason(), None) + }); + } let here = self.here(); self.snapshot_room(here.as_ref())?; @@ -508,7 +514,10 @@ impl<'s> Mechanics<'s> { Report::read(note_of(&reading), None) }; - let report = self.answered_by_the_world(report, &reading, from.as_ref())?; + let mut report = self.answered_by_the_world(report, &reading, from.as_ref())?; + if self.floors_fall()? { + report.note.extend(self.collapse_note()); + } let stayed = self.here().map(|room| id(&room)) == from.as_ref().map(id); let looked = narrates && crate::noticed::is_look_line(command); if narrates && stayed && !looked && report.change.is_some() && !self.over() { @@ -638,6 +647,11 @@ impl<'s> Mechanics<'s> { let fact = crate::refusal::still_needed(thing, &kept.kind); return Refusal::new("unplayable", command, &fact, None).ok(); } + if intent.action == "move" && intent.reached_for_nothing() { + if let Some(fact) = grammar.fallen_named(command) { + return Refusal::new("unplayable", command, &fact, None).ok(); + } + } let named = grammar.carried_named(command); return Refusal::for_intent( intent, @@ -3177,6 +3191,59 @@ impl<'s> Mechanics<'s> { Ok(()) } + /// Every floor this game has reached whose countdown has run out by + /// story time now (`Game::falls_at`) falls in this game: its row keeps + /// the time it gave way (`fell_at`), nobody on it is alive in this game + /// from then on, and no way out leads onto it. A player standing on it + /// falls with it: dead, and the game over. Asked before a line is read + /// and again once the line's time is spent; a rest that runs past the + /// countdown is not refused, and is fatal. The world's rows are never + /// touched, so every other game sees the floor whole. True when it took + /// the player. A database without the column keeps no countdown. + pub(crate) fn floors_fall(&mut self) -> Result { + if !self.store.has_column(store::PLAYTHROUGH_FLOORS, "fell_at") || self.over() { + return Ok(false); + } + let now = self.story_now(); + let due: Vec<(i64, i64)> = { + let game = self.game(); + game.floors_reached() + .into_iter() + .filter(|row| int(row, "fell_at").is_none()) + .filter_map(|row| Some((id(row), game.falls_at(row)?))) + .filter(|(_, at)| *at <= now) + .collect() + }; + for (row, at) in due { + self.update( + store::PLAYTHROUGH_FLOORS, + row, + vec![("fell_at", Value::from(at))], + )?; + } + let here = self.here(); + if !here.as_ref().is_some_and(|room| self.game().fallen(room)) { + return Ok(false); + } + if let Some(player) = self.player() { + if let Some(vitals) = self.vitals_row(&player)? { + let hp = int(&vitals, "hp_current").unwrap_or_default(); + self.harm(&player, hp)?; + } + } + self.end_game(now)?; + Ok(true) + } + + /// The engine-alone note for a floor that gave way under the player. + fn collapse_note(&self) -> Vec { + self.game() + .collapse_to_s() + .map(|fell| format!("the game: {fell}")) + .into_iter() + .collect() + } + /// A fall through a doorway: the storeys between its two rooms at the /// world's gravity, a dexterity save and then the dice, out of a /// generator keyed on the doorway. Nothing is thrown or written in a @@ -4342,10 +4409,34 @@ pub fn whole_room_of(records: &Records, playthrough: i64) -> Room { let game = Game::new(records, playthrough); let here = game.current_location(); let story_protagonist = story_protagonist(records, game.story_id()); + let ways = here.map_or_else(Vec::new, |room| exits_of(records, room)); + let mut fallen = Vec::new(); + for (_, far) in ways.iter().filter(|(_, far)| game.fallen(far)) { + let floor = game + .reached(far) + .and_then(|row| int(row, "location_id")) + .and_then(|floor| records.find("locations", floor)); + let name = floor.map_or("", |floor| string(floor, "name")); + for place in std::iter::once(far).chain(floor) { + if !fallen + .iter() + .any(|known: &Fallen| known.place.id == id(place)) + { + fallen.push(Fallen { + place: Place { + id: id(place), + name: string(place, "name").to_string(), + }, + floor: name.to_string(), + }); + } + } + } let exits = match here { None => Vec::new(), - Some(room) => exits_of(records, room) + Some(_) => ways .into_iter() + .filter(|(_, far)| !game.fallen(far)) .map(|(edge, far)| Exit { place: Place { id: id(&far), @@ -4379,6 +4470,7 @@ pub fn whole_room_of(records: &Records, playthrough: i64) -> Room { name: string(room, "name").to_string(), }), exits, + fallen, cast: game .cast_in(here) .into_iter() diff --git a/src/turn/told.rs b/src/turn/told.rs index 98fdabe..501fcb6 100644 --- a/src/turn/told.rs +++ b/src/turn/told.rs @@ -495,6 +495,9 @@ impl<'s, 'm> Turn<'s, 'm> { self.commit("not_yet", |turn| turn.m.put_off_ending())?; } self.commit("world_clock", |turn| turn.m.catch_up_world())?; + if self.commit("floor_fell", |turn| turn.m.floors_fall())? { + return Ok(Played::Refused(self.m.over_refusal(command))); + } self.commit("starting_room", |turn| { let here = turn.m.here(); turn.m.snapshot_room(here.as_ref()) @@ -594,6 +597,9 @@ impl<'s, 'm> Turn<'s, 'm> { Ok(()) })?; self.safety = self.safety || scene.as_ref().is_some_and(|told| told.safety); + // The act's time is spent: a floor whose countdown ran out under it + // falls now, before anybody strikes. + self.commit("floor_fell_after", |turn| turn.m.floors_fall())?; self.commit("told_tolls", |turn| turn.claim_tolls(scene.as_ref()))?; self.commit("told_floors", |turn| turn.claim_floors(scene.as_ref()))?; self.commit("told_volitions", |turn| { @@ -695,6 +701,16 @@ impl<'s, 'm> Turn<'s, 'm> { "grammar".into(), )); } + // A move onto a floor that has fallen in this game is the + // engine's to refuse, in its own words: no reader is asked. + let fallen = grammar.fallen_named(command); + if fallen.is_some() && reading.refusal == fallen { + let intent = Intent { + declined: fallen, + ..Intent::new("move") + }; + return Ok((intent, "grammar".into())); + } } // A line that only leaves the room it is said in is the engine's: // the one way out is walked, and anything else is refused with the diff --git a/tests/engine.rs b/tests/engine.rs index 2576f88..587a2d6 100644 --- a/tests/engine.rs +++ b/tests/engine.rs @@ -839,6 +839,46 @@ fn a_levels_up_or_a_playthrough_floors_declared_another_way_is_refused() { ); } +#[test] +fn a_database_from_before_the_countdown_is_opened_and_plays() { + let conn = rusqlite::Connection::open_in_memory().unwrap(); + conn.execute_batch(&world("a-floor-that-falls")).unwrap(); + conn.execute_batch( + r#"ALTER TABLE "floors" DROP COLUMN "minutes"; + ALTER TABLE "playthrough_floors" DROP COLUMN "fell_at"; + DELETE FROM schema_migrations WHERE version = '20261031120000';"#, + ) + .unwrap(); + let mut engine = Engine::from_connection(conn).expect("the older shape is opened"); + let story = engine + .story_titled(&format!("A Floor That Falls{TITLE_SUFFIX}")) + .unwrap(); + let playthrough = engine.start(story).unwrap(); + let outcome = engine + .play(playthrough, "/move The First Floor", &mut |_| {}) + .unwrap(); + assert!(outcome.state.location.is_some()); +} + +#[test] +fn a_minutes_or_a_fell_at_declared_another_way_is_refused() { + let differences = changed(migrated( + "a-floor-that-falls", + r#"ALTER TABLE "floors" DROP COLUMN "minutes"; + ALTER TABLE "floors" ADD "minutes" varchar;"#, + )); + assert_eq!(differences, ["found column floors.minutes varchar pk=0"]); + let differences = changed(migrated( + "a-floor-that-falls", + r#"ALTER TABLE "playthrough_floors" DROP COLUMN "fell_at"; + ALTER TABLE "playthrough_floors" ADD "fell_at" datetime(6) NOT NULL DEFAULT '';"#, + )); + assert_eq!( + differences, + ["found column playthrough_floors.fell_at datetime(6) not null default '' pk=0"] + ); +} + #[test] fn a_newer_schema_that_adds_a_unique_index_to_a_table_the_engine_uses_is_refused() { let differences = changed(migrated( diff --git a/tests/floor_falls.rs b/tests/floor_falls.rs new file mode 100644 index 0000000..01e6ba5 --- /dev/null +++ b/tests/floor_falls.rs @@ -0,0 +1,436 @@ +//! A floor with a countdown, as a caller holds it: A Floor That Falls walked +//! into on the live path with replayed replies and by the engine alone, and +//! the rows and the glance read back. +//! +//! The First Floor is two rooms and says `minutes: 30`; Wenna Cole is posted +//! to it, so she stands in the room with the stair down. The Second Floor is +//! two rooms with no countdown. The story starts at 8:00 am, so a game that +//! walks onto the first floor at once sees it fall at 8:30. + +use renderedstep_engine::engine::Engine; +use renderedstep_engine::glance::Floor; +use renderedstep_engine::model::{Replay, Reply}; +use renderedstep_engine::parity::{open_world, TITLE_SUFFIX}; +use renderedstep_engine::playthrough::{Countdown, Game}; +use renderedstep_engine::records::id; +use serde_json::{json, Value}; +use std::path::Path; + +fn world() -> String { + let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("parity/worlds/a-floor-that-falls.sql"); + std::fs::read_to_string(path).expect("a world fixture") +} + +const TITLE: &str = "A Floor That Falls"; + +/// 2026-10-31 08:30:00 UTC: when The First Floor falls for a game that +/// walked onto it at the story's start. +const HALF_PAST_EIGHT: i64 = 1_793_435_400; + +const DEATH: &str = "Odile is dead, and this playthrough is over. Nothing you type can \ + change it. Start a new playthrough to play this world again."; + +fn reply(value: Value) -> Reply { + Reply::from_value(&value).unwrap() +} + +fn place() -> Reply { + reply(json!({"purpose": "location", "content": { + "description": "Black stone passages, cracked across every few paces.", + "lore": "Cut by people nobody remembers.", + "place_kind": "warren", + }})) +} + +fn room() -> Reply { + reply(json!({"purpose": "location", "content": { + "description": "A bare stone chamber.", + "lore": "Old.", + "items": [], + "people": [], + }})) +} + +fn no_arrival() -> Reply { + reply(json!({"purpose": "arrival", "unavailable": true})) +} + +fn narration() -> Reply { + reply(json!({"purpose": "narration", "content": "You wait."})) +} + +/// `line` submitted on the live path, answered by `replies` in turn: the +/// refusal's words, if it was refused, and every request it sent. +fn submitted( + engine: &mut Engine, + playthrough: i64, + line: &str, + replies: Vec, +) -> (Option, Vec) { + let mut replay = Replay::new(replies); + let token = format!("{line}-{}", count(engine, "playthrough_commands")); + let submitted = engine + .submit(playthrough, line, &token, &mut replay, &mut |_| {}) + .unwrap(); + replay.finish().unwrap(); + ( + submitted.turned.refusal.map(|refusal| refusal.text()), + replay.sent().to_vec(), + ) +} + +/// `line` played by the engine alone: its refusal, if any, and its notes. +fn played(engine: &mut Engine, playthrough: i64, line: &str) -> (Option, Vec) { + let report = engine.play(playthrough, line, &mut |_| {}).unwrap().report; + (report.refusal, report.note) +} + +fn opened() -> (Engine, i64) { + let engine = open_world(&world()).unwrap(); + let story = engine + .story_titled(&format!("{TITLE}{TITLE_SUFFIX}")) + .unwrap(); + (engine, story) +} + +fn query(engine: &Engine, sql: &str) -> Option { + engine + .store() + .connection() + .query_row(sql, [], |row| row.get(0)) + .unwrap() +} + +fn count(engine: &Engine, table: &str) -> i64 { + query(engine, &format!("SELECT COUNT(*) FROM {table}")).unwrap() +} + +fn execute(engine: &Engine, sql: &str) { + engine.store().connection().execute_batch(sql).unwrap(); +} + +fn location(engine: &Engine, name: &str) -> i64 { + query( + engine, + &format!("SELECT id FROM locations WHERE name = '{name}'"), + ) + .unwrap() +} + +/// When a game's floor fell, in seconds, none while it stands. +fn fell_at(engine: &Engine, playthrough: i64, floor: i64) -> Option { + query( + engine, + &format!( + "SELECT CAST(strftime('%s', fell_at) AS INTEGER) FROM playthrough_floors \ + WHERE playthrough_id = {playthrough} AND location_id = {floor}" + ), + ) +} + +fn hp(engine: &Engine, playthrough: i64) -> Option { + query( + engine, + &format!( + "SELECT hp_current FROM playthrough_vitals WHERE playthrough_id = {playthrough} \ + AND character_id = (SELECT character_id FROM playthroughs WHERE id = {playthrough})" + ), + ) +} + +/// Every world row the fall of The First Floor could have touched, as text: +/// its own rows and its rooms', every doorway, every person, every floor. +fn world_rows(engine: &Engine) -> String { + let connection = engine.store().connection(); + let first = location(engine, "The First Floor"); + [ + format!("locations WHERE id = {first} OR parent_location_id = {first}"), + "location_connections".into(), + "characters".into(), + "floors".into(), + ] + .iter() + .map(|rows| { + let mut statement = connection + .prepare(&format!("SELECT * FROM {rows} ORDER BY id")) + .unwrap(); + let width = statement.column_count(); + let rows: Vec = statement + .query_map([], |row| { + Ok((0..width) + .map(|at| match row.get_ref(at).unwrap() { + rusqlite::types::ValueRef::Text(text) => { + String::from_utf8_lossy(text).into_owned() + } + other => format!("{other:?}"), + }) + .collect::>() + .join(",")) + }) + .unwrap() + .collect::>() + .unwrap(); + rows.join("\n") + }) + .collect::>() + .join("\n") +} + +/// A game walked onto The First Floor on the live path. +fn on_the_first_floor(engine: &mut Engine, story: i64) -> i64 { + let game = engine.start(story).unwrap(); + submitted( + engine, + game, + "/move The First Floor", + vec![place(), room(), no_arrival()], + ); + game +} + +/// A game that has gone on down to The Second Floor. +fn on_the_second_floor(engine: &mut Engine, story: i64) -> i64 { + let game = on_the_first_floor(engine, story); + submitted( + engine, + game, + "/move The First Floor room 2", + vec![room(), no_arrival()], + ); + submitted( + engine, + game, + "/move The Second Floor", + vec![place(), room(), no_arrival()], + ); + game +} + +#[test] +fn waiting_past_the_countdown_on_the_floor_is_fatal_and_never_refused() { + let (mut engine, story) = opened(); + let first = location(&engine, "The First Floor"); + let game = on_the_first_floor(&mut engine, story); + assert_eq!( + engine.glance(game).unwrap().floor, + Some(Floor { + name: "The First Floor".into(), + falls_at: HALF_PAST_EIGHT, + minutes_left: 29, + countdown: "The First Floor falls in 29 minutes.".into(), + }), + "the walk in took a minute and a half of the thirty" + ); + + let mut sent = Vec::new(); + for _ in 0..5 { + let (refusal, requests) = submitted(&mut engine, game, "/wait", vec![narration()]); + assert_eq!(refusal, None); + sent.extend(requests); + } + assert_eq!( + engine + .glance(game) + .unwrap() + .floor + .map(|floor| floor.countdown), + Some("The First Floor falls in 4 minutes.".into()) + ); + assert_eq!(fell_at(&engine, game, first), None); + + let (refusal, requests) = submitted(&mut engine, game, "/wait", vec![narration()]); + sent.extend(requests); + assert_eq!(refusal, None, "the wait across the countdown is played"); + assert_eq!(fell_at(&engine, game, first), Some(HALF_PAST_EIGHT)); + assert_eq!(hp(&engine, game), Some(0)); + let glance = engine.glance(game).unwrap(); + assert!(glance.over); + assert_eq!(glance.floor, None); + assert_eq!( + glance.collapse.as_deref(), + Some("The First Floor gave way at 8:30 am, and Odile fell with it.") + ); + assert_eq!(glance.ended.as_deref(), Some(DEATH)); + + for request in &sent { + let text = request.to_string(); + for words in ["falls in", "gave way", "has fallen"] { + assert!(!text.contains(words), "a request tells {words:?}: {text}"); + } + } + let (refusal, _) = submitted(&mut engine, game, "/wait", Vec::new()); + assert_eq!(refusal.as_deref(), Some(DEATH)); +} + +#[test] +fn a_floor_falls_behind_a_player_who_went_down_in_their_game_only() { + let (mut engine, story) = opened(); + let first = location(&engine, "The First Floor"); + let game = on_the_second_floor(&mut engine, story); + let room_2 = location(&engine, "The First Floor room 2"); + let before = world_rows(&engine); + for _ in 0..6 { + submitted(&mut engine, game, "/wait", vec![narration()]); + } + assert_eq!(fell_at(&engine, game, first), Some(HALF_PAST_EIGHT)); + assert_eq!(hp(&engine, game), Some(18), "she was not on it"); + let glance = engine.glance(game).unwrap(); + assert!(!glance.over); + assert_eq!(glance.collapse, None); + assert_eq!(glance.floor, None, "The Second Floor has no countdown"); + let exits: Vec = glance.exits.iter().map(|exit| exit.name.clone()).collect(); + assert_eq!(exits, ["The Second Floor room 2"]); + + for line in ["/move The First Floor room 2", "/move The First Floor"] { + let (refusal, _) = played(&mut engine, game, line); + assert_eq!( + refusal.as_deref(), + Some("The First Floor has fallen. There is no way back onto it."), + "{line}" + ); + let (refusal, sent) = submitted(&mut engine, game, line, Vec::new()); + assert!(sent.is_empty(), "no reader is asked"); + assert_eq!( + refusal.as_deref(), + Some("The First Floor has fallen. There is no way back onto it. Nothing has changed."), + "{line}" + ); + } + + // Wenna is dead in this game, and alive in the world and in the next. + let records = engine.store().load().unwrap(); + let wenna_room = records.find("locations", room_2).unwrap(); + assert!(Game::new(&records, game) + .cast_in(Some(wenna_room)) + .is_empty()); + assert!(Game::new(&records, game).fallen(wenna_room)); + assert_eq!(world_rows(&engine), before, "no world row is touched"); + + let second = engine.start(story).unwrap(); + played(&mut engine, second, "/move The First Floor"); + played(&mut engine, second, "/move The First Floor room 2"); + let records = engine.store().load().unwrap(); + let here = Game::new(&records, second); + let present: Vec = here + .cast_in(here.current_location()) + .into_iter() + .filter(|who| id(who) != id(here.protagonist().unwrap())) + .map(id) + .collect(); + assert_eq!( + present, + [query( + &engine, + "SELECT id FROM characters WHERE fullname = 'Wenna Cole'" + ) + .unwrap()] + ); + assert_eq!(fell_at(&engine, second, first), None); + assert_eq!( + engine.glance(second).unwrap().floor.map(|floor| floor.name), + Some("The First Floor".into()), + "the second game's countdown is its own" + ); +} + +#[test] +fn a_countdown_already_run_out_takes_the_player_before_the_line_is_read() { + let (mut engine, story) = opened(); + let first = location(&engine, "The First Floor"); + let game = on_the_first_floor(&mut engine, story); + execute( + &engine, + "UPDATE playthrough_floors SET arrived_at = '2026-10-31 07:00:00';", + ); + let (refusal, notes) = played(&mut engine, game, "harm 1"); + assert_eq!(refusal.as_deref(), Some(DEATH)); + assert_eq!( + notes, + ["the game: The First Floor gave way at 7:30 am, and Odile fell with it."] + ); + assert_eq!(fell_at(&engine, game, first), Some(HALF_PAST_EIGHT - 3600)); + assert_eq!(hp(&engine, game), Some(0)); + + // The same on the live path: refused before any reader is asked. + let (mut engine, story) = opened(); + let game = on_the_first_floor(&mut engine, story); + execute( + &engine, + "UPDATE playthrough_floors SET arrived_at = '2026-10-31 07:00:00';", + ); + let (refusal, sent) = submitted(&mut engine, game, "/wait", Vec::new()); + assert!(sent.is_empty()); + assert_eq!(refusal.as_deref(), Some(DEATH)); + assert_eq!( + engine.glance(game).unwrap().collapse.as_deref(), + Some("The First Floor gave way at 7:30 am, and Odile fell with it.") + ); +} + +#[test] +fn a_floor_with_no_countdown_never_falls() { + let (mut engine, story) = opened(); + execute(&engine, "UPDATE floors SET minutes = NULL;"); + let game = on_the_first_floor(&mut engine, story); + execute( + &engine, + "UPDATE playthrough_floors SET arrived_at = '2026-10-01 07:00:00';", + ); + let (refusal, notes) = played(&mut engine, game, "harm 1"); + assert_eq!(refusal, None); + assert!(notes.is_empty(), "{notes:?}"); + let glance = engine.glance(game).unwrap(); + assert!(!glance.over); + assert_eq!(glance.floor, None); +} + +#[test] +fn a_database_from_before_the_countdown_plays_and_nothing_falls() { + let conn = rusqlite::Connection::open_in_memory().unwrap(); + conn.execute_batch(&world()).unwrap(); + conn.execute_batch( + r#"ALTER TABLE "floors" DROP COLUMN "minutes"; + ALTER TABLE "playthrough_floors" DROP COLUMN "fell_at"; + DELETE FROM schema_migrations WHERE version = '20261031120000';"#, + ) + .unwrap(); + let mut engine = Engine::from_connection(conn).expect("the older shape is opened"); + let story = engine + .story_titled(&format!("{TITLE}{TITLE_SUFFIX}")) + .unwrap(); + let game = on_the_first_floor(&mut engine, story); + for _ in 0..7 { + let (refusal, _) = submitted(&mut engine, game, "/wait", vec![narration()]); + assert_eq!(refusal, None); + } + let glance = engine.glance(game).unwrap(); + assert!(!glance.over); + assert_eq!(glance.floor, None); + assert_eq!(hp(&engine, game), Some(18)); +} + +#[test] +fn the_countdown_is_said_in_hours_and_minutes() { + let said = |minutes_left| { + Countdown { + name: "The First Floor".into(), + falls_at: 0, + minutes_left, + } + .in_words() + }; + assert_eq!(said(25), "The First Floor falls in 25 minutes."); + assert_eq!(said(1), "The First Floor falls in 1 minute."); + assert_eq!(said(60), "The First Floor falls in 1 hour 0 minutes."); + assert_eq!(said(65), "The First Floor falls in 1 hour 5 minutes."); + assert_eq!(said(1152), "The First Floor falls in 19 hours 12 minutes."); + assert_eq!(said(121), "The First Floor falls in 2 hours 1 minute."); +} + +#[test] +fn a_collapse_is_said_at_the_minute_it_came() { + use renderedstep_engine::moment::clock_time; + assert_eq!(clock_time(HALF_PAST_EIGHT), "8:30 am"); + assert_eq!(clock_time(HALF_PAST_EIGHT + 4 * 3600 + 35 * 60), "1:05 pm"); + assert_eq!(clock_time(HALF_PAST_EIGHT - 8 * 3600 - 30 * 60), "12:00 am"); +} diff --git a/tests/vectors/lines.rs b/tests/vectors/lines.rs index ac02131..4e4d41d 100644 --- a/tests/vectors/lines.rs +++ b/tests/vectors/lines.rs @@ -98,6 +98,7 @@ fn build(worlds: &Value, name: &Value) -> Built { key: exit["key"].as_i64(), }) .collect(), + fallen: Vec::new(), cast: list(&world["cast"]).iter().map(person).collect(), lying: list(&world["lying"]) .iter() From 3a4faf3b41f9b16db644c6b676390ecd3a1be743 Mon Sep 17 00:00:00 2001 From: Caleb LeNoir Date: Fri, 30 Oct 2026 13:25:39 -0400 Subject: [PATCH 2/3] Rebuild a-floor-that-falls from the game's loader at renderedstep/server 383a67d The game's pin PR (renderedstep/server#374) reads minutes:, so the dump is now the one its own loader writes from a schema load: minutes is written by the loader rather than by hand. The rows are the same; only timestamps, the schema digest and the order the dump lists them in moved. No golden or records file moves. --- parity/worlds/a-floor-that-falls.sql | 36 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/parity/worlds/a-floor-that-falls.sql b/parity/worlds/a-floor-that-falls.sql index 8841fea..631f036 100644 --- a/parity/worlds/a-floor-that-falls.sql +++ b/parity/worlds/a-floor-that-falls.sql @@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS "ruby_llm_tool_calls" ("id" integer PRIMARY KEY AUTOI CREATE TABLE IF NOT EXISTS "ruby_llm_usages" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "chat_type" varchar NOT NULL, "chat_id" integer NOT NULL, "message_type" varchar, "message_id" integer, "operation" varchar NOT NULL, "provider" varchar NOT NULL, "model" varchar NOT NULL, "status" varchar NOT NULL, "input_tokens" integer, "output_tokens" integer, "cache_read_tokens" integer, "cache_write_tokens" integer, "thinking_tokens" integer, "input_cost" decimal(16,10), "output_cost" decimal(16,10), "cache_read_cost" decimal(16,10), "cache_write_cost" decimal(16,10), "thinking_cost" decimal(16,10), "total_cost" decimal(16,10), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "latency_ms" integer, CONSTRAINT chk_rails_71abd85d6e CHECK (operation IN ('chat', 'embedding', 'moderation', 'image', 'speech', 'transcription', 'ocr', 'rerank')), CONSTRAINT chk_rails_f50895962a CHECK (status IN ('pending', 'succeeded', 'failed', 'cancelled'))); CREATE TABLE IF NOT EXISTS "ruby_llm_v2_backfills" ("task" varchar NOT NULL, "last_id" integer, "completed" boolean DEFAULT FALSE NOT NULL); CREATE TABLE IF NOT EXISTS "universes" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "physics" text, "technology" text, "weapons" text, "civilizations" text, "geographies" text, "history" text, "economics" text, "politics" text, "religion" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "gravity" varchar, "carrying" varchar); -INSERT INTO universes VALUES(1000000001,'Ordinary physics.','Lanterns, iron keys and hand tools.','Short blades and cudgels.','Delvers who sell what they bring up.','A dungeon cut down into black stone under a lantern-lit landing.','The dungeon was cut long ago by people nobody remembers.','Coin for what comes up from below.','Nobody governs the dungeon.','Delvers leave a coin on the landing for luck.','2026-10-30 16:58:16.173191','2026-10-30 16:58:16.173191',NULL,NULL); +INSERT INTO universes VALUES(1000000001,'Ordinary physics.','Lanterns, iron keys and hand tools.','Short blades and cudgels.','Delvers who sell what they bring up.','A dungeon cut down into black stone under a lantern-lit landing.','The dungeon was cut long ago by people nobody remembers.','Coin for what comes up from below.','Nobody governs the dungeon.','Delvers leave a coin on the landing for luck.','2026-10-30 17:25:21.012687','2026-10-30 17:25:21.012687',NULL,NULL); CREATE TABLE IF NOT EXISTS "characters" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "story_id" integer NOT NULL, "fullname" varchar, "nickname" varchar, "age" integer, "sex" varchar, "personality" text, "appearance" text, "likes" text, "dislikes" text, "fears" text, "backstory" text, "is_companion" boolean, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "race_id" integer NOT NULL, "is_protagonist" boolean DEFAULT FALSE NOT NULL, "location_id" integer, "deliberately_absent" boolean DEFAULT FALSE NOT NULL, "level" integer, "hit_die" integer, "strength" integer, "dexterity" integer, "will" integer, "hostile" boolean DEFAULT FALSE NOT NULL, "x" integer, "y" integer, "conscious_desire" text, "unconscious_desire" text, "recognized_need" text, "unrecognized_need" text, "desire_pursuit" varchar, "need_pursuit" varchar, "posted_to_id" integer, CONSTRAINT "fk_rails_56a213cbe2" FOREIGN KEY ("race_id") REFERENCES "races" ("id") @@ -27,8 +27,8 @@ FOREIGN KEY ("posted_to_id") FOREIGN KEY ("story_id") REFERENCES "stories" ("id") ); -INSERT INTO characters VALUES(1000000001,1000000001,'Odile','Odile',29,'female','Careful and stubborn.','A lantern on a pole and a short blade.','A clear way back.','Doors she cannot open.','The floor giving way under her.','A delver who has never been below the first floor.',0,'2026-10-30 16:58:16.385184','2026-10-30 16:58:16.385184',1000000001,1,NULL,0,3,8,14,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); -INSERT INTO characters VALUES(1000000002,1000000001,'Wenna Cole','Wenna',41,'female','Cheerful and talkative.','A patched cloak and a sack of candles.','Company.','Silence.','Being left in the dark.','She sells candles to delvers at the head of the stair down, and has never seen a floor fall.',0,'2026-10-30 16:58:16.411049','2026-10-30 16:58:16.411049',1000000001,0,NULL,1,1,6,8,10,10,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1000000002); +INSERT INTO characters VALUES(1000000001,1000000001,'Odile','Odile',29,'female','Careful and stubborn.','A lantern on a pole and a short blade.','A clear way back.','Doors she cannot open.','The floor giving way under her.','A delver who has never been below the first floor.',0,'2026-10-30 17:25:21.266856','2026-10-30 17:25:21.266856',1000000001,1,NULL,0,3,8,14,12,12,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); +INSERT INTO characters VALUES(1000000002,1000000001,'Wenna Cole','Wenna',41,'female','Cheerful and talkative.','A patched cloak and a sack of candles.','Company.','Silence.','Being left in the dark.','She sells candles to delvers at the head of the stair down, and has never seen a floor fall.',0,'2026-10-30 17:25:21.293406','2026-10-30 17:25:21.293406',1000000001,0,NULL,1,1,6,8,10,10,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1000000002); CREATE TABLE IF NOT EXISTS "chats" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "model_id_string" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "ruby_llm_model_id" integer, "purpose" varchar, "playthrough_id" integer, "character_id" integer, "cancelled" boolean DEFAULT FALSE NOT NULL, "player_id" integer, CONSTRAINT "fk_rails_415520c982" FOREIGN KEY ("playthrough_id") REFERENCES "playthroughs" ("id") @@ -49,8 +49,8 @@ FOREIGN KEY ("down_location_id") FOREIGN KEY ("location_id") REFERENCES "locations" ("id") ON DELETE CASCADE); -INSERT INTO floors VALUES(1000000001,1000000002,2,'safe','none',1000000003,'2026-10-30 16:58:16.247440','2026-10-30 16:58:16.277807',NULL,0,30); -INSERT INTO floors VALUES(1000000002,1000000003,2,'safe','none',NULL,'2026-10-30 16:58:16.265273','2026-10-30 16:58:16.282347',NULL,0,NULL); +INSERT INTO floors VALUES(1000000001,1000000002,2,'safe','none',1000000003,'2026-10-30 17:25:21.105672','2026-10-30 17:25:21.143560',NULL,0,30); +INSERT INTO floors VALUES(1000000002,1000000003,2,'safe','none',NULL,'2026-10-30 17:25:21.127746','2026-10-30 17:25:21.149235',NULL,0,NULL); CREATE TABLE IF NOT EXISTS "interactions" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "character_id" integer NOT NULL, "scene_id" integer, "location_id" integer, "pre_thought" text, "pre_feeling" text, "action" text, "post_feeling" text, "post_thought" text, "inner_resolution" text, "summary" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "user_input" text, "engine_action" varchar, "action_status" varchar, "action_fact" text, CONSTRAINT "fk_rails_294d4ae9f8" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") @@ -93,10 +93,10 @@ FOREIGN KEY ("key_template_id") FOREIGN KEY ("connected_location_id") REFERENCES "locations" ("id") ); -INSERT INTO location_connections VALUES(1000000001,1000000001,1000000002,'adjacent','about a minute','taking stairs','2026-10-30 16:58:16.473881','2026-10-30 16:58:16.473881',NULL,NULL,'open',NULL); -INSERT INTO location_connections VALUES(1000000002,1000000002,1000000001,'adjacent','about a minute','taking stairs','2026-10-30 16:58:16.476405','2026-10-30 16:58:16.476405',NULL,NULL,'open',NULL); -INSERT INTO location_connections VALUES(1000000003,1000000002,1000000003,'adjacent','about a minute','taking stairs','2026-10-30 16:58:16.477946','2026-10-30 16:58:16.477946',NULL,NULL,'open',NULL); -INSERT INTO location_connections VALUES(1000000004,1000000003,1000000002,'adjacent','about a minute','taking stairs','2026-10-30 16:58:16.479047','2026-10-30 16:58:16.479047',NULL,NULL,'open',NULL); +INSERT INTO location_connections VALUES(1000000001,1000000001,1000000002,'adjacent','about a minute','taking stairs','2026-10-30 17:25:21.369868','2026-10-30 17:25:21.369868',NULL,NULL,'open',NULL); +INSERT INTO location_connections VALUES(1000000002,1000000002,1000000001,'adjacent','about a minute','taking stairs','2026-10-30 17:25:21.372514','2026-10-30 17:25:21.372514',NULL,NULL,'open',NULL); +INSERT INTO location_connections VALUES(1000000003,1000000002,1000000003,'adjacent','about a minute','taking stairs','2026-10-30 17:25:21.374280','2026-10-30 17:25:21.374280',NULL,NULL,'open',NULL); +INSERT INTO location_connections VALUES(1000000004,1000000003,1000000002,'adjacent','about a minute','taking stairs','2026-10-30 17:25:21.375751','2026-10-30 17:25:21.375751',NULL,NULL,'open',NULL); CREATE TABLE IF NOT EXISTS "locations" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "story_id" integer NOT NULL, "name" varchar, "description" text, "lore" text, "last_protagonist_visit" datetime(6), "parent_location_id" integer, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "detail_level" varchar DEFAULT 'stub' NOT NULL, "teaser" text, "mobile" boolean DEFAULT FALSE NOT NULL, "danger" varchar DEFAULT 'safe' NOT NULL, "hazard" varchar, "hazard_die" integer, "x" integer, "y" integer, "z" integer, "width" integer, "depth" integer, "population" varchar, "generation_checkpoint" json, "surface" varchar, "kind" varchar, "density" varchar, CONSTRAINT "fk_rails_5bc98acf09" FOREIGN KEY ("parent_location_id") REFERENCES "locations" ("id") @@ -104,9 +104,9 @@ FOREIGN KEY ("parent_location_id") FOREIGN KEY ("story_id") REFERENCES "stories" ("id") ); -INSERT INTO locations VALUES(1000000001,1000000001,'The Landing','A square landing of black stone lit by one lantern, a stair going down from it.','Delvers gather here before they go down.',NULL,NULL,'2026-10-30 16:58:16.219716','2026-10-30 16:58:16.219716','realized','A lantern-lit landing.',0,'safe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,'nobody',NULL,NULL,NULL,NULL); -INSERT INTO locations VALUES(1000000002,1000000001,'The First Floor',NULL,NULL,NULL,NULL,'2026-10-30 16:58:16.240052','2026-10-30 16:58:16.240052','stub','The first level of the dungeon, cracked through, with a stair somewhere that goes further down.',0,'safe',NULL,NULL,NULL,NULL,NULL,12,6,NULL,NULL,NULL,'warren',NULL); -INSERT INTO locations VALUES(1000000003,1000000001,'The Second Floor',NULL,NULL,NULL,NULL,'2026-10-30 16:58:16.259504','2026-10-30 16:58:16.259504','stub','The second level of the dungeon, where the stair ends.',0,'safe',NULL,NULL,NULL,NULL,NULL,12,6,NULL,NULL,NULL,'warren',NULL); +INSERT INTO locations VALUES(1000000001,1000000001,'The Landing','A square landing of black stone lit by one lantern, a stair going down from it.','Delvers gather here before they go down.',NULL,NULL,'2026-10-30 17:25:21.074220','2026-10-30 17:25:21.074220','realized','A lantern-lit landing.',0,'safe',NULL,NULL,NULL,NULL,NULL,NULL,NULL,'nobody',NULL,NULL,NULL,NULL); +INSERT INTO locations VALUES(1000000002,1000000001,'The First Floor',NULL,NULL,NULL,NULL,'2026-10-30 17:25:21.097056','2026-10-30 17:25:21.097056','stub','The first level of the dungeon, cracked through, with a stair somewhere that goes further down.',0,'safe',NULL,NULL,NULL,NULL,NULL,12,6,NULL,NULL,NULL,'warren',NULL); +INSERT INTO locations VALUES(1000000003,1000000001,'The Second Floor',NULL,NULL,NULL,NULL,'2026-10-30 17:25:21.119842','2026-10-30 17:25:21.119842','stub','The second level of the dungeon, where the stair ends.',0,'safe',NULL,NULL,NULL,NULL,NULL,12,6,NULL,NULL,NULL,'warren',NULL); CREATE TABLE IF NOT EXISTS "locations_world_events" ("location_id" integer NOT NULL, "world_event_id" integer NOT NULL, CONSTRAINT "fk_rails_861146f35c" FOREIGN KEY ("location_id") REFERENCES "locations" ("id") @@ -307,7 +307,7 @@ CREATE TABLE IF NOT EXISTS "races" ("id" integer PRIMARY KEY AUTOINCREMENT NOT N FOREIGN KEY ("universe_id") REFERENCES "universes" ("id") ); -INSERT INTO races VALUES(1000000001,1000000001,'Delvers','People who go down into the dungeon for a living.','2026-10-30 16:58:16.176470','2026-10-30 16:58:16.176470',0); +INSERT INTO races VALUES(1000000001,1000000001,'Delvers','People who go down into the dungeon for a living.','2026-10-30 17:25:21.016867','2026-10-30 17:25:21.016867',0); CREATE TABLE IF NOT EXISTS "relay_receipts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "player_id" integer NOT NULL, "route" varchar NOT NULL, "model" varchar NOT NULL, "stream" boolean DEFAULT FALSE NOT NULL, "status" varchar DEFAULT 'open' NOT NULL, "reserved_usd" decimal(12,6) NOT NULL, "cost_usd" decimal(12,6), "cost_source" varchar, "input_tokens" integer, "output_tokens" integer, "upstream_status" integer, "finished_at" datetime(6), "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_deae27bd71" FOREIGN KEY ("player_id") REFERENCES "players" ("id") @@ -322,12 +322,12 @@ FOREIGN KEY ("location_id") FOREIGN KEY ("story_id") REFERENCES "stories" ("id") ); -INSERT INTO scenes VALUES(1000000001,1000000001,1000000001,NULL,'You stand on the lantern-lit landing above the first floor.','2026-10-31 08:00:00','Odile reaches the landing.','2026-10-30 16:58:16.570870','2026-10-30 16:58:16.570870',1,NULL,NULL,NULL,NULL,NULL,0,NULL); +INSERT INTO scenes VALUES(1000000001,1000000001,1000000001,NULL,'You stand on the lantern-lit landing above the first floor.','2026-10-31 08:00:00','Odile reaches the landing.','2026-10-30 17:25:21.485590','2026-10-30 17:25:21.485590',1,NULL,NULL,NULL,NULL,NULL,0,NULL); CREATE TABLE IF NOT EXISTS "stories" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar, "genre" varchar, "preface" text, "summary" text, "start_time" datetime(6), "universe_id" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "generation_snapshot" text, "people_limit" integer, "things_limit" integer, CONSTRAINT "fk_rails_2a912ea846" FOREIGN KEY ("universe_id") REFERENCES "universes" ("id") ); -INSERT INTO stories VALUES(1000000001,'A Floor That Falls (engine sweep)','dungeon crawl','The floors of this dungeon do not stay up for long.','Odile goes down before the first floor gives way, or does not.','2026-10-31 08:00:00',1000000001,'2026-10-30 16:58:16.196351','2026-10-30 16:58:16.196351',NULL,NULL,NULL); +INSERT INTO stories VALUES(1000000001,'A Floor That Falls (engine sweep)','dungeon crawl','The floors of this dungeon do not stay up for long.','Odile goes down before the first floor gives way, or does not.','2026-10-31 08:00:00',1000000001,'2026-10-30 17:25:21.043972','2026-10-30 17:25:21.043972',NULL,NULL,NULL); CREATE TABLE IF NOT EXISTS "system_one_receipts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "player_id" integer, "playthrough_id" integer, "purpose" varchar, "transport" varchar, "cost_usd" decimal(12,6) NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "cost_source" varchar DEFAULT 'estimated' NOT NULL, "latency_ms" integer, CONSTRAINT "fk_rails_4ae941c854" FOREIGN KEY ("player_id") REFERENCES "players" ("id") @@ -347,6 +347,7 @@ FOREIGN KEY ("story_id") REFERENCES "stories" ("id") ); CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY); +INSERT INTO schema_migrations VALUES('20261031120000'); INSERT INTO schema_migrations VALUES('20261031090000'); INSERT INTO schema_migrations VALUES('20261030230000'); INSERT INTO schema_migrations VALUES('20261030200000'); @@ -442,10 +443,9 @@ INSERT INTO schema_migrations VALUES('20250823023925'); INSERT INTO schema_migrations VALUES('20250822184651'); INSERT INTO schema_migrations VALUES('20250822184650'); INSERT INTO schema_migrations VALUES('20250822184649'); -INSERT INTO schema_migrations VALUES('20261031120000'); CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL); -INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-30 16:58:14.956642','2026-10-30 16:58:14.956646'); -INSERT INTO ar_internal_metadata VALUES('schema_sha1','5b41acc531745dba36c583b40f12d94bcf2d8eb7','2026-10-30 16:58:14.961749','2026-10-30 16:58:14.961751'); +INSERT INTO ar_internal_metadata VALUES('environment','test','2026-10-30 17:25:19.682905','2026-10-30 17:25:19.682910'); +INSERT INTO ar_internal_metadata VALUES('schema_sha1','c2813cee903cf430f7b8afb47fa3a3021ac3a2f9','2026-10-30 17:25:19.687990','2026-10-30 17:25:19.687993'); PRAGMA writable_schema=ON; CREATE TABLE IF NOT EXISTS sqlite_sequence(name,seq); DELETE FROM sqlite_sequence; From f46952da9861613aab139fb302af276b5ce04906 Mon Sep 17 00:00:00 2001 From: Caleb LeNoir Date: Fri, 30 Oct 2026 13:25:39 -0400 Subject: [PATCH 3/3] Run CI against the game change that lets a floor fall (renderedstep/server 383a67d) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0750b36..2e16da2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: env: # The game commit whose runner, scripts and judges this checkout is # held to (parity/README.md, "Changing a rule"). - SERVER_COMMIT: 132b475ba88e36b6d9a1bcad4087393d1353cd85 + SERVER_COMMIT: 383a67d626c440df0825ef37a38b7a5dc0a9b3d2 steps: # Side by side, not one inside the other: the game's extension is a # crate of its own, and inside this checkout cargo would read it as a