Cleanup from re-write merge

This commit is contained in:
Darren Eberly
2021-02-21 01:33:09 -05:00
parent 569686ad53
commit a0470a3d6e
19 changed files with 0 additions and 2692 deletions

View File

@@ -1,569 +0,0 @@
"""pytiled_parser objects for Tiled maps.
"""
# pylint: disable=too-few-public-methods
from pathlib import Path
from typing import Dict, List, NamedTuple, Optional, Union
import attr
# See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#data
TileLayerGrid = List[List[int]]
class Color(NamedTuple):
"""Color object.
Attributes:
red: Red, between 1 and 255.
green: Green, between 1 and 255.
blue: Blue, between 1 and 255.
alpha: Alpha, between 1 and 255.
"""
red: int
green: int
blue: int
alpha: int
class OrderedPair(NamedTuple):
"""OrderedPair NamedTuple.
Attributes:
x: X coordinate.
y: Y coordinate.
"""
x: Union[int, float]
y: Union[int, float]
class Property(NamedTuple):
"""Property NamedTuple.
Attributes:
name: Name of property
value: Value of property
"""
name: str
value: str
class Size(NamedTuple):
"""Size NamedTuple.
Attributes:
width: The width of the object.
size: The height of the object.
"""
width: Union[int, float]
height: Union[int, float]
@attr.s(auto_attribs=True)
class Template:
"""FIXME TODO"""
@attr.s(auto_attribs=True)
class Chunk:
"""Chunk object for infinite maps.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#chunk
Attributes:
location: Location of chunk in tiles.
width: The width of the chunk in tiles.
height: The height of the chunk in tiles.
layer_data: The global tile IDs in chunky according to row.
"""
location: OrderedPair
width: int
height: int
chunk_data: TileLayerGrid
@attr.s(auto_attribs=True)
class Image:
"""Image object.
pytiled_parser does not support embedded data in image elements at this time,
even though the TMX format technically does.
Attributes:
source: The reference to the tileset image file. Note that this is a relative
path compared to FIXME
trans: Defines a specific color that is treated as transparent.
width: The image width in pixels (optional, used for tile index correction when
the image changes).
height: The image height in pixels (optional).
"""
source: str
size: Optional[Size] = None
trans: Optional[str] = None
width: Optional[int] = None
height: Optional[int] = None
Properties = Dict[str, Union[int, float, Color, Path, str]]
class Grid(NamedTuple):
"""Contains info for isometric maps.
This element is only used in case of isometric orientation, and determines how tile
overlays for terrain and collision information are rendered.
Args:
orientation: Orientation of the grid for the tiles in this tileset (orthogonal
or isometric).
width: Width of a grid cell.
height: Height of a grid cell.
"""
orientation: str
width: int
height: int
class Terrain(NamedTuple):
"""Terrain object.
Args:
name: The name of the terrain type.
tile: The local tile-id of the tile that represents the terrain visually.
"""
name: str
tile: int
class Frame(NamedTuple):
"""Animation Frame object.
This is only used as a part of an animation for Tile objects.
Args:
tile_id: The local ID of a tile within the parent tile set object.
duration: How long in milliseconds this frame should be displayed before
advancing to the next frame.
"""
tile_id: int
duration: int
@attr.s(auto_attribs=True)
class TileTerrain:
"""Defines each corner of a tile by Terrain index in
'TileSet.terrain_types'.
Defaults to 'None'. 'None' means that corner has no terrain.
Attributes:
top_left: Top left terrain type.
top_right: Top right terrain type.
bottom_left: Bottom left terrain type.
bottom_right: Bottom right terrain type.
"""
top_left: Optional[int] = None
top_right: Optional[int] = None
bottom_left: Optional[int] = None
bottom_right: Optional[int] = None
@attr.s(auto_attribs=True, kw_only=True)
class Layer:
# FIXME:this docstring appears to be innacurate
"""Class that all layers inherit from.
Args:
id: Unique ID of the layer. Each layer that added to a map gets a unique id.
Even if a layer is deleted, no layer ever gets the same ID.
name: The name of the layer object.
tiled_objects: List of tiled_objects in the layer.
offset: Rendering offset of the layer object in pixels.
opacity: Decimal value between 0 and 1 to determine opacity. 1 is completely
opaque, 0 is completely transparent.
properties: Properties for the layer.
color: The color used to display the objects in this group.
draworder: Whether the objects are drawn according to the order of the object
elements in the object group element ('manual'), or sorted by their
y-coordinate ('topdown'). Defaults to 'topdown'.
See:
https://doc.mapeditor.org/en/stable/manual/objects/#changing-stacking-order
for more info.
"""
id_: int
name: str
offset: Optional[OrderedPair]
opacity: Optional[float]
properties: Optional[Properties]
LayerData = Union[TileLayerGrid, List[Chunk]]
"""The tile data for one layer.
Either a 2 dimensional array of integers representing the global tile IDs
for a TileLayerGrid, or a list of chunks for an infinite map layer.
"""
@attr.s(auto_attribs=True, kw_only=True)
class TileLayer(Layer):
"""Tile map layer containing tiles.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#layer
Args:
size: The width of the layer in tiles. The same as the map width unless map is
infitite.
data: Either an 2 dimensional array of integers representing the global tile
IDs for the map layer, or a list of chunks for an infinite map.
"""
size: Size
layer_data: LayerData
@attr.s(auto_attribs=True, kw_only=True)
class TiledObject:
"""TiledObject object.
See:
https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#object
Args:
id: Unique ID of the object. Each object that is placed on a map gets a unique
id. Even if an object was deleted, no object gets the same ID.
gid: Global tiled object ID.
location: The location of the object in pixels.
size: The width of the object in pixels (default: (0, 0)).
rotation: The rotation of the object in degrees clockwise (default: 0).
opacity: The opacity of the object. (default: 255)
name: The name of the object.
type: The type of the object.
properties: The properties of the TiledObject.
template: A reference to a Template object FIXME
"""
id_: int
gid: Optional[int] = None
location: OrderedPair
size: Optional[Size] = None
rotation: Optional[float] = None
opacity: Optional[float] = None
name: Optional[str] = None
type: Optional[str] = None
properties: Optional[Properties] = None
template: Optional[Template] = None
@attr.s()
class RectangleObject(TiledObject):
"""Rectangle shape defined by a point, width, and height.
See: https://doc.mapeditor.org/en/stable/manual/objects/#insert-rectangle
(objects in tiled are rectangles by default, so there is no specific
documentation on the tmx-map-format page for it.)
"""
@attr.s()
class ElipseObject(TiledObject):
"""Elipse shape defined by a point, width, and height.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#ellipse
"""
@attr.s()
class PointObject(TiledObject):
"""Point defined by a point (x,y).
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#point
"""
@attr.s(auto_attribs=True, kw_only=True)
class TileImageObject(TiledObject):
"""Polygon shape defined by a set of connections between points.
See: https://doc.mapeditor.org/en/stable/manual/objects/#insert-tile
Attributes:
gid: Refference to a global tile id.
"""
gid: int
@attr.s(auto_attribs=True, kw_only=True)
class PolygonObject(TiledObject):
"""Polygon shape defined by a set of connections between points.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#polygon
Attributes:
points: FIXME
"""
points: List[OrderedPair]
@attr.s(auto_attribs=True, kw_only=True)
class PolylineObject(TiledObject):
"""Polyline defined by a set of connections between points.
See:
https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#polyline
Attributes:
points: List of coordinates relative to the location of the object.
"""
points: List[OrderedPair]
@attr.s(auto_attribs=True, kw_only=True)
class TextObject(TiledObject):
"""Text object with associated settings.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#text
and https://doc.mapeditor.org/en/stable/manual/objects/#insert-text
Attributes:
font_family: The font family used (default: "sans-serif")
font_size: The size of the font in pixels. (default: 16)
wrap: Whether word wrapping is enabled. (default: False)
color: Color of the text. (default: #000000)
bold: Whether the font is bold. (default: False)
italic: Whether the font is italic. (default: False)
underline: Whether the text is underlined. (default: False)
strike_out: Whether the text is striked-out. (default: False)
kerning: Whether kerning should be used while rendering the text. (default:
False)
horizontal_align: Horizontal alignment of the text (default: "left")
vertical_align: Vertical alignment of the text (defalt: "top")
"""
text: str
font_family: str = "sans-serif"
font_size: int = 16
wrap: bool = False
color: str = "#000000"
bold: bool = False
italic: bool = False
underline: bool = False
strike_out: bool = False
kerning: bool = False
horizontal_align: str = "left"
vertical_align: str = "top"
@attr.s(auto_attribs=True, kw_only=True)
class ObjectLayer(Layer):
"""
TiledObject Group Object.
The object group is in fact a map layer, and is hence called "object layer" in
Tiled.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#objectgroup
Args:
tiled_objects: List of tiled_objects in the layer.
offset: Rendering offset of the layer object in pixels.
color: The color used to display the objects in this group. FIXME: editor only?
draworder: Whether the objects are drawn according to the order of the object
elements in the object group element ('manual'), or sorted by their
y-coordinate ('topdown'). Defaults to 'topdown'. See:
https://doc.mapeditor.org/en/stable/manual/objects/#changing-stacking-order
for more info.
"""
tiled_objects: List[TiledObject]
color: Optional[str] = None
draw_order: Optional[str] = "topdown"
@attr.s(auto_attribs=True, kw_only=True)
class LayerGroup(Layer):
"""Layer Group.
A LayerGroup can be thought of as a layer that contains layers
(potentially including other LayerGroups).
Offset and opacity recursively affect child layers.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#group
Attributes:
Layers: Layers in group.
"""
layers: Optional[List[Union["LayerGroup", Layer, ObjectLayer]]]
@attr.s(auto_attribs=True, kw_only=True)
class ImageLayer(Layer):
"""Image Layer.
An image layer displays a single image.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#imagelayer
Attributes:
image: the image to display for this layer.
"""
image: Image
@attr.s(auto_attribs=True)
class TileSet:
"""Object for storing a TSX with all associated collision data.
Args:
name: The name of this tileset.
max_tile_size: The maximum size of a tile in this tile set in pixels.
spacing: The spacing in pixels between the tiles in this tileset (applies to
the tileset image).
margin: The margin around the tiles in this tileset (applies to the tileset
image).
tile_count: The number of tiles in this tileset.
columns: The number of tile columns in the tileset. For image collection
tilesets it is editable and is used when displaying the tileset.
grid: Only used in case of isometric orientation, and determines how tile
overlays for terrain and collision information are rendered.
tileoffset: Used to specify an offset in pixels when drawing a tile from the
tileset. When not present, no offset is applied.
image: Used for spritesheet tile sets.
terrain_types: List of of terrain types which can be referenced from the
terrain attribute of the tile object. Ordered according to the terrain
element's appearance in the TSX file.
tiles: Dict of Tile objects by Tile.id.
tsx_file: Path of the file containing the tileset, None if loaded internally
from a map
parent_dir: Path of the parent directory of the file containing the tileset,
None if loaded internally from a map
"""
name: str
max_tile_size: Size
spacing: Optional[int] = None
margin: Optional[int] = None
tile_count: Optional[int] = None
columns: Optional[int] = None
tile_offset: Optional[OrderedPair] = None
grid: Optional[Grid] = None
properties: Optional[Properties] = None
image: Optional[Image] = None
terrain_types: Optional[List[Terrain]] = None
tiles: Optional[Dict[int, "Tile"]] = None
tsx_file: Path = None
parent_dir: Path = None
TileSetDict = Dict[int, TileSet]
@attr.s(auto_attribs=True, kw_only=True)
class Tile:
"""Individual tile object.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#tile
Args:
id: The local tile ID within its tileset.
type: The type of the tile. Refers to an object type and is used by tile
objects.
terrain: Defines the terrain type of each corner of the tile.
animation: Each tile can have exactly one animation associated with it.
"""
id_: int
type_: Optional[str] = None
terrain: Optional[TileTerrain] = None
animation: Optional[List[Frame]] = None
objectgroup: Optional[List[TiledObject]] = None
image: Optional[Image] = None
properties: Optional[List[Property]] = None
tileset: Optional[TileSet] = None
flipped_horizontally: bool = False
flipped_diagonally: bool = False
flipped_vertically: bool = False
@attr.s(auto_attribs=True)
class TileMap:
"""Object for storing a TMX with all associated layers and properties.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#map
Attributes:
parent_dir: The directory the TMX file is in. Used for finding relative paths
to TSX files and other assets.
version: The TMX format version.
tiledversion: The Tiled version used to save the file. May be a date (for
snapshot builds).
orientation: Map orientation. Tiled supports "orthogonal", "isometric",
"staggered" and "hexagonal"
renderorder: The order in which tiles on tile layers are rendered. Valid values
are right-down, right-up, left-down and left-up. In all cases, the map is
drawn row-by-row. (only supported for orthogonal maps at the moment)
map_size: The map width in tiles.
tile_size: The width of a tile.
infinite: If the map is infinite or not.
hexsidelength: Only for hexagonal maps. Determines the width or height
(depending on the staggered axis) of the tile's edge, in pixels.
stagger_axis: For staggered and hexagonal maps, determines which axis ("x" or
"y") is staggered.
staggerindex: For staggered and hexagonal maps, determines whether the "even"
or "odd" indexes along the staggered axis are shifted.
backgroundcolor: The background color of the map.
nextlayerid: Stores the next available ID for new layers.
nextobjectid: Stores the next available ID for new objects.
tile_sets: Dict of tile sets used in this map. Key is the first GID for the
tile set. The value is a TileSet object.
layers: List of layer objects by draw order.
"""
parent_dir: Path
tmx_file: Union[str, Path]
version: str
tiled_version: str
orientation: str
render_order: str
map_size: Size
tile_size: Size
infinite: bool
next_layer_id: Optional[int]
next_object_id: int
tile_sets: TileSetDict
layers: List[Layer]
hex_side_length: Optional[int] = None
stagger_axis: Optional[int] = None
stagger_index: Optional[int] = None
background_color: Optional[Color] = None
properties: Optional[Properties] = None

