Integrate role asset studio into custom world agent flow
This commit is contained in:
157
src/data/functionCatalog/functionCatalog.test.ts
Normal file
157
src/data/functionCatalog/functionCatalog.test.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
|
||||
import { SERVER_RUNTIME_FUNCTION_IDS } from '../../../packages/shared/src/contracts/story';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
ALL_FUNCTION_DOCUMENTATION,
|
||||
buildCampTravelHomeOption,
|
||||
buildContinueAdventureOption,
|
||||
buildNpcGiftModalState,
|
||||
buildNpcPreviewTalkOption,
|
||||
buildNpcRecruitModalState,
|
||||
buildNpcTradeModalState,
|
||||
CONTINUE_ADVENTURE_FUNCTION,
|
||||
getFunctionDocumentationById,
|
||||
isNpcPreviewTalkOption,
|
||||
NPC_PREVIEW_TALK_FUNCTION,
|
||||
shouldNpcRecruitOpenModal,
|
||||
} from './index';
|
||||
import type { Encounter, GameState, InventoryItem } from '../../types';
|
||||
|
||||
function createEncounter(overrides: Partial<Encounter> = {}): Encounter {
|
||||
return {
|
||||
id: 'npc-trader',
|
||||
kind: 'npc',
|
||||
npcName: '梁伯',
|
||||
npcDescription: '沿路摆摊的商人。',
|
||||
npcAvatar: '梁',
|
||||
context: '商贩',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function createInventoryItem(
|
||||
id: string,
|
||||
name: string,
|
||||
overrides: Partial<InventoryItem> = {},
|
||||
): InventoryItem {
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
description: `${name} 的测试描述`,
|
||||
quantity: 1,
|
||||
category: 'misc',
|
||||
rarity: 'common',
|
||||
tags: [],
|
||||
value: 1,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function createModalState(overrides: Partial<GameState> = {}): GameState {
|
||||
return {
|
||||
playerInventory: [
|
||||
createInventoryItem('player-potion', '疗伤药'),
|
||||
createInventoryItem('player-charm', '护符'),
|
||||
],
|
||||
companions: [
|
||||
{
|
||||
npcId: 'npc-ally-1',
|
||||
characterId: 'ally-1',
|
||||
name: '阿青',
|
||||
role: '同伴',
|
||||
joinedAtAffinity: 12,
|
||||
},
|
||||
],
|
||||
...overrides,
|
||||
} as GameState;
|
||||
}
|
||||
|
||||
describe('functionCatalog', () => {
|
||||
it('keeps function documentation ids unique and source files resolvable', () => {
|
||||
const documentationIds = ALL_FUNCTION_DOCUMENTATION.map((entry) => entry.id);
|
||||
|
||||
expect(new Set(documentationIds).size).toBe(documentationIds.length);
|
||||
ALL_FUNCTION_DOCUMENTATION.forEach((entry) => {
|
||||
expect(existsSync(entry.source), `${entry.id} -> ${entry.source}`).toBe(
|
||||
true,
|
||||
);
|
||||
expect(getFunctionDocumentationById(entry.id)).toEqual(entry);
|
||||
});
|
||||
});
|
||||
|
||||
it('covers every server runtime function id with documentation metadata', () => {
|
||||
SERVER_RUNTIME_FUNCTION_IDS.forEach((functionId) => {
|
||||
expect(getFunctionDocumentationById(functionId)).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('builds flow helper options with the expected function ids', () => {
|
||||
const continueOption = buildContinueAdventureOption();
|
||||
const campTravelOption = buildCampTravelHomeOption('竹林古道');
|
||||
|
||||
expect(continueOption.functionId).toBe(CONTINUE_ADVENTURE_FUNCTION.id);
|
||||
expect(continueOption.priority).toBe(99);
|
||||
expect(campTravelOption.functionId).toBe('camp_travel_home_scene');
|
||||
expect(campTravelOption.actionText).toBe('前往 竹林古道');
|
||||
expect(campTravelOption.detailText).toBe('离开营地,前往 竹林古道。');
|
||||
});
|
||||
|
||||
it('builds npc preview talk options from the current encounter', () => {
|
||||
const option = buildNpcPreviewTalkOption(createEncounter());
|
||||
|
||||
expect(option.functionId).toBe(NPC_PREVIEW_TALK_FUNCTION.id);
|
||||
expect(option.actionText).toBe('与 梁伯 交谈');
|
||||
expect(isNpcPreviewTalkOption(option)).toBe(true);
|
||||
});
|
||||
|
||||
it('builds modal helper state for trade, gift and recruit flows', () => {
|
||||
const state = createModalState();
|
||||
const encounter = createEncounter();
|
||||
const tradeModal = buildNpcTradeModalState(
|
||||
state,
|
||||
encounter,
|
||||
'先看看货',
|
||||
[
|
||||
createInventoryItem('npc-herb', '止血草'),
|
||||
createInventoryItem('npc-ore', '陨铁碎片'),
|
||||
],
|
||||
);
|
||||
const giftModal = buildNpcGiftModalState(
|
||||
state,
|
||||
encounter,
|
||||
'送你一样东西',
|
||||
'player-charm',
|
||||
);
|
||||
const recruitModal = buildNpcRecruitModalState(
|
||||
state,
|
||||
encounter,
|
||||
'谈谈同行的事',
|
||||
);
|
||||
|
||||
expect(tradeModal.selectedNpcItemId).toBe('npc-herb');
|
||||
expect(tradeModal.selectedPlayerItemId).toBe('player-potion');
|
||||
expect(giftModal.selectedItemId).toBe('player-charm');
|
||||
expect(recruitModal.selectedReleaseNpcId).toBe('npc-ally-1');
|
||||
expect(shouldNpcRecruitOpenModal(2, 2)).toBe(true);
|
||||
expect(shouldNpcRecruitOpenModal(1, 2)).toBe(false);
|
||||
});
|
||||
|
||||
it('prefers the first tradable player item when zero-quantity items exist', () => {
|
||||
const encounter = createEncounter();
|
||||
const tradeModal = buildNpcTradeModalState(
|
||||
createModalState({
|
||||
playerInventory: [
|
||||
createInventoryItem('empty-slot', '空槽位', { quantity: 0 }),
|
||||
createInventoryItem('usable-item', '可售草药', { quantity: 2 }),
|
||||
],
|
||||
}),
|
||||
encounter,
|
||||
'交易',
|
||||
[createInventoryItem('npc-herb', '止血草')],
|
||||
);
|
||||
|
||||
expect(tradeModal.selectedPlayerItemId).toBe('usable-item');
|
||||
});
|
||||
});
|
||||
@@ -21,13 +21,18 @@ export function buildNpcTradeModalState(
|
||||
actionText: string,
|
||||
npcInventory: InventoryItem[],
|
||||
): TradeModalState {
|
||||
const selectedNpcItemId =
|
||||
npcInventory.find((item) => item.quantity > 0)?.id ?? null;
|
||||
const selectedPlayerItemId =
|
||||
state.playerInventory.find((item) => item.quantity > 0)?.id ?? null;
|
||||
|
||||
return {
|
||||
encounter,
|
||||
actionText,
|
||||
introText: buildNpcTradeModalIntroText(encounter),
|
||||
mode: 'buy',
|
||||
selectedNpcItemId: npcInventory[0]?.id ?? null,
|
||||
selectedPlayerItemId: state.playerInventory[0]?.id ?? null,
|
||||
selectedNpcItemId,
|
||||
selectedPlayerItemId,
|
||||
selectedQuantity: 1,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user