changeset 3:1cafd23c69ea default tip

use sprites for creatures
author sheep@ghostwheel
date Fri, 24 Oct 2008 17:20:35 +0200
parents 0b0d0e806f33
children
files creatures.png creatures.py display.py tiles.png world.py zday.py
diffstat 6 files changed, 178 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
Binary file creatures.png has changed
--- a/creatures.py	Sun Dec 16 14:55:57 2007 +0100
+++ b/creatures.py	Fri Oct 24 17:20:35 2008 +0200
@@ -3,11 +3,13 @@
 from util import *
 from light import Light
 
+import pygame
+
 def game_win(disp):
     disp.clear()
     disp.pause()
     sys.exit()
-    
+
 def game_over(disp):
     disp.clear()
     disp.pause()
@@ -16,8 +18,8 @@
 class ActError(Exception):
     pass
 
-class Zombie:
-    def __init__(self, world, pos = None):
+class Zombie(object):
+    def __init__(self, world, pos=None):
         self.world = world
         if pos:
             self.pos = pos
@@ -26,13 +28,12 @@
             y = random.randrange(1, world.height-2)
             while world.flags[x][y] & (FLAG_BLOCKED | FLAG_CREATURE):
                 x += 1
-                if x>=world.width-1:
+                if x >= world.width-1:
                     x = 1
                     y += 1
             self.pos = (x, y)
         self.target = None
         self.effect = None
-        world.creatures.append(self)
         self.world.flags[self.pos[0]][self.pos[1]] |= FLAG_CREATURE
         self.burning = 0
         self.dead = 0
@@ -42,7 +43,7 @@
     def trip(self):
         if not self.burning:
             self.tripped = 1
-        
+
     def untrip(self):
         if not self.burning:
             self.tripped = 0
@@ -56,14 +57,10 @@
             if self.world.flags[x][y] & FLAG_LETHAL:
                 self.world.add_tile(self.pos, MAP_MACHINE+1)
             else:
-                if random.randrange(100)>20:
+                if random.randrange(100) > 20:
                     self.world.add_tile(self.pos, self.world.corpse)
                 else:
                     self.world.blood(self.pos, 4)
-        for i in range(len(self.world.creatures)):
-            if self.world.creatures[i]==self:
-                del self.world.creatures[i]
-                break
 
     def bleed(self, amount=50):
         """Fills the map around the creature with blood."""
@@ -73,14 +70,14 @@
                 x = self.pos[0] + dx
                 y = self.pos[1] + dy
                 splatter = (random.randrange(100)+amount)/50
-                self.world.blood((x,y), splatter)
+                self.world.blood((x, y), splatter)
 
     def wound(self, lethality):
-        if lethality>0:
+        if lethality > 0:
             self.effect = EFF_HIT
-            if random.randrange(100)<lethality:
+            if random.randrange(100) < lethality:
                 self.kill()
-            elif lethality>50:
+            elif lethality > 50:
                 self.trip()
 
     def metabolism(self):
@@ -92,7 +89,7 @@
             self.trip()
         if self.burning:
             self.effect = EFF_BURN
-            if random.randrange(100)>90:
+            if random.randrange(100) > 90:
                 self.kill()
 
     def burn(self):
@@ -210,7 +207,7 @@
                                 self.act_go(0, dy)
                         except ActError:
                             pass
-        
+
 class Hero(Zombie):
     def __init__(self, world, disp, pos=None):
         Zombie.__init__(self, world, pos)
@@ -225,19 +222,21 @@
         self.disp.inv_draw(self.inventory)
         self.stamina = 100
         self.wounds = 0
-        self.dead = 0
+        self.dead = False
         self.food = 0
         self.aim = None
         self.hero = 1
+        self.command = (None, None)
 
 
     def kill(self):
+        self.dead = True
         self.world.msg.write("Aaargh! They got me! Plant an orchid on my grave...")
         game_over(self.disp)
 
     def wound(self, lethality=0):
         self.effect = EFF_HIT
-        if self.stamina==0:
+        if self.stamina == 0:
             self.kill()
         if self.wounds<5:
             self.wounds+=1
@@ -486,82 +485,57 @@
                 break
         
     def act_operate(self):
-        x, y = self.pos;
+        x, y = self.pos
         if self.world.map[x][y] == MAP_ELEV:
             self.world.new_level()
             self.disp.world_draw(self.world)
 
     def act(self):
