From The Mana World
(→‎mobdbtowiki: Activated the traits information, finally; this is an experiment, feel free to comment or modify)
m
Line 106: Line 106:


def printtableheader():
def printtableheader():
     sys.stdout.write('! style="background:#efdead;" | Image\n')
     print '! style="background:#efdead;" | Image'
     sys.stdout.write('! style="background:#efdead;" | Name\n')
     print '! style="background:#efdead;" | Name'
     sys.stdout.write('! style="background:#efdead;" | ID\n')
     print '! style="background:#efdead;" | ID'
     sys.stdout.write('! style="background:#efdead;" | HP\n')
     print '! style="background:#efdead;" | HP'
     sys.stdout.write('! style="background:#efdead;" | DEF\n')
     print '! style="background:#efdead;" | DEF'
     sys.stdout.write('! style="background:#efdead;" | ATT\n')
     print '! style="background:#efdead;" | ATT'
     sys.stdout.write('! style="background:#efdead;" | EXP\n')
     print '! style="background:#efdead;" | EXP'
     sys.stdout.write('! style="background:#efdead;" | JEXP\n')
     print '! style="background:#efdead;" | JEXP'
     sys.stdout.write('! style="background:#efdead;" | Drops\n')
     print '! style="background:#efdead;" | Drops'
     sys.stdout.write('|-\n')
     print '|-'




Line 124: Line 124:


def saveint(string):
def saveint(string):
    a = 0
     try:
     try:
         a = int(string)
         return int(string)
     except:
     except:
         a = 0
         return 0
    return a
          
          


Line 135: Line 133:
     objects = []
     objects = []
     for line in file:
     for line in file:
         s = line[0:line.find('//')].strip().replace('\t','')
         s = line[:line.find('//')].strip().replace('\t','')
         if s:
         if s:
             values = s.split(',')
             values = s.split(',')
Line 144: Line 142:
                 continue
                 continue
             numberofvalues = 57
             numberofvalues = 57
             if (len(values) != numberofvalues):
             if len(values) != numberofvalues:
                 log.append("mob_db: Warning, monster-line with ID %s has %d values instead of %d" % (values[0], len(values), numberofvalues))
                 log.append("mob_db: Warning, monster-line with ID %s has %d values instead of %d" % (values[0], len(values), numberofvalues))
                 if debug:
                 if debug:
                     log.append("  line was %s" % str(values))
                     log.append("  line was %s" % str(values))
                 while (len(values) < numberofvalues - 1):
                 while len(values) < numberofvalues - 1:
                     values.append('')
                     values.append('')
                 while (len(values) > numberofvalues - 1):
                 while len(values) > numberofvalues - 1:
                     values.pop()
                     values.pop()


Line 185: Line 183:
             o.damagemotion  = saveint(values[28])  # Speed of damage animation ???
             o.damagemotion  = saveint(values[28])  # Speed of damage animation ???


             o.drop = []
             o.drop = [whatever() for i in range(8)]
            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].id    = saveint(values[29])  # The following are 8 groups of item IDs and
Line 211: Line 207:
             o.expper        = saveint(values[48])  # ???
             o.expper        = saveint(values[48])  # ???


             o.mvp = []
             o.mvp = [whatever() for i in range(3)]
            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].id      = saveint(values[49])  # The following are 3 groups of item IDs and
Line 233: Line 227:
     global imageurls
     global imageurls
     for m in monsters:
     for m in monsters:
         if imageurls.has_key(m.label):
         if m.label in imageurls:
             m.imgurl = imageurls[m.label]
             m.imgurl = imageurls[m.label]
         else:
         else:
Line 245: Line 239:
         for d in m.drop:
         for d in m.drop:
             # Only add a dropname if it isn't "default" (id=0)
             # Only add a dropname if it isn't "default" (id=0)
             if dropnames.has_key(d.id) and int(d.id):
             if d.id in dropnames and int(d.id):
                 d.name = dropnames[d.id]
                 d.name = dropnames[d.id]
             else:
             else:
Line 257: Line 251:
         if line[0] == '#' or line[0] == ',':
         if line[0] == '#' or line[0] == ',':
             continue
             continue
         s = line[0:line.find('//')].strip()
         s = line[:line.find('//')].strip()
         if s:
         if s:
             values = s.split(',')
             values = s.split(',')
             if (len(values) < 3):
             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)))
                 if len(values) > 0: log.append("mob_db: Warning, item-line with ID %s doesn't even have 3 values. Skipped." % (values[0], len(values)))
             else:
             else:
                 id = int(values[0])
                 id = int(values[0])
Line 269: Line 263:


def printlog():
def printlog():
    global log
     if len(log) > 0:
     if len(log) > 0:
         sys.stdout.write('\n---------------------------------------\n')
         print '\n---------------------------------------'
     for line in log:
     for line in log:
         sys.stdout.write(line+'\n')
         print line




