635 lines
17 KiB
JavaScript
635 lines
17 KiB
JavaScript
/**
|
|
* BUILDING VISUALS & GENETICS SYSTEM
|
|
* Advanced animations for farm automation buildings and genetics lab
|
|
*/
|
|
class BuildingVisualsSystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
this.enabled = true;
|
|
|
|
// Building animations
|
|
this.buildings = new Map();
|
|
this.conveyorBelts = [];
|
|
this.windmills = [];
|
|
this.silos = [];
|
|
|
|
// Genetics lab
|
|
this.geneticsLab = null;
|
|
this.dnaHelixAnimation = null;
|
|
this.mutationVats = [];
|
|
|
|
// Settings
|
|
this.settings = {
|
|
buildingAnimations: true,
|
|
particleEffects: true,
|
|
geneticsUI: true,
|
|
animationSpeed: 1.0
|
|
};
|
|
|
|
this.loadSettings();
|
|
this.init();
|
|
|
|
console.log('✅ Building Visuals System initialized');
|
|
}
|
|
|
|
init() {
|
|
console.log('🏭 Building animations ready');
|
|
}
|
|
|
|
// ========== AUTO-PLANTER ==========
|
|
|
|
createAutoPlanter(x, y) {
|
|
if (!this.settings.buildingAnimations) return null;
|
|
|
|
const planter = {
|
|
x, y,
|
|
arm: null,
|
|
seed: null,
|
|
isPlanting: false
|
|
};
|
|
|
|
// Create mechanical arm
|
|
const arm = this.scene.add.graphics();
|
|
arm.lineStyle(4, 0x888888, 1);
|
|
arm.lineBetween(0, 0, 0, 30); // Vertical arm
|
|
arm.lineBetween(0, 30, 20, 30); // Horizontal extension
|
|
arm.setPosition(x, y);
|
|
arm.setDepth(100);
|
|
planter.arm = arm;
|
|
|
|
this.buildings.set(`planter_${x}_${y}`, planter);
|
|
return planter;
|
|
}
|
|
|
|
animatePlanting(planter, targetX, targetY) {
|
|
if (!planter || planter.isPlanting) return;
|
|
|
|
planter.isPlanting = true;
|
|
|
|
// Arm extends down
|
|
this.scene.tweens.add({
|
|
targets: planter.arm,
|
|
y: planter.y + 20,
|
|
duration: 500,
|
|
ease: 'Power2',
|
|
onComplete: () => {
|
|
// Drop seed
|
|
this.createSeedParticle(targetX, targetY);
|
|
|
|
// Arm retracts
|
|
this.scene.tweens.add({
|
|
targets: planter.arm,
|
|
y: planter.y,
|
|
duration: 500,
|
|
ease: 'Power2',
|
|
onComplete: () => {
|
|
planter.isPlanting = false;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
createSeedParticle(x, y) {
|
|
const seed = this.scene.add.circle(x, y - 20, 3, 0x8B4513);
|
|
seed.setDepth(99);
|
|
|
|
this.scene.tweens.add({
|
|
targets: seed,
|
|
y: y,
|
|
duration: 300,
|
|
ease: 'Bounce.easeOut',
|
|
onComplete: () => {
|
|
// Puff of dirt
|
|
if (this.scene.visualEnhancements) {
|
|
this.scene.visualEnhancements.createSparkleEffect(x, y);
|
|
}
|
|
seed.destroy();
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========== AUTO-HARVESTER ==========
|
|
|
|
createAutoHarvester(x, y) {
|
|
if (!this.settings.buildingAnimations) return null;
|
|
|
|
const harvester = {
|
|
x, y,
|
|
blades: [],
|
|
isHarvesting: false,
|
|
rotation: 0
|
|
};
|
|
|
|
// Create spinning blades
|
|
for (let i = 0; i < 4; i++) {
|
|
const blade = this.scene.add.graphics();
|
|
blade.lineStyle(3, 0xC0C0C0, 1);
|
|
blade.lineBetween(0, 0, 15, 0);
|
|
blade.setPosition(x, y);
|
|
blade.setRotation((Math.PI / 2) * i);
|
|
blade.setDepth(100);
|
|
harvester.blades.push(blade);
|
|
}
|
|
|
|
this.buildings.set(`harvester_${x}_${y}`, harvester);
|
|
return harvester;
|
|
}
|
|
|
|
animateHarvesting(harvester, cropX, cropY) {
|
|
if (!harvester || harvester.isHarvesting) return;
|
|
|
|
harvester.isHarvesting = true;
|
|
|
|
// Spin blades
|
|
for (const blade of harvester.blades) {
|
|
this.scene.tweens.add({
|
|
targets: blade,
|
|
rotation: blade.rotation + Math.PI * 4,
|
|
duration: 1000,
|
|
ease: 'Linear'
|
|
});
|
|
}
|
|
|
|
// Move to crop
|
|
this.scene.tweens.add({
|
|
targets: harvester.blades,
|
|
x: cropX,
|
|
y: cropY,
|
|
duration: 500,
|
|
ease: 'Power2',
|
|
onComplete: () => {
|
|
// Harvest effect
|
|
this.createHarvestEffect(cropX, cropY);
|
|
|
|
// Return to base
|
|
this.scene.tweens.add({
|
|
targets: harvester.blades,
|
|
x: harvester.x,
|
|
y: harvester.y,
|
|
duration: 500,
|
|
ease: 'Power2',
|
|
onComplete: () => {
|
|
harvester.isHarvesting = false;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
createHarvestEffect(x, y) {
|
|
// Crop particles
|
|
const emitter = this.scene.add.particles(x, y, 'particle_white', {
|
|
speed: { min: 30, max: 60 },
|
|
scale: { start: 0.5, end: 0 },
|
|
alpha: { start: 1, end: 0 },
|
|
lifespan: 800,
|
|
quantity: 10,
|
|
tint: 0xFFD700
|
|
});
|
|
|
|
this.scene.time.delayedCall(800, () => emitter.destroy());
|
|
}
|
|
|
|
// ========== CONVEYOR BELT ==========
|
|
|
|
createConveyorBelt(x, y, width, direction = 'right') {
|
|
if (!this.settings.buildingAnimations) return null;
|
|
|
|
const belt = {
|
|
x, y, width, direction,
|
|
items: [],
|
|
speed: 50 // pixels per second
|
|
};
|
|
|
|
// Create belt graphics
|
|
const graphics = this.scene.add.graphics();
|
|
graphics.fillStyle(0x444444, 1);
|
|
graphics.fillRect(x, y, width, 20);
|
|
|
|
// Moving lines to show direction
|
|
for (let i = 0; i < width; i += 20) {
|
|
const line = this.scene.add.rectangle(
|
|
x + i,
|
|
y + 10,
|
|
10,
|
|
2,
|
|
0x888888
|
|
);
|
|
line.setDepth(99);
|
|
|
|
// Animate line movement
|
|
const targetX = direction === 'right' ? x + width : x;
|
|
this.scene.tweens.add({
|
|
targets: line,
|
|
x: targetX,
|
|
duration: (width / belt.speed) * 1000,
|
|
repeat: -1,
|
|
ease: 'Linear'
|
|
});
|
|
}
|
|
|
|
belt.graphics = graphics;
|
|
this.conveyorBelts.push(belt);
|
|
return belt;
|
|
}
|
|
|
|
addItemToBelt(belt, itemSprite) {
|
|
if (!belt) return;
|
|
|
|
belt.items.push(itemSprite);
|
|
itemSprite.setPosition(belt.x, belt.y + 10);
|
|
itemSprite.setDepth(100);
|
|
|
|
// Move item along belt
|
|
const targetX = belt.direction === 'right'
|
|
? belt.x + belt.width
|
|
: belt.x;
|
|
|
|
this.scene.tweens.add({
|
|
targets: itemSprite,
|
|
x: targetX,
|
|
duration: (belt.width / belt.speed) * 1000,
|
|
ease: 'Linear',
|
|
onComplete: () => {
|
|
// Remove from belt
|
|
const index = belt.items.indexOf(itemSprite);
|
|
if (index > -1) {
|
|
belt.items.splice(index, 1);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========== WINDMILL ==========
|
|
|
|
createWindmill(x, y) {
|
|
if (!this.settings.buildingAnimations) return null;
|
|
|
|
const windmill = {
|
|
x, y,
|
|
blades: null,
|
|
powerGlow: null,
|
|
isPowered: false,
|
|
rotation: 0
|
|
};
|
|
|
|
// Create blades
|
|
const blades = this.scene.add.graphics();
|
|
blades.lineStyle(4, 0x8B4513, 1);
|
|
|
|
// Draw 4 blades
|
|
for (let i = 0; i < 4; i++) {
|
|
const angle = (Math.PI / 2) * i;
|
|
const endX = Math.cos(angle) * 30;
|
|
const endY = Math.sin(angle) * 30;
|
|
blades.lineBetween(0, 0, endX, endY);
|
|
}
|
|
|
|
blades.setPosition(x, y);
|
|
blades.setDepth(100);
|
|
windmill.blades = blades;
|
|
|
|
// Power glow
|
|
const glow = this.scene.add.circle(x, y, 40, 0x00ffff, 0);
|
|
glow.setBlendMode(Phaser.BlendModes.ADD);
|
|
glow.setDepth(99);
|
|
windmill.powerGlow = glow;
|
|
|
|
// Rotate blades
|
|
this.scene.tweens.add({
|
|
targets: blades,
|
|
rotation: Math.PI * 2,
|
|
duration: 3000 / this.settings.animationSpeed,
|
|
repeat: -1,
|
|
ease: 'Linear'
|
|
});
|
|
|
|
this.windmills.push(windmill);
|
|
return windmill;
|
|
}
|
|
|
|
setPowerState(windmill, powered) {
|
|
if (!windmill) return;
|
|
|
|
windmill.isPowered = powered;
|
|
|
|
if (powered) {
|
|
// Glow on
|
|
this.scene.tweens.add({
|
|
targets: windmill.powerGlow,
|
|
alpha: 0.4,
|
|
duration: 500
|
|
});
|
|
|
|
// Particle trail
|
|
if (this.settings.particleEffects) {
|
|
const emitter = this.scene.add.particles(
|
|
windmill.x,
|
|
windmill.y,
|
|
'particle_white',
|
|
{
|
|
speed: 20,
|
|
scale: { start: 0.3, end: 0 },
|
|
alpha: { start: 0.6, end: 0 },
|
|
lifespan: 1000,
|
|
frequency: 200,
|
|
quantity: 1,
|
|
tint: 0x00ffff
|
|
}
|
|
);
|
|
windmill.particleEmitter = emitter;
|
|
}
|
|
} else {
|
|
// Glow off
|
|
this.scene.tweens.add({
|
|
targets: windmill.powerGlow,
|
|
alpha: 0,
|
|
duration: 500
|
|
});
|
|
|
|
// Stop particles
|
|
if (windmill.particleEmitter) {
|
|
windmill.particleEmitter.destroy();
|
|
windmill.particleEmitter = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ========== STORAGE SILO ==========
|
|
|
|
createStorageSilo(x, y, capacity = 1000) {
|
|
if (!this.settings.buildingAnimations) return null;
|
|
|
|
const silo = {
|
|
x, y,
|
|
capacity,
|
|
currentAmount: 0,
|
|
fillIndicator: null,
|
|
fillBar: null
|
|
};
|
|
|
|
// Silo structure
|
|
const structure = this.scene.add.graphics();
|
|
structure.fillStyle(0x666666, 1);
|
|
structure.fillRect(x - 20, y - 60, 40, 60);
|
|
structure.fillCircle(x, y - 60, 20);
|
|
structure.setDepth(100);
|
|
silo.structure = structure;
|
|
|
|
// Fill indicator (inside silo)
|
|
const fillBar = this.scene.add.rectangle(
|
|
x,
|
|
y,
|
|
36,
|
|
0,
|
|
0xFFD700,
|
|
0.8
|
|
);
|
|
fillBar.setOrigin(0.5, 1);
|
|
fillBar.setDepth(101);
|
|
silo.fillBar = fillBar;
|
|
|
|
// Percentage text
|
|
const text = this.scene.add.text(x, y - 70, '0%', {
|
|
fontSize: '12px',
|
|
color: '#ffffff',
|
|
fontStyle: 'bold'
|
|
});
|
|
text.setOrigin(0.5);
|
|
text.setDepth(102);
|
|
silo.text = text;
|
|
|
|
this.silos.push(silo);
|
|
return silo;
|
|
}
|
|
|
|
updateSiloFill(silo, amount) {
|
|
if (!silo) return;
|
|
|
|
silo.currentAmount = Phaser.Math.Clamp(amount, 0, silo.capacity);
|
|
const percentage = (silo.currentAmount / silo.capacity) * 100;
|
|
const fillHeight = (percentage / 100) * 56; // Max height
|
|
|
|
// Animate fill
|
|
this.scene.tweens.add({
|
|
targets: silo.fillBar,
|
|
height: fillHeight,
|
|
duration: 500,
|
|
ease: 'Power2'
|
|
});
|
|
|
|
// Update text
|
|
silo.text.setText(`${Math.round(percentage)}%`);
|
|
}
|
|
|
|
// ========== GENETICS LAB ==========
|
|
|
|
createGeneticsLab(x, y) {
|
|
if (!this.settings.geneticsUI) return null;
|
|
|
|
const lab = {
|
|
x, y,
|
|
dnaHelix: null,
|
|
vats: [],
|
|
isActive: false
|
|
};
|
|
|
|
// Lab structure
|
|
const structure = this.scene.add.rectangle(x, y, 80, 60, 0x333333);
|
|
structure.setStrokeStyle(2, 0x00ff00);
|
|
structure.setDepth(100);
|
|
lab.structure = structure;
|
|
|
|
this.geneticsLab = lab;
|
|
return lab;
|
|
}
|
|
|
|
showDNAHelixAnimation(x, y) {
|
|
if (!this.settings.geneticsUI) return null;
|
|
|
|
// Create DNA helix
|
|
const helix = this.scene.add.container(x, y);
|
|
helix.setDepth(200);
|
|
|
|
// Two strands
|
|
const strand1 = [];
|
|
const strand2 = [];
|
|
|
|
for (let i = 0; i < 20; i++) {
|
|
const yPos = i * 10 - 100;
|
|
const angle = (i / 20) * Math.PI * 4;
|
|
|
|
const x1 = Math.cos(angle) * 15;
|
|
const x2 = Math.cos(angle + Math.PI) * 15;
|
|
|
|
const dot1 = this.scene.add.circle(x1, yPos, 3, 0x00ff00);
|
|
const dot2 = this.scene.add.circle(x2, yPos, 3, 0x00ffff);
|
|
|
|
helix.add(dot1);
|
|
helix.add(dot2);
|
|
|
|
strand1.push(dot1);
|
|
strand2.push(dot2);
|
|
|
|
// Connecting lines
|
|
if (i % 3 === 0) {
|
|
const line = this.scene.add.graphics();
|
|
line.lineStyle(1, 0xffffff, 0.5);
|
|
line.lineBetween(x1, yPos, x2, yPos);
|
|
helix.add(line);
|
|
}
|
|
}
|
|
|
|
// Rotate helix
|
|
this.scene.tweens.add({
|
|
targets: helix,
|
|
rotation: Math.PI * 2,
|
|
duration: 4000,
|
|
repeat: -1,
|
|
ease: 'Linear'
|
|
});
|
|
|
|
// Pulse effect
|
|
this.scene.tweens.add({
|
|
targets: helix,
|
|
scale: { from: 1, to: 1.1 },
|
|
duration: 2000,
|
|
yoyo: true,
|
|
repeat: -1
|
|
});
|
|
|
|
this.dnaHelixAnimation = helix;
|
|
return helix;
|
|
}
|
|
|
|
hideDNAHelixAnimation() {
|
|
if (this.dnaHelixAnimation) {
|
|
this.dnaHelixAnimation.destroy();
|
|
this.dnaHelixAnimation = null;
|
|
}
|
|
}
|
|
|
|
createMutationVat(x, y) {
|
|
if (!this.settings.geneticsUI) return null;
|
|
|
|
const vat = {
|
|
x, y,
|
|
container: null,
|
|
liquid: null,
|
|
bubbles: null,
|
|
isActive: false
|
|
};
|
|
|
|
// Vat container
|
|
const container = this.scene.add.graphics();
|
|
container.fillStyle(0x444444, 1);
|
|
container.fillRect(x - 20, y - 40, 40, 40);
|
|
container.lineStyle(2, 0x666666);
|
|
container.strokeRect(x - 20, y - 40, 40, 40);
|
|
container.setDepth(100);
|
|
vat.container = container;
|
|
|
|
// Liquid
|
|
const liquid = this.scene.add.rectangle(
|
|
x,
|
|
y - 20,
|
|
36,
|
|
36,
|
|
0x00ff00,
|
|
0.6
|
|
);
|
|
liquid.setDepth(101);
|
|
vat.liquid = liquid;
|
|
|
|
this.mutationVats.push(vat);
|
|
return vat;
|
|
}
|
|
|
|
activateMutationVat(vat) {
|
|
if (!vat || vat.isActive) return;
|
|
|
|
vat.isActive = true;
|
|
|
|
// Bubbling effect
|
|
const emitter = this.scene.add.particles(
|
|
vat.x,
|
|
vat.y - 40,
|
|
'particle_white',
|
|
{
|
|
speedY: { min: -30, max: -50 },
|
|
speedX: { min: -5, max: 5 },
|
|
scale: { start: 0.2, end: 0 },
|
|
alpha: { start: 0.8, end: 0 },
|
|
lifespan: 1000,
|
|
frequency: 200,
|
|
quantity: 2,
|
|
tint: 0x00ff00
|
|
}
|
|
);
|
|
vat.bubbles = emitter;
|
|
|
|
// Lightning effect
|
|
if (this.settings.particleEffects) {
|
|
this.createLightningEffect(vat.x, vat.y - 60);
|
|
}
|
|
|
|
// Liquid glow pulse
|
|
this.scene.tweens.add({
|
|
targets: vat.liquid,
|
|
alpha: { from: 0.6, to: 0.9 },
|
|
duration: 800,
|
|
yoyo: true,
|
|
repeat: -1
|
|
});
|
|
}
|
|
|
|
createLightningEffect(x, y) {
|
|
const lightning = this.scene.add.graphics();
|
|
lightning.lineStyle(2, 0xffff00, 1);
|
|
|
|
// Zigzag lightning
|
|
let currentX = x;
|
|
let currentY = y;
|
|
|
|
for (let i = 0; i < 5; i++) {
|
|
const nextX = currentX + (Math.random() - 0.5) * 20;
|
|
const nextY = currentY + 10;
|
|
lightning.lineBetween(currentX, currentY, nextX, nextY);
|
|
currentX = nextX;
|
|
currentY = nextY;
|
|
}
|
|
|
|
lightning.setDepth(200);
|
|
|
|
// Flash and fade
|
|
this.scene.tweens.add({
|
|
targets: lightning,
|
|
alpha: 0,
|
|
duration: 200,
|
|
onComplete: () => lightning.destroy()
|
|
});
|
|
}
|
|
|
|
// ========== SETTINGS ==========
|
|
|
|
saveSettings() {
|
|
localStorage.setItem('novafarma_building_visuals', JSON.stringify(this.settings));
|
|
}
|
|
|
|
loadSettings() {
|
|
const saved = localStorage.getItem('novafarma_building_visuals');
|
|
if (saved) {
|
|
this.settings = { ...this.settings, ...JSON.parse(saved) };
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
if (this.dnaHelixAnimation) this.dnaHelixAnimation.destroy();
|
|
for (const vat of this.mutationVats) {
|
|
if (vat.bubbles) vat.bubbles.destroy();
|
|
}
|
|
console.log('🏭 Building Visuals System destroyed');
|
|
}
|
|
}
|