changeset 17:b69ed0cdaee6

refactor level map
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sun, 27 Apr 2014 14:49:31 +0200
parents 199d98200586
children fa49938e730f
files undermine/game.py undermine/level.py undermine/sprites.py
diffstat 3 files changed, 151 insertions(+), 107 deletions(-) [+]
line wrap: on
line diff
--- a/undermine/game.py	Sun Apr 27 00:43:19 2014 -0700
+++ b/undermine/game.py	Sun Apr 27 14:49:31 2014 +0200
@@ -1,23 +1,13 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-import random
 
 import pygame
 
 import undermine.display
 import undermine.resources
 import undermine.sprites
-
-
-MAP_DIRT, MAP_TUNNEL, MAP_BOULDER, MAP_GEM, MAP_HERO = range(5)
-TILES = {
-    MAP_DIRT: 12,
-    MAP_TUNNEL: 15,
-    MAP_BOULDER: 15,
-    MAP_GEM: 15,
-    MAP_HERO: 15,
-}
+import undermine.level
 
 
 class Game(object):
@@ -25,117 +15,80 @@
         self.fps = fps
         self.display = undermine.display.Display(
             (map_size[0] * 64, map_size[1] * 64))
-        self.display.background = undermine.resources.load_image('title.png')
-        self.display.refresh()
         self.clock = pygame.time.Clock()
         self.images = undermine.resources.load_atlas('undermine.png', 64)
-        self.theme = undermine.resources.load_sound('undermine.ogg')
-        self.eulogy= undermine.resources.load_sound('death.ogg')
-        self.make_map(map_size)
+        # self.theme = undermine.resources.load_sound('undermine.ogg')
+        # self.eulogy= undermine.resources.load_sound('death.ogg')
+        self.level = undermine.level.Level(map_size)
+        self.level.generate(self.images)
+        self.hero = self.level.hero
 
     def move(self, dx, dy):
         x, y = self.hero.pos
-        if not (0 <= x + dx < len(self.map[0]) and
-                0 <= y + dy < len(self.map)):
+        try:
+            target = self.level[x + dx, y + dy]
+        except IndexError:
             return
-        if self.map[y + dy][x + dx] == MAP_BOULDER:
+        if target and target.block:
             return
         self.hero.animations.append(self.hero.move_anim(dx, dy))
-        self.hero.place((x + dx, y + dy), False)
-        if self.map[y + dy][x + dx] == MAP_DIRT:
-            self.dig((x + dx, y + dy))
-        elif self.map[y + dy][x + dx] == MAP_GEM:
-            for boulder in self.boulders:
-                if boulder.pos == (x + dx, y + dy):
-                    boulder.animations.append(boulder.pick_anim())
-                    break
-        self.map[y + dy][x + dx] = MAP_HERO
-        self.map[y][x] = MAP_TUNNEL
+        self.dig((x + dx, y + dy))
+        if target and target.pick:
+            target.animations.append(target.pick_anim())
+        self.level.shove((x, y), (dx, dy))
 
     def dig(self, (x, y)):
-        self.display.background.blit(self.images[TILES[MAP_TUNNEL]],
-                                     (x * 64, y * 64))
+        if not self.level.get_dirt((x, y), False):
+            return
+        self.level.set_dirt((x, y), False)
+        self.display.background.blit(self.images[15], (x * 64, y * 64))
         dirt = undermine.sprites.Dirt(self.images)
-        dirt.place((x, y))
+        dirt.pos = x, y
         self.display.back_sprites.add(dirt)
 
     def die(self):
         self.theme.stop()
-        self.eulogy.play()
+        # self.eulogy.play()
         self.hero.animations.append(self.hero.death_anim())
 
