// Sound effects system using Web Audio API - no external files needed const SoundManager = { _ctx: null, _enabled: true, _volume: 0.3, init() { try { this._ctx = new (window.AudioContext || window.webkitAudioContext)(); } catch (e) { console.warn('Web Audio API not supported'); this._enabled = false; } const saved = localStorage.getItem('sound_enabled'); if (saved !== null) this._enabled = saved === 'true'; }, setEnabled(on) { this._enabled = on; localStorage.setItem('sound_enabled', on); }, toggle() { this.setEnabled(!this._enabled); return this._enabled; }, _play(freq, duration, type = 'sine', volume = 1) { if (!this._enabled || !this._ctx) return; if (this._ctx.state === 'suspended') this._ctx.resume(); const osc = this._ctx.createOscillator(); const gain = this._ctx.createGain(); osc.type = type; osc.frequency.setValueAtTime(freq, this._ctx.currentTime); gain.gain.setValueAtTime(this._volume * volume, this._ctx.currentTime); gain.gain.exponentialRampToValueAtTime(0.001, this._ctx.currentTime + duration); osc.connect(gain); gain.connect(this._ctx.destination); osc.start(); osc.stop(this._ctx.currentTime + duration); }, _noise(duration, volume = 1) { if (!this._enabled || !this._ctx) return; if (this._ctx.state === 'suspended') this._ctx.resume(); const bufferSize = this._ctx.sampleRate * duration; const buffer = this._ctx.createBuffer(1, bufferSize, this._ctx.sampleRate); const data = buffer.getChannelData(0); for (let i = 0; i < bufferSize; i++) { data[i] = Math.random() * 2 - 1; } const source = this._ctx.createBufferSource(); source.buffer = buffer; const gain = this._ctx.createGain(); gain.gain.setValueAtTime(this._volume * volume, this._ctx.currentTime); gain.gain.exponentialRampToValueAtTime(0.001, this._ctx.currentTime + duration); source.connect(gain); gain.connect(this._ctx.destination); source.start(); }, caseOpen() { // Descending sweep with click this._play(800, 0.1, 'square', 0.3); setTimeout(() => this._play(400, 0.15, 'sawtooth', 0.2), 80); setTimeout(() => this._play(200, 0.2, 'sine', 0.15), 180); this._noise(0.05, 0.4); }, caseRare() { // Ascending bright chime for rare items this._play(523, 0.15, 'sine', 0.3); setTimeout(() => this._play(659, 0.15, 'sine', 0.3), 100); setTimeout(() => this._play(784, 0.15, 'sine', 0.3), 200); setTimeout(() => this._play(1047, 0.3, 'sine', 0.4), 300); }, caseCovert() { // Epic sound for covert/knife this._play(392, 0.2, 'sawtooth', 0.3); setTimeout(() => this._play(523, 0.2, 'sawtooth', 0.3), 150); setTimeout(() => this._play(659, 0.2, 'sawtooth', 0.3), 300); setTimeout(() => this._play(784, 0.4, 'sine', 0.5), 450); setTimeout(() => this._play(1047, 0.6, 'sine', 0.6), 600); }, slotSpin() { // Ratcheting sound for slot reels for (let i = 0; i < 8; i++) { setTimeout(() => { this._play(300 + Math.random() * 400, 0.05, 'square', 0.15); }, i * 80); } }, slotMatch() { // Win jingle this._play(523, 0.1, 'sine', 0.3); setTimeout(() => this._play(659, 0.1, 'sine', 0.3), 100); setTimeout(() => this._play(784, 0.1, 'sine', 0.3), 200); setTimeout(() => this._play(1047, 0.4, 'sine', 0.5), 300); }, contractSubmit() { // Mechanical sound this._play(150, 0.3, 'sawtooth', 0.2); setTimeout(() => this._play(200, 0.2, 'square', 0.2), 200); setTimeout(() => this._play(250, 0.1, 'square', 0.15), 350); }, contractResult() { // Rising success this._play(400, 0.15, 'sine', 0.3); setTimeout(() => this._play(600, 0.15, 'sine', 0.3), 120); setTimeout(() => this._play(800, 0.3, 'sine', 0.4), 240); }, wheelSpin() { // Ticking as wheel rotates this._play(200, 0.03, 'square', 0.1); }, wheelWin() { // Celebration this._play(523, 0.15, 'sine', 0.3); setTimeout(() => this._play(659, 0.15, 'sine', 0.3), 120); setTimeout(() => this._play(784, 0.2, 'sine', 0.35), 240); setTimeout(() => this._play(1047, 0.5, 'sine', 0.5), 360); }, wheelLose() { // Sad trombone this._play(400, 0.2, 'sine', 0.2); setTimeout(() => this._play(350, 0.2, 'sine', 0.2), 150); setTimeout(() => this._play(300, 0.3, 'sine', 0.15), 300); }, achievement() { // Achievement unlock this._play(659, 0.1, 'sine', 0.3); setTimeout(() => this._play(523, 0.1, 'sine', 0.3), 100); setTimeout(() => this._play(784, 0.1, 'sine', 0.3), 200); setTimeout(() => this._play(659, 0.1, 'sine', 0.3), 300); setTimeout(() => this._play(1047, 0.4, 'sine', 0.5), 400); }, crashBet() { this._play(300, 0.1, 'square', 0.15); }, crashTick() { this._play(500 + Math.random() * 500, 0.03, 'sine', 0.08); }, crashCrashed() { // Explosion this._noise(0.3, 0.6); this._play(80, 0.5, 'sawtooth', 0.4); setTimeout(() => this._play(50, 0.8, 'sine', 0.3), 100); }, crashCashout() { // Cashout success this._play(600, 0.1, 'sine', 0.3); setTimeout(() => this._play(800, 0.1, 'sine', 0.3), 80); setTimeout(() => this._play(1000, 0.2, 'sine', 0.4), 160); }, click() { this._play(800, 0.03, 'sine', 0.1); }, error() { this._play(200, 0.15, 'square', 0.2); }, balanceUpdate() { this._play(1000, 0.05, 'sine', 0.1); } }; // Auto-init document.addEventListener('DOMContentLoaded', () => SoundManager.init());