changeset 30:4c1f8ab0d145

Make mhrok a package, not module
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sat, 10 Mar 2012 11:36:49 +0100
parents 1dbbcefea978
children a4e5d09fac09
files mhrok.png mhrok.py mhrok/__init__.py mhrok/mhrok.png run_mhrok.py
diffstat 5 files changed, 487 insertions(+), 481 deletions(-) [+]
line wrap: on
line diff
Binary file mhrok.png has changed
--- a/mhrok.py	Sat Mar 10 10:20:41 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,481 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-
-import pkgutil
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
-import random
-
-import pygame
-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__()
-        self.image = frames[20].copy()
-        self.image.set_alpha(128)
-        self.rect = self.image.get_rect()
-        self.owner = owner
-
-    def update(self):
-        self.rect.center = self.owner.rect.center
-
-class Vision(pygame.sprite.DirtySprite):
-    def __init__(self, owner, radius):
-        super(Vision, self).__init__()
-        self.owner = owner
-        self.blendmode = pg.BLEND_RGBA_MIN
-        self.dirty = 2
-        self.images = {}
-        for r in xrange(40, 65):
-            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
-        self.image = self.images[radius]
-        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))
-        for r in xrange(radius, radius-25, -1):
-            pygame.draw.ellipse(
-                    image,
-                    (255, 255, 255, max(0, min(255, (r-radius+25) * 10))),
-                    (radius*5/4-r*5/4, radius-r, 2*r*5/4, 2*r), 0)
-        return image
-
-    def update(self):
-        if self.current_radius > self.radius:
-            self.set_radius(max(self.current_radius - 4, self.radius))
-        elif self.current_radius < self.radius:
-            self.set_radius(min(self.current_radius + 4, self.radius))
-        self.rect.center = self.owner.rect.center
-
-
-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()
-        self.frame = 0
-        self.rect.move_ip(4 + 20 * x, 12 * y)
-        self.depth = self.rect.bottom
-        self.direction = None
-
-    def update(self):
-        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
-
-    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):
-        direction = random.choice([None, 0, 1, 2, 3])
-        if direction is not None:
-            x, y = self.pos
-            target = x + DX[direction], y + DY[direction]
-            for mob in level.mobs:
-                if mob.pos == target:
-                    self.sprite.direction = None
-                    return
-            if not level[target]:
-                direction = None
-            else:
-                self.pos = target
-        self.sprite.direction = direction
-
-
-class Bat(Mob):
-    animation = [25, 26]
-
-
-class Chest(Mob):
-    animation = [27]
-
-    def control(self, level):
-        pass
-
-
-class Skeleton(Mob):
-    animation = [35, 35, 35, 36, 36, 36, 35, 35, 35, 37, 37, 37]
-
-
-class Teddybear(Mob):
-    animation = [30, 30, 30, 31, 31, 31, 30, 30, 30, 32, 32, 32]
-
-
-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_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
-        target = x + DX[direction], y + DY[direction]
-        if not level[target]:
-            return False
-        for mob in level.mobs:
-            if mob.pos == target:
-                return False
-        self.sprite.set_animation(self.sprite.walk_animation(direction))
-        self.pos = target
-        return True
-
-    def control(self, level):
-        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
-        keys = pygame.key.get_pressed()
-
-        if keys[pg.K_UP]:
-            return self.walk(level, 1)
-        elif keys[pg.K_LEFT]:
-            return self.walk(level, 0)
-        elif keys[pg.K_DOWN]:
-            return self.walk(level, 3)
-        elif keys[pg.K_RIGHT]:
-            return self.walk(level, 2)
-        else:
-            self.sprite.set_animation(
-                    self.sprite.stand_animation(self.direction))
-            return False
-        return True
-
-
-class SortedUpdates(pygame.sprite.RenderUpdates):
-    """Sprite group with a depth for controlling drawing order."""
-
-    def sprites(self):
-        return sorted(self.spritedict.keys(),
-                      key=lambda sprite: getattr(sprite, "depth", 0))
-
-
-class Level(object):
-    def __init__(self, pc):
-        self.pc = pc
-        self.mobs = [pc]
-        self.width = 31
-        self.height = 38
-
-        self.lit_rooms = []
-        self.map = [[0] * self.width for y in xrange(self.height)]
-
-        areas = [
-            ((0, 0), (9, 11)), ((11, 0), (9, 11)), ((22, 0), (9, 11)),
-            ((0, 13), (9, 11)), ((11, 13), (9, 11)), ((22, 13), (9, 11)),
-            ((0, 26), (9, 11)), ((11, 26), (9, 11)), ((22, 26), (9, 11)),
-        ]
-
-        rooms = []
-        r = random.randint
-        for (x, y), (w, h) in areas:
-            width = r(1, w/2) * 2
-            height = r(1, h/2) * 2
-            x_offset = r(0, (w - width)/2) * 2
-            y_offset = r(0, (h - height)/2) * 2
-            rooms.append(((x + x_offset, y + y_offset), (width, height)))
-
-        random.shuffle(rooms)
-
-        for room, next_room in zip(rooms, rooms[1:]):
-            (x, y), (w, h) = room
-            (nx, ny), (nw, nh) = next_room
-            x1 = nx + r(0, nw/2) * 2
-            y1 = ny + r(0, nh/2) * 2
-            x2 = x + r(0, w/2) * 2
-            y2 = y + r(0, h/2) * 2
-            self.path((x1, y1), (x2, y2), 2)
-
-        for room in rooms:
-            (x, y), (w, h) = room
-            self.fill((x, y), (w + 1, h + 1), 1)
-            for i in xrange(r(0, 2)):
-                self[r(x, x+w), r(y, y+h)] = 3
-            for i in xrange(r(1, 3)):
-                self[r(x, x+w), r(y, y+h)] = 4
-            for i in xrange(r(1, 4)):
-                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)
-        self.pc.place((r(x, x+w), r(y, y+h)))
-        self.rooms = rooms
-
-
-    def __getitem__(self, (x, y)):
-        if self.width > x >= 0 and self.height > y >= 0:
-            return self.map[y][x]
-
-    def __setitem__(self, (x, y), value):
-        if self.width > x >= 0 and self.height > y >= 0:
-            self.map[y][x] = value
-
-    def fill(self, (sx, sy), (width, height), fill):
-        for y in xrange(sy, sy + height):
-            for x in xrange(sx, sx + width):
-                self[x, y] = fill
-
-    def path(self, (sx, sy), (ex, ey), fill):
-        for x in xrange(sx, ex, 1 if sx < ex else -1):
-            if self[x, sy]:
-                return
-            self[x, sy] = fill
-        for y in xrange(sy, ey, 1 if sy < ey else -1):
-            if self[ex, y]:
-                return
-            self[ex, y] = fill
-
-    def find_room(self, x, y):
-        for room in self.rooms:
-            (rx, ry), (rw, rh) = room
-            if rx <= x <= rx + rw and ry <= y <= ry + rh:
-                return room
-
-    def render_background(self, tiles):
-        background = pygame.Surface((self.width * 20 + 20, self.height * 12 + 20))
-        background.fill((16, 8, 0))
-        blood = tiles[21].copy()
-        blood.set_alpha(220)
-        blood2 = tiles[22].copy()
-        blood2.set_alpha(220)
-        for y, row in enumerate(self.map):
-            for x, square in enumerate(row):
-                pos = x * 20 + 4, y * 12
-                border = 0
-                if square == 1:
-                    background.blit(tiles[23], pos)
-                    border = 4
-                elif square == 2:
-                    background.blit(tiles[33], pos)
-                    border = 24
-                elif square == 3:
-                    background.blit(tiles[23], pos)
-                    background.blit(blood, pos)
-                    border = 4
-                elif square == 4:
-                    background.blit(tiles[23], pos)
-                    background.blit(blood2, pos)
-                    border = 4
-                elif square == 5:
-                    background.blit(tiles[38], pos)
-                    border = 4
-                elif square < 0:
-                    background.blit(tiles[39 - square], pos)
-                    border = 24
-                if border:
-                    if not self[x - 1, y]:
-                        background.blit(tiles[5 + border], pos)
-                    if not self[x + 1, y]:
-                        background.blit(tiles[15 + border], pos)
-                    if not self[x, y - 1]:
-                        background.blit(tiles[border], pos)
-                    if not self[x, y + 1]:
-                        background.blit(tiles[10 + border], pos)
-
-        return background
-
-    def render_sprites(self, tiles):
-        sprites = SortedUpdates()
-        shadows = pygame.sprite.RenderUpdates()
-        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):
-    def __init__(self):
-        pygame.display.init()
-        self.screen = pygame.display.set_mode((640, 480))
-        pygame.display.set_caption('Mhrok')
-        self.first_draw = True
-        self.tiles = load_tiles('mhrok.png', 32, 32)
-        self.background = pygame.Surface(self.screen.get_size())
-        self.fog = pygame.Surface(self.screen.get_size(), pg.SRCALPHA)
-        self.fog.fill((0, 0, 0, 255))
-        self.memory = pygame.Surface(self.screen.get_size(), pg.SRCALPHA)
-        self.memory.fill((0, 0, 0, 255))
-        self.dirty = []
-
-    def render_level(self, level):
-        (self.sprites,
-         self.shadows, self.visions) = level.render_sprites(self.tiles)
-        self.background.blit(level.render_background(self.tiles), (0, 0))
-
-    def light_room(self, room):
-        (rx, ry), (rw, rh) = room
-        mx = rx * 20 + 6
-        my = ry * 12 + 12
-        mw = (rw+1) * 20 + 12
-        mh = (rh+1) * 12 + 12
-        darkroom = self.background.subsurface((mx, my, mw, mh))
-        darkroom.set_alpha(16)
-        rect = self.memory.blit(darkroom, (mx, my))
-        self.fog.blit(self.memory, (mx, my), rect)
-        self.dirty.append(rect)
-
-    def draw(self):
-        if self.first_draw:
-            self.screen.blit(self.background, (0, 0))
-            self.shadows.draw(self.screen)
-            self.sprites.draw(self.screen)
-            self.visions.draw(self.fog)
-            self.screen.blit(self.fog, (0, 0))
-            self.dirty = [self.background.get_rect()]
-            self.first_draw = False
-        else:
-            self.screen.blit(self.background, (0, 0))
-            self.visions.clear(self.fog, self.memory)
-            self.sprites.clear(self.screen, self.background)
-            self.shadows.draw(self.screen)
-            self.sprites.draw(self.screen)
-            self.dirty.extend(self.visions.draw(self.fog))
-            self.screen.blit(self.fog, (0, 0))
-        pygame.display.update(self.dirty)
-        self.dirty = []
-
-    def update(self):
-        self.sprites.update()
-        self.shadows.update()
-        self.visions.update()
-        for vision in self.visions:
-            while vision.rooms:
-                room = vision.rooms.pop()
-                self.light_room(room)
-
-
-class Game(object):
-    def __init__(self):
-        self.display = Display()
-        self.clock = pygame.time.Clock()
-        self.game_over = False
-
-        self.pc = Player()
-        self.level = Level(self.pc)
-        self.display.render_level(self.level)
-
-    def main(self):
-        while not self.game_over:
-            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:
-                if self.pc.control(self.level):
-                    for mob in self.level.mobs:
-                        if mob == self.pc:
-                            continue
-                        mob.control(self.level)
-                else:
-                    for mob in self.level.mobs:
-                        if mob == self.pc:
-                            continue
-                        mob.sprite.direction = None
-
-
-def load_tiles(filename, width, height):
-    f = StringIO.StringIO(pkgutil.get_data('mhrok', filename))
-    image = pygame.image.load(f, filename).convert()
-    image_width, image_height = image.get_size()
-    tiles = []
-    for x in range(image_width/width):
-        for y in range(image_height/height):
-            rect = (x * width, y * height, width, height)
-            tiles.append(image.subsurface(rect))
-    return tiles
-
-if __name__ == '__main__':
-    Game().main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mhrok/__init__.py	Sat Mar 10 11:36:49 2012 +0100
@@ -0,0 +1,481 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
+import pkgutil
+try:
+    import cStringIO as StringIO
+except ImportError:
+    import StringIO
+import random
+
+import pygame
+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__()
+        self.image = frames[20].copy()
+        self.image.set_alpha(128)
+        self.rect = self.image.get_rect()
+        self.owner = owner
+
+    def update(self):
+        self.rect.center = self.owner.rect.center
+
+class Vision(pygame.sprite.DirtySprite):
+    def __init__(self, owner, radius):
+        super(Vision, self).__init__()
+        self.owner = owner
+        self.blendmode = pg.BLEND_RGBA_MIN
+        self.dirty = 2
+        self.images = {}
+        for r in xrange(40, 65):
+            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
+        self.image = self.images[radius]
+        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))
+        for r in xrange(radius, radius-25, -1):
+            pygame.draw.ellipse(
+                    image,
+                    (255, 255, 255, max(0, min(255, (r-radius+25) * 10))),
+                    (radius*5/4-r*5/4, radius-r, 2*r*5/4, 2*r), 0)
+        return image
+
+    def update(self):
+        if self.current_radius > self.radius:
+            self.set_radius(max(self.current_radius - 4, self.radius))
+        elif self.current_radius < self.radius:
+            self.set_radius(min(self.current_radius + 4, self.radius))
+        self.rect.center = self.owner.rect.center
+
+
+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()
+        self.frame = 0
+        self.rect.move_ip(4 + 20 * x, 12 * y)
+        self.depth = self.rect.bottom
+        self.direction = None
+
+    def update(self):
+        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
+
+    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):
+        direction = random.choice([None, 0, 1, 2, 3])
+        if direction is not None:
+            x, y = self.pos
+            target = x + DX[direction], y + DY[direction]
+            for mob in level.mobs:
+                if mob.pos == target:
+                    self.sprite.direction = None
+                    return
+            if not level[target]:
+                direction = None
+            else:
+                self.pos = target
+        self.sprite.direction = direction
+
+
+class Bat(Mob):
+    animation = [25, 26]
+
+
+class Chest(Mob):
+    animation = [27]
+
+    def control(self, level):
+        pass
+
+
+class Skeleton(Mob):
+    animation = [35, 35, 35, 36, 36, 36, 35, 35, 35, 37, 37, 37]
+
+
+class Teddybear(Mob):
+    animation = [30, 30, 30, 31, 31, 31, 30, 30, 30, 32, 32, 32]
+
+
+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_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
+        target = x + DX[direction], y + DY[direction]
+        if not level[target]:
+            return False
+        for mob in level.mobs:
+            if mob.pos == target:
+                return False
+        self.sprite.set_animation(self.sprite.walk_animation(direction))
+        self.pos = target
+        return True
+
+    def control(self, level):
+        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
+        keys = pygame.key.get_pressed()
+
+        if keys[pg.K_UP]:
+            return self.walk(level, 1)
+        elif keys[pg.K_LEFT]:
+            return self.walk(level, 0)
+        elif keys[pg.K_DOWN]:
+            return self.walk(level, 3)
+        elif keys[pg.K_RIGHT]:
+            return self.walk(level, 2)
+        else:
+            self.sprite.set_animation(
+                    self.sprite.stand_animation(self.direction))
+            return False
+        return True
+
+
+class SortedUpdates(pygame.sprite.RenderUpdates):
+    """Sprite group with a depth for controlling drawing order."""
+
+    def sprites(self):
+        return sorted(self.spritedict.keys(),
+                      key=lambda sprite: getattr(sprite, "depth", 0))
+
+
+class Level(object):
+    def __init__(self, pc):
+        self.pc = pc
+        self.mobs = [pc]
+        self.width = 31
+        self.height = 38
+
+        self.lit_rooms = []
+        self.map = [[0] * self.width for y in xrange(self.height)]
+
+        areas = [
+            ((0, 0), (9, 11)), ((11, 0), (9, 11)), ((22, 0), (9, 11)),
+            ((0, 13), (9, 11)), ((11, 13), (9, 11)), ((22, 13), (9, 11)),
+            ((0, 26), (9, 11)), ((11, 26), (9, 11)), ((22, 26), (9, 11)),
+        ]
+
+        rooms = []
+        r = random.randint
+        for (x, y), (w, h) in areas:
+            width = r(1, w/2) * 2
+            height = r(1, h/2) * 2
+            x_offset = r(0, (w - width)/2) * 2
+            y_offset = r(0, (h - height)/2) * 2
+            rooms.append(((x + x_offset, y + y_offset), (width, height)))
+
+        random.shuffle(rooms)
+
+        for room, next_room in zip(rooms, rooms[1:]):
+            (x, y), (w, h) = room
+            (nx, ny), (nw, nh) = next_room
+            x1 = nx + r(0, nw/2) * 2
+            y1 = ny + r(0, nh/2) * 2
+            x2 = x + r(0, w/2) * 2
+            y2 = y + r(0, h/2) * 2
+            self.path((x1, y1), (x2, y2), 2)
+
+        for room in rooms:
+            (x, y), (w, h) = room
+            self.fill((x, y), (w + 1, h + 1), 1)
+            for i in xrange(r(0, 2)):
+                self[r(x, x+w), r(y, y+h)] = 3
+            for i in xrange(r(1, 3)):
+                self[r(x, x+w), r(y, y+h)] = 4
+            for i in xrange(r(1, 4)):
+                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)
+        self.pc.place((r(x, x+w), r(y, y+h)))
+        self.rooms = rooms
+
+
+    def __getitem__(self, (x, y)):
+        if self.width > x >= 0 and self.height > y >= 0:
+            return self.map[y][x]
+
+    def __setitem__(self, (x, y), value):
+        if self.width > x >= 0 and self.height > y >= 0:
+            self.map[y][x] = value
+
+    def fill(self, (sx, sy), (width, height), fill):
+        for y in xrange(sy, sy + height):
+            for x in xrange(sx, sx + width):
+                self[x, y] = fill
+
+    def path(self, (sx, sy), (ex, ey), fill):
+        for x in xrange(sx, ex, 1 if sx < ex else -1):
+            if self[x, sy]:
+                return
+            self[x, sy] = fill
+        for y in xrange(sy, ey, 1 if sy < ey else -1):
+            if self[ex, y]:
+                return
+            self[ex, y] = fill
+
+    def find_room(self, x, y):
+        for room in self.rooms:
+            (rx, ry), (rw, rh) = room
+            if rx <= x <= rx + rw and ry <= y <= ry + rh:
+                return room
+
+    def render_background(self, tiles):
+        background = pygame.Surface((self.width * 20 + 20, self.height * 12 + 20))
+        background.fill((16, 8, 0))
+        blood = tiles[21].copy()
+        blood.set_alpha(220)
+        blood2 = tiles[22].copy()
+        blood2.set_alpha(220)
+        for y, row in enumerate(self.map):
+            for x, square in enumerate(row):
+                pos = x * 20 + 4, y * 12
+                border = 0
+                if square == 1:
+                    background.blit(tiles[23], pos)
+                    border = 4
+                elif square == 2:
+                    background.blit(tiles[33], pos)
+                    border = 24
+                elif square == 3:
+                    background.blit(tiles[23], pos)
+                    background.blit(blood, pos)
+                    border = 4
+                elif square == 4:
+                    background.blit(tiles[23], pos)
+                    background.blit(blood2, pos)
+                    border = 4
+                elif square == 5:
+                    background.blit(tiles[38], pos)
+                    border = 4
+                elif square < 0:
+                    background.blit(tiles[39 - square], pos)
+                    border = 24
+                if border:
+                    if not self[x - 1, y]:
+                        background.blit(tiles[5 + border], pos)
+                    if not self[x + 1, y]:
+                        background.blit(tiles[15 + border], pos)
+                    if not self[x, y - 1]:
+                        background.blit(tiles[border], pos)
+                    if not self[x, y + 1]:
+                        background.blit(tiles[10 + border], pos)
+
+        return background
+
+    def render_sprites(self, tiles):
+        sprites = SortedUpdates()
+        shadows = pygame.sprite.RenderUpdates()
+        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):
+    def __init__(self):
+        pygame.display.init()
+        self.screen = pygame.display.set_mode((640, 480))
+        pygame.display.set_caption('Mhrok')
+        self.first_draw = True
+        self.tiles = load_tiles('mhrok.png', 32, 32)
+        self.background = pygame.Surface(self.screen.get_size())
+        self.fog = pygame.Surface(self.screen.get_size(), pg.SRCALPHA)
+        self.fog.fill((0, 0, 0, 255))
+        self.memory = pygame.Surface(self.screen.get_size(), pg.SRCALPHA)
+        self.memory.fill((0, 0, 0, 255))
+        self.dirty = []
+
+    def render_level(self, level):
+        (self.sprites,
+         self.shadows, self.visions) = level.render_sprites(self.tiles)
+        self.background.blit(level.render_background(self.tiles), (0, 0))
+
+    def light_room(self, room):
+        (rx, ry), (rw, rh) = room
+        mx = rx * 20 + 6
+        my = ry * 12 + 12
+        mw = (rw+1) * 20 + 12
+        mh = (rh+1) * 12 + 12
+        darkroom = self.background.subsurface((mx, my, mw, mh))
+        darkroom.set_alpha(32)
+        rect = self.memory.blit(darkroom, (mx, my))
+        self.fog.blit(self.memory, (mx, my), rect)
+        self.dirty.append(rect)
+
+    def draw(self):
+        if self.first_draw:
+            self.screen.blit(self.background, (0, 0))
+            self.shadows.draw(self.screen)
+            self.sprites.draw(self.screen)
+            self.visions.draw(self.fog)
+            self.screen.blit(self.fog, (0, 0))
+            self.dirty = [self.background.get_rect()]
+            self.first_draw = False
+        else:
+            self.screen.blit(self.background, (0, 0))
+            self.visions.clear(self.fog, self.memory)
+            self.sprites.clear(self.screen, self.background)
+            self.shadows.draw(self.screen)
+            self.sprites.draw(self.screen)
+            self.dirty.extend(self.visions.draw(self.fog))
+            self.screen.blit(self.fog, (0, 0))
+        pygame.display.update(self.dirty)
+        self.dirty = []
+
+    def update(self):
+        self.sprites.update()
+        self.shadows.update()
+        self.visions.update()
+        for vision in self.visions:
+            while vision.rooms:
+                room = vision.rooms.pop()
+                self.light_room(room)
+
+
+class Game(object):
+    def __init__(self):
+        self.display = Display()
+        self.clock = pygame.time.Clock()
+        self.game_over = False
+
+        self.pc = Player()
+        self.level = Level(self.pc)
+        self.display.render_level(self.level)
+
+    def main(self):
+        while not self.game_over:
+            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:
+                if self.pc.control(self.level):
+                    for mob in self.level.mobs:
+                        if mob == self.pc:
+                            continue
+                        mob.control(self.level)
+                else:
+                    for mob in self.level.mobs:
+                        if mob == self.pc:
+                            continue
+                        mob.sprite.direction = None
+
+
+def load_tiles(filename, width, height):
+    f = StringIO.StringIO(pkgutil.get_data('mhrok', filename))
+    image = pygame.image.load(f, filename).convert()
+    image_width, image_height = image.get_size()
+    tiles = []
+    for x in range(image_width/width):
+        for y in range(image_height/height):
+            rect = (x * width, y * height, width, height)
+            tiles.append(image.subsurface(rect))
+    return tiles
+
+if __name__ == '__main__':
+    Game().main()
Binary file mhrok/mhrok.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/run_mhrok.py	Sat Mar 10 11:36:49 2012 +0100
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from mhrok import Game
+
+Game().main()