83 lines
2.5 KiB
TypeScript
83 lines
2.5 KiB
TypeScript
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;
|
|
}
|