feat: deepen audiovisual story immersion

This commit is contained in:
2026-07-23 10:57:07 +09:00
parent a9e401aedf
commit 8cf0886d7d
38 changed files with 2037 additions and 144 deletions

View File

@@ -71,6 +71,34 @@ try {
isValidBattleSaveState(legacyStateWithoutSortieOrder, options),
'Expected legacy battle saves without a sortie order to remain valid.'
);
const validPendingBattleEventState = {
...validState,
pendingBattleEvents: [
{
key: 'leader-wavering',
title: '두령 동요',
lines: ['두령의 기세가 꺾였습니다.', '전열을 몰아붙이십시오.'],
priority: 'high',
playCue: true,
presented: true
},
{
key: 'objective-village-approach',
title: '마을 접근',
lines: ['마을 주변의 방어 병력을 확인했습니다.'],
priority: 'low',
playCue: true
}
]
};
const parsedPendingBattleEvents = parseBattleSaveState(JSON.stringify(validPendingBattleEventState), options)?.pendingBattleEvents;
assert(
isValidBattleSaveState(validPendingBattleEventState, options) &&
parsedPendingBattleEvents?.length === 2 &&
parsedPendingBattleEvents[0]?.presented === true &&
parsedPendingBattleEvents !== validPendingBattleEventState.pendingBattleEvents,
'Expected active and queued battle events to round-trip without changing save version 1.'
);
const validRecommendationState = {
...validState,
sortieRecommendation: {
@@ -444,7 +472,21 @@ try {
['invalid triggered events', { triggeredBattleEvents: [1] }],
['unknown triggered event', { triggeredBattleEvents: ['phantom-event'] }],
['duplicate triggered event', { triggeredBattleEvents: ['opening', 'opening'] }],
['too long triggered event', { triggeredBattleEvents: ['x'.repeat(97)] }]
['too long triggered event', { triggeredBattleEvents: ['x'.repeat(97)] }],
['invalid pending events shape', { pendingBattleEvents: [1] }],
['unknown pending event', { pendingBattleEvents: [{ key: 'phantom-event', title: 'Unknown', lines: ['Unknown'], priority: 'normal', playCue: true }] }],
['duplicate pending event', { pendingBattleEvents: [
{ key: 'leader-wavering', title: 'First', lines: ['First'], priority: 'high', playCue: true },
{ key: 'leader-wavering', title: 'Second', lines: ['Second'], priority: 'normal', playCue: false }
] }],
['completed event cannot remain pending', { pendingBattleEvents: [{ key: 'opening', title: 'Opening', lines: ['Opening'], priority: 'critical', playCue: true }] }],
['invalid pending event priority', { pendingBattleEvents: [{ key: 'leader-wavering', title: 'Leader', lines: ['Leader'], priority: 'urgent', playCue: true }] }],
['invalid pending event cue', { pendingBattleEvents: [{ key: 'leader-wavering', title: 'Leader', lines: ['Leader'], priority: 'high', playCue: 'yes' }] }],
['empty pending event lines', { pendingBattleEvents: [{ key: 'leader-wavering', title: 'Leader', lines: [], priority: 'high', playCue: true }] }],
['presented pending event must be first', { pendingBattleEvents: [
{ key: 'leader-wavering', title: 'Leader', lines: ['Leader'], priority: 'high', playCue: true },
{ key: 'objective-village-approach', title: 'Village', lines: ['Village'], priority: 'low', playCue: true, presented: true }
] }]
];
rejectedCases.forEach(([label, patch]) => {