View File

@@ -1,6 +0,0 @@
def is_float(string: str):
try:
float(string)
return True
except (ValueError, TypeError):
return False

View File

@@ -1,69 +0,0 @@
"""Helper unitilies for pytiled_parser."""
from typing import List, Optional
import pytiled_parser.objects as objects
def parse_color(color: str) -> objects.Color:
"""Convert Tiled color format into Arcade's.
Args:
color (str): Tiled formatted color string.
Returns:
:Color: Color object in the format that Arcade understands.
"""
# the actual part we care about is always an even number
if len(color) % 2:
# strip initial '#' character
color = color[1:]
if len(color) == 6:
# full opacity if no alpha specified
alpha = 0xFF
red = int(color[0:2], 16)
green = int(color[2:4], 16)
blue = int(color[4:6], 16)
else:
alpha = int(color[0:2], 16)
red = int(color[2:4], 16)
green = int(color[4:6], 16)
blue = int(color[6:8], 16)
return objects.Color(red, green, blue, alpha)
def _get_tile_set_key(gid: int, tile_set_keys: List[int]) -> int:
"""Gets tile set key given a tile GID.
Args:
gid (int): Global ID of the tile.
tile_set_keys (List[int]): List of tile set keys.
Returns:
int: The key of the tile set that contains the tile for the GID.
"""
# credit to __m4ch1n3__ on ##learnpython for this idea
return max([key for key in tile_set_keys if key <= gid])
def get_tile_by_gid(gid: int, tile_sets: objects.TileSetDict) -> Optional[objects.Tile]:
"""Gets correct Tile for a given global ID.
Args:
tile_sets (objects.TileSetDict): TileSetDict from TileMap.
gid (int): Global tile ID of the tile to be returned.
Returns:
objects.Tile: The Tile object reffered to by the global tile ID.
None: If there is no objects.Tile object in the tile_set.tiles dict for the associated gid.
"""
tile_set_key = _get_tile_set_key(gid, list(tile_sets.keys()))
tile_set = tile_sets[tile_set_key]
if tile_set.tiles is not None:
return tile_set.tiles.get(gid - tile_set_key)
return None

