Files
battery/tools/gen_sprites.py

193 lines
7.6 KiB
Python
Raw Normal View History

2026-07-21 19:36:02 -04:00
#!/usr/bin/env python3
"""Generate simple default sprite PNGs into ../assets/.
Everything here is plain pygame drawing at 32x32 (the tile size). Re-run any
time to regenerate; tweak the draw_* functions, or just replace the PNGs with
your own art (the game loads them by filename either way).
python tools/gen_sprites.py
"""
import os
import pygame
TILE = 32
ASSETS = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "assets")
def _s():
return pygame.Surface((TILE, TILE), pygame.SRCALPHA)
# --- individual sprites ------------------------------------------------------
def draw_block(s):
s.fill((96, 104, 122)) # stone
seam = (70, 76, 92)
pygame.draw.rect(s, seam, (0, 0, TILE, TILE), 2) # border
pygame.draw.line(s, seam, (0, 16), (TILE, 16), 2) # course seam
pygame.draw.line(s, seam, (16, 2), (16, 16), 2) # offset bricks
pygame.draw.line(s, seam, (8, 16), (8, TILE - 2), 2)
pygame.draw.line(s, seam, (24, 16), (24, TILE - 2), 2)
pygame.draw.line(s, (132, 140, 158), (2, 2), (TILE - 3, 2), 1) # top highlight
def draw_fake_block(s):
# Intentionally identical to a real block — that's the whole trap.
draw_block(s)
def draw_player(s):
body = pygame.Rect(6, 2, 20, 28)
pygame.draw.rect(s, (40, 44, 60), body, border_radius=5) # phone body
pygame.draw.rect(s, (18, 20, 30), body, 2, border_radius=5) # outline
pygame.draw.rect(s, (95, 205, 255), (9, 6, 14, 16)) # screen
pygame.draw.rect(s, (20, 30, 45), (12, 10, 3, 4)) # eyes
pygame.draw.rect(s, (20, 30, 45), (18, 10, 3, 4))
pygame.draw.line(s, (20, 30, 45), (12, 17), (20, 17), 2) # smile
pygame.draw.rect(s, (120, 126, 140), (14, 25, 4, 2)) # home button
def draw_player_dead(s):
body = pygame.Rect(6, 2, 20, 28)
pygame.draw.rect(s, (62, 42, 46), body, border_radius=5)
pygame.draw.rect(s, (30, 20, 22), body, 2, border_radius=5)
pygame.draw.rect(s, (72, 76, 86), (9, 6, 14, 16)) # dead grey screen
for ex in (11, 17): # X eyes
pygame.draw.line(s, (225, 85, 85), (ex, 9), (ex + 4, 14), 2)
pygame.draw.line(s, (225, 85, 85), (ex + 4, 9), (ex, 14), 2)
pygame.draw.lines(s, (200, 205, 215), False,
[(9, 8), (14, 13), (12, 18), (21, 21)], 1) # crack
def draw_goal(s):
pad = pygame.Rect(4, 4, 24, 24)
pygame.draw.rect(s, (36, 110, 66), pad, border_radius=5) # charger pad
pygame.draw.rect(s, (90, 230, 140), pad, 2, border_radius=5)
bolt = [(19, 5), (10, 18), (15, 18), (12, 27), (23, 13), (17, 13)]
pygame.draw.polygon(s, (245, 240, 130), bolt) # lightning bolt
pygame.draw.polygon(s, (200, 190, 80), bolt, 1)
def draw_spike(s):
base, edge = (156, 162, 178), (92, 98, 116)
n = 4
w = TILE / n
for i in range(n):
x = i * w
pts = [(x, TILE), (x + w / 2, 3), (x + w, TILE)]
pygame.draw.polygon(s, base, pts)
pygame.draw.polygon(s, edge, pts, 1)
pygame.draw.rect(s, edge, (0, TILE - 4, TILE, 4)) # base strip
def draw_moving_block(s):
s.fill((172, 122, 72)) # crate
pygame.draw.rect(s, (120, 80, 45), (0, 0, TILE, TILE), 2)
for bx, by in [(5, 5), (TILE - 6, 5), (5, TILE - 6), (TILE - 6, TILE - 6)]:
pygame.draw.circle(s, (92, 60, 35), (bx, by), 2) # bolts
plate = pygame.Rect(10, 10, 12, 12)
pygame.draw.rect(s, (152, 106, 60), plate)
pygame.draw.rect(s, (120, 80, 45), plate, 1)
def draw_patrol_block(s):
s.fill((122, 102, 178)) # platform
pygame.draw.rect(s, (80, 65, 125), (0, 0, TILE, TILE), 2)
pygame.draw.rect(s, (162, 148, 208), (2, 2, TILE - 4, 4)) # top highlight
for off in (0, 9): # motion chevrons
pygame.draw.lines(s, (92, 76, 142), False,
[(10, 15 + off), (16, 19 + off), (22, 15 + off)], 2)
def draw_crumble_block(s):
s.fill((166, 136, 96))
pygame.draw.rect(s, (120, 95, 60), (0, 0, TILE, TILE), 2)
cr = (112, 86, 56)
pygame.draw.lines(s, cr, False, [(8, 2), (12, 10), (9, 16), (14, 24), (12, TILE)], 1)
pygame.draw.lines(s, cr, False, [(22, 3), (19, 9), (24, 15), (20, 22)], 1)
pygame.draw.line(s, cr, (2, 14), (9, 16), 1)
pygame.draw.line(s, cr, (24, 15), (TILE, 13), 1)
def draw_arrow_shooter(s):
s.fill((72, 76, 92)) # turret block
pygame.draw.rect(s, (45, 48, 60), (0, 0, TILE, TILE), 2)
for bx, by in [(5, 5), (TILE - 6, 5), (5, TILE - 6), (TILE - 6, TILE - 6)]:
pygame.draw.circle(s, (40, 42, 52), (bx, by), 2) # rivets
pygame.draw.circle(s, (22, 24, 30), (TILE // 2, TILE // 2), 6) # barrel
pygame.draw.circle(s, (150, 64, 64), (TILE // 2, TILE // 2), 3)
def draw_spike_block(s):
base, edge = (152, 158, 174), (84, 88, 104)
m = TILE // 2
tris = [
[(m, 0), (m - 4, 11), (m + 4, 11)], # up
[(m, TILE), (m - 4, TILE - 11), (m + 4, TILE - 11)], # down
[(0, m), (11, m - 4), (11, m + 4)], # left
[(TILE, m), (TILE - 11, m - 4), (TILE - 11, m + 4)], # right
[(2, 2), (13, 6), (6, 13)], # up-left
[(TILE - 2, 2), (TILE - 13, 6), (TILE - 6, 13)], # up-right
[(2, TILE - 2), (13, TILE - 6), (6, TILE - 13)], # down-left
[(TILE - 2, TILE - 2), (TILE - 13, TILE - 6), (TILE - 6, TILE - 13)], # down-right
]
for t in tris:
pygame.draw.polygon(s, base, t)
pygame.draw.polygon(s, edge, t, 1)
pygame.draw.circle(s, (112, 118, 136), (m, m), 8)
pygame.draw.circle(s, edge, (m, m), 8, 1)
def draw_phase_block(s):
# Cyan "energy" block; the game fades its alpha in/out as it phases.
s.fill((70, 150, 190))
pygame.draw.rect(s, (120, 210, 240), (0, 0, TILE, TILE), 2)
pygame.draw.line(s, (150, 230, 255), (4, 4), (TILE - 5, TILE - 5), 1)
pygame.draw.line(s, (150, 230, 255), (TILE - 5, 4), (4, TILE - 5), 1)
pygame.draw.polygon(s, (150, 230, 255),
[(16, 4), (28, 16), (16, 28), (4, 16)], 1)
def draw_arrow(s):
# Orientation-agnostic energy bolt (arrows fly in several directions and the
# engine doesn't rotate the sprite).
cx = cy = TILE // 2
diamond = [(cx, cy - 9), (cx + 9, cy), (cx, cy + 9), (cx - 9, cy)]
pygame.draw.polygon(s, (250, 225, 110), diamond)
pygame.draw.polygon(s, (210, 175, 70), diamond, 2)
pygame.draw.circle(s, (255, 248, 190), (cx, cy), 3)
SPRITES = {
"player": draw_player,
"player_dead": draw_player_dead,
"block": draw_block,
"fake_block": draw_fake_block,
"goal": draw_goal,
"spike": draw_spike,
"moving_block": draw_moving_block,
"patrol_block": draw_patrol_block,
"crumble_block": draw_crumble_block,
"arrow_shooter": draw_arrow_shooter,
"arrow": draw_arrow,
"spike_block": draw_spike_block,
"phase_block": draw_phase_block,
}
def main():
os.environ.setdefault("SDL_VIDEODRIVER", "dummy")
os.environ.setdefault("SDL_AUDIODRIVER", "dummy")
pygame.init()
os.makedirs(ASSETS, exist_ok=True)
for name, fn in SPRITES.items():
surf = _s()
fn(surf)
pygame.image.save(surf, os.path.join(ASSETS, name + ".png"))
pygame.quit()
print(f"wrote {len(SPRITES)} sprites to {os.path.normpath(ASSETS)}")
if __name__ == "__main__":
main()