smurfs / src /rendering /sprite_generator_classic.py
BolyosCsaba
initial commit
82fa936
"""
Classic Sprite Generator - Original smooth/rounded style
"""
import numpy as np
from PIL import Image, ImageDraw
from typing import Tuple
import math
from .sprite_generator_base import SpriteGeneratorBase
from .sprite_generator import Sprite
class ClassicSpriteGenerator(SpriteGeneratorBase):
"""Classic sprite generator with smooth, rounded style"""
def generate_smurf_sprites(self, color_tint: Tuple[int, int, int] = (0, 120, 255),
size_scale: float = 1.0) -> dict:
"""Generate complete smurf sprite set"""
from entities.enums import AnimationType
return {
AnimationType.IDLE.value: self._gen_smurf_idle(color_tint, size_scale),
AnimationType.WALK.value: self._gen_smurf_walk(color_tint, size_scale),
AnimationType.GATHER.value: self._gen_smurf_gather(color_tint, size_scale),
AnimationType.HAPPY.value: self._gen_smurf_happy(color_tint, size_scale),
AnimationType.ANGRY.value: self._gen_smurf_angry(color_tint, size_scale),
AnimationType.CHAT.value: self._gen_smurf_chat(color_tint, size_scale),
AnimationType.PLAY.value: self._gen_smurf_play(color_tint, size_scale)
}
@staticmethod
def _draw_smurf_base(draw, color: Tuple[int, int, int], scale: float, y_offset: int = 0):
"""Draw base smurf body (reusable)"""
s = scale
# Body
draw.ellipse([8*s, (12+y_offset)*s, 24*s, (26+y_offset)*s], fill=color)
# Head
draw.ellipse([10*s, (6+y_offset)*s, 22*s, (16+y_offset)*s], fill=(255, 220, 180))
# Hat
draw.polygon([(10*s, (6+y_offset)*s), (22*s, (6+y_offset)*s), (16*s, (0+y_offset)*s)],
fill=(255, 255, 255))
@staticmethod
def _draw_face_neutral(draw, scale: float, y_offset: int = 0):
"""Draw neutral face"""
s = scale
# Eyes
draw.ellipse([13*s, (10+y_offset)*s, 15*s, (12+y_offset)*s], fill=(0, 0, 0))
draw.ellipse([17*s, (10+y_offset)*s, 19*s, (12+y_offset)*s], fill=(0, 0, 0))
# Small mouth
draw.line([(14*s, (13+y_offset)*s), (18*s, (13+y_offset)*s)], fill=(0, 0, 0), width=1)
@staticmethod
def _draw_face_happy(draw, scale: float, y_offset: int = 0):
"""Draw happy face with big smile"""
s = scale
# Eyes (wider/sparkly)
draw.ellipse([12*s, (9+y_offset)*s, 15*s, (12+y_offset)*s], fill=(0, 0, 0))
draw.ellipse([17*s, (9+y_offset)*s, 20*s, (12+y_offset)*s], fill=(0, 0, 0))
# Big smile (arc)
draw.arc([12*s, (10+y_offset)*s, 20*s, (15+y_offset)*s], 0, 180, fill=(0, 0, 0), width=int(2*scale))
# Rosy cheeks
draw.ellipse([9*s, (12+y_offset)*s, 11*s, (13+y_offset)*s], fill=(255, 150, 150))
draw.ellipse([21*s, (12+y_offset)*s, 23*s, (13+y_offset)*s], fill=(255, 150, 150))
@staticmethod
def _draw_face_angry(draw, scale: float, y_offset: int = 0):
"""Draw angry face with frown"""
s = scale
# Angry eyes (narrow)
draw.ellipse([13*s, (11+y_offset)*s, 15*s, (12+y_offset)*s], fill=(0, 0, 0))
draw.ellipse([17*s, (11+y_offset)*s, 19*s, (12+y_offset)*s], fill=(0, 0, 0))
# Angry eyebrows
draw.line([(12*s, (9+y_offset)*s), (15*s, (10+y_offset)*s)], fill=(0, 0, 0), width=int(2*scale))
draw.line([(17*s, (10+y_offset)*s), (20*s, (9+y_offset)*s)], fill=(0, 0, 0), width=int(2*scale))
# Frown
draw.arc([13*s, (14+y_offset)*s, 19*s, (16+y_offset)*s], 180, 360, fill=(0, 0, 0), width=int(2*scale))
@staticmethod
def _draw_face_chatting(draw, scale: float, y_offset: int = 0, frame: int = 0):
"""Draw chatting face (mouth open/closed)"""
s = scale
# Eyes
draw.ellipse([13*s, (10+y_offset)*s, 15*s, (12+y_offset)*s], fill=(0, 0, 0))
draw.ellipse([17*s, (10+y_offset)*s, 19*s, (12+y_offset)*s], fill=(0, 0, 0))
# Mouth (talking - open/closed)
if frame % 2 == 0:
draw.ellipse([14*s, (13+y_offset)*s, 18*s, (15+y_offset)*s], fill=(0, 0, 0)) # Open
else:
draw.line([(14*s, (13+y_offset)*s), (18*s, (13+y_offset)*s)], fill=(0, 0, 0), width=1) # Closed
def _gen_smurf_idle(self, color: Tuple[int, int, int], scale: float) -> Sprite:
frames = []
base_size = int(32 * scale)
for i in range(2):
img = Image.new('RGBA', (base_size, base_size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
y_offset = int(1 * scale) if i == 0 else 0
self._draw_smurf_base(draw, color, scale, y_offset)
self._draw_face_neutral(draw, scale, y_offset)
frames.append(np.array(img))
return Sprite(frames=frames, frame_speed=15)
def _gen_smurf_walk(self, color: Tuple[int, int, int], scale: float) -> Sprite:
frames = []
base_size = int(32 * scale)
for i in range(4):
img = Image.new('RGBA', (base_size, base_size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
bob = int(2 * scale) if i % 2 == 0 else 0
s = scale
self._draw_smurf_base(draw, color, scale, bob)
self._draw_face_neutral(draw, scale, bob)
# Walking legs
left_leg = int(2 * scale) if i < 2 else int(-2 * scale)
right_leg = int(-2 * scale) if i < 2 else int(2 * scale)
draw.rectangle([11*s, (26+bob)*s, 13*s, (30+bob+left_leg)*s], fill=color)
draw.rectangle([19*s, (26+bob)*s, 21*s, (30+bob+right_leg)*s], fill=color)
frames.append(np.array(img))
return Sprite(frames=frames, frame_speed=8)
def _gen_smurf_gather(self, color: Tuple[int, int, int], scale: float) -> Sprite:
frames = []
base_size = int(32 * scale)
for i in range(4):
img = Image.new('RGBA', (base_size, base_size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
bend = int(i * 2 * scale)
self._draw_smurf_base(draw, color, scale, bend)
self._draw_face_neutral(draw, scale, bend)
frames.append(np.array(img))
return Sprite(frames=frames, frame_speed=10)
def _gen_smurf_happy(self, color: Tuple[int, int, int], scale: float) -> Sprite:
"""Happy jumping with big smile"""
frames = []
base_size = int(32 * scale)
for i in range(6):
img = Image.new('RGBA', (base_size, base_size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
jump = int(abs(math.sin(i * math.pi / 3) * 8) * scale)
self._draw_smurf_base(draw, color, scale, -jump)
self._draw_face_happy(draw, scale, -jump)
frames.append(np.array(img))
return Sprite(frames=frames, frame_speed=5)
def _gen_smurf_angry(self, color: Tuple[int, int, int], scale: float) -> Sprite:
"""Angry shaking with frown"""
frames = []
base_size = int(32 * scale)
for i in range(4):
img = Image.new('RGBA', (base_size, base_size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
shake_x = int((i % 2) * 2 - 1) * int(2 * scale)
# Slightly red-tinted when angry
angry_color = (min(255, color[0] + 50), max(0, color[1] - 20), max(0, color[2] - 20))
# Draw body with offset for shake
s = scale
draw.ellipse([(8+shake_x)*s, 12*s, (24+shake_x)*s, 26*s], fill=angry_color)
draw.ellipse([(10+shake_x)*s, 6*s, (22+shake_x)*s, 16*s], fill=(255, 200, 180))
draw.polygon([((10+shake_x)*s, 6*s), ((22+shake_x)*s, 6*s), ((16+shake_x)*s, 0)],
fill=(255, 255, 255))
# Angry face with correct offset
draw.ellipse([(13+shake_x)*s, 11*s, (15+shake_x)*s, 12*s], fill=(0, 0, 0))
draw.ellipse([(17+shake_x)*s, 11*s, (19+shake_x)*s, 12*s], fill=(0, 0, 0))
draw.line([((12+shake_x)*s, 9*s), ((15+shake_x)*s, 10*s)], fill=(0, 0, 0), width=int(2*scale))
draw.line([((17+shake_x)*s, 10*s), ((20+shake_x)*s, 9*s)], fill=(0, 0, 0), width=int(2*scale))
draw.arc([((13+shake_x)*s, 14*s), ((19+shake_x)*s, 16*s)], 180, 360, fill=(0, 0, 0), width=int(2*scale))
frames.append(np.array(img))
return Sprite(frames=frames, frame_speed=8)
def _gen_smurf_chat(self, color: Tuple[int, int, int], scale: float) -> Sprite:
"""Chatting with mouth movement"""
frames = []
base_size = int(32 * scale)
for i in range(4):
img = Image.new('RGBA', (base_size, base_size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
nod = int((i % 2) * scale)
self._draw_smurf_base(draw, color, scale, nod)
self._draw_face_chatting(draw, scale, nod, i)
frames.append(np.array(img))
return Sprite(frames=frames, frame_speed=12)
def _gen_smurf_play(self, color: Tuple[int, int, int], scale: float) -> Sprite:
"""Playing - bouncing with happy face"""
frames = []
base_size = int(32 * scale)
for i in range(6):
img = Image.new('RGBA', (base_size, base_size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
bounce = int(abs(math.sin(i * math.pi / 3) * 4) * scale)
self._draw_smurf_base(draw, color, scale, bounce)
self._draw_face_happy(draw, scale, bounce)
frames.append(np.array(img))
return Sprite(frames=frames, frame_speed=10)