diff --git a/src/components/backgrounds/BackgroundPreviewSettings.tsx b/src/components/backgrounds/BackgroundPreviewSettings.tsx index f8d5c8665..99cd9e60b 100644 --- a/src/components/backgrounds/BackgroundPreviewSettings.tsx +++ b/src/components/backgrounds/BackgroundPreviewSettings.tsx @@ -4,6 +4,7 @@ import { backgroundSelectors, sceneSelectors, } from "store/features/entities/entitiesState"; +import settingsActions from "store/features/settings/settingsActions"; import editorActions from "store/features/editor/editorActions"; import electronActions from "store/features/electron/electronActions"; import { SceneSelect } from "components/forms/SceneSelect"; @@ -66,7 +67,12 @@ const BackgroundPreviewSettings = ({ ); const [isOpen, setIsOpen] = useState(false); const [buttonFocus, setButtonFocus] = useState(false); - const value = useAppSelector((state) => state.editor.previewAsSceneId); + const value = useAppSelector((state) => { + if (state.project.present.settings.previewAsMono) return "0"; + const sceneId = state.editor.previewAsSceneId; + if (sceneId === "") return "1"; + return sceneId; + }); const scene = useAppSelector((state) => sceneSelectors.selectById(state, value) ); @@ -144,6 +150,11 @@ const BackgroundPreviewSettings = ({ }; const onChange = (newValue: string) => { + dispatch( + settingsActions.editSettings({ + previewAsMono: (newValue == "0"), + }) + ); dispatch(editorActions.setPreviewAsSceneId(newValue)); }; @@ -173,7 +184,7 @@ const BackgroundPreviewSettings = ({ onBlur={closeMenu} maxMenuHeight={200} optional - optionalLabel={l10n("FIELD_DEFAULT_COLORS")} + optionalLabels={[l10n("FIELD_COLOR_MODE_MONO"), l10n("FIELD_DEFAULT_COLORS")]} {...selectMenuStyleProps} /> @@ -190,7 +201,8 @@ const BackgroundPreviewSettings = ({ ? l10n("FIELD_PREVIEW_AS_SCENE", { sceneName: sceneName(scene, sceneIndex), }) - : l10n("FIELD_PREVIEW_AS_DEFAULT")} + : ([l10n("FIELD_PREVIEW_AS_MONO"), l10n("FIELD_PREVIEW_AS_DEFAULT")][+value]) + } diff --git a/src/components/backgrounds/BackgroundViewer.tsx b/src/components/backgrounds/BackgroundViewer.tsx index ab16657cd..2e9e049e1 100644 --- a/src/components/backgrounds/BackgroundViewer.tsx +++ b/src/components/backgrounds/BackgroundViewer.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useAppSelector } from "store/hooks"; import styled from "styled-components"; import { @@ -84,6 +84,12 @@ const BackgroundViewer = ({ backgroundId }: MetaspriteEditorProps) => { (state) => state.project.present.settings.colorMode !== "mono" ); const [palettes, setPalettes] = useState(emptyPalettes); + const previewAsMono = useAppSelector((state) => + !gbcEnabled || state.project.present.settings.previewAsMono + ); + const defaultBGP = useAppSelector((state) => + state.project.present.settings.defaultBGP + ); useEffect(() => { setPalettes( @@ -128,6 +134,8 @@ const BackgroundViewer = ({ backgroundId }: MetaspriteEditorProps) => { src={assetURL("backgrounds", background)} tiles={background.tileColors} palettes={palettes} + previewAsMono={previewAsMono} + monoPalette={defaultBGP} /> )} @@ -162,6 +170,8 @@ const BackgroundViewer = ({ backgroundId }: MetaspriteEditorProps) => { src={assetURL("tilesets", tileset)} tiles={[]} palettes={palettes} + previewAsMono={previewAsMono} + monoPalette={defaultBGP} /> diff --git a/src/components/editors/ActorEditor.tsx b/src/components/editors/ActorEditor.tsx index 01dcc6705..83d400262 100644 --- a/src/components/editors/ActorEditor.tsx +++ b/src/components/editors/ActorEditor.tsx @@ -1,8 +1,9 @@ -import React, { FC, useCallback, useState } from "react"; +import React, { FC, useCallback, useState, useMemo } from "react"; import { actorPrefabSelectors, actorSelectors, sceneSelectors, + paletteSelectors, } from "store/features/entities/entitiesState"; import { DropdownButton } from "ui/buttons/DropdownButton"; import { EditableText } from "ui/form/EditableText"; @@ -43,6 +44,7 @@ import { ActorEditorProperties } from "./actor/ActorEditorProperties"; import { FlexGrow } from "ui/spacing/Spacing"; import { ActorPrefabSelectButton } from "components/forms/ActorPrefabSelectButton"; import { PrefabHeader } from "ui/form/headers/PrefabHeader"; +import { DMG_PALETTE } from "consts"; interface ActorEditorProps { id: string; @@ -62,6 +64,52 @@ export const ActorEditor: FC = ({ id, sceneId }) => { const scene = useAppSelector((state) => sceneSelectors.selectById(state, sceneId) ); + + const gbcEnabled = useAppSelector( + (state) => state.project.present.settings.colorMode !== "mono" + ); + + const previewAsMono = useAppSelector( + (state) => + state.project.present.settings.colorMode === "mono" || + (state.project.present.settings.colorMode === "mixed" && + state.project.present.settings.previewAsMono) + ); + + const palettesLookup = useAppSelector((state) => + paletteSelectors.selectEntities(state) + ); + + const defaultSpritePaletteIds = useAppSelector( + (state) => state.project.present.settings.defaultSpritePaletteIds || [] + ); + + const getPalette = useCallback( + (paletteIndex: number) => { + const sceneSpritePaletteIds = scene?.paletteIds ?? []; + if (sceneSpritePaletteIds[paletteIndex] === "dmg") { + return DMG_PALETTE; + } + return ( + palettesLookup[sceneSpritePaletteIds[paletteIndex]] || + palettesLookup[defaultSpritePaletteIds[paletteIndex]] || + DMG_PALETTE + ); + }, + [defaultSpritePaletteIds, palettesLookup, scene?.paletteIds] + ); + + const palettes = useMemo(() => ([0,1,2,3,4,5,6,7].map(i => + gbcEnabled ? getPalette(i) : DMG_PALETTE)), + [gbcEnabled, getPalette] + ); + + const monoPalettes = useAppSelector((state) => { + const defaultOBP0 = state.project.present.settings.defaultOBP0 ?? [0,0,1,3]; + const defaultOBP1 = state.project.present.settings.defaultOBP1 ?? [0,0,2,3]; + return [scene.dmgOBP0 ?? defaultOBP0, scene.dmgOBP1 ?? defaultOBP1]; + }); + const lockScriptEditor = useAppSelector( (state) => state.editor.lockScriptEditor ); @@ -415,9 +463,19 @@ export const ActorEditor: FC = ({ id, sceneId }) => { {!lockScriptEditor && ( {prefab ? ( - + ) : ( - + )} )} diff --git a/src/components/editors/SceneEditor.tsx b/src/components/editors/SceneEditor.tsx index aa6ac1d3c..fd95039c1 100644 --- a/src/components/editors/SceneEditor.tsx +++ b/src/components/editors/SceneEditor.tsx @@ -7,6 +7,7 @@ import BackgroundWarnings from "components/world/BackgroundWarnings"; import { backgroundSelectors, sceneSelectors, + paletteSelectors, } from "store/features/entities/entitiesState"; import editorActions from "store/features/editor/editorActions"; import clipboardActions from "store/features/clipboard/clipboardActions"; @@ -25,6 +26,7 @@ import { SceneNormalized, SceneParallaxLayer, ScriptEventNormalized, + MonoPalette } from "shared/lib/entities/entitiesTypes"; import { MenuDivider, MenuItem } from "ui/menu/Menu"; import { @@ -59,7 +61,7 @@ import { ClipboardTypePaletteIds, ClipboardTypeScenes, } from "store/features/clipboard/clipboardTypes"; -import { SCREEN_WIDTH } from "consts"; +import { SCREEN_WIDTH, DMG_PALETTE } from "consts"; import { ScriptEventAutoFadeDisabledWarning } from "components/script/ScriptEventAutoFade"; import { SceneSymbolsEditor } from "components/forms/symbols/SceneSymbolsEditor"; import { BackgroundSymbolsEditor } from "components/forms/symbols/BackgroundSymbolsEditor"; @@ -73,6 +75,7 @@ import { ScriptEditorCtx } from "shared/lib/scripts/context"; import { TilesetSelect } from "components/forms/TilesetSelect"; import { FlexGrow } from "ui/spacing/Spacing"; import CachedScroll from "ui/util/CachedScroll"; +import { DMGPalettePicker } from "components/forms/DMGPalettePicker"; interface SceneEditorProps { id: string; @@ -149,7 +152,18 @@ export const SceneEditor = ({ id }: SceneEditorProps) => { const [commonTilesetOpen, setCommonTilesetOpen] = useState( !!scene?.tilesetId ); - + const defaultBGP = useAppSelector((state) => + state.project.present.settings.defaultBGP ?? [0,1,2,3] + ); + const defaultOBP0 = useAppSelector((state) => + state.project.present.settings.defaultOBP0 ?? [0,0,1,3] + ); + const defaultOBP1 = useAppSelector((state) => + state.project.present.settings.defaultOBP1 ?? [0,0,2,3] + ); + const monoEnabled = useAppSelector( + (state) => state.project.present.settings.colorMode !== "color" + ); const colorsEnabled = useAppSelector( (state) => state.project.present.settings.colorMode !== "mono" ); @@ -324,6 +338,21 @@ export const SceneEditor = ({ id }: SceneEditorProps) => { [onChangeSettingProp] ); + const onEditBGP = useCallback( + (palette: MonoPalette) => + onChangeSceneProp("dmgBGP", palette), [onChangeSceneProp] + ); + + const onEditOBP0 = useCallback( + (palette: MonoPalette) => + onChangeSceneProp("dmgOBP0", palette), [onChangeSceneProp] + ); + + const onEditOBP1 = useCallback( + (palette: MonoPalette) => + onChangeSceneProp("dmgOBP1", palette), [onChangeSceneProp] + ); + const selectSidebar = () => { dispatch(editorActions.selectSidebar()); }; @@ -500,6 +529,51 @@ export const SceneEditor = ({ id }: SceneEditorProps) => { const scrollKey = `${scene.id}_${scriptKey}`; + const gbcEnabled = useAppSelector( + (state) => state.project.present.settings.colorMode !== "mono" + ); + + const previewAsMono = useAppSelector( + (state) => + state.project.present.settings.colorMode === "mono" || + (state.project.present.settings.colorMode === "mixed" && + state.project.present.settings.previewAsMono) + ); + + const palettesLookup = useAppSelector((state) => + paletteSelectors.selectEntities(state) + ); + + const getPalette = useCallback( + (paletteIndex: number) => { + const sceneSpritePaletteIds = scene?.paletteIds ?? []; + if (sceneSpritePaletteIds[paletteIndex] === "dmg") { + return DMG_PALETTE; + } + return ( + palettesLookup[sceneSpritePaletteIds[paletteIndex]] || + palettesLookup[defaultSpritePaletteIds[paletteIndex]] || + DMG_PALETTE + ); + }, + [defaultSpritePaletteIds, palettesLookup, scene?.paletteIds] + ); + + const palettes = useMemo(() => ([0,1,2,3,4,5,6,7].map(i => + gbcEnabled ? getPalette(i) : DMG_PALETTE)), + [gbcEnabled, getPalette] + ); + + const backgroundMonoPalette = useMemo(() => + scene.dmgBGP ?? defaultBGP, + [defaultBGP, previewAsMono] + ); + + const spriteMonoPalettes = useMemo(() => + [scene.dmgOBP0 ?? defaultOBP0, scene.dmgOBP1 ?? defaultOBP1], + [defaultOBP0, defaultOBP1, previewAsMono] + ); + return ( @@ -648,6 +722,9 @@ export const SceneEditor = ({ id }: SceneEditorProps) => { onChange={onChangeBackgroundId} is360={scene.type === "LOGO"} includeInfo + palettes={palettes} + previewAsMono={previewAsMono} + monoPalette={backgroundMonoPalette} />
{showCommonTilesetButton && ( @@ -739,14 +816,14 @@ export const SceneEditor = ({ id }: SceneEditorProps) => { )} - {colorsEnabled && ( - {l10n("FIELD_SCENE_BACKGROUND_PALETTES")} + {l10n("FIELD_SCENE_BACKGROUND_PALETTES")} + {colorsEnabled && ( { + )} - } - > - {!background?.autoColor && ( + }> + {monoEnabled && ( + + )} + {colorsEnabled && !background?.autoColor && ( {[0, 1, 2, 3, 4, 5, 6, 7].map((index) => ( { name="playerSpriteSheetId" label={l10n("FIELD_SCENE_SPRITE_PALETTES")} > + {monoEnabled && + + + + + } + {colorsEnabled && ( {[0, 1, 2, 3, 4, 5, 6, 7].map((index) => ( { /> ))} + )} {/* */} - )} {scene.type !== "LOGO" && ( @@ -859,6 +964,9 @@ export const SceneEditor = ({ id }: SceneEditorProps) => { optional optionalLabel={l10n("FIELD_SCENE_TYPE_DEFAULT")} optionalValue={defaultPlayerSprites[scene.type]} + palettes={palettes} + previewAsMono={previewAsMono} + monoPalettes={spriteMonoPalettes} /> diff --git a/src/components/editors/actor/ActorEditorProperties.tsx b/src/components/editors/actor/ActorEditorProperties.tsx index 1a1fd5815..0a1727b3a 100644 --- a/src/components/editors/actor/ActorEditorProperties.tsx +++ b/src/components/editors/actor/ActorEditorProperties.tsx @@ -4,6 +4,8 @@ import entitiesActions from "store/features/entities/entitiesActions"; import { ActorNormalized, CollisionGroup, + Palette, + MonoPalette, } from "shared/lib/entities/entitiesTypes"; import { SidebarColumn } from "ui/sidebars/Sidebar"; import { SpriteSheetSelectButton } from "components/forms/SpriteSheetSelectButton"; @@ -15,10 +17,16 @@ import { useAppDispatch } from "store/hooks"; interface ActorEditorPropertiesProps { actor: ActorNormalized; + palettes?: Palette[]; + previewAsMono?: boolean; + monoPalettes?: MonoPalette[]; } export const ActorEditorProperties: FC = ({ actor, + palettes, + previewAsMono, + monoPalettes, }) => { const dispatch = useAppDispatch(); @@ -77,6 +85,9 @@ export const ActorEditorProperties: FC = ({ frame={0} onChange={onChangeSpriteSheetId} includeInfo + previewAsMono={previewAsMono} + palettes={palettes} + monoPalettes={monoPalettes} /> diff --git a/src/components/editors/prefab/ActorPrefabEditorProperties.tsx b/src/components/editors/prefab/ActorPrefabEditorProperties.tsx index 548cb50d0..a4c3baef1 100644 --- a/src/components/editors/prefab/ActorPrefabEditorProperties.tsx +++ b/src/components/editors/prefab/ActorPrefabEditorProperties.tsx @@ -4,6 +4,8 @@ import entitiesActions from "store/features/entities/entitiesActions"; import { ActorPrefabNormalized, CollisionGroup, + Palette, + MonoPalette } from "shared/lib/entities/entitiesTypes"; import { SidebarColumn } from "ui/sidebars/Sidebar"; import { SpriteSheetSelectButton } from "components/forms/SpriteSheetSelectButton"; @@ -15,10 +17,17 @@ import { useAppDispatch } from "store/hooks"; interface ActorPrefabEditorPropertiesProps { prefab: ActorPrefabNormalized; + palettes?: Palette[]; + previewAsMono?: boolean; + monoPalettes?: MonoPalette[]; } -export const ActorPrefabEditorProperties: FC = - ({ prefab }) => { +export const ActorPrefabEditorProperties: FC = ({ + prefab, + palettes, + previewAsMono, + monoPalettes, +}) => { const dispatch = useAppDispatch(); const onChangeActorPrefabProp = useCallback( @@ -75,6 +84,9 @@ export const ActorPrefabEditorProperties: FC = frame={0} onChange={onChangeSpriteSheetId} includeInfo + previewAsMono={previewAsMono} + palettes={palettes} + monoPalettes={monoPalettes} /> diff --git a/src/components/forms/BackgroundSelectButton.tsx b/src/components/forms/BackgroundSelectButton.tsx index e9f10862d..445bc31ca 100644 --- a/src/components/forms/BackgroundSelectButton.tsx +++ b/src/components/forms/BackgroundSelectButton.tsx @@ -8,13 +8,16 @@ import { SelectMenu, selectMenuStyleProps, } from "ui/form/Select"; +import { Palette, MonoPalette } from "shared/lib/entities/entitiesTypes"; import { RelativePortal } from "ui/layout/RelativePortal"; import { BackgroundSelect } from "./BackgroundSelect"; -import { assetURLStyleProp } from "shared/lib/helpers/assets"; +import { assetURL, assetURLStyleProp } from "shared/lib/helpers/assets"; import { useAppDispatch, useAppSelector } from "store/hooks"; -import { MAX_BACKGROUND_TILES, MAX_BACKGROUND_TILES_CGB } from "consts"; +import { DMG_PALETTE, MAX_BACKGROUND_TILES, MAX_BACKGROUND_TILES_CGB, TILE_SIZE } from "consts"; import { monoOverrideForFilename } from "shared/lib/assets/backgrounds"; import { FlexGrow } from "ui/spacing/Spacing"; +import AutoColorizedImage from "components/world/AutoColorizedImage"; +import ColorizedImage from "components/world/ColorizedImage"; interface BackgroundSelectProps { name: string; @@ -23,6 +26,9 @@ interface BackgroundSelectProps { tilesetId: string; includeInfo?: boolean; onChange?: (newId: string) => void; + palettes?: Palette[]; + previewAsMono?: boolean; + monoPalette?: MonoPalette; } interface WrapperProps { @@ -167,6 +173,9 @@ export const BackgroundSelectButton: FC = ({ is360, tilesetId, includeInfo, + palettes, + previewAsMono, + monoPalette, }) => { const buttonRef = useRef(null); const timerRef = useRef>(); @@ -264,6 +273,8 @@ export const BackgroundSelectButton: FC = ({ } }; + const defaultPalettes = [0,1,2,3,4,5,6,7].map(i => DMG_PALETTE); + return (