Spaces:
Sleeping
Sleeping
| """ | |
| Sprite Generation - Shared utilities and resource/building sprites | |
| Character sprites are generated by separate pipeline classes | |
| """ | |
| import numpy as np | |
| from PIL import Image, ImageDraw | |
| from dataclasses import dataclass | |
| from typing import List | |
| class Sprite: | |
| frames: List[np.ndarray] | |
| current_frame: int = 0 | |
| frame_speed: int = 5 | |
| tick_counter: int = 0 | |
| def next_frame(self) -> np.ndarray: | |
| self.tick_counter += 1 | |
| if self.tick_counter >= self.frame_speed: | |
| self.tick_counter = 0 | |
| self.current_frame = (self.current_frame + 1) % len(self.frames) | |
| return self.frames[self.current_frame] | |
| def get_current_frame(self) -> np.ndarray: | |
| return self.frames[self.current_frame] | |
| def reset(self): | |
| self.current_frame = 0 | |
| self.tick_counter = 0 | |
| class SpriteGenerator: | |
| """Shared sprite generation utilities for resources and buildings""" | |
| # ======================================================================== | |
| # RESOURCE SPRITES | |
| # ======================================================================== | |
| def generate_mushroom_sprite() -> Sprite: | |
| img = Image.new('RGBA', (32, 32), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| # Stem | |
| draw.rectangle([13, 16, 19, 28], fill=(240, 230, 210)) | |
| # Cap | |
| draw.ellipse([8, 10, 24, 22], fill=(200, 50, 50)) | |
| # White spots | |
| draw.ellipse([11, 13, 15, 17], fill=(255, 255, 255)) | |
| draw.ellipse([17, 12, 20, 15], fill=(255, 255, 255)) | |
| return Sprite(frames=[np.array(img)]) | |
| def generate_berry_sprite() -> Sprite: | |
| img = Image.new('RGBA', (32, 32), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| # Bush | |
| draw.ellipse([6, 12, 26, 28], fill=(34, 139, 34)) | |
| # Berries | |
| draw.ellipse([10, 15, 14, 19], fill=(200, 0, 0)) | |
| draw.ellipse([18, 14, 22, 18], fill=(200, 0, 0)) | |
| draw.ellipse([14, 20, 18, 24], fill=(200, 0, 0)) | |
| return Sprite(frames=[np.array(img)]) | |
| def generate_wood_sprite() -> Sprite: | |
| img = Image.new('RGBA', (32, 32), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| # Log pile | |
| draw.rectangle([8, 18, 24, 24], fill=(101, 67, 33)) | |
| draw.rectangle([10, 12, 22, 18], fill=(139, 90, 43)) | |
| draw.rectangle([12, 6, 20, 12], fill=(101, 67, 33)) | |
| # Wood rings | |
| draw.ellipse([14, 8, 18, 10], fill=(80, 50, 20)) | |
| return Sprite(frames=[np.array(img)]) | |
| # ======================================================================== | |
| # BUILDING SPRITES (IMPROVED MUSHROOM HOUSES!) | |
| # ======================================================================== | |
| def generate_house_sprite(level: int = 1) -> Sprite: | |
| """Beautiful mushroom house!""" | |
| size = 64 + (level - 1) * 8 | |
| img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| # Stem (house base) | |
| stem_color = (240, 230, 210) if level == 1 else (250, 240, 220) | |
| draw.rectangle([size//4, size//2, size*3//4, size-4], fill=stem_color) | |
| # Add texture to stem | |
| for i in range(3): | |
| y = size//2 + 10 + i*8 | |
| draw.line([(size//4+2, y), (size*3//4-2, y)], fill=(220, 210, 190), width=1) | |
| # Mushroom cap (red with white spots) | |
| cap_color = (200, 50, 50) if level == 1 else (220, 70, 70) | |
| draw.ellipse([4, size//4, size-4, size//2+10], fill=cap_color) | |
| # White spots on cap | |
| spot_positions = [ | |
| (size//4, size//3), | |
| (size//2 - 4, size//4 + 4), | |
| (size*3//4 - 8, size//3 + 2) | |
| ] | |
| if level > 1: | |
| spot_positions.extend([ | |
| (size//3 - 4, size//2 - 2), | |
| (size*2//3, size//2 - 4) | |
| ]) | |
| for x, y in spot_positions: | |
| spot_size = 6 if level == 1 else 8 | |
| draw.ellipse([x, y, x+spot_size, y+spot_size], fill=(255, 255, 255)) | |
| # Door | |
| door_width = 12 if level == 1 else 14 | |
| door_x = size//2 - door_width//2 | |
| draw.rectangle([door_x, size-18, door_x+door_width, size-4], fill=(101, 67, 33)) | |
| # Door knob | |
| draw.ellipse([door_x+door_width-4, size-12, door_x+door_width-2, size-10], fill=(255, 215, 0)) | |
| # Windows (cute round windows) | |
| if level > 1: | |
| window_size = 8 | |
| # Left window | |
| draw.ellipse([size//4-4, size//2+8, size//4+4, size//2+16], fill=(135, 206, 250)) | |
| draw.line([(size//4, size//2+8), (size//4, size//2+16)], fill=(100, 150, 200), width=1) | |
| # Right window | |
| draw.ellipse([size*3//4-4, size//2+8, size*3//4+4, size//2+16], fill=(135, 206, 250)) | |
| draw.line([(size*3//4, size//2+8), (size*3//4, size//2+16)], fill=(100, 150, 200), width=1) | |
| if level > 2: | |
| # Chimney | |
| draw.rectangle([size*3//4-4, size//4-4, size*3//4+4, size//4+8], fill=(120, 80, 50)) | |
| # Smoke puffs | |
| draw.ellipse([size*3//4-2, size//4-8, size*3//4+2, size//4-4], fill=(200, 200, 200)) | |
| return Sprite(frames=[np.array(img)]) | |
| def generate_storage_sprite(level: int = 1) -> Sprite: | |
| size = 64 | |
| img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| # Barn/warehouse style | |
| draw.rectangle([8, 20, size-8, size-4], fill=(139, 90, 43)) | |
| draw.polygon([(8, 20), (size//2, 8), (size-8, 20)], fill=(120, 80, 50)) | |
| # Capacity bars | |
| for i in range(level): | |
| y = size - 12 - i*8 | |
| draw.rectangle([12, y, size-12, y+6], fill=(200, 180, 140)) | |
| # Door | |
| draw.rectangle([size//2-8, size-16, size//2+8, size-4], fill=(80, 50, 30)) | |
| return Sprite(frames=[np.array(img)]) | |
| def generate_workshop_sprite(level: int = 1) -> Sprite: | |
| size = 64 | |
| img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| # Workshop building | |
| draw.rectangle([8, 24, size-8, size-4], fill=(120, 100, 80)) | |
| draw.polygon([(8, 24), (size//2, 12), (size-8, 24)], fill=(100, 80, 60)) | |
| # Chimney with smoke | |
| draw.rectangle([size-16, 8, size-12, 24], fill=(80, 60, 40)) | |
| draw.ellipse([size-18, 4, size-10, 10], fill=(180, 180, 180)) | |
| # Tool rack visual | |
| draw.line([(12, 32), (size-12, 32)], fill=(60, 40, 20), width=2) | |
| return Sprite(frames=[np.array(img)]) | |
| def generate_townhall_sprite(level: int = 1) -> Sprite: | |
| size = 80 | |
| img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| # Grand mushroom building | |
| draw.rectangle([10, 30, size-10, size-4], fill=(180, 100, 60)) | |
| draw.ellipse([5, 10, size-5, 35], fill=(220, 80, 80)) | |
| # Multiple white spots | |
| spots = [(15, 18), (30, 15), (45, 17), (60, 19)] | |
| for x, y in spots: | |
| draw.ellipse([x, y, x+8, y+8], fill=(255, 255, 255)) | |
| # Flag on top | |
| draw.rectangle([size//2-2, 4, size//2+2, 20], fill=(101, 67, 33)) | |
| draw.polygon([(size//2+2, 8), (size//2+2, 16), (size//2+14, 12)], fill=(255, 0, 0)) | |
| # Large entrance | |
| draw.rectangle([size//2-10, size-24, size//2+10, size-4], fill=(101, 67, 33)) | |
| # Star emblem | |
| draw.text((size//2-6, size//2+4), "★", fill=(255, 215, 0)) | |
| return Sprite(frames=[np.array(img)]) | |
| def generate_grave_sprite() -> Sprite: | |
| img = Image.new('RGBA', (32, 32), (0, 0, 0, 0)) | |
| draw = ImageDraw.Draw(img) | |
| # Tombstone | |
| draw.rectangle([10, 12, 22, 28], fill=(100, 100, 100)) | |
| draw.ellipse([10, 8, 22, 16], fill=(100, 100, 100)) | |
| # Cross engraving | |
| draw.line([(16, 14), (16, 20)], fill=(80, 80, 80), width=2) | |
| draw.line([(13, 17), (19, 17)], fill=(80, 80, 80), width=2) | |
| # Flowers | |
| draw.ellipse([8, 26, 11, 29], fill=(255, 100, 100)) | |
| draw.ellipse([21, 26, 24, 29], fill=(255, 200, 100)) | |
| return Sprite(frames=[np.array(img)]) | |