Added Glavni Smetar (post-apocalyptic punk janitor), Character Design Guidelines (piercings/dreads/tattoos variety), Smetar dialogue system with Zombie Scout interaction, portrait animations, sound layers, typewriter sync. Updated FAZA1 status to 47%. Includes ZombieEconomySystem with Worker Zombies, Sanitation, Contracts, Rare Gifts, Brain feeding.
This commit is contained in:
338
src/dialogues/SmetarDialogues.js
Normal file
338
src/dialogues/SmetarDialogues.js
Normal file
@@ -0,0 +1,338 @@
|
||||
/**
|
||||
* SMETAR DIALOGUE SYSTEM
|
||||
* Interakcija z Zombijem Skavtom
|
||||
*/
|
||||
|
||||
export const SmetarDialogues = {
|
||||
// Glavni intro dialog ko Kai pride s Zombijem
|
||||
intro_with_zombie: {
|
||||
trigger: 'player_near_smetar_with_zombie_scout',
|
||||
participants: ['smetar', 'zombie_scout'],
|
||||
lines: [
|
||||
{
|
||||
speaker: 'smetar',
|
||||
text: "Ej, Kai! Kaj mi spet furaš tega svojega s klobukom? Glej ga, nahrbtnik ima čisto svinjski od tistih vaših ruševin. Šutni ga sem, da mi malo pomaga s temi grafiti na obzidju!",
|
||||
emotion: 'gruff',
|
||||
animation: 'portrait_bounce',
|
||||
soundLayers: ['broom_scraping', 'wind_ambient'],
|
||||
typewriterSpeed: 40 // ms per character
|
||||
},
|
||||
{
|
||||
speaker: 'zombie_scout',
|
||||
text: "*Tiho godrnjanje, rožljanje nahrbtnika* M-m-možgaaaaani... pa metla?",
|
||||
emotion: 'confused_hungry',
|
||||
animation: 'portrait_bounce_slow',
|
||||
soundLayers: ['zombie_grumble', 'backpack_rattle', 'wind_ambient'],
|
||||
typewriterSpeed: 50
|
||||
},
|
||||
{
|
||||
speaker: 'smetar',
|
||||
text: "Ja, metla! Če boš dobro pometal, ti mogoče dam kakšen star košček spomina, ki sem ga izbrskal iz smeti za tvojega šefa.",
|
||||
emotion: 'encouraging',
|
||||
animation: 'portrait_bounce',
|
||||
soundLayers: ['broom_scraping', 'wind_ambient'],
|
||||
typewriterSpeed: 40
|
||||
}
|
||||
],
|
||||
outcomes: {
|
||||
accept: 'quest_sanitation_zombie_help',
|
||||
decline: 'smetar_disappointed'
|
||||
}
|
||||
},
|
||||
|
||||
// Quest assignment
|
||||
quest_assignment: {
|
||||
speaker: 'smetar',
|
||||
text: "Pofafaj tole metlo pa mi pomagaj, al pa mi šutni tiste tri zombije, da naredimo mal reda v tej luknji!",
|
||||
emotion: 'determined',
|
||||
animation: 'portrait_bounce',
|
||||
soundLayers: ['broom_tap', 'wind_ambient'],
|
||||
typewriterSpeed: 40
|
||||
},
|
||||
|
||||
// Ko Kai posodi zombije
|
||||
zombie_loan_accepted: {
|
||||
speaker: 'smetar',
|
||||
text: "Čist fajn! Dej, ti trije, pole sem! *postavi zombije v vrsto* Vi boste čistili ta grafit, vi tisto smejo, pa vi tam pa tisti razbit zabojnik!",
|
||||
emotion: 'commanding',
|
||||
animation: 'portrait_bounce_strong',
|
||||
soundLayers: ['zombie_lineup', 'broom_distribution'],
|
||||
typewriterSpeed: 45,
|
||||
vfx: 'zombie_worker_spawn'
|
||||
},
|
||||
|
||||
// Ko je delo končano
|
||||
work_complete: {
|
||||
speaker: 'smetar',
|
||||
text: "Heh! Dobro delo, fantje! Kai, tvojim zombijem bi lahko dal za jest, pa tukaj je tisto za tebe...",
|
||||
emotion: 'satisfied',
|
||||
animation: 'portrait_bounce',
|
||||
soundLayers: ['success_jingle', 'wind_ambient'],
|
||||
typewriterSpeed: 40,
|
||||
reward: {
|
||||
type: 'rare_gift',
|
||||
item: 'family_memory_fragment',
|
||||
vfx: 'amnesia_blur_trigger'
|
||||
}
|
||||
},
|
||||
|
||||
// Ko najde redko darilo v smeteh
|
||||
rare_gift_found: {
|
||||
speaker: 'smetar',
|
||||
text: "Ej Kai, poglej kaj sem najdu v teh ruševinah - stara družinska slika. Tvoja je, ne? Zgleda da... Kai? Kai! Hej, kam greš?!",
|
||||
emotion: 'surprised',
|
||||
animation: 'portrait_bounce',
|
||||
soundLayers: ['paper_rustle', 'wind_ambient'],
|
||||
typewriterSpeed: 35,
|
||||
trigger_after: 'amnesia_blur_flashback'
|
||||
},
|
||||
|
||||
// Ko mesto ni čisto
|
||||
city_dirty_nag: {
|
||||
speaker: 'smetar',
|
||||
text: "Kai, no! Spet je mesto polno smeti! Kje so tvoji zombi? Rabim pomoč!",
|
||||
emotion: 'frustrated',
|
||||
animation: 'portrait_shake',
|
||||
soundLayers: ['broom_angry_tap'],
|
||||
typewriterSpeed: 40
|
||||
},
|
||||
|
||||
// Ko je mesto 100% čisto
|
||||
city_perfect: {
|
||||
speaker: 'smetar',
|
||||
text: "KONČNO! Prvo mesto v tem post-apokaliptičnem sranju, ki je res čisto! Bravo, Kai. Evo, to si res zaslužil.",
|
||||
emotion: 'proud',
|
||||
animation: 'portrait_bounce_celebration',
|
||||
soundLayers: ['fanfare', 'wind_ambient'],
|
||||
typewriterSpeed: 40,
|
||||
reward: {
|
||||
type: 'legendary_gift',
|
||||
item: 'kai_childhood_photo',
|
||||
vfx: 'amnesia_blur_major'
|
||||
}
|
||||
},
|
||||
|
||||
// Casual banter
|
||||
casual_greeting: [
|
||||
{
|
||||
text: "Ej, pometal sem že 200 ruševin. Tvoj zombi? Kaj, 5?",
|
||||
emotion: 'teasing',
|
||||
animation: 'portrait_bounce'
|
||||
},
|
||||
{
|
||||
text: "Vejpam že od preden so zombiji začeli hodit. Ta dim jih odbija, verjameš?",
|
||||
emotion: 'casual',
|
||||
animation: 'portrait_bounce'
|
||||
},
|
||||
{
|
||||
text: "Ko sem bil mlajši, sem bil bolj punk od tebe. Dreadi do tal!",
|
||||
emotion: 'nostalgic',
|
||||
animation: 'portrait_bounce'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* DIALOGUE ANIMATION CONTROLLER
|
||||
*/
|
||||
export class SmetarDialogueController {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.cinematicVoice = scene.cinematicVoice; // CinematicVoiceSystem
|
||||
this.currentDialogue = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play dialogue sequence with animations
|
||||
*/
|
||||
async playDialogue(dialogueKey) {
|
||||
const dialogue = SmetarDialogues[dialogueKey];
|
||||
if (!dialogue) return;
|
||||
|
||||
this.currentDialogue = dialogue;
|
||||
|
||||
// Single line dialogue
|
||||
if (dialogue.speaker) {
|
||||
await this.playLine(dialogue);
|
||||
}
|
||||
// Multi-line sequence
|
||||
else if (dialogue.lines) {
|
||||
for (const line of dialogue.lines) {
|
||||
await this.playLine(line);
|
||||
await this.wait(500); // Brief pause between lines
|
||||
}
|
||||
}
|
||||
|
||||
// Handle outcomes/rewards
|
||||
if (dialogue.reward) {
|
||||
this.awardReward(dialogue.reward);
|
||||
}
|
||||
|
||||
if (dialogue.trigger_after) {
|
||||
this.scene.events.emit(dialogue.trigger_after);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play single dialogue line with full effects
|
||||
*/
|
||||
async playLine(line) {
|
||||
const speaker = line.speaker;
|
||||
|
||||
// Show portrait with bounce animation
|
||||
this.showPortrait(speaker, line.animation);
|
||||
|
||||
// Start sound layers
|
||||
this.startSoundLayers(line.soundLayers);
|
||||
|
||||
// Create dialogue box
|
||||
const dialogueBox = this.createDialogueBox(speaker, line.text);
|
||||
|
||||
// Typewriter effect with voice sync
|
||||
await this.typewriterEffect(dialogueBox, line.text, line.typewriterSpeed);
|
||||
|
||||
// Voice synthesis
|
||||
if (this.cinematicVoice) {
|
||||
await this.cinematicVoice.speak(
|
||||
line.text,
|
||||
speaker,
|
||||
line.emotion,
|
||||
{
|
||||
typewriterElement: dialogueBox.textElement,
|
||||
ambient: line.soundLayers.includes('wind_ambient') ? 'wind_ruins' : null
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// VFX if specified
|
||||
if (line.vfx) {
|
||||
this.scene.events.emit('vfx:trigger', line.vfx);
|
||||
}
|
||||
|
||||
// Wait for player to dismiss
|
||||
await this.waitForDismiss();
|
||||
|
||||
// Hide dialogue
|
||||
this.hideDialogue(dialogueBox);
|
||||
this.hidePortrait(speaker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Portrait bounce animation (Style 32)
|
||||
*/
|
||||
showPortrait(speaker, animationType) {
|
||||
const portrait = this.scene.add.sprite(150, 500, `portrait_${speaker}`);
|
||||
portrait.setDepth(100);
|
||||
portrait.setAlpha(0);
|
||||
|
||||
// Fade in
|
||||
this.scene.tweens.add({
|
||||
targets: portrait,
|
||||
alpha: 1,
|
||||
duration: 300,
|
||||
ease: 'Sine.easeIn'
|
||||
});
|
||||
|
||||
// Bounce animation
|
||||
if (animationType === 'portrait_bounce') {
|
||||
this.scene.tweens.add({
|
||||
targets: portrait,
|
||||
y: '+=10',
|
||||
duration: 600,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
} else if (animationType === 'portrait_bounce_slow') {
|
||||
this.scene.tweens.add({
|
||||
targets: portrait,
|
||||
y: '+=15',
|
||||
duration: 1000,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
}
|
||||
|
||||
this.currentPortrait = portrait;
|
||||
}
|
||||
|
||||
hidePortrait(speaker) {
|
||||
if (this.currentPortrait) {
|
||||
this.scene.tweens.add({
|
||||
targets: this.currentPortrait,
|
||||
alpha: 0,
|
||||
duration: 300,
|
||||
onComplete: () => this.currentPortrait.destroy()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start ambient sound layers
|
||||
*/
|
||||
startSoundLayers(layers) {
|
||||
if (!layers) return;
|
||||
|
||||
layers.forEach(soundKey => {
|
||||
if (this.scene.sound && this.scene.sound.get(soundKey)) {
|
||||
this.scene.sound.play(soundKey, { volume: 0.3, loop: soundKey.includes('ambient') });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Typewriter effect
|
||||
*/
|
||||
async typewriterEffect(dialogueBox, text, speed) {
|
||||
const textElement = dialogueBox.textElement;
|
||||
textElement.setText('');
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
textElement.setText(text.substring(0, i + 1));
|
||||
await this.wait(speed);
|
||||
}
|
||||
}
|
||||
|
||||
createDialogueBox(speaker, text) {
|
||||
// Placeholder - real implementation would create Phaser graphics
|
||||
return {
|
||||
textElement: this.scene.add.text(200, 550, '', {
|
||||
fontSize: '18px',
|
||||
color: '#FFFFFF',
|
||||
wordWrap: { width: 600 }
|
||||
}).setDepth(101)
|
||||
};
|
||||
}
|
||||
|
||||
hideDialogue(dialogueBox) {
|
||||
if (dialogueBox && dialogueBox.textElement) {
|
||||
dialogueBox.textElement.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
awardReward(reward) {
|
||||
if (reward.type === 'rare_gift' || reward.type === 'legendary_gift') {
|
||||
this.scene.zombieEconomy.awardRareGift(reward.item);
|
||||
}
|
||||
|
||||
if (reward.vfx) {
|
||||
this.scene.events.emit('vfx:trigger', reward.vfx);
|
||||
}
|
||||
}
|
||||
|
||||
wait(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async waitForDismiss() {
|
||||
// Wait for spacebar or click
|
||||
return new Promise(resolve => {
|
||||
const handler = () => {
|
||||
this.scene.input.keyboard.off('keydown-SPACE', handler);
|
||||
resolve();
|
||||
};
|
||||
this.scene.input.keyboard.on('keydown-SPACE', handler);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user