crocparty/pytools/mapdata.py

162 lines
5.5 KiB
Python
Raw Normal View History

2024-02-27 03:51:26 +00:00
import sys
import json
import shared
2024-02-29 05:07:17 +00:00
import textwrap
2024-02-27 03:51:26 +00:00
TEMPLATE = """
// generated code! be nice
2024-02-29 03:42:03 +00:00
#include <stdbool.h>
2024-02-27 03:51:26 +00:00
#include "sys/sys.h"
sys_maptile map_{{map_name}}_data[{{width * height}}] = { {{ tiles|join(",") }} };
sys_map map_{{map_name}} = {
.tiles=map_{{map_name}}_data,
2024-02-27 03:51:26 +00:00
.width={{width}},
.height={{height}},
};
2024-02-28 21:14:48 +00:00
{% for entity_type in entity_types %}
void map_{{map_name}}_{{entity_type}}_create(sys_i32 x, sys_i32 y);
{% endfor %}
2024-02-29 03:05:52 +00:00
{% for entity_name, field_name, c_type, render_mode in entity_fields %}
{% if render_mode == "scalar" %}
void map_{{map_name}}_{{entity_name}}_set_{{field_name}}({{c_type}} value);
{% elif render_mode == "array" %}
void map_{{map_name}}_{{entity_name}}_add_{{field_name}}({{c_type}} value);
{% else %}
wtf; // {{ render_mode }}
{% endif %}
{% endfor %}
2024-02-28 21:14:48 +00:00
void map_{{map_name}}_create_entities() {
{% for entity in entities %}
map_{{map_name}}_{{entity.type}}_create({{entity.x}}, {{entity.y}});
2024-02-29 03:05:52 +00:00
{% for field in entity.fields %}
{% if field.renderMode == "scalar" %}
map_{{map_name}}_{{entity.type}}_set_{{field.name}}({{field.value|safe}});
{% elif field.renderMode == "array" %}
{% for s in field.value %}
map_{{map_name}}_{{entity.type}}_add_{{field.name}}({{s|safe}});
{% endfor %}
{% endif %}
{% endfor %}
2024-02-28 21:14:48 +00:00
{% endfor %}
}
2024-02-27 03:51:26 +00:00
""".lstrip()
def main(map_name, fname_ldtk, fname_c):
2024-02-29 03:05:52 +00:00
width, height, tiles, entities, entity_types, entity_fields = load_mapdata(fname_ldtk)
print(entity_fields)
2024-02-27 03:51:26 +00:00
with open(fname_c, "wt") as output:
output.write(
shared.templates.from_string(TEMPLATE).render(
map_name=map_name,
tiles=tiles,
width=width,
height=height,
2024-02-28 21:14:48 +00:00
entities=entities,
2024-02-29 03:05:52 +00:00
entity_types=entity_types,
entity_fields=entity_fields,
2024-02-27 03:51:26 +00:00
)
)
def load_mapdata(fname_ldtk):
sparse_tiles = {}
2024-02-27 23:09:51 +00:00
entities = []
2024-02-29 03:05:52 +00:00
entity_types = set()
entity_fields = set()
2024-02-27 23:09:51 +00:00
2024-02-27 03:51:26 +00:00
with open(fname_ldtk, "rt") as f:
data = json.load(f)
for level in data["levels"]:
level_x = level["worldX"] // 8
level_y = level["worldY"] // 8
for layer in level["layerInstances"]:
2024-02-27 23:09:51 +00:00
if layer["__identifier"] == "conceptual":
2024-02-27 03:51:26 +00:00
# for right now I use the vague layer to assign tiles
2024-02-27 23:09:51 +00:00
for tile in layer["autoLayerTiles"]:
x, y = tile["px"]
x //= 8
y //= 8
ix = tile["t"]
2024-02-27 03:51:26 +00:00
sparse_tiles[level_x + x, level_y + y] = ix
if layer["__identifier"] == "entities":
for e in layer["entityInstances"]:
2024-02-28 21:14:48 +00:00
# TODO: Other fields?
2024-02-29 03:05:52 +00:00
entity = {
2024-02-28 21:14:48 +00:00
"type": e["__identifier"],
"x": e["__worldX"] // 8,
"y": e["__worldY"] // 8,
2024-02-29 03:05:52 +00:00
"fields": []
}
for f in e["fieldInstances"]:
field = {
"name": f["__identifier"],
}
field["value"], field["renderMode"], rendered_type = format_field_value(f["__type"], f["__value"])
entity["fields"].append(field)
entity_fields.add((
entity["type"],
field["name"],
rendered_type,
field["renderMode"]
))
entities.append(entity)
entity_types.add(entity["type"])
2024-02-27 03:51:26 +00:00
x_min = 0
y_min = 0
assert not any(x for (x, _) in sparse_tiles if x < x_min), "level can't be left of (0, 0)"
assert not any(y for (_, y) in sparse_tiles if y < y_min), "level can't be up from (0, 0)"
width = max(x for (x, _) in sparse_tiles) + 1
height = max(y for (_, y) in sparse_tiles) + 1
dense_tiles = []
for y in range(height):
for x in range(width):
k = (x, y)
if k in sparse_tiles:
dense_tiles.append(sparse_tiles[k])
else:
2024-02-27 23:09:51 +00:00
dense_tiles.append(255)
2024-02-27 03:51:26 +00:00
2024-02-29 03:05:52 +00:00
return width, height, dense_tiles, entities, entity_types, entity_fields
def format_field_value(ty, val):
2024-02-29 03:42:03 +00:00
if ty == "Bool":
2024-02-29 21:20:18 +00:00
return ("true" if val else "false"), "scalar", "bool"
2024-02-29 03:42:03 +00:00
elif ty == "Int":
2024-02-29 03:05:52 +00:00
return str(val), "scalar", "sys_i32"
elif ty == "String":
if val is None:
return "NULL", "scalar", "const char*"
2024-02-29 21:20:18 +00:00
return json.dumps(wrap(val)), "scalar", "const char*" # this is close enough to being right in C
2024-02-29 03:05:52 +00:00
elif ty == "Array<Int>":
return [format_field_value("Int", i)[0] for i in val], "array", "sys_i32"
elif ty == "Array<String>":
return [format_field_value("String", i)[0] for i in val], "array", "const char*"
2024-02-29 03:42:03 +00:00
else:
assert False, f"unknown type: {ty}"
2024-02-27 03:51:26 +00:00
2024-02-29 05:07:17 +00:00
def wrap(s):
return textwrap.fill(s, width=28)
2024-02-27 03:51:26 +00:00
def annot_xy(lst, w, h):
assert len(lst) == w * h
for y in range(h):
for x in range(w):
yield x, y, lst[y * w + x]
if __name__ == "__main__":
assert len(sys.argv) == 4, \
"there must be three args (map name, src ldtk, out c)"
main(sys.argv[1], sys.argv[2], sys.argv[3])