mirror of
https://github.com/OMGeeky/pytiled_parser.git
synced 2026-02-23 15:49:52 +01:00
More TMX work(it's mostly working I think)
This commit is contained in:
@@ -1,14 +1,136 @@
|
|||||||
"""Layer parsing for the TMX Map Format.
|
"""Layer parsing for the TMX Map Format.
|
||||||
"""
|
"""
|
||||||
|
import base64
|
||||||
|
import gzip
|
||||||
|
import importlib.util
|
||||||
import xml.etree.ElementTree as etree
|
import xml.etree.ElementTree as etree
|
||||||
|
import zlib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from pytiled_parser.common_types import OrderedPair, Size
|
from pytiled_parser.common_types import OrderedPair, Size
|
||||||
from pytiled_parser.layer import ImageLayer, Layer
|
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.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
|
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], # 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:
|
def _parse_common(raw_layer: etree.Element) -> Layer:
|
||||||
"""Create a Layer containing all the attributes common to all layer types.
|
"""Create a Layer containing all the attributes common to all layer types.
|
||||||
@@ -56,6 +178,83 @@ def _parse_common(raw_layer: etree.Element) -> Layer:
|
|||||||
return common
|
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
|
||||||
|
"""
|
||||||
|
tile_layer = TileLayer(
|
||||||
|
size=Size(int(raw_layer.attrib["width"]), int(raw_layer.attrib["height"])),
|
||||||
|
**_parse_common(raw_layer).__dict__,
|
||||||
|
)
|
||||||
|
|
||||||
|
data_element = raw_layer.find("./data")
|
||||||
|
if data_element:
|
||||||
|
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:
|
||||||
|
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], # 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))
|
||||||
|
|
||||||
|
return ObjectLayer(
|
||||||
|
tiled_objects=objects,
|
||||||
|
draw_order=raw_layer.attrib["draworder"],
|
||||||
|
**_parse_common(raw_layer).__dict__,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _parse_image_layer(raw_layer: etree.Element) -> ImageLayer:
|
def _parse_image_layer(raw_layer: etree.Element) -> ImageLayer:
|
||||||
"""Parse the raw_layer to an ImageLayer.
|
"""Parse the raw_layer to an ImageLayer.
|
||||||
|
|
||||||
@@ -85,6 +284,26 @@ def _parse_image_layer(raw_layer: etree.Element) -> ImageLayer:
|
|||||||
raise RuntimeError("Tried to parse an image layer that doesn't have an image!")
|
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 = []
|
||||||
|
|
||||||
|
for layer in raw_layer.iter():
|
||||||
|
if layer.tag in ["layer", "objectgroup", "imagelayer", "group"]:
|
||||||
|
layers.append(parse(layer, parent_dir=parent_dir))
|
||||||
|
|
||||||
|
return LayerGroup(layers=layers, **_parse_common(raw_layer).__dict__)
|
||||||
|
|
||||||
|
|
||||||
def parse(
|
def parse(
|
||||||
raw_layer: etree.Element,
|
raw_layer: etree.Element,
|
||||||
parent_dir: Optional[Path] = None,
|
parent_dir: Optional[Path] = None,
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import xml.etree.ElementTree as etree
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from pytiled_parser.common_types import OrderedPair, Size
|
from pytiled_parser.common_types import OrderedPair, Size
|
||||||
|
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_tileset
|
from pytiled_parser.parsers.tmx.tileset import parse as parse_tileset
|
||||||
from pytiled_parser.tiled_map import TiledMap, TilesetDict
|
from pytiled_parser.tiled_map import TiledMap, TilesetDict
|
||||||
|
from pytiled_parser.util import parse_color
|
||||||
|
|
||||||
|
|
||||||
def parse(file: Path) -> TiledMap:
|
def parse(file: Path) -> TiledMap:
|
||||||
@@ -41,10 +44,15 @@ def parse(file: Path) -> TiledMap:
|
|||||||
raw_tileset, int(raw_tileset.attrib["firstgid"])
|
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_ = TiledMap(
|
||||||
map_file=file,
|
map_file=file,
|
||||||
infinite=bool(int(raw_map.attrib["infinite"])),
|
infinite=bool(int(raw_map.attrib["infinite"])),
|
||||||
layers=[parse_layer(layer_, parent_dir) for layer_ in raw_tiled_map["layers"]],
|
layers=layers,
|
||||||
map_size=Size(int(raw_map.attrib["width"]), int(raw_map.attrib["height"])),
|
map_size=Size(int(raw_map.attrib["width"]), int(raw_map.attrib["height"])),
|
||||||
next_layer_id=int(raw_map.attrib["nextlayerid"]),
|
next_layer_id=int(raw_map.attrib["nextlayerid"]),
|
||||||
next_object_id=int(raw_map.attrib["nextobjectid"]),
|
next_object_id=int(raw_map.attrib["nextobjectid"]),
|
||||||
@@ -57,3 +65,52 @@ def parse(file: Path) -> TiledMap:
|
|||||||
tilesets=tilesets,
|
tilesets=tilesets,
|
||||||
version=raw_map.attrib["version"],
|
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:
|
||||||
|
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_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_
|
||||||
|
|||||||
275
pytiled_parser/parsers/tmx/tiled_object.py
Normal file
275
pytiled_parser/parsers/tmx/tiled_object.py
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
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 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"])
|
||||||
|
),
|
||||||
|
visible=bool(int(raw_object.attrib["visible"])),
|
||||||
|
size=Size(
|
||||||
|
float(raw_object.attrib["width"]), float(raw_object.attrib["height"])
|
||||||
|
),
|
||||||
|
rotation=float(raw_object.attrib["rotation"]),
|
||||||
|
name=raw_object.attrib["name"],
|
||||||
|
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 = []
|
||||||
|
for raw_point in raw_object.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 = []
|
||||||
|
for raw_point in raw_object.attrib["polyline"].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 = raw_object.text
|
||||||
|
|
||||||
|
if not text:
|
||||||
|
text = ""
|
||||||
|
# create base Text object
|
||||||
|
text_object = Text(text=text, **_parse_common(raw_object).__dict__)
|
||||||
|
|
||||||
|
# optional attributes
|
||||||
|
if raw_object.attrib.get("color") is not None:
|
||||||
|
text_object.color = parse_color(raw_object.attrib["color"])
|
||||||
|
|
||||||
|
if raw_object.attrib.get("fontfamily") is not None:
|
||||||
|
text_object.font_family = raw_object.attrib["fontfamily"]
|
||||||
|
|
||||||
|
if raw_object.attrib.get("pixelsize") is not None:
|
||||||
|
text_object.font_size = float(raw_object.attrib["pixelsize"])
|
||||||
|
|
||||||
|
if raw_object.attrib.get("bold") is not None:
|
||||||
|
text_object.bold = bool(int(raw_object.attrib["bold"]))
|
||||||
|
|
||||||
|
if raw_object.attrib.get("italic") is not None:
|
||||||
|
text_object.italic = bool(int(raw_object.attrib["italic"]))
|
||||||
|
|
||||||
|
if raw_object.attrib.get("kerning") is not None:
|
||||||
|
text_object.kerning = bool(int(raw_object.attrib["kerning"]))
|
||||||
|
|
||||||
|
if raw_object.attrib.get("strikeout") is not None:
|
||||||
|
text_object.strike_out = bool(int(raw_object.attrib["strikeout"]))
|
||||||
|
|
||||||
|
if raw_object.attrib.get("underline") is not None:
|
||||||
|
text_object.underline = bool(int(raw_object.attrib["underline"]))
|
||||||
|
|
||||||
|
if raw_object.attrib.get("halign") is not None:
|
||||||
|
text_object.horizontal_align = raw_object.attrib["halign"]
|
||||||
|
|
||||||
|
if raw_object.attrib.get("valign") is not None:
|
||||||
|
text_object.vertical_align = raw_object.attrib["valign"]
|
||||||
|
|
||||||
|
if raw_object.attrib.get("wrap") is not None:
|
||||||
|
text_object.wrap = bool(int(raw_object.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"):
|
||||||
|
return _parse_ellipse
|
||||||
|
|
||||||
|
if raw_object.find("./point"):
|
||||||
|
return _parse_point
|
||||||
|
|
||||||
|
if raw_object.attrib.get("gid"):
|
||||||
|
# Only tile objects have the `gid` attribute
|
||||||
|
return _parse_tile
|
||||||
|
|
||||||
|
if raw_object.find("./polygon"):
|
||||||
|
return _parse_polygon
|
||||||
|
|
||||||
|
if raw_object.find("./polyline"):
|
||||||
|
return _parse_polyline
|
||||||
|
|
||||||
|
if raw_object.find("./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 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"])
|
||||||
|
with open(template_path) as template_file:
|
||||||
|
template = etree.parse(template_file).getroot()
|
||||||
|
|
||||||
|
tileset_element = template.find("./tileset")
|
||||||
|
if tileset_element:
|
||||||
|
tileset_path = Path(
|
||||||
|
template_path.parent / tileset_element.attrib["source"]
|
||||||
|
)
|
||||||
|
with open(tileset_path) as tileset_file:
|
||||||
|
new_tileset = etree.parse(tileset_file).getroot()
|
||||||
|
new_tileset_path = tileset_path.parent
|
||||||
|
|
||||||
|
new_object = template.find("./object")
|
||||||
|
if raw_object.attrib.get("id") and new_object:
|
||||||
|
new_object.attrib["id"] = raw_object.attrib["id"]
|
||||||
|
|
||||||
|
if new_object:
|
||||||
|
raw_object = new_object
|
||||||
|
|
||||||
|
if raw_object.attrib.get("gid"):
|
||||||
|
return _parse_tile(raw_object, new_tileset, new_tileset_path)
|
||||||
|
|
||||||
|
return _get_parser(raw_object)(raw_object)
|
||||||
@@ -3,6 +3,7 @@ from pathlib import Path
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from pytiled_parser.common_types import OrderedPair
|
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.properties import parse as parse_properties
|
||||||
from pytiled_parser.parsers.tmx.wang_set import parse as parse_wangset
|
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.tileset import Frame, Grid, Tile, Tileset, Transformations
|
||||||
@@ -83,6 +84,10 @@ def _parse_tile(raw_tile: etree.Element, external_path: Optional[Path] = None) -
|
|||||||
for raw_frame in animation_element.findall("./frame"):
|
for raw_frame in animation_element.findall("./frame"):
|
||||||
tile.animation.append(_parse_frame(raw_frame))
|
tile.animation.append(_parse_frame(raw_frame))
|
||||||
|
|
||||||
|
object_element = raw_tile.find("./objectgroup")
|
||||||
|
if object_element:
|
||||||
|
tile.objects = parse_layer(object_element)
|
||||||
|
|
||||||
properties_element = raw_tile.find("./properties")
|
properties_element = raw_tile.find("./properties")
|
||||||
if properties_element:
|
if properties_element:
|
||||||
tile.properties = parse_properties(properties_element)
|
tile.properties = parse_properties(properties_element)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
|
import xml.etree.ElementTree as etree
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
@@ -145,5 +146,5 @@ class Tile(TiledObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
gid: int
|
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
|
new_tileset_path: Optional[Path] = None
|
||||||
|
|||||||
Reference in New Issue
Block a user