From 89d4c354dfbdcf88fa39702f74216c14cb0e2cbb Mon Sep 17 00:00:00 2001 From: Chris Maltby Date: Wed, 16 Oct 2024 11:06:58 +0100 Subject: [PATCH] Add ability to right click a scene to "Run From Here" allowing quick preview of a specific scene --- CHANGELOG.md | 1 + src/components/world/NavigatorScenes.tsx | 14 +++++- src/components/world/SceneView.tsx | 6 ++- .../world/renderSceneContextMenu.tsx | 41 ++++++++++++++++++ src/consts.ts | 1 + src/lang/en.json | 3 ++ src/shared/lib/resources/types.ts | 1 + .../features/buildGame/buildGameActions.ts | 12 ++++++ .../features/buildGame/buildGameMiddleware.ts | 43 +++++++++++++++---- test/dummydata.ts | 3 ++ test/resources/types.test.ts | 1 + 11 files changed, 115 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bca9ad5f..f78fcc2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add ability to select all scenes with Ctrl/Cmd + A from Game World view - Add plugin manager for installing plugins from the official plugin repository, accessible from the menu at `Plugins / Plugin Manager` - Add support for theme, localization and project template plugins +- Add ability to right click a scene to "Run From Here" allowing quick preview of a specific scene. Optionally can only include the selected scenes for faster build previews in large projects. ### Changed diff --git a/src/components/world/NavigatorScenes.tsx b/src/components/world/NavigatorScenes.tsx index 66f8aa93c..6615ecbdc 100644 --- a/src/components/world/NavigatorScenes.tsx +++ b/src/components/world/NavigatorScenes.tsx @@ -62,7 +62,9 @@ export const NavigatorScenes: FC = ({ const sceneSelectionIds = useAppSelector( (state) => state.editor.sceneSelectionIds ); - + const runSceneSelectionOnly = useAppSelector( + (state) => state.project.present.settings.runSceneSelectionOnly + ); const [folderId, setFolderId] = useState(""); const dispatch = useAppDispatch(); @@ -240,6 +242,7 @@ export const NavigatorScenes: FC = ({ hoverX: 0, hoverY: 0, onRename: () => setRenameId(item.id), + runSceneSelectionOnly, onClose, }); } else if (item.type === "actor") { @@ -265,7 +268,14 @@ export const NavigatorScenes: FC = ({ assertUnreachable(item); } }, - [dispatch, sceneSelectionIds, scenes, startDirection, startSceneId] + [ + dispatch, + runSceneSelectionOnly, + sceneSelectionIds, + scenes, + startDirection, + startSceneId, + ] ); const renderLabel = useCallback( diff --git a/src/components/world/SceneView.tsx b/src/components/world/SceneView.tsx index aceceb28d..b86317d8f 100644 --- a/src/components/world/SceneView.tsx +++ b/src/components/world/SceneView.tsx @@ -232,7 +232,9 @@ const SceneView = memo( const { x: hoverX, y: hoverY } = useAppSelector( (state) => state.editor.hover ); - + const runSceneSelectionOnly = useAppSelector( + (state) => state.project.present.settings.runSceneSelectionOnly + ); const selected = useAppSelector((state) => state.editor.scene === id); const sceneSelectionIds = useAppSelector( (state) => state.editor.sceneSelectionIds @@ -586,6 +588,7 @@ const SceneView = memo( startDirection, hoverX, hoverY, + runSceneSelectionOnly, onClose: onContextMenuClose, }); }, [ @@ -596,6 +599,7 @@ const SceneView = memo( sceneSelectionIds, startDirection, startSceneId, + runSceneSelectionOnly, onContextMenuClose, ]); diff --git a/src/components/world/renderSceneContextMenu.tsx b/src/components/world/renderSceneContextMenu.tsx index 188b63d8b..b3c3138d9 100644 --- a/src/components/world/renderSceneContextMenu.tsx +++ b/src/components/world/renderSceneContextMenu.tsx @@ -3,9 +3,11 @@ import React, { Dispatch } from "react"; import { UnknownAction } from "redux"; import { ActorDirection } from "shared/lib/entities/entitiesTypes"; import l10n from "shared/lib/lang/l10n"; +import buildGameActions from "store/features/buildGame/buildGameActions"; import entitiesActions from "store/features/entities/entitiesActions"; import settingsActions from "store/features/settings/settingsActions"; import { LabelButton } from "ui/buttons/LabelButton"; +import { BlankIcon, CheckIcon } from "ui/icons/Icons"; import { MenuDivider, MenuItem, MenuSection } from "ui/menu/Menu"; interface SceneContextMenuProps { @@ -16,6 +18,7 @@ interface SceneContextMenuProps { startDirection: ActorDirection; hoverX: number; hoverY: number; + runSceneSelectionOnly: boolean; onRename?: () => void; onClose?: () => void; } @@ -29,6 +32,7 @@ const renderSceneContextMenu = ({ startDirection, hoverX, hoverY, + runSceneSelectionOnly, onClose, }: SceneContextMenuProps) => { return [ @@ -122,6 +126,43 @@ const renderSceneContextMenu = ({ /> , + , + } + onClick={() => + dispatch( + buildGameActions.buildGame({ + startSceneId: sceneId, + startX: hoverX, + startY: hoverY, + }) + ) + } + > + {l10n("FIELD_RUN_FROM_HERE")} + , + , + : } + onClick={() => { + dispatch( + settingsActions.editSettings({ + runSceneSelectionOnly: !runSceneSelectionOnly, + }) + ); + }} + > + {l10n("FIELD_INCLUDE_SELECTION_ONLY")} + , + ]} + > + {l10n("FIELD_RUN_SCENE")} + , ...(onRename ? [ , diff --git a/src/consts.ts b/src/consts.ts index ef7c098c4..bd4bb5902 100755 --- a/src/consts.ts +++ b/src/consts.ts @@ -213,6 +213,7 @@ export const defaultProjectSettings: Settings = { compilerPreset: 3000, scriptEventPresets: {}, scriptEventDefaultPresets: {}, + runSceneSelectionOnly: false, }; export const defaultPalettes: Palette[] = [ diff --git a/src/lang/en.json b/src/lang/en.json index ec0b3e26c..0045df985 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -971,6 +971,9 @@ "FIELD_ADDING": "Adding...", "FIELD_CLOSE": "Close", "FIELD_LICENSE": "License", + "FIELD_RUN_SCENE": "Run Scene", + "FIELD_RUN_FROM_HERE": "Run From Here", + "FIELD_INCLUDE_SELECTION_ONLY": "Include Selection Only", "// 7": "Asset Viewer ---------------------------------------------", "ASSET_SEARCH": "Search...", diff --git a/src/shared/lib/resources/types.ts b/src/shared/lib/resources/types.ts index fb9873605..dd409a345 100644 --- a/src/shared/lib/resources/types.ts +++ b/src/shared/lib/resources/types.ts @@ -662,6 +662,7 @@ export const SettingsResource = Type.Object({ Type.Record(Type.String(), ScriptEventPreset) ), scriptEventDefaultPresets: Type.Record(Type.String(), Type.String()), + runSceneSelectionOnly: Type.Boolean(), }); export type SettingsResource = Static; diff --git a/src/store/features/buildGame/buildGameActions.ts b/src/store/features/buildGame/buildGameActions.ts index 58c2f6137..30d40cdb5 100644 --- a/src/store/features/buildGame/buildGameActions.ts +++ b/src/store/features/buildGame/buildGameActions.ts @@ -10,10 +10,18 @@ const buildGame = createAction( buildType = "web", exportBuild = false, debugEnabled = false, + startSceneId, + startX, + startY, + onlySelection, }: { buildType?: BuildType; exportBuild?: boolean; debugEnabled?: boolean; + startSceneId?: string; + startX?: number; + startY?: number; + onlySelection?: boolean; } = { buildType: "web", exportBuild: false, @@ -25,6 +33,10 @@ const buildGame = createAction( buildType, exportBuild, debugEnabled, + startSceneId, + startX, + startY, + onlySelection, }, }; } diff --git a/src/store/features/buildGame/buildGameMiddleware.ts b/src/store/features/buildGame/buildGameMiddleware.ts index 0dd7d06ab..79f0147c8 100644 --- a/src/store/features/buildGame/buildGameMiddleware.ts +++ b/src/store/features/buildGame/buildGameMiddleware.ts @@ -14,7 +14,14 @@ const buildGameMiddleware: Middleware = const state = store.getState(); const dispatch = store.dispatch.bind(store); - const { buildType, exportBuild, debugEnabled } = action.payload; + const { + buildType, + exportBuild, + debugEnabled, + startSceneId, + startX, + startY, + } = action.payload; if (state.console.status === "cancelled") { // Wait until cancel is complete before allowing another build @@ -32,15 +39,35 @@ const buildGameMiddleware: Middleware = const project = denormalizeProject(state.project.present); const engineFields = state.engine.fields; const sceneTypes = state.engine.sceneTypes; + const selectionIds = state.editor.sceneSelectionIds; try { - await API.project.build(project, { - buildType, - engineFields, - exportBuild, - debugEnabled, - sceneTypes, - }); + await API.project.build( + { + ...project, + scenes: + startSceneId && project.settings.runSceneSelectionOnly + ? project.scenes.filter( + (scene) => + scene.id === startSceneId || + selectionIds.includes(scene.id) + ) + : project.scenes, + settings: { + ...project.settings, + startSceneId: startSceneId ?? project.settings.startSceneId, + startX: startX ?? project.settings.startX, + startY: startY ?? project.settings.startY, + }, + }, + { + buildType, + engineFields, + exportBuild, + debugEnabled, + sceneTypes, + } + ); } catch (e) { dispatch(settingsActions.editSettings({ debuggerEnabled: true })); dispatch(navigationActions.setSection("world")); diff --git a/test/dummydata.ts b/test/dummydata.ts index 8904d6da9..cb413ce76 100644 --- a/test/dummydata.ts +++ b/test/dummydata.ts @@ -353,6 +353,7 @@ export const dummyProjectData: ProjectData = { compilerPreset: 3000, scriptEventPresets: {}, scriptEventDefaultPresets: {}, + runSceneSelectionOnly: false, }, }; @@ -623,6 +624,7 @@ export const dummySettingsResource: SettingsResource = { compilerPreset: 3000, scriptEventPresets: {}, scriptEventDefaultPresets: {}, + runSceneSelectionOnly: false, }; export const dummyVariablesResource: VariablesResource = { @@ -731,6 +733,7 @@ export const dummyProjectResources: ProjectResources = { compilerPreset: 3000, scriptEventPresets: {}, scriptEventDefaultPresets: {}, + runSceneSelectionOnly: false, }, }; diff --git a/test/resources/types.test.ts b/test/resources/types.test.ts index 3e827b73d..d0d8b3a30 100644 --- a/test/resources/types.test.ts +++ b/test/resources/types.test.ts @@ -632,6 +632,7 @@ describe("TypeBox Schemas", () => { compilerPreset: 3000, scriptEventPresets: {}, scriptEventDefaultPresets: {}, + runSceneSelectionOnly: false, }; const invalidSettings = { _resourceType: "settings",