How to create this page
If you just want to add a few images or change the appearance of the page. You can jump directly to step 5.
1. First, create individual item images by using the tilesplit tool. It uses the PIL (Python Image Library), so you possibly have to install that first. The usage is
pngsplit <startid> <endid> <items.png>.
for example, like used in the creation of the original page. But obviously you can just generate the new items now:
./tilesplit 1 73 /home/user/_code/tmw/data/graphics/sprites/items.png
This means the IDs used in the client data/items.xml from the client, which begin at 1. The items.png can be found in data/graphics/sprites/ and contains 10 items in a row, which makes it relatively easy to determine the id numbers you want. The program will then copy the files into a subdirectory pics (or pics2, pics3 ..., if that directory already exists).
2. Step two is to upload the images to imageshack, or a similiar provider of your choice. I really, with all of my heart, wish I'd know how to write a tool that believably pretends to be a popular webbrowser. But as long as such a thing does not exist, you've got to do it by hand. While uploading, copy the urls as a linebreak-seperated listing into a textfile. Like so:
http://img296.imageshack.us/img296/2363/013nw.png http://img167.imageshack.us/img167/6766/025ia.png http://img179.imageshack.us/img179/9046/031oj.png http://img301.imageshack.us/img301/6505/041gt.png http://img207.imageshack.us/img207/2556/059or.png
3. You can now optionally convert that list to html code with urlstohtml and compare it with the original items.png to figure out whether you did it all right. Like this:
cat urlist | urlstohtml > foo.html
'4. Now it's time to modify the imageurl list in the code of itemdbtowiki . You can autogenerate the updates to the list imageurls by running urlstopython like so:
cat urllist | urlstopython <startid>
itemdbtowiki uses the IDs from items.xml, which (as i said before) start with 1. If your urllist, started with, let's say, the image 74. You'd call
> cat urllist | urlstopython 74 "http://img296.imageshack.us/img296/2363/743nw.png", # 74 "http://img167.imageshack.us/img167/6766/75ia.png", # 75 "http://img179.imageshack.us/img179/9046/761oj.png", # 76 "http://img301.imageshack.us/img301/6505/771gt.png", # 77 "http://img207.imageshack.us/img207/2556/789or.png", # 78 [...]
5. Now you can run itemdbtowiki to generate the wiki page. The usage is:
itemdbtowiki <item_db.txt> <items.xml> > wikipage.txt
If run without any arguments, it'll look for item_db.txt and items.xml in the current directory.
Python-Tools
All these tools are subject to the GNU General Public License.
tilesplit
#!/usr/bin/python import sys, os, math import Image import httplib, urllib,mimetools import MimeWriter, StringIO def inttostr_zero(i, count): s = "%d" % i while len(s) < count: s = "0"+s return s def getimgdir(): if not os.path.exists('pics'): os.mkdir('pics') return 'pics' else: i = 2 while os.path.exists('pics%d' % i): i +=1 os.mkdir('pics%d' % i) return 'pics%d' % i startindex = int(sys.argv[1]) endindex = int(sys.argv[2]) nlength = math.log10(endindex) img = Image.open(sys.argv[3]) i = startindex-1 imgdir = getimgdir(); while i < endindex: cropbox = ( (i % 10)*32, (i / 10)*32, (i % 10)*32+32, (i / 10)*32+32 ) region = img.crop(cropbox) region.save("%s/%s.png" % (imgdir, inttostr_zero(i+1, nlength) ) ) i = i + 1
urlstohtml
#!/usr/bin/python import sys sys.stdout.write("<html>\n<body>\n") i = 0 for url in sys.stdin: if len(url) > 5: sys.stdout.write('<img src="%s">' % url[:-1]) if i < 9: i += 1 else: sys.stdout.write('\n') i = 0 sys.stdout.write("\n</body>\n</html>\n")
urlstopython
#!/usr/bin/python import sys firstskip = 0 space = "" commentpos = 55 if len(sys.argv) == 1: i = 0 sys.stdout.write('imageurls = [\n') sys.stdout.write('# The index represents the imageID from the items.xml of the client.\n') sys.stdout.write(' ""') else: i = int(sys.argv[1])-1 firstskip = 1 sys.stdout.write(' ') for url in sys.stdin: if len(url) > 5: #if not commentpos: commentpos = len(url) + 5 if firstskip: firstskip = 0 sys.stdout.write('"%s"' % (url[:-1])) else: sys.stdout.write(', %s #%5d\n "%s"' % (space,i,url[:-1])) space = " " * (commentpos-(len(url))) i += 1 sys.stdout.write(' %s #%5d\n' % (space,i)) sys.stdout.write(']\n')
itemdbtowiki
#!/usr/bin/python #Licensed under GNU General Public License import sys, os; import xml.parsers.expat imageurls = [ # The index represents the imageID from the items.xml of the client. "", # 0 "http://img296.imageshack.us/img296/2363/013nw.png", # 1 "http://img167.imageshack.us/img167/6766/025ia.png", # 2 "http://img179.imageshack.us/img179/9046/031oj.png", # 3 "http://img301.imageshack.us/img301/6505/041gt.png", # 4 "http://img207.imageshack.us/img207/2556/059or.png", # 5 "http://img229.imageshack.us/img229/2586/066zf.png", # 6 "http://img236.imageshack.us/img236/2425/077ed.png", # 7 "http://img154.imageshack.us/img154/8776/088cu.png", # 8 "http://img292.imageshack.us/img292/765/096cx.png", # 9 "http://img296.imageshack.us/img296/4291/103ku.png", # 10 "http://img265.imageshack.us/img265/4149/119ml.png", # 11 "http://img296.imageshack.us/img296/5246/125wt.png", # 12 "http://img248.imageshack.us/img248/6633/132dg.png", # 13 "http://img254.imageshack.us/img254/6427/149pm.png", # 14 "http://img279.imageshack.us/img279/2362/155sr.png", # 15 "http://img116.imageshack.us/img116/4408/167sb.png", # 16 "http://img139.imageshack.us/img139/6189/173ut.png", # 17 "http://img223.imageshack.us/img223/1794/187xp.png", # 18 "http://img229.imageshack.us/img229/1659/195yc.png", # 19 "http://img239.imageshack.us/img239/622/205at.png", # 20 "http://img153.imageshack.us/img153/1839/219lz.png", # 21 "http://img296.imageshack.us/img296/2636/228yi.png", # 22 "http://img248.imageshack.us/img248/53/239uu.png", # 23 "http://img254.imageshack.us/img254/4431/242qj.png", # 24 "http://img279.imageshack.us/img279/1489/254er.png", # 25 "http://img116.imageshack.us/img116/9810/269ig.png", # 26 "http://img139.imageshack.us/img139/5177/270mi.png", # 27 "http://img223.imageshack.us/img223/8136/281bl.png", # 28 "http://img229.imageshack.us/img229/6661/297cx.png", # 29 "http://img239.imageshack.us/img239/5787/304xp.png", # 30 "http://img301.imageshack.us/img301/1121/311an.png", # 31 "http://img52.imageshack.us/img52/7388/324fk.png", # 32 "http://img141.imageshack.us/img141/9527/336nu.png", # 33 "http://img100.imageshack.us/img100/7990/349zk.png", # 34 "http://img87.imageshack.us/img87/3489/355nw.png", # 35 "http://img151.imageshack.us/img151/5561/365gf.png", # 36 "http://img91.imageshack.us/img91/955/370ql.png", # 37 "http://img157.imageshack.us/img157/3830/387ay.png", # 38 "http://img32.imageshack.us/img32/2695/391hf.png", # 39 "http://img112.imageshack.us/img112/9297/400la.png", # 40 "http://img301.imageshack.us/img301/5387/412uz.png", # 41 "http://img52.imageshack.us/img52/3072/423xc.png", # 42 "http://img187.imageshack.us/img187/2963/434dc.png", # 43 "http://img125.imageshack.us/img125/4331/442fm.png", # 44 "http://img260.imageshack.us/img260/6627/456rj.png", # 45 "http://img211.imageshack.us/img211/9089/469fy.png", # 46 "http://img219.imageshack.us/img219/1188/479yz.png", # 47 "http://img232.imageshack.us/img232/3517/489oa.png", # 48 "http://img291.imageshack.us/img291/8643/491eg.png", # 49 "http://img157.imageshack.us/img157/1425/502hg.png", # 50 "http://img293.imageshack.us/img293/4524/514dn.png", # 51 "http://img42.imageshack.us/img42/7271/523hc.png", # 52 "http://img187.imageshack.us/img187/5241/535fk.png", # 53 "http://img125.imageshack.us/img125/8093/543yr.png", # 54 "http://img260.imageshack.us/img260/1315/554bt.png", # 55 "http://img211.imageshack.us/img211/9687/564wu.png", # 56 "http://img219.imageshack.us/img219/6842/571wz.png", # 57 "http://img232.imageshack.us/img232/9236/582ue.png", # 58 "http://img291.imageshack.us/img291/435/598ni.png", # 59 "http://img157.imageshack.us/img157/1323/605pt.png", # 60 "http://img293.imageshack.us/img293/1371/618fu.png", # 61 "http://img42.imageshack.us/img42/69/625mj.png", # 62 "http://img187.imageshack.us/img187/6240/637ju.png", # 63 "http://img275.imageshack.us/img275/9440/648zq.png", # 64 "http://img169.imageshack.us/img169/8508/655is.png", # 65 "http://img195.imageshack.us/img195/9153/660zq.png", # 66 "http://img288.imageshack.us/img288/8669/673xe.png", # 67 "http://img242.imageshack.us/img242/3228/688wv.png", # 68 "http://img214.imageshack.us/img214/8675/693wb.png", # 69 "http://img98.imageshack.us/img98/774/703wa.png", # 70 "http://img157.imageshack.us/img157/7590/717em.png", # 71 "http://img299.imageshack.us/img299/4237/721eu.png", # 72 "http://img246.imageshack.us/img246/8151/731cd.png" # 73 ] imagesused = {} class whatever: pass log = [] # parseitems(file) ## Returns list with items from eathena item_db file. def saveint(string, altval = 0): a = 0 try: a = int(string) except: a = altval return a def parsescript(s): # Assumes that there's only one call of each method, otherwise it would need to know # how to combine those function calls. In practice, the latter call would prevail. script = {} scriptentry = "" parentry = "" mode = 0 for a in s: if mode == 0: # looking for method if a.isalpha(): mode = 1 scriptentry += a elif a == '}': mode = 9 elif mode == 1: # reading method name if a in " ;}": if a == " ": mode = 2 elif a == ";": mode = 1 elif a == "}": mode = 9 parentry = "" script[scriptentry] = [] else: scriptentry += a elif mode == 2: #looking for param if a == " ": pass elif a == ";": mode = 0 scriptentry = "" else: mode = 3 parentry = a elif mode == 3: #reading param if (a == " ") or (a == ",") or (a == ";"): script[scriptentry].append(parentry) parentry = "" if (a == ';'): mode = 0 scriptentry = "" else: mode = 2 else: parentry += a elif mode == 9: #finished pass # Convert all possible parameters to integers for i in script.keys(): for j in range(len(script[i])): try: script[i][j] = int(script[i][j]) except: #print script[i][j] pass return script def parseitems(file): objects = [] for line in file: s = line[0:line.find('//')].strip() if s: #Replace commas inbetween {} with |, so we can use split mode = 0 sout = "" for a in s: if mode == 0: #Out of {} if a == '{': mode = 1 sout += a elif mode == 1: #Inside {} if a == ',': sout += '|' else: sout += a if a == '}': mode = 0 values = sout.split(',') if (len(values) != 19): log.append("item_db: Warning, item-line with ID %s has %d values instead of 19" % (values[0], len(values))) while (len(values) < 19): values.append('') while (len(values) > 19): values.pop() o = whatever() o.id = int(values[0]) o.name = values[1] o.jname = values[2] o.type = saveint(values[3]) o.price = saveint(values[4]) o.sell = saveint(values[5]) o.weight = saveint(values[6]) o.atk = saveint(values[7]) o.defense = saveint(values[8]) o.range = saveint(values[9]) o.slot = saveint(values[10],-1) o.job = saveint(values[11],-1) o.gender = saveint(values[12],-1) o.loc = saveint(values[13],-1) o.wlv = saveint(values[14]) o.elv = saveint(values[15]) o.view = saveint(values[16],-1) o.usescript = parsescript(values[17].replace('|',',')) o.equipscript = parsescript(values[18].replace('|',',')) objects.append(o) return objects # parsexmlitems(file) ## Creates a dictionary containing the values of a client items.xml ## Yeah, there are XML parsers in the standard python libraries, but they're too object ## oriented and thus don't fit the style of this program. def parsexmlitems(file): items = {} pre = "<item " term = "/>" attrs = ["id", "image", "art", "name", "description", "type", "weight", "slot"] intattrs = ["id", "image", "art", "type", "weight", "slot"] s = file.read() index = 0 debug = 0 while s[index:].find(pre) >= 0: index += s[index:].find(pre) + len(pre) curitem = {} termstart = index + s[index:].find(term) + len(term) for attr in attrs: found = s[index:].find(attr+'="') if found >= 0: start = index + found + len(attr+'="') end= start + s[start:].find('"') else: start = termstart if (start < termstart): curitem[attr] = s[start:end] for a in intattrs: try: if curitem.has_key(a): curitem[a] = int(curitem[a]) except: log.append("Item-ID %s: Cannot convert integer attribute %s to an integer. Value: '%s'" % (curitem["id"], a, curitem[a])) items[curitem['id']] = curitem return items # addclientinformation(items, citems) ## Entends the item data with the data collected from the client items.xml. Adding imageurls, ## client-name and -description def addclientinformation(items,citems): global imageurls global imagesused for i in items: if citems.has_key(i.id): i.imgurl = imageurls[ citems[i.id]["image"] ] imagesused[ citems[i.id]["image"]] = 1 i.description = citems[i.id]["description"] i.clientname = citems[i.id]["name"] else: i.imgurl = '' i.description = '' i.clientname = '' # gettypedir (items) ## Returns sorted lists of items by itemtype def gettypedir(items): items.sort(lambda x,y: (x.price+x.sell) - (y.price+y.sell)) typedir = whatever() typedir.healthy = [] typedir.inspiring = [] typedir.weapons = [] typedir.combos = [] typedir.armor = [] typedir.other = [] for item in items: if (item.imgurl.strip() or item.clientname.strip()): #if item.id == 537: log.append('"%s", "%s"' % (item.imgurl, item.name) if (item.atk > 0): if item.defense == 0: typedir.weapons.append(item) else: typedir.combos.append(item) elif (item.defense > 0): typedir.armor.append(item) elif item.usescript.has_key("itemheal"): if item.usescript["itemheal"][0] > item.usescript["itemheal"][1]: typedir.healthy.append(item) else: typedir.inspiring.append(item) else: typedir.other.append(item) typedir.weapons.sort(lambda x,y: x.atk - y.atk) typedir.armor.sort(lambda x,y: x.defense - y.defense) typedir.combos.sort(lambda x,y: (x.defense+x.atk) - (y.defense+y.atk)) typedir.healthy.sort(lambda x,y: int(x.usescript["itemheal"][0]) - int(y.usescript["itemheal"][0])) typedir.inspiring.sort(lambda x,y: int(x.usescript["itemheal"][1]) - int(y.usescript["itemheal"][1])) #typedir.other.sort(lambda x,y: (x.price+x.sell) - (y.price+y.sell)) return typedir # printlog() ## Prints the global variable log to stdout def printlog(): global log if len(log) > 0: sys.stdout.write('\n---------------------------------------\n') for line in log: sys.stdout.write(line+'\n') # print<>items(items, title) ## Creates the table in wikicode, depending on what kind of item is being printed def getmoneystring(buy, sell): return '| align="right" | %d GP<br>%d gp\n' % (buy,sell) def getidstring(id): return '| align="center" | [%d]\n' % id def printhealitems(items,title): sys.stdout.write('==%s==\n' % title) sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n') 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 Bonus\n') sys.stdout.write('! style="background:#efdead;" | SP Bonus\n') sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n') sys.stdout.write('! style="background:#efdead;" | Description\n') for i in items: sys.stdout.write('|-\n') sys.stdout.write('| align="center" | %s\n' % i.imgurl) #sys.stdout.write('| %s\n' % i.jname.replace('_',' ')) sys.stdout.write('| %s\n' % i.clientname) sys.stdout.write( getidstring(i.id) ) sys.stdout.write('| align="center" | %d\n' % i.usescript["itemheal"][0]) sys.stdout.write('| align="center" | %d\n' % i.usescript["itemheal"][1]) sys.stdout.write( getmoneystring(i.price,i.sell) ) sys.stdout.write('| %s\n' % i.description) sys.stdout.write('|}\n\n') def printweaponitems(items, title): sys.stdout.write('==%s==\n' % title) sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n') 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;" | Damage<br>(Range)\n') sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n') sys.stdout.write('! style="background:#efdead;" | Description\n') for i in items: sys.stdout.write('|-\n') sys.stdout.write('| align="center" | %s\n' % i.imgurl) #sys.stdout.write('| %s\n' % i.jname.replace('_',' ')) sys.stdout.write('| %s\n' % i.clientname) sys.stdout.write( getidstring(i.id) ) sys.stdout.write('| align="center" | %d (%d)\n' % (i.atk,i.range)) sys.stdout.write( getmoneystring(i.price,i.sell) ) sys.stdout.write('| %s\n' % i.description) sys.stdout.write('|}\n\n') def printarmoritems(items, title): sys.stdout.write('==%s==\n' % title) sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n') 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;" | Defense\n') sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n') sys.stdout.write('! style="background:#efdead;" | Description\n') for i in items: sys.stdout.write('|-\n') sys.stdout.write('| align="center" | %s\n' % i.imgurl) #sys.stdout.write('| %s\n' % i.jname.replace('_',' ')) sys.stdout.write('| %s\n' % i.clientname) sys.stdout.write( getidstring(i.id) ) sys.stdout.write('| align="center" | %d\n' % i.defense) sys.stdout.write( getmoneystring(i.price,i.sell) ) sys.stdout.write('| %s\n' % i.description) sys.stdout.write('|}\n\n') def printcomboitems(items, title): sys.stdout.write('==%s==\n' % title) sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n') 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;" | Damage<br>(Range)\n') sys.stdout.write('! style="background:#efdead;" | Defense\n') sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n') sys.stdout.write('! style="background:#efdead;" | Description\n') for i in items: sys.stdout.write('|-\n') sys.stdout.write('| align="center" | %s\n' % i.imgurl) #sys.stdout.write('| %s\n' % i.jname.replace('_',' ')) sys.stdout.write('| %s\n' % i.clientname) sys.stdout.write( getidstring(i.id) ) sys.stdout.write('| align="center" | %d (%d)\n' % (i.atk,i.range)) sys.stdout.write('| align="center" | %d\n' % i.defense) sys.stdout.write( getmoneystring(i.price,i.sell) ) sys.stdout.write('| %s\n' % i.description) sys.stdout.write('|}\n\n') def getpropertystring(item): s = "" s += "Weight: %d, " % item.weight s += "Slot: %d, " % item.slot s += "Job: %d, " % item.job s += "Gender: %d, " % item.gender s += "Loc: %d, " % item.loc s += "wLV: %d, " % item.wlv s += "eLV: %d, " % item.wlv s += "View: %d " % item.view return s def printotheritems(items, title): sys.stdout.write('==%s==\n' % title) sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n') 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;" | Type\n') #sys.stdout.write('! style="background:#efdead;" | Properties\n') sys.stdout.write('! style="background:#efdead;" | Price<br>BUY/Sell\n') sys.stdout.write('! style="background:#efdead;" | Description\n') for i in items: sys.stdout.write('|-\n') sys.stdout.write('| align="center" | %s\n' % i.imgurl) #sys.stdout.write('| %s\n' % i.jname.replace('_',' ')) sys.stdout.write('| %s\n' % i.clientname) sys.stdout.write( getidstring(i.id) ) #sys.stdout.write('| align="center" | %d\n' % i.type) #sys.stdout.write('| align="center" | %s\n' % getpropertystring(i)) sys.stdout.write( getmoneystring(i.price,i.sell) ) sys.stdout.write('| %s\n' % i.description) sys.stdout.write('|}\n\n') def printunuseditems(title): global imageurls global imagesused ids = [] for i in range(1,len(imageurls)): if not imagesused.has_key(i): ids.append(i) if len(ids): sys.stdout.write('==%s==\n' % title) sys.stdout.write('{| border="1" cellspacing="0" cellpadding="5" width="100%" align="center"\n') sys.stdout.write('| ') for i in ids: sys.stdout.write(imageurls[i] + ' ') sys.stdout.write('\n|}\n\n') ##################################################################### # MAIN ##################################################################### try: if (len(sys.argv) == 1): item_db = "item_db.txt" item_xml = "items.xml" elif (len(sys.argv) == 3): item_db = sys.argv[1] item_xml = sys.argv[2] else: item_db = '' item_xml = '' sys.stdout.write("Wrong number of arguments\n") if item_db and (not os.path.isfile(item_db)): sys.stdout.write("File does not exist: %s\n" % item_db) item_db = '' if item_xml and (not os.path.isfile(item_xml)): sys.stdout.write("File does not exist: %s\n" % item_db) item_db = '' if not (item_db and item_xml): sys.stdout.write("\nUSAGE:\n") sys.stdout.write("dbtowiki without any arguments will use item_db.txt and items.xml in the current directory.\n") sys.stdout.write("to specify custom files, call: dbtowiki <item_db> <item_xml>\n") exit(-1); else: log.append("Item-list [item_db] = %s" % item_db) log.append("Item-list [item_xml] = %s" % item_xml) f = open(item_db) items = parseitems(f); f = open(item_xml) citems = parsexmlitems(f); addclientinformation(items, citems) typedir = gettypedir(items) if len(typedir.healthy) > 0: printhealitems(typedir.healthy, "Health") if len(typedir.inspiring) > 0: printhealitems(typedir.inspiring, "Mana") if len(typedir.weapons) > 0: printweaponitems(typedir.weapons, "Weapons") if len(typedir.armor) > 0: printarmoritems(typedir.armor, "Armor") if len(typedir.combos) > 0: printcomboitems(typedir.combos, "Combos") if len(typedir.other) > 0: printotheritems(typedir.other, "Other") printunuseditems("Still unknown") sys.stdout.write("\n\n") finally: printlog()