54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
import type { GameContent } from '../content/index';
|
|
import type { GameState } from '../engine/game';
|
|
import { evaluateTriggers, type StoryEvent, type TriggerContext } from '../engine/story';
|
|
import type { GamePrefs } from './prefs';
|
|
import type { StoryLogEntry } from './store';
|
|
|
|
export interface StoryUiEffect {
|
|
enteredNodeIds: string[];
|
|
logEntries: StoryLogEntry[];
|
|
shouldOpenPanel: boolean;
|
|
eventLogLines: string[];
|
|
}
|
|
|
|
export function storyEventsToLogEntries(events: StoryEvent[]): StoryLogEntry[] {
|
|
return events
|
|
.filter((e) => e.kind === 'enter')
|
|
.map((e) => ({ nodeId: e.nodeId, prose: e.prose, choiceLabel: e.choiceLabel }));
|
|
}
|
|
|
|
export function shouldAutoNavigateToStory(
|
|
prefs: GamePrefs,
|
|
nodeId: string,
|
|
content: GameContent,
|
|
): boolean {
|
|
const node = content.storyNodesById[nodeId];
|
|
if (!node) return false;
|
|
if (prefs.storyOpenMode === 'manual') return false;
|
|
if (prefs.storyOpenMode === 'auto') return true;
|
|
return (node.choices?.length ?? 0) > 0;
|
|
}
|
|
|
|
export function processStoryTriggers(
|
|
state: GameState,
|
|
content: GameContent,
|
|
ctx: TriggerContext,
|
|
prefs: GamePrefs,
|
|
): StoryUiEffect {
|
|
const { enteredNodeIds, events } = evaluateTriggers(state, content, ctx);
|
|
const logEntries = storyEventsToLogEntries(events);
|
|
const shouldOpenPanel =
|
|
enteredNodeIds.length > 0 &&
|
|
enteredNodeIds.some((id) => shouldAutoNavigateToStory(prefs, id, content));
|
|
const customLogs = events.filter((e) => e.kind === 'log').map((e) => e.prose);
|
|
const eventLogLines = [
|
|
...customLogs,
|
|
...logEntries.map((e) =>
|
|
e.choiceLabel
|
|
? `Story: ${e.choiceLabel}`
|
|
: `Story: ${(content.storyNodesById[e.nodeId]?.prose ?? '').slice(0, 40)}…`,
|
|
),
|
|
];
|
|
return { enteredNodeIds, logEntries, shouldOpenPanel, eventLogLines };
|
|
}
|