feat: deepen battle atmosphere and portrait eras
This commit is contained in:
@@ -97,6 +97,11 @@ try {
|
||||
'critical-boom': '/audio/critical-boom.ogg',
|
||||
'victory-fanfare': '/audio/victory-fanfare.ogg',
|
||||
'defeat-sting': '/audio/defeat-sting.ogg',
|
||||
'ally-turn': '/audio/ally-turn.ogg',
|
||||
'enemy-turn': '/audio/enemy-turn.ogg',
|
||||
'operation-alert': '/audio/operation-alert.ogg',
|
||||
'objective-success': '/audio/objective-success.ogg',
|
||||
'objective-failure': '/audio/objective-failure.ogg',
|
||||
'ui-select': '/audio/ui-select.ogg'
|
||||
});
|
||||
director.registerEffectPools({
|
||||
@@ -366,6 +371,52 @@ try {
|
||||
assert.equal(debug.ambience.activeElementCount, 0, 'legacy playMusic ambience cleanup should complete');
|
||||
assert.equal(audioHarness.activeInstances().length, 1, 'the verifier must finish without orphaned loop elements');
|
||||
|
||||
const tacticalCueStart = audioHarness.instances.length;
|
||||
assert.equal(director.playAllyTurnCue(), true, 'the first allied turn cue should play');
|
||||
assertClose(audioHarness.byOriginalSrc('/audio/ally-turn.ogg').volume, 0.32, 'allied turn cue volume');
|
||||
assert.equal(director.playAllyTurnCue(), false, 'a repeated allied turn cue should be coalesced');
|
||||
assert.equal(director.playEnemyTurnCue(), false, 'turn cue cooldown should be shared across factions');
|
||||
|
||||
assert.equal(director.playOperationAlert(), true, 'the first operation alert should play');
|
||||
assertClose(audioHarness.byOriginalSrc('/audio/operation-alert.ogg').volume, 0.34, 'operation alert volume');
|
||||
assert.equal(director.playOperationAlert(), false, 'operation alerts in one event flush should be coalesced');
|
||||
|
||||
assert.equal(director.playObjectiveAchieved(), true, 'the first objective result should play');
|
||||
assertClose(audioHarness.byOriginalSrc('/audio/objective-success.ogg').volume, 0.34, 'objective success volume');
|
||||
assert.equal(director.playObjectiveFailed(), false, 'objective result cooldown should suppress re-entry');
|
||||
debug = director.getDebugState();
|
||||
assert.deepEqual(
|
||||
debug.semanticCues.cooldownMs,
|
||||
{ turn: 900, operation: 650, objective: 1200 },
|
||||
'semantic cue cooldowns should be visible in debug state'
|
||||
);
|
||||
assert.deepEqual(
|
||||
debug.semanticCues.lastSuppressed,
|
||||
{ group: 'objective', key: 'objective-failure', at: clock.now, remainingMs: 1200 },
|
||||
'debug state should explain the most recently coalesced cue'
|
||||
);
|
||||
assert.equal(audioHarness.instances.length, tacticalCueStart + 3, 'coalesced cues must not construct Audio');
|
||||
|
||||
clock.advance(650);
|
||||
assert.equal(director.playOperationAlert(), true, 'operation alert should replay after its cooldown');
|
||||
clock.advance(250);
|
||||
assert.equal(director.playEnemyTurnCue(), true, 'enemy turn cue should play after the shared turn cooldown');
|
||||
assertClose(audioHarness.byOriginalSrc('/audio/enemy-turn.ogg').volume, 0.3, 'enemy turn cue volume');
|
||||
clock.advance(300);
|
||||
assert.equal(director.playObjectiveFailed(), true, 'objective failure should play after the result cooldown');
|
||||
assertClose(audioHarness.byOriginalSrc('/audio/objective-failure.ogg').volume, 0.32, 'objective failure volume');
|
||||
debug = director.getDebugState();
|
||||
assert.deepEqual(
|
||||
debug.semanticCues.lastPlayed,
|
||||
{ group: 'objective', key: 'objective-failure', at: clock.now },
|
||||
'debug state should expose the latest semantic cue'
|
||||
);
|
||||
assert.equal(audioHarness.instances.length, tacticalCueStart + 6, 'expired cooldowns should permit new Audio');
|
||||
clock.advance(2500);
|
||||
debug = director.getDebugState();
|
||||
assert.equal(debug.activeEffectCount, 0, 'tactical cues should retire after their playback windows');
|
||||
assert.equal(audioHarness.activeInstances().length, 1, 'tactical cue cleanup should leave only music active');
|
||||
|
||||
const semanticEffectStart = audioHarness.instances.length;
|
||||
director.playMeleeSwing(true, false);
|
||||
assert.equal(director.getDebugState().lastEffect?.key, 'melee-swing', 'melee API should use the swing pool');
|
||||
@@ -404,7 +455,7 @@ try {
|
||||
console.log(
|
||||
`Verified dual SoundDirector channels across ${debug.music.elementRevision} music and ` +
|
||||
`${debug.ambience.elementRevision} ambience element revisions, no-repeat effect pools, ` +
|
||||
'semantic combat effects, category multipliers, and deterministic music ducking.'
|
||||
'coalesced tactical cues, semantic combat effects, category multipliers, and deterministic music ducking.'
|
||||
);
|
||||
} finally {
|
||||
restoreGlobals();
|
||||
|
||||
Reference in New Issue
Block a user