feat: A-frame tent + portal walk-through field + texture polish
All checks were successful
Deploy Addons / deploy (push) Successful in 14s
All checks were successful
Deploy Addons / deploy (push) Successful in 14s
- camping: replace cube tent with A-frame slope panels (tent_panel_l/r) + cardinal_direction permutations; vote-skip sleep that mixes player.isSleeping bed sleepers with tent occupants and respects the playersSleepingPercentage gamerule; new weathered-canvas texture. - lobby: walk-through silverlabs:portal_field block (no collision, translucent swirl, cross-plane geo) auto-placed above each portal frame; invisible silverlabs:portal_label entity floats above each portal with the destination world name; transfer detection now scans down through the field to find the destination frame. - postal: regenerate post_office and mailbox block textures so they fill the full block face (brick + POST plaque, full red panel with slot/latch /flag/rivets) instead of small sprites floating on transparent. - dynamite + tow-boat: ship the addons (volumes wired into all four worlds; enabled_packs registers them into Mya's world). - art: build-textures.py extended; build-art-catalog.py added to project. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
111
scripts/build-art-catalog.py
Normal file
111
scripts/build-art-catalog.py
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Build a contact sheet of every texture under art/ — each PNG upscaled
|
||||
with nearest-neighbour (so pixels stay crisp) and labelled with its
|
||||
relative path. Output: art/CATALOG.png
|
||||
|
||||
Run from repo root:
|
||||
python3 scripts/build-art-catalog.py
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
ART = REPO / "art"
|
||||
OUT = ART / "CATALOG.png"
|
||||
|
||||
TILE = 128 # upscaled texture size
|
||||
COLS = 6 # tiles per row
|
||||
PAD_X = 20 # horizontal padding around each tile
|
||||
PAD_Y = 60 # vertical padding (extra room for label below)
|
||||
LABEL_PX = 14 # label font size
|
||||
BG = (30, 30, 46) # dark background
|
||||
FG = (230, 230, 240)
|
||||
SECTION_BG = (50, 60, 90)
|
||||
|
||||
|
||||
def load_font(size):
|
||||
for cand in (
|
||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
||||
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
|
||||
"/mnt/c/Windows/Fonts/consola.ttf",
|
||||
"/mnt/c/Windows/Fonts/arial.ttf",
|
||||
):
|
||||
if os.path.exists(cand):
|
||||
return ImageFont.truetype(cand, size)
|
||||
return ImageFont.load_default()
|
||||
|
||||
|
||||
def collect():
|
||||
"""Return {pack_name: [(relative_path, abs_path), ...]} grouped & sorted."""
|
||||
groups = {}
|
||||
for png in sorted(ART.rglob("*.png")):
|
||||
if png.name == "CATALOG.png":
|
||||
continue
|
||||
rel = png.relative_to(ART)
|
||||
pack = rel.parts[0]
|
||||
groups.setdefault(pack, []).append((rel, png))
|
||||
return groups
|
||||
|
||||
|
||||
def upscale(png_path, size):
|
||||
img = Image.open(png_path).convert("RGBA")
|
||||
return img.resize((size, size), Image.NEAREST)
|
||||
|
||||
|
||||
def main():
|
||||
groups = collect()
|
||||
if not groups:
|
||||
print("No PNGs found under art/. Nothing to do.")
|
||||
return
|
||||
|
||||
font_label = load_font(LABEL_PX)
|
||||
font_section = load_font(LABEL_PX + 8)
|
||||
|
||||
# Calculate overall canvas size
|
||||
section_height = LABEL_PX + 24
|
||||
tile_block_h = TILE + PAD_Y
|
||||
total_h = 40
|
||||
for pack, items in groups.items():
|
||||
rows = (len(items) + COLS - 1) // COLS
|
||||
total_h += section_height + rows * tile_block_h + 20
|
||||
total_w = COLS * (TILE + PAD_X) + PAD_X + 40
|
||||
|
||||
canvas = Image.new("RGBA", (total_w, total_h), BG)
|
||||
draw = ImageDraw.Draw(canvas)
|
||||
|
||||
y = 20
|
||||
for pack, items in groups.items():
|
||||
# Section header
|
||||
draw.rectangle([20, y, total_w - 20, y + section_height - 4], fill=SECTION_BG)
|
||||
draw.text((32, y + 4), pack, font=font_section, fill=FG)
|
||||
y += section_height + 6
|
||||
|
||||
# Tiles
|
||||
for i, (rel, abs_path) in enumerate(items):
|
||||
col = i % COLS
|
||||
row = i // COLS
|
||||
x = 20 + PAD_X // 2 + col * (TILE + PAD_X)
|
||||
ty = y + row * tile_block_h
|
||||
|
||||
try:
|
||||
tile = upscale(abs_path, TILE)
|
||||
canvas.paste(tile, (x, ty), tile)
|
||||
except Exception as e:
|
||||
draw.rectangle([x, ty, x + TILE, ty + TILE], outline=(200, 80, 80), width=2)
|
||||
draw.text((x + 4, ty + 4), f"ERR: {e}", font=font_label, fill=(255, 120, 120))
|
||||
|
||||
# Label: show subpath + filename below the tile
|
||||
label = "/".join(rel.parts[1:]) # drop pack prefix
|
||||
draw.text((x, ty + TILE + 4), label, font=font_label, fill=FG)
|
||||
|
||||
rows = (len(items) + COLS - 1) // COLS
|
||||
y += rows * tile_block_h + 20
|
||||
|
||||
canvas.save(OUT)
|
||||
print(f"Wrote {OUT} — {sum(len(v) for v in groups.values())} textures across {len(groups)} packs.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user