-    def make_map(self, map_size):
-        self.boulders = pygame.sprite.Group()
-        squares = []
-        self.map = []
-        for y in range(map_size[1]):
-            self.map.append([])
-            for x in range(map_size[0]):
-                self.map[-1].append(0)
-                squares.append((x, y))
-        random.shuffle(squares)
-        self.hero = undermine.sprites.Hero(self.images)
-        self.hero.place(squares.pop())
-        self.display.sprites.add(self.hero)
-        self.map[self.hero.pos[1]][self.hero.pos[0]] = MAP_HERO
-        for i in range(8):
-            boulder = undermine.sprites.Boulder(self.images[4])
-            x, y = squares.pop()
-            boulder.place((x, y))
-            self.display.sprites.add(boulder)
-            self.boulders.add(boulder)
-            self.map[y][x] = MAP_BOULDER
-        for i in range(4):
-            gem = undermine.sprites.Gem(self.images[8])
-            x, y = squares.pop()
-            gem.place((x, y))
-            self.display.sprites.add(gem)
-            self.boulders.add(gem)
-            self.map[y][x] = MAP_GEM
-
     def draw_map(self):
-        for y, row in enumerate(self.map):
-            for x, tile in enumerate(row):
-                self.display.background.blit(self.images[TILES[tile]],
-                                             (x * 64, y * 64))
+        self.level.draw(self.display.background, self.images)
+        self.display.sprites.add(self.level)
 
     def update_boulders(self):
-        boulders = sorted(self.boulders, key=lambda b: -b.pos[1])
-        for boulder in boulders:
-            if boulder.animations:
+        for thing in sorted(self.level, key=lambda b: -b.pos[1]):
+            if not thing.fall:
                 continue
-            x, y = boulder.pos
+            if thing.animations:
+                continue
+            x, y = thing.pos
             fell = False
-            if y + 1 < len(self.map) and self.map[y + 1][x] == MAP_TUNNEL:
-                boulder.animations.append(boulder.fall_anim())
-                self.map[y + 1][x] = self.map[y][x]
-                self.map[y][x] = MAP_TUNNEL
-                boulder.place((x, y +1), False)
+            if not self.level.get_support((x, y + 1)):
+                thing.animations.append(thing.fall_anim())
+                self.level.shove((x, y), (0, 1))
                 fell = True
-            elif (y + 1 < len(self.map) and
-                  self.map[y + 1][x] in (MAP_BOULDER, MAP_GEM)):
-                if (x > 0 and
-                    self.map[y][x - 1] == MAP_TUNNEL and
-                    self.map[y + 1][x - 1] == MAP_TUNNEL):
-                    boulder.animations.append(boulder.roll_anim(-1))
-                    self.map[y + 1][x - 1] = self.map[y][x]
-                    self.map[y][x] = MAP_TUNNEL
-                    boulder.place((x - 1, y + 1), False)
+            elif (not self.level.get_dirt((x, y + 1), True) and
+                  self.level[x, y + 1] != self.hero):
+                if (not self.level.get_support((x - 1, y)) and
+                    not self.level.get_support((x - 1, y + 1))):
+                    thing.animations.append(thing.roll_anim(-1))
+                    self.level.shove((x, y), (-1, 1))
                     fell = True
-                elif (x + 1 < len(self.map[0]) and
-                      self.map[y][x + 1] == MAP_TUNNEL and
-                      self.map[y + 1][x + 1] == MAP_TUNNEL):
-                    boulder.animations.append(boulder.roll_anim(1))
-                    self.map[y + 1][x + 1] = self.map[y][x]
-                    self.map[y][x] = MAP_TUNNEL
-                    boulder.place((x + 1, y + 1), False)
+                elif (not self.level.get_support((x + 1, y)) and
+                      not self.level.get_support((x + 1, y + 1))):
+                    thing.animations.append(thing.roll_anim(1))
+                    self.level.shove((x, y), (1, 1))
                     fell = True
-            if (fell and y +2 < len(self.map) and
-                self.map[y + 2][x] == MAP_HERO):
-                    self.die()
+            if fell:
+                self.dig((x, y))
+                try:
+                    if self.level[x, y + 2] == self.hero:
+                        self.die()
+                except IndexError:
+                    pass
 
     def run(self):
