map.py 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. # coding: utf-8
  2. import typing
  3. import tmx
  4. from synergine2_xyz.exceptions import ImproperlyConfiguredMap
  5. class XYZTile(object):
  6. def __init__(self, tile: tmx.Tile) -> None:
  7. self.tile = tile
  8. def property(self, name: str) -> typing.Union[str, int, float]:
  9. for property in self.tile.properties:
  10. if property.name == name:
  11. return property.value
  12. raise ImproperlyConfiguredMap('Tile with id "{}" don\'t contains "{}" property'.format(
  13. self.tile.id,
  14. name,
  15. ))
  16. class UnconstructedTile(XYZTile):
  17. pass
  18. class TMXMap(object):
  19. xyz_tile_class = XYZTile
  20. transform_zero_y_to_bottom = True
  21. """
  22. Define default tileset name for layer. key: layer name, value: tileset name
  23. """
  24. default_layer_tilesets = {} # type: typing.Dict[str, str]
  25. def __init__(self, map_file_path: str) -> None:
  26. self.tmx_map = tmx.TileMap.load(map_file_path)
  27. self.tmx_layers = {} # type: typing.Dict[str, tmx.Layer]
  28. self.tmx_layer_tiles = {} # type: typing.Dict[str, typing.Dict[str, XYZTile]]
  29. self.tmx_tilesets = {} # type: typing.Dict[str, tmx.Tileset]
  30. self.tmx_tiles = {} # type: typing.Dict[int, XYZTile]
  31. self._load()
  32. @property
  33. def height(self) -> int:
  34. return self.tmx_map.height
  35. @property
  36. def width(self) -> int:
  37. return self.tmx_map.width
  38. def get_default_tileset(self) -> tmx.Tileset:
  39. return list(self.tmx_tilesets.values())[0]
  40. def get_default_tile(self, layer_name: str=None) -> XYZTile:
  41. """
  42. Return thirst tile of default tileset or thirst tile of given layer default tileset.
  43. :param layer: if layer given, thirst tile of it's default tileset will be returned
  44. :return: a Tile
  45. """
  46. if layer_name and layer_name in self.default_layer_tilesets:
  47. tileset_name = self.default_layer_tilesets[layer_name]
  48. tileset = self.tileset(tileset_name)
  49. else:
  50. tileset = self.get_default_tileset()
  51. return self.xyz_tile_class(tileset.tiles[0])
  52. def _load(self) -> None:
  53. self._load_layers()
  54. self._load_tilesets()
  55. self._load_tiles()
  56. def _load_layers(self) -> None:
  57. for layer in self.tmx_map.layers:
  58. self.tmx_layers[layer.name] = layer
  59. def _load_tilesets(self) -> None:
  60. for tileset in self.tmx_map.tilesets:
  61. self.tmx_tilesets[tileset.name] = tileset
  62. for tile in tileset.tiles:
  63. self.tmx_tiles[tileset.firstgid + tile.id] = self.xyz_tile_class(tile)
  64. def _load_tiles(self) -> None:
  65. for layer_name, layer in self.tmx_layers.items():
  66. x = -1
  67. y = 0 if not self.transform_zero_y_to_bottom else self.height - 1
  68. y_modifier = 1 if not self.transform_zero_y_to_bottom else -1
  69. self.tmx_layer_tiles[layer_name] = {}
  70. # no tiles
  71. if not isinstance(layer, tmx.Layer):
  72. continue
  73. for layer_tile in layer.tiles:
  74. x += 1
  75. if x == self.width:
  76. x = 0
  77. y += y_modifier
  78. position_key = '{}.{}'.format(x, y)
  79. if not layer_tile.gid:
  80. tile = self.get_default_tile(layer_name=layer_name)
  81. elif not layer_tile.gid in self.tmx_tiles:
  82. # tile = UnconstructedTile()
  83. tile = self.get_default_tile(layer_name=layer_name)
  84. else:
  85. tile = self.tmx_tiles[layer_tile.gid]
  86. self.tmx_layer_tiles[layer_name][position_key] = tile
  87. def layer(self, name: str) -> tmx.Layer:
  88. return self.tmx_layers[name]
  89. def tileset(self, name: str) -> tmx.Tileset:
  90. return self.tmx_tilesets[name]
  91. def tile(self, gid: int, allow_default_tile: bool=False, default_tile: tmx.LayerTile=None) -> XYZTile:
  92. try:
  93. return self.tmx_tiles[gid]
  94. except KeyError:
  95. if default_tile:
  96. return default_tile
  97. if allow_default_tile:
  98. return self.get_default_tile()
  99. raise
  100. def layer_tiles(self, name: str) -> typing.Dict[str, XYZTile]:
  101. return self.tmx_layer_tiles[name]