This commit is contained in:
2026-04-28 02:05:12 +08:00
parent 271db02e4a
commit 1eb090e4a5
39 changed files with 2671 additions and 165 deletions

View File

@@ -452,6 +452,104 @@ describe('storyChoiceRuntime', () => {
expect(setGameState).toHaveBeenLastCalledWith(finalState);
});
it('routes server defeat outcomes into death and revive flow instead of victory settlement', async () => {
const gameState = createState({
worldType: 'WUXIA',
inBattle: true,
playerHp: 6,
playerMaxHp: 30,
playerMana: 10,
playerMaxMana: 10,
currentScenePreset: {
id: 'wuxia-bamboo-road',
name: '竹林古道',
description: '风穿竹影,路面狭长。',
imageSrc: '/scene-a.png',
connectedSceneIds: [],
connections: [],
forwardSceneId: null,
treasureHints: [],
npcs: [],
},
sceneHostileNpcs: [
{
id: 'wolf',
name: '山狼',
action: '逼近',
description: '山狼',
animation: 'idle',
xMeters: 3,
yOffset: 0,
facing: 'left',
attackRange: 1,
speed: 1,
hp: 4,
maxHp: 18,
},
],
});
const finalState = createState({
...gameState,
inBattle: false,
playerHp: 0,
currentEncounter: null,
sceneHostileNpcs: [],
currentNpcBattleOutcome: 'fight_defeat',
});
const setGameState = vi.fn();
const setCurrentStory = vi.fn();
resolveServerRuntimeChoiceMock.mockResolvedValueOnce({
response: {
presentation: {
battle: {
targetId: 'wolf',
damageDealt: 22,
damageTaken: 8,
outcome: 'defeat',
},
resultText: '你在山狼的反扑下倒地。',
},
},
hydratedSnapshot: {
gameState: finalState,
},
nextStory: createStory('不会进入胜利文本'),
});
await runServerRuntimeChoiceAction({
gameState,
currentStory: createStory('当前故事'),
option: createOption('battle_all_in_crush'),
character: createCharacter(),
setBattleReward: vi.fn(),
setAiError: vi.fn(),
setIsLoading: vi.fn(),
setGameState,
setCurrentStory: setCurrentStory as (story: StoryMoment) => void,
buildFallbackStoryForState: () => createStory('fallback'),
turnVisualMs: 1,
});
expect(setGameState).toHaveBeenCalledWith(
expect.objectContaining({
playerHp: 0,
animationState: 'die',
inBattle: false,
}),
);
expect(setCurrentStory).toHaveBeenCalledWith(
expect.objectContaining({
text: expect.stringContaining('重新醒来'),
}),
);
expect(setCurrentStory).not.toHaveBeenCalledWith(
expect.objectContaining({
text: '不会进入胜利文本',
}),
);
});
it('commits server-returned next-scene state after idle_travel_next_scene resolution', async () => {
const gameState = createState({
currentScenePreset: {