diff --git a/pytiled_parser/objects.py b/pytiled_parser/objects.py index b9fab0f..0e7667e 100644 --- a/pytiled_parser/objects.py +++ b/pytiled_parser/objects.py @@ -420,6 +420,21 @@ class LayerGroup(Layer): layers: Optional[List[Union["LayerGroup", Layer, ObjectLayer]]] +@attr.s(auto_attribs=True, kw_only=True) +class ImageLayer(Layer): + """Image Layer. + + An image layer displays a single image. + + See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#imagelayer + + Attributes: + image: the image to display for this layer. + """ + + image: Image + + @attr.s(auto_attribs=True) class TileSet: """Object for storing a TSX with all associated collision data. diff --git a/pytiled_parser/xml_parser.py b/pytiled_parser/xml_parser.py index e400d53..d03fed7 100644 --- a/pytiled_parser/xml_parser.py +++ b/pytiled_parser/xml_parser.py @@ -261,55 +261,109 @@ def _parse_tiled_objects( tiled_objects: List[objects.TiledObject] = [] for object_element in object_elements: - id_ = int(object_element.attrib["id"]) - location_x = float(object_element.attrib["x"]) - location_y = float(object_element.attrib["y"]) - location = objects.OrderedPair(location_x, location_y) - - tiled_object = objects.TiledObject(id_=id_, location=location) - - try: - tiled_object.gid = int(object_element.attrib["gid"]) - except KeyError: - tiled_object.gid = None - - try: - # If any dimension is provided, they both will be - width = float(object_element.attrib["width"]) - height = float(object_element.attrib["height"]) - tiled_object.size = objects.Size(width, height) - except KeyError: - pass - - try: - tiled_object.opacity = float(object_element.attrib["opacity"]) - except KeyError: - pass - - try: - tiled_object.rotation = float(object_element.attrib["rotation"]) - except KeyError: - pass - - try: - tiled_object.name = object_element.attrib["name"] - except KeyError: - pass - - try: - tiled_object.type = object_element.attrib["type"] - except KeyError: - pass - - properties_element = object_element.find("./properties") - if properties_element is not None: - tiled_object.properties = _parse_properties_element(properties_element) - - tiled_objects.append(tiled_object) + my_object = _parse_object(object_element) + if my_object is not None: + tiled_objects.append(my_object) return tiled_objects +def _parse_object(obj): + my_id = obj.attrib["id"] + my_x = float(obj.attrib["x"]) + my_y = float(obj.attrib["y"]) + my_location = objects.OrderedPair(x=my_x, y=my_y) + if "width" in obj.attrib: + my_width = float(obj.attrib["width"]) + else: + my_width = None + if "height" in obj.attrib: + my_height = float(obj.attrib["height"]) + else: + my_height = None + my_name = obj.attrib["name"] + + properties: Optional[objects.Properties] + properties_element = obj.find("./properties") + if properties_element is not None: + properties = _parse_properties_element(properties_element) + else: + properties = None + + # This is where it would be nice if we could assume a walrus + # operator was part of our Python distribution. + + my_object = None + + polygon = obj.findall("./polygon") + + if polygon and len(polygon) > 0: + points = _parse_points(polygon[0].attrib["points"]) + my_object = objects.PolygonObject( + id_=my_id, + name=my_name, + location=my_location, + size=(my_width, my_height), + points=points, + properties=properties, + ) + + if my_object is None: + polyline = obj.findall("./polyline") + + if polyline and len(polyline) > 0: + points = _parse_points(polyline[0].attrib["points"]) + my_object = objects.PolylineObject( + id_=my_id, + name=my_name, + location=my_location, + size=(my_width, my_height), + points=points, + properties=properties, + ) + + if my_object is None: + ellipse = obj.findall("./ellipse") + + if ellipse and len(ellipse): + my_object = objects.ElipseObject( + id_=my_id, + name=my_name, + location=my_location, + size=(my_width, my_height), + properties=properties, + ) + + if my_object is None: + if "template" in obj.attrib: + print( + "Warning, this .tmx file is using an unsupported" + "'template' attribute. Ignoring." + ) + + if my_object is None: + point = obj.findall("./point") + if point: + my_object = objects.PointObject( + id_=my_id, + name=my_name, + location=my_location, + properties=properties, + ) + + if my_object is None: + my_object = objects.RectangleObject( + id_=my_id, + name=my_name, + location=my_location, + size=(my_width, my_height), + properties=properties, + ) + + return my_object + + + def _parse_object_layer(element: etree.Element,) -> objects.ObjectLayer: """Parse the objectgroup element given. @@ -349,6 +403,29 @@ def _parse_object_layer(element: etree.Element,) -> objects.ObjectLayer: ) +def _parse_image_layer(element: etree.Element,) -> objects.ImageLayer: + """Parse the imagelayer element given. + + Args: + element: Element to be parsed. + + Returns: + ImageLayer: The image layer object. + """ + id_, name, offset, opacity, properties = _parse_layer(element) + + image = _parse_image_element(element.find("./image")) + + return objects.ImageLayer( + id_=id_, + name=name, + offset=offset, + opacity=opacity, + properties=properties, + image=image, + ) + + def _parse_layer_group(element: etree.Element,) -> objects.LayerGroup: """Parse the objectgroup element given. @@ -393,6 +470,8 @@ def _get_layer_parser( return _parse_object_layer if layer_tag == "group": return _parse_layer_group + if layer_tag == "imagelayer": + return _parse_image_layer return None @@ -451,7 +530,7 @@ def _parse_points(point_string: str) -> List[objects.OrderedPair]: xys = str_pair.split(",") x = float(xys[0]) y = float(xys[1]) - points.append((x, y)) + points.append(objects.OrderedPair(x, y)) return points @@ -531,69 +610,10 @@ def _parse_tiles(tile_element_list: List[etree.Element]) -> Dict[int, objects.Ti if objectgroup_element: objectgroup = [] object_list = objectgroup_element.findall("./object") - for object in object_list: - my_id = object.attrib["id"] - my_x = float(object.attrib["x"]) - my_y = float(object.attrib["y"]) - if "width" in object.attrib: - my_width = float(object.attrib["width"]) - else: - my_width = None - if "height" in object.attrib: - my_height = float(object.attrib["height"]) - else: - my_height = None - - # This is where it would be nice if we could assume a walrus - # operator was part of our Python distribution. - - my_object = None - - polygon = object.findall("./polygon") - - if polygon and len(polygon) > 0: - points = _parse_points(polygon[0].attrib["points"]) - my_object = objects.PolygonObject( - id_=my_id, - location=(my_x, my_y), - size=(my_width, my_height), - points=points, - ) - - if my_object is None: - polyline = object.findall("./polyline") - - if polyline and len(polyline) > 0: - points = _parse_points(polyline[0].attrib["points"]) - my_object = objects.PolylineObject( - id_=my_id, - location=(my_x, my_y), - size=(my_width, my_height), - points=points, - ) - - if my_object is None: - ellipse = object.findall("./ellipse") - - if ellipse and len(ellipse): - my_object = objects.ElipseObject( - id_=my_id, location=(my_x, my_y), size=(my_width, my_height) - ) - - if my_object is None: - if "template" in object.attrib: - print( - "Warning, this .tmx file is using an unsupported" - "'template' attribute. Ignoring." - ) - continue - - if my_object is None: - my_object = objects.RectangleObject( - id_=my_id, location=(my_x, my_y), size=(my_width, my_height) - ) - - objectgroup.append(my_object) + for obj in object_list: + my_object = _parse_object(obj) + if my_object is not None: + objectgroup.append(my_object) # if this is None, then the Tile is part of a spritesheet image = None