From 25e9960d01c5ca591c4f3c36e0b06b005457d0e1 Mon Sep 17 00:00:00 2001 From: pricewowen <231476659+pricewowen@users.noreply.github.com> Date: Sat, 23 May 2026 23:54:36 -0600 Subject: [PATCH] Removed the auto generated sprites --- README.md | 16 +- .../character/character-layer-preview.tsx | 117 ++++--- components/character/tinted-sprite-icon.tsx | 44 +-- .../onboarding/onboarding-quiz-form.tsx | 31 +- package-lock.json | 321 +----------------- package.json | 4 +- scripts/generate-character-sprites.mjs | 159 --------- 7 files changed, 110 insertions(+), 582 deletions(-) delete mode 100644 scripts/generate-character-sprites.mjs diff --git a/README.md b/README.md index 4e8ab70..54836dd 100644 --- a/README.md +++ b/README.md @@ -38,32 +38,26 @@ On the **Pet** tab, users rename their pet and switch between **Overview** and * npm install ``` -2. Generate placeholder character/shop sprites (if missing): - - ```bash - npm run generate:sprites - ``` - -3. Create local environment file: +2. Create local environment file: ```bash cp .env.example .env.local ``` -4. Add Supabase values to `.env.local`: +3. Add Supabase values to `.env.local`: ```env NEXT_PUBLIC_SUPABASE_URL= NEXT_PUBLIC_SUPABASE_ANON_KEY= ``` -5. Apply migrations locally (Supabase CLI): +4. Apply migrations locally (Supabase CLI): ```bash supabase db reset ``` -6. Start the app: +5. Start the app: ```bash npm run dev @@ -87,7 +81,7 @@ Stored in `avatar_state`: Style columns are validated against allowed variant ids (`none`, `pants-1`, …). Shop accessories overlay equipped variants at render time without overwriting saved customization. -Sprite assets: `public/character/{layer}/` (regenerate via `npm run generate:sprites`). +Sprite assets are added manually under `public/character/{layer}/` (e.g. `public/character/head/head-1.png`). Paths must match the variant ids in `lib/character/presets.ts` and `supabase/seed.sql`. ## Shop diff --git a/components/character/character-layer-preview.tsx b/components/character/character-layer-preview.tsx index 220e19a..ee415d4 100644 --- a/components/character/character-layer-preview.tsx +++ b/components/character/character-layer-preview.tsx @@ -15,6 +15,8 @@ import { import type { LayerColorState, LayerVariantState } from "@/lib/character/types"; import { cn } from "@/lib/utils"; +const EMPTY_EQUIPPED_ITEMS: string[] = []; + type CharacterLayerPreviewProps = { colors: LayerColorState; variants: LayerVariantState; @@ -27,16 +29,21 @@ type CharacterLayerPreviewProps = { export function CharacterLayerPreview({ colors, variants, - equippedItems = [], + equippedItems, scale = 10, className, compact = false, }: CharacterLayerPreviewProps) { const canvasRef = useRef(null); const [displaySize, setDisplaySize] = useState({ width: 0, height: 0 }); + const resolvedEquippedItems = equippedItems ?? EMPTY_EQUIPPED_ITEMS; + const equippedKey = resolvedEquippedItems.join("\0"); + const equippedItemsRef = useRef(resolvedEquippedItems); + equippedItemsRef.current = resolvedEquippedItems; useEffect(() => { let cancelled = false; + const items = equippedItemsRef.current; async function render() { const canvas = canvasRef.current; @@ -45,62 +52,74 @@ export function CharacterLayerPreview({ const ctx = canvas.getContext("2d", { willReadFrequently: true }); if (!ctx) return; - const displayVariants = applyEquippedItemsToVariants( - variants, - equippedItems, - ); - - const activeLayers = CHARACTER_LAYERS.flatMap((layer) => { - const selected = getSelectedVariant(layer.id, displayVariants[layer.id]); - if (!selected) return []; - return [{ layer, variant: selected }]; - }); - - const images = await Promise.all( - activeLayers.map(async ({ layer, variant }) => ({ - layer, - image: await getCachedImage(variant.src), - })), - ); - - if (cancelled || images.length === 0) return; - - const width = images[0]?.image.width ?? 0; - const height = images[0]?.image.height ?? 0; - - canvas.width = width; - canvas.height = height; - setDisplaySize({ width: width * scale, height: height * scale }); - ctx.clearRect(0, 0, width, height); - - for (const { layer, image } of images) { - const layerColor = colors[layer.id]; - const rgb = hslToRgb(layerColor.h, layerColor.s, layerColor.l); - - drawTintedSprite( - ctx, - image, - rgb, - layer.id === "eyes" - ? { - mode: "eyes", - skinColor: hslToRgb( - colors.skin.h, - colors.skin.s, - colors.skin.l, - ), - } - : undefined, + try { + const displayVariants = applyEquippedItemsToVariants( + variants, + items, ); + + const activeLayers = CHARACTER_LAYERS.flatMap((layer) => { + const selected = getSelectedVariant(layer.id, displayVariants[layer.id]); + if (!selected) return []; + return [{ layer, variant: selected }]; + }); + + const images = await Promise.all( + activeLayers.map(async ({ layer, variant }) => ({ + layer, + image: await getCachedImage(variant.src), + })), + ); + + if (cancelled || images.length === 0) return; + + const width = images[0]?.image.width ?? 0; + const height = images[0]?.image.height ?? 0; + + canvas.width = width; + canvas.height = height; + + const nextWidth = width * scale; + const nextHeight = height * scale; + setDisplaySize((current) => + current.width === nextWidth && current.height === nextHeight + ? current + : { width: nextWidth, height: nextHeight }, + ); + + ctx.clearRect(0, 0, width, height); + + for (const { layer, image } of images) { + const layerColor = colors[layer.id]; + const rgb = hslToRgb(layerColor.h, layerColor.s, layerColor.l); + + drawTintedSprite( + ctx, + image, + rgb, + layer.id === "eyes" + ? { + mode: "eyes", + skinColor: hslToRgb( + colors.skin.h, + colors.skin.s, + colors.skin.l, + ), + } + : undefined, + ); + } + } catch { + // Ignore missing or broken sprite assets so the UI stays responsive. } } - render(); + void render(); return () => { cancelled = true; }; - }, [colors, variants, equippedItems, scale]); + }, [colors, variants, equippedKey, scale]); return (
{ cancelled = true; }; - }, [src, color, skinColor]); + }, [src, color.h, color.s, color.l, skinColor?.h, skinColor?.s, skinColor?.l]); return (
-
- -
-

- Step 1 of 2 -

-

- Welcome to Habit Pet -

-

- Choose a focus area, then design your pet in the next step. -

-
+
+

+ Step 1 of 2 +

+

+ Welcome to Habit Pet +

+

+ Choose a focus area, then design your pet in the next step. +

diff --git a/package-lock.json b/package-lock.json index 8fd3459..ba38b58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "@radix-ui/react-dropdown-menu": "^2.1.14", "@radix-ui/react-label": "^2.1.6", "@radix-ui/react-slot": "^1.2.2", - "@supabase/ssr": "^0.10.3", - "@supabase/supabase-js": "^2.106.1", + "@supabase/ssr": "latest", + "@supabase/supabase-js": "latest", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.511.0", @@ -29,10 +29,7 @@ "autoprefixer": "^10.4.20", "eslint": "^9", "eslint-config-next": "15.3.1", - "pg": "^8.16.0", - "pngjs": "^7.0.0", "postcss": "^8", - "supabase": "^2.101.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", "typescript": "^5", @@ -2955,130 +2952,6 @@ "node": ">=20.0.0" } }, - "node_modules/@supabase/cli-darwin-arm64": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/@supabase/cli-darwin-arm64/-/cli-darwin-arm64-2.101.0.tgz", - "integrity": "sha512-SoWYzu2CIE+rADWsZH6H0YZYF8nV2YMelZLbjAqzahb5RRgFOTMWGhKCUj6g4KlNvocUnU19JObUJ7oCx/9aHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@supabase/cli-darwin-x64": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/@supabase/cli-darwin-x64/-/cli-darwin-x64-2.101.0.tgz", - "integrity": "sha512-rPES03BF9KYkirq3X1Yll+T+eORueRvC858Yp2+2aQMvB4qxi6k+WI+ilCQ7NGzOJ3G0jl/ZG/KsSjZIa4AJ6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@supabase/cli-linux-arm64": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/@supabase/cli-linux-arm64/-/cli-linux-arm64-2.101.0.tgz", - "integrity": "sha512-E+hN1GRIFEi+U1Jf4Omw/gjwpSRTpJ5jai58tQana15AaXt3GCoG7z2YzWVnzD/mvAubTqA7SLHsV+A2lcdTGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@supabase/cli-linux-arm64-musl": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/@supabase/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.101.0.tgz", - "integrity": "sha512-B5JFFKKpJLLMwn8vWUB7j5euZPytiDOLo9WYXp0mshg45OaFKjRcDImSzBzx4k0W8MK2x75rFEURca90Ic60Ug==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@supabase/cli-linux-x64": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/@supabase/cli-linux-x64/-/cli-linux-x64-2.101.0.tgz", - "integrity": "sha512-7iU9uA+8pbmW5uz+yGxU0fM83a+8SH/gtKmSSRZrOu9vIGcugg9gLbN9GlWkG5P7I7wEjnoVKb7Jzea6onSeQA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@supabase/cli-linux-x64-musl": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/@supabase/cli-linux-x64-musl/-/cli-linux-x64-musl-2.101.0.tgz", - "integrity": "sha512-pr9Ar1/aVaQVqjNSfwRgXHWJznQTIaxblGO+hKIl9k55Ajahiq8GConCwx6DjX1Wa9VW//5Dji+O0pkmFSfcHw==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@supabase/cli-windows-arm64": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/@supabase/cli-windows-arm64/-/cli-windows-arm64-2.101.0.tgz", - "integrity": "sha512-mSnFDhTIAe9cr1iifv+DTQzpY/0WphywED2YT3yMGeu94jjxrXrreQkywxEaeSXTnc4ys1umtnWOB1CaBlYKag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@supabase/cli-windows-x64": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/@supabase/cli-windows-x64/-/cli-windows-x64-2.101.0.tgz", - "integrity": "sha512-xzZpybVWq62VF102abZr1EP3BbA2bYecwYm3G2dJk+6ua7nAIodu1fiGHAvBSR0BLGQmesNb6KCneawNL3XBsQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@supabase/functions-js": { "version": "2.106.1", "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.106.1.tgz", @@ -9024,103 +8897,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pg": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", - "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.13.0", - "pg-pool": "^3.14.0", - "pg-protocol": "^1.14.0", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.4.0" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", - "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", - "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", - "dev": true, - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", - "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", - "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", - "dev": true, - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -9160,16 +8936,6 @@ "node": ">= 6" } }, - "node_modules/pngjs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", - "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.19.0" - } - }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -9365,49 +9131,6 @@ "dev": true, "license": "MIT" }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", - "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -10237,16 +9960,6 @@ "node": ">=0.10.0" } }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, "node_modules/srvx": { "version": "0.8.9", "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.8.9.tgz", @@ -10547,26 +10260,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/supabase": { - "version": "2.101.0", - "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.101.0.tgz", - "integrity": "sha512-9wqlvXGeI+VjOMUKOGEpcZzTJGLia6IOv8LWN3W2u8F6Xiv4ZqAuTuXCp3MWfkhV5x9KQaBtN5d6IiLEMWzkJA==", - "dev": true, - "license": "MIT", - "bin": { - "supabase": "dist/supabase.js" - }, - "optionalDependencies": { - "@supabase/cli-darwin-arm64": "2.101.0", - "@supabase/cli-darwin-x64": "2.101.0", - "@supabase/cli-linux-arm64": "2.101.0", - "@supabase/cli-linux-arm64-musl": "2.101.0", - "@supabase/cli-linux-x64": "2.101.0", - "@supabase/cli-linux-x64-musl": "2.101.0", - "@supabase/cli-windows-arm64": "2.101.0", - "@supabase/cli-windows-x64": "2.101.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11517,16 +11210,6 @@ "node": ">= 6.0" } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", diff --git a/package.json b/package.json index 3ced42c..000db8e 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,7 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint .", - "generate:sprites": "node scripts/generate-character-sprites.mjs" + "lint": "eslint ." }, "dependencies": { "@radix-ui/react-checkbox": "^1.3.1", @@ -32,7 +31,6 @@ "autoprefixer": "^10.4.20", "eslint": "^9", "eslint-config-next": "15.3.1", - "pngjs": "^7.0.0", "postcss": "^8", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", diff --git a/scripts/generate-character-sprites.mjs b/scripts/generate-character-sprites.mjs deleted file mode 100644 index 7b19637..0000000 --- a/scripts/generate-character-sprites.mjs +++ /dev/null @@ -1,159 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import { PNG } from "pngjs"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const root = path.resolve(__dirname, ".."); -const publicDir = path.join(root, "public"); - -const W = 32; -const H = 48; - -const RED = [255, 0, 0]; -const DARK_RED = [51, 0, 0]; -const WHITE = [255, 255, 255]; -const MAGENTA = [255, 0, 255]; - -function createCanvas() { - return new PNG({ width: W, height: H }); -} - -function setPixel(png, x, y, [r, g, b], alpha = 255) { - if (x < 0 || y < 0 || x >= W || y >= H) { - return; - } - - const index = (W * y + x) << 2; - png.data[index] = r; - png.data[index + 1] = g; - png.data[index + 2] = b; - png.data[index + 3] = alpha; -} - -function fillRect(png, x, y, width, height, color, alpha = 255) { - for (let row = 0; row < height; row += 1) { - for (let col = 0; col < width; col += 1) { - setPixel(png, x + col, y + row, color, alpha); - } - } -} - -function writeSprite(relativePath, png) { - const filePath = path.join(publicDir, relativePath); - fs.mkdirSync(path.dirname(filePath), { recursive: true }); - fs.writeFileSync(filePath, PNG.sync.write(png)); - console.log(`wrote ${relativePath}`); -} - -function drawSkin() { - const png = createCanvas(); - fillRect(png, 11, 6, 10, 10, RED); - fillRect(png, 10, 16, 12, 14, RED); - fillRect(png, 8, 30, 6, 12, RED); - fillRect(png, 18, 30, 6, 12, RED); - fillRect(png, 6, 18, 4, 10, RED); - fillRect(png, 22, 18, 4, 10, RED); - fillRect(png, 12, 8, 8, 2, DARK_RED); - writeSprite("character/skin/skin-1.png", png); -} - -function drawPantsVariant(index, widthOffset) { - const png = createCanvas(); - fillRect(png, 8 + widthOffset, 28, 7 - widthOffset, 14, RED); - fillRect(png, 17, 28, 7 - widthOffset, 14, RED); - if (index === 2) { - fillRect(png, 8, 26, 16, 4, RED); - } - if (index === 3) { - fillRect(png, 7, 24, 18, 6, RED); - } - writeSprite(`character/pants/pants-${index}.png`, png); -} - -function drawShoesVariant(index) { - const png = createCanvas(); - fillRect(png, 6, 40, 8, 4, RED); - fillRect(png, 18, 40, 8, 4, RED); - if (index >= 2) { - fillRect(png, 5, 42, 10, 2, DARK_RED); - fillRect(png, 17, 42, 10, 2, DARK_RED); - } - if (index === 3) { - fillRect(png, 4, 38, 12, 3, RED); - fillRect(png, 16, 38, 12, 3, RED); - } - writeSprite(`character/shoes/shoes-${index}.png`, png); -} - -function drawTorsoVariant(index) { - const png = createCanvas(); - fillRect(png, 9, 16, 14, 14, RED); - if (index >= 2) { - fillRect(png, 7, 18, 3, 10, RED); - fillRect(png, 22, 18, 3, 10, RED); - } - if (index >= 3) { - fillRect(png, 11, 18, 10, 8, DARK_RED); - } - if (index === 4) { - fillRect(png, 10, 14, 12, 4, RED); - } - writeSprite(`character/torso/torso-${index}.png`, png); -} - -function drawEyesVariant(index) { - const png = createCanvas(); - fillRect(png, 12, 10, 3, 3, index === 1 ? RED : WHITE); - fillRect(png, 17, 10, 3, 3, index === 1 ? RED : WHITE); - fillRect(png, 13, 11, 1, 1, MAGENTA); - fillRect(png, 18, 11, 1, 1, MAGENTA); - if (index === 2) { - fillRect(png, 11, 9, 5, 5, WHITE); - fillRect(png, 16, 9, 5, 5, WHITE); - fillRect(png, 13, 11, 2, 2, RED); - fillRect(png, 18, 11, 2, 2, RED); - } - writeSprite(`character/eyes/eyes-${index}.png`, png); -} - -function drawHeadVariant(index) { - const png = createCanvas(); - const y = index <= 3 ? 4 : 2; - fillRect(png, 10, y, 12, index >= 5 ? 12 : 10, RED); - if (index === 2) { - fillRect(png, 8, y + 2, 3, 3, RED); - fillRect(png, 21, y + 2, 3, 3, RED); - } - if (index === 3) { - fillRect(png, 9, y - 1, 14, 3, RED); - } - if (index >= 4) { - fillRect(png, 11, y + 1, 10, 4, DARK_RED); - } - if (index >= 6) { - fillRect(png, 9, y - 2, 14, 4, RED); - } - if (index === 7) { - fillRect(png, 7, y, 4, 8, RED); - fillRect(png, 21, y, 4, 8, RED); - } - writeSprite(`character/head/head-${index}.png`, png); -} - -drawSkin(); -for (let index = 1; index <= 3; index += 1) { - drawPantsVariant(index, index - 1); - drawShoesVariant(index); -} -for (let index = 1; index <= 4; index += 1) { - drawTorsoVariant(index); -} -for (let index = 1; index <= 2; index += 1) { - drawEyesVariant(index); -} -for (let index = 1; index <= 7; index += 1) { - drawHeadVariant(index); -} - -console.log("Character sprites generated.");