110 lines
4.3 KiB
JavaScript
110 lines
4.3 KiB
JavaScript
import { config } from '../config/Config.js';
|
|
import { playSfxOn, playSfxOneOfOn } from '../utils/Sfx.js';
|
|
|
|
/**
|
|
* Combat SFX — ONE shared event→voice map for both combat contexts (the
|
|
* range: js/combat/CombatScene.js, and the main game: js/combat/CombatSystem.js).
|
|
* The contexts call `combatSfx(scene, event)` at their event sites; the
|
|
* map + the playback path live HERE, so the two contexts can never drift.
|
|
*
|
|
* Every event gets a DISTINCT voice from the EXISTING data/sfx.json set
|
|
* (no new sound files):
|
|
*
|
|
* fire scan the blaster ping (player cannon; enemy lances
|
|
* play the same voice, quieter via `vol`)
|
|
* impact ui_click the light tick of a tracer striking
|
|
* shield solar_buzz the energy shimmer of a field absorbing
|
|
* hull mining_split the heavy crack of armor taking the hit
|
|
* kill thunder_01..04 one at random — the boom of a ship dying
|
|
* down deconstruct the player's death (the collapse voice)
|
|
* respawn discovery the player reforming (the "new object in
|
|
* view" voice)
|
|
* levelup solar_flare_01 the combat level landing (the charge-rise
|
|
* voice — js/combat/XpManager.js's 'levelup'
|
|
* event, subscribed by CombatSystem)
|
|
*
|
|
* Playback goes through the shared Sfx.js guards (master switch, audio
|
|
* manager, cache) — a headless scene with no manager simply doesn't
|
|
* sound, but the dev log below still records the call.
|
|
*/
|
|
export const COMBAT_SFX = {
|
|
fire: ['scan'],
|
|
impact: ['ui_click'],
|
|
shield: ['solar_buzz'],
|
|
hull: ['mining_split'],
|
|
kill: ['thunder_01', 'thunder_02', 'thunder_03', 'thunder_04'],
|
|
down: ['deconstruct'],
|
|
respawn: ['discovery'],
|
|
levelup: ['solar_flare_01'],
|
|
};
|
|
|
|
// ---------------------------------------------------------------- dev log
|
|
//
|
|
// Dev hook (documented in the README's combat section): every combatSfx
|
|
// call is recorded here — capped, mirrored to window.__COMBAT_SFX__ —
|
|
// so the wiring is verifiable in a headless browser (no audio manager)
|
|
// in BOTH contexts: dev/combat.sfxcheck.mjs drives real game events and
|
|
// reads the log back. It observes the real call path; it never plays
|
|
// sound itself.
|
|
|
|
const LOG_CAP = 64;
|
|
const log = [];
|
|
|
|
function remember(entry) {
|
|
log.push(entry);
|
|
if (log.length > LOG_CAP) log.splice(0, log.length - LOG_CAP);
|
|
if (typeof window !== 'undefined') {
|
|
window.__COMBAT_SFX__ = { log: log.slice(), last: entry.event };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Play the combat voice for `event` on `scene`.
|
|
*
|
|
* @param {object} scene a Phaser scene (any of the combat contexts')
|
|
* @param {string} event a COMBAT_SFX key
|
|
* @param {object} [o] `vol` = 0..1 multiplier (enemy fire plays quieter)
|
|
* @returns {string|null} the event name (null for an unknown event)
|
|
*/
|
|
export function combatSfx(scene, event, o = {}) {
|
|
const names = COMBAT_SFX[event];
|
|
if (!names) return null;
|
|
const vol = Math.min(1, Math.max(0, Number(o.vol) || 1));
|
|
const pass = { ...o, volume: Number(config.get('sfx.volume', 0.55)) * vol };
|
|
delete pass.vol;
|
|
if (names.length > 1) playSfxOneOfOn(scene, names, pass);
|
|
else playSfxOn(scene, names[0], pass);
|
|
remember({ event, names, t: Date.now() });
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* A hostile took a `dmg`-point hit with `preShields` shield points in
|
|
* front of it (read BEFORE the damage landed): the shield/hull voices —
|
|
* a hit that BREAKS through to the hull plays both, which reads as the
|
|
* shield popping. The `impact` tick is the caller's (it also plays on
|
|
* walls and rams).
|
|
*/
|
|
export function combatSfxEnemyHit(scene, preShields, dmg) {
|
|
if (preShields > 0) combatSfx(scene, 'shield');
|
|
if (preShields < dmg) combatSfx(scene, 'hull');
|
|
}
|
|
|
|
/**
|
|
* Queue the combat voices onto a scene's loader (the range's preload —
|
|
* the main game already loads ALL of data/sfx.json in GameScene).
|
|
* Skipped when the master switch is off (same rule as GameScene).
|
|
*/
|
|
export function loadCombatSfx(scene) {
|
|
if (!scene || !scene.load || !config.get('sfx.enabled', true)) return;
|
|
const seen = new Set();
|
|
for (const names of Object.values(COMBAT_SFX)) {
|
|
for (const n of names) {
|
|
if (seen.has(n)) continue;
|
|
seen.add(n);
|
|
const path = config.get(`sfx.${n}`, '');
|
|
if (typeof path === 'string' && path) scene.load.audio(`sfx_${n}`, path);
|
|
}
|
|
}
|
|
}
|