Classic talk:Item Reference
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()