Mercurial > pygame-tutorial
changeset 5:03fe34478f69
Using tiled for maps
line wrap: on
line diff
--- a/000/005.py Sun Jun 17 00:18:38 2012 +0200 +++ b/000/005.py Sun Jun 17 16:34:43 2012 +0200 @@ -26,13 +26,11 @@ def wait_for_quit(): """Sit there doing nothing until the window is closed.""" - clock = pygame.time.Clock() pygame.display.update() while True: event = pygame.event.wait() if event.type == pg.QUIT: return - clock.tick(60) if __name__ == '__main__':
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/000.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import pygame +import pygame.locals as pg + + +def load_sheet(filename, width=32, height=None): + if height is None: + height = width + image = pygame.image.load(filename) + image = image.convert() + image.set_colorkey((0xff, 0, 0xff)) + w, h = image.get_size() + images = [] + for x in xrange(w // width): + for y in xrange(h // height): + rect = pygame.rect.Rect(x * width, y * height, width, height) + images.append(image.subsurface(rect)) + return images + + +class Game(object): + def __init__(self): + pygame.display.init() + pygame.font.init() + self.screen = pygame.display.set_mode((800, 600)) + self.screen.fill((0x66, 0x66, 0x66)) + self.tilesheet = load_sheet('tilesheet.png') + self.font = pygame.font.Font(None, 16) + + def display(self): + for i, tile in enumerate(self.tilesheet): + x, y = divmod(i, 600 // 33) + self.screen.blit(tile, (x * 33 + 1, y * 33 + 1)) + label = self.font.render('%d' % i, False, (0xff, 0xff, 0xff)) + back = self.font.render('%d' % i, False, (0x00, 0x00, 0x00)) + self.screen.blit(back, (x * 33 + 1 - 1, y * 33 + 1)) + self.screen.blit(back, (x * 33 + 1 + 1, y * 33 + 1)) + self.screen.blit(back, (x * 33 + 1, y * 33 + 1 - 1)) + self.screen.blit(back, (x * 33 + 1, y * 33 + 1 + 1)) + self.screen.blit(label, (x * 33 + 1, y * 33 + 1)) + pygame.display.update() + + def control(self): + event = pygame.event.wait() + if event.type == pg.QUIT: + self.game_over = True + + def run(self): + self.game_over = False + while not self.game_over: + self.display() + self.control() + +if __name__ == '__main__': + game = Game() + game.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/001.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import pygame +import pygame.locals as pg + +LAYERS = [ + [ + 'XXXXXXXXXXXXXXX', + 'X.............X', + 'X.............X', + 'X...XXXXXXX...X', + 'X...XXXXXXX...X', + 'X...XXXXXXX...X', + 'X.............X', + 'X.............X', + 'XXXXXXXXXXXXXXX', + ], +] + +TILES = { + '.': 27, + 'X': 11, +} + + +def load_sheet(filename, width=32, height=None): + if height is None: + height = width + image = pygame.image.load(filename) + image = image.convert() + image.set_colorkey((0xff, 0, 0xff)) + w, h = image.get_size() + images = [] + for x in xrange(w // width): + for y in xrange(h // height): + rect = pygame.rect.Rect(x * width, y * height, width, height) + images.append(image.subsurface(rect)) + return images + + +class Level(object): + def __init__(self, layers, tiles): + self.layers = [] + for layer in layers: + self.layers.append([[tiles[c] for c in row] for row in layer]) + + def render(self, screen, tilesheet): + for layer in self.layers: + for y, row in enumerate(layer): + for x, tile in enumerate(row): + screen.blit(tilesheet[tile], (x * 32, y * 32)) + + +class Game(object): + def __init__(self): + pygame.display.init() + pygame.font.init() + self.screen = pygame.display.set_mode((800, 600)) + self.screen.fill((0x66, 0x66, 0x66)) + self.tilesheet = load_sheet('tilesheet.png') + self.font = pygame.font.Font(None, 16) + + def load_level(self, layers, tiles): + self.level = Level(layers, tiles) + + def display(self): + self.level.render(self.screen, self.tilesheet) + pygame.display.update() + + def control(self): + event = pygame.event.wait() + if event.type == pg.QUIT: + self.game_over = True + + def run(self): + self.game_over = False + while not self.game_over: + self.display() + self.control() + + +if __name__ == '__main__': + game = Game() + game.load_level(LAYERS, TILES) + game.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/002.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import pygame +import pygame.locals as pg + +LAYERS = [ + [ + 'XxXxXXxxXxXxXxx', + 'x.............X', + 'x.............x', + 'x...XxXxXxX...x', + 'X...XxxXXxX...X', + 'X...XxXXxXx...x', + 'x.............X', + 'x.............X', + 'XXxxxXXxXxXXxXx', + ], + [ + ' ', + ' FMMMMMMMMMMMT ', + ' E dWWWWWWWb K ', + ' E K E K ', + ' E K E K ', + ' E K E K ', + ' E YMMMMMMMP K ', + ' LWWWWWWWWWWWJ ', + ' ', + ], +] + +TILES = { + ' ': -1, '.': 27, 'X': 11, 'd': 2, 'K': 3, 'Y': 4, 'b': 14, 'E': 15, + 'P': 16, 'M': 10, 'W': 8, 'L': 7, 'F': 6, 'J': 13, 'T': 12, 'x': 9, +} + + +def load_sheet(filename, width=32, height=None): + if height is None: + height = width + image = pygame.image.load(filename) + image = image.convert() + image.set_colorkey((0xff, 0, 0xff)) + w, h = image.get_size() + images = [] + for x in xrange(w // width): + for y in xrange(h // height): + rect = pygame.rect.Rect(x * width, y * height, width, height) + images.append(image.subsurface(rect)) + return images + + +class Level(object): + def __init__(self, layers, tiles): + self.layers = [] + for layer in layers: + self.layers.append([[tiles[c] for c in row] for row in layer]) + + def render(self, screen, tilesheet): + for layer in self.layers: + for y, row in enumerate(layer): + for x, tile in enumerate(row): + if tile >= 0: + screen.blit(tilesheet[tile], (x * 32, y * 32)) + + +class Game(object): + def __init__(self): + pygame.display.init() + pygame.font.init() + self.screen = pygame.display.set_mode((800, 600)) + self.screen.fill((0x66, 0x66, 0x66)) + self.tilesheet = load_sheet('tilesheet.png') + self.font = pygame.font.Font(None, 16) + + def load_level(self, layers, tiles): + self.level = Level(layers, tiles) + + def display(self): + self.level.render(self.screen, self.tilesheet) + pygame.display.update() + + def control(self): + event = pygame.event.wait() + if event.type == pg.QUIT: + self.game_over = True + + def run(self): + self.game_over = False + while not self.game_over: + self.display() + self.control() + + +if __name__ == '__main__': + game = Game() + game.load_level(LAYERS, TILES) + game.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/003.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import pygame +import pygame.locals as pg +from pytmx import tmxloader + + +class Level(object): + def __init__(self, filename): + self.tiled_map = tmxloader.load_pygame(filename) + + def render(self, screen): + w = self.tiled_map.tilewidth + h = self.tiled_map.tileheight + get_image = self.tiled_map.getTileImage + for l, layer in enumerate(self.tiled_map.tilelayers): + for y in xrange(self.tiled_map.height): + for x in xrange(self.tiled_map.width): + image = get_image(x, y, l) + if image: + screen.blit(image, (x * w, y * h)) + + +class Game(object): + def __init__(self): + pygame.display.init() + pygame.font.init() + self.screen = pygame.display.set_mode((800, 600)) + self.screen.fill((0x66, 0x66, 0x66)) + + def load_level(self, filename): + self.level = Level(filename) + + def display(self): + self.level.render(self.screen) + pygame.display.update() + + def control(self): + event = pygame.event.wait() + if event.type == pg.QUIT: + self.game_over = True + + def run(self): + self.game_over = False + while not self.game_over: + self.display() + self.control() + + +if __name__ == '__main__': + game = Game() + game.load_level('level.tmx') + game.run()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/level.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="25" height="18" tilewidth="32" tileheight="32"> + <tileset firstgid="1" name="control" tilewidth="32" tileheight="32"> + <properties> + <property name="block" value="1"/> + </properties> + <image source="control.png" trans="ff00ff" width="64" height="32"/> + <tile id="0"> + <properties> + <property name="block" value="1"/> + </properties> + </tile> + </tileset> + <tileset firstgid="3" name="tilesheet" tilewidth="32" tileheight="32"> + <image source="tilesheet.png" trans="ff00ff" width="288" height="192"/> + </tileset> + <layer name="Control" width="25" height="18"> + <data encoding="base64" compression="zlib"> + eJxjZGBgYKQDpiUY7nYwUYBH7Ri1Y9SOoWEHNcFws4PWGABJRwF/ + </data> + </layer> + <layer name="Background" width="25" height="18"> + <data encoding="base64" compression="zlib"> + eJyTZ2BgkKcDVqIhHu52UDPcR6Id+MJ61A762zFc0hW17aBHWTJU7aA1BgDmfzlJ + </data> + </layer> + <layer name="Corners" width="25" height="18"> + <data encoding="base64" compression="zlib"> + eJy1VdtqwzAMFWQbFHaBwNq1DyFhD92lD7Zr//+v1WY65ERTRyFMIOzKro6OJCtJRJL8SNA1qi3KLNG5x8L/xd1E+7uqH/+g94Q7OnF1tM/mrNBanDt91XPVQeZcjHT/tep2he4Ia9A1EI9i7EJ7rk3642yk/UB+rN1KvNEGX4+Or2YveoYcIta2f6u6d/RAPsDnVh5BMXA+mVizxvRu4mFfmeJFPc4OP+RjonPwFcXge168ydiBLbJ8g5P63hAOYzw4voABns3ey9xXQZb8OVdFOWXFsPMAGB3xQJ3whry++qr67ehJzzE/PB4Wm+0v9H87n7jO3BuoLXLqYXTyu+acMwj7tdjwhXeAvDOPo6ybg5+y7FGuB3qlSSa+ntgZCWk+evrN7+NJ1s3Ba/qsWODC3xbuRc47vjtB5tlh5yMk0noBeX8qEg== + </data> + </layer> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx/__init__.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,5 @@ +from tmxloader import load_tmx, load_pygame +from pytmx import TiledElement, TiledMap, TiledTileset, TiledLayer, \ + TiledObjectGroup, TiledObject +from utils import buildDistributionRects +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx/constants.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,11 @@ +# internal flags +TRANS_FLIPX = 1 +TRANS_FLIPY = 2 +TRANS_ROT = 4 + + +# Tiled gid flags +GID_TRANS_FLIPX = 1<<31 +GID_TRANS_FLIPY = 1<<30 +GID_TRANS_ROT = 1<<29 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx/pytmx.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,572 @@ +from itertools import chain, product +from xml.etree import ElementTree +from collections import defaultdict +from utils import decode_gid, types, parse_properties, read_points +from constants import * + + + +""" +Class declarations and other important stuff +""" + + + +class TiledElement(object): + def set_properties(self, node): + """ + read the xml attributes and tiled "properties" from a xml node and fill + in the values into the object's dictionary. Names will be checked to + make sure that they do not conflict with reserved names. + """ + + # set the attributes reserved for tiled + [ setattr(self, k, types[str(k)](v)) for (k,v) in node.items() ] + + # set the attributes that are derived from tiled 'properties' + for k,v in parse_properties(node).items(): + if k in self.reserved: + msg = "{} \"{}\" has a property called \"{}\"" + print msg.format(self.__class__.__name__, self.name, k, self.__class__.__name__) + msg = "This name is reserved for {} objects and can cannot be used." + print msg.format(self.__class__.__name__) + print "Please change the name in Tiled and try again." + print v + raise ValueError + setattr(self, k, types[str(k)](v)) + + +class TiledMap(TiledElement): + """ + Contains the tile layers, tile images, object groups, and objects from a + Tiled TMX map. + """ + + reserved = "version orientation width height tilewidth tileheight properties tileset layer objectgroup".split() + + + def __init__(self, filename): + from collections import defaultdict + + TiledElement.__init__(self) + self.tilesets = [] # list of TiledTileset objects + self.tilelayers = [] # list of TiledLayer objects + self.objectgroups = [] # list of TiledObjectGroup objects + self.tile_properties = {} # dict of tiles that have metadata + self.filename = filename + + # only used tiles are actually loaded, so there will be a difference + # between the GID's in the Tile map data (tmx) and the data in this + # class and the layers. This dictionary keeps track of that difference. + self.gidmap = defaultdict(list) + + # should be filled in by a loader function + self.images = [] + + # defaults from the TMX specification + self.version = 0.0 + self.orientation = None + self.width = 0 + self.height = 0 + self.tilewidth = 0 + self.tileheight = 0 + + self.imagemap = {} # mapping of gid and trans flags to real gids + self.maxgid = 1 + + self.load() + + + def __repr__(self): + return "<{}: \"{}\">".format(self.__class__.__name__, self.filename) + + + def getTileImage(self, x, y, layer): + """ + return the tile image for this location + x and y must be integers and are in tile coordinates, not pixel + + return value will be 0 if there is no tile with that location. + """ + + try: + gid = self.tilelayers[int(layer)].data[int(y)][int(x)] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} is not valid." + raise Exception, msg.format(x, y, layer) + except TypeError: + msg = "Tiles must be specified in integers." + raise TypeError, msg + + else: + try: + return self.images[gid] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} has invalid GID: {3}" + raise Exception, msg.format(x, y, layer, gid) + + + def getTileGID(self, x, y, layer): + """ + return GID of a tile in this location + x and y must be integers and are in tile coordinates, not pixel + """ + + try: + return self.tilelayers[int(layer)].data[int(y)][int(x)] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} is invalid" + raise Exception, msg.format(x, y, layer) + + + def getDrawOrder(self): + """ + return a list of objects in the order that they should be drawn + this will also exclude any layers that are not set to visible + + may be useful if you have objects and want to control rendering + from tiled + """ + + raise NotImplementedError + + + def getTileImages(self, r, layer): + """ + return a group of tiles in an area + expects a pygame rect or rect-like list/tuple + + useful if you don't want to repeatedly call getTileImage + """ + + raise NotImplementedError + + + def getObjects(self): + """ + Return iterator of all the objects associated with this map + """ + + return chain(*(i for i in self.objectgroups)) + + + def getTileProperties(self, (x, y, layer)): + """ + return the properties for the tile, if any + x and y must be integers and are in tile coordinates, not pixel + + returns a dict of there are properties, otherwise will be None + """ + + try: + gid = self.tilelayers[int(layer)].data[int(y)][int(x)] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} is invalid." + raise Exception, msg.format(x, y, layer) + + else: + try: + return self.tile_properties[gid] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} has invaid GID: {3}" + raise Exception, msg.format(x, y, layer, gid) + except KeyError: + return None + + + def getLayerData(self, layer): + """ + Return the data for a layer. + + Data is an array of arrays. + + >>> pos = data[y][x] + """ + + try: + return self.tilelayers[layer].data + except IndexError: + msg = "Layer {} does not exist." + raise ValueError, msg.format(layer) + + + def getTileLocation(self, gid): + # experimental way to find locations of a tile by the GID + + p = product(xrange(self.width), + xrange(self.height), + xrange(len(self.tilelayers))) + + return [ (x,y,l) for (x,y,l) in p + if self.tilelayers[l].data[y][x] == gid ] + + + def getTilePropertiesByGID(self, gid): + try: + return self.tile_properties[gid] + except KeyError: + return None + + + def setTileProperties(self, gid, d): + """ + set the properties of a tile by GID. + must use a standard python dict as d + """ + + try: + self.tile_properties[gid] = d + except KeyError: + msg = "GID #{} does not exist." + raise ValueError, msg.format(gid) + + + def getTilePropertiesByLayer(self, layer): + """ + Return a list of tile properties (dict) in use in this tile layer. + """ + + try: + layer = int(layer) + except: + msg = "Layer must be an integer. Got {} instead." + raise ValueError, msg.format(type(layer)) + + p = product(range(self.width),range(self.height)) + layergids = set( self.tilelayers[layer].data[y][x] for x, y in p ) + + props = [] + for gid in layergids: + try: + props.append((gid, self.tile_properties[gid])) + except: + continue + + return props + + + def registerGID(self, real_gid, flags=0): + """ + used to manage the mapping of GID between the tmx data and the internal + data. + + number returned is gid used internally + """ + + if real_gid: + try: + return self.imagemap[(real_gid, flags)][0] + except KeyError: + # this tile has not been encountered before, or it has been + # transformed in some way. make a new GID for it. + gid = self.maxgid + self.maxgid += 1 + self.imagemap[(real_gid, flags)] = (gid, flags) + self.gidmap[real_gid].append((gid, flags)) + return gid + + else: + return 0 + + + + def mapGID(self, real_gid): + """ + used to lookup a GID read from a TMX file's data + """ + + try: + return self.gidmap[int(real_gid)] + except KeyError: + return None + except TypeError: + msg = "GID's must be an integer" + raise TypeError, msg + + + def loadTileImages(self, filename): + raise NotImplementedError + + + def load(self): + """ + parse a map node from a tiled tmx file + """ + etree = ElementTree.parse(self.filename).getroot() + self.set_properties(etree) + + # initialize the gid mapping + self.imagemap[(0,0)] = 0 + + for node in etree.findall('layer'): + self.tilelayers.append(TiledLayer(self, node)) + + for node in etree.findall('objectgroup'): + self.objectgroups.append(TiledObjectGroup(self, node)) + + for node in etree.findall('tileset'): + self.tilesets.append(TiledTileset(self, node)) + + # "tile objects", objects with a GID, have need to have their + # attributes set after the tileset is loaded + for o in self.getObjects(): + p = self.getTilePropertiesByGID(o.gid) + if p: + o.name = "TileObject" + o.__dict__.update(p) + + + @property + def visibleTileLayers(self): + return [layer for layer in self.tilelayers if layer.visible] + + +class TiledTileset(TiledElement): + reserved = "firstgid source name tilewidth tileheight spacing margin image tile properties".split() + + def __init__(self, parent, node): + TiledElement.__init__(self) + self.parent = parent + + # defaults from the specification + self.firstgid = 0 + self.source = None + self.name = None + self.tilewidth = 0 + self.tileheight = 0 + self.spacing = 0 + self.margin = 0 + self.tiles = {} + + self.parse(node) + + def __repr__(self): + return "<{}: \"{}\">".format(self.__class__.__name__, self.name) + + + def parse(self, node): + """ + parse a tileset element and return a tileset object and properties for + tiles as a dict + + a bit of mangling is doew here so that tilesets that have external + TSX files appear the same as those that don't + """ + import os + + + # if true, then node references an external tileset + source = node.get('source', False) + if source: + if source[-4:].lower() == ".tsx": + + # external tilesets don't save this, store it for later + self.firstgid = int(node.get('firstgid')) + + # we need to mangle the path - tiled stores relative paths + dirname = os.path.dirname(self.parent.filename) + path = os.path.abspath(os.path.join(dirname, source)) + try: + node = ElementTree.parse(path).getroot() + except IOError: + msg = "Cannot load external tileset: {}" + raise Exception, msg.format(path) + + else: + msg = "Found external tileset, but cannot handle type: {}" + raise Exception, msg.format(self.source) + + self.set_properties(node) + + # since tile objects [probably] don't have a lot of metadata, + # we store it seperately in the parent (a TiledMap instance) + for child in node.iter('tile'): + real_gid = int(child.get("id")) + p = parse_properties(child) + p['width'] = self.tilewidth + p['height'] = self.tileheight + for gid, flags in self.parent.mapGID(real_gid + self.firstgid): + self.parent.setTileProperties(gid, p) + + + image_node = node.find('image') + self.source = image_node.get('source') + self.trans = image_node.get("trans", None) + + +class TiledLayer(TiledElement): + reserved = "name x y width height opacity properties data".split() + + def __init__(self, parent, node): + TiledElement.__init__(self) + self.parent = parent + self.data = [] + + # defaults from the specification + self.name = None + self.opacity = 1.0 + self.visible = True + + self.parse(node) + + + def __repr__(self): + return "<{}: \"{}\">".format(self.__class__.__name__, self.name) + + + def parse(self, node): + """ + parse a layer element + """ + from utils import group + from itertools import product, imap + from struct import unpack + import array + + self.set_properties(node) + + data = None + next_gid = None + + data_node = node.find('data') + + encoding = data_node.get("encoding", None) + if encoding == "base64": + from base64 import decodestring + data = decodestring(data_node.text.strip()) + + elif encoding == "csv": + next_gid = imap(int, "".join( + line.strip() for line in data_node.text.strip() + ).split(",")) + + elif encoding: + msg = "TMX encoding type: {} is not supported." + raise Exception, msg.format(encoding) + + compression = data_node.get("compression", None) + if compression == "gzip": + from StringIO import StringIO + import gzip + with gzip.GzipFile(fileobj=StringIO(data)) as fh: + data = fh.read() + + elif compression == "zlib": + import zlib + data = zlib.decompress(data) + + elif compression: + msg = "TMX compression type: {} is not supported." + raise Exception, msg.format(str(attr["compression"])) + + # if data is None, then it was not decoded or decompressed, so + # we assume here that it is going to be a bunch of tile elements + # TODO: this will probably raise an exception if there are no tiles + if encoding == next_gid == None: + def get_children(parent): + for child in parent.findall('tile'): + yield int(child.get('gid')) + + next_gid = get_children(data_node) + + elif data: + # data is a list of gid's. cast as 32-bit ints to format properly + # create iterator to effeciently parse data + next_gid=imap(lambda i:unpack("<L", "".join(i))[0], group(data, 4)) + + # using bytes here limits the layer to 256 unique tiles + # may be a limitation for very detailed maps, but most maps are not + # so detailed. + [ self.data.append(array.array("B")) for i in xrange(self.height) ] + + for (y, x) in product(xrange(self.height), xrange(self.width)): + self.data[y].append(self.parent.registerGID(*decode_gid(next(next_gid)))) + + +class TiledObjectGroup(TiledElement, list): + """ + Stores TiledObjects. Supports any operation of a normal list. + """ + reserved = "name color x y width height opacity object properties".split() + + def __init__(self, parent, node): + TiledElement.__init__(self) + self.parent = parent + + # defaults from the specification + self.name = None + + self.parse(node) + + + def __repr__(self): + return "<{}: \"{}\">".format(self.__class__.__name__, self.name) + + + def parse(self, node): + """ + parse a objectgroup element and return a object group + """ + + self.set_properties(node) + + for child in node.findall('object'): + o = TiledObject(self.parent, child) + self.append(o) + + +class TiledObject(TiledElement): + reserved = "name type x y width height gid properties polygon polyline image".split() + + def __init__(self, parent, node): + TiledElement.__init__(self) + self.parent = parent + + # defaults from the specification + self.name = None + self.type = None + self.x = 0 + self.y = 0 + self.width = 0 + self.height = 0 + self.gid = 0 + + self.parse(node) + + + def __repr__(self): + return "<{}: \"{}\">".format(self.__class__.__name__, self.name) + + + def parse(self, node): + self.set_properties(node) + + # correctly handle "tile objects" (object with gid set) + if self.gid: + self.gid = self.parent.registerGID(self.gid) + + polygon = node.find('polygon') + if polygon is not None: + x1 = x2 = y1 = y2 = 0 + self.points = read_points(polygon.get('points')) + self.closed = 1 + for x, y in self.points: + if x < x1: x1 = x + if x > x2: x2 = x + if y < y1: y1 = y + if y > y2: y2 = y + self.width = abs(x1) + abs(x2) + self.height = abs(y1) + abs(y2) + + polyline = node.find('polyline') + if polyline is not None: + x1 = x2 = y1 = y2 = 0 + self.points = read_points(polyline.get('points')) + self.closed = 0 + for x, y in self.points: + if x < x1: x1 = x + if x > x2: x2 = x + if y < y1: y1 = y + if y > y2: y2 = y + self.width = abs(x1) + abs(x2) + self.height = abs(y1) + abs(y2)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx/tmxloader.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,304 @@ +""" +Map loader for TMX Files +bitcraft (leif dot theden at gmail.com) +v.15 - for python 2.7 + +If you have any problems or suggestions, please contact me via email. +Tested with Tiled 0.8.1 for Mac. + +released under the LGPL v3 + +=============================================================================== + +This map loader can be used to load maps created in the Tiled map editor. It +provides a simple way to get tiles and associated metadata so that you can draw +a map onto the screen. It does not force you to draw your map in any +particular way. + +This is *not* a rendering engine. It will load the data that is necessary to +render a map onto the screen. All tiles will be loaded into in memory and +available to blit onto the screen. + + +Design Goals: + Simple api + Memory efficient and fast + +Features: + Loads data and "properties" metadata from Tile's TMX format + "Properties" for: maps, tilesets, layers, objectgroups, objects, and tiles + Point data for polygon and polyline objects + Automatic flipping and rotation of tiles + Supports base64, csv, gzip, zlib and uncompressed XML + Image loading with pygame + +New in .15: + loader: fixed issue where objects with tile gid did not load properties + loader: polygon and polyline objects + loader: new lookup methods use iterators + loader: loading function moved into classes + loader: data/images can be reloaded on the fly + loader: uses etree for faster xml parsing + +New in .14: + loader: Fixed gid lookup for "buildDistributionRects" + loader: Added useful output to a few classes "__repr__" + loader: Fixed a gid mapping issue that broke rotated tiles + pygame: fixed colorkey handling + pygame: correctly handles margins and spacing between tiles in tilesets + pygame: b/c of changes, now correctly renders tiled's example maps + added scrolling demo + +New in .13: + loader: Renamed "get_tile_image" to "getTileImage" + loader: Removed duplicates returned from getTilePropertiesByLayer + loader: Modified confusing messages for GID errors + loader: Fixed bug where transformed tile properties are not available + loader: No longer loads metadata for tiles that are not used + loader: Reduced tile cache to 256 unique tiles + loader: Removed 'visible' from list of reserved words + loader: Added 'buildDistributionRects' and maputils module + loader: Added some misc. functions for retrieving properties + pygame: Smarter tile management made tile loading cache useless; removed it + pygame: pygame.RLEACCEL flag added when appropriate + +New in .12: + loader: Fixed bug where tile properties could contain reserved words + loader: Reduced size of image index by only allocating space for used tiles + +New in .11: + loader: Added support for tileset properties + loader: Now checks for property names that are reserved for internal use + loader: Added support for rotated tiles + pygame: Only the tiles that are used in the map will be loaded into memory + pygame: Added support for rotated tiles + pygame: Added option to force a bitsize (depth) for surfaces + pygame: Added option to convert alpha transparency to colorkey transparency + pygame: Tilesets no longer load with per-pixel alphas by default + pygame: Colorkey transparency should be correctly handled now + + +NOTES: + +* The Tiled "properties" have reserved names. + +If you use "properties" for any of the following object types, you cannot use +any of theese words as a name for your property. A ValueError will be raised +if there are any conflicts. + +As of 0.8.1, these values are: + +map: version, orientation, width, height, tilewidth, tileheight + properties, tileset, layer, objectgroup + +tileset: firstgid, source, name, tilewidth, tileheight, spacing, margin, + image, tile, properties + +tile: id, image, properties + +layer: name, x, y, width, height, opacity, properties, data + +objectgroup: name, color, x, y, width, height, opacity, object, properties + +object: name, type, x, y, width, height, gid, properties, polygon, + polyline, image + + + +I have been intentionally not including a module rendering utility since +rendering a map will have different requirements for every situation. However, +I can appreciate that some poeple won't understand how it works unless they see +it, so I am including a sample map and viewer. It includes a scrolling/zooming +renderer. They are for demonstation purposes, and may not be suitable for all +projects. Use at your own risk. + +I've also included a copy of this loader that may work with python 3.x. I +personally do not think that python 3.x should be used with pygame, yet (and I +am not the only person). You can try it if you insist on using pygame with +python 3.x, but I don't update that often. + +=============================================================================== + +Basic usage sample: + + >>> from pytmx import tmxloader + >>> tmxdata = tmxloader.load_pygame("map.tmx") + + +When you want to draw tiles, you simply call "getTileImage": + + >>> image = tmxdata.getTileImage(x, y, layer) + >>> screen.blit(position, image) + + +Maps, tilesets, layers, objectgroups, and objects all have a simple way to +access metadata that was set inside tiled: they all become object attributes. + + >>> layer = tmxdata.layers[0] + >>> print layer.tilewidth + 32 + >>> print layer.weather + 'sunny' + + +Tiles properties are the exception here, and must be accessed through +"getTileProperties". The data is a regular Python dictionary: + + >>> tile = tmxdata.getTileProperties(x, y, layer) + >>> tile["name"] + 'CobbleStone' + + + +Please see the TiledMap class for some api information. +""" +from utils import types +from constants import * + + + +def load_tmx(filename, *args, **kwargs): + # for .14 compatibility + from pytmx import TiledMap + + tiledmap = TiledMap(filename) + return tiledmap + + +def load_images_pygame(tmxdata, mapping, *args, **kwargs): + """ + due to the way the tiles are loaded, they will be in the same pixel format + as the display when it is loaded. take this into consideration if you + intend to support different screen pixel formats. + + by default, the images will not have per-pixel alphas. this can be + changed by including "pixelalpha=True" in the keywords. this will result + in much slower blitting speeds. + + if the tileset's image has colorkey transparency set in Tiled, the loader + will return images that have their transparency already set. using a + tileset with colorkey transparency will greatly increase the speed of + rendering the map. + + optionally, you can force the loader to strip the alpha channel of the + tileset image and to fill in the missing areas with a color, then use that + new color as a colorkey. the resulting tiles will render much faster, but + will not preserve the transparency of the tile if it uses partial + transparency (which you shouldn't be doing anyway, this is SDL). + """ + from itertools import product + from pygame import Surface, mask + import pygame, os + + + def handle_transformation(tile, flags): + if flags: + fx = flags & TRANS_FLIPX == TRANS_FLIPX + fy = flags & TRANS_FLIPY == TRANS_FLIPY + r = flags & TRANS_ROT == TRANS_ROT + + if r: + # not sure why the flip is required...but it is. + newtile = pygame.transform.rotate(tile, 270) + newtile = pygame.transform.flip(newtile, 1, 0) + + if fx or fy: + newtile = pygame.transform.flip(newtile, fx, fy) + + elif fx or fy: + newtile = pygame.transform.flip(tile, fx, fy) + + # preserve any flags that may have been lost after the transformation + return newtile.convert(tile) + + else: + return tile + + + pixelalpha = kwargs.get("pixelalpha", False) + force_colorkey = kwargs.get("force_colorkey", False) + force_bitdepth = kwargs.get("depth", False) + + if force_colorkey: + try: + force_colorkey = pygame.Color(*force_colorkey) + except: + msg = "Cannot understand color: {}" + raise Exception, msg.format(force_colorkey) + + tmxdata.images = [0] * tmxdata.maxgid + + for firstgid, t in sorted((t.firstgid, t) for t in tmxdata.tilesets): + path = os.path.join(os.path.dirname(tmxdata.filename), t.source) + + image = pygame.image.load(path) + + w, h = image.get_size() + tile_size = (t.tilewidth, t.tileheight) + real_gid = t.firstgid - 1 + + if t.trans: + tileset_colorkey = pygame.Color("#{}".format(t.trans)) + + # i dont agree with margins and spacing, but i'll support it anyway + # such is life. okay.jpg + tilewidth = t.tilewidth + t.spacing + tileheight = t.tileheight + t.spacing + + # some tileset images may be slightly larger than the tile area + # ie: may include a banner, copyright, ect. this compensates for that + width = ((int((w-t.margin*2) + t.spacing) / tilewidth) * tilewidth) - t.spacing + height = ((int((h-t.margin*2) + t.spacing) / tileheight) * tileheight) - t.spacing + + # using product avoids the overhead of nested loops + p = product(xrange(t.margin, height+t.margin, tileheight), + xrange(t.margin, width+t.margin, tilewidth)) + + for (y, x) in p: + real_gid += 1 + gids = tmxdata.mapGID(real_gid) + if gids == []: continue + + # we do some tests to correctly handle the tile and set the right + # blitting flags. just grab a section of it. + temp = image.subsurface(((x,y), tile_size)) + + # count the number of pixels in the tile that are not transparent + px = mask.from_surface(temp).count() + + # there are no transparent pixels in the image + if px == tile_size[0] * tile_size[1]: + tile = temp.convert() + + # there are transparent pixels, and set to force a colorkey + elif force_colorkey: + tile = Surface(tile_size) + tile.fill(force_colorkey) + tile.blit(temp, (0,0)) + tile.set_colorkey(force_colorkey, pygame.RLEACCEL) + + # there are transparent pixels, and tiled set a colorkey + elif t.trans: + tile = temp.convert() + tile.set_colorkey(tileset_colorkey, pygame.RLEACCEL) + + # there are transparent pixels, and set for perpixel alpha + elif pixelalpha: + tile = temp.convert_alpha() + + # there are transparent pixels, and we won't handle them + else: + tile = temp.convert() + + for gid, flags in gids: + tmxdata.images[gid] = handle_transformation(tile, flags) + + +def load_pygame(filename, *args, **kwargs): + tmxdata = load_tmx(filename, *args, **kwargs) + load_images_pygame(tmxdata, None, *args, **kwargs) + + return tmxdata + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx/tmxloader3.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,612 @@ +""" +Map loader for TMX Files +bitcraft (leif.theden at gmail.com) +v.7 -- for python 3.x + +If you have any problems, please contact me via email. +Tested with Tiled 0.7.1 for Mac. + +released under the LGPL v3 + +====================================================================== +Design Goals: + Simple api + Memory efficient and fast + Quick access to tiles, attributes, and properties + +Non-Goals: + Rendering + +Works: + Pygame image loading + Map loading with all required types + Properties for all types: maps, layers, objects, tiles + Automatic flipping of tiles + +Todo: + Pygame: test colorkey transparency + + Optimized for maps that do not make heavy use of tile + properties. If i find that it is used a lot then i can rework + it for better performance. + +====================================================================== + +Basic usage sample: + + >>> import tmxloader + >>> tiledmap = tmxloader.load_pygame("map.tmx") + + +When you want to draw tiles, you simply call "get_tile_image": + + >>> image = tiledmap.get_tile_image(x, y, layer) + >>> screen.blit(position, image) + + +Layers, objectgroups, tilesets, and maps all have a simple way to access +metadata that was set inside tiled: they all become class attributes. + + >>> print(layer.tilewidth) + 32 + >>> print(layer.weather) + 'sunny' + + +Tiles are the exception here, and must be accessed through "getTileProperties" +and are regular Python dictionaries: + + >>> tile = tiledmap.getTileProperties(x, y, layer) + >>> tile["name"] + 'CobbleStone' + +""" + +from itertools import chain + + +# internal flags +FLIP_X = 1 +FLIP_Y = 2 + + +# Tiled gid flags +GID_FLIP_X = 1<<31 +GID_FLIP_Y = 1<<30 + + +class TiledElement(object): + pass + +class TiledMap(TiledElement): + """ + not really useful unless "loaded" ie: don't instance directly. + see the pygame loader for inspiration + """ + + def __init__(self): + TiledElement.__init__(self) + self.layers = [] # list of all layer types (tile layers + object layers) + self.tilesets = [] # list of TiledTileset objects + self.tilelayers = [] # list of TiledLayer objects + self.objectgroups = [] # list of TiledObjectGroup objects + self.tile_properties = {} # dict of tiles that have additional metadata (properties) + self.filename = None + + # this is a work around to tiled's strange way of storing gid's + self.images = [0] + + # defaults from the TMX specification + self.version = 0.0 + self.orientation = None + self.width = 0 + self.height = 0 + self.tilewidth = 0 + self.tileheight = 0 + + def get_tile_image(self, x, y, layer): + """ + return the tile image for this location + x and y must be integers and are in tile coordinates, not pixel + + return value will be 0 if there is no tile with that location. + """ + + try: + gid = self.tilelayers[layer].data[y][x] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} is invalid.".format(x, y, layer) + raise Exception(msg) + + else: + try: + return self.images[gid] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} has invalid GID: {3}/{4}.".format(x, y, layer, gid, len(self.images)) + raise Exception(msg) + + def getTileGID(self, x, y, layer): + """ + return GID of a tile in this location + x and y must be integers and are in tile coordinates, not pixel + """ + + try: + return self.tilelayers[layer].data[y][x] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} is invalid.".format(x, y, layer) + raise Exception(msg) + + def getDrawOrder(self): + """ + return a list of objects in the order that they should be drawn + this will also exclude any layers that are not set to visible + + may be useful if you have objects and want to control rendering + from tiled + """ + + raise NotImplementedError + + def getTileImages(self, r, layer): + """ + return a group of tiles in an area + expects a pygame rect or rect-like list/tuple + + usefull if you don't want to repeatedly call get_tile_image + probably not the most effecient way of doing this, but oh well. + """ + + raise NotImplementedError + + def getObjects(self): + """ + Return iterator all of the objects associated with this map + """ + + return chain(*[ i.objects for i in self.objectgroups ]) + + def getTileProperties(self, x, y, layer): + """ + return the properties for the tile, if any + x and y must be integers and are in tile coordinates, not pixel + + returns a dict of there are properties, otherwise will be None + """ + + try: + gid = self.tilelayers[layer].data[y][x] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} is invalid.".format(x, y, layer) + raise Exception(msg) + + else: + try: + return self.tile_properties[gid] + except (IndexError, ValueError): + msg = "Coords: ({0},{1}) in layer {2} has invaid GID: {3}/{4}.".format(x, y, layer, gid, len(self.images)) + raise Exception(msg) + + def getTilePropertiesByGID(self, gid): + try: + return self.tile_properties[gid] + except KeyError: + return None + +# the following classes get their attributes filled in with the loader + +class TiledTileset(TiledElement): + def __init__(self): + TiledElement.__init__(self) + + # defaults from the specification + self.firstgid = 0 + self.lastgid = 0 + self.name = None + self.tilewidth = 0 + self.tileheight = 0 + self.spacing = 0 + self.margin = 0 + +class TiledLayer(TiledElement): + def __init__(self): + TiledElement.__init__(self) + self.data = None + + # defaults from the specification + self.name = None + self.opacity = 1.0 + self.visible = 1 + +class TiledObjectGroup(TiledElement): + def __init__(self): + TiledElement.__init__(self) + self.objects = [] + + # defaults from the specification + self.name = None + +class TiledObject(TiledElement): + __slots__ = ['name', 'type', 'x', 'y', 'width', 'height', 'gid'] + + def __init__(self): + TiledElement.__init__(self) + + # defaults from the specification + self.name = None + self.type = None + self.x = 0 + self.y = 0 + self.width = 0 + self.height = 0 + self.gid = 0 + + +def load_tmx(filename): + """ + Utility function to parse a Tiled TMX and return a usable object. + Images will not be loaded, so probably not useful to call this directly + + See the load_pygame func for an idea of what to do + """ + + from xml.dom.minidom import parse + from itertools import tee, islice, chain + from collections import defaultdict + from struct import unpack + import array, os + + # used to change the unicode string returned from minidom to + # proper python variable types. + types = { + "version": float, + "orientation": str, + "width": int, + "height": int, + "tilewidth": int, + "tileheight": int, + "firstgid": int, + "source": str, + "name": str, + "spacing": int, + "margin": int, + "source": str, + "trans": str, + "id": int, + "opacity": float, + "visible": bool, + "encoding": str, + "compression": str, + "gid": int, + "type": str, + "x": int, + "y": int, + "value": str, + } + + def pairwise(iterable): + # return a list as a sequence of pairs + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + def group(l, n): + # return a list as a sequence of n tuples + return zip(*[islice(l, i, None, n) for i in range(n)]) + + def parse_properties(node): + """ + parse a node and return a dict that represents a tiled "property" + """ + + d = {} + + for child in node.childNodes: + if child.nodeName == "properties": + for subnode in child.getElementsByTagName("property"): + # the "properties" from tiled's tmx have an annoying + # quality that "name" and "value" is included as part of it. + # so we mangle it to get that stuff out. + d.update(dict(pairwise([ str(i.value) for i in list(subnode.attributes.values()) ]))) + + return d + + def get_properties(node): + """ + parses a node and returns a dict that contains the data from the node's + attributes and any data from "property" elements as well. + """ + + d = {} + + # get tag attributes + d.update(get_attributes(node)) + + # get vlues of the properties element, if any + d.update(parse_properties(node)) + + return d + + def set_properties(obj, node): + """ + read the xml attributes and tiled "properties" from a xml node and fill in + the values into an object's dictionary + """ + + [ setattr(obj, k, v) for k,v in list(get_properties(node).items()) ] + + def get_attributes(node): + """ + get the attributes from a node and fix them to the correct type + """ + + d = defaultdict(lambda:None) + + for k, v in list(node.attributes.items()): + k = str(k) + d[k] = types[k](v) + + return d + + def decode_gid(raw_gid): + # gid's are encoded with extra information + # as of 0.7.0 it determines if the tile should be flipped when rendered + + flags = 0 + if raw_gid & GID_FLIP_X == GID_FLIP_X: flags += FLIP_X + if raw_gid & GID_FLIP_Y == GID_FLIP_Y: flags += FLIP_Y + gid = raw_gid & ~(GID_FLIP_X | GID_FLIP_Y) + + return gid, flags + + + def parse_map(node): + """ + parse a map node from a tiled tmx file + return a tiledmap + """ + + tiledmap = TiledMap() + tiledmap.filename = filename + set_properties(tiledmap, map_node) + + for node in map_node.getElementsByTagName("tileset"): + t, tiles = parse_tileset(node) + tiledmap.tilesets.append(t) + tiledmap.tile_properties.update(tiles) + + for node in dom.getElementsByTagName("layer"): + l = parse_layer(tiledmap.tilesets, node) + tiledmap.tilelayers.append(l) + tiledmap.layers.append(l) + + for node in dom.getElementsByTagName("objectgroup"): + o = parse_objectgroup(node) + tiledmap.objectgroups.append(o) + tiledmap.layers.append(o) + + return tiledmap + + + def parse_tileset(node, firstgid=None): + """ + parse a tileset element and return a tileset object and properties for tiles as a dict + """ + + tileset = TiledTileset() + set_properties(tileset, node) + tiles = {} + + if firstgid != None: + tileset.firstgid = firstgid + + # since tile objects probably don't have a lot of metadata, + # we store it seperately from the class itself + for child in node.childNodes: + if child.nodeName == "tile": + p = get_properties(child) + gid = p["id"] + tileset.firstgid + del p["id"] + tiles[gid] = p + + # check for tiled "external tilesets" + if hasattr(tileset, "source"): + if tileset.source[-4:].lower() == ".tsx": + try: + # we need to mangle the path some because tiled stores relative paths + path = os.path.join(os.path.dirname(filename), tileset.source) + tsx = parse(path) + except IOError: + raise IOError("Cannot load external tileset: " + path) + + tileset_node = tsx.getElementsByTagName("tileset")[0] + tileset, tiles = parse_tileset(tileset_node, tileset.firstgid) + else: + raise Exception("Found external tileset, but cannot handle type: " + tileset.source) + + # if we have an "image" tag, process it here + try: + image_node = node.getElementsByTagName("image")[0] + except IndexError: + pass + else: + attr = get_attributes(image_node) + tileset.source = attr["source"] + tileset.trans = attr["trans"] + + # calculate the number of tiles in this tileset + x, r = divmod(attr["width"], tileset.tilewidth) + y, r = divmod(attr["height"], tileset.tileheight) + + tileset.lastgid = tileset.firstgid + x + y + + return tileset, tiles + + + def parse_layer(tilesets, node): + """ + parse a layer element and return a layer object + + tilesets is required since we need to mangle gid's here + """ + + layer = TiledLayer() + layer.data = [] + layer.flipped_tiles = [] + set_properties(layer, node) + + data = None + next_gid = None + + data_node = node.getElementsByTagName("data")[0] + attr = get_attributes(data_node) + + if attr["encoding"] == "base64": + from base64 import b64decode + data = b64decode(bytes(data_node.lastChild.nodeValue, 'ascii')) + + elif attr["encoding"] == "csv": + next_gid = map(int, "".join([line.strip() for line in data_node.lastChild.nodeValue]).split(",")) + + elif not attr["encoding"] == None: + raise Exception("TMX encoding type: " + str(attr["encoding"]) + " is not supported.") + + if attr["compression"] == "gzip": + from io import BytesIO + import gzip + with gzip.GzipFile(fileobj=BytesIO(data)) as fh: + data = fh.read() + + elif not attr["compression"] == None: + raise Exception("TMX compression type: " + str(attr["compression"]) + " is not supported.") + + # if data is None, then it was not decoded or decompressed, so + # we assume here that it is going to be a bunch of tile elements + if attr["encoding"] == next_gid == None: + def get_children(parent): + for child in parent.getElementsByTagName("tile"): + yield int(child.getAttribute("gid")) + + next_gid = get_children(data_node) + + elif not data == None: + # cast the data as a list of 32-bit integers + def u(i): return unpack("<L", bytes(i))[0] + next_gid = map(u, group(data, 4)) + + gids = [ i.firstgid for i in tilesets ] + + # fill up our 2D array of gid's. + for y in range(layer.height): + + # store as 16-bit ints, since we will never use enough tiles to fill a 32-bit int + layer.data.append(array.array("H")) + + for x in range(layer.width): + gid, flags = decode_gid(next(next_gid)) + if not flags == 0: layer.flipped_tiles.append((x, y, gid, flags)) + layer.data[y].append(gid) + + return layer + + + def parse_objectgroup(node): + """ + parse a objectgroup element and return a object group + """ + + objgroup = TiledObjectGroup() + set_properties(objgroup, node) + + for subnode in node.getElementsByTagName("object"): + obj = TiledObject() + set_properties(obj, subnode) + objgroup.objects.append(obj) + + return objgroup + + + # open and read our TMX (which is really just xml) + dom = parse(filename) + map_node = dom.getElementsByTagName("map")[0] + return parse_map(map_node) + + +def load_pygame(filename): + """ + load a tiled TMX map for use with pygame + """ + + from pygame import Surface + import pygame, os + + tiledmap = load_tmx(filename) + + # cache will find duplicate tiles to reduce memory usage + # mostly this is a problem in the blank areas of a tilemap + cache = {} + + # just a precaution to make sure tileset images are added in the correct order + for firstgid, t in sorted([ (t.firstgid, t) for t in tiledmap.tilesets ]): + path = os.path.join(os.path.dirname(tiledmap.filename), t.source) + + image = pygame.image.load(path) + + w, h = image.get_rect().size + + tile_size = (t.tilewidth, t.tileheight) + + # some tileset images may be slightly larger than the tiles area + # ie: may include a banner, copyright, ect. this compensates for that + for y in range(0, int(h / t.tileheight) * t.tileheight, t.tileheight): + for x in range(0, int(w / t.tilewidth) * t.tilewidth, t.tilewidth): + + # somewhat handle transparency, though colorkey handling is not tested + if t.trans == None: + tile = Surface(tile_size, pygame.SRCALPHA) + else: + tile = Surface(tile_size) + + # blit the smaller portion onto a new image from the larger one + tile.blit(image, (0,0), ((x, y), tile_size)) + + # make a unique id for this image, not sure if this is the best way, but it works + key = pygame.image.tostring(tile, "RGBA") + + # make sure we don't have a duplicate tile + try: + tile = cache[key] + except KeyError: + if t.trans == None: + tile = tile.convert_alpha() + else: + tile = tile.convert() + tile.set_colorkey(t.trans) + + # update the cache + cache[key] = tile + + tiledmap.images.append(tile) + + # correctly handle transformed tiles. currently flipped tiles + # work by creating a new gid for the flipped tile and changing the gid + # in the layer to the new gid. + for layer in tiledmap.tilelayers: + for x, y, gid, trans in layer.flipped_tiles: + fx = trans & FLIP_X == FLIP_X + fy = trans & FLIP_Y == FLIP_Y + + tile = pygame.transform.flip(tiledmap.images[gid], fx, fy) + tiledmap.images.append(tile) + + # change the original gid in the layer data to the new gid + layer.data[y][x] = len(tiledmap.images) - 1 + + del layer.flipped_tiles + del cache + + return tiledmap + +if __name__ == '__main__': + print('[tmxloader] starting built-in test') + load_tmx("../resources/maps/village.tmx")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx/utils.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,251 @@ +from pygame import Rect +from itertools import tee, islice, izip, product +from collections import defaultdict +from constants import * + + + +""" +Various code that doesn't really fit anywhere else +""" + + +def read_points(text): + return [ tuple(map(lambda x: int(x), i.split(','))) + for i in text.split() ] + + + +def parse_properties(node): + """ + parse a node and return a dict that represents a tiled "property" + """ + + # the "properties" from tiled's tmx have an annoying quality that "name" + # and "value" is included. here we mangle it to get that junk out. + + d = {} + + for child in node.findall('properties'): + for subnode in child.findall('property'): + d[subnode.get('name')] = subnode.get('value') + + return d + + +def decode_gid(raw_gid): + # gid's are encoded with extra information + # as of 0.7.0 it determines if the tile should be flipped when rendered + # as of 0.8.0 bit 30 determines if GID is rotated + + flags = 0 + if raw_gid & GID_TRANS_FLIPX == GID_TRANS_FLIPX: flags += TRANS_FLIPX + if raw_gid & GID_TRANS_FLIPY == GID_TRANS_FLIPY: flags += TRANS_FLIPY + if raw_gid & GID_TRANS_ROT == GID_TRANS_ROT: flags += TRANS_ROT + gid = raw_gid & ~(GID_TRANS_FLIPX | GID_TRANS_FLIPY | GID_TRANS_ROT) + + return gid, flags + + +def handle_bool(text): + # properly convert strings to a bool + try: + return bool(int(text)) + except: + pass + + try: + text = str(text).lower() + if text == "true": return True + if text == "yes": return True + if text == "false": return False + if text == "no": return False + except: + pass + + raise ValueError + + +# used to change the unicode string returned from xml to proper python +# variable types. +types = defaultdict(lambda: str) +types.update({ + "version": float, + "orientation": str, + "width": int, + "height": int, + "tilewidth": int, + "tileheight": int, + "firstgid": int, + "source": str, + "name": str, + "spacing": int, + "margin": int, + "source": str, + "trans": str, + "id": int, + "opacity": float, + "visible": handle_bool, + "encoding": str, + "compression": str, + "gid": int, + "type": str, + "x": int, + "y": int, + "value": str, +}) + + +def pairwise(iterable): + # return a list as a sequence of pairs + a, b = tee(iterable) + next(b, None) + return izip(a, b) + + +def group(l, n): + # return a list as a sequence of n tuples + return izip(*(islice(l, i, None, n) for i in xrange(n))) + + +def buildDistributionRects(tmxmap, layer, tileset=None, real_gid=None): + """ + generate a set of non-overlapping rects that represents the distribution + of the specfied gid. + + useful for generating rects for use in collision detection + """ + + if isinstance(tileset, int): + try: + tileset = tmxmap.tilesets[tileset] + except IndexError: + msg = "Tileset #{} not found in map {}." + raise IndexError, msg.format(tileset, tmxmap) + + elif isinstance(tileset, str): + try: + tileset = [ t for t in tmxmap.tilesets if t.name == tileset ].pop() + except IndexError: + msg = "Tileset \"{}\" not found in map {}." + raise ValueError, msg.format(tileset, tmxmap) + + elif tileset: + msg = "Tileset must be either a int or string. got: {}" + raise ValueError, msg.format(type(tileset)) + + gid = None + if real_gid: + try: + gid, flags = tmxmap.mapGID(real_gid)[0] + except IndexError: + msg = "GID #{} not found" + raise ValueError, msg.format(real_gid) + + + if isinstance(layer, int): + layer_data = tmxmap.getLayerData(layer).data + elif isinstance(layer, str): + try: + layer = [ l for l in tmxmap.tilelayers if l.name == layer ].pop() + layer_data = layer.data + except IndexError: + msg = "Layer \"{}\" not found in map {}." + raise ValueError, msg.format(layer, tmxmap) + + p = product(xrange(tmxmap.width), xrange(tmxmap.height)) + if gid: + points = [ (x,y) for (x,y) in p if layer_data[y][x] == gid ] + else: + points = [ (x,y) for (x,y) in p if layer_data[y][x] ] + + rects = simplify(points, tmxmap.tilewidth, tmxmap.tileheight) + return rects + + +def simplify(all_points, tilewidth, tileheight): + """ + klugde: + + "A kludge (or kluge) is a workaround, a quick-and-dirty solution, + a clumsy or inelegant, yet effective, solution to a problem, typically + using parts that are cobbled together." + + -- wikipedia + + turn a list of points into a rects + adjacent rects will be combined. + + plain english: + the input list must be a a list of tuples that represent + the areas to be combined into rects + the rects will be blended together over solid groups + + so if data is something like: + + 0 1 1 1 0 0 0 + 0 1 1 0 0 0 0 + 0 0 0 0 0 4 0 + 0 0 0 0 0 4 0 + 0 0 0 0 0 0 0 + 0 0 1 1 1 1 1 + + you'll have the 4 rects that mask the area like this: + + ..######...... + ..####........ + ..........##.. + ..........##.. + .............. + ....########## + + pretty cool, right? + + there may be cases where the number of rectangles is not as low as possible, + but i haven't found that it is excessively bad. certainly much better than + making a list of rects, one for each tile on the map! + + """ + + def pick_rect(points, rects): + ox, oy = sorted([ (sum(p), p) for p in points ])[0][1] + x = ox + y = oy + ex = None + + while 1: + x += 1 + if not (x, y) in points: + if ex == None: + ex = x - 1 + + if ((ox, y+1) in points): + if x == ex + 1 : + y += 1 + x = ox + + else: + y -= 1 + break + else: + if x <= ex: y-= 1 + break + + c_rect = Rect(ox*tilewidth,oy*tileheight,\ + (ex-ox+1)*tilewidth,(y-oy+1)*tileheight) + + rects.append(c_rect) + + rect = Rect(ox,oy,ex-ox+1,y-oy+1) + kill = [ p for p in points if rect.collidepoint(p) ] + [ points.remove(i) for i in kill ] + + if points: + pick_rect(points, rects) + + rect_list = [] + while all_points: + pick_rect(all_points, rect_list) + + return rect_list +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/README Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,44 @@ +Map loader for TMX Files +bitcraft (leif dot theden at gmail.com) +v.15 - for python 2.7 + +If you have any problems or suggestions, please contact me via email. +Tested with Tiled 0.8.0 for Mac. + +released under the LGPL v3 + + +=============================================================================== + +This map loader can be used to load maps created in the Tiled map editor. It +provides a simple way to get tiles and associated metadata so that you can draw +a map onto the screen. + +This is *not* a rendering engine. It will load the data that is necessary to +render a map onto the screen. All tiles will be loaded into in memory and +available to blit onto the screen. + + +Design Goals: + Simple api + Memory efficient and fast + +Features: + Loads data and "properties" metadata from Tile's TMX format + "Properties" for: maps, tilesets, layers, objectgroups, objects, and tiles + Point data for polygon and polyline objects + Automatic flipping and rotation of tiles + Supports base64, csv, gzip, zlib and uncompressed XML + Image loading with pygame + + +=============================================================================== + +Please see tmxloader.py's docstring for version information and sample usage. +Check tests/test.py and tests/demo.py for examples on how to use the library. + + +=============================================================================== + +The 16x16 overworld tiles were created by MrBeast at opengameart.org. CC-BY 3.0 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/lgpl-3.0.txt Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/16x16-overworld.tsx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<tileset name="16x16-overworld" tilewidth="16" tileheight="16"> + <image source="16x16-overworld.png" width="256" height="336"/> + <tile id="17"> + <properties> + <property name="name" value="grass"/> + </properties> + </tile> + <tile id="275"> + <properties> + <property name="name" value="castle-door"/> + </properties> + </tile> +</tileset>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/demo.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,237 @@ +""" +This is tested on pygame 1.9 and python 2.7. +This will not work on python 3. Don't ask either. I will say 'no'. +bitcraft (leif dot theden at gmail.com) + +Rendering demo for the TMXLoader. This simply shows that the loader works. +If you need a rendering library that will handle large maps and scrolling, you +can check out my lib2d project at pygame.org. Have fun! + + +Known bugs: + Tile Objects are not handled by any renderer. + + +""" + +class TiledRenderer(object): + """ + Super simple way to render a tiled map + """ + + def __init__(self, filename): + from pytmx import tmxloader + self.tiledmap = tmxloader.load_pygame(filename, pixelalpha=True) + + + def render(self, surface): + # not going for effeciency here + # for demonstration purposes only + + tw = self.tiledmap.tilewidth + th = self.tiledmap.tileheight + gt = self.tiledmap.getTileImage + + for l in xrange(0, len(self.tiledmap.tilelayers)): + for y in xrange(0, self.tiledmap.height): + for x in xrange(0, self.tiledmap.width): + tile = gt(x, y, l) + if tile: surface.blit(tile, (x*tw, y*th)) + + +class ScrollingRenderer(TiledRenderer): + """ + Simple way for rendering a scrolling map that is larger than the display. + + !!!SUPER IMPORTANT NOTE - YOU MUST READ!!! + + THIS IS NOT THE CORRECT WAY TO DO SCROLLING GAMES IN PYGAME! THE + UNDERLYING LIBRARY, SDL, IS NOT SUITED FOR REAL-TIME SCROLLING GAMES. THIS + IS ONLY A DEMONSTRATION ON SCROLLING A MAP USING THIS LIBRARY AND IS IN NO + WAY IMPLIED TO BE THE BEST OR CORRECT WAY. + """ + + def __init__(self, filename): + super(ScrollingRenderer, self).__init__(filename) + self.width = self.tiledmap.width * self.tiledmap.tilewidth + self.height = self.tiledmap.height * self.tiledmap.tileheight + + self.mapwidth = self.tiledmap.width + self.mapheight = self.tiledmap.height + + self.halfwidth = self.tiledmap.width / 2 + self.halfheight = self.tiledmap.height / 2 + 1 + + + def render(self, surface, (cx, cy)): + sw, sh = surface.get_size() + tw = self.tiledmap.tilewidth + th = self.tiledmap.tileheight + gt = self.tiledmap.getTileImage + + stw = int(math.ceil(float(sw) / tw)) + 1 + sth = int(math.ceil(float(sh) / th)) + 1 + + txf, pxf = divmod((cx-sw/2), tw) + tyf, pyf = divmod((cy-sh/2), th) + + if stw + txf > self.mapwidth: stw -= 1 + if sth + tyf > self.mapheight: sth -= 1 + + p = product(xrange(stw), xrange(sth), + xrange(len(self.tiledmap.tilelayers))) + + for x, y, l in p: + tile = gt(x+txf, y+tyf, l) + if tile: surface.blit(tile, (x*tw-pxf, y*th-pyf)) + + + +import pygame +from pygame.locals import * +import math +from itertools import product + + +pygame.init() +pygame.font.init() +screen = pygame.display.set_mode((480, 480)) +pygame.display.set_caption('TMXLoader Test') + + +def simpleTest(filename): + screen_buf = pygame.Surface((240, 240)) + screen_buf.fill((0,128,255)) + formosa = TiledRenderer(filename) + formosa.render(screen_buf) + pygame.transform.scale(screen_buf, screen.get_size(), screen) + f = pygame.font.Font(pygame.font.get_default_font(), 20) + i = f.render("simple demo. press any key to continue", 1, (180,180,0)) + screen.blit(i, (0,0)) + pygame.display.flip() + + run = True + while run: + try: + event = pygame.event.wait() + if (event.type == QUIT) or (event.type == KEYDOWN): run = False + + except KeyboardInterrupt: + run = False + + +def scrollTest(filename): + buf_dim = [screen.get_width() / 2, screen.get_height() / 2] + center = [buf_dim[0]/2, buf_dim[1]/2] + movt = [0, 0, 0] + + clock = pygame.time.Clock() + screen_buf = pygame.Surface(buf_dim) + formosa = ScrollingRenderer(filename) + mw = formosa.tiledmap.width * formosa.tiledmap.tilewidth + mh = formosa.tiledmap.height * formosa.tiledmap.tileheight + + f = pygame.font.Font(pygame.font.get_default_font(), 20) + t = ["scroll demo. press escape to quit", + "arrow keys move", + "z and x will zoom the map"] + + text = [ f.render(i, 1, (180, 180, 0)) for i in t ] + + def draw(): + bw, bh = screen_buf.get_size() + sw, sh = screen.get_size() + + if (sw >= bw) and (sh >= bh): + y = 0 + screen_buf.fill((0,128,255)) + formosa.render(screen_buf, center) + pygame.transform.smoothscale(screen_buf, (sw, sh), screen) + for i in text: + screen.blit(i, (0,y)) + y += i.get_height() + else: + pass + + + draw() + run = True + while run: + try: + clock.tick(30) + event = pygame.event.poll() + + if event.type == QUIT: run = False + elif event.type == KEYDOWN: + if event.key == K_z: + movt[2] -= 2 + if event.key == K_x: + movt[2] += 2 + elif event.key == K_UP: + movt[1] -= 1 + elif event.key == K_DOWN: + movt[1] += 1 + elif event.key == K_LEFT: + movt[0] -= 1 + elif event.key == K_RIGHT: + movt[0] += 1 + elif event.key == K_ESCAPE: + run = False + + center[0] += movt[0] + center[1] += movt[1] + if not movt[2] == 0: + buf_dim[0] += movt[2] + buf_dim[1] += movt[2] + if (buf_dim[0] < 1) or (buf_dim[1] < 0): + buf_dim[0] += 1 - buf_dim[0] + buf_dim[1] += 1 - buf_dim[1] + if buf_dim[0] > screen.get_width() / 2: + buf_dim = [screen.get_width() / 2, screen.get_height() / 2] + movt[2] = 0 + screen_buf = pygame.Surface(buf_dim) + + sw, sh = screen_buf.get_size() + hsw = sw / 2 + hsh = sh / 2 + + if formosa.width > sw: + if center[0] < hsw: + center[0] = hsw + movt[0] = 0 + elif center[0] > mw - hsw-1: + center[0] = mw - hsw-1 + movt[0] = 0 + else: + center[0] = formosa.width / 2 + + if formosa.height > sh: + if center[1] < hsh: + center[1] = hsh + movt[1] = 0 + elif center[1] > mh - hsh - 1: + center[1] = mh - hsh - 1 + movt[1] = 0 + else: + center[1] = formosa.height / 2 + + draw() + pygame.display.flip() + + + except KeyboardInterrupt: + run = False + + +if __name__ == "__main__": + import sys + try: + filename = sys.argv[1] + except: + print "no TMX map specified, using default" + filename = "formosa-base64-gzip.tmx" + + simpleTest(filename) + scrollTest(filename) + + pygame.quit()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/desert.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="40" height="40" tilewidth="32" tileheight="32"> + <tileset firstgid="1" name="Desert" tilewidth="32" tileheight="32" spacing="1" margin="1"> + <image source="tmw_desert_spacing.png" width="265" height="199"/> + </tileset> + <layer name="Ground" width="40" height="40"> + <data encoding="base64" compression="gzip"> + H4sIAAAAAAAAA+2Y2QrCMBBFo31wA+sCrlirdd/q/3+dIzZUhtAmMRlH6cPBBlJyvDO2g5EQIvJIFwiBnudzbLgp/JYMvH4lv2dWI2AMTBj4qKgBdSBA3j7OmgIzA+ZAC2gDHYIsVsDagMSj36HAbwOcgO0X/YrySzTzpPDbMfS7Iy/sdxGv/r8S++Gew+uyvHBfuvBT/Q4OKD95XeaHc3WV3x6tpY9pfr7riz0xx6x+upwd+DUymiJ/FxURaO6TLD70C0U+X8jvWkRHc5/qPhu/scjniz4wKGGouU91n8/eoyYW6jmHk2dM6GMzm1F43bJPnWfn+/sncXS+Ljp+T6d7tpfSL2Xoh/uam59Nfd/nCyq/1MAPZ8ktP1WtOftR5mczm1H6/QsxA4dfAWfF7b/BqsYVFZ/xABSXR5cAGQAA + </data> + </layer> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/formosa-base64-gzip.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16"> + <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16"> + <image source="16x16-overworld.png" width="256" height="336"/> + <tile id="17"> + <properties> + <property name="name" value="grass"/> + </properties> + </tile> + <tile id="245"> + <properties> + <property name="type" value="GrassCave"/> + </properties> + </tile> + <tile id="261"> + <properties> + <property name="type" value="SandCave"/> + </properties> + </tile> + <tile id="275"> + <properties> + <property name="name" value="castle-door"/> + </properties> + </tile> + </tileset> + <layer name="Grass and Water" width="15" height="15"> + <data encoding="base64" compression="gzip"> + H4sIAAAAAAAAAxNiYGAQGsXDEmdToI8cvdlk6s0mUy+6PkJ6sakfDBgA3TcUToQDAAA= + </data> + </layer> + <layer name="Tile Layer 1" width="15" height="15"> + <data encoding="base64" compression="gzip"> + H4sIAAAAAAAAA92OSw5AMBRF3wuCMYIplsAG2Jg9o5i5kopPqtTQTU7S33m9RGbpQA8GuRdgBBOYH1yLiWzg8H7mYu0Bn/VzAtyHIDq4MdYJSPk8Ryj8gqm963Wco4rO3SLI/F9VGnSoJKWhW1761ybyxct/6K3vM4nKyzR86fc2Cx4LRKqEAwAA + </data> + </layer> + <layer name="Tile Layer 2" width="15" height="15"> + <data encoding="base64" compression="gzip"> + H4sIAAAAAAAAA2NgGAXEAi9GBgcgPkCOXiugXiuoXi1GhgQgOwFoVgIxeoHqHbSgeoF6FgD1LgDyF5DjDlIA0A4QboCySXIzUC0Ig/WS6magehBuIMvRQxAAAAvGQyuEAwAA + </data> + </layer> + <objectgroup name="Object Layer 1" width="15" height="15"> + <object name="Castle" x="192" y="16"> + <properties> + <property name="Population" value="234"/> + </properties> + <polygon points="0,0 0,32 48,32 48,0"/> + </object> + <object name="Towers Area" x="0" y="192"> + <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/> + </object> + <object name="Coastline" x="0" y="208"> + <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/> + </object> + <object name="GenericObject" x="96" y="64" width="32" height="32"/> + <object name="SandCave" gid="262" x="64" y="80"/> + <object name="Road" x="224" y="48"> + <properties> + <property name="MoveCost" value="2"/> + </properties> + <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/> + </object> + </objectgroup> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/formosa-base64-zlib.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16"> + <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16"> + <image source="16x16-overworld.png" width="256" height="336"/> + <tile id="17"> + <properties> + <property name="name" value="grass"/> + </properties> + </tile> + <tile id="245"> + <properties> + <property name="type" value="GrassCave"/> + </properties> + </tile> + <tile id="261"> + <properties> + <property name="type" value="SandCave"/> + </properties> + </tile> + <tile id="275"> + <properties> + <property name="name" value="castle-door"/> + </properties> + </tile> + </tileset> + <layer name="Grass and Water" width="15" height="15"> + <data encoding="base64" compression="zlib"> + eJwTYmBgEBrFwxJnU6CPHL3ZZOrNJlMvuj5CerGpHwwYAHdzIkA= + </data> + </layer> + <layer name="Tile Layer 1" width="15" height="15"> + <data encoding="base64" compression="zlib"> + eJzdjksOQDAURd8LgjGCKZbABtiYPaOYuZKKT6rU0E1O0t95vURm6UAPBrkXYAQTmB9ci4ls4PB+5mLtAZ/1cwLchyA6uDHWCUj5PEco/IKpvet1nKOKzt0iyPxfVRp0qCSloVte+tcm8sXLf+it7zOJyss0fOn3NgsYKxS4 + </data> + </layer> + <layer name="Tile Layer 2" width="15" height="15"> + <data encoding="base64" compression="zlib"> + eJxjYBgFxAIvRgYHID5Ajl4roF4rqF4tRoYEIDsBaFYCMXqB6h20oHqBehYA9S4A8heQ4w5SANAOEG6AsklyM1AtCIP1kupmoHoQbiDL0UMQAAAYyhAJ + </data> + </layer> + <objectgroup name="Object Layer 1" width="15" height="15"> + <object name="Castle" x="192" y="16"> + <properties> + <property name="Population" value="234"/> + </properties> + <polygon points="0,0 0,32 48,32 48,0"/> + </object> + <object name="Towers Area" x="0" y="192"> + <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/> + </object> + <object name="Coastline" x="0" y="208"> + <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/> + </object> + <object name="GenericObject" x="96" y="64" width="32" height="32"/> + <object name="SandCave" gid="262" x="64" y="80"/> + <object name="Road" x="224" y="48"> + <properties> + <property name="MoveCost" value="2"/> + </properties> + <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/> + </object> + </objectgroup> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/formosa-base64.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16"> + <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16"> + <image source="16x16-overworld.png" width="256" height="336"/> + <tile id="17"> + <properties> + <property name="name" value="grass"/> + </properties> + </tile> + <tile id="245"> + <properties> + <property name="type" value="GrassCave"/> + </properties> + </tile> + <tile id="261"> + <properties> + <property name="type" value="SandCave"/> + </properties> + </tile> + <tile id="275"> + <properties> + <property name="name" value="castle-door"/> + </properties> + </tile> + </tileset> + <layer name="Grass and Water" width="15" height="15"> + <data encoding="base64"> + EgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAAGsAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAawAAAGsAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAABrAAAAawAAAGsAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAAGsAAABrAAAAawAAAGsAAABrAAAAEgAAABIAAAASAAAAEgAAABIAAAASAAAAEgAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAAawAAAGsAAABrAAAA + </data> + </layer> + <layer name="Tile Layer 1" width="15" height="15"> + <data encoding="base64"> + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8wAAAPQAAAD1AAAAAAAAAPYAAAD3AAAA+AAAAPkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwEAAAQBAAAFAQAAAAAAAAAAAAAHAQAACAEAAAkBAAD3AAAA+AAAAPkAAAAAAAAAAAAAAAAAAAAAAAAAEwEAABQBAAAVAQAAAAAAAAAAAAAXAQAAGAEAABkBAAAHAQAACAEAAAkBAAD2AAAAAAAAAAAAAAAAAAAAAAAAACQBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXAQAAGAEAABkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPYAAAD2AAAAAAAAAAAAAAAAAAAAAAAAACQBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMBAAA1AQAANQEAADQBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQBAAAAAAAAAAAAAAAAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgAAACMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAiAAAAIgAAACMAAAAAAAAAAAAAAAAAAAAAAAAAIgAAACIAAAAiAAAAIgAAACIAAAAiAAAAIgAAACMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + </data> + </layer> + <layer name="Tile Layer 2" width="15" height="15"> + <data encoding="base64"> + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASgEAQEoBAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgEAQDoBAMAAAAAAKgEAYDoBAGBKAQBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgEAQCoBAMAAAAAASgEAoDoBAKAqAQCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgEAACoBAIAAAAAAKgEAYDoBAGBKAQBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgEAADoBAIAAAAAASgEAoDoBAKAqAQCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASgEAAEoBAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + </data> + </layer> + <objectgroup name="Object Layer 1" width="15" height="15"> + <object name="Castle" x="192" y="16"> + <properties> + <property name="Population" value="234"/> + </properties> + <polygon points="0,0 0,32 48,32 48,0"/> + </object> + <object name="Towers Area" x="0" y="192"> + <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/> + </object> + <object name="Coastline" x="0" y="208"> + <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/> + </object> + <object name="GenericObject" x="96" y="64" width="32" height="32"/> + <object name="SandCave" gid="262" x="64" y="80"/> + <object name="Road" x="224" y="48"> + <properties> + <property name="MoveCost" value="2"/> + </properties> + <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/> + </object> + </objectgroup> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/formosa-csv.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16"> + <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16"> + <image source="16x16-overworld.png" width="256" height="336"/> + <tile id="17"> + <properties> + <property name="name" value="grass"/> + </properties> + </tile> + <tile id="245"> + <properties> + <property name="type" value="GrassCave"/> + </properties> + </tile> + <tile id="261"> + <properties> + <property name="type" value="SandCave"/> + </properties> + </tile> + <tile id="275"> + <properties> + <property name="name" value="castle-door"/> + </properties> + </tile> + </tileset> + <layer name="Grass and Water" width="15" height="15"> + <data encoding="csv"> +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +18,18,18,18,18,18,18,18,18,18,18,18,18,18,107, +18,18,18,18,18,18,18,18,18,18,18,18,18,107,107, +18,18,18,18,18,18,18,18,18,18,18,18,107,107,107, +18,18,18,18,18,18,18,18,18,18,18,107,107,107,107, +18,18,18,18,18,18,18,18,18,18,107,107,107,107,107, +18,18,18,18,18,18,18,107,107,107,107,107,107,107,107, +107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, +107,107,107,107,107,107,107,107,107,107,107,107,107,107,107 +</data> + </layer> + <layer name="Tile Layer 1" width="15" height="15"> + <data encoding="csv"> +0,0,0,0,0,0,0,0,0,0,0,0,243,244,245, +0,246,247,248,249,0,0,0,0,0,0,0,259,260,261, +0,0,263,264,265,247,248,249,0,0,0,0,275,276,277, +0,0,279,280,281,263,264,265,246,0,0,0,0,2147483940,0, +0,0,0,0,0,279,280,281,0,0,0,0,0,2147483940,0, +0,0,0,0,0,0,0,246,246,0,0,0,0,2147483940,0, +0,0,0,0,0,0,0,0,0,0,323,309,309,2147483956,0, +0,0,0,0,0,0,0,0,0,0,308,0,0,0,66, +0,0,0,0,0,0,0,0,0,0,0,0,0,66,35, +0,0,0,0,0,0,0,0,0,0,0,0,66,35,0, +0,0,0,0,0,0,0,0,0,0,0,66,35,0,0, +0,0,0,0,0,0,0,0,0,0,66,35,0,0,0, +0,0,0,0,0,0,0,66,34,34,35,0,0,0,0, +34,34,34,34,34,34,34,35,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +</data> + </layer> + <layer name="Tile Layer 2" width="15" height="15"> + <data encoding="csv"> +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1073742154,3221225802,0,0,0,0,0,0,0,0,0,0,0,0,0, +1073742138,3221225786,0,1610613034,1610613050,1610613066,0,0,0,0,0,0,0,0,0, +1073742122,3221225770,0,2684354890,2684354874,2684354858,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +298,2147483946,0,1610613034,1610613050,1610613066,0,0,0,0,0,0,0,0,0, +314,2147483962,0,2684354890,2684354874,2684354858,0,0,0,0,0,0,0,0,0, +330,2147483978,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +</data> + </layer> + <objectgroup name="Object Layer 1" width="15" height="15"> + <object name="Castle" x="192" y="16"> + <properties> + <property name="Population" value="234"/> + </properties> + <polygon points="0,0 0,32 48,32 48,0"/> + </object> + <object name="Towers Area" x="0" y="192"> + <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/> + </object> + <object name="Coastline" x="0" y="208"> + <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/> + </object> + <object name="GenericObject" x="96" y="64" width="32" height="32"/> + <object name="SandCave" gid="262" x="64" y="80"/> + <object name="Road" x="224" y="48"> + <properties> + <property name="MoveCost" value="2"/> + </properties> + <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/> + </object> + </objectgroup> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/formosa-xml.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,735 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="15" height="15" tilewidth="16" tileheight="16"> + <tileset firstgid="1" name="16x16-overworld" tilewidth="16" tileheight="16"> + <image source="16x16-overworld.png" width="256" height="336"/> + <tile id="17"> + <properties> + <property name="name" value="grass"/> + </properties> + </tile> + <tile id="245"> + <properties> + <property name="type" value="GrassCave"/> + </properties> + </tile> + <tile id="261"> + <properties> + <property name="type" value="SandCave"/> + </properties> + </tile> + <tile id="275"> + <properties> + <property name="name" value="castle-door"/> + </properties> + </tile> + </tileset> + <layer name="Grass and Water" width="15" height="15"> + <data> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="107"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="18"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + <tile gid="107"/> + </data> + </layer> + <layer name="Tile Layer 1" width="15" height="15"> + <data> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="243"/> + <tile gid="244"/> + <tile gid="245"/> + <tile gid="0"/> + <tile gid="246"/> + <tile gid="247"/> + <tile gid="248"/> + <tile gid="249"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="259"/> + <tile gid="260"/> + <tile gid="261"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="263"/> + <tile gid="264"/> + <tile gid="265"/> + <tile gid="247"/> + <tile gid="248"/> + <tile gid="249"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="275"/> + <tile gid="276"/> + <tile gid="277"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="279"/> + <tile gid="280"/> + <tile gid="281"/> + <tile gid="263"/> + <tile gid="264"/> + <tile gid="265"/> + <tile gid="246"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="2147483940"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="279"/> + <tile gid="280"/> + <tile gid="281"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="2147483940"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="246"/> + <tile gid="246"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="2147483940"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="323"/> + <tile gid="309"/> + <tile gid="309"/> + <tile gid="2147483956"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="308"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="66"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="66"/> + <tile gid="35"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="66"/> + <tile gid="35"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="66"/> + <tile gid="35"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="66"/> + <tile gid="35"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="66"/> + <tile gid="34"/> + <tile gid="34"/> + <tile gid="35"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="34"/> + <tile gid="34"/> + <tile gid="34"/> + <tile gid="34"/> + <tile gid="34"/> + <tile gid="34"/> + <tile gid="34"/> + <tile gid="35"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + </data> + </layer> + <layer name="Tile Layer 2" width="15" height="15"> + <data> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="1073742154"/> + <tile gid="3221225802"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="1073742138"/> + <tile gid="3221225786"/> + <tile gid="0"/> + <tile gid="1610613034"/> + <tile gid="1610613050"/> + <tile gid="1610613066"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="1073742122"/> + <tile gid="3221225770"/> + <tile gid="0"/> + <tile gid="2684354890"/> + <tile gid="2684354874"/> + <tile gid="2684354858"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="298"/> + <tile gid="2147483946"/> + <tile gid="0"/> + <tile gid="1610613034"/> + <tile gid="1610613050"/> + <tile gid="1610613066"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="314"/> + <tile gid="2147483962"/> + <tile gid="0"/> + <tile gid="2684354890"/> + <tile gid="2684354874"/> + <tile gid="2684354858"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="330"/> + <tile gid="2147483978"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + <tile gid="0"/> + </data> + </layer> + <objectgroup name="Object Layer 1" width="15" height="15"> + <object name="Castle" x="192" y="16"> + <properties> + <property name="Population" value="234"/> + </properties> + <polygon points="0,0 0,32 48,32 48,0"/> + </object> + <object name="Towers Area" x="0" y="192"> + <polygon points="0,0 32,0 48,-16 96,-16 96,-96 48,-96 32,-112 0,-112"/> + </object> + <object name="Coastline" x="0" y="208"> + <polyline points="0,0 112,0 128,-16 160,-16 240,-96"/> + </object> + <object name="GenericObject" x="96" y="64" width="32" height="32"/> + <object name="SandCave" gid="262" x="64" y="80"/> + <object name="Road" x="224" y="48"> + <properties> + <property name="MoveCost" value="2"/> + </properties> + <polyline points="0,0 0,48 -64,48 -64,64 -48,64"/> + </object> + </objectgroup> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/frnknstn.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="10" height="10" tilewidth="32" tileheight="32"> + <tileset firstgid="1" name="tiles" tilewidth="32" tileheight="32"> + <image source="tiles.png" width="64" height="64"/> + <tile id="1"> + <properties> + <property name="color" value="blue"/> + </properties> + </tile> + </tileset> + <layer name="Tile Layer 1" width="10" height="10"> + <data encoding="base64" compression="zlib"> + eJyNkMEKADAIQh32/9+8HQqk2djhEWWYRABxYBJSla5Pfc1W0n1U+8H5l6a9y07MOZx/3+1ZaHyJO6+79fqVzjfbTAEP + </data> + </layer> + <objectgroup name="Object Layer 1" width="10" height="10"> + <object gid="2" x="96" y="128"/> + </objectgroup> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/pytmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,1 @@ +../pytmx \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/sewers.tmx Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" orientation="orthogonal" width="50" height="50" tilewidth="24" tileheight="24"> + <tileset firstgid="1" name="sewer_tileset" tilewidth="24" tileheight="24"> + <image source="sewer_tileset.png" trans="ff00ff" width="192" height="217"/> + </tileset> + <layer name="Bottom" width="50" height="50"> + <data encoding="base64" compression="zlib"> + eJzt19kKwjAQBdDim0sFqwguL3Vf/sP//ySnkIFhSGrSdEnxPhyQxqJ32kySPMuyJTkZC5KLa2dyNEo1Lsds3wkl/4f+7V/0/f+Y40Je5GpUn2+J5NiQCdmO/HlMyYzMI3JwLUJx7drIsSIFWUfk4FrY3GvGuHb8vr4jcmhNcnAtpIflmmar3VA5nqaWMUKeQ1d9dyht59iRPTmMPEdpua8Put/Frh88P5q84zFkv/NZP2TOlOaH7Hc+64fMmdL8cPVbV9+tcn5MzpTmR0gv12uDbQ4MNT8AIA73Uq3v3hqLe2ldTx4D21k1tf2ubw59Vk1tX+KbQ59VXfuSlMn9SJHV70tS5jqrYu8BAAAAAAAAAABd+wIHfQq1 + </data> + </layer> + <layer name="Top" width="50" height="50" opacity="0.49"> + <data encoding="base64" compression="zlib"> + eJzt1jsKgDAQQEELtVKvYuGvEDvvfya3MBeQQAzMwCPdsum2af5lL71AJv7xL7X+Y46WtzXayq7z2RGd0f2+V6a5bdRFfaZ5pQzRGE2lFwEAoArpDk7Veg+nOzjlHgYAAAAAIIcHvboDlQ== + </data> + </layer> +</map>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/002/pytmx_docs/tests/test.py Sun Jun 17 16:34:43 2012 +0200 @@ -0,0 +1,91 @@ +""" +This is tested on pygame 1.9 and python 2.7. +This will not work on python 3. Don't ask either. I will say 'no'. +bitcraft (leif dot theden at gmail.com) + +Rendering demo for the TMXLoader. This simply shows that the loader works. +If you need a rendering library that will handle large maps and scrolling, you +can check out my lib2d project at pygame.org. Have fun! +""" + +class TiledRenderer(object): + """ + Super simple way to render a tiled map + """ + + def __init__(self, filename): + from pytmx import tmxloader + self.tiledmap = tmxloader.load_pygame(filename, pixelalpha=True) + + + def render(self, surface): + # not going for effeciency here + # for demonstration purposes only + + tw = self.tiledmap.tilewidth + th = self.tiledmap.tileheight + gt = self.tiledmap.getTileImage + + for l in xrange(0, len(self.tiledmap.tilelayers)): + for y in xrange(0, self.tiledmap.height): + for x in xrange(0, self.tiledmap.width): + tile = gt(x, y, l) + if tile: surface.blit(tile, (x*tw, y*th)) + + for og in self.tiledmap.objectgroups: + for o in og: + if hasattr(o, 'points'): + points = [ (i[0]+o.x, i[1]+o.y) for i in o.points ] + pygame.draw.lines(surface, (255,128,128), o.closed, points, 2) + else: + pygame.draw.rect(surface, (255,128,128), + (o.x, o.y, o.width, o.height), 2) + +import pygame +from pygame.locals import * +import math, glob +from itertools import product + + +pygame.init() +pygame.font.init() +screen = pygame.display.set_mode((480, 480)) +pygame.display.set_caption('TMXLoader Test') + + +def simpleTest(filename): + screen_buf = pygame.Surface((240, 240)) + screen_buf.fill((0,128,255)) + formosa = TiledRenderer(filename) + formosa.render(screen_buf) + pygame.transform.scale(screen_buf, screen.get_size(), screen) + f = pygame.font.Font(pygame.font.get_default_font(), 20) + i = f.render(filename, 1, (180,180,0)) + screen.blit(i, (0,0)) + pygame.display.flip() + + print "Objects in map:" + for o in formosa.tiledmap.getObjects(): + print o + for k, v in o.__dict__.items(): + print " ", k, v + + print "GID (tile) properties:" + for k, v in formosa.tiledmap.tile_properties.items(): + print " ", k, v + + run = True + while run: + try: + event = pygame.event.wait() + if (event.type == QUIT) or (event.type == KEYDOWN): run = False + + except KeyboardInterrupt: + run = False + + +for filename in glob.glob('*.tmx'): + print "testing", filename + simpleTest(filename) + +pygame.quit()
