cfb6e62331
Список сцен активной линии в порядке бокового списка редактора; переход с записью в историю. Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
import type { GraphNodeId, Project, SceneGraphEdge, SceneGraphNode, SceneId } from '../types';
|
|
|
|
import { reconcileSceneListOrder } from './sceneListOrder';
|
|
import {
|
|
buildStoryRootMap,
|
|
getNodeStoryRoot,
|
|
getSideStoryComponentNodeIds,
|
|
isSideStoryRoot,
|
|
} from './sceneGraphLineage';
|
|
|
|
export type ActiveStorylineSceneEntry = {
|
|
sceneId: SceneId;
|
|
graphNodeId: GraphNodeId;
|
|
};
|
|
|
|
/** Узлы графа, принадлежащие активной сюжетной линии текущего положения. */
|
|
export function getActiveStorylineGraphNodeIds(
|
|
nodes: SceneGraphNode[],
|
|
edges: SceneGraphEdge[],
|
|
currentGraphNodeId: GraphNodeId,
|
|
): Set<GraphNodeId> {
|
|
const root = getNodeStoryRoot(nodes, edges, currentGraphNodeId);
|
|
if (root === 'main') {
|
|
const rootMap = buildStoryRootMap(nodes, edges);
|
|
const ids = new Set<GraphNodeId>();
|
|
for (const [id, r] of rootMap) {
|
|
if (r === 'main') ids.add(id);
|
|
}
|
|
return ids;
|
|
}
|
|
if (isSideStoryRoot(root)) {
|
|
return getSideStoryComponentNodeIds(nodes, edges, root);
|
|
}
|
|
return new Set();
|
|
}
|
|
|
|
function pickGraphNodeForScene(
|
|
sceneId: SceneId,
|
|
storylineNodeIds: Set<GraphNodeId>,
|
|
nodes: SceneGraphNode[],
|
|
currentGraphNodeId: GraphNodeId | null,
|
|
): GraphNodeId | null {
|
|
const matching = nodes.filter((n) => storylineNodeIds.has(n.id) && n.sceneId === sceneId);
|
|
if (matching.length === 0) return null;
|
|
if (currentGraphNodeId && matching.some((n) => n.id === currentGraphNodeId)) {
|
|
return currentGraphNodeId;
|
|
}
|
|
return matching[0]?.id ?? null;
|
|
}
|
|
|
|
/**
|
|
* Сцены активной линии в порядке бокового списка редактора; каждая sceneId — один раз.
|
|
*/
|
|
export function listActiveStorylineScenes(project: Project): ActiveStorylineSceneEntry[] {
|
|
const currentGraphNodeId = project.currentGraphNodeId;
|
|
if (!currentGraphNodeId) return [];
|
|
|
|
const storylineNodeIds = getActiveStorylineGraphNodeIds(
|
|
project.sceneGraphNodes,
|
|
project.sceneGraphEdges,
|
|
currentGraphNodeId,
|
|
);
|
|
if (storylineNodeIds.size === 0) return [];
|
|
|
|
const order = reconcileSceneListOrder(project.scenes, project.sceneListOrder);
|
|
const out: ActiveStorylineSceneEntry[] = [];
|
|
for (const sceneId of order) {
|
|
const graphNodeId = pickGraphNodeForScene(
|
|
sceneId,
|
|
storylineNodeIds,
|
|
project.sceneGraphNodes,
|
|
currentGraphNodeId,
|
|
);
|
|
if (graphNodeId) out.push({ sceneId, graphNodeId });
|
|
}
|
|
return out;
|
|
}
|