changeset 18:13da8f9bed5f

further clean up the level rendering
author sheep@ghostwheel
date Mon, 06 Jul 2009 13:18:46 +0200
parents 31c17b907fad
children ec5011a0651d
files qq.py
diffstat 1 files changed, 110 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/qq.py	Sun Jul 05 23:32:42 2009 +0200
+++ b/qq.py	Mon Jul 06 13:18:46 2009 +0200
@@ -4,7 +4,8 @@
 import ConfigParser
 
 import pygame
-from pygame.locals import *
+import pygame.locals as pg
+# from pygame.locals import *
 
 
 # Motion offsets for particular directions
@@ -14,30 +15,36 @@
 
 
 class TileCache:
-    """Load the tilesets lazily."""
+    """Load the tilesets lazily into global cache"""
 
-    def __init__(self):
-        self.cache = {}
+    cache = {}
 
-    def __call__(self, filename, width=32, height=None):
-        if height is None:
-            height = width
-        key = (filename, width, height)
+    def __init__(self,  width=32, height=None):
+        self.width = width
+        self.height = height or width
+
+    def __getitem__(self, filename):
+        key = (filename, self.width, self.height)
         try:
             return self.cache[key]
-        except:
-            pass
+        except KeyError:
+            tile_table = self.load_tile_table(filename, self.width, self.height)
+            self.cache[key] = tile_table
+            return tile_table
+
+    def load_tile_table(self, filename, width, height):
+        """Load the image and split into tiles."""
+
         image = pygame.image.load(filename).convert()
         image_width, image_height = image.get_size()
-        tiles = []
-        for x in range(0, image_width/width):
+        tile_table = []
+        for tile_x in range(0, image_width/width):
             line = []
-            tiles.append(line)
-            for y in range(0, image_height/height):
-                line.append(image.subsurface((x*width, y*height, width,
-                                              height)))
-        self.cache[key] = tiles
-        return tiles
+            tile_table.append(line)
+            for tile_y in range(0, image_height/height):
+                rect = (tile_x*width, tile_y*height, width, height)
+                line.append(image.subsurface(rect))
+        return tile_table
 
 
 class SortedUpdates(pygame.sprite.RenderUpdates):
@@ -54,17 +61,17 @@
 class Shadow(pygame.sprite.Sprite):
     """Sprite for shadows."""
 
-    def __init__(self, sprite):
-        self.image = tiles("shadow.png", 24, 24)[0][0]
+    def __init__(self, owner):
+        self.image = TileCache(24, 24)["shadow.png"][0][0]
         self.image.set_alpha(64)
         pygame.sprite.Sprite.__init__(self)
         self.rect = self.image.get_rect()
-        self.sprite = sprite
+        self.owner = owner
 
     def update(self):
         """Move the shadow to the right place."""
 
-        self.rect.midbottom = self.sprite.rect.midbottom
+        self.rect.midbottom = self.owner.rect.midbottom
 
 
 class Sprite(pygame.sprite.Sprite):
@@ -73,13 +80,14 @@
     is_player = False
 
     def __init__(self, pos=(0, 0), frames=None):
-        pygame.sprite.Sprite.__init__(self)
+        super(Sprite, self).__init__()
         if frames:
             self.frames = frames
         self.image = self.frames[0][0]
         self.rect = self.image.get_rect()
         self.animation = self.stand_animation()
         self.pos = pos
+        self.depth = 0
 
     def _get_pos(self):
         """Check the current position of the sprite on the map."""
@@ -121,7 +129,7 @@
     is_player = True
 
     def __init__(self, pos=(1, 1)):
-        self.frames = tiles("player.png")
+        self.frames = TileCache()["player.png"]
         Sprite.__init__(self, pos)
         self.direction = 2
         self.animation = None
@@ -150,11 +158,16 @@
             except StopIteration:
                 self.animation = None
 
-
-class Level:
+class Level(object):
     """Load and store the map of the level, together with all the items."""
 
     def __init__(self, filename="level.map"):