-        x,y = self.pos
+        x, y = self.pos
         running = 0
         done = 0
-#        self.target = None
-        
-                
         if self.target and (not self.world.flags[self.target[0]][self.target[1]] & FLAG_CREATURE or
                 not self.world.flags[self.target[0]][self.target[1]] & FLAG_VISIBLE or
-                self.target==self.pos):
-            self.target=None
-        if self.target==None:
+                self.target == self.pos):
+            self.target = None
+        if self.target is None:
             for creat in self.world.creatures:
-                if self!=creat and self.world.flags[creat.pos[0]][creat.pos[1]] & FLAG_VISIBLE and not creat.burning:
+                if self != creat and self.world.flags[creat.pos[0]][creat.pos[1]] & FLAG_VISIBLE and not creat.burning:
                     if not self.target or distance(self.pos, creat.pos) < distance(self.pos, self.target):
                         self.target = creat.pos
-        self.disp.light_draw(self.light)
-        self.disp.draw_stats(self)
-        while not done:
-            done = 1
-            cmd = None
-            for creat in self.world.creatures:
-                self.disp.effect_draw(creat)
-                creat.effect = None
-            self.disp.msg_draw(self.world.msg)
-            self.disp.select(None)
-            self.disp.refresh()
-            cmd, param = self.disp.get_command(animate=1)
-            for creat in self.world.creatures:
-                self.disp.creat_draw(creat)
-            self.disp.select(self.target)
-            while not cmd:
-                self.disp.msg_draw(self.world.msg)
-                self.disp.refresh()
-                cmd, param = self.disp.get_command()
-            try:
-                if cmd == 'quit':
-                    sys.exit()
-                elif cmd == 'refresh':
-                    done = 0
-                elif cmd == 'wait':
-                    self.world.msg.write("Yawn...")
-                elif cmd == 'loot':
-                    self.act_loot()
-                elif cmd == 'use':
-                    self.act_use(param)
-                elif cmd == 'shoot':
-                    self.act_shoot()
-                elif cmd == 'trash':
-                    self.act_trash(param)
-                elif cmd == 'aim':
-                    self.act_aim()
-                    done = 0
-                elif cmd == 'operate':
-                    self.act_operate()
-                elif cmd == 'run' and param==None:
-                    done = 0
-                    running = 1
-                    self.world.msg.write("Where to run?")
-                elif cmd=='go':
-                  if running:
-                    running = 0
-                    self.act_run(param[0], param[1])
-                  else:
-                      self.act_go(param[0], param[1])
-            except ActError:
-                done = 0
-            self.disp.scroll_map(self.pos, self.world)
-            x,y = self.pos
-            self.light.cast(x, y)
-        self.world.msg.clear()
+        cmd, param = self.command
+        if cmd == 'quit':
+            sys.exit()
+        elif cmd == 'refresh':
+            done = 0
+            raise ActError()
+        elif cmd == 'wait':
+            self.world.msg.write("Yawn...")
+        elif cmd == 'loot':
+            self.act_loot()
+        elif cmd == 'use':
+            self.act_use(param)
+        elif cmd == 'shoot':
+            self.act_shoot()
+        elif cmd == 'trash':
+            self.act_trash(param)
+        elif cmd == 'aim':
+            self.act_aim()
+            done = 0
+            raise ActError()
+        elif cmd == 'operate':
+            self.act_operate()
+        elif cmd == 'run' and param is None:
+            done = 0
+            running = 1
+            self.world.msg.write("Where to run?")
+            raise ActError()
+        elif cmd == 'go':
+          if running:
+            running = 0
+            self.act_run(param[0], param[1])
+          else:
+              self.act_go(param[0], param[1])
 
 class Inventory:
     def __init__(self, world):
--- a/display.py	Sun Dec 16 14:55:57 2007 +0100
+++ b/display.py	Fri Oct 24 17:20:35 2008 +0200
@@ -6,6 +6,43 @@
 from prefs import *
 from data import *
 
