feat(scene): battle grid overlay with color and trap VFX stacking

Add configurable square/hex grid in the scene editor (persisted and shown on control/presentation) and keep trap activation effects above trap icons.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-27 06:47:59 +08:00
parent 4aa0f257d5
commit bdeb64e356
17 changed files with 413 additions and 15 deletions
+1
View File
@@ -387,6 +387,7 @@ export async function buildProjectFromFoundryDocuments(
previewRotationDeg: 0,
darkenScene: false,
traps: [],
grid: { enabled: false, type: 'square', sizeN: 0.06, color: '#ffffff' },
media: { videos: [], audios: audioRefs },
settings: {
autoplayVideo: previewAssetType === 'video',
+6
View File
@@ -51,6 +51,7 @@ import type {
} from '../../shared/types';
import { PROJECT_SCHEMA_VERSION } from '../../shared/types';
import { normalizeMaterialLegend } from '../../shared/types/materialLegend';
import { DEFAULT_SCENE_GRID, normalizeSceneGrid } from '../../shared/types/sceneGrid';
import { normalizeSceneTrap } from '../../shared/types/sceneTraps';
import type { AssetId, GraphNodeId, MaterialId, NpcGroupId, NpcId, NpcRelationId } from '../../shared/types/ids';
import {
@@ -616,11 +617,13 @@ export class ZipProjectStore {
previewRotationDeg: 0,
darkenScene: false,
traps: [],
grid: { ...DEFAULT_SCENE_GRID },
} satisfies Scene);
const next: Scene = {
...base,
traps: base.traps ?? [],
grid: base.grid ?? { ...DEFAULT_SCENE_GRID },
...(patch.title !== undefined ? { title: patch.title } : null),
...(patch.description !== undefined ? { description: patch.description } : null),
...(patch.previewAssetId !== undefined ? { previewAssetId: patch.previewAssetId } : null),
@@ -640,6 +643,7 @@ export class ZipProjectStore {
.filter((t): t is SceneTrap => Boolean(t)),
}
: null),
...(patch.grid !== undefined ? { grid: normalizeSceneGrid(patch.grid) } : null),
...(patch.settings ? { settings: { ...base.settings, ...patch.settings } } : null),
...(patch.media ? { media: { ...base.media, ...patch.media } } : null),
...(patch.layout ? { layout: { ...base.layout, ...patch.layout } } : null),
@@ -2325,6 +2329,7 @@ function normalizeScene(s: Scene): Scene {
const traps = (Array.isArray(rawTraps) ? rawTraps : [])
.map((t) => normalizeSceneTrap(t))
.filter((t): t is SceneTrap => Boolean(t));
const grid = normalizeSceneGrid((s as unknown as { grid?: unknown }).grid);
const rawAudios = Array.isArray(raw.audios) ? raw.audios : [];
const audios = rawAudios
@@ -2354,6 +2359,7 @@ function normalizeScene(s: Scene): Scene {
previewRotationDeg,
darkenScene,
traps,
grid,
layout: layoutIn ?? { x: 0, y: 0 },
media: {
videos: raw.videos ?? [],