91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
import type {
|
||
CampEvent,
|
||
CompanionReactionRecord,
|
||
ConsequenceRecord,
|
||
StorySignal,
|
||
WorldMutation,
|
||
} from '../../types';
|
||
|
||
function dedupeById(records: ConsequenceRecord[]) {
|
||
const map = new Map<string, ConsequenceRecord>();
|
||
records.forEach((record) => map.set(record.id, record));
|
||
return [...map.values()];
|
||
}
|
||
|
||
export function appendConsequenceRecord(params: {
|
||
existing?: ConsequenceRecord[] | null;
|
||
signals?: StorySignal[];
|
||
reactions?: CompanionReactionRecord[];
|
||
worldMutations?: WorldMutation[];
|
||
campEvent?: CampEvent | null;
|
||
}) {
|
||
const next: ConsequenceRecord[] = [...(params.existing ?? [])];
|
||
|
||
(params.signals ?? []).forEach((signal) => {
|
||
next.push({
|
||
id: `consequence:signal:${signal.id}`,
|
||
category:
|
||
signal.signalType === 'accept_contract'
|
||
? 'thread'
|
||
: signal.signalType === 'give_item'
|
||
? 'companion'
|
||
: signal.signalType === 'obtain_carrier'
|
||
? 'world'
|
||
: 'thread',
|
||
title: signal.signalType,
|
||
summary: `你触发了 ${signal.signalType}。`,
|
||
weight: signal.signalType === 'accept_contract' ? 2 : 1,
|
||
relatedIds: [...(signal.threadIds ?? []), signal.actorId ?? '', signal.sceneId ?? '', signal.carrierId ?? ''].filter(Boolean),
|
||
irreversible:
|
||
signal.signalType === 'accept_contract' ||
|
||
signal.signalType === 'give_item' ||
|
||
signal.signalType === 'resolve_contract_step',
|
||
});
|
||
});
|
||
|
||
(params.reactions ?? []).forEach((reaction) => {
|
||
next.push({
|
||
id: `consequence:reaction:${reaction.id}`,
|
||
category: 'companion',
|
||
title: `${reaction.characterId}:${reaction.reactionType}`,
|
||
summary: reaction.reason,
|
||
weight: reaction.reactionType === 'disapprove' ? 3 : reaction.reactionType === 'approve' ? 2 : 1,
|
||
relatedIds: reaction.relatedThreadIds,
|
||
irreversible: reaction.reactionType === 'disapprove',
|
||
});
|
||
});
|
||
|
||
(params.worldMutations ?? []).forEach((mutation) => {
|
||
next.push({
|
||
id: `consequence:mutation:${mutation.id}`,
|
||
category: mutation.mutationType === 'npc_attitude' ? 'faction' : 'world',
|
||
title: mutation.mutationType,
|
||
summary: mutation.reason,
|
||
weight: mutation.mutationType === 'route_lock' || mutation.mutationType === 'route_unlock' ? 3 : 2,
|
||
relatedIds: [mutation.targetId, ...mutation.relatedThreadIds],
|
||
irreversible: mutation.mutationType === 'route_lock' || mutation.mutationType === 'route_unlock',
|
||
});
|
||
});
|
||
|
||
if (params.campEvent) {
|
||
next.push({
|
||
id: `consequence:camp:${params.campEvent.id}`,
|
||
category: 'companion',
|
||
title: params.campEvent.title,
|
||
summary: params.campEvent.triggerReason,
|
||
weight: 2,
|
||
relatedIds: [...params.campEvent.participantCharacterIds, ...params.campEvent.relatedThreadIds],
|
||
irreversible: params.campEvent.eventType === 'decision',
|
||
});
|
||
}
|
||
|
||
return dedupeById(next).slice(-24);
|
||
}
|
||
|
||
export function buildConsequenceLedgerSummary(records: ConsequenceRecord[]) {
|
||
return records
|
||
.slice(-5)
|
||
.map((record) => `- ${record.title}:${record.summary}`)
|
||
.join('\n');
|
||
}
|