fix: sequence battle feedback and autosaves

This commit is contained in:
2026-07-29 03:49:21 +09:00
parent d4bd489b3d
commit 26ab244ad2
6 changed files with 2002 additions and 48 deletions

View File

@@ -100,11 +100,136 @@ assert.match(deploymentPanel, /combatAssetsReady\s*\? '전투 시작'/s);
const resolveDamageTarget = privateMethodBody(battleSource, 'tryResolveDamageTarget');
assert.match(resolveDamageTarget, /triggerFirstEngagementEvent\(attacker, target\)/);
assert.match(
resolveDamageTarget,
/await this\.showCombatExchangeMapResults\(result, true\);/,
'the final attack path must await the full readable map-result window before completing the unit action'
);
assert.doesNotMatch(
resolveDamageTarget,
/await this\.delay\(620\)/,
'attack completion must follow the popup lifetime instead of a shorter fixed delay'
);
assert(
resolveDamageTarget.indexOf('await this.showCombatExchangeMapResults(result, true)') <
resolveDamageTarget.indexOf('this.finishUnitAction(attacker'),
'the attack result must finish displaying before finishUnitAction can open the turn-end prompt'
);
const resolveSupportTarget = privateMethodBody(battleSource, 'tryResolveSupportTarget');
assert.match(
resolveSupportTarget,
/const popupDuration = this\.showSupportMapResult\(result\);\s*await this\.waitSceneDuration\(popupDuration\);/s,
'the final support path must await the duration returned by its map-result popup'
);
assert.doesNotMatch(
resolveSupportTarget,
/await this\.delay\(620\)/,
'support completion must follow the popup lifetime instead of a shorter fixed delay'
);
assert(
resolveSupportTarget.indexOf('await this.waitSceneDuration(popupDuration)') <
resolveSupportTarget.indexOf('this.finishUnitAction(user'),
'the support result must finish displaying before finishUnitAction can open the turn-end prompt'
);
const showSupportMapResult = privateMethodBody(battleSource, 'showSupportMapResult');
assert.equal(
countMatches(showSupportMapResult, /return this\.showMapResultPopup\(/g),
2,
'both healing and buff support results must return their actual popup lifetime'
);
const settleEnemyIntentCounterplay = privateMethodBody(
battleSource,
'settleEnemyIntentCounterplay'
);
assert.match(
settleEnemyIntentCounterplay,
/const popupDuration = this\.showMapResultPopup\([\s\S]*return \{[\s\S]*message:[\s\S]*popupDuration/s,
'intent counterplay must expose the lifetime of its follow-up result'
);
const showMapResultPopup = privateMethodBody(
battleSource,
'showMapResultPopup'
);
assert.match(
showMapResultPopup,
/extendBattleFeedbackReadableWindow\([\s\S]*duration \+ delay \+ battleFeedbackSettlePaddingMs/s,
'map results must extend the shared action-feedback readability gate'
);
const showObjectiveMapFeedback = privateMethodBody(
battleSource,
'showObjectiveMapFeedback'
);
assert.equal(
countMatches(
showObjectiveMapFeedback,
/extendBattleFeedbackReadableWindow\(/g
),
2,
'visible and offscreen objective feedback must both extend the action-feedback readability gate'
);
const finishUnitAction = privateMethodBody(
battleSource,
'finishUnitAction'
);
assert.match(
finishUnitAction,
/collectBattleEventsWithoutPresentation\(\);[\s\S]*await this\.waitForBattleFeedbackReadability\(\);[\s\S]*this\.showNextBattleEvent\(\);[\s\S]*await this\.waitForBattleEventPresentation\(\);/s,
'post-action map feedback must finish before queued battle events, and both must finish before turn completion'
);
assert(
finishUnitAction.indexOf(
'await this.waitForBattleEventPresentation()'
) <
finishUnitAction.indexOf(
'this.showTurnEndPrompt(message)'
),
'the turn-end prompt must follow all transient and modal action feedback'
);
const outcomeResolutionIndex = finishUnitAction.indexOf(
'this.resolveBattleOutcomeIfNeeded()'
);
const postOutcomeEventWaitIndex = finishUnitAction.indexOf(
'await this.waitForBattleEventPresentation()',
outcomeResolutionIndex
);
assert(
outcomeResolutionIndex >= 0 &&
postOutcomeEventWaitIndex > outcomeResolutionIndex &&
postOutcomeEventWaitIndex <
finishUnitAction.indexOf('this.showTurnEndPrompt(message)'),
'a victory-gate event created during outcome resolution must also clear before the turn-end prompt'
);
const firstEngagementEvent = privateMethodBody(battleSource, 'triggerFirstEngagementEvent');
assert.match(firstEngagementEvent, /volunteerPromiseLineForBattle\(/);
assert.match(firstEngagementEvent, /triggerBattleEvent\(firstBattleVolunteerPromiseEventKey[\s\S]*\{ playCue: false \}\)/);
assert.match(
firstEngagementEvent,
/presentationWasDeferred[\s\S]*deferBattleEventPresentation = true[\s\S]*finally[\s\S]*deferBattleEventPresentation =\s*presentationWasDeferred/s,
'the first-engagement notice must queue behind attack results instead of covering them'
);
const triggerBattleEvent = privateMethodBody(battleSource, 'triggerBattleEvent');
assert.match(triggerBattleEvent, /options\.playCue !== false/);
const sceneDurationWait = privateMethodBody(battleSource, 'waitSceneDuration');
assert.match(
sceneDurationWait,
/Phaser\.Scenes\.Events\.SHUTDOWN[\s\S]*resolve\(\)/s,
'action-result waits must resolve when BattleScene shuts down'
);
const scaledBattleDelay = privateMethodBody(battleSource, 'delay');
assert.match(
scaledBattleDelay,
/return this\.waitSceneDuration\(\s*this\.scaledBattleDuration\(ms\)\s*\)/s,
'scaled combat delays must share the scene-shutdown-safe wait path'
);
const firstBattleTutorialSchedule = privateMethodBody(
battleSource,
'scheduleFirstBattleTutorial'
);
assert.match(
firstBattleTutorialSchedule,
/!this\.activeBattleEvent[\s\S]*this\.battleEventQueue\.length === 0[\s\S]*this\.battleEventObjects\.length === 0/s,
'a restored pending battle event must display before the first-battle tutorial resumes'
);
const movementSound = privateMethodBody(battleSource, 'playMovementSound');
assert.match(movementSound, /const minInterval = isMounted \? 125 : 180/);