+class CreatureSprite(pygame.sprite.DirtySprite):
+    def __init__(self, disp, creat):
+        pygame.sprite.DirtySprite.__init__(self)
+        self.creat = creat
+        self.disp = disp
+        self.frame = 0
+        self.frames = self.disp.creatures[1]
+        self.update()
+
+    def update(self):
+        if self.creat.dead:
+            self.kill()
+            return
+        if self.creat.burning:
+            self.frames = self.disp.creatures[2]
+        if self.frame == 0:
+            self.frame = 1
+        else:
+            self.frame = 0
+        self.image = self.frames[self.frame]
+        x, y = self.creat.pos
+        self.rect = pygame.rect.Rect(self.disp.xtile*(x-self.disp.xmap),
+                     self.disp.ytile*(y-self.disp.ymap)+self.disp.msgh, 16, 16)
+
+
+class CursorSprite(pygame.sprite.DirtySprite):
+    def __init__(self, disp):
+        self.disp = disp
+        pygame.sprite.DirtySprite.__init__(self)
+        self.rect = pygame.rect.Rect(0, 0, 16, 16)
+        self.image = disp.effects[0][0]
+
+    def move(self, x, y):
+        self.rect.topleft = (x*self.disp.xtile, y*self.disp.ytile+self.disp.msgh)
+
+    def update(self):
+        pass
 
 class DisplayPygame:
     def __init__(self):
@@ -18,25 +55,39 @@
         self.msgh = 32
         self.xtile = 16
         self.ytile = 16
-        self.dirty =[]
-        self.empty = pygame.Surface((self.xtile,self.ytile))
+        self.dirty = []
+        self.empty = pygame.Surface((self.xtile, self.ytile))
         self.empty.fill((0x0f, 0x0f, 0x0f))
-        self.fog = pygame.Surface((self.xtile,self.ytile))
+        self.fog = pygame.Surface((self.xtile, self.ytile))
         self.fog.fill((0x44, 0x00, 0x44))
         self.fog.set_alpha(90, RLEACCEL)
-        self.cursor = pygame.Surface((self.xtile,self.ytile))
+        self.cursor = pygame.Surface((self.xtile, self.ytile))
         self.cursor.fill((0x00, 0x66, 0x00))
         self.cursor.set_alpha(90, RLEACCEL)
-        self.screen.fill((0x0f, 0x0f, 0x0f))
+        self.buffer = pygame.Surface((self.maxmapx*self.xtile,
+                                      self.maxmapy*self.ytile), 0, self.screen)
+        self.buffer.fill((0x0f, 0x0f, 0x0f))
         pygame.display.update()
-       
+
         self.creatures = self._read_frames("creatures.png")
         self.effects = self._read_frames("effects.png")
         self.tiles = self._read_frames("tiles.png")
-        self.cursor = self.effects[1][0]
- 
+
+        self.sprites = pygame.sprite.RenderUpdates()
+        self.cursor_sprite = CursorSprite(self)
+
+
+    def add_creatures(self, creatures):
+        for creat in creatures:
+            self.sprites.add(CreatureSprite(self, creat))
+
+    def add_hero(self, hero):
+        hero_sprite = CreatureSprite(self, hero)
+        hero_sprite.frames = self.creatures[0]
+        self.sprites.add(hero_sprite)
+
     def _read_frames(self, filename, size=16):
-        '''Read an image and split it into tiles.'''
+        """Read an image and split it into tiles."""
 
         image = pygame.image.load(filename).convert()
         w, h = image.get_size()
@@ -61,10 +112,10 @@
         ax, ay = x - self.xmap, y - self.ymap
         if (ax<0) or (ax>=self.maxmapx) or (ay<0) or (ay>=self.maxmapy):
             return
-        r = Rect(self.xtile*ax, self.msgh+self.ytile*ay, self.xtile, self.ytile)
+        r = Rect(self.xtile*ax, self.ytile*ay, self.xtile, self.ytile)
         if clear:
             self.screen.fill((0x00, 0x00, 0x00), r)
-        self.screen.blit(img, r)
+        self.buffer.blit(img, r)
         self.dirty.append(r)
 
     def tile_draw(self, pos, world):
@@ -89,7 +140,7 @@
     def world_draw(self, world):
         r =Rect(0, self.msgh, self.xtile*self.maxmapx, self.ytile*self.maxmapy)
         self.dirty.append(r)
