move collisions in own layer

master
Thomas Lindner 2021-12-26 23:22:56 +01:00
parent e6dd495c9a
commit 4857c999d3
8 changed files with 495 additions and 30380 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
imgs/collisions.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 B

6554
main.json

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,14 @@
#!/usr/bin/env python3
import argparse
from argparse import ArgumentParser
from lxml import etree
from pathlib import Path
from PIL import Image
import sys
import xml.etree.ElementTree as ET
def main():
parser = argparse.ArgumentParser(description="Cleanup rc3.world maps")
parser = ArgumentParser(description="Cleanup rc3.world maps")
parser.add_argument("map", help="map in .tmx format")
parser.add_argument("-o", "--output", help="where to write modified map")
parser.add_argument(
@ -20,17 +22,34 @@ def main():
help="add mapCopyright/tilesetCopyright properties",
action="store_true",
)
parser.add_argument(
"--separate-collisions",
help="move collisions into a separate 'collisions' layer",
action="store_true",
)
parser.add_argument(
"--split-tilesets",
help="split tilesets bigger than 4096x4096",
action="store_true",
)
args = parser.parse_args()
if not args.output and not args.in_place:
output = args.output
if args.in_place:
output = args.map
if not output:
print("Output unspecified.", file=sys.stderr)
sys.exit(1)
tree = ET.parse(args.map)
tree = etree.parse(args.map)
map = tree.getroot()
if args.fix_copyright:
fix_copyright(map)
tree.write(args.output or args.map, xml_declaration=False)
if args.separate_collisions:
separate_collisions(map)
if args.split_tilesets:
split_tilesets(map)
tree.write(output)
def fix_copyright(map):
@ -38,8 +57,8 @@ def fix_copyright(map):
license = input("Map license? [CC0] ") or "CC0"
properties = map.find("properties")
if properties is None:
properties = ET.SubElement(map, "properties")
ET.SubElement(properties, "property", name="mapCopyright", value=license)
properties = etree.SubElement(map, "properties")
etree.SubElement(properties, "property", name="mapCopyright", value=license)
for tileset in map.findall("tileset"):
if tileset.find("properties/property[@name='tilesetCopyright']") is None:
@ -48,11 +67,153 @@ def fix_copyright(map):
)
properties = tileset.find("properties")
if properties is None:
properties = ET.SubElement(tileset, "properties")
ET.SubElement(
properties = etree.SubElement(tileset, "properties")
etree.SubElement(
properties, "property", name="tilesetCopyright", value=license
)
def separate_collisions(map):
# find tiles with collides property
colliding_tiles = set()
firstgid = 1
tilecount = 0
for tileset in map.findall("tileset"):
if tileset.attrib["name"] == "collisions":
continue
firstgid = int(tileset.attrib["firstgid"])
tilecount = int(tileset.attrib["tilecount"])
for tile in tileset.findall("tile"):
properties = tile.find("properties")
if properties is None:
continue
for collision_property in properties.findall("property[@name='collides']"):
properties.remove(collision_property)
if collision_property.attrib["value"] != "true":
continue
colliding_tiles.add(firstgid + int(tile.attrib["id"]))
# create/find collisions tileset (single white tile)
collisions_tileset = map.find("tileset[@name='collisions']")
if collisions_tileset is None:
collisions_tileset = etree.Element(
"tileset",
firstgid=str(firstgid + tilecount),
name="collisions",
tilewidth="32",
tileheight="32",
tilecount="1",
columns="1",
)
tileset.addnext(collisions_tileset)
etree.SubElement(
etree.SubElement(collisions_tileset, "properties"),
"property",
name="tilesetCopyright",
value="not copyrightable",
)
image_source = Path("imgs") / "collisions.png"
image_source.parent.mkdir(parents=True, exist_ok=True)
Image.new("1", (32, 32), (1)).save(image_source)
etree.SubElement(
collisions_tileset,
"image",
source=str(image_source),
width="32",
height="32",
)
etree.SubElement(
etree.SubElement(
etree.SubElement(collisions_tileset, "tile", id="0"), "properties"
),
"property",
name="collides",
type="bool",
value="true",
)
collisions_gid = int(collisions_tileset.attrib["firstgid"])
# merge collisions
collisions = [0] * int(map.attrib["width"]) * int(map.attrib["height"])
for layer in map.findall("layer"):
if layer.attrib["name"] == "collisions":
continue
data = [int(x) for x in layer.find("data[@encoding='csv']").text.split(",")]
for i, gid in enumerate(data):
if gid in colliding_tiles:
collisions[i] = collisions_gid
# create/find collisions layer and save collision data
collisions_data = map.find("layer[@name='collisions']/data")
if collisions_data is None:
collisions_layer = etree.Element(
"layer",
id=map.attrib["nextlayerid"],
name="collisions",
width=map.attrib["width"],
height=map.attrib["height"],
)
map.find("layer").addprevious(collisions_layer)
map.attrib["nextlayerid"] = str(int(map.attrib["nextlayerid"]) + 1)
collisions_data = etree.SubElement(
collisions_layer,
"data",
encoding="csv",
)
collisions_data.text = ",".join([str(x) for x in collisions])
def split_tilesets(map):
# we need the offset of the tileset inside the map element to insert new tilesets correctly
for offset, tileset in enumerate(map):
if tileset.tag != "tileset":
continue
image = tileset.find("image")
if int(image.attrib["width"]) <= 4096 and int(image.attrib["height"]) <= 4096:
continue
dosplit = input("Split tileset '%s'? [yN] " % tileset.attrib["name"])
if dosplit.lower() != "y":
continue
properties = tileset.find("properties")
firstgid = int(tileset.attrib["firstgid"])
tilewidth = int(tileset.attrib["tilewidth"])
tileheight = int(tileset.attrib["tileheight"])
image_source = Path(image.attrib["source"])
with Image.open(image_source) as im:
for i, x in enumerate(range(0, im.width, 4096)):
for j, y in enumerate(range(0, im.height, 4096)):
croped_source = image_source.with_stem(
"%s_%02d%02d" % (image_source.stem, i, j)
)
im.crop(
(x, y, min(x + 4096, im.width), min(y + 4096, im.height))
).save(croped_source)
width = min(4096, im.width - x)
height = min(4096, im.height - y)
columns = width // tilewidth
tilecount = columns * (height // tileheight)
tileset_croped = etree.Element(
"tileset",
name="%s (%d, %d)" % (tileset.attrib["name"], i, j),
firstgid=str(firstgid),
tilewidth=str(tilewidth),
tileheight=str(tileheight),
tilecount=str(tilecount),
columns=str(columns),
)
map.insert(offset + i + j, tileset_croped)
firstgid += tilecount
if properties is not None:
tileset_croped.append(properties)
etree.SubElement(
tileset_croped,
"image",
source=str(croped_source),
width=str(width),
height=str(height),
)
map.remove(tileset)
if __name__ == "__main__":
main()