KAZIK/tools/generators/pack_world_v4.py
Artvasil09 3ad5bf4c9d Develop assembly-only casino scenes and native games
Add native Durak, poker, blackjack, roulette, Buckshot and billiards scenes with animation assets and regression tests. Improve arrival, city, hall, profiles, shops, audio and exit handling; remove the portable C game core and refresh shipping executables.
2026-10-08 22:39:19 +03:00

84 lines
3.5 KiB
Python

"""Offline packing of approved bar/street art. Native runtime remains MASM."""
import json
from collections import deque
from PIL import Image
import pack_arrival_assets as packs
from pack_hall_assets import ROOT, SOURCE, extract, normalize
def clean_cell(cell):
"""Discard small disconnected fragments leaking in from neighboring cells."""
alpha = cell.getchannel("A")
width, height = cell.size
remaining = {y * width + x for y in range(height) for x in range(width)
if alpha.getpixel((x, y))}
groups = []
while remaining:
seed = remaining.pop()
pending, group = deque([seed]), [seed]
while pending:
point = pending.popleft()
x, y = point % width, point // width
for dx, dy in ((-1, 0), (1, 0), (0, -1), (0, 1)):
a, b = x + dx, y + dy
neighbor = b * width + a
if 0 <= a < width and 0 <= b < height and neighbor in remaining:
remaining.remove(neighbor)
group.append(neighbor)
pending.append(neighbor)
groups.append(group)
largest = max(map(len, groups))
for group in groups:
if len(group) < largest * .08:
for point in group:
alpha.putpixel((point % width, point // width), 0)
cell.putalpha(alpha)
return cell.crop(alpha.getbbox())
def main():
packs.OUTPUT = ROOT / "assets/animations/hall"
# Use the observed gutters, not the grid requested in the generation prompt.
bartender = extract("bartender-v4.png", 4, 6)
# The first pose's detached glass and reaching hand are one animation cell.
source = packs.solid_alpha(Image.open(SOURCE / "drink-hand-v4.png").convert("RGBA"))
xs, ys = [0, 274, 466, 701, 950, 1205, 1388, 1536], [0, 285, 571, 869]
drink = []
for top, bottom in zip(ys, ys[1:]):
for left, right in zip(xs, xs[1:]):
cell = source.crop((left, top, right, bottom))
drink.append(clean_cell(cell))
source = packs.solid_alpha(Image.open(SOURCE / "street-life-v4.png").convert("RGBA"))
street = []
ys = [0, 234, 457, 663, 869, 1024]
for top, bottom in zip(ys, ys[1:]):
for column in range(8):
cell = source.crop((column * 192, top, (column + 1) * 192, bottom))
street.append(clean_cell(cell))
frames = {
"bartender-v4.bin": normalize(bartender, (256, 256)),
"drink-v4.bin": normalize(drink, (256, 256)),
"street-life-v4.bin": (normalize(street[:16], (192, 320)) +
normalize(street[16:32], (192, 320)) +
normalize(street[32:], (192, 320), False)),
}
manifest = {}
for name, items in frames.items():
packs.write_pack(name, items)
preview = Image.new("RGBA", (items[0].width * 4,
items[0].height * ((len(items) + 3) // 4)),
(25, 29, 31, 255))
for index, frame in enumerate(items):
preview.alpha_composite(frame, ((index % 4) * frame.width,
(index // 4) * frame.height))
preview.save(ROOT / "build/hall" / (name + ".png"))
manifest[name] = {"frames": len(items), "size": items[0].size}
(packs.OUTPUT / "manifest-v4.json").write_text(json.dumps(manifest, indent=2),
encoding="ascii")
print(json.dumps(manifest))
if __name__ == "__main__":
main()