diff --git a/src/components/canvas/Blocks.tsx b/src/components/canvas/Blocks.tsx index ba6467d..e2575ca 100644 --- a/src/components/canvas/Blocks.tsx +++ b/src/components/canvas/Blocks.tsx @@ -36,6 +36,7 @@ function Blocks({ usableBlocks, coords, scale, + version, setLoading, }: Props) { const app = useApp(); @@ -49,7 +50,7 @@ function Blocks({ // Tile solid colors at smaller scales if (scale >= 0.5) { blocks.forEach((block) => { - tilemap.tile(textures[`${block.name}.png`] ?? missingTexture, block.x * 16, block.y * 16); + tilemap.tile(textures[block.name], block.x * 16, block.y * 16); }); } else { blocks.forEach((block) => { @@ -67,7 +68,7 @@ function Blocks({ tileBlocks(); }, []); - useEffect(tileBlocks, [blocks]); + useEffect(tileBlocks, [blocks, version]); useEffect(() => { if (!tilemapRef.current) return; diff --git a/src/components/canvas/Canvas.tsx b/src/components/canvas/Canvas.tsx index 451bc20..e98a11a 100644 --- a/src/components/canvas/Canvas.tsx +++ b/src/components/canvas/Canvas.tsx @@ -11,6 +11,8 @@ import { TexturesContext } from "@/context/Textures"; import { ThemeContext } from "@/context/Theme"; import { ToolContext } from "@/context/Tool"; +import { useTextures } from "@/hooks/useTextures"; + import Blocks from "./Blocks"; import Cursor from "./Cursor"; import Grid from "./Grid"; @@ -29,10 +31,11 @@ function Canvas() { const { image, imageDimensions, usableBlocks } = useContext(ImageContext); const { setLoading } = useContext(LoadingContext); const { settings } = useContext(SettingsContext); - const { missingTexture, textures, solidTextures } = useContext(TexturesContext); + const { missingTexture, solidTextures } = useContext(TexturesContext); const { isDark } = useContext(ThemeContext); const { tool, radius, selectedBlock, cssCursor, setTool, setSelectedBlock, setCssCursor } = useContext(ToolContext); + const textures = useTextures(version); const stageContainerRef = useRef(null); const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); diff --git a/src/components/dialogs/OpenImage.tsx b/src/components/dialogs/OpenImage.tsx index 2288c30..06ba9bd 100644 --- a/src/components/dialogs/OpenImage.tsx +++ b/src/components/dialogs/OpenImage.tsx @@ -18,7 +18,7 @@ import { Separator } from "@/components/ui/separator"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Toggle } from "@/components/ui/toggle"; -import { getBlockData } from "@/utils/getBlockData"; +import { useBlockData } from "@/hooks/useBlockData"; import BlockSelector from "./open-image/BlockSelector"; import VersionCombobox from "../VersionCombobox"; @@ -34,7 +34,7 @@ function OpenImage({ close }: DialogProps) { }, }); - const blockData = getBlockData(version); + const blockData = useBlockData(version); const divRef = useRef(null); const userModifiedBlocks = useRef(false); diff --git a/src/components/dialogs/SaveImage.tsx b/src/components/dialogs/SaveImage.tsx index 97db53d..c2c88bd 100644 --- a/src/components/dialogs/SaveImage.tsx +++ b/src/components/dialogs/SaveImage.tsx @@ -2,17 +2,18 @@ import { useContext, useState } from "react"; import * as PIXI from "pixi.js"; import { CanvasContext } from "@/context/Canvas"; -import { TexturesContext } from "@/context/Textures"; import { Button } from "@/components/ui/button"; import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; +import { useTextures } from "@/hooks/useTextures"; + function SaveImage({ close }: DialogProps) { - const { blocks, canvasSize } = useContext(CanvasContext); - const { missingTexture, textures } = useContext(TexturesContext); + const { blocks, canvasSize, version } = useContext(CanvasContext); const [fileName, setFileName] = useState("blockmatic"); + const textures = useTextures(version); const onSubmit = () => { const width = canvasSize.maxX - canvasSize.minX; @@ -26,8 +27,7 @@ function SaveImage({ close }: DialogProps) { const container = new PIXI.Container(); blocks.forEach((block) => { - const texture = textures[`${block.name}.png`] ?? missingTexture; - const sprite = new PIXI.Sprite(texture); + const sprite = new PIXI.Sprite(textures[block.name]); sprite.x = block.x * 16; sprite.y = block.y * 16; container.addChild(sprite); diff --git a/src/components/dialogs/open-image/BlockSelector.tsx b/src/components/dialogs/open-image/BlockSelector.tsx index 026e2a7..bbe6e8b 100644 --- a/src/components/dialogs/open-image/BlockSelector.tsx +++ b/src/components/dialogs/open-image/BlockSelector.tsx @@ -2,10 +2,10 @@ import React, { useContext, useMemo, useState } from "react"; import { Container, Graphics, Sprite, Stage } from "@pixi/react"; import { CanvasContext } from "@/context/Canvas"; -import { TexturesContext } from "@/context/Textures"; import { ThemeContext } from "@/context/Theme"; -import { getBlockData } from "@/utils/getBlockData"; +import { useBlockData } from "@/hooks/useBlockData"; +import { useTextures } from "@/hooks/useTextures"; interface Props { stageWidth: number; @@ -17,10 +17,10 @@ interface Props { function BlockSelector({ stageWidth, searchInput, selectedBlocks, setSelectedBlocks, userModifiedBlocks }: Props) { const { version } = useContext(CanvasContext); - const { missingTexture, textures } = useContext(TexturesContext); const { isDark } = useContext(ThemeContext); - const blockData = getBlockData(version); + const blockData = useBlockData(version); + const textures = useTextures(version); const [hoverPosition, setHoverPosition] = useState(null); @@ -46,7 +46,6 @@ function BlockSelector({ stageWidth, searchInput, selectedBlocks, setSelectedBlo > {filteredBlocks.map((block, index) => { - const texture = textures[`${block}.png`] ?? missingTexture; const x = (index % blocksPerColumn) * (32 + 2) + 2; const y = Math.floor(index / blocksPerColumn) * (32 + 2) + 2; @@ -54,7 +53,7 @@ function BlockSelector({ stageWidth, searchInput, selectedBlocks, setSelectedBlo <> (null); const [selectedBlockPosition, setSelectedBlockPosition] = useState({ x: 0, y: 0 }); - const blockData = getBlockData(version); - const blocksPerColumn = Math.floor(stageWidth / (32 + 2)); + + const blockData = useBlockData(version); const filteredBlocks = useMemo(() => Object.keys(blockData).filter((value) => value.includes(searchInput)), [searchInput, blockData]); + const textures = useTextures(version, filteredBlocks); const getBlockPosition = (index: number): Position => { const x = (index % blocksPerColumn) * (32 + 2) + 2; @@ -62,7 +62,7 @@ function BlockSelector({ stageWidth, searchInput }: Props) { > {filteredBlocks.map((block, index) => { - const texture = textures[`${block}.png`] ?? missingTexture; + const texture = textures[block]; const { x, y } = getBlockPosition(index); return ( diff --git a/src/components/tool-settings/ColorPicker.tsx b/src/components/tool-settings/ColorPicker.tsx index da61b26..6721b9f 100644 --- a/src/components/tool-settings/ColorPicker.tsx +++ b/src/components/tool-settings/ColorPicker.tsx @@ -8,7 +8,7 @@ import { Label } from "@/components/ui/label"; import { CanvasContext } from "@/context/Canvas"; import { ToolContext } from "@/context/Tool"; -import { getBlockData } from "@/utils/getBlockData"; +import { useBlockData } from "@/hooks/useBlockData"; import { findBlockFromRgb } from "@/utils/findBlockFromRgb"; function ColorPicker() { @@ -19,7 +19,7 @@ function ColorPicker() { const rgb = useMemo(() => hsvaToRgba(hsva), [hsva]); const limitRgba = (x: number) => Math.min(Math.max(x, 0), 255); - const blockData = getBlockData(version); + const blockData = useBlockData(version); useEffect(() => { const blockInfo = blockData[selectedBlock]; diff --git a/src/components/tool-settings/Replace.tsx b/src/components/tool-settings/Replace.tsx index 957c00b..9bb7a63 100644 --- a/src/components/tool-settings/Replace.tsx +++ b/src/components/tool-settings/Replace.tsx @@ -6,12 +6,16 @@ import { ToolContext } from "@/context/Tool"; import { TexturesContext } from "@/context/Textures"; import { Label } from "@/components/ui/label"; -import { Button } from "../ui/button"; +import { Button } from "@/components/ui/button"; + +import { useTextures } from "@/hooks/useTextures"; function Replace() { - const { setBlocks } = useContext(CanvasContext); + const { version, setBlocks } = useContext(CanvasContext); const { selectedBlock, tool, setTool } = useContext(ToolContext); - const { missingTexture, textures } = useContext(TexturesContext); + const { missingTexture } = useContext(TexturesContext); + + const textures = useTextures(version); const [oldTool, setOldTool] = useState("hand"); const [waitingId, setWaitingId] = useState(null); @@ -55,7 +59,7 @@ function Replace() { > - + @@ -67,7 +71,7 @@ function Replace() { > - + diff --git a/src/components/toolbar/SelectedBlock.tsx b/src/components/toolbar/SelectedBlock.tsx index cb81af2..6d09f0e 100644 --- a/src/components/toolbar/SelectedBlock.tsx +++ b/src/components/toolbar/SelectedBlock.tsx @@ -4,26 +4,26 @@ import * as PIXI from "pixi.js"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -import { TexturesContext } from "@/context/Textures"; import { ToolContext } from "@/context/Tool"; import _blockData from "@/data/blocks/data.json"; +import { useTextures } from "@/hooks/useTextures"; +import { CanvasContext } from "@/context/Canvas"; const blockData: BlockData = _blockData; function SelectedBlock() { - const { missingTexture, textures } = useContext(TexturesContext); + const { version } = useContext(CanvasContext); const { selectedBlock } = useContext(ToolContext); + const textures = useTextures(version); const divRef = useRef(null); const [selectedBlockName, setSelectedBlockName] = useState("Stone"); - const [selectedBlockTexture, setSelectedBlockTexture] = useState(); useEffect(() => { const blockInfo = blockData[selectedBlock]; setSelectedBlockName(blockInfo.name); - setSelectedBlockTexture(textures[`${selectedBlock}.png`] ?? missingTexture); - }, [textures, selectedBlock, missingTexture]); + }, [textures, selectedBlock]); return ( @@ -32,7 +32,7 @@ function SelectedBlock() {
- +
diff --git a/src/context/Textures.tsx b/src/context/Textures.tsx index 85a2f41..cc3263c 100644 --- a/src/context/Textures.tsx +++ b/src/context/Textures.tsx @@ -4,12 +4,15 @@ import * as PIXI from "pixi.js"; import { LoadingContext } from "./Loading"; import spritesheet from "@/data/blocks/spritesheet.json"; +import programmerArtSpritesheet from "@/data/blocks/programmer-art/spritesheet.json"; + import _blockData from "@/data/blocks/data.json"; const blockData: BlockData = _blockData; interface Context { missingTexture: PIXI.Texture | undefined; textures: Record; + programmerArtTextures: Record; solidTextures: Record; } @@ -24,6 +27,7 @@ export const TexturesProvider = ({ children }: Props) => { const [missingTexture, setMissingTexture] = useState(); const [textures, setTextures] = useState>({}); + const [programmerArtTextures, setProgrammerArtTextures] = useState>({}); const [solidTextures, setSolidTextures] = useState>({}); useEffect(() => { @@ -34,12 +38,18 @@ export const TexturesProvider = ({ children }: Props) => { setMissingTexture(new PIXI.Texture(missingBaseTexture)); // Load textures + // Add air texture + const airBaseTexture = new PIXI.BaseTexture("/blocks/air.png"); + const airTexture = new PIXI.Texture(airBaseTexture); + const sheet = new PIXI.Spritesheet(PIXI.BaseTexture.from("/blocks/spritesheet.png"), spritesheet); sheet.parse().then((t) => { - // Add air texture - const airBaseTexture = new PIXI.BaseTexture("/blocks/air.png"); + setTextures({ ...t, "air.png": airTexture }); + }); - setTextures({ ...t, "air.png": new PIXI.Texture(airBaseTexture) }); + const programmerArtSheet = new PIXI.Spritesheet(PIXI.BaseTexture.from("/blocks/programmer-art/spritesheet.png"), programmerArtSpritesheet); + programmerArtSheet.parse().then((t) => { + setProgrammerArtTextures({ ...t, "air.png": airTexture }); }); // Load solid textures @@ -68,5 +78,5 @@ export const TexturesProvider = ({ children }: Props) => { setLoading(false); }, []); - return {children}; + return {children}; }; diff --git a/src/hooks/useBlockData.ts b/src/hooks/useBlockData.ts new file mode 100644 index 0000000..6ce8986 --- /dev/null +++ b/src/hooks/useBlockData.ts @@ -0,0 +1,18 @@ +import { useMemo } from "react"; + +import _blockData from "@/data/blocks/data.json"; +const blockData: BlockData = _blockData; + +export function useBlockData(version: number): BlockData { + return useMemo(() => { + const result: BlockData = {}; + + for (const key in blockData) { + if (blockData[key].version <= version) { + result[key] = blockData[key]; + } + } + + return result; + }, [version]); +} diff --git a/src/hooks/useTextures.ts b/src/hooks/useTextures.ts new file mode 100644 index 0000000..c83ae14 --- /dev/null +++ b/src/hooks/useTextures.ts @@ -0,0 +1,23 @@ +import { useContext, useMemo } from "react"; +import { BaseTexture, Texture } from "pixi.js"; + +import { TexturesContext } from "@/context/Textures"; +import { useBlockData } from "./useBlockData"; + +export function useTextures(version: number, blocks?: string[]): Record { + const { missingTexture, textures, programmerArtTextures } = useContext(TexturesContext); + + const blockData = useBlockData(version); + const blocksToUse = blocks || Object.keys(blockData); + + return useMemo(() => { + return blocksToUse.reduce>((textureMap, block) => { + if (version <= 1130) { + textureMap[block] = programmerArtTextures[`${block}.png`] ?? missingTexture; + } else { + textureMap[block] = textures[`${block}.png`] ?? missingTexture; + } + return textureMap; + }, {}); + }, [blocksToUse, version, missingTexture, textures, programmerArtTextures]); +} diff --git a/src/utils/getBlockData.ts b/src/utils/getBlockData.ts deleted file mode 100644 index 1b9352c..0000000 --- a/src/utils/getBlockData.ts +++ /dev/null @@ -1,14 +0,0 @@ -import _blockData from "@/data/blocks/data.json"; -const blockData: BlockData = _blockData; - -export function getBlockData(version: number) { - const filteredData: BlockData = {}; - - for (const key in blockData) { - if (blockData[key].version <= version) { - filteredData[key] = blockData[key]; - } - } - - return filteredData; -}