Skip to content

Commit f63890c

Browse files
authored
Merge pull request #42 from benjamin-kirkbride/development
Merge Development for v1.5.2
2 parents 4215d6c + 91ecb7f commit f63890c

File tree

33 files changed

+277
-465
lines changed

33 files changed

+277
-465
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
## [1.5.2] - 2021-07-28
10+
11+
This release contains some re-working to properly support templates.
12+
13+
Previously templates were supposedly fully supported, however they were broken for tile objects. Now there should be out of the box support for auto-loading tile objects from templates. Regardless of where the tileset is defined. See Issue https://github.com/benjamin-kirkbride/pytiled_parser/issues/41 for details
14+
15+
There are no API changes as far as usage is concerned, these were all internal changes to properly support the feature.
16+
17+
This release also contains some minor fixes to docstrings and typing annotations/linting problems.
18+
919
## [1.5.1] - 2021-07-09
1020

1121
This release contains two bugfixes:

pytiled_parser/layer.py

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
import base64
1212
import gzip
1313
import importlib.util
14-
import sys
1514
import zlib
1615
from pathlib import Path
17-
from typing import Any, Callable, List, Optional, Union
16+
from typing import Any, List, Optional, Union
1817
from typing import cast as type_cast
1918

2019
import attr
@@ -284,7 +283,9 @@ def _decode_tile_layer_data(
284283

285284

286285
def _cast_chunk(
287-
raw_chunk: RawChunk, encoding: Optional[str] = None, compression: str = None
286+
raw_chunk: RawChunk,
287+
encoding: Optional[str] = None,
288+
compression: Optional[str] = None,
288289
) -> Chunk:
289290
"""Cast the raw_chunk to a Chunk.
290291
@@ -403,7 +404,8 @@ def _cast_tile_layer(raw_layer: RawLayer) -> TileLayer:
403404

404405

405406
def _cast_object_layer(
406-
raw_layer: RawLayer, parent_dir: Optional[Path] = None
407+
raw_layer: RawLayer,
408+
parent_dir: Optional[Path] = None,
407409
) -> ObjectLayer:
408410
"""Cast the raw_layer to an ObjectLayer.
409411
@@ -458,46 +460,38 @@ def _cast_group_layer(
458460
layers = []
459461

460462
for layer in raw_layer["layers"]:
461-
layers.append(cast(layer, parent_dir))
463+
layers.append(cast(layer, parent_dir=parent_dir))
462464

463465
return LayerGroup(layers=layers, **_get_common_attributes(raw_layer).__dict__)
464466

465467

466-
def _get_caster(type_: str) -> Callable[[RawLayer], Layer]:
467-
"""Get the caster function for the raw layer.
468-
469-
Args:
470-
type_: the type of the layer
471-
472-
Returns:
473-
Callable[[RawLayer], Layer]: The caster function.
474-
"""
475-
casters = {
476-
"tilelayer": _cast_tile_layer,
477-
"objectgroup": _cast_object_layer,
478-
"imagelayer": _cast_image_layer,
479-
"group": _cast_group_layer,
480-
}
481-
return casters[type_]
482-
483-
484-
def cast(raw_layer: RawLayer, parent_dir: Optional[Path] = None) -> Layer:
468+
def cast(
469+
raw_layer: RawLayer,
470+
parent_dir: Optional[Path] = None,
471+
) -> Layer:
485472
"""Cast a raw Tiled layer into a pytiled_parser type.
486473
487474
This function will determine the type of layer and cast accordingly.
488475
489476
Args:
490477
raw_layer: Raw layer to be cast.
478+
parent_dir: The parent directory that the map file is in.
491479
492480
Returns:
493481
Layer: a properly typed Layer.
494-
"""
495-
caster = _get_caster(raw_layer["type"])
496482
497-
if (
498-
caster.__name__ == "_cast_object_layer"
499-
or caster.__name__ == "_cast_group_layer"
500-
):
501-
return caster(raw_layer, parent_dir)
502-
else:
503-
return caster(raw_layer)
483+
Raises:
484+
RuntimeError: For an invalid layer type being provided
485+
"""
486+
type_ = raw_layer["type"]
487+
488+
if type_ == "objectgroup":
489+
return _cast_object_layer(raw_layer, parent_dir)
490+
elif type_ == "group":
491+
return _cast_group_layer(raw_layer, parent_dir)
492+
elif type_ == "imagelayer":
493+
return _cast_image_layer(raw_layer)
494+
elif type_ == "tilelayer":
495+
return _cast_tile_layer(raw_layer)
496+
497+
raise RuntimeError(f"An invalid layer type of {type_} was supplied")

pytiled_parser/tiled_map.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,16 @@ def parse_map(file: Path) -> TiledMap:
132132
tileset_path = Path(parent_dir / raw_tileset["source"])
133133
with open(tileset_path) as raw_tileset_file:
134134
tilesets[raw_tileset["firstgid"]] = tileset.cast(
135-
json.load(raw_tileset_file), external_path=tileset_path.parent
135+
json.load(raw_tileset_file),
136+
raw_tileset["firstgid"],
137+
external_path=tileset_path.parent,
136138
)
137139
else:
138140
# Is an embedded Tileset
139141
raw_tileset = typing_cast(RawTileSet, raw_tileset)
140-
tilesets[raw_tileset["firstgid"]] = tileset.cast(raw_tileset)
142+
tilesets[raw_tileset["firstgid"]] = tileset.cast(
143+
raw_tileset, raw_tileset["firstgid"]
144+
)
141145

142146
if isinstance(raw_tiled_map["version"], float):
143147
version = str(raw_tiled_map["version"])
@@ -160,6 +164,37 @@ def parse_map(file: Path) -> TiledMap:
160164
version=version,
161165
)
162166

167+
layers = [layer for layer in map_.layers if hasattr(layer, "tiled_objects")]
168+
169+
for my_layer in layers:
170+
for tiled_object in my_layer.tiled_objects: # type: ignore
171+
if hasattr(tiled_object, "new_tileset"):
172+
if tiled_object.new_tileset:
173+
already_loaded = None
174+
for val in map_.tilesets.values():
175+
if val.name == tiled_object.new_tileset["name"]:
176+
already_loaded = val
177+
break
178+
179+
if not already_loaded:
180+
highest_firstgid = max(map_.tilesets.keys())
181+
last_tileset_count = map_.tilesets[highest_firstgid].tile_count
182+
new_firstgid = highest_firstgid + last_tileset_count
183+
map_.tilesets[new_firstgid] = tileset.cast(
184+
tiled_object.new_tileset,
185+
new_firstgid,
186+
tiled_object.new_tileset_path,
187+
)
188+
tiled_object.gid = tiled_object.gid + (new_firstgid - 1)
189+
190+
else:
191+
tiled_object.gid = tiled_object.gid + (
192+
already_loaded.firstgid - 1
193+
)
194+
195+
tiled_object.new_tileset = None
196+
tiled_object.new_tileset_path = None
197+
163198
if raw_tiled_map.get("backgroundcolor") is not None:
164199
map_.background_color = parse_color(raw_tiled_map["backgroundcolor"])
165200

pytiled_parser/tiled_object.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# pylint: disable=too-few-public-methods
22
import json
33
from pathlib import Path
4-
from typing import Callable, Dict, List, Optional, Union
4+
from typing import Any, Callable, Dict, List, Optional, Union
55

66
import attr
77
from typing_extensions import TypedDict
@@ -148,6 +148,8 @@ class Tile(TiledObject):
148148
"""
149149

150150
gid: int
151+
new_tileset: Optional[Dict[str, Any]] = None
152+
new_tileset_path: Optional[Path] = None
151153

152154

153155
class RawTextDict(TypedDict):
@@ -257,7 +259,11 @@ def _cast_point(raw_tiled_object: RawTiledObject) -> Point:
257259
return Point(**_get_common_attributes(raw_tiled_object).__dict__)
258260

259261

260-
def _cast_tile(raw_tiled_object: RawTiledObject) -> Tile:
262+
def _cast_tile(
263+
raw_tiled_object: RawTiledObject,
264+
new_tileset: Optional[Dict[str, Any]] = None,
265+
new_tileset_path: Optional[Path] = None,
266+
) -> Tile:
261267
"""Cast the raw_tiled_object to a Tile object.
262268
263269
Args:
@@ -268,7 +274,12 @@ def _cast_tile(raw_tiled_object: RawTiledObject) -> Tile:
268274
"""
269275
gid = raw_tiled_object["gid"]
270276

271-
return Tile(gid=gid, **_get_common_attributes(raw_tiled_object).__dict__)
277+
return Tile(
278+
gid=gid,
279+
new_tileset=new_tileset,
280+
new_tileset_path=new_tileset_path,
281+
**_get_common_attributes(raw_tiled_object).__dict__
282+
)
272283

273284

274285
def _cast_polygon(raw_tiled_object: RawTiledObject) -> Polygon:
@@ -393,29 +404,46 @@ def _get_caster(
393404

394405

395406
def cast(
396-
raw_tiled_object: RawTiledObject, parent_dir: Optional[Path] = None
407+
raw_tiled_object: RawTiledObject,
408+
parent_dir: Optional[Path] = None,
397409
) -> TiledObject:
398410
"""Cast the raw tiled object into a pytiled_parser type
399411
400412
Args:
401413
raw_tiled_object: Raw Tiled object that is to be cast.
414+
parent_dir: The parent directory that the map file is in.
402415
403416
Returns:
404417
TiledObject: a properly typed Tiled object.
418+
419+
Raises:
420+
RuntimeError: When a required parameter was not sent based on a condition.
405421
"""
422+
new_tileset = None
423+
new_tileset_path = None
424+
406425
if raw_tiled_object.get("template"):
407426
if not parent_dir:
408427
raise RuntimeError(
409428
"A parent directory must be specified when using object templates"
410429
)
411430
template_path = Path(parent_dir / raw_tiled_object["template"])
412431
with open(template_path) as raw_template_file:
413-
loaded_template = json.load(raw_template_file)["object"]
432+
template = json.load(raw_template_file)
433+
if "tileset" in template:
434+
tileset_path = Path(
435+
template_path.parent / template["tileset"]["source"]
436+
)
437+
with open(tileset_path) as raw_tileset_file:
438+
new_tileset = json.load(raw_tileset_file)
439+
new_tileset_path = tileset_path.parent
440+
441+
loaded_template = template["object"]
414442
for key in loaded_template:
415443
if key != "id":
416444
raw_tiled_object[key] = loaded_template[key] # type: ignore
417445

418-
caster = _get_caster(raw_tiled_object)
446+
if raw_tiled_object.get("gid"):
447+
return _cast_tile(raw_tiled_object, new_tileset, new_tileset_path)
419448

420-
tiled_object = caster(raw_tiled_object)
421-
return tiled_object
449+
return _get_caster(raw_tiled_object)(raw_tiled_object)

pytiled_parser/tileset.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ class Tileset:
130130
tile_count: int
131131
columns: int
132132

133+
firstgid: int
134+
133135
type: str = "tileset"
134136

135137
spacing: int = 0
@@ -144,7 +146,6 @@ class Tileset:
144146

145147
transformations: Optional[Transformations] = None
146148

147-
firstgid: Optional[int] = None
148149
background_color: Optional[Color] = None
149150
tile_offset: Optional[OrderedPair] = None
150151
transparent_color: Optional[Color] = None
@@ -329,11 +330,16 @@ def _cast_grid(raw_grid: RawGrid) -> Grid:
329330
)
330331

331332

332-
def cast(raw_tileset: RawTileSet, external_path: Optional[Path] = None) -> Tileset:
333+
def cast(
334+
raw_tileset: RawTileSet,
335+
firstgid: int,
336+
external_path: Optional[Path] = None,
337+
) -> Tileset:
333338
"""Cast the raw tileset into a pytiled_parser type
334339
335340
Args:
336341
raw_tileset: Raw Tileset to be cast.
342+
firstgid: GID corresponding the first tile in the set.
337343
external_path: The path to the tileset if it is not an embedded one.
338344
339345
Returns:
@@ -348,6 +354,7 @@ def cast(raw_tileset: RawTileSet, external_path: Optional[Path] = None) -> Tiles
348354
columns=raw_tileset["columns"],
349355
spacing=raw_tileset["spacing"],
350356
margin=raw_tileset["margin"],
357+
firstgid=firstgid,
351358
)
352359

353360
if raw_tileset.get("version") is not None:
@@ -373,9 +380,6 @@ def cast(raw_tileset: RawTileSet, external_path: Optional[Path] = None) -> Tiles
373380
if raw_tileset.get("imageheight") is not None:
374381
tileset.image_height = raw_tileset["imageheight"]
375382

376-
if raw_tileset.get("firstgid") is not None:
377-
tileset.firstgid = raw_tileset["firstgid"]
378-
379383
if raw_tileset.get("backgroundcolor") is not None:
380384
tileset.background_color = parse_color(raw_tileset["backgroundcolor"])
381385

pytiled_parser/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""pytiled_parser version"""
22

3-
__version__ = "1.5.1"
3+
__version__ = "1.5.2"

tests/test_data/map_tests/embedded_tileset/expected.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
image_height=199,
2222
margin=1,
2323
spacing=1,
24+
firstgid=1,
2425
name="tileset",
2526
tile_count=48,
2627
tile_height=32,
2728
tile_width=32,
28-
firstgid=1,
2929
)
3030
},
3131
)

tests/test_data/map_tests/external_tileset_dif_dir/expected.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
tiled_version="1.6.0",
8686
tile_height=32,
8787
tile_width=32,
88+
firstgid=1,
8889
version="1.6",
8990
type="tileset",
9091
grid=tileset.Grid(orientation="orthogonal", width=1, height=1),

tests/test_data/map_tests/hexagonal/expected.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
.resolve(),
144144
image_width=106,
145145
image_height=72,
146+
firstgid=1,
146147
margin=0,
147148
spacing=0,
148149
name="tileset",

tests/test_data/map_tests/no_background_color/expected.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
.resolve(),
2222
image_width=265,
2323
image_height=199,
24+
firstgid=1,
2425
margin=1,
2526
spacing=1,
2627
name="tile_set_image",

tests/test_data/map_tests/no_layers/expected.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
.resolve(),
2323
image_width=265,
2424
image_height=199,
25+
firstgid=1,
2526
margin=1,
2627
spacing=1,
2728
name="tile_set_image",

0 commit comments

Comments
 (0)