diff options
-rwxr-xr-x | wiki/redesign.py | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/wiki/redesign.py b/wiki/redesign.py new file mode 100755 index 0000000..a6e9b16 --- /dev/null +++ b/wiki/redesign.py @@ -0,0 +1,764 @@ +#! /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=False +aeros=False +bifs=False +skipCI=False + +wikib=open("EleMonsters.html", "w") +wikib.write('<html><head><meta charset=utf8 /><title>EleGen File</title></head><body>') + + +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="Nature","#7A0" + elif rc == 3: + tl,cl="Fire","#F00" + elif rc == 4: + tl,cl="Error(W)","#093" + elif rc == 5: + tl,cl="Error(P)","#040" + elif rc == 6: + tl,cl="Holy","#afa" + elif rc == 7: + tl,cl="Dark","#908" + elif rc == 8: + tl,cl="Error(G)","#404" + elif rc == 9: + tl,cl="Error(U)","#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/pre-re/mob_db.conf", "r", encoding="utf-8") + else: + src=open("../../server-data/db/pre-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)) + if (mb.walk != "NORMAL"): + 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 + + + # 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 + + + # Over100 Special Formula + if OVER100: + lv=100+OVER100 + + try: + calc_exp = max(1, int(math.floor(effective_hp * (math.sqrt(attack_factor) + math.sqrt(dodge_factor) + math.sqrt(persuit_factor) + 55)**3 * aggression_factor / 2000000))) + hxp=int(calc_exp*(1+lv/50.0)) + lxp=int(calc_exp) + except: + hxp=1 + lxp=1 + print("Warning: Invalid exp for mob \033[1m%s\033[0m" % (mb.name.replace("<b>", "").replace("</b>", ""))) + + + # 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="" + 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('<hr/>') + +wikib.write('<hr/>') +err=int(Ele[4])+int(Ele[8])+int(Ele[5]) +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> +</table> +""" % ( +"Neutral", Ele[0], +"Fire", Ele[3], +"Water", Ele[1], +"Nature", Ele[2], +"Holy", Ele[6], +"Dark", Ele[7], +"Error", err)) + +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) |