Adrian (2026-11-02): the cores should be transparent so the Pokémon shows; the Pokémon's death sound when a sacrifice is made or a rebirth ends; then start the next phase (Phase 6 approved). - Cores drawn as a solid frame around tinted glass, rendered translucent, no occlusion (tools/machine_assets.py). - The shown Pokémon plays Cobblemon's faint animation and its cry pitched down (Cobblemon has no death sounds), vanishes after 2 s; a reborn Pokémon then shows in its new form. Tests check the new form. - Phase 7 decisions (every guardian gets area attacks, Groudon for the Basalt Furnace, Giratina replaces the Ender Dragon, dying or leaving the arena loses) and design in docs/design.md; README status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
168 lines
5.7 KiB
Python
168 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Writes the machine cores' textures, block models, item models and blockstates (Phase 6).
|
|
|
|
Each core is an "orientable" block (front, side, top) facing the player who placed it. The 16x16 textures are drawn
|
|
here, from a palette and an emblem per machine, so they can be redrawn after a change. Our own art (pack license).
|
|
The cores are see-through (Adrian, 2026-11-02: the Pokémon in the chamber behind the core must be visible): a solid
|
|
frame around tinted glass, the emblem faint on the front; the models render translucent.
|
|
|
|
Usage: tools/machine_assets.py
|
|
"""
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from PIL import Image
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
ASSETS = ROOT / "mod/src/main/resources/assets/cobblesurvival"
|
|
|
|
# name: (dark, mid, light, accent, emblem)
|
|
CORES = {
|
|
"heat_core": ((92, 34, 26), (150, 62, 44), (186, 92, 66), (255, 160, 40), "flame"),
|
|
"dynamo_core": ((112, 58, 36), (184, 104, 70), (224, 150, 108), (255, 230, 70), "bolt"),
|
|
"synthesis_core": ((70, 74, 80), (128, 134, 142), (180, 186, 194), (90, 220, 120), "drop"),
|
|
"altar_core": ((22, 18, 26), (52, 44, 58), (84, 72, 92), (190, 60, 220), "eye"),
|
|
"rebirth_core": ((70, 40, 110), (130, 90, 190), (186, 150, 236), (255, 200, 245), "star"),
|
|
}
|
|
|
|
EMBLEMS = {
|
|
"flame": [
|
|
"......#.........",
|
|
".....##.........",
|
|
".....###..#.....",
|
|
"....####.##.....",
|
|
"...#########....",
|
|
"...##########...",
|
|
"..###++######...",
|
|
"..##++++#####...",
|
|
"..##+++++####...",
|
|
"...##+++++##....",
|
|
"....########....",
|
|
],
|
|
"bolt": [
|
|
"........####....",
|
|
".......####.....",
|
|
"......####......",
|
|
".....####.......",
|
|
"....#######.....",
|
|
".......####.....",
|
|
"......####......",
|
|
".....###........",
|
|
"....##..........",
|
|
"...#............",
|
|
],
|
|
"drop": [
|
|
".......#........",
|
|
"......###.......",
|
|
".....#####......",
|
|
"....###+###.....",
|
|
"...###+++###....",
|
|
"...##+++++##....",
|
|
"...###+++###....",
|
|
"....#######.....",
|
|
".....#####......",
|
|
],
|
|
"eye": [
|
|
"................",
|
|
".....######.....",
|
|
"...##......##...",
|
|
"..#...####...#..",
|
|
".#...##++##...#.",
|
|
"..#...####...#..",
|
|
"...##......##...",
|
|
".....######.....",
|
|
],
|
|
"star": [
|
|
".......#........",
|
|
".......#........",
|
|
"......###.......",
|
|
"..###########...",
|
|
"....#######.....",
|
|
".....#####......",
|
|
"....###.###.....",
|
|
"...##.....##....",
|
|
],
|
|
}
|
|
|
|
|
|
GLASS_ALPHA = 70
|
|
EMBLEM_ALPHA = 150
|
|
|
|
|
|
def frame(image, dark, mid, light):
|
|
"""A two-pixel frame: light top-left edge, dark bottom-right edge, mid inner ring."""
|
|
for i in range(16):
|
|
for a, b in ((i, 0), (0, i)):
|
|
image.putpixel((a, b), light + (255,))
|
|
for a, b in ((i, 15), (15, i)):
|
|
image.putpixel((a, b), dark + (255,))
|
|
for i in range(1, 15):
|
|
for a, b in ((i, 1), (1, i), (i, 14), (14, i)):
|
|
image.putpixel((a, b), mid + (255,))
|
|
|
|
|
|
def glass(dark, mid, light, accent):
|
|
"""Tinted glass inside the frame, with a few lighter streaks so the face still reads as a surface."""
|
|
image = Image.new("RGBA", (16, 16), (0, 0, 0, 0))
|
|
tint = tuple((m + a) // 2 for m, a in zip(mid, accent))
|
|
for y in range(2, 14):
|
|
for x in range(2, 14):
|
|
streak = (x - y) in (3, 4) or (x - y) == -6
|
|
image.putpixel((x, y), (light if streak else tint) + (GLASS_ALPHA + (50 if streak else 0),))
|
|
frame(image, dark, mid, light)
|
|
return image
|
|
|
|
|
|
def side(dark, mid, light, accent):
|
|
return glass(dark, mid, light, accent)
|
|
|
|
|
|
def top(dark, mid, light, accent):
|
|
image = glass(dark, mid, light, accent)
|
|
for x, y in ((2, 2), (13, 2), (2, 13), (13, 13)):
|
|
image.putpixel((x, y), accent + (255,))
|
|
return image
|
|
|
|
|
|
def front(dark, mid, light, accent, emblem):
|
|
image = glass(dark, mid, light, accent)
|
|
rows = EMBLEMS[emblem]
|
|
top_row = (16 - len(rows)) // 2
|
|
glow = tuple(min(255, c + 60) for c in accent)
|
|
for y, row in enumerate(rows):
|
|
for x, c in enumerate(row):
|
|
if 2 <= x <= 13 and 2 <= top_row + y <= 13 and c in "#+":
|
|
image.putpixel((x, top_row + y), (accent if c == "#" else glow) + (EMBLEM_ALPHA,))
|
|
return image
|
|
|
|
|
|
def write_json(path, data):
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(data, indent=2) + "\n")
|
|
|
|
|
|
def main():
|
|
textures = ASSETS / "textures/block"
|
|
textures.mkdir(parents=True, exist_ok=True)
|
|
for name, (dark, mid, light, accent, emblem) in CORES.items():
|
|
side(dark, mid, light, accent).save(textures / f"{name}_side.png")
|
|
top(dark, mid, light, accent).save(textures / f"{name}_top.png")
|
|
front(dark, mid, light, accent, emblem).save(textures / f"{name}_front.png")
|
|
write_json(ASSETS / f"models/block/{name}.json", {
|
|
"parent": "minecraft:block/orientable",
|
|
"render_type": "minecraft:translucent",
|
|
"textures": {"top": f"cobblesurvival:block/{name}_top", "front": f"cobblesurvival:block/{name}_front",
|
|
"side": f"cobblesurvival:block/{name}_side"},
|
|
})
|
|
write_json(ASSETS / f"models/item/{name}.json", {"parent": f"cobblesurvival:block/{name}"})
|
|
rotation = {"north": 0, "east": 90, "south": 180, "west": 270}
|
|
write_json(ASSETS / f"blockstates/{name}.json", {
|
|
"variants": {f"facing={facing}": {"model": f"cobblesurvival:block/{name}", **({"y": y} if y else {})}
|
|
for facing, y in rotation.items()},
|
|
})
|
|
print(f"wrote {len(CORES)} cores to {ASSETS.relative_to(ROOT)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|