view skaapsteker/level.py @ 612:a91b2e4400a5

Fallback to using simplejson if json does not exist (this appears to be all that is needed to add Python2.5 compatibility).
author Simon Cross <hodgestar@gmail.com>
date Mon, 11 Apr 2011 23:54:56 +0200
parents 4de4f94c326d
children 851c8726696c
line wrap: on
line source

try:
    import json
except ImportError:
    import simplejson as json

from pygame import Rect, Surface, Color, mixer
from pygame.sprite import LayeredUpdates

from . import data
from .constants import SCREEN, Layers
from .sprites.base import TILE_SIZE, find_sprite, Geography
from .widgets.text import Text



class TileSet(object):
    def __init__(self, dirname):
        self.dirname = dirname
        self.tile_factories = {
            '.': lambda pos: None,
            '?': lambda pos: None, # Object placement helper
            '-': self.tile_factory('floor.png', True),
            'X': self.tile_factory('wall.png', True, True),
            '<': self.tile_factory('floor-end-l.png', True),
            '>': self.tile_factory('floor-end-r.png', True),
            '=': self.tile_factory('floor-start-l.png', True),
            '+': self.tile_factory('floor-start-r.png', True),
            '(': self.tile_factory('wall-end-l.png', True, True),
            ')': self.tile_factory('wall-end-r.png', True, True),
            'q': self.tile_factory('wall-end-tr.png', True, True),
            'd': self.tile_factory('wall-end-br.png', True, True),
            'p': self.tile_factory('wall-end-tl.png', True, True),
            'b': self.tile_factory('wall-end-bl.png', True, True),
            '{': self.tile_factory('arch-l.png'),
            '}': self.tile_factory('arch-r.png'),
            '|': self.tile_factory('bamboo.png', layer=Layers.BACKGROUND, dirname='common'),
            'h': self.tile_factory('hanging-lantern.png', layer=Layers.IN_FRONT, dirname='common'),
            's': self.tile_factory('standing-lantern.png', layer=Layers.IN_FRONT, dirname='common'),
            '!': self.tile_factory('dangly-bits.png', layer=Layers.IN_FRONT, dirname='common'),
            'k': self.tile_factory('skaapsteker.png', layer=Layers.SKAAPSTEKER, dirname='skaapsteker'),
            }


    def get_tile(self, tilechar, pos):
        return self.tile_factories[tilechar](pos)


    def tile_factory(self, image_name, floor=False, block=False, layer=Layers.BACKGROUND, dirname=None):
        if not dirname:
            dirname = self.dirname
        image = data.load_image('tiles/' + dirname + '/' + image_name)
        def _tilefac(pos):
            tile = Geography(pos, image)
            tile.block = block
            tile.floor = floor
            tile._layer = layer
            if not (tile.block or tile.floor):
                tile.collides_with = set()
            return tile
        return _tilefac



class Level(object):
    def __init__(self, leveldef, soundsystem):
        self.name = leveldef
        self.level_data = json.loads(data.load('levels/' + leveldef + '.json').read())
        self.sprites = LayeredUpdates()
        self._soundsystem = soundsystem
        self.build_tiles()
        self.build_backgrounds()
        self.setup_enemies()
        self.setup_doorways()
        self._background_music = None
        if 'music' in self.level_data:
            # soundsystem will call data.filepath
            self._background_music = 'music/' + self.level_data['music'].get('track', '')
            self._background_volume = self.level_data['music'].get('volume', 1.0)

    def build_backgrounds(self):
        self.backgrounds = []
        for i, background in enumerate(self.level_data['backgrounds']):
            image = data.load_image('backgrounds/' + background)
            clip_rect = Rect((0, 0), self.pixel_size)
            if i == 0:
                clip_rect = Rect((0, 0), (int(self.pixel_size[0] * 0.75), int(self.pixel_size[1] * 0.75)))
            clip_rect = clip_rect.clip(image.get_rect())
            clip_rect = clip_rect.union(Rect((0, 0), SCREEN))
            clip_rect.midbottom = image.get_rect().midbottom
            image = image.subsurface(clip_rect)
            self.backgrounds.append(image)

    def enter(self):
        if self._background_music:
            self._soundsystem.play_background_music(self._background_music, self._background_volume)

    def leave(self):
        pass

    def build_tiles(self):
        self.tileset = TileSet(self.level_data['tileset'])
        self.tiles = LayeredUpdates()
        tile_data = self.level_data['tiles']
        self.tile_size = (len(tile_data[0]), len(tile_data))
        self.pixel_size = (self.tile_size[0] * TILE_SIZE[0], self.tile_size[1] * TILE_SIZE[1])
        for y, row in enumerate(tile_data):
            for x, char in enumerate(row):
                tile = self.tileset.get_tile(char, (x, y))
                if tile:
                    self.tiles.add(tile)
                    self.sprites.add(tile)


    def setup_enemies(self):
        self.enemies = LayeredUpdates()
        for enemy_def in self.level_data['enemies']:
            enemy = find_sprite(enemy_def, 'enemies')
            self.enemies.add(enemy)
            self.sprites.add(enemy)


    def setup_doorways(self):
        self.doorways = {}
        for door_name, door_def in self.level_data['doorways'].items():
            doorway = find_sprite(door_def, 'base')
            self.doorways[door_name] = doorway
            self.sprites.add(doorway)


    def get_surface(self):
        return Surface(self.pixel_size)


    def get_background_pos(self, background, viewport):
        vp_x, vp_y = viewport.size
        bg_scalable = background.get_rect().inflate(-vp_x, -vp_y).size
        lev_scalable = Rect((0, 0), self.pixel_size).inflate(-vp_x, -vp_y).size
        return ((viewport.left * bg_scalable[0]) / lev_scalable[0],
                (viewport.top * bg_scalable[1]) / lev_scalable[1])


    def draw_background(self, surface):
        clip_rect = surface.get_clip()
        for background in self.backgrounds:
            bg_pos = self.get_background_pos(background, clip_rect)
            surface.blit(background, clip_rect.topleft, Rect(bg_pos, clip_rect.size))


    def draw_tiles(self, surface):
        self.tiles.draw(surface)


    def draw(self, surface):
        self.draw_background(surface)



class DebugLevel(Level):
    """Used by level-editor"""
    def build_tiles(self):
        super(DebugLevel, self).build_tiles()
        tile_data = self.level_data['tiles']
        for y, row in enumerate(tile_data):
            for x, char in enumerate(row):
                if char == '?':
                    self.check_debug_tile(x, y)


    def check_debug_tile(self, x, y):
        objects = []
        objects += self.level_data.get('enemies', [])
        objects += self.level_data.get('sprites', [])
        doorways = self.level_data.get('doorways', {})
        objects += doorways.values()

        for object_ in objects:
            if [x, y] == object_['pos']:
                break
        else:
            tile = self.get_debug_tile(x, y)
            print "Debug tile at (%i, %i)" % (x, y)
            self.tiles.add(tile)
            self.sprites.add(tile)


    def get_debug_tile(self, x, y):
        surface = Surface((64, 64))
        surface.fill(Color('black'))
        text = Text("? Tile at\n(%i, %i)" % (x, y), (2, 2), size=14, color='white')
        text.draw(surface)
        return Geography((x, y), surface.convert_alpha())