changeset 26:7864a945fa1b

Overhaul the animation system to make it more turn-based
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sat, 10 Mar 2012 09:57:57 +0100
parents 5aca833e2031
children d0ca7aad951c
files mhrok.py
diffstat 1 files changed, 133 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/mhrok.py	Tue Mar 06 23:03:34 2012 +0100
+++ b/mhrok.py	Sat Mar 10 09:57:57 2012 +0100
@@ -13,6 +13,10 @@
 from pygame import locals as pg
 
 
+DX = [-1,  0,  1,  0]
+DY = [0, -1,  0,  1]
+
+
 class Shadow(pygame.sprite.Sprite):
     def __init__(self, frames, owner):
         super(Shadow, self).__init__()
@@ -35,6 +39,7 @@
             self.images[r] = self.get_image(r)
         self.radius = radius
         self.set_radius(radius)
+        self.rooms = []
 
     def set_radius(self, radius):
         self.current_radius = radius
@@ -42,7 +47,6 @@
         self.rect = self.image.get_rect()
         self.rect.center = self.owner.rect.center
 
-
     def get_image(self, radius):
         image = pygame.Surface((radius*5/2, radius*2), pg.SRCALPHA)
         image.fill((255, 255, 255, 255))
@@ -61,13 +65,10 @@
         self.rect.center = self.owner.rect.center
 
 
-class Mob(pygame.sprite.Sprite):
-    animations = [[0]]
-    movements = [(0, 0)]
-
-    def __init__(self, frames, (x, y)):
-        super(Mob, self).__init__()
-        self.animation = 0
+class MobSprite(pygame.sprite.Sprite):
+    def __init__(self, frames, (x, y), animation):
+        super(MobSprite, self).__init__()
+        self.animation = animation
         self.frames = frames
         self.image = self.frames[0]
         self.rect = self.image.get_rect()
@@ -76,83 +77,129 @@
         self.depth = self.rect.bottom
 
     def update(self):
-        self.rect.move_ip(*self.movements[self.animation])
-        self.frame = (self.frame + 1) % len(self.animations[self.animation])
-        self.image = self.frames[self.animations[self.animation][self.frame]]
-        self.depth = self.rect.bottom
+        self.frame = (self.frame + 1) % len(self.animation)
+        self.image = self.frames[self.animation[self.frame]]
+        #if self.direction is not None:
+        #    self.rect.move_ip((DX[self.direction]*5, DY[self.direction]*3))
+        #    self.depth = self.rect.bottom
+
+class Mob(object):
+    Sprite = MobSprite
+
+    def __init__(self, (x, y)):
+        self.pos = x, y
+        self.direction = None
+
+    def make_sprite(self, tiles):
+        self.sprite = self.Sprite(tiles, self.pos, self.animation)
+        shadow = Shadow(tiles, self.sprite)
+        return self.sprite, shadow, None
+
+    def control(self, level):
+        self.direction = random.choice([None, 0, 1, 2, 3])
 
 
 class Bat(Mob):
-    animations = [
-        [25, 26]
-    ]
-    movements = [(0, 0), (0, 0)]
+    animation = [25, 26]
 
 
 class Chest(Mob):
-    animations = [
-        [27],
-        [28],
-    ]
+    animation = [27]
 
 
 class Skeleton(Mob):
-    animations = [
-        [35, 35, 35, 36, 36, 36, 35, 35, 35, 37, 37, 37],
-    ]
+    animation = [35, 35, 35, 36, 36, 36, 35, 35, 35, 37, 37, 37]
 
 
 class Teddybear(Mob):