Line 279: Line 272:
     i = 0
     i = 0
     output = ""
     output = ""
     monster.drop.sort(lambda x,y: y.per-x.per)
     monster.drop.sort(key=lambda x: x.per, reverse=True)
     for d in monster.drop:
     for d in monster.drop:
         if d.name:  
         if d.name:  
             if (i != 0):
             if i != 0:
                 output += '<br>'
                 output += '<br>'
             s = ""
             s = ""
Line 298: Line 291:
                     s = "%.1f"  % (d.per/100.0)
                     s = "%.1f"  % (d.per/100.0)
             output += ("%s (%s%%)" % (d.name.replace('\t',''), s))
             output += ("%s (%s%%)" % (d.name.replace('\t',''), s))
             i = i + 1
             i += 1
     return output
     return output


Line 321: Line 314:
     # Attack master    13    0x2000    8192    Used for summoned monsters
     # Attack master    13    0x2000    8192    Used for summoned monsters


     sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n')
     print '{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"'


     i = 0
     i = 0
     for m in monsters:
     for m in monsters:
         if (i == headerafterrow):
         if i == headerafterrow:
             i = 0
             i = 0
         if (i == 0):
         if i == 0:
             printtableheader()
             printtableheader()


         # Image
         # Image
         sys.stdout.write('| align="center" | %s\n' % m.imgurl)
         print '| align="center" | %s' % m.imgurl


         # Name, Stationary/Assists traits and Mutations
         # Name, Stationary/Assists traits and Mutations
         sys.stdout.write('| %s' % m.name)
         sys.stdout.write('| %s' % m.name)
         if (m.mode >> 0 & 1 == 0):
         if m.mode >> 0 & 1 == 0:
             sys.stdout.write('<br />' + traitstart + 'Stationary' + traitend)
             sys.stdout.write('<br />' + traitstart + 'Stationary' + traitend)
         if (m.mode >> 3 & 1 == 1):
         if m.mode >> 3 & 1 == 1:
             sys.stdout.write('<br />' + traitstart + 'Assists' + traitend)
             sys.stdout.write('<br />' + traitstart + 'Assists' + traitend)
         if (m.mutnr > 0):
         if m.mutnr > 0:
             sys.stdout.write('<br />' + mutationstart + 'May mutate %d attribute' % m.mutnr)
             sys.stdout.write('<br />' + mutationstart + 'May mutate %d attribute' % m.mutnr)
             if (m.mutnr > 1):
             if m.mutnr > 1:
                 sys.stdout.write('s')
                 sys.stdout.write('s')
             sys.stdout.write(' up to %d%%' % m.mutstr + mutationend)
             sys.stdout.write(' up to %d%%' % m.mutstr + mutationend)
         #else:
         #else:
         #    sys.stdout.write('<br />' + mutationstart + 'Does not mutate' + mutationend)
         #    sys.stdout.write('<br />' + mutationstart + 'Does not mutate' + mutationend)
         sys.stdout.write('\n')
         print


         # ID, Health and Defense
         # ID, Health and Defense
         sys.stdout.write('| align="center" | %d\n'  % m.id)
         print '| align="center" | %d'  % m.id
         sys.stdout.write('| align="center" | %d\n'  % m.hp)
         print '| align="center" | %d'  % m.hp
         sys.stdout.write('| align="center" | %d%%\n' % m.defense)
         print '| align="center" | %d%%' % m.defense


         # Attack and No-attack/Aggressive traits
         # Attack and No-attack/Aggressive traits
         if (m.mode >> 7 & 1 == 0):
         if m.mode >> 7 & 1 == 0:
             sys.stdout.write('| align="center" | ' + traitstart + 'N/A' + traitend)
             sys.stdout.write('| align="center" | ' + traitstart + 'N/A' + traitend)
         else:
         else:
Line 361: Line 354:
             else:
             else:
                 sys.stdout.write('| align="center" | %d' % m.attackmin)
                 sys.stdout.write('| align="center" | %d' % m.attackmin)
             if (m.mode >> 2 & 1 == 1):
             if m.mode >> 2 & 1 == 1:
                 sys.stdout.write('<br />' + traitstart + 'Aggro' + traitend)
                 sys.stdout.write('<br />' + traitstart + 'Aggro' + traitend)
         sys.stdout.write('\n')
         print


         # Experience and Job experience, following *tmw-eathena*/src/map/mob.c
         # Experience and Job experience, following *tmw-eathena*/src/map/mob.c
         calc_exp = 0
         calc_exp = 0


         if (m.experience == 0):
         if m.experience == 0:
             if (m.hp <= 1):
             if m.hp <= 1:
                 calc_exp = 1
                 calc_exp = 1


             mod_def = 100 - m.defense
             mod_def = 100 - m.defense


             if (mod_def == 0):
             if mod_def == 0:
                 mod_def = 1
                 mod_def = 1


