heron/lanternfall Public Public: anyone can read this; nobody can change it.
workbenchthis machine
lanternfall: let the floor talk back
Playing the live floor showed the flat spot: a plain step says nothing, which
is right, but it meant eighteen steps down a corridor left the opening line
sitting there as if nothing had happened.
Two lines, both of which a player actually needs:
- stepping onto a stair says so ('A stair down, here.', and on floor 1 'The
way out, here. Climb and the gold is yours.') - the way out is the whole
economy of the game and it was invisible until you guessed at the button.
- something entering your lantern says so ONCE, by comparing what was in
sight before the step with what is in sight after, so a monster you are
already fighting does not narrate every turn.
28 tests.
2 files
changed +34
−0
src/game.test.ts +23
| @@ -112,6 +112,29 @@ test("the stairs are the only way down, and climbing out banks the gold", () => | ||
| 112 | 112 | db.close(); |
| 113 | 113 | }); |
| 114 | 114 | |
| 115 | test("the floor says what you are standing on", () => { | |
| 116 | const db = fresh(); | |
| 117 | const delver = delverFor(db, "hand-a"); | |
| 118 | const floor = floorAt(1); | |
| 119 | db.query("UPDATE delver SET cell = ?2 WHERE id = ?1").run(delver.id, (floor.descent as number) - 1); | |
| 120 | const onto = act(db, "hand-a", "east"); | |
| 121 | expect(onto.log.join(" ")).toContain("A stair down, here."); | |
| 122 | const back = act(db, "hand-a", "west"); | |
| 123 | expect(back.log.join(" ")).not.toContain("stair"); | |
| 124 | db.close(); | |
| 125 | }); | |
| 126 | ||
| 127 | test("something coming into the light is worth saying once", () => { | |
| 128 | const db = fresh(); | |
| 129 | const delver = delverFor(db, "hand-a"); | |
| 130 | db.query( | |
| 131 | "INSERT INTO monster (depth, spawn, cell, kind, hp) VALUES (1, ?1, ?1, 'rat', 2)", | |
| 132 | ).run(delver.cell + 2); | |
| 133 | const first = act(db, "hand-a", "wait"); | |
| 134 | expect(first.log.join(" ")).toContain("still moving"); | |
| 135 | db.close(); | |
| 136 | }); | |
| 137 | ||
| 115 | 138 | test("resting heals, but only when nothing can see you", () => { |
| 116 | 139 | const db = fresh(); |
| 117 | 140 | const delver = delverFor(db, "hand-a"); |
src/game.ts +11
| @@ -204,6 +204,9 @@ export function act(db: Database, hand: string, action: Action): TurnResult { | ||
| 204 | 204 | log.push("There is no stair here."); |
| 205 | 205 | } |
| 206 | 206 | } else if (action !== "wait") { |
| 207 | const watchedBefore = store | |
| 208 | .monstersOn(db, delver.depth, now) | |
| 209 | .some((m) => distance(m.cell, delver.cell) <= LANTERN && seesFrom(floor, delver.cell, m.cell)); | |
| 207 | 210 | const here = xy(delver.cell); |
| 208 | 211 | const step = DIRECTIONS[action]; |
| 209 | 212 | const nx = here.x + step.dx; |
| @@ -250,6 +253,14 @@ export function act(db: Database, hand: string, action: Action): TurnResult { | ||
| 250 | 253 | log.push(`A cache of ${coin} gold, tucked into the wall.`); |
| 251 | 254 | } |
| 252 | 255 | } |
| 256 | const under = floor.tiles[target]; | |
| 257 | if (under === ">") log.push("A stair down, here."); | |
| 258 | else if (under === "<") | |
| 259 | log.push(delver.depth === 1 ? "The way out, here. Climb and the gold is yours." : "A stair up, here."); | |
| 260 | const watchedNow = store | |
| 261 | .monstersOn(db, delver.depth, now) | |
| 262 | .some((m) => distance(m.cell, target) <= LANTERN && seesFrom(floor, target, m.cell)); | |
| 263 | if (watchedNow && !watchedBefore) log.push("Something moves at the edge of your lantern."); | |
| 253 | 264 | const grave = store.graveAt(db, delver.depth, target); |
| 254 | 265 | if (grave && grave.looted === 0) { |
| 255 | 266 | const share = Math.floor(grave.gold / 2); |