Repository navigation
EN Create Puzzle Design
Français · Type: explanation · Audience: author · Context: engine repository, created project · Status: ready · Applies from: 4.2.5
A practical, opinionated guide for an author, often working alone with an AI assistant: what to write so that the game is fun, fair and finished. The format is in the Create a game pages, the method in Working with an AI; this page is about taste.
The sample game, The Pantry Key (games/demo/ in the engine repository): Pixel the cat, three rooms (house,
garden, market), about 15 minutes. Every id quoted here exists in that folder. The game's texts are in English and
quoted as they are. The code excerpts are simplified: the sample game's files also carry each line's stable id
({ id, text }), which the identity contract adds (State and saves).
The classic LucasArts adventures (Monkey Island, Day of the Tentacle) settled a few rules. They still hold.
| Rule | What it means here |
|---|---|
| No death | The hero never dies and never loses. A built-in minigame cannot be failed: winning or skipping it plays then. |
| No dead ends | The player can never reach a state where the game cannot be finished. npm run solve -- --prove proves it (npm run solve finds one winning path). |
| Every action is answered | Any verb on any thing gets a line. A wrong try is a joke, not a silence. |
| Humour lives in the failures | Most of what a player reads is a reaction to a wrong idea. That is where the voice is. |
| Thinking, not pixel hunting | Every useful thing is named, visible and has a Look line. The puzzle is in the head, not in the hitbox. |
| A small cast with strong voices | Four or five characters you can tell apart from one line. |
| One goal per room | The player always knows what they want here. The storyboard says it in goal. |
| 10 to 20 minutes per room | Long enough to explore, short enough to finish on a bus. |
The sample game follows all of them. Pixel cannot fail. Pulling Biscuit gives "Never pull a cat. Cat law, article one." The key is never hidden in a corner: it is in a tank that a character tells you about.
In a created project the solver runs as npx quest-n-click solve (and npx quest-n-click solve --prove). What each
result lets you conclude is in What the solver proves.
Write the story before any room file. Four answers, one line each:
| Question | The sample game |
|---|---|
| Premise in one sentence | Grandma locked the sardines in the pantry and lost the key. |
| The hero's want | Pixel wants the sardines. |
| The obstacle per room | House: nobody knows where the key is. Garden: it fell in a tank with a stuck tap. Market: it is a deposit for a lantern. |
| The ending | The pantry opens, two cats share one tin, the sealed card judges the player's guess. |
If you cannot fill this table, do not open the editor yet. Then write the story as the storyboard, one board per room and one panel per beat, and review it on a phone until the author says it is final: see The storyboard, source of truth. Only then write rooms.
A look list cycles, one line per look. Use the rhythm normal, normal, normal, absurd: three useful or plain lines,
then a joke that rewards the player who keeps clicking. The sample game's pantry:
pantry: ['The pantry cupboard. The sardines live in there.',
'Locked. I can hear the sardines. (Sardines are silent. I hear them anyway.)',
'Still locked. Still sardines.', 'I could stare at it all day. I might.'],Two lines are fine for decor (gnome: "He has seen things." then "He never blinks. Respect."). Never put the solution
only in the fourth line.
| Shape | How it is written | In the sample game |
|---|---|---|
| Fetch and give |
give rule with a = item, b = actor |
bouquet → seller gives the key
|
| Combine two items (or item on a thing) |
use rule with a and b; in rules.on if it works everywhere |
pipe on tank
|
| State machine prop |
states + { prop: [...] } command + if: { prop: [...] }
|
armchair: remote → searched; tank: full → draining; pantry: locked → open
|
| Dialogue unlock | a talk topic that sets a flag or unlocks a place |
Grandma's "Where is the key?" unlocks garden; Lou's topic sets deposit_known
|
| Minigame as a gate |
{ minigame, params, then } inside a rule |
pipes drains the tank, pick wins the bouquet |
Anything else (timers, real-time action, random outcomes) fights the solver. Avoid it. When a famous mechanic seems to need something else, Classic mechanics shows how the DSL writes it.
-
3 to 6 steps per room. The garden: take
spare_pipe→ usepipeontank→ pipes → look atsock→ talk toshell_phone. Five steps. - One "aha" per room. The garden's is the sock: you drain a tank for a key and find a note instead. The market's is the anniversary: the seller wanted flowers because he forgot his wedding anniversary.
-
Cross rooms once or twice. The
tokencomes from the house armchair and pays at the market. That is enough. - Say the goal out loud. Grandpa says "Drain it. But the tap is stuck." The player knows what, not how.
Each room has hints. The engine gives the first entry whose until condition is still false. Write them in the
order of the puzzle, one entry per step, vague then precise inside an entry. The garden:
hints: [
{ until: { any: ['pipe_taken', 'tank_drained'] }, lines: ['There is a spare pipe on the ground. Take it, sweetie.'] },
{ until: 'tank_drained', lines: ['Use the pipe on the water tank.', 'Pipe. Tank. Together. Go!'] },
{ until: 'lou_has_key', lines: ['Something came out of the tank. Look at it.'] },
{ until: { unlocked: 'market' }, lines: ['Talk into my shell phone and call Lou.'] },
{ until: { has: 'key' }, lines: ['Lou is at the market. Open the map.'] },
{ until: 'pantry_open', lines: ['Come home with that key, sweetie!'] },
],Two habits: use any when a step can be skipped (the pipe is gone once the tank is drained), and always cover the
rest of the game, so the hint voice is never silent in a room the player comes back to.
Before touching a puzzle, ask the graph what hangs on it: npm run page:puzzles (engine repository), the Puzzles panel
of the Studio's Check tab (either context), or the MCP tool puzzle_graph with an id. The card of the pantry key in the
sample game reads: acquired by give bouquet + seller; used by use key + pantry and the finale's goal; requires first
bouquet, flowers_done, bouquet_given; unlocks the pantry and the end. Remove the bouquet trade and the card tells
you what breaks before the solver does. The overview lists three smells: a thing read but never produced (a flag
nobody sets), a thing produced but never used (a flag or item that leads nowhere), and a flag set only by actions
that already need it (it can never become true; the validator warns too). The page itself:
Review pages.
The solver's warnings (dead ends, items never used, items never obtained) and how to read them are in What the solver proves.
| Element | Rule of thumb |
|---|---|
| Hotspots + named props + actors | 8 to 15 per room. Fewer feels empty, more feels like pixel hunting. The sample game's house has 11 named things. |
| Props with states | Only for things the story changes (armchair, tank, pantry). |
| Decor props | No name = not clickable (table, chair, stall_left). Use them to dress the scene. |
| Exits | One obvious way out, at the edge of the screen, named after where it goes (back_door: "Back to Grandma's house."). |
- Logical space: 640 × 400, origin top-left, characters placed by their feet.
-
The floor band. The walk area sits in the lower part of the background;
floor(default 395) is its bottom. Keep it free of drawn furniture, so props and actors stand on it. - Character heights are in pixels of that space: in the sample game, Pixel is 36, Grandma 120, Grandpa (seated) 100. A human around 110 to 125 next to a 640-wide room reads right on a phone.
-
Approach points: set them in the editor (
?edit=<room>), so the hero walks next to a thing, not onto it.validatewarns when awalktarget has no geometry. -
Exits are clickable things, not invisible edges. A locked exit still answers: the house
windowsays "Not yet. First, the key. Grandma knows things." until the garden is unlocked. -
One checkpoint per room in
game.checkpoints, with the inventory, flags and props the player would have. The sample game hashouse,garden,market,finale. Open?dev&at=marketto test that room alone.
The reference for all of this is in Rooms, Geometry and Exits.
-
2 to 4 talk topics per character, one that advances the plot. Grandma: "Where is the key?" (plot), "What is for
dinner?" (gag with
nth), "Why lock the sardines?" (character). The plot topic goes first. -
Topics can appear and change with
if: Grandpa's "There was no key in the tank!" shows only oncetank_drained. -
choicesparingly. Use it when the player's answer is fun to pick, not to branch the story. The sample game has two: the opening guess (it setsguess, judged at the end) and Lou's "argue or accept", where both answers lead forward. -
Hug and bye are global (
globalTalk): "Can I have a cuddle?" and "Bye!" are added to every menu. Give each character ahugline and arefuseline incast.ts: cheap, and players love them. -
Speech colours come from
coloron each character. Make them distinct: Grandma#ff9ec4, Grandpa#8fd3ff, Lou#b8ff8f, the seller#ffb36b. The hint voicegrandma_voicereuses Grandma's colour on purpose. - The transcript keeps every line. Players will scroll back: do not hide the only clue in a line said during a cutscene with no other trace.
-
Line length for a phone: under 90 characters, two lines on screen.
validatewarns above 140, but 90 is the comfortable limit. Split long speeches into severalsays.
The format is in Dialogues.
Most clicks hit no written rule. What answers them is the game's voice.
-
rules.fallbacks: 3 to 6 variants per verb, drawn at random, never twice in a row. One array per verb, plususe2for "Use A with B". The sample game has two or three; aim higher in a real game. -
Write them in character. Pixel's
push: "It does not move. I need more breakfast." Pixel'suse2: "These do not go together. Like cats and baths."{objet},{cible}and{nom}insert names. -
kindsanswer a whole family at once: everycatrefuses to be pulled, everypersonrefuses to be carried. A precisetargetwins over akind. -
refuseper character answers any Give that has no rule: Lou says "Thanks, but my pockets are full. Of other people's things." - "That doesn't work" is never acceptable. It tells the player nothing and kills the mood. A fallback should be a joke, a hint at the hero's want, or both.
The format is in Reactions.
Use one as a gate the player understands: the fiction says why you play it. The tank needs another way out, so you lay pipes. The seller wants flowers, so you pick them.
-
Always a Skip, except the finale's
scratchticket. Skipping still runsthen, so the story goes on. -
30 seconds, not 3 minutes. Three rounds of
pick, one 4 × 3 grid ofpipes.helpAftermakes the right pipe glow when the player is lost. -
introandwinlines frame it in the hint voice.
| Minigame | Fits a fiction about |
|---|---|
pipes |
water, plumbing, irrigation, anything that flows |
cables |
electricity, a switchboard, a tangle behind the TV |
pick |
shopping, choosing the right one, a recipe, a line-up |
hide |
hide and seek, finding someone or something in a scene |
runner |
a chase, an escape, a race to catch a bus |
stroke |
calming an animal, putting a baby to sleep |
scratch |
the sealed ending only |
code-wheel |
a paper code wheel, the 1990s "copy protection" played as a joke (4.1.15) |
Limit ·
code-wheel-can-failThe "no failure" rule holds for the built-in minigames above, with one exception: acode-wheelinstorymode, lost after its tries, endsfailed. The parameters of every minigame are in Built-in minigames.
- Add one from three places. With two, a door is enough.
-
Vehicles (
vehicle: 'car','plane') give each trip a small animation and a sense of distance. -
The "news" marker shows where something new is waiting. The sample game: the garden has news while
!tank_drained, the market while the hero does not hold the key, the house when the hero holds the key and the pantry is still closed. -
No locked places shown. A place appears when a rule
unlocks it, with atoast("New on the map: the garden"). Players should never see a pin they cannot use.
The format is in World map.
-
Music per room, set with
musicon the room. Short and quiet: the player hears it for 15 minutes. The sample game plays one theme in every room (music: 'theme'), split into stems so the mix changes with the room: see Music in stems. -
An sfx on every state change. Armchair searched:
cloth. Tank drained:drop. Pantry:latchthendoor_open. Item gained:select,pluck,metal. A change with no sound feels like a bug. -
The hint voice is an
offscreencharacter (grandma_voice) answering through the hint item (shell_phone). Make it someone the player likes, with a tic ("sweetie"), so hints feel like help, not a manual.
- Death and failure states. They punish exploration, and every one must be proved recoverable.
- Timers, real-time action and random outcomes. The solver explores choices, not reflexes or dice: a puzzle built on them cannot be proved finishable, and a phone player on a bus cannot play it. When a story needs urgency, a world script that changes an item as time passes (the grog mug of Classic mechanics) keeps it provable.
- Pixel hunting. A thing that matters is named, visible and answers Look.
These rules make games that are fair and provable, and short sessions that fit a phone. They cost tension: no
deadline, no loss, no luck. A game that wants those leans on a custom minigame, whose inside the solver does not see
(only its declared effects), and gives up part of the proof for it.
- The sample game:
games/demo/(game.ts,rooms/,cast.ts,rules.ts,storyboard.json). - Checking a game like a player, and the checklist before sharing the link: Playtests, Checking your game.
- What the solver proves · Classic mechanics · The storyboard · Images.
Generated publication · publication générée — current documentation · documentation courante: quest'n'click 4.4.0 r1, documentation revision · révision de la documentation 8dc717c69acb62c4742b69c54a7e7dfb620f75d8.
Earlier versions and permalinks · versions précédentes et liens permanents: https://wanoo.github.io/quest-n-click/docs/
An edit made in this wiki is overwritten at the next publication · une modification faite dans ce wiki est écrasée à la publication suivante.
Edit the source · Modifier la source · Report an error · Signaler une erreur
Index (6)
Start (6)
Play (9)
Install (17)
- Requirements
- Install from the release archive
- Install from npm, once published
- Install from the repository to work on the engine
- doctor: checking your machine
- Image toolchain
- Audio toolchain: ffmpeg, fluidsynth, Furnace, uv
- Upgrade
- Upgrade notes: 4.4
- Upgrade notes: 4.3
- Upgrade notes: 4.2
- Upgrade notes: 4.1
- Upgrade notes: v3
- Upgrade notes: v2 to v3
- Coming from web-scumm
- Package reference
- Install troubleshooting
Create a game (55)
- Your first game
- Layout of a created project and of the engine repository
- The working loop
- Content principles and the identity contract
- Rooms
- Geometry: placement, walk zones, masks, layers, camera
- Something is visible but unreachable
- Reactions
- Game commands
- Conditions
- Flags, variables and events: which to use
- Custom code
- Characters
- Items and inventory
- World map
- Dialogues
- Scripts and events
- Moving characters: patrols and walks
- Exits
- Chapters and objectives
- State and saves: persistent or temporary, renaming an id, migrations
- Staging: wide rooms and animations
- The stage: lights, particles, floors
- Skin and fonts
- Title and credits
- Heroes: several characters and hero choice
- Built-in minigames
- A custom minigame
- Translations
- Music in stems
- Sound effects
- Voices: per language and per hero
- Images: prompts, cutting sheets, transparency, scale
- Asset provenance
- The storyboard, source of truth
- Puzzle design
- Classic mechanics
- Review pages
- DSL reference
- Checking your game
- What the solver proves
- Solver options
- Playtests
- Remix
- Speedrun for authors
- The sealed ending
- Production template
- Example games
- Working with an AI
- Build and release a game
- Publish on GitHub Pages
- The static game on Clever Cloud
- Other static hosts
- Sub-path, cache and service worker, incomplete deploys, going back
- Testing on a phone over the LAN
Local Studio (24)
- The local Studio: overview and launch
- Edit a room
- Placement and stage
- Preview, apply, undo
- Storyboard
- Assets and cutting
- Check: validator, solver, critical path, heat, health, coverage
- Before and after captures
- Semantic diff
- Play and replay a session
- Rule explainer
- Language and objectives
- Voices and the heroes × languages matrix
- Map
- Notes
- Remix in the Studio
- Music
- The Assistant: bring your own key
- Demo mode and studio-apply
- Local and LAN security
- Studio API
- Local MCP: setting up each client
- MCP tools
- Studio troubleshooting
Team Studio (21)
- What runs where
- Concepts: revision, transaction, span, lease, preview, build
- Starting as an invited dev
- Working together day to day
- Roles and actions: the full matrix
- Invitations
- Managing members
- Write refused on the hosted Studio: stale base revision (409)
- Write refused on the hosted Studio: span locked (423)
- Recovering after a lost lease, a disconnection or a stale preview
- Comments
- Map and semantic diff
- Publishing builds
- Remote MCP
- The game in Git
- Upgrading the engine of a hosted game
- Hosted assets
- The server's command line
- Health and metrics routes
- Trying it locally with compose.yml
- Known limits
Clever Cloud (23)
- Clever Cloud: overview, cost and prerequisites
- Apps and add-ons
- Domains and TLS
- Keycloak
- Generic OIDC and GitLab
- Configuration
- First deployment
- Backups
- RPO and RTO
- Restore the Studio on a fresh installation
- Redeploys
- Upgrading the server and checking state after a migration
- Limits of going back
- Monitoring
- Security
- Rotating keys
- Recovering admin access
- Changing domain or OIDC provider
- Worker, PostgreSQL or Cellar down
- Exporting the game and leaving the installation
- Retention
- Clever Cloud troubleshooting
- Costs and teardown
Reality Bridge and connectors (16)
- The Reality Bridge: what it is
- Quick start
- Guarantees
- Install and run the Bridge
- Backup and restore
- Several tenants
- Signals and the manifest
- The Bridge's command line
- Connectors overview
- Email connector
- Telnet connector
- SSH connector
- Open Badges connector
- The connector SDK
- Security: Biscuit and signatures
- Bridge and connector troubleshooting
Contribute (25)
- Welcome
- Developer setup
- The dev loop
- Before a pull request
- Conventions
- Writing documentation
- Architecture
- Code tour
- Import boundaries
- Which tests run when
- Mutation testing
- The coverage ratchet
- The quality baseline
- CI explained
- Field passes
- Benchmarks
- Releasing
- npm packages
- Decisions and ADRs
- Governance
- Bot pull requests
- Contributing with AI agents
- Security policy
- Code of conduct
- Licences and credits