Line 383: Line 376:
             aggression_factor = 1
             aggression_factor = 1


             if (m.mode % 4 == 4):
             if False:
                 aggression_factor = 10 / 9
                 aggression_factor = 10 / 9


Line 390: Line 383:
             calc_exp = int(math.floor(effective_hp * (math.sqrt(attack_factor) + math.sqrt(dodge_factor) + math.sqrt(persuit_factor) + 55)**3 * aggression_factor / 2000000 * base_exp_rate / 100))
             calc_exp = int(math.floor(effective_hp * (math.sqrt(attack_factor) + math.sqrt(dodge_factor) + math.sqrt(persuit_factor) + 55)**3 * aggression_factor / 2000000 * base_exp_rate / 100))


             if (calc_exp < 1):
             if calc_exp < 1:
                 calc_exp = 1
                 calc_exp = 1
         else:
         else:
             calc_exp = m.experience
             calc_exp = m.experience


         sys.stdout.write('| align="center" | %d\n' % calc_exp)
         print '| align="center" | %d' % calc_exp
         sys.stdout.write('| align="center" | %d\n' % m.jobexperience)
         print '| align="center" | %d' % m.jobexperience


         # Drops and Looter trait
         # Drops and Looter trait
         sys.stdout.write('| %s' % getdropstring(m))
         sys.stdout.write('| %s' % getdropstring(m))
         if (m.mode >> 1 & 1 == 1):
         if m.mode >> 1 & 1 == 1:
             sys.stdout.write('<br />' + traitstart + 'Picks up loot' + traitend)
             sys.stdout.write('<br />' + traitstart + 'Picks up loot' + traitend)
         sys.stdout.write('\n')
         print


         sys.stdout.write('|-\n')
         print '|-'
         i = i + 1
         i += 1


     sys.stdout.write('|}\n')
     print '|}'




#MAIN
#MAIN
try:
try:
     if (len(sys.argv) == 1):
     if len(sys.argv) == 1:
         mob_db = "mob_db.txt"
         mob_db = "mob_db.txt"
         item_db = "item_db.txt"
         item_db = "item_db.txt"
     elif (len(sys.argv) == 3):
     elif len(sys.argv) == 3:
         mob_db = sys.argv[1]
         mob_db = sys.argv[1]
         item_db = sys.argv[2]
         item_db = sys.argv[2]
Line 421: Line 414:
         mob_db = ''
         mob_db = ''
         item_db = ''
         item_db = ''
         sys.stdout.write("Wrong number of arguments\n")
         print "Wrong number of arguments"


     if (mob_db and item_db) :
     if mob_db and item_db :
         if (not os.path.isfile(mob_db)):
         if not os.path.isfile(mob_db):
             sys.stdout.write("File does not exist: %s\n" % mob_db)
             print "File does not exist: %s" % mob_db
             mob_db = ''
             mob_db = ''
         if (not os.path.isfile(item_db)):
         if not os.path.isfile(item_db):
             sys.stdout.write("File does not exist: %s\n" % item_db)
             print "File does not exist: %s" % item_db
             item_db = ''
             item_db = ''
      
      
     if not (mob_db and item_db):
     if not (mob_db and item_db):
         sys.stdout.write("\nUSAGE:\n")
         print "\nUSAGE:"
         sys.stdout.write("%s without any arguments will use item_db.txt and mob_db.txt in the current directory.\n" % sys.argv[0])
         print "%s without any arguments will use item_db.txt and mob_db.txt in the current directory." % sys.argv[0]
         sys.stdout.write("to specify custom files, call: %s <mob_db> <item_db>\n" % sys.argv[0])
         print "to specify custom files, call: %s <mob_db> <item_db>" % sys.argv[0]
         exit(-1);
         exit(-1);
     else:
     else:
Line 447: Line 440:
         addimageurls(monsters)
         addimageurls(monsters)
         adddropnames(monsters,itemnames)
         adddropnames(monsters,itemnames)
         monsters.sort(lambda x, y: x.hp*(x.attackmin+x.attackmax) - y.hp*(y.attackmin+y.attackmax))
         monsters.sort(key=lambda x: x.hp*(x.attackmin+x.attackmax))


         for line in intro:
         for line in intro:
             sys.stdout.write(line+'\n')
             print line


         printmonsters(monsters)
         printmonsters(monsters)

Revision as of 08:07, 1 April 2011

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/env python
# -*- coding: UTF-8 -*-

# Licensed under GNU General Public License

import sys, os, datetime, math;


