Fix templates for tile objects. See #41

This commit is contained in:
Darren Eberly
2021-07-28 22:37:30 -04:00
parent 0ec7941491
commit 30f52fd7ce
4 changed files with 109 additions and 48 deletions

View File

@@ -11,10 +11,9 @@ See:
import base64
import gzip
import importlib.util
import sys
import zlib
from pathlib import Path
from typing import Any, Callable, List, Optional, Union
from typing import Any, List, Optional, Union
from typing import cast as type_cast
import attr
@@ -284,7 +283,9 @@ def _decode_tile_layer_data(
def _cast_chunk(
raw_chunk: RawChunk, encoding: Optional[str] = None, compression: str = None
raw_chunk: RawChunk,
encoding: Optional[str] = None,
compression: Optional[str] = None,
) -> Chunk:
"""Cast the raw_chunk to a Chunk.
@@ -403,7 +404,8 @@ def _cast_tile_layer(raw_layer: RawLayer) -> TileLayer:
def _cast_object_layer(
raw_layer: RawLayer, parent_dir: Optional[Path] = None
raw_layer: RawLayer,
parent_dir: Optional[Path] = None,
) -> ObjectLayer:
"""Cast the raw_layer to an ObjectLayer.
@@ -458,46 +460,38 @@ def _cast_group_layer(
layers = []
for layer in raw_layer["layers"]:
layers.append(cast(layer, parent_dir))
layers.append(cast(layer, parent_dir=parent_dir))
return LayerGroup(layers=layers, **_get_common_attributes(raw_layer).__dict__)
def _get_caster(type_: str) -> Callable[[RawLayer], Layer]:
"""Get the caster function for the raw layer.
Args:
type_: the type of the layer
Returns:
Callable[[RawLayer], Layer]: The caster function.
"""
casters = {
"tilelayer": _cast_tile_layer,
"objectgroup": _cast_object_layer,
"imagelayer": _cast_image_layer,
"group": _cast_group_layer,
}
return casters[type_]
def cast(raw_layer: RawLayer, parent_dir: Optional[Path] = None) -> Layer:
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.
"""
caster = _get_caster(raw_layer["type"])
if (
caster.__name__ == "_cast_object_layer"
or caster.__name__ == "_cast_group_layer"
):
return caster(raw_layer, parent_dir)
else:
return caster(raw_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")

View File

@@ -132,12 +132,16 @@ def parse_map(file: Path) -> TiledMap:
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), external_path=tileset_path.parent
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)
tilesets[raw_tileset["firstgid"]] = tileset.cast(
raw_tileset, raw_tileset["firstgid"]
)
if isinstance(raw_tiled_map["version"], float):
version = str(raw_tiled_map["version"])
@@ -160,6 +164,37 @@ def parse_map(file: Path) -> TiledMap:
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"])

View File

@@ -1,7 +1,7 @@
# pylint: disable=too-few-public-methods
import json
from pathlib import Path
from typing import Callable, Dict, List, Optional, Union
from typing import Any, Callable, Dict, List, Optional, Union
import attr
from typing_extensions import TypedDict
@@ -148,6 +148,8 @@ class Tile(TiledObject):
"""
gid: int
new_tileset: Optional[Dict[str, Any]] = None
new_tileset_path: Optional[Path] = None
class RawTextDict(TypedDict):
@@ -257,7 +259,11 @@ def _cast_point(raw_tiled_object: RawTiledObject) -> Point:
return Point(**_get_common_attributes(raw_tiled_object).__dict__)
def _cast_tile(raw_tiled_object: RawTiledObject) -> Tile:
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:
@@ -268,7 +274,12 @@ def _cast_tile(raw_tiled_object: RawTiledObject) -> Tile:
"""
gid = raw_tiled_object["gid"]
return Tile(gid=gid, **_get_common_attributes(raw_tiled_object).__dict__)
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:
@@ -393,16 +404,24 @@ def _get_caster(
def cast(
raw_tiled_object: RawTiledObject, parent_dir: Optional[Path] = None
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(
@@ -410,12 +429,21 @@ def cast(
)
template_path = Path(parent_dir / raw_tiled_object["template"])
with open(template_path) as raw_template_file:
loaded_template = json.load(raw_template_file)["object"]
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
caster = _get_caster(raw_tiled_object)
if raw_tiled_object.get("gid"):
return _cast_tile(raw_tiled_object, new_tileset, new_tileset_path)
tiled_object = caster(raw_tiled_object)
return tiled_object
return _get_caster(raw_tiled_object)(raw_tiled_object)

View File

@@ -130,6 +130,8 @@ class Tileset:
tile_count: int
columns: int
firstgid: int
type: str = "tileset"
spacing: int = 0
@@ -144,7 +146,6 @@ class Tileset:
transformations: Optional[Transformations] = None
firstgid: Optional[int] = None
background_color: Optional[Color] = None
tile_offset: Optional[OrderedPair] = None
transparent_color: Optional[Color] = None
@@ -329,11 +330,16 @@ def _cast_grid(raw_grid: RawGrid) -> Grid:
)
def cast(raw_tileset: RawTileSet, external_path: Optional[Path] = None) -> Tileset:
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:
@@ -348,6 +354,7 @@ def cast(raw_tileset: RawTileSet, external_path: Optional[Path] = None) -> Tiles
columns=raw_tileset["columns"],
spacing=raw_tileset["spacing"],
margin=raw_tileset["margin"],
firstgid=firstgid,
)
if raw_tileset.get("version") is not None:
@@ -373,9 +380,6 @@ def cast(raw_tileset: RawTileSet, external_path: Optional[Path] = None) -> Tiles
if raw_tileset.get("imageheight") is not None:
tileset.image_height = raw_tileset["imageheight"]
if raw_tileset.get("firstgid") is not None:
tileset.firstgid = raw_tileset["firstgid"]
if raw_tileset.get("backgroundcolor") is not None:
tileset.background_color = parse_color(raw_tileset["backgroundcolor"])