1
This commit is contained in:
@@ -72,7 +72,9 @@ function buildBackstoryReveal(label: string) {
|
||||
};
|
||||
}
|
||||
|
||||
function buildSavedProfile() {
|
||||
function buildSavedProfile(options: {
|
||||
openingOppositeNpcId?: string;
|
||||
} = {}) {
|
||||
const profile = normalizeCustomWorldProfileRecord({
|
||||
id: 'saved-runtime-profile',
|
||||
settingText: '被海雾吞没的旧航路群岛',
|
||||
@@ -318,9 +320,9 @@ function buildSavedProfile() {
|
||||
title: '第一幕',
|
||||
summary: '陆衡先开口试探玩家。',
|
||||
stageCoverage: ['opening'],
|
||||
encounterNpcIds: ['story-primary-only', 'story-act-only'],
|
||||
primaryNpcId: 'story-primary-only',
|
||||
oppositeNpcId: 'story-act-only',
|
||||
encounterNpcIds: ['沈砺旧识', '陆衡'],
|
||||
primaryNpcId: '沈砺旧识',
|
||||
oppositeNpcId: options.openingOppositeNpcId ?? '陆衡',
|
||||
eventDescription: '陆衡拿着异常账本,在开盘前拦住玩家。',
|
||||
linkedThreadIds: [],
|
||||
advanceRule: 'after_primary_contact',
|
||||
@@ -389,6 +391,8 @@ function readSnapshot() {
|
||||
isStoryLoading: boolean;
|
||||
firstLandmarkResidueTitle: string | null;
|
||||
playerCharacterName: string | null;
|
||||
runtimeMode: string | null;
|
||||
runtimePersistenceDisabled: boolean;
|
||||
playerInventoryNames: string[];
|
||||
playerEquipment: {
|
||||
weapon: string | null;
|
||||
@@ -398,8 +402,15 @@ function readSnapshot() {
|
||||
};
|
||||
}
|
||||
|
||||
function GameFlowHarness() {
|
||||
const profile = useMemo(() => buildSavedProfile(), []);
|
||||
function GameFlowHarness({
|
||||
openingOppositeNpcId,
|
||||
}: {
|
||||
openingOppositeNpcId?: string;
|
||||
} = {}) {
|
||||
const profile = useMemo(
|
||||
() => buildSavedProfile({ openingOppositeNpcId }),
|
||||
[openingOppositeNpcId],
|
||||
);
|
||||
const playableCharacters = useMemo(
|
||||
() => buildCustomWorldPlayableCharacters(profile),
|
||||
[profile],
|
||||
@@ -441,6 +452,8 @@ function GameFlowHarness() {
|
||||
gameState.customWorldProfile?.landmarks[0]?.narrativeResidues?.[0]
|
||||
?.title ?? null,
|
||||
playerCharacterName: gameState.playerCharacter?.name ?? null,
|
||||
runtimeMode: gameState.runtimeMode ?? null,
|
||||
runtimePersistenceDisabled: gameState.runtimePersistenceDisabled === true,
|
||||
playerInventoryNames: gameState.playerInventory.map((item) => item.name),
|
||||
playerEquipment: {
|
||||
weapon: gameState.playerEquipment.weapon?.name ?? null,
|
||||
@@ -515,6 +528,8 @@ test('saved custom world result settings flow into game state after entering the
|
||||
});
|
||||
|
||||
expect(readSnapshot().playerCharacterName).toBe('沈砺');
|
||||
expect(readSnapshot().runtimeMode).toBe('test');
|
||||
expect(readSnapshot().runtimePersistenceDisabled).toBe(true);
|
||||
expect(readSnapshot().playerInventoryNames).toContain('旧潮短刃');
|
||||
expect(readSnapshot().playerInventoryNames).toContain('旧潮图残页');
|
||||
expect(readSnapshot().playerEquipment.weapon).toBe('旧潮短刃');
|
||||
@@ -547,3 +562,38 @@ test('saved custom world result settings flow into game state after entering the
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test('custom world opening act accepts runtime npc id references and still starts configured npc chat', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<GameFlowHarness openingOppositeNpcId="character-npc-story-act-only" />);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '选择世界' }));
|
||||
await waitFor(() => {
|
||||
expect(readSnapshot().worldType).toBe(WorldType.CUSTOM);
|
||||
});
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '确认角色' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(readSnapshot().currentEncounterId).toBe('story-act-only');
|
||||
});
|
||||
expect(readSnapshot().currentEncounterName).toBe('陆衡');
|
||||
await waitFor(() => {
|
||||
expect(readSnapshot().currentStoryNpcName).toBe('陆衡');
|
||||
});
|
||||
expect(aiServiceMocks.streamNpcChatTurn).toHaveBeenCalledWith(
|
||||
WorldType.CUSTOM,
|
||||
expect.objectContaining({ name: '沈砺' }),
|
||||
expect.objectContaining({ id: 'story-act-only', npcName: '陆衡' }),
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
[],
|
||||
'',
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
npcInitiatesConversation: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user