Refine NPC interactions and runtime item generation

This commit is contained in:
2026-04-05 17:13:07 +08:00
parent c49c64896a
commit 89cecda7da
58 changed files with 4199 additions and 1562 deletions

View File

@@ -94,7 +94,7 @@ const TURN_VISUAL_MS = 820;
const OPENING_CAMP_DIALOGUE_FUNCTION_ID =
STORY_OPENING_CAMP_DIALOGUE_FUNCTION.id;
const NPC_PREVIEW_TALK_FUNCTION_ID = NPC_PREVIEW_TALK_FUNCTION.id;
const FALLBACK_COMPANION_NAME = '鍚屼即';
const FALLBACK_COMPANION_NAME = '同伴';
export type {
CharacterChatModalState,
@@ -152,9 +152,9 @@ function _buildLocalCharacterChatSummary(
function _buildLocalCharacterChatSuggestions(character: Character) {
return [
'I want to hear you explain that a little more clearly.',
`${character.name}, what are you really worried about?`,
'Let the wider situation wait for a moment. I want to understand you a bit more.',
'我想听你把这件事再说得更明白一点。',
`${character.name},你现在真正担心的是什么?`,
'先把外面的局势放一放,我想更了解你一些。',
];
}
@@ -175,10 +175,10 @@ function buildPartyRelationshipNotes(state: GameState) {
};
state.companions.forEach((companion) =>
appendNote(companion.characterId, '褰撳墠鍚岃'),
appendNote(companion.characterId, '当前同行'),
);
state.roster.forEach((companion) =>
appendNote(companion.characterId, '钀ュ湴寰呭懡'),
appendNote(companion.characterId, '营地待命'),
);
return lines.length > 0 ? lines.join('\n') : null;
@@ -190,12 +190,12 @@ function buildRecentConversationEventText(state: GameState) {
.map((item) => item.text)
.join('\n');
if (
/|||||/u.test(recentText)
/|||||/u.test(recentText)
) {
return 'You just went through a clash or spar, and the tension has not fully faded yet.';
return '你们刚经历过一场交锋或切磋,空气里的紧张感还没有完全散去。';
}
if (/|稿||/u.test(recentText)) {
return 'You have just cooperated in practice, so the atmosphere is a little less distant now.';
if (/|||/u.test(recentText)) {
return '你们刚并肩配合过一次,彼此之间的距离感稍微淡了一些。';
}
return null;
}
@@ -221,7 +221,7 @@ function inferConversationSituation(
.map((item) => item.text)
.join('\n');
if (
/|||||/u.test(recentText)
/|||||/u.test(recentText)
) {
return 'post_battle_breath' as const;
}
@@ -307,7 +307,7 @@ function buildStoryContextFromState(
const encounter = state.currentEncounter;
return extras.encounterNpcStateOverride
?? state.npcStates[getNpcEncounterKey(encounter)]
?? buildInitialNpcState(encounter, state.worldType);
?? buildInitialNpcState(encounter, state.worldType, state);
})()
: null;
const encounterDirective =
@@ -659,8 +659,8 @@ function hasRenderableDialogueTurns(text: string, npcName: string) {
}
function getTypewriterDelay(char: string) {
if (/[??]/u.test(char)) return 240;
if (/[?;:]/u.test(char)) return 150;
if (/[!?]/u.test(char)) return 240;
if (/[;:]/u.test(char)) return 150;
if (/\s/u.test(char)) return 45;
return 90;
}
@@ -831,7 +831,7 @@ export function useStoryGeneration({
const getResolvedNpcState = (state: GameState, encounter: Encounter) =>
state.npcStates[getNpcEncounterKey(encounter)] ??
buildInitialNpcState(encounter, state.worldType);
buildInitialNpcState(encounter, state.worldType, state);
const buildNpcStory = useCallback(
(
@@ -841,6 +841,7 @@ export function useStoryGeneration({
overrideText?: string,
) =>
buildNpcEncounterStoryMoment({
state,
encounter,
npcState: getResolvedNpcState(state, encounter),
playerCharacter: character,
@@ -960,7 +961,7 @@ export function useStoryGeneration({
const npcState =
state.npcStates[getNpcEncounterKey(encounter)] ??
buildInitialNpcState(encounter, state.worldType);
buildInitialNpcState(encounter, state.worldType, state);
if (npcState.chattedCount > 2) {
return {};
}
@@ -1484,7 +1485,7 @@ export function useStoryGeneration({
setCurrentStory(nextStory);
} catch (error) {
console.error('Failed to start story:', error);
setAiError(error instanceof Error ? error.message : '未知 AI 错误');
setAiError(error instanceof Error ? error.message : '未知智能生成错误');
setCurrentStory(
buildFallbackStoryForState(gameState, gameState.playerCharacter),
);