+        self.tileset = ''
+        self.map = []
+        self.items = {}
+        self.key = {}
+        self.width = 0
+        self.height = 0
         self.load_file(filename)
 
     def load_file(self, filename="level.map"):
@@ -162,16 +175,13 @@
 
         parser = ConfigParser.ConfigParser()
         parser.read(filename)
-        self.items = {}
-        self.key = {}
+        self.tileset = parser.get("level", "tileset")
         self.map = parser.get("level", "map").split("\n")
         for section in parser.sections():
             if len(section) == 1:
-                value = parser.get(section, "name")
-                self.key[section] = dict(parser.items(section))
-        tile_width, tile_height = 24, 16
-        self.tiles = tiles(parser.get("level", "tileset"),
-                           tile_width, tile_height)
+                # value = parser.get(section, "name")
+                desc = dict(parser.items(section))
+                self.key[section] = desc
         self.width = len(self.map[0])
         self.height = len(self.map)
         y = 0
@@ -188,55 +198,55 @@
     def render(self):
         """Draw the level on the surface."""
 
-        self.image = pygame.Surface((self.width*tile_width,
-                                     self.height*tile_height))
-        self.overlays = {}
-        y = 0
+        wall = self.is_wall
+        tile_width, tile_height = 24, 16
+        tiles = TileCache(tile_width, tile_height)[self.tileset]
+        image = pygame.Surface((self.width*tile_width, self.height*tile_height))
+        overlays = {}
+        map_y = 0
         for line in self.map:
-            x = 0
+            map_x = 0
             for c in line:
-                if not self.is_wall(x, y):
-                    try:
-                        tile_x, tile_y = self.key[c]['tile'].split(',')
-                        tile_x = int(tile_x)
-                        tile_y = int(tile_y)
-                    except ValueError, KeyError:
-                        tile_x, tile_y = 0, 3
-                    self.image.blit(self.tiles[tile_x][tile_y],
-                                    (x*tile_width, y*tile_height))
+                if wall(map_x, map_y):
+                    if not wall(map_x, map_y+1):
+                        if wall(map_x+1, map_y) and wall(map_x-1, map_y):
+                            tile = 1, 2
+                        elif wall(map_x+1, map_y):
+                            tile = 0, 2
+                        elif wall(map_x-1, map_y):
+                            tile = 2, 2
+                        else:
+                            tile = 3, 2
+                    else:
+                        if wall(map_x+1, map_y+1) and wall(map_x-1, map_y+1):
+                            tile = 1, 1
+                        elif wall(map_x+1, map_y+1):
+                            tile = 0, 1
+                        elif wall(map_x-1, map_y+1):
+                            tile = 2, 1
+                        else:
+                            tile = 3, 1
+                    if not wall(map_x, map_y-1):
+                        if wall(map_x+1, map_y) and wall(map_x-1, map_y):
+                            over = 1, 0
+                        elif wall(map_x+1, map_y):
+                            over = 0, 0
+                        elif wall(map_x-1, map_y):
+                            over = 2, 0
+                        else:
+                            over = 3, 0
+                        overlays[(map_x, map_y)] = tiles[over[0]][over[1]]
                 else:
-                    # wall
-                    if not self.is_wall(x, y+1):
-                        if self.is_wall(x+1, y) and self.is_wall(x-1, y):
-                            tile_image = self.tiles[1][2]
-                        elif self.is_wall(x+1, y):
-                            tile_image = self.tiles[0][2]
-                        elif self.is_wall(x-1, y):
-                            tile_image = self.tiles[2][2]
-                        else:
-                            tile_image = self.tiles[3][2]
-                    else:
-                        if self.is_wall(x+1, y+1) and self.is_wall(x-1, y+1):
-                            tile_image = self.tiles[1][1]
-                        elif self.is_wall(x+1, y+1):
-                            tile_image = self.tiles[0][1]
-                        elif self.is_wall(x-1, y+1):
-                            tile_image = self.tiles[2][1]
-                        else:
-                            tile_image = self.tiles[3][1]
-                    self.image.blit(tile_image, (x*tile_width, y*tile_height))
-                    if not self.is_wall(x, y-1):
-                        if self.is_wall(x+1, y) and self.is_wall(x-1, y):
-                            overlay_image = self.tiles[1][0]
-                        elif self.is_wall(x+1, y):
-                            overlay_image = self.tiles[0][0]
-                        elif self.is_wall(x-1, y):
-                            overlay_image = self.tiles[2][0]
-                        else:
-                            overlay_image = self.tiles[3][0]
-                        self.overlays[(x, y)] = overlay_image
-                x += 1
-            y += 1
+                    try:
+                        tile = self.key[c]['tile'].split(',')
+                        tile = int(tile[0]), int(tile[1])
+                    except (ValueError, KeyError):
+                        tile = 0, 3
+                tile_image = tiles[tile[0]][tile[1]]
+                image.blit(tile_image, (map_x*tile_width, map_y*tile_height))
+                map_x += 1
+            map_y += 1
+        return image, overlays
 
     def get_tile(self, x, y):
         """Tell what's at the specified position of the map."""
@@ -275,10 +285,15 @@
 
 
 class Game:
+    """The main game object."""
+
     def __init__(self):
         self.screen = pygame.display.get_surface()
         self.pressed_key = None
         self.game_over = False
+        self.shadows = pygame.sprite.RenderUpdates()
+        self.sprites = SortedUpdates()
+        self.overlays = pygame.sprite.RenderUpdates()
         self.use_level(Level())
 
     def use_level(self, level):
@@ -293,34 +308,41 @@
                 sprite = Player(pos)
                 self.player = sprite
             else:
-                sprite = Sprite(pos, tiles(tile["sprite"]))
+                sprite = Sprite(pos, TileCache()[tile["sprite"]])
             self.sprites.add(sprite)
             self.shadows.add(Shadow(sprite))
-        for pos, image in level.overlays.iteritems():
+        self.background, overlays = self.level.render()
+        for pos, image in overlays.iteritems():
             x, y = pos
             overlay = pygame.sprite.Sprite(self.overlays)
             overlay.image = image
             overlay.rect = image.get_rect().move(x*24, y*16-16)
-        self.background = self.level.image.copy()
 
     def control(self):
         """Handle the controls of the game."""
 
         keys = pygame.key.get_pressed()
+
         def pressed(key):
+            """Check if the key is pressed."""
+
             return self.pressed_key == key or keys[key]
+
         def walk(d):
+            """Start walking in specified direction."""
+
             x, y = self.player.pos
             self.player.direction = d
             if not self.level.is_blocking(x+DX[d], y+DY[d]):
                 self.player.animation = self.player.walk_animation()
-        if pressed(K_UP):
+
+        if pressed(pg.K_UP):
             walk(0)
-        elif pressed(K_DOWN):
+        elif pressed(pg.K_DOWN):
             walk(2)
-        elif pressed(K_LEFT):
+        elif pressed(pg.K_LEFT):
             walk(3)
-        elif pressed(K_RIGHT):
+        elif pressed(pg.K_RIGHT):
             walk(1)
         self.pressed_key = None
 
@@ -349,9 +371,9 @@
             pygame.display.update(dirty)
             clock.tick(15)
             for event in pygame.event.get():
-                if event.type == QUIT:
+                if event.type == pg.QUIT:
                     self.game_over = True
-                elif event.type == KEYDOWN:
+                elif event.type == pg.KEYDOWN:
                     self.pressed_key = event.key
 
 
@@ -359,5 +381,4 @@
     pygame.init()
     pygame.display.set_mode((424, 320))
     # font = pygame.font.Font("MgOpenModataBold.ttf", 11)
-    tiles = TileCache()
     Game().main()