#!/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()