File size: 8,485 Bytes
82fa936
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
"""
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

@dataclass
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
    # ========================================================================
    
    @staticmethod
    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)])
    
    @staticmethod
    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)])
    
    @staticmethod
    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!)
    # ========================================================================
    
    @staticmethod
    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)])
    
    @staticmethod
    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)])
    
    @staticmethod
    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)])
    
    @staticmethod
    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)])
    
    @staticmethod
    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)])