View File

@@ -1,940 +0,0 @@
"""Handle XML parsing for TMX files"""
import base64
import functools
import gzip
import re
import xml.etree.ElementTree as etree
import zlib
from pathlib import Path
from typing import Callable, Dict, List, Optional, Tuple, Union
import pytiled_parser.objects as objects
import pytiled_parser.typing_helpers as TH
from pytiled_parser.utilities import parse_color
def _decode_base64_data(
data_text: str, layer_width: int, compression: Optional[str] = None
) -> objects.TileLayerGrid:
"""Decode base64 data.
Args:
data_text: Data to be decoded.
layer_width: Width of each layer in tiles.
compression: The type of compression for the data.
Raises:
ValueError: If compression type is unsupported.
Returns:
objects.TileLayerGrid: Tile grid.
"""
tile_grid: objects.TileLayerGrid = [[]]
unencoded_data = base64.b64decode(data_text)
if compression == "zlib":
unzipped_data = zlib.decompress(unencoded_data)
elif compression == "gzip":
unzipped_data = gzip.decompress(unencoded_data)
elif compression is None:
unzipped_data = unencoded_data
else:
raise ValueError(f"Unsupported compression type: '{compression}'.")
# Turn bytes into 4-byte integers
byte_count = 0
int_count = 0
int_value = 0
row_count = 0
for byte in unzipped_data:
int_value += byte << (byte_count * 8)
byte_count += 1
if not byte_count % 4:
byte_count = 0
int_count += 1
tile_grid[row_count].append(int_value)
int_value = 0
if not int_count % layer_width:
row_count += 1
tile_grid.append([])
tile_grid.pop()
return tile_grid
def _decode_csv_data(data_text: str) -> objects.TileLayerGrid:
"""Decodes csv encoded layer data.
Args:
data_text: Data to be decoded.
Returns:
objects.TileLayerGrid: Tile grid.
"""
tile_grid = []
lines: List[str] = data_text.split("\n")
# remove erroneous empty lists due to a newline being on both ends of text
lines = lines[1:-1]
for line in lines:
line_list = line.split(",")
# FIXME: what is this for?
while "" in line_list:
line_list.remove("")
line_list_int = [int(item) for item in line_list]
tile_grid.append(line_list_int)
return tile_grid
# I'm not sure if this is the best way to do this
TileLayerDecoder = Union[
Callable[[str], objects.TileLayerGrid],
Callable[[str, int, Optional[str]], objects.TileLayerGrid],
]
def _decode_tile_layer_data(
element: etree.Element,
layer_width: int,
encoding: str,
compression: Optional[str] = None,
) -> objects.TileLayerGrid:
"""Decodes tile layer data or chunk data.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#tmx-data
Args:
element: Element to have text decoded.
layer_width: Number of tiles per column in this layer. Used for determining
when to cut off a row when decoding base64 encoding layers.
encoding: Encoding format of the layer data.
compression: Compression format of the layer data.
Raises:
ValueError: Encoding type is not supported.
Returns:
objects.TileLayerGrid: Tile grid.
"""
data_text: str = element.text # type: ignore
if encoding == "csv":
return _decode_csv_data(data_text)
if encoding == "base64":
return _decode_base64_data(data_text, layer_width, compression)
raise ValueError(f"{encoding} is not a supported encoding")
def _parse_data(element: etree.Element, layer_width: int) -> objects.LayerData:
"""Parses layer data.
Will parse CSV, base64, gzip-base64, or zlip-base64 encoded data.
Args:
element: Data element to parse.
layer_width: Layer width. Used for base64 decoding.
Returns:
LayerData: Data object containing layer data or chunks of data.
"""
encoding = element.attrib["encoding"]
compression = None
try:
compression = element.attrib["compression"]
except KeyError:
pass
chunk_elements = element.findall("./chunk")
if chunk_elements:
chunks: List[objects.Chunk] = []
for chunk_element in chunk_elements:
x = int(chunk_element.attrib["x"])
y = int(chunk_element.attrib["y"])
location = objects.OrderedPair(x, y)
width = int(chunk_element.attrib["width"])
height = int(chunk_element.attrib["height"])
layer_data = _decode_tile_layer_data(
chunk_element, layer_width, encoding, compression
)
chunks.append(objects.Chunk(location, width, height, layer_data))
return chunks
return _decode_tile_layer_data(element, layer_width, encoding, compression)
def _parse_layer(
layer_element: etree.Element,
) -> Tuple[
int,
str,
Optional[objects.OrderedPair],
Optional[float],
Optional[objects.Properties],
]:
"""Parses all of the attributes for a Layer object.
Args:
layer_element: The layer element to be parsed.
Returns:
FIXME
"""
id_ = int(layer_element.attrib["id"])
name = layer_element.attrib["name"]
offset: Optional[objects.OrderedPair]
offset_x = layer_element.attrib.get("offsetx")
offset_y = layer_element.attrib.get("offsety")
if offset_x and offset_y:
assert TH.is_float(offset_x)
assert TH.is_float(offset_y)
offset = objects.OrderedPair(float(offset_x), float(offset_y))
else:
offset = None
opacity: Optional[float]
opacity_attrib = layer_element.attrib.get("opacity")
if opacity_attrib:
opacity = float(opacity_attrib)
else:
opacity = None
properties: Optional[objects.Properties]
properties_element = layer_element.find("./properties")
if properties_element is not None:
properties = _parse_properties_element(properties_element)
else:
properties = None
return id_, name, offset, opacity, properties
def _parse_tile_layer(element: etree.Element,) -> objects.TileLayer:
"""Parses tile layer element.
Args:
element: The layer element to be parsed.
Raises:
ValueError: Element has no child data element.
Returns:
TileLayer: The tile layer object.
"""
id_, name, offset, opacity, properties = _parse_layer(element)
width = int(element.attrib["width"])
height = int(element.attrib["height"])
size = objects.Size(width, height)
data_element = element.find("./data")
assert data_element is not None
layer_data: objects.LayerData = _parse_data(data_element, width)
return objects.TileLayer(
id_=id_,
name=name,
offset=offset,
opacity=opacity,
properties=properties,
size=size,
layer_data=layer_data,
)
def _parse_tiled_objects(
object_elements: List[etree.Element],
) -> List[objects.TiledObject]:
"""Parses objects found in a 'objectgroup' element.
Args:
object_elements: List of object elements to be parsed.
Returns:
list: List of parsed tiled objects.
"""
tiled_objects: List[objects.TiledObject] = []
for object_element in object_elements:
my_object = _parse_object(object_element)
if my_object is not None:
tiled_objects.append(my_object)
return tiled_objects
def _parse_object(obj):
my_id = obj.attrib["id"]
my_x = float(obj.attrib["x"])
my_y = float(obj.attrib["y"])
my_location = objects.OrderedPair(x=my_x, y=my_y)
if "width" in obj.attrib:
my_width = float(obj.attrib["width"])
else:
my_width = None
if "height" in obj.attrib:
my_height = float(obj.attrib["height"])
else:
my_height = None
my_name = obj.attrib["name"]
properties: Optional[objects.Properties]
properties_element = obj.find("./properties")
if properties_element is not None:
properties = _parse_properties_element(properties_element)
else:
properties = None
# This is where it would be nice if we could assume a walrus
# operator was part of our Python distribution.
my_object = None
polygon = obj.findall("./polygon")
if polygon and len(polygon) > 0:
points = _parse_points(polygon[0].attrib["points"])
my_object = objects.PolygonObject(
id_=my_id,
name=my_name,
location=my_location,
size=(my_width, my_height),
points=points,
properties=properties,
)
if my_object is None:
polyline = obj.findall("./polyline")
if polyline and len(polyline) > 0:
points = _parse_points(polyline[0].attrib["points"])
my_object = objects.PolylineObject(
id_=my_id,
name=my_name,
location=my_location,
size=(my_width, my_height),
points=points,
properties=properties,
)
if my_object is None:
ellipse = obj.findall("./ellipse")
if ellipse and len(ellipse):
my_object = objects.ElipseObject(
id_=my_id,
name=my_name,
location=my_location,
size=(my_width, my_height),
properties=properties,
)
if my_object is None:
if "template" in obj.attrib:
print(
"Warning, this .tmx file is using an unsupported"
"'template' attribute. Ignoring."
)
if my_object is None:
point = obj.findall("./point")
if point:
my_object = objects.PointObject(
id_=my_id,
name=my_name,
location=my_location,
properties=properties,
)
if my_object is None:
my_object = objects.RectangleObject(
id_=my_id,
name=my_name,
location=my_location,
size=(my_width, my_height),
properties=properties,
)
return my_object
def _parse_object_layer(element: etree.Element,) -> objects.ObjectLayer:
"""Parse the objectgroup element given.
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#objectgroup
Args:
element: Element to be parsed.
Returns:
ObjectLayer: The object layer object.
"""
id_, name, offset, opacity, properties = _parse_layer(element)
color = None
try:
color = element.attrib["color"]
except KeyError:
pass
draw_order = None
try:
draw_order = element.attrib["draworder"]
except KeyError:
pass
tiled_objects = _parse_tiled_objects(element.findall("./object"))
return objects.ObjectLayer(
id_=id_,
name=name,
offset=offset,
opacity=opacity,
properties=properties,
color=color,
draw_order=draw_order,
tiled_objects=tiled_objects,
)
def _parse_image_layer(element: etree.Element,) -> objects.ImageLayer:
"""Parse the imagelayer element given.
Args:
element: Element to be parsed.
Returns:
ImageLayer: The image layer object.
"""
id_, name, offset, opacity, properties = _parse_layer(element)
image = _parse_image_element(element.find("./image"))
return objects.ImageLayer(
id_=id_,
name=name,
offset=offset,
opacity=opacity,
properties=properties,
image=image,
)
def _parse_layer_group(element: etree.Element,) -> objects.LayerGroup:
"""Parse the objectgroup element given.
Args:
element: Element to be parsed.
Returns:
LayerGroup: The layer group object.
"""
id_, name, offset, opacity, properties = _parse_layer(element)
layers = _get_layers(element)
return objects.LayerGroup(
id_=id_,
name=name,
offset=offset,
opacity=opacity,
properties=properties,
layers=layers,
)
def _get_layer_parser(
layer_tag: str,
) -> Optional[Callable[[etree.Element], objects.Layer]]:
"""Gets a the parser for the layer type specified.
Layer tags are 'layer' for a tile layer, 'objectgroup' for an object layer, and
'group' for a layer group. If anything else is passed, returns None.
Args:
layer_tag: Specifies the layer type to be parsed based on the element tag.
Returns:
Callable: the function to be used to parse the layer.
None: The element is not a map layer.
"""
if layer_tag == "layer":
return _parse_tile_layer
if layer_tag == "objectgroup":
return _parse_object_layer
if layer_tag == "group":
return _parse_layer_group
if layer_tag == "imagelayer":
return _parse_image_layer
return None
def _get_layers(map_element: etree.Element) -> List[objects.Layer]:
"""Parse layer type element given.
Retains draw order based on the returned lists index FIXME: confirm
Args:
map_element: The element containing the layer.
Returns:
List[Layer]: A list of the layers, ordered by draw order. FIXME: confirm
"""
layers: List[objects.Layer] = []
for element in map_element.findall("./"):
layer_parser = _get_layer_parser(element.tag)
if layer_parser:
layers.append(layer_parser(element))
return layers
@functools.lru_cache()
def _parse_external_tile_set(
parent_dir: Path, tile_set_element: etree.Element
) -> objects.TileSet:
"""Parses an external tile set.
Caches the results to speed up subsequent maps with identical tilesets.
Args:
parent_dir: Directory that TMX is in.
tile_set_element: Tile set element.
Returns:
objects.Tileset: The tileset being parsed.
"""
source = Path(tile_set_element.attrib["source"])
resolved_path = parent_dir / source
tile_set_tree = etree.parse(str(parent_dir / Path(source))).getroot()
parsed_tile_set = _parse_tile_set(tile_set_tree)
parsed_tile_set.tsx_file = resolved_path
parsed_tile_set.parent_dir = resolved_path.parent
return parsed_tile_set
def _parse_points(point_string: str) -> List[objects.OrderedPair]:
str_pairs = point_string.split(" ")
points = []
for str_pair in str_pairs:
xys = str_pair.split(",")
x = float(xys[0])
y = float(xys[1])
points.append(objects.OrderedPair(x, y))
return points
def _parse_tiles(tile_element_list: List[etree.Element]) -> Dict[int, objects.Tile]:
"""Parse a list of tile elements.
Args:
tile_element_list: List of tile elements.
Returns:
Dict[int, objects.Tile]: Dictionary containing Tile objects by their ID.
"""
tiles: Dict[int, objects.Tile] = {}
for tile_element in tile_element_list:
# id is not optional
id_ = int(tile_element.attrib["id"])
# optional attributes
_type = None
try:
_type = tile_element.attrib["type"]
except KeyError:
pass
terrain = None
try:
tile_terrain_attrib = tile_element.attrib["terrain"]
except KeyError:
pass
else:
# below is an attempt to explain how terrains are handled.
# 'terrain' attribute is a comma seperated list of 4 values,
# each is either an integer or blank
# convert to list of values
terrain_list_attrib = re.split(",", tile_terrain_attrib)
# terrain_list is list of indexes of Tileset.terrain_types
terrain_list: List[Optional[int]] = []
# each index in terrain_list_attrib refers to a corner
for corner in terrain_list_attrib:
if not corner:
terrain_list.append(None)
else:
terrain_list.append(int(corner))
terrain = objects.TileTerrain(*terrain_list)
# tile element optional sub-elements
properties: Optional[List[objects.Property]] = None
tile_properties_element = tile_element.find("./properties")
if tile_properties_element:
properties = []
property_list = tile_properties_element.findall("./property")
for property_ in property_list:
name = property_.attrib["name"]
value = property_.attrib["value"]
obj = objects.Property(name, value)
properties.append(obj)
# tile element optional sub-elements
animation: Optional[List[objects.Frame]] = None
tile_animation_element = tile_element.find("./animation")
if tile_animation_element:
animation = []
frames = tile_animation_element.findall("./frame")
for frame in frames:
# tileid refers to the Tile.id of the animation frame
animated_id = int(frame.attrib["tileid"])
# duration is in MS. Should perhaps be converted to seconds.
# FIXME: make decision
duration = int(frame.attrib["duration"])
animation.append(objects.Frame(animated_id, duration))
# tile element optional sub-elements
objectgroup: Optional[List[objects.TiledObject]] = None
objectgroup_element = tile_element.find("./objectgroup")
if objectgroup_element:
objectgroup = []
object_list = objectgroup_element.findall("./object")
for obj in object_list:
my_object = _parse_object(obj)
if my_object is not None:
objectgroup.append(my_object)
# if this is None, then the Tile is part of a spritesheet
image = None
image_element = tile_element.find("./image")
if image_element is not None:
image = _parse_image_element(image_element)
# print(f"Adding '{id_}', {image}, {objectgroup}")
tiles[id_] = objects.Tile(
id_=id_,
type_=_type,
terrain=terrain,
animation=animation,
image=image,
properties=properties,
tileset=None,
objectgroup=objectgroup,
)
return tiles
def _parse_image_element(image_element: etree.Element) -> objects.Image:
"""Parse image element given.
Args:
image_element (etree.Element): Image element to be parsed.
Returns:
objects.Image: FIXME what is this?
"""
# FIXME doc
image = objects.Image(image_element.attrib["source"])
width_attrib = image_element.attrib.get("width")
height_attrib = image_element.attrib.get("height")
if width_attrib and height_attrib:
image.size = objects.Size(int(width_attrib), int(height_attrib))
try:
image.trans = image_element.attrib["trans"]
except KeyError:
pass
return image
def _parse_properties_element(properties_element: etree.Element) -> objects.Properties:
# FIXME: wtf is this pseudo 'attributes' section?
"""Adds Tiled property to Properties dict.
Each property element has a number of attributes:
name: Name of property.
property_type: Type of property. Can be string, int, float, bool, color or
file. Defaults to string.
value: The value of the property.
Args:
properties_element: Element to be parsed.
Returns:
objects.Properties: Dict of the property values by property name.
"""
properties: objects.Properties = {}
for property_element in properties_element.findall("./property"):
name = property_element.attrib["name"]
try:
property_type = property_element.attrib["type"]
except KeyError:
# strings do not have an attribute in property elements
property_type = "string"
value = property_element.attrib["value"]
property_types = ["string", "int", "float", "bool", "color", "file"]
assert property_type in property_types, f"Invalid type for property {name}"
if property_type == "int":
properties[name] = int(value)
elif property_type == "float":
properties[name] = float(value)
elif property_type == "color":
properties[name] = value
elif property_type == "file":
properties[name] = Path(value)
elif property_type == "bool":
if value == "true":
properties[name] = True
else:
properties[name] = False
else:
properties[name] = value
return properties
def _parse_tile_set(tile_set_element: etree.Element) -> objects.TileSet:
"""Parses a tile set that is embedded into a TMX.
Args:
tile_set_element: Element to be parsed.
Returns:
objects.TileSet: Tile Set from element.
"""
# get all basic attributes
name = tile_set_element.attrib["name"]
max_tile_width = int(tile_set_element.attrib["tilewidth"])
max_tile_height = int(tile_set_element.attrib["tileheight"])
max_tile_size = objects.Size(max_tile_width, max_tile_height)
spacing = None
try:
spacing = int(tile_set_element.attrib["spacing"])
except KeyError:
pass
margin = None
try:
margin = int(tile_set_element.attrib["margin"])
except KeyError:
pass
tile_count = None
try:
tile_count = int(tile_set_element.attrib["tilecount"])
except KeyError:
pass
columns = None
try:
columns = int(tile_set_element.attrib["columns"])
except KeyError:
pass
tile_offset = None
tileoffset_element = tile_set_element.find("./tileoffset")
if tileoffset_element is not None:
tile_offset_x = int(tileoffset_element.attrib["x"])
tile_offset_y = int(tileoffset_element.attrib["y"])
tile_offset = objects.OrderedPair(tile_offset_x, tile_offset_y)
grid = None
grid_element = tile_set_element.find("./grid")
if grid_element is not None:
grid_orientation = grid_element.attrib["orientation"]
grid_width = int(grid_element.attrib["width"])
grid_height = int(grid_element.attrib["height"])
grid = objects.Grid(grid_orientation, grid_width, grid_height)
properties = None
properties_element = tile_set_element.find("./properties")
if properties_element is not None:
properties = _parse_properties_element(properties_element)
terrain_types: Optional[List[objects.Terrain]] = None
terrain_types_element = tile_set_element.find("./terraintypes")
if terrain_types_element is not None:
terrain_types = []
for terrain in terrain_types_element.findall("./terrain"):
name = terrain.attrib["name"]
terrain_tile = int(terrain.attrib["tile"])
terrain_types.append(objects.Terrain(name, terrain_tile))
image = None
image_element = tile_set_element.find("./image")
if image_element is not None:
image = _parse_image_element(image_element)
tile_element_list = tile_set_element.findall("./tile")
tiles = _parse_tiles(tile_element_list)
tileset = objects.TileSet(
name,
max_tile_size,
spacing,
margin,
tile_count,
columns,
tile_offset,
grid,
properties,
image,
terrain_types,
tiles,
)
# Go back and create a circular link so tiles know what tileset they are
# part of. Needed for animation.
for id_, tile in tiles.items():
tile.tileset = tileset
return tileset
def _get_tile_sets(map_element: etree.Element, parent_dir: Path) -> objects.TileSetDict:
"""Get tile sets.
Args:
map_element: Element to be parsed.
parent_dir: Directory that TMX is in.
Returns:
objects.TileSetDict: Dict of tile sets in the TMX by first_gid
"""
# parse all tilesets
tile_sets: objects.TileSetDict = {}
tile_set_element_list = map_element.findall("./tileset")
for tile_set_element in tile_set_element_list:
# tiled docs are ambiguous about the 'firstgid' attribute
# current understanding is for the purposes of mapping the layer
# data to the tile set data, add the 'firstgid' value to each
# tile 'id'; this means that the 'firstgid' is specific to each,
# tile set as they pertain to the map, not tile set specific as
# the tiled docs can make it seem
# 'firstgid' the key for each TileMap
first_gid = int(tile_set_element.attrib["firstgid"])
try:
# check if is an external TSX
source = tile_set_element.attrib["source"]
except KeyError:
# the tile set is embedded
name = tile_set_element.attrib["name"]
tile_sets[first_gid] = _parse_tile_set(tile_set_element)
else:
# tile set is external
tile_sets[first_gid] = _parse_external_tile_set(
parent_dir, tile_set_element
)
return tile_sets
def parse_tile_map(tmx_file: Union[str, Path]) -> objects.TileMap:
"""Parse tile map.
Args:
tmx_file: TMX file to be parsed.
Returns:
objects.TileMap: TileMap object generated from the TMX file provided.
"""
# setting up XML parsing
map_tree = etree.parse(str(tmx_file))
map_element = map_tree.getroot()
# positional arguments for TileMap
parent_dir = Path(tmx_file).parent
version = map_element.attrib["version"]
tiled_version = map_element.attrib["tiledversion"]
orientation = map_element.attrib["orientation"]
render_order = map_element.attrib["renderorder"]
map_width = int(map_element.attrib["width"])
map_height = int(map_element.attrib["height"])
map_size = objects.Size(map_width, map_height)
tile_width = int(map_element.attrib["tilewidth"])
tile_height = int(map_element.attrib["tileheight"])
tile_size = objects.Size(tile_width, tile_height)
infinite_attribute = map_element.attrib["infinite"]
infinite = bool(infinite_attribute == "1")
if "nextlayerid" in map_element.attrib:
next_layer_id = int(map_element.attrib["nextlayerid"])
else:
next_layer_id = None
if "nextobjectid" in map_element.attrib:
next_object_id = int(map_element.attrib["nextobjectid"])
else:
next_object_id = None
tile_sets = _get_tile_sets(map_element, parent_dir)
layers = _get_layers(map_element)
tile_map = objects.TileMap(
parent_dir,
tmx_file,
version,
tiled_version,
orientation,
render_order,
map_size,
tile_size,
infinite,
next_layer_id,
next_object_id,
tile_sets,
layers,
)
try:
tile_map.hex_side_length = int(map_element.attrib["hexsidelength"])
except KeyError:
pass
try:
tile_map.stagger_axis = map_element.attrib["staggeraxis"]
except KeyError:
pass
try:
tile_map.stagger_index = map_element.attrib["staggerindex"]
except KeyError:
pass
try:
color = parse_color(map_element.attrib["backgroundcolor"])
tile_map.background_color = (color.red, color.green, color.blue)
except KeyError:
pass
properties_element = map_tree.find("./properties")
if properties_element is not None:
tile_map.properties = _parse_properties_element(properties_element)
return tile_map

