summaryrefslogtreecommitdiff
path: root/wiki
diff options
context:
space:
mode:
Diffstat (limited to 'wiki')
-rwxr-xr-xwiki/elegen.py426
-rwxr-xr-xwiki/lanalysis.py394
-rwxr-xr-xwiki/redesign.py1050
-rwxr-xr-xwiki/sedesign.py515
-rw-r--r--wiki/tmp-arm247
-rw-r--r--wiki/tmp-dec8
-rw-r--r--wiki/tmp-etc25
-rw-r--r--wiki/tmp-wpn59
-rwxr-xr-xwiki/webwikigen.py758
-rwxr-xr-xwiki/wikigen.py898
10 files changed, 4380 insertions, 0 deletions
diff --git a/wiki/elegen.py b/wiki/elegen.py
new file mode 100755
index 0000000..e8385f0
--- /dev/null
+++ b/wiki/elegen.py
@@ -0,0 +1,426 @@
+#! /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
+
+wikib=open("EleMonsters.html", "w")
+wikib.write('<html><head><meta charset=utf8 /><title>EleGen File</title></head><body>')
+totalid=0
+bifs=False
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 Ele Generator")
+ print("This stuff analyzes and sorts mobs elementals, races and walkmasks.")
+ ##print "Evol client data validator."
+ print("Run at: " + datetime.datetime.now().isoformat())
+ ##print "https://gitlab.com/evol/evol-tools/blob/master/testxml/testxml.py"
+ printSeparator()
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+Mobs0=[]
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+Mobs6=[]
+Mobs7=[]
+Mobs8=[]
+Mobs9=[]
+MobsA=[]
+
+SysDrops=[]
+
+def fwalk(wmask):
+ if wmask == 'WATER':
+ return '<font color=#00f>%s</font>' % (wmask)
+ elif wmask == 'AIR':
+ return '<font color=#093>%s</font>' % (wmask)
+ elif wmask == 'WALL':
+ return '<font color=#f00>%s</font>' % (wmask)
+ elif wmask == 'NORMAL' or wmask == 'DEFAULT':
+ return '<font color=#111>%s</font>' % (wmask)
+ else:
+ print("Invalid walk mask: "+wmask)
+ exit(1)
+
+def WhatRace(rac):
+ rc=rac.race
+ if rc == 0:
+ return "Formless"
+ elif rc == 1:
+ return "Undead"
+ elif rc == 2:
+ return "Brute"
+ elif rc == 3:
+ return "Plant"
+ elif rc == 4:
+ return "Insect"
+ elif rc == 5:
+ return "Mineral"
+ elif rc == 6:
+ return "Evil"
+ elif rc == 7:
+ return "SemiHuman"
+ elif rc == 8:
+ return "Angel"
+ elif rc == 9:
+ return "Dragon"
+ elif rc == 10:
+ return "Player"
+ elif rc == 11:
+ return "Boss"
+ elif rc == 12:
+ return "NonBoss"
+ elif rc == 14:
+ return "NonSemiHuman"
+ elif rc == 15:
+ return "NonPlayer"
+ elif rc == 16:
+ return "SemiPlayer"
+ elif rc == 17:
+ return "NonSemiPlayer"
+ else:
+ print("ERROR, INVALID RACE ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+
+def WhatElem(rac):
+ rc=rac.elem
+ tl="ERROR"
+ cl="#F00"
+ if rc == 0:
+ tl,cl="Neutral","#000"
+ elif rc == 1:
+ tl,cl="Water","#00F"
+ elif rc == 2:
+ tl,cl="Earth","#7A0"
+ elif rc == 3:
+ tl,cl="Fire","#F00"
+ elif rc == 4:
+ tl,cl="Wind","#093"
+ elif rc == 5:
+ tl,cl="Nature","#040"
+ elif rc == 6:
+ tl,cl="Holy","#afa"
+ elif rc == 7:
+ tl,cl="Dark","#908"
+ elif rc == 8:
+ tl,cl="Ghost","#404"
+ elif rc == 9:
+ tl,cl="Undead","#440"
+ else:
+ print("ERROR, INVALID ELEM ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+ return "<font color=%s>%s</font>" % (cl, tl)
+
+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"
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="0"
+ self.jp="0"
+ self.st=""
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="0"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+ # New
+ self.race=-1
+ self.elem=-1
+ self.elel=-1
+ self.walk="NORMAL"
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.elem)
+ except:
+ maab=9901
+
+ if maab == 0:
+ Mobs0.append(ab)
+ elif maab == 1:
+ Mobs1.append(ab)
+ elif maab == 2:
+ Mobs2.append(ab)
+ elif maab == 3:
+ Mobs3.append(ab)
+ elif maab == 4:
+ Mobs4.append(ab)
+ elif maab == 5:
+ Mobs5.append(ab)
+ elif maab == 6:
+ Mobs6.append(ab)
+ elif maab == 7:
+ Mobs7.append(ab)
+ elif maab == 8:
+ Mobs8.append(ab)
+ elif maab == 9:
+ Mobs9.append(ab)
+ else:
+ MobsA.append(ab)
+
+def testMobs():
+ global totalid
+ print("Generating Elem-Mob Wiki...")
+ src=open("../../serverdata/db/re/mob_db.conf", "r")
+ wikib.write("<h1 id=#x>EleGen Monster Database</h1>\n")
+ start=False
+ dropper=False
+ x=Mob() # Only for pyflakes2
+
+ for a in src:
+ if a == "{\n":
+ if start and not bifs and ((int(x.hp) <= 50) or (x.race == 3)):
+ start=False
+
+ if start:
+ MobAlloc(x)
+ else:
+ start=True
+ x=Mob()
+
+ if " Id:" in a:
+ x.id=stp(a)
+ totalid+=1
+ if x.id == "ID":
+ continue
+
+ if " 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 " MoveSpeed:" in a:
+ x.move=stp(a)
+ elif " ViewRange:" in a:
+ x.view=stp(a)
+ elif " AttackDelay:" in a:
+ x.delay=stp(a)
+ elif " Looter: true" in a:
+ x.st+="<font color=#790>Lot</font>,"
+ elif " Assist: true" in a:
+ x.st+="<font color=#0a0>Ass</font>,"
+ elif " Aggressive: true" in a:
+ x.st+="<font color=#f00>Agr</font>,"
+ elif " WalkMask:" in a:
+ x.walk=stp(a)
+ elif " Element:" in a:
+ tmp=stp(a)
+ tmp2=tmp.split(',')
+ try:
+ x.elem=int(tmp2[0])
+ x.elel=int(tmp2[1])
+ except:
+ print("Invalid Element for mob %s: %s" % (x.id, tmp))
+ exit(1)
+ elif " Race:" in a:
+ try:
+ x.race=int(stp(a))
+ except:
+ print("Invalid Race for mob %s: %s" % (x.id, a))
+ exit(1)
+ 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('Element: ','').replace('Race: ','').replace('AttackDelay: ', '').replace('WalkMask: ','').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('(','').replace(')','').replace('WALK_','')
+
+
+def MonsterWrite(tbl):
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Drops</th></tr>\n")
+ for i in sorted(tbl, key=lambda tbl: int(tbl.mobpt)):
+ if i.id == 'ID':
+ continue
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("\n<a href=#x>(↑) Return to top</a><br/><br/>\n\n")
+
+def writeMob():
+ wikib.write("Total monsters: %d<br/>" % totalid)
+ wikib.write("<ul>\
+<li><a href=#0>Neutral</a></li>\n\
+<li><a href=#1>Water</a></li>\n\
+<li><a href=#2>Earth</a></li>\n\
+<li><a href=#3>Fire</a></li>\n\
+<li><a href=#4>Wind</a></li>\n\
+<li><a href=#5>Nature (Poison)</a></li>\n\
+<li><a href=#6>Holy</a></li>\n\
+<li><a href=#7>Dark (Shadow)</a></li>\n\
+<li><a href=#8>Ghost</a></li>\n\
+<li><a href=#9>Undead</a></li>\n\
+<li><a href=#Nan>-</a></li>\n\
+<li><a href=Elements><font color=#f00>Elemental Reference</font></a></li>\n\
+</ul> ")
+
+ wikib.write("<h1 id=0>Neutral</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs0))
+ MonsterWrite(Mobs0)
+ wikib.write("<h1 id=1>Water</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs1))
+ MonsterWrite(Mobs1)
+ wikib.write("<h1 id=2>Earth</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs2))
+ MonsterWrite(Mobs2)
+ wikib.write("<h1 id=3>Fire</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs3))
+ MonsterWrite(Mobs3)
+ wikib.write("<h1 id=4>Wind</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs4))
+ MonsterWrite(Mobs4)
+ wikib.write("<h1 id=5>Nature</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs5))
+ MonsterWrite(Mobs5)
+ wikib.write("<h1 id=6>Holy</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs6))
+ MonsterWrite(Mobs6)
+ wikib.write("<h1 id=7>Dark</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs7))
+ MonsterWrite(Mobs7)
+ wikib.write("<h1 id=8>Ghost</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs8))
+ MonsterWrite(Mobs8)
+ wikib.write("<h1 id=9>Undead</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(Mobs9))
+ MonsterWrite(Mobs9)
+
+ wikib.write("<h1 id=NaN>Error</h1>\n\n")
+ wikib.write("<i>Total: %d Monsters</i><br/>\n\n" % len(MobsA))
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<pre>%s</pre>" % (content)
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_core(mb):
+ buff=""
+ buff+="Lvl: %s<br/>\n" % (mb.mobpt)
+ buff+="HP: <b><font color=#0a0>%s</font></b><br/>\n" % (mb.hp)
+ buff+="ATK: <b><font color=#a00>%s</font></b><br/>\n" % (mb.atk)
+ if mb.st != "":
+ buff+="Modes: <i>%s</i>" % (mb.st)
+ return buff
+
+def mb_eleg(mb):
+ buff=""
+ buff+="Race: %s<br/>\n" % (WhatRace(mb))
+ buff+="Walk: %s<br/>\n" % (fwalk(mb.walk))
+ buff+="Element: %s<br/>\n" % (WhatElem(mb))
+ return buff
+
+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+="AtkDelay: %s ms\n" % (mb.delay)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ buff+="Element Level: %d\n" % (mb.elel)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ try:
+ buff+="MobPoints: %d\n" % (int(mb.mobpt)*11/10)
+ except:
+ pass
+ buff+="%s\n" % (mb.xp)
+ buff+="%s\n" % (mb.jp)
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ for ax in mb.drops:
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.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
+
+
+
+showHeader()
+
+testMobs()
+print("Total monsters: %d" % totalid)
+
+wikib.write('</body></html>')
+wikib.close()
+#print(str(SysDrops))
+
+showFooter()
+exit(0)
diff --git a/wiki/lanalysis.py b/wiki/lanalysis.py
new file mode 100755
index 0000000..bc2ffe2
--- /dev/null
+++ b/wiki/lanalysis.py
@@ -0,0 +1,394 @@
+#! /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
+
+wikib=open("EleMonsters.html", "w")
+wikib.write('<html><head><meta charset=utf8 /><title>EleGen File</title></head><body>')
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 Ele Generator")
+ print("This stuff analyzes and sorts mobs elementals, races and walkmasks.")
+ ##print "Evol client data validator."
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("Output is: EleMonsters.html")
+ ##print "https://gitlab.com/evol/evol-tools/blob/master/testxml/testxml.py"
+ printSeparator()
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+Mobs6=[]
+MobsA=[]
+
+SysDrops=[]
+
+def fwalk(wmask):
+ if wmask == 'WATER':
+ return '<font color=#00f>%s</font>' % (wmask)
+ elif wmask == 'AIR':
+ return '<font color=#093>%s</font>' % (wmask)
+ elif wmask == 'WALL':
+ return '<font color=#f00>%s</font>' % (wmask)
+ elif wmask == 'NORMAL' or wmask == 'DEFAULT':
+ return '<font color=#111>%s</font>' % (wmask)
+ else:
+ print("Invalid walk mask: "+wmask)
+ exit(1)
+
+def WhatRace(rac):
+ rc=rac.race
+ if rc == 0:
+ return "Formless"
+ elif rc == 1:
+ return "Undead"
+ elif rc == 2:
+ return "Brute"
+ elif rc == 3:
+ return "Plant"
+ elif rc == 4:
+ return "Insect"
+ elif rc == 5:
+ return "Fish"
+ elif rc == 6:
+ return "-"
+ elif rc == 7:
+ return "SemiHuman"
+ elif rc == 8:
+ return "Angel"
+ elif rc == 9:
+ return "Dragon"
+ elif rc == 10:
+ return "Player"
+ elif rc == 11:
+ return "Boss"
+ elif rc == 12:
+ return "NonBoss"
+ elif rc == 14:
+ return "NonSemiHuman"
+ elif rc == 15:
+ return "NonPlayer"
+ elif rc == 16:
+ return "SemiPlayer"
+ elif rc == 17:
+ return "NonSemiPlayer"
+ else:
+ print("ERROR, INVALID RACE ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+
+def WhatElem(rac):
+ rc=rac.elem
+ tl="ERROR"
+ cl="#F00"
+ if rc == 0:
+ tl,cl="Neutral","#000"
+ elif rc == 1:
+ tl,cl="Water","#00F"
+ elif rc == 2:
+ tl,cl="Earth","#7A0"
+ elif rc == 3:
+ tl,cl="Fire","#F00"
+ elif rc == 4:
+ tl,cl="Wind","#093"
+ elif rc == 5:
+ tl,cl="Nature","#040"
+ elif rc == 6:
+ tl,cl="Holy","#afa"
+ elif rc == 7:
+ tl,cl="Dark","#908"
+ elif rc == 8:
+ tl,cl="Ghost","#404"
+ elif rc == 9:
+ tl,cl="Undead","#440"
+ else:
+ print("ERROR, INVALID ELEM ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+ return "<font color=%s>%s</font>" % (cl, tl)
+
+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.boss=False
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="Exp: 0"
+ self.jp="JExp: 0"
+ self.st=""
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="0"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+ # New
+ self.race=-1
+ self.elem=-1
+ self.elel=-1
+ self.walk="NORMAL"
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.mobpt)
+ except:
+ maab=9901
+
+ if maab <= 20:
+ Mobs1.append(ab)
+ elif maab <= 40:
+ Mobs2.append(ab)
+ elif maab <= 60:
+ Mobs3.append(ab)
+ elif maab <= 80:
+ Mobs4.append(ab)
+ elif maab <= 100:
+ Mobs5.append(ab)
+ elif maab <= 150:
+ Mobs6.append(ab)
+ else:
+ MobsA.append(ab)
+
+def testMobs():
+ print("Generating Elem-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("<h1 id=#x>EleGen Monster Database</h1>\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()
+
+ if " Id:" in a:
+ x.id=stp(a)
+ if x.id == "ID":
+ continue
+
+ if " 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 " 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 " Looter: true" in a:
+ x.st+="<font color=#790>Lot</font>,"
+ elif " Assist: true" in a:
+ x.st+="<font color=#0a0>Ass</font>,"
+ elif " Aggressive: true" in a:
+ x.st+="<font color=#f00>Agr</font>,"
+ elif " WalkMask:" in a:
+ x.walk=stp(a)
+ elif " Element:" in a:
+ tmp=stp(a)
+ tmp2=tmp.split(',')
+ try:
+ x.elem=int(tmp2[0])
+ x.elel=int(tmp2[1])
+ except:
+ print("Invalid Element for mob %s: %s" % (x.id, tmp))
+ exit(1)
+ elif " Race:" in a:
+ try:
+ x.race=int(stp(a))
+ except:
+ print("Invalid Race for mob %s: %s" % (x.id, a))
+ exit(1)
+ 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('Element: ','').replace('Race: ','').replace('AttackDelay: ', '').replace('WalkMask: ','').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('(','').replace(')','').replace('WALK_','')
+
+
+def MonsterWrite(tbl):
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Drops</th></tr>\n")
+ for i in sorted(tbl, key=lambda tbl: int(tbl.mobpt)):
+ if i.id == 'ID':
+ continue
+ if i.boss:
+ i.name="<b>"+i.name+"</b>"
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("\n<a href=#x>(↑) Return to top</a><br/><br/>\n\n")
+
+def writeMob():
+ wikib.write("<ul>\
+<li><a href=#0>Starter</a></li>\n\
+<li><a href=#1>Apprentice</a></li>\n\
+<li><a href=#2>Intermediary</a></li>\n\
+<li><a href=#3>Advanced</a></li>\n\
+<li><a href=#4>Expert</a></li>\n\
+<li><a href=#5>Master</a></li>\n\
+<li><a href=#Nan>OoS</a></li>\n\
+</ul> ")
+
+ wikib.write("<h1 id=0>Lv 0-20</h1>\n\n")
+ MonsterWrite(Mobs1)
+ wikib.write("<h1 id=1>Lv 21-40</h1>\n\n")
+ MonsterWrite(Mobs2)
+ wikib.write("<h1 id=2>Lv 41-60</h1>\n\n")
+ MonsterWrite(Mobs3)
+ wikib.write("<h1 id=3>Lv 61-80</h1>\n\n")
+ MonsterWrite(Mobs4)
+ wikib.write("<h1 id=4>Lv 81-100</h1>\n\n")
+ MonsterWrite(Mobs5)
+ wikib.write("<h1 id=5>Lv 101-150</h1>\n\n")
+ MonsterWrite(Mobs6)
+
+ wikib.write("<h1 id=NaN>Lv 101+</h1>\n\n")
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<b>"+summary+"</b><br/><pre>"+content+"</pre>"
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_core(mb):
+ buff=""
+ buff+="Lvl: %s<br/>\n" % (mb.mobpt)
+ buff+="HP: <b><font color=#0a0>%s</font></b><br/>\n" % (mb.hp)
+ buff+="ATK: <b><font color=#a00>%s</font></b><br/>\n" % (mb.atk)
+ if mb.st != "":
+ buff+="Modes: <i>%s</i>" % (mb.st)
+ return buff
+
+def mb_eleg(mb):
+ buff=""
+ buff+="Race: %s<br/>\n" % (WhatRace(mb))
+ buff+="Walk: %s<br/>\n" % (fwalk(mb.walk))
+ buff+="Element: %s<br/>\n" % (WhatElem(mb))
+ return buff
+
+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+="AtkDelay: %s ms\n" % (mb.delay)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ buff+="Element Level: %d\n" % (mb.elel)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ buff+="<font color=#060><u>%s</b></u></font>\n" % (mb.xp.replace(' ', ' <b>'))
+ buff+="<font color=#006><u>%s</b></u></font>\n" % (mb.jp.replace(' ', ' <b>'))
+ try:
+ buff+="MobPoints: %d\n" % (int(mb.mobpt)*11/10)
+ except:
+ pass
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ for ax in mb.drops:
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.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
+
+
+
+showHeader()
+
+testMobs()
+
+wikib.write('<br/><hr/>')
+wikib.write("Run at: " + datetime.datetime.now().isoformat())
+wikib.write('</body></html>')
+wikib.close()
+#print(str(SysDrops))
+
+showFooter()
+exit(0)
diff --git a/wiki/redesign.py b/wiki/redesign.py
new file mode 100755
index 0000000..8e94c52
--- /dev/null
+++ b/wiki/redesign.py
@@ -0,0 +1,1050 @@
+#! /usr/bin/env python3
+# -*- coding: utf8 -*-
+#
+# Copyright (C) 2010-2011 Evol Online
+# Copyright (C) 2018 TMW-2
+# Author: Andrei Karas (4144)
+# Author: Jesusalva
+#
+# The use of the data
+
+import datetime
+import sys
+
+stgen=True
+aeros=False
+bifs=False
+skipCI=False
+
+wikib=open("EleMonsters.html", "w")
+wikib.write('<html><head><meta charset=utf8 /><title>EleGen File</title></head><body>')
+
+exp=[ 9,16,25,36,77,112,153,200,253,320,
+ 385,490,585,700,830,970,1120,1260,1420,1620,
+ 1860,1990,2240,2504,2950,3426,3934,4474,6889,7995,
+ 9174,10425,11748,13967,15775,17678,19677,21773,28543,34212,
+ 38065,42102,46323,53026,58419,64041,69892,75973,92468,115254,
+ 128692,142784,157528,178184,196300,215198,234879,255341,310188,365914,
+ 402508,442769,487051,535756,589342,648281,713112,784421,862867,949158,
+ 1044076,1148484,1263331,1389671,1528642,1681509,1849671,2034639,2238111,
+ 2461928,
+ 2708132,2978946,3276840,3604530,3964987,4361495,4797656,5277432,5805184,6095442,
+ 6400217,6720231,7056251,7409070,7779531,8168509,8576941,9005793,9456080,9928893,
+ 10425342,10946613,11493946,12068655,12672087,13305690,13970980,14669530,15403013,16173172,
+ 16981833,17830934,18722479,19658611,20641552,21673632,22757319,23895184,25089950,25591758,
+ 26103599,26625670,27158181,27701356,28255388,28820507,29396920,29984867,30584571,31196260,
+ 31820184,32456596,33105727,33767845,34443202,35132065,35834705,36551410,37282446,38028099,
+ 38788671,39564454,40355745,41162860,41986116,42825838,43682362,44556009,45447138,45901616,
+ 46360641,46824255,47292503,47765432,48243093,48725528,49212784,49704921,50201982,50704003,
+ 51211047,51723162,52240405,52762810,53290446,53823354,54361598,54905214,55454271,56008820,
+ 56568910,57134610,57705965,58283032,58865873,59454536,60049086,60649586,61256083,61868649,
+ 62487336,63112216,63743344,64380779,65024597,65674843,66331602,66994930,67664885,68341538,
+ 69024959,69715207,70412360,71116485,71827658,72545940,73271398,74004114,74744153,75491595,
+ 76246518,77008984,77779077,78556879,79342456,80135878,80937236,81746616,82564087,83389730,
+ 84223638,85065880,85916545,86775711,87643468,88519912,89405121,90299171,91202163,92114182,
+ 93035322,93965687,94905347,95854406,96812948,97781076,98758892,99746478,100743951,101751398,
+ 102768911,103796609,104834582,105882930,106941769,108011193,109091315,110182230,111284052,112396904,
+ 113520874,114656091,115802657,116960692,118130296,119311601,120504716,121709770,122926875,124156150,
+ 125397711,126651686,127918206,129197392,130489375,131794267,133112212,134443338,135787774,137145652,
+ 138517109,139902284,141301316,142714335,144141489,145582902,147038738,148509126,149994219,150894194,
+ 151799571,152710366,153626627,154548387,155475676,156408540,157346998,158291079,159240837,160196288,
+ 161157464,162124419,163097165,164075757,165060214,166050576,167046878,168049166,169057470,170071823,
+ 171092262,172118826,173151541,174190460,175235604,176287026,177344758,178408837,179479294,180556179,
+ 181639518,182729367,183825754,184928711,186038293,187154528,188277464,189407130,190543577,191686849,
+ 192836981,193994007,195157974,196328927,197506910,198691949,199884102,201083405,202289908,203503646,
+ 204724676,205953024,207188754,208431886,209682484,210940582,212206225,213479467,214760350,216048919,
+ 217345224,218649298,219961193,221280960,222608645,223944302,225287974,226639708,227999552,229367554,
+ 230743762,232128226,233520999,234922133,236331675,237749668,239176170,240611236,242054915,243507252,
+ 244968307,246438117,247916753,249404252,250900687,252406101,253920540,255444067,256976742,258518609,
+ 260069729,261630158,263199938,264779135,266367816,267966025,269573820,271191260,272818408,274455326,
+ 276102061,277758679,279425234,281101787,282788399,284485141,286192050,287909214,289636677,291374496,
+ 293122752,294881486,296650782,298430689,300221275,302022609,303834742,305657759,307491713,309336672,
+ 311192696,313059863,314938223,316827859,318728836,320641216,322565061,324500459,326447460,328406143,
+ 330376584,332358847,334353004,336359128,338377288,340407551,342449994,344504696,346571722,348651151,
+ 350743062,352847532,354964629,357094423,359236990,361392422,363560778,365742141,367936599,370144221,
+ 372365091,374599279,376846874,379107955,381382611,383670915,385972949,388288794,390618537,392962257,
+ 395320037,397691969,400078129,402478601,404893475,407322841,409766782,412225383,414698739,417186939,
+ 419690069,422208209,424741456,427289911,429853651,432432776,435027370,437637543,440263366,442024421,
+ 443792521,445567690,447349963,449139360,450935926,452739673,454550638,456368839,458194326,460027115,
+ 461867221,463714693,465569558,467431848,469301579,471178794,473063508,474955766,476855601,478763022,
+ 480678083,482600803,484531205,486469328,488415217,490368880,492330355,494299675,496276878,498261990,
+ 500255043,502256068,504265102,506282171,508307305,510340540,512381907,514431439,516489176,518555144,
+ 520629376,522711891,524802748,526901963,529009581,531125618,533250127,535383137,537524678,539674777,
+ 541833486,544000830,546176836,548361554,550555012,552757234,554968274,557188159,559416915,561654583,
+ 563901210,566156820,568421455,570695142,572977924,575269847,577570928,579881219,582200741,584529542,
+ 586867672,589215145,591572009,593938303,596314063,598699319,601094122,603498497,605912494,608336152,
+ 610769496,613212575,615665434,618128102,620600617,623083026,625575357,628077658,630589971,633112332,
+ 635644789,638187373,640740134,643303101,645876314,648459827,651053675,653657891,656272528,658897623,
+ 661533224,664179368,666836088,669503437,672181451,674870184,677569669,680279959,683001081,685733085,
+ 688476023,691229930,693994859,696770843,699557927,702356165,705165600,707986270,710818214,713661496,
+ 716516145,719382218,722259755,725148792,728049398,730961596,733885443,736820993,739768279,742727359,
+ 745698276,748681079,751675801,754682510,757701238,760732051,763774987,766830096,769897426,772977027,
+ 776068941,779173220,782289911,785419075,788560754,791714999,794881861,798061393,801253640,804458666,
+ 807676501,810907214,814150847,817407448,820677081,823959788,827255638,830564665,833886935,837222492
+ ]
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ global stgen, aeros, bifs, skipCI
+ print("TMW2 Ele Generator")
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("Usage: ./redesign.py [default|aeros|none|update|all] [<path_to_serverdata>]")
+ if len(sys.argv) >= 2:
+ if sys.argv[1] == "default":
+ stgen=True
+ aeros=False
+ bifs=False
+ skipCI=False
+ elif sys.argv[1] == "aeros":
+ stgen=False
+ aeros=True
+ bifs=False
+ skipCI=False
+ elif sys.argv[1] == "none":
+ stgen=False
+ aeros=False
+ bifs=False
+ skipCI=False
+ elif sys.argv[1] == "update":
+ stgen=True
+ aeros=False
+ bifs=False
+ skipCI=True
+ elif sys.argv[1] == "all":
+ stgen=True
+ aeros=True
+ bifs=True
+ skipCI=False
+ else:
+ exit(1)
+ print("This stuff analyzes and sorts monsters and then create base stats for Moubootaur Legends.")
+ print("Drops aren't calculated or taken in account, TWEAK AS NEEDED")
+ printSeparator()
+ print("Output is: EleMonsters.html")
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+Mobs6=[]
+MobsA=[]
+
+# [N, W, E, F, W, -, H, H, G, -]
+Ele=[0, 0, 0, 0, 0, 0, 0, 0, 0]
+
+# This is for Aeros
+Plants=[]
+Level50=[]
+Level100=[]
+Aggressive=[]
+Assistant=[]
+Looter=[]
+Boss=[]
+
+SysDrops=[]
+
+def fwalk(wmask):
+ if wmask == 'WATER':
+ return '<font color=#00f>%s</font>' % (wmask)
+ elif wmask == 'AIR':
+ return '<font color=#093>%s</font>' % (wmask)
+ elif wmask == 'WALL':
+ return '<font color=#f00>%s</font>' % (wmask)
+ elif wmask == 'NORMAL' or wmask == 'DEFAULT':
+ return '<font color=#111>%s</font>' % (wmask)
+ else:
+ print("Invalid walk mask: "+wmask)
+ exit(1)
+
+def WhatRace(rac):
+ rc=rac.race
+ if rc == 0:
+ return "Formless"
+ elif rc == 1:
+ return "Undead"
+ elif rc == 2:
+ return "Brute"
+ elif rc == 3:
+ return "Plant"
+ elif rc == 4:
+ return "Insect"
+ elif rc == 5:
+ return "Mineral"
+ elif rc == 6:
+ return "-"
+ elif rc == 7:
+ return "SemiHuman"
+ elif rc == 8:
+ return "Angel"
+ elif rc == 9:
+ return "Dragon"
+ elif rc == 10:
+ return "Player"
+ elif rc == 11:
+ return "Boss"
+ elif rc == 12:
+ return "NonBoss"
+ elif rc == 14:
+ return "NonSemiHuman"
+ elif rc == 15:
+ return "NonPlayer"
+ elif rc == 16:
+ return "SemiPlayer"
+ elif rc == 17:
+ return "NonSemiPlayer"
+ else:
+ print("ERROR, INVALID RACE ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+
+def WhatElem(rac):
+ rc=rac.elem
+ tl="ERROR"
+ cl="#F00"
+ if rc == 0:
+ tl,cl="Neutral","#000"
+ elif rc == 1:
+ tl,cl="Water","#00F"
+ elif rc == 2:
+ tl,cl="Earth","#7A0"
+ elif rc == 3:
+ tl,cl="Fire","#F00"
+ elif rc == 4:
+ tl,cl="Wind","#093"
+ elif rc == 5:
+ tl,cl="Nature","#040"
+ elif rc == 6:
+ tl,cl="Holy","#afa"
+ elif rc == 7:
+ tl,cl="Dark","#908"
+ elif rc == 8:
+ tl,cl="Ghost","#404"
+ elif rc == 9:
+ tl,cl="Undead","#440"
+ else:
+ print("ERROR, INVALID ELEM ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+ Ele[rc]+=1
+ return "<font color=%s>%s</font>" % (cl, tl)
+
+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.boss=False
+ self.plant=False
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="Exp: 0"
+ self.jp="JExp: 0"
+ self.st=""
+ self.dfn=0
+ self.mdf=0
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="1"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+ # New
+ self.race=-1
+ self.elem=-1
+ self.elel=-1
+ self.walk="NORMAL"
+
+ # 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 <= 20 or skipCI:
+ Mobs1.append(ab)
+ elif maab <= 40:
+ Mobs2.append(ab)
+ elif maab <= 60:
+ Mobs3.append(ab)
+ elif maab <= 80:
+ Mobs4.append(ab)
+ elif maab <= 100:
+ Mobs5.append(ab)
+ elif maab <= 150:
+ Mobs6.append(ab)
+ else:
+ MobsA.append(ab)
+
+ # Aeros allocation
+ """Plants=[]
+ Level50=[]
+ Level100=[]
+ Aggressive=[]
+ Assistant=[]
+ Looter=[]
+ Boss=[]"""
+ normie=True
+ if ab.plant:
+ Plants.append(ab.id)
+ normie=False
+ if ab.boss:
+ Boss.append(ab.id)
+ normie=False
+ if normie:
+ if "Agr" in ab.st:
+ Aggressive.append(ab.id)
+ normie=False
+ if "Lot" in ab.st:
+ Looter.append(ab.id)
+ normie=False
+ if "Ass" in ab.st:
+ Assistant.append(ab.id)
+ normie=False
+ if normie:
+ if maab <= 55:
+ Level50.append(ab.id)
+ else:
+ Level100.append(ab.id)
+
+def testMobs():
+ print("Generating Elem-Mob Wiki...")
+ if len(sys.argv) >= 3:
+ src=open(sys.argv[2]+"/db/re/mob_db.conf", "r", encoding="utf-8")
+ else:
+ src=open("../../serverdata/db/re/mob_db.conf", "r", encoding="utf-8")
+
+ wikib.write("<h1 id=#x>EleGen Monster Database</h1>\n")
+ start=False
+ dropper=False
+ skip=0
+ x=Mob() # Only for pyflakes2
+
+ for a2 in src:
+ a=a2.replace(' ', '\t');
+ if a == "{\n":
+ if skip:
+ skip=0
+ if start:
+ MobAlloc(x)
+ else:
+ start=True
+ x=Mob()
+
+ if skip:
+ start=False
+ x=Mob()
+ continue
+ if " Id:" in a:
+ x.id=stp(a)
+ if x.id == "ID":
+ continue
+
+ if " 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 " MoveSpeed:" in a:
+ x.move=stp(a)
+ elif " ViewRange:" in a:
+ x.view=stp(a)
+ elif " AttackDelay:" in a:
+ x.delay=stp(a)
+
+ elif " Def:" in a:
+ x.dfn=stp(a)
+ elif " Mdef:" in a:
+ x.mdf=stp(a)
+
+ 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 " Boss: true" in a:
+ x.boss=True
+ elif " Plant: true" in a:
+ x.plant=True
+ elif " Looter: true" in a:
+ x.st+="<font color=#790>Lot</font>,"
+ elif " Assist: true" in a:
+ x.st+="<font color=#0a0>Ass</font>,"
+ elif " Aggressive: true" in a:
+ x.st+="<font color=#f00>Agr</font>,"
+ elif " WalkMask:" in a:
+ x.walk=stp(a)
+ elif " Element:" in a:
+ tmp=stp(a)
+ tmp2=tmp.split(',')
+ try:
+ x.elem=int(tmp2[0])
+ x.elel=int(tmp2[1])
+ except:
+ print("Invalid Element for mob %s: %s" % (x.id, tmp))
+ exit(1)
+ elif " Race:" in a:
+ try:
+ x.race=int(stp(a))
+ except:
+ print("Invalid Race for mob %s: %s" % (x.id, a))
+ exit(1)
+ elif 'Drops: ' in a:
+ dropper=True
+ elif dropper and '}' in a:
+ dropper=False
+ elif dropper:
+ x.drops.append(stp(a).split(": "))
+ elif "Plant: true" in a:
+ skip=1
+ # 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('Element: ','').replace('Race: ','').replace('AttackDelay: ', '').replace('WalkMask: ','').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('(','').replace(')','').replace('WALK_','').replace('Def: ','').replace('Mdef: ','')
+
+
+def MonsterWrite(tbl):
+ global skipCI
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ if stgen:
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Stgen</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Stats</th><th>Drops</th></tr>\n")
+ else:
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Stats</th><th>Drops</th></tr>\n")
+
+ if not skipCI:
+ tbl=sorted(tbl, key=lambda tbl: int(tbl.mobpt))
+
+ for i in tbl:
+ if i.id == 'ID':
+ continue
+
+ # Special skips for REDESIGN
+ #if (int(i.id) < 1187):
+ # continue
+ if not bifs and ((int(i.hp) <= 50) or (i.race == 3)):
+ continue
+
+ if i.boss:
+ i.name="<b>"+i.name+"</b>"
+ if stgen:
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mbdt('advise',mb_stgen(i)) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('stats', mb_rdstat(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ else:
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('stats', mb_rdstat(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("<b>Total: %02d Monsters</b><br/>\n" % len(tbl))
+ wikib.write("\n<a href=#x>(↑) Return to top</a><br/><br/>\n\n")
+
+def writeMob():
+ wikib.write("<ul>\
+<li><a href=#0>Starter</a></li>\n\
+<li><a href=#1>Apprentice</a></li>\n\
+<li><a href=#2>Intermediary</a></li>\n\
+<li><a href=#3>Advanced</a></li>\n\
+<li><a href=#4>Expert</a></li>\n\
+<li><a href=#5>Master</a></li>\n\
+<li><a href=#Nan>OoS</a></li>\n\
+</ul> ")
+
+ wikib.write("<h1 id=0>Lv 0-20</h1>\n\n")
+ MonsterWrite(Mobs1)
+ wikib.write("<h1 id=1>Lv 21-40</h1>\n\n")
+ MonsterWrite(Mobs2)
+ wikib.write("<h1 id=2>Lv 41-60</h1>\n\n")
+ MonsterWrite(Mobs3)
+ wikib.write("<h1 id=3>Lv 61-80</h1>\n\n")
+ MonsterWrite(Mobs4)
+ wikib.write("<h1 id=4>Lv 81-100</h1>\n\n")
+ MonsterWrite(Mobs5)
+ wikib.write("<h1 id=5>Lv 101-150</h1>\n\n")
+ MonsterWrite(Mobs6)
+
+ wikib.write("<h1 id=NaN>Lv 101+</h1>\n\n")
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<b>"+summary+"</b><br/><pre>"+content+"</pre>"
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_core(mb):
+ buff=""
+ buff+="Lvl: %s<br/>\n" % (mb.mobpt)
+ buff+="HP: <b><font color=#0a0>%s</font></b><br/>\n" % (mb.hp)
+ buff+="ATK: <b><font color=#a00>%s</font></b><br/>\n" % (mb.atk)
+ buff+="DEF: <b><font color=#775>%s/%s</font></b><br/>\n" % (mb.dfn, mb.mdf)
+ if mb.st != "":
+ buff+="Modes: <i>%s</i>" % (mb.st)
+ return buff
+
+def mb_eleg(mb):
+ buff=""
+ buff+="Race: %s<br/>\n" % (WhatRace(mb))
+ buff+="Walk: %s<br/>\n" % (fwalk(mb.walk))
+ buff+="Element: %s<br/>\n" % (WhatElem(mb))
+ return buff
+
+############################################################
+def mb_stgen(mb):
+ lv=int(mb.mobpt)
+ st=int(mb.str.replace(' ', '').replace('Str:',''))
+ #ag=int(mb.agi.replace(' ', '').replace('Agi:',''))
+ vi=int(mb.vit.replace(' ', '').replace('Vit:',''))
+ #it=int(mb.int.replace(' ', '').replace('Int:',''))
+ #dx=int(mb.dex.replace(' ', '').replace('Dex:',''))
+ #lk=int(mb.luk.replace(' ', '').replace('Luk:',''))
+
+ # Attack Range vs Attack Delay
+ ar=int(mb.range)
+ ad=int(mb.delay)
+ mv=int(mb.move)
+ magr=False
+ mass=False
+ if ('Agr' in mb.st):
+ magr=True
+ if ('Ass' in mb.st):
+ mass=True
+
+ if (not ar):
+ ar=1
+
+ # Fórmula do dano:
+ # Tal que level 10 cause 150 dano
+ # Tal que level 50 cause 1000 dano
+ # Tal que a fórmula seja exponencial
+ """
+ lhp=lv**1.5+lv*(vi/100)+lv*15
+ hhp=lv**1.8+lv*(vi/100)+lv*15
+ # Casos especiais
+ if mb.boss:
+ lhp*=1.6
+ hhp*=4.2
+ if "slime" in mb.name.lower():
+ lhp*=0.4
+ hhp*=0.4
+ if ar > 1:
+ lhp-=ar**2
+ hhp-=ar**2
+
+ # Fórmula da HPTable: 400+(x*50)
+ # MASTERED
+ lat=(lv*40+lv**1.2+lv*(st/100))/ar
+ hat=(lv*40+lv**1.5+lv*(st/100))/ar
+ # Casos especiais
+ if mb.boss:
+ lat*=1.2
+ hat*=1.4
+ if "slime" in mb.name.lower():
+ lat*=0.3
+ hat*=0.3
+ # Attack is DPS. So we must adjust to be a damage per second
+ lat*=(ad/1872)
+ hat*=(ad/1872)
+ # Formula is not reliable
+ lat/=2
+ hat/=2
+ # Consider Aggressive and Assist mobs
+ if magr:
+ lat*=0.8
+ hat*=0.8
+ if mass:
+ lat*=0.9
+ hat*=0.9
+
+ # Experience
+ lxp=(lv**1.2+lat+(lhp/8)) / max(1,(12-(lv/10)))
+ hxp=(lv**1.6+hat+(hhp/8)) / max(1,(15-(lv/10)))
+ # Casos especiais
+ if mb.boss:
+ lxp*=4.2
+ hxp*=4.7
+ """
+
+ # Over100 Special Formula
+ OVER100=0
+ if lv > 100:
+ OVER100=lv-100
+ lv=100+OVER100*0.25
+
+ # Fórmula da HPTable: 400+(x*50)
+ # MASTERED
+ lat=(lv*40+lv**1.2+lv*(st/100))
+ hat=(lv*40+lv**1.5+lv*(st/100))
+ if (ar > 1):
+ lat*=max(0.5, 1.0-((ar-1)/10.0))
+ hat*=max(0.5, 1.0-((ar-1)/10.0))
+
+ # Casos especiais
+ if mb.boss:
+ lat*=1.2
+ hat*=1.4
+ if "slime" in mb.name.lower():
+ lat*=0.3
+ hat*=0.3
+ # Attack is DPS. So we must adjust to be a damage per second
+ lat*=(ad/1872)
+ hat*=(ad/1872)
+ # Formula is not reliable
+ lat*=0.55
+ hat*=0.55
+ lat*=max(0.5, 1.0-(lv/10.0))
+ hat*=max(0.5, 1.0-(lv/10.0))
+ # Consider Aggressive and Assist mobs
+ if magr:
+ lat*=0.78
+ hat*=0.78
+ if mass:
+ lat*=0.89
+ hat*=0.89
+
+
+ # Over100 Special Formula
+ if OVER100:
+ lv=100+OVER100*2
+
+ # HP: Each 10 levels give you 100 extra weapon damage
+ # I expect from 6 to 14 hits to kill
+ # You deliver in average two hits per second
+ # Mobs deliver one hit each four seconds (and that's 1hand advantage)
+ lhp=lv*20*6+lv*(vi/100)
+ hhp=lv*20*14+lv*(vi/100)
+ if mb.boss:
+ lhp*=1.4
+ hhp*=1.8
+ if "slime" in mb.name.lower():
+ lhp*=0.6
+ hhp*=0.6
+ if ar > 1 and not OVER100:
+ lhp*=0.9
+ hhp*=0.9
+
+ # Experience is way too non-linear and I prefer to do it with reference formula
+ # like I was doing before
+ # But let's use a formula based on mobs-to-lvl-up where you must kill 1580 lv 60 mobs
+ # to raise from lv 60 (you can kill lv 50 mobs easier - but then I expect 3700 kills)
+ # mobs to kill to raise level
+
+ # This is the current mob-lvl-exp-table generated by this software
+ # As you see, it is somewhat similar to TMW Org.
+ # Remember aggressive, fast, and assistant mobs give even more exp
+
+ # Level 1: total 1 mobs individual 3 xp (1.450)
+ # Level 2: total 3 mobs individual 5 xp (1.450)
+ # Level 3: total 8 mobs individual 5 xp (1.450)
+ # Level 4: total 16 mobs individual 4 xp (1.450)
+ # Level 5: total 26 mobs individual 7 xp (1.450)
+ # Level 6: total 39 mobs individual 8 xp (1.450)
+ # Level 7: total 56 mobs individual 9 xp (1.450)
+ # Level 8: total 77 mobs individual 9 xp (1.450)
+ # Level 9: total 101 mobs individual 10 xp (1.450)
+ # Level 10: total 129 mobs individual 11 xp (1.450)
+ # Level 20: total 667 mobs individual 21 xp (1.450)
+ # Level 30: total 1767 mobs individual 57 xp (1.450)
+ # Level 31: total 1912 mobs individual 63 xp (1.450)
+ # Level 32: total 2064 mobs individual 68 xp (1.450)
+ # Level 33: total 2223 mobs individual 73 xp (1.450)
+ # Level 34: total 2390 mobs individual 84 xp (1.450)
+ # Level 35: total 2563 mobs individual 91 xp (1.450)
+ # Level 36: total 2747 mobs individual 96 xp (1.455)
+ # Level 37: total 2942 mobs individual 101 xp (1.460)
+ # Level 38: total 3148 mobs individual 105 xp (1.465)
+ # Level 39: total 3366 mobs individual 130 xp (1.470)
+ # Level 40: total 3597 mobs individual 148 xp (1.475)
+ # Level 41: total 3841 mobs individual 156 xp (1.480)
+ # Level 42: total 4098 mobs individual 163 xp (1.485)
+ # Level 43: total 4369 mobs individual 170 xp (1.490)
+ # Level 44: total 4656 mobs individual 185 xp (1.495)
+ # Level 45: total 4958 mobs individual 193 xp (1.500)
+ # Level 46: total 5276 mobs individual 201 xp (1.505)
+ # Level 47: total 5611 mobs individual 208 xp (1.510)
+ # Level 48: total 5963 mobs individual 215 xp (1.515)
+ # Level 49: total 6334 mobs individual 249 xp (1.520)
+ # Level 50: total 6724 mobs individual 295 xp (1.525)
+ # Level 51: total 7133 mobs individual 314 xp (1.530)
+ # Level 52: total 7564 mobs individual 331 xp (1.535)
+ # Level 53: total 8016 mobs individual 348 xp (1.540)
+ # Level 54: total 8491 mobs individual 375 xp (1.545)
+ # Level 55: total 8990 mobs individual 393 xp (1.550)
+ # Level 56: total 9512 mobs individual 411 xp (1.555)
+ # Level 57: total 10061 mobs individual 428 xp (1.560)
+ # Level 58: total 10636 mobs individual 443 xp (1.565)
+ # Level 59: total 11239 mobs individual 514 xp (1.570)
+ # Level 60: total 11871 mobs individual 579 xp (1.575)
+ # Level 61: total 12533 mobs individual 608 xp (1.580)
+ # Level 62: total 13226 mobs individual 638 xp (1.585)
+ # Level 63: total 13952 mobs individual 670 xp (1.590)
+ # Level 64: total 14712 mobs individual 704 xp (1.595)
+ # Level 65: total 15508 mobs individual 740 xp (1.600)
+ # Level 66: total 16340 mobs individual 778 xp (1.605)
+ # Level 67: total 17211 mobs individual 818 xp (1.610)
+ # Level 68: total 18122 mobs individual 861 xp (1.615)
+ # Level 69: total 19075 mobs individual 905 xp (1.620)
+ # Level 70: total 20071 mobs individual 952 xp (1.625)
+ # Level 80: total 32389 mobs individual 1783 xp (1.650)
+ # Level 90: total 47806 mobs individual 3635 xp (1.650)
+ # Level 100: total 66308 mobs individual 4976 xp (1.650)
+
+ # Over100 Special Formula
+ if OVER100:
+ lv=100+OVER100
+
+ try:
+ mxp=exp[lv]
+ hxp=exp[lv]
+ lxp=exp[lv-1]
+ minxp=exp[lv-1]
+ except:
+ mxp=exp[len(exp)-1]
+ hxp=exp[len(exp)-1]
+ lxp=exp[len(exp)-2]
+ minxp=exp[len(exp)-2]
+ print("Warning: Invalid exp for mob \033[1m%s\033[0m" % (mb.name.replace("<b>", "").replace("</b>", "")))
+
+ if not mb.boss:
+ if lv > 35:
+ fx=1.45+((lv-35)/200.0)
+ else:
+ fx=1.45
+ if fx > 1.65:
+ fx=1.65
+
+ # Aggressive, assistant and fast mobs yield more exp
+ # Slow monsters yield less exp, because they're easier to kill on any playstyle
+ if magr:
+ fx-=0.09
+ else:
+ fx-=0.04
+ if mass:
+ fx-=0.08
+ if mv < 200:
+ fx-=0.02
+ if mv > 500:
+ fx+=0.02
+
+ # 1 ^ anything = 1, so we need a better rule
+ if lv != 1:
+ lxp=minxp/(lv**fx)
+ else:
+ lxp=3 # 3~5 is fine
+ # Boss are BOSS. It gives roughly 0.1% ~ 10.0% from lvlup xp.
+ else:
+ lxp=mxp/1000
+ hxp=mxp/10
+ del minxp, mxp
+
+ # Over100 Special Formula (kinda)
+ olv=0
+ if lv > 80:
+ olv=lv+0
+ lv=80+(lv-80)*0.25
+
+ # Defense follows the same player formula
+ dfn=((lv**1.255)*2.5)
+ dfn=dfn*350.0/810.0
+ mdf=max(0, lv-5)+(lv/10.0)
+ if not mb.boss:
+ mdf/=2
+ if ar > 3:
+ dfn/=2
+
+ # Nerf mobs def in 50% (to be more realistic to what we have)
+ dfn/=2
+
+ # Over100 Special Formula
+ if OVER100:
+ lv=100+OVER100
+ else:
+ lv=olv+0
+ del olv
+
+ # Force HP to be higher
+ # It'll only start applying from level 40 onwards
+ # It gives a bonus of 0.5% HP per mob level
+ # This means a level 100 mob got 60 times that stronger: 30%
+ if lv > 40:
+ lhp=lhp*(1.0+((lv-40)/210.0))
+ lhp=int(lhp)
+
+ # Norm
+ lhp=int(lhp)
+ hhp=int(hhp)
+ lat=int(lat)
+ hat=int(hat)
+ lxp=int(lxp)
+ hxp=int(hxp)
+ dfn=int(dfn)
+ mdf=int(mdf)
+
+ buff="<pre><font size=-2px>"
+ buff+="HP Range: %s ~ %s<br/>\n" % (lhp, hhp)
+ buff+="ATK Range: %s ~ %s<br/>\n" % (lat, hat)
+ buff+="Maximum XP: %s ~ %s<br/>\n" % (lxp, hxp)
+ buff+="DEF: %s / %s<br/>\n" % (dfn, mdf)
+ buff+="<b>Drop, Move, Elegen, aspd<br/>\n"
+ buff+="</b></font></pre>"
+ return buff
+
+def mb_rdstat(mb):
+ buff="<pre><font size=-4px>"
+ buff+="%s\n" % (mb.str)
+ buff+="%s\n" % (mb.agi)
+ buff+="%s\n" % (mb.vit)
+ buff+="%s\n" % (mb.int)
+ buff+="%s\n" % (mb.dex)
+ buff+="%s\n" % (mb.luk)
+ buff+="</font></pre>"
+ return buff
+
+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+="AtkDelay: %s ms\n" % (mb.delay)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ buff+="Element Level: %d\n" % (mb.elel)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ buff+="<font color=#060><u>%s</b></u></font>\n" % (mb.xp.replace(' ', ' <b>'))
+ buff+="<font color=#006><u>%s</b></u></font>\n" % (mb.jp.replace(' ', ' <b>'))
+ try:
+ buff+="MobPoints: %d\n" % (int(mb.mobpt)*11/10)
+ except:
+ pass
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ tdr=0
+ for ax in mb.drops:
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.0) + ' %\n'
+ tdr+=int(ax[1])
+ 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])
+
+ if tdr < 500:
+ print("[INFO] %s has %d/500 drops. Disqualified for Realm of Drops." % (mb.name, tdr))
+ return buff
+
+
+
+showHeader()
+
+testMobs()
+
+wikib.write('<br/><hr/>')
+wikib.write("Run at: " + datetime.datetime.now().isoformat())
+wikib.write('<hr/>')
+
+wikib.write("""
+<b>Player Stats (melee warrior)</b>
+<table border=1>
+<tr><th>Level</th><th>Stats 1</th><th>Stats 2</th><th>Average</th></tr>
+
+<tr><th>00</th>
+<td>str: 2 agi: 5 vit: 7 int:1 dex: 6 luk: 3</td>
+<td>str: 2 agi: 5 vit: 7 int:1 dex: 6 luk: 3</td>
+<th>str: 2 agi: 5 vit: 7 int:1 dex: 6 luk: 3</th></tr>
+
+<tr><th>10</th>
+<td>str:15 agi:10 vit: 5 int:1 dex:10 luk: 4</td>
+<td>str:11 agi:11 vit: 6 int:1 dex:11 luk: 7</td>
+<th>str:13 agi:11 vit: 5 int:1 dex:10 luk: 6</th></tr>
+
+<tr><th>20</th>
+<td>str:19 agi:10 vit:10 int:1 dex:20 luk:10</td>
+<td>str:14 agi:19 vit: 8 int:1 dex:17 luk:11</td>
+<th>str:17 agi:14 vit: 9 int:1 dex:18 luk:11</th></tr>
+
+<tr><th>30</th>
+<td>str:25 agi:30 vit:10 int:1 dex:20 luk:10</td>
+<td>str:22 agi:28 vit:12 int:1 dex:21 luk:15</td>
+<th>str:24 agi:29 vit:11 int:1 dex:20 luk:13</th></tr>
+
+<tr><th>40</th>
+<td>str:38 agi:30 vit:20 int:1 dex:25 luk:17</td>
+<td>str:28 agi:35 vit:14 int:1 dex:32 luk:21</td>
+<th>str:33 agi:33 vit:17 int:1 dex:28 luk:19</th></tr>
+
+<tr><th>50</th>
+<td>str:50 agi:40 vit:30 int:1 dex:25 luk:18</td>
+<td>str:41 agi:41 vit:15 int:1 dex:41 luk:26</td>
+<th>str:46 agi:41 vit:22 int:1 dex:33 luk:27</th></tr>
+
+<tr><th>60</th>
+<td>str:54 agi:50 vit:40 int:1 dex:35 luk:20</td>
+<td>str:52 agi:52 vit:16 int:1 dex:45 luk:32</td>
+<th>str:53 agi:51 vit:28 int:1 dex:40 luk:26</th></tr>
+
+<tr><th>70</th>
+<td>str:60 agi:60 vit:43 int:1 dex:50 luk:20</td>
+<td>str:61 agi:61 vit:22 int:1 dex:51 luk:38</td>
+<th>str:60 agi:61 vit:33 int:1 dex:50 luk:29</th></tr>
+
+<tr><th>80</th>
+<td>str:80 agi:60 vit:50 int:1 dex:50 luk:25</td>
+<td>str:65 agi:71 vit:32 int:1 dex:60 luk:41</td>
+<th>str:72 agi:66 vit:41 int:1 dex:55 luk:33</th></tr>
+
+<tr><th>90</th>
+<td>str:80 agi:70 vit:60 int:1 dex:60 luk:31</td>
+<td>str:71 agi:71 vit:41 int:1 dex:71 luk:51</td>
+<th>str:76 agi:70 vit:50 int:1 dex:66 luk:41</th></tr>
+
+<tr><th>100</th>
+<td>str:90 agi:80 vit:69 int:1 dex:60 luk:31</td>
+<td>str:71 agi:81 vit:42 int:1 dex:81 luk:61</td>
+<th>str:81 agi:80 vit:56 int:1 dex:70 luk:46</th></tr>
+
+<tr><th>110</th>
+<td>str:90 agi:90 vit:69 int:1 dex:70 luk:35</td>
+<td>str:82 agi:82 vit:50 int:1 dex:82 luk:65</td>
+<th>str:86 agi:86 vit:60 int:1 dex:76 luk:50</th></tr>
+
+<tr><th>120</th>
+<td>str:99 agi:90 vit:69 int:1 dex:70 luk:45</td>
+<td>str:86 agi:86 vit:51 int:1 dex:86 luk:68</td>
+<th>str:93 agi:88 vit:60 int:1 dex:78 luk:56</th></tr>
+
+<tr><th>130</th>
+<td>str:99 agi:91 vit:75 int:1 dex:70 luk:55</td>
+<td>str:91 agi:90 vit:51 int:1 dex:90 luk:68</td>
+<th>str:95 agi:91 vit:63 int:1 dex:80 luk:61</th></tr>
+
+</table>
+""")
+wikib.write('<hr/>')
+wikib.write("""
+<h3>Elemental Count</h3>
+<table border=1>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+<tr><td>%s</td><td>%02d</td></tr>
+</table>
+""" % (
+"Neutral", Ele[0],
+"Water", Ele[1],
+"Earth", Ele[2],
+"Fire", Ele[3],
+"Wind", Ele[4],
+"Holy", Ele[6],
+"Dark", Ele[7],
+"Ghost", Ele[8],
+"Error", Ele[5]))
+
+wikib.write('</body></html>')
+wikib.close()
+#print(str(SysDrops))
+
+# Aeros allocation
+if aeros:
+ print("// These arrays are filled automatically by redesign.py");
+ print("setarray .ML_Plants, "+
+ str(Plants).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Boss, "+
+ str(Boss).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Aggr, "+
+ str(Aggressive).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Asst, "+
+ str(Assistant).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Loot, "+
+ str(Looter).replace('[','').replace(']','').replace("'","")+";")
+ print("setarray .ML_Lv50, "+
+ str(Level50).replace('[','').replace(']','').replace("'","").replace('ID, ','')+
+ ";")
+ print("setarray .ML_Lv99, "+
+ str(Level100).replace('[','').replace(']','').replace("'","")+";")
+
+showFooter()
+exit(0)
diff --git a/wiki/sedesign.py b/wiki/sedesign.py
new file mode 100755
index 0000000..26d956b
--- /dev/null
+++ b/wiki/sedesign.py
@@ -0,0 +1,515 @@
+#! /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
+
+stgen=True
+
+wikia=open("EleItems.html", "w")
+
+# the TYPEs we use to determine where to pack things
+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
+IT_WEAPON={ 'RANGED': [], # RANGED WEAPONS
+ 'MAGICAL':[], # MAGICAL WEAPONS
+ 'HAND_2':[], # TWO HAND (LR)
+ 'HAND_1':[]} # WEAPONS (R)
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 EleItems Generator")
+ print("Run at: " + datetime.datetime.now().isoformat())
+ print("Usage: ./sedesign.py [info|level|none] [<path_to_serverdata>]")
+ print("SeDesign determines the reference defense for every item.")
+ print("Tweak as needed. Bonuses/Rarity/etc not taken in account.")
+ print("Running this without Redesign (or vice versa) will break balance")
+ printSeparator()
+ print("Output is: EleItems.html")
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+
+
+
+
+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=""
+ self.fc=0.0
+
+ # 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=""
+
+ # Visual
+ self.sl="0" # Slots
+ self.ac=False # Allow Cards
+
+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 and it.id != "ID(int"):
+ print("WARNING, item id "+it.id+" bad slots length: %d (%s)" % (len(it.sl), it.sl))
+
+ a=it.type
+ if "IT_ARMOR" in a:
+ if 'EQP_ACC_L' in it.loc:
+ IT_ARMOR['EQP_ACC_L'].append(it)
+ it.fc=0.00
+ elif 'EQP_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it)
+ it.fc=0.00
+ elif 'EQP_HEAD_MID' in it.loc:
+ IT_ARMOR['EQP_HEAD_MID'].append(it)
+ if ("bSpeedAddRate, -" in it.script or "bSpeedAddRate,-" in it.script):
+ it.fc=0.40
+ else:
+ it.fc=0.30
+ elif 'EQP_SHOES' in it.loc:
+ IT_ARMOR['EQP_SHOES'].append(it)
+ it.fc=0.08
+ elif 'EQP_GARMENT' in it.loc:
+ IT_ARMOR['EQP_GARMENT'].append(it)
+ it.fc=0.07
+ elif 'EQP_HEAD_LOW' in it.loc:
+ IT_ARMOR['EQP_HEAD_LOW'].append(it)
+ it.fc=0.10
+ elif 'EQP_HEAD_TOP' in it.loc:
+ IT_ARMOR['EQP_HEAD_TOP'].append(it)
+ if ("bSpeedAddRate, -" in it.script or "bSpeedAddRate,-" in it.script):
+ it.fc=0.15
+ else:
+ it.fc=0.10
+ elif 'EQP_HAND_L' in it.loc:
+ IT_ARMOR['EQP_HAND_L'].append(it)
+ it.fc=0.25
+ elif '1024' in it.loc:
+ IT_ARMOR['1024'].append(it)
+ it.fc=0.00
+ elif '2048' in it.loc:
+ IT_ARMOR['2048'].append(it)
+ it.fc=0.00
+ elif 'EQP_SHADOW_SHOES' in it.loc:
+ IT_ARMOR['EQP_MOUNT'].append(it)
+ it.fc=0.00
+ elif 'EQP_SHADOW_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it) # Not really
+ it.fc=0.00
+ else:
+ raise Exception("Invalid Loc for ID %s: %s" % (it.id, it.loc))
+ elif "IT_WEAPON" in a:
+ if int(it.matk) > 0:
+ IT_WEAPON["MAGICAL"].append(it)
+ elif int(it.range) > 2:
+ IT_WEAPON["RANGED"].append(it)
+ elif "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)
+
+def newItemDB():
+ print("\nGenerating Item Wiki...")
+ if len(sys.argv) >= 3:
+ src=open(sys.argv[2]+"/db/re/item_db.conf", "r", encoding="utf-8")
+ else:
+ src=open("../../server-data/db/re/item_db.conf", "r", encoding="utf-8")
+
+ lg=False
+ x=It()
+ for a2 in src:
+ a=a2.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:
+ lg=True
+ x.script+="<pre>"
+ elif lg and "\">" in a:
+ lg=False
+ x.script+="</pre>"
+ elif lg:
+ if not "announce" in a and not "debugmes" in a and not "logmes" in a:
+ x.script+=str(a.replace('\t', '').replace('getiteminfo(getequipid(', 'iteminfo((').replace('Flee2','Block'))
+
+ # 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("<h1>Armors</h1>\n\
+<ul>\
+<li><a href=#armors>Armors</a></li>\n\
+<li><a href=#left-accessory>Left Accessory</a></li>\n\
+<li><a href=#right-accessory>Right Accessory</a></li>\n\
+<li><a href=#headgear>Headgear</a></li>\n\
+<li><a href=#chest>Chest</a></li>\n\
+<li><a href=#pants>Pants</a></li>\n\
+<li><a href=#shoes>Shoes</a></li>\n\
+<li><a href=#necklaces>Necklaces</a></li>\n\
+<li><a href=#rings>Rings</a></li>\n\
+<li><a href=#gloves>Gloves</a></li>\n\
+<li><a href=#shields>Shields</a></li>\n\
+</ul>\n\n")
+ wikia.write("<u>Restrictions Reference</u><br/>\n")
+ wikia.write("<ul>\n\
+<li> * - Rare item with drop announce.</li>\n\
+<li> (dp) - This item cannot be dropped.</li>\n\
+<li> (tr) - This item cannot de traded.</li>\n\
+<li> (sl) - This item cannot be sold.</li>\n\
+<li> (gg) - This item cannot go to storage.</li>\n\
+</ul><br/>\n")
+
+ ####################################################################
+ wikia.write("<h2>Armors</h2>\n")
+
+ ArmorWrite("Left Accessory",'EQP_ACC_L')
+ ArmorWrite("Right Accessory",'EQP_ACC_R')
+ ArmorWrite("Headgear",'EQP_HEAD_TOP')
+ ArmorWrite("Chest",'EQP_HEAD_MID')
+ ArmorWrite("Pants",'EQP_HEAD_LOW')
+ ArmorWrite("Shoes",'EQP_SHOES')
+ ArmorWrite("Necklaces",'1024')
+ ArmorWrite("Rings",'2048')
+ ArmorWrite("Gloves",'EQP_GARMENT')
+ ArmorWrite("Shields",'EQP_HAND_L')
+ #for i in sorted(IT_ARMOR['EQP_HEAD_TOP'], key=lambda xcv: int(xcv.lvl)):
+ # # Name | Level | Location | Specification
+ # print("`%s`|%d| :grey_question: | " % (i.name, int(i.lvl)))
+
+ ####################################################################
+ wikia.write("<hr/><h2>Weapons</h2>\n")
+
+ # 1 Hand Items
+ wikia.write("<h3>1H Weapons</h3>\n")
+ ItemWrite(IT_WEAPON['HAND_1'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True)
+
+ # 2 Hand Items
+ wikia.write("<h3>2H Weapons</h3>\n")
+ ItemWrite(IT_WEAPON['HAND_2'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, RANGE=True)
+
+ # Ranged Items
+ wikia.write("<h3>Ranged Weapons</h3>\n")
+ ItemWrite(IT_WEAPON['RANGED'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, LVL=True, RANGE=True)
+
+ # Magic Items
+ wikia.write("<h3>Magical Weapons</h3>\n")
+ ItemWrite(IT_WEAPON['MAGICAL'], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, ATK=True, MATK=True, LVL=True, RANGE=True)
+
+# Write AegisName with restrictions
+def hl(it):
+ buff=""
+ if it.rare:
+ buff+="*"
+ buff+=it.aegis
+ buff+=" "
+ if not it.drop:
+ buff+="<a href='#restrictions-reference'>(dp)</a>"
+ if not it.trade:
+ buff+="<a href='#restrictions-reference'>(tr)</a>"
+ if not it.sell:
+ buff+="<a href='#restrictions-reference'>(sl)</a>"
+ if not it.store:
+ buff+="<a href='#restrictions-reference'>(gg)</a>"
+ 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=False, DEF=False, LVL=False, ATK=False, MATK=False, RANGE=False, HEALING=False, SCRIPT=False, DROPPER=False):
+ global stgen
+ wikia.write("<table border=1>\n")
+ wikia.write("<tr>")
+ if ID:
+ wikia.write("<th>ID</th>")
+ if AEGIS:
+ wikia.write("<th>Aegis</th>")
+ if NAME:
+ wikia.write("<th>Name</th>")
+ if PRICE:
+ wikia.write("<th>Price</th>")
+ if WEIGHT:
+ wikia.write("<th>Weight</th>")
+ if DEF:
+ wikia.write("<th>Def</th>")
+ if stgen:
+ wikia.write("<th>Adj.Def</th>")
+ if LVL:
+ wikia.write("<th>Lvl</th>")
+ if ATK:
+ wikia.write("<th>Atk</th>")
+ if MATK:
+ wikia.write("<th>Matk</th>")
+ if ATK and MATK:
+ wikia.write("<th>+</th>")
+ if ATK or MATK:
+ if stgen:
+ wikia.write("<th>Adj. Atk.</th>")
+ if RANGE:
+ wikia.write("<th>Range</th>")
+ if HEALING:
+ wikia.write("<th>Min</th>")
+ wikia.write("<th>Max</th>")
+ wikia.write("<th>Delay</th>")
+ if SCRIPT:
+ wikia.write("<th>Script</th>")
+ if DROPPER:
+ wikia.write("<th>Mobs</th>")
+
+ wikia.write("</tr>\n")
+
+ try:
+ if (sys.argv[1] in ["level", "info"]):
+ sort=sorted(tbl, key=lambda xcv: int(xcv.lvl))
+ elif (sys.argv[1] == "none"):
+ sort=tbl
+ else:
+ print("Syntax: ./sedesign.py [info|level|none]")
+ sort=tbl
+
+ if (sys.argv[1] == "info"):
+ stgen=False
+ except:
+ sort=tbl
+
+ for i in sort:
+ wikia.write('<tr>')
+
+ if ID:
+ wikia.write("<td><a name=\"%s\"></a>%s</td>" % (i.id,i.id))
+ if AEGIS:
+ wikia.write("<td>%s</td>" % hl(i))
+ if NAME:
+ wikia.write("<td>%s</td>" % i.name)
+ if PRICE:
+ wikia.write("<td>%s GP</td>" % i.price)
+ if WEIGHT:
+ wikia.write("<td>%s g</td>" % i.weight)
+ if DEF:
+ wikia.write("<td>Def: %s</td>" % i.defs)
+ if stgen:
+ lv=int(i.lvl)
+
+ bb=(lv**1.255)*2.5*i.fc
+ hc=bb*350.0/810.0 # Hercules value: Capped at 350
+
+ # Magic penalty
+ if ("bMatk," in i.script):
+ hc=hc/2.0
+
+ # Precise rounding
+ if (hc % 1 >= 0.5):
+ hc+=1
+ wikia.write("<td>Adj. Df: %d</td>" % int(hc))
+ if LVL:
+ wikia.write("<td>Lv: %s</td>" % i.lvl)
+ if ATK:
+ wikia.write("<td>Atk: %s</td>" % i.atk)
+ if MATK:
+ wikia.write("<td>%s</td>" % i.matk)
+ if ATK and MATK:
+ try:
+ tmpatmat=i.atk.replace("Atk: ", "")
+ #print("`%s`+`%s`=?" % (tmpatmat, i.matk))
+ wikia.write("<td><i>%s</i></td>" % str(int(i.matk)+int(tmpatmat)))
+ except:
+ wikia.write("<td><i>?</i></td>")
+ if ATK or MATK:
+ if stgen:
+ lv=int(i.lvl)
+ at=int(i.atk)
+ fc=7.5
+ ## Two hand swords are stronger
+ if i in IT_WEAPON['HAND_2']:
+ fc+=0.62
+ ## Calculate guns
+ if int(i.id) == 6010 or int(i.id) == 6050:
+ lv+=20
+ if int(i.id) == 6020:
+ lv=0
+ if int(i.id) == 6040:
+ fc/=2.5
+
+ ## Progression
+ if int(i.matk) <= 0 or int(at) > 0:
+ fc-=1.0
+ fc+=((100-lv)/100.0)
+
+ ## Even slower progression (note bows doesn't gets the 0.8 2hand bonus)
+ if not i in IT_WEAPON['RANGED']:
+ fc-=(lv/15.0)/10.0
+
+ ## After level 50 weapons progression is slowed down
+ if lv > 45:
+ fc-=(lv-45)/90.0
+
+ ## This is because mob HP scaling is buggy
+ if lv > 45 and not i in IT_WEAPON['RANGED']:
+ fc-=(lv-45)/100.0
+
+ ## Magic Weapons adjustment
+ if int(i.matk) > 0:
+ fc+=0.0
+
+ ## Physical and Magic Weapons adjustment
+ if int(i.matk) > 0 and int(at) > 0:
+ fc=7.0+((100-lv)/100.0)
+
+ ## HAT is for craft or rare items.
+ lat=lv*fc
+ hat=lv*(fc+max(0.01, 1.0-(lv/100.0)))
+
+ # Edge Cases
+ ## Don't recalculate noob weapon
+ if lv <= 20:
+ lat=hat=at
+
+ wikia.write("<th>%d ~ %d <i>(%.2f)</i></th>" % (lat, hat, fc))
+ if RANGE:
+ wikia.write("<td>%s</td>" % i.range)
+ if HEALING:
+ wikia.write("<td>%s</td>" % i.minheal)
+ wikia.write("<td>%s</td>" % i.maxheal)
+ wikia.write("<td>%s s</td>" % i.delheal)
+ if SCRIPT:
+ wikia.write("<td>%s</td>" % i.script)
+ if DROPPER:
+ wikia.write("<td>-</td>")
+
+ wikia.write("</tr>")
+
+ wikia.write("</table><br/>\n")
+ wikia.write("\n<br/><a href=#items>(↑) Return to top</a><Br/><br/>\n\n")
+
+def ArmorWrite(name,scope):
+ wikia.write("<h3>"+name+"</h3>\n")
+ ItemWrite(IT_ARMOR[scope], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, DEF=True, LVL=True, SCRIPT=True)
+
+
+
+
+
+
+
+
+
+
+
+showHeader()
+
+wikia.write("<html><head>")
+wikia.write('<title>SeDesign</title><meta charset=utf8 />\n')
+wikia.write("<body>")
+newItemDB()
+
+# Ending
+wikia.write("<hr/>")
+wikia.write("Run at: " + datetime.datetime.now().isoformat())
+wikia.write("</body></html>")
+wikia.close()
+
+# Print for reference the landmarks
+i=0
+while i < 100:
+ i+=10
+ bb=(i**1.255)*2.5
+ hc=bb*350.0/810.0 # Hercules value: Capped at 350
+ df=(100.0 - hc / (hc + 400.0) * 90.0) / 100.0 * 100
+ print("%d Level %d Defense (%.2f%% DMG)" % (i, hc, df) );
+
+showFooter()
+exit(0)
diff --git a/wiki/tmp-arm b/wiki/tmp-arm
new file mode 100644
index 0000000..53e733e
--- /dev/null
+++ b/wiki/tmp-arm
@@ -0,0 +1,247 @@
+"791": 0,
+"1151": 4,
+"1152": 8,
+"1153": 5,
+"1155": 0,
+"1156": 5,
+"1159": 2,
+"1161": 2,
+"1162": 2,
+"1163": 2,
+"1164": 8,
+"1165": 3,
+"1169": 5,
+"1170": 5,
+"1171": 5,
+"1178": 10,
+"3200": 2,
+"3201": 2,
+"3202": 0,
+"3203": 0,
+"3204": 1,
+"3205": 4,
+"3206": 0,
+"3207": 0,
+"3210": 1,
+"2900": 1,
+"2901": 0,
+"2902": 1,
+"2903": 3,
+"2904": 3,
+"2905": 3,
+"2906": 9,
+"2907": 9,
+"2908": 3,
+"2909": 5,
+"2910": 3,
+"2911": 3,
+"2913": 3,
+"2914": 4,
+"2915": 5,
+"2916": 4,
+"2917": 2,
+"2918": 2,
+"2919": 6,
+"2920": 6,
+"2921": 4,
+"2923": 1,
+"2924": 0,
+"2926": 0,
+"2927": 9,
+"2928": 3,
+"2929": 9,
+"2930": 1,
+"2932": 6,
+"2934": 0,
+"2935": 0,
+"2936": 1,
+"2937": 4,
+"2938": 0,
+"2939": 0,
+"2940": 4,
+"2941": 7,
+"2942": 8,
+"2943": 6,
+"2944": 4,
+"2945": 3,
+"2946": 1,
+"2983": 1,
+"2985": 2,
+"2986": 2,
+"2987": 3,
+"2989": 0,
+"2990": 0,
+"2991": 2,
+"2992": 1,
+"2993": 2,
+"2994": 2,
+"2995": 3,
+"2996": 15,
+"2997": 3,
+"2998": 4,
+"2999": 5,
+"3000": 2,
+"3001": 5,
+"3002": 3,
+"3003": 5,
+"3004": 8,
+"3005": 8,
+"3006": 1,
+"3007": 6,
+"3008": 7,
+"3010": 3,
+"3011": 2,
+"3012": 5,
+"3013": 3,
+"3014": 1,
+"3015": 2,
+"3017": 2,
+"3018": 6,
+"3019": 4,
+"3020": 7,
+"3021": 0,
+"3022": 1,
+"3023": 3,
+"3024": 3,
+"3028": 3,
+"3029": 3,
+"3030": 10,
+"1300": 0,
+"1301": 4,
+"1302": 0,
+"1303": 2,
+"1304": 3,
+"1305": 5,
+"1306": 7,
+"1307": 7,
+"1308": 5,
+"1309": 0,
+"1310": 4,
+"1311": 8,
+"1312": 6,
+"1313": 0,
+"1314": 2,
+"1315": 1,
+"1316": 2,
+"1317": 5,
+"1318": 4,
+"1319": 1,
+"1320": 3,
+"1321": 0,
+"1323": 8,
+"1324": 3,
+"1325": 0,
+"1326": 0,
+"1327": 0,
+"1328": 6,
+"1329": 7,
+"1330": 0,
+"1331": 10,
+"1332": 1,
+"1333": 8,
+"1334": 3,
+"1335": 0,
+"1336": 6,
+"1337": 7,
+"1338": 2,
+"1339": 5,
+"2200": 0,
+"2201": 3,
+"2202": 8,
+"2203": 7,
+"2204": 5,
+"2205": 1,
+"2206": 1,
+"2207": 3,
+"2208": 5,
+"2209": 1,
+"2210": 8,
+"2211": 6,
+"2212": 2,
+"2213": 4,
+"2214": 4,
+"2215": 7,
+"2216": 10,
+"2217": 0,
+"2218": 2,
+"2219": 2,
+"2220": 3,
+"1800": 1,
+"1801": 8,
+"1802": 5,
+"1803": 0,
+"1804": 3,
+"1805": 2,
+"1806": 2,
+"1807": 3,
+"1808": 4,
+"1809": 5,
+"1810": 6,
+"1811": 9,
+"1812": 7,
+"1813": 8,
+"1814": 7,
+"1815": 10,
+"1816": 0,
+"1817": 1,
+"1818": 2,
+"1819": 3,
+"1000": 4,
+"1001": 5,
+"1002": 4,
+"1003": 4,
+"1004": 4,
+"1005": 6,
+"1007": 4,
+"1008": 6,
+"1009": 8,
+"1010": 7,
+"1012": 2,
+"1013": 2,
+"1014": 4,
+"1015": 5,
+"1016": 5,
+"1017": 5,
+"1019": 5,
+"2500": 0,
+"2501": 4,
+"2510": 0,
+"2511": 0,
+"2512": 2,
+"2000": 3,
+"2001": 5,
+"2002": 7,
+"2003": 9,
+"2004": 0,
+"2005": 1,
+"2006": 2,
+"2007": 2,
+"2008": 4,
+"2009": 5,
+"2010": 6,
+"2011": 9,
+"2012": 9,
+"2013": 7,
+"2014": 7,
+"2015": 8,
+"2016": 0,
+"2700": 0,
+"2701": 1,
+"2702": 3,
+"2703": 7,
+"2704": 5,
+"2705": 10,
+"2706": 6,
+"2707": 0,
+"2708": 6,
+"2709": 4,
+"2710": 2,
+"2711": 8,
+"2712": 9,
+"2713": 1,
+"2714": 2,
+"2715": 2,
+"2716": 3,
+"2717": 3,
+"2718": 4,
+"2719": 4,
diff --git a/wiki/tmp-dec b/wiki/tmp-dec
new file mode 100644
index 0000000..8337ac2
--- /dev/null
+++ b/wiki/tmp-dec
@@ -0,0 +1,8 @@
+"3208": BurglarMask,
+"3209": BanditMask,
+"3211": AutumnMask,
+"2922": YetiMask,
+"2925": OperaMask,
+"2931": SkullMask,
+"3009": FafiMask,
+"3016": TerraniteMask,
diff --git a/wiki/tmp-etc b/wiki/tmp-etc
new file mode 100644
index 0000000..5e1d879
--- /dev/null
+++ b/wiki/tmp-etc
@@ -0,0 +1,25 @@
+Armor:
+ "1150": FourLeafClover,
+ "1154": Doll,
+ "1157": LeatherBall,
+ "1158": ZarkorScroll,
+ "1160": PlushMouboo,
+ "1166": Blanket,
+ "1167": EarthScroll,
+ "1168": CursedSkull,
+ "1172": LeatherQuiver,
+ "1173": IronQuiver,
+ "1174": BronzeQuiver,
+ "1175": PlatinumQuiver,
+ "1176": DragonStar,
+ "1177": MichelSoul,
+ "1179": RubberDucky,
+ "1006": LifestonePendant,
+ "1011": 1,
+ "1020": 2,
+ "1021": 1,
+ "2508": 2,
+ "2509": 3,
+ "2720": 0
+
+
diff --git a/wiki/tmp-wpn b/wiki/tmp-wpn
new file mode 100644
index 0000000..f86e7b4
--- /dev/null
+++ b/wiki/tmp-wpn
@@ -0,0 +1,59 @@
+"3500": 0,
+"3501": 2,
+"3502": 3,
+"3503": 2,
+"3504": 4,
+"3505": 1,
+"3506": 0,
+"3507": 1,
+"3508": 7,
+"3509": 9,
+"3510": 0,
+"3511": 10,
+"3512": 3,
+"3513": 8,
+"3514": 3,
+"3515": 6,
+"3516": 8,
+"3518": 2,
+"3519": 6,
+"3520": 2,
+"3527": 5,
+"3528": 3,
+"3529": 1,
+"3531": 6,
+"3536": 6,
+"3538": 6,
+"3539": 6,
+"3600": 99,
+"6040": 6,
+"3517": 4,
+"3521": 2,
+"3522": 6,
+"3523": 4,
+"3524": 5,
+"3525": 6,
+"3526": 7,
+"3530": 7,
+"3532": 9,
+"3533": 7,
+"3534": 8,
+"3537": 6,
+"3601": 99,
+"3602": 99,
+"3603": 99,
+"6000": 0,
+"6001": 1,
+"6002": 2,
+"6003": 4,
+"6004": 6,
+"6005": 8,
+"6006": 10,
+"6010": 6,
+"6030": 6,
+"6050": 7,
+"7020": 6,
+"7400": 4,
+"7401": 5,
+"7421": 10,
+"3604": 99,
diff --git a/wiki/webwikigen.py b/wiki/webwikigen.py
new file mode 100755
index 0000000..51a8d7a
--- /dev/null
+++ b/wiki/webwikigen.py
@@ -0,0 +1,758 @@
+#! /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
+
+wikia=open("Items.md", "w")
+wikib=open("Monsters.md", "w")
+wikic=open("../../client-data/x.diff", "w")
+
+# 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_HEAD_TOP':[], # HATS/HELMETS
+ 'EQP_HAND_L': []} # SHIELDS
+
+Mobs1=[]
+Mobs2=[]
+Mobs3=[]
+Mobs4=[]
+Mobs5=[]
+MobsA=[]
+
+SysDrops=[]
+
+
+def printSeparator():
+ print("--------------------------------------------------------------------------------")
+
+def showHeader():
+ print("TMW2 Wiki Generator")
+ ##print "Evol client data validator."
+ print("Run at: " + datetime.datetime.now().isoformat())
+ ##print "https://gitlab.com/evol/evol-tools/blob/master/testxml/testxml.py"
+ printSeparator()
+
+def showFooter():
+ #pass
+ #printSeparator()
+ print("Done.")
+
+
+
+
+
+def fwalk(wmask):
+ if wmask == 'WATER':
+ return '<font color=#00f>%s</font>' % (wmask)
+ elif wmask == 'AIR':
+ return '<font color=#093>%s</font>' % (wmask)
+ elif wmask == 'WALL':
+ return '<font color=#f00>%s</font>' % (wmask)
+ elif wmask == 'NORMAL' or wmask == 'DEFAULT':
+ return '<font color=#111>%s</font>' % (wmask)
+ else:
+ print("Invalid walk mask: "+wmask)
+ exit(1)
+
+def WhatRace(rac):
+ rc=rac.race
+ if rc == 0:
+ return "Formless"
+ elif rc == 1:
+ return "Undead"
+ elif rc == 2:
+ return "Brute"
+ elif rc == 3:
+ return "Plant"
+ elif rc == 4:
+ return "Insect"
+ elif rc == 5:
+ return "Fish"
+ elif rc == 6:
+ return "-"
+ elif rc == 7:
+ return "SemiHuman"
+ elif rc == 8:
+ return "Angel"
+ elif rc == 9:
+ return "Dragon"
+ elif rc == 10:
+ return "Player"
+ elif rc == 11:
+ return "Boss"
+ elif rc == 12:
+ return "NonBoss"
+ elif rc == 14:
+ return "NonSemiHuman"
+ elif rc == 15:
+ return "NonPlayer"
+ elif rc == 16:
+ return "SemiPlayer"
+ elif rc == 17:
+ return "NonSemiPlayer"
+ else:
+ print("ERROR, INVALID RACE ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+
+def WhatElem(rac):
+ rc=rac.elem
+ tl="ERROR"
+ cl="#F00"
+ if rc == 0:
+ tl,cl="Neutral","#000"
+ elif rc == 1:
+ tl,cl="Water","#00F"
+ elif rc == 2:
+ tl,cl="Earth","#7A0"
+ elif rc == 3:
+ tl,cl="Fire","#F00"
+ elif rc == 4:
+ tl,cl="Wind","#093"
+ elif rc == 5:
+ tl,cl="Poison","#040"
+ elif rc == 6:
+ tl,cl="Holy","#afa"
+ elif rc == 7:
+ tl,cl="Dark","#908"
+ elif rc == 8:
+ tl,cl="Ghost","#404"
+ elif rc == 9:
+ tl,cl="Undead","#440"
+ else:
+ print("ERROR, INVALID ELEM ID: %d (ID: %s)" % (rc, rac.id))
+ exit(1)
+ return "<font color=%s>%s</font>" % (cl, tl)
+
+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"
+
+ # Defensive
+ self.mobpt="0" # Mob Points “Level”
+ self.hp="0"
+ self.xp="0"
+ self.jp="0"
+ self.st=""
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="0"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+ # Elegen Info
+ self.race=-1
+ self.elem=-1
+ self.elel=-1
+ self.walk="NORMAL"
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.mobpt)
+ except:
+ maab=9901
+
+ if maab <= 20:
+ Mobs1.append(ab)
+ elif maab <= 40:
+ Mobs2.append(ab)
+ elif maab <= 60:
+ Mobs3.append(ab)
+ elif maab <= 80:
+ Mobs4.append(ab)
+ elif maab <= 100:
+ Mobs5.append(ab)
+ else:
+ MobsA.append(ab)
+
+def testMobs():
+ print("Generating Mob Wiki...")
+ src=open("../../server-data/db/re/mob_db.conf", "r")
+ wikib.write("<h1 id='monster-database'>Monster Database</h1>\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()
+
+ if " Id:" in a:
+ x.id=stp(a)
+ if x.id == "ID":
+ continue
+
+ if " 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 " MoveSpeed:" in a:
+ x.move=stp(a)
+ elif " ViewRange:" in a:
+ x.view=stp(a)
+ elif " AttackDelay:" in a:
+ x.delay=stp(a)
+ elif " Looter: true" in a:
+ x.st+="<font color=#790>Lot</font>,"
+ elif " Assist: true" in a:
+ x.st+="<font color=#0a0>Ass</font>,"
+ elif " Aggressive: true" in a:
+ x.st+="<font color=#f00>Agr</font>,"
+ elif " WalkMask:" in a:
+ x.walk=stp(a)
+ elif " Element:" in a:
+ tmp=stp(a)
+ tmp2=tmp.split(',')
+ try:
+ x.elem=int(tmp2[0])
+ x.elel=int(tmp2[1])
+ except:
+ print("Invalid Element for mob %s: %s" % (x.id, tmp))
+ exit(1)
+ elif " Race:" in a:
+ try:
+ x.race=int(stp(a))
+ except:
+ print("Invalid Race for mob %s: %s" % (x.id, a))
+ exit(1)
+ 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('Element: ','').replace('Race: ','').replace('AttackDelay: ', '').replace('WalkMask: ','').replace('MoveSpeed: ', '').replace('AttackRange: ', '').replace('ViewRange: ','').replace('Attack: ','').replace('ViewRange: ','').replace('Hp: ','').replace('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.').replace('(','').replace(')','').replace('WALK_','')
+
+
+def MonsterWrite(tbl):
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ wikib.write("<tr><th>ID</th><th>Name</th><th>Mob Info</th><th>Elegen</th><th>Misc Info</th><th>Rewards</th><th>Drops</th></tr>\n")
+ for i in tbl:
+ if i.id == 'ID':
+ continue
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>"+
+ mb_core(i) +"</td><td>"+
+ mb_eleg(i) +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("\n<a href=#monster-database>(↑) Return to top</a>\n\n")
+
+def writeMob():
+ wikib.write("\
++ [Level 0-20](#starter)\n\
++ [Level 21-40](#apprentice)\n\
++ [Level 41-60](#intermediary)\n\
++ [Level 61-80](#advanced)\n\
++ [Level 81-100](#expert)\n\
++ [Level 100+](#out-of-scope)\n\n\
+ ")
+
+ wikib.write("## Starter\n\n")
+ MonsterWrite(Mobs1)
+
+ wikib.write("## Apprentice\n\n")
+ MonsterWrite(Mobs2)
+
+ wikib.write("## Intermediary\n\n")
+ MonsterWrite(Mobs3)
+
+ wikib.write("## Advanced\n\n")
+ MonsterWrite(Mobs4)
+
+ wikib.write("## Expert\n\n")
+ MonsterWrite(Mobs5)
+
+ wikib.write("## Out Of Scope\n\n")
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+def mb_core(mb):
+ buff=""
+ buff+="Lvl: %s<br/>\n" % (mb.mobpt)
+ buff+="HP: <b><font color=#0a0>%s</font></b><br/>\n" % (mb.hp)
+ buff+="ATK: <b><font color=#a00>%s</font></b><br/>\n" % (mb.atk)
+ if mb.st != "":
+ buff+="Modes: <i>%s</i>" % (mb.st)
+ return buff
+
+def mb_eleg(mb):
+ buff=""
+ buff+="Race: %s<br/>\n" % (WhatRace(mb))
+ buff+="Walk: %s<br/>\n" % (fwalk(mb.walk))
+ buff+="Element: %s<br/>\n" % (WhatElem(mb))
+ return buff
+
+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+="AtkDelay: %s ms\n" % (mb.delay)
+ buff+="Move speed: %s ms\n" % (mb.move)
+ buff+="Element Level: %d\n" % (mb.elel)
+ return buff
+
+def mb_rdrw(mb):
+ buff=""
+ try:
+ buff+="MobPoints: %d\n" % (int(mb.mobpt)*11/10)
+ except:
+ pass
+ buff+="%s\n" % (mb.xp)
+ buff+="%s\n" % (mb.jp)
+ return buff
+
+def mb_rddrop(mb):
+ buff=""
+ for ax in mb.drops:
+ # Ignore disabled drops
+ if ax[0].startswith("//"):
+ continue
+
+ # Write drop
+ try:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.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_ACC_R' in it.loc:
+ IT_ARMOR['EQP_ACC_R'].append(it)
+ else:
+ raise Exception("Invalid Loc for ID %s: %s" % (it.id, it.loc))
+
+def newItemDB():
+ print("Generating Item Wiki...")
+ src=open("../../server-data/db/re/item_db.conf", "r")
+
+ x=It()
+ for a in src:
+ 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\
++ [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)
+
+ ####################################################################
+ 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')
+ ArmorWrite("Right Accessory",'EQP_ACC_R')
+ ArmorWrite("Headgear",'EQP_HEAD_TOP')
+ ArmorWrite("Chest",'EQP_HEAD_MID')
+ ArmorWrite("Pants",'EQP_HEAD_LOW')
+ ArmorWrite("Shoes",'EQP_SHOES')
+ ArmorWrite("Necklaces",'1024')
+ ArmorWrite("Rings",'2048')
+ 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+="<a href='#restrictions-reference'>(dp)</a>"
+ if not it.trade:
+ buff+="<a href='#restrictions-reference'>(tr)</a>"
+ if not it.sell:
+ buff+="<a href='#restrictions-reference'>(sl)</a>"
+ if not it.store:
+ buff+="<a href='#restrictions-reference'>(gg)</a>"
+ 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=False, DEF=False, LVL=False, ATK=False, RANGE=False, HEALING=False, SCRIPT=False, DROPPER=False):
+ wikia.write("<table border=1>\n")
+ wikia.write("<tr>")
+ if ID:
+ wikia.write("<th>ID</th>")
+ if AEGIS:
+ wikia.write("<th>Aegis</th>")
+ if NAME:
+ wikia.write("<th>Name</th>")
+ if PRICE:
+ wikia.write("<th>Price</th>")
+ if WEIGHT:
+ wikia.write("<th>Weight</th>")
+ if DEF:
+ wikia.write("<th>Def</th>")
+ if LVL:
+ wikia.write("<th>Lvl</th>")
+ if ATK:
+ wikia.write("<th>Atk</th>")
+ wikia.write("<th>Matk</th>")
+ if RANGE:
+ wikia.write("<th>Range</th>")
+ if HEALING:
+ wikia.write("<th>Min</th>")
+ wikia.write("<th>Max</th>")
+ wikia.write("<th>Delay</th>")
+ if SCRIPT:
+ wikia.write("<th>Script</th>")
+ if DROPPER:
+ wikia.write("<th>Mobs</th>")
+
+ wikia.write("</tr>\n")
+
+ for i in tbl:
+ wikia.write('<tr>')
+
+ if ID:
+ wikia.write("<td><a name=\"%s\"></a>%s</td>" % (i.id,i.id))
+ if AEGIS:
+ wikia.write("<td>%s</td>" % hl(i))
+ if NAME:
+ wikia.write("<td>%s</td>" % i.name)
+ if PRICE:
+ wikia.write("<td>%s GP</td>" % i.price)
+ if WEIGHT:
+ wikia.write("<td>%s g</td>" % i.weight)
+ if DEF:
+ wikia.write("<td>Def: %s</td>" % i.defs)
+ if LVL:
+ wikia.write("<td>Lv: %s</td>" % i.lvl)
+ if ATK:
+ wikia.write("<td>Atk: %s</td>" % i.atk)
+ wikia.write("<td>%s</td>" % i.matk)
+ if RANGE:
+ wikia.write("<td>%s</td>" % i.range)
+ if HEALING:
+ wikia.write("<td>%s</td>" % i.minheal)
+ wikia.write("<td>%s</td>" % i.maxheal)
+ wikia.write("<td>%s s</td>" % i.delheal)
+ if SCRIPT:
+ wikia.write("<td>%s</td>" % 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: 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("<td>%s</td>" % mbdt("monsters", tmp_droppers))
+ else:
+ wikia.write("<td>-</td>")
+
+ wikia.write("</tr>")
+
+ wikia.write("</table>\n")
+ wikia.write("\n[(↑) Return to top](#items)\n\n")
+
+def ArmorWrite(name,scope):
+ wikia.write("## "+name+"\n")
+ ItemWrite(IT_ARMOR[scope], ID=True, AEGIS=True, PRICE=True, WEIGHT=True, DEF=True, LVL=True, SCRIPT=True)
+
+showHeader()
+
+testMobs()
+newItemDB()
+
+wikia.close()
+wikib.close()
+wikic.close()
+#print(str(SysDrops))
+
+showFooter()
+exit(0)
diff --git a/wiki/wikigen.py b/wiki/wikigen.py
new file mode 100755
index 0000000..c5ce71e
--- /dev/null
+++ b/wiki/wikigen.py
@@ -0,0 +1,898 @@
+#! /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 [<path_to_serverdata> <path_to_clientdata>]")
+ ##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=""
+
+ # Offensive
+ self.atk="[0, 0]"
+ self.range="0"
+ self.move="0"
+ self.delay="0"
+ self.drops=[]
+
+def MobAlloc(ab):
+ try:
+ maab=int(ab.mobpt)
+ except:
+ maab=9901
+
+ if maab <= 20:
+ Mobs1.append(ab)
+ elif maab <= 40:
+ Mobs2.append(ab)
+ elif maab <= 60:
+ Mobs3.append(ab)
+ elif maab <= 80:
+ Mobs4.append(ab)
+ elif maab <= 100:
+ Mobs5.append(ab)
+ elif maab <= 150:
+ 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()
+
+ 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 " 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 " Looter: true" in a:
+ x.st+="Lot,"
+ elif " Assist: true" in a:
+ x.st+="Ass,"
+ elif " Aggressive: true" in a:
+ x.st+="Agr,"
+ 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('Id: ','').replace('Lv: ','').replace('view range','').replace('attack range','').replace('move speed','').replace('health','').replace('(int','').replace('attack delay','atk.')
+
+
+def MonsterWrite(tbl):
+ # TODO: Check _mobs files to determine the usual monster density (a misc info to aid adding proper drop specs)
+ wikib.write("<table border=1>\n")
+ wikib.write("<tr><th>ID</th><th>Name</th><th>HP</th><th>Atk</th><th>Delay</th><th>Modes</th><th>Misc Info</th><th>Rewards</th><th>Drops</th></tr>\n")
+ for i in tbl:
+ if not i.chch:
+ wikid.write("%s:%s\n" % (i.id, i.name))
+ if i.boss:
+ i.name="<b>"+i.name+"</b>"
+ wikib.write('<tr><td><a name="' + i.id + '"></a>' +
+ i.id +"</td><td>"+
+ i.name +"</td><td>HP: "+
+ i.hp +"</td><td>Atk: "+
+ i.atk +"</td><td>"+
+ i.delay +" ms</td><td>"+
+ i.st +"</td><td>"+
+ mbdt('misc', mb_rdmisc(i)) +"</td><td>"+
+ mbdt('Exp\'s', mb_rdrw(i)) +"</td><td>"+
+ mbdt('drops', mb_rddrop(i)) +"</td></tr>\n"
+ )
+ wikib.write("</table>\n")
+ wikib.write("\n[(↑) Return to top](#monster-database)\n\n")
+
+def writeMob():
+ wikib.write("\
++ [Level 0-20](#starter)\n\
++ [Level 21-40](#apprentice)\n\
++ [Level 41-60](#intermediary)\n\
++ [Level 61-80](#advanced)\n\
++ [Level 81-100](#expert)\n\
++ [Level 101-150](#master)\n\
++ [Level 100+](#out-of-scope)\n\n\
+ ")
+
+ wikib.write("## Starter\n\n")
+ MonsterWrite(Mobs1)
+
+ wikib.write("## Apprentice\n\n")
+ MonsterWrite(Mobs2)
+
+ wikib.write("## Intermediary\n\n")
+ MonsterWrite(Mobs3)
+
+ wikib.write("## Advanced\n\n")
+ MonsterWrite(Mobs4)
+
+ wikib.write("## Expert\n\n")
+ MonsterWrite(Mobs5)
+
+ wikib.write("## Master\n\n")
+ MonsterWrite(Mobs6)
+
+ wikib.write("## Out Of Scope\n\n")
+ MonsterWrite(MobsA)
+
+
+def mbdt(summary, content):
+ return "<details>\
+<summary>"+summary+"</summary>\
+<pre>"+content+"</pre></details>"
+
+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:
+ buff+=ax[0]+': ' + str(int(ax[1])/100.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"
+ self.typheal="0"
+ self.rarheal="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:
+ 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)
+ elif "@type" in a:
+ x.typheal=sti(a)
+ try:
+ x.minheal=str(int(x.rarheal) * (int(x.typheal)*1 + 1)) + " %"
+ x.maxheal=str(int(x.rarheal) * (int(x.typheal)*2 + 1)) + " %"
+ if (x.delheal == "0"):
+ x.delheal=int(x.typheal)*2 + 1
+ except:
+ x.delheal="ERROR"
+ pass
+ elif "@rarity" in a:
+ x.rarheal=sti(a)
+ x.minheal=str(int(x.rarheal) * (int(x.typheal)*1 + 1)) + " %"
+ x.maxheal=str(int(x.rarheal) * (int(x.typheal)*2 + 1)) + " %"
+
+ # 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('@type=','').replace('@rarity=','').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+="<a href='#restrictions-reference'>(dp)</a>"
+ if not it.trade:
+ buff+="<a href='#restrictions-reference'>(tr)</a>"
+ if not it.sell:
+ buff+="<a href='#restrictions-reference'>(sl)</a>"
+ if not it.store:
+ buff+="<a href='#restrictions-reference'>(gg)</a>"
+ 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=False, DEF=False, LVL=False, ATK=False, RANGE=False, HEALING=False, SCRIPT=False, DROPPER=False):
+ wikia.write("<table border=1>\n")
+ wikia.write("<tr>")
+ if ID:
+ wikia.write("<th>ID</th>")
+ if AEGIS:
+ wikia.write("<th>Aegis</th>")
+ if NAME:
+ wikia.write("<th>Name</th>")
+ if PRICE:
+ wikia.write("<th>Price</th>")
+ if WEIGHT:
+ wikia.write("<th>Weight</th>")
+ if DEF:
+ wikia.write("<th>Def</th>")
+ if LVL:
+ wikia.write("<th>Lvl</th>")
+ if ATK:
+ wikia.write("<th>Atk</th>")
+ wikia.write("<th>Matk</th>")
+ if RANGE:
+ wikia.write("<th>Range</th>")
+ if HEALING:
+ wikia.write("<th>Min</th>")
+ wikia.write("<th>Max</th>")
+ wikia.write("<th>Delay</th>")
+ if SCRIPT:
+ wikia.write("<th>Script</th>")
+ if DROPPER:
+ wikia.write("<th>Mobs</th>")
+
+ wikia.write("</tr>\n")
+
+ for i in tbl:
+ """
+ # To sort by weight, for example, uncomment this block.
+ try:
+ zt=int(i.weight)
+ except:
+ tbl.remove(i)
+
+ for i in sorted(tbl, key=lambda xcv: int(xcv.weight), reverse=True):
+ """
+
+ wikia.write('<tr>')
+
+ if ID:
+ wikia.write("<td><a name=\"%s\"></a>%s</td>" % (i.id,i.id))
+ if AEGIS:
+ wikia.write("<td>%s</td>" % hl(i))
+ if NAME:
+ wikia.write("<td>%s</td>" % i.name)
+ if PRICE:
+ wikia.write("<td>%s GP</td>" % i.price)
+ if WEIGHT:
+ wikia.write("<td>%s g</td>" % i.weight)
+ if DEF:
+ wikia.write("<td>Def: %s</td>" % i.defs)
+ if LVL:
+ wikia.write("<td>Lv: %s</td>" % i.lvl)
+ if ATK:
+ wikia.write("<td>Atk: %s</td>" % i.atk)
+ wikia.write("<td>%s</td>" % i.matk)
+ if RANGE:
+ wikia.write("<td>%s</td>" % i.range)
+ if HEALING:
+ wikia.write("<td>%s</td>" % i.minheal)
+ wikia.write("<td>%s</td>" % i.maxheal)
+ wikia.write("<td>%s s</td>" % i.delheal)
+ if SCRIPT:
+ wikia.write("<td>%s</td>" % 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("<td>%s</td>" % mbdt("monsters", tmp_droppers))
+ else:
+ wikia.write("<td>-</td>")
+
+ wikia.write("</tr>")
+
+ wikia.write("</table>\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=[]
+ self.level=0
+
+class QuestEntry:
+ def __init__(self):
+ # Basic
+ self.complete=False
+ self.entry=[] # collection of <text>
+ self.giver=""
+ self.reward=""
+ self.loc=""
+
+def sortlv(val):
+ return val[1]
+
+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 and '-->' in e:
+ continue
+ elif '<!--' in e:
+ ig=True
+ elif '-->' in e:
+ ig=False
+ if '<effect' in e:
+ continue
+
+ if ig:
+ continue
+
+ # Handle Quest Headers
+ if '</var' in e:
+ if (nw):
+ nw=False
+ else:
+ qlog.append(q)
+ elif '<var' in e:
+ if 'nowiki' in e:
+ nw=True
+ g=qnt(e)
+ try:
+ q=Quest(int( g.replace('varid=','') ))
+ except:
+ print("Invalid quest: %s" % g)
+ exit(1)
+
+ # Handle quest entries
+ if '</quest>' in e:
+ q.ent.append(qe)
+ elif '<quest ' in e:
+ qe=QuestEntry()
+ l=e.split('"')
+ rc=[False, ""]
+ for arg in l:
+ if not rc[0]:
+ if "name" in arg:
+ rc=[True, 'name']
+ elif "group" in arg:
+ rc=[True, 'group']
+ elif "complete" in arg:
+ rc=[True, arg.replace('=','').replace(' ','')]
+ else:
+ if rc[1] == "name":
+ q.name=arg
+ elif rc[1] == "group":
+ q.group=arg
+ elif rc[1] == "complete":
+ qe.complete=True
+ elif rc[1] == "incomplete":
+ qe.complete=False
+ else:
+ print("Invalid <quest> tag: %s (arg was %s) (line was %s)" % (e, rc[1], l))
+ exit(1)
+ rc=[False, ""]
+
+ # Fill stuff in Quest Entry
+ if '<text' in e:
+ a=qnt2(e)
+ qe.entry.append( a.replace('<text>','').replace('</text>','').replace('<text ','<').replace("@@1", "text").replace("@@", "").strip() )
+ elif '<wiki' in e:
+ a=qnt2(e)
+ qe.entry.append( a.replace('<wiki>','').replace('</wiki>','').replace('<wiki ','<').replace("@@1", "text").replace("@@", "").strip() )
+ elif '<questgiver' in e:
+ a=qnt2(e)
+ qe.giver=a.replace('<questgiver>','').replace('</questgiver>','').strip()
+ elif '<reward' in e:
+ a=qnt2(e)
+ qe.reward=a.replace('<reward>','').replace('</reward>','').replace("@@", "text").replace('<reward ','<').strip()
+ elif '<coord' in e:
+ a=qnt2(e)
+ b=a.split('>')
+ qe.loc=b[1].replace('</coordinates','').strip()
+ elif '<level' in e:
+ a=qnt2(e)
+ qe.entry.append( "Required Level: " + a.replace('<level>','').replace('</level>','').strip() )
+ if (not q.level):
+ try:
+ q.level=int(a.replace('<level>','').replace('</level>','').strip())
+ except:
+ pass
+
+ # 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:
+ if (i.level):
+ aktbl[i.group].append(("[%s](q/%d) (Lv %d)" % (i.name, i.id, i.level), i.level))
+ else:
+ aktbl[i.group].append(("[%s](q/%d)" % (i.name, i.id), i.level))
+ except:
+ if (i.level):
+ aktbl[i.group]=[("[%s](q/%d) (Lv %d)" % (i.name, i.id, i.level), i.level)]
+ else:
+ aktbl[i.group]=[("[%s](q/%d)" % (i.name, i.id), i.level)]
+ aksort.append(i.group)
+
+ for key in aktbl:
+ #print(aktbl[key]);
+ aktbl[key]=sorted(aktbl[key], key=sortlv)
+
+ # Individual file
+ f=open("../../wiki/q/"+str(i.id)+'.md', "w")
+ f.write("<!-- --- title: %d: %s -->\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)
+ # TODO: Sort by Quest Level
+ for a in aktbl[key]:
+ f.write('+ '+a[0]+'\n')
+ f.close()
+
+showHeader()
+
+testMobs()
+newItemDB()
+DoQuest()
+
+wikia.close()
+wikib.close()
+wikic.close()
+wikid.close()
+#print(str(SysDrops))
+
+showFooter()
+exit(0)