bg
[sso] / shaken_and_spit_out / pgfw / Sprite.py
1 from os import listdir
2 from os.path import isfile, join
3 from sys import exc_info, stdout
4 from glob import glob
5 from traceback import print_exc, print_stack
6
7 from pygame import Color, Rect, Surface
8 from pygame.image import load
9 from pygame.transform import flip
10 from pygame.locals import *
11
12 from Animation import Animation
13 from Vector import Vector
14
15 class Sprite(Animation):
16
17 def __init__(self, parent, framerate=None):
18 Animation.__init__(self, parent, self.shift_frame, framerate)
19 self.frames = []
20 self.mirrored = False
21 self.hidden = False
22 self.alpha = 255
23 self.locations = [Location(self)]
24 self.framesets = [Frameset(self, framerate=framerate)]
25 self.set_frameset(0)
26 self.motion_overflow = Vector()
27 self.display_surface = self.get_display_surface()
28
29 def __getattr__(self, name):
30 if name in ("location", "rect"):
31 return self.locations[0]
32 if hasattr(Animation, "__getattr__"):
33 return Animation.__getattr__(self, name)
34 raise AttributeError, name
35
36 def set_frameset(self, identifier):
37 if isinstance(identifier, str):
38 for ii, frameset in enumerate(self.framesets):
39 if frameset.name == identifier:
40 identifier = ii
41 break
42 self.frameset_index = identifier
43 self.register_interval()
44 self.update_location_size()
45 if self.get_current_frameset().length() > 1:
46 self.play()
47
48 def register_interval(self):
49 self.register(self.shift_frame,
50 interval=self.get_current_frameset().framerate)
51
52 def get_current_frameset(self):
53 return self.framesets[self.frameset_index]
54
55 def update_location_size(self):
56 size = self.get_current_frameset().rect.size
57 for location in self.locations:
58 location.size = size
59 location.fader.init_surface()
60
61 def set_framerate(self, framerate):
62 self.get_current_frameset().set_framerate(framerate)
63 self.register_interval()
64
65 def load_from_path(self, path, transparency=False, ppa=True, key=None,
66 extension=None, omit=False):
67 if isfile(path):
68 paths = [path]
69 else:
70 if extension:
71 paths = sorted(glob(join(path, "*." + extension)))
72 else:
73 paths = [join(path, name) for name in sorted(listdir(path))]
74 for path in paths:
75 img = load(path)
76 if transparency:
77 if ppa:
78 frame = img.convert_alpha()
79 else:
80 frame = self.fill_colorkey(img, key)
81 else:
82 frame = img.convert()
83 self.add_frame(frame, omit)
84
85 def fill_colorkey(self, img, key=None):
86 if not key:
87 key = (255, 0, 255)
88 img = img.convert_alpha()
89 frame = Surface(img.get_size())
90 frame.fill(key)
91 frame.set_colorkey(key)
92 frame.blit(img, (0, 0))
93 return frame
94
95 def add_frame(self, frame, omit=False):
96 self.frames.append(frame)
97 frame.set_alpha(self.alpha)
98 if not omit:
99 frameset = self.get_current_frameset()
100 frameset.add_index(self.frames.index(frame))
101 self.update_location_size()
102 if frameset.length() > 1:
103 self.play()
104
105 def shift_frame(self):
106 self.get_current_frameset().shift()
107
108 def get_current_frame(self):
109 return self.frames[self.get_current_frameset().get_current_id()]
110
111 def move(self, dx=0, dy=0):
112 for location in self.locations:
113 location.move_ip(dx, dy)
114
115 def reset_motion_overflow(self):
116 for location in self.locations:
117 location.reset_motion_overflow()
118
119 def collide(self, other):
120 if not isinstance(other, Rect):
121 other = other.rect
122 for location in self.locations:
123 if location.colliderect(other):
124 return location
125
126 def mirror(self):
127 frames = self.frames
128 for ii, frame in enumerate(frames):
129 frames[ii] = flip(frame, True, False)
130 self.mirrored = not self.mirrored
131
132 def clear_frames(self):
133 self.frames = []
134 for frameset in self.framesets:
135 frameset.order = []
136 frameset.reset()
137 frameset.measure_rect()
138
139 def add_location(self, topleft=None, offset=(0, 0), count=1, base=0):
140 if topleft is not None:
141 for ii in xrange(count):
142 self.locations.append(Location(
143 self, Rect(topleft, self.locations[0].size)))
144 else:
145 base = self.locations[base]
146 current_offset = list(offset)
147 for ii in xrange(count):
148 self.locations.append(Location(self,
149 base.move(*current_offset)))
150 current_offset[0] += offset[0]
151 current_offset[1] += offset[1]
152 return self.locations[-1]
153
154 def fade(self, length=0, out=None, index=None):
155 if index is None:
156 for location in self.locations:
157 location.fader.start(length, out)
158 else:
159 self.locations[index].fader.start(length, out)
160
161 def set_alpha(self, alpha):
162 self.alpha = alpha
163 for frame in self.frames:
164 frame.set_alpha(alpha)
165 for location in self.locations:
166 location.fader.set_alpha()
167
168 def add_frameset(self, order, framerate=None, name=None):
169 frameset = Frameset(self, order, framerate, name)
170 self.framesets.append(frameset)
171 return frameset
172
173 def hide(self):
174 self.hidden = True
175
176 def unhide(self):
177 self.hidden = False
178
179 def remove_locations(self, location=None):
180 if location:
181 self.locations.remove(location)
182 else:
183 self.locations = self.locations[:1]
184
185 def reverse(self, frameset=None):
186 if frameset:
187 frameset.reverse()
188 else:
189 for frameset in self.framesets:
190 frameset.reverse()
191
192 def update(self):
193 Animation.update(self)
194 self.draw()
195
196 def draw(self):
197 for location in self.locations:
198 location.fader.draw()
199
200
201 class Location(Rect):
202
203 def __init__(self, sprite, rect=(0, 0, 0, 0)):
204 self.sprite = sprite
205 Rect.__init__(self, rect)
206 self.motion_overflow = Vector()
207 self.fader = Fader(self)
208
209 def move_ip(self, dx, dy):
210 if isinstance(dx, float) or isinstance(dy, float):
211 excess = self.update_motion_overflow(dx, dy)
212 Rect.move_ip(self, int(dx) + excess[0], int(dy) + excess[1])
213 else:
214 Rect.move_ip(self, dx, dy)
215
216 def update_motion_overflow(self, dx, dy):
217 overflow = self.motion_overflow
218 overflow.move(dx - int(dx), dy - int(dy))
219 excess = map(int, overflow)
220 overflow[0] -= int(overflow[0])
221 overflow[1] -= int(overflow[1])
222 return excess
223
224 def reset_motion_overflow(self):
225 self.motion_overflow.place_at_origin()
226
227
228 class Fader(Surface):
229
230 def __init__(self, location):
231 self.location = location
232 self.time_filter = location.sprite.get_game().time_filter
233 self.reset()
234
235 def reset(self):
236 self.init_surface()
237 self.fade_remaining = None
238
239 def init_surface(self):
240 Surface.__init__(self, self.location.size)
241 if self.location.sprite.frames:
242 background = Surface(self.get_size())
243 sprite = self.location.sprite
244 key = sprite.get_current_frame().get_colorkey() or (255, 0, 255)
245 self.set_colorkey(key)
246 background.fill(key)
247 self.background = background
248 self.set_alpha()
249
250 def set_alpha(self, alpha=None):
251 if alpha is None:
252 alpha = self.location.sprite.alpha
253 Surface.set_alpha(self, alpha)
254
255 def start(self, length, out=None):
256 if self.fade_remaining <= 0:
257 alpha = self.get_alpha()
258 maximum = self.location.sprite.alpha
259 if out is None:
260 out = alpha == maximum
261 if out and alpha > 0 or not out and alpha < maximum:
262 self.fade_length = self.fade_remaining = length
263 self.start_time = self.time_filter.get_ticks()
264 self.fading_out = out
265
266 def draw(self):
267 sprite = self.location.sprite
268 if self.fade_remaining >= 0:
269 self.update_alpha()
270 self.clear()
271 frame = sprite.get_current_frame()
272 frame.set_alpha(255)
273 self.blit(frame, (0, 0))
274 frame.set_alpha(sprite.alpha)
275 if not sprite.hidden:
276 self.blit_to_display(self)
277 elif self.fade_remaining is None or self.get_alpha() >= sprite.alpha:
278 if self.fade_remaining >= 0:
279 self.update_alpha()
280 if not sprite.hidden:
281 self.blit_to_display(sprite.get_current_frame())
282
283 def blit_to_display(self, frame):
284 self.location.sprite.display_surface.blit(frame, self.location)
285
286 def update_alpha(self):
287 remaining = self.fade_remaining = self.fade_length - \
288 (self.time_filter.get_ticks() - self.start_time)
289 ratio = self.fade_length and float(remaining) / self.fade_length
290 if not self.fading_out:
291 ratio = 1 - ratio
292 maximum = self.location.sprite.alpha
293 alpha = int(ratio * maximum)
294 if alpha > maximum:
295 alpha = maximum
296 elif alpha < 0:
297 alpha = 0
298 self.set_alpha(alpha)
299
300 def clear(self):
301 self.blit(self.background, (0, 0))
302
303
304 class Frameset():
305
306 def __init__(self, sprite, order=[], framerate=None, name=None):
307 self.sprite = sprite
308 self.name = name
309 self.reversed = False
310 self.order = []
311 self.rect = Rect(0, 0, 0, 0)
312 self.add_index(order)
313 self.set_framerate(framerate)
314 self.reset()
315
316 def add_index(self, order):
317 if isinstance(order, int):
318 order = [order]
319 self.order += order
320 self.measure_rect()
321
322 def set_framerate(self, framerate):
323 self.framerate = framerate
324
325 def reset(self):
326 self.current_index = 0
327
328 def get_current_id(self):
329 return self.order[self.current_index]
330
331 def measure_rect(self):
332 max_width, max_height = 0, 0
333 frames = self.sprite.frames
334 for index in self.order:
335 frame = frames[index]
336 width, height = frame.get_size()
337 max_width = max(width, max_width)
338 max_height = max(height, max_height)
339 self.rect.size = max_width, max_height
340
341 def shift(self):
342 if len(self.order) > 1:
343 self.increment_index()
344
345 def increment_index(self):
346 increment = 1 if not self.reversed else -1
347 index = self.current_index + increment
348 if index < 0:
349 index = self.length() - 1
350 elif index >= self.length():
351 index = 0
352 self.current_index = index
353
354 def length(self):
355 return len(self.order)
356
357 def reverse(self):
358 self.reversed = not self.reversed