Problems
Formatting error in Howto step 4
There's a wiki formatting error in step 4 of "How to create the page". I have no idea where that comes from. -Ados, 2005-07-20
Prices are off
Right now, the prices in the autogenerated list are not always right. For example an arrow sells for 2 GP, not 1, i believe that is because the item_db in the CVS is not the one actually currently used. -Ados, 2005-07-20
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.
Step 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).
Step 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
Step 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
Step 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 [...]
Step 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()