feat(sasch40): add retro birthday game subproject and k8s deployment
- Add Vue 3 + Vite + TypeScript retro game: 'Saschas Kronkorken Kreuzzug: 40 Jahre Bass & Gerstensaft' - Add dynamic GameBoy layout: controls visible only during arcade modes, fullscreen during dialogue and tavern - Add QR code welcome & prequel screen with lore, feature preview, and audio hints - Add competitive party highscore and ranking breakdown on royal certificate - Add dedicated individual 1:1 character portraits for Sascha, Verena, NAT, Hansi, Mop, Lutz, Armend, Amy, Mane, Pü, and Mario - Add Dockerfile, Makefile, and K8s deployment at sasch40.haumdaucher.de - Update openclaw timeout configuration
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
export interface CharacterConfig {
|
||||
id: string
|
||||
name: string
|
||||
title: string
|
||||
portrait: string
|
||||
description: string
|
||||
famousQuote: string
|
||||
themeColor: string
|
||||
}
|
||||
|
||||
export const CHARACTERS: Record<string, CharacterConfig> = {
|
||||
sascha: {
|
||||
id: 'sascha',
|
||||
name: 'König Sascha I.',
|
||||
title: 'Der 40-jährige Imperator',
|
||||
portrait: '/assets/characters/sascha/portrait.jpg',
|
||||
description: 'Herrscher über Haumdaucher, Träger der heiligen Sonnenbrille und Beschützer des 135-BPM-Basses.',
|
||||
famousQuote: 'O zapft is im Reiche! Wo san meine Kronkorken?!',
|
||||
themeColor: '#d4af37'
|
||||
},
|
||||
mop: {
|
||||
id: 'mop',
|
||||
name: 'Mop (Moritz)',
|
||||
title: 'Der Welten-Erschaffer & Matrix-Architekt',
|
||||
portrait: '/assets/characters/mop/portrait.jpg',
|
||||
description: 'Sitzt auf einer Wolke über dem Schloss und baut aus Pixeln und Hopfen diese Welt.',
|
||||
famousQuote: 'Just adding a few more polygons here... wait, pixels. Pixels.',
|
||||
themeColor: '#38bdf8'
|
||||
},
|
||||
nat: {
|
||||
id: 'nat',
|
||||
name: 'NAT (Josef)',
|
||||
title: 'Hof-Chronist & Barde des Wildschweins',
|
||||
portrait: '/assets/characters/nat/portrait.jpg',
|
||||
description: 'Kennt alle 3 Staffeln Bridgerton in- und auswendig, trägt florale Bomberjacken und erzählt epische Geschichten.',
|
||||
famousQuote: 'Also pass auf Sascha... ich damals mit meinen Girls ab...',
|
||||
themeColor: '#22c55e'
|
||||
},
|
||||
verena: {
|
||||
id: 'verena',
|
||||
name: 'Königin Verena',
|
||||
title: 'Herrin des Hofes',
|
||||
portrait: '/assets/characters/verena/portrait.jpg',
|
||||
description: 'Liebt Sascha über alles auf der Welt, hat aber ein unumstößliches königliches Edikt erlassen.',
|
||||
famousQuote: 'Ich hab dich lieb mein Schatz, aber ich putze nach der Party fei ned!',
|
||||
themeColor: '#ec4899'
|
||||
},
|
||||
hansi: {
|
||||
id: 'hansi',
|
||||
name: 'Hansi (Johann)',
|
||||
title: 'Ritter der floralen Garde',
|
||||
portrait: '/assets/characters/hansi/portrait.jpg',
|
||||
description: 'Perfekt gestylter Schnurrbart und florale Plattenrüstung.',
|
||||
famousQuote: 'Habe die Ehre! Der Bass im Rittersaal ist auf Punkt.',
|
||||
themeColor: '#6366f1'
|
||||
},
|
||||
lutz: {
|
||||
id: 'lutz',
|
||||
name: 'Lutz',
|
||||
title: 'Der ZTUL-Hofschornsteinfeger',
|
||||
portrait: '/assets/characters/lutz/portrait.jpg',
|
||||
description: 'Ein Hüne von Gestalt mit Kappe, runder Brille und dem legendären ZTUL-Trikot.',
|
||||
famousQuote: 'Glück auf, Euer Durchlaucht! Schornstein ist frei für den Party-Nebel.',
|
||||
themeColor: '#64748b'
|
||||
},
|
||||
armend: {
|
||||
id: 'armend',
|
||||
name: 'Armend',
|
||||
title: 'Meister-Schmied der Kronkorken-Manufaktur',
|
||||
portrait: '/assets/characters/armend/portrait.jpg',
|
||||
description: 'Prachtvoller Vollbart, formt aus 40 Kronkorken den goldenen Sound-Verstärker.',
|
||||
famousQuote: 'Schlag zu, solange das Blech warm ist!',
|
||||
themeColor: '#f97316'
|
||||
},
|
||||
amy: {
|
||||
id: 'amy',
|
||||
name: 'Amy (Amelie)',
|
||||
title: 'Die Hof-Künstlerin',
|
||||
portrait: '/assets/characters/amy/portrait.jpg',
|
||||
description: 'Kreatives Genie in Latzhose mit Dutt und feinen Kräuter-Zigarren.',
|
||||
famousQuote: 'Rave the Kingdom! Die Farben brauchen mehr Bass!',
|
||||
themeColor: '#a855f7'
|
||||
},
|
||||
pue: {
|
||||
id: 'pue',
|
||||
name: 'Pü (Jakob)',
|
||||
title: 'Der Lichtler des Hofes',
|
||||
portrait: '/assets/characters/pue/portrait.jpg',
|
||||
description: 'Früherer Club-Lichtmeister, bedient nun die magischen Stroboskop-Fackeln.',
|
||||
famousQuote: 'Majestät, wann zünden wir die Stroboskop-Fackeln zum Drop?',
|
||||
themeColor: '#eab308'
|
||||
},
|
||||
mario: {
|
||||
id: 'mario',
|
||||
name: 'Mario der Gaukler',
|
||||
title: 'Hof-Jongleur & Stimmungs-Garant',
|
||||
portrait: '/assets/characters/mario/portrait.jpg',
|
||||
description: 'Jongliert geschickt mit Hopfen, Hefe und Bierkrügen.',
|
||||
famousQuote: 'Hoppla die Waldfee! Ein Krug für den König!',
|
||||
themeColor: '#14b8a6'
|
||||
},
|
||||
mane: {
|
||||
id: 'mane',
|
||||
name: 'Mane (Ramona)',
|
||||
title: 'Die königliche Wanderin',
|
||||
portrait: '/assets/characters/mane/portrait.jpg',
|
||||
description: 'Wandert random mit Rucksack und Wanderstock quer durch alle Bildschirme.',
|
||||
famousQuote: 'Schee ists hier! Noch 3 Kilometer bis zum Gipfel-Bier!',
|
||||
themeColor: '#84cc16'
|
||||
},
|
||||
djs: {
|
||||
id: 'djs',
|
||||
name: 'Die Hof-DJs',
|
||||
title: 'Minnesänger der 135 BPM',
|
||||
portrait: '/assets/characters/pue/portrait.jpg',
|
||||
description: 'Stehen an der königlichen Kanzel am doppelten Eichenholz-Plattenteller.',
|
||||
famousQuote: 'Drop it like it\'s 1426! Mehr Bass auf die Burgzinnen!',
|
||||
themeColor: '#06b6d4'
|
||||
},
|
||||
gefolge: {
|
||||
id: 'gefolge',
|
||||
name: 'Das Haumdaucher Gefolge',
|
||||
title: 'Die feiernde Reichs-Crowd',
|
||||
portrait: '/assets/characters/hansi/portrait.jpg',
|
||||
description: 'Viktor der Vampir, der bärtige Guatemalteke, Fabian, Albin, Maier Flo, Markus & Nathalie.',
|
||||
famousQuote: 'Ein Hoch auf König Sascha! Hoch soll er leben!',
|
||||
themeColor: '#e11d48'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
export interface DialogueChoice {
|
||||
text: string
|
||||
nextId?: string
|
||||
action?: 'addKronkorken' | 'unlockZuluman' | 'unlockBridgerton' | 'startPlatformer' | 'startExpress' | 'startFinale'
|
||||
reward?: number
|
||||
reply?: {
|
||||
speakerId: string
|
||||
text: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface DialogueNode {
|
||||
id: string
|
||||
speakerId: string
|
||||
text: string
|
||||
choices: DialogueChoice[]
|
||||
}
|
||||
|
||||
export const PROLOGUE_DIALOGUES: Record<string, DialogueNode> = {
|
||||
start: {
|
||||
id: 'start',
|
||||
speakerId: 'nat',
|
||||
text: 'Seyd gegrüßet, Sascha! 40 Lenze zählet dein edles Antlitz! Aber ich sag\'s dir wie\'s is... der Hofstaat ist in heller Aufruhr. Ein Drama wie damals beim königlichen Ball in Staffel 2 von Bridgerton! Sämtliche 40 königlichen Kronkorken san über ganz Haumdaucher verstreut!',
|
||||
choices: [
|
||||
{
|
||||
text: 'Zefix Josef! Ohne Kronkorken kein Bier, und ohne Bier kein Bass!',
|
||||
nextId: 'mop_appears',
|
||||
action: 'unlockBridgerton'
|
||||
},
|
||||
{
|
||||
text: 'Bridgerton?! Josef, konzentrier dich! Wo san meine 40 Kronkorken?!',
|
||||
nextId: 'mop_appears'
|
||||
},
|
||||
{
|
||||
text: 'Lass mich raten... Du mit deinen Girls ab und zack, Leergut weg?',
|
||||
nextId: 'nat_girls',
|
||||
action: 'addKronkorken',
|
||||
reward: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
nat_girls: {
|
||||
id: 'nat_girls',
|
||||
speakerId: 'nat',
|
||||
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.',
|
||||
nextId: 'mop_appears'
|
||||
}
|
||||
]
|
||||
},
|
||||
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!',
|
||||
choices: [
|
||||
{
|
||||
text: 'Auf geht\'s Mop! Starte den Kronkorken-Kreuzzug!',
|
||||
action: 'startPlatformer'
|
||||
},
|
||||
{
|
||||
text: 'Zeig mir erst noch meine Sonnenbrille für den Style.',
|
||||
nextId: 'sascha_ready'
|
||||
}
|
||||
]
|
||||
},
|
||||
sascha_ready: {
|
||||
id: 'sascha_ready',
|
||||
speakerId: 'sascha',
|
||||
text: 'Sonnenbrille sitzt. Hermelinmantel weht im Wind. Haumdaucher, der König kommt!',
|
||||
choices: [
|
||||
{
|
||||
text: 'Level 1 starten: Die Hopfen-Pfade!',
|
||||
action: 'startPlatformer'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const GSTANZL_DUEL = [
|
||||
{
|
||||
insult: 'Dein Techno-Bass ist so dünn wie schales Exportbier!',
|
||||
answers: [
|
||||
{ text: 'Dafür schallert mein Drop lauter als dei ganze Ahnengalerie!', correct: true },
|
||||
{ text: 'Ich trinke eigentlich lieber Weißwein.', correct: false },
|
||||
{ text: 'Aber meine Sonnenbrille ist dafür sehr dunkel!', correct: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
insult: 'Mit 40 Lenzen bist du doch reif fürs Altenteil, alter Mann!',
|
||||
answers: [
|
||||
{ text: 'Mit 40 fängt der Rave erst an, und i tanz di locker an die Wand!', correct: true },
|
||||
{ text: 'Ja mei, der Rücken zwickt schon a bisserl.', correct: false },
|
||||
{ text: 'Ich frage mal schnell meinen Steuerberater.', correct: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
insult: 'Niemand im ganzen Herzogtum feiert deine 135 BPM!',
|
||||
answers: [
|
||||
{ text: 'Ganz Haumdaucher bebt, wenn der König den Fader bewegt!', correct: true },
|
||||
{ text: 'Vielleicht sollte ich lieber Schlager spielen.', correct: false },
|
||||
{ text: 'Pü hat aber gesagt, das Licht passt dazu.', correct: false }
|
||||
]
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user