193 lines
7.6 KiB
Python
193 lines
7.6 KiB
Python
#!/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()
|