This commit is contained in:
2026-04-27 22:50:18 +08:00
parent ded6f6ee2a
commit b6c6640548
77 changed files with 5240 additions and 833 deletions

View File

@@ -77,6 +77,39 @@ type IncrementRuntimeStats = (
increments: RuntimeStatsIncrements,
) => GameState;
function isImmediateCombatChoice(option: StoryOption) {
return (
option.functionId.startsWith('battle_') ||
option.functionId === 'inventory_use'
);
}
function shouldResolveCombatChoiceLocally(
gameState: GameState,
currentStory: StoryMoment | null,
option: StoryOption,
) {
if (!isImmediateCombatChoice(option)) {
return false;
}
if (gameState.inBattle) {
return true;
}
const hasBattleMarkers =
Boolean(gameState.currentBattleNpcId || gameState.currentNpcBattleMode) ||
gameState.sceneHostileNpcs.some((hostileNpc) => hostileNpc.hp > 0);
const storyStillShowsBattleChoices = Boolean(
currentStory?.options.some(isImmediateCombatChoice),
);
// 中文注释:真实运行态里可能短暂出现“可见层仍在战斗,但逻辑态 inBattle
// 已经被提前切回 false”的窗口。如果这时玩家点击了还在面板上的 battle_* /
// inventory_use 选项,必须继续走本地逐帧战斗链,不能误分流到服务端直结算。
return hasBattleMarkers || storyStillShowsBattleChoices;
}
export function createStoryChoiceActions({
gameState,
currentStory,
@@ -219,7 +252,10 @@ export function createStoryChoiceActions({
return;
}
if (isRpgRuntimeServerFunctionId(option.functionId)) {
if (
isRpgRuntimeServerFunctionId(option.functionId) &&
!shouldResolveCombatChoiceLocally(gameState, currentStory, option)
) {
await runServerRuntimeChoiceAction({
gameState,
currentStory,