Collab_RPG/assets/converters.py
2024-07-31 14:38:29 +02:00

307 lines
12 KiB
Python

import xml.etree.ElementTree as ET
import json
import os
import sys
sys.path.append("../assets/")
import fxconv
from tiled import *
VERBOSE = 1
SIGN_TYPES = ["SGN", "INFO"]
FACES = ["MALE", "FEMALE", "MILKMAN", "POLICE"]
PRECISION = 8
def convert(input, output, params, target):
if params["custom-type"] == "tmx":
convert_map(input, output, params, target)
return 0
elif params["custom-type"] == "dialog":
convert_dialog(input, output, params, target)
return 0
def convert_map(input, output, params, target):
if VERBOSE: print(f"INFO: Converting map {input} -> {output}")
input_map = Map(input)
dialog_file = ""
background_layer = []
foreground_layer = []
walkable_layer = []
map_x = 0
map_y = 0
width = 0
height = 0
outdoor_tileset = None
walkable_tileset = None
dialog_num = 0
dialog_ids = []
npc_paths = {}
npcs = {}
signs = {}
map_struct = fxconv.Structure()
# Get the dialog file
try:
if VERBOSE: print("INFO: Getting the dialog file")
dialog_file = input_map.get_property("dialogFile")
if VERBOSE: print(f"INFO: Dialog file: {dialog_file}.")
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get the dialog file.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Get the map position
try:
if VERBOSE: print("INFO: Getting the map position")
map_x = int(input_map.get_property("mapX"))
map_y = int(input_map.get_property("mapY"))
if VERBOSE: print(f"INFO: Map position: ({map_x}, {map_y}).")
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get the map position.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Get informations about dialogs
try:
if VERBOSE: print("INFO: Getting informations about dialogs")
with open(f"{input_map.parent_dir}/{dialog_file}", "r") as file:
dialog_data = json.load(file)
dialog_num = len(dialog_data["dialogs"])
for i in dialog_data["dialogs"]:
dialog_ids.append(i["ID"])
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get informations about dialogs.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Get the outdoor tileset
try:
if VERBOSE: print("INFO: Getting the outdoor tileset")
outdoor_tileset = input_map.get_tileset_by_firstgid(1)
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get the outdoor tileset.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Get the walkable tileset
try:
if VERBOSE: print("INFO: Getting the walkable tileset")
walkable_tileset = input_map.get_tileset_by_firstgid(409)
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get the walkable tileset.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Get the background
try:
if VERBOSE: print("INFO: Getting the background layer")
bg_layer = input_map.get_layer_by_name("Background")
# The bg layer will be used to set the map width and height.
width = bg_layer.get_width()
height = bg_layer.get_height()
if VERBOSE: print(f"INFO: Map size: ({width}, {height}).")
# Get the layer data himself
background_layer = bg_layer.get_data_with_tileset(outdoor_tileset)
# Check if the size of the layer data is correct.
if len(background_layer) != width*height:
raise Exception("Bad layer size!")
if VERBOSE: print("INFO: Layer data has the right size.")
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get the background layer.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Get the foreground
try:
if VERBOSE: print("INFO: Getting the foreground layer")
fg_layer = input_map.get_layer_by_name("Foreground")
# Get the layer data himself
foreground_layer = fg_layer.get_data_with_tileset(outdoor_tileset)
# Check if the size of the layer data is correct.
if len(foreground_layer) != width*height:
raise Exception("Bad layer size!")
if VERBOSE: print("INFO: Layer data has the right size.")
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get the foreground layer.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Get the walkable layer
try:
if VERBOSE: print("INFO: Getting the walkable layer")
wk_layer = input_map.get_layer_by_name("Walkable")
# Get the layer data himself
walkable_layer = wk_layer.get_data_with_tileset(walkable_tileset)
# Check if the size of the layer data is correct.
if len(walkable_layer) != width*height:
raise Exception("Bad layer size!")
if VERBOSE: print("INFO: Layer data has the right size.")
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get the walkable layer.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Get the extra data
try:
if VERBOSE: print("INFO: Getting the extra data")
ed_objgroup = input_map.get_objectgroup_by_name("ExtraData")
# Get the paths the NPCs take.
for object in ed_objgroup.objects:
if object.get_data_type() == "polyline":
npc_paths[object.id] = object.get_data()
# Get the NPCs
for object in ed_objgroup.objects:
if object.get_data_type() == "point" and object.type == "NPC":
path = [0, 0]
if int(object.get_property("hasPath")):
if object.get_property("path") in npc_paths:
path = npc_paths[object.get_property("path")]
else:
raise Exception("Path required but not found!")
dialog_id = 0
has_dialog = 0
try:
dialog_id = int(object.get_property("dialogID"))
has_dialog = 1
except:
pass
data = {
"position": object.get_data(),
"name": object.name,
"needAction": int(object.get_property("needAction")),
"dialogID": dialog_id,
"hasDialog": has_dialog,
"face": FACES.index(object.get_property("face")),
"path": path
}
npcs[object.id] = data
# Get the signs
for object in ed_objgroup.objects:
if object.get_data_type() == "point" and object.type in SIGN_TYPES:
data = {
"position": object.get_data(),
"name": object.name,
"needAction": int(object.get_property("needAction")),
"dialogID": int(object.get_property("dialogID")),
"icon": SIGN_TYPES.index(object.type)
}
signs[object.id] = data
except Exception as e:
sys.stderr.write(f"ERROR: Failed to get the extra data.\n"
+ f" Error message: {e}\n")
sys.exit(1)
# Generate the structs
# Map struct
map_struct += fxconv.u32(map_x)
map_struct += fxconv.u32(map_y)
map_struct += fxconv.u32(width)
map_struct += fxconv.u32(height)
map_struct += fxconv.u32(3)
map_struct += fxconv.u32(outdoor_tileset.columns)
tileset_name = os.path.splitext(os.path.basename(outdoor_tileset.source))[0]
map_struct += fxconv.ref(f"img_{tileset_name}")
walkable_data = bytes()
for i in walkable_layer:
if i < 0: i = 0
walkable_data += fxconv.u8(i)
map_struct += fxconv.ptr(walkable_data)
# Load NPCs
map_struct += fxconv.u32(len(npcs))
npc_struct = fxconv.Structure()
for i in npcs.values():
npc_struct += fxconv.u32((i["position"][0]+i["path"][0])<<PRECISION)
npc_struct += fxconv.u32((i["position"][1]+i["path"][1])<<PRECISION)
npc_struct += fxconv.u32(i["position"][0])
npc_struct += fxconv.u32(i["position"][1])
npc_struct += fxconv.u16(i["face"])
npc_struct += fxconv.u8(0)
npc_struct += fxconv.u8(i["hasDialog"])
npc_struct += fxconv.u32(i["dialogID"])
npc_struct += fxconv.u32(i["needAction"])
npc_struct += fxconv.string(i["name"])
npc_struct += fxconv.u32(len(i["path"]) > 2)
npc_struct += fxconv.u32(len(i["path"])//2)
npc_struct += fxconv.u32(0)
xpath = bytes()
ypath = bytes()
x = True
for n in i["path"]:
if x: xpath += fxconv.u16(n)
else: ypath += fxconv.u16(n)
x = not x
npc_struct += fxconv.ptr(xpath)
npc_struct += fxconv.ptr(ypath)
npc_struct += fxconv.u32(0) # TODO: Type
npc_struct += fxconv.u8(0) # TODO: Group
npc_struct += fxconv.u8(0) # TODO: Hostile to
npc_struct += fxconv.u16(0) # TODO: Padding (what is it ?)
map_struct += fxconv.ptr(npc_struct)
# Load signs
map_struct += fxconv.u32(len(signs))
sign_struct = fxconv.Structure()
for i in signs.values():
sign_struct += fxconv.u32(i["position"][0])
sign_struct += fxconv.u32(i["position"][1])
sign_struct += fxconv.u32(i["icon"])
sign_struct += fxconv.string(i["name"])
sign_struct += fxconv.u32(i["dialogID"])
sign_struct += fxconv.u32(i["needAction"])
map_struct += fxconv.ptr(sign_struct)
# Load portals
map_struct += fxconv.u32(0) # TODO: Portal support in-game
map_struct += fxconv.ptr(bytes())
map_struct += fxconv.u32(dialog_num)
dialog_name = os.path.splitext(os.path.basename(dialog_file))[0]
map_struct += fxconv.ref(f"_{dialog_name}")
background_data = bytes()
for i in background_layer:
background_data += fxconv.u16(i)
map_struct += fxconv.ptr(background_data)
foreground_data = bytes()
for i in foreground_layer:
foreground_data += fxconv.u16(i)
map_struct += fxconv.ptr(foreground_data)
# Create the fxconv object
name = os.path.splitext(os.path.basename(input))[0]
fxconv.elf(map_struct, output, f"_{name}", **target)
def convert_dialog(input, output, params, target):
if VERBOSE: print(f"INFO: Converting dialog file {input} -> {output}")
dialog_data = None
try:
with open(input, "r") as file:
dialog_data = json.load(file)
except Exception as e:
sys.stderr.write(f"ERROR: Failed parse json.\n"
+ f" Error message: {e}\n")
sys.exit(1)
dialog_struct = fxconv.Structure()
try:
for i in dialog_data["dialogs"]:
dialog_id = i["ID"]
dialog_struct += fxconv.u32(dialog_id)
dialog_struct += fxconv.string(i["dialog"])
dialog_struct += fxconv.u32(i["isQuestion"])
dialog_struct += fxconv.string(i["choice"].replace('$', '\0'))
dialog_struct += fxconv.string(i["conclusion1"])
dialog_struct += fxconv.u32(i["next1"])
dialog_struct += fxconv.string(i["conclusion2"])
dialog_struct += fxconv.u32(i["next2"])
dialog_struct += fxconv.u32(i["nextOther"])
# Save this struct
name = os.path.splitext(os.path.basename(input))[0]
fxconv.elf(dialog_struct, output, f"__{name}", **target)
except Exception as e:
sys.stderr.write(f"ERROR: Failed convert dialogs.\n"
+ f" Error message: {e}\n")
sys.exit(1)