1
This commit is contained in:
@@ -1,86 +1,9 @@
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
import {
|
||||
acceptQuest,
|
||||
buildChapterQuestForScene,
|
||||
getChapterQuestForScene,
|
||||
} from '../../data/questFlow';
|
||||
import { resolveSceneChapterBlueprint } from '../../services/customWorldSceneActRuntime';
|
||||
import {
|
||||
hasEncounterEntity,
|
||||
interpolateEncounterTransitionState,
|
||||
} from '../../data/encounterTransition';
|
||||
import { applyStoryReasoningRecovery } from '../../data/storyRecovery';
|
||||
import {
|
||||
buildFallbackActorNarrativeProfile,
|
||||
normalizeActorNarrativeProfile,
|
||||
} from '../../services/storyEngine/actorNarrativeProfile';
|
||||
import { resolveCurrentActState } from '../../services/storyEngine/actPlanner';
|
||||
import { buildAuthorialConstraintPack } from '../../services/storyEngine/authorialConstraintPack';
|
||||
import { evaluateBranchBudget } from '../../services/storyEngine/branchBudgetPlanner';
|
||||
import {
|
||||
advanceCampaignState,
|
||||
resolveCampaignState,
|
||||
} from '../../services/storyEngine/campaignDirector';
|
||||
import { compileCampaignFromWorldProfile } from '../../services/storyEngine/campaignPackCompiler';
|
||||
import {
|
||||
buildCampEvent,
|
||||
evaluateCampEventOpportunity,
|
||||
} from '../../services/storyEngine/campEventDirector';
|
||||
import {
|
||||
advanceChapterState,
|
||||
resolveCurrentChapterState,
|
||||
} from '../../services/storyEngine/chapterDirector';
|
||||
import {
|
||||
advanceCompanionArc,
|
||||
buildCompanionArcStates,
|
||||
} from '../../services/storyEngine/companionArcDirector';
|
||||
import {
|
||||
applyCompanionReactionToStance,
|
||||
buildCompanionReactionBatch,
|
||||
} from '../../services/storyEngine/companionReactionDirector';
|
||||
import { resolveAllCompanionResolutions } from '../../services/storyEngine/companionResolutionDirector';
|
||||
import { appendConsequenceRecord } from '../../services/storyEngine/consequenceLedger';
|
||||
import { buildContentDiffReport } from '../../services/storyEngine/contentDiffReport';
|
||||
import { resolveEndingState } from '../../services/storyEngine/endingResolver';
|
||||
import { buildEpilogueSummary } from '../../services/storyEngine/epilogueComposer';
|
||||
import { buildFactionTensionState } from '../../services/storyEngine/factionTensionState';
|
||||
import { resolveCurrentJourneyBeat } from '../../services/storyEngine/journeyBeatPlanner';
|
||||
import { buildNarrativeCodex } from '../../services/storyEngine/narrativeCodex';
|
||||
import { runNarrativeConsistencyChecks } from '../../services/storyEngine/narrativeConsistencyChecks';
|
||||
import { buildNarrativeQaReport } from '../../services/storyEngine/narrativeQaReport';
|
||||
import {
|
||||
recordReplaySeed,
|
||||
replayNarrativeRun,
|
||||
} from '../../services/storyEngine/narrativeRegressionReplay';
|
||||
import { captureNarrativeTelemetry } from '../../services/storyEngine/narrativeTelemetry';
|
||||
import { updatePlayerStyleProfileFromAction } from '../../services/storyEngine/playerStyleProfiler';
|
||||
import { runPlaythroughMatrix } from '../../services/storyEngine/playthroughMatrixLab';
|
||||
import { buildContinueGameDigest } from '../../services/storyEngine/recapDigest';
|
||||
import { buildReleaseGateReport } from '../../services/storyEngine/releaseGateReport';
|
||||
import { buildSaveMigrationManifest } from '../../services/storyEngine/saveMigrationManifest';
|
||||
import { resolveScenarioPack } from '../../services/storyEngine/scenarioPackRegistry';
|
||||
import {
|
||||
buildSetpieceDirective,
|
||||
evaluateSetpieceOpportunity,
|
||||
} from '../../services/storyEngine/setpieceDirector';
|
||||
import { appendChronicleEntries } from '../../services/storyEngine/storyChronicle';
|
||||
import { buildThemePackFromWorldProfile } from '../../services/storyEngine/themePack';
|
||||
import { buildThreadContractsFromProfile } from '../../services/storyEngine/threadContract';
|
||||
import { buildInitialSceneActRuntimeState } from '../../services/customWorldSceneActRuntime';
|
||||
import {
|
||||
collectStorySignals,
|
||||
resolveSignalsToThreadUpdates,
|
||||
} from '../../services/storyEngine/threadSignalRouter';
|
||||
import {
|
||||
buildEncounterVisibilitySlice,
|
||||
createEmptyStoryEngineMemoryState,
|
||||
} from '../../services/storyEngine/visibilityEngine';
|
||||
import {
|
||||
applyWorldMutationsToGameState,
|
||||
resolveWorldMutations,
|
||||
} from '../../services/storyEngine/worldMutationRouter';
|
||||
import { buildFallbackWorldStoryGraph } from '../../services/storyEngine/worldStoryGraph';
|
||||
import { createHistoryMoment } from '../../services/storyHistory';
|
||||
import type {
|
||||
Character,
|
||||
@@ -93,516 +16,6 @@ import type { CommitGeneratedState } from '../generatedState';
|
||||
const ENCOUNTER_ENTRY_DURATION_MS = 1800;
|
||||
const ENCOUNTER_ENTRY_TICK_MS = 180;
|
||||
|
||||
function dedupeStrings(values: Array<string | null | undefined>, limit = 10) {
|
||||
return [
|
||||
...new Set(values.map((value) => value?.trim() ?? '').filter(Boolean)),
|
||||
].slice(0, limit);
|
||||
}
|
||||
|
||||
function hydrateStoryEngineMemory(state: GameState): GameState {
|
||||
const storyEngineMemory =
|
||||
state.storyEngineMemory ?? createEmptyStoryEngineMemoryState();
|
||||
if (!state.customWorldProfile || state.currentEncounter?.kind !== 'npc') {
|
||||
return {
|
||||
...state,
|
||||
storyEngineMemory,
|
||||
};
|
||||
}
|
||||
|
||||
const role =
|
||||
state.customWorldProfile.storyNpcs.find(
|
||||
(npc) =>
|
||||
npc.id === state.currentEncounter?.id ||
|
||||
npc.name === state.currentEncounter?.npcName,
|
||||
) ??
|
||||
state.customWorldProfile.playableNpcs.find(
|
||||
(npc) =>
|
||||
npc.id === state.currentEncounter?.id ||
|
||||
npc.name === state.currentEncounter?.npcName,
|
||||
);
|
||||
if (!role) {
|
||||
return {
|
||||
...state,
|
||||
storyEngineMemory,
|
||||
};
|
||||
}
|
||||
|
||||
const themePack =
|
||||
state.customWorldProfile.themePack ??
|
||||
buildThemePackFromWorldProfile(state.customWorldProfile);
|
||||
const storyGraph =
|
||||
state.customWorldProfile.storyGraph ??
|
||||
buildFallbackWorldStoryGraph(state.customWorldProfile, themePack);
|
||||
const narrativeProfile = normalizeActorNarrativeProfile(
|
||||
role.narrativeProfile,
|
||||
buildFallbackActorNarrativeProfile(role, storyGraph, themePack),
|
||||
);
|
||||
const npcState =
|
||||
state.npcStates[
|
||||
state.currentEncounter.id ?? state.currentEncounter.npcName
|
||||
];
|
||||
const activeThreadIds =
|
||||
storyEngineMemory.activeThreadIds.length > 0
|
||||
? storyEngineMemory.activeThreadIds
|
||||
: narrativeProfile.relatedThreadIds.slice(0, 4);
|
||||
const visibilitySlice = buildEncounterVisibilitySlice({
|
||||
narrativeProfile,
|
||||
backstoryReveal: state.currentEncounter.backstoryReveal ?? null,
|
||||
disclosureStage:
|
||||
npcState?.affinity != null
|
||||
? npcState.affinity < 15
|
||||
? 'guarded'
|
||||
: npcState.affinity < 45
|
||||
? 'partial'
|
||||
: npcState.affinity < 75
|
||||
? 'honest'
|
||||
: 'deep'
|
||||
: 'guarded',
|
||||
isFirstMeaningfulContact: npcState?.firstMeaningfulContactResolved !== true,
|
||||
seenBackstoryChapterIds: npcState?.seenBackstoryChapterIds ?? [],
|
||||
storyEngineMemory,
|
||||
activeThreadIds,
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
storyEngineMemory: {
|
||||
...storyEngineMemory,
|
||||
discoveredFactIds: dedupeStrings(
|
||||
[
|
||||
...storyEngineMemory.discoveredFactIds,
|
||||
...visibilitySlice.sayableFactIds,
|
||||
],
|
||||
16,
|
||||
),
|
||||
activeThreadIds: dedupeStrings(
|
||||
[...storyEngineMemory.activeThreadIds, ...activeThreadIds],
|
||||
6,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function findNewInventoryItems(previousState: GameState, nextState: GameState) {
|
||||
const previousIds = new Set(
|
||||
previousState.playerInventory.map((item) => item.id),
|
||||
);
|
||||
return nextState.playerInventory.filter((item) => !previousIds.has(item.id));
|
||||
}
|
||||
|
||||
function ensureSceneChapterQuestState(params: {
|
||||
previousState: GameState;
|
||||
nextState: GameState;
|
||||
}) {
|
||||
const storyEngineMemory =
|
||||
params.nextState.storyEngineMemory ?? createEmptyStoryEngineMemoryState();
|
||||
const scene = params.nextState.currentScenePreset;
|
||||
if (
|
||||
params.nextState.currentScene !== 'Story' ||
|
||||
!params.nextState.worldType ||
|
||||
!scene?.id
|
||||
) {
|
||||
return {
|
||||
...params.nextState,
|
||||
storyEngineMemory,
|
||||
};
|
||||
}
|
||||
|
||||
const openedSceneChapterIds = dedupeStrings(
|
||||
[...(storyEngineMemory.openedSceneChapterIds ?? [])],
|
||||
64,
|
||||
);
|
||||
if (openedSceneChapterIds.includes(scene.id)) {
|
||||
return {
|
||||
...params.nextState,
|
||||
storyEngineMemory: {
|
||||
...storyEngineMemory,
|
||||
openedSceneChapterIds,
|
||||
currentSceneActState:
|
||||
buildInitialSceneActRuntimeState({
|
||||
profile: params.nextState.customWorldProfile,
|
||||
sceneId: scene.id,
|
||||
storyEngineMemory,
|
||||
}) ?? storyEngineMemory.currentSceneActState ?? null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const nextMemory = {
|
||||
...storyEngineMemory,
|
||||
openedSceneChapterIds: [...openedSceneChapterIds, scene.id],
|
||||
currentSceneActState:
|
||||
buildInitialSceneActRuntimeState({
|
||||
profile: params.nextState.customWorldProfile,
|
||||
sceneId: scene.id,
|
||||
storyEngineMemory,
|
||||
}) ?? storyEngineMemory.currentSceneActState ?? null,
|
||||
};
|
||||
const existingChapterQuest = getChapterQuestForScene(
|
||||
params.nextState.quests,
|
||||
scene.id,
|
||||
);
|
||||
if (existingChapterQuest) {
|
||||
return {
|
||||
...params.nextState,
|
||||
storyEngineMemory: nextMemory,
|
||||
};
|
||||
}
|
||||
|
||||
const sceneChapter = resolveSceneChapterBlueprint(
|
||||
params.nextState.customWorldProfile,
|
||||
scene.id,
|
||||
);
|
||||
const sceneChapterContext = sceneChapter
|
||||
? {
|
||||
sceneTaskDescription: sceneChapter.sceneTaskDescription,
|
||||
actEventDescriptions: sceneChapter.acts
|
||||
.map((act) => act.eventDescription)
|
||||
.filter(Boolean),
|
||||
primaryNpcName:
|
||||
params.nextState.customWorldProfile?.storyNpcs.find(
|
||||
(npc) => npc.id === sceneChapter.acts[0]?.primaryNpcId,
|
||||
)?.name ?? sceneChapter.acts[0]?.primaryNpcId ?? null,
|
||||
}
|
||||
: null;
|
||||
|
||||
const chapterQuest = buildChapterQuestForScene({
|
||||
scene,
|
||||
worldType: params.nextState.worldType,
|
||||
sceneChapterContext,
|
||||
context: {
|
||||
worldType: params.nextState.worldType,
|
||||
actState: params.nextState.storyEngineMemory?.actState ?? null,
|
||||
recentStoryMoments: params.nextState.storyHistory.slice(-6),
|
||||
playerCharacter: params.nextState.playerCharacter,
|
||||
playerProgression: params.nextState.playerProgression ?? null,
|
||||
},
|
||||
});
|
||||
if (!chapterQuest) {
|
||||
return {
|
||||
...params.nextState,
|
||||
storyEngineMemory: nextMemory,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...params.nextState,
|
||||
storyEngineMemory: nextMemory,
|
||||
quests: acceptQuest(params.nextState.quests, chapterQuest),
|
||||
};
|
||||
}
|
||||
|
||||
function applyStoryEngineEchoes(params: {
|
||||
previousState: GameState;
|
||||
nextState: GameState;
|
||||
actionText: string;
|
||||
lastFunctionId?: string | null;
|
||||
}) {
|
||||
const hydratedState = hydrateStoryEngineMemory(params.nextState);
|
||||
const contracts = hydratedState.customWorldProfile
|
||||
? (hydratedState.customWorldProfile.threadContracts ??
|
||||
buildThreadContractsFromProfile(hydratedState.customWorldProfile))
|
||||
: [];
|
||||
const newItems = findNewInventoryItems(params.previousState, hydratedState);
|
||||
const signals = collectStorySignals({
|
||||
prevState: params.previousState,
|
||||
nextState: hydratedState,
|
||||
actionText: params.actionText,
|
||||
lastFunctionId: params.lastFunctionId,
|
||||
rewardItems: newItems,
|
||||
});
|
||||
const stateWithSignals = resolveSignalsToThreadUpdates({
|
||||
state: hydratedState,
|
||||
signals,
|
||||
contracts,
|
||||
});
|
||||
const stateWithSceneChapter = ensureSceneChapterQuestState({
|
||||
previousState: params.previousState,
|
||||
nextState: stateWithSignals,
|
||||
});
|
||||
const reactions = buildCompanionReactionBatch({
|
||||
state: stateWithSceneChapter,
|
||||
signals,
|
||||
actionText: params.actionText,
|
||||
});
|
||||
const stateWithReactions = applyCompanionReactionToStance({
|
||||
state: stateWithSceneChapter,
|
||||
reactions,
|
||||
});
|
||||
const storyEngineMemory =
|
||||
stateWithReactions.storyEngineMemory ?? createEmptyStoryEngineMemoryState();
|
||||
const chapterState = advanceChapterState({
|
||||
previousChapter:
|
||||
stateWithReactions.chapterState ??
|
||||
storyEngineMemory.currentChapter ??
|
||||
null,
|
||||
nextChapter: resolveCurrentChapterState({
|
||||
state: stateWithReactions,
|
||||
}),
|
||||
});
|
||||
const journeyBeat = resolveCurrentJourneyBeat({
|
||||
state: {
|
||||
...stateWithReactions,
|
||||
chapterState,
|
||||
storyEngineMemory: {
|
||||
...storyEngineMemory,
|
||||
currentChapter: chapterState,
|
||||
},
|
||||
},
|
||||
chapterState,
|
||||
});
|
||||
const companionArcStates = advanceCompanionArc({
|
||||
previous: storyEngineMemory.companionArcStates,
|
||||
next: buildCompanionArcStates({
|
||||
state: stateWithReactions,
|
||||
reactions,
|
||||
}),
|
||||
});
|
||||
const campEvent = evaluateCampEventOpportunity({
|
||||
state: stateWithReactions,
|
||||
chapterState,
|
||||
journeyBeat,
|
||||
companionArcStates,
|
||||
})
|
||||
? buildCampEvent({
|
||||
state: stateWithReactions,
|
||||
chapterState,
|
||||
journeyBeat,
|
||||
companionArcStates,
|
||||
})
|
||||
: null;
|
||||
const worldMutations = resolveWorldMutations({
|
||||
state: stateWithReactions,
|
||||
signals,
|
||||
chapterState,
|
||||
});
|
||||
const stateWithMutations = applyWorldMutationsToGameState({
|
||||
state: stateWithReactions,
|
||||
mutations: worldMutations,
|
||||
});
|
||||
const setpieceDirective = evaluateSetpieceOpportunity({
|
||||
state: stateWithMutations,
|
||||
chapterState,
|
||||
journeyBeat,
|
||||
})
|
||||
? buildSetpieceDirective({
|
||||
state: stateWithMutations,
|
||||
chapterState,
|
||||
journeyBeat,
|
||||
})
|
||||
: null;
|
||||
const chronicle = appendChronicleEntries({
|
||||
state: stateWithMutations,
|
||||
chapterState,
|
||||
worldMutations,
|
||||
reactions,
|
||||
signals,
|
||||
campEvent,
|
||||
setpieceDirective,
|
||||
});
|
||||
const factionTensionStates = buildFactionTensionState(
|
||||
stateWithMutations.customWorldProfile,
|
||||
storyEngineMemory,
|
||||
);
|
||||
const actState = resolveCurrentActState({
|
||||
state: stateWithMutations,
|
||||
chapterState,
|
||||
});
|
||||
const campaignState = advanceCampaignState({
|
||||
previous:
|
||||
storyEngineMemory.campaignState ??
|
||||
stateWithMutations.campaignState ??
|
||||
null,
|
||||
next: resolveCampaignState({
|
||||
state: stateWithMutations,
|
||||
actState,
|
||||
}),
|
||||
});
|
||||
const consequenceLedger = appendConsequenceRecord({
|
||||
existing: storyEngineMemory.consequenceLedger,
|
||||
signals,
|
||||
reactions,
|
||||
worldMutations,
|
||||
campEvent,
|
||||
});
|
||||
const authorialConstraintPack = buildAuthorialConstraintPack({
|
||||
profile: stateWithMutations.customWorldProfile,
|
||||
});
|
||||
const compiledPacks = stateWithMutations.customWorldProfile
|
||||
? compileCampaignFromWorldProfile({
|
||||
profile: stateWithMutations.customWorldProfile,
|
||||
})
|
||||
: null;
|
||||
const activeScenarioPack =
|
||||
resolveScenarioPack(stateWithMutations.activeScenarioPackId) ??
|
||||
compiledPacks?.scenarioPack ??
|
||||
null;
|
||||
const activeCampaignPack = compiledPacks?.campaignPack ?? null;
|
||||
const playerStyleProfile = updatePlayerStyleProfileFromAction({
|
||||
current: storyEngineMemory.playerStyleProfile,
|
||||
actionText: params.actionText,
|
||||
});
|
||||
const companionResolutions = resolveAllCompanionResolutions({
|
||||
state: stateWithMutations,
|
||||
arcStates: companionArcStates,
|
||||
ledger: consequenceLedger,
|
||||
reactions,
|
||||
});
|
||||
const endingState =
|
||||
actState?.status === 'finale' || actState?.status === 'resolved'
|
||||
? resolveEndingState({
|
||||
state: stateWithMutations,
|
||||
companionResolutions,
|
||||
factionTensionStates,
|
||||
})
|
||||
: (storyEngineMemory.endingState ?? null);
|
||||
const epilogueSummary = endingState
|
||||
? buildEpilogueSummary({
|
||||
endingState,
|
||||
companionResolutions,
|
||||
})
|
||||
: null;
|
||||
const currentJourneyBeatId =
|
||||
journeyBeat?.id ?? storyEngineMemory.currentJourneyBeatId ?? null;
|
||||
const branchBudgetStatus = evaluateBranchBudget({
|
||||
consequenceLedger,
|
||||
authorialConstraintPack,
|
||||
endingFamilyCount: endingState ? 1 : 0,
|
||||
});
|
||||
const baseMemoryForQa = {
|
||||
...storyEngineMemory,
|
||||
currentChapter: chapterState,
|
||||
currentJourneyBeatId,
|
||||
currentJourneyBeat: journeyBeat,
|
||||
companionArcStates,
|
||||
worldMutations,
|
||||
chronicle,
|
||||
factionTensionStates,
|
||||
currentCampEvent: campEvent,
|
||||
currentSetpieceDirective: setpieceDirective,
|
||||
campaignState,
|
||||
actState,
|
||||
consequenceLedger,
|
||||
companionResolutions,
|
||||
endingState,
|
||||
authorialConstraintPack,
|
||||
branchBudgetStatus,
|
||||
playerStyleProfile,
|
||||
};
|
||||
const consistencyIssues = runNarrativeConsistencyChecks({
|
||||
memory: baseMemoryForQa,
|
||||
threadContracts: contracts,
|
||||
branchBudgetStatus,
|
||||
});
|
||||
const narrativeQaReport = buildNarrativeQaReport({
|
||||
issues: consistencyIssues,
|
||||
});
|
||||
const simulationRunResults =
|
||||
activeScenarioPack && activeCampaignPack
|
||||
? runPlaythroughMatrix({
|
||||
scenarioPackId: activeScenarioPack.id,
|
||||
campaignPack: activeCampaignPack,
|
||||
memory: {
|
||||
...baseMemoryForQa,
|
||||
narrativeQaReport,
|
||||
},
|
||||
seeds: ['baseline', 'companion', 'explore'],
|
||||
})
|
||||
: [];
|
||||
const replaySummary = simulationRunResults[0]
|
||||
? replayNarrativeRun({
|
||||
recordedSeed: recordReplaySeed({
|
||||
seed: simulationRunResults[0].seed,
|
||||
label: `${activeCampaignPack?.title ?? 'campaign'} 基线回放`,
|
||||
}),
|
||||
result: simulationRunResults[0],
|
||||
}).summary
|
||||
: null;
|
||||
const releaseGateReport = buildReleaseGateReport({
|
||||
qaReport: narrativeQaReport,
|
||||
simulationResults: simulationRunResults,
|
||||
unresolvedThreadCount:
|
||||
stateWithMutations.storyEngineMemory?.activeThreadIds.length ?? 0,
|
||||
});
|
||||
const saveMigrationManifest = buildSaveMigrationManifest({
|
||||
version: 'story-engine-v5',
|
||||
});
|
||||
const telemetrySnapshot = captureNarrativeTelemetry({
|
||||
memory: {
|
||||
...baseMemoryForQa,
|
||||
narrativeQaReport,
|
||||
},
|
||||
qaReport: narrativeQaReport,
|
||||
});
|
||||
const contentDiffReport = buildContentDiffReport({
|
||||
previousProfile: params.previousState.customWorldProfile,
|
||||
nextProfile: stateWithMutations.customWorldProfile,
|
||||
previousCampaignPack: null,
|
||||
nextCampaignPack: activeCampaignPack,
|
||||
});
|
||||
const narrativeCodex = buildNarrativeCodex({
|
||||
...stateWithMutations,
|
||||
chapterState,
|
||||
campaignState,
|
||||
storyEngineMemory: {
|
||||
...baseMemoryForQa,
|
||||
narrativeQaReport,
|
||||
releaseGateReport,
|
||||
simulationRunResults,
|
||||
},
|
||||
});
|
||||
const continueDigest =
|
||||
buildContinueGameDigest({
|
||||
state: {
|
||||
...stateWithMutations,
|
||||
chapterState,
|
||||
campaignState,
|
||||
storyEngineMemory: {
|
||||
...baseMemoryForQa,
|
||||
currentJourneyBeatId,
|
||||
narrativeQaReport,
|
||||
releaseGateReport,
|
||||
simulationRunResults,
|
||||
narrativeCodex,
|
||||
saveMigrationManifest,
|
||||
},
|
||||
},
|
||||
}) +
|
||||
[
|
||||
epilogueSummary,
|
||||
replaySummary,
|
||||
telemetrySnapshot.summary,
|
||||
contentDiffReport.summary,
|
||||
`发布门禁:${releaseGateReport.status} / ${releaseGateReport.summary}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n');
|
||||
|
||||
return {
|
||||
...stateWithMutations,
|
||||
chapterState,
|
||||
campaignState,
|
||||
activeScenarioPackId:
|
||||
activeScenarioPack?.id ?? stateWithMutations.activeScenarioPackId ?? null,
|
||||
activeCampaignPackId:
|
||||
activeCampaignPack?.id ?? stateWithMutations.activeCampaignPackId ?? null,
|
||||
storyEngineMemory: {
|
||||
...baseMemoryForQa,
|
||||
currentJourneyBeatId,
|
||||
continueGameDigest: continueDigest,
|
||||
narrativeQaReport,
|
||||
narrativeCodex,
|
||||
releaseGateReport,
|
||||
simulationRunResults,
|
||||
saveMigrationManifest,
|
||||
recentCompanionReactions: [
|
||||
...(storyEngineMemory.recentCompanionReactions ?? []),
|
||||
...reactions,
|
||||
].slice(-6),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export type GenerateStoryForState = (params: {
|
||||
state: GameState;
|
||||
character: Character;
|
||||
@@ -664,15 +77,10 @@ export function createStoryProgressionActions({
|
||||
lastFunctionId,
|
||||
) => {
|
||||
const nextHistory = appendStoryHistory(gameState, actionText, resultText);
|
||||
const stateWithHistory = applyStoryEngineEchoes({
|
||||
previousState: gameState,
|
||||
nextState: {
|
||||
...nextState,
|
||||
storyHistory: nextHistory,
|
||||
} as GameState,
|
||||
actionText,
|
||||
lastFunctionId,
|
||||
});
|
||||
const stateWithHistory = {
|
||||
...nextState,
|
||||
storyHistory: nextHistory,
|
||||
} as GameState;
|
||||
|
||||
setGameState(stateWithHistory);
|
||||
setAiError(null);
|
||||
@@ -686,13 +94,7 @@ export function createStoryProgressionActions({
|
||||
choice: actionText,
|
||||
lastFunctionId,
|
||||
});
|
||||
const recoveredState = applyStoryEngineEchoes({
|
||||
previousState: gameState,
|
||||
nextState: applyStoryReasoningRecovery(stateWithHistory),
|
||||
actionText,
|
||||
lastFunctionId,
|
||||
});
|
||||
setGameState(recoveredState);
|
||||
setGameState(stateWithHistory);
|
||||
setCurrentStory(nextStory);
|
||||
} catch (error) {
|
||||
console.error('Failed to continue scripted story:', error);
|
||||
@@ -744,15 +146,10 @@ export function createStoryProgressionActions({
|
||||
}
|
||||
|
||||
const nextHistory = appendStoryHistory(gameState, actionText, resultText);
|
||||
const stateWithHistory = applyStoryEngineEchoes({
|
||||
previousState: gameState,
|
||||
nextState: {
|
||||
...resolvedState,
|
||||
storyHistory: nextHistory,
|
||||
} as GameState,
|
||||
actionText,
|
||||
lastFunctionId,
|
||||
});
|
||||
const stateWithHistory = {
|
||||
...resolvedState,
|
||||
storyHistory: nextHistory,
|
||||
} as GameState;
|
||||
|
||||
setGameState(stateWithHistory);
|
||||
|
||||
@@ -764,13 +161,7 @@ export function createStoryProgressionActions({
|
||||
choice: actionText,
|
||||
lastFunctionId,
|
||||
});
|
||||
const recoveredState = applyStoryEngineEchoes({
|
||||
previousState: gameState,
|
||||
nextState: applyStoryReasoningRecovery(stateWithHistory),
|
||||
actionText,
|
||||
lastFunctionId,
|
||||
});
|
||||
setGameState(recoveredState);
|
||||
setGameState(stateWithHistory);
|
||||
setCurrentStory(nextStory);
|
||||
} catch (error) {
|
||||
console.error('Failed to continue encounter-entry story:', error);
|
||||
|
||||
Reference in New Issue
Block a user