- Generate and integrate authentic 16-bit LucasArts taproom scene with Theke, brass taps, bartender, drinking patrons, and bunker trapdoor - Realign Schankraum hotspots for Verena, Lutz in hot tub, Armend at forge, room transitions, and centered bunker trapdoor - Update Papa Markus canonical portrait and Taiwan street dining scene to be bald and clean-shaven with glasses and modern attire - Enforce hotspot zero-occlusion rules for signposts, characters, and mobile room navigation bars - Document Papa Markus specifications, tavern invariants, and layout guidelines in AGENTS.md - Add automated sister capture and playthrough recording scripts
20 KiB
AGENTS.md — Saschas Kronkorken Kreuzzug (sasch40)
Note
Domain: Mobile-first 2.5D retro birthday adventure game for King Sascha I.'s 40th jubilee.
Live Production URL:https://sasch40.haumdaucher.de
Kubernetes Manifest:k8s/web/sasch40.yaml(Namespace:web)
Source Code:projects/sasch40/frontend/
Reference Photos:projects/sasch40/source_photos/
🏛️ Architectural Decisions & Non-Negotiable Rules
This document records the foundational design decisions, their rationale ("Why"), and constraints for future AI coding agents. Do not regress or remove these patterns.
1. QR-Code Landing & Atmospheric Prequel (IntroScreen.vue)
- The Decision: The application MUST ALWAYS start with an informative, atmospheric intro/lore screen before any gameplay begins.
- Why: Party guests scan a physical QR code on beer bottles or tables with their smartphones. They do not know what the link opens. Without an introductory landing page, users are confused when suddenly dropped into an arcade mini-game.
- Content Requirements:
- Title: „Saschas Kronkorken Kreuzzug: 40 Jahre Bass & Gerstensaft“.
- Royal Avatar: King Sascha I. with sunglasses and golden crown.
- Lore: Explains the 40-year reign over Haumdaucher, 135 BPM techno culture, the theft of the 40 sacred Kronkorken, and a preview of the upcoming acts.
- CTA: Prominent button „SPIEL STARTEN / RETTE DAS JUBILÄUM!“.
- Audio prompt: „Smartphone-Lautstärke aufdrehen für echten 135-BPM-Mittelalter-Techno!“.
2. Full-Screen Mobile-First Web App (GameBoyFrame.vue)
- The Decision: The GameBoy bezel chassis, D-Pad, and external A/B action buttons have been completely removed across the entire application in favor of a 100% full-screen responsive mobile layout.
- Why: Mobile players interact directly with high-fidelity touch controls on modern smartphone screens. Virtual GameBoy bezels and D-Pads wasted over 40% of the viewport and degraded playability in arcade and visual-novel acts.
- The Rule:
- The app container fills 100% of viewport height and width.
- Header: Line 1 displays
KÖNIG SASCHAS KRONKORKENKREUZZUG, Line 2 displays the act badge (e.g.AKT III). The right side displays inventory counts (👑,🍻) and the persistent⚙️settings icon. - Controls: Act II uses tap/touch screen halves (
left/right) or arrow keys; Act IV uses full-width on-screen highway strum buttons ([D LINKS],[G MITTE],[A RECHTS]).
2b. Global Settings & Audio Control Modal (GameBoyFrame.vue)
- The Decision: A persistent
⚙️settings icon is available at the top-right of the GameBoy frame. - Why: Mobile players need a straightforward way to mute sound in public, pause during conversations, or cleanly restart the adventure.
- The Rule: Clicking
⚙️opens a modal that pauses game loops, provides toggleable audio (🔊 Sound: AN / 🔇 Sound: AUS), and offers a🔄 Spielstand zurücksetzenoption with safety confirmation.
2c. Prologue Branching & Themed Game Overs (PrologueView.vue)
- The Decision: Prologue choices can trigger humorous non-fatal Game Over screens with themed reset actions:
- 🔥 Flo Maier (Feierdeifl): Choosing to burn the castle leads to a fiery castle inferno and a
🚒 MIT DEM LÖSCHZUG NEU STARTENbutton. - 🧛 Viktor (Partyvampir): Choosing the vampire bite turns Sascha into dust at sunrise and provides a
🧄 MIT KNOBLAUCH-KUR NEU STARTENbutton.
- 🔥 Flo Maier (Feierdeifl): Choosing to burn the castle leads to a fiery castle inferno and a
- Why: Gives playful branching flavor without permanently penalizing the player.
2d. Akt II: Doodle Jump Schwesternrettung (PlatformerView.vue)
- The Decision: Act II is a bottom-to-top vertical bouncing climber across 3 progressive levels to rescue King Sascha's three sisters:
- Level 1: Rescue Saskia (+3 Kronkorken, 1 Helles)
- Level 2: Rescue Maja (+3 Kronkorken, 1 Helles)
- Level 3: Rescue Miriam (+4 Kronkorken, 1 Helles)
- Mechanics:
- Continuous automatic upward bouncing upon landing on platforms.
- Spring platforms (
🌀) for high-velocity boost with custom synthesizer audio. - Seamless screen wrap-around (moving off the left edge wraps to the right and vice versa).
- Gentle fall protection: falling below the visible screen softly places Sascha onto a safe platform near his lower altitude rather than triggering a punitive death.
2e. Akt III: Raum „Hinter den Bergen“ & Liar's Dice (TavernView.vue)
- The Decision: Akt III features a special mountain hideaway scene „Hinter den Bergen“ accessible via Hinterland signpost or tab bar.
- Why: Introduces Saschas father Papa Markus and his Taiwanese connection with Gaoliang drinking culture into the anniversary narrative.
- Papa Markus Canonical Appearance:
- Head & Face: Completely bald dome (Glatze), strictly clean-shaven (NO BEARD, NO STUBBLE), dark rectangular Ray-Ban glasses, strong masculine angular jawline & nose closely resembling Sascha (see
source_photos/markus2.jpeg). - Attire: Smart-casual dark jacket and dark open-collar shirt (STRICTLY NO LEDERHOSEN).
- Scene Composition: Seated outdoors directly in front of a bustling Taiwanese street restaurant (re-chao bistro) at a wooden night-market table with Taiwanese business companions in modern suits. On the table: small shot glasses, dice cups for Liar's Dice (Chui Niu / 吹牛), and a bottle of 58% Kinmen Gaoliang liquor.
- Atmospheric Clash: Authentic Taiwan street eatery with cyberpunk Tokyo lighting, glowing neon kanji/hanzi signs, and red paper lanterns, surrounded by towering medieval alpine cliffs and pine trees in 1990s LucasArts SCUMM 16-bit pixel art style.
- Head & Face: Completely bald dome (Glatze), strictly clean-shaven (NO BEARD, NO STUBBLE), dark rectangular Ray-Ban glasses, strong masculine angular jawline & nose closely resembling Sascha (see
- Mechanics:
- Minigame Liar's Dice (Chui Niu / 吹牛): 5 dice each, 1s are wild, sequential bidding, bluff-calling reveals, loser drinks a 58% Kinmen Gaoliang shot (
🍶). - Rewards: +10 Kronkorken, inventory item 58% Kinmen Gaoliang, Easter Egg title
taiwan_dice_champion.
- Minigame Liar's Dice (Chui Niu / 吹牛): 5 dice each, 1s are wild, sequential bidding, bluff-calling reveals, loser drinks a 58% Kinmen Gaoliang shot (
2f. Akt IV: Beat-Only Metronom & Harmonische Lautenakkorde (PartyExpressView.vue & synthAudio.ts)
- The Decision: During the Castle Stage rock concert in Act IV, the synthesizer BGM switches to
beat_onlymode (melody muted, playing solely the 135-BPM kick/hi-hat rhythm). - Why: Playing a rhythm game while a pre-recorded synthesizer melody plays in parallel creates cacophony and ruins the illusion that the player's notes compose the music.
- The Rule: Successful hits on D, G, A trigger full 3-note harmonic medieval chords; misses trigger a muted plink. On act completion or exit, BGM restores to
full.
3. Protagonist System & Extensibility (characters.ts)
- The Decision: All character portraits are strictly 1:1 square pixel-art illustrations based directly on real-life friends and family photos, maintained in a centralized registry.
- Why: Guests at the party must immediately recognize themselves and their friends. Previous attempts at generic AI portraits caused confusion because hair, beards, and styles did not match the actual people.
- Reference Photos:
- Stored in
projects/sasch40/source_photos/(sascha_*.jpg,nat.jpg,lutz*.jpg,amelie.jpg,armend.jpg,moritz2.jpg, etc.).
- Stored in
- In-Game Portraits:
- Stored in
projects/sasch40/frontend/public/assets/characters/<id>/portrait.png.
- Stored in
- How to Add or Modify Protagonists:
- Add a 1:1 square pixel-art portrait into
public/assets/characters/<id>/portrait.png. - Register the protagonist in
projects/sasch40/frontend/src/config/characters.ts:export const CHARACTERS: Record<string, CharacterConfig> = { neuer_gast: { id: 'neuer_gast', name: 'Name (Spitzname)', title: 'Königlicher Hof-Titel', portrait: '/assets/characters/neuer_gast/portrait.png', quote: 'Charakteristischer Spruch' } } - Add an interactive hotspot or convoy sprite in
TavernView.vueorPartyExpressView.vue.
- Add a 1:1 square pixel-art portrait into
- Portrait Display Rule: Always use
aspect-ratio: 1 / 1andobject-fit: coverwith rounded borders (border-radius: 8pxor50%for badges). Never allow portrait stretching or horizontal letterboxing.
4. Background Art Integrity & Cleanliness
- The Decision: Background scenes must remain clean of any AI-hallucinated pseudo-English text or stray UI elements.
- Why: AI image generators often bake random adventure-game dialogue boxes or text into the scene.
- The Rule:
public/assets/scenes/schankraum_v3.jpg: Seamless warm dark oak wood floorboards with natural planks.- Any scene footer navigation (like
.scene-bottom-nav) must be styled as natural wooden wainscoting with clear German labels.
5. Mobile Canvas Safe-Zones & Responsive HUDs
- The Decision: Critical game HUD elements (score, act title, instructions) must either be drawn as HTML/CSS overlays or placed within a minimum safe margin of 24px on canvas.
- Why: Smartphones vary widely in screen aspect ratio (16:9, 19.5:9, 20:9). When a 360x480 canvas is rendered with
object-fit: cover, the left and right (or top and bottom) edges can be cropped by 10–25px on narrow screens. - The Rule:
- In
PlatformerView.vue: The Korken counter, sister progress, and level title are rendered via an absolute HTML overlay (.platformer-hud-top) so they are 100% immune to canvas cropping. - In
PartyExpressView.vue: The 3-lane strings, score counter, combo meter, and crowd meter use responsive percentages.
- In
6. Royal Certificate & Competitive Highscore (CertificateModal.vue)
- The Decision: The game concludes with a royal certificate featuring a competitive point breakdown and rank.
- Why: Party guests compare scores at the bar to determine who is the ultimate party knight.
- Score Breakdown:
- Kronkorken collected:
100 ptseach. - Helles tapped:
250 ptseach. - Guests served / Concert performance: up to
3,000 pts. - Gstanzl duel victory:
1,500 pts. - Discovered secrets (Bridgerton dialogue, Party Bunker, Zuluman mirror):
500 ptseach.
- Kronkorken collected:
- Ranks:
S-Rang (Legendär): >= 11,000 points („Unsterblicher Großfürst des Basses“).A-Rang (Erhaben): >= 9,500 points („Meister-Brauer der königlichen Garde“).B-Rang (Ritterlich): >= 7,500 points („Ehren-Ritter der Hopfen-Manufaktur“).C-Rang (Trinkfest): < 7,500 points („Kneipen-Knappe im Ausnüchterungs-Dienst“).
- Call-to-Action: Includes a screenshot / print button („📸 URKUNDE DRUCKEN / SCREENSHOT“) and Queen Verena's secret postscript.
7. Zero-Dependency Web Audio Synthesizer (synthAudio.ts)
- The Decision: All sound effects, fanfares, lute chords, and the 135 BPM techno bassline are synthesized in pure code using the Web Audio API.
- Why: External audio files (.mp3/.wav) take megabytes of bandwidth, stutter on mobile cellular networks, and trigger browser autoplay blocks.
- The Rule: The audio context initializes on the first user interaction (touch/click). Sound effects use oscillators and gain envelopes; 135 BPM kick and sub-bass use recurring periodic intervals.
8. Unhashed Asset Cache Invalidation (nginx.conf)
- The Decision: Unhashed static files (
/assets/scenes/*.jpg,/assets/characters/*/*.jpg) MUST be served withCache-Control: "no-cache, no-store, must-revalidate"andExpires: -1. - Why: In mobile browsers (Safari iOS, Chrome Mobile), default caching headers (
expires 30d) cause updated character portraits and scene backgrounds to remain stale for weeks on user devices. - The Rule: In
nginx.conf, static media matches\.(png|jpg|jpeg|gif|ico|svg|webp|mp3|wav|ogg)$withno-cache, no-store, must-revalidate. Bundled JS/CSS matches\.(js|css)$withpublic, immutablefor 1 year.
9. Party Bunker Trapdoor & Double-Tap Pattern (TavernView.vue)
- The Decision: Secret trapdoor (
.trapdoor-hotspotin Schankraum leading to the VIP Party Bunker) must be 100% invisible initially and requires a deliberate double-click or double-tap to unlock. - Why: A single accidental click on the floor should not whisk the player away into an Easter Egg bunker before they explore the tavern.
- The Rule:
- Rendered with transparent background, no border, no outline, no box shadow, no hover change, and no tooltip (
:title="undefined"). - Double-click/double-tap detection combines
@dblclickand timestamp delta (now - lastTrapTap < 450ms) to work flawlessly across desktop and mobile touchscreens. - Upon double-tap, it reveals the wood trapdoor sprite, labels itself „Luke zum Party Bunker“, plays a celebratory fanfare, unlocks the Easter egg in the game store (+500 points), and transitions to the Party Bunker.
- Rendered with transparent background, no border, no outline, no box shadow, no hover change, and no tooltip (
10. Multi-Room World Architecture & Tavern Minigames (TavernView.vue)
- The Decision: Act III is structured as a spatial, interconnected five-room village hub rather than a cramped single screen:
- Gaststube (Schankraum):
- Authentic Tavern Atmosphere: Patrons and villagers sitting at rustic wooden tables drinking beer from foamy glass steins.
- Theke & Schankanlage: Wooden bar counter ("Theke") with shiny brass taps where a bartender serves and pours fresh beer into steins.
- Queen Verena: Standing on the upper wooden gallery/balcony holding a foamy beer stein.
- Lutz (ZTUL): Relaxing in the steaming wooden hot pot / wash tub on the corner gallery, wearing funny party sunglasses and a feathered hat.
- Armend: Hammering bottle caps on his anvil right next to a blazing blacksmith forge fire with glowing coals.
- Techno Gimmicks: Wooden beer barrel retrofitted with a high-end subwoofer cone, glowing cyan LED ring, and golden dust cap; subtle neon cables along beams.
- Navigation & Exits: Sturdy wooden door to Nebenraum on the left, arched stone doorway to the sunny Marktplatz on the right, and the secret „Luke zum Party Bunker“ in the floorboards leading down towards the bunker.
- Königlicher Nebenraum (Kemenate & Atelier): Amy painting the royal „Rave the Kingdom!“ banner at her easel in overalls, and the golden Zuluman magic mirror on the wall.
- Haumdaucher Marktplatz: Mario acrobatically juggling chainsaws and hops on the cobblestones; NAT (Josef) on the wooden festival stage with wild boar banners telling stories of his girls and heroic deeds.
- Alpines Hinterland: Mane (Ramona) hiking the alpine trail with walking stick; Hansi on his departure journey towards Münchanien.
- Geheimes VIP-Party-Bunker: Secret rave with disco chandelier, stroboscope, Pü's crystal staff, DJ Flo Maier, and Viktor the night-guard.
- Gaststube (Schankraum):
- Hotspot UI Placement & Visibility Rules:
- Zero Text Occlusion: Hotspot badges must NEVER be positioned directly over hand-painted text on signs (e.g. the carved arrow text „Hinter den Bergen“ must remain fully legible; place the button slightly above or to the side).
- Zero Character Occlusion: Hotspot badges must NEVER cover the face or body of another character (e.g. Hansi's button must sit high on the distant mountain path, well clear of Mane's head and backpack).
- Bottom Margin Safety: Room transition buttons must be placed at a safe vertical altitude (
top: <= 82%) so they are never obscured or cropped by the bottom room-switch navigation tabs on mobile screens.
- Minigames & Exit Safety:
- Bayerisches Gstanzl-Fechten: Multi-round insult rhyming duel. Preceded by an explanatory rules modal. Must always include a visible
[✕]close button so players are never trapped. - Wildschwein-Schnick-Schnack-Schnuck: Best-of-3 tactical duel against NAT. Preceded by an explanatory rules modal (🐗 Keiler bricht 🌿 Dickicht, 🌿 Dickicht fängt 🏹 Lanze, 🏹 Lanze erlegt 🐗 Keiler). Includes
[✕]close button and active button blur to prevent mobile sticky hover states.
- Bayerisches Gstanzl-Fechten: Multi-round insult rhyming duel. Preceded by an explanatory rules modal. Must always include a visible
10b. Akt IV: Mittelalter Guitar Hero — Hansis Abschiedskonzert (PartyExpressView.vue)
- The Decision: Act IV is a 3-lane medieval lute rhythm game set on an epic castle festival stage in front of 12,000 cheering fans.
- Why: Perfectly bridges the story from Hansi's departure in the Hinterland to a grand musical send-off before the royal coronation.
- The Rule:
- 3 color-coded lanes (
🔴 Saite 1,🟡 Saite 2,🔵 Saite 3). - Notes spawn synchronized to 135 BPM audio clicks and melodic riffs.
- Three large, finger-friendly touch buttons positioned comfortably at the lower screen for thumb play.
- Combo multiplier increases up to 4x with unbroken streaks.
- Generous hit window for fun, non-punishing festival gameplay.
- 3 color-coded lanes (
11. LocalStorage Persistence & Clean Game Reset (gameStore.ts)
- The Decision: Game state automatically synchronizes to
localStorage(sasch40_game_save_v1) on every mutation and is cleanly wiped when clicking „🔄 NOCHMAL SPIELEN“. - Why: Mobile users frequently refresh or switch apps to snap photos. Without persistence, a browser reload resets their progress back to Act I.
- The Rule:
useGameStoreautomatically initializes fromlocalStorageif valid data exists.store.$subscribewrites the state tolocalStoragewhenever any value changes.resetGame()clearslocalStorage.removeItem(...), sets all stats to zero, and returns to the Prequel Intro.
🛠️ Build, Test & Deployment Workflow
Fast Docker Packaging (Local ARM64 Compile)
To avoid 10-minute QEMU emulation hangs when building linux/amd64 images on Apple Silicon Macs, the frontend is compiled locally on the host machine before Docker copies the static output:
cd projects/sasch40/frontend
# 1. Compile natively (< 2 seconds)
npm run build
# 2. Package, push & deploy to k8s (< 10 seconds total)
make all
The Makefile targets:
compile: Runsnpm run build(vue-tsc -b && vite build).build: Runsdocker build --no-cache --platform linux/amd64 -t sasch40:latest .(copiesdist/directly intonginx:alpine).tag: Tags asregistry.haumdaucher.de/sasch40:latest.push: Pushes to the Haumdaucher registry.deploy: Executeskubectl rollout restart deployment/sasch40 -n weband monitors rollout status.
Automated Headless Chrome Playtest
Before considering any change complete, run the automated Puppeteer playtest script:
cd projects/sasch40/frontend
node scripts/playtest.mjs
This script:
- Navigates to
https://sasch40.haumdaucher.de. - Verifies the Intro Screen and clicks Start.
- Simulates dialogue options on the Castle Balcony (Prologue).
- Tests movement and jump controls in Act II (Hopfen-Pfade).
- Interacts with all 5 rooms, hotspots, dialogue modals, Zuluman mirror, Luke zum Hinterraum, Gstanzl duel, and the Wildschwein duel in Act III (Taverne).
- Tests reload persistence in
localStorage. - Advances through the Party Express (Act IV) and reaches the Royal Certificate (Act V).
- Tests the
🔄 NOCHMAL SPIELENbutton and verifies return to Intro. - Captures 25 screenshots to verify zero visual regressions.
- Asserts zero console errors, zero failed HTTP requests, and zero broken images.