View File

@@ -1,3 +0,0 @@
import pytest
pytest.main(["--tb=native", "-s", "tests"])

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="10" height="10" tilewidth="32" tileheight="32" infinite="0" nextlayerid="16" nextobjectid="10">
<tileset firstgid="1" source="tile_set_image.tsx"/>
<layer id="1" name="Tile Layer 1" width="10" height="10">
<data encoding="csv">
1,2,3,4,5,6,7,8,30,30,
9,10,11,12,13,14,15,16,30,30,
17,18,19,20,21,22,23,24,30,30,
25,26,27,28,29,30,31,32,30,30,
33,34,35,36,37,38,39,40,30,30,
41,42,43,44,45,46,47,48,30,30,
30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30
</data>
</layer>
<layer id="2" name="Tile Layer 2" width="10" height="10" opacity="0.5">
<data encoding="csv">
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,46,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,6,7,7,7,7,7,8,0,
0,0,14,15,15,15,15,15,16,0,
0,0,22,23,23,23,23,23,24,0
</data>
</layer>
<group id="3" name="Group 1">
<properties>
<property name="bool property" type="bool" value="true"/>
</properties>
<layer id="5" name="Tile Layer 4" width="10" height="10" offsetx="49" offsety="-50">
<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,0,0,0,0,0,
0,31,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0
</data>
</layer>
<layer id="4" name="Tile Layer 3" width="10" height="10">
<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,
1,2,3,0,0,0,0,0,0,0,
9,10,11,0,0,0,0,0,0,0,
17,18,19,0,0,0,0,0,0,0
</data>
</layer>
</group>
<objectgroup color="#000000" draworder="index" id="6" name="Object Layer 1" opacity="0.9" offsetx="4.66667" offsety="-4.33333">
<object id="1" name="rectangle 1" type="rectangle type" x="200.25" y="210.75" width="47.25" height="25" rotation="15"/>
<object id="2" name="polygon 1" type="polygon type" x="252.5" y="87.75" rotation="-21">
<polygon points="0,0 -41.25,24.25 -11,67.25 25.75,39.75 -9,37.75"/>
</object>
<object id="3" name="elipse 1" type="elipse type" x="198.75" y="102.5" width="17.75" height="14.25">
<ellipse/>
</object>
<object id="4" name="point 1" type="point type" x="174.25" y="186">
<point/>
</object>
<object id="7" name="insert text 1" type="insert text type" x="11.3958" y="48.5833" width="107.625" height="27.25">
<text fontfamily="Sans Serif" pixelsize="17" wrap="1" color="#b40303" italic="1" underline="1" strikeout="1">Hello World</text>
</object>
<object id="6" name="inserted tile 1" type="inserted tile type" gid="3221225503" x="47.25" y="72.5" width="47" height="53" rotation="31">
<properties>
<property name="tile property bool" type="bool" value="true"/>
</properties>
</object>
<object id="8" name="polyline 1" type="polyline type" x="144.667" y="112">
<polyline points="0,0 -14.3333,35.6667 15.3333,18.3333"/>
</object>
<object id="9" name="polygon 2" type="polygon type" x="69.8333" y="168.333">
<polygon points="-3.25,-17.25 -15,10.75 20.75,4.5"/>
</object>
</objectgroup>
</map>

