681 lines
20 KiB
JavaScript
681 lines
20 KiB
JavaScript
/**
|
|
* ⚡ 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();
|
|
}
|
|
}
|