This commit is contained in:
2026-01-16 02:43:46 +01:00
parent bc2225ad64
commit 3ae8d39f9c
218 changed files with 87850 additions and 353 deletions

View File

@@ -0,0 +1,680 @@
/**
* ⚡ BUILDING UPGRADE SYSTEM - Week 1 Priority
* Generator + Power Grid + Electrician NPC + Maintenance
*
* Features:
* - Generator building (electricity for city)
* - Power grid system (poles connecting generator)
* - Electrician NPC employment (2 Cekini/day)
* - Repair mechanics (generator, poles, UV lights)
* - Breakdown prevention when Electrician employed
* - Electric sparks VFX + Repair sparkles
*
* Assets Used:
* - /assets/buildings/generator.png
* - /assets/buildings/power_pole_straight.png
* - /assets/buildings/power_pole_corner.png
* - /assets/characters/electrician/ (11 sprites)
* - /assets/vfx/electric_spark.png
* - /assets/vfx/sparkle_repair.png
*/
export default class BuildingUpgradeSystem {
constructor(scene) {
this.scene = scene;
// Generator system
this.generator = null;
this.generatorHealth = 100;
this.generatorMaxHealth = 100;
this.generatorActive = false;
this.generatorBreakdownChance = 0.05; // 5% per day without Electrician
// Power grid
this.powerPoles = [];
this.isPowered = false;
// Electrician NPC
this.electrician = null;
this.electricianEmployed = false;
this.electricianSalary = 2; // Cekini per day
this.electricianWorkSchedule = {
inspectionTime: 10, // 10 AM
repairTime: 14 // 2 PM
};
// Repair system
this.repairCost = {
generator: 50, // Wood
powerPole: 20,
uvLight: 30
};
// Buildings that need electricity
this.electricBuildings = [];
this.init();
}
init() {
console.log('[BuildingUpgrade] Initializing building upgrade system...');
// Load sprites
this.loadSprites();
// Setup daily maintenance check
this.setupMaintenanceRoutine();
}
loadSprites() {
// Generator
if (!this.scene.textures.exists('generator')) {
this.scene.load.image('generator', 'assets/buildings/generator.png');
}
// Power poles
if (!this.scene.textures.exists('power_pole_straight')) {
this.scene.load.image('power_pole_straight', 'assets/buildings/power_pole_straight.png');
}
if (!this.scene.textures.exists('power_pole_corner')) {
this.scene.load.image('power_pole_corner', 'assets/buildings/power_pole_corner.png');
}
// Electrician sprites
const directions = ['south', 'north', 'east', 'west'];
const actions = ['idle', 'walk'];
directions.forEach(dir => {
actions.forEach(action => {
const key = `electrician_${action}_${dir}`;
if (!this.scene.textures.exists(key)) {
this.scene.load.image(key, `assets/characters/electrician/${action}_${dir}.png`);
}
});
});
// Action sprites
if (!this.scene.textures.exists('electrician_action_repair')) {
this.scene.load.image('electrician_action_repair', 'assets/characters/electrician/action_repair.png');
}
if (!this.scene.textures.exists('electrician_action_inspect')) {
this.scene.load.image('electrician_action_inspect', 'assets/characters/electrician/action_inspect.png');
}
if (!this.scene.textures.exists('electrician_portrait')) {
this.scene.load.image('electrician_portrait', 'assets/characters/electrician/portrait.png');
}
// VFX
if (!this.scene.textures.exists('electric_spark')) {
this.scene.load.image('electric_spark', 'assets/vfx/electric_spark.png');
}
if (!this.scene.textures.exists('sparkle_repair')) {
this.scene.load.image('sparkle_repair', 'assets/vfx/sparkle_repair.png');
}
this.scene.load.start();
}
/**
* Build Generator
*/
buildGenerator(x, y) {
if (this.generator) {
console.warn('[BuildingUpgrade] Generator already exists!');
return false;
}
// Check resources (if resource system exists)
const cost = { wood: 100, stone: 50, coal: 20 };
if (this.scene.resourceLogisticsSystem) {
if (!this.scene.resourceLogisticsSystem.hasResources(cost)) {
console.warn('[BuildingUpgrade] Not enough resources to build generator!');
return false;
}
// Spend resources
Object.entries(cost).forEach(([type, amount]) => {
this.scene.resourceLogisticsSystem.removeResource(type, amount);
});
}
// Create generator sprite
this.generator = this.scene.add.sprite(x, y, 'generator');
this.generator.setOrigin(0.5, 0.5);
this.generator.setInteractive();
// Generator data
this.generator.buildingData = {
type: 'generator',
health: this.generatorMaxHealth,
active: true,
lastMaintenance: Date.now()
};
// Click to interact
this.generator.on('pointerdown', () => {
this.interactWithGenerator();
});
// Activate generator
this.generatorActive = true;
this.isPowered = true;
// Create smoke effect
this.createGeneratorSmoke(x, y);
console.log('[BuildingUpgrade] Generator built at', x, y);
return true;
}
/**
* Create smoke effect for generator
*/
createGeneratorSmoke(x, y) {
// Simple smoke particles
const smoke = this.scene.add.particles(x, y - 40, 'electric_spark', {
speed: { min: 20, max: 40 },
angle: { min: 260, max: 280 },
scale: { start: 0.3, end: 0 },
alpha: { start: 0.3, end: 0 },
lifespan: 2000,
frequency: 500,
tint: 0x666666,
blendMode: 'NORMAL'
});
this.generator.smokeEffect = smoke;
}
/**
* Place power pole
*/
placePowerPole(x, y, type = 'straight') {
const sprite = type === 'straight' ? 'power_pole_straight' : 'power_pole_corner';
const pole = this.scene.add.sprite(x, y, sprite);
pole.setOrigin(0.5, 1); // Bottom-centered
pole.setInteractive();
pole.poleData = {
type: type,
health: 100,
connected: false
};
this.powerPoles.push(pole);
// Check connections
this.updatePowerGrid();
console.log('[BuildingUpgrade] Power pole placed at', x, y);
return pole;
}
/**
* Update power grid connections
*/
updatePowerGrid() {
if (!this.generator) {
this.isPowered = false;
return;
}
// Simple proximity check for now
// TODO: Implement proper grid pathfinding
this.powerPoles.forEach(pole => {
const distance = Phaser.Math.Distance.Between(
this.generator.x, this.generator.y,
pole.x, pole.y
);
pole.poleData.connected = distance < 500; // 500px max distance
});
this.isPowered = this.generatorActive;
}
/**
* Spawn Electrician NPC
*/
spawnElectrician(x, y) {
if (this.electrician) {
console.warn('[BuildingUpgrade] Electrician already exists!');
return;
}
this.electrician = this.scene.add.sprite(x, y, 'electrician_idle_south');
this.electrician.setOrigin(0.5, 0.5);
this.electrician.setInteractive();
// NPC data
this.electrician.npcData = {
name: 'Electrician',
type: 'town_worker',
employed: false,
salary: this.electricianSalary,
workLocation: this.generator,
skills: ['generator_repair', 'power_grid', 'uv_lights']
};
// Click to hire/talk
this.electrician.on('pointerdown', () => {
this.interactWithElectrician();
});
// Idle animation
this.createElectricianIdleAnimation();
console.log('[BuildingUpgrade] Electrician spawned at', x, y);
}
/**
* Create idle animation for Electrician
*/
createElectricianIdleAnimation() {
if (!this.electrician) return;
this.scene.time.addEvent({
delay: 3000,
callback: () => {
if (this.electrician && !this.electrician.isWorking) {
// Look around with tools
const directions = ['south', 'east', 'west'];
const randomDir = Phaser.Utils.Array.GetRandom(directions);
this.electrician.setTexture(`electrician_idle_${randomDir}`);
}
},
loop: true
});
}
/**
* Interact with Electrician (hire or request repair)
*/
interactWithElectrician() {
if (!this.electrician) return;
if (!this.electricianEmployed) {
this.showElectricianEmploymentDialog();
} else {
this.showElectricianDialog();
}
}
/**
* Show employment dialog
*/
showElectricianEmploymentDialog() {
const dialogText = `Need an electrician?\n\nSalary: ${this.electricianSalary} Cekini/day\n\nI'll maintain your generator, prevent breakdowns, and repair everything for FREE when employed!`;
if (this.scene.dialogueSystem) {
this.scene.dialogueSystem.showDialog({
portrait: 'electrician_portrait',
name: 'Electrician',
text: dialogText,
choices: [
{
text: `Hire (${this.electricianSalary} Cekini/day)`,
callback: () => this.hireElectrician()
},
{
text: 'Not now',
callback: () => console.log('[BuildingUpgrade] Employment declined')
}
]
});
} else {
console.log('[BuildingUpgrade] Employment dialog:', dialogText);
// Auto-hire for testing
this.hireElectrician();
}
}
/**
* Hire Electrician
*/
hireElectrician() {
this.electricianEmployed = true;
this.electrician.npcData.employed = true;
// Add to worker count
if (this.scene.cityManagementSystem) {
this.scene.cityManagementSystem.addPopulation('worker', 1);
}
console.log('[BuildingUpgrade] Electrician hired! Salary:', this.electricianSalary, 'Cekini/day');
// Start work routine
this.startElectricianWorkRoutine();
}
/**
* Start Electrician's work routine
*/
startElectricianWorkRoutine() {
if (!this.electricianEmployed) return;
// Daily inspection at 10 AM
this.scene.time.addEvent({
delay: 60000, // Check every minute
callback: () => {
if (this.scene.timeSystem) {
const hour = this.scene.timeSystem.getCurrentHour();
// Morning inspection
if (hour === this.electricianWorkSchedule.inspectionTime) {
this.electricianInspectGenerator();
}
// Afternoon repair if needed
if (hour === this.electricianWorkSchedule.repairTime) {
if (this.generatorHealth < this.generatorMaxHealth) {
this.electricianRepairGenerator();
}
}
}
},
loop: true
});
}
/**
* Electrician inspects generator
*/
electricianInspectGenerator() {
if (!this.electrician || !this.generator) return;
console.log('[BuildingUpgrade] Electrician inspecting generator...');
// Walk to generator
this.walkElectricianTo(this.generator.x, this.generator.y + 60, () => {
// Show inspect action
this.electrician.setTexture('electrician_action_inspect');
this.electrician.isWorking = true;
// Check health
this.scene.time.delayedCall(2000, () => {
console.log(`[BuildingUpgrade] Generator health: ${this.generatorHealth}%`);
// Return to idle
this.electrician.setTexture('electrician_idle_south');
this.electrician.isWorking = false;
});
});
}
/**
* Electrician repairs generator
*/
electricianRepairGenerator() {
if (!this.electrician || !this.generator) return;
console.log('[BuildingUpgrade] Electrician repairing generator...');
// Walk to generator
this.walkElectricianTo(this.generator.x, this.generator.y + 60, () => {
// Show repair action
this.electrician.setTexture('electrician_action_repair');
this.electrician.isWorking = true;
// Play electric sparks VFX
this.playElectricSparks(this.generator.x, this.generator.y);
// Repair after 3 seconds
this.scene.time.delayedCall(3000, () => {
// Restore health
this.generatorHealth = this.generatorMaxHealth;
if (this.generator.buildingData) {
this.generator.buildingData.health = this.generatorMaxHealth;
}
// Play repair sparkles
this.playRepairSparkles(this.generator.x, this.generator.y);
console.log('[BuildingUpgrade] Generator fully repaired!');
// Return to idle
this.electrician.setTexture('electrician_idle_south');
this.electrician.isWorking = false;
});
});
}
/**
* Walk Electrician to location
*/
walkElectricianTo(targetX, targetY, onComplete) {
if (!this.electrician) return;
// Calculate direction
const dx = targetX - this.electrician.x;
const direction = dx > 0 ? 'east' : 'west';
// Walk animation
this.scene.tweens.add({
targets: this.electrician,
x: targetX,
y: targetY,
duration: 2000,
ease: 'Linear',
onUpdate: () => {
this.electrician.setTexture(`electrician_walk_${direction}`);
},
onComplete: () => {
if (onComplete) onComplete();
}
});
}
/**
* Play electric sparks VFX
*/
playElectricSparks(x, y) {
if (!this.scene.textures.exists('electric_spark')) return;
const emitter = this.scene.add.particles(x, y, 'electric_spark', {
speed: { min: 100, max: 200 },
angle: { min: 0, max: 360 },
scale: { start: 0.8, end: 0.2 },
alpha: { start: 1, end: 0 },
lifespan: 400,
quantity: 3,
frequency: 100,
blendMode: 'ADD',
tint: 0x00FFFF // Blue electric
});
// Stop after 3 seconds
this.scene.time.delayedCall(3000, () => {
emitter.stop();
this.scene.time.delayedCall(500, () => emitter.destroy());
});
}
/**
* Play repair sparkles VFX (4-frame animation!)
*/
playRepairSparkles(x, y) {
if (!this.scene.textures.exists('sparkle_repair')) return;
const emitter = this.scene.add.particles(x, y - 20, 'sparkle_repair', {
speed: { min: 20, max: 50 },
angle: { min: 0, max: 360 },
scale: { start: 1, end: 0 },
alpha: { start: 1, end: 0 },
lifespan: 1000,
quantity: 12,
blendMode: 'ADD',
tint: 0xFFD700 // Gold sparkles
});
// Destroy after animation
this.scene.time.delayedCall(1500, () => {
emitter.destroy();
});
}
/**
* Daily maintenance check
*/
setupMaintenanceRoutine() {
this.scene.time.addEvent({
delay: 86400000, // Daily (or faster in-game)
callback: () => this.performDailyMaintenance(),
loop: true
});
}
/**
* Perform daily maintenance
*/
performDailyMaintenance() {
if (!this.generator || !this.generatorActive) return;
if (!this.electricianEmployed) {
// Chance of breakdown without Electrician
if (Math.random() < this.generatorBreakdownChance) {
this.generatorBreakdown();
} else {
// Gradual degradation
this.generatorHealth -= 10;
if (this.generatorHealth < 0) {
this.generatorBreakdown();
}
}
} else {
// Electrician prevents breakdowns!
console.log('[BuildingUpgrade] Electrician prevented breakdown!');
}
}
/**
* Generator breakdown
*/
generatorBreakdown() {
console.warn('[BuildingUpgrade] GENERATOR BREAKDOWN!');
this.generatorActive = false;
this.generatorHealth = 0;
this.isPowered = false;
// Visual feedback
if (this.generator && this.generator.smokeEffect) {
this.generator.smokeEffect.stop();
}
// Show warning
if (this.scene.centralPopupSystem) {
this.scene.centralPopupSystem.showMessage(
'GENERATOR BREAKDOWN! Hire an Electrician or repair manually!',
'critical'
);
}
// Turn off electric buildings
this.powerOffBuildings();
}
/**
* Power off all electric buildings
*/
powerOffBuildings() {
this.electricBuildings.forEach(building => {
if (building.powerOff) {
building.powerOff();
}
});
}
/**
* Interact with generator
*/
interactWithGenerator() {
if (!this.generator) return;
const health = this.generatorHealth;
const status = this.generatorActive ? 'ACTIVE' : 'OFFLINE';
const info = `
=== GENERATOR STATUS ===
Status: ${status}
Health: ${health}/${this.generatorMaxHealth}
Power: ${this.isPowered ? 'ONLINE' : 'OFFLINE'}
Maintenance: ${this.electricianEmployed ? 'Automatic' : 'Manual'}
`;
console.log(info);
if (this.scene.centralPopupSystem) {
this.scene.centralPopupSystem.showMessage(info, 'info');
}
}
/**
* Show Electrician dialogue
*/
showElectricianDialog() {
const dialogues = [
"Generator's running smooth. No problems here.",
"Just did my daily inspection. All systems nominal.",
"Power grid is stable. Good work building it.",
"I'll keep everything running. Don't worry.",
"Found a loose wire this morning. Fixed it.",
"UV lights in the basement need checking soon."
];
const randomDialogue = Phaser.Utils.Array.GetRandom(dialogues);
if (this.scene.dialogueSystem) {
this.scene.dialogueSystem.showDialog({
portrait: 'electrician_portrait',
name: 'Electrician',
text: randomDialogue
});
} else {
console.log('[BuildingUpgrade] Electrician:', randomDialogue);
}
}
/**
* Register electric building
*/
registerElectricBuilding(building) {
this.electricBuildings.push(building);
}
/**
* Check if power is available
*/
hasPower() {
return this.isPowered;
}
/**
* Pay daily salaries
*/
payDailySalaries() {
if (!this.electricianEmployed) return;
if (this.scene.economySystem) {
this.scene.economySystem.spendCekini(this.electricianSalary);
console.log(`[BuildingUpgrade] Paid Electrician ${this.electricianSalary} Cekini`);
}
}
update(time, delta) {
// System updates handled by time events
}
destroy() {
if (this.generator) {
if (this.generator.smokeEffect) this.generator.smokeEffect.destroy();
this.generator.destroy();
}
this.powerPoles.forEach(pole => pole.destroy());
this.powerPoles = [];
if (this.electrician) this.electrician.destroy();
}
}