from random import randint from PIL import Image import fxconv import json import pathlib import csv import os DEBUG = 0 def convert(input, output, params, target): if params["custom-type"] == "map": print("ERROR : Asset ", params["name"], " has legacy type map") return 1 elif params["custom-type"] == "world": convert_world(input, output, params, target) return 0 elif params["custom-type"] == "custom-image": convert_custom_image(input, output, params, target) return 0 elif params["custom-type"] == "font": convert_font(input, output, params, target) return 0 elif params["custom-type"] == "dialogs": print("ERROR : Asset ", params["name"], " has legacy type dialog") #convert_dialogs(input, output, params, target) return 0 else: return 1 def convert_world(input, output, params, target): print( "WE ARE COMPUTING THE WORLD", input ) data = json.load(open(input, "r")) nbMaps = ["fileName" in i for i in data["maps"]].count(True) if DEBUG: print( "We have to treat ", nbMaps, " maps") if DEBUG: print( "So let's go ... ") structWorld = fxconv.Structure() structWorld += fxconv.string("DEBUG") #structExtra = fxconv.Structure() for i in range(nbMaps): nameMap = data["maps"][i]["fileName"].replace(".tmx","") nameMapFree = nameMap.split("/")[-1] #count the number of "back" (cd ..) to locate the map on the computer nbRetour = nameMap.count("..")+1 #create the map absolute path nameTMX = "/".join(input.split("/")[:-nbRetour]) + "/" + nameMap + ".tmx" nameJSON = "/".join(input.split("/")[:-nbRetour]) + "/" + nameMap + ".json" commandline = 'tiled --export-map json ' + nameTMX + ' ' + nameJSON if DEBUG: print( "TILED COMMAND LINE FOR MAPS : ", commandline ) os.system( commandline ) mapPath = "/".join(input.split("/")[:-nbRetour]) + "/" + nameMap + ".json" if DEBUG: print("Map ", i , " name : ", mapPath ) xmin = data["maps"][i]["x"] if DEBUG: print( "xmin = ", xmin ) ymin = data["maps"][i]["y"] if DEBUG: print( "ymin = ", ymin ) xmax = data["maps"][i]["x"] + data["maps"][i]["width"] if DEBUG: print( "xmax = ", xmax ) ymax = data["maps"][i]["y"] + data["maps"][i]["height"] if DEBUG: print( "ymax = ", ymax ) map = get_tile_map_data( mapPath, output, params, target, xmin, ymin, xmax, ymax) if DEBUG: print( "Map = ", map ) structWorld += fxconv.ptr( map ) structWorld += fxconv.u32(0) structWorld += fxconv.string("DEBUG") #generate ! fxconv.elf(structWorld, output, "_" + params["name"], **target) def get_tile_map_data(input, output, params, target, xmin, ymin, xmax, ymax): print( "WE ARE COMPUTING THE MAP : ", input ) data = json.load(open(input, "r")) #find the tileset in use. it's a relative path (like ../tileset.tsx) nameTileset = data["tilesets"][0]["source"].replace(".tsx","") if DEBUG: print(nameTileset) #the name of the tileset without the .something nameTilesetFree = nameTileset.split("/")[-1] #count the number of "back" (cd ..) to locate the tileset on the computer nbRetour = nameTileset.count("..")+1 #create the tileset absolute path tilesetTSX = "/".join(input.split("/")[:-nbRetour]) + "/" + nameTileset + ".tsx" tilesetJSON = "/".join(input.split("/")[:-nbRetour]) + "/" + nameTileset + ".json" commandline = 'tiled --export-tileset json ' + tilesetTSX + ' ' + tilesetJSON if DEBUG: print( "TILED COMMAND LINE FOR TILESET : ", commandline ) os.system( commandline ) tileset = open(tilesetJSON, "r") data_tileset = json.load(tileset) tileset_size = data_tileset.get("columns") tileset.close() #find the ID of the first tile in the walkable tileset () indexWalkable = data["tilesets"][1]["firstgid"] if DEBUG: print(indexWalkable) #Extract from the json the width, height w, h = data["width"], data["height"] #nbTileLayer is the number of "true" layers (without ObjectsLayer) nbTilelayer = ["data" in i for i in data["layers"]].count(True) - 1 if DEBUG: print( nbTilelayer) #index of the various layers (may change from one map to another) layer_walkable = 0 layer_foreground = 0 layer_background = 0 #create the structure of the map structMap = fxconv.Structure() structMap += fxconv.u32(w) + fxconv.u32(h) + fxconv.u32(nbTilelayer) structMap += fxconv.u32(tileset_size) structMap += fxconv.u32(xmin) + fxconv.u32(ymin) + fxconv.u32(xmax) + fxconv.u32(ymax) structMap += fxconv.ref(f"img_{nameTilesetFree}") #extraction of the data contained in the layer "Walkable" of the map for i in range(nbTilelayer+1): datavalid = data["layers"][i] if datavalid["name"]=="Walkable": layer_walkable = i if DEBUG: print( "Walkable Tile Data in layer : ", layer_walkable) break elif i==nbTilelayer: print( "ERROR : No Walkable layer data !!!" ) walk_data = bytes() layer = data["layers"][layer_walkable] for tile in layer["data"]: #print( tile ) if tile == 0: walk_data += fxconv.u8(tile) #if walkable_data = 0 then it is a blanck cell so nothing to change else : walk_data += fxconv.u8(tile-indexWalkable) #if !=0 than we need to shift the tile number by considering the first tileID (given by indexwalkable) structMap += fxconv.ptr(walk_data) nbextra = 0 extradata = fxconv.Structure() nbextra, extradata = get_extra_map_data(input, output, params, target, xmin, ymin, xmax, ymax) if (nbextra==0): structMap += fxconv.u32( 0 ) structMap += fxconv.u32( 0 ) else: structMap += fxconv.u32( int(nbextra) ) structMap += fxconv.ptr( extradata ) nameDialog = data["properties"][0]["value"] dialogfile = "/".join(input.split("/")[:-nbRetour]) + "/" + nameDialog if DEBUG: print( "THE DIALOGS ARE CONTAINED IN THE FILE : ", dialogfile ) nbdiag = 0 diagdata = fxconv.Structure() nbdiag, diagdata = convert_dialogs(dialogfile, output, params, target) if (nbdiag==0): structMap += fxconv.u32( 0 ) structMap += fxconv.u32( 0 ) else: structMap += fxconv.u32( int(nbdiag) ) structMap += fxconv.ptr( diagdata ) #extraction of the data contained in the layer "Background" and "Foreground" of the map #import the Background layer of the map for i in range(nbTilelayer+1): datavalid = data["layers"][i] if datavalid["name"]=="Background": layer_background = i if DEBUG: print( "Background Tile Data in layer : ", layer_background) break elif i==nbTilelayer: print( "ERROR : No Background layer data !!!" ) layer_data = bytes() layer = data["layers"][layer_background] for tile in layer["data"]: layer_data += fxconv.u16(tile-1) structMap += fxconv.ptr(layer_data) #import the foreground layer of the map for i in range(nbTilelayer+1): datavalid = data["layers"][i] if datavalid["name"]=="Foreground": layer_foreground = i if DEBUG: print( "Foreground Tile Data in layer : ", layer_foreground) break elif i==nbTilelayer: print( "ERROR : No Foreground layer data !!!" ) layer_data = bytes() layer = data["layers"][layer_foreground] for tile in layer["data"]: layer_data += fxconv.u16(tile-1) structMap += fxconv.ptr(layer_data) return structMap def get_extra_map_data(input, output, params, target, xmin, ymin, xmax, ymax): if DEBUG: print( "WE ARE COMPUTING THE EXTRA DATA OF THE MAP : ", input ) data = json.load(open(input, "r")) nblayer = ["id" in i for i in data["layers"]].count(True) - 1 if DEBUG: print( "I found ", nblayer, " of extradata") #index of the various layers (may change from one map to another) layer_extradata = 0 #import the foreground layer of the map for i in range(nblayer+1): datavalid = data["layers"][i] if datavalid["name"]=="ExtraData": layer_extradata = i if DEBUG: print( "Extra Data in layer : ", layer_extradata) break elif i==nblayer: print( "ERROR : No ExtraData layer data !!!" ) return 0, fxconv.u32(0) #create the structure of the map structData = fxconv.Structure() nbSign = 0 nbNPC = 0 nbPortal = 0 nbDiag = 0 npcs = fxconv.Structure() signs = fxconv.Structure() portals = fxconv.Structure() layer = data["layers"][layer_extradata] for i in layer["objects"]: #get the type of the item tpe = i["type"] #we check if the type corresponds to a items of type Point in Tiled if tpe in ("NPC"): currData = fxconv.Structure() x = i["x"] + xmin y = i["y"] + ymin nme = i["name"] dialogID = None needAction = None path = 0 path_length = 0 xdata = None ydata = None face_type = "MALE" #we now fill all the properties of this item for j in i["properties"]: #property "dialog" if j["name"]=="dialogID": dialogID = j[ "value" ] nbDiag += 1 #property "isQuestion" elif j["name"]=="needAction": needAction = j[ "value" ] else: #Extra properties for NPCs (path and face) if tpe=="NPC": if j["name"]=="face": face_type = j["value"] elif j["name"]=="hasPath": pathID = None path = j[ "value" ] if path==1: if DEBUG: print( "PNJ has path - NOW LOOKING FOR RELEVANT DATA" ) # we start looking for path data with first the ID of the path Object for u in i["properties"]: if u["name"]=="path": pathID = u[ "value" ] if DEBUG: print( "path ID is identified : ID= ", pathID ) for v in layer["objects"]: if v[ "id" ] == pathID: if DEBUG: print( "path data found : " ) xdata = bytes() ydata = bytes() for w in v[ "polyline" ]: path_length = path_length + 1 if DEBUG: print( "X= ", w[ "x" ], " Y= ", w[ "y" ] ) xdata += fxconv.u16( int( w[ "x" ] ) ) ydata += fxconv.u16( int( w[ "y" ] ) ) else: if DEBUG: print( "PNJ has no Path" ) else: print( "UNIDENTIFIED PROPERTY : ", j["name"]) if DEBUG: print( "OBJECT X= ", x, " Y= ", y, "STR= ", dialogID ) print( " Type= ", tpe, " Name= ", nme, "Face =", face_type) print( " Action?= ", needAction ) currData += fxconv.u32(0) currData += fxconv.u32(0) currData += fxconv.u32( int(x) ) currData += fxconv.u32( int(y) ) currData += fxconv.u16(0) #TODO : faceid currData += fxconv.u8(0) currData += fxconv.u8(1) currData += fxconv.u32( int(dialogID) ) currData += fxconv.u32( int(needAction) ) currData += fxconv.string( nme ) if path==0: currData += fxconv.u32(0) currData += fxconv.u32(0) currData += fxconv.u32(0) currData += fxconv.u32(0) currData += fxconv.u32(0) else: o_xdata = fxconv.Structure() o_xdata += xdata o_ydata = fxconv.Structure() o_ydata += ydata currData += fxconv.u32(path) currData += fxconv.u32(path_length) currData += fxconv.u32(0) currData += fxconv.ptr(o_xdata) currData += fxconv.ptr(o_ydata) #TODO currData += fxconv.i32(0) currData += fxconv.u8(0) currData += fxconv.u8(0) currData += fxconv.u16(0) nbNPC += 1 signs += currData elif tpe in ["SGN", "INFO"]: currData = fxconv.Structure() x = i["x"] + xmin y = i["y"] + ymin nme = i["name"] if tpe == "SIGN": icon = 0 else: icon = 1 dialogID = None needAction = None #we now fill all the properties of this item for j in i["properties"]: #property "dialog" if j["name"]=="dialogID": dialogID = j[ "value" ] nbDiag += 1 #property "isQuestion" elif j["name"]=="needAction": needAction = j[ "value" ] else: print( "UNIDENTIFIED PROPERTY : ", j["name"]) currData += fxconv.u32( int(x) ) currData += fxconv.u32( int(y) ) currData += fxconv.u32(icon) currData += fxconv.string( nme ) currData += fxconv.u32( int(dialogID) ) currData += fxconv.u32( int(needAction) ) nbSign += 1 signs += currData elif tpe == "PORTAL": nbPortal+=1 currData = fxconv.Structure() x = i["x"] + xmin y = i["y"] + ymin h = -1 w = -1 tp_interior = -1 tp_to = -1 for j in i["properties"]: if j["name"] == "h": h = j["value"] if j["name"] == "w": w = j["value"] if j["name"] == "tp_interior": tp_interior = j["value"] if j["name"] == "tp_to": tp_to = j["value"] if w==-1 or h==-1 or tp_interior==-1 or tp_to==-1: print("ERROR : Invalid portal " + i["ID"]) continue currData += fxconv.u32(int(x)) currData += fxconv.u32(int(y)) currData += fxconv.u32(int(w)) currData += fxconv.u32(int(h)) currData += fxconv.u16(int(tp_interior)) currData += fxconv.u16(int(tp_to)) portals += currData #else we do nothing else: print( 'Unknown object type "'+tpe+'" !' ) structData += fxconv.u32(nbNPC) if nbNPC: structData += fxconv.ptr(npcs) else: structData += fxconv.u32(0) structData += fxconv.u32(nbSign) if nbSign: structData += fxconv.ptr(signs) else: structData += fxconv.u32(0) structData += fxconv.u32(nbPortal) if nbPortal: structData += fxconv.ptr(portals) else: structData += fxconv.u32(0) return nbDiag, structData def convert_custom_image(input, output, params, target): scale = int(params.get("scale", 1)) # Upscale image before converting im = Image.open(input) im = im.resize((im.width * scale, im.height * scale), resample=Image.NEAREST) o = fxconv.convert_image_cg(im, params) fxconv.elf(o, output, "_" + params["name"], **target) def convert_font(input, output, params, target): o = fxconv.convert_topti(input, params) fxconv.elf(o, output, "_" + params["name"], **target) def convert_dialogs(input, output, params, target): if DEBUG: print( "WE ARE COMPUTING THE DIALOGS FROM : ", input ) data = json.load(open(input, "r")) structDialogs = fxconv.Structure() nbdialogs = 0 for d in data["dialogs"]: if DEBUG: print( int(d[ "ID" ])) # print( d[ "dialog" ] ) if DEBUG: print( int(d[ "isQuestion" ]) ) # print( d[ "choice" ].replace('$', chr(0)) ) # print( d[ "conclusion1" ] ) # print( int(d[ "next1" ] ) ) # print( d[ "conclusion2" ] ) # print( int(d[ "next2" ] ) ) # print( int(d[ "nextOther" ]) ) nbdialogs = nbdialogs + 1 structDialogs += fxconv.u32( int(d[ "ID" ] ) ) structDialogs += fxconv.string( d[ "dialog" ] ) structDialogs += fxconv.u32( int(d[ "isQuestion" ] ) ) structDialogs += fxconv.string( d[ "choice" ].replace('$', chr(0)) ) structDialogs += fxconv.string( d[ "conclusion1" ] ) structDialogs += fxconv.u32( int(d[ "next1" ] ) ) structDialogs += fxconv.string( d[ "conclusion2" ] ) structDialogs += fxconv.u32( int(d[ "next2" ] ) ) structDialogs += fxconv.u32( int(d[ "nextOther" ] ) ) return nbdialogs, structDialogs #fxconv.elf(structDialogs, output, "_" + params["name"], **target)