mirror of
https://github.com/OMGeeky/pytiled_parser.git
synced 2025-12-26 17:02:28 +01:00
@@ -12,8 +12,10 @@ PyTiled Parser is not tied to any particular graphics library or game engine.
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
from .common_types import OrderedPair, Size
|
||||
from .exception import UnknownFormat
|
||||
from .layer import ImageLayer, Layer, LayerGroup, ObjectLayer, TileLayer
|
||||
from .parser import parse_map
|
||||
from .properties import Properties
|
||||
from .tiled_map import TiledMap, parse_map
|
||||
from .tiled_map import TiledMap
|
||||
from .tileset import Tile, Tileset
|
||||
from .version import __version__
|
||||
|
||||
2
pytiled_parser/exception.py
Normal file
2
pytiled_parser/exception.py
Normal file
@@ -0,0 +1,2 @@
|
||||
class UnknownFormat(Exception):
|
||||
pass
|
||||
@@ -8,27 +8,14 @@ See:
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
import base64
|
||||
import gzip
|
||||
import importlib.util
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
from typing import Any, List, Optional, Union
|
||||
from typing import cast as type_cast
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import attr
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from . import properties as properties_
|
||||
from . import tiled_object
|
||||
from .common_types import Color, OrderedPair, Size
|
||||
from .util import parse_color
|
||||
|
||||
zstd_spec = importlib.util.find_spec("zstd")
|
||||
if zstd_spec:
|
||||
import zstd # pylint: disable=import-outside-toplevel
|
||||
else:
|
||||
zstd = None # pylint: disable=invalid-name
|
||||
from pytiled_parser.common_types import Color, OrderedPair, Size
|
||||
from pytiled_parser.properties import Properties
|
||||
from pytiled_parser.tiled_object import TiledObject
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
@@ -51,8 +38,8 @@ class Layer:
|
||||
"""
|
||||
|
||||
name: str
|
||||
opacity: float
|
||||
visible: bool
|
||||
opacity: float = 1
|
||||
visible: bool = True
|
||||
|
||||
coordinates: OrderedPair = OrderedPair(0, 0)
|
||||
parallax_factor: OrderedPair = OrderedPair(1, 1)
|
||||
@@ -60,7 +47,7 @@ class Layer:
|
||||
|
||||
id: Optional[int] = None
|
||||
size: Optional[Size] = None
|
||||
properties: Optional[properties_.Properties] = None
|
||||
properties: Optional[Properties] = None
|
||||
tint_color: Optional[Color] = None
|
||||
|
||||
|
||||
@@ -127,7 +114,7 @@ class ObjectLayer(Layer):
|
||||
for more info.
|
||||
"""
|
||||
|
||||
tiled_objects: List[tiled_object.TiledObject]
|
||||
tiled_objects: List[TiledObject]
|
||||
|
||||
draw_order: Optional[str] = "topdown"
|
||||
|
||||
@@ -162,341 +149,3 @@ class LayerGroup(Layer):
|
||||
"""
|
||||
|
||||
layers: Optional[List[Layer]]
|
||||
|
||||
|
||||
class RawChunk(TypedDict):
|
||||
"""The keys and their types that appear in a Chunk JSON Object.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/json-map-format/#chunk
|
||||
"""
|
||||
|
||||
data: Union[List[int], str]
|
||||
height: int
|
||||
width: int
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
class RawLayer(TypedDict):
|
||||
"""The keys and their types that appear in a Layer JSON Object.
|
||||
|
||||
See: https://doc.mapeditor.org/en/stable/reference/json-map-format/#layer
|
||||
"""
|
||||
|
||||
chunks: List[RawChunk]
|
||||
compression: str
|
||||
data: Union[List[int], str]
|
||||
draworder: str
|
||||
encoding: str
|
||||
height: int
|
||||
id: int
|
||||
image: str
|
||||
layers: List[Any]
|
||||
name: str
|
||||
objects: List[tiled_object.RawTiledObject]
|
||||
offsetx: float
|
||||
offsety: float
|
||||
parallaxx: float
|
||||
parallaxy: float
|
||||
opacity: float
|
||||
properties: List[properties_.RawProperty]
|
||||
startx: int
|
||||
starty: int
|
||||
tintcolor: str
|
||||
transparentcolor: str
|
||||
type: str
|
||||
visible: bool
|
||||
width: int
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
def _convert_raw_tile_layer_data(data: List[int], layer_width: int) -> List[List[int]]:
|
||||
"""Convert raw layer data into a nested lit based on the layer width
|
||||
|
||||
Args:
|
||||
data: The data to convert
|
||||
layer_width: Width of the layer
|
||||
|
||||
Returns:
|
||||
List[List[int]]: A nested list containing the converted data
|
||||
"""
|
||||
tile_grid: List[List[int]] = [[]]
|
||||
|
||||
column_count = 0
|
||||
row_count = 0
|
||||
for item in data:
|
||||
column_count += 1
|
||||
tile_grid[row_count].append(item)
|
||||
if not column_count % layer_width and column_count < len(data):
|
||||
row_count += 1
|
||||
tile_grid.append([])
|
||||
|
||||
return tile_grid
|
||||
|
||||
|
||||
def _decode_tile_layer_data(
|
||||
data: str, compression: str, layer_width: int
|
||||
) -> List[List[int]]:
|
||||
"""Decode Base64 Encoded tile data. Optionally supports gzip and zlib compression.
|
||||
|
||||
Args:
|
||||
data: The base64 encoded data
|
||||
compression: Either zlib, gzip, or empty. If empty no decompression is done.
|
||||
|
||||
Returns:
|
||||
List[List[int]]: A nested list containing the decoded data
|
||||
|
||||
Raises:
|
||||
ValueError: For an unsupported compression type.
|
||||
"""
|
||||
unencoded_data = base64.b64decode(data)
|
||||
if compression == "zlib":
|
||||
unzipped_data = zlib.decompress(unencoded_data)
|
||||
elif compression == "gzip":
|
||||
unzipped_data = gzip.decompress(unencoded_data)
|
||||
elif compression == "zstd" and zstd is None:
|
||||
raise ValueError(
|
||||
"zstd compression support is not installed."
|
||||
"To install use 'pip install pytiled-parser[zstd]'"
|
||||
)
|
||||
elif compression == "zstd":
|
||||
unzipped_data = zstd.decompress(unencoded_data)
|
||||
else:
|
||||
unzipped_data = unencoded_data
|
||||
|
||||
tile_grid: List[int] = []
|
||||
|
||||
byte_count = 0
|
||||
int_count = 0
|
||||
int_value = 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.append(int_value)
|
||||
int_value = 0
|
||||
|
||||
return _convert_raw_tile_layer_data(tile_grid, layer_width)
|
||||
|
||||
|
||||
def _cast_chunk(
|
||||
raw_chunk: RawChunk,
|
||||
encoding: Optional[str] = None,
|
||||
compression: Optional[str] = None,
|
||||
) -> Chunk:
|
||||
"""Cast the raw_chunk to a Chunk.
|
||||
|
||||
Args:
|
||||
raw_chunk: RawChunk to be casted to a Chunk
|
||||
encoding: Encoding type. ("base64" or None)
|
||||
compression: Either zlib, gzip, or empty. If empty no decompression is done.
|
||||
|
||||
Returns:
|
||||
Chunk: The Chunk created from the raw_chunk
|
||||
"""
|
||||
if encoding == "base64":
|
||||
assert isinstance(compression, str)
|
||||
assert isinstance(raw_chunk["data"], str)
|
||||
data = _decode_tile_layer_data(
|
||||
raw_chunk["data"], compression, raw_chunk["width"]
|
||||
)
|
||||
else:
|
||||
data = _convert_raw_tile_layer_data(
|
||||
raw_chunk["data"], raw_chunk["width"] # type: ignore
|
||||
)
|
||||
|
||||
chunk = Chunk(
|
||||
coordinates=OrderedPair(raw_chunk["x"], raw_chunk["y"]),
|
||||
size=Size(raw_chunk["width"], raw_chunk["height"]),
|
||||
data=data,
|
||||
)
|
||||
|
||||
return chunk
|
||||
|
||||
|
||||
def _get_common_attributes(raw_layer: RawLayer) -> Layer:
|
||||
"""Create a Layer containing all the attributes common to all layers.
|
||||
|
||||
This is to create the stub Layer object that can then be used to create the actual
|
||||
specific sub-classes of Layer.
|
||||
|
||||
Args:
|
||||
raw_layer: Raw Tiled object get common attributes from
|
||||
|
||||
Returns:
|
||||
Layer: The attributes in common of all layers
|
||||
"""
|
||||
common_attributes = Layer(
|
||||
name=raw_layer["name"],
|
||||
opacity=raw_layer["opacity"],
|
||||
visible=raw_layer["visible"],
|
||||
)
|
||||
|
||||
# if startx is present, starty is present
|
||||
if raw_layer.get("startx") is not None:
|
||||
common_attributes.coordinates = OrderedPair(
|
||||
raw_layer["startx"], raw_layer["starty"]
|
||||
)
|
||||
|
||||
if raw_layer.get("id") is not None:
|
||||
common_attributes.id = raw_layer["id"]
|
||||
|
||||
# if either width or height is present, they both are
|
||||
if raw_layer.get("width") is not None:
|
||||
common_attributes.size = Size(raw_layer["width"], raw_layer["height"])
|
||||
|
||||
if raw_layer.get("offsetx") is not None:
|
||||
common_attributes.offset = OrderedPair(
|
||||
raw_layer["offsetx"], raw_layer["offsety"]
|
||||
)
|
||||
|
||||
if raw_layer.get("properties") is not None:
|
||||
common_attributes.properties = properties_.cast(raw_layer["properties"])
|
||||
|
||||
parallax = [1.0, 1.0]
|
||||
|
||||
if raw_layer.get("parallaxx") is not None:
|
||||
parallax[0] = raw_layer["parallaxx"]
|
||||
|
||||
if raw_layer.get("parallaxy") is not None:
|
||||
parallax[1] = raw_layer["parallaxy"]
|
||||
|
||||
common_attributes.parallax_factor = OrderedPair(parallax[0], parallax[1])
|
||||
|
||||
if raw_layer.get("tintcolor") is not None:
|
||||
common_attributes.tint_color = parse_color(raw_layer["tintcolor"])
|
||||
|
||||
return common_attributes
|
||||
|
||||
|
||||
def _cast_tile_layer(raw_layer: RawLayer) -> TileLayer:
|
||||
"""Cast the raw_layer to a TileLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: RawLayer to be casted to a TileLayer
|
||||
|
||||
Returns:
|
||||
TileLayer: The TileLayer created from raw_layer
|
||||
"""
|
||||
tile_layer = TileLayer(**_get_common_attributes(raw_layer).__dict__)
|
||||
|
||||
if raw_layer.get("chunks") is not None:
|
||||
tile_layer.chunks = []
|
||||
for chunk in raw_layer["chunks"]:
|
||||
if raw_layer.get("encoding") is not None:
|
||||
tile_layer.chunks.append(
|
||||
_cast_chunk(chunk, raw_layer["encoding"], raw_layer["compression"])
|
||||
)
|
||||
else:
|
||||
tile_layer.chunks.append(_cast_chunk(chunk))
|
||||
|
||||
if raw_layer.get("data") is not None:
|
||||
if raw_layer.get("encoding") is not None:
|
||||
tile_layer.data = _decode_tile_layer_data(
|
||||
data=type_cast(str, raw_layer["data"]),
|
||||
compression=raw_layer["compression"],
|
||||
layer_width=raw_layer["width"],
|
||||
)
|
||||
else:
|
||||
tile_layer.data = _convert_raw_tile_layer_data(
|
||||
raw_layer["data"], raw_layer["width"] # type: ignore
|
||||
)
|
||||
|
||||
return tile_layer
|
||||
|
||||
|
||||
def _cast_object_layer(
|
||||
raw_layer: RawLayer,
|
||||
parent_dir: Optional[Path] = None,
|
||||
) -> ObjectLayer:
|
||||
"""Cast the raw_layer to an ObjectLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: RawLayer to be casted to an ObjectLayer
|
||||
Returns:
|
||||
ObjectLayer: The ObjectLayer created from raw_layer
|
||||
"""
|
||||
|
||||
tiled_objects = []
|
||||
for tiled_object_ in raw_layer["objects"]:
|
||||
tiled_objects.append(tiled_object.cast(tiled_object_, parent_dir))
|
||||
|
||||
return ObjectLayer(
|
||||
tiled_objects=tiled_objects,
|
||||
draw_order=raw_layer["draworder"],
|
||||
**_get_common_attributes(raw_layer).__dict__,
|
||||
)
|
||||
|
||||
|
||||
def _cast_image_layer(raw_layer: RawLayer) -> ImageLayer:
|
||||
"""Cast the raw_layer to a ImageLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: RawLayer to be casted to a ImageLayer
|
||||
|
||||
Returns:
|
||||
ImageLayer: The ImageLayer created from raw_layer
|
||||
"""
|
||||
image_layer = ImageLayer(
|
||||
image=Path(raw_layer["image"]), **_get_common_attributes(raw_layer).__dict__
|
||||
)
|
||||
|
||||
if raw_layer.get("transparentcolor") is not None:
|
||||
image_layer.transparent_color = parse_color(raw_layer["transparentcolor"])
|
||||
|
||||
return image_layer
|
||||
|
||||
|
||||
def _cast_group_layer(
|
||||
raw_layer: RawLayer, parent_dir: Optional[Path] = None
|
||||
) -> LayerGroup:
|
||||
"""Cast the raw_layer to a LayerGroup.
|
||||
|
||||
Args:
|
||||
raw_layer: RawLayer to be casted to a LayerGroup
|
||||
|
||||
Returns:
|
||||
LayerGroup: The LayerGroup created from raw_layer
|
||||
"""
|
||||
|
||||
layers = []
|
||||
|
||||
for layer in raw_layer["layers"]:
|
||||
layers.append(cast(layer, parent_dir=parent_dir))
|
||||
|
||||
return LayerGroup(layers=layers, **_get_common_attributes(raw_layer).__dict__)
|
||||
|
||||
|
||||
def cast(
|
||||
raw_layer: RawLayer,
|
||||
parent_dir: Optional[Path] = None,
|
||||
) -> Layer:
|
||||
"""Cast a raw Tiled layer into a pytiled_parser type.
|
||||
|
||||
This function will determine the type of layer and cast accordingly.
|
||||
|
||||
Args:
|
||||
raw_layer: Raw layer to be cast.
|
||||
parent_dir: The parent directory that the map file is in.
|
||||
|
||||
Returns:
|
||||
Layer: a properly typed Layer.
|
||||
|
||||
Raises:
|
||||
RuntimeError: For an invalid layer type being provided
|
||||
"""
|
||||
type_ = raw_layer["type"]
|
||||
|
||||
if type_ == "objectgroup":
|
||||
return _cast_object_layer(raw_layer, parent_dir)
|
||||
elif type_ == "group":
|
||||
return _cast_group_layer(raw_layer, parent_dir)
|
||||
elif type_ == "imagelayer":
|
||||
return _cast_image_layer(raw_layer)
|
||||
elif type_ == "tilelayer":
|
||||
return _cast_tile_layer(raw_layer)
|
||||
|
||||
raise RuntimeError(f"An invalid layer type of {type_} was supplied")
|
||||
|
||||
29
pytiled_parser/parser.py
Normal file
29
pytiled_parser/parser.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from pathlib import Path
|
||||
|
||||
from pytiled_parser import UnknownFormat
|
||||
from pytiled_parser.parsers.json.tiled_map import parse as json_map_parse
|
||||
from pytiled_parser.parsers.tmx.tiled_map import parse as tmx_map_parse
|
||||
from pytiled_parser.tiled_map import TiledMap
|
||||
from pytiled_parser.util import check_format
|
||||
|
||||
|
||||
def parse_map(file: Path) -> TiledMap:
|
||||
"""Parse the raw Tiled map into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
file: Path to the map file
|
||||
|
||||
Returns:
|
||||
Tiledmap: a properly typed TiledMap
|
||||
"""
|
||||
parser = check_format(file)
|
||||
|
||||
# The type ignores are because mypy for some reaosn thinks those functions return Any
|
||||
if parser == "tmx":
|
||||
return tmx_map_parse(file) # type: ignore
|
||||
elif parser == "json":
|
||||
return json_map_parse(file) # type: ignore
|
||||
else:
|
||||
raise UnknownFormat(
|
||||
"Unknown Map Format, please use either the TMX or JSON format."
|
||||
)
|
||||
364
pytiled_parser/parsers/json/layer.py
Normal file
364
pytiled_parser/parsers/json/layer.py
Normal file
@@ -0,0 +1,364 @@
|
||||
"""Layer parsing for the JSON Map Format.
|
||||
"""
|
||||
import base64
|
||||
import gzip
|
||||
import importlib.util
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
from typing import Any, List, Optional, Union, cast
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
from pytiled_parser.layer import (
|
||||
Chunk,
|
||||
ImageLayer,
|
||||
Layer,
|
||||
LayerGroup,
|
||||
ObjectLayer,
|
||||
TileLayer,
|
||||
)
|
||||
from pytiled_parser.parsers.json.properties import RawProperty
|
||||
from pytiled_parser.parsers.json.properties import parse as parse_properties
|
||||
from pytiled_parser.parsers.json.tiled_object import RawObject
|
||||
from pytiled_parser.parsers.json.tiled_object import parse as parse_object
|
||||
from pytiled_parser.util import parse_color
|
||||
|
||||
zstd_spec = importlib.util.find_spec("zstd")
|
||||
if zstd_spec:
|
||||
import zstd
|
||||
else:
|
||||
zstd = None
|
||||
|
||||
|
||||
class RawChunk(TypedDict):
|
||||
"""The keys and their types that appear in a Tiled JSON Chunk Object.
|
||||
|
||||
Tiled Doc: https://doc.mapeditor.org/en/stable/reference/json-map-format/#chunk
|
||||
"""
|
||||
|
||||
data: Union[List[int], str]
|
||||
height: int
|
||||
width: int
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
class RawLayer(TypedDict):
|
||||
"""The keys and their types that appear in a Tiled JSON Layer Object.
|
||||
|
||||
Tiled Doc: https://doc.mapeditor.org/en/stable/reference/json-map-format/#layer
|
||||
"""
|
||||
|
||||
chunks: List[RawChunk]
|
||||
compression: str
|
||||
data: Union[List[int], str]
|
||||
draworder: str
|
||||
encoding: str
|
||||
height: int
|
||||
id: int
|
||||
image: str
|
||||
layers: List[Any]
|
||||
name: str
|
||||
objects: List[RawObject]
|
||||
offsetx: float
|
||||
offsety: float
|
||||
parallaxx: float
|
||||
parallaxy: float
|
||||
opacity: float
|
||||
properties: List[RawProperty]
|
||||
startx: int
|
||||
starty: int
|
||||
tintcolor: str
|
||||
transparentcolor: str
|
||||
type: str
|
||||
visible: bool
|
||||
width: int
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
def _convert_raw_tile_layer_data(data: List[int], layer_width: int) -> List[List[int]]:
|
||||
"""Convert raw layer data into a nested lit based on the layer width
|
||||
|
||||
Args:
|
||||
data: The data to convert
|
||||
layer_width: Width of the layer
|
||||
|
||||
Returns:
|
||||
List[List[int]]: A nested list containing the converted data
|
||||
"""
|
||||
tile_grid: List[List[int]] = [[]]
|
||||
|
||||
column_count = 0
|
||||
row_count = 0
|
||||
for item in data:
|
||||
column_count += 1
|
||||
tile_grid[row_count].append(item)
|
||||
if not column_count % layer_width and column_count < len(data):
|
||||
row_count += 1
|
||||
tile_grid.append([])
|
||||
|
||||
return tile_grid
|
||||
|
||||
|
||||
def _decode_tile_layer_data(
|
||||
data: str, compression: str, layer_width: int
|
||||
) -> List[List[int]]:
|
||||
"""Decode Base64 Encoded tile data. Optionally supports gzip and zlib compression.
|
||||
|
||||
Args:
|
||||
data: The base64 encoded data
|
||||
compression: Either zlib, gzip, or empty. If empty no decompression is done.
|
||||
|
||||
Returns:
|
||||
List[List[int]]: A nested list containing the decoded data
|
||||
|
||||
Raises:
|
||||
ValueError: For an unsupported compression type.
|
||||
"""
|
||||
unencoded_data = base64.b64decode(data)
|
||||
if compression == "zlib":
|
||||
unzipped_data = zlib.decompress(unencoded_data)
|
||||
elif compression == "gzip":
|
||||
unzipped_data = gzip.decompress(unencoded_data)
|
||||
elif compression == "zstd" and zstd is None:
|
||||
raise ValueError(
|
||||
"zstd compression support is not installed."
|
||||
"To install use 'pip install pytiled-parser[zstd]'"
|
||||
)
|
||||
elif compression == "zstd":
|
||||
unzipped_data = zstd.decompress(unencoded_data)
|
||||
else:
|
||||
unzipped_data = unencoded_data
|
||||
|
||||
tile_grid: List[int] = []
|
||||
|
||||
byte_count = 0
|
||||
int_count = 0
|
||||
int_value = 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.append(int_value)
|
||||
int_value = 0
|
||||
|
||||
return _convert_raw_tile_layer_data(tile_grid, layer_width)
|
||||
|
||||
|
||||
def _parse_chunk(
|
||||
raw_chunk: RawChunk,
|
||||
encoding: Optional[str] = None,
|
||||
compression: Optional[str] = None,
|
||||
) -> Chunk:
|
||||
"""Parse the raw_chunk to a Chunk.
|
||||
|
||||
Args:
|
||||
raw_chunk: RawChunk to be parsed to a Chunk
|
||||
encoding: Encoding type. ("base64" or None)
|
||||
compression: Either zlib, gzip, or empty. If empty no decompression is done.
|
||||
|
||||
Returns:
|
||||
Chunk: The Chunk created from the raw_chunk
|
||||
"""
|
||||
if encoding == "base64":
|
||||
assert isinstance(compression, str)
|
||||
assert isinstance(raw_chunk["data"], str)
|
||||
data = _decode_tile_layer_data(
|
||||
raw_chunk["data"], compression, raw_chunk["width"]
|
||||
)
|
||||
else:
|
||||
data = _convert_raw_tile_layer_data(
|
||||
raw_chunk["data"], raw_chunk["width"] # type: ignore
|
||||
)
|
||||
|
||||
chunk = Chunk(
|
||||
coordinates=OrderedPair(raw_chunk["x"], raw_chunk["y"]),
|
||||
size=Size(raw_chunk["width"], raw_chunk["height"]),
|
||||
data=data,
|
||||
)
|
||||
|
||||
return chunk
|
||||
|
||||
|
||||
def _parse_common(raw_layer: RawLayer) -> Layer:
|
||||
"""Create a Layer containing all the attributes common to all layer types.
|
||||
|
||||
This is to create the stub Layer object that can then be used to create the actual
|
||||
specific sub-classes of Layer.
|
||||
|
||||
Args:
|
||||
raw_layer: Raw layer get common attributes from
|
||||
|
||||
Returns:
|
||||
Layer: The attributes in common of all layer types
|
||||
"""
|
||||
common = Layer(
|
||||
name=raw_layer["name"],
|
||||
opacity=raw_layer["opacity"],
|
||||
visible=raw_layer["visible"],
|
||||
)
|
||||
|
||||
# if startx is present, starty is present
|
||||
if raw_layer.get("startx") is not None:
|
||||
common.coordinates = OrderedPair(raw_layer["startx"], raw_layer["starty"])
|
||||
|
||||
if raw_layer.get("id") is not None:
|
||||
common.id = raw_layer["id"]
|
||||
|
||||
# if either width or height is present, they both are
|
||||
if raw_layer.get("width") is not None:
|
||||
common.size = Size(raw_layer["width"], raw_layer["height"])
|
||||
|
||||
if raw_layer.get("offsetx") is not None:
|
||||
common.offset = OrderedPair(raw_layer["offsetx"], raw_layer["offsety"])
|
||||
|
||||
if raw_layer.get("properties") is not None:
|
||||
common.properties = parse_properties(raw_layer["properties"])
|
||||
|
||||
parallax = [1.0, 1.0]
|
||||
|
||||
if raw_layer.get("parallaxx") is not None:
|
||||
parallax[0] = raw_layer["parallaxx"]
|
||||
|
||||
if raw_layer.get("parallaxy") is not None:
|
||||
parallax[1] = raw_layer["parallaxy"]
|
||||
|
||||
common.parallax_factor = OrderedPair(parallax[0], parallax[1])
|
||||
|
||||
if raw_layer.get("tintcolor") is not None:
|
||||
common.tint_color = parse_color(raw_layer["tintcolor"])
|
||||
|
||||
return common
|
||||
|
||||
|
||||
def _parse_tile_layer(raw_layer: RawLayer) -> TileLayer:
|
||||
"""Parse the raw_layer to a TileLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: RawLayer to be parsed to a TileLayer.
|
||||
|
||||
Returns:
|
||||
TileLayer: The TileLayer created from raw_layer
|
||||
"""
|
||||
tile_layer = TileLayer(**_parse_common(raw_layer).__dict__)
|
||||
|
||||
if raw_layer.get("chunks") is not None:
|
||||
tile_layer.chunks = []
|
||||
for chunk in raw_layer["chunks"]:
|
||||
if raw_layer.get("encoding") is not None:
|
||||
tile_layer.chunks.append(
|
||||
_parse_chunk(chunk, raw_layer["encoding"], raw_layer["compression"])
|
||||
)
|
||||
else:
|
||||
tile_layer.chunks.append(_parse_chunk(chunk))
|
||||
|
||||
if raw_layer.get("data") is not None:
|
||||
if raw_layer.get("encoding") is not None:
|
||||
tile_layer.data = _decode_tile_layer_data(
|
||||
data=cast(str, raw_layer["data"]),
|
||||
compression=raw_layer["compression"],
|
||||
layer_width=raw_layer["width"],
|
||||
)
|
||||
else:
|
||||
tile_layer.data = _convert_raw_tile_layer_data(
|
||||
raw_layer["data"], raw_layer["width"] # type: ignore
|
||||
)
|
||||
|
||||
return tile_layer
|
||||
|
||||
|
||||
def _parse_object_layer(
|
||||
raw_layer: RawLayer,
|
||||
parent_dir: Optional[Path] = None,
|
||||
) -> ObjectLayer:
|
||||
"""Parse the raw_layer to an ObjectLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: RawLayer to be parsed to an ObjectLayer.
|
||||
|
||||
Returns:
|
||||
ObjectLayer: The ObjectLayer created from raw_layer
|
||||
"""
|
||||
objects = []
|
||||
for object_ in raw_layer["objects"]:
|
||||
objects.append(parse_object(object_, parent_dir))
|
||||
|
||||
return ObjectLayer(
|
||||
tiled_objects=objects,
|
||||
draw_order=raw_layer["draworder"],
|
||||
**_parse_common(raw_layer).__dict__,
|
||||
)
|
||||
|
||||
|
||||
def _parse_image_layer(raw_layer: RawLayer) -> ImageLayer:
|
||||
"""Parse the raw_layer to an ImageLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: RawLayer to be parsed to an ImageLayer.
|
||||
|
||||
Returns:
|
||||
ImageLayer: The ImageLayer created from raw_layer
|
||||
"""
|
||||
image_layer = ImageLayer(
|
||||
image=Path(raw_layer["image"]), **_parse_common(raw_layer).__dict__
|
||||
)
|
||||
|
||||
if raw_layer.get("transparentcolor") is not None:
|
||||
image_layer.transparent_color = parse_color(raw_layer["transparentcolor"])
|
||||
|
||||
return image_layer
|
||||
|
||||
|
||||
def _parse_group_layer(
|
||||
raw_layer: RawLayer, parent_dir: Optional[Path] = None
|
||||
) -> LayerGroup:
|
||||
"""Parse the raw_layer to a LayerGroup.
|
||||
|
||||
Args:
|
||||
raw_layer: RawLayer to be parsed to a LayerGroup.
|
||||
|
||||
Returns:
|
||||
LayerGroup: The LayerGroup created from raw_layer
|
||||
"""
|
||||
layers = []
|
||||
|
||||
for layer in raw_layer["layers"]:
|
||||
layers.append(parse(layer, parent_dir=parent_dir))
|
||||
|
||||
return LayerGroup(layers=layers, **_parse_common(raw_layer).__dict__)
|
||||
|
||||
|
||||
def parse(
|
||||
raw_layer: RawLayer,
|
||||
parent_dir: Optional[Path] = None,
|
||||
) -> Layer:
|
||||
"""Parse a raw Layer into a pytiled_parser object.
|
||||
|
||||
This function will determine the type of layer and parse accordingly.
|
||||
|
||||
Args:
|
||||
raw_layer: Raw layer to be parsed.
|
||||
parent_dir: The parent directory that the map file is in.
|
||||
|
||||
Returns:
|
||||
Layer: A parsed Layer.
|
||||
|
||||
Raises:
|
||||
RuntimeError: For an invalid layer type being provided
|
||||
"""
|
||||
type_ = raw_layer["type"]
|
||||
|
||||
if type_ == "objectgroup":
|
||||
return _parse_object_layer(raw_layer, parent_dir)
|
||||
elif type_ == "group":
|
||||
return _parse_group_layer(raw_layer, parent_dir)
|
||||
elif type_ == "imagelayer":
|
||||
return _parse_image_layer(raw_layer)
|
||||
elif type_ == "tilelayer":
|
||||
return _parse_tile_layer(raw_layer)
|
||||
|
||||
raise RuntimeError(f"An invalid layer type of {type_} was supplied")
|
||||
48
pytiled_parser/parsers/json/properties.py
Normal file
48
pytiled_parser/parsers/json/properties.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""Property parsing for the JSON Map Format
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Union, cast
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from pytiled_parser.properties import Properties, Property
|
||||
from pytiled_parser.util import parse_color
|
||||
|
||||
RawValue = Union[float, str, bool]
|
||||
|
||||
|
||||
class RawProperty(TypedDict):
|
||||
"""The keys and their values that appear in a Tiled JSON Property Object.
|
||||
|
||||
Tiled Docs: https://doc.mapeditor.org/en/stable/reference/json-map-format/#property
|
||||
"""
|
||||
|
||||
name: str
|
||||
type: str
|
||||
value: RawValue
|
||||
|
||||
|
||||
def parse(raw_properties: List[RawProperty]) -> Properties:
|
||||
"""Parse a list of `RawProperty` objects into `Properties`.
|
||||
|
||||
Args:
|
||||
raw_properties: The list of `RawProperty` objects to parse.
|
||||
|
||||
Returns:
|
||||
Properties: The parsed `Property` objects.
|
||||
"""
|
||||
|
||||
final: Properties = {}
|
||||
value: Property
|
||||
|
||||
for raw_property in raw_properties:
|
||||
if raw_property["type"] == "file":
|
||||
value = Path(cast(str, raw_property["value"]))
|
||||
elif raw_property["type"] == "color":
|
||||
value = parse_color(cast(str, raw_property["value"]))
|
||||
else:
|
||||
value = raw_property["value"]
|
||||
final[raw_property["name"]] = value
|
||||
|
||||
return final
|
||||
170
pytiled_parser/parsers/json/tiled_map.py
Normal file
170
pytiled_parser/parsers/json/tiled_map.py
Normal file
@@ -0,0 +1,170 @@
|
||||
import json
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
from typing import List, Union, cast
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from pytiled_parser.common_types import Size
|
||||
from pytiled_parser.exception import UnknownFormat
|
||||
from pytiled_parser.parsers.json.layer import RawLayer
|
||||
from pytiled_parser.parsers.json.layer import parse as parse_layer
|
||||
from pytiled_parser.parsers.json.properties import RawProperty
|
||||
from pytiled_parser.parsers.json.properties import parse as parse_properties
|
||||
from pytiled_parser.parsers.json.tileset import RawTileSet
|
||||
from pytiled_parser.parsers.json.tileset import parse as parse_json_tileset
|
||||
from pytiled_parser.parsers.tmx.tileset import parse as parse_tmx_tileset
|
||||
from pytiled_parser.tiled_map import TiledMap, TilesetDict
|
||||
from pytiled_parser.util import check_format, parse_color
|
||||
|
||||
|
||||
class RawTilesetMapping(TypedDict):
|
||||
|
||||
firstgid: int
|
||||
source: str
|
||||
|
||||
|
||||
class RawTiledMap(TypedDict):
|
||||
"""The keys and their types that appear in a Tiled JSON Map Object.
|
||||
|
||||
Tiled Docs: https://doc.mapeditor.org/en/stable/reference/json-map-format/#map
|
||||
"""
|
||||
|
||||
backgroundcolor: str
|
||||
compressionlevel: int
|
||||
height: int
|
||||
hexsidelength: int
|
||||
infinite: bool
|
||||
layers: List[RawLayer]
|
||||
nextlayerid: int
|
||||
nextobjectid: int
|
||||
orientation: str
|
||||
properties: List[RawProperty]
|
||||
renderorder: str
|
||||
staggeraxis: str
|
||||
staggerindex: str
|
||||
tiledversion: str
|
||||
tileheight: int
|
||||
tilesets: List[RawTilesetMapping]
|
||||
tilewidth: int
|
||||
type: str
|
||||
version: Union[str, float]
|
||||
width: int
|
||||
|
||||
|
||||
def parse(file: Path) -> TiledMap:
|
||||
"""Parse the raw Tiled map into a pytiled_parser type.
|
||||
|
||||
Args:
|
||||
file: Path to the map file.
|
||||
|
||||
Returns:
|
||||
TiledMap: A parsed TiledMap.
|
||||
"""
|
||||
with open(file) as map_file:
|
||||
raw_tiled_map = json.load(map_file)
|
||||
|
||||
parent_dir = file.parent
|
||||
|
||||
raw_tilesets: List[Union[RawTileSet, RawTilesetMapping]] = raw_tiled_map["tilesets"]
|
||||
tilesets: TilesetDict = {}
|
||||
|
||||
for raw_tileset in raw_tilesets:
|
||||
if raw_tileset.get("source") is not None:
|
||||
# Is an external Tileset
|
||||
tileset_path = Path(parent_dir / raw_tileset["source"])
|
||||
parser = check_format(tileset_path)
|
||||
with open(tileset_path) as raw_tileset_file:
|
||||
if parser == "json":
|
||||
tilesets[raw_tileset["firstgid"]] = parse_json_tileset(
|
||||
json.load(raw_tileset_file),
|
||||
raw_tileset["firstgid"],
|
||||
external_path=tileset_path.parent,
|
||||
)
|
||||
elif parser == "tmx":
|
||||
raw_tileset_external = etree.parse(raw_tileset_file).getroot()
|
||||
tilesets[raw_tileset["firstgid"]] = parse_tmx_tileset(
|
||||
raw_tileset_external,
|
||||
raw_tileset["firstgid"],
|
||||
external_path=tileset_path.parent,
|
||||
)
|
||||
else:
|
||||
raise UnknownFormat(
|
||||
"Unkown Tileset format, please use either the TSX or JSON format."
|
||||
)
|
||||
|
||||
else:
|
||||
# Is an embedded Tileset
|
||||
raw_tileset = cast(RawTileSet, raw_tileset)
|
||||
tilesets[raw_tileset["firstgid"]] = parse_json_tileset(
|
||||
raw_tileset, raw_tileset["firstgid"]
|
||||
)
|
||||
|
||||
if isinstance(raw_tiled_map["version"], float):
|
||||
version = str(raw_tiled_map["version"])
|
||||
else:
|
||||
version = raw_tiled_map["version"]
|
||||
|
||||
# `map` is a built-in function
|
||||
map_ = TiledMap(
|
||||
map_file=file,
|
||||
infinite=raw_tiled_map["infinite"],
|
||||
layers=[parse_layer(layer_, parent_dir) for layer_ in raw_tiled_map["layers"]],
|
||||
map_size=Size(raw_tiled_map["width"], raw_tiled_map["height"]),
|
||||
next_layer_id=raw_tiled_map["nextlayerid"],
|
||||
next_object_id=raw_tiled_map["nextobjectid"],
|
||||
orientation=raw_tiled_map["orientation"],
|
||||
render_order=raw_tiled_map["renderorder"],
|
||||
tiled_version=raw_tiled_map["tiledversion"],
|
||||
tile_size=Size(raw_tiled_map["tilewidth"], raw_tiled_map["tileheight"]),
|
||||
tilesets=tilesets,
|
||||
version=version,
|
||||
)
|
||||
|
||||
layers = [layer for layer in map_.layers if hasattr(layer, "tiled_objects")]
|
||||
|
||||
for my_layer in layers:
|
||||
for tiled_object in my_layer.tiled_objects: # type: ignore
|
||||
if hasattr(tiled_object, "new_tileset"):
|
||||
if tiled_object.new_tileset:
|
||||
already_loaded = None
|
||||
for val in map_.tilesets.values():
|
||||
if val.name == tiled_object.new_tileset["name"]:
|
||||
already_loaded = val
|
||||
break
|
||||
|
||||
if not already_loaded:
|
||||
highest_firstgid = max(map_.tilesets.keys())
|
||||
last_tileset_count = map_.tilesets[highest_firstgid].tile_count
|
||||
new_firstgid = highest_firstgid + last_tileset_count
|
||||
map_.tilesets[new_firstgid] = parse_json_tileset(
|
||||
tiled_object.new_tileset,
|
||||
new_firstgid,
|
||||
tiled_object.new_tileset_path,
|
||||
)
|
||||
tiled_object.gid = tiled_object.gid + (new_firstgid - 1)
|
||||
|
||||
else:
|
||||
tiled_object.gid = tiled_object.gid + (
|
||||
already_loaded.firstgid - 1
|
||||
)
|
||||
|
||||
tiled_object.new_tileset = None
|
||||
tiled_object.new_tileset_path = None
|
||||
|
||||
if raw_tiled_map.get("backgroundcolor") is not None:
|
||||
map_.background_color = parse_color(raw_tiled_map["backgroundcolor"])
|
||||
|
||||
if raw_tiled_map.get("hexsidelength") is not None:
|
||||
map_.hex_side_length = raw_tiled_map["hexsidelength"]
|
||||
|
||||
if raw_tiled_map.get("properties") is not None:
|
||||
map_.properties = parse_properties(raw_tiled_map["properties"])
|
||||
|
||||
if raw_tiled_map.get("staggeraxis") is not None:
|
||||
map_.stagger_axis = raw_tiled_map["staggeraxis"]
|
||||
|
||||
if raw_tiled_map.get("staggerindex") is not None:
|
||||
map_.stagger_index = raw_tiled_map["staggerindex"]
|
||||
|
||||
return map_
|
||||
321
pytiled_parser/parsers/json/tiled_object.py
Normal file
321
pytiled_parser/parsers/json/tiled_object.py
Normal file
@@ -0,0 +1,321 @@
|
||||
"""Object parsing for the JSON Map Format.
|
||||
"""
|
||||
import json
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
from pytiled_parser.parsers.json.properties import RawProperty
|
||||
from pytiled_parser.parsers.json.properties import parse as parse_properties
|
||||
from pytiled_parser.tiled_object import (
|
||||
Ellipse,
|
||||
Point,
|
||||
Polygon,
|
||||
Polyline,
|
||||
Rectangle,
|
||||
Text,
|
||||
Tile,
|
||||
TiledObject,
|
||||
)
|
||||
from pytiled_parser.util import load_object_template, parse_color
|
||||
|
||||
|
||||
class RawText(TypedDict):
|
||||
"""The keys and their types that appear in a Tiled JSON Text Object.
|
||||
|
||||
Tiled Doc: https://doc.mapeditor.org/en/stable/reference/json-map-format/#text-example
|
||||
"""
|
||||
|
||||
text: str
|
||||
color: str
|
||||
|
||||
fontfamily: str
|
||||
pixelsize: float # this is `font_size` in Text
|
||||
|
||||
bold: bool
|
||||
italic: bool
|
||||
strikeout: bool
|
||||
underline: bool
|
||||
kerning: bool
|
||||
|
||||
halign: str
|
||||
valign: str
|
||||
wrap: bool
|
||||
|
||||
|
||||
class RawObject(TypedDict):
|
||||
"""The keys and their types that appear in a Tiled JSON Object.
|
||||
|
||||
Tiled Doc: https://doc.mapeditor.org/en/stable/reference/json-map-format/#object
|
||||
"""
|
||||
|
||||
id: int
|
||||
gid: int
|
||||
template: str
|
||||
x: float
|
||||
y: float
|
||||
width: float
|
||||
height: float
|
||||
rotation: float
|
||||
visible: bool
|
||||
name: str
|
||||
type: str
|
||||
properties: List[RawProperty]
|
||||
ellipse: bool
|
||||
point: bool
|
||||
polygon: List[Dict[str, float]]
|
||||
polyline: List[Dict[str, float]]
|
||||
text: RawText
|
||||
|
||||
|
||||
def _parse_common(raw_object: RawObject) -> TiledObject:
|
||||
"""Create an Object containing all the attributes common to all types of objects.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to get common attributes from
|
||||
|
||||
Returns:
|
||||
Object: The attributes in common of all types of objects
|
||||
"""
|
||||
|
||||
common = TiledObject(
|
||||
id=raw_object["id"],
|
||||
coordinates=OrderedPair(raw_object["x"], raw_object["y"]),
|
||||
visible=raw_object["visible"],
|
||||
size=Size(raw_object["width"], raw_object["height"]),
|
||||
rotation=raw_object["rotation"],
|
||||
name=raw_object["name"],
|
||||
type=raw_object["type"],
|
||||
)
|
||||
|
||||
if raw_object.get("properties") is not None:
|
||||
common.properties = parse_properties(raw_object["properties"])
|
||||
|
||||
return common
|
||||
|
||||
|
||||
def _parse_ellipse(raw_object: RawObject) -> Ellipse:
|
||||
"""Parse the raw object into an Ellipse.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to be parsed to an Ellipse
|
||||
|
||||
Returns:
|
||||
Ellipse: The Ellipse object created from the raw object
|
||||
"""
|
||||
return Ellipse(**_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_rectangle(raw_object: RawObject) -> Rectangle:
|
||||
"""Parse the raw object into a Rectangle.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to be parsed to a Rectangle
|
||||
|
||||
Returns:
|
||||
Rectangle: The Rectangle object created from the raw object
|
||||
"""
|
||||
return Rectangle(**_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_point(raw_object: RawObject) -> Point:
|
||||
"""Parse the raw object into a Point.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to be parsed to a Point
|
||||
|
||||
Returns:
|
||||
Point: The Point object created from the raw object
|
||||
"""
|
||||
return Point(**_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_polygon(raw_object: RawObject) -> Polygon:
|
||||
"""Parse the raw object into a Polygon.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to be parsed to a Polygon
|
||||
|
||||
Returns:
|
||||
Polygon: The Polygon object created from the raw object
|
||||
"""
|
||||
polygon = []
|
||||
for point in raw_object["polygon"]:
|
||||
polygon.append(OrderedPair(point["x"], point["y"]))
|
||||
|
||||
return Polygon(points=polygon, **_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_polyline(raw_object: RawObject) -> Polyline:
|
||||
"""Parse the raw object into a Polyline.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to be parsed to a Polyline
|
||||
|
||||
Returns:
|
||||
Polyline: The Polyline object created from the raw object
|
||||
"""
|
||||
polyline = []
|
||||
for point in raw_object["polyline"]:
|
||||
polyline.append(OrderedPair(point["x"], point["y"]))
|
||||
|
||||
return Polyline(points=polyline, **_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_tile(
|
||||
raw_object: RawObject,
|
||||
new_tileset: Optional[Dict[str, Any]] = None,
|
||||
new_tileset_path: Optional[Path] = None,
|
||||
) -> Tile:
|
||||
"""Parse the raw object into a Tile.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to be parsed to a Tile
|
||||
|
||||
Returns:
|
||||
Tile: The Tile object created from the raw object
|
||||
"""
|
||||
gid = raw_object["gid"]
|
||||
|
||||
return Tile(
|
||||
gid=gid,
|
||||
new_tileset=new_tileset,
|
||||
new_tileset_path=new_tileset_path,
|
||||
**_parse_common(raw_object).__dict__
|
||||
)
|
||||
|
||||
|
||||
def _parse_text(raw_object: RawObject) -> Text:
|
||||
"""Parse the raw object into Text.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to be parsed to a Text
|
||||
|
||||
Returns:
|
||||
Text: The Text object created from the raw object
|
||||
"""
|
||||
# required attributes
|
||||
raw_text: RawText = raw_object["text"]
|
||||
text = raw_text["text"]
|
||||
|
||||
# create base Text object
|
||||
text_object = Text(text=text, **_parse_common(raw_object).__dict__)
|
||||
|
||||
# optional attributes
|
||||
if raw_text.get("color") is not None:
|
||||
text_object.color = parse_color(raw_text["color"])
|
||||
|
||||
if raw_text.get("fontfamily") is not None:
|
||||
text_object.font_family = raw_text["fontfamily"]
|
||||
|
||||
if raw_text.get("pixelsize") is not None:
|
||||
text_object.font_size = raw_text["pixelsize"]
|
||||
|
||||
if raw_text.get("bold") is not None:
|
||||
text_object.bold = raw_text["bold"]
|
||||
|
||||
if raw_text.get("italic") is not None:
|
||||
text_object.italic = raw_text["italic"]
|
||||
|
||||
if raw_text.get("kerning") is not None:
|
||||
text_object.kerning = raw_text["kerning"]
|
||||
|
||||
if raw_text.get("strikeout") is not None:
|
||||
text_object.strike_out = raw_text["strikeout"]
|
||||
|
||||
if raw_text.get("underline") is not None:
|
||||
text_object.underline = raw_text["underline"]
|
||||
|
||||
if raw_text.get("halign") is not None:
|
||||
text_object.horizontal_align = raw_text["halign"]
|
||||
|
||||
if raw_text.get("valign") is not None:
|
||||
text_object.vertical_align = raw_text["valign"]
|
||||
|
||||
if raw_text.get("wrap") is not None:
|
||||
text_object.wrap = raw_text["wrap"]
|
||||
|
||||
return text_object
|
||||
|
||||
|
||||
def _get_parser(raw_object: RawObject) -> Callable[[RawObject], TiledObject]:
|
||||
"""Get the parser function for a given raw object.
|
||||
|
||||
Only used internally by the JSON parser.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object that is analyzed to determine the parser function.
|
||||
|
||||
Returns:
|
||||
Callable[[RawObject], Object]: The parser function.
|
||||
"""
|
||||
if raw_object.get("ellipse"):
|
||||
return _parse_ellipse
|
||||
|
||||
if raw_object.get("point"):
|
||||
return _parse_point
|
||||
|
||||
if raw_object.get("gid"):
|
||||
# Only tile objects have the `gid` key
|
||||
return _parse_tile
|
||||
|
||||
if raw_object.get("polygon"):
|
||||
return _parse_polygon
|
||||
|
||||
if raw_object.get("polyline"):
|
||||
return _parse_polyline
|
||||
|
||||
if raw_object.get("text"):
|
||||
return _parse_text
|
||||
|
||||
# If it's none of the above, rectangle is the only one left.
|
||||
# Rectangle is the only object which has no special properties to signify that.
|
||||
return _parse_rectangle
|
||||
|
||||
|
||||
def parse(
|
||||
raw_object: RawObject,
|
||||
parent_dir: Optional[Path] = None,
|
||||
) -> TiledObject:
|
||||
"""Parse the raw object into a pytiled_parser version
|
||||
|
||||
Args:
|
||||
raw_object: Raw object that is to be cast.
|
||||
parent_dir: The parent directory that the map file is in.
|
||||
|
||||
Returns:
|
||||
Object: A parsed Object.
|
||||
|
||||
Raises:
|
||||
RuntimeError: When a parameter that is conditionally required was not sent.
|
||||
"""
|
||||
new_tileset = None
|
||||
new_tileset_path = None
|
||||
|
||||
if raw_object.get("template"):
|
||||
if not parent_dir:
|
||||
raise RuntimeError(
|
||||
"A parent directory must be specified when using object templates."
|
||||
)
|
||||
template_path = Path(parent_dir / raw_object["template"])
|
||||
template, new_tileset, new_tileset_path = load_object_template(template_path)
|
||||
|
||||
if isinstance(template, dict):
|
||||
loaded_template = template["object"]
|
||||
for key in loaded_template:
|
||||
if key != "id":
|
||||
raw_object[key] = loaded_template[key] # type: ignore
|
||||
elif isinstance(template, etree.Element):
|
||||
# load the XML object into the JSON object
|
||||
raise NotImplementedError(
|
||||
"Loading TMX object templates inside a JSON map is currently not supported, "
|
||||
"but will be in a future release."
|
||||
)
|
||||
|
||||
if raw_object.get("gid"):
|
||||
return _parse_tile(raw_object, new_tileset, new_tileset_path)
|
||||
|
||||
return _get_parser(raw_object)(raw_object)
|
||||
272
pytiled_parser/parsers/json/tileset.py
Normal file
272
pytiled_parser/parsers/json/tileset.py
Normal file
@@ -0,0 +1,272 @@
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from pytiled_parser.common_types import OrderedPair
|
||||
from pytiled_parser.parsers.json.layer import RawLayer
|
||||
from pytiled_parser.parsers.json.layer import parse as parse_layer
|
||||
from pytiled_parser.parsers.json.properties import RawProperty
|
||||
from pytiled_parser.parsers.json.properties import parse as parse_properties
|
||||
from pytiled_parser.parsers.json.wang_set import RawWangSet
|
||||
from pytiled_parser.parsers.json.wang_set import parse as parse_wangset
|
||||
from pytiled_parser.tileset import Frame, Grid, Tile, Tileset, Transformations
|
||||
from pytiled_parser.util import parse_color
|
||||
|
||||
|
||||
class RawFrame(TypedDict):
|
||||
"""The keys and their types that appear in a Frame JSON Object."""
|
||||
|
||||
duration: int
|
||||
tileid: int
|
||||
|
||||
|
||||
class RawTileOffset(TypedDict):
|
||||
"""The keys and their types that appear in a TileOffset JSON Object."""
|
||||
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
class RawTransformations(TypedDict):
|
||||
"""The keys and their types that appear in a Transformations JSON Object."""
|
||||
|
||||
hflip: bool
|
||||
vflip: bool
|
||||
rotate: bool
|
||||
preferuntransformed: bool
|
||||
|
||||
|
||||
class RawTile(TypedDict):
|
||||
"""The keys and their types that appear in a Tile JSON Object."""
|
||||
|
||||
animation: List[RawFrame]
|
||||
id: int
|
||||
image: str
|
||||
imageheight: int
|
||||
imagewidth: int
|
||||
opacity: float
|
||||
properties: List[RawProperty]
|
||||
objectgroup: RawLayer
|
||||
type: str
|
||||
|
||||
|
||||
class RawGrid(TypedDict):
|
||||
"""The keys and their types that appear in a Grid JSON Object."""
|
||||
|
||||
height: int
|
||||
width: int
|
||||
orientation: str
|
||||
|
||||
|
||||
class RawTileSet(TypedDict):
|
||||
"""The keys and their types that appear in a TileSet JSON Object."""
|
||||
|
||||
backgroundcolor: str
|
||||
columns: int
|
||||
firstgid: int
|
||||
grid: RawGrid
|
||||
image: str
|
||||
imageheight: int
|
||||
imagewidth: int
|
||||
margin: int
|
||||
name: str
|
||||
properties: List[RawProperty]
|
||||
source: str
|
||||
spacing: int
|
||||
tilecount: int
|
||||
tiledversion: str
|
||||
tileheight: int
|
||||
tileoffset: RawTileOffset
|
||||
tiles: List[RawTile]
|
||||
tilewidth: int
|
||||
transparentcolor: str
|
||||
transformations: RawTransformations
|
||||
version: Union[str, float]
|
||||
wangsets: List[RawWangSet]
|
||||
|
||||
|
||||
def _parse_frame(raw_frame: RawFrame) -> Frame:
|
||||
"""Parse the raw_frame to a Frame.
|
||||
|
||||
Args:
|
||||
raw_frame: RawFrame to be parsed to a Frame
|
||||
|
||||
Returns:
|
||||
Frame: The Frame created from the raw_frame
|
||||
"""
|
||||
|
||||
return Frame(duration=raw_frame["duration"], tile_id=raw_frame["tileid"])
|
||||
|
||||
|
||||
def _parse_tile_offset(raw_tile_offset: RawTileOffset) -> OrderedPair:
|
||||
"""Parse the raw_tile_offset to an OrderedPair.
|
||||
|
||||
Args:
|
||||
raw_tile_offset: RawTileOffset to be parsed to an OrderedPair
|
||||
|
||||
Returns:
|
||||
OrderedPair: The OrderedPair created from the raw_tile_offset
|
||||
"""
|
||||
|
||||
return OrderedPair(raw_tile_offset["x"], raw_tile_offset["y"])
|
||||
|
||||
|
||||
def _parse_transformations(raw_transformations: RawTransformations) -> Transformations:
|
||||
"""Parse the raw_transformations to a Transformations object.
|
||||
|
||||
Args:
|
||||
raw_transformations: RawTransformations to be parsed to a Transformations
|
||||
|
||||
Returns:
|
||||
Transformations: The Transformations created from the raw_transformations
|
||||
"""
|
||||
|
||||
return Transformations(
|
||||
hflip=raw_transformations["hflip"],
|
||||
vflip=raw_transformations["vflip"],
|
||||
rotate=raw_transformations["rotate"],
|
||||
prefer_untransformed=raw_transformations["preferuntransformed"],
|
||||
)
|
||||
|
||||
|
||||
def _parse_grid(raw_grid: RawGrid) -> Grid:
|
||||
"""Parse the raw_grid to a Grid object.
|
||||
|
||||
Args:
|
||||
raw_grid: RawGrid to be parsed to a Grid
|
||||
|
||||
Returns:
|
||||
Grid: The Grid created from the raw_grid
|
||||
"""
|
||||
|
||||
return Grid(
|
||||
orientation=raw_grid["orientation"],
|
||||
width=raw_grid["width"],
|
||||
height=raw_grid["height"],
|
||||
)
|
||||
|
||||
|
||||
def _parse_tile(raw_tile: RawTile, external_path: Optional[Path] = None) -> Tile:
|
||||
"""Parse the raw_tile to a Tile object.
|
||||
|
||||
Args:
|
||||
raw_tile: RawTile to be parsed to a Tile
|
||||
|
||||
Returns:
|
||||
Tile: The Tile created from the raw_tile
|
||||
"""
|
||||
|
||||
id_ = raw_tile["id"]
|
||||
tile = Tile(id=id_)
|
||||
|
||||
if raw_tile.get("animation") is not None:
|
||||
tile.animation = []
|
||||
for frame in raw_tile["animation"]:
|
||||
tile.animation.append(_parse_frame(frame))
|
||||
|
||||
if raw_tile.get("objectgroup") is not None:
|
||||
tile.objects = parse_layer(raw_tile["objectgroup"])
|
||||
|
||||
if raw_tile.get("properties") is not None:
|
||||
tile.properties = parse_properties(raw_tile["properties"])
|
||||
|
||||
if raw_tile.get("image") is not None:
|
||||
if external_path:
|
||||
tile.image = Path(external_path / raw_tile["image"]).absolute().resolve()
|
||||
else:
|
||||
tile.image = Path(raw_tile["image"])
|
||||
|
||||
if raw_tile.get("imagewidth") is not None:
|
||||
tile.image_width = raw_tile["imagewidth"]
|
||||
|
||||
if raw_tile.get("imageheight") is not None:
|
||||
tile.image_height = raw_tile["imageheight"]
|
||||
|
||||
if raw_tile.get("type") is not None:
|
||||
tile.type = raw_tile["type"]
|
||||
|
||||
return tile
|
||||
|
||||
|
||||
def parse(
|
||||
raw_tileset: RawTileSet,
|
||||
firstgid: int,
|
||||
external_path: Optional[Path] = None,
|
||||
) -> Tileset:
|
||||
"""Parse the raw tileset into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_tileset: Raw Tileset to be parsed.
|
||||
firstgid: GID corresponding the first tile in the set.
|
||||
external_path: The path to the tileset if it is not an embedded one.
|
||||
|
||||
Returns:
|
||||
TileSet: a properly typed TileSet.
|
||||
"""
|
||||
|
||||
tileset = Tileset(
|
||||
name=raw_tileset["name"],
|
||||
tile_count=raw_tileset["tilecount"],
|
||||
tile_width=raw_tileset["tilewidth"],
|
||||
tile_height=raw_tileset["tileheight"],
|
||||
columns=raw_tileset["columns"],
|
||||
spacing=raw_tileset["spacing"],
|
||||
margin=raw_tileset["margin"],
|
||||
firstgid=firstgid,
|
||||
)
|
||||
|
||||
if raw_tileset.get("version") is not None:
|
||||
if isinstance(raw_tileset["version"], float):
|
||||
tileset.version = str(raw_tileset["version"])
|
||||
else:
|
||||
tileset.version = raw_tileset["version"]
|
||||
|
||||
if raw_tileset.get("tiledversion") is not None:
|
||||
tileset.tiled_version = raw_tileset["tiledversion"]
|
||||
|
||||
if raw_tileset.get("image") is not None:
|
||||
if external_path:
|
||||
tileset.image = (
|
||||
Path(external_path / raw_tileset["image"]).absolute().resolve()
|
||||
)
|
||||
else:
|
||||
tileset.image = Path(raw_tileset["image"])
|
||||
|
||||
if raw_tileset.get("imagewidth") is not None:
|
||||
tileset.image_width = raw_tileset["imagewidth"]
|
||||
|
||||
if raw_tileset.get("imageheight") is not None:
|
||||
tileset.image_height = raw_tileset["imageheight"]
|
||||
|
||||
if raw_tileset.get("backgroundcolor") is not None:
|
||||
tileset.background_color = parse_color(raw_tileset["backgroundcolor"])
|
||||
|
||||
if raw_tileset.get("tileoffset") is not None:
|
||||
tileset.tile_offset = _parse_tile_offset(raw_tileset["tileoffset"])
|
||||
|
||||
if raw_tileset.get("transparentcolor") is not None:
|
||||
tileset.transparent_color = parse_color(raw_tileset["transparentcolor"])
|
||||
|
||||
if raw_tileset.get("grid") is not None:
|
||||
tileset.grid = _parse_grid(raw_tileset["grid"])
|
||||
|
||||
if raw_tileset.get("properties") is not None:
|
||||
tileset.properties = parse_properties(raw_tileset["properties"])
|
||||
|
||||
if raw_tileset.get("tiles") is not None:
|
||||
tiles = {}
|
||||
for raw_tile in raw_tileset["tiles"]:
|
||||
tiles[raw_tile["id"]] = _parse_tile(raw_tile, external_path=external_path)
|
||||
tileset.tiles = tiles
|
||||
|
||||
if raw_tileset.get("wangsets") is not None:
|
||||
wangsets = []
|
||||
for raw_wangset in raw_tileset["wangsets"]:
|
||||
wangsets.append(parse_wangset(raw_wangset))
|
||||
tileset.wang_sets = wangsets
|
||||
|
||||
if raw_tileset.get("transformations") is not None:
|
||||
tileset.transformations = _parse_transformations(raw_tileset["transformations"])
|
||||
|
||||
return tileset
|
||||
104
pytiled_parser/parsers/json/wang_set.py
Normal file
104
pytiled_parser/parsers/json/wang_set.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from typing import List
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from pytiled_parser.parsers.json.properties import RawProperty
|
||||
from pytiled_parser.parsers.json.properties import parse as parse_properties
|
||||
from pytiled_parser.util import parse_color
|
||||
from pytiled_parser.wang_set import WangColor, WangSet, WangTile
|
||||
|
||||
|
||||
class RawWangTile(TypedDict):
|
||||
"""The keys and their types that appear in a Wang Tile JSON Object."""
|
||||
|
||||
tileid: int
|
||||
# Tiled stores these IDs as a list represented like so:
|
||||
# [top, top_right, right, bottom_right, bottom, bottom_left, left, top_left]
|
||||
wangid: List[int]
|
||||
|
||||
|
||||
class RawWangColor(TypedDict):
|
||||
"""The keys and their types that appear in a Wang Color JSON Object."""
|
||||
|
||||
color: str
|
||||
name: str
|
||||
probability: float
|
||||
tile: int
|
||||
properties: List[RawProperty]
|
||||
|
||||
|
||||
class RawWangSet(TypedDict):
|
||||
"""The keys and their types that appear in a Wang Set JSON Object."""
|
||||
|
||||
colors: List[RawWangColor]
|
||||
name: str
|
||||
properties: List[RawProperty]
|
||||
tile: int
|
||||
type: str
|
||||
wangtiles: List[RawWangTile]
|
||||
|
||||
|
||||
def _parse_wang_tile(raw_wang_tile: RawWangTile) -> WangTile:
|
||||
"""Parse the raw wang tile into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wang_tile: RawWangTile to be parsed.
|
||||
|
||||
Returns:
|
||||
WangTile: A properly typed WangTile.
|
||||
"""
|
||||
return WangTile(tile_id=raw_wang_tile["tileid"], wang_id=raw_wang_tile["wangid"])
|
||||
|
||||
|
||||
def _parse_wang_color(raw_wang_color: RawWangColor) -> WangColor:
|
||||
"""Parse the raw wang color into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wang_color: RawWangColor to be parsed.
|
||||
|
||||
Returns:
|
||||
WangColor: A properly typed WangColor.
|
||||
"""
|
||||
wang_color = WangColor(
|
||||
name=raw_wang_color["name"],
|
||||
color=parse_color(raw_wang_color["color"]),
|
||||
tile=raw_wang_color["tile"],
|
||||
probability=raw_wang_color["probability"],
|
||||
)
|
||||
|
||||
if raw_wang_color.get("properties") is not None:
|
||||
wang_color.properties = parse_properties(raw_wang_color["properties"])
|
||||
|
||||
return wang_color
|
||||
|
||||
|
||||
def parse(raw_wangset: RawWangSet) -> WangSet:
|
||||
"""Parse the raw wangset into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wangset: Raw Wangset to be parsed.
|
||||
|
||||
Returns:
|
||||
WangSet: A properly typed WangSet.
|
||||
"""
|
||||
|
||||
colors = []
|
||||
for raw_wang_color in raw_wangset["colors"]:
|
||||
colors.append(_parse_wang_color(raw_wang_color))
|
||||
|
||||
tiles = {}
|
||||
for raw_wang_tile in raw_wangset["wangtiles"]:
|
||||
tiles[raw_wang_tile["tileid"]] = _parse_wang_tile(raw_wang_tile)
|
||||
|
||||
wangset = WangSet(
|
||||
name=raw_wangset["name"],
|
||||
tile=raw_wangset["tile"],
|
||||
wang_type=raw_wangset["type"],
|
||||
wang_colors=colors,
|
||||
wang_tiles=tiles,
|
||||
)
|
||||
|
||||
if raw_wangset.get("properties") is not None:
|
||||
wangset.properties = parse_properties(raw_wangset["properties"])
|
||||
|
||||
return wangset
|
||||
360
pytiled_parser/parsers/tmx/layer.py
Normal file
360
pytiled_parser/parsers/tmx/layer.py
Normal file
@@ -0,0 +1,360 @@
|
||||
"""Layer parsing for the TMX Map Format.
|
||||
"""
|
||||
import base64
|
||||
import gzip
|
||||
import importlib.util
|
||||
import xml.etree.ElementTree as etree
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
from pytiled_parser.layer import (
|
||||
Chunk,
|
||||
ImageLayer,
|
||||
Layer,
|
||||
LayerGroup,
|
||||
ObjectLayer,
|
||||
TileLayer,
|
||||
)
|
||||
from pytiled_parser.parsers.tmx.properties import parse as parse_properties
|
||||
from pytiled_parser.parsers.tmx.tiled_object import parse as parse_object
|
||||
from pytiled_parser.util import parse_color
|
||||
|
||||
zstd_spec = importlib.util.find_spec("zstd")
|
||||
if zstd_spec:
|
||||
import zstd
|
||||
else:
|
||||
zstd = None
|
||||
|
||||
|
||||
def _convert_raw_tile_layer_data(data: List[int], layer_width: int) -> List[List[int]]:
|
||||
"""Convert raw layer data into a nested lit based on the layer width
|
||||
|
||||
Args:
|
||||
data: The data to convert
|
||||
layer_width: Width of the layer
|
||||
|
||||
Returns:
|
||||
List[List[int]]: A nested list containing the converted data
|
||||
"""
|
||||
tile_grid: List[List[int]] = [[]]
|
||||
|
||||
column_count = 0
|
||||
row_count = 0
|
||||
for item in data:
|
||||
column_count += 1
|
||||
tile_grid[row_count].append(item)
|
||||
if not column_count % layer_width and column_count < len(data):
|
||||
row_count += 1
|
||||
tile_grid.append([])
|
||||
|
||||
return tile_grid
|
||||
|
||||
|
||||
def _decode_tile_layer_data(
|
||||
data: str, compression: str, layer_width: int
|
||||
) -> List[List[int]]:
|
||||
"""Decode Base64 Encoded tile data. Optionally supports gzip and zlib compression.
|
||||
|
||||
Args:
|
||||
data: The base64 encoded data
|
||||
compression: Either zlib, gzip, or empty. If empty no decompression is done.
|
||||
|
||||
Returns:
|
||||
List[List[int]]: A nested list containing the decoded data
|
||||
|
||||
Raises:
|
||||
ValueError: For an unsupported compression type.
|
||||
"""
|
||||
unencoded_data = base64.b64decode(data)
|
||||
if compression == "zlib":
|
||||
unzipped_data = zlib.decompress(unencoded_data)
|
||||
elif compression == "gzip":
|
||||
unzipped_data = gzip.decompress(unencoded_data)
|
||||
elif compression == "zstd" and zstd is None:
|
||||
raise ValueError(
|
||||
"zstd compression support is not installed."
|
||||
"To install use 'pip install pytiled-parser[zstd]'"
|
||||
)
|
||||
elif compression == "zstd":
|
||||
unzipped_data = zstd.decompress(unencoded_data)
|
||||
else:
|
||||
unzipped_data = unencoded_data
|
||||
|
||||
tile_grid: List[int] = []
|
||||
|
||||
byte_count = 0
|
||||
int_count = 0
|
||||
int_value = 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.append(int_value)
|
||||
int_value = 0
|
||||
|
||||
return _convert_raw_tile_layer_data(tile_grid, layer_width)
|
||||
|
||||
|
||||
def _parse_chunk(
|
||||
raw_chunk: etree.Element,
|
||||
encoding: Optional[str] = None,
|
||||
compression: Optional[str] = None,
|
||||
) -> Chunk:
|
||||
"""Parse the raw_chunk to a Chunk.
|
||||
|
||||
Args:
|
||||
raw_chunk: XML Element to be parsed to a Chunk
|
||||
encoding: Encoding type. ("base64" or None)
|
||||
compression: Either zlib, gzip, or empty. If empty no decompression is done.
|
||||
|
||||
Returns:
|
||||
Chunk: The Chunk created from the raw_chunk
|
||||
"""
|
||||
if encoding == "base64":
|
||||
assert isinstance(compression, str)
|
||||
data = _decode_tile_layer_data(
|
||||
raw_chunk.text, compression, int(raw_chunk.attrib["width"]) # type: ignore
|
||||
)
|
||||
else:
|
||||
data = _convert_raw_tile_layer_data(
|
||||
[int(v.strip()) for v in raw_chunk.text.split(",")], # type: ignore
|
||||
int(raw_chunk.attrib["width"]),
|
||||
)
|
||||
|
||||
return Chunk(
|
||||
coordinates=OrderedPair(int(raw_chunk.attrib["x"]), int(raw_chunk.attrib["y"])),
|
||||
size=Size(int(raw_chunk.attrib["width"]), int(raw_chunk.attrib["height"])),
|
||||
data=data,
|
||||
)
|
||||
|
||||
|
||||
def _parse_common(raw_layer: etree.Element) -> Layer:
|
||||
"""Create a Layer containing all the attributes common to all layer types.
|
||||
|
||||
This is to create the stub Layer object that can then be used to create the actual
|
||||
specific sub-classes of Layer.
|
||||
|
||||
Args:
|
||||
raw_layer: XML Element to get common attributes from
|
||||
|
||||
Returns:
|
||||
Layer: The attributes in common of all layer types
|
||||
"""
|
||||
if raw_layer.attrib.get("name") is None:
|
||||
raw_layer.attrib["name"] = ""
|
||||
|
||||
common = Layer(
|
||||
name=raw_layer.attrib["name"],
|
||||
)
|
||||
|
||||
if raw_layer.attrib.get("opacity") is not None:
|
||||
common.opacity = float(raw_layer.attrib["opacity"])
|
||||
|
||||
if raw_layer.attrib.get("visible") is not None:
|
||||
common.visible = bool(int(raw_layer.attrib["visible"]))
|
||||
|
||||
if raw_layer.attrib.get("id") is not None:
|
||||
common.id = int(raw_layer.attrib["id"])
|
||||
|
||||
if raw_layer.attrib.get("offsetx") is not None:
|
||||
common.offset = OrderedPair(
|
||||
float(raw_layer.attrib["offsetx"]), float(raw_layer.attrib["offsety"])
|
||||
)
|
||||
|
||||
properties_element = raw_layer.find("./properties")
|
||||
if properties_element is not None:
|
||||
common.properties = parse_properties(properties_element)
|
||||
|
||||
parallax = [1.0, 1.0]
|
||||
|
||||
if raw_layer.attrib.get("parallaxx") is not None:
|
||||
parallax[0] = float(raw_layer.attrib["parallaxx"])
|
||||
|
||||
if raw_layer.attrib.get("parallaxy") is not None:
|
||||
parallax[1] = float(raw_layer.attrib["parallaxy"])
|
||||
|
||||
common.parallax_factor = OrderedPair(parallax[0], parallax[1])
|
||||
|
||||
if raw_layer.attrib.get("tintcolor") is not None:
|
||||
common.tint_color = parse_color(raw_layer.attrib["tintcolor"])
|
||||
|
||||
return common
|
||||
|
||||
|
||||
def _parse_tile_layer(raw_layer: etree.Element) -> TileLayer:
|
||||
"""Parse the raw_layer to a TileLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: XML Element to be parsed to a TileLayer.
|
||||
|
||||
Returns:
|
||||
TileLayer: The TileLayer created from raw_layer
|
||||
"""
|
||||
common = _parse_common(raw_layer).__dict__
|
||||
del common["size"]
|
||||
tile_layer = TileLayer(
|
||||
size=Size(int(raw_layer.attrib["width"]), int(raw_layer.attrib["height"])),
|
||||
**common,
|
||||
)
|
||||
|
||||
data_element = raw_layer.find("data")
|
||||
if data_element is not None:
|
||||
encoding = None
|
||||
if data_element.attrib.get("encoding") is not None:
|
||||
encoding = data_element.attrib["encoding"]
|
||||
|
||||
compression = ""
|
||||
if data_element.attrib.get("compression") is not None:
|
||||
compression = data_element.attrib["compression"]
|
||||
|
||||
raw_chunks = data_element.findall("chunk")
|
||||
if not raw_chunks:
|
||||
if encoding and encoding != "csv":
|
||||
tile_layer.data = _decode_tile_layer_data(
|
||||
data=data_element.text, # type: ignore
|
||||
compression=compression,
|
||||
layer_width=int(raw_layer.attrib["width"]),
|
||||
)
|
||||
else:
|
||||
tile_layer.data = _convert_raw_tile_layer_data(
|
||||
[int(v.strip()) for v in data_element.text.split(",")], # type: ignore
|
||||
int(raw_layer.attrib["width"]),
|
||||
)
|
||||
else:
|
||||
chunks = []
|
||||
for raw_chunk in raw_chunks:
|
||||
chunks.append(
|
||||
_parse_chunk(
|
||||
raw_chunk,
|
||||
encoding,
|
||||
compression,
|
||||
)
|
||||
)
|
||||
|
||||
if chunks:
|
||||
tile_layer.chunks = chunks
|
||||
|
||||
return tile_layer
|
||||
|
||||
|
||||
def _parse_object_layer(
|
||||
raw_layer: etree.Element, parent_dir: Optional[Path] = None
|
||||
) -> ObjectLayer:
|
||||
"""Parse the raw_layer to an ObjectLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: XML Element to be parsed to an ObjectLayer.
|
||||
|
||||
Returns:
|
||||
ObjectLayer: The ObjectLayer created from raw_layer
|
||||
"""
|
||||
objects = []
|
||||
for object_ in raw_layer.findall("./object"):
|
||||
objects.append(parse_object(object_, parent_dir))
|
||||
|
||||
object_layer = ObjectLayer(
|
||||
tiled_objects=objects,
|
||||
**_parse_common(raw_layer).__dict__,
|
||||
)
|
||||
|
||||
if raw_layer.attrib.get("draworder") is not None:
|
||||
object_layer.draw_order = raw_layer.attrib["draworder"]
|
||||
|
||||
return object_layer
|
||||
|
||||
|
||||
def _parse_image_layer(raw_layer: etree.Element) -> ImageLayer:
|
||||
"""Parse the raw_layer to an ImageLayer.
|
||||
|
||||
Args:
|
||||
raw_layer: XML Element to be parsed to an ImageLayer.
|
||||
|
||||
Returns:
|
||||
ImageLayer: The ImageLayer created from raw_layer
|
||||
"""
|
||||
image_element = raw_layer.find("./image")
|
||||
if image_element is not None:
|
||||
source = Path(image_element.attrib["source"])
|
||||
|
||||
transparent_color = None
|
||||
if image_element.attrib.get("trans") is not None:
|
||||
transparent_color = parse_color(image_element.attrib["trans"])
|
||||
|
||||
image_layer = ImageLayer(
|
||||
image=source,
|
||||
transparent_color=transparent_color,
|
||||
**_parse_common(raw_layer).__dict__,
|
||||
)
|
||||
print(image_layer.size)
|
||||
return image_layer
|
||||
|
||||
raise RuntimeError("Tried to parse an image layer that doesn't have an image!")
|
||||
|
||||
|
||||
def _parse_group_layer(
|
||||
raw_layer: etree.Element, parent_dir: Optional[Path] = None
|
||||
) -> LayerGroup:
|
||||
"""Parse the raw_layer to a LayerGroup.
|
||||
|
||||
Args:
|
||||
raw_layer: XML Element to be parsed to a LayerGroup.
|
||||
|
||||
Returns:
|
||||
LayerGroup: The LayerGroup created from raw_layer
|
||||
"""
|
||||
layers: List[Layer] = []
|
||||
for layer in raw_layer.findall("./layer"):
|
||||
layers.append(_parse_tile_layer(layer))
|
||||
|
||||
for layer in raw_layer.findall("./objectgroup"):
|
||||
layers.append(_parse_object_layer(layer, parent_dir))
|
||||
|
||||
for layer in raw_layer.findall("./imagelayer"):
|
||||
layers.append(_parse_image_layer(layer))
|
||||
|
||||
for layer in raw_layer.findall("./group"):
|
||||
layers.append(_parse_group_layer(layer, parent_dir))
|
||||
# layers = []
|
||||
# layers = [
|
||||
# parse(child_layer, parent_dir=parent_dir)
|
||||
# for child_layer in raw_layer.iter()
|
||||
# if child_layer.tag in ["layer", "objectgroup", "imagelayer", "group"]
|
||||
# ]
|
||||
|
||||
return LayerGroup(layers=layers, **_parse_common(raw_layer).__dict__)
|
||||
|
||||
|
||||
def parse(
|
||||
raw_layer: etree.Element,
|
||||
parent_dir: Optional[Path] = None,
|
||||
) -> Layer:
|
||||
"""Parse a raw Layer into a pytiled_parser object.
|
||||
|
||||
This function will determine the type of layer and parse accordingly.
|
||||
|
||||
Args:
|
||||
raw_layer: Raw layer to be parsed.
|
||||
parent_dir: The parent directory that the map file is in.
|
||||
|
||||
Returns:
|
||||
Layer: A parsed Layer.
|
||||
|
||||
Raises:
|
||||
RuntimeError: For an invalid layer type being provided
|
||||
"""
|
||||
type_ = raw_layer.tag
|
||||
|
||||
if type_ == "objectgroup":
|
||||
return _parse_object_layer(raw_layer, parent_dir)
|
||||
elif type_ == "group":
|
||||
return _parse_group_layer(raw_layer, parent_dir)
|
||||
elif type_ == "imagelayer":
|
||||
return _parse_image_layer(raw_layer)
|
||||
elif type_ == "layer":
|
||||
return _parse_tile_layer(raw_layer)
|
||||
|
||||
raise RuntimeError(f"An invalid layer type of {type_} was supplied")
|
||||
33
pytiled_parser/parsers/tmx/properties.py
Normal file
33
pytiled_parser/parsers/tmx/properties.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
from typing import List, Union, cast
|
||||
|
||||
from pytiled_parser.properties import Properties, Property
|
||||
from pytiled_parser.util import parse_color
|
||||
|
||||
|
||||
def parse(raw_properties: etree.Element) -> Properties:
|
||||
|
||||
final: Properties = {}
|
||||
value: Property
|
||||
|
||||
for raw_property in raw_properties.findall("property"):
|
||||
|
||||
type_ = raw_property.attrib.get("type")
|
||||
value_ = raw_property.attrib["value"]
|
||||
if type_ == "file":
|
||||
value = Path(value_)
|
||||
elif type_ == "color":
|
||||
value = parse_color(value_)
|
||||
elif type_ == "int" or type_ == "float":
|
||||
value = float(value_)
|
||||
elif type_ == "bool":
|
||||
if value_ == "true":
|
||||
value = True
|
||||
else:
|
||||
value = False
|
||||
else:
|
||||
value = value_
|
||||
final[raw_property.attrib["name"]] = value
|
||||
|
||||
return final
|
||||
132
pytiled_parser/parsers/tmx/tiled_map.py
Normal file
132
pytiled_parser/parsers/tmx/tiled_map.py
Normal file
@@ -0,0 +1,132 @@
|
||||
import json
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
from pytiled_parser.exception import UnknownFormat
|
||||
from pytiled_parser.parsers.json.tileset import parse as parse_json_tileset
|
||||
from pytiled_parser.parsers.tmx.layer import parse as parse_layer
|
||||
from pytiled_parser.parsers.tmx.properties import parse as parse_properties
|
||||
from pytiled_parser.parsers.tmx.tileset import parse as parse_tmx_tileset
|
||||
from pytiled_parser.tiled_map import TiledMap, TilesetDict
|
||||
from pytiled_parser.util import check_format, parse_color
|
||||
|
||||
|
||||
def parse(file: Path) -> TiledMap:
|
||||
"""Parse the raw Tiled map into a pytiled_parser type.
|
||||
|
||||
Args:
|
||||
file: Path to the map file.
|
||||
|
||||
Returns:
|
||||
TiledMap: A parsed TiledMap.
|
||||
"""
|
||||
with open(file) as map_file:
|
||||
raw_map = etree.parse(map_file).getroot()
|
||||
|
||||
parent_dir = file.parent
|
||||
|
||||
raw_tilesets = raw_map.findall("./tileset")
|
||||
tilesets: TilesetDict = {}
|
||||
|
||||
for raw_tileset in raw_tilesets:
|
||||
if raw_tileset.attrib.get("source") is not None:
|
||||
# Is an external Tileset
|
||||
tileset_path = Path(parent_dir / raw_tileset.attrib["source"])
|
||||
parser = check_format(tileset_path)
|
||||
with open(tileset_path) as tileset_file:
|
||||
if parser == "tmx":
|
||||
raw_tileset_external = etree.parse(tileset_file).getroot()
|
||||
tilesets[int(raw_tileset.attrib["firstgid"])] = parse_tmx_tileset(
|
||||
raw_tileset_external,
|
||||
int(raw_tileset.attrib["firstgid"]),
|
||||
external_path=tileset_path.parent,
|
||||
)
|
||||
elif parser == "json":
|
||||
tilesets[int(raw_tileset.attrib["firstgid"])] = parse_json_tileset(
|
||||
json.load(tileset_file),
|
||||
int(raw_tileset.attrib["firstgid"]),
|
||||
external_path=tileset_path.parent,
|
||||
)
|
||||
else:
|
||||
raise UnknownFormat(
|
||||
"Unkown Tileset format, please use either the TSX or JSON format."
|
||||
)
|
||||
|
||||
else:
|
||||
# Is an embedded Tileset
|
||||
tilesets[int(raw_tileset.attrib["firstgid"])] = parse_tmx_tileset(
|
||||
raw_tileset, int(raw_tileset.attrib["firstgid"])
|
||||
)
|
||||
|
||||
layers = []
|
||||
for element in raw_map.iter():
|
||||
if element.tag in ["layer", "objectgroup", "imagelayer", "group"]:
|
||||
layers.append(parse_layer(element, parent_dir))
|
||||
|
||||
map_ = TiledMap(
|
||||
map_file=file,
|
||||
infinite=bool(int(raw_map.attrib["infinite"])),
|
||||
layers=layers,
|
||||
map_size=Size(int(raw_map.attrib["width"]), int(raw_map.attrib["height"])),
|
||||
next_layer_id=int(raw_map.attrib["nextlayerid"]),
|
||||
next_object_id=int(raw_map.attrib["nextobjectid"]),
|
||||
orientation=raw_map.attrib["orientation"],
|
||||
render_order=raw_map.attrib["renderorder"],
|
||||
tiled_version=raw_map.attrib["tiledversion"],
|
||||
tile_size=Size(
|
||||
int(raw_map.attrib["tilewidth"]), int(raw_map.attrib["tileheight"])
|
||||
),
|
||||
tilesets=tilesets,
|
||||
version=raw_map.attrib["version"],
|
||||
)
|
||||
|
||||
layers = [layer for layer in map_.layers if hasattr(layer, "tiled_objects")]
|
||||
|
||||
for my_layer in layers:
|
||||
for tiled_object in my_layer.tiled_objects:
|
||||
if hasattr(tiled_object, "new_tileset"):
|
||||
if tiled_object.new_tileset is not None:
|
||||
already_loaded = None
|
||||
for val in map_.tilesets.values():
|
||||
if val.name == tiled_object.new_tileset.attrib["name"]:
|
||||
already_loaded = val
|
||||
break
|
||||
|
||||
if not already_loaded:
|
||||
print("here")
|
||||
highest_firstgid = max(map_.tilesets.keys())
|
||||
last_tileset_count = map_.tilesets[highest_firstgid].tile_count
|
||||
new_firstgid = highest_firstgid + last_tileset_count
|
||||
map_.tilesets[new_firstgid] = parse_tmx_tileset(
|
||||
tiled_object.new_tileset,
|
||||
new_firstgid,
|
||||
tiled_object.new_tileset_path,
|
||||
)
|
||||
tiled_object.gid = tiled_object.gid + (new_firstgid - 1)
|
||||
|
||||
else:
|
||||
tiled_object.gid = tiled_object.gid + (
|
||||
already_loaded.firstgid - 1
|
||||
)
|
||||
|
||||
tiled_object.new_tileset = None
|
||||
tiled_object.new_tileset_path = None
|
||||
|
||||
if raw_map.attrib.get("backgroundcolor") is not None:
|
||||
map_.background_color = parse_color(raw_map.attrib["backgroundcolor"])
|
||||
|
||||
if raw_map.attrib.get("hexsidelength") is not None:
|
||||
map_.hex_side_length = int(raw_map.attrib["hexsidelength"])
|
||||
|
||||
properties_element = raw_map.find("./properties")
|
||||
if properties_element:
|
||||
map_.properties = parse_properties(properties_element)
|
||||
|
||||
if raw_map.attrib.get("staggeraxis") is not None:
|
||||
map_.stagger_axis = raw_map.attrib["staggeraxis"]
|
||||
|
||||
if raw_map.attrib.get("staggerindex") is not None:
|
||||
map_.stagger_index = raw_map.attrib["staggerindex"]
|
||||
|
||||
return map_
|
||||
293
pytiled_parser/parsers/tmx/tiled_object.py
Normal file
293
pytiled_parser/parsers/tmx/tiled_object.py
Normal file
@@ -0,0 +1,293 @@
|
||||
import json
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
from typing import Callable, Optional
|
||||
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
from pytiled_parser.parsers.tmx.properties import parse as parse_properties
|
||||
from pytiled_parser.tiled_object import (
|
||||
Ellipse,
|
||||
Point,
|
||||
Polygon,
|
||||
Polyline,
|
||||
Rectangle,
|
||||
Text,
|
||||
Tile,
|
||||
TiledObject,
|
||||
)
|
||||
from pytiled_parser.util import load_object_template, parse_color
|
||||
|
||||
|
||||
def _parse_common(raw_object: etree.Element) -> TiledObject:
|
||||
"""Create an Object containing all the attributes common to all types of objects.
|
||||
|
||||
Args:
|
||||
raw_object: XML Element to get common attributes from
|
||||
|
||||
Returns:
|
||||
Object: The attributes in common of all types of objects
|
||||
"""
|
||||
|
||||
common = TiledObject(
|
||||
id=int(raw_object.attrib["id"]),
|
||||
coordinates=OrderedPair(
|
||||
float(raw_object.attrib["x"]), float(raw_object.attrib["y"])
|
||||
),
|
||||
)
|
||||
|
||||
if raw_object.attrib.get("width") is not None:
|
||||
common.size = Size(
|
||||
float(raw_object.attrib["width"]), float(raw_object.attrib["height"])
|
||||
)
|
||||
|
||||
if raw_object.attrib.get("visible") is not None:
|
||||
common.visible = bool(int(raw_object.attrib["visible"]))
|
||||
|
||||
if raw_object.attrib.get("rotation") is not None:
|
||||
common.rotation = float(raw_object.attrib["rotation"])
|
||||
|
||||
if raw_object.attrib.get("name") is not None:
|
||||
common.name = raw_object.attrib["name"]
|
||||
|
||||
if raw_object.attrib.get("type") is not None:
|
||||
common.type = raw_object.attrib["type"]
|
||||
|
||||
properties_element = raw_object.find("./properties")
|
||||
if properties_element:
|
||||
common.properties = parse_properties(properties_element)
|
||||
|
||||
return common
|
||||
|
||||
|
||||
def _parse_ellipse(raw_object: etree.Element) -> Ellipse:
|
||||
"""Parse the raw object into an Ellipse.
|
||||
|
||||
Args:
|
||||
raw_object: XML Element to be parsed to an Ellipse
|
||||
|
||||
Returns:
|
||||
Ellipse: The Ellipse object created from the raw object
|
||||
"""
|
||||
return Ellipse(**_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_rectangle(raw_object: etree.Element) -> Rectangle:
|
||||
"""Parse the raw object into a Rectangle.
|
||||
|
||||
Args:
|
||||
raw_object: XML Element to be parsed to a Rectangle
|
||||
|
||||
Returns:
|
||||
Rectangle: The Rectangle object created from the raw object
|
||||
"""
|
||||
return Rectangle(**_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_point(raw_object: etree.Element) -> Point:
|
||||
"""Parse the raw object into a Point.
|
||||
|
||||
Args:
|
||||
raw_object: XML Element to be parsed to a Point
|
||||
|
||||
Returns:
|
||||
Point: The Point object created from the raw object
|
||||
"""
|
||||
return Point(**_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_polygon(raw_object: etree.Element) -> Polygon:
|
||||
"""Parse the raw object into a Polygon.
|
||||
|
||||
Args:
|
||||
raw_object: XML Element to be parsed to a Polygon
|
||||
|
||||
Returns:
|
||||
Polygon: The Polygon object created from the raw object
|
||||
"""
|
||||
polygon = []
|
||||
polygon_element = raw_object.find("./polygon")
|
||||
if polygon_element is not None:
|
||||
for raw_point in polygon_element.attrib["points"].split(" "):
|
||||
point = raw_point.split(",")
|
||||
polygon.append(OrderedPair(float(point[0]), float(point[1])))
|
||||
|
||||
return Polygon(points=polygon, **_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_polyline(raw_object: etree.Element) -> Polyline:
|
||||
"""Parse the raw object into a Polyline.
|
||||
|
||||
Args:
|
||||
raw_object: Raw object to be parsed to a Polyline
|
||||
|
||||
Returns:
|
||||
Polyline: The Polyline object created from the raw object
|
||||
"""
|
||||
polyline = []
|
||||
polyline_element = raw_object.find("./polyline")
|
||||
if polyline_element is not None:
|
||||
for raw_point in polyline_element.attrib["points"].split(" "):
|
||||
point = raw_point.split(",")
|
||||
polyline.append(OrderedPair(float(point[0]), float(point[1])))
|
||||
|
||||
return Polyline(points=polyline, **_parse_common(raw_object).__dict__)
|
||||
|
||||
|
||||
def _parse_tile(
|
||||
raw_object: etree.Element,
|
||||
new_tileset: Optional[etree.Element] = None,
|
||||
new_tileset_path: Optional[Path] = None,
|
||||
) -> Tile:
|
||||
"""Parse the raw object into a Tile.
|
||||
|
||||
Args:
|
||||
raw_object: XML Element to be parsed to a Tile
|
||||
|
||||
Returns:
|
||||
Tile: The Tile object created from the raw object
|
||||
"""
|
||||
return Tile(
|
||||
gid=int(raw_object.attrib["gid"]),
|
||||
new_tileset=new_tileset,
|
||||
new_tileset_path=new_tileset_path,
|
||||
**_parse_common(raw_object).__dict__
|
||||
)
|
||||
|
||||
|
||||
def _parse_text(raw_object: etree.Element) -> Text:
|
||||
"""Parse the raw object into Text.
|
||||
|
||||
Args:
|
||||
raw_object: XML Element to be parsed to a Text
|
||||
|
||||
Returns:
|
||||
Text: The Text object created from the raw object
|
||||
"""
|
||||
# required attributes
|
||||
text_element = raw_object.find("./text")
|
||||
|
||||
if text_element is not None:
|
||||
text = text_element.text
|
||||
|
||||
if not text:
|
||||
text = ""
|
||||
# create base Text object
|
||||
text_object = Text(text=text, **_parse_common(raw_object).__dict__)
|
||||
|
||||
# optional attributes
|
||||
|
||||
if text_element.attrib.get("color") is not None:
|
||||
text_object.color = parse_color(text_element.attrib["color"])
|
||||
|
||||
if text_element.attrib.get("fontfamily") is not None:
|
||||
text_object.font_family = text_element.attrib["fontfamily"]
|
||||
|
||||
if text_element.attrib.get("pixelsize") is not None:
|
||||
text_object.font_size = float(text_element.attrib["pixelsize"])
|
||||
|
||||
if text_element.attrib.get("bold") is not None:
|
||||
text_object.bold = bool(int(text_element.attrib["bold"]))
|
||||
|
||||
if text_element.attrib.get("italic") is not None:
|
||||
text_object.italic = bool(int(text_element.attrib["italic"]))
|
||||
|
||||
if text_element.attrib.get("kerning") is not None:
|
||||
text_object.kerning = bool(int(text_element.attrib["kerning"]))
|
||||
|
||||
if text_element.attrib.get("strikeout") is not None:
|
||||
text_object.strike_out = bool(int(text_element.attrib["strikeout"]))
|
||||
|
||||
if text_element.attrib.get("underline") is not None:
|
||||
text_object.underline = bool(int(text_element.attrib["underline"]))
|
||||
|
||||
if text_element.attrib.get("halign") is not None:
|
||||
text_object.horizontal_align = text_element.attrib["halign"]
|
||||
|
||||
if text_element.attrib.get("valign") is not None:
|
||||
text_object.vertical_align = text_element.attrib["valign"]
|
||||
|
||||
if text_element.attrib.get("wrap") is not None:
|
||||
text_object.wrap = bool(int(text_element.attrib["wrap"]))
|
||||
|
||||
return text_object
|
||||
|
||||
|
||||
def _get_parser(raw_object: etree.Element) -> Callable[[etree.Element], TiledObject]:
|
||||
"""Get the parser function for a given raw object.
|
||||
|
||||
Only used internally by the TMX parser.
|
||||
|
||||
Args:
|
||||
raw_object: XML Element that is analyzed to determine the parser function.
|
||||
|
||||
Returns:
|
||||
Callable[[Element], Object]: The parser function.
|
||||
"""
|
||||
if raw_object.find("./ellipse") is not None:
|
||||
return _parse_ellipse
|
||||
|
||||
if raw_object.find("./point") is not None:
|
||||
return _parse_point
|
||||
|
||||
if raw_object.find("./polygon") is not None:
|
||||
return _parse_polygon
|
||||
|
||||
if raw_object.find("./polyline") is not None:
|
||||
return _parse_polyline
|
||||
|
||||
if raw_object.find("./text") is not None:
|
||||
return _parse_text
|
||||
|
||||
# If it's none of the above, rectangle is the only one left.
|
||||
# Rectangle is the only object which has no properties to signify that.
|
||||
return _parse_rectangle
|
||||
|
||||
|
||||
def parse(raw_object: etree.Element, parent_dir: Optional[Path] = None) -> TiledObject:
|
||||
"""Parse the raw object into a pytiled_parser version
|
||||
|
||||
Args:
|
||||
raw_object: XML Element that is to be parsed.
|
||||
parent_dir: The parent directory that the map file is in.
|
||||
|
||||
Returns:
|
||||
TiledObject: A parsed Object.
|
||||
|
||||
Raises:
|
||||
RuntimeError: When a parameter that is conditionally required was not sent.
|
||||
"""
|
||||
new_tileset = None
|
||||
new_tileset_path = None
|
||||
|
||||
if raw_object.attrib.get("template"):
|
||||
if not parent_dir:
|
||||
raise RuntimeError(
|
||||
"A parent directory must be specified when using object templates."
|
||||
)
|
||||
template_path = Path(parent_dir / raw_object.attrib["template"])
|
||||
template, new_tileset, new_tileset_path = load_object_template(template_path)
|
||||
|
||||
if isinstance(template, etree.Element):
|
||||
new_object = template.find("./object")
|
||||
if new_object is not None:
|
||||
if raw_object.attrib.get("id") is not None:
|
||||
new_object.attrib["id"] = raw_object.attrib["id"]
|
||||
|
||||
if raw_object.attrib.get("x") is not None:
|
||||
new_object.attrib["x"] = raw_object.attrib["x"]
|
||||
|
||||
if raw_object.attrib.get("y") is not None:
|
||||
new_object.attrib["y"] = raw_object.attrib["y"]
|
||||
|
||||
raw_object = new_object
|
||||
elif isinstance(template, dict):
|
||||
# load the JSON object into the XML object
|
||||
raise NotImplementedError(
|
||||
"Loading JSON object templates inside a TMX map is currently not supported, "
|
||||
"but will be in a future release."
|
||||
)
|
||||
|
||||
if raw_object.attrib.get("gid"):
|
||||
return _parse_tile(raw_object, new_tileset, new_tileset_path)
|
||||
|
||||
return _get_parser(raw_object)(raw_object)
|
||||
194
pytiled_parser/parsers/tmx/tileset.py
Normal file
194
pytiled_parser/parsers/tmx/tileset.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from pytiled_parser.common_types import OrderedPair
|
||||
from pytiled_parser.parsers.tmx.layer import parse as parse_layer
|
||||
from pytiled_parser.parsers.tmx.properties import parse as parse_properties
|
||||
from pytiled_parser.parsers.tmx.wang_set import parse as parse_wangset
|
||||
from pytiled_parser.tileset import Frame, Grid, Tile, Tileset, Transformations
|
||||
from pytiled_parser.util import parse_color
|
||||
|
||||
|
||||
def _parse_frame(raw_frame: etree.Element) -> Frame:
|
||||
"""Parse the raw_frame to a Frame object.
|
||||
|
||||
Args:
|
||||
raw_frame: XML Element to be parsed to a Frame
|
||||
|
||||
Returns:
|
||||
Frame: The Frame created from the raw_frame
|
||||
"""
|
||||
|
||||
return Frame(
|
||||
duration=int(raw_frame.attrib["duration"]),
|
||||
tile_id=int(raw_frame.attrib["tileid"]),
|
||||
)
|
||||
|
||||
|
||||
def _parse_grid(raw_grid: etree.Element) -> Grid:
|
||||
"""Parse the raw_grid to a Grid object.
|
||||
|
||||
Args:
|
||||
raw_grid: XML Element to be parsed to a Grid
|
||||
|
||||
Returns:
|
||||
Grid: The Grid created from the raw_grid
|
||||
"""
|
||||
|
||||
return Grid(
|
||||
orientation=raw_grid.attrib["orientation"],
|
||||
width=int(raw_grid.attrib["width"]),
|
||||
height=int(raw_grid.attrib["height"]),
|
||||
)
|
||||
|
||||
|
||||
def _parse_transformations(raw_transformations: etree.Element) -> Transformations:
|
||||
"""Parse the raw_transformations to a Transformations object.
|
||||
|
||||
Args:
|
||||
raw_transformations: XML Element to be parsed to a Transformations
|
||||
|
||||
Returns:
|
||||
Transformations: The Transformations created from the raw_transformations
|
||||
"""
|
||||
|
||||
return Transformations(
|
||||
hflip=bool(int(raw_transformations.attrib["hflip"])),
|
||||
vflip=bool(int(raw_transformations.attrib["vflip"])),
|
||||
rotate=bool(int(raw_transformations.attrib["rotate"])),
|
||||
prefer_untransformed=bool(
|
||||
int(raw_transformations.attrib["preferuntransformed"])
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _parse_tile(raw_tile: etree.Element, external_path: Optional[Path] = None) -> Tile:
|
||||
"""Parse the raw_tile to a Tile object.
|
||||
|
||||
Args:
|
||||
raw_tile: XML Element to be parsed to a Tile
|
||||
|
||||
Returns:
|
||||
Tile: The Tile created from the raw_tile
|
||||
"""
|
||||
|
||||
tile = Tile(id=int(raw_tile.attrib["id"]))
|
||||
|
||||
if raw_tile.attrib.get("type") is not None:
|
||||
tile.type = raw_tile.attrib["type"]
|
||||
|
||||
animation_element = raw_tile.find("./animation")
|
||||
if animation_element is not None:
|
||||
tile.animation = []
|
||||
for raw_frame in animation_element.findall("./frame"):
|
||||
tile.animation.append(_parse_frame(raw_frame))
|
||||
|
||||
object_element = raw_tile.find("./objectgroup")
|
||||
if object_element is not None:
|
||||
tile.objects = parse_layer(object_element)
|
||||
|
||||
properties_element = raw_tile.find("./properties")
|
||||
if properties_element is not None:
|
||||
tile.properties = parse_properties(properties_element)
|
||||
|
||||
image_element = raw_tile.find("./image")
|
||||
if image_element is not None:
|
||||
if external_path:
|
||||
tile.image = (
|
||||
Path(external_path / image_element.attrib["source"])
|
||||
.absolute()
|
||||
.resolve()
|
||||
)
|
||||
else:
|
||||
tile.image = Path(image_element.attrib["source"])
|
||||
|
||||
tile.image_width = int(image_element.attrib["width"])
|
||||
tile.image_height = int(image_element.attrib["height"])
|
||||
|
||||
return tile
|
||||
|
||||
|
||||
def parse(
|
||||
raw_tileset: etree.Element,
|
||||
firstgid: int,
|
||||
external_path: Optional[Path] = None,
|
||||
) -> Tileset:
|
||||
tileset = Tileset(
|
||||
name=raw_tileset.attrib["name"],
|
||||
tile_count=int(raw_tileset.attrib["tilecount"]),
|
||||
tile_width=int(raw_tileset.attrib["tilewidth"]),
|
||||
tile_height=int(raw_tileset.attrib["tileheight"]),
|
||||
columns=int(raw_tileset.attrib["columns"]),
|
||||
firstgid=firstgid,
|
||||
)
|
||||
|
||||
if raw_tileset.attrib.get("version") is not None:
|
||||
tileset.version = raw_tileset.attrib["version"]
|
||||
|
||||
if raw_tileset.attrib.get("tiledversion") is not None:
|
||||
tileset.tiled_version = raw_tileset.attrib["tiledversion"]
|
||||
|
||||
if raw_tileset.attrib.get("backgroundcolor") is not None:
|
||||
tileset.background_color = parse_color(raw_tileset.attrib["backgroundcolor"])
|
||||
|
||||
if raw_tileset.attrib.get("spacing") is not None:
|
||||
tileset.spacing = int(raw_tileset.attrib["spacing"])
|
||||
|
||||
if raw_tileset.attrib.get("margin") is not None:
|
||||
tileset.margin = int(raw_tileset.attrib["margin"])
|
||||
|
||||
image_element = raw_tileset.find("image")
|
||||
if image_element is not None:
|
||||
if external_path:
|
||||
tileset.image = (
|
||||
Path(external_path / image_element.attrib["source"])
|
||||
.absolute()
|
||||
.resolve()
|
||||
)
|
||||
else:
|
||||
tileset.image = Path(image_element.attrib["source"])
|
||||
|
||||
tileset.image_width = int(image_element.attrib["width"])
|
||||
tileset.image_height = int(image_element.attrib["height"])
|
||||
|
||||
if image_element.attrib.get("trans") is not None:
|
||||
my_string = image_element.attrib["trans"]
|
||||
if my_string[0] != "#":
|
||||
my_string = f"#{my_string}"
|
||||
tileset.transparent_color = parse_color(my_string)
|
||||
|
||||
tileoffset_element = raw_tileset.find("./tileoffset")
|
||||
if tileoffset_element is not None:
|
||||
tileset.tile_offset = OrderedPair(
|
||||
int(tileoffset_element.attrib["x"]), int(tileoffset_element.attrib["y"])
|
||||
)
|
||||
|
||||
grid_element = raw_tileset.find("./grid")
|
||||
if grid_element is not None:
|
||||
tileset.grid = _parse_grid(grid_element)
|
||||
|
||||
properties_element = raw_tileset.find("./properties")
|
||||
if properties_element is not None:
|
||||
tileset.properties = parse_properties(properties_element)
|
||||
|
||||
tiles = {}
|
||||
for tile_element in raw_tileset.findall("./tile"):
|
||||
tiles[int(tile_element.attrib["id"])] = _parse_tile(
|
||||
tile_element, external_path=external_path
|
||||
)
|
||||
if tiles:
|
||||
tileset.tiles = tiles
|
||||
|
||||
wangsets_element = raw_tileset.find("./wangsets")
|
||||
if wangsets_element is not None:
|
||||
wangsets = []
|
||||
for raw_wangset in wangsets_element.findall("./wangset"):
|
||||
wangsets.append(parse_wangset(raw_wangset))
|
||||
tileset.wang_sets = wangsets
|
||||
|
||||
transformations_element = raw_tileset.find("./transformations")
|
||||
if transformations_element is not None:
|
||||
tileset.transformations = _parse_transformations(transformations_element)
|
||||
|
||||
return tileset
|
||||
74
pytiled_parser/parsers/tmx/wang_set.py
Normal file
74
pytiled_parser/parsers/tmx/wang_set.py
Normal file
@@ -0,0 +1,74 @@
|
||||
import xml.etree.ElementTree as etree
|
||||
|
||||
from pytiled_parser.parsers.tmx.properties import parse as parse_properties
|
||||
from pytiled_parser.util import parse_color
|
||||
from pytiled_parser.wang_set import WangColor, WangSet, WangTile
|
||||
|
||||
|
||||
def _parse_wang_tile(raw_wang_tile: etree.Element) -> WangTile:
|
||||
"""Parse the raw wang tile into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wang_tile: XML Element to be parsed.
|
||||
|
||||
Returns:
|
||||
WangTile: A properly typed WangTile.
|
||||
"""
|
||||
ids = [int(v.strip()) for v in raw_wang_tile.attrib["wangid"].split(",")]
|
||||
return WangTile(tile_id=int(raw_wang_tile.attrib["tileid"]), wang_id=ids)
|
||||
|
||||
|
||||
def _parse_wang_color(raw_wang_color: etree.Element) -> WangColor:
|
||||
"""Parse the raw wang color into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wang_color: XML Element to be parsed.
|
||||
|
||||
Returns:
|
||||
WangColor: A properly typed WangColor.
|
||||
"""
|
||||
wang_color = WangColor(
|
||||
name=raw_wang_color.attrib["name"],
|
||||
color=parse_color(raw_wang_color.attrib["color"]),
|
||||
tile=int(raw_wang_color.attrib["tile"]),
|
||||
probability=float(raw_wang_color.attrib["probability"]),
|
||||
)
|
||||
|
||||
properties = raw_wang_color.find("./properties")
|
||||
if properties:
|
||||
wang_color.properties = parse_properties(properties)
|
||||
|
||||
return wang_color
|
||||
|
||||
|
||||
def parse(raw_wangset: etree.Element) -> WangSet:
|
||||
"""Parse the raw wangset into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wangset: XML Element to be parsed.
|
||||
|
||||
Returns:
|
||||
WangSet: A properly typed WangSet.
|
||||
"""
|
||||
|
||||
colors = []
|
||||
for raw_wang_color in raw_wangset.findall("./wangcolor"):
|
||||
colors.append(_parse_wang_color(raw_wang_color))
|
||||
|
||||
tiles = {}
|
||||
for raw_wang_tile in raw_wangset.findall("./wangtile"):
|
||||
tiles[int(raw_wang_tile.attrib["tileid"])] = _parse_wang_tile(raw_wang_tile)
|
||||
|
||||
wangset = WangSet(
|
||||
name=raw_wangset.attrib["name"],
|
||||
tile=int(raw_wangset.attrib["tile"]),
|
||||
wang_type=raw_wangset.attrib["type"],
|
||||
wang_colors=colors,
|
||||
wang_tiles=tiles,
|
||||
)
|
||||
|
||||
properties = raw_wangset.find("./properties")
|
||||
if properties:
|
||||
wangset.properties = parse_properties(properties)
|
||||
|
||||
return wangset
|
||||
@@ -1,55 +1,18 @@
|
||||
"""Properties Module
|
||||
|
||||
This module casts raw properties from Tiled maps into a dictionary of
|
||||
properly typed Properties.
|
||||
This module defines types for Property objects.
|
||||
For more about properties in Tiled maps see the below link:
|
||||
https://doc.mapeditor.org/en/stable/manual/custom-properties/
|
||||
|
||||
The types defined in this module get added to other objects
|
||||
such as Layers, Maps, Objects, etc
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Union
|
||||
from typing import cast as type_cast
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
from typing import Dict, Union
|
||||
|
||||
from .common_types import Color
|
||||
from .util import parse_color
|
||||
|
||||
Property = Union[float, Path, str, bool, Color]
|
||||
|
||||
|
||||
Properties = Dict[str, Property]
|
||||
|
||||
|
||||
RawValue = Union[float, str, bool]
|
||||
|
||||
|
||||
class RawProperty(TypedDict):
|
||||
"""A dictionary of raw properties."""
|
||||
|
||||
name: str
|
||||
type: str
|
||||
value: RawValue
|
||||
|
||||
|
||||
def cast(raw_properties: List[RawProperty]) -> Properties:
|
||||
"""Cast a list of `RawProperty`s into `Properties`
|
||||
|
||||
Args:
|
||||
raw_properties: The list of `RawProperty`s to cast.
|
||||
|
||||
Returns:
|
||||
Properties: The casted `Properties`.
|
||||
"""
|
||||
|
||||
final: Properties = {}
|
||||
value: Property
|
||||
|
||||
for property_ in raw_properties:
|
||||
if property_["type"] == "file":
|
||||
value = Path(type_cast(str, property_["value"]))
|
||||
elif property_["type"] == "color":
|
||||
value = parse_color(type_cast(str, property_["value"]))
|
||||
else:
|
||||
value = property_["value"]
|
||||
final[property_["name"]] = value
|
||||
|
||||
return final
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Union
|
||||
from typing import cast as typing_cast
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import attr
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from . import layer, properties, tileset
|
||||
from .common_types import Color, Size
|
||||
from .layer import Layer, RawLayer
|
||||
from .properties import Properties, RawProperty
|
||||
from .tileset import RawTileSet, Tileset
|
||||
from .util import parse_color
|
||||
from pytiled_parser.common_types import Color, Size
|
||||
from pytiled_parser.layer import Layer
|
||||
from pytiled_parser.properties import Properties
|
||||
from pytiled_parser.tileset import Tileset
|
||||
|
||||
TilesetDict = Dict[int, Tileset]
|
||||
|
||||
@@ -68,146 +61,3 @@ class TiledMap:
|
||||
hex_side_length: Optional[int] = None
|
||||
stagger_axis: Optional[str] = None
|
||||
stagger_index: Optional[str] = None
|
||||
|
||||
|
||||
class _RawTilesetMapping(TypedDict):
|
||||
""" The way that tilesets are stored in the Tiled JSON formatted map."""
|
||||
|
||||
firstgid: int
|
||||
source: str
|
||||
|
||||
|
||||
class _RawTiledMap(TypedDict):
|
||||
"""The keys and their types that appear in a Tiled JSON Map.
|
||||
|
||||
Keys:
|
||||
compressionlevel: not documented - https://github.com/bjorn/tiled/issues/2815
|
||||
"""
|
||||
|
||||
backgroundcolor: str
|
||||
compressionlevel: int
|
||||
height: int
|
||||
hexsidelength: int
|
||||
infinite: bool
|
||||
layers: List[RawLayer]
|
||||
nextlayerid: int
|
||||
nextobjectid: int
|
||||
orientation: str
|
||||
properties: List[RawProperty]
|
||||
renderorder: str
|
||||
staggeraxis: str
|
||||
staggerindex: str
|
||||
tiledversion: str
|
||||
tileheight: int
|
||||
tilesets: List[_RawTilesetMapping]
|
||||
tilewidth: int
|
||||
type: str
|
||||
version: Union[str, float]
|
||||
width: int
|
||||
|
||||
|
||||
def parse_map(file: Path) -> TiledMap:
|
||||
"""Parse the raw Tiled map into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
file: Path to the map's JSON file
|
||||
|
||||
Returns:
|
||||
TileSet: a properly typed TileSet.
|
||||
"""
|
||||
|
||||
with open(file) as map_file:
|
||||
raw_tiled_map = json.load(map_file)
|
||||
|
||||
parent_dir = file.parent
|
||||
|
||||
raw_tilesets: List[Union[RawTileSet, _RawTilesetMapping]] = raw_tiled_map[
|
||||
"tilesets"
|
||||
]
|
||||
tilesets: TilesetDict = {}
|
||||
|
||||
for raw_tileset in raw_tilesets:
|
||||
if raw_tileset.get("source") is not None:
|
||||
# Is an external Tileset
|
||||
tileset_path = Path(parent_dir / raw_tileset["source"])
|
||||
with open(tileset_path) as raw_tileset_file:
|
||||
tilesets[raw_tileset["firstgid"]] = tileset.cast(
|
||||
json.load(raw_tileset_file),
|
||||
raw_tileset["firstgid"],
|
||||
external_path=tileset_path.parent,
|
||||
)
|
||||
else:
|
||||
# Is an embedded Tileset
|
||||
raw_tileset = typing_cast(RawTileSet, raw_tileset)
|
||||
tilesets[raw_tileset["firstgid"]] = tileset.cast(
|
||||
raw_tileset, raw_tileset["firstgid"]
|
||||
)
|
||||
|
||||
if isinstance(raw_tiled_map["version"], float):
|
||||
version = str(raw_tiled_map["version"])
|
||||
else:
|
||||
version = raw_tiled_map["version"]
|
||||
|
||||
# `map` is a built-in function
|
||||
map_ = TiledMap(
|
||||
map_file=file,
|
||||
infinite=raw_tiled_map["infinite"],
|
||||
layers=[layer.cast(layer_, parent_dir) for layer_ in raw_tiled_map["layers"]],
|
||||
map_size=Size(raw_tiled_map["width"], raw_tiled_map["height"]),
|
||||
next_layer_id=raw_tiled_map["nextlayerid"],
|
||||
next_object_id=raw_tiled_map["nextobjectid"],
|
||||
orientation=raw_tiled_map["orientation"],
|
||||
render_order=raw_tiled_map["renderorder"],
|
||||
tiled_version=raw_tiled_map["tiledversion"],
|
||||
tile_size=Size(raw_tiled_map["tilewidth"], raw_tiled_map["tileheight"]),
|
||||
tilesets=tilesets,
|
||||
version=version,
|
||||
)
|
||||
|
||||
layers = [layer for layer in map_.layers if hasattr(layer, "tiled_objects")]
|
||||
|
||||
for my_layer in layers:
|
||||
for tiled_object in my_layer.tiled_objects: # type: ignore
|
||||
if hasattr(tiled_object, "new_tileset"):
|
||||
if tiled_object.new_tileset:
|
||||
already_loaded = None
|
||||
for val in map_.tilesets.values():
|
||||
if val.name == tiled_object.new_tileset["name"]:
|
||||
already_loaded = val
|
||||
break
|
||||
|
||||
if not already_loaded:
|
||||
highest_firstgid = max(map_.tilesets.keys())
|
||||
last_tileset_count = map_.tilesets[highest_firstgid].tile_count
|
||||
new_firstgid = highest_firstgid + last_tileset_count
|
||||
map_.tilesets[new_firstgid] = tileset.cast(
|
||||
tiled_object.new_tileset,
|
||||
new_firstgid,
|
||||
tiled_object.new_tileset_path,
|
||||
)
|
||||
tiled_object.gid = tiled_object.gid + (new_firstgid - 1)
|
||||
|
||||
else:
|
||||
tiled_object.gid = tiled_object.gid + (
|
||||
already_loaded.firstgid - 1
|
||||
)
|
||||
|
||||
tiled_object.new_tileset = None
|
||||
tiled_object.new_tileset_path = None
|
||||
|
||||
if raw_tiled_map.get("backgroundcolor") is not None:
|
||||
map_.background_color = parse_color(raw_tiled_map["backgroundcolor"])
|
||||
|
||||
if raw_tiled_map.get("hexsidelength") is not None:
|
||||
map_.hex_side_length = raw_tiled_map["hexsidelength"]
|
||||
|
||||
if raw_tiled_map.get("properties") is not None:
|
||||
map_.properties = properties.cast(raw_tiled_map["properties"])
|
||||
|
||||
if raw_tiled_map.get("staggeraxis") is not None:
|
||||
map_.stagger_axis = raw_tiled_map["staggeraxis"]
|
||||
|
||||
if raw_tiled_map.get("staggerindex") is not None:
|
||||
map_.stagger_index = raw_tiled_map["staggerindex"]
|
||||
|
||||
return map_
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# pylint: disable=too-few-public-methods
|
||||
import json
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, List, Optional, Union
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import attr
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from . import properties as properties_
|
||||
from .common_types import Color, OrderedPair, Size
|
||||
from .util import parse_color
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True, kw_only=True)
|
||||
@@ -37,10 +35,9 @@ class TiledObject:
|
||||
coordinates: OrderedPair
|
||||
size: Size = Size(0, 0)
|
||||
rotation: float = 0
|
||||
visible: bool
|
||||
|
||||
name: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
visible: bool = True
|
||||
name: str = ""
|
||||
type: str = ""
|
||||
|
||||
properties: properties_.Properties = {}
|
||||
|
||||
@@ -148,302 +145,5 @@ class Tile(TiledObject):
|
||||
"""
|
||||
|
||||
gid: int
|
||||
new_tileset: Optional[Dict[str, Any]] = None
|
||||
new_tileset: Optional[Union[etree.Element, Dict[str, Any]]] = None
|
||||
new_tileset_path: Optional[Path] = None
|
||||
|
||||
|
||||
class RawTextDict(TypedDict):
|
||||
""" The keys and their types that appear in a Text JSON Object."""
|
||||
|
||||
text: str
|
||||
color: str
|
||||
|
||||
fontfamily: str
|
||||
pixelsize: float # this is `font_size` in Text
|
||||
|
||||
bold: bool
|
||||
italic: bool
|
||||
strikeout: bool
|
||||
underline: bool
|
||||
kerning: bool
|
||||
|
||||
halign: str
|
||||
valign: str
|
||||
wrap: bool
|
||||
|
||||
|
||||
class RawTiledObject(TypedDict):
|
||||
""" The keys and their types that appear in a Tiled JSON Object."""
|
||||
|
||||
id: int
|
||||
gid: int
|
||||
template: str
|
||||
x: float
|
||||
y: float
|
||||
width: float
|
||||
height: float
|
||||
rotation: float
|
||||
visible: bool
|
||||
name: str
|
||||
type: str
|
||||
properties: List[properties_.RawProperty]
|
||||
ellipse: bool
|
||||
point: bool
|
||||
polygon: List[Dict[str, float]]
|
||||
polyline: List[Dict[str, float]]
|
||||
text: Dict[str, Union[float, str]]
|
||||
|
||||
|
||||
RawTiledObjects = List[RawTiledObject]
|
||||
|
||||
|
||||
def _get_common_attributes(raw_tiled_object: RawTiledObject) -> TiledObject:
|
||||
"""Create a TiledObject containing all the attributes common to all tiled objects
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object get common attributes from
|
||||
|
||||
Returns:
|
||||
TiledObject: The attributes in common of all Tiled Objects
|
||||
"""
|
||||
|
||||
common_attributes = TiledObject(
|
||||
id=raw_tiled_object["id"],
|
||||
coordinates=OrderedPair(raw_tiled_object["x"], raw_tiled_object["y"]),
|
||||
visible=raw_tiled_object["visible"],
|
||||
size=Size(raw_tiled_object["width"], raw_tiled_object["height"]),
|
||||
rotation=raw_tiled_object["rotation"],
|
||||
name=raw_tiled_object["name"],
|
||||
type=raw_tiled_object["type"],
|
||||
)
|
||||
|
||||
if raw_tiled_object.get("properties") is not None:
|
||||
common_attributes.properties = properties_.cast(raw_tiled_object["properties"])
|
||||
|
||||
return common_attributes
|
||||
|
||||
|
||||
def _cast_ellipse(raw_tiled_object: RawTiledObject) -> Ellipse:
|
||||
"""Cast the raw_tiled_object to an Ellipse object.
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object to be casted to an Ellipse
|
||||
|
||||
Returns:
|
||||
Ellipse: The Ellipse object created from the raw_tiled_object
|
||||
"""
|
||||
return Ellipse(**_get_common_attributes(raw_tiled_object).__dict__)
|
||||
|
||||
|
||||
def _cast_rectangle(raw_tiled_object: RawTiledObject) -> Rectangle:
|
||||
"""Cast the raw_tiled_object to a Rectangle object.
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object to be casted to a Rectangle
|
||||
|
||||
Returns:
|
||||
Rectangle: The Rectangle object created from the raw_tiled_object
|
||||
"""
|
||||
return Rectangle(**_get_common_attributes(raw_tiled_object).__dict__)
|
||||
|
||||
|
||||
def _cast_point(raw_tiled_object: RawTiledObject) -> Point:
|
||||
"""Cast the raw_tiled_object to a Point object.
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object to be casted to a Point
|
||||
|
||||
Returns:
|
||||
Point: The Point object created from the raw_tiled_object
|
||||
"""
|
||||
return Point(**_get_common_attributes(raw_tiled_object).__dict__)
|
||||
|
||||
|
||||
def _cast_tile(
|
||||
raw_tiled_object: RawTiledObject,
|
||||
new_tileset: Optional[Dict[str, Any]] = None,
|
||||
new_tileset_path: Optional[Path] = None,
|
||||
) -> Tile:
|
||||
"""Cast the raw_tiled_object to a Tile object.
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object to be casted to a Tile
|
||||
|
||||
Returns:
|
||||
Tile: The Tile object created from the raw_tiled_object
|
||||
"""
|
||||
gid = raw_tiled_object["gid"]
|
||||
|
||||
return Tile(
|
||||
gid=gid,
|
||||
new_tileset=new_tileset,
|
||||
new_tileset_path=new_tileset_path,
|
||||
**_get_common_attributes(raw_tiled_object).__dict__
|
||||
)
|
||||
|
||||
|
||||
def _cast_polygon(raw_tiled_object: RawTiledObject) -> Polygon:
|
||||
"""Cast the raw_tiled_object to a Polygon object.
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object to be casted to a Polygon
|
||||
|
||||
Returns:
|
||||
Polygon: The Polygon object created from the raw_tiled_object
|
||||
"""
|
||||
polygon = []
|
||||
for point in raw_tiled_object["polygon"]:
|
||||
polygon.append(OrderedPair(point["x"], point["y"]))
|
||||
|
||||
return Polygon(points=polygon, **_get_common_attributes(raw_tiled_object).__dict__)
|
||||
|
||||
|
||||
def _cast_polyline(raw_tiled_object: RawTiledObject) -> Polyline:
|
||||
"""Cast the raw_tiled_object to a Polyline object.
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled Object to be casted to a Polyline
|
||||
|
||||
Returns:
|
||||
Polyline: The Polyline object created from the raw_tiled_object
|
||||
"""
|
||||
polyline = []
|
||||
for point in raw_tiled_object["polyline"]:
|
||||
polyline.append(OrderedPair(point["x"], point["y"]))
|
||||
|
||||
return Polyline(
|
||||
points=polyline, **_get_common_attributes(raw_tiled_object).__dict__
|
||||
)
|
||||
|
||||
|
||||
def _cast_text(raw_tiled_object: RawTiledObject) -> Text:
|
||||
"""Cast the raw_tiled_object to a Text object.
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object to be casted to a Text object
|
||||
|
||||
Returns:
|
||||
Text: The Text object created from the raw_tiled_object
|
||||
"""
|
||||
# required attributes
|
||||
raw_text_dict: RawTextDict = raw_tiled_object["text"]
|
||||
text = raw_text_dict["text"]
|
||||
|
||||
# create base Text object
|
||||
text_object = Text(text=text, **_get_common_attributes(raw_tiled_object).__dict__)
|
||||
|
||||
# optional attributes
|
||||
if raw_text_dict.get("color") is not None:
|
||||
text_object.color = parse_color(raw_text_dict["color"])
|
||||
|
||||
if raw_text_dict.get("fontfamily") is not None:
|
||||
text_object.font_family = raw_text_dict["fontfamily"]
|
||||
|
||||
if raw_text_dict.get("pixelsize") is not None:
|
||||
text_object.font_size = raw_text_dict["pixelsize"]
|
||||
|
||||
if raw_text_dict.get("bold") is not None:
|
||||
text_object.bold = raw_text_dict["bold"]
|
||||
|
||||
if raw_text_dict.get("italic") is not None:
|
||||
text_object.italic = raw_text_dict["italic"]
|
||||
|
||||
if raw_text_dict.get("kerning") is not None:
|
||||
text_object.kerning = raw_text_dict["kerning"]
|
||||
|
||||
if raw_text_dict.get("strikeout") is not None:
|
||||
text_object.strike_out = raw_text_dict["strikeout"]
|
||||
|
||||
if raw_text_dict.get("underline") is not None:
|
||||
text_object.underline = raw_text_dict["underline"]
|
||||
|
||||
if raw_text_dict.get("halign") is not None:
|
||||
text_object.horizontal_align = raw_text_dict["halign"]
|
||||
|
||||
if raw_text_dict.get("valign") is not None:
|
||||
text_object.vertical_align = raw_text_dict["valign"]
|
||||
|
||||
if raw_text_dict.get("wrap") is not None:
|
||||
text_object.wrap = raw_text_dict["wrap"]
|
||||
|
||||
return text_object
|
||||
|
||||
|
||||
def _get_caster(
|
||||
raw_tiled_object: RawTiledObject,
|
||||
) -> Callable[[RawTiledObject], TiledObject]:
|
||||
"""Get the caster function for the raw tiled object.
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object that is analysed to determine which caster
|
||||
to return.
|
||||
|
||||
Returns:
|
||||
Callable[[RawTiledObject], TiledObject]: The caster function.
|
||||
"""
|
||||
if raw_tiled_object.get("ellipse"):
|
||||
return _cast_ellipse
|
||||
|
||||
if raw_tiled_object.get("point"):
|
||||
return _cast_point
|
||||
|
||||
if raw_tiled_object.get("gid"):
|
||||
# Only Tile objects have the `gid` key (I think)
|
||||
return _cast_tile
|
||||
|
||||
if raw_tiled_object.get("polygon"):
|
||||
return _cast_polygon
|
||||
|
||||
if raw_tiled_object.get("polyline"):
|
||||
return _cast_polyline
|
||||
|
||||
if raw_tiled_object.get("text"):
|
||||
return _cast_text
|
||||
|
||||
return _cast_rectangle
|
||||
|
||||
|
||||
def cast(
|
||||
raw_tiled_object: RawTiledObject,
|
||||
parent_dir: Optional[Path] = None,
|
||||
) -> TiledObject:
|
||||
"""Cast the raw tiled object into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_tiled_object: Raw Tiled object that is to be cast.
|
||||
parent_dir: The parent directory that the map file is in.
|
||||
|
||||
Returns:
|
||||
TiledObject: a properly typed Tiled object.
|
||||
|
||||
Raises:
|
||||
RuntimeError: When a required parameter was not sent based on a condition.
|
||||
"""
|
||||
new_tileset = None
|
||||
new_tileset_path = None
|
||||
|
||||
if raw_tiled_object.get("template"):
|
||||
if not parent_dir:
|
||||
raise RuntimeError(
|
||||
"A parent directory must be specified when using object templates"
|
||||
)
|
||||
template_path = Path(parent_dir / raw_tiled_object["template"])
|
||||
with open(template_path) as raw_template_file:
|
||||
template = json.load(raw_template_file)
|
||||
if "tileset" in template:
|
||||
tileset_path = Path(
|
||||
template_path.parent / template["tileset"]["source"]
|
||||
)
|
||||
with open(tileset_path) as raw_tileset_file:
|
||||
new_tileset = json.load(raw_tileset_file)
|
||||
new_tileset_path = tileset_path.parent
|
||||
|
||||
loaded_template = template["object"]
|
||||
for key in loaded_template:
|
||||
if key != "id":
|
||||
raw_tiled_object[key] = loaded_template[key] # type: ignore
|
||||
|
||||
if raw_tiled_object.get("gid"):
|
||||
return _cast_tile(raw_tiled_object, new_tileset, new_tileset_path)
|
||||
|
||||
return _get_caster(raw_tiled_object)(raw_tiled_object)
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
# pylint: disable=too-few-public-methods
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, NamedTuple, Optional, Union
|
||||
from typing import Dict, List, NamedTuple, Optional
|
||||
|
||||
import attr
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from . import layer
|
||||
from . import properties as properties_
|
||||
from .common_types import Color, OrderedPair
|
||||
from .util import parse_color
|
||||
from .wang_set import RawWangSet, WangSet
|
||||
from .wang_set import cast as cast_wangset
|
||||
from .wang_set import WangSet
|
||||
|
||||
|
||||
class Grid(NamedTuple):
|
||||
@@ -153,261 +150,3 @@ class Tileset:
|
||||
properties: Optional[properties_.Properties] = None
|
||||
tiles: Optional[Dict[int, Tile]] = None
|
||||
wang_sets: Optional[List[WangSet]] = None
|
||||
|
||||
|
||||
class RawFrame(TypedDict):
|
||||
""" The keys and their types that appear in a Frame JSON Object."""
|
||||
|
||||
duration: int
|
||||
tileid: int
|
||||
|
||||
|
||||
class RawTileOffset(TypedDict):
|
||||
""" The keys and their types that appear in a TileOffset JSON Object."""
|
||||
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
class RawTransformations(TypedDict):
|
||||
""" The keys and their types that appear in a Transformations JSON Object."""
|
||||
|
||||
hflip: bool
|
||||
vflip: bool
|
||||
rotate: bool
|
||||
preferuntransformed: bool
|
||||
|
||||
|
||||
class RawTile(TypedDict):
|
||||
""" The keys and their types that appear in a Tile JSON Object."""
|
||||
|
||||
animation: List[RawFrame]
|
||||
id: int
|
||||
image: str
|
||||
imageheight: int
|
||||
imagewidth: int
|
||||
opacity: float
|
||||
properties: List[properties_.RawProperty]
|
||||
objectgroup: layer.RawLayer
|
||||
type: str
|
||||
|
||||
|
||||
class RawGrid(TypedDict):
|
||||
""" The keys and their types that appear in a Grid JSON Object."""
|
||||
|
||||
height: int
|
||||
width: int
|
||||
orientation: str
|
||||
|
||||
|
||||
class RawTileSet(TypedDict):
|
||||
""" The keys and their types that appear in a TileSet JSON Object."""
|
||||
|
||||
backgroundcolor: str
|
||||
columns: int
|
||||
firstgid: int
|
||||
grid: RawGrid
|
||||
image: str
|
||||
imageheight: int
|
||||
imagewidth: int
|
||||
margin: int
|
||||
name: str
|
||||
properties: List[properties_.RawProperty]
|
||||
source: str
|
||||
spacing: int
|
||||
tilecount: int
|
||||
tiledversion: str
|
||||
tileheight: int
|
||||
tileoffset: RawTileOffset
|
||||
tiles: List[RawTile]
|
||||
tilewidth: int
|
||||
transparentcolor: str
|
||||
transformations: RawTransformations
|
||||
version: Union[str, float]
|
||||
wangsets: List[RawWangSet]
|
||||
|
||||
|
||||
def _cast_frame(raw_frame: RawFrame) -> Frame:
|
||||
"""Cast the raw_frame to a Frame.
|
||||
|
||||
Args:
|
||||
raw_frame: RawFrame to be casted to a Frame
|
||||
|
||||
Returns:
|
||||
Frame: The Frame created from the raw_frame
|
||||
"""
|
||||
|
||||
return Frame(duration=raw_frame["duration"], tile_id=raw_frame["tileid"])
|
||||
|
||||
|
||||
def _cast_tile_offset(raw_tile_offset: RawTileOffset) -> OrderedPair:
|
||||
"""Cast the raw_tile_offset to an OrderedPair.
|
||||
|
||||
Args:
|
||||
raw_tile_offset: RawTileOffset to be casted to an OrderedPair
|
||||
|
||||
Returns:
|
||||
OrderedPair: The OrderedPair created from the raw_tile_offset
|
||||
"""
|
||||
|
||||
return OrderedPair(raw_tile_offset["x"], raw_tile_offset["y"])
|
||||
|
||||
|
||||
def _cast_tile(raw_tile: RawTile, external_path: Optional[Path] = None) -> Tile:
|
||||
"""Cast the raw_tile to a Tile object.
|
||||
|
||||
Args:
|
||||
raw_tile: RawTile to be casted to a Tile
|
||||
|
||||
Returns:
|
||||
Tile: The Tile created from the raw_tile
|
||||
"""
|
||||
|
||||
id_ = raw_tile["id"]
|
||||
tile = Tile(id=id_)
|
||||
|
||||
if raw_tile.get("animation") is not None:
|
||||
tile.animation = []
|
||||
for frame in raw_tile["animation"]:
|
||||
tile.animation.append(_cast_frame(frame))
|
||||
|
||||
if raw_tile.get("objectgroup") is not None:
|
||||
tile.objects = layer.cast(raw_tile["objectgroup"])
|
||||
|
||||
if raw_tile.get("properties") is not None:
|
||||
tile.properties = properties_.cast(raw_tile["properties"])
|
||||
|
||||
if raw_tile.get("image") is not None:
|
||||
if external_path:
|
||||
tile.image = Path(external_path / raw_tile["image"]).absolute().resolve()
|
||||
else:
|
||||
tile.image = Path(raw_tile["image"])
|
||||
|
||||
if raw_tile.get("imagewidth") is not None:
|
||||
tile.image_width = raw_tile["imagewidth"]
|
||||
|
||||
if raw_tile.get("imageheight") is not None:
|
||||
tile.image_height = raw_tile["imageheight"]
|
||||
|
||||
if raw_tile.get("type") is not None:
|
||||
tile.type = raw_tile["type"]
|
||||
|
||||
return tile
|
||||
|
||||
|
||||
def _cast_transformations(raw_transformations: RawTransformations) -> Transformations:
|
||||
"""Cast the raw_transformations to a Transformations object.
|
||||
|
||||
Args:
|
||||
raw_transformations: RawTransformations to be casted to a Transformations
|
||||
|
||||
Returns:
|
||||
Transformations: The Transformations created from the raw_transformations
|
||||
"""
|
||||
|
||||
return Transformations(
|
||||
hflip=raw_transformations["hflip"],
|
||||
vflip=raw_transformations["vflip"],
|
||||
rotate=raw_transformations["rotate"],
|
||||
prefer_untransformed=raw_transformations["preferuntransformed"],
|
||||
)
|
||||
|
||||
|
||||
def _cast_grid(raw_grid: RawGrid) -> Grid:
|
||||
"""Cast the raw_grid to a Grid object.
|
||||
|
||||
Args:
|
||||
raw_grid: RawGrid to be casted to a Grid
|
||||
|
||||
Returns:
|
||||
Grid: The Grid created from the raw_grid
|
||||
"""
|
||||
|
||||
return Grid(
|
||||
orientation=raw_grid["orientation"],
|
||||
width=raw_grid["width"],
|
||||
height=raw_grid["height"],
|
||||
)
|
||||
|
||||
|
||||
def cast(
|
||||
raw_tileset: RawTileSet,
|
||||
firstgid: int,
|
||||
external_path: Optional[Path] = None,
|
||||
) -> Tileset:
|
||||
"""Cast the raw tileset into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_tileset: Raw Tileset to be cast.
|
||||
firstgid: GID corresponding the first tile in the set.
|
||||
external_path: The path to the tileset if it is not an embedded one.
|
||||
|
||||
Returns:
|
||||
TileSet: a properly typed TileSet.
|
||||
"""
|
||||
|
||||
tileset = Tileset(
|
||||
name=raw_tileset["name"],
|
||||
tile_count=raw_tileset["tilecount"],
|
||||
tile_width=raw_tileset["tilewidth"],
|
||||
tile_height=raw_tileset["tileheight"],
|
||||
columns=raw_tileset["columns"],
|
||||
spacing=raw_tileset["spacing"],
|
||||
margin=raw_tileset["margin"],
|
||||
firstgid=firstgid,
|
||||
)
|
||||
|
||||
if raw_tileset.get("version") is not None:
|
||||
if isinstance(raw_tileset["version"], float):
|
||||
tileset.version = str(raw_tileset["version"])
|
||||
else:
|
||||
tileset.version = raw_tileset["version"]
|
||||
|
||||
if raw_tileset.get("tiledversion") is not None:
|
||||
tileset.tiled_version = raw_tileset["tiledversion"]
|
||||
|
||||
if raw_tileset.get("image") is not None:
|
||||
if external_path:
|
||||
tileset.image = (
|
||||
Path(external_path / raw_tileset["image"]).absolute().resolve()
|
||||
)
|
||||
else:
|
||||
tileset.image = Path(raw_tileset["image"])
|
||||
|
||||
if raw_tileset.get("imagewidth") is not None:
|
||||
tileset.image_width = raw_tileset["imagewidth"]
|
||||
|
||||
if raw_tileset.get("imageheight") is not None:
|
||||
tileset.image_height = raw_tileset["imageheight"]
|
||||
|
||||
if raw_tileset.get("backgroundcolor") is not None:
|
||||
tileset.background_color = parse_color(raw_tileset["backgroundcolor"])
|
||||
|
||||
if raw_tileset.get("tileoffset") is not None:
|
||||
tileset.tile_offset = _cast_tile_offset(raw_tileset["tileoffset"])
|
||||
|
||||
if raw_tileset.get("transparentcolor") is not None:
|
||||
tileset.transparent_color = parse_color(raw_tileset["transparentcolor"])
|
||||
|
||||
if raw_tileset.get("grid") is not None:
|
||||
tileset.grid = _cast_grid(raw_tileset["grid"])
|
||||
|
||||
if raw_tileset.get("properties") is not None:
|
||||
tileset.properties = properties_.cast(raw_tileset["properties"])
|
||||
|
||||
if raw_tileset.get("tiles") is not None:
|
||||
tiles = {}
|
||||
for raw_tile in raw_tileset["tiles"]:
|
||||
tiles[raw_tile["id"]] = _cast_tile(raw_tile, external_path=external_path)
|
||||
tileset.tiles = tiles
|
||||
|
||||
if raw_tileset.get("wangsets") is not None:
|
||||
wangsets = []
|
||||
for raw_wangset in raw_tileset["wangsets"]:
|
||||
wangsets.append(cast_wangset(raw_wangset))
|
||||
tileset.wang_sets = wangsets
|
||||
|
||||
if raw_tileset.get("transformations") is not None:
|
||||
tileset.transformations = _cast_transformations(raw_tileset["transformations"])
|
||||
|
||||
return tileset
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
"""Utility Functions for PyTiled"""
|
||||
import json
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from pytiled_parser.common_types import Color
|
||||
|
||||
@@ -27,3 +31,52 @@ def parse_color(color: str) -> Color:
|
||||
)
|
||||
|
||||
raise ValueError("Improperly formatted color passed to parse_color")
|
||||
|
||||
|
||||
def check_format(file_path: Path) -> str:
|
||||
with open(file_path) as file:
|
||||
line = file.readline().rstrip().strip()
|
||||
if line[0] == "<":
|
||||
return "tmx"
|
||||
else:
|
||||
return "json"
|
||||
|
||||
|
||||
def load_object_template(file_path: Path) -> Any:
|
||||
template_format = check_format(file_path)
|
||||
|
||||
new_tileset = None
|
||||
new_tileset_path = None
|
||||
|
||||
if template_format == "tmx":
|
||||
with open(file_path) as template_file:
|
||||
template = etree.parse(template_file).getroot()
|
||||
|
||||
tileset_element = template.find("./tileset")
|
||||
if tileset_element is not None:
|
||||
tileset_path = Path(file_path.parent / tileset_element.attrib["source"])
|
||||
new_tileset = load_object_tileset(tileset_path)
|
||||
new_tileset_path = tileset_path.parent
|
||||
elif template_format == "json":
|
||||
with open(file_path) as template_file:
|
||||
template = json.load(template_file)
|
||||
if "tileset" in template:
|
||||
tileset_path = Path(file_path.parent / template["tileset"]["source"]) # type: ignore
|
||||
new_tileset = load_object_tileset(tileset_path)
|
||||
new_tileset_path = tileset_path.parent
|
||||
|
||||
return (template, new_tileset, new_tileset_path)
|
||||
|
||||
|
||||
def load_object_tileset(file_path: Path) -> Any:
|
||||
tileset_format = check_format(file_path)
|
||||
|
||||
new_tileset = None
|
||||
|
||||
with open(file_path) as tileset_file:
|
||||
if tileset_format == "tmx":
|
||||
new_tileset = etree.parse(tileset_file).getroot()
|
||||
elif tileset_format == "json":
|
||||
new_tileset = json.load(tileset_file)
|
||||
|
||||
return new_tileset
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""pytiled_parser version"""
|
||||
|
||||
__version__ = "1.5.4"
|
||||
__version__ = "2.0.0-beta"
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import attr
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from . import properties as properties_
|
||||
from .common_types import Color
|
||||
from .util import parse_color
|
||||
from pytiled_parser.common_types import Color
|
||||
from pytiled_parser.properties import Properties
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
@@ -22,7 +20,7 @@ class WangColor:
|
||||
name: str
|
||||
probability: float
|
||||
tile: int
|
||||
properties: Optional[properties_.Properties] = None
|
||||
properties: Optional[Properties] = None
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
@@ -33,100 +31,4 @@ class WangSet:
|
||||
wang_type: str
|
||||
wang_tiles: Dict[int, WangTile]
|
||||
wang_colors: List[WangColor]
|
||||
properties: Optional[properties_.Properties] = None
|
||||
|
||||
|
||||
class RawWangTile(TypedDict):
|
||||
""" The keys and their types that appear in a Wang Tile JSON Object."""
|
||||
|
||||
tileid: int
|
||||
# Tiled stores these IDs as a list represented like so:
|
||||
# [top, top_right, right, bottom_right, bottom, bottom_left, left, top_left]
|
||||
wangid: List[int]
|
||||
|
||||
|
||||
class RawWangColor(TypedDict):
|
||||
""" The keys and their types that appear in a Wang Color JSON Object."""
|
||||
|
||||
color: str
|
||||
name: str
|
||||
probability: float
|
||||
tile: int
|
||||
properties: List[properties_.RawProperty]
|
||||
|
||||
|
||||
class RawWangSet(TypedDict):
|
||||
""" The keys and their types that appear in a Wang Set JSON Object."""
|
||||
|
||||
colors: List[RawWangColor]
|
||||
name: str
|
||||
properties: List[properties_.RawProperty]
|
||||
tile: int
|
||||
type: str
|
||||
wangtiles: List[RawWangTile]
|
||||
|
||||
|
||||
def _cast_wang_tile(raw_wang_tile: RawWangTile) -> WangTile:
|
||||
"""Cast the raw wang tile into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wang_tile: RawWangTile to be cast.
|
||||
|
||||
Returns:
|
||||
WangTile: A properly typed WangTile.
|
||||
"""
|
||||
return WangTile(tile_id=raw_wang_tile["tileid"], wang_id=raw_wang_tile["wangid"])
|
||||
|
||||
|
||||
def _cast_wang_color(raw_wang_color: RawWangColor) -> WangColor:
|
||||
"""Cast the raw wang color into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wang_color: RawWangColor to be cast.
|
||||
|
||||
Returns:
|
||||
WangColor: A properly typed WangColor.
|
||||
"""
|
||||
wang_color = WangColor(
|
||||
name=raw_wang_color["name"],
|
||||
color=parse_color(raw_wang_color["color"]),
|
||||
tile=raw_wang_color["tile"],
|
||||
probability=raw_wang_color["probability"],
|
||||
)
|
||||
|
||||
if raw_wang_color.get("properties") is not None:
|
||||
wang_color.properties = properties_.cast(raw_wang_color["properties"])
|
||||
|
||||
return wang_color
|
||||
|
||||
|
||||
def cast(raw_wangset: RawWangSet) -> WangSet:
|
||||
"""Cast the raw wangset into a pytiled_parser type
|
||||
|
||||
Args:
|
||||
raw_wangset: Raw Wangset to be cast.
|
||||
|
||||
Returns:
|
||||
WangSet: A properly typed WangSet.
|
||||
"""
|
||||
|
||||
colors = []
|
||||
for raw_wang_color in raw_wangset["colors"]:
|
||||
colors.append(_cast_wang_color(raw_wang_color))
|
||||
|
||||
tiles = {}
|
||||
for raw_wang_tile in raw_wangset["wangtiles"]:
|
||||
tiles[raw_wang_tile["tileid"]] = _cast_wang_tile(raw_wang_tile)
|
||||
|
||||
wangset = WangSet(
|
||||
name=raw_wangset["name"],
|
||||
tile=raw_wangset["tile"],
|
||||
wang_type=raw_wangset["type"],
|
||||
wang_colors=colors,
|
||||
wang_tiles=tiles,
|
||||
)
|
||||
|
||||
if raw_wangset.get("properties") is not None:
|
||||
wangset.properties = properties_.cast(raw_wangset["properties"])
|
||||
|
||||
return wangset
|
||||
properties: Optional[Properties] = None
|
||||
|
||||
@@ -8,8 +8,9 @@ from typing import List
|
||||
import attr
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
from .common_types import OrderedPair, Size
|
||||
from .tiled_map import TiledMap, parse_map
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
from pytiled_parser.parser import parse_map
|
||||
from pytiled_parser.tiled_map import TiledMap
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
@@ -55,7 +56,7 @@ class RawWorld(TypedDict):
|
||||
onlyShowAdjacentMaps: bool
|
||||
|
||||
|
||||
def _cast_world_map(raw_world_map: RawWorldMap, map_file: Path) -> WorldMap:
|
||||
def _parse_world_map(raw_world_map: RawWorldMap, map_file: Path) -> WorldMap:
|
||||
"""Parse the RawWorldMap into a WorldMap.
|
||||
|
||||
Args:
|
||||
@@ -94,7 +95,7 @@ def parse_world(file: Path) -> World:
|
||||
if raw_world.get("maps"):
|
||||
for raw_map in raw_world["maps"]:
|
||||
map_path = Path(parent_dir / raw_map["fileName"])
|
||||
maps.append(_cast_world_map(raw_map, map_path))
|
||||
maps.append(_parse_world_map(raw_map, map_path))
|
||||
|
||||
if raw_world.get("patterns"):
|
||||
for raw_pattern in raw_world["patterns"]:
|
||||
@@ -131,7 +132,7 @@ def parse_world(file: Path) -> World:
|
||||
}
|
||||
|
||||
map_path = Path(parent_dir / map_file)
|
||||
maps.append(_cast_world_map(raw_world_map, map_path))
|
||||
maps.append(_parse_world_map(raw_world_map, map_path))
|
||||
|
||||
world = World(maps=maps)
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ tests =
|
||||
pytest
|
||||
pytest-cov
|
||||
black
|
||||
pylint
|
||||
flake8
|
||||
mypy
|
||||
isort<5,>=4.2.5
|
||||
|
||||
@@ -104,3 +104,7 @@ strict_optional = True
|
||||
|
||||
[mypy-tests.*]
|
||||
ignore_errors = True
|
||||
|
||||
[flake8]
|
||||
max-line-length = 88
|
||||
exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache
|
||||
|
||||
73
tests/test_cross_template/map.json
Normal file
73
tests/test_cross_template/map.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{ "backgroundcolor":"#ff0004",
|
||||
"compressionlevel":0,
|
||||
"height":6,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"draworder":"topdown",
|
||||
"id":2,
|
||||
"name":"Object Layer 1",
|
||||
"objects":[
|
||||
{
|
||||
"id":2,
|
||||
"template":"template-rectangle.tx",
|
||||
"x":98.4987608686521,
|
||||
"y":46.2385012811358
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":3,
|
||||
"nextobjectid":8,
|
||||
"orientation":"orthogonal",
|
||||
"properties":[
|
||||
{
|
||||
"name":"bool property - true",
|
||||
"type":"bool",
|
||||
"value":true
|
||||
},
|
||||
{
|
||||
"name":"color property",
|
||||
"type":"color",
|
||||
"value":"#ff49fcff"
|
||||
},
|
||||
{
|
||||
"name":"file property",
|
||||
"type":"file",
|
||||
"value":"..\/..\/..\/..\/..\/..\/var\/log\/syslog"
|
||||
},
|
||||
{
|
||||
"name":"float property",
|
||||
"type":"float",
|
||||
"value":1.23456789
|
||||
},
|
||||
{
|
||||
"name":"int property",
|
||||
"type":"int",
|
||||
"value":13
|
||||
},
|
||||
{
|
||||
"name":"string property",
|
||||
"type":"string",
|
||||
"value":"Hello, World!!"
|
||||
}],
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"1.7.1",
|
||||
"tileheight":32,
|
||||
"tilesets":[
|
||||
{
|
||||
"firstgid":1,
|
||||
"source":"tileset.json"
|
||||
},
|
||||
{
|
||||
"firstgid":49,
|
||||
"source":"tile_set_image_for_template.json"
|
||||
}],
|
||||
"tilewidth":32,
|
||||
"type":"map",
|
||||
"version":"1.6",
|
||||
"width":8
|
||||
}
|
||||
16
tests/test_cross_template/map.tmx
Normal file
16
tests/test_cross_template/map.tmx
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" backgroundcolor="#ff0004" nextlayerid="3" nextobjectid="8">
|
||||
<properties>
|
||||
<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="tileset.tsx"/>
|
||||
<tileset firstgid="49" source="tile_set_image_for_template.tsx"/>
|
||||
<objectgroup id="2" name="Object Layer 1">
|
||||
<object id="2" template="template-rectangle.json" x="98.4988" y="46.2385"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
12
tests/test_cross_template/template-rectangle.json
Normal file
12
tests/test_cross_template/template-rectangle.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{ "object":
|
||||
{
|
||||
"height":38.2811778048473,
|
||||
"id":1,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"type":"",
|
||||
"visible":true,
|
||||
"width":63.6585878103079
|
||||
},
|
||||
"type":"template"
|
||||
}
|
||||
4
tests/test_cross_template/template-rectangle.tx
Normal file
4
tests/test_cross_template/template-rectangle.tx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
<object width="63.6586" height="38.2812"/>
|
||||
</template>
|
||||
16
tests/test_cross_template/test_cross_template.py
Normal file
16
tests/test_cross_template/test_cross_template.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from pytiled_parser import parse_map
|
||||
|
||||
|
||||
def test_cross_template_tmx_json():
|
||||
with pytest.raises(NotImplementedError):
|
||||
parse_map(Path(os.path.dirname(os.path.abspath(__file__))) / "map.tmx")
|
||||
|
||||
|
||||
def test_cross_template_json_tmx():
|
||||
with pytest.raises(NotImplementedError):
|
||||
parse_map(Path(os.path.dirname(os.path.abspath(__file__))) / "map.json")
|
||||
14
tests/test_cross_template/tile_set_image_for_template.json
Normal file
14
tests/test_cross_template/tile_set_image_for_template.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{ "columns":1,
|
||||
"image":"..\/..\/images\/tile_04.png",
|
||||
"imageheight":32,
|
||||
"imagewidth":32,
|
||||
"margin":0,
|
||||
"name":"tile_set_image_for_template",
|
||||
"spacing":0,
|
||||
"tilecount":1,
|
||||
"tiledversion":"1.7.1",
|
||||
"tileheight":32,
|
||||
"tilewidth":32,
|
||||
"type":"tileset",
|
||||
"version":"1.6"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tile_set_image_for_template" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||
<image source="../../images/tile_04.png" width="32" height="32"/>
|
||||
</tileset>
|
||||
14
tests/test_cross_template/tileset.json
Normal file
14
tests/test_cross_template/tileset.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{ "columns":8,
|
||||
"image":"..\/test_data\/images\/tmw_desert_spacing.png",
|
||||
"imageheight":199,
|
||||
"imagewidth":265,
|
||||
"margin":1,
|
||||
"name":"tile_set_image",
|
||||
"spacing":1,
|
||||
"tilecount":48,
|
||||
"tiledversion":"1.6.0",
|
||||
"tileheight":32,
|
||||
"tilewidth":32,
|
||||
"type":"tileset",
|
||||
"version":"1.6"
|
||||
}
|
||||
4
tests/test_cross_template/tileset.tsx
Normal file
4
tests/test_cross_template/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" 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>
|
||||
28
tests/test_data/layer_tests/all_layer_types/map.tmx
Normal file
28
tests/test_data/layer_tests/all_layer_types/map.tmx
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="6" nextobjectid="3">
|
||||
<tileset firstgid="1" source="tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="8" height="6" tintcolor="#aaffff" offsetx="1" offsety="3" parallaxx="1.4" parallaxy="1.3">
|
||||
<properties>
|
||||
<property name="test" value="test property"/>
|
||||
</properties>
|
||||
<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>
|
||||
<group id="4" name="Group 1" tintcolor="#0000ff" parallaxx="1.4">
|
||||
<objectgroup id="2" name="Object Layer 1">
|
||||
<object id="1" x="46.3333" y="39" width="69.3333" height="52.6667"/>
|
||||
</objectgroup>
|
||||
</group>
|
||||
<imagelayer id="3" name="Image Layer 1" tintcolor="#ff0000" offsetx="1" offsety="4">
|
||||
<image source="../../images/tile_04.png" trans="000000" width="32" height="32"/>
|
||||
</imagelayer>
|
||||
<imagelayer id="5" name="Image Layer 2" parallaxy="1.4">
|
||||
<image source="../../images/tile_04.png" width="32" height="32"/>
|
||||
</imagelayer>
|
||||
</map>
|
||||
4
tests/test_data/layer_tests/all_layer_types/tileset.tsx
Normal file
4
tests/test_data/layer_tests/all_layer_types/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" 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>
|
||||
17
tests/test_data/layer_tests/b64/map.tmx
Normal file
17
tests/test_data/layer_tests/b64/map.tmx
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="3">
|
||||
<tileset firstgid="1" source="../all_layer_types/tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="8" height="6">
|
||||
<data encoding="base64">
|
||||
AQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABcAAAAYAAAAGQAAABoAAAAbAAAAHAAAAB0AAAAeAAAAHwAAACAAAAAhAAAAIgAAACMAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8AAAAwAAAA
|
||||
</data>
|
||||
</layer>
|
||||
<group id="4" name="Group 1">
|
||||
<objectgroup id="2" name="Object Layer 1">
|
||||
<object id="1" x="46.3333" y="39" width="69.3333" height="52.6667"/>
|
||||
</objectgroup>
|
||||
</group>
|
||||
<imagelayer id="3" name="Image Layer 1">
|
||||
<image source="../../images/tile_04.png" trans="000000" width="32" height="32"/>
|
||||
</imagelayer>
|
||||
</map>
|
||||
4
tests/test_data/layer_tests/b64/tileset.tsx
Normal file
4
tests/test_data/layer_tests/b64/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" 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>
|
||||
17
tests/test_data/layer_tests/b64_gzip/map.tmx
Normal file
17
tests/test_data/layer_tests/b64_gzip/map.tmx
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="3">
|
||||
<tileset firstgid="1" source="../all_layer_types/tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="8" height="6">
|
||||
<data encoding="base64" compression="gzip">
|
||||
H4sIAAAAAAAACg3DBRKCQAAAwDMRA7BQLMTE9v+vY3dmWyGEth279uwbOTB26MixExNTM6fOnLtwae7KtYUbt+7ce7D0aOXJsxev3rxb+/Dpy7cfv/782wAcvDirwAAAAA==
|
||||
</data>
|
||||
</layer>
|
||||
<group id="4" name="Group 1">
|
||||
<objectgroup id="2" name="Object Layer 1">
|
||||
<object id="1" x="46.3333" y="39" width="69.3333" height="52.6667"/>
|
||||
</objectgroup>
|
||||
</group>
|
||||
<imagelayer id="3" name="Image Layer 1">
|
||||
<image source="../../images/tile_04.png" trans="000000" width="32" height="32"/>
|
||||
</imagelayer>
|
||||
</map>
|
||||
4
tests/test_data/layer_tests/b64_gzip/tileset.tsx
Normal file
4
tests/test_data/layer_tests/b64_gzip/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" 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>
|
||||
17
tests/test_data/layer_tests/b64_zlib/map.tmx
Normal file
17
tests/test_data/layer_tests/b64_zlib/map.tmx
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="5" nextobjectid="3">
|
||||
<tileset firstgid="1" source="../all_layer_types/tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="8" height="6">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eJwNwwUSgkAAAMAzEQOwUCzExPb/r2N3ZlshhLYdu/bsGzkwdujIsRMTUzOnzpy7cGnuyrWFG7fu3Huw9GjlybMXr968W/vw6cu3H7/+/NsAMw8EmQ==
|
||||
</data>
|
||||
</layer>
|
||||
<group id="4" name="Group 1">
|
||||
<objectgroup id="2" name="Object Layer 1">
|
||||
<object id="1" x="46.3333" y="39" width="69.3333" height="52.6667"/>
|
||||
</objectgroup>
|
||||
</group>
|
||||
<imagelayer id="3" name="Image Layer 1">
|
||||
<image source="../../images/tile_04.png" trans="000000" width="32" height="32"/>
|
||||
</imagelayer>
|
||||
</map>
|
||||
4
tests/test_data/layer_tests/b64_zlib/tileset.tsx
Normal file
4
tests/test_data/layer_tests/b64_zlib/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" 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>
|
||||
35
tests/test_data/layer_tests/infinite_map/map.tmx
Normal file
35
tests/test_data/layer_tests/infinite_map/map.tmx
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="1" nextlayerid="6" nextobjectid="3">
|
||||
<editorsettings>
|
||||
<chunksize width="4" height="8"/>
|
||||
<export target="../tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/all_layer_types"/>
|
||||
</editorsettings>
|
||||
<tileset firstgid="1" source="../all_layer_types/tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="16" height="16" offsetx="163.089" offsety="116.463">
|
||||
<properties>
|
||||
<property name="test" value="test property"/>
|
||||
</properties>
|
||||
<data encoding="csv">
|
||||
<chunk x="0" y="0" width="4" height="8">
|
||||
1,2,3,4,
|
||||
9,10,11,12,
|
||||
17,18,19,20,
|
||||
25,26,27,28,
|
||||
33,34,35,36,
|
||||
41,42,43,44,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
</chunk>
|
||||
<chunk x="4" y="0" width="4" height="8">
|
||||
5,6,7,8,
|
||||
13,14,15,16,
|
||||
21,22,23,24,
|
||||
29,30,31,32,
|
||||
37,38,39,40,
|
||||
45,46,47,48,
|
||||
0,0,0,0,
|
||||
0,0,0,0
|
||||
</chunk>
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
||||
14
tests/test_data/layer_tests/infinite_map_b64/map.tmx
Normal file
14
tests/test_data/layer_tests/infinite_map_b64/map.tmx
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" width="8" height="6" tilewidth="32" tileheight="32" infinite="1" nextlayerid="6" nextobjectid="3">
|
||||
<tileset firstgid="1" source="../all_layer_types/tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="16" height="16" offsetx="1" offsety="3">
|
||||
<properties>
|
||||
<property name="test" value="test property"/>
|
||||
</properties>
|
||||
<data encoding="base64">
|
||||
<chunk x="0" y="0" width="16" height="16">
|
||||
AQAAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAABoAAAAbAAAAHAAAAB0AAAAeAAAAHwAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAvAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||
</chunk>
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
||||
13
tests/test_data/layer_tests/no_layers/map.tmx
Normal file
13
tests/test_data/layer_tests/no_layers/map.tmx
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" 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="file property" type="file" value="../tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/test_data/layer_tests/tests/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="tileset.tsx"/>
|
||||
</map>
|
||||
4
tests/test_data/layer_tests/no_layers/tileset.tsx
Normal file
4
tests/test_data/layer_tests/no_layers/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" 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>
|
||||
6
tests/test_data/map_tests/embedded_tileset/map.tmx
Normal file
6
tests/test_data/map_tests/embedded_tileset/map.tmx
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||
<tileset firstgid="1" name="tileset" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
|
||||
<image source="../../images/tmw_desert_spacing.png" width="265" height="199"/>
|
||||
</tileset>
|
||||
</map>
|
||||
17
tests/test_data/map_tests/external_tileset_dif_dir/map.tmx
Normal file
17
tests/test_data/map_tests/external_tileset_dif_dir/map.tmx
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" backgroundcolor="#ff0004" nextlayerid="3" nextobjectid="1">
|
||||
<properties>
|
||||
<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="tileset/tileset.tsx"/>
|
||||
<layer id="2" name="Layer 1" width="8" height="6">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eAFjYWBgYAZiJiBmBOKhBgAIGAAL
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tileset" tilewidth="32" tileheight="32" tilecount="4" columns="0">
|
||||
<grid orientation="orthogonal" width="1" height="1"/>
|
||||
<tile id="0" type="tile">
|
||||
<properties>
|
||||
<property name="float property" type="float" value="2.2"/>
|
||||
</properties>
|
||||
<image width="32" height="32" source="../../../images/tile_01.png"/>
|
||||
<animation>
|
||||
<frame tileid="0" duration="100"/>
|
||||
<frame tileid="1" duration="100"/>
|
||||
<frame tileid="2" duration="100"/>
|
||||
<frame tileid="3" duration="100"/>
|
||||
</animation>
|
||||
</tile>
|
||||
<tile id="1" type="tile">
|
||||
<properties>
|
||||
<property name="string property" value="testing"/>
|
||||
</properties>
|
||||
<image width="32" height="32" source="../../../images/tile_02.png"/>
|
||||
<objectgroup draworder="index">
|
||||
<object id="2" x="13.4358" y="13.5305" width="14.4766" height="13.7197"/>
|
||||
<object id="3" x="13.8143" y="1.98699" width="14.2874" height="11.0704">
|
||||
<ellipse/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</tile>
|
||||
<tile id="2" type="tile">
|
||||
<properties>
|
||||
<property name="bool property" type="bool" value="true"/>
|
||||
</properties>
|
||||
<image width="32" height="32" source="../../../images/tile_03.png"/>
|
||||
</tile>
|
||||
<tile id="3" type="tile">
|
||||
<image width="32" height="32" source="../../../images/tile_04.png"/>
|
||||
</tile>
|
||||
</tileset>
|
||||
18
tests/test_data/map_tests/hexagonal/map.tmx
Normal file
18
tests/test_data/map_tests/hexagonal/map.tmx
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="hexagonal" renderorder="right-down" width="10" height="10" tilewidth="14" tileheight="12" infinite="0" hexsidelength="6" staggeraxis="y" staggerindex="odd" nextlayerid="2" nextobjectid="1">
|
||||
<tileset firstgid="1" source="tileset.tsx"/>
|
||||
<layer id="1" name="Tile Layer 1" width="10" height="10">
|
||||
<data encoding="csv">
|
||||
3,3,3,3,9,9,9,9,17,17,
|
||||
3,3,3,9,9,9,9,17,17,17,
|
||||
3,3,3,9,9,9,9,9,17,17,
|
||||
3,3,1,7,9,9,9,15,17,17,
|
||||
1,1,12,5,7,7,7,15,15,15,
|
||||
12,1,5,5,7,7,7,15,15,15,
|
||||
2,2,5,5,5,5,4,14,14,14,
|
||||
2,2,5,5,5,4,14,14,14,14,
|
||||
2,2,2,5,5,5,4,14,14,14,
|
||||
2,2,2,2,5,5,4,4,14,14
|
||||
</data>
|
||||
</layer>
|
||||
</map>
|
||||
5
tests/test_data/map_tests/hexagonal/tileset.tsx
Normal file
5
tests/test_data/map_tests/hexagonal/tileset.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tileset" tilewidth="18" tileheight="18" tilecount="20" columns="5">
|
||||
<tileoffset x="0" y="1"/>
|
||||
<image source="../../images/hexmini.png" width="106" height="72"/>
|
||||
</tileset>
|
||||
4
tests/test_data/map_tests/no_background_color/map.tmx
Normal file
4
tests/test_data/map_tests/no_background_color/map.tmx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.6" tiledversion="1.6.0" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||
<tileset firstgid="1" source="tileset.tsx"/>
|
||||
</map>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.6" tiledversion="1.6.0" 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>
|
||||
12
tests/test_data/map_tests/no_layers/map.tmx
Normal file
12
tests/test_data/map_tests/no_layers/map.tmx
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" backgroundcolor="#ff0004" nextlayerid="2" nextobjectid="1">
|
||||
<properties>
|
||||
<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="tileset.tsx"/>
|
||||
</map>
|
||||
4
tests/test_data/map_tests/no_layers/tileset.tsx
Normal file
4
tests/test_data/map_tests/no_layers/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.6" tiledversion="1.6.0" 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>
|
||||
18
tests/test_data/map_tests/template/map.tmx
Normal file
18
tests/test_data/map_tests/template/map.tmx
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.5" tiledversion="1.7.0" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="8" height="6" tilewidth="32" tileheight="32" infinite="0" backgroundcolor="#ff0004" nextlayerid="3" nextobjectid="8">
|
||||
<properties>
|
||||
<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="tileset.tsx"/>
|
||||
<tileset firstgid="49" source="tile_set_image_for_template.tsx"/>
|
||||
<objectgroup id="2" name="Object Layer 1">
|
||||
<object id="2" template="template-rectangle.tx" x="98.4988" y="46.2385"/>
|
||||
<object id="6" template="template-tile-spritesheet.tx" x="46" y="136.667"/>
|
||||
<object id="7" template="template-tile-image.tx" x="141.333" y="148"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
4
tests/test_data/map_tests/template/template-rectangle.tx
Normal file
4
tests/test_data/map_tests/template/template-rectangle.tx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
<object width="63.6586" height="38.2812"/>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
<tileset firstgid="1" source="tile_set_single_image.tsx"/>
|
||||
<object gid="1" width="32" height="32"/>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
<tileset firstgid="1" source="tile_set_image_for_template.tsx"/>
|
||||
<object gid="1" width="32" height="32"/>
|
||||
</template>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tile_set_image_for_template" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||
<image source="../../images/tile_04.png" width="32" height="32"/>
|
||||
</tileset>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tile_set_single_image" tilewidth="32" tileheight="32" tilecount="1" columns="0">
|
||||
<grid orientation="orthogonal" width="1" height="1"/>
|
||||
<tile id="0">
|
||||
<image width="32" height="32" source="../../images/tile_02.png"/>
|
||||
</tile>
|
||||
</tileset>
|
||||
4
tests/test_data/map_tests/template/tileset.tsx
Normal file
4
tests/test_data/map_tests/template/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" 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>
|
||||
4
tests/test_data/tilesets/image/tileset.tsx
Normal file
4
tests/test_data/tilesets/image/tileset.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" 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>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tile_set_image" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8" backgroundcolor="#5500ff">
|
||||
<image source="../../images/tmw_desert_spacing.png" width="265" height="199"/>
|
||||
</tileset>
|
||||
8
tests/test_data/tilesets/image_grid/tileset.tsx
Normal file
8
tests/test_data/tilesets/image_grid/tileset.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tile_set_image" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
|
||||
<editorsettings>
|
||||
<export target="../tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/image" format=""/>
|
||||
</editorsettings>
|
||||
<grid orientation="isometric" width="32" height="32"/>
|
||||
<image source="../../images/tmw_desert_spacing.png" width="265" height="199"/>
|
||||
</tileset>
|
||||
14
tests/test_data/tilesets/image_properties/tileset.tsx
Normal file
14
tests/test_data/tilesets/image_properties/tileset.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tile_set_image" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
|
||||
<editorsettings>
|
||||
<export target="../tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/image" format=""/>
|
||||
</editorsettings>
|
||||
<properties>
|
||||
<property name="bool property" type="bool" value="true"/>
|
||||
<property name="color property" type="color" value="#ff0000ff"/>
|
||||
<property name="float property" type="float" value="5.6"/>
|
||||
<property name="int property" type="int" value="5"/>
|
||||
<property name="string property" value="testing"/>
|
||||
</properties>
|
||||
<image source="../../images/tmw_desert_spacing.png" width="265" height="199"/>
|
||||
</tileset>
|
||||
8
tests/test_data/tilesets/image_tile_offset/tileset.tsx
Normal file
8
tests/test_data/tilesets/image_tile_offset/tileset.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tile_set_image" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
|
||||
<editorsettings>
|
||||
<export target="../tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/tests/test_data/tilesets/image" format=""/>
|
||||
</editorsettings>
|
||||
<tileoffset x="3" y="5"/>
|
||||
<image source="../../images/tmw_desert_spacing.png" width="265" height="199"/>
|
||||
</tileset>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tile_set_image" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
|
||||
<transformations hflip="1" vflip="0" rotate="0" preferuntransformed="0"/>
|
||||
<image source="../../images/tmw_desert_spacing.png" width="265" height="199"/>
|
||||
</tileset>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tileset" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
|
||||
<image source="../../images/tmw_desert_spacing.png" trans="ff00ff" width="265" height="199"/>
|
||||
</tileset>
|
||||
37
tests/test_data/tilesets/individual_images/tileset.tsx
Normal file
37
tests/test_data/tilesets/individual_images/tileset.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tileset" tilewidth="32" tileheight="32" tilecount="4" columns="0">
|
||||
<grid orientation="orthogonal" width="1" height="1"/>
|
||||
<tile id="0" type="tile">
|
||||
<properties>
|
||||
<property name="float property" type="float" value="2.2"/>
|
||||
</properties>
|
||||
<image width="32" height="32" source="../../images/tile_01.png"/>
|
||||
<animation>
|
||||
<frame tileid="0" duration="100"/>
|
||||
<frame tileid="1" duration="100"/>
|
||||
<frame tileid="2" duration="100"/>
|
||||
<frame tileid="3" duration="100"/>
|
||||
</animation>
|
||||
</tile>
|
||||
<tile id="1" type="tile">
|
||||
<properties>
|
||||
<property name="string property" value="testing"/>
|
||||
</properties>
|
||||
<image width="32" height="32" source="../../images/tile_02.png"/>
|
||||
<objectgroup draworder="index">
|
||||
<object id="2" x="13.4358" y="13.5305" width="14.4766" height="13.7197"/>
|
||||
<object id="3" x="13.8143" y="1.98699" width="14.2874" height="11.0704">
|
||||
<ellipse/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</tile>
|
||||
<tile id="2" type="tile">
|
||||
<properties>
|
||||
<property name="bool property" type="bool" value="true"/>
|
||||
</properties>
|
||||
<image width="32" height="32" source="../../images/tile_03.png"/>
|
||||
</tile>
|
||||
<tile id="3" type="tile">
|
||||
<image width="32" height="32" source="../../images/tile_04.png"/>
|
||||
</tile>
|
||||
</tileset>
|
||||
52
tests/test_data/tilesets/terrain/tileset.tsx
Normal file
52
tests/test_data/tilesets/terrain/tileset.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.5" tiledversion="1.7.0" name="tileset" tilewidth="32" tileheight="32" spacing="1" margin="1" tilecount="48" columns="8">
|
||||
<image source="../../images/tmw_desert_spacing.png" width="265" height="199"/>
|
||||
<wangsets>
|
||||
<wangset name="Terrains" type="mixed" tile="-1">
|
||||
<wangcolor name="Sand" color="#ff0000" tile="-1" probability="1"/>
|
||||
<wangcolor name="Cobblestone" color="#00ff00" tile="-1" probability="1"/>
|
||||
<wangcolor name="Pavement" color="#0000ff" tile="-1" probability="1"/>
|
||||
<wangcolor name="Dirt" color="#ff7700" tile="-1" probability="1"/>
|
||||
<wangtile tileid="0" wangid="1,1,0,2,0,1,1,1"/>
|
||||
<wangtile tileid="1" wangid="1,1,0,2,2,2,0,1"/>
|
||||
<wangtile tileid="2" wangid="1,1,1,1,0,2,0,1"/>
|
||||
<wangtile tileid="3" wangid="4,4,0,1,0,4,4,4"/>
|
||||
<wangtile tileid="4" wangid="4,4,4,4,0,1,0,4"/>
|
||||
<wangtile tileid="5" wangid="1,1,0,4,0,1,1,1"/>
|
||||
<wangtile tileid="6" wangid="1,1,0,4,4,4,0,1"/>
|
||||
<wangtile tileid="7" wangid="1,1,1,1,0,4,0,1"/>
|
||||
<wangtile tileid="8" wangid="0,2,2,2,0,1,1,1"/>
|
||||
<wangtile tileid="9" wangid="2,2,2,2,2,2,2,2"/>
|
||||
<wangtile tileid="10" wangid="0,1,1,1,0,2,2,2"/>
|
||||
<wangtile tileid="11" wangid="0,1,0,4,4,4,4,4"/>
|
||||
<wangtile tileid="12" wangid="0,4,4,4,4,4,0,1"/>
|
||||
<wangtile tileid="13" wangid="0,4,4,4,0,1,1,1"/>
|
||||
<wangtile tileid="14" wangid="4,4,4,4,4,4,4,4"/>
|
||||
<wangtile tileid="15" wangid="0,1,1,1,0,4,4,4"/>
|
||||
<wangtile tileid="16" wangid="0,2,0,1,1,1,1,1"/>
|
||||
<wangtile tileid="17" wangid="2,2,0,1,1,1,0,2"/>
|
||||
<wangtile tileid="18" wangid="0,1,1,1,1,1,0,2"/>
|
||||
<wangtile tileid="19" wangid="2,2,0,1,0,2,2,2"/>
|
||||
<wangtile tileid="20" wangid="2,2,2,2,0,1,0,2"/>
|
||||
<wangtile tileid="21" wangid="0,4,0,1,1,1,1,1"/>
|
||||
<wangtile tileid="22" wangid="4,4,0,1,1,1,0,4"/>
|
||||
<wangtile tileid="23" wangid="0,1,1,1,1,1,0,4"/>
|
||||
<wangtile tileid="24" wangid="1,1,0,3,0,1,1,1"/>
|
||||
<wangtile tileid="25" wangid="1,1,0,3,3,3,0,1"/>
|
||||
<wangtile tileid="26" wangid="1,1,1,1,0,3,0,1"/>
|
||||
<wangtile tileid="27" wangid="0,1,0,2,2,2,2,2"/>
|
||||
<wangtile tileid="28" wangid="0,2,2,2,2,2,0,1"/>
|
||||
<wangtile tileid="29" wangid="1,1,1,1,1,1,1,1"/>
|
||||
<wangtile tileid="32" wangid="0,3,3,3,0,1,1,1"/>
|
||||
<wangtile tileid="33" wangid="3,3,3,3,3,3,3,3"/>
|
||||
<wangtile tileid="34" wangid="0,1,1,1,0,3,3,3"/>
|
||||
<wangtile tileid="35" wangid="3,3,0,1,0,3,3,3"/>
|
||||
<wangtile tileid="36" wangid="3,3,3,3,0,1,0,3"/>
|
||||
<wangtile tileid="40" wangid="0,3,0,1,1,1,1,1"/>
|
||||
<wangtile tileid="41" wangid="3,3,0,1,1,1,0,3"/>
|
||||
<wangtile tileid="42" wangid="0,1,1,1,1,1,0,3"/>
|
||||
<wangtile tileid="43" wangid="0,1,0,3,3,3,3,3"/>
|
||||
<wangtile tileid="44" wangid="0,3,3,3,3,3,0,1"/>
|
||||
</wangset>
|
||||
</wangsets>
|
||||
</tileset>
|
||||
@@ -2,11 +2,14 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from pytiled_parser import layer
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
from pytiled_parser.parsers.json.layer import parse as parse_json
|
||||
from pytiled_parser.parsers.tmx.layer import parse as parse_tmx
|
||||
|
||||
TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
|
||||
TEST_DATA = TESTS_DIR / "test_data"
|
||||
@@ -25,8 +28,36 @@ ALL_LAYER_TESTS = [
|
||||
]
|
||||
|
||||
|
||||
def fix_object(my_object):
|
||||
my_object.coordinates = OrderedPair(
|
||||
round(my_object.coordinates[0], 4),
|
||||
round(my_object.coordinates[1], 4),
|
||||
)
|
||||
my_object.size = Size(round(my_object.size[0], 4), round(my_object.size[1], 4))
|
||||
|
||||
|
||||
def fix_layer(layer):
|
||||
layer.offset = OrderedPair(round(layer.offset[0], 3), round(layer.offset[1], 3))
|
||||
layer.coordinates = OrderedPair(
|
||||
round(layer.coordinates[0], 4), round(layer.coordinates[1], 4)
|
||||
)
|
||||
if layer.size:
|
||||
layer.size = Size(round(layer.size[0], 4), round(layer.size[1], 4))
|
||||
layer.parallax_factor = OrderedPair(
|
||||
round(layer.parallax_factor[0], 4),
|
||||
round(layer.parallax_factor[1], 4),
|
||||
)
|
||||
if hasattr(layer, "tiled_objects"):
|
||||
for tiled_object in layer.tiled_objects:
|
||||
fix_object(tiled_object)
|
||||
if hasattr(layer, "layers"):
|
||||
for child_layer in layer.layers:
|
||||
fix_layer(child_layer)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("parser_type", ["json", "tmx"])
|
||||
@pytest.mark.parametrize("layer_test", ALL_LAYER_TESTS)
|
||||
def test_layer_integration(layer_test):
|
||||
def test_layer_integration(parser_type, layer_test):
|
||||
# it's a PITA to import like this, don't do it
|
||||
# https://stackoverflow.com/a/67692/1342874
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
@@ -35,10 +66,33 @@ def test_layer_integration(layer_test):
|
||||
expected = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(expected)
|
||||
|
||||
raw_layers_path = layer_test / "map.json"
|
||||
if parser_type == "json":
|
||||
raw_layers_path = layer_test / "map.json"
|
||||
with open(raw_layers_path) as raw_layers_file:
|
||||
raw_layers = json.load(raw_layers_file)["layers"]
|
||||
layers = [parse_json(raw_layer) for raw_layer in raw_layers]
|
||||
elif parser_type == "tmx":
|
||||
raw_layers_path = layer_test / "map.tmx"
|
||||
with open(raw_layers_path) as raw_layers_file:
|
||||
raw_layer = etree.parse(raw_layers_file).getroot()
|
||||
layers = []
|
||||
for layer in raw_layer.findall("./layer"):
|
||||
layers.append(parse_tmx(layer))
|
||||
|
||||
with open(raw_layers_path) as raw_layers_file:
|
||||
raw_layers = json.load(raw_layers_file)["layers"]
|
||||
layers = [layer.cast(raw_layer) for raw_layer in raw_layers]
|
||||
for layer in raw_layer.findall("./objectgroup"):
|
||||
layers.append(parse_tmx(layer))
|
||||
|
||||
for layer in raw_layer.findall("./group"):
|
||||
layers.append(parse_tmx(layer))
|
||||
|
||||
for layer in raw_layer.findall("./imagelayer"):
|
||||
layers.append(parse_tmx(layer))
|
||||
|
||||
for layer in layers:
|
||||
fix_layer(layer)
|
||||
|
||||
for layer in expected.EXPECTED:
|
||||
fix_layer(layer)
|
||||
print(layer.size)
|
||||
|
||||
assert layers == expected.EXPECTED
|
||||
|
||||
@@ -5,7 +5,8 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from pytiled_parser import tiled_map
|
||||
from pytiled_parser import parse_map
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
|
||||
TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
|
||||
TEST_DATA = TESTS_DIR / "test_data"
|
||||
@@ -21,17 +22,64 @@ ALL_MAP_TESTS = [
|
||||
]
|
||||
|
||||
|
||||
def fix_object(my_object):
|
||||
my_object.coordinates = OrderedPair(
|
||||
round(my_object.coordinates[0], 3), round(my_object.coordinates[1], 3)
|
||||
)
|
||||
my_object.size = Size(round(my_object.size[0], 4), round(my_object.size[1], 4))
|
||||
|
||||
|
||||
def fix_tileset(tileset):
|
||||
tileset.version = None
|
||||
tileset.tiled_version = None
|
||||
if tileset.tiles:
|
||||
for tile in tileset.tiles.values():
|
||||
if tile.objects:
|
||||
for my_object in tile.objects.tiled_objects:
|
||||
fix_object(my_object)
|
||||
|
||||
|
||||
def fix_layer(layer):
|
||||
for tiled_object in layer.tiled_objects:
|
||||
fix_object(tiled_object)
|
||||
|
||||
|
||||
def fix_map(map):
|
||||
map.version = None
|
||||
map.tiled_version = None
|
||||
for layer in [layer for layer in map.layers if hasattr(layer, "tiled_objects")]:
|
||||
fix_layer(layer)
|
||||
|
||||
for tileset in map.tilesets.values():
|
||||
fix_tileset(tileset)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("parser_type", ["json", "tmx"])
|
||||
@pytest.mark.parametrize("map_test", ALL_MAP_TESTS)
|
||||
def test_map_integration(map_test):
|
||||
def test_map_integration(parser_type, map_test):
|
||||
# it's a PITA to import like this, don't do it
|
||||
# https://stackoverflow.com/a/67692/1342874
|
||||
spec = importlib.util.spec_from_file_location("expected", map_test / "expected.py")
|
||||
expected = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(expected)
|
||||
|
||||
raw_maps_path = map_test / "map.json"
|
||||
if parser_type == "json":
|
||||
raw_maps_path = map_test / "map.json"
|
||||
elif parser_type == "tmx":
|
||||
raw_maps_path = map_test / "map.tmx"
|
||||
|
||||
casted_map = tiled_map.parse_map(raw_maps_path)
|
||||
casted_map = parse_map(raw_maps_path)
|
||||
|
||||
# file detection when running from unit tests is broken
|
||||
expected.EXPECTED.map_file = casted_map.map_file
|
||||
|
||||
# who even knows what/how/when the gods determine what the
|
||||
# version values in maps/tileset files are, so we're just not
|
||||
# gonna check them, because they don't make sense anyways.
|
||||
#
|
||||
# Yes the values could be set to None in the expected objects
|
||||
# directly, but alas, this is just test code that's already stupid fast
|
||||
# and I'm lazy because there's too many of them already existing.
|
||||
fix_map(expected.EXPECTED)
|
||||
fix_map(casted_map)
|
||||
assert casted_map == expected.EXPECTED
|
||||
|
||||
@@ -5,7 +5,17 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from pytiled_parser import common_types, tiled_object
|
||||
from pytiled_parser import common_types
|
||||
from pytiled_parser.parsers.json.tiled_object import parse
|
||||
from pytiled_parser.tiled_object import (
|
||||
Ellipse,
|
||||
Point,
|
||||
Polygon,
|
||||
Polyline,
|
||||
Rectangle,
|
||||
Text,
|
||||
Tile,
|
||||
)
|
||||
|
||||
ELLIPSES = [
|
||||
(
|
||||
@@ -23,7 +33,7 @@ ELLIPSES = [
|
||||
"y":81.1913152210981
|
||||
}
|
||||
""",
|
||||
tiled_object.Ellipse(
|
||||
Ellipse(
|
||||
id=6,
|
||||
size=common_types.Size(57.4013868364215, 18.5517790155735),
|
||||
name="name: ellipse",
|
||||
@@ -48,7 +58,7 @@ ELLIPSES = [
|
||||
"y":53.9092872570194
|
||||
}
|
||||
""",
|
||||
tiled_object.Ellipse(
|
||||
Ellipse(
|
||||
id=7,
|
||||
size=common_types.Size(6.32943048766625, 31.4288962146186),
|
||||
name="name: ellipse - invisible",
|
||||
@@ -73,7 +83,7 @@ ELLIPSES = [
|
||||
"y":120.040923041946
|
||||
}
|
||||
""",
|
||||
tiled_object.Ellipse(
|
||||
Ellipse(
|
||||
id=8,
|
||||
size=common_types.Size(29.6828464249176, 24.2264408321018),
|
||||
name="name: ellipse - rotated",
|
||||
@@ -98,7 +108,7 @@ ELLIPSES = [
|
||||
"y":127.679890871888
|
||||
}
|
||||
""",
|
||||
tiled_object.Ellipse(
|
||||
Ellipse(
|
||||
id=29,
|
||||
name="name: ellipse - no width or height",
|
||||
rotation=0,
|
||||
@@ -124,7 +134,7 @@ RECTANGLES = [
|
||||
"y":23.571672160964
|
||||
}
|
||||
""",
|
||||
tiled_object.Rectangle(
|
||||
Rectangle(
|
||||
id=1,
|
||||
size=common_types.Size(45.3972945322269, 41.4686825053996),
|
||||
name="name: rectangle",
|
||||
@@ -148,7 +158,7 @@ RECTANGLES = [
|
||||
"y":91.0128452881664
|
||||
}
|
||||
""",
|
||||
tiled_object.Rectangle(
|
||||
Rectangle(
|
||||
id=4,
|
||||
size=common_types.Size(30.9923837671934, 32.7384335568944),
|
||||
name="name: rectangle - invisible",
|
||||
@@ -172,7 +182,7 @@ RECTANGLES = [
|
||||
"y":23.3534159372513
|
||||
}
|
||||
""",
|
||||
tiled_object.Rectangle(
|
||||
Rectangle(
|
||||
id=5,
|
||||
size=common_types.Size(10, 22),
|
||||
name="name: rectangle - rotated",
|
||||
@@ -196,7 +206,7 @@ RECTANGLES = [
|
||||
"y":53.4727748095942
|
||||
}
|
||||
""",
|
||||
tiled_object.Rectangle(
|
||||
Rectangle(
|
||||
id=28,
|
||||
size=common_types.Size(0, 0),
|
||||
name="name: rectangle - no width or height",
|
||||
@@ -251,7 +261,7 @@ RECTANGLES = [
|
||||
"y":131.826759122428
|
||||
}
|
||||
""",
|
||||
tiled_object.Rectangle(
|
||||
Rectangle(
|
||||
id=30,
|
||||
size=common_types.Size(21.170853700125, 13.7501420938956),
|
||||
name="name: rectangle - properties",
|
||||
@@ -287,7 +297,7 @@ POINTS = [
|
||||
"y":82.9373650107991
|
||||
}
|
||||
""",
|
||||
tiled_object.Point(
|
||||
Point(
|
||||
id=2,
|
||||
name="name: point",
|
||||
rotation=0,
|
||||
@@ -311,7 +321,7 @@ POINTS = [
|
||||
"y":95.8144822098443
|
||||
}
|
||||
""",
|
||||
tiled_object.Point(
|
||||
Point(
|
||||
id=3,
|
||||
name="name: point invisible",
|
||||
rotation=0,
|
||||
@@ -338,7 +348,7 @@ TILES = [
|
||||
"y":48.3019211094691
|
||||
}
|
||||
""",
|
||||
tiled_object.Tile(
|
||||
Tile(
|
||||
id=13,
|
||||
size=common_types.Size(32, 32),
|
||||
name="name: tile",
|
||||
@@ -364,7 +374,7 @@ TILES = [
|
||||
"y":168.779356598841
|
||||
}
|
||||
""",
|
||||
tiled_object.Tile(
|
||||
Tile(
|
||||
id=14,
|
||||
size=common_types.Size(32, 32),
|
||||
name="name: tile - invisible",
|
||||
@@ -390,7 +400,7 @@ TILES = [
|
||||
"y":59.8695009662385
|
||||
}
|
||||
""",
|
||||
tiled_object.Tile(
|
||||
Tile(
|
||||
id=15,
|
||||
size=common_types.Size(32, 32),
|
||||
name="name: tile - horizontal flipped",
|
||||
@@ -416,7 +426,7 @@ TILES = [
|
||||
"y":60.742525861089
|
||||
}
|
||||
""",
|
||||
tiled_object.Tile(
|
||||
Tile(
|
||||
id=16,
|
||||
size=common_types.Size(32, 32),
|
||||
name="name: tile - vertical flipped",
|
||||
@@ -442,7 +452,7 @@ TILES = [
|
||||
"y":95.6635216551097
|
||||
}
|
||||
""",
|
||||
tiled_object.Tile(
|
||||
Tile(
|
||||
id=17,
|
||||
size=common_types.Size(32, 32),
|
||||
name="name: tile - both flipped",
|
||||
@@ -468,7 +478,7 @@ TILES = [
|
||||
"y":142.62
|
||||
}
|
||||
""",
|
||||
tiled_object.Tile(
|
||||
Tile(
|
||||
id=18,
|
||||
size=common_types.Size(32, 32),
|
||||
name="name: tile - rotated",
|
||||
@@ -517,7 +527,7 @@ POLYGONS = [
|
||||
"y":38.6313515971354
|
||||
}
|
||||
""",
|
||||
tiled_object.Polygon(
|
||||
Polygon(
|
||||
id=9,
|
||||
name="name: polygon",
|
||||
points=[
|
||||
@@ -560,7 +570,7 @@ POLYGONS = [
|
||||
"y":24.4446970558145
|
||||
}
|
||||
""",
|
||||
tiled_object.Polygon(
|
||||
Polygon(
|
||||
id=10,
|
||||
name="name: polygon - invisible",
|
||||
points=[
|
||||
@@ -613,7 +623,7 @@ POLYGONS = [
|
||||
"y":19.8613163578493
|
||||
}
|
||||
""",
|
||||
tiled_object.Polygon(
|
||||
Polygon(
|
||||
id=11,
|
||||
name="name: polygon - rotated",
|
||||
points=[
|
||||
@@ -660,7 +670,7 @@ POLYLINES = [
|
||||
"y":90.1398203933159
|
||||
}
|
||||
""",
|
||||
tiled_object.Polyline(
|
||||
Polyline(
|
||||
id=12,
|
||||
name="name: polyline",
|
||||
points=[
|
||||
@@ -701,7 +711,7 @@ POLYLINES = [
|
||||
"y":163.333333333333
|
||||
}
|
||||
""",
|
||||
tiled_object.Polyline(
|
||||
Polyline(
|
||||
id=31,
|
||||
name="name: polyline - invisible",
|
||||
points=[
|
||||
@@ -742,7 +752,7 @@ POLYLINES = [
|
||||
"y":128.666666666667
|
||||
}
|
||||
""",
|
||||
tiled_object.Polyline(
|
||||
Polyline(
|
||||
id=32,
|
||||
name="name: polyline - rotated",
|
||||
points=[
|
||||
@@ -778,7 +788,7 @@ TEXTS = [
|
||||
"y":93.2986813686484
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=19,
|
||||
name="name: text",
|
||||
text="Hello World",
|
||||
@@ -809,7 +819,7 @@ TEXTS = [
|
||||
"y":112.068716607935
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=20,
|
||||
name="name: text - invisible",
|
||||
text="Hello World",
|
||||
@@ -840,7 +850,7 @@ TEXTS = [
|
||||
"y":78.4572581561896
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=21,
|
||||
name="name: text - rotated",
|
||||
text="Hello World",
|
||||
@@ -874,7 +884,7 @@ TEXTS = [
|
||||
"y":101.592417869728
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=22,
|
||||
name="name: text - different font",
|
||||
text="Hello World",
|
||||
@@ -907,7 +917,7 @@ TEXTS = [
|
||||
"y":154.192167784472
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=23,
|
||||
name="name: text - no word wrap",
|
||||
text="Hello World",
|
||||
@@ -939,7 +949,7 @@ TEXTS = [
|
||||
"y":1.19455496191883
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=24,
|
||||
name="name: text - right bottom align",
|
||||
text="Hello World",
|
||||
@@ -973,7 +983,7 @@ TEXTS = [
|
||||
"y": 3.81362964647039
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=25,
|
||||
name="text: center center align",
|
||||
rotation=0,
|
||||
@@ -1006,7 +1016,7 @@ TEXTS = [
|
||||
"y": 60.7785040354666
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=26,
|
||||
name="name: text - justified",
|
||||
rotation=0,
|
||||
@@ -1038,7 +1048,7 @@ TEXTS = [
|
||||
"y": 130.620495623508
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=27,
|
||||
name="name: text - red",
|
||||
rotation=0,
|
||||
@@ -1075,7 +1085,7 @@ TEXTS = [
|
||||
"y":22
|
||||
}
|
||||
""",
|
||||
tiled_object.Text(
|
||||
Text(
|
||||
id=31,
|
||||
name="name: text - font options",
|
||||
rotation=0,
|
||||
@@ -1100,7 +1110,7 @@ OBJECTS = ELLIPSES + RECTANGLES + POINTS + TILES + POLYGONS + POLYLINES + TEXTS
|
||||
@pytest.mark.parametrize("raw_object_json,expected", OBJECTS)
|
||||
def test_parse_layer(raw_object_json, expected):
|
||||
raw_object = json.loads(raw_object_json)
|
||||
result = tiled_object.cast(raw_object)
|
||||
result = parse(raw_object)
|
||||
|
||||
assert result == expected
|
||||
|
||||
@@ -1118,4 +1128,4 @@ def test_parse_no_parent_dir():
|
||||
|
||||
json_object = json.loads(raw_object)
|
||||
with pytest.raises(RuntimeError):
|
||||
tiled_object.cast(json_object)
|
||||
parse(json_object)
|
||||
492
tests/test_tiled_object_tmx.py
Normal file
492
tests/test_tiled_object_tmx.py
Normal file
@@ -0,0 +1,492 @@
|
||||
"""Tests for objects"""
|
||||
import xml.etree.ElementTree as etree
|
||||
from contextlib import ExitStack as does_not_raise
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from pytiled_parser import common_types
|
||||
from pytiled_parser.parsers.tmx.tiled_object import parse
|
||||
from pytiled_parser.tiled_object import (
|
||||
Ellipse,
|
||||
Point,
|
||||
Polygon,
|
||||
Polyline,
|
||||
Rectangle,
|
||||
Text,
|
||||
Tile,
|
||||
)
|
||||
|
||||
ELLIPSES = [
|
||||
(
|
||||
"""
|
||||
<object id="6" x="37.5401" y="81.1913" width="57.4014" height="18.5518" name="ellipse">
|
||||
<ellipse/>
|
||||
</object>
|
||||
""",
|
||||
Ellipse(
|
||||
id=6,
|
||||
size=common_types.Size(57.4014, 18.5518),
|
||||
name="ellipse",
|
||||
coordinates=common_types.OrderedPair(37.5401, 81.1913),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="7" x="22.6986" y="53.9093" width="6.3294" height="31.4289" name="ellipse - invisible" visible="0">
|
||||
<ellipse/>
|
||||
</object>
|
||||
""",
|
||||
Ellipse(
|
||||
id=7,
|
||||
size=common_types.Size(6.3294, 31.4289),
|
||||
name="ellipse - invisible",
|
||||
visible=False,
|
||||
coordinates=common_types.OrderedPair(22.6986, 53.9093),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="8" x="35.7940" y="120.0409" width="29.6828" height="24.2264" name="ellipse - rotated" rotation="111">
|
||||
<ellipse/>
|
||||
</object>
|
||||
""",
|
||||
Ellipse(
|
||||
id=8,
|
||||
size=common_types.Size(29.6828, 24.2264),
|
||||
name="ellipse - rotated",
|
||||
rotation=111,
|
||||
coordinates=common_types.OrderedPair(35.7940, 120.0409),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="29" x="72.4611" y="127.6799" name="ellipse - no width or height">
|
||||
<ellipse/>
|
||||
</object>
|
||||
""",
|
||||
Ellipse(
|
||||
id=29,
|
||||
name="ellipse - no width or height",
|
||||
coordinates=common_types.OrderedPair(72.4611, 127.6799),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
RECTANGLES = [
|
||||
(
|
||||
"""
|
||||
<object id="1" x="27.7185" y="23.5717" width="45.3973" height="41.4687" name="rectangle"/>
|
||||
""",
|
||||
Rectangle(
|
||||
id=1,
|
||||
size=common_types.Size(45.3973, 41.4687),
|
||||
coordinates=common_types.OrderedPair(27.7185, 23.5717),
|
||||
name="rectangle",
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="4" x="163.9104" y="91.0128" width="30.9924" height="32.7384" name="rectangle - invisible" visible="0"/>
|
||||
""",
|
||||
Rectangle(
|
||||
id=4,
|
||||
size=common_types.Size(30.9924, 32.7384),
|
||||
coordinates=common_types.OrderedPair(163.9104, 91.0128),
|
||||
name="rectangle - invisible",
|
||||
visible=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="5" x="183.3352" y="23.3534" width="10" height="22" name="rectangle - rotated" rotation="10"/>
|
||||
""",
|
||||
Rectangle(
|
||||
id=5,
|
||||
size=common_types.Size(10, 22),
|
||||
coordinates=common_types.OrderedPair(183.3352, 23.3534),
|
||||
name="rectangle - rotated",
|
||||
rotation=10,
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="28" x="131.1720" y="53.4728" name="rectangle - no width or height"/>
|
||||
""",
|
||||
Rectangle(
|
||||
id=28,
|
||||
coordinates=common_types.OrderedPair(131.1720, 53.4728),
|
||||
name="rectangle - no width or height",
|
||||
),
|
||||
),
|
||||
(
|
||||
r"""
|
||||
<object id="30" x="39.0679" y="131.8268" width="21.1709" height="13.7501" name="rectangle - properties">
|
||||
<properties>
|
||||
<property name="bool property" type="bool" value="false"/>
|
||||
<property name="color property" type="color" value="#ffaa0000"/>
|
||||
<property name="file property" type="file" value="..\/..\/..\/..\/..\/..\/dev\/null"/>
|
||||
<property name="float property" type="float" value="42.1"/>
|
||||
<property name="int property" type="int" value="8675309"/>
|
||||
<property name="string property" value="pytiled_parser rulez!1!!"/>
|
||||
</properties>
|
||||
</object>
|
||||
""",
|
||||
Rectangle(
|
||||
id=30,
|
||||
size=common_types.Size(21.1709, 13.7501),
|
||||
coordinates=common_types.OrderedPair(39.0679, 131.8268),
|
||||
name="rectangle - properties",
|
||||
properties={
|
||||
"bool property": False,
|
||||
"color property": common_types.Color(170, 0, 0, 255),
|
||||
"file property": Path("../../../../../../dev/null"),
|
||||
"float property": 42.1,
|
||||
"int property": 8675309,
|
||||
"string property": "pytiled_parser rulez!1!!",
|
||||
},
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
POINTS = [
|
||||
(
|
||||
"""
|
||||
<object id="2" x="159.9818" y="82.9374" name="point">
|
||||
<point/>
|
||||
</object>
|
||||
""",
|
||||
Point(
|
||||
id=2, coordinates=common_types.OrderedPair(159.9818, 82.9374), name="point"
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="2" x="159.9818" y="82.9374" name="point - invisible" visible="0">
|
||||
<point/>
|
||||
</object>
|
||||
""",
|
||||
Point(
|
||||
id=2,
|
||||
coordinates=common_types.OrderedPair(159.9818, 82.9374),
|
||||
name="point - invisible",
|
||||
visible=False,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
POLYGONS = [
|
||||
(
|
||||
"""
|
||||
<object id="9" x="89.4851" y="38.6314" name="polygon">
|
||||
<polygon points="0,0 19.4248,27.0638 19.6431,3.0556 -2.6191,15.9327 25.3177,16.3692"/>
|
||||
</object>
|
||||
""",
|
||||
Polygon(
|
||||
id=9,
|
||||
coordinates=common_types.OrderedPair(89.4851, 38.6314),
|
||||
name="polygon",
|
||||
points=[
|
||||
common_types.OrderedPair(0, 0),
|
||||
common_types.OrderedPair(19.4248, 27.0638),
|
||||
common_types.OrderedPair(19.6431, 3.0556),
|
||||
common_types.OrderedPair(-2.6191, 15.9327),
|
||||
common_types.OrderedPair(25.3177, 16.3692),
|
||||
],
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="9" x="89.4851" y="38.6314" name="polygon - invisible" visible="0">
|
||||
<polygon points="0,0 19.4248,27.0638 19.6431,3.0556 -2.6191,15.9327 25.3177,16.3692"/>
|
||||
</object>
|
||||
""",
|
||||
Polygon(
|
||||
id=9,
|
||||
coordinates=common_types.OrderedPair(89.4851, 38.6314),
|
||||
name="polygon - invisible",
|
||||
points=[
|
||||
common_types.OrderedPair(0, 0),
|
||||
common_types.OrderedPair(19.4248, 27.0638),
|
||||
common_types.OrderedPair(19.6431, 3.0556),
|
||||
common_types.OrderedPair(-2.6191, 15.9327),
|
||||
common_types.OrderedPair(25.3177, 16.3692),
|
||||
],
|
||||
visible=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="9" x="89.4851" y="38.6314" name="polygon - rotated" rotation="123">
|
||||
<polygon points="0,0 19.4248,27.0638 19.6431,3.0556 -2.6191,15.9327 25.3177,16.3692"/>
|
||||
</object>
|
||||
""",
|
||||
Polygon(
|
||||
id=9,
|
||||
coordinates=common_types.OrderedPair(89.4851, 38.6314),
|
||||
name="polygon - rotated",
|
||||
points=[
|
||||
common_types.OrderedPair(0, 0),
|
||||
common_types.OrderedPair(19.4248, 27.0638),
|
||||
common_types.OrderedPair(19.6431, 3.0556),
|
||||
common_types.OrderedPair(-2.6191, 15.9327),
|
||||
common_types.OrderedPair(25.3177, 16.3692),
|
||||
],
|
||||
rotation=123,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
POLYLINES = [
|
||||
(
|
||||
"""
|
||||
<object id="12" x="124.1878" y="90.1398" name="polyline">
|
||||
<polyline points="0,0 -13.3136,41.0321 21.3891,16.8057"/>
|
||||
</object>
|
||||
""",
|
||||
Polyline(
|
||||
id=12,
|
||||
coordinates=common_types.OrderedPair(124.1878, 90.1398),
|
||||
name="polyline",
|
||||
points=[
|
||||
common_types.OrderedPair(0, 0),
|
||||
common_types.OrderedPair(-13.3136, 41.0321),
|
||||
common_types.OrderedPair(21.3891, 16.8057),
|
||||
],
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="12" x="124.1878" y="90.1398" name="polyline - invisible" visible="0">
|
||||
<polyline points="0,0 -13.3136,41.0321 21.3891,16.8057"/>
|
||||
</object>
|
||||
""",
|
||||
Polyline(
|
||||
id=12,
|
||||
coordinates=common_types.OrderedPair(124.1878, 90.1398),
|
||||
name="polyline - invisible",
|
||||
points=[
|
||||
common_types.OrderedPair(0, 0),
|
||||
common_types.OrderedPair(-13.3136, 41.0321),
|
||||
common_types.OrderedPair(21.3891, 16.8057),
|
||||
],
|
||||
visible=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="12" x="124.1878" y="90.1398" name="polyline - rotated" rotation="110">
|
||||
<polyline points="0,0 -13.3136,41.0321 21.3891,16.8057"/>
|
||||
</object>
|
||||
""",
|
||||
Polyline(
|
||||
id=12,
|
||||
coordinates=common_types.OrderedPair(124.1878, 90.1398),
|
||||
name="polyline - rotated",
|
||||
points=[
|
||||
common_types.OrderedPair(0, 0),
|
||||
common_types.OrderedPair(-13.3136, 41.0321),
|
||||
common_types.OrderedPair(21.3891, 16.8057),
|
||||
],
|
||||
rotation=110,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
TEXTS = [
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text" width="92.375" height="19">
|
||||
<text>Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text",
|
||||
text="Hello World",
|
||||
size=common_types.Size(92.375, 19),
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text - wrap" width="92.375" height="19">
|
||||
<text wrap="1">Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text - wrap",
|
||||
text="Hello World",
|
||||
wrap=True,
|
||||
size=common_types.Size(92.375, 19),
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text - rotated" width="92.375" height="19" rotation="110">
|
||||
<text>Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text - rotated",
|
||||
text="Hello World",
|
||||
rotation=110,
|
||||
size=common_types.Size(92.375, 19),
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text - different font" width="92.375" height="19" rotation="110">
|
||||
<text fontfamily="DejaVu Sans" pixelsize="19">Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text - different font",
|
||||
text="Hello World",
|
||||
font_size=19,
|
||||
font_family="DejaVu Sans",
|
||||
rotation=110,
|
||||
size=common_types.Size(92.375, 19),
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text - right bottom align" width="92.375" height="19">
|
||||
<text halign="right" valign="bottom">Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text - right bottom align",
|
||||
text="Hello World",
|
||||
horizontal_align="right",
|
||||
vertical_align="bottom",
|
||||
size=common_types.Size(92.375, 19),
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text - center center align" width="92.375" height="19">
|
||||
<text halign="center" valign="center">Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text - center center align",
|
||||
text="Hello World",
|
||||
horizontal_align="center",
|
||||
vertical_align="center",
|
||||
size=common_types.Size(92.375, 19),
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text - justified" width="92.375" height="19">
|
||||
<text halign="justify">Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text - justified",
|
||||
text="Hello World",
|
||||
horizontal_align="justify",
|
||||
size=common_types.Size(92.375, 19),
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text - colored" width="92.375" height="19">
|
||||
<text color="#aa0000">Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text - colored",
|
||||
text="Hello World",
|
||||
color=common_types.Color(170, 0, 0, 255),
|
||||
size=common_types.Size(92.375, 19),
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="19" x="93.2987" y="81.7106" name="text - font options" width="92.375" height="19">
|
||||
<text bold="1" italic="1" kerning="1" strikeout="1" underline="1" wrap="1">Hello World</text>
|
||||
</object>
|
||||
""",
|
||||
Text(
|
||||
id=19,
|
||||
name="text - font options",
|
||||
text="Hello World",
|
||||
size=common_types.Size(92.375, 19),
|
||||
bold=True,
|
||||
italic=True,
|
||||
kerning=True,
|
||||
strike_out=True,
|
||||
underline=True,
|
||||
wrap=True,
|
||||
coordinates=common_types.OrderedPair(93.2987, 81.7106),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
TILES = [
|
||||
(
|
||||
"""
|
||||
<object id="13" x="111.8981" y="48.3019" width="32" height="32" name="tile" gid="79"/>
|
||||
""",
|
||||
Tile(
|
||||
id=13,
|
||||
size=common_types.Size(32, 32),
|
||||
name="tile",
|
||||
coordinates=common_types.OrderedPair(111.8981, 48.3019),
|
||||
gid=79,
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object type="tile" id="13" x="111.8981" y="48.3019" width="32" height="32" name="tile - invisible" gid="79" visible="0"/>
|
||||
""",
|
||||
Tile(
|
||||
id=13,
|
||||
size=common_types.Size(32, 32),
|
||||
name="tile - invisible",
|
||||
type="tile",
|
||||
coordinates=common_types.OrderedPair(111.8981, 48.3019),
|
||||
gid=79,
|
||||
visible=False,
|
||||
),
|
||||
),
|
||||
(
|
||||
"""
|
||||
<object id="13" x="111.8981" y="48.3019" width="32" height="32" name="tile - rotated" gid="79" rotation="110"/>
|
||||
""",
|
||||
Tile(
|
||||
id=13,
|
||||
size=common_types.Size(32, 32),
|
||||
name="tile - rotated",
|
||||
coordinates=common_types.OrderedPair(111.8981, 48.3019),
|
||||
gid=79,
|
||||
rotation=110,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
OBJECTS = ELLIPSES + RECTANGLES + POINTS + POLYGONS + POLYLINES + TEXTS + TILES
|
||||
|
||||
|
||||
@pytest.mark.parametrize("raw_object_tmx,expected", OBJECTS)
|
||||
def test_parse_layer(raw_object_tmx, expected):
|
||||
raw_object = etree.fromstring(raw_object_tmx)
|
||||
result = parse(raw_object)
|
||||
|
||||
assert result == expected
|
||||
@@ -2,11 +2,14 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import xml.etree.ElementTree as etree
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from pytiled_parser import tileset
|
||||
from pytiled_parser.common_types import OrderedPair, Size
|
||||
from pytiled_parser.parsers.json.tileset import parse as parse_json
|
||||
from pytiled_parser.parsers.tmx.tileset import parse as parse_tmx
|
||||
|
||||
TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
|
||||
TEST_DATA = TESTS_DIR / "test_data"
|
||||
@@ -26,8 +29,26 @@ ALL_TILESET_DIRS = [
|
||||
]
|
||||
|
||||
|
||||
def fix_object(my_object):
|
||||
my_object.coordinates = OrderedPair(
|
||||
round(my_object.coordinates[0], 4), round(my_object.coordinates[1], 4)
|
||||
)
|
||||
my_object.size = Size(round(my_object.size[0], 4), round(my_object.size[1], 4))
|
||||
|
||||
|
||||
def fix_tileset(tileset):
|
||||
tileset.version = None
|
||||
tileset.tiled_version = None
|
||||
if tileset.tiles:
|
||||
for tile in tileset.tiles.values():
|
||||
if tile.objects:
|
||||
for my_object in tile.objects.tiled_objects:
|
||||
fix_object(my_object)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("parser_type", ["json", "tmx"])
|
||||
@pytest.mark.parametrize("tileset_dir", ALL_TILESET_DIRS)
|
||||
def test_tilesets_integration(tileset_dir):
|
||||
def test_tilesets_integration(parser_type, tileset_dir):
|
||||
# it's a PITA to import like this, don't do it
|
||||
# https://stackoverflow.com/a/67692/1342874
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
@@ -36,9 +57,16 @@ def test_tilesets_integration(tileset_dir):
|
||||
expected = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(expected)
|
||||
|
||||
raw_tileset_path = tileset_dir / "tileset.json"
|
||||
if parser_type == "json":
|
||||
raw_tileset_path = tileset_dir / "tileset.json"
|
||||
with open(raw_tileset_path) as raw_tileset:
|
||||
tileset_ = parse_json(json.loads(raw_tileset.read()), 1)
|
||||
elif parser_type == "tmx":
|
||||
raw_tileset_path = tileset_dir / "tileset.tsx"
|
||||
with open(raw_tileset_path) as raw_tileset:
|
||||
tileset_ = parse_tmx(etree.parse(raw_tileset).getroot(), 1)
|
||||
|
||||
with open(raw_tileset_path) as raw_tileset:
|
||||
tileset_ = tileset.cast(json.loads(raw_tileset.read()), 1)
|
||||
fix_tileset(tileset_)
|
||||
fix_tileset(expected.EXPECTED)
|
||||
|
||||
assert tileset_ == expected.EXPECTED
|
||||
|
||||
Reference in New Issue
Block a user