Advertisement
Kitomas

2024-07-19 (13/13)

Jul 19th, 2024
189
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 24.88 KB | None | 0 0
  1. /******************************************************************************/
  2. /******************************************************************************/
  3. //"kw32g\asset_scripts\_copy_all_scenes.py":
  4. _PRINT = False
  5. project_folder = '\\kw32g'
  6.  
  7. from time import time
  8. from os import chdir, system as cmd
  9. from os.path import exists
  10.  
  11.  
  12.  
  13. def tryCmd(s, ps=True):
  14.     if _PRINT:
  15.         print(s)
  16.         if ps: cmd('powershell -command '+s)
  17.         else : cmd(s)
  18.     else:
  19.         if ps: cmd(f'powershell -command {s} >nul')
  20.         else : cmd(s+' >nul')
  21.    
  22.    
  23.  
  24. def main(cwd = None):
  25.     #if not None, cwd should be _scene/tools
  26.     if cwd != None: chdir(cwd)
  27.    
  28.     timeStart = time()
  29.    
  30.    
  31.     #happens whether or not _PRINT is True
  32.     print("copying all scenes...")
  33.    
  34.    
  35.     #and the game's root folder should be on
  36.     #the same level as the assets folder
  37.     game_bin = f'..\\..\\..\\{project_folder}\\bin'
  38.     deb_scn  = game_bin+'\\_Debug\\dat\\scene'
  39.     rel_scn  = game_bin+'\\_Release\\dat\\scene'
  40.    
  41.    
  42.     #delete save to account for any potential
  43.     #new differences in binary structure
  44.     #(otherwise it'll fail to read properly!)
  45.     tryCmd(f'rm {game_bin}\\_Debug\\save\\*.*')
  46.     tryCmd(f'rm {game_bin}\\_Release\\save\\*.*')  
  47.    
  48.     deb_sys = f'{game_bin}\\_Debug\\dat\\system_states.bin'
  49.     rel_sys = f'{game_bin}\\_Release\\dat\\system_states.bin'
  50.     if exists(deb_sys): tryCmd(f'rm {deb_sys}')
  51.     if exists(rel_sys): tryCmd(f'rm {rel_sys}')
  52.    
  53.    
  54.     #removes all scenes from game files
  55.     tryCmd(f'rm {deb_scn}\\*.*')
  56.     tryCmd(f'rm {rel_scn}\\*.*')
  57.    
  58.    
  59.     #copies new scenes to game files
  60.     tryCmd(f'XCOPY /E /Y ..\\_ksd {deb_scn}')
  61.     tryCmd(f'XCOPY /E /Y ..\\_ksd {rel_scn}')
  62.    
  63.     if _PRINT:
  64.         print(f'SCENE COPIED IN {time()-timeStart} SECONDS')
  65.  
  66.  
  67.    
  68. if __name__ == "__main__":
  69.     main()
  70.     tryCmd('pause')/******************************************************************************/
  71. /******************************************************************************/
  72. //"kw32g\asset_scripts\_generate_arrlen.py":
  73. project_folder = '\\kw32g'
  74.  
  75. from os import chdir, system as cmd, listdir
  76. from os.path import isfile, join
  77.  
  78.  
  79.  
  80. arrlen_fmt = '\
  81. #ifndef _ARRLEN_HPP\n\
  82. #define _ARRLEN_HPP\n\
  83. \n\
  84. #define gl_scenes_len      {}\n\
  85. #define gl_backgrounds_len {}\n\
  86. #define gl_tilesets_len    {}\n\
  87. #define gl_objImg_len      {}\n\
  88. #define gl_ambience_len    {}\n\
  89. \n\
  90. #endif /* _ARRLEN_HPP */\n'
  91.  
  92. def saveArrlen(path,  scenes, backgrounds, tilesets, objImgs, ambiences):
  93.     arrlen = arrlen_fmt.format(scenes, backgrounds, tilesets, objImgs, ambiences)
  94.     #print(arrlen)
  95.     file = open(path, 'w')
  96.     file.write(arrlen)
  97.     file.close()
  98.    
  99.    
  100.    
  101. def filesInDirectory(_dir):
  102.     return len([name for name in listdir(_dir) if isfile(join(_dir, name))])
  103.  
  104.  
  105.  
  106. #cwd should be root of assets folder
  107. def main(cwd = None):
  108.     if cwd != None: chdir(cwd)
  109.    
  110.    
  111.     scenes_len      = filesInDirectory('.\\_scene\\_tmx\\')-2 #-2 to remove scene 0 and py script
  112.     backgrounds_len = filesInDirectory('.\\_background\\_qoi\\')
  113.     tilesets_len    = filesInDirectory('.\\_tileset\\_qoi\\')-1 #-1 for tileset_missing
  114.     objImg_len      = filesInDirectory('.\\_objimg\\_qoi\\')
  115.     ambience_len    = filesInDirectory('.\\_ambience\\_qoa\\')
  116.    
  117.    
  118.     print("generating arrlen...")
  119.     saveArrlen(f'..{project_folder}\\include\\arrlen.hpp',
  120.                scenes_len,
  121.                backgrounds_len,
  122.                tilesets_len,
  123.                objImg_len,
  124.                ambience_len)
  125.    
  126.    
  127.     if __name__ == '__main__':
  128.         cmd('powershell -command pause')
  129.         exit(0)
  130.        
  131.  
  132.  
  133. if __name__ == '__main__': main()/******************************************************************************/
  134. /******************************************************************************/
  135. //"kw32g\asset_scripts\_generate_arrlen_clone.py":
  136. project_folder = '\\kw32g'
  137.  
  138. from os import chdir, system as cmd, listdir
  139. from os.path import isfile, join
  140.  
  141.  
  142.  
  143. arrlen_fmt = '\
  144. #ifndef _ARRLEN_HPP\n\
  145. #define _ARRLEN_HPP\n\
  146. \n\
  147. #define gl_scenes_len      {}\n\
  148. #define gl_backgrounds_len {}\n\
  149. #define gl_tilesets_len    {}\n\
  150. #define gl_objImg_len      {}\n\
  151. #define gl_ambience_len    {}\n\
  152. \n\
  153. #endif /* _ARRLEN_HPP */\n'
  154.  
  155. def saveArrlen(path,  scenes, backgrounds, tilesets, objImgs, ambiences):
  156.     arrlen = arrlen_fmt.format(scenes, backgrounds, tilesets, objImgs, ambiences)
  157.     #print(arrlen)
  158.     file = open(path, 'w')
  159.     file.write(arrlen)
  160.     file.close()
  161.    
  162.    
  163.    
  164. def filesInDirectory(_dir):
  165.     return len([name for name in listdir(_dir) if isfile(join(_dir, name))])
  166.  
  167.  
  168.  
  169. #cwd should be assets/_scene/_tools
  170. def main(cwd = None):
  171.     if cwd != None: chdir(cwd)
  172.    
  173.    
  174.     scenes_len      = filesInDirectory('..\\..\\_scene\\_tmx\\')-2 #-2 to remove scene 0 and py script
  175.     backgrounds_len = filesInDirectory('..\\..\\_background\\_qoi\\')
  176.     tilesets_len    = filesInDirectory('..\\..\\_tileset\\_qoi\\')-1 #-1 for tileset_missing
  177.     objImg_len      = filesInDirectory('..\\..\\_objimg\\_qoi\\')
  178.     ambience_len    = filesInDirectory('..\\..\\_ambience\\_qoa\\')
  179.    
  180.    
  181.     print("generating arrlen...")
  182.     saveArrlen(f'..\\..\\..{project_folder}\\include\\arrlen.hpp',
  183.                scenes_len,
  184.                backgrounds_len,
  185.                tilesets_len,
  186.                objImg_len,
  187.                ambience_len)
  188.    
  189.    
  190.     if __name__ == '__main__':
  191.         cmd('powershell -command pause')
  192.         exit(0)
  193.        
  194.  
  195.  
  196. if __name__ == '__main__': main()/******************************************************************************/
  197. /******************************************************************************/
  198. //"kw32g\asset_scripts\_parse_scene.py":
  199. if __name__ == "__main__":
  200.     print("_parse_scene.py should be used as a module only!")
  201.     exit(0)
  202.    
  203. _DEBUG = False
  204. _PRINT_LAYERS = False #_DEBUG must be True as well
  205.  
  206. from os.path import isfile, exists
  207. from pprint import pprint
  208. import xml.etree.ElementTree as ET
  209.  
  210.  
  211.  
  212.  
  213. obj_data_types = {
  214.      "u8",  "s8",
  215.     "u16", "s16",
  216.     "u24", "s24",
  217.     "u32", "s32",
  218.     "u64", "s64",
  219.     "f32", "f64",
  220.     "rgb", "argb"
  221. }
  222.  
  223.  
  224.  
  225.  
  226. def printError(s, fatal=True):
  227.     if _DEBUG or fatal: print("Error: {}".format(s))
  228.     if fatal: exit(-1)
  229.     else    : return False
  230.  
  231. def sceneIdToPath(scene_id):
  232.     return "../_tmx/scene_{}.tmx".format(scene_id)
  233.    
  234. #poor man's regex lol
  235. def extractTilesetName(path):
  236.     #will turn something like "../foo/tileset_collision.tsx" into "collision"
  237.     name = path.split("/")[-1]
  238.     return (("_").join(name.split("."))).split("_")[1]
  239.    
  240. def tryInt(string):
  241.     try:
  242.         return int(string)
  243.     except ValueError:
  244.         return None
  245.     except TypeError:
  246.         return None
  247.        
  248. def tryDict(dict, key):
  249.     try:
  250.         return dict[key]
  251.     except KeyError:
  252.         return None
  253.  
  254. def keyWarn(properties, name, fatal=False, cat="property"):
  255.     if tryDict(properties, name) == None:
  256.         #intentionally indented twice
  257.         if    fatal: print("  Error: {} \"{}\" doesn't exist!".format(cat, name)); exit(-1)
  258.         elif _DEBUG: print("  Warning: {} \"{}\" doesn't exist!".format(cat, name))
  259.         return True #key does not exist
  260.     else:
  261.         return False #key exists
  262.  
  263. #value is a hex quad string of "#AARRGGBB"
  264. def toRGBA(value):
  265.     alpha = int(value[1:3], 16)
  266.     red   = int(value[3:5], 16)
  267.     blue  = int(value[5:7], 16)
  268.     green = int(value[7:9], 16)
  269.     return (red, green, blue, alpha)
  270.  
  271. #dataValue should always be of type str
  272. def convertType(dataType, dataValue):
  273.     #(i wish python had switch statements...)
  274.     if   dataType == "int"    : return int(float(dataValue))
  275.     elif dataType == "bool"   : return eval(dataValue.capitalize())
  276.     elif dataType == "float"  : return float(dataValue)
  277.     elif dataType == "color"  : return toRGBA(dataValue)
  278.     elif dataType == "object" : return int(float(dataValue))
  279.     #elif dataType == 'file'  : return dataValue #(redundant)
  280.     else                      : return dataValue
  281.  
  282. def tryTypeWarn(type_str):
  283.     type_num = tryInt(type_str.split("_")[0])
  284.     if type_num != None:
  285.         return type_num
  286.     else:
  287.         if _DEBUG: print("Warning: type \"{}\" is invalid".format(type_str))
  288.         return 0
  289.        
  290. #(mostly) yoinked from stackoverflow lol
  291. def checkForDuplicateIndexes(thelist):
  292.   seen = set()
  293.   for x in thelist:
  294.     if x[0][0] in seen:
  295.         printError("found duplicate obj data index \"{}\"".format(x[0][0]))
  296.     seen.add(x[0][0])
  297.  
  298.  
  299.  
  300.  
  301.  
  302. templates = {} #dictionary of [path]=(a,b,has_gid)
  303. def handleTemplate(path):
  304.     if not exists(path): printError("template \"{}\" does not exist".format(path))
  305.     if not isfile(path): printError("template \"{}\" is not a file".format(path))
  306.     if path in templates:
  307.         return templates[path][0], templates[path][1], templates[path][2]
  308.     tmplp = 'template property'
  309.    
  310.     #(a should include width, height type, and name,
  311.     # and b should include its custom properties
  312.     props_a, props_b  =  {}, []
  313.     has_gid = False
  314.    
  315.     tree = ET.parse(path)
  316.     tmpl = tree.getroot() #t[e]mpl[ate]
  317.     objs = tmpl.findall('object')
  318.     if len(objs) != 1: printError(f'template "{path}" must have exactly 1 object')
  319.     obj = objs[0]
  320.    
  321.    
  322.     props_a  = obj.attrib
  323.     has_gid  = not keyWarn(props_a, 'gid', cat=tmplp)
  324.    
  325.    
  326.     _props_b = obj.find('properties')
  327.     if _props_b != None:
  328.         for prop in _props_b.findall("property"):
  329.             pName  = prop.attrib["name" ].split("_")
  330.             pType  = prop.attrib["type" ]
  331.             pValue = prop.attrib["value"]
  332.             if len(pName) < 2: printError("\"{}\" isn't a valid obj data property name".format(pName[0]))
  333.             pIndex = tryInt(pName[0])
  334.             if pIndex == None: printError("\"{}\" has an invalid obj data property index".format(pName[0]))
  335.             pName[0] = pIndex
  336.             props_b.append(  (pName, convertType(pType, pValue))  )
  337.    
  338.    
  339.     templates[path] = (props_a, props_b, has_gid)
  340.     return props_a, props_b, has_gid
  341.  
  342.  
  343.  
  344.  
  345.  
  346. def parseObject(obj_in):
  347.     obj_out = { "data":[] }
  348.     props_a = obj_in.attrib
  349.     op = "object property"
  350.     nml = "NAMELESS"
  351.    
  352.     if _DEBUG: print(" parsing object \"{}\"...".format(tryDict(props_a, "name") or nml))
  353.     if keyWarn(props_a, "name"  , cat=op): obj_out["name"  ] = nml
  354.     else                                 : obj_out["name"  ] = props_a["name"]
  355.     if keyWarn(props_a, "x"     , cat=op): obj_out["x"     ] = 0
  356.     else                                 : obj_out["x"     ] = convertType("int", props_a["x"])
  357.     if keyWarn(props_a, "y"     , cat=op): obj_out["y"     ] = 0
  358.     else                                 : obj_out["y"     ] = convertType("int", props_a["y"])
  359.     if keyWarn(props_a, "width" , cat=op): obj_out["width" ] = 0
  360.     else                                 : obj_out["width" ] = convertType("int", props_a["width"])
  361.     if keyWarn(props_a, "height", cat=op): obj_out["height"] = 0
  362.     else                                 : obj_out["height"] = convertType("int", props_a["height"])
  363.     if keyWarn(props_a, "type"  , cat=op): obj_out["type"  ] = 0
  364.     else                                 : obj_out["type"  ] = tryTypeWarn(props_a["type"])
  365.    
  366.    
  367.     #tile objects for some reason use a bottom-up position,
  368.     #so it needs to be adjusted to make it top-down
  369.     obj_was_shifted = False
  370.     if not keyWarn(props_a, "gid", cat=op):
  371.         obj_out["y"] -= obj_out["height"]
  372.         if obj_out["height"] != 0: obj_was_shifted = True
  373.    
  374.    
  375.     #if object uses a template, you need to read it in order to
  376.     #get the object's width, height type, and name (unless they've been manually set)
  377.     template_props_b = []
  378.     if not keyWarn(props_a, "template", cat=op):
  379.         template_props_a, template_props_b, has_gid = handleTemplate(props_a["template"])
  380.        
  381.         #only set obj_out's stuff if it hasn't been changed from the template
  382.         if obj_out["name"  ] == nml: obj_out["name"  ] = template_props_a["name"]
  383.         if obj_out["width" ] == 0  : obj_out["width" ] = convertType("int", template_props_a["width" ])
  384.         if obj_out["height"] == 0  : obj_out["height"] = convertType("int", template_props_a["height"])
  385.         if obj_out["type"  ] == 0  : obj_out["type"  ] = tryTypeWarn(template_props_a["type"])
  386.        
  387.         #only shift the y position if it hasn't been shifted already
  388.         #(and the object template is a tile type)
  389.         if has_gid and not obj_was_shifted:
  390.             obj_out["y"] -= obj_out["height"]
  391.    
  392.    
  393.     #this should occur whether or not _DEBUG is True
  394.     if obj_out["type"] == 0: print("  Warning: object \"{}\"'s type is equal to 0".format(obj_out["name"]))
  395.     if obj_out["type"]  < 0: return None #if < 0, treat object as 'inactive/only-in-editor'
  396.    
  397.     #hitbox offset is not an intrinsic object property,
  398.      #so they need to be specified with a custom property
  399.     obj_out["hb_offset_x"] = 0
  400.     obj_out["hb_offset_y"] = 0
  401.  
  402.  
  403.     _props_b = obj_in.find("properties")
  404.     props_b  = []
  405.    
  406.     if _props_b != None:
  407.         for prop in _props_b.findall("property"):
  408.             pName  = prop.attrib["name" ].split("_")
  409.             pType  = prop.attrib["type" ]
  410.             pValue = prop.attrib["value"]
  411.             if len(pName) < 2: printError("\"{}\" isn't a valid obj data property name".format(pName[0]))
  412.             pIndex = tryInt(pName[0])
  413.             if pIndex == None: printError("\"{}\" has an invalid obj data property index".format(pName[0]))
  414.             pName[0] = pIndex
  415.             props_b.append(  (pName, convertType(pType, pValue))  )
  416.    
  417.         checkForDuplicateIndexes(props_b) #properties can't share the same index
  418.         props_b = sorted(props_b,  key = lambda x: x[0][0]  ) #sort by their indexes
  419.        
  420.  
  421.     #add template_props_b to props_b, only if a given property index is not found in props_p
  422.     if len(template_props_b) > 0:
  423.         to_add = []
  424.         if len(props_b) > 0:
  425.             for tpb in template_props_b:
  426.                 found_index = False
  427.                 for pb in props_b:
  428.                     if tpb[0] == pb[0]:
  429.                         found_index = True
  430.                        
  431.                 if not found_index:
  432.                     to_add.append(tpb)
  433.         else:
  434.             #if props_b is completely empty, use all of the template's default properties
  435.             to_add = template_props_b
  436.            
  437.         props_b += to_add
  438.         props_b  = sorted(props_b,  key = lambda x: x[0][0]  ) #sort by their indexes (again)
  439.  
  440.    
  441.     data = []
  442.     for prop in props_b:
  443.         pType  = prop[0][1].lower()
  444.         pName  = ("_").join(prop[0][2:])
  445.         pValue = prop[1]
  446.         if pType not in obj_data_types:
  447.             printError("\"{}\" is not a valid obj data type".format(pType))
  448.         if pName[0:10] == "hb_offset_":
  449.             if pType != "u8":
  450.                 printError("hb_offset_<x/y> is not of type u8")
  451.             if pName != "hb_offset_x"  and  pName != "hb_offset_y":
  452.                 printError("malformed offset name \"{}\"".format(pName))
  453.             obj_out[pName] = pValue
  454.         else:
  455.             data.append(  (pType, pValue, pName)  )
  456.    
  457.    
  458.     obj_out["data"] = data
  459.  
  460.     return obj_out
  461.  
  462.  
  463.  
  464.  
  465. def printLayer(layers, name):
  466.     if tryDict(layers, name) == None: return
  467.     count = 0
  468.     print("  {}: [".format(name), end="")
  469.     for tile in layers[name]:
  470.         if count%32 == 0: print("\n    ", end="")
  471.         print(str(tile).rjust(3), end=",")
  472.         count += 1
  473.     print("\n  ],")
  474.    
  475. def printScene(scene, printLayers=_PRINT_LAYERS):
  476.     print("  --PROPERTIES--:")
  477.     pprint(scene["properties"], indent=4)
  478.    
  479.     if printLayers:
  480.         print("  --LAYERS--")
  481.         printLayer(scene["layers"], "collision")
  482.         printLayer(scene["layers"], "fg"       )
  483.         printLayer(scene["layers"], "mg"       )
  484.        
  485.     print("  --OBJECTS--")
  486.     pprint(scene["objs"])
  487.  
  488. def parseSceneMap(scene_id, announce=True):
  489.     filename = sceneIdToPath(scene_id)
  490.     if not exists(filename): printError("scene \"{}\" does not exist".format(filename))
  491.     if not isfile(filename): printError("scene \"{}\" is not a file".format(filename))
  492.     if _DEBUG or announce: print("PARSING \"{}\":".format(filename))
  493.     tree = ET.parse(filename)
  494.     map  = tree.getroot()
  495.    
  496.     #get map's intrinsic properties
  497.     width       = int(map.attrib["width"     ]) #should be 32
  498.     height      = int(map.attrib["height"    ]) #should be 18
  499.     tileWidth  = int(map.attrib["tilewidth" ]) #should be 24 (unused)
  500.     tileHeight = int(map.attrib["tileheight"]) #should be 24 (unused)
  501.     mapLength   = width*height
  502.    
  503.     if width      != 32: printError("map width is not 32")
  504.     if height     != 18: printError("map height is not 18")
  505.     if tileWidth  != 24: printError("tile width is not 24")
  506.     if tileHeight != 24: printError("tile height is not 24")
  507.    
  508.    
  509.    
  510.     #get map's custom properties
  511.     props = {}
  512.     for prop in map.find("properties"):
  513.         pName  = tryDict(prop.attrib, "name" ) or "NAMELESS"
  514.         pType  = tryDict(prop.attrib, "type" )
  515.         pValue = tryDict(prop.attrib, "value") or "NOVALUE"
  516.         props[ pName ] = convertType(pType, pValue)
  517.  
  518.     if  keyWarn(props, "bmp_bg"    ):  props["bmp_bg"    ] = 0
  519.     if  keyWarn(props, "repeat_bg" ):  props["repeat_bg" ] = False
  520.     #if keyWarn(props, "objs_len"  ):  props["objs_len"  ] = 0 #(calculated later)
  521.     #if keyWarn(props, "tileset_a" ):  props["tileset_a" ] = 0 #(calculated later)
  522.     #if keyWarn(props, "tileset_b" ):  props["tileset_b" ] = 0 #(calculated later)
  523.     if  keyWarn(props, "edge_n"    ):  props["edge_n"    ] = 0
  524.     if  keyWarn(props, "edge_s"    ):  props["edge_s"    ] = 0
  525.     if  keyWarn(props, "edge_w"    ):  props["edge_w"    ] = 0
  526.     if  keyWarn(props, "edge_e"    ):  props["edge_e"    ] = 0
  527.     #if keyWarn(props, "scene"     ):  props["scene"     ] = 0 #(calculated later)
  528.     if  keyWarn(props, "music"     ):  props["music"     ] = 0
  529.     if  keyWarn(props, "ambience_a"):  props["ambience_a"] = 0
  530.     if  keyWarn(props, "ambience_b"):  props["ambience_b"] = 0
  531.    
  532.    
  533.    
  534.     #calculate tileset boundaries
  535.     _tsets = []
  536.     for tset in map.findall("tileset"):
  537.         tFirstGID = int(tryDict(tset.attrib, "firstgid")) - 1
  538.         tName     = extractTilesetName(tryDict(tset.attrib, "source"))
  539.         if tName != "obj-img":
  540.             _tsets.append(  ( tFirstGID, tryInt(tName) or 0 )  )
  541.     tsets = sorted(_tsets,  key = lambda x: x[0]  ) #sorted by first element
  542.    
  543.     if   len(tsets) < 3: printError("map cannot have less than 3 tilesets (including collision)")
  544.     elif len(tsets) > 3: printError("map cannot have more than 3 tilesets (including collision)")
  545.        
  546.      #there should only be 1 tileset that registers as null (the collision's tileset)
  547.     tset_offsets = [0,] * 3
  548.     tset_nulls, tset_valids  =  0, 0
  549.     tset_a,     tset_b       =  0, 0
  550.     for i in range(len(tsets)):
  551.         #collision tileset
  552.         if    tsets[i][1] == 0:  tset_offsets[i] = -1;  tset_nulls  += 1
  553.         #tileset_a
  554.         elif  tset_valids == 0:  tset_a = tsets[i][1];  tset_valids += 1
  555.         #tileset_b
  556.         else                  :  tset_b = tsets[i][1];  tset_offsets[i] = 128
  557.    
  558.     if tset_nulls != 1: printError("map must have exactly 1 null tileset, reserved for collider map")
  559.  
  560.    
  561.    
  562.     #get map's layer data
  563.     layers = {}
  564.     for layer in map.findall("layer"):
  565.         lName   =      tryDict(layer.attrib,"name"  ) or "NAMELESS"
  566.         lWidth  = int( tryDict(layer.attrib,"width" ) or 0 )
  567.         lHeight = int( tryDict(layer.attrib,"height") or 0 )
  568.         lLength = lWidth*lHeight
  569.         # n-1 to make sure both tile 0 and 1 are treated as transparent
  570.         lData   = [ max(int(n)-1, 0) for n in layer.find("data").text.split(",") ] #csv to list
  571.         if lLength != mapLength : valid = printError("layer dims inconsistent with map dimensions")
  572.         if lLength != len(lData): valid = printError("layer dims inconsistent with its attributes")
  573.         layers[ lName ] = lData
  574.    
  575.     valid = True
  576.     if "collision" not in layers: valid = printError("layer \"collision\" doesn't exist", quit=False)
  577.     if "fg"        not in layers: valid = printError("layer \"fg\" doesn't exist",        quit=False)
  578.     if "mg"        not in layers: valid = printError("layer \"mg\" doesn't exist",        quit=False)
  579.     if not valid: exit(-1)
  580.    
  581.     for i in range(len(layers["collision"])):
  582.         #collider info is 6 bits (2^6 = 64),
  583.          #which is a factor of a normal tileset's 7 (2^7 = 128) anyway
  584.         layers["collision"][i] %= 64
  585.         #(this does technically allow non-collision tiles to be
  586.         # included in the collision layer, so don't do that unless you want to, idk)
  587.        
  588.     for i in range(len(layers["fg"])): #(fg & mg should be the same length)
  589.         #fg
  590.         tile = layers["fg"][i]
  591.         offset = tset_offsets[tile//128] #tile//128 should be between 0 -> 2
  592.         if offset == -1  and  (tile%128) != 0:
  593.             printError("fg cannot contain collision map tiles")
  594.         layers["fg"][i] = (tile%128) + max(offset, 0)
  595.         if layers["fg"][i] == 128: layers["fg"][i] = 0 #128 is also transparent
  596.        
  597.         #mg
  598.         tile = layers["mg"][i]
  599.         offset = tset_offsets[tile//128]
  600.         if offset == -1  and  (tile%128) != 0:
  601.             printError("mg cannot contain collision map tiles")
  602.         layers["mg"][i] = (tile%128) + max(offset, 0)
  603.         if layers["mg"][i] == 128: layers["mg"][i] = 0 #128 is also transparent
  604.        
  605.     #check if a given layer's data should be omitted from scene descriptor file
  606.     fg_nonzero = False
  607.     mg_nonzero = False
  608.     for i in range(len(layers["fg"])): #(fg & mg should be the same length)
  609.         if (layers["fg"][i]%128) != 0: fg_nonzero = True
  610.         if (layers["mg"][i]%128) != 0: mg_nonzero = True
  611.      
  612.     if not fg_nonzero  and  not mg_nonzero:
  613.         #fixed a bug that necessitated parsing to halt like this in the first place
  614.         #printError("fg and mg cannot both be empty")
  615.         #instead, i could just warn, since if both are empty, there would
  616.         #be no collision data as they are stored on those layers
  617.         print(f"  Warning: scene {scene_id} is completely empty (no collision data written!)")
  618.         #(the 2 leading spaces are intentional)
  619.    
  620.    
  621.     props["objs_len"  ] = 0
  622.     props["tileset_a" ] = tset_a
  623.     props["tileset_b" ] = tset_b
  624.     props["scene"     ] = scene_id
  625.     props["fg_nonzero"] = fg_nonzero
  626.     props["mg_nonzero"] = mg_nonzero
  627.    
  628.     #get scene objects
  629.     obj_groups = map.findall("objectgroup")
  630.     objs = []
  631.     if len(obj_groups) >  1: printError("scene cannot have more than 1 object group")
  632.     if len(obj_groups) == 1:
  633.         #(this loop will not run at all if there are no objects)
  634.         for obj in obj_groups[0].findall("object"):
  635.             #current_object will be None if its type is LESS than 0,
  636.             #in which case it simply won't be counted.
  637.             #this is useful for putting markers as to the destination
  638.             #of things like teleporters, for instance
  639.             current_object = parseObject(obj)
  640.             if current_object != None:
  641.                 objs.append(current_object)
  642.                 props["objs_len" ] += 1
  643.  
  644.  
  645.  
  646.     #automatically mark edge as a loop if that edge is null
  647.     if props["edge_n"] == 0: props["edge_n"] = scene_id
  648.     if props["edge_s"] == 0: props["edge_s"] = scene_id
  649.     if props["edge_w"] == 0: props["edge_w"] = scene_id
  650.     if props["edge_e"] == 0: props["edge_e"] = scene_id
  651.    
  652.     #error if all 4 edges make the scene loop
  653.     #edges = (scene_id, props["edge_n"], props["edge_s"],
  654.     #                   props["edge_w"], props["edge_e"])
  655.     #if all(i==edges[0] for i in edges):
  656.     #    printError("all 4 edges make scene loop")
  657.     #(i want to have the option to do this, so i'm
  658.     # removing this check for now!)
  659.  
  660.  
  661.  
  662.     #calculate background id from an optional image layer
  663.     #(this takes precedence over props["bmp_bg"], and will overwrite it)
  664.     image_layers = map.findall("imagelayer")
  665.     if len(image_layers) > 1: printError("scene cannot have >1 image layer")
  666.     elif len(image_layers) != 0:
  667.         images = image_layers[0].findall("image")
  668.         if len(images) > 0:
  669.             bg_name = images[0].attrib['source']
  670.            
  671.             #this can also be used for the background filenames too lol
  672.             bg_id = tryInt(extractTilesetName(bg_name))
  673.             if bg_id == None: printError(f'bg name "{bg_name}" is invalid')
  674.            
  675.             props["bmp_bg"] = bg_id
  676.    
  677.  
  678.  
  679.     scene = {}
  680.     scene["properties"] = props
  681.     scene["layers"    ] = layers
  682.     scene["objs"      ] = objs
  683.    
  684.     if _DEBUG: printScene(scene)
  685.    
  686.     return scene
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement