Revert "Redraw wall/bush maze tiles with richer pixel-art detail"
The user asked to go back to the pre-commit assets. The original
BMP_{1..4}_*.png tiles have been restored. The generator script
introduced in that commit is also removed.
|
Before Width: | Height: | Size: 1014 B After Width: | Height: | Size: 174 B |
|
Before Width: | Height: | Size: 930 B After Width: | Height: | Size: 189 B |
|
Before Width: | Height: | Size: 955 B After Width: | Height: | Size: 184 B |
|
Before Width: | Height: | Size: 993 B After Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 1015 B After Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 193 B |
|
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 171 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 194 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 191 B |
|
Before Width: | Height: | Size: 991 B After Width: | Height: | Size: 187 B |
|
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 187 B |
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 187 B |
|
Before Width: | Height: | Size: 1004 B After Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 189 B |
|
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 184 B |
|
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 193 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 193 B |
|
Before Width: | Height: | Size: 994 B After Width: | Height: | Size: 171 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 195 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 193 B |
|
Before Width: | Height: | Size: 1013 B After Width: | Height: | Size: 184 B |
|
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 187 B |
|
Before Width: | Height: | Size: 956 B After Width: | Height: | Size: 187 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 159 B |
|
Before Width: | Height: | Size: 950 B After Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 160 B |
|
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 167 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 172 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 174 B |
|
Before Width: | Height: | Size: 986 B After Width: | Height: | Size: 155 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 168 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 162 B |
|
Before Width: | Height: | Size: 1019 B After Width: | Height: | Size: 161 B |
|
Before Width: | Height: | Size: 941 B After Width: | Height: | Size: 170 B |
|
Before Width: | Height: | Size: 954 B After Width: | Height: | Size: 167 B |
|
Before Width: | Height: | Size: 1012 B After Width: | Height: | Size: 218 B |
|
Before Width: | Height: | Size: 952 B After Width: | Height: | Size: 214 B |
|
Before Width: | Height: | Size: 964 B After Width: | Height: | Size: 217 B |
|
Before Width: | Height: | Size: 970 B After Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 211 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 967 B After Width: | Height: | Size: 222 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 218 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 208 B |
|
Before Width: | Height: | Size: 1011 B After Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 195 B |
|
Before Width: | Height: | Size: 957 B After Width: | Height: | Size: 208 B |
@@ -1,401 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Regenerate the 32x32 wall/bush tile sprites for the maze with more detail.
|
|
||||||
|
|
||||||
Replaces the noisy checkerboard BMP_{theme}_{N,S,E,W,NE,NW,SE,SW,EN,ES,WN,WS}.png
|
|
||||||
tiles with hand-designed pixel art bush clusters, keeping the original
|
|
||||||
palette per theme (or a richer one for theme 1).
|
|
||||||
|
|
||||||
Each tile is 32x32. The wall/bush area is on a specific side(s) of the tile:
|
|
||||||
N = wall on the top side, passage below
|
|
||||||
S = wall on the bottom side, passage above
|
|
||||||
E = wall on the right side, passage left
|
|
||||||
W = wall on the left side, passage right
|
|
||||||
NE = wall on top + right (outer corner)
|
|
||||||
NW = wall on top + left
|
|
||||||
SE = wall on bottom + right
|
|
||||||
SW = wall on bottom + left
|
|
||||||
EN = inner corner: passage top-left, wall bottom-right (L-shape)
|
|
||||||
ES = inner corner: passage top-right, wall bottom-left
|
|
||||||
WN = inner corner: passage bottom-left, wall top-right
|
|
||||||
WS = inner corner: passage bottom-right, wall top-left
|
|
||||||
|
|
||||||
Run from repo root:
|
|
||||||
python3 tools/redraw_walls.py
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# Output directory
|
|
||||||
OUT_DIR = Path(__file__).resolve().parents[1] / "assets" / "Rat"
|
|
||||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
SIZE = 32
|
|
||||||
TRANSPARENT = (128, 128, 128, 255) # engine treats this color as transparent
|
|
||||||
|
|
||||||
# Palette per theme. Each theme gets a 5-color bush palette:
|
|
||||||
# (deep_shadow, dark, mid, light, highlight)
|
|
||||||
THEME_PALETTES = {
|
|
||||||
# Theme 1: lush green bushes (the canonical "hedge maze" look).
|
|
||||||
1: {
|
|
||||||
"deep": (0, 70, 0, 255),
|
|
||||||
"dark": (0, 110, 0, 255),
|
|
||||||
"mid": (0, 160, 0, 255),
|
|
||||||
"light": (60, 220, 60, 255),
|
|
||||||
"highlight": (170, 255, 170, 255),
|
|
||||||
},
|
|
||||||
# Theme 2: yellow/golden dry brush.
|
|
||||||
2: {
|
|
||||||
"deep": (90, 90, 0, 255),
|
|
||||||
"dark": (130, 130, 0, 255),
|
|
||||||
"mid": (200, 200, 0, 255),
|
|
||||||
"light": (240, 240, 80, 255),
|
|
||||||
"highlight": (255, 255, 200, 255),
|
|
||||||
},
|
|
||||||
# Theme 3: white/silver snowy brush.
|
|
||||||
3: {
|
|
||||||
"deep": (140, 140, 140, 255),
|
|
||||||
"dark": (180, 180, 180, 255),
|
|
||||||
"mid": (220, 220, 220, 255),
|
|
||||||
"light": (245, 245, 245, 255),
|
|
||||||
"highlight": (255, 255, 255, 255),
|
|
||||||
},
|
|
||||||
# Theme 4: ashen/red lava brush.
|
|
||||||
4: {
|
|
||||||
"deep": (90, 0, 0, 255),
|
|
||||||
"dark": (150, 25, 25, 255),
|
|
||||||
"mid": (210, 50, 50, 255),
|
|
||||||
"light": (240, 110, 90, 255),
|
|
||||||
"highlight": (255, 200, 180, 255),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Deterministic pseudo-random helpers.
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
def _hash(x: int, y: int, salt: int = 0) -> int:
|
|
||||||
"""Tiny integer hash in [0, 65536)."""
|
|
||||||
h = (x * 374761393 + y * 668265263 + salt * 2147483647) & 0xFFFFFFFF
|
|
||||||
h = (h ^ (h >> 13)) * 1274126177 & 0xFFFFFFFF
|
|
||||||
return h & 0xFFFF
|
|
||||||
|
|
||||||
|
|
||||||
def _chance(x: int, y: int, threshold: int, salt: int = 0) -> bool:
|
|
||||||
return _hash(x, y, salt) < threshold
|
|
||||||
|
|
||||||
|
|
||||||
def _hash_seed(x: int, y: int, salt: int) -> int:
|
|
||||||
"""Return a deterministic seed in [0, 8) used to pick a cluster variant."""
|
|
||||||
return _hash(x, y, salt) % 8
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Cluster patterns: small 3x3 / 4x4 / 5x5 stamps used to add visible "leaves"
|
|
||||||
# inside the bush body. Each cluster is a small relative offset (+dx, +dy) and
|
|
||||||
# a color choice.
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# A cluster is (dx, dy, color_key). The cluster is placed at integer pixel
|
|
||||||
# positions; each pixel within a small radius is set to that color (overriding
|
|
||||||
# the base body color). This adds visible "leaf clusters" on top of the body.
|
|
||||||
def _leaf_cluster_positions(cx: int, cy: int, salt: int) -> list:
|
|
||||||
"""Return [(dx, dy, color_key), ...] relative to (cx, cy) for one leaf."""
|
|
||||||
# Choose a variant: 0-7
|
|
||||||
variant = _hash_seed(cx, cy, salt)
|
|
||||||
# Variants:
|
|
||||||
# 0: 3 leaves in a triangle
|
|
||||||
# 1: 4 leaves in a small plus
|
|
||||||
# 2: 5 leaves in a + shape
|
|
||||||
# 3: small cluster of 3
|
|
||||||
# 4: scattered 3
|
|
||||||
# 5: 4 leaves diagonal
|
|
||||||
# 6: single + 2 around
|
|
||||||
# 7: tight 5 cluster
|
|
||||||
clusters = {
|
|
||||||
0: [(0, 0, "highlight"), (-2, 1, "light"), (1, 2, "light")],
|
|
||||||
1: [(0, 0, "highlight"), (2, 0, "light"), (-2, 0, "light"), (0, 2, "light")],
|
|
||||||
2: [(0, 0, "highlight"), (2, 0, "light"), (-2, 0, "light"), (0, 2, "light"), (0, -2, "mid")],
|
|
||||||
3: [(0, 0, "light"), (-1, 1, "mid"), (1, -1, "mid")],
|
|
||||||
4: [(0, 0, "highlight"), (2, 2, "light"), (-2, -1, "light")],
|
|
||||||
5: [(0, 0, "highlight"), (2, 2, "light"), (-2, -2, "light"), (1, -1, "mid")],
|
|
||||||
6: [(0, 0, "highlight"), (-1, 0, "light"), (0, -1, "light")],
|
|
||||||
7: [(0, 0, "highlight"), (1, 0, "light"), (-1, 0, "light"), (0, 1, "light"), (0, -1, "light")],
|
|
||||||
}
|
|
||||||
return clusters[variant]
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Tile layout: where is the wall (muro di cespugli)?
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
def ideal_mask(shape: str) -> list:
|
|
||||||
"""Return a 32x32 list-of-lists mask of the IDEAL wall footprint (True = bush)."""
|
|
||||||
mask = [[False] * SIZE for _ in range(SIZE)]
|
|
||||||
if shape == "N":
|
|
||||||
for y in range(SIZE // 2):
|
|
||||||
for x in range(SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
elif shape == "S":
|
|
||||||
for y in range(SIZE // 2, SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
elif shape == "E":
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE // 2, SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
elif shape == "W":
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE // 2):
|
|
||||||
mask[y][x] = True
|
|
||||||
elif shape == "NE":
|
|
||||||
for y in range(SIZE // 2):
|
|
||||||
for x in range(SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE // 2, SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
elif shape == "NW":
|
|
||||||
for y in range(SIZE // 2):
|
|
||||||
for x in range(SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE // 2):
|
|
||||||
mask[y][x] = True
|
|
||||||
elif shape == "SE":
|
|
||||||
for y in range(SIZE // 2, SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE // 2, SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
elif shape == "SW":
|
|
||||||
for y in range(SIZE // 2, SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE // 2):
|
|
||||||
mask[y][x] = True
|
|
||||||
elif shape == "EN":
|
|
||||||
for y in range(SIZE // 2, SIZE):
|
|
||||||
for x in range(SIZE // 2, SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
for x in range(SIZE // 2, SIZE):
|
|
||||||
mask[SIZE // 2][x] = True
|
|
||||||
for y in range(SIZE // 2, SIZE):
|
|
||||||
mask[y][SIZE // 2] = True
|
|
||||||
elif shape == "ES":
|
|
||||||
for y in range(SIZE // 2, SIZE):
|
|
||||||
for x in range(SIZE // 2):
|
|
||||||
mask[y][x] = True
|
|
||||||
for x in range(SIZE // 2):
|
|
||||||
mask[SIZE // 2][x] = True
|
|
||||||
for y in range(SIZE // 2, SIZE):
|
|
||||||
mask[y][SIZE // 2 - 1] = True
|
|
||||||
elif shape == "WN":
|
|
||||||
for y in range(SIZE // 2):
|
|
||||||
for x in range(SIZE // 2, SIZE):
|
|
||||||
mask[y][x] = True
|
|
||||||
for x in range(SIZE // 2, SIZE):
|
|
||||||
mask[SIZE // 2 - 1][x] = True
|
|
||||||
for y in range(SIZE // 2):
|
|
||||||
mask[y][SIZE // 2] = True
|
|
||||||
elif shape == "WS":
|
|
||||||
for y in range(SIZE // 2):
|
|
||||||
for x in range(SIZE // 2):
|
|
||||||
mask[y][x] = True
|
|
||||||
for x in range(SIZE // 2):
|
|
||||||
mask[SIZE // 2 - 1][x] = True
|
|
||||||
for y in range(SIZE // 2):
|
|
||||||
mask[y][SIZE // 2 - 1] = True
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Unknown shape: {shape}")
|
|
||||||
return mask
|
|
||||||
|
|
||||||
|
|
||||||
def erode_boundary(mask: list, salt: int) -> list:
|
|
||||||
"""Make the wall/passage boundary irregular and natural."""
|
|
||||||
new_mask = [row[:] for row in mask]
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
is_wall = mask[y][x]
|
|
||||||
diffs = 0
|
|
||||||
for dy, dx in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
|
|
||||||
ny, nx = y + dy, x + dx
|
|
||||||
if not (0 <= ny < SIZE and 0 <= nx < SIZE) or mask[ny][nx] != is_wall:
|
|
||||||
diffs += 1
|
|
||||||
if diffs == 0:
|
|
||||||
continue
|
|
||||||
if is_wall:
|
|
||||||
# carve a notch out of the bush (more aggressive)
|
|
||||||
if _chance(x, y, 7500, salt):
|
|
||||||
new_mask[y][x] = False
|
|
||||||
else:
|
|
||||||
# let a leaf cluster stick out into the passage
|
|
||||||
if _chance(x, y, 5500, salt + 1):
|
|
||||||
near = False
|
|
||||||
for dy in range(-2, 3):
|
|
||||||
for dx in range(-2, 3):
|
|
||||||
ny, nx = y + dy, x + dx
|
|
||||||
if 0 <= ny < SIZE and 0 <= nx < SIZE and mask[ny][nx]:
|
|
||||||
near = True
|
|
||||||
break
|
|
||||||
if near:
|
|
||||||
break
|
|
||||||
if near:
|
|
||||||
new_mask[y][x] = True
|
|
||||||
return new_mask
|
|
||||||
|
|
||||||
|
|
||||||
def compute_depth(mask: list) -> list:
|
|
||||||
"""For each wall pixel, return its distance from the wall/passage boundary.
|
|
||||||
|
|
||||||
Used to determine which shade of green to use (deeper = darker).
|
|
||||||
"""
|
|
||||||
depth = [[0] * SIZE for _ in range(SIZE)]
|
|
||||||
# Boundary pixels have depth 1; interior grows with distance from boundary.
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
if not mask[y][x]:
|
|
||||||
continue
|
|
||||||
# find nearest passage pixel via BFS-lite (4-direction)
|
|
||||||
# Use a simple expansion: search outward until we find a False.
|
|
||||||
d = 1
|
|
||||||
found = False
|
|
||||||
while d < SIZE:
|
|
||||||
ring = False
|
|
||||||
for dy in range(-d, d + 1):
|
|
||||||
for dx in range(-d, d + 1):
|
|
||||||
if abs(dy) != d and abs(dx) != d:
|
|
||||||
continue
|
|
||||||
ny, nx = y + dy, x + dx
|
|
||||||
if not (0 <= ny < SIZE and 0 <= nx < SIZE):
|
|
||||||
ring = True
|
|
||||||
break
|
|
||||||
if not mask[ny][nx]:
|
|
||||||
ring = True
|
|
||||||
break
|
|
||||||
if ring:
|
|
||||||
break
|
|
||||||
if ring:
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
d += 1
|
|
||||||
depth[y][x] = d if found else SIZE
|
|
||||||
return depth
|
|
||||||
|
|
||||||
|
|
||||||
def fill_bush_detail(mask: list, depth: list, pal: dict, salt: int) -> list:
|
|
||||||
"""Assign palette colors to wall pixels, with leaf clusters overlaid."""
|
|
||||||
pixels = [[None] * SIZE for _ in range(SIZE)]
|
|
||||||
# 1) base layer: pick color by depth.
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
if not mask[y][x]:
|
|
||||||
pixels[y][x] = TRANSPARENT
|
|
||||||
continue
|
|
||||||
d = depth[y][x]
|
|
||||||
if d <= 1:
|
|
||||||
pixels[y][x] = pal["dark"]
|
|
||||||
elif d == 2:
|
|
||||||
pixels[y][x] = pal["dark"]
|
|
||||||
elif d == 3:
|
|
||||||
pixels[y][x] = pal["mid"]
|
|
||||||
elif d == 4:
|
|
||||||
pixels[y][x] = pal["mid"]
|
|
||||||
else:
|
|
||||||
pixels[y][x] = pal["deep"]
|
|
||||||
# 2) scattered mid/light/highlight "speckles" to break uniformity.
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
if not mask[y][x]:
|
|
||||||
continue
|
|
||||||
d = depth[y][x]
|
|
||||||
# A light speckle anywhere on the bush
|
|
||||||
if d <= 4 and _chance(x, y, 4500, salt + 11):
|
|
||||||
pixels[y][x] = pal["light"]
|
|
||||||
# Highlight speckle near the edge only
|
|
||||||
if d <= 2 and _chance(x, y, 3000, salt + 23):
|
|
||||||
pixels[y][x] = pal["highlight"]
|
|
||||||
# 3) Overlay leaf clusters at deterministic anchor points.
|
|
||||||
# Anchors are picked from a low-density grid so each tile gets a few clusters.
|
|
||||||
anchor_spacing = 6
|
|
||||||
for ay in range(2, SIZE - 2, anchor_spacing):
|
|
||||||
for ax in range(2, SIZE - 2, anchor_spacing):
|
|
||||||
if not mask[ay][ax]:
|
|
||||||
continue
|
|
||||||
# Some anchors are skipped for variety
|
|
||||||
if not _chance(ax, ay, 22000, salt + 31):
|
|
||||||
continue
|
|
||||||
leaves = _leaf_cluster_positions(ax, ay, salt + 41)
|
|
||||||
for dx, dy, color_key in leaves:
|
|
||||||
nx, ny = ax + dx, ay + dy
|
|
||||||
if 0 <= nx < SIZE and 0 <= ny < SIZE and mask[ny][nx]:
|
|
||||||
pixels[ny][nx] = pal[color_key]
|
|
||||||
# 4) Soft "rim" highlight along the wall/passage boundary on the
|
|
||||||
# passage-facing side (depth=1) — adds a sense of light catching the
|
|
||||||
# outer leaves.
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
if not mask[y][x]:
|
|
||||||
continue
|
|
||||||
if depth[y][x] == 1 and _chance(x, y, 5000, salt + 53):
|
|
||||||
pixels[y][x] = pal["light"]
|
|
||||||
return pixels
|
|
||||||
|
|
||||||
|
|
||||||
def render_tile(shape: str, theme_index: int) -> list:
|
|
||||||
pal = THEME_PALETTES[theme_index]
|
|
||||||
salt = (theme_index * 1000) + sum(ord(c) for c in shape)
|
|
||||||
base = ideal_mask(shape)
|
|
||||||
eroded = erode_boundary(base, salt)
|
|
||||||
depth = compute_depth(eroded)
|
|
||||||
pixels = fill_bush_detail(eroded, depth, pal, salt + 99)
|
|
||||||
return pixels
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# PNG writer (PIL mode P matching original BMP_{theme}_{shape}.png format).
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
def save_png_mode_p(pixels: list, path: Path) -> None:
|
|
||||||
from PIL import Image
|
|
||||||
# Build a unique palette
|
|
||||||
used = {}
|
|
||||||
# Reserve index 0 for the transparent gray (engine convention)
|
|
||||||
used[TRANSPARENT] = 0
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
c = pixels[y][x]
|
|
||||||
if c not in used:
|
|
||||||
used[c] = len(used)
|
|
||||||
palette = [0] * (256 * 3)
|
|
||||||
for color, idx in used.items():
|
|
||||||
palette[idx * 3] = color[0]
|
|
||||||
palette[idx * 3 + 1] = color[1]
|
|
||||||
palette[idx * 3 + 2] = color[2]
|
|
||||||
pal_img = Image.new("P", (SIZE, SIZE))
|
|
||||||
pal_img.putpalette(palette)
|
|
||||||
for y in range(SIZE):
|
|
||||||
for x in range(SIZE):
|
|
||||||
pal_img.putpixel((x, y), used[pixels[y][x]])
|
|
||||||
pal_img.save(path, optimize=True)
|
|
||||||
|
|
||||||
|
|
||||||
SHAPES = ["N", "S", "E", "W",
|
|
||||||
"NE", "NW", "SE", "SW",
|
|
||||||
"EN", "ES", "WN", "WS"]
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
written = []
|
|
||||||
for theme_index in range(1, 5):
|
|
||||||
for shape in SHAPES:
|
|
||||||
pixels = render_tile(shape, theme_index)
|
|
||||||
path = OUT_DIR / f"BMP_{theme_index}_{shape}.png"
|
|
||||||
save_png_mode_p(pixels, path)
|
|
||||||
written.append(str(path.relative_to(OUT_DIR.parents[1])))
|
|
||||||
print(f"Wrote {len(written)} tile sprites.")
|
|
||||||
for w in written:
|
|
||||||
print(f" {w}")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||