fix: align audiovisual and narrative feedback
This commit is contained in:
@@ -4,6 +4,15 @@ import { readFileSync } from 'node:fs';
|
||||
const battleSource = readFileSync('src/game/scenes/BattleScene.ts', 'utf8');
|
||||
const campSource = readFileSync('src/game/scenes/CampScene.ts', 'utf8');
|
||||
const soundSource = readFileSync('src/game/audio/SoundDirector.ts', 'utf8');
|
||||
const storySource = readFileSync('src/game/scenes/StoryScene.ts', 'utf8');
|
||||
|
||||
const applyStoryPage = privateMethodBody(storySource, 'applyPage');
|
||||
assert.match(
|
||||
applyStoryPage,
|
||||
/chapterText\?\.setText\(page\.chapter\)\.setVisible\(!page\.cutscene\)/,
|
||||
'cutscenes must hide the generic chapter label so it cannot overlap the result or reward strip'
|
||||
);
|
||||
assert.match(storySource, /chapterVisible: this\.chapterText\?\.visible \?\? false/);
|
||||
|
||||
const tickStatuses = privateMethodBody(battleSource, 'tickBattleStatuses');
|
||||
assert.match(tickStatuses, /let burnTicked = false/);
|
||||
@@ -74,6 +83,17 @@ const deploymentConfirmation = privateMethodBody(battleSource, 'confirmPreBattle
|
||||
assert.match(deploymentConfirmation, /showOpeningBattleEvent\(\)/);
|
||||
assert.doesNotMatch(deploymentConfirmation, /soundDirector\.playSelect\(\)/, 'opening alert must not overlap a selection cue');
|
||||
|
||||
const combatAssetLoading = privateMethodBody(battleSource, 'ensureScenarioCombatAssets');
|
||||
assert.match(
|
||||
combatAssetLoading,
|
||||
/if \(this\.phase === 'deployment'\) \{\s*this\.renderSortieOrderHud\(\);\s*this\.renderDeploymentPanel\(\);/s,
|
||||
'deployment text and its status HUD must be rebuilt once delayed combat assets settle'
|
||||
);
|
||||
const deploymentPanel = privateMethodBody(battleSource, 'renderDeploymentPanel');
|
||||
assert.match(deploymentPanel, /const combatAssetsReady =/);
|
||||
assert.match(deploymentPanel, /'준비 중 · 누르면 자동 시작'/);
|
||||
assert.match(deploymentPanel, /combatAssetsReady\s*\? '전투 시작'/s);
|
||||
|
||||
const resolveDamageTarget = privateMethodBody(battleSource, 'tryResolveDamageTarget');
|
||||
assert.match(resolveDamageTarget, /triggerBattleEvent\('first-engagement'[\s\S]*\{ playCue: false \}\)/);
|
||||
const triggerBattleEvent = privateMethodBody(battleSource, 'triggerBattleEvent');
|
||||
@@ -83,13 +103,27 @@ const movementSound = privateMethodBody(battleSource, 'playMovementSound');
|
||||
assert.match(movementSound, /const minInterval = isMounted \? 125 : 180/);
|
||||
assert.match(movementSound, /Math\.floor\(duration \/ minInterval\) \+ 1/);
|
||||
assert.match(movementSound, /pulseCount > 1 \? duration \/ \(pulseCount - 1\) : 0/);
|
||||
assert.match(movementSound, /playMovementStep\(isMounted, index, \{\s*terrain,/s);
|
||||
assert.match(
|
||||
movementSound,
|
||||
/playMovementStep\(isMounted, index, \{\s*terrain: movementAudioTerrainForPulse\(terrainPath, index, pulseCount\),/s
|
||||
);
|
||||
assert.doesNotMatch(movementSound, /stopAfterMs/, 'movement one-shots should clean up on ended instead of truncating each step');
|
||||
assert.equal(
|
||||
countMatches(battleSource, /playMovementSound\(unit, movementDuration, movementDistance, battleMap\.terrain\[y\]\?\.\[x\]\)/g),
|
||||
countMatches(battleSource, /this\.movementAudioTerrainPath\(unit, fromX, fromY, x, y\)/g),
|
||||
2,
|
||||
'both synchronous and asynchronous movement paths must pass destination terrain to movement audio'
|
||||
'both synchronous and asynchronous movement paths must pass a sampled terrain route to movement audio'
|
||||
);
|
||||
assert.match(battleSource, /particles\?\.kind === 'rain'/, 'rain battles should route movement through the wet pool');
|
||||
const movementPlayback = privateMethodBody(soundSource, 'playMovementEffect');
|
||||
assert.match(movementPlayback, /activeVoices\.length >= this\.movementVoiceCap[\s\S]*return false/);
|
||||
assert.doesNotMatch(
|
||||
movementPlayback,
|
||||
/retireEffectElement/,
|
||||
'movement voice limiting should skip an excess pulse instead of hard-cutting a playing sample'
|
||||
);
|
||||
const meleeRushPlayback = publicMethodBody(soundSource, 'playMeleeRush');
|
||||
assert.match(meleeRushPlayback, /return this\.playEffect\(/, 'melee rush should bypass the saturated movement-step voice group');
|
||||
assert.doesNotMatch(meleeRushPlayback, /playMovementEffect/, 'melee rush must not be dropped by the movement-step ceiling');
|
||||
|
||||
const mapCombat = privateMethodBody(battleSource, 'playCombatMapPresentation');
|
||||
assert.match(mapCombat, /contactDelayMs = Math\.max\(70, Math\.round\(duration \* 0\.42\)\)/);
|
||||
|
||||
Reference in New Issue
Block a user