-        self.theme.play(loops=-1)
+        # self.theme.play(loops=-1)
         self.draw_map()
         self.display.refresh()
         while True:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/undermine/level.py	Sun Apr 27 14:49:31 2014 +0200
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import random
+import undermine.sprites
+
+
+class Level(object):
+    def __init__(self, size=(12, 12)):
+        self.size = size
+        self.dirt = [[True] * size[0] for y in range(size[1])]
+        self.things = [[None] * size[0] for y in range(size[1])]
+
+    def get(self, (x, y), default=None):
+        try:
+            return self[x, y]
+        except IndexError:
+            return default
+
+    def __getitem__(self, (x, y)):
+        return self.things[y][x]
+
+    def __setitem__(self, (x, y), thing):
+        self.things[y][x] = thing
+        if thing:
+            thing.pos = x, y
+
+    def get_dirt(self, (x, y), default=None):
+        try:
+            return self.dirt[y][x]
+        except IndexError:
+            return default
+
+    def get_support(self, (x, y)):
+        return self.get_dirt((x, y), True) or self[x, y] and self[x, y].support
+
+    def set_dirt(self, (x, y), dirt):
+        try:
+            self.dirt[y][x] = dirt
+        except IndexError:
+            pass
+
+    def shove(self, (x, y), (dx, dy)):
+        self[x + dx, y + dy] = self[x, y]
+        self[x, y] = None
+
+    def draw(self, surface, images):
+        for y, row in enumerate(self.dirt):
+            for x, dirt in enumerate(row):
+                image = images[12] if dirt else images[15]
+                surface.blit(image, (x * 64, y * 64))
+
+    def __iter__(self):
+        for row in self.things:
+            for thing in row:
+                if thing:
+                    yield thing
+
+    def generate(self, images):
+        squares = [(x, y)
+            for y, row in enumerate(self.dirt)
+            for x, dirt in enumerate(row)
+        ]
+        random.shuffle(squares)
+        self.hero = undermine.sprites.Hero(images)
+        self[squares.pop()] = self.hero
+        self.set_dirt(self.hero.pos, False)
+        for i in range(8):
+            self[squares.pop()] = undermine.sprites.Boulder(images[4])
+        for i in range(4):
+            self[squares.pop()] = undermine.sprites.Gem(images[8])
--- a/undermine/sprites.py	Sun Apr 27 00:43:19 2014 -0700
+++ b/undermine/sprites.py	Sun Apr 27 14:49:31 2014 +0200
@@ -4,20 +4,28 @@
 import pygame
 
 
-class Animated(pygame.sprite.Sprite):
+class Thing(pygame.sprite.Sprite):
+    block = False
+    pick = False
+    fall = False
+    support = False
+
     def __init__(self, image):
-        super(Animated, self).__init__()
+        super(Thing, self).__init__()
         self.image = image
         self.rect = image.get_rect()
         self.animations = []
-        self.pos = 0, 0
+        self._pos = None
+
+    @property
+    def pos(self):
+        return self._pos
 
-    def place(self, (x, y), move=True):
-        dx = x - self.pos[0]
-        dy = y - self.pos[1]
-        self.pos = x, y
-        if move:
-            self.rect.move_ip(dx * 64, dy * 64)
+    @pos.setter
+    def pos(self, (x, y)):
+        if self._pos is None:
+            self.rect.move_ip(x * 64, y * 64)
+        self._pos = x, y
 
     def update(self):
         animations = []
@@ -32,7 +40,10 @@
 
 
 
-class Hero(Animated):
+class Hero(Thing):
+    block = True
+    support = True
+
     def __init__(self, images):
         super(Hero, self).__init__(images[0])
         self.images = images
@@ -65,7 +76,11 @@
         self.kill()
 
 
-class Boulder(Animated):
+class Boulder(Thing):
+    block = True
+    fall = True
+    support = True
+
     def fall_anim(self):
         for step in [1, 2, 4 , 8, 16, 32, 1]:
             self.rect.move_ip(0, step)
@@ -80,6 +95,11 @@
             yield None
 
 class Gem(Boulder):
+    block = False
+    pick = True
+    fall = True
+    support = True
+
     def pick_anim(self):
         for step in [27, 9, 3, 1]:
             self.rect.move_ip(0, -step)
@@ -90,7 +110,7 @@
         self.kill()
 
 
-class Dirt(Animated):
+class Dirt(Thing):
     def __init__(self, images):
         super(Dirt, self).__init__(images[8])
         self.images = images