#! /usr/bin/env python3 # -*- coding: utf8 -*- # # Copyright (C) 2010-2011 Evol Online # Copyright (C) 2018 TMW-2 # Author: Andrei Karas (4144) # Author: Jesusalva import datetime import sys wikia=open("Items.md", "w") wikib=open("Monsters.md", "w") wikic=open("../../client-data/dyes.diff", "w") # Dye Report wikid=open("../../server-data/changechase.diff", "w") # ChangeChase Report # the TYPEs we use to determine where to pack things IT_HEALING=[] IT_ETC=[] IT_USABLE=[] IT_AMMO=[] IT_CARD=[] IT_PETEGG=[] IT_WEAPON={ 'HAND_2': [], # TWO HAND (LR) 'HAND_1':[]} # WEAPONS (R) IT_ARMOR={ 'MISC': [], # FOR FAILURE 'EQP_ACC_L': [], # ACCESSORY LEFT 'EQP_ACC_R': [], # ACCESSORT RIGHT 'EQP_HEAD_MID': [], # CHEST 'EQP_SHOES': [], # FEET 'EQP_GARMENT': [], # GLOVES 'EQP_HEAD_LOW':[], # PANTS '1024': [], # NECKLACES (should be EQP_COSTUME_HEAD_TOP instead of number) '2048': [], # RINGS (should be EQP_COSTUME_HEAD_MID instead of number) 'EQP_MOUNT':[], # MOUNTS (ie. EQP_SHADOW_SHOES) 'EQP_HEAD_TOP':[], # HATS/HELMETS 'EQP_HAND_L': []} # SHIELDS Mobs1=[] Mobs2=[] Mobs3=[] Mobs4=[] Mobs5=[] Mobs6=[] MobsA=[] SysDrops=[] def printSeparator(): print("--------------------------------------------------------------------------------") def showHeader(): print("TMW2 Wiki Generator") ##print "Evol client data validator." print("Run at: " + datetime.datetime.now().isoformat()) print("Usage: ./wikigen.py [ ]") ##print "https://gitlab.com/evol/evol-tools/blob/master/testxml/testxml.py" printSeparator() def showFooter(): #pass #printSeparator() print("Done.") class Mob: def __init__(self): # Basic self.id="0" #self.aegis="UnknownMonster" # SpriteName is not used anywhere, we are using its ID self.name="Unknown Monster Name" self.view="1" self.chch=False self.boss=False # Defensive self.mobpt="0" # Mob Points “Level” self.hp="0" self.xp="0" self.jp="0" self.st="" self.df="0" self.mdf="0" # Offensive self.atk="[0, 0]" self.range="0" self.move="0" self.delay="0" self.drops=[] # Stats self.str='0' self.agi='0' self.vit='0' self.int='0' self.dex='0' self.luk='0' def MobAlloc(ab): try: maab=int(ab.mobpt) except: maab=9901 if maab <= 2000: Mobs1.append(ab) elif maab <= 4000: Mobs2.append(ab) elif maab <= 6000: Mobs3.append(ab) elif maab <= 8000: Mobs4.append(ab) elif maab <= 10000: Mobs5.append(ab) elif maab <= 15000: Mobs6.append(ab) elif maab != 9901: MobsA.append(ab) else: print("WARNING, Disregarding \"%s\" (ID: %s) as invalid mob" % (ab.name, ab.id)) def testMobs(): print("\nGenerating Mob Wiki...") if len(sys.argv) >= 2: src=open(sys.argv[1]+"/db/re/mob_db.conf", "r") else: src=open("../../server-data/db/re/mob_db.conf", "r") #wikib.write("# Monster Database\n") start=False dropper=False x=Mob() # Only for pyflakes2 for a in src: if a == "{\n": if start: MobAlloc(x) else: start=True x=Mob() a=a.replace('\t', ' ') if " Id:" in a: x.id=stp(a) elif " Name:" in a: x.name=stp(a) elif " Hp:" in a: x.hp=stp(a) elif " Lv:" in a: x.mobpt=stp(a) elif " Exp:" in a: x.xp=stp(a) elif " JExp:" in a: x.jp=stp(a) elif " Attack:" in a: x.atk=stp(a) elif " AttackRange:" in a: x.range=stp(a) elif " Def:" in a: x.df=stp(a) elif " Mdef:" in a: x.mdf=stp(a) elif " MoveSpeed:" in a: x.move=stp(a) elif " ViewRange:" in a: x.view=stp(a) elif " AttackDelay:" in a: x.delay=stp(a) elif " Boss: true" in a: x.boss=True elif " Str:" in a: x.str=stp(a) elif " Agi:" in a: x.agi=stp(a) elif " Vit:" in a: x.vit=stp(a) elif " Int:" in a: x.int=stp(a) elif " Dex:" in a: x.dex=stp(a) elif " Luk:" in a: x.luk=stp(a) elif " Looter: true" in a: x.st+="Looter," elif " Assist: true" in a: x.st+="Assist," elif " Aggressive: true" in a: x.st+="Aggro," elif " ChangeChase: true" in a: x.chch=True elif 'Drops: ' in a: dropper=True elif dropper and '}' in a: dropper=False elif dropper: x.drops.append(stp(a).split(": ")) # Write last entry MobAlloc(x) writeMob() """ wikib.write('\n\n|Mode|Desc|\n|----|----|\n') wikib.write('|Lot|Looter|\n') wikib.write('|Ass|Assist|\n') wikib.write('|Agr|Aggressive|\n') """ src.close() def stp(x): return x.replace('\n', '').replace('|', '').replace('(int, defaults to ', '').replace(')', '').replace('basic experience', '').replace('"','').replace(" ","").replace("\t","").replace('(string', '').replace('Name: ','').replace('AttackDelay: ', '').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Def: ','').replace('Mdef: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('Str: ', '').replace('Dex: ', '').replace('Agi: ', '').replace('Vit: ', '').replace('Int: ', '').replace('Luk: ', '') def MonsterWrite(tbl): # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs) for i in tbl: if not i.chch: wikid.write("%s:%s\n" % (i.id, i.name)) aegis2=i.name.replace(" ", "_") if i.boss: i.name="'''"+i.name+"'''" if i.st == "": i.st="Normal" wikib.write('|-\n') wikib.write('| align="center" | [[Image:%s.png]]\n' % (aegis2)) wikib.write("""| height="82px;" | [[%s|%s]] '' (%s) ''
'' Level: %s - %s/%s/%s/%s/%s/%s '' - '' %s ''\n""" % (aegis2, i.name, i.id, i.mobpt, i.str, i.agi, i.vit, i.int, i.dex, i.luk, i.st)) wikib.write("""| align="center" | %s\n| align="center" | %s / %s\n""" % (i.hp, i.df, i.mdf)) try: dps=str(int(i.atk)*1000/int(i.delay)) except: dps="over %s ms" % i.delay wikib.write("""| align="center" | %s (%s)
''DPS %s''\n""" % (i.atk, i.range, dps)) wikib.write("""| align="center" | %s (%s)\n""" % (i.xp, i.jp)) wikib.write("""| ''
%s
''\n""" % (mb_rddrop(i))) wikib.write("""|-\n""") wikib.write("""|-\n|}\n""") def writeMob(): wikib.write("""{{Meta}}{{I18n}} {{Category playerinfo}} {{Status_green}} This is for '''THE MANA WORLD: REVOLT'''. If you're interested in the Legacy game, see instead: [[Legacy:Monsters]] The monsters are sorted roughly by their fighting strength, calculated solely by their level. For more information on the drops please see the [[Items]]. '''Key:''' HP is health points, DEF is defense, ATT is attack, EXP is the calculated base experience, JEXP is the job experience. The others are self-explanatory. Traits (such as aggressive) are written in ''italics''. {| border="1" cellspacing="0" cellpadding="5" class="responsive" align="center" ! style="background:#efdead;" | Image ! style="background:#efdead;" | Name (ID) ! style="background:#efdead;" | HP ! style="background:#efdead;" | DEF/MDEF ! style="background:#efdead;" | ATK
MIN/MAX (RANGE) ! style="background:#efdead;" | EXP/(JEXP) ! style="background:#efdead;" | Drops (Rate %) """) MonsterWrite(Mobs1) def mbdt(summary, content): return "
\ "+summary+"\
"+content+"
" def mb_rdmisc(mb): buff="" if "agr" in mb.st.lower(): buff+="View Range: %s\n" % (mb.view) buff+="Attack Range: %s\n" % (mb.range) buff+="Move speed: %s ms\n" % (mb.move) return buff def mb_rdrw(mb): buff="" buff+="MobPoints: %s\n" % (mb.mobpt) buff+="%s\n" % (mb.xp) buff+="%s\n" % (mb.jp) return buff def mb_rddrop(mb): buff="" # sorted for ax in sorted(mb.drops, key=lambda xcv: float(xcv[1]), reverse=True): # Ignore disabled drops if ax[0].startswith("//"): continue # Write drop try: url=ax[0].replace(" ", "_") ctr=str(int(ax[1])/100.0) buff+="[[%s|%s]] (%s%%)
" % (url, ax[0], ctr) #buff+=ax[0]+': ' + + ' %\n' except IndexError: print("Fatal: invalid %s mob with %s drops" % (mb.name, str(ax))) exit(1) except: print("[Warning] %s incorrect drop: %s" % (mb.name, str(ax))) buff+=ax[0]+': ' + ax[1] + ' ppm\n' # Save to SysDrops SysDrops.append([ax[0], ax[1], mb.name]) return buff class It: def __init__(self): # Basic self.id="0" self.aegis="UnknownItem" self.name="Unknown Item Name" self.price="0" # Sell price, of course self.weight="0" self.type="IT_ETC" # default type self.loc="" # Offensive/Defensive self.atk="0" self.matk="0" self.range="0" self.defs="0" # Restrictions (EquipLv) self.lvl="0" self.drop=True self.trade=True self.sell=True self.store=True # Special settings self.rare=False # DropAnnounce self.script=False # Visual self.sl="0" # Slots self.ac=False # Allow Cards # Script settings self.minheal="0" self.maxheal="0" self.delheal="0" def ItAlloc(it): if (it.sl == "0" and it.ac) or (it.sl in ["1","2","3","4"] and not it.ac): print("WARNING, item id "+it.id+" invalid dye/card setting!") if (len(it.sl) > 1): print("WARNING, item id "+it.id+" bad slots length: %d (%s)" % (len(it.sl), it.sl)) if it.ac: wikic.write(it.id + ": " + it.name + "\n") a=it.type if "IT_HEALING" in a: IT_HEALING.append(it) elif "IT_ETC" in a: IT_ETC.append(it) elif "IT_USABLE" in a: IT_USABLE.append(it) elif "IT_AMMO" in a: IT_AMMO.append(it) elif "IT_CARD" in a: IT_CARD.append(it) elif "IT_PETEGG" in a: IT_PETEGG.append(it) elif "IT_WEAPON" in a: if "HAND_L" in it.loc or "EQP_ARMS" in it.loc: IT_WEAPON["HAND_2"].append(it) elif "HAND_R" in it.loc: IT_WEAPON["HAND_1"].append(it) else: raise Exception("Invalid location for weapon: %s" % it.loc) elif "IT_ARMOR" in a: if 'EQP_ACC_L' in it.loc: IT_ARMOR['EQP_ACC_L'].append(it) elif 'EQP_ACC_R' in it.loc: IT_ARMOR['EQP_ACC_R'].append(it) elif 'EQP_HEAD_MID' in it.loc: IT_ARMOR['EQP_HEAD_MID'].append(it) elif 'EQP_SHOES' in it.loc: IT_ARMOR['EQP_SHOES'].append(it) elif 'EQP_GARMENT' in it.loc: IT_ARMOR['EQP_GARMENT'].append(it) elif 'EQP_HEAD_LOW' in it.loc: IT_ARMOR['EQP_HEAD_LOW'].append(it) elif 'EQP_HEAD_TOP' in it.loc: IT_ARMOR['EQP_HEAD_TOP'].append(it) elif 'EQP_HAND_L' in it.loc: IT_ARMOR['EQP_HAND_L'].append(it) elif '1024' in it.loc: IT_ARMOR['1024'].append(it) elif '2048' in it.loc: IT_ARMOR['2048'].append(it) elif 'EQP_SHADOW_SHOES' in it.loc: IT_ARMOR['EQP_MOUNT'].append(it) elif 'EQP_SHADOW_ACC_R' in it.loc: IT_ARMOR['EQP_ACC_R'].append(it) # Not really else: raise Exception("Invalid Loc for ID %s: %s" % (it.id, it.loc)) def newItemDB(): print("\nGenerating Item Wiki...") if len(sys.argv) >= 2: src=open(sys.argv[1]+"/db/re/item_db.conf", "r") else: src=open("../../server-data/db/re/item_db.conf", "r") x=It() for a in src: a=a.replace("\t", " ") if a == "{\n": ItAlloc(x) x=It() # sti() block if " Id:" in a: x.id=sti(a) elif " AegisName:" in a: x.aegis=sti(a) elif " Name:" in a: x.name=stin(a) elif " Sell:" in a: x.price=sti(a) elif " Weight:" in a: x.weight=sti(a) elif " Type:" in a: x.type=sti(a) elif " Loc:" in a: x.loc=sti(a) elif " Atk:" in a: x.atk=sti(a) elif " Matk:" in a: x.matk=sti(a) elif " Range:" in a: x.range=sti(a) elif " Def:" in a: x.defs=sti(a) elif " EquipLv:" in a: x.lvl=sti(a) elif " Slots:" in a: x.sl=sti(a) elif " AllowCards:" in a: x.ac=True # Write booleans elif "DropAnnounce: true" in a: x.rare=True elif "nodrop: true" in a: x.drop=False elif "notrade: true" in a: x.trade=False elif "noselltonpc: true" in a: x.sell=False elif "nostorage: true" in a: x.store=False elif "Script" in a: x.script=True # For healing items elif "@min " in a: x.minheal=sti(a) elif "@max " in a: x.maxheal=sti(a) elif "@delay" in a: x.delheal=sti(a) # Write last entry ItAlloc(x) writeItems() src.close() def sti(x): return x.replace('\n', '').replace('|', '').replace(')', '').replace('Id: ', '').replace('"','').replace(" ","").replace("\t","").replace('AegisName: ', '').replace('Name: ','').replace('Sell: ', '').replace('Weight: ', '').replace('Type: ', '').replace('Loc: ', '').replace('Atk: ', '').replace('Matk: ', '').replace('Range: ', '').replace('Def: ', '').replace('EquipLv: ', '').replace('Slots: ','').replace(" ", "").replace('@min=','').replace('@max=','').replace('@delay=','').replace(';','') def stin(x): return x.replace('\n', '').replace('|', '').replace(')', '').replace('Id: ', '').replace('"','').replace(" ","").replace("\t","").replace('Name: ','').replace(';','') def writeItems(): wikia.write("# Items\n\ + [Healing Items](#healing-items)\n\ + [Usable Items](#usable-items)\n\ + [Generic Items](#generic-items)\n\ + [Ammo](#ammo)\n\ + [Cards](#cards)\n\ + [Pet Eggs](#pet-eggs)\n\ + [Mounts](#mounts)\n\ + [Weapons](#weapons)\n\ + [1H Weapons](#1h-weapons)\n\ + [2H Weapons](#2h-weapons)\n\ + [Armors](#armors)\n\ + [Left Accessory](#left-accessory)\n\ + [Right Accessory](#right-accessory)\n\ + [Headgear](#headgear)\n\ + [Chest](#chest)\n\ + [Pants](#pants)\n\ + [Shoes](#shoes)\n\ + [Necklaces](#necklaces)\n\ + [Rings](#rings)\n\ + [Gloves](#gloves)\n\ + [Shields](#shields)\n\ \n\n") wikia.write("#### Restrictions Reference\n") wikia.write("Special Aegis Name Markers:\n\ + * - Rare item with drop announce.\n\ + (dp) - This item cannot be dropped.\n\ + (tr) - This item cannot de traded.\n\ + (sl) - This item cannot be sold.\n\ + (gg) - This item cannot go to storage.\n\n") # Healing Items wikia.write("## Healing Items\n\n") ItemWrite(IT_HEALING, ID=True, AEGIS=True, PRICE=True, WEIGHT=True, HEALING=True, DROPPER=True) # Usable Items wikia.write("## Usable Items\n") ItemWrite(IT_USABLE, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True, DROPPER=True) # Generic Items wikia.write("## Generic Items\n") ItemWrite(IT_ETC, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True, DROPPER=True) # Ammo Items wikia.write("## Ammo\n") ItemWrite(IT_AMMO, ID=True, AEGIS=True, NAME=True, WEIGHT=True, ATK=True) # Card Items wikia.write("## Cards\n") ItemWrite(IT_CARD, ID=True, AEGIS=True, NAME=True, PRICE=True, WEIGHT=True) # Pet Egg Items wikia.write("## Pet Eggs\n") ItemWrite(IT_PETEGG, ID=True, AEGIS=True, NAME=True, WEIGHT=True) # Mount Items wikia.write("## Mounts\n") ItemWrite(IT_ARMOR['EQP_MOUNT'], ID=True, AEGIS=True, NAME=True, WEIGHT=True) #################################################################### wikia.write("# Weapons\n") # 1 Hand Items wikia.write("## 1H Weapons\n") ItemWrite(IT_WEAPON['HAND_1'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, DROPPER=True) # 2 Hand Items wikia.write("## 2H Weapons\n") ItemWrite(IT_WEAPON['HAND_2'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, RANGE=True) #################################################################### wikia.write("# Armors\n") ArmorWrite("Left Accessory",'EQP_ACC_L', False) ArmorWrite("Right Accessory",'EQP_ACC_R', False) ArmorWrite("Headgear",'EQP_HEAD_TOP') ArmorWrite("Chest",'EQP_HEAD_MID') ArmorWrite("Pants",'EQP_HEAD_LOW') ArmorWrite("Shoes",'EQP_SHOES') ArmorWrite("Necklaces",'1024', False) ArmorWrite("Rings",'2048', False) ArmorWrite("Gloves",'EQP_GARMENT') ArmorWrite("Shields",'EQP_HAND_L') # Write AegisName with restrictions def hl(it): buff="" if it.rare: buff+="*" buff+=it.aegis buff+=" " if not it.drop: buff+="(dp)" if not it.trade: buff+="(tr)" if not it.sell: buff+="(sl)" if not it.store: buff+="(gg)" return buff # wikia.write("Id|Aegis|Name|Weight|Atk|Matk|\n") # wikia.write("Id|Aegis|Name|Price|Weight|\n") def ItemWrite(tbl, ID=False, AEGIS=False, NAME=False, PRICE=False, WEIGHT=True, DEF=False, LVL=False, ATK=False, RANGE=False, HEALING=False, SCRIPT=False, DROPPER=False): wikia.write("\n") wikia.write("") if ID: wikia.write("") if AEGIS: wikia.write("") if NAME: wikia.write("") if PRICE: wikia.write("") if WEIGHT: wikia.write("") if DEF: wikia.write("") if LVL: wikia.write("") if ATK: wikia.write("") wikia.write("") if RANGE: wikia.write("") if HEALING: wikia.write("") wikia.write("") wikia.write("") if SCRIPT: wikia.write("") if DROPPER: wikia.write("") wikia.write("\n") for i in tbl: try: zt=int(i.weight) except: zt=0 tbl.remove(i) del zt for i in sorted(tbl, key=lambda xcv: int(xcv.weight), reverse=True): if ID: wikia.write("" % (i.id,i.id)) if AEGIS: wikia.write("" % hl(i)) if NAME: wikia.write("" % i.name) if PRICE: wikia.write("" % i.price) if WEIGHT: wikia.write("" % i.weight) if DEF: wikia.write("" % i.defs) if LVL: wikia.write("" % i.lvl) if ATK: wikia.write("" % i.atk) wikia.write("" % i.matk) if RANGE: wikia.write("" % i.range) if HEALING: wikia.write("" % i.minheal) wikia.write("" % i.maxheal) wikia.write("" % i.delheal) if SCRIPT: wikia.write("" % i.script) # TODO: Check for item Aegis in npc/ folder too, to determine shops and quests. if DROPPER: tmp_droppers="" tmp_drpalign=[] for ax in SysDrops: if ax[0] == i.aegis: tmp_drpalign.append([ax[2], ax[1]]) if len(tmp_drpalign) > 0: for a in sorted(tmp_drpalign, key=lambda xcv: float(xcv[1]), reverse=True): try: ppm=int(a[1])/100.0 tmp_droppers+=("%s: %.2f %% \n" % (a[0], ppm)) except: print("[Warning] %s whodrop error: %s" % (i.name, str(a))) wikia.write("" % mbdt("monsters", tmp_droppers)) else: wikia.write("") wikia.write("") wikia.write("
IDAegisNamePriceWeightDefLvlAtkMatkRangeMinMaxDelayScriptMobs
%s%s%s%s GP%s gDef: %sLv: %sAtk: %s%s%s%s%s%s s%s%s-
\n") wikia.write("\n[(↑) Return to top](#items)\n\n") def ArmorWrite(name,scope,defitem=True): wikia.write("## "+name+"\n") ItemWrite(IT_ARMOR[scope], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, DEF=defitem, LVL=True, DROPPER=True) class Quest: def __init__(self, ide): # Basic self.id=ide self.name="Unknown Quest Name" self.group="Unknown" self.ent=[] class QuestEntry: def __init__(self): # Basic self.complete=False self.entry=[] # collection of self.giver="" self.reward="" self.loc="" def qnt(string): return string.replace(' ','').replace('"','').replace("'","").replace('<','').replace('>','').replace('nowiki=1', '').replace('nowiki', '') def qnt2(string): return string.replace('##B','**').replace('##b','**').replace('##0','*').replace('##1','*').replace('##2','*').replace('##3','*').replace('##','*') def DoQuest(): print("\nGenerating Quest Wiki...") if len(sys.argv) >= 3: src=open(sys.argv[2]+"/quests.xml", "r") else: src=open("../../client-data/quests.xml", "r") qlog=[] q=Quest(-1) qe=QuestEntry() ig=False nw=False for e in src: # Handle Comments and Ignored lines if '' in e: continue elif '' in e: ig=False if '' in e: q.ent.append(qe) elif ' tag: %s (arg was %s) (line was %s)" % (e, rc[1], l)) exit(1) rc=[False, ""] # Fill stuff in Quest Entry if '','').replace('','').replace('','').replace('','').replace('','').replace('','').strip() elif '','').replace('','').replace("@@", "text").replace('') qe.loc=b[1].replace('','').replace('','').strip() ) # Done reading file src.close() aktbl={} aksort=[] print("\033[32;1mTotal quests: %d\033[0m" % len(qlog)) for i in qlog: if i.name=="Unknown Quest Name": print("Warning, invalid quest: %d" % (i.id)) qlog.remove(i) continue # Total Table #print(str(i.id)+": "+i.name) try: aktbl[i.group].append("[%s](q/%d)" % (i.name, i.id)) except: aktbl[i.group]=["[%s](q/%d)" % (i.name, i.id)] aksort.append(i.group) # Individual file f=open("../../wiki/q/"+str(i.id)+'.md', "w") f.write("\n\n" % (i.id, i.name)) f.write("# %s\n" % i.name) f.write('\n') totalcnt=0 for a in i.ent: totalcnt+=1 f.write('\n### %d Stage\n\n' % totalcnt) if a.complete: f.write('*This completes quest*\n\n') if a.giver != "" or a.reward != "" or a.loc != "": f.write('```\n') if a.giver != "": f.write('Quest Giver: %s\n' % a.giver) if a.reward != "": f.write('Reward: %s\n' % a.reward.replace('@@', '')) if a.loc != "": f.write('Location: %s\n' % a.loc) f.write('```\n\n') for line in a.entry: f.write('%s\n' % line) f.write('\n\n****\nThis file is generated automatically. Editing it will have no effect.\n') f.close() # Write total table f=open("Quests.txt", "w") f.write("***Total quests: %d***\n" % len(qlog)) for key in aksort: f.write('\n## %s\n\n' % key) for a in aktbl[key]: f.write('+ '+a+'\n') f.close() showHeader() testMobs() newItemDB() DoQuest() wikia.close() wikib.close() wikic.close() wikid.close() #print(str(SysDrops)) showFooter() exit(0)