1
This commit is contained in:
@@ -123,19 +123,12 @@ export function buildPreparedOpeningAdventure({
|
||||
|
||||
export async function playOpeningAdventureSequence({
|
||||
gameState,
|
||||
character,
|
||||
encounter,
|
||||
preparedStory,
|
||||
setGameState,
|
||||
setCurrentStory,
|
||||
setAiError,
|
||||
setIsLoading,
|
||||
buildDialogueStoryMoment,
|
||||
buildStoryContextFromState,
|
||||
getStoryGenerationHostileNpcs,
|
||||
hasRenderableDialogueTurns,
|
||||
inferOpeningCampFollowupOptions,
|
||||
getTypewriterDelay,
|
||||
}: {
|
||||
gameState: GameState;
|
||||
character: Character;
|
||||
@@ -168,160 +161,69 @@ export async function playOpeningAdventureSequence({
|
||||
) => Promise<StoryOption[]>;
|
||||
getTypewriterDelay: (char: string) => number;
|
||||
}) {
|
||||
const {
|
||||
fallbackText,
|
||||
openingOptions,
|
||||
resultText: openingBackground,
|
||||
} = preparedStory;
|
||||
const actionText = `在营地与 ${encounter.npcName} 交换开场判断`;
|
||||
const { fallbackText, openingOptions } = preparedStory;
|
||||
const campScene = gameState.worldType
|
||||
? getWorldCampScenePreset(gameState.worldType)
|
||||
: null;
|
||||
const entryState: GameState = {
|
||||
...gameState,
|
||||
currentScenePreset: campScene ?? gameState.currentScenePreset,
|
||||
currentEncounter: {
|
||||
...encounter,
|
||||
xMeters: encounter.xMeters ?? CALL_OUT_ENTRY_X_METERS,
|
||||
},
|
||||
};
|
||||
const resolvedEncounter: Encounter = {
|
||||
const storyEncounter: Encounter = {
|
||||
...encounter,
|
||||
xMeters: RESOLVED_ENTITY_X_METERS,
|
||||
};
|
||||
const storyEncounter: Encounter = {
|
||||
...resolvedEncounter,
|
||||
specialBehavior: 'camp_companion',
|
||||
};
|
||||
const resolvedState: GameState = {
|
||||
...gameState,
|
||||
currentScenePreset: campScene ?? gameState.currentScenePreset,
|
||||
currentEncounter: resolvedEncounter,
|
||||
npcInteractionActive: false,
|
||||
currentEncounter: storyEncounter,
|
||||
npcInteractionActive: true,
|
||||
};
|
||||
|
||||
setGameState(entryState);
|
||||
setAiError(null);
|
||||
setIsLoading(true);
|
||||
setIsLoading(false);
|
||||
|
||||
try {
|
||||
if (hasEncounterEntity(resolvedState)) {
|
||||
const runTicks = Math.max(
|
||||
1,
|
||||
Math.ceil(ENCOUNTER_ENTRY_DURATION_MS / ENCOUNTER_ENTRY_TICK_MS),
|
||||
);
|
||||
const tickDurationMs = Math.max(
|
||||
1,
|
||||
Math.round(ENCOUNTER_ENTRY_DURATION_MS / runTicks),
|
||||
);
|
||||
|
||||
for (let tick = 1; tick <= runTicks; tick += 1) {
|
||||
const progress = tick / runTicks;
|
||||
setGameState(
|
||||
interpolateEncounterTransitionState(
|
||||
entryState,
|
||||
resolvedState,
|
||||
progress,
|
||||
),
|
||||
);
|
||||
await new Promise((resolve) =>
|
||||
window.setTimeout(resolve, tickDurationMs),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const storyState: GameState = {
|
||||
...resolvedState,
|
||||
currentEncounter: storyEncounter,
|
||||
npcInteractionActive: false,
|
||||
};
|
||||
|
||||
setGameState(storyState);
|
||||
setCurrentStory(buildDialogueStoryMoment(encounter.npcName, '', [], true));
|
||||
|
||||
let openingText = fallbackText;
|
||||
let resolvedOpeningOptions = sortStoryOptionsByPriority(openingOptions);
|
||||
|
||||
try {
|
||||
const response = await generateNextStep(
|
||||
gameState.worldType!,
|
||||
character,
|
||||
getStoryGenerationHostileNpcs(storyState),
|
||||
gameState.storyHistory,
|
||||
actionText,
|
||||
buildStoryContextFromState(storyState, {
|
||||
lastFunctionId: OPENING_CAMP_DIALOGUE_FUNCTION_ID,
|
||||
}),
|
||||
setGameState(resolvedState);
|
||||
setCurrentStory({
|
||||
text: fallbackText,
|
||||
options: sortStoryOptionsByPriority(openingOptions),
|
||||
displayMode: 'dialogue',
|
||||
dialogue: [
|
||||
{
|
||||
availableOptions: openingOptions,
|
||||
speaker: 'npc',
|
||||
speakerName: encounter.npcName,
|
||||
text: fallbackText,
|
||||
},
|
||||
);
|
||||
|
||||
const generatedText = response.storyText.trim();
|
||||
if (
|
||||
generatedText &&
|
||||
hasRenderableDialogueTurns(generatedText, encounter.npcName)
|
||||
) {
|
||||
openingText = generatedText;
|
||||
}
|
||||
if (response.options.length > 0) {
|
||||
resolvedOpeningOptions = sortStoryOptionsByPriority(response.options);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to infer opening camp dialogue:', error);
|
||||
setAiError(error instanceof Error ? error.message : '未知智能生成错误');
|
||||
}
|
||||
|
||||
const finalHistory = [
|
||||
...gameState.storyHistory,
|
||||
createHistoryMoment(actionText, 'action'),
|
||||
createHistoryMoment(openingText, 'result', openingOptions),
|
||||
];
|
||||
const finalState: GameState = {
|
||||
...storyState,
|
||||
storyHistory: finalHistory,
|
||||
};
|
||||
|
||||
setGameState(finalState);
|
||||
const openingOptionsPromise = inferOpeningCampFollowupOptions(
|
||||
finalState,
|
||||
character,
|
||||
resolvedOpeningOptions,
|
||||
openingBackground,
|
||||
openingText,
|
||||
);
|
||||
|
||||
let displayedText = '';
|
||||
for (const nextChar of openingText) {
|
||||
displayedText += nextChar;
|
||||
setCurrentStory(
|
||||
buildDialogueStoryMoment(encounter.npcName, displayedText, [], true),
|
||||
);
|
||||
await new Promise((resolve) =>
|
||||
window.setTimeout(resolve, getTypewriterDelay(nextChar)),
|
||||
);
|
||||
}
|
||||
|
||||
const finalOpeningOptions = await openingOptionsPromise;
|
||||
setCurrentStory(
|
||||
buildDialogueStoryMoment(
|
||||
encounter.npcName,
|
||||
openingText,
|
||||
finalOpeningOptions,
|
||||
false,
|
||||
),
|
||||
);
|
||||
],
|
||||
streaming: false,
|
||||
npcChatState: {
|
||||
npcId: storyEncounter.id ?? storyEncounter.npcName,
|
||||
npcName: storyEncounter.npcName,
|
||||
turnCount: 0,
|
||||
customInputPlaceholder: '输入你想对 TA 说的话',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to play opening adventure sequence:', error);
|
||||
setAiError(error instanceof Error ? error.message : '未知智能生成错误');
|
||||
setCurrentStory(
|
||||
buildDialogueStoryMoment(
|
||||
encounter.npcName,
|
||||
fallbackText,
|
||||
openingOptions,
|
||||
false,
|
||||
),
|
||||
);
|
||||
setGameState(resolvedState);
|
||||
setCurrentStory({
|
||||
text: fallbackText,
|
||||
options: sortStoryOptionsByPriority(openingOptions),
|
||||
displayMode: 'dialogue',
|
||||
dialogue: [
|
||||
{
|
||||
speaker: 'npc',
|
||||
speakerName: encounter.npcName,
|
||||
text: fallbackText,
|
||||
},
|
||||
],
|
||||
streaming: false,
|
||||
npcChatState: {
|
||||
npcId: storyEncounter.id ?? storyEncounter.npcName,
|
||||
npcName: storyEncounter.npcName,
|
||||
turnCount: 0,
|
||||
customInputPlaceholder: '输入你想对 TA 说的话',
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user