feat(grid): именованные шаблоны сетки кампании

Несколько шаблонов в проекте с ручным сохранением и применением (включает сетку); без автоподстановки.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-09-03 12:04:47 +08:00
parent 278eb78637
commit a4d7ab77e7
14 changed files with 681 additions and 6 deletions
@@ -84,6 +84,7 @@ function minimalProject(overrides: Partial<Project> = {}): Project {
assets: {},
campaignAudios: [],
materials: [],
gridTemplates: [],
npcs: [],
npcGroups: [],
npcRelations: [],
+30
View File
@@ -13,6 +13,7 @@ import type { AssetId, NpcGroupId, ProjectId, TokenId } from '../types/ids';
import {
asAssetId,
asGraphNodeId,
asGridTemplateId,
asMaterialId,
asNpcGroupId,
asNpcId,
@@ -23,6 +24,11 @@ import {
asTokenId,
} from '../types/ids';
import { normalizeNpcExternalUrl } from '../types/npcExternalUrl';
import {
MAX_SCENE_GRID_TEMPLATES,
sceneGridTemplateStyleFromGrid,
type SceneGridTemplate,
} from '../types/sceneGrid';
import {
getConnectedComponent,
@@ -318,6 +324,7 @@ export function buildPartialExportProject(
assets,
campaignAudios: source.campaignAudios.map((a) => ({ ...a })),
materials: (source.materials ?? []).map((m) => ({ ...m })),
gridTemplates: (source.gridTemplates ?? []).map((t) => ({ ...t })),
currentSceneId: mainStart?.sceneId ?? firstSide?.sceneId ?? null,
currentGraphNodeId: mainStart?.id ?? firstSide?.id ?? null,
};
@@ -626,6 +633,28 @@ export function mergeStorylinesIntoProject(
materialAssetIds.add(mapped);
}
const gridTemplates = [...(target.gridTemplates ?? [])];
const gridTemplateNameKeys = new Set(gridTemplates.map((t) => t.name.trim().toLowerCase()));
for (const t of source.gridTemplates ?? []) {
if (gridTemplates.length >= MAX_SCENE_GRID_TEMPLATES) break;
let name = t.name.trim();
if (!name) continue;
const baseKey = name.toLowerCase();
if (gridTemplateNameKeys.has(baseKey)) {
let n = 2;
while (gridTemplateNameKeys.has(`${baseKey} (${String(n)})`)) n += 1;
name = `${name} (${String(n)})`;
}
const style = sceneGridTemplateStyleFromGrid(t);
const next: SceneGridTemplate = {
id: asGridTemplateId(`gt_${generateId()}`),
name,
...style,
};
gridTemplates.push(next);
gridTemplateNameKeys.add(name.toLowerCase());
}
// Группы только для создаваемых НПС выбранных линий.
const npcsToCreate = exportedNpcs.filter((n) => npcResolutionBySource.get(n.id)?.mode !== 'use');
const neededGroupIds = collectNpcGroupIdsForNpcs(source.npcGroups ?? [], npcsToCreate);
@@ -759,6 +788,7 @@ export function mergeStorylinesIntoProject(
assets,
campaignAudios,
materials,
gridTemplates,
npcs,
npcGroups,
npcRelations,