Files
Genarrative/src/services/storyEngine/threadContract.ts
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

92 lines
2.7 KiB
TypeScript

import type {
CustomWorldProfile,
StoryThread,
ThreadContract,
ThreadContractStep,
} from '../../types';
function createStep(
contractId: string,
suffix: string,
title: string,
revealText: string,
completionSignalIds: string[],
optionalFactIds: string[],
) {
return {
id: `${contractId}:${suffix}`,
title,
revealText,
completionSignalIds,
optionalFactIds,
} satisfies ThreadContractStep;
}
export function buildThreadContract(params: {
thread: StoryThread;
issuerActorId?: string | null;
}) {
const { thread, issuerActorId = thread.involvedActorIds[0] ?? null } = params;
const contractId = `thread-contract:${thread.id}`;
const narrativeType: ThreadContract['narrativeType'] =
/|||线/u.test(`${thread.title} ${thread.summary}`)
? 'investigation'
: /|/u.test(`${thread.title} ${thread.summary}`)
? 'trial'
: /||/u.test(`${thread.title} ${thread.summary}`)
? 'relationship'
: /|/u.test(`${thread.title} ${thread.summary}`)
? 'escort'
: /||/u.test(`${thread.title} ${thread.summary}`)
? 'recovery'
: 'investigation';
const primarySceneSignal = thread.relatedLocationIds[0]
? [`enter_scene:${thread.relatedLocationIds[0]}`, `inspect_scene:${thread.relatedLocationIds[0]}`]
: [`talk_to_actor:${issuerActorId ?? 'actor'}`];
const actorSignal = issuerActorId
? [`talk_to_actor:${issuerActorId}`]
: [];
return {
id: contractId,
threadId: thread.id,
issuerActorId,
narrativeType,
currentStepId: `${contractId}:step_1`,
visibleStage: 0,
steps: [
createStep(
contractId,
'step_1',
`追上 ${thread.title} 的表层线索`,
thread.summary,
primarySceneSignal,
thread.relatedLocationIds.map((locationId) => `scene:${locationId}`),
),
createStep(
contractId,
'step_2',
`${thread.title} 的线头对回角色`,
`${thread.stakes} 还需要有人当面松口。`,
actorSignal.length > 0 ? actorSignal : [`resolve_contract_step:${thread.id}`],
thread.involvedActorIds.map((actorId) => `actor:${actorId}`),
),
],
followupThreadIds: [],
} satisfies ThreadContract;
}
export function buildThreadContractsFromProfile(profile: CustomWorldProfile) {
const threads = [
...(profile.storyGraph?.visibleThreads ?? []),
...(profile.storyGraph?.hiddenThreads ?? []),
];
return threads.map((thread) =>
buildThreadContract({
thread,
issuerActorId: thread.involvedActorIds[0] ?? null,
}),
);
}