feat(sasch40): major overhaul with doodle jump, party bunker, and guitar hero

- Add global settings & pause modal (gear icon) with mute and reset, removing obsolete bag button
- Extend Prologue with Flo Maier (castle fire game over & Löschzug restart) and Viktor (vampire bite game over & Knoblauch restart)
- Rebuild Akt II as 3-level vertical Doodle Jump climber to rescue sisters Saskia, Maja, and Miriam with custom pixel portraits
- Update Akt III Schankraum with seamless oak floor, double-tap 'Luke zum Party Bunker', duel intro modals, and Hansi's departure in Hinterland
- Rebuild Akt IV as 3-lane medieval Guitar Hero rock concert with live crowd meter, combo multipliers, and Hansi duo
- Update AGENTS.md architectural documentation and playtest test suite
This commit is contained in:
Moritz Graf
2026-09-24 18:22:28 +02:00
parent 6374ba9e8a
commit 8a0b65c3fa
22 changed files with 2307 additions and 1250 deletions
@@ -111,21 +111,48 @@ export const CHARACTERS: Record<string, CharacterConfig> = {
flo_maier: {
id: 'flo_maier',
name: 'Flo Maier',
title: 'Hof-DJ & Sound-Ingenieur',
title: 'Hof-DJ & Feierdeifl',
portrait: '/assets/characters/flo_maier/portrait_v2.jpg',
description: 'Mastermind an den Turntables und Behüter der heiligen 135-BPM-Frequenz.',
famousQuote: 'Dieser Track drückt mehr als die königliche Kutsche!',
description: 'Mastermind an den Turntables und berüchtigter Feierdeifl des Reiches.',
famousQuote: 'Soll i ois ozindn?! A riesigs Freudenfeuer!',
themeColor: '#0284c7'
},
viktor: {
id: 'viktor',
name: 'Viktor',
title: 'Nachtwächter & Party-Ritter',
title: 'Partyvampir & Nachtwächter',
portrait: '/assets/characters/viktor/portrait_v2.jpg',
description: 'Wacht im samtenen Vampir-Umhang über das Nachtleben und sorgt für eiskaltes Helles.',
famousQuote: 'Die Nacht ist jung und der Gerstensaft fließt in Strömen!',
description: 'Wacht im samtenen Vampir-Umhang über das Nachtleben und lauert auf frisches Helles.',
famousQuote: 'Ein Biss, und du ravst für alle Ewigkeit!',
themeColor: '#b91c1c'
},
saskia: {
id: 'saskia',
name: 'Schwester Saskia',
title: 'Die Erstgeborene Schwester',
portrait: '/assets/characters/saskia/portrait.jpg',
description: 'Wurde auf den ersten Turm in den Wolken verschleppt und ruft nach König Sascha!',
famousQuote: 'Sascha, beeil di! Der Turm wackelt im 135-BPM-Takt!',
themeColor: '#f43f5e'
},
maja: {
id: 'maja',
name: 'Schwester Maja',
title: 'Die Zweite Schwester',
portrait: '/assets/characters/maja/portrait.jpg',
description: 'Gefangen in den Wipfeln des Haumdaucher Himmelsgartens.',
famousQuote: 'Hol mi runter, Sascha, i hob an riesen Durst auf a Helles!',
themeColor: '#8b5cf6'
},
miriam: {
id: 'miriam',
name: 'Schwester Miriam',
title: 'Die Jüngste Schwester',
portrait: '/assets/characters/miriam/portrait.jpg',
description: 'Hockt auf den höchsten Himmelszinnen und wartet auf ihren großen Bruder.',
famousQuote: 'Bester Bruder! Alle drei Schwestern gerettet! Jetzt aber zügig zum Schankraum!',
themeColor: '#06b6d4'
},
djs: {
id: 'djs',
name: 'Die Hof-DJs',
@@ -145,3 +172,4 @@ export const CHARACTERS: Record<string, CharacterConfig> = {
themeColor: '#e11d48'
}
}
@@ -1,7 +1,7 @@
export interface DialogueChoice {
text: string
nextId?: string
action?: 'addKronkorken' | 'unlockZuluman' | 'unlockBridgerton' | 'startPlatformer' | 'startExpress' | 'startFinale'
action?: 'addKronkorken' | 'unlockZuluman' | 'unlockBridgerton' | 'startPlatformer' | 'startExpress' | 'startFinale' | 'resetPrologue'
reward?: number
reply?: {
speakerId: string
@@ -13,6 +13,7 @@ export interface DialogueNode {
id: string
speakerId: string
text: string
isGameOver?: boolean
choices: DialogueChoice[]
}
@@ -24,12 +25,12 @@ export const PROLOGUE_DIALOGUES: Record<string, DialogueNode> = {
choices: [
{
text: 'Zefix Josef! Ohne Kronkorken kein Bier, und ohne Bier kein Bass!',
nextId: 'mop_appears',
nextId: 'flo_maier_arrives',
action: 'unlockBridgerton'
},
{
text: 'Bridgerton?! Josef, konzentrier dich! Wo san meine 40 Kronkorken?!',
nextId: 'mop_appears'
nextId: 'flo_maier_arrives'
},
{
text: 'Lass mich raten... Du mit deinen Girls ab und zack, Leergut weg?',
@@ -45,18 +46,72 @@ export const PROLOGUE_DIALOGUES: Record<string, DialogueNode> = {
text: 'Ja mei, woher weißt du das immer?! Wir zogen durch die Lande, die Musik war laut, der Gerstensaft floss in Strömen... und dann hat der Wind die Korken über die Dächer geweht!',
choices: [
{
text: 'Na servus. Dann muss der König wohl selbst ran.',
text: 'Na servus. Mal schauen wer da noch auf dem Balkon lauert...',
nextId: 'flo_maier_arrives'
}
]
},
flo_maier_arrives: {
id: 'flo_maier_arrives',
speakerId: 'flo_maier',
text: 'Habe die Ehre Sascha! Zum 40er zünd i da den ganzen Schlosshof o! A riesigs Freudenfeuer aus Hopfen und Eichenbalken! Des drückt mehr als jeder Bass! Was moanst?!',
choices: [
{
text: 'Ja freilich, zünd ois o, Flo! Freudenfeuer marsch! 🔥',
nextId: 'flo_game_over'
},
{
text: 'Na, spinnst du Feierdeifl?! Mei Schloss bleibt stehn, heid wird friedlich geraved!',
nextId: 'viktor_arrives'
}
]
},
flo_game_over: {
id: 'flo_game_over',
speakerId: 'flo_maier',
text: '💥 WUUUUSCH! Alles steht lichterloh in Flammen! Das Schloss ist Asche, der DJ-Pult geschmolzen und das Bier verdampft. Niemand feiert auf verbrannter Erde!',
isGameOver: true,
choices: [
{
text: '🔄 Löschzug rufen & von vorn beginnen',
action: 'resetPrologue'
}
]
},
viktor_arrives: {
id: 'viktor_arrives',
speakerId: 'viktor',
text: 'Guten Abend, mein König... Wie wär\'s mit einem kleinen Biss in deinen königlichen Hals? Dann ravt die gesamte Sippschaft unsterblich als Partyvampire bis in alle Ewigkeit!',
choices: [
{
text: 'Ja geil, mach mi unsterblich, Viktor! *Hals hinhalten* 🧛',
nextId: 'viktor_game_over'
},
{
text: 'Na danke Viktor, heid gibt\'s nur kühles Helles statt Blut!',
nextId: 'mop_appears'
}
]
},
viktor_game_over: {
id: 'viktor_game_over',
speakerId: 'viktor',
text: '🧛 *CHOMP!* Sascha ist nun ein Partyvampir! Doch ach weh: Es ist strahlender Sonnenschein über Haumdaucher... Du zerfällst zu feinem Glitzerstaub auf den Schlosszinnen!',
isGameOver: true,
choices: [
{
text: '🔄 Knoblauch essen & von vorn beginnen',
action: 'resetPrologue'
}
]
},
mop_appears: {
id: 'mop_appears',
speakerId: 'mop',
text: 'Habe die Ehre, Sascha! Keine Panik, dein Erschaffer hat schon die Sprungfedern in deine königlichen Stiefel programmiert. Sammle mindestens 15 Kronkorken auf den Hopfen-Pfaden, bevor wir in die Taverne einziehen!',
text: 'Habe die Ehre, Sascha! Gut, dass das Schloss noch steht. Aber ein Notruf erreichte die Matrix: Deine drei Schwestern Saskia, Maja und Miriam wurden von Kronkorken-Kobolden hoch in die Wolken entführt! Du musst mit deinen Sprungstiefeln von Plattform zu Plattform klettern und alle drei befreien!',
choices: [
{
text: 'Auf geht\'s Mop! Starte den Kronkorken-Kreuzzug!',
text: 'Auf geht\'s Mop! Meine Schwestern warten – Kronkorken-Sprungfedern an!',
action: 'startPlatformer'
},
{
@@ -68,16 +123,41 @@ export const PROLOGUE_DIALOGUES: Record<string, DialogueNode> = {
sascha_ready: {
id: 'sascha_ready',
speakerId: 'sascha',
text: 'Sonnenbrille sitzt. Hermelinmantel weht im Wind. Haumdaucher, der König kommt!',
text: 'Sonnenbrille sitzt. Hermelinmantel weht im Wind. Saskia, Maja, Miriam – euer königlicher Bruder holt euch da runter!',
choices: [
{
text: 'Level 1 starten: Die Hopfen-Pfade!',
text: 'Akt II starten: Die Schwesternrettung!',
action: 'startPlatformer'
}
]
}
}
export const SISTER_RESCUE_DIALOGUES = {
saskia: {
id: 'rescue_saskia',
speakerId: 'saskia',
name: 'Schwester Saskia',
text: 'Sascha! Du hast mich gerettet! Die Kobolde wollten mich zwingen, Radler zu trinken! Hier, nimm diesen königlichen Kronkorken! Aber beeil dich: Maja ist noch weiter oben im Himmelsgarten gefangen!',
btnText: 'Weiter zu Level 2: Maja retten! ☁️'
},
maja: {
id: 'rescue_maja',
speakerId: 'maja',
name: 'Schwester Maja',
text: 'Königlicher Bruder! Endlich! I hab scho dacht, i verdurste hier oben in den Wolken! Nimm dieses kühle Helle und diesen Kronkorken zur Stärkung. Aber gib Gas: Miriam hockt ganz oben auf den höchsten Zinnen!',
btnText: 'Weiter zu Level 3: Miriam retten! 🏰'
},
miriam: {
id: 'rescue_miriam',
speakerId: 'miriam',
name: 'Schwester Miriam',
text: 'JAAAA! Sascha, bester Bruder aller Zeiten! Alle drei Schwestern sind gerettet und in Sicherheit! Jetzt aber nix wie runter in die Taverne zur königlichen Audienz!',
btnText: 'Zu Akt III: Der Schankraum! 🍻'
}
}
export const GSTANZL_DUEL = [
{
insult: 'Dein Techno-Bass ist so dünn wie schales Exportbier!',