Implemented 3 advanced game systems: CinematicVoiceSystem (emotional depth, reverb, typewriter sync, Zombie Scout audio), DynamicEnvironmentAudio (material doors, adaptive rain, puddles, footsteps), ElectionSystem (chaos phase, voting, inauguration, city improvements). All systems integrated with ADHD-friendly features and smooth transitions.
This commit is contained in:
435
src/systems/ElectionSystem.js
Normal file
435
src/systems/ElectionSystem.js
Normal file
@@ -0,0 +1,435 @@
|
||||
/**
|
||||
* ELECTION & SOCIAL ORDER SYSTEM
|
||||
* Mrtva Dolina - City Evolution Through Democracy
|
||||
*
|
||||
* Features:
|
||||
* - Chaos phase (no leader, messy city)
|
||||
* - Election trigger (after 5+ NPCs arrive)
|
||||
* - Vote gathering & influence system
|
||||
* - Mayor inauguration with visual/audio changes
|
||||
* - Unlocks city improvements (walls, patrols)
|
||||
*/
|
||||
|
||||
export class ElectionSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
|
||||
// Election state
|
||||
this.electionPhase = 'none'; // none, chaos, campaign, complete
|
||||
this.mayorElected = false;
|
||||
this.currentMayor = null;
|
||||
|
||||
// Candidates
|
||||
this.candidates = [
|
||||
{
|
||||
id: 'mayor_default',
|
||||
name: 'Župan',
|
||||
votes: 0,
|
||||
platform: 'Obzidje in varnost',
|
||||
supportingNPCs: []
|
||||
},
|
||||
{
|
||||
id: 'ivan_kovac',
|
||||
name: 'Ivan Kovač',
|
||||
votes: 0,
|
||||
platform: 'Proizvodni razvoj',
|
||||
supportingNPCs: []
|
||||
},
|
||||
{
|
||||
id: 'tehnik',
|
||||
name: 'Tehnik',
|
||||
votes: 0,
|
||||
platform: 'Tehnološki napredek',
|
||||
supportingNPCs: []
|
||||
}
|
||||
];
|
||||
|
||||
// City visual state
|
||||
this.cityState = {
|
||||
cleanliness: 0, // 0-100
|
||||
security: 0, // 0-100
|
||||
morale: 0 // 0-100
|
||||
};
|
||||
|
||||
// Trash/debris objects for visual chaos
|
||||
this.debrisObjects = [];
|
||||
|
||||
// Population tracking
|
||||
this.npcCount = 0;
|
||||
this.electionThreshold = 5; // Trigger election at 5 NPCs
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Listen for NPC arrival events
|
||||
this.scene.events.on('npc:arrived', this.onNPCArrival, this);
|
||||
this.scene.events.on('quest:completed', this.onQuestCompleted, this);
|
||||
|
||||
console.log('✅ ElectionSystem initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* NPC ARRIVAL - Track population
|
||||
*/
|
||||
onNPCArrival(npcData) {
|
||||
this.npcCount++;
|
||||
|
||||
console.log(`👤 NPC arrived: ${npcData.name}. Total: ${this.npcCount}`);
|
||||
|
||||
// Check if chaos phase should start
|
||||
if (this.npcCount >= 3 && this.electionPhase === 'none') {
|
||||
this.startChaosPhase();
|
||||
}
|
||||
|
||||
// Check if election should trigger
|
||||
if (this.npcCount >= this.electionThreshold && this.electionPhase === 'chaos') {
|
||||
this.triggerElection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CHAOS PHASE - City is disorganized
|
||||
*/
|
||||
startChaosPhase() {
|
||||
this.electionPhase = 'chaos';
|
||||
|
||||
console.log('💥 CHAOS PHASE STARTED - City needs leadership!');
|
||||
|
||||
// Spawn trash/debris around town
|
||||
this.spawnDebris(15); // 15 trash piles
|
||||
|
||||
// Lower city stats
|
||||
this.cityState.cleanliness = 20;
|
||||
this.cityState.security = 10;
|
||||
this.cityState.morale = 30;
|
||||
|
||||
// NPCs start discussing need for leader
|
||||
this.startChaosDialogues();
|
||||
|
||||
// Show notification
|
||||
this.scene.events.emit('show-notification', {
|
||||
title: 'Stanje Kaosa',
|
||||
message: 'Ljudje potrebujejo vodjo! Uredite red v mestu.',
|
||||
icon: '⚠️',
|
||||
duration: 5000
|
||||
});
|
||||
|
||||
// Update status board
|
||||
this.updateStatusBoard();
|
||||
}
|
||||
|
||||
spawnDebris(count) {
|
||||
const debrisTypes = ['trash_pile', 'broken_crate', 'rubble', 'scattered_papers'];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const x = Phaser.Math.Between(100, this.scene.cameras.main.width - 100);
|
||||
const y = Phaser.Math.Between(100, this.scene.cameras.main.height - 100);
|
||||
|
||||
const type = Phaser.Utils.Array.GetRandom(debrisTypes);
|
||||
const debris = this.scene.add.sprite(x, y, type);
|
||||
debris.setDepth(1);
|
||||
|
||||
this.debrisObjects.push(debris);
|
||||
}
|
||||
}
|
||||
|
||||
startChaosDialogues() {
|
||||
// NPCs randomly discuss the chaos
|
||||
const dialogues = [
|
||||
{ npc: 'sivilja', text: 'Ta kaos je neznosn! Rabimo vodjo!' },
|
||||
{ npc: 'pek', text: 'Kdo bo prinesel red v to mesto?' },
|
||||
{ npc: 'ivan_kovac', text: 'Brez organizacije ne moremo preživeti.' }
|
||||
];
|
||||
|
||||
// Emit dialogue events periodically
|
||||
this.chaosDialogueTimer = setInterval(() => {
|
||||
const dialogue = Phaser.Utils.Array.GetRandom(dialogues);
|
||||
this.scene.events.emit('npc:dialogue', dialogue);
|
||||
}, 30000); // Every 30 seconds
|
||||
}
|
||||
|
||||
/**
|
||||
* TRIGGER ELECTION
|
||||
*/
|
||||
triggerElection() {
|
||||
this.electionPhase = 'campaign';
|
||||
|
||||
console.log('🗳️ ELECTION TRIGGERED - Campaign begins!');
|
||||
|
||||
// Stop chaos dialogues
|
||||
if (this.chaosDialogueTimer) {
|
||||
clearInterval(this.chaosDialogueTimer);
|
||||
}
|
||||
|
||||
// Create election quest
|
||||
this.createElectionQuest();
|
||||
|
||||
// Show notification
|
||||
this.scene.events.emit('show-notification', {
|
||||
title: 'Volitve za Župana',
|
||||
message: 'Mesto potrebuje vodjo! Pomagaj pri zbiranju glasov.',
|
||||
icon: '🗳️',
|
||||
duration: 5000
|
||||
});
|
||||
|
||||
// NPCs start campaign dialogues
|
||||
this.startCampaignDialogues();
|
||||
}
|
||||
|
||||
createElectionQuest() {
|
||||
if (!this.scene.questSystem) return;
|
||||
|
||||
const electionQuest = {
|
||||
id: 'election_campaign',
|
||||
title: 'Zbiranje Glasov za Župana',
|
||||
type: 'social',
|
||||
priority: 5,
|
||||
description: 'Pomagaj izbrati župana za Mrtvo Dolino.',
|
||||
objectives: [
|
||||
{
|
||||
id: 'talk_to_npcs',
|
||||
text: 'Pogovor s 5 NPC-ji o volitvah',
|
||||
type: 'interaction',
|
||||
required: 5,
|
||||
current: 0
|
||||
},
|
||||
{
|
||||
id: 'support_candidate',
|
||||
text: 'Podpri kandidata z opravljanjem questov',
|
||||
type: 'flag',
|
||||
complete: false
|
||||
}
|
||||
],
|
||||
rewards: {
|
||||
xp: 1000,
|
||||
unlocks: ['mayor_office', 'city_improvements']
|
||||
},
|
||||
dialogue: {
|
||||
start: ['Ljudi potrebujejo vodjo. Kdo bo župan?'],
|
||||
complete: ['Volitve so končane! Novi župan je izvoljen!']
|
||||
},
|
||||
npc: 'mayor'
|
||||
};
|
||||
|
||||
this.scene.questSystem.registerQuest(electionQuest);
|
||||
this.scene.questSystem.startQuest('election_campaign');
|
||||
}
|
||||
|
||||
startCampaignDialogues() {
|
||||
// Each candidate promotes their platform
|
||||
const campaignLines = {
|
||||
mayor_default: 'Glasujte zame! Zgradil bom obzidje in patruljo!',
|
||||
ivan_kovac: 'Potrebujemo proizvodnjo! Podprite me!',
|
||||
tehnik: 'Tehnologija je prihodnost! Volite tehnološki napredek!'
|
||||
};
|
||||
|
||||
// NPCs express support for different candidates
|
||||
this.campaignDialogueTimer = setInterval(() => {
|
||||
const candidate = Phaser.Utils.Array.GetRandom(this.candidates);
|
||||
this.scene.events.emit('npc:dialogue', {
|
||||
npc: candidate.id,
|
||||
text: campaignLines[candidate.id]
|
||||
});
|
||||
}, 45000); // Every 45 seconds
|
||||
}
|
||||
|
||||
/**
|
||||
* VOTING - Player influences votes through quests
|
||||
*/
|
||||
onQuestCompleted(questId) {
|
||||
if (this.electionPhase !== 'campaign') return;
|
||||
|
||||
// Check which candidate benefits from this quest
|
||||
const questCandidateMap = {
|
||||
'obzidje': 'mayor_default',
|
||||
'pekov_recept': 'mayor_default',
|
||||
'tehnikova_naprava': 'tehnik',
|
||||
'siviljina_prosnja': 'ivan_kovac'
|
||||
};
|
||||
|
||||
const candidateId = questCandidateMap[questId];
|
||||
if (candidateId) {
|
||||
this.addVote(candidateId, 1);
|
||||
|
||||
// Show feedback
|
||||
this.scene.events.emit('show-floating-text', {
|
||||
x: this.scene.player.x,
|
||||
y: this.scene.player.y - 50,
|
||||
text: `+1 glas za ${candidateId}`,
|
||||
color: '#FFD700'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addVote(candidateId, votes = 1) {
|
||||
const candidate = this.candidates.find(c => c.id === candidateId);
|
||||
if (candidate) {
|
||||
candidate.votes += votes;
|
||||
console.log(`🗳️ ${candidate.name} dobil ${votes} glas(ov). Skupaj: ${candidate.votes}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* COMPLETE ELECTION - Inaugurate mayor
|
||||
*/
|
||||
completeElection() {
|
||||
if (this.mayorElected) return;
|
||||
|
||||
// Count votes and determine winner
|
||||
const winner = this.candidates.reduce((prev, current) =>
|
||||
(prev.votes > current.votes) ? prev : current
|
||||
);
|
||||
|
||||
this.currentMayor = winner;
|
||||
this.mayorElected = true;
|
||||
this.electionPhase = 'complete';
|
||||
|
||||
console.log(`🏛️ ${winner.name} je izvoljen za župana!`);
|
||||
|
||||
// Inauguration sequence
|
||||
this.inauguration(winner);
|
||||
}
|
||||
|
||||
inauguration(mayor) {
|
||||
// Visual changes
|
||||
this.cleanUpCity();
|
||||
|
||||
// Mayor moves to town hall
|
||||
if (this.scene.npcs && this.scene.npcs[mayor.id]) {
|
||||
const mayorNPC = this.scene.npcs[mayor.id];
|
||||
this.scene.tweens.add({
|
||||
targets: mayorNPC,
|
||||
x: this.scene.townHallX || 400,
|
||||
y: this.scene.townHallY || 300,
|
||||
duration: 3000,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
}
|
||||
|
||||
// Change music to ordered/military theme
|
||||
if (this.scene.sound && this.scene.sound.get('background_music')) {
|
||||
this.scene.sound.get('background_music').stop();
|
||||
}
|
||||
this.scene.sound.play('mayor_anthem', { loop: true, volume: 0.5 });
|
||||
|
||||
// Unlock new features
|
||||
this.unlockMayorFeatures();
|
||||
|
||||
// Show inauguration cutscene
|
||||
this.scene.events.emit('show-notification', {
|
||||
title: `Župan ${mayor.name}`,
|
||||
message: `${mayor.name} je uradno inauguriran! Mesto je zdaj pod vodstvom.`,
|
||||
icon: '🏛️',
|
||||
duration: 7000
|
||||
});
|
||||
|
||||
// Update city stats
|
||||
this.cityState.cleanliness = 80;
|
||||
this.cityState.security = 70;
|
||||
this.cityState.morale = 90;
|
||||
|
||||
this.updateStatusBoard();
|
||||
|
||||
// Complete election quest
|
||||
if (this.scene.questSystem) {
|
||||
this.scene.questSystem.completeQuest('election_campaign');
|
||||
}
|
||||
}
|
||||
|
||||
cleanUpCity() {
|
||||
// Remove all debris with animation
|
||||
this.debrisObjects.forEach((debris, index) => {
|
||||
this.scene.tweens.add({
|
||||
targets: debris,
|
||||
alpha: 0,
|
||||
scaleX: 0,
|
||||
scaleY: 0,
|
||||
duration: 1000,
|
||||
delay: index * 100,
|
||||
onComplete: () => debris.destroy()
|
||||
});
|
||||
});
|
||||
|
||||
this.debrisObjects = [];
|
||||
|
||||
// Add clean visual elements (flags, guards, etc.)
|
||||
this.addCityImprovements();
|
||||
}
|
||||
|
||||
addCityImprovements() {
|
||||
// Add flags
|
||||
const flagPositions = [
|
||||
{ x: 200, y: 150 },
|
||||
{ x: 400, y: 150 },
|
||||
{ x: 600, y: 150 }
|
||||
];
|
||||
|
||||
flagPositions.forEach(pos => {
|
||||
const flag = this.scene.add.sprite(pos.x, pos.y, 'city_flag');
|
||||
flag.setDepth(10);
|
||||
|
||||
// Waving animation
|
||||
this.scene.tweens.add({
|
||||
targets: flag,
|
||||
scaleX: 1.1,
|
||||
duration: 1000,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut'
|
||||
});
|
||||
});
|
||||
|
||||
// Add guards (if available)
|
||||
// ... patrol implementation
|
||||
}
|
||||
|
||||
unlockMayorFeatures() {
|
||||
// Unlock wall building
|
||||
if (this.scene.buildingSystem) {
|
||||
this.scene.buildingSystem.unlock('wall_wooden');
|
||||
this.scene.buildingSystem.unlock('wall_stone');
|
||||
}
|
||||
|
||||
// Unlock patrol system
|
||||
if (this.scene.defenseSystem) {
|
||||
this.scene.defenseSystem.unlockPatrols();
|
||||
}
|
||||
|
||||
// Unlock mayor's office
|
||||
this.scene.events.emit('building:unlocked', 'mayor_office');
|
||||
|
||||
console.log('🔓 Mayor features unlocked: walls, patrols, office');
|
||||
}
|
||||
|
||||
updateStatusBoard() {
|
||||
// Update city status display
|
||||
this.scene.events.emit('city:stats_updated', {
|
||||
cleanliness: this.cityState.cleanliness,
|
||||
security: this.cityState.security,
|
||||
morale: this.cityState.morale,
|
||||
population: this.npcCount,
|
||||
mayor: this.currentMayor ? this.currentMayor.name : 'None'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get election results for UI display
|
||||
*/
|
||||
getElectionResults() {
|
||||
return {
|
||||
phase: this.electionPhase,
|
||||
candidates: this.candidates,
|
||||
winner: this.currentMayor,
|
||||
cityState: this.cityState
|
||||
};
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.chaosDialogueTimer) clearInterval(this.chaosDialogueTimer);
|
||||
if (this.campaignDialogueTimer) clearInterval(this.campaignDialogueTimer);
|
||||
|
||||
this.debrisObjects.forEach(obj => obj.destroy());
|
||||
this.debrisObjects = [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user