summaryrefslogblamecommitdiff
path: root/hercules/tmx_converter.py
blob: 96a37d9a144923776f96a6e44f48ed20f6480771 (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                         



                                                   


                                                                                
                                                      






















                                                                           
                


                               

                























                                                                                                                        

                                
                      
                          



                                





                            




































                           


                    
                
                         

                            
 

                                    











                             


                           
                  


                       
                           





                            
                  




                            

                          





                              
               



                          
                      











                            
                       
 






                            
                                    









                                                         
                                               









                                                                  
                                                             













                                                                            
                          





















                                                                         
                         











                                                                                

                                                                                 












                                                                                  
                                


                                                                                      













                                                                          



                                                                




                                                                     









                                         











                                        

                                                                          





                                         

                                                







                                                 





                                                 











                                                   
























                                                                                
 


































                                                                                                         
                   
                                                                 
                                 
                                    
                           




                                                                                                                                                              

                                                                      
                                       

                                                                      















                                                                                                                                                        
                                        

                                                                          


                                                                      

                                        
                                                                         




                                                                                                                                                    
                                                   








                                                                                                                                                                                                                                                                                       

                                                                 

                                                             

                                    
                             

                                                                     

                                                                                                                                                                            




                                                                 

                                                               


                                                                                               

                                    
                             

                                                                     

                                                                                       
                                                        
                                                                                                                                          


                                    

















                                                                                               
                                                                                                                                                                        










                                                                                                                                                             













                                                                                                                  

                                                                          














                                                                              
                                                                        















                                                                          
                                                          




                                                                                             
                                 















                                                                      



                                                               
                                           







                                                                                   

                                                                                     
                                                                                           

                                                                                                      
                                                                                                                  



                                                                             

                                                                                                     



                                                             




                                                                    




                                                                       

                  


                          
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

## TMW2 Script
## Modified by Jesusalva for Moubootaur Legends
###################################################

##    tmx_converter.py - Extract walkmap, warp, and spawn information from maps.
##
##    Copyright © 2012 Ben Longbons <b.r.longbons@gmail.com>
##    Copyright © 2016-2017 The Mana World Developers
##
##    This file is part of The Mana World
##
##    This program is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 2 of the License, or
##    (at your option) any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program.  If not, see <http://www.gnu.org/licenses/>.


from __future__ import print_function

import sys
import os
import posixpath
import xml.sax
import traceback

dump_all = False # wall of text
check_mobs = False # mob_db.txt
heigherror=True
fatalError=False

# lower case versions of everything except 'spawn' and 'warp'
other_object_types = set([
    'particle_effect',
    'npc', # not interpreted by client
    'script', # for ManaServ
    'fixme', # flag for things that didn't have a type before
    'music',
])

# Somebody has put ManaServ fields in our data!
other_spawn_fields = (
    'spawn_rate',
)
other_warp_fields = (
)

TILESIZE = 32
SEPARATOR = ''
MESSAGE = 'This file is generated automatically. All manually added changes will be removed when running the Converter.'
CLIENT_MAPS = 'maps'
SERVER_WLK = 'data'
SERVER_NPCS = 'npc'
MOB_DB_CONF = 'db/re/mob_db.conf'
MAP_CONF = 'conf/map/maps.conf'
MAP_DB_CONF = 'db/map_index.txt'
NPC_MOBS = '_mobs.txt'
NPC_CONFIG = '_config.txt'
NPC_WARPS = '_warps.txt'
NPC_IMPORTS = '_import.txt'
NPC_MASTER_IMPORTS = NPC_IMPORTS

def ifte(ifs, thens, elses):
    if ifs:
        return thens
    else:
        return elses

class State(object):
    pass
State.INITIAL = State()
State.LAYER = State()
State.DATA = State()
State.FINAL = State()

class Object(object):
    __slots__ = (
        'name',
        'x', 'y',
        'w', 'h',
    )
class Mob(Object):
    __slots__ = (
        'monster_id',
        'max_beings',
        'spawn',
        'death',
        'script',
    ) + other_spawn_fields
    def __init__(self):
        self.max_beings = 1
        self.spawn = 0
        self.death = 0
        self.script = ''

class Save(Object):
    __slots__ = (
        'inn',
    )

class Warp(Object):
    __slots__ = (
        'dest_map',
        'dest_x',
        'dest_y',
        'npc_id', 
        'trigger_x',
        'trigger_y',
        'notes',
    ) + other_warp_fields
    def __init__(self):
        self.npc_id = 'WARP'

# TMW2 CUSTOM OBJECTS
####################################
class Slide(Object):
    __slots__ = (
        'dest_x',
        'dest_y',
        'npc_id', 
        'trigger_x',
        'trigger_y',
        'notes',
    ) + other_warp_fields
    def __init__(self):
        self.npc_id = 'SLIDE'

class DynCollision(Object):
    __slots__ = (
        'colid',
        'enabled',
    )
    def __init__(self):
        self.colid = 1
        self.enabled = True

class DungeonSwitch(Object):
    __slots__ = (
        'enabled',
        'distance',
        'callfunc',
        'doevent',
        'args',
    )
    def __init__(self):
        self.enabled = False
        self.distance = 2
        self.callfunc = ''
        self.doevent = ''
        self.args = ''

class FunctionTrigger(Object):
    __slots__ = (
        'callfunc',
        'doevent',
        'args',
    )
    def __init__(self):
        self.callfunc = ''
        self.doevent = ''
        self.args = ''

class Trap(Object):
    __slots__ = (
        'disarmtime',
        'stuntime',
        'damage',
        'target',
    )
    def __init__(self):
        self.disarmtime = 15
        self.stuntime = 3
        self.damage = 80
        self.target = 3

class TreasureChest(Object):
    __slots__ = (
        'distance',
    )
    def __init__(self):
        self.distance = 2

####################################
class ContentHandler(xml.sax.ContentHandler):
    __slots__ = (
        'locator',  # keeps track of location in document
        'state',    # state of height info
        'tilesets', # first gid of each tileset
        'buffer',   # characters within a section
        'encoding', # encoding of layer data
        'compression', # compression of layer data
        'width',    # width of the height layer
        'height',   # height of the height layer
        'firstgid', # first gid of height layer
        'heightmap',# height map 
        'base',     # base name of current map
        'npc_dir',  # world/map/npc/<base>
        'mobs',     # open file to _mobs.txt
        'warps',    # open file to _warps.txt
        'imports',  # open file to _import.txt
        'name',     # name property of the current map
        'object',   # stores properties of the latest <object> tag
        'mob_ids',  # set of all mob types that spawn here
    )
    def __init__(self, npc_dir, mobs, confs, warps, imports):
        xml.sax.ContentHandler.__init__(self)
        self.locator = None
        self.state = State.INITIAL
        self.tilesets = set([0]) # consider the null tile as its own tileset
        self.buffer = bytearray()
        self.encoding = None
        self.compression = None
        self.width = None
        self.height = None
        self.firstgid = 0
        self.heightmap = ''
        self.base = posixpath.basename(npc_dir)
        self.npc_dir = npc_dir
        self.mobs = mobs
        self.confs = confs
        self.warps = warps
        self.imports = imports
        self.object = None
        self.mob_ids = set()
        self.mob_cnt = False
        self.save_cnt = False
        self.warp_cnt = False

    def setDocumentLocator(self, loc):
        self.locator = loc

    # this method randomly cuts in the middle of a line; thus funky logic
    def characters(self, s):
        if not s.strip():
            return
        if self.state is State.DATA:
            self.buffer += s.encode('ascii')

    def startDocument(self):
        pass

    def startElement(self, name, attr):
        global heigherror
        if dump_all:
            attrs = ' '.join('%s="%s"' % (k,v) for k,v in attr.items())
            if attrs:
                print('<%s %s>' % (name, attrs))
            else:
                print('<%s>' % name)

        if self.state is State.INITIAL:
            if name == u'property' and attr[u'name'].lower() == u'name':
                self.name = attr[u'value']
                self.mobs.write('// %s\n' % MESSAGE)
                self.mobs.write('// Map %s: %s mobs\n' % (self.base, self.name))
                self.confs.write('// %s\n' % MESSAGE)
                self.confs.write('// Map %s: %s conf\n' % (self.base, self.name))
                self.warps.write('// %s\n' % MESSAGE)
                self.warps.write('// Map %s: %s warps\n' % (self.base, self.name))

            if name == u'tileset':
                self.tilesets.add(int(attr[u'firstgid']))
                if 'name' in attr.__dict__['_attrs'].keys():
                  if attr[u'name'] == u'Height Numbers':
                    self.firstgid = int(attr[u'firstgid'])

            if name == u'layer' and attr[u'name'].lower().startswith(u'height'):
                self.width = int(attr[u'width'])
                self.height = int(attr[u'height'])
                self.state = State.LAYER
                heigherror=False
                # Map width must be enough to fill the largest widescreen on market
                if (self.width < 1920/TILESIZE):
                    print('Bad map width: %d (min. %d)' % (self.width, 1920/TILESIZE))
        elif self.state is State.LAYER:
            if name == u'data':
                if attr.get(u'encoding','') not in (u'', u'csv'):
                    print('Bad encoding:', attr.get(u'encoding',''))
                    return
                self.encoding = attr.get(u'encoding','')
                if attr.get(u'compression','') not in (u'', u'none'):
                    print('Bad compression:', attr.get(u'compression',''))
                    return
                self.compression = attr.get(u'compression','')
                self.state = State.DATA
        elif self.state is State.FINAL:
            if name == u'object':
                obj_type = attr[u'type'].lower()
                x = int(int(attr[u'x']) / TILESIZE);
                y = int(int(attr[u'y']) / TILESIZE);
                w = int(int(attr.get(u'width', 0)) / TILESIZE);
                h = int(int(attr.get(u'height', 0)) / TILESIZE);
                # I'm not sure exactly what the w/h shrinking is for,
                # I just copied it out of the old converter.
                # I know that the x += w/2 is to get centers, though.
                if obj_type == 'spawn':
                    self.object = Mob()
                    w =  int((w - 1) / 2)
                    h =  int((h - 1) / 2)
                    if w < 0:
                        w = 0
                    else:
                        x += w
                    if h < 0:
                        h = 0
                    else:
                        y += h
                elif obj_type == 'save':
                    self.object = Save()
                    x += w/2
                    y += h/2
                    w -= 2
                    h -= 2
                elif obj_type == 'warp':
                    self.object = Warp()
                    x += w/2
                    y += h/2
                    w -= 1
                    h -= 1
                ### TMW2 INSTANCES
                ##########################################################
                elif obj_type == 'slide':
                    self.object = Slide()
                    x += w/2
                    y += h/2
                    w -= 1
                    h -= 1
                elif obj_type == 'dyncollision':
                    self.object = DynCollision()
                    w -= 1
                    h -= 1
                elif obj_type == 'switch':
                    self.object = DungeonSwitch()
                    x += w/2
                    y += h/2
                    w -= 1
                    h -= 1
                elif obj_type == 'treasure':
                    self.object = TreasureChest()
                    x += w/2
                    y += h/2
                    w -= 1
                    h -= 1
                elif obj_type == 'function':
                    self.object = FunctionTrigger()
                    x += w/2
                    y += h/2
                    w -= 1
                    h -= 1
                elif obj_type == 'trap':
                    self.object = Trap()
                    x += w/2
                    y += h/2
                    w -= 1
                    h -= 1
                else:
                    if obj_type not in other_object_types:
                        print('Unknown object type:', obj_type, file=sys.stderr)
                    self.object = None
                    return
                obj = self.object
                obj.x = x
                obj.y = y
                obj.w = w
                obj.h = h
                obj.name = attr[u'name']
            elif name == u'property':
                obj = self.object
                if obj is None:
                    return
                key = attr[u'name'].lower()
                value = attr[u'value']
                # Not true due to defaulting
                #assert not hasattr(obj, key)
                try:
                    value = int(value)
                except ValueError:
                    pass
                setattr(obj, key, value)


    def add_warp_line(self, line):
        self.warps.write(line)

    def endElement(self, name):
        if dump_all:
            print('</%s>' % name)

        if name == u'object':
            obj = self.object
            if isinstance(obj, Mob):
                mob_id = obj.monster_id
                if mob_id < 1000:
                    mob_id += 1002
                if check_mobs:
                    try:
                        name = mob_names[mob_id]
                    except KeyError:
                        print('Warning: unknown mob ID: %d (%s)' % (mob_id, obj.name))
                    else:
                        if name != obj.name:
                            print('Warning: wrong mob name: %s (!= %s)' % (obj.name, name))
                            obj.name = name
                self.mob_ids.add(mob_id)
                if obj.script:
                    obj.script = ",%s" % (obj.script)
                self.mobs.write(
                    SEPARATOR.join([
                        '%s,%d,%d,%d,%d\t' % (self.base, obj.x, obj.y, obj.w, obj.h),
                        'monster\t',
                        obj.name,
                        '\t%d,%d,%d,%d%s\n' % (mob_id, obj.max_beings, obj.spawn, obj.death, obj.script),
                    ])
                )
                self.mob_cnt = True
            elif isinstance(obj, Save):
                """
                obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
                self.confs.write(
                    SEPARATOR.join([
                        '',
                        '%s,%d,%d,0\tscript\t#save_%s\tNPC_SAVE_POINT,{\n' % (self.base, obj.x, obj.y, obj_name),
                        '    savepointparticle .map$, .x, .y, %s;\n    close;\n\nOnInit:\n    .distance = 2;\n    .sex = G_OTHER;\n    end;\n}\n' % (obj.inn),
                    ])
                )
                self.save_cnt = True
                """
                print("[WARNING] Object type \"Save\" is deprecated!")
            elif isinstance(obj, Warp):
                if (obj.npc_id == u'WARP'):
                    obj_name = "#%s_%s_%s" % (self.base, obj.x, obj.y)
                    if (obj.dest_map.lower() in ["slide", "self"]):
                        self.warps.write(
                            SEPARATOR.join([
                                '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
                                'script\t',
                                '%s\tNPC_HIDDEN,%d,%d,{\n\tend;\nOnTouch:\n\tslide %d,%d; end;\n}\n' % (obj_name, obj.w, obj.h, obj.dest_x, obj.dest_y),
                            ])
                        )
                    else:
                        self.warps.write(
                            SEPARATOR.join([
                                '%s,%d,%d,0\t' % (self.base, obj.x, obj.y),
                                'warp\t',
                                '%s\t%s,%s,%s,%d,%d\n' % (obj_name, obj.w, obj.h, obj.dest_map, obj.dest_x, obj.dest_y),
                            ])
                        )
                    self.warp_cnt = True
            ### TMW2 INSTANCES
            ##############################################################
            elif isinstance(obj, Slide):
                if (obj.npc_id == u'SLIDE'):
                    obj_name = "#%s_%s_%s" % (self.base, obj.x, obj.y)
                    self.warps.write(
                        SEPARATOR.join([
                            '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
                            'script\t',
                            '%s\tNPC_HIDDEN,%d,%d,{\n\tend;\nOnTouch:\n\tslide %d,%d; end;\n}\n' % (obj_name, obj.w, obj.h, obj.dest_x, obj.dest_y),
                        ])
                    )
                    self.warp_cnt = True
                elif (not obj.npc_id == u'SCRIPT'):
                    obj_name = "#%s_%s_%s" % (self.base, obj.x, obj.y)
                    self.warps.write(
                        SEPARATOR.join([
                            '%s,%d,%d,0\tscript\t%s_h\tNPC_HIDDEN,0,0,{\n' % (self.base, obj.x, obj.y, obj_name),
                            'OnTouch:\n    warp "%s", %d, %d;\nclose;\n\nOnUnTouch:\n    doevent "%s::OnUnTouch";\n}\n' % (obj.dest_map, obj.dest_x, obj.dest_y, obj_name),
                            '%s,%d,%d,0\tscript\t%s\t%s,%d,%d,{\n    close;\nOnTouch:\n    doorTouch;\n\nOnUnTouch:\n    doorUnTouch;\n\nOnTimer340:\n    doorTimer;\n\nOnInit:\n    doorInit;\n}\n\n' % (self.base, obj.x, obj.y, obj_name, obj.npc_id, obj.trigger_x, obj.trigger_y),
                        ])
                    )
                    self.warp_cnt = True
            elif isinstance(obj, DynCollision):
                obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
                triggere = ifte(obj.enabled, "\nOnInit:", "")
                triggerd = ifte(obj.enabled, "", "\nOnInit:")
                self.confs.write(
                    SEPARATOR.join([
                        '\n',
                        '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
                        'script\t',
                        '#%s\tNPC_HIDDEN,{\n\tend;\nOnDisable:%s\n\tdelcells "%s"; end;\n' % (obj_name, triggerd, obj_name),
                        'OnEnable:%s\n\tsetcells "%s", %d, %d, %d, %d, %d, "%s";\n}\n' % (triggere, self.base, obj.x, obj.y, obj.x+obj.w, obj.y+obj.h, obj.colid, obj_name),
                    ])
                )
                self.save_cnt = True
            elif isinstance(obj, DungeonSwitch):
                obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
                status = ifte(obj.enabled, "OFFLINE", "ONLINE")
                nstats = ifte(obj.enabled, "ONLINE", "OFFLINE")
                func_name = ifte(obj.callfunc != "", "\tcallfunc \"%s\"%s;\n" % (obj.callfunc, 
                                ifte(obj.args != "", ", %s" % obj.args, "")), "")
                scrp_name = ifte(obj.doevent != "", "\tdoevent \"%s\";\n" % (obj.doevent), "")
                self.confs.write(
                    SEPARATOR.join([
                        '\n',
                        '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
                        'script\t',
                        '#%s\tNPC_SWITCH_%s,{\n' % (obj_name, status),
                        '\tif (getnpcclass() == NPC_SWITCH_%s)\n\t\tend;\n' % (nstats),
                        '%s%s' % (func_name, scrp_name),
                        '\tsetnpcdisplay "#%s", NPC_SWITCH_%s;\n\tend;\nOnInit:\n\t.distance=%d;\n}\n' % (obj_name, nstats, obj.distance),
                    ])
                )
                self.save_cnt = True
            elif isinstance(obj, FunctionTrigger):
                obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
                func_name = ifte(obj.callfunc != "", "\tcallfunc \"%s\"%s;\n" % (obj.callfunc, 
                                ifte(obj.args != "", ", %s" % obj.args, "")), "")
                scrp_name = ifte(obj.doevent != "", "\tdoevent \"%s\";\n" % (obj.doevent), "")
                self.confs.write(
                    SEPARATOR.join([
                        '\n',
                        '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
                        'script\t',
                        '#%s\tNPC_HIDDEN,%d,%d,{\n\tend;\n' % (obj_name, obj.w, obj.h),
                        'OnTouch:\n%s%s\tend;\n}\n' % (func_name, scrp_name),
                    ])
                )
                self.save_cnt = True
            elif isinstance(obj, Trap):
                obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
                npcid = ifte(obj.disarmtime, "NPC_TRAP", "NPC_TRAP_ONLINE")
                timer = ifte(obj.disarmtime, "OnTimer%d:\n\tstopnpctimer; setnpctimer 0; setnpcdisplay \"#%s\", NPC_TRAP; end;\n" % (obj.disarmtime*1000, obj_name), "")
                self.confs.write(
                    SEPARATOR.join([
                        '\n',
                        '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
                        'script\t',
                        '#%s\t%s,%d,%d,{\n\tmesn strcharinfo(0);\n\tmesq l("Something seems off with that!");\n\tclose;\n' % (obj_name, npcid, obj.w, obj.h),
                        '%s%s' % (ifte(obj.target & 1, "OnTouch:\n", ""), ifte(obj.target & 2, "OnTouchNPC:\n", "")),
                        '\tIronTrap(%d, %d, %d);\n\tend;\n%s}\n' % (obj.damage, obj.disarmtime, obj.stuntime, timer),
                    ])
                )
                self.save_cnt = True
            elif isinstance(obj, TreasureChest):
                obj_name = "%s_%s_%s" % (self.base, obj.x, obj.y)
                self.confs.write(
                    SEPARATOR.join([
                        '\n',
                        '%s,%d,%d,0\t' % (self.base, obj.x, (obj.y)),
                        'script\t',
                        '#%s\tNPC_CHEST,{\n\tTreasureBox();' % (obj_name),
                        '\n\tspecialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing',
                        '\n\tclose;\nOnInit:\n\t.distance=%d;' % (obj.distance),
                        '\n\tend;\n}\n',
                    ])
                )
                self.save_cnt = True

            ##############################################################

        if name == u'data':
            if self.state is State.DATA:
                if self.encoding == u'csv':
                  for x in self.buffer.split(','):
                    if int(x) > 0:
                      self.heightmap += str((int(x) - int(self.firstgid)) + 1)
                    else:
                      self.heightmap += str(x)
                self.state = State.FINAL

    def endDocument(self):
        if not self.mob_cnt:
            os.remove(posixpath.join(main.this_map_npc_dir, NPC_MOBS))
        if not self.save_cnt:
            os.remove(posixpath.join(main.this_map_npc_dir, NPC_CONFIG))
        if not self.warp_cnt:
            os.remove(posixpath.join(main.this_map_npc_dir, NPC_WARPS))

        imp_cnt = (len(os.walk(self.npc_dir).next()[2]))

        if imp_cnt > 0:
            self.imports.write('// Map %s: %s\n' % (self.base, self.name))
            self.imports.write('// %s\n' % MESSAGE)

            npcs = os.listdir(self.npc_dir)
            npcs.sort()
            for x in npcs:
                if x == NPC_IMPORTS:
                    continue
                if x.startswith('.'):
                    continue
                if x.endswith('.txt') or x.endswith('.c'):
                    self.imports.write('"%s",\n' % posixpath.join(SERVER_NPCS, self.base, x))
        else:
            os.remove(posixpath.join(main.this_map_npc_dir, NPC_IMPORTS))

def main(argv):
    global heigherror, fatalError
    _, client_data, server_data = argv
    tmx_dir = posixpath.join(client_data, CLIENT_MAPS)
    npc_dir = posixpath.join(server_data, SERVER_NPCS)
    if check_mobs:
        global mob_names
        mob_names = {}
        with open(posixpath.join(server_data, MOB_DB_CONF)) as mob_db:
            for line in mob_db:
                if not line.strip():
                    continue
                if line.startswith('//'):
                    continue

    npc_master = []
    map_basenames = []

    map_conf = open(posixpath.join(server_data,MAP_CONF), 'w')
    map_db = open(posixpath.join(server_data,MAP_DB_CONF), 'w')
    map_conf.write("map_removed: (\n)\nmap_list: (\n")
    map_count = 1
    for arg in sorted(os.listdir(tmx_dir)):
        base, ext = posixpath.splitext(arg)

        if ext == '.tmx':
            map_basenames.append(base)
            tmx = posixpath.join(tmx_dir, arg)
            main.this_map_npc_dir = posixpath.join(npc_dir, base)
            os.path.isdir(main.this_map_npc_dir) or os.mkdir(main.this_map_npc_dir)
            print('Converting %s' % (tmx))
            try:
             with open(posixpath.join(main.this_map_npc_dir, NPC_MOBS), 'w') as mobs:
                with open(posixpath.join(main.this_map_npc_dir, NPC_CONFIG), 'w') as confs:
                    with open(posixpath.join(main.this_map_npc_dir, NPC_WARPS), 'w') as warps:
                        with open(posixpath.join(main.this_map_npc_dir, NPC_IMPORTS), 'w') as imports:
                            xml.sax.parse(tmx, ContentHandler(main.this_map_npc_dir, mobs, confs, warps, imports))
            except:
             traceback.print_exc()
             print("ERROR: MAP \"%s\" WAS NOT CONVERTED. ERROR FOUND!" % tmx)
             fatalError=True
            if os.path.isfile(posixpath.join(main.this_map_npc_dir, NPC_IMPORTS)):
                npc_master.append('@include "%s"\n' % posixpath.join(SERVER_NPCS, base, NPC_IMPORTS))
            if heigherror:
                print("ERROR: Height layer possibly missing")
                fatalError=True
            heigherror=True
            
            map_db.write('%s %d\n' % (arg.split('.')[0], map_count))
            map_conf.write('    "%s",\n' % (arg.split('.')[0]))
            map_count += 1
    map_conf.write(")\n")
    with open(posixpath.join(npc_dir, NPC_MASTER_IMPORTS), 'w') as out:
        out.write('// %s\n\n' % MESSAGE)
        npc_master.sort()
        for line in npc_master:
            out.write(line)
    if fatalError:
        exit(1)

if __name__ == '__main__':
    main(sys.argv)