feat: use programmer art textures when on versions 1.13 and below

This commit is contained in:
trafficlunar 2024-12-29 19:40:24 +00:00
parent dca9e43e99
commit 0bfe70809d
13 changed files with 97 additions and 53 deletions

View file

@ -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;

View file

@ -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<HTMLDivElement>(null);
const [mousePosition, setMousePosition] = useState<Position>({ x: 0, y: 0 });

View file

@ -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<HTMLDivElement>(null);
const userModifiedBlocks = useRef(false);

View file

@ -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);

View file

@ -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<Position | null>(null);
@ -46,7 +46,6 @@ function BlockSelector({ stageWidth, searchInput, selectedBlocks, setSelectedBlo
>
<Container>
{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
<>
<Sprite
key={block}
texture={texture}
texture={textures[block]}
x={x}
y={y}
scale={2}

View file

@ -3,11 +3,11 @@ import { Container, Graphics, Sprite, Stage } from "@pixi/react";
import { BlocksIcon } from "lucide-react";
import { CanvasContext } from "@/context/Canvas";
import { TexturesContext } from "@/context/Textures";
import { ThemeContext } from "@/context/Theme";
import { ToolContext } from "@/context/Tool";
import { getBlockData } from "@/utils/getBlockData";
import { useBlockData } from "@/hooks/useBlockData";
import { useTextures } from "@/hooks/useTextures";
interface Props {
stageWidth: number;
@ -16,17 +16,17 @@ interface Props {
function BlockSelector({ stageWidth, searchInput }: Props) {
const { version } = useContext(CanvasContext);
const { missingTexture, textures } = useContext(TexturesContext);
const { isDark } = useContext(ThemeContext);
const { selectedBlock, setSelectedBlock } = useContext(ToolContext);
const [hoverPosition, setHoverPosition] = useState<Position | null>(null);
const [selectedBlockPosition, setSelectedBlockPosition] = useState<Position | null>({ 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) {
>
<Container>
{filteredBlocks.map((block, index) => {
const texture = textures[`${block}.png`] ?? missingTexture;
const texture = textures[block];
const { x, y } = getBlockPosition(index);
return (

View file

@ -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];

View file

@ -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<Tool>("hand");
const [waitingId, setWaitingId] = useState<number | null>(null);
@ -55,7 +59,7 @@ function Replace() {
>
<Stage width={32} height={32} options={{ backgroundAlpha: 0 }}>
<Container>
<Sprite texture={textures[`${block1}.png`] ?? missingTexture} scale={2} />
<Sprite texture={textures[block1] ?? missingTexture} scale={2} />
</Container>
</Stage>
</button>
@ -67,7 +71,7 @@ function Replace() {
>
<Stage width={32} height={32} options={{ backgroundAlpha: 0 }}>
<Container>
<Sprite texture={textures[`${block2}.png`] ?? missingTexture} scale={2} />
<Sprite texture={textures[block2] ?? missingTexture} scale={2} />
</Container>
</Stage>
</button>

View file

@ -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<HTMLDivElement>(null);
const [selectedBlockName, setSelectedBlockName] = useState("Stone");
const [selectedBlockTexture, setSelectedBlockTexture] = useState<PIXI.Texture>();
useEffect(() => {
const blockInfo = blockData[selectedBlock];
setSelectedBlockName(blockInfo.name);
setSelectedBlockTexture(textures[`${selectedBlock}.png`] ?? missingTexture);
}, [textures, selectedBlock, missingTexture]);
}, [textures, selectedBlock]);
return (
<TooltipProvider>
@ -32,7 +32,7 @@ function SelectedBlock() {
<div ref={divRef} className="absolute bottom-1 w-8 h-8 outline outline-1 outline-zinc-800 dark:outline-zinc-200">
<Stage width={divRef.current?.clientWidth} height={divRef.current?.clientHeight}>
<Container>
<Sprite texture={selectedBlockTexture} scale={2} />
<Sprite texture={textures[selectedBlock]} scale={2} />
</Container>
</Stage>
</div>

View file

@ -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<string, PIXI.Texture>;
programmerArtTextures: Record<string, PIXI.Texture>;
solidTextures: Record<string, PIXI.Texture>;
}
@ -24,6 +27,7 @@ export const TexturesProvider = ({ children }: Props) => {
const [missingTexture, setMissingTexture] = useState<PIXI.Texture>();
const [textures, setTextures] = useState<Record<string, PIXI.Texture>>({});
const [programmerArtTextures, setProgrammerArtTextures] = useState<Record<string, PIXI.Texture>>({});
const [solidTextures, setSolidTextures] = useState<Record<string, PIXI.Texture>>({});
useEffect(() => {
@ -34,12 +38,18 @@ export const TexturesProvider = ({ children }: Props) => {
setMissingTexture(new PIXI.Texture(missingBaseTexture));
// Load textures
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");
const airTexture = new PIXI.Texture(airBaseTexture);
setTextures({ ...t, "air.png": new PIXI.Texture(airBaseTexture) });
const sheet = new PIXI.Spritesheet(PIXI.BaseTexture.from("/blocks/spritesheet.png"), spritesheet);
sheet.parse().then((t) => {
setTextures({ ...t, "air.png": airTexture });
});
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 <TexturesContext.Provider value={{ missingTexture, textures, solidTextures }}>{children}</TexturesContext.Provider>;
return <TexturesContext.Provider value={{ missingTexture, textures, programmerArtTextures, solidTextures }}>{children}</TexturesContext.Provider>;
};

18
src/hooks/useBlockData.ts Normal file
View file

@ -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]);
}

23
src/hooks/useTextures.ts Normal file
View file

@ -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<string, Texture> {
const { missingTexture, textures, programmerArtTextures } = useContext(TexturesContext);
const blockData = useBlockData(version);
const blocksToUse = blocks || Object.keys(blockData);
return useMemo(() => {
return blocksToUse.reduce<Record<string, Texture>>((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]);
}

View file

@ -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;
}