# 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:#4a4a4a">'
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]]",
'CloverPatch':   "[[Image:CloverPatch.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':    "[[Image:GrassSnake.png]]",
'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':      "[[Image:Silkworm.png]]",
'Snake' :        "[[Image:Snake.png]]",
'Spider':        "[[Image:Spider.png]]",
'SpikyMushroom': "[[Image:Shroom.png]]",
'Squirrel':      "[[Image:Squirrel.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 calculated base 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():
    print '! style="background:#efdead;" | Image'
    print '! style="background:#efdead;" | Name'
    print '! style="background:#efdead;" | ID'
    print '! style="background:#efdead;" | HP'
    print '! style="background:#efdead;" | DEF'
    print '! style="background:#efdead;" | ATT'
    print '! style="background:#efdead;" | EXP'
    print '! style="background:#efdead;" | JEXP'
    print '! style="background:#efdead;" | Drops'
    print '|-'


class whatever: pass

log = []


def saveint(string):
    try:
        return int(string)
    except:
        return 0
        

def parsemonsters(file):
    objects = []
    for line in file:
        s = line[:line.find('//')].strip().replace('\t','')
        if s:
            values = s.split(',')
            if line[0] == '#':
                if debug:
                    log.append("FOUND COMMENT LINE: %s" % str(values))
                    log.append("NUMBER OF FIELDS IN COMMENT LINE: %d" % len(values))
                continue
            numberofvalues = 57
            if len(values) != numberofvalues:
                log.append("mob_db: Warning, monster-line with ID %s has %d values instead of %d" % (values[0], len(values), numberofvalues))
                if debug:
                    log.append("  line was %s" % str(values))
                while len(values) < numberofvalues - 1:
                    values.append('')
                while len(values) > numberofvalues - 1:
                    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 = [whatever() for i in range(8)]

            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 = [whatever() for i in range(3)]

            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 m.label in imageurls:
            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 d.id in dropnames 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] == '#' or line[0] == ',':
            continue
        s = line[: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 doesn't even have 3 values. Skipped." % (values[0], len(values)))
            else:
                id = int(values[0])
                dic[id] = values[2]
    return dic


def printlog():
    if len(log) > 0:
        print '\n---------------------------------------'
    for line in log:
        print line


def getdropstring(monster):
    i = 0
    output = ""
    monster.drop.sort(key=lambda x: x.per, reverse=True)
    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 += 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

    print '{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"'

    i = 0
    for m in monsters:
        if i == headerafterrow:
            i = 0
        if i == 0:
            printtableheader()

        # Image
        print '| align="center" | %s' % 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)
        print

        # ID, Health and Defense
        print '| align="center" | %d'   % m.id
        print '| align="center" | %d'   % m.hp
        print '| align="center" | %d%%' % 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 + 'Aggro' + traitend)
        print

        # Experience and Job experience, following *tmw-eathena*/src/map/mob.c
        calc_exp = 0

        if m.experience == 0:
            if m.hp <= 1:
                calc_exp = 1

            mod_def = 100 - m.defense

            if mod_def == 0:
                mod_def = 1

            effective_hp = ((50 - m.luck) * m.hp / 50) + (2 * m.luck * m.hp / mod_def)
            attack_factor = (m.attackmin + m.attackmax + m.strength / 3 + m.dexterity / 2 + m.luck) * (1872 / m.attackdelay) / 4
            dodge_factor = (m.level + m.agility + m.luck / 2)**(4 / 3)
            persuit_factor = (3 + m.range1) * (m.mode % 2) * 1000 / m.speed
            aggression_factor = 1

            if False:
                aggression_factor = 10 / 9

            base_exp_rate = 100 # From *tmw-eathena-data*/conf/battle_athena.conf

            calc_exp = int(math.floor(effective_hp * (math.sqrt(attack_factor) + math.sqrt(dodge_factor) + math.sqrt(persuit_factor) + 55)**3 * aggression_factor / 2000000 * base_exp_rate / 100))

            if calc_exp < 1:
                calc_exp = 1
        else:
            calc_exp = m.experience

        print '| align="center" | %d' % calc_exp
        print '| align="center" | %d' % 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)
        print

        print '|-'
        i += 1

    print '|}'


#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 = ''
        print "Wrong number of arguments"

    if mob_db and item_db :
        if not os.path.isfile(mob_db):
            print "File does not exist: %s" % mob_db
            mob_db = ''
        if not os.path.isfile(item_db):
            print "File does not exist: %s" % item_db
            item_db = ''
    
    if not (mob_db and item_db):
        print "\nUSAGE:"
        print "%s without any arguments will use item_db.txt and mob_db.txt in the current directory." % sys.argv[0]
        print "to specify custom files, call: %s <mob_db> <item_db>" % sys.argv[0]
        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(key=lambda x: x.hp*(x.attackmin+x.attackmax))

        for line in intro:
            print line

        printmonsters(monsters)

finally:
    printlog()