all repos — Legends-RPG @ 1a3befbd2ae5f0125f78787694d02e2fa31b0db4

A fantasy mini-RPG built with Python and Pygame.

data/pytmx/tmxloader.py (view raw)

  1import itertools
  2import os
  3
  4import pygame
  5
  6from data.pytmx import pytmx
  7from .constants import *
  8
  9
 10__all__ = ['load_pygame', 'load_tmx']
 11
 12
 13def handle_transformation(tile, flags):
 14    if flags:
 15        fx = flags & TRANS_FLIPX == TRANS_FLIPX
 16        fy = flags & TRANS_FLIPY == TRANS_FLIPY
 17        r = flags & TRANS_ROT == TRANS_ROT
 18
 19        if r:
 20            # not sure why the flip is required...but it is.
 21            newtile = pygame.transform.rotate(tile, 270)
 22            newtile = pygame.transform.flip(newtile, 1, 0)
 23
 24            if fx or fy:
 25                newtile = pygame.transform.flip(newtile, fx, fy)
 26
 27        elif fx or fy:
 28            newtile = pygame.transform.flip(tile, fx, fy)
 29
 30        return newtile
 31
 32    else:
 33        return tile
 34
 35
 36def smart_convert(original, colorkey, force_colorkey, pixelalpha):
 37    """
 38    this method does several tests on a surface to determine the optimal
 39    flags and pixel format for each tile surface.
 40
 41    this is done for the best rendering speeds and removes the need to
 42    convert() the images on your own
 43    """
 44    tile_size = original.get_size()
 45
 46    # count the number of pixels in the tile that are not transparent
 47    px = pygame.mask.from_surface(original).count()
 48
 49    # there are no transparent pixels in the image
 50    if px == tile_size[0] * tile_size[1]:
 51        tile = original.convert()
 52
 53    # there are transparent pixels, and set to force a colorkey
 54    elif force_colorkey:
 55        tile = pygame.Surface(tile_size)
 56        tile.fill(force_colorkey)
 57        tile.blit(original, (0, 0))
 58        tile.set_colorkey(force_colorkey, pygame.RLEACCEL)
 59
 60    # there are transparent pixels, and tiled set a colorkey
 61    elif colorkey:
 62        tile = original.convert()
 63        tile.set_colorkey(colorkey, pygame.RLEACCEL)
 64
 65    # there are transparent pixels, and set for perpixel alpha
 66    elif pixelalpha:
 67        tile = original.convert_alpha()
 68
 69    # there are transparent pixels, and we won't handle them
 70    else:
 71        tile = original.convert()
 72
 73    return tile
 74
 75
 76def _load_images_pygame(tmxdata, mapping, *args, **kwargs):
 77    """
 78    Utility function to load images.
 79
 80
 81    due to the way the tiles are loaded, they will be in the same pixel format
 82    as the display when it is loaded.  take this into consideration if you
 83    intend to support different screen pixel formats.
 84
 85    by default, the images will not have per-pixel alphas.  this can be
 86    changed by including "pixelalpha=True" in the keywords.  this will result
 87    in much slower blitting speeds.
 88
 89    if the tileset's image has colorkey transparency set in Tiled, the loader
 90    will return images that have their transparency already set.  using a
 91    tileset with colorkey transparency will greatly increase the speed of
 92    rendering the map.
 93
 94    optionally, you can force the loader to strip the alpha channel of the
 95    tileset image and to fill in the missing areas with a color, then use that
 96    new color as a colorkey.  the resulting tiles will render much faster, but
 97    will not preserve the transparency of the tile if it uses partial
 98    transparency (which you shouldn't be doing anyway, this is SDL).
 99
100    TL;DR:
101    Don't attempt to convert() or convert_alpha() the individual tiles.  It is
102    already done for you.
103    """
104
105    pixelalpha = kwargs.get("pixelalpha", False)
106    force_colorkey = kwargs.get("force_colorkey", False)
107
108    if force_colorkey:
109        pixelalpha = True
110
111    if force_colorkey:
112        try:
113            force_colorkey = pygame.Color(*force_colorkey)
114        except:
115            msg = 'Cannot understand color: {0}'
116            print msg.format(force_colorkey)
117            raise ValueError
118
119    # change background color into something nice
120    if tmxdata.background_color:
121        tmxdata.background_color = pygame.Color(tmxdata.background_color)
122
123    # initialize the array of images
124    tmxdata.images = [0] * tmxdata.maxgid
125
126    for ts in tmxdata.tilesets:
127        path = os.path.join(os.path.dirname(tmxdata.filename), ts.source)
128        image = pygame.image.load(path)
129        w, h = image.get_size()
130
131        # margins and spacing
132        tilewidth = ts.tilewidth + ts.spacing
133        tileheight = ts.tileheight + ts.spacing
134        tile_size = ts.tilewidth, ts.tileheight
135
136        # some tileset images may be slightly larger than the tile area
137        # ie: may include a banner, copyright, ect.  this compensates for that
138        width = int((((w - ts.margin * 2 + ts.spacing) / tilewidth) * tilewidth) - ts.spacing)
139        height = int((((h - ts.margin * 2 + ts.spacing) / tileheight) * tileheight) - ts.spacing)
140
141        # trim off any pixels on the right side that isn't a tile
142        # this happens if extra graphics are included on the left, but they are not actually part of the tileset
143        width -= (w - ts.margin) % tilewidth
144
145        # using product avoids the overhead of nested loops
146        p = itertools.product(xrange(ts.margin, height + ts.margin, tileheight),
147                              xrange(ts.margin, width + ts.margin, tilewidth))
148
149        colorkey = getattr(ts, 'trans', None)
150        if colorkey:
151            colorkey = pygame.Color('#{0}'.format(colorkey))
152
153        for real_gid, (y, x) in enumerate(p, ts.firstgid):
154            if x + ts.tilewidth-ts.spacing > width:
155                continue
156
157            gids = tmxdata.map_gid(real_gid)
158
159            if gids:
160                original = image.subsurface(((x, y), tile_size))
161
162                for gid, flags in gids:
163                    tile = handle_transformation(original, flags)
164                    tile = smart_convert(tile, colorkey, force_colorkey, pixelalpha)
165                    tmxdata.images[gid] = tile
166
167    # load image layer images
168    for layer in tmxdata.all_layers:
169        if isinstance(layer, pytmx.TiledImageLayer):
170            colorkey = getattr(layer, 'trans', None)
171            if colorkey:
172                colorkey = pygame.Color("#{0}".format(colorkey))
173
174            source = getattr(layer, 'source', None)
175            if source:
176                real_gid = len(tmxdata.images)
177                gid = tmxdata.register_gid(real_gid)
178                layer.gid = gid
179                path = os.path.join(os.path.dirname(tmxdata.filename), source)
180                image = pygame.image.load(path)
181                image = smart_convert(image, colorkey, force_colorkey, pixelalpha)
182                tmxdata.images.append(image)
183
184
185def load_pygame(filename, *args, **kwargs):
186    """
187    PYGAME USERS: Use me.
188
189    Load a TMX file, load the images, and return a TiledMap class that is ready to use.
190    """
191    tmxdata = pytmx.TiledMap(filename)
192    _load_images_pygame(tmxdata, None, *args, **kwargs)
193    return tmxdata
194
195
196load_tmx = pytmx.TiledMap