View File

@@ -1,390 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="1" nextlayerid="3" nextobjectid="1">
<tileset firstgid="1" source="tile_set_image.tsx"/>
<layer id="1" name="Tile Layer 1" width="8" height="6">
<data encoding="csv">
<chunk x="-32" y="-32" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="-16" y="-32" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="0" y="-32" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="16" y="-32" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="-32" y="-16" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="-16" y="-16" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,1,2,3,
30,30,30,30,30,30,30,30,30,30,30,30,30,9,10,11,
30,30,30,30,30,30,30,30,30,30,30,30,30,17,18,19
</chunk>
<chunk x="0" y="-16" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
4,5,6,7,8,30,30,30,30,30,30,30,30,30,30,30,
12,13,14,15,16,30,30,30,30,30,30,30,30,30,30,30,
20,21,22,23,24,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="16" y="-16" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="-32" y="0" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="-16" y="0" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,25,26,27,
30,30,30,30,30,30,30,30,30,30,30,30,30,33,34,35,
30,30,30,30,30,30,30,30,30,30,30,30,30,41,42,43,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="0" y="0" width="16" height="16">
28,29,30,31,32,30,30,30,30,30,30,30,30,30,30,30,
36,37,38,39,40,30,30,30,30,30,30,30,30,30,30,30,
44,45,46,47,48,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="16" y="0" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="-32" y="16" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="-16" y="16" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="0" y="16" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
<chunk x="16" y="16" width="16" height="16">
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,
30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30
</chunk>
</data>
</layer>
<layer id="2" name="Tile Layer 2" width="8" height="6">
<data encoding="csv">
<chunk x="-32" y="-16" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,21,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,29,
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,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,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
</chunk>
<chunk x="16" y="-16" width="16" height="16">
0,0,20,21,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,28,29,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,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,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,0,0,0,0,0,0,0,0,0,0,0
</chunk>
<chunk x="-16" y="0" width="16" height="16">
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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
20,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk>
<chunk x="16" y="0" width="16" height="16">
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,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,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,
20,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
28,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</chunk>
<chunk x="-16" y="16" width="16" height="16">
28,29,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,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,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,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
</chunk>
</data>
</layer>
</map>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
<properties>
<property name="bool property - false" type="bool" value="false"/>
<property name="bool property - true" type="bool" value="true"/>
<property name="color property" type="color" value="#ff49fcff"/>
<property name="file property" type="file" value="/var/log/syslog"/>
<property name="float property" type="float" value="1.23456789"/>
<property name="int property" type="int" value="13"/>
<property name="string property" value="Hello, World!!"/>
</properties>
<tileset firstgid="1" source="tile_set_image.tsx"/>
<layer id="1" name="Tile Layer 1" width="8" height="6">
<data encoding="csv">
1,2,3,4,5,6,7,8,
9,10,11,12,13,14,15,16,
17,18,19,20,21,22,23,24,
25,26,27,28,29,30,31,32,
33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48
</data>
</layer>
</map>

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
<properties>
<property name="bool property - false" type="bool" value="false"/>
<property name="bool property - true" type="bool" value="true"/>
<property name="color property" type="color" value="#ff49fcff"/>
<property name="file property" type="file" value="../../../../../../../../var/log/syslog"/>
<property name="float property" type="float" value="1.23456789"/>
<property name="int property" type="int" value="13"/>
<property name="string property" value="Hello, World!!"/>
</properties>
<tileset firstgid="1" source="tile_set_image.tsx"/>
<layer id="1" name="Tile Layer 1" width="8" height="6">
<data encoding="base64" compression="gzip">
H4sIAAAAAAAAAw3DBRKCQAAAwDMRA7BQLMTE9v+vY3dmWyGEth279uwbOTB26MixExNTM6fOnLtwae7KtYUbt+7ce7D0aOXJsxev3rxb+/Dpy7cfv/782wAcvDirwAAAAA==
</data>
</layer>
</map>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
<tileset firstgid="1" source="tile_set_image_objects.tsx"/>
<layer id="1" name="Tile Layer 1" width="8" height="6">
<data encoding="csv">
1,2,3,4,5,6,7,8,
9,10,11,12,13,14,15,16,
17,18,19,20,21,22,23,24,
25,26,27,28,29,30,31,32,
33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48
</data>
</layer>
</map>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.2" tiledversion="1.3.1" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
<properties>
<property name="bool property - false" type="bool" value="false"/>
<property name="bool property - true" type="bool" value="true"/>
<property name="color property" type="color" value="#ff49fcff"/>
<property name="float property" type="float" value="1.23456789"/>
<property name="int property" type="int" value="13"/>
<property name="string property" value="Hello, World!!"/>
</properties>
<tileset firstgid="1" source="tile_set_image.tsx"/>
<layer id="1" name="Tile Layer 1" width="8" height="6" offsetx="16" offsety="-16.42">
<data encoding="csv">
1,2,3,4,5,6,7,8,
9,10,11,12,13,14,15,16,
17,18,19,20,21,22,23,24,
25,26,27,28,29,30,31,32,
33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48
</data>
</layer>
</map>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="1.2.3" name="tile_set_image" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
<image source="images/tmw_desert_spacing.png" width="265" height="199"/>
</tileset>

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.2" tiledversion="1.2.3" name="tile_set_image" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
<image source="images/tmw_desert_spacing.png" width="265" height="199"/>
<tile id="9">
<objectgroup draworder="index">
<object id="2" name="wall" type="rectangle type" x="1" y="1" width="32" height="32" rotation="1"/>
</objectgroup>
</tile>
<tile id="19">
<objectgroup draworder="index">
<object id="1" name="wall corner" type="polygon type" x="32" y="1" rotation="1">
<polygon points="0,0 -32,0 -32,32 -16,32.1818 -15.8182,16.9091 0.181818,17.0909"/>
</object>
</objectgroup>
</tile>
<tile id="20">
<objectgroup draworder="index">
<object id="1" name="polyline" type="polyline type" x="1.45455" y="1.45455" rotation="1">
<polyline points="0,0 25.0909,21.2727 9.63636,28.3636"/>
</object>
</objectgroup>
</tile>
<tile id="31">
<objectgroup draworder="index">
<object id="1" name="rock 1" type="elipse type" x="5.09091" y="2.54545" width="19.6364" height="19.2727" rotation="1">
<ellipse/>
</object>
<object id="2" name="rock 2" type="elipse type" x="16.1818" y="22" width="8.54545" height="8.36364" rotation="-1">
<ellipse/>
</object>
</objectgroup>
</tile>
<tile id="45">
<objectgroup draworder="index">
<object id="1" name="sign" type="point type" x="14.7273" y="26.3636">
<point/>
</object>
</objectgroup>
</tile>
</tileset>

