Files
novafarma/EMERGENCY_SYSTEMS_RECOVERY/NoirCitySystem.js
2026-01-16 02:43:46 +01:00

386 lines
10 KiB
JavaScript

/**
* LIVING NOIR CITY - AMBIENT WORLD SYSTEM
* Stray cats, barking dogs, atmospheric sounds
*/
class NoirCitySystem {
constructor(scene) {
this.scene = scene;
this.animals = [];
this.ambientSounds = [];
this.isActive = false;
}
/**
* INITIALIZE CITY ATMOSPHERE
*/
init() {
this.spawnStrayAnimals();
this.setupAmbientSounds();
this.startAtmosphere();
this.isActive = true;
console.log('🌆 Noir city atmosphere activated');
}
/**
* SPAWN STRAY CATS
*/
spawnStrayAnimals() {
// Spawn 3-5 stray cats in random locations
const catCount = Phaser.Math.Between(3, 5);
for (let i = 0; i < catCount; i++) {
this.spawnCat();
}
// Spawn 2-3 dogs in shadows
const dogCount = Phaser.Math.Between(2, 3);
for (let i = 0; i < dogCount; i++) {
this.spawnDog();
}
}
/**
* SPAWN A STRAY CAT
*/
spawnCat() {
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 cat = this.scene.physics.add.sprite(x, y, 'stray_cat');
cat.setDepth(15);
cat.setScale(0.8);
// Cat behavior
cat.animalType = 'cat';
cat.state = 'idle'; // 'idle', 'running', 'hiding'
cat.runSpeed = 200;
// Random idle movement
this.scene.time.addEvent({
delay: Phaser.Math.Between(3000, 8000),
callback: () => this.catIdleBehavior(cat),
loop: true
});
this.animals.push(cat);
return cat;
}
/**
* CAT IDLE BEHAVIOR
*/
catIdleBehavior(cat) {
if (!cat || cat.state === 'running') return;
const behavior = Phaser.Math.Between(1, 3);
switch (behavior) {
case 1: // Sit and clean
cat.state = 'idle';
cat.setVelocity(0, 0);
cat.play('cat_sit', true);
break;
case 2: // Wander
cat.state = 'idle';
const wanderX = Phaser.Math.Between(-30, 30);
const wanderY = Phaser.Math.Between(-30, 30);
cat.setVelocity(wanderX, wanderY);
cat.play('cat_walk', true);
this.scene.time.delayedCall(2000, () => {
if (cat) cat.setVelocity(0, 0);
});
break;
case 3: // Jump on trash can
cat.play('cat_jump', true);
break;
}
}
/**
* CAT RUNS AWAY FROM LONGBOARD
*/
catRunAway(cat, kai) {
const distance = Phaser.Math.Distance.Between(cat.x, cat.y, kai.x, kai.y);
if (distance < 80 && kai.body.speed > 50) {
cat.state = 'running';
// Run opposite direction from Kai
const angle = Phaser.Math.Angle.Between(kai.x, kai.y, cat.x, cat.y);
const velocityX = Math.cos(angle) * cat.runSpeed;
const velocityY = Math.sin(angle) * cat.runSpeed;
cat.setVelocity(velocityX, velocityY);
cat.play('cat_run', true);
// Play meow sound
if (this.scene.sound.get('cat_meow')) {
this.scene.sound.play('cat_meow', { volume: 0.3 });
}
// Stop running after escaping
this.scene.time.delayedCall(1500, () => {
if (cat) {
cat.state = 'idle';
cat.setVelocity(0, 0);
}
});
}
}
/**
* SPAWN A STRAY DOG (barks from shadows)
*/
spawnDog() {
const x = Phaser.Math.Between(50, this.scene.cameras.main.width - 50);
const y = Phaser.Math.Between(50, this.scene.cameras.main.height - 50);
const dog = this.scene.physics.add.sprite(x, y, 'stray_dog');
dog.setDepth(14);
dog.setAlpha(0.7); // Slightly transparent (in shadows)
dog.animalType = 'dog';
dog.barkTimer = null;
// Random barking
dog.barkTimer = this.scene.time.addEvent({
delay: Phaser.Math.Between(8000, 15000),
callback: () => this.dogBark(dog),
loop: true
});
this.animals.push(dog);
return dog;
}
/**
* DOG BARKING
*/
dogBark(dog) {
if (!dog) return;
dog.play('dog_bark', true);
// Play bark sound (spatial audio)
if (this.scene.sound.get('dog_bark')) {
this.scene.sound.play('dog_bark', {
volume: 0.4,
// Spatial audio based on distance (if available)
});
}
// Show bark indicator
const bark = this.scene.add.text(
dog.x,
dog.y - 30,
'WOOF!',
{
fontSize: '12px',
fontFamily: 'Arial',
color: '#ffffff',
stroke: '#000000',
strokeThickness: 3
}
);
bark.setOrigin(0.5);
bark.setDepth(50);
bark.setAlpha(0.7);
this.scene.tweens.add({
targets: bark,
alpha: 0,
y: bark.y - 20,
duration: 1000,
onComplete: () => bark.destroy()
});
}
/**
* SETUP AMBIENT SOUNDS
*/
setupAmbientSounds() {
// City ambient loop
if (this.scene.sound.get('city_ambient')) {
const cityAmbient = this.scene.sound.add('city_ambient', {
volume: 0.2,
loop: true
});
cityAmbient.play();
this.ambientSounds.push(cityAmbient);
}
// Wind ambience
if (this.scene.sound.get('wind_ambient')) {
const wind = this.scene.sound.add('wind_ambient', {
volume: 0.15,
loop: true
});
wind.play();
this.ambientSounds.push(wind);
}
// Random distant sounds
this.scene.time.addEvent({
delay: Phaser.Math.Between(10000, 20000),
callback: () => this.playRandomDistantSound(),
loop: true
});
}
/**
* PLAY RANDOM DISTANT SOUND
*/
playRandomDistantSound() {
const sounds = [
'distant_siren',
'metal_clang',
'glass_break',
'trash_can_fall',
'crow_caw'
];
const randomSound = Phaser.Utils.Array.GetRandom(sounds);
if (this.scene.sound.get(randomSound)) {
this.scene.sound.play(randomSound, {
volume: 0.1,
detune: Phaser.Math.Between(-200, 200) // Vary pitch
});
}
}
/**
* ATMOSPHERIC EFFECTS
*/
startAtmosphere() {
// Dust particles floating
this.addDustParticles();
// Paper/trash blowing in wind
this.addBlowingTrash();
// Flickering streetlights (if night)
this.addFlickeringLights();
}
/**
* ADD DUST PARTICLES
*/
addDustParticles() {
if (!this.scene.add.particles) return;
const particles = this.scene.add.particles('dust_particle');
const emitter = particles.createEmitter({
x: { min: 0, max: this.scene.cameras.main.width },
y: -20,
speedY: { min: 20, max: 50 },
speedX: { min: -10, max: 10 },
scale: { start: 0.1, end: 0.3 },
alpha: { start: 0.3, end: 0 },
lifespan: 5000,
frequency: 200,
quantity: 1
});
emitter.setDepth(5);
}
/**
* ADD BLOWING TRASH
*/
addBlowingTrash() {
// Spawn occasional paper/trash that blows across screen
this.scene.time.addEvent({
delay: Phaser.Math.Between(8000, 15000),
callback: () => {
const paper = this.scene.add.sprite(
-50,
Phaser.Math.Between(100, this.scene.cameras.main.height - 100),
'paper_trash'
);
paper.setDepth(10);
// Blow across screen
this.scene.tweens.add({
targets: paper,
x: this.scene.cameras.main.width + 50,
angle: 360,
duration: 8000,
ease: 'Linear',
onComplete: () => paper.destroy()
});
},
loop: true
});
}
/**
* ADD FLICKERING STREETLIGHTS
*/
addFlickeringLights() {
// Find all streetlight sprites
const lights = this.scene.children.list.filter(child =>
child.texture && child.texture.key === 'streetlight'
);
lights.forEach(light => {
// Random flicker
this.scene.time.addEvent({
delay: Phaser.Math.Between(2000, 8000),
callback: () => {
// Quick flicker
light.setAlpha(0.3);
this.scene.time.delayedCall(100, () => {
light.setAlpha(1);
this.scene.time.delayedCall(50, () => {
light.setAlpha(0.3);
this.scene.time.delayedCall(100, () => {
light.setAlpha(1);
});
});
});
},
loop: true
});
});
}
/**
* UPDATE (called every frame)
*/
update(kai) {
if (!this.isActive) return;
// Update cat behavior (run from Kai if on longboard)
this.animals.forEach(animal => {
if (animal.animalType === 'cat') {
this.catRunAway(animal, kai);
}
});
}
/**
* DESTROY ALL ANIMALS AND SOUNDS
*/
destroy() {
this.animals.forEach(animal => animal.destroy());
this.animals = [];
this.ambientSounds.forEach(sound => sound.stop());
this.ambientSounds = [];
this.isActive = false;
}
}
export default NoirCitySystem;