This commit is contained in:
82
src/services/storyEngine/sceneNarrativeDirector.ts
Normal file
82
src/services/storyEngine/sceneNarrativeDirector.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import type {
|
||||
ActorNarrativeProfile,
|
||||
NpcDisclosureStage,
|
||||
SceneNarrativeDirective,
|
||||
VisibilitySlice,
|
||||
} from '../../types';
|
||||
|
||||
type BuildSceneNarrativeDirectiveParams = {
|
||||
sceneId?: string | null;
|
||||
sceneName?: string | null;
|
||||
encounterId?: string | null;
|
||||
encounterName?: string | null;
|
||||
recentActions?: string[] | null;
|
||||
activeThreadIds?: string[] | null;
|
||||
foregroundCarrierIds?: string[] | null;
|
||||
visibilitySlice?: VisibilitySlice | null;
|
||||
encounterNarrativeProfile?: ActorNarrativeProfile | null;
|
||||
disclosureStage?: NpcDisclosureStage | null;
|
||||
isFirstMeaningfulContact?: boolean;
|
||||
affinity?: number | null;
|
||||
};
|
||||
|
||||
function dedupeStrings(values: Array<string | null | undefined>, limit = 6) {
|
||||
return [...new Set(values.map((value) => value?.trim() ?? '').filter(Boolean))]
|
||||
.slice(0, limit);
|
||||
}
|
||||
|
||||
function resolveRevealBudget(
|
||||
disclosureStage?: NpcDisclosureStage | null,
|
||||
isFirstMeaningfulContact?: boolean,
|
||||
) {
|
||||
if (isFirstMeaningfulContact || disclosureStage === 'guarded') {
|
||||
return 'low' as const;
|
||||
}
|
||||
if (disclosureStage === 'partial' || disclosureStage === 'honest') {
|
||||
return 'medium' as const;
|
||||
}
|
||||
return 'high' as const;
|
||||
}
|
||||
|
||||
function resolveEmotionalCadence(params: BuildSceneNarrativeDirectiveParams) {
|
||||
if ((params.affinity ?? 0) < -10) {
|
||||
return 'hostile' as const;
|
||||
}
|
||||
if (params.isFirstMeaningfulContact) {
|
||||
return 'curious' as const;
|
||||
}
|
||||
if ((params.visibilitySlice?.forbiddenFactIds.length ?? 0) > 3) {
|
||||
return 'mysterious' as const;
|
||||
}
|
||||
if ((params.affinity ?? 0) >= 45) {
|
||||
return 'intimate' as const;
|
||||
}
|
||||
if ((params.recentActions ?? []).some((action) => /战|伤|逃|追|压/u.test(action))) {
|
||||
return 'tense' as const;
|
||||
}
|
||||
return 'curious' as const;
|
||||
}
|
||||
|
||||
export function buildSceneNarrativeDirective(
|
||||
params: BuildSceneNarrativeDirectiveParams,
|
||||
) {
|
||||
const primaryPressure =
|
||||
params.encounterNarrativeProfile?.immediatePressure ||
|
||||
params.recentActions?.[0] ||
|
||||
`${params.sceneName ?? '当前场景'}里仍有未被说透的动静。`;
|
||||
|
||||
return {
|
||||
primaryPressure,
|
||||
activeThreadIds: dedupeStrings(params.activeThreadIds ?? [], 4),
|
||||
foregroundActorIds: dedupeStrings([
|
||||
params.encounterId,
|
||||
params.encounterName,
|
||||
], 3),
|
||||
foregroundCarrierIds: dedupeStrings(params.foregroundCarrierIds ?? [], 4),
|
||||
revealBudget: resolveRevealBudget(
|
||||
params.disclosureStage,
|
||||
params.isFirstMeaningfulContact,
|
||||
),
|
||||
emotionalCadence: resolveEmotionalCadence(params),
|
||||
} satisfies SceneNarrativeDirective;
|
||||
}
|
||||
Reference in New Issue
Block a user