feat(grid): именованные шаблоны сетки кампании
Несколько шаблонов в проекте с ручным сохранением и применением (включает сетку); без автоподстановки. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -67,7 +67,8 @@ import {
|
||||
type NpcDisposition,
|
||||
} from '../../shared/types/npcDisposition';
|
||||
import { normalizeNpcExternalUrl } from '../../shared/types/npcExternalUrl';
|
||||
import { DEFAULT_SCENE_GRID, normalizeSceneGrid } from '../../shared/types/sceneGrid';
|
||||
import { DEFAULT_SCENE_GRID, normalizeSceneGrid, normalizeSceneGridTemplates, sceneGridFromTemplate, sceneGridTemplateStyleFromGrid, MAX_SCENE_GRID_TEMPLATES } from '../../shared/types/sceneGrid';
|
||||
import type { SceneGridTemplate } from '../../shared/types/sceneGrid';
|
||||
import {
|
||||
asPreviewRotationDeg,
|
||||
previewRotationStepsCw,
|
||||
@@ -78,6 +79,7 @@ import { normalizeSceneTrap } from '../../shared/types/sceneTraps';
|
||||
import type {
|
||||
AssetId,
|
||||
GraphNodeId,
|
||||
GridTemplateId,
|
||||
MaterialId,
|
||||
NpcGroupId,
|
||||
NpcId,
|
||||
@@ -86,6 +88,7 @@ import type {
|
||||
import {
|
||||
asAssetId,
|
||||
asGraphNodeId,
|
||||
asGridTemplateId,
|
||||
asMaterialId,
|
||||
asNpcGroupId,
|
||||
asNpcId,
|
||||
@@ -320,6 +323,7 @@ export class ZipProjectStore {
|
||||
assets: {},
|
||||
campaignAudios: [],
|
||||
materials: [],
|
||||
gridTemplates: [],
|
||||
npcs: [],
|
||||
npcGroups: [],
|
||||
npcRelations: [],
|
||||
@@ -1411,6 +1415,70 @@ export class ZipProjectStore {
|
||||
return latest;
|
||||
}
|
||||
|
||||
async addGridTemplate(input: {
|
||||
name: string;
|
||||
type: SceneGridTemplate['type'];
|
||||
sizeN: number;
|
||||
color: string;
|
||||
}): Promise<Project> {
|
||||
if (!this.openProject) throw new Error('No open project');
|
||||
const name = input.name.trim();
|
||||
if (!name) throw new Error('Grid template name is required');
|
||||
const style = sceneGridTemplateStyleFromGrid(input);
|
||||
await this.updateProject((p) => {
|
||||
const list = normalizeSceneGridTemplates(p.gridTemplates);
|
||||
if (list.length >= MAX_SCENE_GRID_TEMPLATES) {
|
||||
throw new Error('Grid template limit reached');
|
||||
}
|
||||
const template: SceneGridTemplate = {
|
||||
id: asGridTemplateId(`gt_${this.randomId()}`),
|
||||
name,
|
||||
...style,
|
||||
};
|
||||
return { ...p, gridTemplates: [...list, template] };
|
||||
});
|
||||
const latest = this.getOpenProject();
|
||||
if (!latest) throw new Error('No open project');
|
||||
return latest;
|
||||
}
|
||||
|
||||
async renameGridTemplate(templateId: GridTemplateId, name: string): Promise<Project> {
|
||||
if (!this.openProject) throw new Error('No open project');
|
||||
const nextName = name.trim();
|
||||
if (!nextName) throw new Error('Grid template name is required');
|
||||
await this.updateProject((p) => {
|
||||
const list = normalizeSceneGridTemplates(p.gridTemplates);
|
||||
const idx = list.findIndex((t) => t.id === templateId);
|
||||
if (idx < 0) throw new Error('Grid template not found');
|
||||
const next = [...list];
|
||||
next[idx] = { ...next[idx]!, name: nextName };
|
||||
return { ...p, gridTemplates: next };
|
||||
});
|
||||
const latest = this.getOpenProject();
|
||||
if (!latest) throw new Error('No open project');
|
||||
return latest;
|
||||
}
|
||||
|
||||
async deleteGridTemplate(templateId: GridTemplateId): Promise<Project> {
|
||||
if (!this.openProject) throw new Error('No open project');
|
||||
await this.updateProject((p) => ({
|
||||
...p,
|
||||
gridTemplates: normalizeSceneGridTemplates(p.gridTemplates).filter((t) => t.id !== templateId),
|
||||
}));
|
||||
const latest = this.getOpenProject();
|
||||
if (!latest) throw new Error('No open project');
|
||||
return latest;
|
||||
}
|
||||
|
||||
async applyGridTemplate(sceneId: SceneId, templateId: GridTemplateId): Promise<Scene> {
|
||||
if (!this.openProject) throw new Error('No open project');
|
||||
const open = this.openProject;
|
||||
const template = normalizeSceneGridTemplates(open.project.gridTemplates).find((t) => t.id === templateId);
|
||||
if (!template) throw new Error('Grid template not found');
|
||||
if (!open.project.scenes[sceneId]) throw new Error('Scene not found');
|
||||
return this.updateScene(sceneId, { grid: sceneGridFromTemplate(template) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт или обновляет НПС.
|
||||
* При создании `filePath` (аватар) обязателен; при обновлении можно сменить только имя/описание/аватар.
|
||||
@@ -2756,6 +2824,7 @@ function normalizeProject(p: Project): Project {
|
||||
scenes: scenesPruned,
|
||||
campaignAudios,
|
||||
materials,
|
||||
gridTemplates: normalizeSceneGridTemplates((p as unknown as { gridTemplates?: unknown }).gridTemplates),
|
||||
npcs,
|
||||
npcGroups,
|
||||
npcRelations,
|
||||
|
||||
Reference in New Issue
Block a user