From The Mana World
Generating the Monster reference page
This is a python script to generate the complete Monster reference page. The script takes the following arguments:
mobdbtowiki <path to item database> <path to monster database>
If you run dbtowiki
without any arguments, the script will try to find the files item_db.txt
and mob_db.txt
inside the working directory. The script will print the text to stdout (standard output). It is then only a matter of copying and pasting in that text. If you want to edit the introductory text or add images, please do so in the script itself below.
mobdbtowiki
#!/usr/bin/python # Licensed under GNU General Public License import sys, os, datetime; # VARIOUS SETTINGS # If something seems not to work, set this value to 1 to get more information debug = 0 # Number of rows between table header headerafterrow = 10 # Formatting of traits traitstart = '\'\'<span style="color:#ad1818">' traitend = '</span>\'\'' # Formatting of mutations mutationstart = '\'\'<span style="color:#18ad18">' mutationend = '</span>\'\'' ##################################### ## ## ## ADD WIKILINKS TO IMAGES BELOW ## ## ## ##################################### imageurls = { 'AlizarinPlant': "[[Image:Plant-Alizarin.png]]", 'MauvePlant': "[[Image:Plant-Mauve.png]]", 'Bat': "[[Image:Bat.png]]", 'BlackScorpion': "[[Image:BlackScorpion.png]]", 'CaveSnake': "[[Image:LampSnake.png]]", 'CobaltPlant': "[[Image:Plant-Cobalt.png]]", 'EasterFluffy': "[[Image:Fluffy.png]]", 'EvilMushroom': "[[Image:Evilmushroom.png]]", 'FireGoblin': "[[Image:FireGoblin.png]]", 'FireSkull': "[[Image:FireSkull.png]]", 'Flower': "[[Image:Sleepflower.png]]", 'Fluffy': "[[Image:Fluffy.png]]", 'GambogePlant': "[[Image:Plant-Gamboge.png]]", 'GiantMaggot': "[[Image:GiantMaggot.png]]", 'GrassSnake': "", 'GreenSlime': "[[Image:GreenSlime.png]]", 'JackO': "[[Image:JackO.png]]", 'LogHead': "[[Image:Stumpy.png]]", 'Maggot': "[[Image:Maggot.png]]", 'Mouboo': "[[Image:Mouboo.png]]", 'MountainSnake': "[[Image:MountainSnake.png]]", 'Pinkie': "[[Image:Violet.png]]", 'PoisonSkull': "[[Image:PoisonSkull.png]]", 'RedScorpion': "[[Image:RedScorpion.png]]", 'RedSlime': "[[Image:RedSlime.png]]", 'RudolphSlime': "[[Image:Rudolphslime.png]]", 'SantaSlime': "[[Image:Santaslime.png]]", 'Scorpion': "[[Image:Scorpion.png]]", 'SeaSlime': "[[Image:Sea-slime.png]]", 'Silkworm': "", 'Snake' : "[[Image:Snake.png]]", 'Spider': "[[Image:Spider.png]]", 'SpikyMushroom': "[[Image:Shroom.png]]", 'YellowSlime': "[[Image:YellowSlime.png]]" # Note that the last line above should not be ended by a comma! } ########################################## ## ## ## CHANGE THE INTRODUCTORY TEXT BELOW ## ## ## ########################################## intro = [] # Each appended line will automatically end with a line break intro.append("{{Category_playerinfo}}") intro.append("{{Status_green}}") intro.append("") intro.append("'''Page last generated on %s.'''" % datetime.date.today()) intro.append("") intro.append("'''Warning:''' This reference might be out of date. The python script to generate this page can be found on the discussion page. Please be aware that any manual changes made to this page may be lost when the page is generated anew. Also, this reference might not reflect what is currently in the game. [http://gitorious.org/projects/tmw-eathena-data/repos/mainline/blobs/master/db/mob_db.txt You can view the most up-to-date version here.]") intro.append("") intro.append("The monsters are sorted roughly by their fighting strength, calculated as <code>health_points * (attack_min + attack_max)</code>. For more information on the drops please see the [[item reference]].") intro.append("") intro.append("'''Key:''' HP is health points, DEF is defense, ATT is attack, EXP is the experience, JEXP is the job experience. The others are self-explanatory. Traits (such as Aggressive) are written in " + traitstart + "italics" + traitend + ".") intro.append("") # Table headers def printtableheader(): sys.stdout.write('! style="background:#efdead;" | Image\n') sys.stdout.write('! style="background:#efdead;" | Name\n') sys.stdout.write('! style="background:#efdead;" | ID\n') sys.stdout.write('! style="background:#efdead;" | HP\n') sys.stdout.write('! style="background:#efdead;" | DEF\n') sys.stdout.write('! style="background:#efdead;" | ATT\n') sys.stdout.write('! style="background:#efdead;" | EXP\n') sys.stdout.write('! style="background:#efdead;" | JEXP\n') sys.stdout.write('! style="background:#efdead;" | Drops\n') sys.stdout.write('|-\n') class whatever: pass log = [] def saveint(string): a = 0 try: a = int(string) except: a = 0 return a def parsemonsters(file): objects = [] for line in file: s = line[0:line.find('//')].strip().replace('\t','') if s: values = s.split(',') if line[0] == '#': if debug: log.append("FOUND COMMENT LINE: %s" % str(values)) continue if (len(values) != 57): log.append("mob_db: Warning, monster-line with ID %s has %d values instead of 57" % (values[0], len(values))) if debug: log.append(" line was %s" % str(values)) while (len(values) < 56): values.append('') while (len(values) > 56): values.pop() o = whatever() o.id = saveint(values[0]) # Monster ID o.label = values[1] # The label (name) used in GM commands o.name = values[2] # The name known to the server (not to the client) o.level = saveint(values[3]) # Level o.hp = saveint(values[4]) # Health points o.sp = saveint(values[5]) # SP o.experience = saveint(values[6]) # Experience points o.jobexperience = saveint(values[7]) # Job experience points o.range1 = saveint(values[8]) # Range of attack o.attackmin = saveint(values[9]) # Minimum attack damage o.attackmax = saveint(values[10]) # Maximum attack damage o.defense = saveint(values[11]) # Defense (relative in percent) o.magicaldefense = saveint(values[12]) # Magical defense (ditto) o.strength = saveint(values[13]) # Strength level o.agility = saveint(values[14]) # Agility level o.vitality = saveint(values[15]) # Vitality level o.intelligence = saveint(values[16]) # Intelligence level o.dexterity = saveint(values[17]) # Dexterity level o.luck = saveint(values[18]) # Luck level o.range2 = saveint(values[19]) # Some-other range ??? o.range3 = saveint(values[20]) # Line-of-sight range ??? o.scale = saveint(values[21]) # The size type o.race = saveint(values[22]) # Race o.element = saveint(values[23]) # Element level and type o.mode = saveint(values[24]) # Behaviour type (aggressive etc.) o.speed = saveint(values[25]) # Walking speed (faster for lower values) o.attackdelay = saveint(values[26]) # Attack delay (attack speed is the inverse) o.attackmotion = saveint(values[27]) # Speed of attack animation ??? o.damagemotion = saveint(values[28]) # Speed of damage animation ??? o.drop = [] for i in range(8): o.drop.append(whatever()) o.drop[0].id = saveint(values[29]) # The following are 8 groups of item IDs and o.drop[0].per = saveint(values[30]) # drop rates (100 = 1%) for drops 1 to 8 o.drop[1].id = saveint(values[31]) o.drop[1].per = saveint(values[32]) o.drop[2].id = saveint(values[33]) o.drop[2].per = saveint(values[34]) o.drop[3].id = saveint(values[35]) o.drop[3].per = saveint(values[36]) o.drop[4].id = saveint(values[37]) o.drop[4].per = saveint(values[38]) o.drop[5].id = saveint(values[39]) o.drop[5].per = saveint(values[40]) o.drop[6].id = saveint(values[41]) o.drop[6].per = saveint(values[42]) o.drop[7].id = saveint(values[43]) o.drop[7].per = saveint(values[44]) o.item1 = saveint(values[45]) # ??? o.item2 = saveint(values[46]) # ??? o.mexp = saveint(values[47]) # ??? o.expper = saveint(values[48]) # ??? o.mvp = [] for i in range(3): o.mvp.append(whatever()) o.mvp[0].id = saveint(values[49]) # The following are 3 groups of item IDs and o.mvp[0].per = saveint(values[50]) # drop rates (100 = 1%) for what drops ??? o.mvp[1].id = saveint(values[51]) o.mvp[1].per = saveint(values[52]) o.mvp[2].id = saveint(values[53]) o.mvp[2].per = saveint(values[54]) o.mutnr = saveint(values[55]) # Number of mutations o.mutstr = saveint(values[56]) # Mutation strength objects.append(o) return objects def addimageurls(monsters): global imageurls for m in monsters: if imageurls.has_key(m.label): m.imgurl = imageurls[m.label] else: m.imgurl = '' if debug: log.append('Warning: Could not find imageurl for %s' % m.label) def adddropnames(monsters,dropnames): for m in monsters: for d in m.drop: # Only add a dropname if it isn't "default" (id=0) if dropnames.has_key(d.id) and int(d.id): d.name = dropnames[d.id] else: d.name = '' def parseitemnames(file): global log dic = {} for line in file: if line[0] == '#': continue s = line[0:line.find('//')].strip() if s: values = s.split(',') if (len(values) < 3): if len(values) > 0: log.append("mob_db: Warning, item-line with ID %s doesnt even have 3 values. Skipped." % (values[0], len(values))) else: id = int(values[0]) dic[id] = values[2]; return dic def printlog(): global log if len(log) > 0: sys.stdout.write('\n---------------------------------------\n') for line in log: sys.stdout.write(line+'\n') def getdropstring(monster): i = 0 output = "" monster.drop.sort(lambda x,y: y.per-x.per) for d in monster.drop: if d.name: if (i != 0): output += '<br>' s = "" if d.per >= 1000: s = "%d" % (d.per/100) elif d.per >= 100: if (d.per % 100) != 0: s = "%1.1f" % (d.per/100.0) else: s = "%d" % (d.per/100) else: if (d.per % 1000) != 0: s = "%.2f" % (d.per/100.0) else: s = "%.1f" % (d.per/100.0) output += ("%s (%s%%)" % (d.name.replace('\t',''), s)) i = i + 1 return output def printmonsters(monsters): # Key to monster behaviour/trait modes # # TRAIT ID FIELD = MODE COMMENT # Moving 0 0x0001 1 # Looter 1 0x0002 2 # Aggressor 2 0x0004 4 # Assister 3 0x0008 8 # ? 4 0x0010 16 Currently not used # ? 5 0x0020 32 Used (but what does it do?) # ? 6 0x0040 64 Currently not used # ? 7 0x0080 128 Currently not used # ? 8 0x0100 256 Currently not used # ? 9 0x0200 512 Currently not used # ? 10 0x0400 1024 Currently not used # ? 11 0x0800 2048 Currently not used # ? 12 0x1000 4096 Currently not used # Attack master 13 0x2000 8192 Used for summoned monsters sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n') i = 0 for m in monsters: if (i == headerafterrow): i = 0 if (i == 0): printtableheader() # Image sys.stdout.write('| align="center" | %s\n' % m.imgurl) # Name, Stationary/Assists traits and Mutations sys.stdout.write('| %s' % m.name) if (m.mode >> 0 & 1 == 0): sys.stdout.write('<br />' + traitstart + 'Stationary' + traitend) if (m.mode >> 3 & 1 == 1): sys.stdout.write('<br />' + traitstart + 'Assists' + traitend) #if (m.mutnr > 0): # sys.stdout.write('<br />' + mutationstart + 'May mutate %d attribute' % m.mutnr) # if (m.mutnr > 1): # sys.stdout.write('s') # sys.stdout.write(' up to %d%%.' % m.mutstr + mutationend) #else: # sys.stdout.write('<br />' + mutationstart + 'Does not mutate.' + mutationend) sys.stdout.write('\n') # ID, Health and Defense sys.stdout.write('| align="center" | %d\n' % m.id) sys.stdout.write('| align="center" | %d\n' % m.hp) sys.stdout.write('| align="center" | %d%%\n' % m.defense) # Attack and No-attack/Aggressive traits if (m.mode >> 7 & 1 == 0): sys.stdout.write('| align="center" | ' + traitstart + 'N/A' + traitend) else: if m.attackmin < m.attackmax: sys.stdout.write('| align="center" | %d-%d' % (m.attackmin, m.attackmax)) else: sys.stdout.write('| align="center" | %d' % m.attackmin) if (m.mode >> 2 & 1 == 1): sys.stdout.write('<br />' + traitstart + 'Aggressive' + traitend) sys.stdout.write('\n') # Experience and Job experience sys.stdout.write('| align="center" | %d\n' % m.experience) sys.stdout.write('| align="center" | %d\n' % m.jobexperience) # Drops and Looter trait sys.stdout.write('| %s' % getdropstring(m)) if (m.mode >> 1 & 1 == 1): sys.stdout.write('<br />' + traitstart + 'Picks up loot' + traitend) sys.stdout.write('\n') sys.stdout.write('|-\n') i = i + 1 sys.stdout.write('|}\n') #MAIN try: if (len(sys.argv) == 1): mob_db = "mob_db.txt" item_db = "item_db.txt" elif (len(sys.argv) == 3): mob_db = sys.argv[1] item_db = sys.argv[2] else: mob_db = '' item_db = '' sys.stdout.write("Wrong number of arguments\n") if (mob_db and item_db) : if (not os.path.isfile(mob_db)): sys.stdout.write("File does not exist: %s\n" % mob_db) mob_db = '' if (not os.path.isfile(item_db)): sys.stdout.write("File does not exist: %s\n" % item_db) item_db = '' if not (mob_db and item_db): sys.stdout.write("\nUSAGE:\n") sys.stdout.write("dbtowiki without any arguments will use item_db.txt and mob_db.txt in the current directory.\n") sys.stdout.write("to specify custom files, call: dbtowiki <mob_db> <item_db>\n") exit(-1); else: if debug: log.append("Monster-list [mob_db] = %s" % mob_db) log.append("Item-list [item_db] = %s" % item_db) f = open(mob_db) monsters = parsemonsters(f); f = open(item_db) itemnames = parseitemnames(f); addimageurls(monsters) adddropnames(monsters,itemnames) monsters.sort(lambda x, y: x.hp*(x.attackmin+x.attackmax) - y.hp*(y.attackmin+y.attackmax)) for line in intro: sys.stdout.write(line+'\n') printmonsters(monsters) finally: printlog()