File diff suppressed because one or more lines are too long

View File

@@ -1,223 +0,0 @@
"""Unit tests for pytiled_parser"""
import xml.etree.ElementTree as etree
from contextlib import ExitStack as does_not_raise
import pytest
from pytiled_parser import objects, utilities, xml_parser
LAYER_DATA = [
(
'<layer id="1" name="Tile Layer 1" width="10" height="10">' + "</layer>",
(int(1), "Tile Layer 1", None, None, None),
),
(
'<layer id="2" name="Tile Layer 2" width="10" height="10" opacity="0.5">'
+ "</layer>",
(int(2), "Tile Layer 2", None, float(0.5), None),
),
(
'<layer id="5" name="Tile Layer 4" width="10" height="10" offsetx="49" offsety="-50">'
+ "<properties>"
+ "</properties>"
+ "</layer>",
(int(5), "Tile Layer 4", objects.OrderedPair(49, -50), None, "properties",),
),
]
@pytest.mark.parametrize("xml,expected", LAYER_DATA)
def test_parse_layer(xml, expected, monkeypatch):
def mockreturn(*args):
return "properties"
monkeypatch.setattr(xml_parser, "_parse_properties_element", mockreturn)
result = xml_parser._parse_layer(etree.fromstring(xml))
assert result == expected
@pytest.mark.parametrize(
"test_input,expected",
[
("#001122", (0x00, 0x11, 0x22, 0xFF)),
("001122", (0x00, 0x11, 0x22, 0xFF)),
("#FF001122", (0x00, 0x11, 0x22, 0xFF)),
("FF001122", (0x00, 0x11, 0x22, 0xFF)),
("FF001122", (0x00, 0x11, 0x22, 0xFF)),
],
)
def test_color_parsing(test_input, expected):
assert utilities.parse_color(test_input) == expected
layer_data = [
(
etree.fromstring(
"<data>\n1,2,3,4,5,6,7,8,\n"
"9,10,11,12,13,14,15,16,\n"
"17,18,19,20,21,22,23,24,\n"
"25,26,27,28,29,30,31,32,\n"
"33,34,35,36,37,38,39,40,\n"
"41,42,43,44,45,46,47,48\n</data>"
),
8,
"csv",
None,
[
[1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
],
does_not_raise(),
),
(
etree.fromstring("<data>\n0,0,0,0,0\n</data>"),
5,
"csv",
None,
[[0, 0, 0, 0, 0]],
does_not_raise(),
),
(
etree.fromstring(
"<data>AQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABcAAAAYAAAAGQAAABoAAAAbAAAAHAAAAB0AAAAeAAAAHwAAACAAAAAhAAAAIgAAACMAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8AAAAwAAAA</data>"
),
8,
"base64",
None,
[
[1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
],
does_not_raise(),
),
(
etree.fromstring(
"<data>eJwNwwUSgkAAAMAzEQOwUCzExPb/r2N3ZlshhLYdu/bsGzkwdujIsRMTUzOnzpy7cGnuyrWFG7fu3Huw9GjlybMXr968W/vw6cu3H7/+/NsAMw8EmQ==</data>"
),
8,
"base64",
"zlib",
[
[1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
],
does_not_raise(),
),
(
etree.fromstring(
"<data>H4sIAAAAAAAAAw3DBRKCQAAAwDMRA7BQLMTE9v+vY3dmWyGEth279uwbOTB26MixExNTM6fOnLtwae7KtYUbt+7ce7D0aOXJsxev3rxb+/Dpy7cfv/782wAcvDirwAAAAA==</data>"
),
8,
"base64",
"gzip",
[
[1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
],
does_not_raise(),
),
(
etree.fromstring("<data>SGVsbG8gV29ybGQh</data>"),
8,
"base64",
"lzma",
[
[1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
],
pytest.raises(ValueError),
),
(
etree.fromstring(
"<data>/ .---- --..-- ..--- --..-- ...-- --..-- ....- --..-- ..... --..-- -.... --..-- --... --..-- ---.. --..-- / ----. --..-- .---- ----- --..-- .---- .---- --..-- .---- ..--- --..-- .---- ...-- --..-- .---- ....- --..-- .---- ..... --..-- .---- -.... --..-- / .---- --... --..-- .---- ---.. --..-- .---- ----. --..-- ..--- ----- --..-- ..--- .---- --..-- ..--- ..--- --..-- ..--- ...-- --..-- ..--- ....- --..-- / ..--- ..... --..-- ..--- -.... --..-- ..--- --... --..-- ..--- ---.. --..-- ..--- ----. --..-- ...-- ----- --..-- ...-- .---- --..-- ...-- ..--- --..-- / ...-- ...-- --..-- ...-- ....- --..-- ...-- ..... --..-- ...-- -.... --..-- ...-- --... --..-- ...-- ---.. --..-- ...-- ----. --..-- ....- ----- --..-- / ....- .---- --..-- ....- ..--- --..-- ....- ...-- --..-- ....- ....- --..-- ....- ..... --..-- ....- -.... --..-- ....- --... --..-- ....- ---..</data>"
),
8,
"morse",
None,
[
[1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
],
pytest.raises(ValueError),
),
]
@pytest.mark.parametrize(
"layer_data,width,encoding,compression,expected,raises", layer_data
)
def test_decode_layer_data(layer_data, width, encoding, compression, expected, raises):
with raises:
assert (
xml_parser._decode_tile_layer_data(layer_data, width, encoding, compression)
== expected
)
# FIXME: use hypothesis for this
def create_tile_set(qty_of_tiles):
""" Create tile set of specific size.
"""
tile_set = objects.TileSet(None, None)
if qty_of_tiles == 0:
return tile_set
tiles = {}
for tile_id in range(qty_of_tiles):
tiles[tile_id] = objects.Tile(id_=tile_id)
tile_set.tiles = tiles
return tile_set
tile_by_gid = [
(1, {1: create_tile_set(0)}, None),
(1, {1: create_tile_set(1)}, objects.Tile(id_=0)),
(1, {1: create_tile_set(2)}, objects.Tile(id_=0)),
(2, {1: create_tile_set(1)}, None),
(10, {1: create_tile_set(10)}, objects.Tile(id_=9)),
(1, {1: create_tile_set(1), 2: create_tile_set(1)}, objects.Tile(id_=0)),
(2, {1: create_tile_set(1), 2: create_tile_set(1)}, objects.Tile(id_=0)),
(3, {1: create_tile_set(1), 2: create_tile_set(1)}, None),
(15, {1: create_tile_set(5), 6: create_tile_set(10)}, objects.Tile(id_=9)),
(
20,
{1: create_tile_set(5), 6: create_tile_set(10), 16: create_tile_set(10),},
objects.Tile(id_=4),
),
]
@pytest.mark.parametrize("gid,tile_sets,expected", tile_by_gid)
def test_get_tile_by_gid(gid, tile_sets, expected):
assert utilities.get_tile_by_gid(gid, tile_sets) == expected

View File

@@ -1,98 +0,0 @@
import os
from pathlib import Path
import pytest
import pytiled_parser
TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
TEST_DATA = TESTS_DIR / "test_data"
def test_map_simple():
"""
TMX with a very simple spritesheet tile set and some properties.
"""
test_map = pytiled_parser.parse_tile_map(TEST_DATA / "test_map_simple.tmx")
# map
# unsure how to get paths to compare propperly
assert test_map.parent_dir == TEST_DATA
assert test_map.version == "1.2"
assert test_map.tiled_version == "1.2.3"
assert test_map.orientation == "orthogonal"
assert test_map.render_order == "right-down"
assert test_map.map_size == (8, 6)
assert test_map.tile_size == (32, 32)
assert test_map.infinite == False
assert test_map.next_layer_id == 2
assert test_map.next_object_id == 1
# optional, not for orthogonal maps
assert test_map.hex_side_length == None
assert test_map.stagger_axis == None
assert test_map.stagger_index == None
assert test_map.background_color == None
assert test_map.properties == {
"bool property - false": False,
"bool property - true": True,
"color property": "#ff49fcff",
"file property": Path("/var/log/syslog"),
"float property": 1.23456789,
"int property": 13,
"string property": "Hello, World!!",
}
# tileset
assert test_map.tile_sets[1].name == "tile_set_image"
assert test_map.tile_sets[1].max_tile_size == (32, 32)
assert test_map.tile_sets[1].spacing == 1
assert test_map.tile_sets[1].margin == 1
assert test_map.tile_sets[1].tile_count == 48
assert test_map.tile_sets[1].columns == 8
assert test_map.tile_sets[1].tile_offset == None
assert test_map.tile_sets[1].grid == None
assert test_map.tile_sets[1].properties == None
# unsure how to get paths to compare propperly
assert str(test_map.tile_sets[1].image.source) == ("images/tmw_desert_spacing.png")
assert test_map.tile_sets[1].image.trans == None
assert test_map.tile_sets[1].image.size == (265, 199)
assert test_map.tile_sets[1].terrain_types == None
assert test_map.tile_sets[1].tiles == {}
# layers
assert test_map.layers[0].layer_data == [
[1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
]
assert test_map.layers[0].id_ == 1
assert test_map.layers[0].name == "Tile Layer 1"
assert test_map.layers[0].offset == None
assert test_map.layers[0].opacity == None
assert test_map.layers[0].properties == None
assert test_map.layers[0].size == (8, 6)
@pytest.mark.parametrize(
"test_input,expected",
[
("#001122", (0x00, 0x11, 0x22, 0xFF)),
("001122", (0x00, 0x11, 0x22, 0xFF)),
("#FF001122", (0x00, 0x11, 0x22, 0xFF)),
("FF001122", (0x00, 0x11, 0x22, 0xFF)),
("FF001122", (0x00, 0x11, 0x22, 0xFF)),
],
)
def test_color_parsing(test_input, expected):
"""
Tiled has a few different types of color representations.
"""
assert pytiled_parser.utilities.parse_color(test_input) == expected

View File

@@ -1,81 +0,0 @@
"""test test_map_simple_offset.tmx"""
import os
from pathlib import Path
import pytiled_parser
TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
TEST_DATA = TESTS_DIR / "test_data"
def test_map_simple():
"""
TMX with a very simple spritesheet tile set and some properties.
"""
test_map = pytiled_parser.parse_tile_map(TEST_DATA / "test_map_simple_offset.tmx")
# map
# unsure how to get paths to compare propperly
assert test_map.parent_dir == TEST_DATA
assert test_map.version == "1.2"
assert test_map.tiled_version == "1.3.1"
assert test_map.orientation == "orthogonal"
assert test_map.render_order == "right-down"
assert test_map.map_size == (8, 6)
assert test_map.tile_size == (32, 32)
assert test_map.infinite == False
assert test_map.next_layer_id == 2
assert test_map.next_object_id == 1
# optional, not for orthogonal maps
assert test_map.hex_side_length == None
assert test_map.stagger_axis == None
assert test_map.stagger_index == None
assert test_map.background_color == None
assert test_map.properties == {
"bool property - false": False,
"bool property - true": True,
"color property": "#ff49fcff",
"float property": 1.23456789,
"int property": 13,
"string property": "Hello, World!!",
}
# tileset
assert test_map.tile_sets[1].name == "tile_set_image"
assert test_map.tile_sets[1].max_tile_size == (32, 32)
assert test_map.tile_sets[1].spacing == 1
assert test_map.tile_sets[1].margin == 1
assert test_map.tile_sets[1].tile_count == 48
assert test_map.tile_sets[1].columns == 8
assert test_map.tile_sets[1].tile_offset == None
assert test_map.tile_sets[1].grid == None
assert test_map.tile_sets[1].properties == None
# unsure how to get paths to compare propperly
assert str(test_map.tile_sets[1].image.source) == ("images/tmw_desert_spacing.png")
assert test_map.tile_sets[1].image.trans == None
assert test_map.tile_sets[1].image.size == (265, 199)
assert test_map.tile_sets[1].terrain_types == None
assert test_map.tile_sets[1].tiles == {}
# layers
assert test_map.layers[0].layer_data == [
[1, 2, 3, 4, 5, 6, 7, 8],
[9, 10, 11, 12, 13, 14, 15, 16],
[17, 18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48],
]
assert test_map.layers[0].id_ == 1
assert test_map.layers[0].name == "Tile Layer 1"
assert test_map.layers[0].offset == pytiled_parser.objects.OrderedPair(
x=16.0, y=-16.42
)
assert test_map.layers[0].opacity == None
assert test_map.layers[0].properties == None
assert test_map.layers[0].size == (8, 6)

View File

@@ -1,19 +0,0 @@
"""Tests for typing helpers"""
import pytest
from pytiled_parser import typing_helpers as TH
TEST_IS_FLOAT_PARAMS = [
(1, True),
("1", True),
(1.1, True),
("1.1", True),
("one", False),
(None, False),
]
@pytest.mark.parametrize("string,expected", TEST_IS_FLOAT_PARAMS)
def test_is_float(string, expected):
assert TH.is_float(string) == expected