Two Keys: co-op mystery games for couples. Eight games on four engines (story, defuse, sync, labyrinth), two-device and couch play, mobile-first UI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 22:36:12 +02:00
co-authored by Claude Opus 5.5
commit 0de6617c82
85 changed files with 15692 additions and 0 deletions
+259
View File
@@ -0,0 +1,259 @@
id: blue-lantern
engine: story
category: murder-mystery
title: The Last Set at the Blue Lantern
tagline: Cape Town, 1958. The band played on. The boss didn't.
estimatedMinutes: 50
difficulty: 2
players: { min: 2, max: 2 }
theme: { primary: "#1f3a68", accent: "#e8b04a", background: "#0b0f1c", headingFont: "Playfair Display" }
roles:
A:
name: The Detective
blurb: You have the band's paperwork, the police search log, and the stage manager's notes.
B:
name: The Regular
blurb: You've drunk here every Friday for ten years. You know the staff, the bar, and the gossip.
intro: |
Friday night at **the Blue Lantern**, the hottest jazz club on Harbour Street.
The last set kicks off at 23:00. Smoke, brass, a full house.
When the band takes its bow at 23:25, club owner **Vincent Cole** is found in his
office behind the stage. One bullet. Nobody heard a thing.
One of you is the detective who caught the case. The other is the regular who knows
everybody's business. **Put your heads together.**
suspects:
- id: ruby
name: Ruby Day
role: Singer
description: The voice of the Blue Lantern. Fought with Vincent about her pay last week.
- id: sammy
name: Sammy Keys
role: Pianist
description: Quiet genius. Writes most of the club's songs. Never talks about money.
- id: frankie
name: Frankie Moretti
role: Moneylender
description: Vincent owed him. A lot. Sat at the bar all night watching the door.
- id: lou
name: Lou Banner
role: Bartender
description: Sees everything, says nothing. Vincent's oldest employee.
- id: grace
name: Grace Cole
role: Vincent's wife
description: Hates jazz. Came tonight anyway. Nobody knows why.
chapters:
- id: ch1
title: Under the Drums
narrative: |
The office is right behind the stage. A gunshot in there should have stopped the room cold.
Unless something louder was playing at the time.
clues:
A:
- id: a1-setlist
title: Last set, taped to the piano
type: document
body: |
**THE LAST SET** (no breaks, straight through)
1. Night Train · 4 min
2. Blue Moon · 6 min
3. Take Five · 5 min
4. Caravan · 7 min
5. Goodnight Sweetheart · 3 min
B:
- id: b1-lou
title: What Lou the bartender told you
type: note
body: |
"Band kicked off the last set at eleven on the dot. Max always closes **Caravan**
with a drum solo, the **last two minutes** of the tune. Loud enough to rattle the glasses.
If there was a shot, it was under that solo. Nothing else is that loud in here."
lock:
id: lock-solo
prompt: At what time did the drum solo start?
inputType: code
format: "HH:MM"
answers: ["2320", "1120", "1120PM"]
hints:
- Add up the songs before Caravan, then work out when Caravan ended.
- Caravan runs 23:15 to 23:22.
- The solo is the last two minutes of Caravan, so it starts at 23:20.
successText: |
**23:20.** For two minutes, the whole room was drowned in drums.
That's when Vincent died.
keyName: Drumstick Key
- id: ch2
title: Who Left the Stage?
narrative: |
Everyone has an alibi. But an alibi for "the evening" isn't an alibi for 23:20 to 23:22.
clues:
A:
- id: a2-stagemanager
title: Stage manager's notes
type: report
body: |
- Ruby only sings on vocal numbers. **Caravan is instrumental**, so she's off stage for it.
- **During Max's drum solo, the rest of the band walks off** and comes back for the final bar. House tradition.
- The stage-left door leads straight to the office corridor. Ten steps.
B:
- id: b2-gossip
title: Who you saw, and who saw who
type: note
body: |
- **Frankie** was at the bar the whole set. Lou poured him a whiskey right as the drums kicked in.
- **Lou** never left the bar. You were sitting right in front of him.
- **Grace** was in the coat-check queue from about 23:15 to 23:25, complaining to Dolly the coat girl.
- **Ruby** came to the coat-check at 23:21 to borrow Dolly's lighter. Dolly lit it for her.
lock:
id: lock-alibi
prompt: Who had NO alibi during the drum solo?
inputType: choice
options:
- { id: ruby, label: Ruby Day }
- { id: sammy, label: Sammy Keys }
- { id: frankie, label: Frankie Moretti }
- { id: lou, label: Lou Banner }
- { id: grace, label: Grace Cole }
answers: ["sammy"]
hints:
- The stage manager says something surprising about who's on stage during a drum solo.
- Everyone in the band except Max leaves during the solo. Which of them has a witness?
- Ruby was at the coat-check. Sammy was off stage, alone, ten steps from the office.
successText: |
**Sammy Keys.** Everyone assumed the pianist was at his piano.
But during the solo, the piano bench sat empty.
keyName: Stage Door Key
- id: ch3
title: Vincent's Safe
narrative: |
Vincent's office safe is still locked. Four-digit dial. Lou says Vincent
"kept the combination in his head and his heart."
clues:
A:
- id: a3-sheet
title: Sheet music on Vincent's desk
type: document
body: |
*"Lantern Waltz", Vincent's favourite tune.*
The first four notes are circled in red ink:
## C · A · F · E
Scribbled underneath: *"The safe plays my song."*
B:
- id: b3-piano
title: Sticker strip on the rehearsal piano
type: diagram
body: |
Sammy numbered the white keys for the beginners' class:
```
| C | D | E | F | G | A | B |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
```
lock:
id: lock-safe
prompt: Enter the safe combination.
inputType: code
format: 4 digits
answers: ["1643"]
hints:
- One of you has the notes. The other has the numbers for the notes.
- Use the beginner sticker numbers, not the alphabet.
- C=1, A=6, F=4, E=3.
successText: |
The safe swings open. Inside: a record contract from Galaxy Records.
**Twelve songs, all written by Sammy Keys. All registered under Vincent Cole's name.**
Signing date: Monday. Advance: more than Sammy would earn in twenty years.
keyName: Safe Key
- id: ch4
title: The Missing Gun
narrative: |
Vincent was shot with his own .38 from his desk drawer. The police have torn the club apart.
No gun.
clues:
A:
- id: a4-search
title: Police search log
type: log
body: |
| Area | Searched | Result |
|------------------|----------|--------|
| Office | Yes | Empty drawer |
| Dressing rooms | Yes | Nothing |
| Bar + storeroom | Yes | Nothing |
| Coat-check | Yes | Nothing |
| Drum kit, bass case | Yes | Nothing |
| Alley bins | Yes | Nothing |
| Grand piano | **No** | Lid locked, owner's prized Steinway |
B:
- id: b4-max
title: Overheard from Max the drummer
type: transcript
body: |
"When we came back for the last chorus, Sammy's first chord sounded *wrong*.
Dead. A thunk, like something heavy was lying on the strings.
He played the rest of the night up on the high keys only. Never seen him do that."
lock:
id: lock-gun
prompt: Where is the gun hidden?
inputType: text
format: One or two words
answers: ["PIANO", "THEPIANO", "GRANDPIANO", "STEINWAY", "INSIDETHEPIANO", "INPIANO"]
hints:
- Something was sitting on some strings.
- Which part of the club did the police not search?
- The grand piano.
successText: |
The police force the lid. Lying across the low strings of the Steinway:
**Vincent's .38**, one round fired.
keyName: Piano Key
finale:
type: accusation
prompt: Four keys. One killer. Make the call together.
maxAttempts: 2
questions:
- id: who
label: Who killed Vincent Cole?
options:
- { id: ruby, label: Ruby Day }
- { id: sammy, label: Sammy Keys }
- { id: frankie, label: Frankie Moretti }
- { id: lou, label: Lou Banner }
- { id: grace, label: Grace Cole }
answer: sammy
- id: how
label: How?
options:
- { id: gunsolo, label: Shot with Vincent's .38 under the drum solo, gun hidden in the piano }
- { id: alley, label: Shot from the alley through the office window }
- { id: poison, label: Poison in his whiskey, gunshot staged later }
answer: gunsolo
- id: why
label: Why?
options:
- { id: debt, label: An unpaid gambling debt }
- { id: songs, label: Vincent stole his songs and was about to sell them }
- { id: pay, label: A fight over wages }
- { id: love, label: A love triangle with Grace }
answer: songs
successEnding: |
Sammy sits at the piano one last time and plays four notes. C, A, F, E.
"I wrote that one too," he says. "Every song he called his. I found the contract Tuesday."
He slipped off stage when the band cleared for Max's solo, took Vincent's gun from the
drawer, and was back for the final bar before anyone looked up.
**Case closed.** The band plays on. 🎷🔑
failEnding: |
The wrong person goes away. Sammy keeps playing, a little sadder every year.
reveal: |
**Sammy Keys** shot Vincent with his own **.38** during the **drum solo** and hid the gun
**inside the piano**, because Vincent had **stolen his songs** and was about to sell them.
+194
View File
@@ -0,0 +1,194 @@
id: clockmakers-attic
engine: story
category: escape-room
title: The Clockmaker's Attic
tagline: Locked in an old clockmaker's attic. Every clock is ticking. So are you.
estimatedMinutes: 40
difficulty: 1
players: { min: 2, max: 2 }
theme: { primary: "#8a5a2b", accent: "#f2c66d", background: "#17110b", headingFont: "Playfair Display" }
roles:
A:
name: The Tinkerer
blurb: You're on the workbench side. Clocks, gears, and the old man's letters.
B:
name: The Collector
blurb: You're on the shelves side. Paintings, samplers, and his treasure inventory.
intro: |
You bought an old house on a whim. The attic door was painted shut.
Tonight you finally pry it open, step inside together... and it **slams behind you**.
Dust. Brass. A hundred clocks, all still ticking. A letter on the workbench:
> *"Whoever finds my attic, welcome. I've hidden the way out in my favourite things.
> Work together. Nothing in here opens for one pair of hands. — Old Mr Albrecht"*
A tall beam of shelves splits the room in half. You can hear each other, but you can't see each other's side.
chapters:
- id: ch1
title: The Three Clocks
narrative: |
An old sea trunk sits in the middle of the room. Brass padlock, three dials.
clues:
A:
- id: a1-clocks
title: Three clocks on the workbench
type: diagram
body: |
```
🐦 ROBIN clock ..... hour hand on 3
🦉 OWL clock ....... hour hand on 7
🦊 FOX clock ....... hour hand on 9
```
All three stopped. The trunk lock has three dials, 0–9.
B:
- id: b1-sampler
title: Embroidered sampler on the wall
type: note
body: |
*Stitched in faded thread:*
**First the bird that sings the dawn,
then the hunter on the lawn,
last the bird that rules the night.
Set them so, and set them right.**
lock:
id: lock-trunk
prompt: Set the trunk's three dials.
inputType: code
format: 3 digits
answers: ["397"]
hints:
- The sampler gives you an order. The clocks give you numbers.
- Dawn bird = robin. Hunter = fox. Night bird = owl.
- Robin 3, fox 9, owl 7.
successText: |
The trunk creaks open. Inside, wrapped in velvet: a **music box** with four coloured buttons,
and a small brass key.
keyName: Trunk Key
- id: ch2
title: The Music Box
narrative: |
The music box is silent. Four buttons on its lid: **red, blue, green, gold**.
Press them in the right order and it should play.
clues:
A:
- id: a2-musicbox
title: Engraving under the lid
type: note
body: |
*"Play the colours of the sky-bow, top to bottom.
All but the one my pendulum stole."*
B:
- id: b2-painting
title: Small oil painting on the shelf
type: photo
body: |
*A painted rainbow over a village. Only four bands, top to bottom:
**red, gold, green, blue**.*
- id: b2-pendulum
title: The grandfather clock on your side
type: photo
body: |
*A tall clock. Its brass pendulum has been painted a deep **green**.
A label on the case: "Borrowed a colour for my favourite clock. — A."*
lock:
id: lock-musicbox
prompt: Which order do you press the buttons?
inputType: text
format: Three colours, in order
answers: ["REDGOLDBLUE"]
hints:
- The "sky-bow" is a rainbow. The Collector can see one.
- The pendulum stole green. Leave it out.
- Red, gold, blue.
successText: |
The music box plays a tinkling waltz. A drawer pops out of its base.
Inside: a crank handle and a sketch of the **gear wall**.
keyName: Music Box Key
- id: ch3
title: The Gear Wall
narrative: |
On the far wall, three brass gears mesh together. A crank socket sits on the biggest one.
A drawer below has a number dial.
clues:
A:
- id: a3-gears
title: The gear wall
type: diagram
body: |
```
(Gear A: 48 teeth) ⟷ (Gear B: 16 teeth) ⟷ (Gear C: 24 teeth)
↑ crank
```
Each gear meshes with the next. Every tooth that passes on one passes on the other.
B:
- id: b3-instructions
title: Note pinned to the shelf
type: note
body: |
*Turn my crank exactly **5 full turns**.
The drawer wants to know how many full turns **Gear C** makes.*
lock:
id: lock-gears
prompt: How many turns does Gear C make?
inputType: code
format: A number
answers: ["10"]
hints:
- Count teeth. Five turns of Gear A moves 5 × 48 teeth past every gear.
- Gear B doesn't change the answer. Only the first and last gear matter.
- 240 teeth ÷ 24 teeth per turn = 10.
successText: |
The drawer slides open. Inside: Mr Albrecht's last letter, and his treasure inventory, complete.
keyName: Gear Key
finale:
type: escape
prompt: The attic door has a seven-letter word lock.
clues:
A:
- id: af-letter
title: Mr Albrecht's last letter
type: letter
body: |
*My dears,*
*Line up my seven treasures **in the order I made them**, oldest first.
Their first letters are the word I'd give the two of you.*
*— A.*
B:
- id: bf-inventory
title: Treasure inventory (dates engraved on each)
type: table
body: |
| Treasure | Year made |
|----------------|-----------|
| Violin | 1922 |
| Fob watch | 1902 |
| Rocking horse | 1915 |
| Egg timer | 1931 |
| Oil lamp | 1909 |
| Rug loom | 1938 |
| Easel | 1920 |
lock:
id: lock-door
prompt: Enter the seven-letter word.
inputType: text
format: 7 letters
answers: ["FOREVER"]
hints:
- Sort the treasures by year, oldest to newest.
- "1902 Fob watch, 1909 Oil lamp, 1915 Rocking horse..."
- F-O-R-E-V-E-R.
successEnding: |
**FOREVER.** The lock clicks. The door swings open onto the stairs.
On the back of the door, carved into the wood: *"Albrecht & Marta, 1901. Forever."*
He built the whole attic as a puzzle for his wife. Now it was one for you.
**You escaped!** 🕰️🔑
+92
View File
@@ -0,0 +1,92 @@
id: in-sync
engine: sync
category: co-op
title: In Sync
tagline: How well do you really know each other? Prove it. Three keys, three rounds.
estimatedMinutes: 20
difficulty: 1
players: { min: 2, max: 2 }
theme: { primary: "#c2185b", accent: "#ffb3c7", background: "#14080d", headingFont: "Playfair Display" }
roles:
A:
name: Partner A
blurb: You'll be in the spotlight in Round 2.
B:
name: Partner B
blurb: You'll be in the spotlight in Round 3.
intro: |
No clues. No killer. Just the two of you.
Answer privately. Answers flip over at the same time.
**Match on 3 out of 5 to earn a key.** Miss a round and you get one replay with new questions.
No peeking at each other's screens. We'll know. (We won't. But you will.) 😏
rules:
questionsPerRound: 5
pointsForKey: 3
replaysPerRound: 1
rounds:
- id: r1
title: Same Wavelength
type: match
blurb: Both pick the answer you think your partner will pick. Same answer = point.
keyName: Wavelength Key
- id: r2
title: All About A
type: predict
subject: A
blurb: Partner A answers honestly about themselves. Partner B guesses.
keyName: Spotlight Key A
- id: r3
title: All About B
type: predict
subject: B
blurb: Partner B answers honestly about themselves. Partner A guesses.
keyName: Spotlight Key B
# MATCH bank: both see the same question and options.
match:
- { id: m01, q: "Perfect Friday night?", options: [Movie on the couch, Dinner out, Friends over, Early night] }
- { id: m02, q: "Our dream holiday?", options: [Beach, Mountains, Big city, Road trip] }
- { id: m03, q: "Best pizza topping?", options: [Pepperoni, Mushroom, Pineapple, Just cheese] }
- { id: m04, q: "If we got a pet tomorrow?", options: [Dog, Cat, Fish, Absolutely not] }
- { id: m05, q: "Who'd survive longer in a zombie movie?", options: [Me, My partner, Neither, We'd stick together] }
- { id: m06, q: "Best season?", options: [Summer, Autumn, Winter, Spring] }
- { id: m07, q: "Our go-to takeaway?", options: [Burgers, Pizza, Sushi, Curry] }
- { id: m08, q: "Best way to make up after a fight?", options: [Talk it out, Hug first, Snacks, Give it an hour] }
- { id: m09, q: "Morning person or night owl? (Us as a couple)", options: [Morning, Night, Depends on the coffee, Neither] }
- { id: m10, q: "Which superpower would we share?", options: [Teleporting, Mind reading, Time travel, Flying] }
- { id: m11, q: "Rainy Sunday plan?", options: [Board games, Series binge, Baking, Sleep in] }
- { id: m12, q: "Who says 'I love you' more?", options: [Me, My partner, Even, We say it with snacks] }
- { id: m13, q: "Best date we could do this month?", options: [Picnic, Concert, Cooking class, Stargazing] }
- { id: m14, q: "Coffee or tea?", options: [Coffee, Tea, Both, Neither] }
- { id: m15, q: "Our couple theme song genre?", options: [Hip-hop, Worship, Pop, Old-school love songs] }
- { id: m16, q: "Ideal house?", options: [Farmhouse, City apartment, Beach cottage, Cabin in the woods] }
# PREDICT bank: subject answers about themselves; partner guesses. Text is shown with the
# subject's name swapped in: "{subject}" → their role name, "you/your" for the subject's own view.
predict:
- { id: p01, q: "{subject}'s comfort food?", options: [Mac and cheese, Chocolate, Chips, Something spicy] }
- { id: p02, q: "{subject}'s biggest pet peeve?", options: [Loud chewing, Being late, Messy kitchen, Slow walkers] }
- { id: p03, q: "{subject}'s ideal birthday?", options: [Big party, Quiet dinner, Surprise trip, Day off doing nothing] }
- { id: p04, q: "What would {subject} grab first in a fire (people and pets safe)?", options: [Phone, Photos, Laptop, A sentimental item] }
- { id: p05, q: "{subject}'s love language?", options: [Words, Time together, Gifts, Acts of service, Touch] }
- { id: p06, q: "{subject}'s secret talent?", options: [Singing, Cooking, Impressions, Dancing] }
- { id: p07, q: "{subject} would rather...", options: [Never use social media again, Never eat dessert again] }
- { id: p08, q: "{subject}'s scariest thing?", options: [Heights, Spiders, Deep water, Public speaking] }
- { id: p09, q: "{subject}'s dream job (if money didn't matter)?", options: [Artist, Chef, Traveller, Teacher] }
- { id: p10, q: "{subject}'s go-to karaoke move?", options: [Power ballad, Rap verse, Duet, Refuse entirely] }
- { id: p11, q: "How does {subject} recharge?", options: [Alone time, Friends, Outdoors, Sleep] }
- { id: p12, q: "{subject}'s favourite way to receive a compliment?", options: [In private, In front of people, In writing, With a hug] }
- { id: p13, q: "{subject}'s worst habit?", options: [Phone scrolling, Procrastinating, Snacking late, Overthinking] }
- { id: p14, q: "What does {subject} miss most about childhood?", options: [No responsibilities, School friends, Holidays, Mom's cooking] }
- { id: p15, q: "{subject}'s perfect lazy breakfast?", options: [Pancakes, Toast and eggs, Cereal, Just coffee] }
- { id: p16, q: "If {subject} won the lotto, first buy?", options: [House, Car, Trip, Pay off family's debts] }
- { id: p17, q: "{subject} in a group project is...", options: [The leader, The ideas person, The quiet worker, The one who brings snacks] }
- { id: p18, q: "{subject}'s favourite memory of us?", options: [Our first date, A trip we took, A random ordinary day, Something only we know] }
- { id: p19, q: "When {subject} is stressed, they need...", options: [Space, A hug, A plan, Food] }
- { id: p20, q: "{subject}'s favourite movie genre?", options: [Comedy, Action, Romance, Thriller] }
- { id: p21, q: "What would {subject} name a pet goldfish?", options: [Something funny, A human name, A food name, A famous person's name] }
- { id: p22, q: "{subject}'s most-used emoji?", options: ["😂", "❤️", "🙏", "🔥"] }
+87
View File
@@ -0,0 +1,87 @@
id: labyrinth-of-lanterns
engine: labyrinth
category: co-op
title: Labyrinth of Lanterns
tagline: One of you is lost in the dark. The other holds the only map.
estimatedMinutes: 25
difficulty: 2
players: { min: 2, max: 2 }
theme: { primary: "#3d2c6b", accent: "#ffb347", background: "#09070f", headingFont: "Playfair Display" }
roles:
A:
name: Navigator
blurb: You hold the ancient map. You can see every wall, glyph and trap. You can't see your partner.
B:
name: Explorer
blurb: You're inside the labyrinth with one candle. You only see the floor right under your feet.
intro: |
Beneath an old cathedral lies a labyrinth built for lovers. Legend says only a couple who
truly listen to each other can carry the **three lanterns** out.
**Explorer:** describe every glyph you step on and which ways you can go.
**Navigator:** work out where they are, guide them to the lanterns, and steer them **around the traps**.
The Explorer can't see the traps. One wrong step and they wake up back at the entrance.
# Grid legend:
# # wall . floor S start
# E exit (opens only when all 3 lanterns are carried)
# K lantern key (collected on entry)
# T trap (invisible to Explorer; on entry → back to S, keys kept, +60s penalty)
# Coordinates are (x, y) with (0,0) top-left.
grid:
- "###########"
- "#S..#.....#"
- "#.#.#.###.#"
- "#.#...#K..#"
- "#.#####.#.#"
- "#...T...#K#"
- "###.###.#.#"
- "#K..#.T.#.#"
- "#.###.#.#.#"
- "#..T....#E#"
- "###########"
# Floor glyphs: Explorer sees the glyph of their current cell; Navigator sees all of them on the map.
# Many floor cells are deliberately blank so the Navigator has to track moves, not just glyphs.
glyphs:
- { x: 1, y: 1, glyph: sun, label: "☀ A carved sun (the entrance)" }
- { x: 3, y: 1, glyph: spiral, label: "🌀 A spiral" }
- { x: 5, y: 3, glyph: crescent, label: "☾ A crescent moon" }
- { x: 9, y: 1, glyph: eye, label: "👁 An open eye" }
- { x: 1, y: 5, glyph: feather, label: "🪶 A feather" }
- { x: 3, y: 7, glyph: wave, label: "🌊 Three wavy lines" }
- { x: 7, y: 5, glyph: crown, label: "👑 A crown" }
- { x: 7, y: 9, glyph: star, label: "★ A star" }
- { x: 9, y: 7, glyph: rose, label: "🌹 A rose" }
- { x: 5, y: 9, glyph: heart, label: "♥ Two hearts, intertwined" }
lanterns:
- { x: 7, y: 3, name: Lantern of Patience }
- { x: 9, y: 5, name: Lantern of Trust }
- { x: 1, y: 7, name: Lantern of Listening }
explorerView:
# What the Explorer gets each move (server-built, never the grid):
# { glyphLabel | null, open: { n, e, s, w }, lanternHere: bool, keysCarried: n, atExit: bool }
flavour:
blank: "Bare stone. Your candle flickers."
trap: "The floor gives way! Darkness... you wake up at the carved sun."
lantern: "A lantern glows to life in your hands! ({name})"
exitLocked: "A heavy door with three empty lantern hooks. It won't budge."
exitOpen: "You hang the three lanterns. The door swings open onto starlight."
navigatorView:
# Navigator sees the full grid, glyphs, lanterns (dimmed once collected), traps, exit.
# Navigator NEVER receives the Explorer's coordinates. They do get: keysCarried, and a
# toast "Your Explorer fell into a trap and is back at the entrance." when it happens.
showMoveCount: true
scoring:
trapPenaltySeconds: 60
parMoves: 60
successEnding: |
The Explorer climbs out into the cathedral garden carrying three lanterns.
The Navigator is waiting with the map rolled up and a grin.
**Patience. Trust. Listening.** Turns out that's the whole map. 🏮🔑
+323
View File
@@ -0,0 +1,323 @@
id: southern-star
engine: story
category: murder-mystery
title: Murder on the Southern Star
tagline: An overnight train through the Karoo. A diamond king. A long, cold night.
estimatedMinutes: 70
difficulty: 3
players: { min: 2, max: 2 }
theme: { primary: "#2e4a3a", accent: "#c9a227", background: "#0d1411", headingFont: "Playfair Display" }
roles:
A:
name: The Passenger Detective
blurb: You're travelling in carriage 5. You have the crew's notebooks and the passengers' papers.
B:
name: The Railway Inspector
blurb: You work for the line. You have the train plans, the timetable, and the platform reports.
intro: |
1936. The **Southern Star** sleeper leaves Cape Town for Johannesburg at 18:00.
Leather, brass, and a dining car that serves oysters in the middle of the desert.
At 06:30, the steward knocks on the Gold Suite with **Lord Henry Ashby's** morning tea.
No answer. The door is locked.
Ashby made his fortune in diamonds and his enemies in everything else.
The train won't stop until Kimberley. **You have until then.**
suspects:
- id: thomas
name: Thomas Webb
role: Ashby's valet
description: Served him for fifteen years. Knows every habit, every grudge.
- id: vera
name: Countess Vera Orlova
role: Travelling aristocrat
description: Glamorous, charming, dripping in diamonds. Dined with Ashby last night.
- id: hugo
name: Dr Hugo Brandt
role: Physician
description: Says he's travelling to a medical conference. Asks a lot of questions for a doctor.
- id: penny
name: Miss Penny Lowe
role: Governess
description: Travelling alone. Was seen crying in the dining car.
- id: abel
name: Abel Mokoena
role: Conductor
description: Twenty years on the line. Holds the master key box.
chapters:
- id: ch1
title: The Gold Suite
narrative: |
The Gold Suite is locked from the inside. The conductor's master key box has a
four-digit lock, and the conductor swears he "always works it out from the plans."
clues:
A:
- id: a1-conductor
title: Conductor's notebook (inside cover)
type: note
body: |
*Master box, my little rule so I never write it down:*
**carriage number × compartment number. Always four digits, pad with zeros at the front.**
B:
- id: b1-plan
title: Southern Star, first-class plan
type: map
body: |
```
CARRIAGE 4 | Comp 10 Orlova | Comp 11 Lowe | Comp 12 (empty)
CARRIAGE 5 | Comp 17 Brandt | Comp 18 (you) | Comp 19 Webb
CARRIAGE 6 | Comp 23 ★ GOLD SUITE ★ (Lord Ashby) | Comp 24 Linen
```
lock:
id: lock-master
prompt: Open the master key box.
inputType: code
format: 4 digits
answers: ["0138"]
hints:
- You need two numbers from the train plan and one rule from the conductor.
- Gold Suite is carriage 6, compartment 23.
- 6 × 23 = 138, padded to four digits.
successText: |
The box opens. The conductor's master key turns the lock.
Lord Ashby lies on the floor of the Gold Suite. His gold pocket watch, shattered, lies beside him.
keyName: Master Key
- id: ch2
title: The Stopped Watch
narrative: |
The pocket watch smashed when he fell. Its hands are frozen.
An obvious clue. Maybe too obvious.
clues:
A:
- id: a2-watch
title: Ashby's broken pocket watch
type: photo
body: |
*A gold hunter watch, glass cracked. The hands are stopped at exactly **01:15**.
Engraved inside the lid: "Greenwich, always."*
- id: a2-valet
title: Statement of Thomas Webb, valet
type: transcript
body: |
"His Lordship never changed that watch from **Greenwich time**. Drove me mad.
He said a gentleman doesn't chase the sun. Out here it's always **two hours behind** the station clocks."
- id: a2-doctor
title: Dr Brandt's examination
type: report
body: |
Single small puncture wound at the base of the skull. Very thin, very deep.
Something like a long needle. Death almost instant.
B:
- id: b2-timetable
title: Southern Star timetable (local time)
type: table
body: |
| Station | Arrive | Depart |
|------------------|--------|--------|
| Cape Town | · | 18:00 |
| Worcester | 21:05 | 21:15 |
| Touws River | 23:40 | 23:50 |
| **Matjiesfontein** | **03:10** | **03:20** |
| Laingsburg | 04:05 | 04:10 |
| Kimberley | 13:30 | · |
- id: b2-steward
title: Night steward's report
type: report
body: |
Heard a heavy thump from carriage 6 while we stood at a platform.
Didn't think anything of it. Luggage falls all the time.
lock:
id: lock-tod
prompt: What was the real local time of the murder?
inputType: code
format: "HH:MM"
answers: ["0315", "315", "315AM"]
hints:
- The watch shows a time, but not local time.
- Greenwich is two hours behind local time here. Add two hours.
- 01:15 + 2 hours = 03:15, while the train was stopped at Matjiesfontein.
successText: |
**03:15 local time.** The train was standing at **Matjiesfontein** platform from 03:10 to 03:20.
That's when the thump was heard. Someone used those ten minutes.
keyName: Watch Key
- id: ch3
title: The Telegram
narrative: |
Tucked into Ashby's Bible: a telegram, collected at Worcester. No sender's name.
clues:
A:
- id: a3-telegram
title: Telegram, received Worcester 21:10
type: letter
body: |
```
HAVE THE DOCUMENTS READY STOP
USE THE SLEEPER NOT THE DAY COACH STOP
GIVE NOTHING TO THE COUNTESS STOP
ONLY CASH NO CHEQUES STOP
DESTROY THIS MESSAGE STOP
```
B:
- id: b3-ledger
title: Note in Ashby's ledger
type: note
body: |
*My private man never signs a telegram. He hides his name where every line begins.
Clever chap. Worth every penny.*
lock:
id: lock-telegram
prompt: Who sent the telegram?
inputType: text
format: A first name
answers: ["HUGO", "DRHUGO", "HUGOBRANDT", "DRHUGOBRANDT", "BRANDT"]
hints:
- The ledger tells you exactly where to look.
- Read the first letter of each line, top to bottom.
- H · U · G · O.
successText: |
**HUGO.** "Dr" Hugo Brandt isn't going to a conference. He's Ashby's **private investigator**.
And the telegram warned Ashby about one person by title: **the Countess**.
keyName: Telegram Key
- id: ch4
title: The Ten Minutes at Matjiesfontein
narrative: |
Everyone says they were asleep at 03:15. One of them is lying.
clues:
A:
- id: a4-corridor
title: Night steward's corridor watch
type: log
body: |
I sat at the **connecting door between carriages 4, 5 and 6** from 02:00 to 04:00.
**Nobody passed through the corridor.** I'd swear it on my mother.
- id: a4-luggage
title: Passenger luggage declarations
type: table
body: |
| Passenger | Notable items |
|----------------|---------------------------------------|
| Countess Orlova| Sable **fur coat**, fur hat, jewel case |
| Miss Lowe | Wool cardigan, two suitcases |
| Dr Brandt | Doctor's bag, camera |
| Mr Webb | Valet kit, uniform |
B:
- id: b4-porter
title: Matjiesfontein platform porter's report
type: report
body: |
03:12 · A lady in a **long fur coat** stepped down from **carriage 6** onto the platform.
03:18 · Same lady walked quickly along the platform and climbed back aboard at **carriage 4**.
She was holding her hat on with one hand. Her hatpin must have come loose.
Nobody else left the train.
lock:
id: lock-liar
prompt: Who lied about being asleep?
inputType: choice
options:
- { id: thomas, label: Thomas Webb }
- { id: vera, label: Countess Vera Orlova }
- { id: hugo, label: Dr Hugo Brandt }
- { id: penny, label: Miss Penny Lowe }
- { id: abel, label: Abel Mokoena }
answers: ["vera"]
hints:
- If nobody used the corridor, how did someone get from carriage 6 back to carriage 4?
- The porter saw a lady in a fur coat. Who packed a fur coat?
- Countess Orlova.
successText: |
**Countess Vera Orlova.** She walked the platform to dodge the steward in the corridor.
Carriage 4 is hers. Carriage 6 is the Gold Suite. And her "loose" hatpin is now missing from her hat.
keyName: Platform Key
- id: ch5
title: Ashby's Last Note
narrative: |
In the Gold Suite's writing desk: Ashby's notebook. One line of numbers, underlined twice.
clues:
A:
- id: a5-notebook
title: Ashby's notebook, last entry
type: note
body: |
*The "Countess". Her stones are paste. Ask the line, it knows:*
## V. = 3 · 7 · 8 · 4 · 2 · 8
*Confirm with Hugo at Kimberley. Then the police.*
B:
- id: b5-stops
title: Karoo branch stopping list (numbered)
type: document
body: |
1. Ashton
2. Elandsdrift
3. Fraserburg Road
4. Grootvlei
5. Klaarstroom
6. Matjiesfontein
7. Ottersrust
8. Rietbron
9. Touws River
lock:
id: lock-motive
prompt: What did Ashby discover Vera was?
inputType: text
format: One word
answers: ["FORGER", "AFORGER", "JEWELFORGER"]
hints:
- '"Ask the line": the numbers point at the railway stops.'
- Take the first letter of each numbered stop.
- 3=F, 7=O, 8=R, 4=G, 2=E, 8=R.
successText: |
**FORGER.** Ashby, the diamond king, saw it over dinner: the Countess's diamonds were fakes.
And so was the Countess. Vera Orlova had sold forged stones to half of Johannesburg society.
keyName: Diamond Key
finale:
type: accusation
prompt: Kimberley is in sight. Name the killer before the train stops.
maxAttempts: 2
questions:
- id: who
label: Who killed Lord Ashby?
options:
- { id: thomas, label: Thomas Webb }
- { id: vera, label: Countess Vera Orlova }
- { id: hugo, label: Dr Hugo Brandt }
- { id: penny, label: Miss Penny Lowe }
- { id: abel, label: Abel Mokoena }
answer: vera
- id: how
label: How?
options:
- { id: hatpin, label: Stabbed at the base of the skull with her hatpin }
- { id: poison, label: Poison in his nightcap }
- { id: strangled, label: Strangled with a silk scarf }
- { id: watch, label: Struck with his own pocket watch }
answer: hatpin
- id: why
label: Why?
options:
- { id: will, label: She was named in his will }
- { id: expose, label: He had found out she was a jewel forger and was going to the police }
- { id: love, label: He rejected her advances }
- { id: debt, label: She owed him money }
answer: expose
successEnding: |
The Countess laughs, then stops. "He looked at my necklace over the soup and *smiled*.
I knew that smile. I've seen it on jewellers."
At Matjiesfontein she stepped onto the platform, slipped into the Gold Suite from outside,
and drove her hatpin home while the train stood still. Ten minutes. Back in her bed by 03:20.
**Case closed.** The Southern Star rolls into Kimberley on time. 🚂🔑
failEnding: |
The Countess steps off at Kimberley, smiles at the police, and vanishes into the crowd.
reveal: |
**Countess Vera Orlova** stabbed Ashby with her **hatpin** during the Matjiesfontein stop,
because he'd discovered she was a **jewel forger** and was going to the police.
+196
View File
@@ -0,0 +1,196 @@
id: station-echo
engine: story
category: escape-room
title: "Signal Lost: Station Echo"
tagline: An Antarctic research station. The power's out. The air is running thin.
estimatedMinutes: 45
difficulty: 2
players: { min: 2, max: 2 }
theme: { primary: "#2b6f8a", accent: "#7fe3ff", background: "#060d12", headingFont: "Playfair Display" }
roles:
A:
name: Comms Officer
blurb: You're locked in the radio room. Manuals, crew records, the commander's logs.
B:
name: Engineer
blurb: You're in the generator bay. Panels, plaques, sensors, and a blinking warning light.
intro: |
**Station Echo**, Antarctica. Winter. Six months of night.
A storm knocked out the main power at 02:14. The rest of the crew evacuated on the last shuttle.
You two missed it. Now you're sealed in separate modules, talking over a crackly intercom.
Emergency air: **45 minutes.** Restore the generator, find the leak, and call home.
chapters:
- id: ch1
title: The Warning Light
narrative: |
In the generator bay, a red warning lamp is blinking a pattern. Over and over.
The station computer is down. That light is trying to tell you something.
clues:
A:
- id: a1-morse
title: Radio room laminated card
type: table
body: |
| | | | | | |
|---|---|---|---|---|---|
| A `.-` | B `-...` | C `-.-.` | D `-..` | E `.` | F `..-.` |
| G `--.` | H `....` | I `..` | J `.---` | K `-.-` | L `.-..` |
| M `--` | N `-.` | O `---` | P `.--.` | Q `--.-` | R `.-.` |
| S `...` | T `-` | U `..-` | V `...-` | W `.--` | X `-..-` |
| Y `-.--` | Z `--..` | | | | |
*Short flash = dot. Long flash = dash. Long dark gap = new letter.*
B:
- id: b1-lamp
title: Generator bay warning lamp
type: signal
signal: "...-/./-./-"
body: |
A red lamp, pulsing in short and long flashes, then going dark and starting again.
Describe what you see to the Comms Officer.
lock:
id: lock-lamp
prompt: What is the warning light saying?
inputType: text
format: One word
answers: ["VENT"]
hints:
- The Engineer should call out "short" or "long" for each flash, and say when there's a gap.
- There are four letters. The first is three short flashes and a long one.
- V · E · N · T.
successText: |
**VENT.** The air vents are open to a breached module. Somewhere, your air is leaking out.
A panel lights up: *SEAL BREACHED MODULE TO CONTINUE.*
keyName: Signal Key
- id: ch2
title: Find the Breach
narrative: |
Five modules. One of them is bleeding air. Seal the wrong one, and you waste the time you don't have.
clues:
A:
- id: a2-readings
title: Live pressure readings (radio room screen)
type: table
body: |
| Module | Code | Pressure now (kPa) |
|-----------------|-------|--------------------|
| Habitat | HAB-1 | 98.1 |
| Laboratory | LAB-2 | 85.0 |
| Greenhouse | GRN-3 | 64.0 |
| Comms | COM-4 | 70.2 |
| Docking bay | DCK-5 | 110.0 |
B:
- id: b2-baseline
title: Engineering plaque, generator bay
type: document
body: |
**NORMAL OPERATING PRESSURE**
HAB-1: 101.3 · LAB-2: 101.3 · GRN-3: 95.0 · COM-4: 90.0 · DCK-5: 120.0 (kPa)
⚠️ **A module is BREACHED when its pressure has dropped by MORE THAN 30% of normal.**
lock:
id: lock-breach
prompt: Which module is breached? (module code)
inputType: text
format: e.g. ABC-1
answers: ["GRN3", "GREENHOUSE", "GRN"]
hints:
- Work out each module's drop as a percentage of its own normal pressure.
- Comms dropped about 22%. Lab dropped about 16%. One drops by over 30%.
- "Greenhouse: 95 → 64 is a drop of about 33%."
successText: |
Blast doors thunder shut on the **Greenhouse**. The hissing stops.
The air gauge steadies. Now get the power back on.
keyName: Seal Key
- id: ch3
title: Generator Restart
narrative: |
The generator console wants a restart code. A sticky note is taped to the console.
clues:
A:
- id: a3-roster
title: Crew roster (radio room)
type: table
body: |
| Crew member | Status at 02:30 |
|---------------|-----------------|
| Cmdr Okafor | Evacuated ✈️ |
| Dr Lindqvist | Evacuated ✈️ |
| Dr Petersen | Aboard |
| Sgt Moyo | Aboard |
| Dr Chen | Evacuated ✈️ |
| Tech Ruiz | Evacuated ✈️ |
| **You (Comms)** | Aboard |
| **You (Engineer)** | Aboard |
| Cook Dlamini | Aboard |
B:
- id: b3-sticky
title: Sticky note on the console
type: note
body: |
*Restart code = our call-sign number × crew still aboard. Don't forget to count yourselves! — Ruiz*
- id: b3-callsign
title: Station plaque
type: photo
body: |
*A brass plaque by the door: **STATION ECHO · CALL SIGN EC-127**.*
lock:
id: lock-generator
prompt: Enter the generator restart code.
inputType: code
format: Up to 4 digits
answers: ["635"]
hints:
- You need the call-sign number and the number of people still aboard.
- Five people are still aboard, including the two of you.
- 127 × 5 = 635.
successText: |
The generator coughs, then roars. Lights flood every module.
The radio room's long-range transmitter warms up. One more message and you're going home.
keyName: Power Key
finale:
type: escape
prompt: The transmitter is receiving a coded beacon from the rescue plane. Reply with the same word to confirm.
clues:
A:
- id: af-beacon
title: Radio room beacon lamp
type: signal
signal: "..../---/--/."
body: |
The incoming-signal lamp is flashing a pattern from the rescue plane.
Describe it to the Engineer.
B:
- id: bf-chart
title: Morse chart taped inside the generator panel
type: table
body: |
| | | | | | |
|---|---|---|---|---|---|
| A `.-` | B `-...` | C `-.-.` | D `-..` | E `.` | F `..-.` |
| G `--.` | H `....` | I `..` | J `.---` | K `-.-` | L `.-..` |
| M `--` | N `-.` | O `---` | P `.--.` | Q `--.-` | R `.-.` |
| S `...` | T `-` | U `..-` | V `...-` | W `.--` | X `-..-` |
| Y `-.--` | Z `--..` | | | | |
lock:
id: lock-beacon
prompt: Transmit the beacon word.
inputType: text
format: One word
answers: ["HOME"]
hints:
- Roles have swapped. This time the Comms Officer reads the light.
- Four letters. The first is four short flashes.
- H · O · M · E.
successEnding: |
**HOME.** The radio crackles: *"Echo, we read you. Wheels down in ten. Put the kettle on."*
Through the frost on the window, landing lights cut through the polar night.
**You escaped!** 📡🔑
+151
View File
@@ -0,0 +1,151 @@
id: tick-tock-together
engine: defuse
category: co-op
title: Tick Tock Together
tagline: One of you sees the bomb. One of you has the manual. Talk fast.
estimatedMinutes: 25
difficulty: 2
players: { min: 2, max: 2 }
theme: { primary: "#b3261e", accent: "#ffcc00", background: "#0e0e0e", headingFont: "JetBrains Mono" }
roles:
A:
name: Technician
blurb: You see the device. You can't see the manual. Describe everything.
B:
name: Expert
blurb: You have the manual. You can't see the device. Ask the right questions.
intro: |
A cartoonishly suspicious box has been left at your favourite restaurant, ticking.
It's a training device (probably). Still, let's not find out.
**Technician:** describe what you see. **Expert:** find the rule and tell them what to do.
Three strikes and it's over. Every strike makes the clock run faster.
# ---------- Device definition ----------
device:
serial:
length: 6
alphabet: "ABCDEFGHJKLMNPQRSTUVWXYZ0123456789" # last char is ALWAYS a digit
batteries: { min: 0, max: 4 }
levels:
- id: lvl1
title: Wire Warm-up
timeSeconds: 180
modules: [wires]
keyName: Red Wire Key
- id: lvl2
title: Push It
timeSeconds: 240
modules: [wires, button]
keyName: Big Button Key
- id: lvl3
title: Hearts & Symbols
timeSeconds: 300
modules: [wires, button, keypad]
keyName: Heart Key
strikes: { max: 3, speedUpPerStrike: 0.25 }
# ---------- Module generation ----------
modules:
wires:
count: { min: 3, max: 5 }
colors: [red, blue, yellow, green, white]
# Technician view: wires listed top to bottom, each with colour swatch + colour NAME label.
# Action: { type: "cut", index: 1-based }
button:
labels: [HOLD, PRESS, ABORT, LOVE]
colors: [red, blue, white, yellow]
stripColors: [blue, white, yellow, red]
# Strip colour is generated up front but only shown to the Technician WHILE holding.
# Actions: { type: "tap" } or { type: "hold" } then { type: "release", timerText }
# Server checks the release against the timer value it computes itself.
keypad:
symbols:
heart: "♥ Heart"
star: "★ Star"
moon: "☾ Moon"
sun: "☀ Sun"
club: "♣ Club"
flower: "✿ Flower"
umbrella: "☂ Umbrella"
plane: "✈ Plane"
music: "♫ Music note"
anchor: "⚓ Anchor"
diamond: "♦ Diamond"
spade: "♠ Spade"
columns:
- [heart, star, moon, sun, club, flower]
- [umbrella, heart, plane, music, star, anchor]
- [diamond, sun, spade, umbrella, flower, music]
- [anchor, spade, club, plane, moon, diamond]
# Generator: pick a column at random, pick 4 symbols from it, shuffle.
# Columns share at most 2 symbols, so every 4-symbol set matches exactly one column.
# Action: { type: "press", symbol } one at a time.
# ---------- The manual (shown ONLY to the Expert) ----------
manual: |
# Bomb Defusal Manual (Couples' Edition)
**Ask your Technician for the serial number and battery count first. You'll need both.**
---
## Module: Wires
Wires are numbered **top to bottom**, starting at 1. Cut exactly one.
Check these rules **in order**. Use the **first** one that applies.
1. If there are **exactly two red** wires → cut the **second red** wire.
2. If the serial number's **last digit is even** and there is **any yellow** wire → cut the **first yellow** wire.
3. If there are **no blue** wires → cut the **last** wire.
4. If there are **exactly five** wires → cut the **fourth** wire.
5. Otherwise → cut the **first** wire.
---
## Module: The Button
Check these rules **in order**. Use the **first** one that applies.
1. If the button says **LOVE** → **tap** it. (Obviously.)
2. If the button is **red** and says **HOLD** → **tap** it.
3. If there are **2 or more batteries** and the button says **PRESS** → **tap** it.
4. Otherwise → **hold** the button. A coloured strip lights up beside it. Release when the timer shows:
- **Blue** strip → a **4** anywhere on the timer
- **Yellow** strip → a **5** anywhere on the timer
- **Any other colour** → a **1** anywhere on the timer
---
## Module: Hearts & Symbols
The keypad shows four symbols. Exactly **one** column below contains all four.
Press the four symbols in the order they appear **from top to bottom** in that column.
| Column 1 | Column 2 | Column 3 | Column 4 |
|----------|----------|----------|----------|
| ♥ Heart | ☂ Umbrella | ♦ Diamond | ⚓ Anchor |
| ★ Star | ♥ Heart | ☀ Sun | ♠ Spade |
| ☾ Moon | ✈ Plane | ♠ Spade | ♣ Club |
| ☀ Sun | ♫ Music note | ☂ Umbrella | ✈ Plane |
| ♣ Club | ★ Star | ✿ Flower | ☾ Moon |
| ✿ Flower | ⚓ Anchor | ♫ Music note | ♦ Diamond |
# ---------- Solver fixtures (MUST pass as unit tests) ----------
fixtures:
wires:
- { serial: "TK4L82", batteries: 1, wires: [red, blue, red, white], expect: { type: cut, index: 3 } }
- { serial: "QX7M35", batteries: 0, wires: [yellow, blue, white], expect: { type: cut, index: 1 } }
- { serial: "ZP2R44", batteries: 2, wires: [green, yellow, white, yellow, red], expect: { type: cut, index: 2 } }
- { serial: "AB9C11", batteries: 3, wires: [red, green, white, yellow], expect: { type: cut, index: 4 } }
- { serial: "LM3N07", batteries: 1, wires: [blue, green, white, red, yellow], expect: { type: cut, index: 4 } }
button:
- { batteries: 0, label: LOVE, color: red, strip: blue, expect: { type: tap } }
- { batteries: 0, label: HOLD, color: red, strip: white, expect: { type: tap } }
- { batteries: 3, label: PRESS, color: blue, strip: yellow, expect: { type: tap } }
- { batteries: 1, label: PRESS, color: white, strip: blue, expect: { type: hold, releaseDigit: 4 } }
- { batteries: 2, label: ABORT, color: yellow, strip: yellow, expect: { type: hold, releaseDigit: 5 } }
- { batteries: 4, label: HOLD, color: blue, strip: red, expect: { type: hold, releaseDigit: 1 } }
keypad:
- { symbols: [star, flower, heart, sun], expect: [heart, star, sun, flower] }
- { symbols: [spade, plane, anchor, moon], expect: [anchor, spade, plane, moon] }
- { symbols: [music, umbrella, diamond, sun], expect: [diamond, sun, umbrella, music] }
+363
View File
@@ -0,0 +1,363 @@
id: vineyard-hollow
engine: story
category: murder-mystery
title: Death at Vineyard Hollow
tagline: A harvest gala. A priceless vintage. A host who never made it to dessert.
estimatedMinutes: 60
difficulty: 2
players: { min: 2, max: 2 }
theme: { primary: "#7b1e3a", accent: "#d4a373", background: "#160c10", headingFont: "Playfair Display" }
roles:
A:
name: The Inspector
blurb: You hold the police file. Statements, the medical examiner, the lab.
B:
name: The Estate Insider
blurb: You know the estate. Logs, maps, the family's private papers.
intro: |
Saturday night. The Hale family's annual harvest gala at **Vineyard Hollow**.
Candles, jazz on the lawn, two hundred guests in their best.
At 23:05, Clara Hale goes down to the barrel cellar to find her husband, **Edmund Hale**,
who promised to open "something special" for the toast. She finds him slumped against
Rack 9, a wine glass shattered beside him.
The police have locked down the estate. Nobody leaves until the truth comes out.
One of you has the official file. The other knows every corner of this estate.
**Neither of you can crack this alone.**
suspects:
- id: clara
name: Clara Hale
role: Wife of the victim
description: Hostess of the gala. Married to Edmund for 37 years. Composed. Too composed?
- id: marcus
name: Marcus Hale
role: Son and heir
description: Charming, broke, and very interested in his inheritance.
- id: nina
name: Dr Nina Vos
role: Family doctor
description: Treats Edmund's heart condition. Carries her medical bag everywhere.
- id: pieter
name: Pieter Swart
role: Head winemaker
description: Twenty years at Vineyard Hollow. Made the wines that made the Hale name.
- id: isabelle
name: Isabelle Rourke
role: Wine buyer
description: Represents a group that has been trying to buy the estate for two years.
chapters:
- id: ch1
title: The Cellar Door
narrative: |
The security system re-locked the cellar after the police went down.
The keypad blinks red, waiting for a four-digit code. The officer who knew it went home.
clues:
A:
- id: a1-butler
title: Statement of Hendricks, the butler
type: note
body: |
"Mr Hale changed the cellar code last month. He was very pleased with himself.
He said it was *the year it all began*, read the way wine is tasted in his family:
**from the finish back to the start.** I'm afraid I never worked it out."
B:
- id: b1-plaque
title: Photo of the main gate
type: photo
body: |
*A brass plaque on a stone gatepost, polished to a shine:*
**VINEYARD HOLLOW · EST. 1874 · Stellenbosch Valley**
- id: b1-wedding
title: Framed photo in the hallway
type: photo
body: |
*Edmund and Clara on their wedding day under an oak tree.
Engraved on the frame: "E & C · 1989".*
shared:
- id: s1-suspects
title: Who was at the gala
type: document
body: |
Five people had reason to be near the cellar tonight. See the suspect list.
Officers note everyone was in good spirits until around 20:00.
lock:
id: lock-cellar
prompt: Enter the 4-digit cellar code.
inputType: code
format: 4 digits
answers: ["4781"]
hints:
- The butler's note tells you HOW to build the code. The estate tells you WHAT number to use.
- "\"The year it all began\" is the year the estate was founded."
- 1874 read backwards is 4781.
successText: |
The keypad clicks green. Cold air rolls up the stone stairs.
The barrel cellar smells of oak, and of something bitter underneath.
keyName: Cellar Key
- id: ch2
title: The Hour of Death
narrative: |
Edmund lies where he fell. The medical examiner has taken her readings.
The cellar has a keycard reader that logs every entry and exit.
When did Edmund die? And who was down here with him?
clues:
A:
- id: a2-me
title: Medical examiner's preliminary note
type: report
body: |
**Subject:** Edmund Hale, 64
**Core body temperature at 23:10:** 34.5 °C
**Normal living temperature:** 37.0 °C
The cellar is held at a constant 14 °C. Under these conditions the body
will have cooled at approximately **1.0 °C per hour**.
Estimated margin: plus or minus 15 minutes.
- id: a2-statements
title: Suspect statements (summary)
type: document
body: |
- **Clara:** "I was hosting on the lawn all night. I went down at five past eleven and found him."
- **Marcus:** "I went down around half nine to talk to Dad. Lights were off. I figured he'd gone up."
- **Dr Vos:** "Edmund asked me to check his heart in the cellar. I left him at half past eight, alive and grumbling about the band."
- **Pieter Swart:** "I lost my keycard around eight. Someone must have taken it. I was in the tasting room all evening."
- **Isabelle Rourke:** "I never went near the cellar. Check your little log."
B:
- id: b2-log
title: Cellar keycard log, Saturday
type: log
body: |
| Time | Card holder | Event |
|-------|------------------|-------|
| 20:10 | Edmund Hale | IN |
| 20:18 | Dr Nina Vos | IN |
| 20:31 | Dr Nina Vos | OUT |
| 20:35 | Pieter Swart | IN |
| 20:52 | Pieter Swart | OUT |
| 21:30 | Marcus Hale | IN |
| 21:33 | Marcus Hale | OUT |
| 23:05 | Clara Hale | IN |
| 23:10 | POLICE OVERRIDE | IN |
*Isabelle Rourke was not issued a cellar card.*
lock:
id: lock-tod
prompt: What was Edmund's time of death? (24-hour time)
inputType: code
format: "HH:MM"
answers: ["2040", "840PM", "840"]
hints:
- Work out how many degrees Edmund's body lost after death.
- He lost 2.5 °C. At 1 °C per hour, that's 2 hours 30 minutes.
- 23:10 minus 2h30 = 20:40.
successText: |
**Time of death: about 20:40 (± 15 minutes).**
Check the log. In that window, two cards were in the cellar:
**Dr Nina Vos** (until 20:31) and **Pieter Swart** (20:35 to 20:52).
Marcus came much later. By 21:30, Edmund had been dead almost an hour.
keyName: Pocket Watch Key
- id: ch3
title: The Poisoned Bottle
narrative: |
No wound. No struggle. The lab rushed the glass by Edmund's hand.
Six bottles were opened in the cellar tonight. Only one killed him.
clues:
A:
- id: a3-lab
title: Forensic lab, rush result
type: report
body: |
Residue in the victim's glass: **a cardiac glycoside** (a heart toxin).
The wine in the glass came from a source bottle that was:
- a **red** wine
- **decanted** before serving
- sealed with a cork whose lot number **ends in 7**
B:
- id: b3-cellarbook
title: Sommelier's cellar book, bottles opened tonight
type: table
body: |
| Wine | Colour | Cork lot | Decanted? |
|-----------------------|--------|----------|-----------|
| 2015 Chenin Blanc | White | VH-1157 | Yes |
| 2004 Pinotage | Red | VH-3302 | Yes |
| 2010 Merlot | Red | VH-4417 | No |
| 1998 Cabernet Reserve | Red | VH-2207 | Yes |
| 2019 Rosé | Pink | VH-5527 | No |
| 2012 Shiraz | Red | VH-6608 | Yes |
*Margin note: "1998 Reserve brought up from Rack 9 at Mr Hale's request."*
lock:
id: lock-bottle
prompt: Which bottle was poisoned? (vintage year or name)
inputType: text
format: e.g. a year or wine name
answers: ["1998", "1998CABERNETRESERVE", "CABERNETRESERVE", "1998RESERVE", "RESERVE", "VH2207", "2207"]
hints:
- Filter the cellar book using all three lab conditions at once.
- Red + decanted leaves Pinotage, Cabernet Reserve and Shiraz. Now check the cork lots.
- Only VH-2207 ends in 7, so it's the 1998 Cabernet Reserve.
successText: |
**The 1998 Cabernet Reserve.** Edmund's most treasured wine, the vintage that won
Vineyard Hollow its first gold medal. He kept it in **Rack 9**, his private rack,
and only opened it for the people closest to him.
Whoever poisoned it knew exactly what he'd pour tonight.
keyName: Silver Corkscrew Key
- id: ch4
title: Edmund's Diary
narrative: |
In Edmund's jacket: a leather pocket diary. The last entry is scrambled.
Edmund loved puzzles. His family says he "hid his secrets where he hid his heart."
clues:
A:
- id: a4-diary
title: Diary, final entry
type: note
body: |
*Clara mustn't see this yet. Rack-shift, as always.*
## ORAN BFJAC OARMJH
*(Below, in ordinary ink:) Tell Rourke the sale is on, once the label audit is done.*
- id: a4-alphabet
title: Alphabet strip (from the evidence kit)
type: diagram
body: |
```
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
1 2 3 4 5 6 7 8 9 ... 26
```
B:
- id: b4-map
title: Barrel cellar map
type: map
body: |
```
[Rack 1] Chenin Blanc [Rack 6] Pinotage
[Rack 2] Sauvignon Blanc [Rack 7] Shiraz
[Rack 3] Rosé [Rack 8] Export stock
[Rack 4] Merlot [Rack 9] 1998 Cabernet Reserve
[Rack 5] Blends ♥ E.H. PRIVATE ♥
```
- id: b4-clara
title: Clara's comment to the police
type: note
body: |
"Edmund used to say Rack 9 was where he kept his heart. Sentimental old fool."
lock:
id: lock-diary
prompt: Decode the diary entry.
inputType: text
format: The decoded message (3 words)
answers: ["FIRESWARTFRIDAY"]
hints:
- Every letter is shifted along the alphabet by the same number. "Rack-shift" tells you which number.
- His heart is in Rack 9. Shift every letter BACK by 9.
- O→F, R→I, A→R, N→E… The first word is FIRE.
successText: |
**"FIRE SWART FRIDAY."**
Edmund was about to fire his head winemaker of twenty years.
And the line underneath, "once the label audit is done," starts to look a lot less innocent.
An auditor's letter in the study confirms it: years of cheap blends sold as Reserve.
keyName: Diary Key
- id: ch5
title: The Source of the Poison
narrative: |
Dr Vos carries heart medication in her bag. The obvious answer. Too obvious?
The full toxicology report has just come in.
clues:
A:
- id: a5-tox
title: Full toxicology addendum
type: report
body: |
The glycoside is **not** pharmaceutical digoxin. No tablet binders, coatings, or fillers.
Profile matches a **crude extract made from fresh leaves** of *Digitalis purpurea*,
a common garden plant with tall purple bell-shaped flowers.
- id: a5-bag
title: Dr Vos, medical bag inventory
type: table
body: |
| Item | Count |
|--------------------------|-------|
| Digoxin 0.25mg tablets | 30 / 30 (seal unbroken) |
| Stethoscope | 1 |
| BP cuff | 1 |
B:
- id: b5-groundskeeper
title: Groundskeeper's notebook
type: note
body: |
**Thu:** Someone's been cutting leaves in the foxglove bed behind the winery workshop.
Only reachable through the workshop. Only key is Mr Swart's.
**Sat 22:00:** Tidying the workshop. Found a cellar keycard in Mr Swart's apron pocket.
Put it on his bench. Thought he said he'd lost it?
lock:
id: lock-plant
prompt: Name the plant the poison came from.
inputType: text
format: One word
answers: ["FOXGLOVE", "DIGITALIS", "DIGITALISPURPUREA", "FOXGLOVES"]
hints:
- The lab gives the scientific name; the estate gives the common one.
- Digitalis purpurea = the plant growing behind the workshop.
- Foxglove.
successText: |
**Foxglove.** Dr Vos's tablets are untouched.
The poison came from a bed only reachable through Pieter Swart's workshop.
And the keycard he swore he'd lost was in his own apron, an hour after the murder.
keyName: Workshop Key
finale:
type: accusation
prompt: You have all five keys. Make your accusation together.
maxAttempts: 2
questions:
- id: who
label: Who killed Edmund Hale?
options:
- { id: clara, label: Clara Hale }
- { id: marcus, label: Marcus Hale }
- { id: nina, label: Dr Nina Vos }
- { id: pieter, label: Pieter Swart }
- { id: isabelle, label: Isabelle Rourke }
answer: pieter
- id: how
label: How?
options:
- { id: tablets, label: Crushed digoxin tablets from Dr Vos's bag }
- { id: foxglove, label: Foxglove leaf extract in the 1998 Reserve }
- { id: chenin, label: Poison in the Chenin Blanc }
- { id: blow, label: A blow to the head in the dark }
answer: foxglove
- id: why
label: Why?
options:
- { id: debts, label: To inherit and clear gambling debts }
- { id: affair, label: Jealousy over an affair }
- { id: fraud, label: To stop being fired and exposed for label fraud }
- { id: sale, label: To sabotage the sale and buy the estate cheap }
answer: fraud
successEnding: |
Pieter Swart sets down his glass and says nothing for a long time.
"Twenty years," he finally says. "Every medal on that wall is mine. He was going to
throw me out and tell the world I was a fraud."
He brewed the foxglove in his workshop, slipped into the cellar at 20:35 while Edmund
decanted the Reserve for the toast, and claimed his card was "lost" to cover the log.
**Case closed.** Well played, detectives. 🍷🔑
failEnding: |
The police make an arrest on your theory. Three weeks later the case collapses
and the real killer is long gone.
reveal: |
**Pieter Swart**, with **foxglove extract in the 1998 Reserve**, to stop Edmund
**firing him and exposing years of label fraud**.