endfield-mcp
Version:
MCP Server for Arknights: Endfield (TypeScript / stdio + Streamable HTTP)
97 lines • 3.41 kB
JavaScript
/**
* Story scene reader — load and parse one dialogue scene by conv key.
*
* Reads `conv/<key>.json` on-demand from the store. Scenes are not cached —
* they are large and infrequently re-read, so we re-read per call rather
* than hold 97MB of dialogue in memory. Lines are normalized to the
* StoryLine three-state shape. Player choices are extracted from
* optionGroups (a separate structure from the lines array).
*
* Depends on `./storyCore.js` for the store accessor and mission-name map.
*/
import { store, missionNames } from "./storyCore.js";
// ---------------------------------------------------------------------------
// Line normalization
// ---------------------------------------------------------------------------
/**
* Normalize a raw conv line to the StoryLine three-state shape.
*
* Exported for direct unit testing of the three-state classification
* (speaker→dialog / actor→dialog / text-only→narration / empty→narration).
*/
export function normalizeLine(line) {
// SNS/chat variant: has `speaker` or `cid` instead of `actor`.
if (line.speaker && line.text) {
return {
type: "dialog",
role: line.speaker,
text: line.text,
};
}
// Standard dialog: has `actor` (resolved display name).
if (line.actor && line.text) {
return {
type: "dialog",
role: line.actor,
text: line.text,
};
}
// Narration: only `id` + `text`, no actor.
if (line.text) {
return {
type: "narration",
role: null,
text: line.text,
};
}
// Fallback (shouldn't happen in well-formed data).
return {
type: "narration",
role: null,
text: "",
};
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Load and parse one dialogue scene by conv key.
*
* Reads conv/<key>.json on-demand (does not cache — scenes are large
* and infrequently re-read). Lines are normalized to the StoryLine
* three-state shape. Player choices are extracted from optionGroups.
*/
export function readStory(convKey) {
const path = `conv/${convKey}.json`;
if (!store().exists(path))
return null;
// readJsonInt64Safe: conv line `id` fields are int64-sized in source
// data. We don't currently surface line ids, but use the safe parser
// defensively (matching characters.ts and texts.ts) so a future
// enhancement that adds line ids doesn't silently truncate them.
const raw = store().readJsonInt64Safe(path);
const names = missionNames();
const mission = raw.mission ?? "";
const sceneNum = typeof raw.scene === "string"
? Number(raw.scene) || 0
: raw.scene ?? 0;
const lines = (raw.lines ?? []).map((line) => normalizeLine(line));
const choices = [];
for (const group of raw.optionGroups ?? []) {
for (const opt of group.options ?? []) {
choices.push({
index: opt.i ?? 0,
text: opt.text ?? "",
});
}
}
return {
key: convKey,
mission,
missionName: names[mission] ?? mission,
scene: sceneNum,
lines,
choices,
};
}
//# sourceMappingURL=storyScenes.js.map