-    animations = [
-        [30, 30, 30, 31, 31, 31, 30, 30, 30, 32, 32, 32],
-    ]
+    animation = [30, 30, 30, 31, 31, 31, 30, 30, 30, 32, 32, 32]
 
 
-class PlayerMob(Mob):
-    animations = [
-        [0, 0, 1, 1, 0, 0, 2, 2],
-        [5, 5, 6, 6, 5, 5, 7, 7],
-        [10, 10, 11, 11, 10, 10, 12, 12],
-        [15, 15, 16, 16, 15, 15, 16, 16],
-        [0],
-        [5],
-        [10],
-        [15],
-    ]
-    movements = [
-        (0, 3),
-        (-5, 0),
-        (0, -3),
-        (5, 0),
-        (0, 0),
-        (0, 0),
-        (0, 0),
-        (0, 0),
-    ]
+class PlayerSprite(pygame.sprite.Sprite):
+    def __init__(self, frames, (x, y)):
+        super(PlayerSprite, self).__init__()
+        self.direction = 0
+        self.frames = frames
+        self.image = self.frames[0]
+        self.animation = self.stand_animation(self.direction)
+        self.rect = self.image.get_rect()
+        self.rect.move_ip(4 + 20 * x, 12 * y)
+        self.depth = self.rect.bottom
+
+    def update(self):
+        if self.animation:
+            try:
+                self.animation.next()
+            except StopIteration:
+                self.animation = None
 
+    def walk_animation(self, direction):
+        frames = [
+            [5, 6, 5, 7],
+            [10, 11, 10, 12],
+            [15, 16, 15, 16],
+            [0, 1, 0, 2],
+        ][direction]
+        for frame in frames:
+            yield None
+            self.rect.move_ip((DX[direction]*5, DY[direction]*3))
+            self.image = self.frames[frame]
+            self.depth = self.rect.bottom
+
+    def stand_animation(self, direction):
+        yield None
+        frame = [5, 10, 15, 0][direction]
+        self.image = self.frames[frame]
+
+    def set_animation(self, animation):
+        self.animation = animation
+        try:
+            self.animation.next()
+        except StopIteration:
+            self.animation = None
 
 class Player(object):
     def place(self, (x, y)):
         self.pos = x, y
+        self.direction = 3
+        self.sprite = None
 
-    def make_mob(self, tiles):
-        self.mob = PlayerMob(tiles, self.pos)
-        return self.mob
+    def make_sprite(self, tiles):
+        self.sprite = PlayerSprite(tiles, self.pos)
+        shadow = Shadow(tiles, self.sprite)
+        self.vision = Vision(self.sprite, 64)
+        return self.sprite, shadow, self.vision
+
+    def walk(self, level, direction):
+        self.direction = direction
+        x, y = self.pos
+        if not level[x + DX[direction], y + DY[direction]]:
+            return
+        self.sprite.set_animation(self.sprite.walk_animation(direction))
+        self.pos = x + DX[direction], y + DY[direction]
 
     def control(self, level):
-        mx, my = self.mob.rect.topleft
-        x, y = (mx+1)/20, (my+6)/12
         keys = pygame.key.get_pressed()
-        if keys[pg.K_UP] and level[(mx - 4 + 5) / 20, (my + 6 - 3) / 12]:
-            self.mob.animation = 2
-        elif keys[pg.K_LEFT] and level[(mx - 9 + 5) / 20, (my + 6) / 12]:
-            self.mob.animation = 1
-        elif keys[pg.K_DOWN] and level[(mx - 4 + 5) / 20, (my + 9 + 3) / 12]:
-            self.mob.animation = 0
-        elif keys[pg.K_RIGHT] and level[(mx + 1 + 10) / 20, (my + 6) / 12]:
-            self.mob.animation = 3
+        if keys[pg.K_UP]:
+            self.walk(level, 1)
+        elif keys[pg.K_LEFT]:
+            self.walk(level, 0)
+        elif keys[pg.K_DOWN]:
+            self.walk(level, 3)
+        elif keys[pg.K_RIGHT]:
+            self.walk(level, 2)
         else:
-            self.mob.animation = 4 + self.mob.animation % 4
+            self.sprite.set_animation(
+                    self.sprite.stand_animation(self.direction))
+
+        room = level.find_room(*self.pos)
+        if room:
+            self.vision.radius = 64
+            if room not in level.lit_rooms:
+                level.lit_rooms.append(room)
+                self.vision.rooms.append(room)
+        else:
+            self.vision.radius = 40
 
 
 class SortedUpdates(pygame.sprite.RenderUpdates):
