feat: scene traps, material legends, and scene editor window
Add trap overlays with session state, material legend editor/panel, and a dedicated scene editor window wired through IPC and project persistence. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+61
-3
@@ -21,6 +21,7 @@ import type { Project } from '../shared/types';
|
||||
|
||||
import { EffectsStore } from './effects/effectsStore';
|
||||
import { SceneDarknessStore } from './effects/sceneDarknessStore';
|
||||
import { SceneTrapsStore } from './sceneTraps/sceneTrapsStore';
|
||||
import { installIpcRouter, registerHandler, setLicenseAssert } from './ipc/router';
|
||||
import { LicenseService } from './license/licenseService';
|
||||
import { MaterialsOverlayStore } from './materials/materialsOverlayStore';
|
||||
@@ -50,10 +51,12 @@ import {
|
||||
openMaterialsWindow,
|
||||
openMultiWindow,
|
||||
openNpcsEditorWindow,
|
||||
openSceneEditorWindow,
|
||||
openNpcsWindow,
|
||||
openSceneDescriptionWindow,
|
||||
closeMaterialsWindow,
|
||||
closeNpcsEditorWindow,
|
||||
closeSceneEditorWindow,
|
||||
closeNpcsWindow,
|
||||
sendToAppWindows,
|
||||
togglePresentationFullscreen,
|
||||
@@ -147,6 +150,7 @@ function installAppMenuForSession(): void {
|
||||
|
||||
const effectsStore = new EffectsStore();
|
||||
const sceneDarknessStore = new SceneDarknessStore();
|
||||
const sceneTrapsStore = new SceneTrapsStore();
|
||||
const sceneViewStore = new SceneViewStore();
|
||||
const videoStore = new VideoPlaybackStore();
|
||||
const materialsOverlayStore = new MaterialsOverlayStore();
|
||||
@@ -212,6 +216,20 @@ function syncSceneDarknessForProject(project: Project): void {
|
||||
sceneDarknessStore.switchScene(cacheKey, enabled);
|
||||
}
|
||||
|
||||
function emitSceneTrapsState(): void {
|
||||
const state = sceneTrapsStore.getState();
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
win.webContents.send(ipcChannels.sceneTraps.stateChanged, { state });
|
||||
}
|
||||
}
|
||||
|
||||
function syncSceneTrapsForProject(project: Project): void {
|
||||
const cacheKey = project.currentGraphNodeId ?? project.currentSceneId ?? null;
|
||||
const scene = project.currentSceneId ? project.scenes[project.currentSceneId] : undefined;
|
||||
const trapIds = (scene?.traps ?? []).map((t) => t.id);
|
||||
sceneTrapsStore.switchScene(cacheKey, trapIds);
|
||||
}
|
||||
|
||||
function emitVideoState(): void {
|
||||
const state = videoStore.getState();
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
@@ -361,8 +379,12 @@ async function main() {
|
||||
sceneDarknessStore.resetSession();
|
||||
openMultiWindow();
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) syncSceneDarknessForProject(project);
|
||||
if (project) {
|
||||
syncSceneDarknessForProject(project);
|
||||
syncSceneTrapsForProject(project);
|
||||
}
|
||||
emitSceneDarknessState();
|
||||
emitSceneTrapsState();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.closeMultiWindow, () => {
|
||||
@@ -403,6 +425,14 @@ async function main() {
|
||||
closeNpcsEditorWindow();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.openSceneEditor, () => {
|
||||
openSceneEditorWindow();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.closeSceneEditor, () => {
|
||||
closeSceneEditorWindow();
|
||||
return { ok: true };
|
||||
});
|
||||
registerHandler(ipcChannels.windows.openNpcs, () => {
|
||||
openNpcsWindow();
|
||||
return { ok: true };
|
||||
@@ -458,11 +488,13 @@ async function main() {
|
||||
materialsOverlayStore.clear();
|
||||
npcsOverlayStore.clear();
|
||||
sceneDarknessStore.resetSession();
|
||||
sceneTrapsStore.resetSession();
|
||||
sceneViewStore.reset();
|
||||
emitEffectsState();
|
||||
emitMaterialsOverlayState();
|
||||
emitNpcsOverlayState();
|
||||
emitSceneDarknessState();
|
||||
emitSceneTrapsState();
|
||||
emitSceneViewState();
|
||||
emitSessionState();
|
||||
return { ok: true };
|
||||
@@ -481,11 +513,15 @@ async function main() {
|
||||
npcsOverlayStore.clear();
|
||||
sceneViewStore.reset();
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) syncSceneDarknessForProject(project);
|
||||
if (project) {
|
||||
syncSceneDarknessForProject(project);
|
||||
syncSceneTrapsForProject(project);
|
||||
}
|
||||
emitEffectsState();
|
||||
emitMaterialsOverlayState();
|
||||
emitNpcsOverlayState();
|
||||
emitSceneDarknessState();
|
||||
emitSceneTrapsState();
|
||||
emitSceneViewState();
|
||||
emitSessionState();
|
||||
return { currentSceneId: projectStore.getOpenProject()?.currentSceneId ?? null };
|
||||
@@ -504,11 +540,15 @@ async function main() {
|
||||
npcsOverlayStore.clear();
|
||||
sceneViewStore.reset();
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) syncSceneDarknessForProject(project);
|
||||
if (project) {
|
||||
syncSceneDarknessForProject(project);
|
||||
syncSceneTrapsForProject(project);
|
||||
}
|
||||
emitEffectsState();
|
||||
emitMaterialsOverlayState();
|
||||
emitNpcsOverlayState();
|
||||
emitSceneDarknessState();
|
||||
emitSceneTrapsState();
|
||||
emitSceneViewState();
|
||||
emitSessionState();
|
||||
const p = projectStore.getOpenProject();
|
||||
@@ -524,6 +564,10 @@ async function main() {
|
||||
syncSceneDarknessForProject(project);
|
||||
emitSceneDarknessState();
|
||||
}
|
||||
if (project && project.currentSceneId === sceneId && patch.traps !== undefined) {
|
||||
syncSceneTrapsForProject(project);
|
||||
emitSceneTrapsState();
|
||||
}
|
||||
emitSessionState();
|
||||
return { scene: next };
|
||||
});
|
||||
@@ -627,6 +671,11 @@ async function main() {
|
||||
emitSessionState();
|
||||
return { project };
|
||||
});
|
||||
registerHandler(ipcChannels.project.setMaterialLegend, async ({ materialId, legend }) => {
|
||||
const project = await projectStore.setMaterialLegend(materialId, legend);
|
||||
emitSessionState();
|
||||
return { project };
|
||||
});
|
||||
registerHandler(ipcChannels.project.pickMaterialImage, async () => {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openFile'],
|
||||
@@ -1099,6 +1148,15 @@ async function main() {
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
registerHandler(ipcChannels.sceneTraps.getState, () => {
|
||||
return { state: sceneTrapsStore.getState() };
|
||||
});
|
||||
registerHandler(ipcChannels.sceneTraps.dispatch, ({ event }) => {
|
||||
sceneTrapsStore.dispatch(event);
|
||||
emitSceneTrapsState();
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
registerHandler(ipcChannels.sceneView.getState, () => {
|
||||
return { state: sceneViewStore.getState() };
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user