355 lines
11 KiB
JavaScript
355 lines
11 KiB
JavaScript
/**
|
|
* EnhancedAudioSystem.js
|
|
*
|
|
* Complete audio system with:
|
|
* - Ambient loops (crickets, wind, city noise)
|
|
* - Animal sounds (random intervals near farm)
|
|
* - Intro heartbeat + blur effect
|
|
* - Accessibility (visual indicators)
|
|
* - Xbox haptic feedback
|
|
* - .wav -> .ogg optimization
|
|
*
|
|
* Created: Jan 10, 2026
|
|
* Author: David "HIPO" Kotnik
|
|
* Studio: Hipodevil666 Studios™
|
|
*/
|
|
|
|
export default class EnhancedAudioSystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
|
|
// Audio references
|
|
this.ambientLoops = {};
|
|
this.animalSounds = {};
|
|
this.currentAmbient = null;
|
|
|
|
// Animal sound timers
|
|
this.animalTimers = {};
|
|
|
|
// Haptic feedback support
|
|
this.hapticEnabled = true;
|
|
|
|
// Visual accessibility indicators
|
|
this.visualIndicators = {};
|
|
|
|
console.log('🔊 Enhanced Audio System initialized!');
|
|
}
|
|
|
|
/**
|
|
* Load all audio assets
|
|
*/
|
|
preloadAudio() {
|
|
const scene = this.scene;
|
|
|
|
// AMBIENT LOOPS
|
|
scene.load.audio('ambient_crickets', 'assets/audio/ambient/crickets_loop.ogg');
|
|
scene.load.audio('ambient_wind', 'assets/audio/ambient/wind_loop.ogg');
|
|
scene.load.audio('ambient_city', 'assets/audio/ambient/city_noise_loop.ogg');
|
|
scene.load.audio('ambient_forest', 'assets/audio/ambient/forest_loop.ogg');
|
|
|
|
// ANIMAL SOUNDS
|
|
scene.load.audio('animal_sheep', 'assets/audio/animals/sheep.ogg');
|
|
scene.load.audio('animal_pig', 'assets/audio/animals/pig.ogg');
|
|
scene.load.audio('animal_chicken', 'assets/audio/animals/chicken.ogg');
|
|
scene.load.audio('animal_horse', 'assets/audio/animals/horse.ogg');
|
|
scene.load.audio('animal_goat', 'assets/audio/animals/goat.ogg');
|
|
scene.load.audio('animal_cow', 'assets/audio/animals/cow.ogg');
|
|
|
|
// INTRO EFFECTS
|
|
scene.load.audio('intro_heartbeat', 'assets/audio/effects/heartbeat.ogg');
|
|
|
|
// UI SOUNDS
|
|
scene.load.audio('raid_warning', 'assets/audio/ui/raid_alarm.ogg');
|
|
|
|
console.log('🎵 Audio assets queued for loading...');
|
|
}
|
|
|
|
/**
|
|
* Initialize audio after load
|
|
*/
|
|
initialize() {
|
|
const scene = this.scene;
|
|
|
|
// Create ambient loops
|
|
this.ambientLoops = {
|
|
crickets: scene.sound.add('ambient_crickets', { loop: true, volume: 0.3 }),
|
|
wind: scene.sound.add('ambient_wind', { loop: true, volume: 0.2 }),
|
|
city: scene.sound.add('ambient_city', { loop: true, volume: 0.15 }),
|
|
forest: scene.sound.add('ambient_forest', { loop: true, volume: 0.25 })
|
|
};
|
|
|
|
// Create animal sounds
|
|
this.animalSounds = {
|
|
sheep: scene.sound.add('animal_sheep', { volume: 0.4 }),
|
|
pig: scene.sound.add('animal_pig', { volume: 0.4 }),
|
|
chicken: scene.sound.add('animal_chicken', { volume: 0.35 }),
|
|
horse: scene.sound.add('animal_horse', { volume: 0.5 }),
|
|
goat: scene.sound.add('animal_goat', { volume: 0.4 }),
|
|
cow: scene.sound.add('animal_cow', { volume: 0.45 })
|
|
};
|
|
|
|
console.log('🎵 Enhanced Audio System ready!');
|
|
}
|
|
|
|
/**
|
|
* Play ambient loop based on biome
|
|
*/
|
|
playAmbient(biomeType) {
|
|
// Stop current ambient
|
|
if (this.currentAmbient) {
|
|
this.currentAmbient.stop();
|
|
}
|
|
|
|
// Select ambient based on biome
|
|
let ambient = null;
|
|
switch (biomeType) {
|
|
case 'grassland':
|
|
case 'farm':
|
|
ambient = this.ambientLoops.crickets;
|
|
break;
|
|
case 'forest':
|
|
ambient = this.ambientLoops.forest;
|
|
break;
|
|
case 'wasteland':
|
|
case 'radioactive':
|
|
ambient = this.ambientLoops.wind;
|
|
break;
|
|
case 'town':
|
|
case 'city':
|
|
ambient = this.ambientLoops.city;
|
|
break;
|
|
default:
|
|
ambient = this.ambientLoops.crickets;
|
|
}
|
|
|
|
if (ambient) {
|
|
ambient.play();
|
|
this.currentAmbient = ambient;
|
|
console.log(`🎵 Playing ambient: ${biomeType}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start random animal sounds near farm
|
|
*/
|
|
startAnimalSounds(playerX, playerY) {
|
|
// Random intervals: 5-15 seconds
|
|
Object.keys(this.animalSounds).forEach(animal => {
|
|
this.animalTimers[animal] = this.scene.time.addEvent({
|
|
delay: Phaser.Math.Between(5000, 15000),
|
|
callback: () => {
|
|
// Check if player is near farm (within 500px)
|
|
// This is simplified - you'd check actual farm position
|
|
const nearFarm = true; // TODO: Implement proximity check
|
|
|
|
if (nearFarm && !this.animalSounds[animal].isPlaying) {
|
|
this.animalSounds[animal].play();
|
|
console.log(`🐑 ${animal} sound played!`);
|
|
}
|
|
},
|
|
loop: true
|
|
});
|
|
});
|
|
|
|
console.log('🐄 Animal sounds started!');
|
|
}
|
|
|
|
/**
|
|
* Stop animal sounds
|
|
*/
|
|
stopAnimalSounds() {
|
|
Object.values(this.animalTimers).forEach(timer => {
|
|
if (timer) timer.remove();
|
|
});
|
|
this.animalTimers = {};
|
|
console.log('🔇 Animal sounds stopped!');
|
|
}
|
|
|
|
/**
|
|
* Play intro sequence (heartbeat + blur effect)
|
|
*/
|
|
playIntroSequence() {
|
|
const scene = this.scene;
|
|
|
|
// Play heartbeat
|
|
const heartbeat = scene.sound.add('intro_heartbeat', { volume: 0.6 });
|
|
heartbeat.play();
|
|
|
|
// Blur-to-clear effect (Kai's amnesia)
|
|
const blurStrength = 10;
|
|
const camera = scene.cameras.main;
|
|
|
|
// Apply initial blur (using postFX if available)
|
|
// Note: Phaser 3.60+ has built-in blur, older versions need custom shader
|
|
if (camera.setPostPipeline) {
|
|
// Modern Phaser blur
|
|
camera.setPostPipeline('BlurPostFX');
|
|
}
|
|
|
|
// Clear blur over 3 seconds (synchronized with heartbeat)
|
|
scene.tweens.add({
|
|
targets: camera,
|
|
scrollX: 0, // Placeholder - actual blur would use custom property
|
|
duration: 3000,
|
|
ease: 'Power2',
|
|
onUpdate: (tween) => {
|
|
// Reduce blur over time
|
|
const progress = tween.progress;
|
|
// TODO: Update actual blur shader strength here
|
|
},
|
|
onComplete: () => {
|
|
// Remove blur effect
|
|
if (camera.resetPostPipeline) {
|
|
camera.resetPostPipeline();
|
|
}
|
|
console.log('👁️ Vision cleared - amnesia intro complete!');
|
|
}
|
|
});
|
|
|
|
// Haptic feedback (heartbeat pulse)
|
|
this.vibrate(200, 500); // 200ms pulse, 500ms between
|
|
|
|
console.log('💓 Intro sequence playing...');
|
|
}
|
|
|
|
/**
|
|
* Show visual indicator for deaf accessibility
|
|
*/
|
|
showVisualIndicator(type, duration = 2000) {
|
|
const scene = this.scene;
|
|
const { width, height } = scene.cameras.main;
|
|
|
|
let indicator;
|
|
let color = 0xFFFFFF;
|
|
let icon = '!';
|
|
|
|
switch (type) {
|
|
case 'raid':
|
|
color = 0xFF0000; // Red
|
|
icon = '⚠️ RAID!';
|
|
break;
|
|
case 'animal':
|
|
color = 0x00FF00; // Green
|
|
icon = '🐄';
|
|
break;
|
|
case 'danger':
|
|
color = 0xFF8800; // Orange
|
|
icon = '⚡';
|
|
break;
|
|
default:
|
|
icon = '🔔';
|
|
}
|
|
|
|
// Create visual indicator
|
|
indicator = scene.add.text(width / 2, 100, icon, {
|
|
fontSize: '48px',
|
|
color: '#' + color.toString(16).padStart(6, '0'),
|
|
stroke: '#000000',
|
|
strokeThickness: 4,
|
|
shadow: {
|
|
offsetX: 0,
|
|
offsetY: 0,
|
|
color: '#' + color.toString(16).padStart(6, '0'),
|
|
blur: 20,
|
|
fill: true
|
|
}
|
|
}).setOrigin(0.5);
|
|
|
|
indicator.setScrollFactor(0); // Fixed to camera
|
|
indicator.setDepth(1000); // Always on top
|
|
|
|
// Pulse animation
|
|
scene.tweens.add({
|
|
targets: indicator,
|
|
scaleX: 1.2,
|
|
scaleY: 1.2,
|
|
alpha: 0.7,
|
|
duration: 500,
|
|
yoyo: true,
|
|
repeat: Math.floor(duration / 1000)
|
|
});
|
|
|
|
// Remove after duration
|
|
scene.time.delayedCall(duration, () => {
|
|
indicator.destroy();
|
|
});
|
|
|
|
console.log(`👁️ Visual indicator shown: ${type}`);
|
|
}
|
|
|
|
/**
|
|
* Xbox controller vibration (haptic feedback)
|
|
*/
|
|
vibrate(duration = 200, interval = 0) {
|
|
if (!this.hapticEnabled) return;
|
|
|
|
const scene = this.scene;
|
|
|
|
// Check for gamepad support
|
|
if (scene.input.gamepad && scene.input.gamepad.total > 0) {
|
|
const pad = scene.input.gamepad.getPad(0);
|
|
|
|
if (pad && pad.vibration) {
|
|
// Vibrate motors (weak, strong)
|
|
pad.vibration.playEffect('dual-rumble', {
|
|
startDelay: 0,
|
|
duration: duration,
|
|
weakMagnitude: 0.5,
|
|
strongMagnitude: 1.0
|
|
});
|
|
|
|
// Repeat if interval specified
|
|
if (interval > 0) {
|
|
scene.time.delayedCall(duration + interval, () => {
|
|
this.vibrate(duration, interval);
|
|
});
|
|
}
|
|
|
|
console.log(`🎮 Haptic feedback: ${duration}ms`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Play raid warning with visual + haptic feedback
|
|
*/
|
|
playRaidWarning() {
|
|
const scene = this.scene;
|
|
|
|
// Audio warning
|
|
const raidSound = scene.sound.add('raid_warning', { volume: 0.7 });
|
|
raidSound.play();
|
|
|
|
// Visual indicator (accessibility)
|
|
this.showVisualIndicator('raid', 3000);
|
|
|
|
// Haptic feedback (3 strong pulses)
|
|
this.vibrate(300, 300);
|
|
this.vibrate(300, 600);
|
|
this.vibrate(300, 900);
|
|
|
|
console.log('⚠️ RAID WARNING! (audio + visual + haptic)');
|
|
}
|
|
|
|
/**
|
|
* Update system (called in scene's update loop)
|
|
*/
|
|
update(time, delta) {
|
|
// Future: proximity checks for animal sounds, etc.
|
|
}
|
|
|
|
/**
|
|
* Cleanup
|
|
*/
|
|
destroy() {
|
|
// Stop all sounds
|
|
if (this.currentAmbient) {
|
|
this.currentAmbient.stop();
|
|
}
|
|
|
|
this.stopAnimalSounds();
|
|
|
|
console.log('🔇 Enhanced Audio System destroyed!');
|
|
}
|
|
}
|