新增聊天室座驾系统

This commit is contained in:
pllx
2026-04-30 09:40:50 +08:00
parent 45ce8b2b2d
commit 3c95478097
32 changed files with 3982 additions and 53 deletions
+396 -1
View File
@@ -19,6 +19,10 @@
* hearts 爱心飘落(温暖双音)
* confetti 彩带庆典(礼炮碎响 + 清亮点缀)
* fireflies 萤火虫(稀疏微光铃音)
* j35 歼-35 战机(喷气低频 + 高速呼啸 + 音爆扫频)
* 99a 99A 主战坦克(履带低频 + 炮击冲击 + 金属震动)
* df5c 东风-5C(发射低频 + 尾焰轰鸣 + 高空呼啸)
* fujian 福建舰(海浪低频 + 舰载机掠过 + 甲板提示音)
*/
const EffectSounds = (() => {
@@ -762,6 +766,385 @@ const EffectSounds = (() => {
};
}
/**
* 启动歼-35 战机音效:喷气低频、空中掠过和音爆扫频。
*
* @returns {Function} 停止函数
*/
function _startJ35() {
const ctx = _getCtx();
const master = ctx.createGain();
master.gain.value = 0.58;
master.connect(ctx.destination);
const turbine = ctx.createOscillator();
const subRumble = ctx.createOscillator();
const turbineFilter = ctx.createBiquadFilter();
const turbineGain = ctx.createGain();
turbine.type = "sawtooth";
subRumble.type = "triangle";
turbine.frequency.setValueAtTime(72, ctx.currentTime);
turbine.frequency.exponentialRampToValueAtTime(210, ctx.currentTime + 1.8);
turbine.frequency.exponentialRampToValueAtTime(96, ctx.currentTime + 7.6);
subRumble.frequency.setValueAtTime(34, ctx.currentTime);
subRumble.frequency.exponentialRampToValueAtTime(68, ctx.currentTime + 1.8);
subRumble.frequency.exponentialRampToValueAtTime(38, ctx.currentTime + 7.6);
turbineFilter.type = "lowpass";
turbineFilter.frequency.setValueAtTime(420, ctx.currentTime);
turbineFilter.frequency.exponentialRampToValueAtTime(2600, ctx.currentTime + 1.6);
turbineFilter.frequency.exponentialRampToValueAtTime(520, ctx.currentTime + 7.8);
turbineGain.gain.setValueAtTime(0.001, ctx.currentTime);
turbineGain.gain.linearRampToValueAtTime(0.32, ctx.currentTime + 0.35);
turbineGain.gain.linearRampToValueAtTime(0.42, ctx.currentTime + 1.6);
turbineGain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 8.1);
turbine.connect(turbineFilter);
subRumble.connect(turbineFilter);
turbineFilter.connect(turbineGain);
turbineGain.connect(master);
turbine.start(ctx.currentTime);
subRumble.start(ctx.currentTime);
turbine.stop(ctx.currentTime + 8.2);
subRumble.stop(ctx.currentTime + 8.2);
_scheduleNoiseSweep(ctx, master, {
delay: 0.12,
duration: 2.6,
startFreq: 180,
endFreq: 6200,
volume: 0.24,
q: 0.9,
});
_scheduleNoiseSweep(ctx, master, {
delay: 2.05,
duration: 0.72,
startFreq: 9000,
endFreq: 1200,
volume: 0.34,
q: 1.4,
});
_scheduleNoiseSweep(ctx, master, {
delay: 4.2,
duration: 1.05,
startFreq: 7200,
endFreq: 280,
volume: 0.2,
q: 1.8,
filterType: "highpass",
});
[0.42, 0.9, 1.32, 5.1].forEach((delay, index) => {
_scheduleTone(ctx, master, {
delay,
duration: 0.11,
freq: [1046.5, 1318.5, 1567.98, 2093][index % 4],
endFreq: [1567.98, 1975.53, 2349.32, 3135.96][index % 4],
volume: 0.045,
type: "square",
});
});
const endTimer = setTimeout(() => {
master.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.8);
setTimeout(() => {
try {
master.disconnect();
} catch (_) {}
}, 900);
}, 8800);
return () => {
clearTimeout(endTimer);
try {
master.gain.setValueAtTime(0, ctx.currentTime);
turbine.stop();
subRumble.stop();
master.disconnect();
} catch (_) {}
};
}
/**
* 启动 99A 主战坦克音效:履带低频、炮击冲击与金属震动。
*
* @returns {Function} 停止函数
*/
function _startType99A() {
const ctx = _getCtx();
const master = ctx.createGain();
master.gain.value = 0.52;
master.connect(ctx.destination);
const engine = ctx.createOscillator();
const track = ctx.createOscillator();
const filter = ctx.createBiquadFilter();
const gain = ctx.createGain();
engine.type = "sawtooth";
track.type = "square";
engine.frequency.setValueAtTime(42, ctx.currentTime);
engine.frequency.exponentialRampToValueAtTime(68, ctx.currentTime + 1.1);
engine.frequency.exponentialRampToValueAtTime(46, ctx.currentTime + 7.5);
track.frequency.setValueAtTime(18, ctx.currentTime);
track.frequency.linearRampToValueAtTime(24, ctx.currentTime + 2.0);
track.frequency.linearRampToValueAtTime(18, ctx.currentTime + 7.5);
filter.type = "lowpass";
filter.frequency.setValueAtTime(220, ctx.currentTime);
filter.frequency.linearRampToValueAtTime(520, ctx.currentTime + 1.4);
filter.frequency.linearRampToValueAtTime(260, ctx.currentTime + 7.8);
gain.gain.setValueAtTime(0.001, ctx.currentTime);
gain.gain.linearRampToValueAtTime(0.32, ctx.currentTime + 0.35);
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 8.1);
engine.connect(filter);
track.connect(filter);
filter.connect(gain);
gain.connect(master);
engine.start(ctx.currentTime);
track.start(ctx.currentTime);
engine.stop(ctx.currentTime + 8.2);
track.stop(ctx.currentTime + 8.2);
_scheduleNoiseSweep(ctx, master, {
delay: 0.2,
duration: 2.8,
startFreq: 80,
endFreq: 420,
volume: 0.18,
q: 0.8,
filterType: "lowpass",
});
_scheduleNoiseSweep(ctx, master, {
delay: 4.02,
duration: 0.5,
startFreq: 180,
endFreq: 65,
volume: 0.42,
q: 0.7,
filterType: "lowpass",
});
_scheduleNoiseSweep(ctx, master, {
delay: 4.05,
duration: 0.18,
startFreq: 4800,
endFreq: 900,
volume: 0.16,
q: 1.2,
});
[0.5, 1.1, 1.7, 2.25, 3.0, 3.55, 5.2, 5.85].forEach((delay) => {
_scheduleTone(ctx, master, {
delay,
duration: 0.08,
freq: 72,
endFreq: 44,
volume: 0.045,
type: "square",
});
});
const endTimer = setTimeout(() => {
master.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.8);
setTimeout(() => {
try {
master.disconnect();
} catch (_) {}
}, 900);
}, 8400);
return () => {
clearTimeout(endTimer);
try {
master.gain.setValueAtTime(0, ctx.currentTime);
engine.stop();
track.stop();
master.disconnect();
} catch (_) {}
};
}
/**
* 启动东风-5C预览音效:发射低频、尾焰轰鸣与高空呼啸。
*
* @returns {Function} 停止函数
*/
function _startDf5c() {
const ctx = _getCtx();
const master = ctx.createGain();
master.gain.value = 0.58;
master.connect(ctx.destination);
const rumble = ctx.createOscillator();
const flame = ctx.createOscillator();
const filter = ctx.createBiquadFilter();
const gain = ctx.createGain();
rumble.type = "sawtooth";
flame.type = "triangle";
rumble.frequency.setValueAtTime(28, ctx.currentTime);
rumble.frequency.exponentialRampToValueAtTime(76, ctx.currentTime + 2.2);
rumble.frequency.exponentialRampToValueAtTime(38, ctx.currentTime + 7.4);
flame.frequency.setValueAtTime(96, ctx.currentTime);
flame.frequency.exponentialRampToValueAtTime(220, ctx.currentTime + 2.8);
flame.frequency.exponentialRampToValueAtTime(112, ctx.currentTime + 7.2);
filter.type = "lowpass";
filter.frequency.setValueAtTime(180, ctx.currentTime);
filter.frequency.exponentialRampToValueAtTime(1800, ctx.currentTime + 2.8);
filter.frequency.exponentialRampToValueAtTime(420, ctx.currentTime + 7.8);
gain.gain.setValueAtTime(0.001, ctx.currentTime);
gain.gain.linearRampToValueAtTime(0.4, ctx.currentTime + 0.9);
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 8.0);
rumble.connect(filter);
flame.connect(filter);
filter.connect(gain);
gain.connect(master);
rumble.start(ctx.currentTime);
flame.start(ctx.currentTime);
rumble.stop(ctx.currentTime + 8.1);
flame.stop(ctx.currentTime + 8.1);
_scheduleNoiseSweep(ctx, master, {
delay: 0.08,
duration: 3.4,
startFreq: 90,
endFreq: 2600,
volume: 0.28,
q: 0.8,
filterType: "lowpass",
});
_scheduleNoiseSweep(ctx, master, {
delay: 2.1,
duration: 2.6,
startFreq: 720,
endFreq: 8200,
volume: 0.22,
q: 1.3,
});
_scheduleNoiseSweep(ctx, master, {
delay: 4.8,
duration: 1.1,
startFreq: 9600,
endFreq: 1600,
volume: 0.18,
q: 1.8,
filterType: "highpass",
});
[0.2, 0.46, 0.74, 1.04, 1.34].forEach((delay) => {
_scheduleTone(ctx, master, {
delay,
duration: 0.12,
freq: 58,
endFreq: 32,
volume: 0.07,
type: "square",
});
});
const endTimer = setTimeout(() => {
master.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.8);
setTimeout(() => {
try {
master.disconnect();
} catch (_) {}
}, 900);
}, 8500);
return () => {
clearTimeout(endTimer);
try {
master.gain.setValueAtTime(0, ctx.currentTime);
rumble.stop();
flame.stop();
master.disconnect();
} catch (_) {}
};
}
/**
* 启动福建舰预览音效:海浪低频、舰载机掠过与甲板提示音。
*
* @returns {Function} 停止函数
*/
function _startFujian() {
const ctx = _getCtx();
const master = ctx.createGain();
master.gain.value = 0.42;
master.connect(ctx.destination);
const engine = ctx.createOscillator();
const wake = ctx.createOscillator();
const filter = ctx.createBiquadFilter();
const gain = ctx.createGain();
engine.type = "sawtooth";
wake.type = "triangle";
engine.frequency.setValueAtTime(38, ctx.currentTime);
engine.frequency.exponentialRampToValueAtTime(52, ctx.currentTime + 2.4);
engine.frequency.exponentialRampToValueAtTime(34, ctx.currentTime + 8.2);
wake.frequency.setValueAtTime(74, ctx.currentTime);
wake.frequency.exponentialRampToValueAtTime(92, ctx.currentTime + 2.2);
wake.frequency.exponentialRampToValueAtTime(60, ctx.currentTime + 8.2);
filter.type = "lowpass";
filter.frequency.setValueAtTime(240, ctx.currentTime);
filter.frequency.exponentialRampToValueAtTime(720, ctx.currentTime + 2.0);
filter.frequency.exponentialRampToValueAtTime(260, ctx.currentTime + 8.4);
gain.gain.setValueAtTime(0.001, ctx.currentTime);
gain.gain.linearRampToValueAtTime(0.26, ctx.currentTime + 0.55);
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 8.6);
engine.connect(filter);
wake.connect(filter);
filter.connect(gain);
gain.connect(master);
engine.start(ctx.currentTime);
wake.start(ctx.currentTime);
engine.stop(ctx.currentTime + 8.7);
wake.stop(ctx.currentTime + 8.7);
_scheduleNoiseSweep(ctx, master, {
delay: 0.1,
duration: 7.8,
startFreq: 120,
endFreq: 520,
volume: 0.18,
q: 0.7,
filterType: "lowpass",
});
_scheduleNoiseSweep(ctx, master, {
delay: 3.15,
duration: 1.35,
startFreq: 420,
endFreq: 7600,
volume: 0.22,
q: 1.5,
});
[0.8, 1.35, 2.0, 3.0, 4.7, 5.45].forEach((delay, index) => {
_scheduleTone(ctx, master, {
delay,
duration: 0.12,
freq: [880, 1174.66, 1567.98][index % 3],
endFreq: [987.77, 1318.51, 1760][index % 3],
volume: 0.045,
type: "sine",
});
});
const endTimer = setTimeout(() => {
master.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.8);
setTimeout(() => {
try {
master.disconnect();
} catch (_) {}
}, 900);
}, 9000);
return () => {
clearTimeout(endTimer);
try {
master.gain.setValueAtTime(0, ctx.currentTime);
engine.stop();
wake.stop();
master.disconnect();
} catch (_) {}
};
}
// ─── 公开 API ──────────────────────────────────────────────────
/**
@@ -770,7 +1153,7 @@ const EffectSounds = (() => {
* 当 AudioContext 处于 suspended 状态时,先 resume() 再播放,
* 解决页面无用户手势时的自动静音问题(如管理员进房自动烟花)。
*
* @param {string} type 'lightning' | 'fireworks' | 'rain' | 'snow' | 'sakura' | 'meteors' | 'gold-rain' | 'hearts' | 'confetti' | 'fireflies'
* @param {string} type 'lightning' | 'fireworks' | 'rain' | 'snow' | 'sakura' | 'meteors' | 'gold-rain' | 'hearts' | 'confetti' | 'fireflies' | 'j35' | '99a' | 'df5c' | 'fujian'
*/
function play(type) {
// 用户开启禁音则跳过
@@ -813,6 +1196,18 @@ const EffectSounds = (() => {
case "fireflies":
_stopFn = _startFireflies();
break;
case "j35":
_stopFn = _startJ35();
break;
case "99a":
_stopFn = _startType99A();
break;
case "df5c":
_stopFn = _startDf5c();
break;
case "fujian":
_stopFn = _startFujian();
break;
default:
break;
}