135 lines
3.8 KiB
TypeScript
135 lines
3.8 KiB
TypeScript
import {
|
|
getForwardScenePreset,
|
|
getScenePresetById,
|
|
getScenePresetsByWorld,
|
|
getTravelScenePreset,
|
|
} from '../../data/scenePresets';
|
|
import { getFunctionEffect } from '../../data/stateFunctions';
|
|
import type {
|
|
Character,
|
|
GameState,
|
|
StoryOption,
|
|
WorldType,
|
|
} from '../../types';
|
|
import { classifyCombatOption } from '../combatStoryUtils';
|
|
import { buildIdleAfterSequence } from '../idleAdventureFlow';
|
|
import {
|
|
applyRecoveryEffectToState,
|
|
type BattlePlan,
|
|
} from './battlePlan';
|
|
import { buildEscapeAfterSequence } from './escapeFlow';
|
|
|
|
type OptionKind = 'battle' | 'escape' | 'idle';
|
|
|
|
export type ResolvedChoiceState = {
|
|
optionKind: OptionKind;
|
|
battlePlan: BattlePlan | null;
|
|
afterSequence: GameState;
|
|
};
|
|
|
|
function getShiftedScenePreset(
|
|
worldType: WorldType | null,
|
|
currentScenePreset: GameState['currentScenePreset'],
|
|
sceneShift: number | undefined,
|
|
): GameState['currentScenePreset'] {
|
|
if (!worldType || sceneShift === undefined || sceneShift === 0) return currentScenePreset;
|
|
if (!currentScenePreset) return getScenePresetsByWorld(worldType)[0] ?? null;
|
|
|
|
if (sceneShift > 0) {
|
|
let nextScene = currentScenePreset;
|
|
for (let index = 0; index < sceneShift; index += 1) {
|
|
nextScene = getForwardScenePreset(worldType, nextScene.id) ?? nextScene;
|
|
}
|
|
return nextScene;
|
|
}
|
|
|
|
const scenes = getScenePresetsByWorld(worldType);
|
|
if (scenes.length === 0) return currentScenePreset;
|
|
const currentIndex = scenes.findIndex(scene => scene.id === currentScenePreset.id);
|
|
const safeIndex = currentIndex >= 0 ? currentIndex : 0;
|
|
const nextIndex = ((safeIndex + sceneShift) % scenes.length + scenes.length) % scenes.length;
|
|
return scenes[nextIndex] ?? null;
|
|
}
|
|
|
|
function getSceneTargetForFunction(
|
|
worldType: WorldType | null,
|
|
currentScenePreset: GameState['currentScenePreset'],
|
|
option: StoryOption,
|
|
): GameState['currentScenePreset'] {
|
|
if (!worldType) return currentScenePreset;
|
|
|
|
const targetSceneId =
|
|
typeof option.runtimePayload?.targetSceneId === 'string'
|
|
? option.runtimePayload.targetSceneId
|
|
: typeof option.runtimePayload?.escapeTargetSceneId === 'string'
|
|
? option.runtimePayload.escapeTargetSceneId
|
|
: null;
|
|
if (targetSceneId) {
|
|
return getScenePresetById(worldType, targetSceneId) ?? currentScenePreset;
|
|
}
|
|
|
|
if (option.functionId === 'idle_travel_next_scene') {
|
|
return getTravelScenePreset(worldType, currentScenePreset?.id) ?? currentScenePreset;
|
|
}
|
|
|
|
return getShiftedScenePreset(
|
|
worldType,
|
|
currentScenePreset,
|
|
getFunctionEffect(option.functionId).sceneShift,
|
|
);
|
|
}
|
|
|
|
export function buildResolvedChoiceState(params: {
|
|
state: GameState;
|
|
option: StoryOption;
|
|
character: Character;
|
|
buildBattlePlan: (state: GameState, option: StoryOption, character: Character) => BattlePlan;
|
|
}) {
|
|
const {
|
|
state,
|
|
option,
|
|
character,
|
|
buildBattlePlan,
|
|
} = params;
|
|
const nextScenePreset = getSceneTargetForFunction(
|
|
state.worldType,
|
|
state.currentScenePreset,
|
|
option,
|
|
);
|
|
const optionKind = classifyCombatOption(option);
|
|
|
|
if (optionKind === 'battle') {
|
|
const battlePlan = buildBattlePlan(state, option, character);
|
|
const battleResult: GameState = {
|
|
...battlePlan.finalState,
|
|
currentScenePreset: nextScenePreset ?? battlePlan.finalState.currentScenePreset,
|
|
};
|
|
|
|
return {
|
|
optionKind,
|
|
battlePlan,
|
|
afterSequence: battleResult,
|
|
} satisfies ResolvedChoiceState;
|
|
}
|
|
|
|
if (optionKind === 'escape') {
|
|
return {
|
|
optionKind,
|
|
battlePlan: null,
|
|
afterSequence: buildEscapeAfterSequence(state, option, nextScenePreset),
|
|
} satisfies ResolvedChoiceState;
|
|
}
|
|
|
|
return {
|
|
optionKind,
|
|
battlePlan: null,
|
|
afterSequence: buildIdleAfterSequence({
|
|
state,
|
|
option,
|
|
character,
|
|
nextScenePreset,
|
|
applyRecoveryEffectToState,
|
|
}),
|
|
} satisfies ResolvedChoiceState;
|
|
}
|