from os import listdir from os.path import isfile, join from sys import exc_info, stdout from glob import glob from traceback import print_exc, print_stack from pygame import Color, Rect, Surface from pygame.image import load from pygame.transform import flip from pygame.locals import * from Animation import Animation from Vector import Vector class Sprite(Animation): def __init__(self, parent, framerate=None): Animation.__init__(self, parent, self.shift_frame, framerate) self.frames = [] self.mirrored = False self.hidden = False self.alpha = 255 self.locations = [Location(self)] self.framesets = [Frameset(self, framerate=framerate)] self.set_frameset(0) self.motion_overflow = Vector() self.display_surface = self.get_display_surface() def __getattr__(self, name): if name in ("location", "rect"): return self.locations[0] if hasattr(Animation, "__getattr__"): return Animation.__getattr__(self, name) raise AttributeError, name def set_frameset(self, identifier): if isinstance(identifier, str): for ii, frameset in enumerate(self.framesets): if frameset.name == identifier: identifier = ii break self.frameset_index = identifier self.register_interval() self.update_location_size() if self.get_current_frameset().length() > 1: self.play() def register_interval(self): self.register(self.shift_frame, interval=self.get_current_frameset().framerate) def get_current_frameset(self): return self.framesets[self.frameset_index] def update_location_size(self): size = self.get_current_frameset().rect.size for location in self.locations: location.size = size location.fader.init_surface() def set_framerate(self, framerate): self.get_current_frameset().set_framerate(framerate) self.register_interval() def load_from_path(self, path, transparency=False, ppa=True, key=None, extension=None, omit=False): if isfile(path): paths = [path] else: if extension: paths = sorted(glob(join(path, "*." + extension))) else: paths = [join(path, name) for name in sorted(listdir(path))] for path in paths: img = load(path) if transparency: if ppa: frame = img.convert_alpha() else: frame = self.fill_colorkey(img, key) else: frame = img.convert() self.add_frame(frame, omit) def fill_colorkey(self, img, key=None): if not key: key = (255, 0, 255) img = img.convert_alpha() frame = Surface(img.get_size()) frame.fill(key) frame.set_colorkey(key) frame.blit(img, (0, 0)) return frame def add_frame(self, frame, omit=False): self.frames.append(frame) frame.set_alpha(self.alpha) if not omit: frameset = self.get_current_frameset() frameset.add_index(self.frames.index(frame)) self.update_location_size() if frameset.length() > 1: self.play() def shift_frame(self): self.get_current_frameset().shift() def get_current_frame(self): return self.frames[self.get_current_frameset().get_current_id()] def move(self, dx=0, dy=0): for location in self.locations: location.move_ip(dx, dy) def reset_motion_overflow(self): for location in self.locations: location.reset_motion_overflow() def collide(self, other): if not isinstance(other, Rect): other = other.rect for location in self.locations: if location.colliderect(other): return location def mirror(self): frames = self.frames for ii, frame in enumerate(frames): frames[ii] = flip(frame, True, False) self.mirrored = not self.mirrored def clear_frames(self): self.frames = [] for frameset in self.framesets: frameset.order = [] frameset.reset() frameset.measure_rect() def add_location(self, topleft=None, offset=(0, 0), count=1, base=0): if topleft is not None: for ii in xrange(count): self.locations.append(Location( self, Rect(topleft, self.locations[0].size))) else: base = self.locations[base] current_offset = list(offset) for ii in xrange(count): self.locations.append(Location(self, base.move(*current_offset))) current_offset[0] += offset[0] current_offset[1] += offset[1] return self.locations[-1] def fade(self, length=0, out=None, index=None): if index is None: for location in self.locations: location.fader.start(length, out) else: self.locations[index].fader.start(length, out) def set_alpha(self, alpha): self.alpha = alpha for frame in self.frames: frame.set_alpha(alpha) for location in self.locations: location.fader.set_alpha() def add_frameset(self, order, framerate=None, name=None): frameset = Frameset(self, order, framerate, name) self.framesets.append(frameset) return frameset def hide(self): self.hidden = True def unhide(self): self.hidden = False def remove_locations(self, location=None): if location: self.locations.remove(location) else: self.locations = self.locations[:1] def reverse(self, frameset=None): if frameset: frameset.reverse() else: for frameset in self.framesets: frameset.reverse() def update(self, flags=0): Animation.update(self) self.draw(flags) def draw(self, flags=0): for location in self.locations: location.fader.draw(flags) class Location(Rect): def __init__(self, sprite, rect=(0, 0, 0, 0)): self.sprite = sprite Rect.__init__(self, rect) self.motion_overflow = Vector() self.fader = Fader(self) def move_ip(self, dx, dy): if isinstance(dx, float) or isinstance(dy, float): excess = self.update_motion_overflow(dx, dy) Rect.move_ip(self, int(dx) + excess[0], int(dy) + excess[1]) else: Rect.move_ip(self, dx, dy) def update_motion_overflow(self, dx, dy): overflow = self.motion_overflow overflow.move(dx - int(dx), dy - int(dy)) excess = map(int, overflow) overflow[0] -= int(overflow[0]) overflow[1] -= int(overflow[1]) return excess def reset_motion_overflow(self): self.motion_overflow.place_at_origin() class Fader(Surface): def __init__(self, location): self.location = location self.time_filter = location.sprite.get_game().time_filter self.reset() def reset(self): self.init_surface() self.fade_remaining = None def init_surface(self): Surface.__init__(self, self.location.size) if self.location.sprite.frames: background = Surface(self.get_size()) sprite = self.location.sprite key = sprite.get_current_frame().get_colorkey() or (255, 0, 255) self.set_colorkey(key) background.fill(key) self.background = background self.set_alpha() def set_alpha(self, alpha=None): if alpha is None: alpha = self.location.sprite.alpha Surface.set_alpha(self, alpha) def start(self, length, out=None): if self.fade_remaining <= 0: alpha = self.get_alpha() maximum = self.location.sprite.alpha if out is None: out = alpha == maximum if out and alpha > 0 or not out and alpha < maximum: self.fade_length = self.fade_remaining = length self.start_time = self.time_filter.get_ticks() self.fading_out = out def draw(self, flags): sprite = self.location.sprite if self.fade_remaining >= 0: self.update_alpha() self.clear() frame = sprite.get_current_frame() frame.set_alpha(255) self.blit(frame, (0, 0)) frame.set_alpha(sprite.alpha) if not sprite.hidden: self.blit_to_display(self, flags) elif self.fade_remaining is None or self.get_alpha() >= sprite.alpha: if self.fade_remaining >= 0: self.update_alpha() if not sprite.hidden: self.blit_to_display(sprite.get_current_frame(), flags) def blit_to_display(self, frame, flags): self.location.sprite.display_surface.blit(frame, self.location, None, flags) def update_alpha(self): remaining = self.fade_remaining = self.fade_length - \ (self.time_filter.get_ticks() - self.start_time) ratio = self.fade_length and float(remaining) / self.fade_length if not self.fading_out: ratio = 1 - ratio maximum = self.location.sprite.alpha alpha = int(ratio * maximum) if alpha > maximum: alpha = maximum elif alpha < 0: alpha = 0 self.set_alpha(alpha) def clear(self): self.blit(self.background, (0, 0)) class Frameset(): def __init__(self, sprite, order=[], framerate=None, name=None): self.sprite = sprite self.name = name self.reversed = False self.order = [] self.rect = Rect(0, 0, 0, 0) self.add_index(order) self.set_framerate(framerate) self.reset() def add_index(self, order): if isinstance(order, int): order = [order] self.order += order self.measure_rect() def set_framerate(self, framerate): self.framerate = framerate def reset(self): self.current_index = 0 def get_current_id(self): return self.order[self.current_index] def measure_rect(self): max_width, max_height = 0, 0 frames = self.sprite.frames for index in self.order: frame = frames[index] width, height = frame.get_size() max_width = max(width, max_width) max_height = max(height, max_height) self.rect.size = max_width, max_height def shift(self): if len(self.order) > 1: self.increment_index() def increment_index(self): increment = 1 if not self.reversed else -1 index = self.current_index + increment if index < 0: index = self.length() - 1 elif index >= self.length(): index = 0 self.current_index = index def length(self): return len(self.order) def reverse(self): self.reversed = not self.reversed