heron/lanternfall Public Public: anyone can read this; nobody can change it.
workbenchthis machine
Files fa8aea72e9cab5ed2a004bcd8bfa540e643d8b9d
| 1 | /** |
| 2 | * Names and epitaphs, composed rather than typed. Nobody writes free text |
| 3 | * into this dungeon: a delver's name comes from their hand, and the dead |
| 4 | * speak in a fixed grammar. It keeps the world's voice consistent, and it |
| 5 | * means a public game needs no moderation. |
| 6 | */ |
| 7 | |
| 8 | const FIRST = [ |
| 9 | "Alder", "Bryn", "Coll", "Dunnock", "Eira", "Fen", "Gorse", "Hale", |
| 10 | "Isolde", "Jarrow", "Kestrel", "Lirien", "Morrow", "Nix", "Orrin", "Pell", |
| 11 | "Quill", "Rook", "Sable", "Thorn", "Ursel", "Vane", "Wren", "Yarrow", |
| 12 | ]; |
| 13 | const SECOND = [ |
| 14 | "Ashdown", "Barrowe", "Coldwater", "Dimmick", "Emberly", "Fallow", |
| 15 | "Greaves", "Hollowell", "Ironsides", "Jessop", "Kindle", "Lampwright", |
| 16 | "Mordaunt", "Nettles", "Oakes", "Pyre", "Quarles", "Rushbrook", |
| 17 | "Stannard", "Tallow", "Underhill", "Vesper", "Wickham", "Yewtree", |
| 18 | ]; |
| 19 | |
| 20 | function hash(text: string, salt = 0): number { |
| 21 | let h = 2166136261 ^ salt; |
| 22 | for (const ch of text) h = Math.imul(h ^ ch.charCodeAt(0), 16777619) >>> 0; |
| 23 | return h >>> 0; |
| 24 | } |
| 25 | |
| 26 | export function nameFor(hand: string, attempt: number): string { |
| 27 | const h = hash(hand, attempt * 7919); |
| 28 | return `${FIRST[h % FIRST.length]} ${SECOND[(h >>> 9) % SECOND.length]}`; |
| 29 | } |
| 30 | |
| 31 | const ENDINGS = [ |
| 32 | "died with their lantern still lit", |
| 33 | "was cornered in a doorway", |
| 34 | "bled out two steps from the stair", |
| 35 | "went one room further", |
| 36 | "did not hear it coming", |
| 37 | "stopped to count their gold", |
| 38 | "was found facing the wrong way", |
| 39 | "ran out of floor", |
| 40 | ]; |
| 41 | |
| 42 | const COUNSEL = [ |
| 43 | "beware the corridor", |
| 44 | "try the far door", |
| 45 | "seek the stair, not the gold", |
| 46 | "do not follow the walls", |
| 47 | "turn back at the rats", |
| 48 | "the dark room is a trap", |
| 49 | "rest before the descent", |
| 50 | "carry less, climb sooner", |
| 51 | ]; |
| 52 | |
| 53 | /** What the stone says. Deterministic in where and how the delver died. */ |
| 54 | export function epitaphFor(name: string, depth: number, cell: number, turns: number): string { |
| 55 | const h = hash(`${name}:${depth}:${cell}:${turns}`); |
| 56 | const ending = ENDINGS[h % ENDINGS.length]; |
| 57 | const counsel = COUNSEL[(h >>> 11) % COUNSEL.length]; |
| 58 | return `${name} ${ending} on floor ${depth}. Traveller: ${counsel}.`; |
| 59 | } |
| 60 | |
| 61 | export const ordinal = (n: number): string => |
| 62 | ["surface", "first", "second", "third", "fourth", "fifth"][n] ?? `floor ${n}`; |