304 lines
11 KiB
JavaScript
304 lines
11 KiB
JavaScript
/**
|
|
* Audio System - Звуковая система (Web Audio API)
|
|
*/
|
|
|
|
class AudioSystem {
|
|
constructor() {
|
|
this.context = null;
|
|
this.enabled = true;
|
|
this.volume = 0.5;
|
|
this.musicEnabled = true;
|
|
this.musicOscillator = null;
|
|
this.musicGain = null;
|
|
}
|
|
|
|
// Инициализация аудио контекста
|
|
init() {
|
|
try {
|
|
this.context = new (window.AudioContext || window.webkitAudioContext)();
|
|
return true;
|
|
} catch (e) {
|
|
console.warn('Audio not supported:', e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Возобновить контекст (нужно после взаимодействия пользователя)
|
|
resume() {
|
|
if (this.context && this.context.state === 'suspended') {
|
|
this.context.resume();
|
|
}
|
|
}
|
|
|
|
// Звук шагов
|
|
playFootstep() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
const osc = this.context.createOscillator();
|
|
const gain = this.context.createGain();
|
|
|
|
osc.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
osc.type = 'square';
|
|
osc.frequency.setValueAtTime(80, this.context.currentTime);
|
|
osc.frequency.exponentialRampToValueAtTime(40, this.context.currentTime + 0.1);
|
|
|
|
gain.gain.setValueAtTime(this.volume * 0.1, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.1);
|
|
|
|
osc.start(this.context.currentTime);
|
|
osc.stop(this.context.currentTime + 0.1);
|
|
}
|
|
|
|
// Звук удара
|
|
playHit() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
// Шум удара
|
|
const bufferSize = this.context.sampleRate * 0.1;
|
|
const buffer = this.context.createBuffer(1, bufferSize, this.context.sampleRate);
|
|
const data = buffer.getChannelData(0);
|
|
|
|
for (let i = 0; i < bufferSize; i++) {
|
|
data[i] = (Math.random() * 2 - 1) * (1 - i / bufferSize);
|
|
}
|
|
|
|
const noise = this.context.createBufferSource();
|
|
noise.buffer = buffer;
|
|
|
|
const filter = this.context.createBiquadFilter();
|
|
filter.type = 'lowpass';
|
|
filter.frequency.value = 1000;
|
|
|
|
const gain = this.context.createGain();
|
|
gain.gain.setValueAtTime(this.volume * 0.3, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.15);
|
|
|
|
noise.connect(filter);
|
|
filter.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
noise.start();
|
|
}
|
|
|
|
// Звук убийства
|
|
playKill() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
// Низкий удар
|
|
const osc = this.context.createOscillator();
|
|
const gain = this.context.createGain();
|
|
|
|
osc.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
osc.type = 'sawtooth';
|
|
osc.frequency.setValueAtTime(200, this.context.currentTime);
|
|
osc.frequency.exponentialRampToValueAtTime(30, this.context.currentTime + 0.3);
|
|
|
|
gain.gain.setValueAtTime(this.volume * 0.4, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.3);
|
|
|
|
osc.start(this.context.currentTime);
|
|
osc.stop(this.context.currentTime + 0.3);
|
|
}
|
|
|
|
// Звукденег
|
|
playCoin() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
const osc = this.context.createOscillator();
|
|
const gain = this.context.createGain();
|
|
|
|
osc.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
osc.type = 'sine';
|
|
osc.frequency.setValueAtTime(800, this.context.currentTime);
|
|
osc.frequency.setValueAtTime(1200, this.context.currentTime + 0.05);
|
|
osc.frequency.setValueAtTime(1600, this.context.currentTime + 0.1);
|
|
|
|
gain.gain.setValueAtTime(this.volume * 0.2, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.15);
|
|
|
|
osc.start(this.context.currentTime);
|
|
osc.stop(this.context.currentTime + 0.15);
|
|
}
|
|
|
|
// Звук предмета
|
|
playItem() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
const osc = this.context.createOscillator();
|
|
const gain = this.context.createGain();
|
|
|
|
osc.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
osc.type = 'sine';
|
|
osc.frequency.setValueAtTime(600, this.context.currentTime);
|
|
osc.frequency.setValueAtTime(900, this.context.currentTime + 0.08);
|
|
|
|
gain.gain.setValueAtTime(this.volume * 0.15, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.2);
|
|
|
|
osc.start(this.context.currentTime);
|
|
osc.stop(this.context.currentTime + 0.2);
|
|
}
|
|
|
|
// Звук еды
|
|
playEat() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
const osc = this.context.createOscillator();
|
|
const gain = this.context.createGain();
|
|
|
|
osc.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
osc.type = 'triangle';
|
|
osc.frequency.setValueAtTime(400, this.context.currentTime);
|
|
osc.frequency.exponentialRampToValueAtTime(200, this.context.currentTime + 0.15);
|
|
|
|
gain.gain.setValueAtTime(this.volume * 0.2, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.15);
|
|
|
|
osc.start(this.context.currentTime);
|
|
osc.stop(this.context.currentTime + 0.15);
|
|
}
|
|
|
|
// Звук HP
|
|
playHeal() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
const osc1 = this.context.createOscillator();
|
|
const osc2 = this.context.createOscillator();
|
|
const gain = this.context.createGain();
|
|
|
|
osc1.connect(gain);
|
|
osc2.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
osc1.type = 'sine';
|
|
osc1.frequency.setValueAtTime(500, this.context.currentTime);
|
|
osc1.frequency.setValueAtTime(700, this.context.currentTime + 0.1);
|
|
|
|
osc2.type = 'sine';
|
|
osc2.frequency.setValueAtTime(750, this.context.currentTime + 0.05);
|
|
osc2.frequency.setValueAtTime(1000, this.context.currentTime + 0.15);
|
|
|
|
gain.gain.setValueAtTime(this.volume * 0.2, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.25);
|
|
|
|
osc1.start(this.context.currentTime);
|
|
osc2.start(this.context.currentTime + 0.05);
|
|
osc1.stop(this.context.currentTime + 0.2);
|
|
osc2.stop(this.context.currentTime + 0.25);
|
|
}
|
|
|
|
// Звук получения урона
|
|
playDamage() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
const osc = this.context.createOscillator();
|
|
const gain = this.context.createGain();
|
|
|
|
osc.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
osc.type = 'sawtooth';
|
|
osc.frequency.setValueAtTime(150, this.context.currentTime);
|
|
osc.frequency.exponentialRampToValueAtTime(50, this.context.currentTime + 0.2);
|
|
|
|
gain.gain.setValueAtTime(this.volume * 0.3, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.2);
|
|
|
|
osc.start(this.context.currentTime);
|
|
osc.stop(this.context.currentTime + 0.2);
|
|
}
|
|
|
|
// Звук UI (кнопка)
|
|
playUIClick() {
|
|
if (!this.enabled || !this.context) return;
|
|
|
|
const osc = this.context.createOscillator();
|
|
const gain = this.context.createGain();
|
|
|
|
osc.connect(gain);
|
|
gain.connect(this.context.destination);
|
|
|
|
osc.type = 'sine';
|
|
osc.frequency.setValueAtTime(1000, this.context.currentTime);
|
|
|
|
gain.gain.setValueAtTime(this.volume * 0.1, this.context.currentTime);
|
|
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.05);
|
|
|
|
osc.start(this.context.currentTime);
|
|
osc.stop(this.context.currentTime + 0.05);
|
|
}
|
|
|
|
// Фоновая музыка (простая мелодия)
|
|
startMusic() {
|
|
if (!this.musicEnabled || !this.context) return;
|
|
|
|
// Остановить предыдущую музыку
|
|
this.stopMusic();
|
|
|
|
// Создать осциллятор для фоновой музыки
|
|
this.musicOscillator = this.context.createOscillator();
|
|
this.musicGain = this.context.createGain();
|
|
|
|
this.musicOscillator.connect(this.musicGain);
|
|
this.musicGain.connect(this.context.destination);
|
|
|
|
this.musicOscillator.type = 'sine';
|
|
this.musicOscillator.frequency.value = 220;
|
|
this.musicGain.gain.value = this.volume * 0.05;
|
|
|
|
// Менять частоту каждые 2 секунды
|
|
const notes = [220, 262, 294, 330, 349, 392, 440, 392, 349, 330, 294, 262];
|
|
let noteIndex = 0;
|
|
|
|
this.musicInterval = setInterval(() => {
|
|
if (this.musicEnabled && this.musicOscillator) {
|
|
this.musicOscillator.frequency.value = notes[noteIndex];
|
|
noteIndex = (noteIndex + 1) % notes.length;
|
|
}
|
|
}, 2000);
|
|
|
|
this.musicOscillator.start();
|
|
}
|
|
|
|
// Остановить музыку
|
|
stopMusic() {
|
|
if (this.musicInterval) {
|
|
clearInterval(this.musicInterval);
|
|
}
|
|
if (this.musicOscillator) {
|
|
this.musicOscillator.stop();
|
|
this.musicOscillator = null;
|
|
}
|
|
}
|
|
|
|
// Переключить звук
|
|
toggle() {
|
|
this.enabled = !this.enabled;
|
|
return this.enabled;
|
|
}
|
|
|
|
// Переключить музыку
|
|
toggleMusic() {
|
|
this.musicEnabled = !this.musicEnabled;
|
|
if (this.musicEnabled) {
|
|
this.startMusic();
|
|
} else {
|
|
this.stopMusic();
|
|
}
|
|
return this.musicEnabled;
|
|
}
|
|
}
|
|
|
|
// Глобальный экземпляр
|
|
window.Audio = new AudioSystem();
|