many
This commit is contained in:
parent
0b9ff1363b
commit
0a6148c73f
67 changed files with 2653 additions and 1519 deletions
|
|
@ -6,11 +6,20 @@ appearance. Do not invent separate tool-UI-only designs.
|
|||
"""
|
||||
from __future__ import annotations
|
||||
from pathlib import Path
|
||||
import math
|
||||
import json, math, re
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
OUT = ROOT / "assets" / "ui"
|
||||
REGISTRY = ROOT / "js" / "item_visual_registry.js"
|
||||
|
||||
|
||||
def visual_registry() -> dict:
|
||||
text = REGISTRY.read_text(encoding="utf-8")
|
||||
match = re.search(r"String\.raw`([\s\S]*?)`", text)
|
||||
if not match:
|
||||
raise RuntimeError("Could not find JSON visual registry in js/item_visual_registry.js")
|
||||
return json.loads(match.group(1))
|
||||
|
||||
|
||||
def save_icon(name: str, draw_fn) -> None:
|
||||
|
|
@ -29,6 +38,45 @@ def draw_drop(fill, stroke, highlight):
|
|||
return fn
|
||||
|
||||
|
||||
def rgba(value, fallback):
|
||||
if not value:
|
||||
return fallback
|
||||
if value.startswith("#"):
|
||||
value = value.lstrip("#")
|
||||
if len(value) == 6:
|
||||
return tuple(int(value[i:i+2], 16) for i in (0, 2, 4)) + (255,)
|
||||
match = re.match(r"rgba?\(([^)]+)\)", value.replace(" ", ""))
|
||||
if match:
|
||||
parts = match.group(1).split(",")
|
||||
r, g, b = (int(float(parts[i])) for i in range(3))
|
||||
a = int(float(parts[3]) * 255) if len(parts) > 3 else 255
|
||||
return (r, g, b, a)
|
||||
return fallback
|
||||
|
||||
|
||||
def draw_powder(fill, stroke, highlight):
|
||||
def fn(d: ImageDraw.ImageDraw) -> None:
|
||||
d.ellipse([18, 62, 78, 76], fill=(60, 45, 35, 36))
|
||||
for i in range(20):
|
||||
x = 48 + math.cos(i * 1.9) * (8 + (i % 5) * 4)
|
||||
y = 56 - abs(math.sin(i * 1.3)) * 24 + (i % 3) * 3
|
||||
r = 5 + (i % 4)
|
||||
d.ellipse([x - r, y - r * 0.65, x + r, y + r * 0.65], fill=highlight if i % 4 == 0 else fill, outline=stroke, width=1)
|
||||
d.ellipse([31, 31, 52, 39], fill=rgba("rgba(255,255,255,0.62)", (255, 255, 255, 158)))
|
||||
return fn
|
||||
|
||||
|
||||
def draw_pill(fill, stroke, highlight, scale=1.0):
|
||||
def fn(d: ImageDraw.ImageDraw) -> None:
|
||||
cx, cy = 48, 48
|
||||
w, h = 66 * scale, 28 * scale
|
||||
box = [cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2]
|
||||
d.rounded_rectangle(box, radius=int(h / 2), fill=fill, outline=stroke, width=4)
|
||||
d.line([(cx, cy - h / 2 + 2), (cx, cy + h / 2 - 2)], fill=highlight, width=4)
|
||||
d.ellipse([cx - w * 0.32, cy - h * 0.24, cx - w * 0.08, cy - h * 0.04], fill=(255, 255, 255, 110))
|
||||
return fn
|
||||
|
||||
|
||||
def draw_laxative(d: ImageDraw.ImageDraw) -> None:
|
||||
d.rounded_rectangle([14, 28, 82, 68], radius=15, fill=(244, 212, 154, 255), outline=(98, 61, 29, 235), width=4)
|
||||
# Zunchi mound motif inside the tablet.
|
||||
|
|
@ -68,13 +116,31 @@ def draw_sleep(d: ImageDraw.ImageDraw) -> None:
|
|||
|
||||
|
||||
def main() -> None:
|
||||
save_icon("tool_laxative.webp", draw_laxative)
|
||||
save_icon("tool_mystery_drug.webp", draw_mystery)
|
||||
save_icon("tool_ammo.webp", draw_bullet)
|
||||
save_icon("tool_sleep_drug.webp", draw_sleep)
|
||||
save_icon("tool_water.webp", draw_drop((86, 157, 224, 220), (45, 101, 168, 150), (255, 255, 255, 150)))
|
||||
save_icon("tool_zunda_juice.webp", draw_drop((125, 218, 82, 235), (63, 150, 50, 160), (238, 255, 226, 160)))
|
||||
save_icon("tool_mercury.webp", draw_drop((210, 216, 222, 240), (112, 122, 132, 165), (255, 255, 255, 170)))
|
||||
registry = visual_registry()
|
||||
renderer_map = {
|
||||
"laxative_tablet": draw_laxative,
|
||||
"mystery_tablet": draw_mystery,
|
||||
"bullet": draw_bullet,
|
||||
"sleep_tablet": draw_sleep,
|
||||
}
|
||||
for item_id, visual in registry.items():
|
||||
if item_id == "ball":
|
||||
continue
|
||||
palette = visual.get("palette") or {}
|
||||
renderer = visual.get("renderer")
|
||||
fill = rgba(palette.get("fill"), (255, 250, 241, 255))
|
||||
stroke = rgba(palette.get("stroke"), (141, 111, 72, 220))
|
||||
highlight = rgba(palette.get("highlight") or palette.get("accent"), (255, 255, 255, 150))
|
||||
if renderer == "oval_droplet":
|
||||
draw_fn = draw_drop(fill, stroke, highlight)
|
||||
elif renderer == "powder_pile":
|
||||
draw_fn = draw_powder(fill, stroke, highlight)
|
||||
elif renderer == "split_pill":
|
||||
draw_fn = draw_pill(fill, stroke, highlight, visual.get("uiPreviewScale") or visual.get("scale") or 1.0)
|
||||
else:
|
||||
draw_fn = renderer_map.get(renderer)
|
||||
if draw_fn:
|
||||
save_icon(f"tool_{item_id}.webp", draw_fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue