Add native assembly scenes and assets for Slots (777), Coin flip, and European Roulette; add 3D hall furniture packs and prompts; improve Russian UI strings in CP1251; update launcher hash and rebuild shipping binaries.
78 lines
3.4 KiB
Python
78 lines
3.4 KiB
Python
"""Offline scene packing and exact 37-pocket rotor for the MASM roulette renderer."""
|
|
import json
|
|
import math
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
import pack_arrival_assets as packs
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
ORDER = [0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30,
|
|
8, 23, 10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28,
|
|
12, 35, 3, 26]
|
|
RED = {1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36}
|
|
|
|
|
|
def rotor(numbered=True):
|
|
im = Image.new("RGBA", (512, 512))
|
|
pixels = im.load()
|
|
for y in range(512):
|
|
for x in range(512):
|
|
dx, dy = x - 256, y - 256
|
|
radius = math.hypot(dx, dy)
|
|
if radius > 240:
|
|
continue
|
|
noise = ((x * 193 + y * 37 + (x ^ y) * 13) % 17) - 8
|
|
base = (82, 41, 25)
|
|
if radius >= 236 or 181 <= radius < 185 or 225 <= radius < 229:
|
|
base = (199, 158, 74)
|
|
elif radius >= 185:
|
|
angle = math.atan2(dy, dx) % math.tau
|
|
slot = int((angle / math.tau * 37 + .5) % 37)
|
|
value = ORDER[slot]
|
|
base = (20, 95, 56) if value == 0 else (
|
|
(132, 32, 38) if value in RED else (25, 30, 28))
|
|
boundary = abs((angle / math.tau * 37 + .5) % 1 - .5)
|
|
if boundary > .465:
|
|
base = (211, 176, 94)
|
|
elif radius < 28:
|
|
base = (191, 145, 58) if radius > 22 else (234, 207, 137)
|
|
pixels[x, y] = tuple(max(0, min(255, c + noise)) for c in base) + (255,)
|
|
if not numbered:
|
|
return im
|
|
font = ImageFont.truetype("C:/Windows/Fonts/arialbd.ttf", 20)
|
|
for index, value in enumerate(ORDER):
|
|
angle = index * math.tau / 37
|
|
label = Image.new("RGBA", (44, 26))
|
|
draw = ImageDraw.Draw(label)
|
|
draw.text((22, 13), str(value), font=font, anchor="mm", fill=(247, 238, 211))
|
|
label = label.rotate(-index * 360 / 37 - 90, expand=True, resample=Image.Resampling.NEAREST)
|
|
cx, cy = round(256 + 208 * math.cos(angle)), round(256 + 208 * math.sin(angle))
|
|
im.alpha_composite(label, (cx - label.width // 2, cy - label.height // 2))
|
|
return im
|
|
|
|
|
|
def main(rotor_only=False):
|
|
packs.OUTPUT = ROOT / "assets/animations/wheel"
|
|
preview = ROOT / "build/wheel"
|
|
preview.mkdir(parents=True, exist_ok=True)
|
|
manifest = {"order": ORDER, "zero_pockets": 1}
|
|
for name in (() if rotor_only else ("table", "closeup")):
|
|
image = Image.open(ROOT / "assets/sprites/wheel" / (name + "-v1.png")).convert("RGBA")
|
|
image = image.resize((640, 480), Image.Resampling.NEAREST)
|
|
image.putalpha(255)
|
|
packs.write_pack(name + "-v1.bin", [image])
|
|
image.save(preview / (name + ".png"))
|
|
# Numerals now belong to the native screen-space renderer, not a warped image.
|
|
disk = rotor(numbered=False).resize((480, 480), Image.Resampling.NEAREST)
|
|
packs.write_pack("rotor-v2.bin", [disk])
|
|
disk.save(preview / "rotor-v2.png")
|
|
(packs.OUTPUT / "manifest-v1.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="ascii")
|
|
print("Packed the exact 37-pocket rotor; labels are rendered by MASM")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--rotor-only", action="store_true")
|
|
main(parser.parse_args().rotor_only)
|