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.
84 lines
3.5 KiB
Python
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()
|