from PIL import Image, ImageDraw
S = 4 SIZE = 96
def canvas():
img = Image.new("RGBA", (SIZE * S, SIZE * S), (0, 0, 0, 0))
return img, ImageDraw.Draw(img)
def save(img, name):
img = img.resize((SIZE, SIZE), Image.LANCZOS)
img.save(f"assets/sprites/{name}.png")
print(name)
def px(*vals):
return tuple(v * S for v in vals)
OUTLINE = (52, 38, 28, 255)
WHITE = (255, 255, 255, 255)
LIGHT = (235, 235, 235, 255)
img, d = canvas()
def arrow_poly(inset):
i = inset
return [px(8 + i, 40 + i), px(52 - i//2, 40 + i), px(52 - i//2, 22 + i),
px(90 - i, 48), px(52 - i//2, 74 - i), px(52 - i//2, 56 - i),
px(8 + i, 56 - i)]
d.polygon(arrow_poly(0), fill=OUTLINE)
d.polygon(arrow_poly(4), fill=WHITE)
save(img, "arrow")
img, d = canvas()
LEGS = [((34, 32), (16, 20), (6, 10)), ((30, 42), (12, 38), (2, 32)),
((30, 54), (12, 58), (2, 64)), ((34, 64), (16, 76), (6, 86))]
for hip, knee, foot in LEGS:
for (a, b) in [(hip, knee), (knee, foot)]:
d.line([px(*a), px(*b)], fill=OUTLINE, width=5 * S)
mx, my = 96 - hip[0], hip[1]
kx, ky = 96 - knee[0], knee[1]
fx, fy = 96 - foot[0], foot[1]
d.line([px(62, hip[1]), px(kx - 26, ky)], fill=OUTLINE, width=5 * S)
d.line([px(kx - 26, ky), px(fx - 26, fy)], fill=OUTLINE, width=5 * S)
d.ellipse([px(20, 18), px(76, 78)], fill=OUTLINE)
d.ellipse([px(24, 22), px(72, 74)], fill=WHITE)
d.ellipse([px(32, 32), px(64, 64)], fill=LIGHT)
for (sx, sy) in [(34, 28), (58, 26), (30, 60), (60, 66), (46, 70)]:
d.ellipse([px(sx, sy), px(sx + 4, sy + 4)], fill=LIGHT)
d.ellipse([px(66, 20), px(80, 32)], fill=OUTLINE)
d.ellipse([px(68, 22), px(78, 30)], fill=WHITE)
d.line([px(66, 38), px(78, 32)], fill=OUTLINE, width=3 * S)
d.line([px(66, 58), px(78, 64)], fill=OUTLINE, width=3 * S)
d.ellipse([px(74, 26), px(88, 40)], fill=OUTLINE)
d.ellipse([px(74, 56), px(88, 70)], fill=OUTLINE)
d.ellipse([px(76, 28), px(86, 38)], fill=(252, 252, 252, 255))
d.ellipse([px(76, 58), px(86, 68)], fill=(252, 252, 252, 255))
d.ellipse([px(80, 31), px(85, 36)], fill=(20, 16, 12, 255))
d.ellipse([px(80, 61), px(85, 66)], fill=(20, 16, 12, 255))
save(img, "crab")
img, d = canvas()
d.ellipse([px(16, 16), px(88, 88)], fill=OUTLINE)
d.ellipse([px(22, 22), px(82, 82)], fill=WHITE)
d.polygon([px(52, 52), px(100, 24), px(100, 80)], fill=(0, 0, 0, 0))
d.polygon([px(52, 52), px(96, 30), px(96, 74)], fill=(0, 0, 0, 0))
save(img, "claw")
GULL_WHITE = (248, 250, 252, 255)
GULL_GREY = (176, 186, 196, 255)
GULL_DARK = (52, 56, 62, 255)
BEAK = (240, 190, 60, 255)
img, d = canvas()
d.polygon([px(16, 40), px(2, 44), px(2, 52), px(16, 56)], fill=GULL_WHITE)
d.polygon([px(8, 42), px(2, 44), px(2, 52), px(8, 54)], fill=GULL_DARK)
d.ellipse([px(12, 28), px(74, 68)], fill=OUTLINE)
d.ellipse([px(15, 31), px(71, 65)], fill=GULL_WHITE)
d.ellipse([px(18, 35), px(60, 61)], fill=GULL_GREY)
d.polygon([px(44, 38), px(10, 44), px(26, 48), px(46, 44)], fill=GULL_DARK)
d.polygon([px(44, 58), px(10, 52), px(26, 48), px(46, 52)], fill=GULL_DARK)
d.ellipse([px(56, 32), px(86, 62)], fill=OUTLINE)
d.ellipse([px(58, 34), px(84, 60)], fill=GULL_WHITE)
d.polygon([px(82, 42), px(97, 46), px(97, 50), px(82, 54)], fill=(150, 110, 30, 255))
d.polygon([px(82, 43), px(95, 47), px(95, 49), px(82, 53)], fill=BEAK)
d.ellipse([px(88, 49), px(92, 53)], fill=(214, 70, 60, 255))
d.ellipse([px(68, 36), px(76, 44)], fill=(255, 255, 255, 255))
d.ellipse([px(68, 52), px(76, 60)], fill=(255, 255, 255, 255))
d.ellipse([px(71, 38), px(75, 42)], fill=(24, 22, 20, 255))
d.ellipse([px(71, 54), px(75, 58)], fill=(24, 22, 20, 255))
save(img, "gull")
img, d = canvas()
for sign in (-1, 1):
def wy(y):
return 48 + sign * y
d.polygon([px(30, wy(6)), px(16, wy(34)), px(28, wy(46)),
px(48, wy(40)), px(50, wy(8))], fill=OUTLINE)
d.polygon([px(32, wy(8)), px(19, wy(33)), px(28, wy(43)),
px(46, wy(38)), px(48, wy(10))], fill=GULL_GREY)
d.polygon([px(19, wy(33)), px(28, wy(43)), px(24, wy(46)),
px(14, wy(36))], fill=GULL_DARK)
spot_y0, spot_y1 = sorted((wy(37), wy(41)))
d.ellipse([px(20, spot_y0), px(24, spot_y1)], fill=GULL_WHITE)
d.polygon([px(20, 42), px(4, 40), px(2, 48), px(4, 56), px(20, 54)], fill=GULL_WHITE)
d.polygon([px(8, 41), px(2, 46), px(2, 50), px(8, 55)], fill=GULL_DARK)
d.ellipse([px(14, 36), px(70, 60)], fill=OUTLINE)
d.ellipse([px(17, 38), px(67, 58)], fill=GULL_WHITE)
d.ellipse([px(56, 34), px(84, 62)], fill=OUTLINE)
d.ellipse([px(58, 36), px(82, 60)], fill=GULL_WHITE)
d.polygon([px(80, 43), px(96, 47), px(96, 49), px(80, 53)], fill=BEAK)
d.ellipse([px(88, 48), px(92, 52)], fill=(214, 70, 60, 255))
d.ellipse([px(68, 38), px(76, 46)], fill=(255, 255, 255, 255))
d.ellipse([px(71, 40), px(75, 44)], fill=(24, 22, 20, 255))
save(img, "gull_fly")
img, d = canvas()
pts = [px(20, 78), px(10, 46), px(28, 20), px(58, 12), px(84, 30),
px(88, 62), px(66, 84)]
d.polygon(pts, fill=(64, 62, 58, 255))
d.polygon([px(28, 24), px(56, 16), px(78, 32), px(52, 44), px(24, 44)],
fill=(112, 110, 104, 255))
d.polygon([px(24, 46), px(52, 46), px(46, 74), px(22, 70)],
fill=(88, 86, 82, 255))
save(img, "rock")
img, d = canvas()
d.ellipse([px(10, 18), px(86, 82)], fill=(196, 168, 120, 255)) d.ellipse([px(16, 24), px(80, 76)], fill=(94, 70, 44, 255))
d.ellipse([px(24, 32), px(72, 68)], fill=(44, 32, 20, 255))
save(img, "hole")
img, d = canvas()
d.rectangle([px(12, 12), px(84, 84)], fill=OUTLINE)
d.rectangle([px(16, 16), px(80, 80)], fill=WHITE)
for i in range(4):
off = 20 + i * 15
d.rectangle([px(off, 8), px(off + 8, 18)], fill=(0, 0, 0, 0))
d.rectangle([px(off, 78), px(off + 8, 90)], fill=(0, 0, 0, 0))
d.rectangle([px(8, off), px(18, off + 8)], fill=(0, 0, 0, 0))
d.rectangle([px(78, off), px(90, off + 8)], fill=(0, 0, 0, 0))
for cx, cy in [(20, 20), (76, 20), (20, 76), (76, 76)]:
d.ellipse([px(cx - 9, cy - 9), px(cx + 9, cy + 9)], fill=OUTLINE)
d.ellipse([px(cx - 6, cy - 6), px(cx + 6, cy + 6)], fill=LIGHT)
d.rectangle([px(30, 30), px(66, 66)], fill=LIGHT)
d.rectangle([px(35, 35), px(61, 61)], fill=WHITE)
d.rectangle([px(42, 66), px(54, 80)], fill=OUTLINE)
d.ellipse([px(42, 60), px(54, 72)], fill=OUTLINE)
d.rectangle([px(45, 69), px(51, 80)], fill=LIGHT)
d.ellipse([px(45, 63), px(51, 74)], fill=LIGHT)
import random as _rnd
_r = _rnd.Random("castle")
for _ in range(26):
x = _r.randrange(18, 78)
y = _r.randrange(18, 78)
if 30 <= x <= 66 and 30 <= y <= 66:
continue
d.ellipse([px(x, y), px(x + 2, y + 2)], fill=LIGHT)
save(img, "castle")
import random
for name, base, speck_dark, speck_light in [
("sand_a", (237, 217, 176, 255), (219, 196, 152, 255), (247, 233, 203, 255)),
("sand_b", (230, 207, 161, 255), (211, 187, 141, 255), (242, 226, 192, 255)),
]:
rng = random.Random(name) img = Image.new("RGBA", (SIZE * S, SIZE * S), base)
d = ImageDraw.Draw(img)
for _ in range(210): x, y = rng.randrange(SIZE * S), rng.randrange(SIZE * S)
r = rng.randrange(1 * S, 2 * S)
col = speck_dark if rng.random() < 0.6 else speck_light
d.ellipse([x - r, y - r, x + r, y + r], fill=col)
for _ in range(4): x, y = rng.randrange(8 * S, 88 * S), rng.randrange(8 * S, 88 * S)
r = rng.randrange(2 * S, 4 * S)
col = (208, 186, 148, 255) if rng.random() < 0.5 else (245, 238, 220, 255)
d.ellipse([x - r, y - r, x + r, y + r], fill=col)
save(img, name)
img = Image.new("RGBA", (SIZE * S, SIZE * S), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
steps = 24
for i in range(steps):
t = i / (steps - 1) r = int((46 - 34 * t) * S)
a = int(6 + 110 * t * t)
d.ellipse([px(48, 48)[0] - r, px(48, 48)[1] - r,
px(48, 48)[0] + r, px(48, 48)[1] + r], fill=(0, 0, 0, a))
save(img, "shadow")
img = Image.new("RGBA", (SIZE * S, SIZE * S), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
d.rounded_rectangle([px(1, 4), px(95, 92)], radius=14 * S, fill=(74, 58, 44, 255))
d.rounded_rectangle([px(3, 12), px(93, 84)], radius=12 * S, fill=(104, 82, 60, 255))
d.rounded_rectangle([px(3, 12), px(93, 38)], radius=10 * S, fill=(128, 104, 78, 255)) rng = random.Random("plank")
for _ in range(7): x0 = rng.randrange(8, 60)
y0 = rng.randrange(30, 76)
d.line([px(x0, y0), px(x0 + rng.randrange(10, 26), y0)],
fill=(86, 66, 48, 255), width=3 * S)
save(img, "plank")
img, d = canvas()
t = 7 l = 26 for cx, cy, sx, sy in [(4, 4, 1, 1), (92, 4, -1, 1), (4, 92, 1, -1), (92, 92, -1, -1)]:
d.rectangle([px(min(cx, cx + sx * l), min(cy, cy + sy * t)),
px(max(cx, cx + sx * l), max(cy, cy + sy * t))], fill=WHITE)
d.rectangle([px(min(cx, cx + sx * t), min(cy, cy + sy * l)),
px(max(cx, cx + sx * t), max(cy, cy + sy * l))], fill=WHITE)
save(img, "bracket")
img, d = canvas()
GOLD = (244, 196, 48, 255)
GOLD_DARK = (196, 148, 24, 255)
CROWN_OUTLINE = (92, 64, 20, 255)
d.rectangle([px(14, 62), px(82, 82)], fill=CROWN_OUTLINE)
d.rectangle([px(17, 65), px(79, 79)], fill=GOLD_DARK)
d.polygon([px(14, 66), px(14, 22), px(34, 48), px(48, 16), px(62, 48),
px(82, 22), px(82, 66)], fill=CROWN_OUTLINE)
d.polygon([px(18, 60), px(18, 32), px(35, 53), px(48, 25), px(61, 53),
px(78, 32), px(78, 60)], fill=GOLD)
for x in (26, 48, 70):
d.ellipse([px(x - 4, 68), px(x + 4, 76)], fill=(214, 60, 60, 255))
save(img, "crown")
img, d = canvas()
rng = random.Random("kelp")
for i, x0 in enumerate((22, 36, 50, 64, 76)):
sway = rng.randrange(-8, 9)
top = rng.randrange(10, 26)
d.line([px(x0, 88), px(x0 + sway // 2, 56), px(x0 + sway, top)],
fill=(26, 84, 44, 255), width=7 * S)
d.line([px(x0, 88), px(x0 + sway // 2, 56), px(x0 + sway, top)],
fill=(44, 128, 66, 255), width=4 * S)
d.ellipse([px(x0 + sway - 5, top - 6), px(x0 + sway + 5, top + 4)],
fill=(62, 156, 82, 255))
save(img, "kelp")
img, d = canvas()
d.rounded_rectangle([px(4, 4), px(92, 92)], radius=26 * S, fill=(70, 130, 168, 235))
d.rounded_rectangle([px(9, 9), px(87, 87)], radius=22 * S, fill=(96, 160, 196, 255))
for r, alpha in ((30, 140), (18, 180)):
d.ellipse([px(48 - r, 48 - r), px(48 + r, 48 + r)],
outline=(214, 236, 246, alpha), width=2 * S)
d.ellipse([px(30, 26), px(52, 40)], fill=(196, 226, 240, 120))
save(img, "pool")
img = Image.new("RGBA", (SIZE * S, SIZE * S), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
d.rounded_rectangle([px(4, 36), px(92, 60)], radius=11 * S, fill=(74, 58, 44, 255))
d.rounded_rectangle([px(7, 39), px(89, 57)], radius=9 * S, fill=(122, 96, 68, 255))
d.rounded_rectangle([px(7, 39), px(89, 46)], radius=8 * S, fill=(148, 120, 88, 255))
rng = random.Random("log")
for _ in range(5):
x0 = rng.randrange(14, 66)
y0 = rng.randrange(42, 54)
d.line([px(x0, y0), px(x0 + rng.randrange(8, 18), y0)],
fill=(96, 74, 52, 255), width=2 * S)
d.ellipse([px(38, 38), px(58, 58)], fill=(52, 38, 28, 255))
d.ellipse([px(42, 42), px(54, 54)], fill=(178, 148, 108, 255))
save(img, "log")
img = Image.new("RGBA", (SIZE * S, SIZE * S), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
rng = random.Random("puff")
for _ in range(9):
cx = rng.randrange(28, 68)
cy = rng.randrange(28, 68)
r = rng.randrange(10, 22) * S
d.ellipse([cx * S - r, cy * S - r, cx * S + r, cy * S + r],
fill=(255, 255, 255, 90))
d.ellipse([px(30, 30), px(66, 66)], fill=(255, 255, 255, 140))
save(img, "puff")
img, d = canvas()
d.polygon([px(48, 6), px(56, 40), px(90, 48), px(56, 56),
px(48, 90), px(40, 56), px(6, 48), px(40, 40)], fill=WHITE)
d.polygon([px(48, 26), px(52, 44), px(70, 48), px(52, 52),
px(48, 70), px(44, 52), px(26, 48), px(44, 44)],
fill=(255, 255, 255, 255))
save(img, "star")
img = Image.new("RGBA", (SIZE * S, SIZE * S), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
for i in range(6):
cx = 8 + i * 16
d.ellipse([px(cx - 9, 30), px(cx + 9, 54)], fill=(255, 255, 255, 200))
d.rectangle([px(0, 0), px(96, 40)], fill=(255, 255, 255, 200))
save(img, "foam")
img, d = canvas()
d.ellipse([px(22, 22), px(74, 74)], fill=(58, 44, 32, 255))
d.ellipse([px(28, 28), px(68, 68)], fill=(96, 74, 52, 255))
d.ellipse([px(34, 34), px(54, 54)], fill=(126, 100, 72, 255))
save(img, "post")
img, d = canvas()
LEGS_B = [((34, 32), (14, 26), (4, 18)), ((30, 42), (14, 34), (2, 26)),
((30, 54), (14, 62), (2, 70)), ((34, 64), (14, 70), (4, 80))]
for hip, knee, foot in LEGS_B:
for (a, b) in [(hip, knee), (knee, foot)]:
d.line([px(*a), px(*b)], fill=OUTLINE, width=5 * S)
kx, ky = 96 - knee[0], knee[1]
fx, fy = 96 - foot[0], foot[1]
d.line([px(62, hip[1]), px(kx - 26, ky)], fill=OUTLINE, width=5 * S)
d.line([px(kx - 26, ky), px(fx - 26, fy)], fill=OUTLINE, width=5 * S)
d.ellipse([px(20, 18), px(76, 78)], fill=OUTLINE)
d.ellipse([px(24, 22), px(72, 74)], fill=WHITE)
d.ellipse([px(32, 32), px(64, 64)], fill=LIGHT)
for (sx, sy) in [(34, 28), (58, 26), (30, 60), (60, 66), (46, 70)]:
d.ellipse([px(sx, sy), px(sx + 4, sy + 4)], fill=LIGHT)
d.ellipse([px(66, 20), px(80, 32)], fill=OUTLINE)
d.ellipse([px(68, 22), px(78, 30)], fill=WHITE)
d.line([px(66, 38), px(78, 32)], fill=OUTLINE, width=3 * S)
d.line([px(66, 58), px(78, 64)], fill=OUTLINE, width=3 * S)
d.ellipse([px(74, 26), px(88, 40)], fill=OUTLINE)
d.ellipse([px(74, 56), px(88, 70)], fill=OUTLINE)
d.ellipse([px(76, 28), px(86, 38)], fill=(252, 252, 252, 255))
d.ellipse([px(76, 58), px(86, 68)], fill=(252, 252, 252, 255))
d.ellipse([px(80, 31), px(85, 36)], fill=(20, 16, 12, 255))
d.ellipse([px(80, 61), px(85, 66)], fill=(20, 16, 12, 255))
save(img, "crab_b")
img = Image.new("RGBA", (SIZE * S, SIZE * S), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
for row in range(SIZE * S):
a = int(150 * (1.0 - row / (SIZE * S)))
d.line([(0, row), (SIZE * S, row)], fill=(120, 100, 70, a))
save(img, "wet")
img = Image.new("RGBA", (SIZE * S, SIZE * S), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
rng = random.Random("cloud")
for (cx, cy, r) in [(20, 58, 14), (36, 50, 19), (54, 46, 21), (72, 54, 15),
(46, 58, 18), (62, 58, 14)]:
rr = r * S
d.ellipse([cx * S - rr, cy * S - rr, cx * S + rr, cy * S + rr],
fill=(255, 255, 255, 235))
d.rectangle([px(10, 56), px(82, 66)], fill=(255, 255, 255, 235))
d.rectangle([px(12, 62), px(80, 66)], fill=(210, 218, 230, 180))
save(img, "cloud")
img, d = canvas()
HULL = (122, 78, 44, 255)
HULL_DARK = (92, 58, 32, 255)
SAIL = (245, 242, 230, 255)
MAST = (70, 50, 34, 255)
d.polygon([px(10, 62), px(86, 62), px(74, 80), px(22, 80)], fill=HULL)
d.polygon([px(10, 62), px(86, 62), px(83, 67), px(13, 67)], fill=HULL_DARK)
d.rectangle([px(46, 14), px(50, 62)], fill=MAST)
d.polygon([px(50, 16), px(50, 58), px(82, 58)], fill=SAIL)
d.polygon([px(44, 22), px(44, 58), px(20, 58)], fill=(230, 224, 206, 255))
d.polygon([px(46, 14), px(46, 8), px(58, 11)], fill=(214, 60, 50, 255))
save(img, "boat")