Act III, cold-palette EGA. Six rooms, 100 points, verified end-to-end headlessly at 100/100 with the win firing. The homage John asked for is a register, not a costume. Nielsen played Drebin Sr oblivious — the joke was he never noticed. Neeson plays Jr sincere: he notices everything and means all of it, so his text is written straight and often genuinely moving, and the comedy is what it turns out to be about. He gives a Taken speech about his particular set of skills, chief among them paperwork. He takes four armed robbers in eleven seconds dressed as a schoolgirl with his back to the actual crime. Cane gets the same sincerity back at him from the wrong face. The three-era payoff pays off: the owl sprite is generated monochrome and placed in a full-colour room, so Frank Drebin Sr is visibly lit by a different decade than everything around him. The palette rule from DESIGN §2 doing the emotional work at the climax. Planned cases 10 and 11 merged into one folder — same film, same cast, no duplicated sprite set, and the owl payoff lands in the same case as its setup. Third authoring trap documented: sets_flag fires on use, never on take (points does both). A takeable item silently failed to set its flag and the gate it fed never opened; gate on `needs`, which checks inventory, instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
374 lines
20 KiB
Markdown
374 lines
20 KiB
Markdown
# Police Squad Quest — design bible
|
||
|
||
A Sierra-style adventure game in the *Police Squad!* / *Naked Gun* universe,
|
||
built on [MRPGI](../MRPGI). Source research: [RESEARCH.md](RESEARCH.md).
|
||
|
||
---
|
||
|
||
## 1. The joke engine
|
||
|
||
The premise isn't "a funny adventure game". It's a specific collision:
|
||
|
||
> **Sierra's *Police Quest* (1987) × ZAZ's *Police Squad!* (1982).**
|
||
|
||
*Police Quest* was designed by Jim Walls, a real ex-cop, and was famously
|
||
**pedantic**: you had to walk around your car and check the tyres, radio in
|
||
correctly, draw your weapon *before* firing (or you shot yourself in the leg),
|
||
and read the Miranda rights in the right order. Get the procedure wrong → you
|
||
die. It is the single most self-serious game Sierra ever shipped.
|
||
|
||
*Police Squad!* is the same subject matter played as total farce.
|
||
|
||
**So the game grades you on procedure with a completely straight face, and the
|
||
reward for perfect procedure is catastrophe anyway.** That's the machine. It's
|
||
mechanical, not just written — the player *performs* the joke rather than
|
||
watching it.
|
||
|
||
Three rules fall out of that:
|
||
|
||
1. **The game never winks.** All system text is flat police-procedural prose.
|
||
Score readouts, death cards, refusals — deadpan. The comedy is what the flat
|
||
text is describing. (See RESEARCH.md §4.)
|
||
2. **Correct procedure is mandatory and useless.** You *must* do it. It never
|
||
helps.
|
||
3. **The parser is Frank's brain.** A text parser is a literal-minded machine,
|
||
and literalism is ZAZ's core joke. `look` answers take things at face value.
|
||
|
||
### Worked example — the opening of Case One
|
||
|
||
```
|
||
> take badge
|
||
Taken. (+2)
|
||
> east
|
||
You have not signed for a sidearm. Regulations are regulations.
|
||
> take revolver
|
||
Taken. (+2)
|
||
> east
|
||
```
|
||
Two points for procedure. Then you get to the crime scene and the body is gone
|
||
because Nordberg filed it.
|
||
|
||
---
|
||
|
||
## 2. The two Drebins, and the three eras
|
||
|
||
John's instinct that Frank Drebin Sr should be "a 1950s style cop" is exactly
|
||
right, and it's supported by the source: *Police Squad!* was a straight parody of
|
||
***M Squad*** (1957–60) and *Felony Squad*. The 2025 film then makes Neeson's
|
||
Frank Drebin **Jr.** canon — and gives Sr. a supernatural cameo as an owl. A
|
||
two-Drebin game is the franchise's own structure, not a liberty.
|
||
|
||
| Act | Who | Look | Palette |
|
||
|-----|-----|------|---------|
|
||
| **I** | Frank Drebin **Sr** — the TV series | 1950s hardboiled cop show, shot on videotape | EGA **0 / 7 / 8 / 15** + brown ego. Monochrome. |
|
||
| **II** | Frank Drebin **Sr** — the films | 1988–94 blockbuster | Full 16-colour EGA |
|
||
| **III** | Frank Drebin **Jr** (Neeson) — 2025 | modern LA, tech-money | Full EGA, cold blues/cyans |
|
||
|
||
**The palette restriction is free and does all the work.** MRPGI is EGA anyway;
|
||
Act I simply doesn't use the other twelve colours, so the era switch is visible
|
||
the instant a case loads. Act III's owl is the transition device: Sr. shows up
|
||
to help Jr., which is canon and also lets both Drebins share a screen.
|
||
|
||
**Drebin Jr's homage** (John's specific ask): Neeson's register is *sincerity*
|
||
where Nielsen's was obliviousness. Jr. isn't stupid — he's earnest, gravel-voiced,
|
||
and completely committed to a plan that makes no sense. Write Jr's `look` text as
|
||
genuinely moving. Then have it be about a vending machine.
|
||
|
||
---
|
||
|
||
## 3. Structure — one folder per case
|
||
|
||
MRPGI loads any directory with `--game DIR`, so **each mission is a standalone
|
||
game folder** under `cases/`. No global room numbering, no hub-state plumbing,
|
||
cases can be built in any order and in parallel.
|
||
|
||
```
|
||
cases/case-01-substantial-gift/
|
||
game.json manifest: name, start_room, intro, extra verbs, flags, max_score
|
||
sprites.json MODELBEAST prompts (input to tools/sprites.py — not read by the engine)
|
||
sprites/*.png generated art; ego.png is the player
|
||
rooms/room*.json the case
|
||
```
|
||
|
||
`./play.sh` lists cases and runs one. Skipped: a hub world and cross-case save —
|
||
add when there are enough cases to want them.
|
||
|
||
---
|
||
|
||
## 4. Mechanics — all of it is pure JSON
|
||
|
||
MRPGI already has every field this design needs. **No engine work.**
|
||
|
||
| Want | Field | Use |
|
||
|------|-------|-----|
|
||
| Procedure gate on a doorway | `exit_flags` + `exit_blocked` | "You have not signed for a sidearm." |
|
||
| Sierra death | `kills` on object or dialogue choice | The `use_text` is the death card |
|
||
| Police Quest scoring | `points` + manifest `max_score` | Points for *procedure*, not progress |
|
||
| Evidence that appears once found | `visible_flag` | Dusting reveals the print |
|
||
| Things that vanish | `hidden_by_flag` | The body Nordberg files |
|
||
| Item locks | `needs`, `consumes` | Warrant, evidence bag |
|
||
| Interrogation | `dialogue` + `requires_flag` / `sets_flag` | Miranda rights in order |
|
||
|
||
Two small engine changes were made upstream in MRPGI, both general-purpose and
|
||
useful to any game: a folder can supply **`sprites/ego.png`** to replace the
|
||
default party robot (plus optional `ego1.png` for the walk bob), and
|
||
**`death_text`** in `game.json` overrides the death banner, which was previously
|
||
hardcoded to another game's flavour text. Nothing else was added — every
|
||
mechanic below already existed.
|
||
|
||
### Custom verbs
|
||
|
||
`game.json` merges extra synonyms into the verb table, and the AI-parser
|
||
whitelist is generated from the same table so they can't drift:
|
||
|
||
```json
|
||
"verbs": [
|
||
{ "name": "use", "words": ["dust", "cuff", "book", "frisk", "radio", "mirandize", "arrest"] },
|
||
{ "name": "look", "words": ["investigate", "detect"] },
|
||
{ "name": "talk", "words": ["interrogate", "question"] }
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 5. The campaign
|
||
|
||
### Act I — Frank Drebin Sr, *Police Squad!* (monochrome)
|
||
|
||
Six cases, one per episode. Each opens with a **special guest star being murdered
|
||
in the title card** — a real object in the room whose only purpose is to die.
|
||
|
||
| # | Case | From | Hook | Procedure gag |
|
||
|---|------|------|------|---------------|
|
||
| 01 | **A Substantial Gift** | Ep 1 | Credit union manager shot; teller Sally Decker did it for orthodontist money and framed a customer | Sign out badge + sidearm; dust for prints; Mirandize in order — **built, see §6** |
|
||
| 02 | **Ring of Fear** | Ep 2 | Undercover as a boxing manager; a fighter's wife kidnapped to force a dive | You must maintain your cover identity *and* file correct overtime paperwork |
|
||
| 03 | **Rendezvous at Big Gulch** | Ep 3 | Protection racket; Frank and Norberg run a **locksmith shop** as a front | The front business is graded on customer service. Real locks. Real customers. |
|
||
| 04 | **Revenge and Remorse** | Ep 4 | Courthouse bombing, Eddie Casales framed | Bomb disposal by the manual, in order. The manual is wrong. |
|
||
| 05 | **The Butler Did It** | Ep 5 | Kidnapping at a rich man's birthday party | Chain of custody on a birthday cake |
|
||
| 06 | **Testimony of Evil** | Ep 6 | Comedian dies; nightclub drug ring | Undercover as a stand-up. You must actually pick punchlines. Dying on stage = a Sierra death card. |
|
||
|
||
### Act II — Frank Drebin Sr, the films (full colour)
|
||
|
||
| # | Case | From | Hook |
|
||
|---|------|------|------|
|
||
| 07 | **From the Files** | *Naked Gun* (1988) | **BUILT — see §7.** Nordberg shot on Ludwig's freighter; the Queen's reception; then Anaheim Stadium, where you **umpire a baseball game in order to legally frisk the players**. |
|
||
| 08 | **The Smell of Fear** | *Naked Gun 2½* (1991) | Spot the fake Dr. Meinheimer by testing a photographic memory the double doesn't have. Includes the zoo, the SWAT tank, and disarming a nuclear device — solution: unplug it. |
|
||
| 09 | **The Final Insult** | *33⅓* (1994) | Retired Frank goes undercover in prison as "Nick the Slasher McGurk". Bomb in the Best Picture envelope. Marriage counselling is a live stat you must not tank. |
|
||
|
||
### Act III — Frank Drebin Jr, 2025 (full colour, cold palette)
|
||
|
||
| # | Case | From | Hook |
|
||
|---|------|------|------|
|
||
| 10 | **The P.L.O.T. Device** | *Naked Gun* (2025) | **BUILT — see §8.** The whole film in one case: the bank cold open, Simon Davenport's "suicide", Richard Cane, and the New Year's ball drop where **Frank Drebin Sr arrives as an owl**. |
|
||
|
||
*(Originally planned as two cases; merged into one folder — same film, same cast,
|
||
no duplicated sprite set, and the owl payoff lands in the same case as its setup.)*
|
||
|
||
### The prologue — "1958"
|
||
|
||
A short unlockable Act 0: **Drebin Sr as a rookie**, played completely straight
|
||
as *M Squad*. Same monochrome palette, no jokes in the system text at all — until
|
||
the last screen. It's the origin of the owl.
|
||
|
||
---
|
||
|
||
## 6. Case One — as built
|
||
|
||
**`cases/case-01-substantial-gift/`** — the vertical slice, playable now.
|
||
|
||
Five rooms, 100 points.
|
||
|
||
| Room | Place | Contains |
|
||
|------|-------|----------|
|
||
| 0 | Police Squad HQ, squad room | Hocken (briefing), Nordberg (injured), **Al** (74px — his head leaves the top of the screen and no one comments), your desk, badge, sidearm, case file |
|
||
| 1 | The street | Johnny the Snitch's shoeshine stand, the squad car |
|
||
| 2 | The credit union | The body, the vault, the teller counter, **Sally Decker** |
|
||
| 3 | The crime lab | Ted Olson and the microscope |
|
||
| 4 | The orthodontist's | The dental chair, the braces cast, the arrest |
|
||
|
||
**The chain:** sign out badge + revolver (procedure gate on the HQ exit) → read
|
||
the case file → crime scene → dust the counter for prints → take the evidence bag
|
||
→ lab: Olson matches the print → the orthodontist's bill names Sally → return and
|
||
Mirandize her **in the correct order** (wrong order = she walks and you get a
|
||
Sierra death card from Hocken, which is not a real death, which is the joke) →
|
||
arrest.
|
||
|
||
**Running gags wired in as mechanics:**
|
||
- **Al** — a 74px sprite in a 168px room. Placed so his head is above the frame.
|
||
His `look` text describes only his shoes.
|
||
- **Johnny** — `talk johnny` costs a dollar and will answer *anything*. His
|
||
dialogue tree has more nodes about orthodontics and macroeconomics than the case.
|
||
- **Nordberg** — appears in every room in worse condition. Never mentioned.
|
||
- **The guest star** — dies in the title card. `look guest` in room 0 before he
|
||
dies, and he's already dead.
|
||
- **The freeze-frame ending** — the win text describes the cast holding still
|
||
while things audibly keep going wrong.
|
||
|
||
---
|
||
|
||
## 7. Case Seven — the umpire problem
|
||
|
||
**`cases/case-07-from-the-files/`** — Act II, full-colour EGA. Six rooms, 100 points.
|
||
|
||
Hospital → Ludwig Shipping → the Queen's reception → the officials' room →
|
||
home plate → the upper deck.
|
||
|
||
**The central mechanic: the strike zone is the puzzle.** Drebin has been fired,
|
||
so he has no power to search anyone. But a home plate umpire can crouch six
|
||
inches from a professional athlete, take his bat off him, and inspect his
|
||
equipment — and nobody in a stadium of sixty thousand thinks twice, because
|
||
that is simply what umpires do. **So every frisk needs an umpire-legitimate
|
||
pretext, in order:**
|
||
|
||
1. `use homeplate` — brush the plate. The only reason a masked man on his knees
|
||
beside an athlete is acceptable. Sets `at_plate`.
|
||
2. `use catcher` — inspect the mitt for a foreign substance. Clean.
|
||
3. `use batter` — check the bat for pine tar above the legal limit. Clean.
|
||
4. `use slugger` — Number 44, who has been staring at the owner's box since the
|
||
second inning. A pager taped inside the batting glove.
|
||
|
||
Skip the pretext and the object simply refuses (`requires_flag`). Announce
|
||
yourself as police — a dialogue choice that is right there, and correct, and
|
||
what a police officer would do — and sixty thousand people come over the
|
||
railings. Sierra death.
|
||
|
||
**The other inversion:** at the Queen's reception, Ludwig hands a two-hundred-
|
||
year-old firearm to the person you are there to protect, and you tackle a head
|
||
of state. That is *correct procedure*, it ends your career, and **being fired is
|
||
the only way to progress** — a private citizen can't walk onto that field, but
|
||
an umpire can walk anywhere.
|
||
|
||
Also in: Nordberg conducting an entire briefing through a wired jaw; Ludwig
|
||
owning two hundred and six ships and one of them being called I LUV LUDWIG;
|
||
the anthem lyric sheet; the steamroller, established in Room 5 as scenery an
|
||
hour before it is needed.
|
||
|
||
**Two authoring traps this case caught, worth knowing before writing Case 08:**
|
||
- **Object names must not be prefixes of each other.** `ludwigdesk` shadowed
|
||
`ludwig`, so `talk ludwig` addressed the furniture. The parser matches
|
||
loosely; renaming it to `desk` fixed it.
|
||
- **Flag-gated edge exits need walkable floor into the trigger zone.** The north
|
||
exit fires at `y ≤ 21`, which was inside the painted stands (a `Wall` band) —
|
||
the upper deck was unreachable on foot until a tunnel was carved through it.
|
||
Verify every exit by *walking* it, not with `goto_room`.
|
||
|
||
---
|
||
|
||
## 8. Case Ten — Drebin Jr, and the owl
|
||
|
||
**`cases/case-10-plot-device/`** — Act III, cold-palette EGA. Six rooms, 100 points.
|
||
|
||
Bank → Police Squad → the morgue → Mulholland → Cane's club → the arena.
|
||
|
||
**The Neeson homage is a register, not a costume.** Nielsen played Drebin Sr
|
||
*oblivious* — the joke was that he never noticed. Neeson plays Drebin Jr
|
||
**sincere** — he notices everything and means all of it, completely, at all
|
||
times. So Jr's text is written straight and often genuinely moving, and the
|
||
comedy is the collision with what it's about:
|
||
|
||
- He gives a *Taken* speech about his particular set of skills. Chief among
|
||
them is paperwork.
|
||
- He takes four armed robbers in eleven seconds without drawing his weapon,
|
||
which is the finest work of his career, and he does it dressed as a
|
||
schoolgirl, with his back to the actual crime.
|
||
- He tells Beth he's sorry for her loss and **means it**, and that turns out to
|
||
be the only useful thing in the room.
|
||
- The villain gets the same sincerity back at him from the wrong face:
|
||
*"I'm not going to hurt anybody. I'm going to make it quiet."*
|
||
|
||
**The three-era payoff.** Act I is monochrome, Act III is cold blue. The owl
|
||
sprite is generated **monochrome** and placed in a full-colour room, so Frank
|
||
Drebin Sr is visibly lit by a different decade than everything around him —
|
||
the palette rule from §2 doing the emotional work at the climax. `look owl`
|
||
before the finale is the quietest text in the game. `use owl` is two words.
|
||
|
||
**Also in:** the hot dog cart that has always been in the bank and which he has
|
||
never once asked about; the animated paperclip assistant; the trousers, which
|
||
he retrieves with enormous dignity because standards are not a thing you keep
|
||
only when they are being counted.
|
||
|
||
**Third authoring trap (add to the §7 list):** `sets_flag` fires on **use**,
|
||
never on **take** — `points` does both. A takeable item that was supposed to
|
||
set a flag silently didn't, and the gate it fed never opened. Gate on `needs`
|
||
(which checks inventory) instead of inventing a flag for "is carrying it".
|
||
|
||
---
|
||
|
||
## 9. The glitches are canon
|
||
|
||
*Police Quest II* shipped with bugs that are, without alteration, Naked Gun
|
||
jokes. A player who has never heard of Sierra reads them as Drebin being Drebin;
|
||
a player who has reads them as a love letter. **So we ship them deliberately.**
|
||
|
||
| Real PQ2 bug | In Police Squad Quest |
|
||
|---|---|
|
||
| Keys locked in the car, station doors lock behind you → **softlocked in the parking lot on screen one** | The squad car in Case One: *"The keys are locked inside, on the seat, in plain view, behind your own reflection."* Non-fatal. Nobody mentions it. |
|
||
| **Keith**, your partner, slacks off and smokes; driving back to the station makes him go do "paperwork" | **Nordberg.** Casts, traction, cigarette, assigned as your partner, has not stood up. His paperwork is a magazine. |
|
||
| Firing without raising the weapon → **you shoot yourself in the foot** | Hocken dialogue: *"Permission to test-fire my sidearm indoors, Captain."* → *"Granted."* → Sierra death. An optional trap, exactly as Sierra used it. |
|
||
| Holding a direction while drawing → **hands and gun detach and float above your head** | Act II gag: a `raise` verb that reports the weapon is now above the ceiling, and stays there for the rest of the case. |
|
||
| **"Headless chicken" pathfinding** — NPCs spiral endlessly around obstacles | Al, in Case One, *"continues walking in a tight, rapid circle around the water cooler, as he has all morning."* |
|
||
| **Metal detector only checks if you're facing up** — walk diagonally past it | Act III airport screen. The detector is correct and the regulation is correct and you go through diagonally. |
|
||
| **Waving a drawn gun at airport security** with no reaction until you cross a specific vector | Same screen. The guard reacts to geometry, not to guns. |
|
||
| **Air-walking** — keep the swimming animation on dry land | Act II, after the zoo. Frank swims across the car park. |
|
||
| **Levitating into the sky** off a transition | Act III finale — but it's the owl. |
|
||
| Typing **`halt`** resets the enemy's shot counter → infinite standoff | `halt` is already a verb in Case One's manifest. In the Act II standoff it works, forever, and the game never acknowledges why. |
|
||
| **Infinite score** by re-dusting the ashtray / re-questioning the amnesiac witness | Case One, dusting the counter: *"The department pays out on a lifted print once. You may dust this counter as many times as you like. It has been looked into."* |
|
||
|
||
The rule: **play the bug straight.** Never nudge the player. The comedy is that
|
||
the game is behaving exactly as documented.
|
||
|
||
---
|
||
|
||
## 10. Art pipeline
|
||
|
||
`tools/sprites.py <case>` reads that case's `sprites.json` and generates every
|
||
missing sprite on the **MODELBEAST** farm (free, local, m3ultra):
|
||
|
||
```
|
||
flux_local (text→image, 512²) → bg_remove_local (RMBG-2.0 cutout) → Pillow
|
||
nearest-neighbour trim+downscale to the sprite's target height
|
||
```
|
||
|
||
Trimming to the alpha bounding box before scaling is what puts the subject's feet
|
||
on the bottom edge, which is how MRPGI positions and depth-sorts sprites. The
|
||
engine quantizes to EGA itself on load, so the script only owes it a small
|
||
transparent PNG.
|
||
|
||
A shared `STYLE` constant in the script enforces the Sierra look; per-sprite
|
||
prompts stay short. Act I prompts end in *"black and white monochrome grayscale
|
||
only"* — that one phrase is the whole 1950s era treatment.
|
||
|
||
```bash
|
||
python3 tools/sprites.py cases/case-01-substantial-gift # fill gaps
|
||
python3 tools/sprites.py cases/case-01-substantial-gift --force # redo all
|
||
python3 tools/sprites.py cases/case-01-substantial-gift --only ego,al
|
||
```
|
||
|
||
Seeds are derived from the sprite name, so a rerun reproduces the same art.
|
||
|
||
### On LoRAs (tested, negative result)
|
||
|
||
The farm has ~40 LoRAs, but **none of them currently apply to this pipeline**, and
|
||
it's worth writing down why so nobody burns an afternoon on it twice:
|
||
|
||
- The style LoRAs worth wanting (`style_retro-flux`, `old_timey_v4`) live in
|
||
`vendor/comfyui/models/loras/`, which is **not** in `MB_LORA_DIRS` — a bare
|
||
name 404s. A full path loads them.
|
||
- Loaded that way they are still a **no-op**. They're FLUX.1-dev LoRAs and the
|
||
only ungated model is `flux2-klein-4b`, a different architecture. An A/B at a
|
||
fixed seed gives three visually identical images (checksums differ by a
|
||
hair; nothing you can see). `Iso-Pixel-05` is SD-architecture — same result.
|
||
- Using them for real needs either the HF-gated `dev` model, or routing
|
||
SD/SDXL LoRAs through the `comfyui_sd` operator instead.
|
||
|
||
**And it wouldn't help much anyway.** The pixel-art look comes from the
|
||
nearest-neighbour crush to ~24px, not from the model — a style LoRA at 512px
|
||
is thrown away by the downscale. Plain `flux2-klein-4b` is the right tool here.
|
||
Where the farm's bulk capacity *does* pay off is quantity: 48 sprites across two
|
||
cases, free, in a few minutes.
|
||
|
||
**Farm gotcha:** the guest token caps concurrent jobs, and going over returns
|
||
`429`/`401` — which reads like an auth failure and isn't. `tools/sprites.py`
|
||
retries with backoff; if a whole batch fails, something else of yours is using
|
||
the farm. Don't run two clients at once.
|