-        self.screen.fill((0x00, 0x00, 0x00), Rect(0, self.msgh, self.xtile*self.maxmapx, self.ytile*self.maxmapy))
+        self.buffer.fill((0x00, 0x00, 0x00))
         for x in range(world.width):
             for y in range(world.height):
                 self.tile_draw((x, y), world)
@@ -120,6 +171,7 @@
         self.dirty.append(r)
 
     def refresh(self):
+        self.screen.blit(self.buffer, (0, self.msgh))
         pygame.display.update(self.dirty)
         self.dirty = []
 
@@ -181,9 +233,12 @@
 
     def select(self, pos):
         if not pos:
+#            self.cursor_sprite.kill()
             return
         x, y = pos
-        self._blit_map(x, y, self.cursor, False)
+        #self._blit_map(x, y, self.cursor, False)
+        self.cursor_sprite.move(x, y)
+        self.sprites.add(self.cursor_sprite)
 
     def get_command(self, animate=0):
         shift = 0
@@ -208,3 +263,8 @@
 
     def effect_draw(self, creat):
         pass
+
+    def clear_func(self, screen, rect):
+        source = rect.move(0, -self.msgh)
+        screen.blit(self.buffer, rect, source)
+
Binary file tiles.png has changed
--- a/world.py	Sun Dec 16 14:55:57 2007 +0100
+++ b/world.py	Fri Oct 24 17:20:35 2008 +0200
@@ -35,7 +35,8 @@
     """Hold all the information about the current state of
     the game, the map and the creatures."""
 
-    def __init__(self):
+    def __init__(self, disp):
+        self.disp = disp
         self.width, self.height = 64, 64
         self.hero = None
         self.msg = Mesg()
@@ -70,10 +71,7 @@
             self.erode()
         else:
             self.final()
-        if self.hero:
-            self.creatures = [self.hero]
-        else:
-            self.creatures = []
+        self.creatures = []
         self.populate()
 
     def find_creature(self, pos):
@@ -174,7 +172,7 @@
             self.add_tile((x, y), self.named_tile("up"))
             self.flags[x][y] |= FLAG_CREATURE
         for i in xrange(random.randrange(50+5*self.level, 90+20*self.level)):
-            Zombie(self)
+            self.creatures.append(Zombie(self))
 
 
     def put_doors(self, pos1, pos2, tile):
--- a/zday.py	Sun Dec 16 14:55:57 2007 +0100
+++ b/zday.py	Fri Oct 24 17:20:35 2008 +0200
@@ -18,17 +18,58 @@
 
 from display import DisplayPygame
 from world import World
-from creatures import Hero
+from creatures import Hero, ActError
+
+from prefs import command_keys
+
+import pygame
 
 def main():
     disp = DisplayPygame()
-    world = World()
+    world = World(disp)
     world.hero = Hero(world, disp)
     disp.world_draw(world)
-    while 1:
+    disp.add_creatures(world.creatures)
+    disp.add_hero(world.hero)
+    clock = pygame.time.Clock()
+    while not world.hero.dead:
         for creat in world.creatures:
             creat.metabolism()
             if not creat.dead:
                 creat.act()
+        world.msg.clear()
+        disp.light_draw(world.hero.light)
+        disp.draw_stats(world.hero)
+        done = False
+        while not done:
+            done = True
+            cmd = None
+            disp.msg_draw(world.msg)
+            disp.refresh()
+            disp.select(world.hero.target)
+            while cmd is None:
+                disp.sprites.clear(disp.screen, disp.clear_func)
+                disp.sprites.update()
+                pygame.display.update(disp.sprites.draw(disp.screen))
+                clock.tick(4)
+                for event in pygame.event.get():
+                    if event.type == pygame.QUIT:
+                        world.hero.dead = True
+                    elif event.type == pygame.locals.KEYDOWN:
+                        name = pygame.key.name(event.key)
+                        if len(name) == 1:
+                            mods = event.mod
+                            if mods & pygame.locals.KMOD_LSHIFT or mods & pygame.locals.KMOD_RSHIFT:
+                                name = chr(ord(name)-32) # XXX A hack for uppercase
+                        cmd, param = command_keys.get(name, (None, None))
+            world.hero.command = cmd, param
+            try:
+                world.hero.act()
+            except ActError:
+                done = False
+        disp.scroll_map(world.hero.pos, world)
+        world.hero.light.cast(world.hero.pos[0], world.hero.pos[1])
+
+
 
 main()