changeset 202:3074784c93f4

Animation support
author Neil Muller <drnlmuller@gmail.com>
date Fri, 04 Sep 2009 20:23:30 +0000
parents fe1e9c18d4d7
children 653da96db572
files gamelib/animations.py gamelib/engine.py gamelib/equipment.py gamelib/gameboard.py
diffstat 4 files changed, 101 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gamelib/animations.py	Fri Sep 04 20:23:30 2009 +0000
@@ -0,0 +1,52 @@
+"""Animation Loops"""
+
+from pgu.vid import Sprite
+
+import imagecache
+from misc import Position
+
+class Animation(Sprite):
+    """Animation loop.
+
+       These are derived from sprites, since they behave similiary in most
+       respects, but, to ensure draw ordering, we don't add them to
+       the sprites list.
+       
+       Ideally, animations should be quite short."""
+
+    def __init__(self, sequence, tile_pos):
+        # Create the first frame
+        self.iter = iter(sequence)
+        Sprite.__init__(self, self.iter.next(), (-1000, -1000))
+        if hasattr(tile_pos, 'to_tuple'):
+            self.pos = tile_pos
+        else:
+            self.pos = Position(tile_pos[0], tile_pos[1])
+        self.removed = False
+
+    def fix_pos(self, tv):
+        ppos = tv.tile_to_view(self.pos.to_tuple())
+        self.rect.x = ppos[0]
+        self.rect.y = ppos[1]
+
+    def animate(self):
+        """Step to the next frame.
+
+           Set removed flag when we hit the end of the sequence"""
+        try:
+            self.setimage(self.iter.next())
+        except StopIteration:
+            self.removed = True
+
+class MuzzleFlash(Animation):
+
+    SEQUENCE_LEFT = [imagecache.load_image('sprites/muzzle_flash.png')]
+    SEQUENCE_RIGHT = [imagecache.load_image('sprites/muzzle_flash.png',
+        ("right_facing",))]
+
+    def __init__(self, chicken):
+        if chicken.facing == 'right':
+            Animation.__init__(self, self.SEQUENCE_RIGHT, chicken.pos)
+        else:
+            Animation.__init__(self, self.SEQUENCE_LEFT, chicken.pos)
+
--- a/gamelib/engine.py	Fri Sep 04 20:16:51 2009 +0000
+++ b/gamelib/engine.py	Fri Sep 04 20:23:30 2009 +0000
@@ -110,6 +110,7 @@
         sound.play_sound("daybreak.ogg")
         # disable timer
         pygame.time.set_timer(MOVE_FOX_ID, 0)
+        pygame.time.set_timer(ANIM_ID, 50)
         self.game.gameboard.advance_day()
         self.game.gameboard.clear_foxes()
         sound.background_music("daytime.ogg")
@@ -121,6 +122,8 @@
         elif e.type is KEYDOWN and e.key == K_ESCAPE:
             self.game.gameboard.reset_cursor()
             return GameOver(self.game)
+        elif e.type is ANIM_ID:
+            self.game.gameboard.run_animations()
         elif e.type is KEYDOWN and e.key == K_n:
             return pygame.event.post(START_NIGHT)
         elif events_equal(e, GO_MAIN_MENU):
@@ -151,6 +154,7 @@
         # Add a timer to the event queue
         self.cycle_count = 0
         pygame.time.set_timer(MOVE_FOX_ID, 200)
+        pygame.time.set_timer(ANIM_ID, 50)
         self.game.gameboard.spawn_foxes()
         sound.background_music("nighttime.ogg")
 
@@ -166,6 +170,8 @@
         elif e.type is KEYDOWN and e.key == K_ESCAPE:
             self.game.gameboard.reset_cursor()
             return GameOver(self.game)
+        elif e.type is ANIM_ID:
+            self.game.gameboard.run_animations()
         elif e.type is MOVE_FOX_ID:
             self.cycle_count += 1
             if self.cycle_count > constants.NIGHT_LENGTH:
@@ -194,6 +200,7 @@
         sound.stop_background_music()
         self.game.create_game_over()
         pygame.time.set_timer(MOVE_FOX_ID, 0)
+        pygame.time.set_timer(ANIM_ID, 0)
 
     def event(self, e):
         if e.type is KEYDOWN:
@@ -224,5 +231,6 @@
 GO_MAIN_MENU = pygame.event.Event(USEREVENT, name="GO_MAIN_MENU")
 GO_HELP_SCREEN = pygame.event.Event(USEREVENT, name="GO_HELP_SCREEN")
 MOVE_FOX_ID = USEREVENT + 1
+ANIM_ID = USEREVENT + 6
 MOVE_FOXES = pygame.event.Event(MOVE_FOX_ID, name="MOVE_FOXES")
 QUIT = pygame.event.Event(QUIT)
--- a/gamelib/equipment.py	Fri Sep 04 20:16:51 2009 +0000
+++ b/gamelib/equipment.py	Fri Sep 04 20:23:30 2009 +0000
@@ -3,6 +3,7 @@
 import random
 import sound
 import imagecache
+import animations
 
 class Equipment(object):
     IS_EQUIPMENT = True
@@ -50,6 +51,8 @@
         """Is the potentially unlucky target actually unlucky?"""
         if hasattr(self, 'HIT_SOUND'):
             sound.play_sound(self.HIT_SOUND)
+        if hasattr(self, 'ANIMATION'):
+            gameboard.animations.append(self.ANIMATION(wielder))
         roll = random.randint(1, 100)
         base_hit = self._get_parameter('BASE_HIT', wielder)
         range_penalty = self._get_parameter('RANGE_PENALTY', wielder)
@@ -74,6 +77,8 @@
 
     CHICKEN_IMAGE_FILE = 'sprites/equip_rifle.png'
 
+    ANIMATION = animations.MuzzleFlash
+
 class Knife(Weapon):
     TYPE = "KNIFE"
     NAME = "knife"
--- a/gamelib/gameboard.py	Fri Sep 04 20:16:51 2009 +0000
+++ b/gamelib/gameboard.py	Fri Sep 04 20:23:30 2009 +0000
@@ -149,9 +149,39 @@
 
     def paint(self, surface):
         self.vid.paint(surface)
+        # Blit animation frames on top of the drawing
+        x, y = self.vid.view.x, self.vid.view.y
+        for anim in self.gameboard.animations:
+            anim.fix_pos(self.vid)
+            anim.irect.x = anim.rect.x - anim.shape.x
+            anim.irect.y = anim.rect.y - anim.shape.y
+            surface.blit(anim.image, (anim.irect.x - x, anim.irect.y - y))
+            # We don't store anim._irect, since we only update anims if the
+            # image changes, which kills irect
 
     def update(self, surface):
-        return self.vid.update(surface)
+        us = []
+        x, y = self.vid.view.x, self.vid.view.y
+        for anim in self.gameboard.animations[:]:
+            """Handle completed animations"""
+            if anim.removed:
+                us.append(pygame.Rect(anim.irect.x - x, anim.irect.y - y,
+                    anim.irect.width, anim.irect.height))
+                self.gameboard.animations.remove(anim)
+                # Flag the underlying tiles/sprites to be redrawn
+                self.vid.alayer[anim.pos.y][anim.pos.x]=1
+        us.extend(self.vid.update(surface))
+        for anim in self.gameboard.animations:
+            if anim.updated: 
+                anim.fix_pos(self.vid)
+                # setimage has happened
+                anim.irect.x = anim.rect.x - anim.shape.x
+                anim.irect.y = anim.rect.y - anim.shape.y
+                surface.blit(anim.image, (anim.irect.x - x, anim.irect.y - y))
+                anim.updated = 0
+                us.append(pygame.Rect(anim.irect.x - x, anim.irect.y - y,
+                    anim.irect.width, anim.irect.height))
+        return us
 
     def move_view(self, x, y):
         self.vid.view.move_ip((x, y))
@@ -186,6 +216,7 @@
         self.chickens = set()
         self.foxes = set()
         self.buildings = []
+        self.animations = []
         self.cash = 0
         self.eggs = 0
         self.days = 0
@@ -593,6 +624,10 @@
                 self.tv.sprites.remove(fox)
         self.foxes = set() # Remove all the foxes
 
+    def run_animations(self):
+        for anim in self.animations:
+            anim.animate()
+
     def move_foxes(self):
         """Move the foxes.