@@ -166,7 +213,7 @@
 class Level(object):
     def __init__(self, pc):
         self.pc = pc
-        self.mobs = {}
+        self.mobs = [pc]
         self.width = 31
         self.height = 38
 
@@ -207,8 +254,8 @@
             for i in xrange(r(1, 3)):
                 self[r(x, x+w), r(y, y+h)] = 4
             for i in xrange(r(1, 4)):
-                self.mobs[r(x, x+w), r(y, y+h)] = random.choice(
-                        [Bat, Chest, Teddybear, Skeleton])
+                MobClass = random.choice([Bat, Chest, Teddybear, Skeleton])
+                self.mobs.append(MobClass((r(x, x + w), r(y, y + h))))
         self[r(x, x+w), r(y, y+h)] = 5
 
         (x, y), (w, h) = random.choice(rooms)
@@ -291,14 +338,16 @@
     def render_sprites(self, tiles):
         sprites = SortedUpdates()
         shadows = pygame.sprite.RenderUpdates()
-        for (x, y), Mob in self.mobs.iteritems():
-            mob = Mob(tiles, (x, y))
-            sprites.add(mob)
-            shadows.add(Shadow(tiles, mob))
-        mob = self.pc.make_mob(tiles)
-        sprites.add(mob)
-        shadows.add(Shadow(tiles, mob))
-        return sprites, shadows, mob
+        visions = pygame.sprite.LayeredDirty()
+        for mob in self.mobs:
+            sprite, shadow, vision = mob.make_sprite(tiles)
+            if sprite:
+                sprites.add(sprite)
+            if shadow:
+                shadows.add(shadow)
+            if vision:
+                visions.add(vision)
+        return sprites, shadows, visions
 
 
 class Display(object):
@@ -316,11 +365,9 @@
         self.dirty = []
 
     def render_level(self, level):
-        self.sprites, self.shadows, pcmob = level.render_sprites(self.tiles)
+        (self.sprites,
+         self.shadows, self.visions) = level.render_sprites(self.tiles)
         self.background.blit(level.render_background(self.tiles), (0, 0))
-        self.visions = pygame.sprite.LayeredDirty()
-        self.vision = Vision(pcmob, 64)
-        self.visions.add(self.vision)
 
     def light_room(self, room):
         (rx, ry), (rw, rh) = room
@@ -330,7 +377,7 @@
         mh = (rh+1) * 12 + 12
         darkroom = self.background.subsurface((mx, my, mw, mh))
         darkroom.set_alpha(16)
-        rect=self.memory.blit(darkroom, (mx, my))
+        rect = self.memory.blit(darkroom, (mx, my))
         self.fog.blit(self.memory, (mx, my), rect)
         self.dirty.append(rect)
 
@@ -357,7 +404,11 @@
     def update(self):
         self.sprites.update()
         self.shadows.update()
-        self.vision.update()
+        self.visions.update()
+        for vision in self.visions:
+            while vision.rooms:
+                room = vision.rooms.pop()
+                self.light_room(room)
 
 
 class Game(object):
@@ -372,24 +423,17 @@
 
     def main(self):
         while not self.game_over:
-            self.pc.control(self.level)
-            mx, my = self.pc.mob.rect.topleft
-            room = self.level.find_room((mx+1)/20, (my+6)/12)
-            if room:
-                self.display.vision.radius = 64
-                if room not in self.level.lit_rooms:
-                    self.level.lit_rooms.append(room)
-                    self.display.light_room(room)
-            else:
-                self.display.vision.radius = 40
-            self.display.draw()
             self.clock.tick(15)
             self.display.update()
+            self.display.draw()
             for event in pygame.event.get():
                 if event.type == pg.QUIT or (
                         event.type == pg.KEYDOWN and
                         event.key == pg.K_ESCAPE):
                     self.game_over = True
+            if self.pc.sprite.animation is None:
+                for mob in self.level.mobs:
+                    mob.control(self.level)
 
 
 def load_tiles(filename, width, height):