""" 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)