mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-01 16:57:50 +00:00
1105 lines
38 KiB
Python
Executable File
1105 lines
38 KiB
Python
Executable File
#!/usr/bin/env python
|
||
#encoding: utf8
|
||
"""
|
||
wmlunits -- tool to output information on all units in HTML
|
||
|
||
Run without arguments to see usage.
|
||
"""
|
||
|
||
# Makes things faster on 32-bit systems
|
||
try: import psyco; psyco.full()
|
||
except ImportError: pass
|
||
|
||
import sys, os, re, glob, shutil, copy, urllib2, gc, optparse, gettext
|
||
|
||
import wesnoth.wmlparser2 as wmlparser2
|
||
import unit_tree.helpers as helpers
|
||
import unit_tree.animations as animations
|
||
|
||
html_header = '''
|
||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<link rel=stylesheet href=\"%(path)sstyle.css\" type=\"text/css\">
|
||
</head>
|
||
<body>'''.strip()
|
||
|
||
html_footer = '''
|
||
<div id="footer">
|
||
<p><a href="http://www.wesnoth.org/wiki/Wesnoth:Copyrights">Copyright</a> © 2003-2009 The Battle for Wesnoth</p>
|
||
<p>Supported by <a href="http://www.jexiste.fr/">Jexiste</a></p>
|
||
</div>
|
||
</body></html>
|
||
'''.strip()
|
||
|
||
error_only_once = {}
|
||
def error_message(message):
|
||
if message in error_only_once: return
|
||
error_only_once[message] = 1
|
||
sys.stderr.write(message)
|
||
|
||
helpers.error_message = error_message
|
||
|
||
def reset_errors():
|
||
error_only_once = {}
|
||
|
||
class Translation:
|
||
def __init__(self, localedir, langcode):
|
||
self.catalog = {}
|
||
self.localedir = localedir
|
||
self.langcode = langcode
|
||
class Dummy:
|
||
def ugettext(self, x):
|
||
if not x: return ""
|
||
caret = x.find("^")
|
||
if caret < 0: return x
|
||
return x[caret + 1:]
|
||
self.dummy = Dummy()
|
||
|
||
def translate(self, string, textdomain):
|
||
if textdomain not in self.catalog:
|
||
try:
|
||
self.catalog[textdomain] = gettext.translation(
|
||
textdomain, self.localedir, [self.langcode])
|
||
except IOError:
|
||
self.catalog[textdomain] = self.dummy
|
||
except IndexError:
|
||
# not sure why, but this happens within the
|
||
# gettext.translation call sometimes
|
||
self.catalog[textdomain] = self.dummy
|
||
|
||
r = self.catalog[textdomain].ugettext(string)
|
||
|
||
return r
|
||
|
||
class GroupByRace:
|
||
def __init__(self, wesnoth, campaign):
|
||
self.wesnoth = wesnoth
|
||
self.campaign = campaign
|
||
|
||
def unitfilter(self, unit):
|
||
if not self.campaign: return True
|
||
return unit.campaign == self.campaign
|
||
|
||
def groups(self, unit):
|
||
return [unit.race]
|
||
|
||
def group_name(self, group):
|
||
if not group: return "None"
|
||
return T(group, "plural_name")
|
||
|
||
class GroupByFaction:
|
||
def __init__(self, wesnoth, era):
|
||
self.wesnoth = wesnoth
|
||
self.era = era
|
||
|
||
def unitfilter(self, unit):
|
||
return self.era in unit.eras
|
||
|
||
def groups(self, unit):
|
||
return [x for x in unit.factions if x[0] == self.era]
|
||
|
||
def group_name(self, group):
|
||
era = self.wesnoth.era_lookup[group[0]]
|
||
faction = era.faction_lookup[group[1]]
|
||
name = T(faction, "name")
|
||
name = name[name.rfind("=") + 1:]
|
||
name = T(era, "name") + " / " + name
|
||
return name
|
||
|
||
global_htmlout = None
|
||
def T(tag, att):
|
||
return tag.get_text_val(att, translation =
|
||
global_htmlout.translate)
|
||
|
||
class HTMLOutput:
|
||
def __init__(self, isocode, output, campaign, wesnoth, verbose = False):
|
||
global global_htmlout
|
||
self.output = output
|
||
self.campaign = campaign
|
||
self.verbose = verbose
|
||
self.target = "index.html"
|
||
self.wesnoth = wesnoth
|
||
self.forest = None
|
||
self.translation = Translation(options.transdir, isocode)
|
||
self.isocode = isocode
|
||
global_htmlout = self
|
||
|
||
def translate(self, string, domain):
|
||
return self.translation.translate(string, domain)
|
||
|
||
def analyze_units(self, grouper):
|
||
"""
|
||
This takes all units belonging to a campaign, then groups them either
|
||
by race or faction, and creates an advancements tree out of it.
|
||
"""
|
||
|
||
# Build an advancement tree forest of all units.
|
||
forest = self.forest = helpers.UnitForest()
|
||
units_added = {}
|
||
for uid, u in self.wesnoth.unit_lookup.items():
|
||
if grouper.unitfilter(u):
|
||
forest.add_node(helpers.UnitNode(u))
|
||
units_added[uid] = u
|
||
|
||
# Always add any child units, even if they have been filtered out..
|
||
while units_added:
|
||
new_units_added = {}
|
||
for uid, u in units_added.items():
|
||
for auid in u.advance:
|
||
if not auid in units_added:
|
||
try:
|
||
au = self.wesnoth.unit_lookup[auid]
|
||
except KeyError:
|
||
error_message(
|
||
"Warning: Unit %s not found as advancement of %s\n" %
|
||
(auid, repr(uid)))
|
||
continue
|
||
forest.add_node(helpers.UnitNode(au))
|
||
new_units_added[auid] = au
|
||
units_added = new_units_added
|
||
|
||
|
||
forest.update()
|
||
|
||
# Partition trees by race/faction of first unit.
|
||
groups = {}
|
||
breadth = 0
|
||
|
||
for tree in forest.trees.values():
|
||
u = tree.unit
|
||
ugroups = grouper.groups(u)
|
||
for group in ugroups:
|
||
groups[group] = groups.get(group, []) + [tree]
|
||
breadth += tree.breadth
|
||
|
||
thelist = groups.keys()
|
||
thelist.sort()
|
||
|
||
rows_count = breadth + len(thelist)
|
||
# Create empty grid.
|
||
rows = []
|
||
for j in range(rows_count):
|
||
column = []
|
||
for i in range(6):
|
||
column.append((1, 1, None))
|
||
rows.append(column)
|
||
|
||
# Sort advancement trees by name of first unit and place into the grid.
|
||
def by_name(t1, t2):
|
||
u1 = t1.unit
|
||
u2 = t2.unit
|
||
|
||
u1name = T(u1, "name")
|
||
u2name = T(u2, "name")
|
||
return cmp(u1name, u2name)
|
||
|
||
def grid_place(nodes, x):
|
||
nodes.sort(by_name)
|
||
for node in nodes:
|
||
level = node.unit.level
|
||
rows[x][level] = (1, node.breadth, node)
|
||
for i in range(1, node.breadth):
|
||
rows[x + i][level] = (0, 0, node)
|
||
grid_place(node.children, x)
|
||
x += node.breadth
|
||
return x
|
||
|
||
x = 0
|
||
for group in thelist:
|
||
node = helpers.GroupNode(group)
|
||
node.name = grouper.group_name(group)
|
||
|
||
rows[x][0] = (6, 1, node)
|
||
for i in range(1, 6):
|
||
rows[x][i] = (0, 0, None)
|
||
nodes = groups[group]
|
||
x += 1
|
||
x = grid_place(nodes, x)
|
||
|
||
self.unitgrid = rows
|
||
|
||
def write_navbar(self):
|
||
def write(x): self.output.write(x)
|
||
|
||
languages = find_languages()
|
||
langlist = languages.keys()
|
||
langlist.sort()
|
||
|
||
write("""
|
||
<div class="header">
|
||
<a href="http://www.wesnoth.org">
|
||
<img src="../wesnoth-logo.jpg" alt="Wesnoth logo"/>
|
||
</a>
|
||
</div>
|
||
<div class="topnav">
|
||
<a href="../index.html">Wesnoth Units database</a>
|
||
</div>
|
||
""")
|
||
|
||
write("<table class=\"navbar\">")
|
||
|
||
write("<tr>\n")
|
||
|
||
def abbrev(name):
|
||
abbrev = name[0]
|
||
word_seperators = [" ", "_", "+", "(", ")"]
|
||
for i in range(1, len(name)):
|
||
if name[i] in ["+", "(", ")"] or name[i - 1] in word_seperators and name[i] not in word_seperators:
|
||
abbrev += name[i]
|
||
return abbrev
|
||
|
||
# Campaigns
|
||
x = self.translate("addon_type^Campaign", "wesnoth")
|
||
write("<th>%s</th></tr><tr><td>" % x)
|
||
cids = [[], []]
|
||
for cid in self.wesnoth.campaign_lookup.keys():
|
||
if cid in self.wesnoth.is_mainline_campaign:
|
||
cids[0].append(cid)
|
||
else:
|
||
cids[1].append(cid)
|
||
|
||
for i in range(2):
|
||
cnames = cids[i]
|
||
cnames.sort()
|
||
for cname in cnames:
|
||
lang = self.isocode
|
||
if cname == "mainline":
|
||
campname = self.translate("Multiplayer", "wesnoth")
|
||
campabbrev = campname
|
||
else:
|
||
if not cname in self.wesnoth.is_mainline_campaign:
|
||
lang = "C" # only mainline campaigns have translations right now
|
||
campname = T(self.wesnoth.campaign_lookup[cname], "name")
|
||
if not campname:
|
||
campname = cname
|
||
campabbrev = T(self.wesnoth.campaign_lookup[cname], "abbrev")
|
||
if not campabbrev:
|
||
campabbrev = abbrev(campname)
|
||
|
||
write(" <a title=\"%s\" href=\"../%s/%s.html\">%s</a><br/>\n" % (
|
||
campname, lang, cname, campabbrev))
|
||
if i == 0 and cids[1]:
|
||
write("-<br/>\n")
|
||
write("</td>\n")
|
||
write("</tr>\n")
|
||
|
||
# Eras
|
||
write("<tr>\n")
|
||
x = self.translate("Era", "wesnoth")
|
||
write("<th>%s</th></tr><tr><td>" % x)
|
||
eids = [[], []]
|
||
for eid in self.wesnoth.era_lookup.keys():
|
||
if eid in self.wesnoth.is_mainline_era:
|
||
eids[0].append(eid)
|
||
else:
|
||
eids[1].append(eid)
|
||
for i in range(2):
|
||
lang = self.isocode
|
||
if i == 1: lang = "C" # only mainline translations
|
||
eranames = [(T(self.wesnoth.era_lookup[eid], "name"),
|
||
eid) for eid in eids[i]]
|
||
eranames.sort()
|
||
for eraname, eid in eranames:
|
||
write(" <a title=\"%s\" href=\"../%s/%s.html\">%s</a><br/>" % (
|
||
eraname, lang, eid, abbrev(eraname)))
|
||
if i == 0 and eids[1]:
|
||
write("-<br/>\n")
|
||
write("</td>\n")
|
||
write("</tr>\n")
|
||
|
||
# Races
|
||
if self.campaign == "mainline":
|
||
write("<tr>\n")
|
||
x = self.translate("Race", "wesnoth")
|
||
write("<th>%s</th></tr><tr><td>" % x)
|
||
write("<a href=\"mainline.html\">%s</a><br/>\n" % (
|
||
self.translate("all", "wesnoth-editor")))
|
||
racenames = {}
|
||
for u in self.wesnoth.unit_lookup.values():
|
||
if u.campaign != self.campaign: continue
|
||
race = u.race
|
||
racename = T(race, "plural_name")
|
||
racenames[racename] = race.get_text_val("id")
|
||
racenames = racenames.items()
|
||
racenames.sort()
|
||
|
||
for racename, rid in racenames:
|
||
write(" <a href=\"mainline.html#%s\">%s</a><br/>" % (
|
||
racename, racename))
|
||
write("</td>\n")
|
||
write("</tr>\n")
|
||
|
||
# Languages
|
||
write("<tr>\n")
|
||
x = self.translate("Language", "wesnoth")
|
||
write("<th>%s</th></tr><tr><td>" % x)
|
||
for lang in langlist:
|
||
labb = lang
|
||
underscore = labb.find("_")
|
||
if underscore > 0: labb = labb[:underscore]
|
||
write(" <a title=\"%s\" href=\"../%s/%s\">%s</a><br/>\n" % (
|
||
languages[lang], lang, self.target,
|
||
labb))
|
||
write("</td>\n")
|
||
write("</tr>\n")
|
||
|
||
write("</table>\n")
|
||
|
||
def pic(self, u, x):
|
||
image = self.wesnoth.get_unit_value(x, "image")
|
||
if not image:
|
||
if x.name == "female":
|
||
baseunit = self.wesnoth.get_base_unit(u)
|
||
if baseunit:
|
||
female = baseunit.get_all(tag = "female")
|
||
return self.pic(u, female[0])
|
||
else:
|
||
return self.pic(u, u)
|
||
error_message(
|
||
"Warning: Missing image for unit %s(%s).\n" % (
|
||
u.get_text_val("id"), x.name))
|
||
return None
|
||
picname = image_collector.add_image(u.campaign, image)
|
||
image = os.path.join("../pics", picname)
|
||
return image
|
||
|
||
def get_abilities(self, u):
|
||
anames = []
|
||
already = {}
|
||
for abilities in u.get_all(tag = "abilities"):
|
||
try: c = abilities.get_all()
|
||
except AttributeError: c = []
|
||
for ability in c:
|
||
id = ability.get_text_val("id")
|
||
if id in already: continue
|
||
already[id] = True
|
||
name = T(ability, "name")
|
||
if not name: name = id
|
||
if not name: name = ability.name
|
||
anames.append(name)
|
||
return anames
|
||
|
||
def get_recursive_attacks(self, this_unit):
|
||
|
||
def copy_attributes(copy_from, copy_to):
|
||
for c in copy_from.data:
|
||
if isinstance(c, wmlparser2.AttributeNode):
|
||
copy_to.data.append(c)
|
||
|
||
# Use attacks of base_units as base, if we have one.
|
||
base_unit = self.wesnoth.get_base_unit(this_unit)
|
||
attacks = []
|
||
if base_unit:
|
||
attacks = copy.deepcopy(self.get_recursive_attacks(base_unit))
|
||
|
||
base_attacks_count = len(attacks)
|
||
for i, attack in enumerate(this_unit.get_all(tag = "attack")):
|
||
# Attack merging is order based.
|
||
if i < base_attacks_count:
|
||
copy_attributes(attack, attacks[i])
|
||
else:
|
||
attacks.append(attack)
|
||
|
||
return attacks
|
||
|
||
def write_units(self):
|
||
def write(x): self.output.write(x)
|
||
def _(x): return self.translate(x, "wesnoth")
|
||
rows = self.unitgrid
|
||
write("<table class=\"units\">\n")
|
||
write("<colgroup>")
|
||
for i in range(6):
|
||
write("<col class=\"col%d\" />" % i)
|
||
write("</colgroup>")
|
||
|
||
pic = image_collector.add_image("mainline",
|
||
"../../../images/misc/leader-crown.png")
|
||
crownimage = os.path.join("../pics", pic)
|
||
ms = None
|
||
for row in range(len(rows)):
|
||
write("<tr>\n")
|
||
for column in range(6):
|
||
hspan, vspan, un = rows[row][column]
|
||
if vspan:
|
||
attributes = ""
|
||
if hspan == 1 and vspan == 1:
|
||
pass
|
||
elif hspan == 1:
|
||
attributes += " rowspan=%d" % vspan
|
||
elif vspan == 1:
|
||
attributes += " colspan=%d" % hspan
|
||
|
||
if un and isinstance(un, helpers.GroupNode):
|
||
# Find the current multiplayer side so we can show the
|
||
# little crowns..
|
||
ms = None
|
||
try:
|
||
eid, fid = un.data
|
||
era = self.wesnoth.era_lookup[eid]
|
||
ms = era.faction_lookup[fid]
|
||
except TypeError:
|
||
pass
|
||
racename = un.name
|
||
attributes += " class=\"raceheader\""
|
||
write("<td%s>" % attributes)
|
||
write("<a name=\"%s\">%s</a>" % (racename, racename))
|
||
write("</td>\n")
|
||
elif un:
|
||
u = un.unit
|
||
attributes += " class=\"unitcell\""
|
||
write("<td%s>" % attributes)
|
||
|
||
uid = u.get_text_val("id")
|
||
def uval(name):
|
||
return self.wesnoth.get_unit_value(u, name,
|
||
translation = self.translation.translate)
|
||
name = uval("name")
|
||
cost = uval("cost")
|
||
hp = uval("hitpoints")
|
||
mp = uval("movement")
|
||
xp = uval("experience")
|
||
level = uval("level")
|
||
|
||
crown = ""
|
||
if ms:
|
||
if un.id in ms.units:
|
||
crown = u" ♟"
|
||
if un.id in ms.is_leader:
|
||
crown = u" ♚"
|
||
|
||
link = "../%s/%s.html" % (self.isocode, uid)
|
||
write("<div class=\"i\"><a href=\"%s\" title=\"id=%s\">%s</a>" % (
|
||
link, uid, u"ℹ"))
|
||
write("</div>")
|
||
write("<div class=\"l\">L%s%s</div>" % (level, crown))
|
||
write("<a href=\"%s\">%s</a><br/>" % (link, name))
|
||
|
||
write('<div class="pic">')
|
||
image = self.pic(u, u)
|
||
|
||
write('<a href=\"%s\">' % link)
|
||
|
||
if crown == u" ♚":
|
||
write('<div style="background: url(%s)">' % image)
|
||
write('<img src="%s" alt="(image)" />' % crownimage)
|
||
write("</div>")
|
||
else:
|
||
write('<img src="%s" alt="(image)" />' % image)
|
||
|
||
write('</a>\n</div>\n')
|
||
write("<div class=\"attributes\">")
|
||
write("%s%s<br />" % (_("Cost: "), cost))
|
||
write("%s%s<br />" % (_("HP: "), hp))
|
||
write("%s%s<br />" % (_("MP: "), mp))
|
||
write("%s%s<br />" % (_("XP: "), xp))
|
||
|
||
# Write info about abilities.
|
||
anames = self.get_abilities(u)
|
||
if anames:
|
||
write("\n<div style=\"clear:both\">")
|
||
write(", ".join(anames))
|
||
write("</div>")
|
||
|
||
# Write info about attacks.
|
||
write("\n<div style=\"clear:both\">")
|
||
attacks = self.get_recursive_attacks(u)
|
||
for attack in attacks:
|
||
|
||
n = T(attack, "number")
|
||
x = T(attack, "damage")
|
||
x = "%s - %s" % (x, n)
|
||
write("%s " % x)
|
||
|
||
r = T(attack, "range")
|
||
t = T(attack, "type")
|
||
write("%s (%s)" % (_(r), _(t)))
|
||
|
||
s = []
|
||
specials = attack.get_all(tag = "specials")
|
||
if specials:
|
||
for special in specials[0].get_all():
|
||
sname = T(special, "name")
|
||
if sname:
|
||
s.append(sname)
|
||
s = ", ".join(s)
|
||
if s: write(" (%s)" % s)
|
||
write("<br />")
|
||
write("</div>")
|
||
|
||
write("</div>")
|
||
write("</td>\n")
|
||
else:
|
||
write("<td class=\"empty\"></td>")
|
||
write("</tr>\n")
|
||
write("</table>\n")
|
||
|
||
def write_units_tree(self, grouper, title):
|
||
self.output.write(html_header % {"path" : "../"})
|
||
|
||
self.analyze_units(grouper)
|
||
self.write_navbar()
|
||
|
||
self.output.write("<h1>%s</h1>" % title)
|
||
|
||
self.write_units()
|
||
|
||
self.output.write(html_footer)
|
||
|
||
def write_unit_report(self, output, unit):
|
||
def write(x): self.output.write(x)
|
||
def _(x): return self.translate(x, "wesnoth")
|
||
|
||
def find_attr(what, key):
|
||
if unit.movetype:
|
||
mtx = unit.movetype.get_all(tag = what)
|
||
mty = None
|
||
if mtx:
|
||
mty = mtx[0].get_text_val(key)
|
||
x = unit.get_all(att = what)
|
||
y = None
|
||
if x:
|
||
y = x[0].get_text_val(key,
|
||
translation = self.translation.translate)
|
||
if y:
|
||
return True, y
|
||
if unit.movetype and mty != None:
|
||
return False, mty
|
||
return False, "-"
|
||
|
||
self.output = output
|
||
write(html_header % {"path" : "../"})
|
||
self.write_navbar()
|
||
|
||
def uval(name):
|
||
return self.wesnoth.get_unit_value(unit, name,
|
||
translation = self.translation.translate)
|
||
|
||
# Write unit name, picture and description.
|
||
uid = unit.get_text_val("id")
|
||
uname = uval("name")
|
||
display_name = uname
|
||
|
||
female = unit.get_all(tag = "female")
|
||
if female:
|
||
fname = T(female[0], "name")
|
||
if fname and fname != uname:
|
||
display_name += "<br/>" + fname
|
||
|
||
write("<h1>%s</h1>\n" % display_name)
|
||
|
||
write('<div class="pic">')
|
||
if female:
|
||
mimage = self.pic(unit, unit)
|
||
fimage = self.pic(unit, female[0])
|
||
if not fimage: fimage = mimage
|
||
write('<img src="%s" alt="(image)" />\n' % mimage)
|
||
write('<img src="%s" alt="(image)" />\n' % fimage)
|
||
else:
|
||
image = self.pic(unit, unit)
|
||
write('<img src="%s" alt="(image)" />\n' % image)
|
||
write('</div>\n')
|
||
|
||
description = uval("description")
|
||
|
||
# TODO: what is unit_description?
|
||
if not description: description = uval("unit_description")
|
||
if not description: description = "-"
|
||
write("<p>%s</p>\n" % re.sub("\n","\n<br />", description) )
|
||
|
||
# Base info.
|
||
hp = uval("hitpoints")
|
||
mp = uval("movement")
|
||
xp = uval("experience")
|
||
level = uval("level")
|
||
alignment = uval("alignment")
|
||
|
||
write("<table class=\"unitinfo\">\n")
|
||
write("<tr>\n")
|
||
write("<th>%s" % _("Advances from: "))
|
||
write("</th><td>\n")
|
||
for pid in self.forest.get_parents(uid):
|
||
punit = self.wesnoth.unit_lookup[pid]
|
||
if unit.campaign == "mainline" and punit.campaign != "mainline":
|
||
continue
|
||
link = "../%s/%s.html" % (self.isocode, pid)
|
||
name = self.wesnoth.get_unit_value(punit, "name",
|
||
translation = self.translation.translate)
|
||
write("\n<a href=\"%s\">%s</a>" % (link, name))
|
||
write("</td>\n")
|
||
write("</tr><tr>\n")
|
||
write("<th>%s" % _("Advances to: "))
|
||
write("</th><td>\n")
|
||
for cid in self.forest.get_children(uid):
|
||
link = "../%s/%s.html" % (self.isocode, cid)
|
||
try:
|
||
cunit = self.wesnoth.unit_lookup[cid]
|
||
if unit.campaign == "mainline" and cunit.campaign != "mainline":
|
||
continue
|
||
name = self.wesnoth.get_unit_value(cunit, "name",
|
||
translation = self.translation.translate)
|
||
except KeyError:
|
||
error_message("Warning: Unit %s not found.\n" % cid)
|
||
name = cid
|
||
if unit.campaign == "mainline": continue
|
||
write("\n<a href=\"%s\">%s</a>" % (link, name))
|
||
write("</td>\n")
|
||
write("</tr>\n")
|
||
|
||
for val, text in [
|
||
("cost", _("Cost: ")),
|
||
("hitpoints", _("HP: ")),
|
||
("movement", _("Movement") + ": "),
|
||
("experience", _("XP: ")),
|
||
("level", _("Level") + ": "),
|
||
("alignment", _("Alignment: ")),
|
||
("id", "ID")]:
|
||
write("<tr>\n")
|
||
write("<th>%s</th>" % text)
|
||
x = uval(val)
|
||
if val == "alignment": x = _(x)
|
||
write("<td>%s</td>" % x)
|
||
write("</tr>\n")
|
||
|
||
# Write info about abilities.
|
||
anames = self.get_abilities(unit)
|
||
|
||
write("<tr>\n")
|
||
write("<th>%s</th>" % _("Abilities: "))
|
||
write("<td>" + (", ".join(anames)) + "</td>")
|
||
write("</tr>\n")
|
||
|
||
write("</table>\n")
|
||
|
||
# Write info about attacks.
|
||
write("<table class=\"unitinfo\">\n")
|
||
|
||
attacks = self.get_recursive_attacks(unit)
|
||
for attack in attacks:
|
||
write("<tr>")
|
||
|
||
aid = attack.get_text_val("name")
|
||
aname = T(attack, "description")
|
||
|
||
icon = attack.get_text_val("icon")
|
||
if not icon:
|
||
icon = "attacks/%s.png" % aid
|
||
|
||
picname, error = image_collector.add_image_check(unit.campaign, icon)
|
||
if error:
|
||
error_message("Error: No attack icon '%s' found for '%s'.\n" % (
|
||
icon, uid))
|
||
icon = os.path.join("../pics", picname)
|
||
write("<td><img src=\"%s\" alt=\"(image)\"/></td>" % icon)
|
||
|
||
write("<td>%s" % aname)
|
||
|
||
t = T(attack, "type")
|
||
write("<br/>%s</td>" % _(t))
|
||
|
||
n = attack.get_text_val("number")
|
||
x = attack.get_text_val("damage")
|
||
x = "%s - %s" % (x, n)
|
||
write("<td>%s" % x)
|
||
|
||
r = T(attack, "range")
|
||
write("<br/>%s</td>" % _(r))
|
||
|
||
s = []
|
||
specials = attack.get_all(tag = "specials")
|
||
if specials:
|
||
for special in specials[0].get_all():
|
||
sname = T(special, "name")
|
||
if sname:
|
||
s.append(sname)
|
||
else:
|
||
error_message(
|
||
"Warning: Weapon special %s has no name for %s.\n" % (
|
||
special.name, uid))
|
||
s = "<br/>".join(s)
|
||
write("<td>%s</td>" % s)
|
||
|
||
|
||
write("</tr>")
|
||
write("</table>\n")
|
||
|
||
# Write info about resistances.
|
||
resistances = [
|
||
"blade",
|
||
"pierce",
|
||
"impact",
|
||
"fire",
|
||
"cold",
|
||
"arcane"]
|
||
|
||
write("<table class=\"unitinfo\">\n")
|
||
write("<tr>\n")
|
||
write("<th colspan=\"2\">%s</th>\n" % _("Resistances: "))
|
||
|
||
write("</tr>\n")
|
||
for rid in resistances:
|
||
special, r = find_attr("resistance", rid)
|
||
if r == "-": r = 100
|
||
try: r = "%d%%" % (100 - int(r))
|
||
except ValueError:
|
||
error_message("Warning: Invalid resistance %s for %s.\n" % (
|
||
r, uid))
|
||
rcell = "td"
|
||
if special: rcell += ' class="special"'
|
||
write("<tr>\n")
|
||
write("<th>%s</th><td>%s</td>\n" % (_(rid), r))
|
||
write("</tr>\n")
|
||
write("</table>\n")
|
||
|
||
# Write info about movement costs and terrain defense.
|
||
write("<table class=\"unitinfo\">\n")
|
||
write("<tr>\n")
|
||
write("<th>%s</th><th>%s</th><th>%s</th>\n" % (
|
||
_("Terrain"), _("Movement Cost"), _("Defense") ))
|
||
write("</tr>\n")
|
||
|
||
terrains = self.wesnoth.terrain_lookup
|
||
terrainlist = []
|
||
for tstring, t in terrains.items():
|
||
tid = t.get_text_val("id")
|
||
if tid in ["off_map", "fog", "shroud"]: continue
|
||
if t.get_all(att = "aliasof"): continue
|
||
name = T(t, "name")
|
||
terrainlist.append((name, tid))
|
||
terrainlist.sort()
|
||
|
||
for tname, tid in terrainlist:
|
||
not_from_race, c = find_attr("movement_costs", tid)
|
||
|
||
ccell = "td"
|
||
if c == "99": ccell += ' class="grayed"'
|
||
dcell = "td"
|
||
not_from_race, d = find_attr("defense", tid)
|
||
|
||
if d == "-": d = 100
|
||
try: d = "%d%%" % (100 - int(d))
|
||
except ValueError:
|
||
error_message("Warning: Invalid defense %s for %s.\n" % (
|
||
d, uid))
|
||
|
||
write("<tr>\n")
|
||
write("<td>%s</td><%s>%s</td><%s>%s</td>\n" % (
|
||
tname, ccell, c, dcell, d))
|
||
write("</tr>\n")
|
||
write("</table>\n")
|
||
|
||
write(html_footer)
|
||
|
||
languages_found = {}
|
||
def find_languages():
|
||
"""
|
||
Returns a dictionary mapping isocodes to languages.
|
||
"""
|
||
global languages
|
||
if languages_found: return languages_found
|
||
parser = wmlparser2.Parser(options.wesnoth, options.config_dir,
|
||
options.data_dir, no_preprocess = False)
|
||
parser.parse_text("{languages}")
|
||
|
||
for locale in parser.get_all(tag = "locale"):
|
||
isocode = locale.get_text_val("locale")
|
||
name = locale.get_text_val("name")
|
||
languages_found[isocode] = name
|
||
return languages_found
|
||
|
||
class MyFile:
|
||
"""
|
||
I don't understand why this is needed..
|
||
"""
|
||
def __init__(self, filename, mode):
|
||
self.f = open(filename, mode)
|
||
def write(self, x):
|
||
x = x.encode("utf8")
|
||
self.f.write(x)
|
||
def close(self):
|
||
self.f.close()
|
||
|
||
def generate_campaign_report(out_path, isocode, campaign, wesnoth):
|
||
path = os.path.join(out_path, isocode )
|
||
if not os.path.isdir(path): os.mkdir(path)
|
||
output = MyFile(os.path.join(path, "%s.html" % campaign), "w")
|
||
html = HTMLOutput(isocode, output, campaign, wesnoth)
|
||
html.target = "%s.html" % campaign
|
||
grouper = GroupByRace(wesnoth, campaign)
|
||
|
||
if campaign == "mainline":
|
||
title = html.translate("Multiplayer", "wesnoth")
|
||
else:
|
||
title = wesnoth.campaign_lookup[campaign].get_text_val("name",
|
||
translation = html.translate)
|
||
if not title:
|
||
title = campaign
|
||
|
||
html.write_units_tree(grouper, title)
|
||
|
||
def generate_era_report(out_path, isocode, eid, wesnoth):
|
||
path = os.path.join(out_path, isocode)
|
||
if not os.path.isdir(path): os.mkdir(path)
|
||
|
||
output = MyFile(os.path.join(path, "%s.html" % eid), "w")
|
||
html = HTMLOutput(isocode, output, eid, wesnoth)
|
||
html.target = "%s.html" % eid
|
||
grouper = GroupByFaction(wesnoth, eid)
|
||
era = wesnoth.era_lookup[eid]
|
||
ename = era.get_text_val("name", translation = html.translate)
|
||
html.write_units_tree(grouper, ename)
|
||
|
||
def generate_single_unit_reports(out_path, isocode, wesnoth):
|
||
|
||
odir = os.path.join(out_path, isocode)
|
||
if not os.path.isdir(odir):
|
||
os.mkdir(odir)
|
||
|
||
html = HTMLOutput(isocode, None, "units", wesnoth)
|
||
grouper = GroupByRace(wesnoth, None)
|
||
html.analyze_units(grouper)
|
||
|
||
for uid, unit in wesnoth.unit_lookup.items():
|
||
if isocode != "C" and not uid in wesnoth.is_mainline_unit: continue
|
||
output = MyFile(os.path.join(odir, "%s.html" % uid), "w")
|
||
html.target = "%s.html" % uid
|
||
html.write_unit_report(output, unit)
|
||
|
||
def write_index(out_path):
|
||
output = MyFile(os.path.join(out_path, "index.html"), "w")
|
||
output.write("""
|
||
<html><head>
|
||
<meta http-equiv="refresh" content="0;url=C/mainline.html">
|
||
</head>
|
||
<body>
|
||
<a href="C/mainline.html">Redirecting to Wesnoth units database...</a>
|
||
</body>
|
||
</html>
|
||
""")
|
||
|
||
def copy_images():
|
||
if not options.nocopy:
|
||
print("Recolorizing pictures.")
|
||
image_collector.copy_and_color_images(options.output)
|
||
shutil.copy2(os.path.join(image_collector.datadir,
|
||
"data/tools/unit_tree/style.css"), options.output)
|
||
for grab in [
|
||
"http://www.wesnoth.org/mw/skins/glamdrol/headerbg.jpg",
|
||
"http://www.wesnoth.org/mw/skins/glamdrol/wesnoth-logo.jpg",
|
||
"http://www.wesnoth.org/mw/skins/glamdrol/navbg.png"]:
|
||
local = os.path.join(options.output, grab[grab.rfind("/") + 1:])
|
||
if not os.path.exists(local):
|
||
print "Fetching", grab
|
||
url = urllib2.urlopen(grab)
|
||
file(local, "w").write(url.read())
|
||
|
||
def parse_game():
|
||
stuff = helpers.WesnothList(
|
||
options.wesnoth,
|
||
options.config_dir,
|
||
options.data_dir,
|
||
options.transdir)
|
||
|
||
def p(x): sys.stdout.write(x); sys.stdout.flush()
|
||
def pn(x): print(x)
|
||
|
||
# Parse some stuff we may need.
|
||
p("Parsing terrains ... ")
|
||
n = stuff.add_terrains()
|
||
pn("%d terrains found." % n)
|
||
|
||
p("Parsing mainline eras ... ")
|
||
n = stuff.add_mainline_eras()
|
||
pn("%d mainline eras found." % n)
|
||
stuff.is_mainline_era = {}
|
||
for c in stuff.era_lookup.keys():
|
||
stuff.is_mainline_era[c] = True
|
||
|
||
stuff.is_mainline_campaign = {}
|
||
if options.campaigns:
|
||
p("Parsing mainline campaigns ... ")
|
||
n = stuff.add_mainline_campaigns()
|
||
pn("%d mainline campaigns found." % n)
|
||
for c in stuff.campaign_lookup.keys():
|
||
stuff.is_mainline_campaign[c] = True
|
||
|
||
# Parse all unit data
|
||
# This reads in units.cfg, giving us all the mainline units (and races).
|
||
p("Parsing mainline units ... ")
|
||
n = stuff.add_mainline_units()
|
||
pn("%d mainline units found." % n)
|
||
|
||
# Now we read each mainline campaign in turn to get its units.
|
||
cnames = stuff.campaign_lookup.keys()
|
||
for cname in cnames:
|
||
p(" Parsing %s units ... " % cname)
|
||
n = stuff.add_campaign_units(cname, image_collector)
|
||
pn("%d units found." % n)
|
||
|
||
mainline_campaigns = set(stuff.campaign_lookup.keys())
|
||
|
||
stuff.is_mainline_unit = {}
|
||
for uid in stuff.unit_lookup.keys():
|
||
stuff.is_mainline_unit[uid] = True
|
||
|
||
if options.addons:
|
||
p("Parsing addons ... ")
|
||
n = stuff.add_addons(image_collector)
|
||
pn("%d campaigns, %d eras, %d multiplayer units found." % n)
|
||
|
||
# Now we read each addon campaign in turn to get its units.
|
||
cnames = stuff.campaign_lookup.keys()
|
||
for cname in cnames:
|
||
if cname in mainline_campaigns: continue
|
||
campaign = stuff.campaign_lookup[cname]
|
||
p(" Parsing %s units ... " % cname)
|
||
n = stuff.add_addon_campaign_units(cname, image_collector)
|
||
pn("%d units found." % n)
|
||
|
||
stuff.find_unit_factions()
|
||
|
||
return stuff
|
||
|
||
def generate_report(stuff, isocode):
|
||
"""
|
||
We output:
|
||
* All mainline units sorted by race
|
||
* Each mainline era's units sorted by faction
|
||
* Each mainline campaign's units sorted by race
|
||
* Each addon era's units sorted by faction
|
||
* Each addon campaign's units sorted by race
|
||
"""
|
||
|
||
print "Generating report for %s." % (isocode)
|
||
reset_errors()
|
||
|
||
# Report generation
|
||
write_index(options.output)
|
||
|
||
campaigns = stuff.campaign_lookup.keys()
|
||
|
||
generate_campaign_report(options.output, isocode, "mainline", stuff)
|
||
|
||
for campaign in campaigns:
|
||
if isocode != "C" and not campaign in stuff.is_mainline_campaign: continue
|
||
generate_campaign_report(options.output, isocode, campaign, stuff)
|
||
|
||
for eid in stuff.era_lookup.keys():
|
||
if isocode != "C" and not eid in stuff.is_mainline_era: continue
|
||
generate_era_report(options.output, isocode, eid, stuff)
|
||
|
||
# Single unit reports.
|
||
generate_single_unit_reports(options.output, isocode, stuff)
|
||
|
||
if __name__ == '__main__':
|
||
|
||
# We change the process name to "wmlunits"
|
||
try:
|
||
import ctypes
|
||
libc = ctypes.CDLL("libc.so.6")
|
||
libc.prctl(15, "wmlunits", 0, 0, 0)
|
||
except: # oh well...
|
||
pass
|
||
|
||
global options
|
||
global image_collector
|
||
|
||
gc.disable()
|
||
|
||
op = optparse.OptionParser()
|
||
op.add_option("-C", "--config-dir",
|
||
help = "Specify the user configuration dir (wesnoth --config-path).")
|
||
op.add_option("-D", "--data-dir",
|
||
help = "Specify the wesnoth data dir (wesnoth --path).")
|
||
op.add_option("-l", "--language", default = "all",
|
||
help = "Specify a language to use. Else outputs is produced for all languages.")
|
||
op.add_option("-o", "--output",
|
||
help = "Specify the output directory.")
|
||
op.add_option("-n", "--nocopy", action = "store_true",
|
||
help = "No copying of files. By default all images are copied to the output dir.")
|
||
op.add_option("-w", "--wesnoth",
|
||
help = "Specify the wesnoth executable to use. Whatever data " +
|
||
"and config paths that executable is configured for will be " +
|
||
"used to find game files and addons.")
|
||
op.add_option("-t", "--transdir",
|
||
help = "Specify the directory with gettext message catalogues. " +
|
||
"Defaults to ./translations.", default = "translations")
|
||
op.add_option("-c", "--campaigns", action = "store_true",
|
||
help = "Include units from mainline campaigns.")
|
||
op.add_option("-a", "--addons", action = "store_true",
|
||
help = "Include also addon units (from all installed eras and campaigns). " +
|
||
"Note that some add-ons may try to modify stock units but such changes are " +
|
||
"not reflected in this version of wmlunits.")
|
||
options, args = op.parse_args()
|
||
|
||
if not options.output:
|
||
op.print_help()
|
||
sys.exit(-1)
|
||
|
||
options.output = os.path.expanduser(options.output)
|
||
|
||
if not options.wesnoth:
|
||
options.wesnoth = "wesnoth"
|
||
|
||
if not options.transdir:
|
||
options.transdir = os.getcwd()
|
||
|
||
if options.language == "all":
|
||
languages = find_languages().keys()
|
||
languages.sort()
|
||
else:
|
||
languages = [options.language]
|
||
|
||
image_collector = helpers.ImageCollector(options.wesnoth,
|
||
options.config_dir, options.data_dir)
|
||
|
||
wesnoth = parse_game()
|
||
|
||
# Generate output dir.
|
||
if not os.path.isdir(options.output):
|
||
os.mkdir(options.output)
|
||
|
||
# Store rid and id for each unit so we have it easier.
|
||
for u in wesnoth.unit_lookup.values():
|
||
r = u.race
|
||
if r: r = r.get_text_val("id", "none")
|
||
else: r = "none"
|
||
u.rid = r
|
||
u.id = u.get_text_val("id")
|
||
|
||
# Write a list with all unit ids, just for fun.
|
||
uids = wesnoth.unit_lookup.keys()
|
||
def by_race(u1, u2):
|
||
r = cmp(wesnoth.unit_lookup[u1].rid,
|
||
wesnoth.unit_lookup[u2].rid)
|
||
if r == 0: r = cmp(u1, u2)
|
||
return r
|
||
uids.sort(by_race)
|
||
race = None
|
||
f = MyFile(os.path.join(options.output, "uids.html"), "w")
|
||
f.write("<html><body>")
|
||
for uid in uids:
|
||
u = wesnoth.unit_lookup[uid]
|
||
if u.rid != race:
|
||
if race != None: f.write("</ul>")
|
||
f.write("<p>%s</p>\n" % u.rid)
|
||
f.write("<ul>")
|
||
race = u.rid
|
||
f.write("<li>%s</li>\n" % uid)
|
||
f.write("</ul>")
|
||
f.write("</body></html>")
|
||
f.close()
|
||
|
||
# Write animation statistics
|
||
f = MyFile(os.path.join(options.output, "animations.html"), "w")
|
||
animations.write_table(f, wesnoth)
|
||
f.close()
|
||
|
||
# Now finally lets do the unit reports in all languages.
|
||
for isocode in languages:
|
||
generate_report(wesnoth, isocode)
|
||
|
||
if not options.nocopy:
|
||
copy_images()
|