[wmlunits] Updated wmlunits to use the new --preprocess parser.

Now all add-ons which can be played with wesnoth can be parsed by
wmlunits (only those).
This commit is contained in:
Elias Pschernig 2010-05-28 15:39:48 +00:00
parent 37792aa611
commit 26a3dc2046
3 changed files with 396 additions and 395 deletions

View file

@ -8,15 +8,15 @@ def write_animation(out, aa, name):
out.write("%d" % c[0])
def count_animations(out, name, a, c):
frames = a.get_subs("frame")
frames = a.get_all(tag = "frame")
if frames:
c[0] += len(frames)
c[1] += 1
for a2 in a.get_subs("animation"):
for a2 in a.get_all(tag = "animation"):
count_animations(out, name, a2, c)
for a2 in a.get_subs("if"):
for a2 in a.get_all(tag = "if"):
count_animations(out, name, a2, c)
for a2 in a.get_subs("else"):
for a2 in a.get_all(tag = "else"):
count_animations(out, name, a2, c)
def write_table_row(out, unit, color, name = None):
@ -54,12 +54,12 @@ def write_table_row(out, unit, color, name = None):
needed["healing_anim"] = False
needed["leading_anim"] = False
needed["teleport"] = False
for abil in unit.get_subs("abilities"):
if abil.get_subs("heals"):
for abil in unit.get_all(tag = "abilities"):
if abil.get_all(tag = "heals"):
needed["healing_anim"] = True
if abil.get_subs("leadership"):
if abil.get_all(tag = "leadership"):
needed["leading_anim"] = True
if abil.get_subs("teleport"):
if abil.get_all(tag = "teleport"):
needed["teleport"] = True
if name == None: name = unit.id
@ -68,7 +68,7 @@ def write_table_row(out, unit, color, name = None):
for t in anim_types:
if needed[t]:
aa = unit.get_subs(t)
aa = unit.get_all(tag = t)
if t == "extra_anim":
out.write("<td class=\"none\">")
else:
@ -80,10 +80,10 @@ def write_table_row(out, unit, color, name = None):
out.write("</tr>\n")
female = unit.get_first("female")
if female: write_table_row(out, female, color, name + "[f]")
female = unit.get_all(tag = "female")
if female: write_table_row(out, female[0], color, name + "[f]")
for variation in unit.get_all("variation"):
for variation in unit.get_all(tag = "variation"):
write_table_row(out, variation, color, name + "[%s]" %
variation.get_text_val("variation_name"))
@ -109,12 +109,12 @@ def put_units(f, us):
<th>level out</th>
<th>extra</th>
<th>animation</th>
<th>resistance_anim</th>
<th>recruiting_anim</th>
<th>pre_movement_anim</th>
<th>post_movement_anim</th>
<th>draw_weapon_anim</th>
<th>sheath_weapon_anim</th>
<th>resistance</th>
<th>recruiting</th>
<th>pre movement</th>
<th>post movement</th>
<th>draw weapon</th>
<th>sheath weapon</th>
""".lstrip())
f.write("</tr>\n")

View file

@ -1,98 +1,63 @@
"""
Various helpers for use by the wmlunits tool.
"""
import sys, os, re, glob, shutil, copy, urllib2
from subprocess import Popen
import sys, os, re, glob, shutil, copy, urllib2, subprocess
import wesnoth.wmldata as wmldata
import wesnoth.wmlparser as wmlparser
import wesnoth.wmlparser2 as wmlparser2
import wesnoth.wmltools as wmltools
class ParserWithCoreMacros:
"""
A wrapper around the WML parser to do some things like we want.
"""
def __init__(self, isocode, datadir, userdir, transdir):
self.datadir = datadir
self.userdir = userdir
# Handle translations.
self.translations = wmltools.Translations(transdir)
def gettext(textdomain, x):
return self.translations.get(textdomain, isocode, x, x)
self.gettext = gettext
def get_datadir(wesnoth_exe):
p = subprocess.Popen([wesnoth_exe, "--path"],
stdout = subprocess.PIPE, stderr = subprocess.PIPE)
out, err = p.communicate()
return out.strip()
# Create a new parser for the macros.
parser = wmlparser.Parser(datadir)
parser.gettext = self.gettext
# Parse core macros.
parser.parse_text("{core/macros/}\n")
parser.parse_top(None)
self.core_macros = parser.macros
def parse(self, text_to_parse, ignore_macros = None,
ignore_fatal_errors = False, verbose = False):
# Create the real parser.
parser = wmlparser.Parser(self.datadir, self.userdir)
parser.verbose = verbose
parser.gettext = self.gettext
parser.macros = copy.copy(self.core_macros)
#parser.verbose = True
# Suppress complaints about undefined terrain macros
parser.set_macro_not_found_callback(lambda wmlparser, name, params:
name.startswith("TERRAIN") or name == "DISABLE_TRANSITIONS")
if ignore_macros:
parser.macro_callback = ignore_macros
# Create a WML root element and parse the given text into it.
WML = wmldata.DataSub("WML")
parser.parse_text(text_to_parse)
parser.parse_top(WML)
return WML
def get_userdir(wesnoth_exe):
p = subprocess.Popen([wesnoth_exe, "--config-path"],
stdout = subprocess.PIPE, stderr = subprocess.PIPE)
out, err = p.communicate()
return out.strip()
class ImageCollector:
"""
A class to collect all the images which need to be copied to the HTML
output folder.
"""
def __init__(self, datadir, userdir):
def __init__(self, wesnoth_exe):
self.images = {}
self.pathes_per_campaign = {}
self.ipathes = {}
self.paths_per_campaign = {}
self.ipaths = {}
self.notfound = {}
self.datadir = datadir
self.userdir = userdir
self.id = 0
self.verbose = 0
self.datadir = get_datadir(wesnoth_exe)
self.userdir = get_userdir(wesnoth_exe)
def add_binary_pathes_from_WML(self, campaign, WML):
self.pathes_per_campaign[campaign] = self.pathes_per_campaign.get(
def add_binary_paths_from_WML(self, campaign, WML):
self.paths_per_campaign[campaign] = self.paths_per_campaign.get(
campaign, [])
for binpath in WML.get_all("binary_path"):
for binpath in WML.get_all(tag = "binary_path"):
path = binpath.get_text_val("path")
self.pathes_per_campaign[campaign].append(path)
self.paths_per_campaign[campaign].append(path)
def find_image(self, i, c):
if c == "mainline":
bases = [os.path.join(self.datadir, "core/images")]
bases = [os.path.join(self.datadir, "data/core/images")]
else:
bases = [os.path.join(self.datadir, "core/images")]
binpaths = self.pathes_per_campaign.get(c, [])
bases = [os.path.join(self.datadir, "data/core/images")]
binpaths = self.paths_per_campaign.get(c, [])
binpaths.reverse()
for x in binpaths:
if x.startswith("data/"):
for idir in ["images", "images/units"]:
bases.append(os.path.join(self.datadir, x[5:], idir))
bases.append(os.path.join(self.datadir, x, idir))
if self.userdir:
bases.append(os.path.join(self.userdir, x[5:], idir))
bases.append(os.path.join(self.userdir, x, idir))
for base in bases:
tilde = i.find("~")
if tilde >= 0:
i = i[:tilde]
ipath = os.path.join("%s" % base, i)
if os.path.exists(ipath): return ipath, None
return None, bases
@ -101,8 +66,8 @@ class ImageCollector:
if (campaign, path) in self.notfound:
return self.notfound[(campaign, path)], True
ipath, error = self.find_image(path, campaign)
if ipath in self.ipathes:
return self.ipathes[ipath], False
if ipath in self.ipaths:
return self.ipaths[ipath], False
name = "%05d_" % self.id
name += os.path.basename(path)
@ -110,7 +75,7 @@ class ImageCollector:
self.images[name] = ipath, path, campaign, error
if ipath:
self.ipathes[ipath] = name
self.ipaths[ipath] = name
return name, False
else:
self.notfound[(campaign, path)] = name
@ -135,7 +100,7 @@ class ImageCollector:
# helpers.py currently executing.
command = os.path.join(os.path.dirname(__file__),
"TeamColorizer")
p = Popen([command, ipath, opath])
p = subprocess.Popen([command, ipath, opath])
p.wait()
else:
sys.stderr.write(
@ -150,23 +115,25 @@ class WesnothList:
"""
Lists various Wesnoth stuff like units, campaigns, terrains, factions...
"""
def __init__(self, isocode, datadir, userdir, transdir):
def __init__(self, wesnoth_exe, transdir):
self.unit_lookup = {}
self.race_lookup = {}
self.terrain_lookup = {}
self.movetype_lookup = {}
self.era_lookup = {}
self.campaign_lookup = {}
self.parser = ParserWithCoreMacros(isocode, datadir, userdir, transdir)
self.parser = wmlparser2.Parser(wesnoth_exe)
self.parser = wmlparser2.Parser(wesnoth_exe)
def add_terrains(self):
"""
We need those for movement costs and defenses.
"""
WML = self.parser.parse("{core/terrain.cfg}\n")
self.parser.parse_text("{core/terrain.cfg}\n")
n = 0
for terrain in WML.get_all("terrain_type"):
for terrain in self.parser.get_all(tag = "terrain_type"):
tstring = terrain.get_text_val("string")
self.terrain_lookup[tstring] = terrain
n += 1
@ -180,7 +147,7 @@ class WesnothList:
if not eid: return
self.era_lookup[eid] = era
era.faction_lookup = {}
for multiplayer_side in era.get_all("multiplayer_side"):
for multiplayer_side in era.get_all(tag = "multiplayer_side"):
fid = multiplayer_side.get_text_val("id")
if fid == "Random": continue
era.faction_lookup[fid] = multiplayer_side
@ -214,10 +181,10 @@ class WesnothList:
"""
Find all mainline eras.
"""
WML = self.parser.parse("{multiplayer/eras.cfg}\n")
self.parser.parse_text("{multiplayer/eras.cfg}")
n = 0
for era in WML.get_all("era"):
for era in self.parser.get_all(tag = "era"):
self.add_era(era)
n += 1
return n
@ -226,9 +193,9 @@ class WesnothList:
"""
Find all mainline campaigns.
"""
WML = self.parser.parse("{campaigns}\n")
self.parser.parse_text("{campaigns}")
n = 0
for campaign in WML.find_all("campaign"):
for campaign in self.parser.get_all(tag = "campaign"):
self.add_campaign(campaign)
n += 1
return n
@ -237,45 +204,65 @@ class WesnothList:
"""
Find all addon eras and campaigns.
"""
n = 0
try:
WML = self.parser.parse("""
#define MULTIPLAYER\n#enddef
#define RANDOM_SIDE\n#enddef
{~add-ons}
""")
except wmlparser.Error, e:
try:
print(e)
except UnicodeEncodeError:
print(e.text.encode("utf8", "ignore"))
return n
for campaign in WML.find_all("campaign"):
cid = self.add_campaign(campaign)
for era in WML.find_all("era"):
eid = self.add_era(era)
if not eid: continue
image_collector.add_binary_pathes_from_WML(eid, WML)
n = self.add_units(WML, "addons")
self.parser.parse_text("{multiplayer}{~add-ons}", "MULTIPLAYER")
image_collector.add_binary_pathes_from_WML("addons", WML)
return n
cn = 0
for campaign in self.parser.get_all(tag = "campaign"):
cid = self.add_campaign(campaign)
cn += 1
def add_units(self, WML, campaign):
en = 0
for era in self.parser.get_all(tag = "era"):
eid = self.add_era(era)
en += 1
if not eid: continue
image_collector.add_binary_paths_from_WML(eid,
self.parser.root)
un = self.add_units("addons")
image_collector.add_binary_paths_from_WML("addons",
self.parser.root)
return cn, en, un
def add_mainline_units(self):
self.parser.parse_text("{core/units.cfg}")
return self.add_units("mainline")
def add_campaign_units(self, cname, image_collector):
campaign = self.campaign_lookup[cname]
define = campaign.get_text_val("define")
self.parser.parse_text("{campaigns}", define)
image_collector.add_binary_paths_from_WML(cname,
self.parser.root)
return self.add_units(cname)
def add_addon_campaign_units(self, cname, image_collector):
campaign = self.campaign_lookup[cname]
define = campaign.get_text_val("define")
self.parser.parse_text("{~add-ons}", define)
image_collector.add_binary_paths_from_WML(cname,
self.parser.root)
return self.add_units(cname)
def add_units(self, campaign):
"""
We assume each unit, in mainline and all addons, has one unique id. So
we reference them everywhere by this id, and here can add them all to
one big collection.
"""
addunits = WML.get_all("units")
addunits = self.parser.get_all(tag = "units")
addunits += self.parser.get_all(tag = "+units")
if not addunits: return 0
def getall(oftype):
r = []
for units in addunits:
r += units.get_all(oftype)
r += units.get_all(tag = oftype)
return r
# Find all unit types.
@ -285,10 +272,10 @@ class WesnothList:
unit.get_text_val("hide_help", "no") in ["no", "false"]:
uid = unit.get_text_val("id")
if uid in self.unit_lookup:
sys.stderr.write(
("Fatal: Unit id \"%s\" already exists - either it has" +
" to be renamed, or there's a bug in this script.\n") %
uid)
pass
# TODO: We might want to compare the two units
# with the same id and if one is different try
# to do something clever like rename it...
else:
self.unit_lookup[uid] = unit
unit.campaign = campaign
@ -366,8 +353,9 @@ class WesnothList:
unit.factions.append((eid, fid))
def get_base_unit(self, unit):
b = unit.get_first("base_unit")
b = unit.get_all(tag = "base_unit")
if b:
b = b[0]
buid = b.get_text_val("id")
try: baseunit = self.unit_lookup[buid]
except KeyError:
@ -378,12 +366,12 @@ class WesnothList:
return baseunit
return None
def get_unit_value(self, unit, attribute, default = None):
value = unit.get_text_val(attribute, None)
def get_unit_value(self, unit, attribute, default = None, translation = None):
value = unit.get_text_val(attribute, None, translation)
if value == None:
baseunit = self.get_base_unit(unit)
if baseunit:
return self.get_unit_value(baseunit, attribute, default)
return self.get_unit_value(baseunit, attribute, default, translation)
return default
return value
@ -416,7 +404,6 @@ class UnitForest:
u.children.append(c)
if not uid in c.parent_ids:
c.parent_ids.append(uid)
# Put all roots into the forest
for uid, u in self.lookup.items():

View file

@ -10,13 +10,9 @@ Run without arguments to see usage.
try: import psyco; psyco.full()
except ImportError: pass
import sys, os, re, glob, shutil, copy, urllib2, gc
import sys, os, re, glob, shutil, copy, urllib2, gc, optparse, gettext
import wesnoth.wmldata as wmldata
import wesnoth.wmlparser as wmlparser
import wesnoth.wmltools as wmltools
sys.path.append(".")
import wesnoth.wmlparser2 as wmlparser2
import unit_tree.helpers as helpers
import unit_tree.animations as animations
@ -37,6 +33,34 @@ html_footer = '''
</body></html>
'''.strip()
class Translation:
def __init__(self, localedir, langcode):
self.catalog = {}
self.localedir = localedir
self.langcode = langcode
class Dummy:
def ugettext(self, x):
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
try:
r = self.catalog[textdomain].ugettext(string)
except UnicodeDecodeError as e:
print(textdomain)
print(repr(string))
print(type(string))
raise
return r
class GroupByRace:
def __init__(self, wesnoth, campaign):
self.wesnoth = wesnoth
@ -51,7 +75,7 @@ class GroupByRace:
def group_name(self, group):
if not group: return "None"
return group.get_text_val("plural_name")
return T(group, "plural_name")
class GroupByFaction:
def __init__(self, wesnoth, era):
@ -67,24 +91,31 @@ class GroupByFaction:
def group_name(self, group):
era = self.wesnoth.era_lookup[group[0]]
faction = era.faction_lookup[group[1]]
name = faction.get_text_val("name")
name = T(faction, "name")
name = name[name.rfind("=") + 1:]
name = era.get_text_val("name") + " / " + name
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):
self.isocode = isocode
global global_htmlout
self.output = output
self.campaign = campaign
self.verbose = verbose
self.target = "index.html"
self.wesnoth = wesnoth
self.forest = None
def get_translation(self, textdomain, key):
return self.wesnoth.parser.translations.get(textdomain, self.isocode,
key, key)
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):
"""
@ -148,8 +179,8 @@ class HTMLOutput:
u1 = t1.unit
u2 = t2.unit
u1name = u1.get_text_val("name")
u2name = u2.get_text_val("name")
u1name = T(u1, "name")
u2name = T(u2, "name")
return cmp(u1name, u2name)
def grid_place(nodes, x):
@ -179,7 +210,7 @@ class HTMLOutput:
def write_navbar(self):
def write(x): self.output.write(x)
def _(x): return self.get_translation("wesnoth", x)
languages = find_languages()
langlist = languages.keys()
langlist.sort()
@ -208,7 +239,7 @@ class HTMLOutput:
return abbrev
# Campaigns
x = _("TitleScreen button^Campaign")
x = self.translate("TitleScreen button^Campaign", "wesnoth")
write("<th>%s</th></tr><tr><td>" % x)
cids = [[], []]
for cid in self.wesnoth.campaign_lookup.keys():
@ -217,19 +248,20 @@ class HTMLOutput:
else:
cids[1].append(cid)
lang = self.isocode
for i in range(2):
cnames = cids[i]
cnames.sort()
for cname in cnames:
lang = self.isocode
if cname == "mainline":
campname = self.get_translation("wesnoth", "Multiplayer")
campname = self.translate("Multiplayer", "wesnoth")
campabbrev = campname
else:
campname = self.wesnoth.campaign_lookup[cname].get_text_val("name")
campname = T(self.wesnoth.campaign_lookup[cname], "name")
if not campname:
campname = cname
campabbrev = self.wesnoth.campaign_lookup[cname].get_text_val("abbrev")
campabbrev = T(self.wesnoth.campaign_lookup[cname], "abbrev")
if not campabbrev:
campabbrev = abbrev(campname)
@ -242,7 +274,7 @@ class HTMLOutput:
# Eras
write("<tr>\n")
x = _("Era")
x = self.translate("Era", "wesnoth")
write("<th>%s</th></tr><tr><td>" % x)
eids = [[], []]
for eid in self.wesnoth.era_lookup.keys():
@ -251,12 +283,12 @@ class HTMLOutput:
else:
eids[1].append(eid)
for i in range(2):
eranames = [(self.wesnoth.era_lookup[eid].get_text_val("name"),
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)))
eraname, lang, eid, abbrev(eraname)))
if i == 0 and eids[1]:
write("-<br/>\n")
write("</td>\n")
@ -265,15 +297,15 @@ class HTMLOutput:
# Races
if self.campaign == "mainline":
write("<tr>\n")
x = _("Race")
x = self.translate("Race", "wesnoth")
write("<th>%s</th></tr><tr><td>" % x)
write("<a href=\"mainline.html\">%s</a><br/>\n" % (
self.get_translation("wesnoth-editor", "all")))
self.translate("all", "wesnoth-editor")))
racenames = {}
for u in self.wesnoth.unit_lookup.values():
if u.campaign != self.campaign: continue
race = u.race
racename = race.get_text_val("plural_name")
racename = T(race, "plural_name")
racenames[racename] = race.get_text_val("id")
racenames = racenames.items()
racenames.sort()
@ -286,7 +318,7 @@ class HTMLOutput:
# Languages
write("<tr>\n")
x = _("Language")
x = self.translate("Language", "wesnoth")
write("<th>%s</th></tr><tr><td>" % x)
for lang in langlist:
labb = lang
@ -306,8 +338,8 @@ class HTMLOutput:
if x.name == "female":
baseunit = self.wesnoth.get_base_unit(u)
if baseunit:
female = baseunit.get_first("female")
return self.pic(u, female)
female = baseunit.get_all(tag = "female")
return self.pic(u, female[0])
sys.stderr.write(
"Warning: Missing image for unit %s(%s).\n" % (
u.get_text_val("id"), x.name))
@ -319,14 +351,14 @@ class HTMLOutput:
def get_abilities(self, u):
anames = []
already = {}
for abilities in u.get_all("abilities"):
try: c = abilities.children()
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 = ability.get_text_val("name")
name = T(ability, "name")
if not name: name = id
if not name: name = ability.name
anames.append(name)
@ -335,9 +367,9 @@ class HTMLOutput:
def get_recursive_attacks(self, this_unit):
def copy_attributes(copy_from, copy_to):
for c in copy_from.children():
if isinstance(c, wmldata.DataText):
copy_to.set_text_val(c.name, c.data)
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)
@ -346,7 +378,7 @@ class HTMLOutput:
attacks = copy.deepcopy(self.get_recursive_attacks(base_unit))
base_attacks_count = len(attacks)
for i, attack in enumerate(this_unit.get_all("attack")):
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])
@ -357,7 +389,7 @@ class HTMLOutput:
def write_units(self):
def write(x): self.output.write(x)
def _(x): return self.get_translation("wesnoth", x)
def _(x): return self.translate(x, "wesnoth")
rows = self.unitgrid
write("<table class=\"units\">\n")
write("<colgroup>")
@ -403,12 +435,15 @@ class HTMLOutput:
write("<td%s>" % attributes)
uid = u.get_text_val("id")
name = self.wesnoth.get_unit_value(u, "name")
cost = self.wesnoth.get_unit_value(u, "cost")
hp = self.wesnoth.get_unit_value(u, "hitpoints")
mp = self.wesnoth.get_unit_value(u, "movement")
xp = self.wesnoth.get_unit_value(u, "experience")
level = self.wesnoth.get_unit_value(u, "level")
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:
@ -438,14 +473,10 @@ class HTMLOutput:
write('</a>\n</div>\n')
write("<div class=\"attributes\">")
write("%s%s<br />" % (
self.get_translation("wesnoth", "Cost: "), cost))
write("%s%s<br />" % (
self.get_translation("wesnoth", "HP: "), hp))
write("%s%s<br />" % (
self.get_translation("wesnoth", "MP: "), mp))
write("%s%s<br />" % (
self.get_translation("wesnoth", "XP: "), xp))
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)
@ -459,20 +490,20 @@ class HTMLOutput:
attacks = self.get_recursive_attacks(u)
for attack in attacks:
n = attack.get_text_val("number")
x = attack.get_text_val("damage")
n = T(attack, "number")
x = T(attack, "damage")
x = "%s - %s" % (x, n)
write("%s " % x)
r = attack.get_text_val("range")
t = attack.get_text_val("type")
r = T(attack, "range")
t = T(attack, "type")
write("%s (%s)" % (_(r), _(t)))
s = []
specials = attack.get_first("specials")
specials = attack.get_all(tag = "specials")
if specials:
for special in specials.children():
sname = special.get_text_val("name")
for special in specials[0].get_all():
sname = T(special, "name")
if sname:
s.append(sname)
s = ", ".join(s)
@ -501,16 +532,19 @@ class HTMLOutput:
def write_unit_report(self, output, unit):
def write(x): self.output.write(x)
def _(x): return self.get_translation("wesnoth", x)
def _(x): return self.translate(x, "wesnoth")
def find_attr(what, key):
if unit.movetype:
mtx = unit.movetype.get_first(what)
mtx = unit.movetype.get_all(tag = what)
mty = None
if mtx: mty = mtx.get_text_val(key)
x = unit.get_first(what)
if mtx:
mty = mtx[0].get_text_val(key)
x = unit.get_all(att = what)
y = None
if x: y = x.get_text_val(key)
if x:
y = x[0].get_text_val(key,
translation = self.translation.translate)
if y:
return True, y
if unit.movetype and mty != None:
@ -521,14 +555,18 @@ class HTMLOutput:
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 = self.wesnoth.get_unit_value(unit, "name")
uname = uval("name")
display_name = uname
female = unit.get_first("female")
female = unit.get_all(tag = "female")
if female:
fname = female.get_text_val("name")
fname = T(female[0], "name")
if fname and fname != uname:
display_name += "<br/>" + fname
@ -537,7 +575,7 @@ class HTMLOutput:
write('<div class="pic">')
if female:
mimage = self.pic(unit, unit)
fimage = self.pic(unit, female)
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)
@ -546,19 +584,19 @@ class HTMLOutput:
write('<img src="%s" alt="(image)" />\n' % image)
write('</div>\n')
description = self.wesnoth.get_unit_value(unit, "description")
description = uval("description")
# TODO: what is unit_description?
if not description: description = unit.get_text_val("unit_description")
if not description: description = uval("unit_description")
if not description: description = "-"
write("<p>%s</p>\n" % __import__('re').sub("\n","\n<br />", description) )
write("<p>%s</p>\n" % re.sub("\n","\n<br />", description) )
# Base info.
hp = self.wesnoth.get_unit_value(unit, "hitpoints")
mp = self.wesnoth.get_unit_value(unit, "movement")
xp = self.wesnoth.get_unit_value(unit, "experience")
level = self.wesnoth.get_unit_value(unit, "level")
alignment = self.wesnoth.get_unit_value(unit, "alignment")
hp = uval("hitpoints")
mp = uval("movement")
xp = uval("experience")
level = uval("level")
alignment = uval("alignment")
write("<table class=\"unitinfo\">\n")
write("<tr>\n")
@ -569,11 +607,12 @@ class HTMLOutput:
if unit.campaign == "mainline" and punit.campaign != "mainline":
continue
link = "../%s/%s.html" % (self.isocode, pid)
name = self.wesnoth.get_unit_value(punit, "name")
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" % self.get_translation("wesnoth", "Advances to: "))
write("<th>%s" % _("Advances to: "))
write("</th><td>\n")
for cid in self.forest.get_children(uid):
link = "../%s/%s.html" % (self.isocode, cid)
@ -581,7 +620,8 @@ class HTMLOutput:
cunit = self.wesnoth.unit_lookup[cid]
if unit.campaign == "mainline" and cunit.campaign != "mainline":
continue
name = self.wesnoth.get_unit_value(cunit, "name")
name = self.wesnoth.get_unit_value(cunit, "name",
translation = self.translation.translate)
except KeyError:
sys.stderr.write("Warning: Unit %s not found.\n" % cid)
name = cid
@ -600,8 +640,8 @@ class HTMLOutput:
("id", "ID")]:
write("<tr>\n")
write("<th>%s</th>" % text)
x = self.wesnoth.get_unit_value(unit, val)
if val == "alignment": x = self.get_translation("wesnoth", x)
x = uval(val)
if val == "alignment": x = _(x)
write("<td>%s</td>" % x)
write("</tr>\n")
@ -623,7 +663,7 @@ class HTMLOutput:
write("<tr>")
aid = attack.get_text_val("name")
aname = attack.get_text_val("description")
aname = T(attack, "description")
icon = attack.get_text_val("icon")
if not icon:
@ -638,22 +678,22 @@ class HTMLOutput:
write("<td>%s" % aname)
t = attack.get_text_val("type")
write("<br/>%s</td>" % self.get_translation("wesnoth", t))
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 = attack.get_text_val("range")
write("<br/>%s</td>" % self.get_translation("wesnoth", r))
r = T(attack, "range")
write("<br/>%s</td>" % _(r))
s = []
specials = attack.get_first("specials")
specials = attack.get_all(tag = "specials")
if specials:
for special in specials.children():
sname = special.get_text_val("name")
for special in specials[0].get_all():
sname = T(special, "name")
if sname:
s.append(sname)
else:
@ -678,8 +718,7 @@ class HTMLOutput:
write("<table class=\"unitinfo\">\n")
write("<tr>\n")
write("<th colspan=\"2\">%s</th>\n" %
self.get_translation("wesnoth", "Resistances: "))
write("<th colspan=\"2\">%s</th>\n" % _("Resistances: "))
write("</tr>\n")
for rid in resistances:
@ -692,8 +731,7 @@ class HTMLOutput:
rcell = "td"
if special: rcell += ' class="special"'
write("<tr>\n")
write("<th>%s</th><td>%s</td>\n" % (
self.get_translation("wesnoth", rid), r))
write("<th>%s</th><td>%s</td>\n" % (_(rid), r))
write("</tr>\n")
write("</table>\n")
@ -709,8 +747,8 @@ class HTMLOutput:
for tstring, t in terrains.items():
tid = t.get_text_val("id")
if tid in ["off_map", "fog", "shroud"]: continue
if t.get_first("aliasof"): continue
name = t.get_text_val("name")
if t.get_all(att = "aliasof"): continue
name = T(t, "name")
terrainlist.append((name, tid))
terrainlist.sort()
@ -743,12 +781,10 @@ def find_languages():
"""
global languages
if languages_found: return languages_found
parser = wmlparser.Parser(datadir)
WML = wmldata.DataSub("WML")
parser.parse_text("{languages}\n")
parser.parse_top(WML)
parser = wmlparser2.Parser(options.wesnoth)
parser.parse_text("{languages}")
for locale in WML.get_all("locale"):
for locale in parser.get_all(tag = "locale"):
isocode = locale.get_text_val("locale")
name = locale.get_text_val("name")
languages_found[isocode] = name
@ -767,7 +803,6 @@ class MyFile:
self.f.close()
def generate_campaign_report(out_path, isocode, campaign, wesnoth):
print "Generating report for %s_%s." % (isocode, campaign)
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")
@ -776,28 +811,26 @@ def generate_campaign_report(out_path, isocode, campaign, wesnoth):
grouper = GroupByRace(wesnoth, campaign)
if campaign == "mainline":
title = html.get_translation("wesnoth", "Multiplayer")
title = html.translate("Multiplayer", "wesnoth")
else:
title = wesnoth.campaign_lookup[campaign].get_text_val("name")
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):
print "Generating report for %s_%s." % (isocode, eid)
path = os.path.join(out_path, isocode)
if not os.path.isdir(path): os.mkdir(path)
era = wesnoth.era_lookup[eid]
ename = era.get_text_val("name")
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)
title = ename
html.write_units_tree(grouper, title)
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):
@ -828,9 +861,10 @@ def write_index(out_path):
def copy_images():
if not options.nocopy:
print "Copying files."
print("Recolorizing pictures.")
image_collector.copy_and_color_images(options.output)
shutil.copy2(os.path.join(datadir, "tools/unit_tree/style.css"), 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",
@ -841,7 +875,66 @@ def copy_images():
url = urllib2.urlopen(grab)
file(local, "w").write(url.read())
def output(isocode):
def parse_game():
stuff = helpers.WesnothList(options.wesnoth, 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())
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
@ -850,81 +943,10 @@ def output(isocode):
* Each addon era's units sorted by faction
* Each addon campaign's units sorted by race
"""
print "WML parser language reset to %s." % isocode
stuff = helpers.WesnothList(isocode, datadir, userdir, transdir)
# Parse some stuff we may need.
print "Parsing terrains ...",
n = stuff.add_terrains()
print "%d terrains found." % n
print "Parsing mainline eras ...",
n = stuff.add_mainline_eras()
print "%d mainline eras found." % n
stuff.is_mainline_era = {}
for c in stuff.era_lookup.keys():
stuff.is_mainline_era[c] = True
print "Parsing mainline campaigns ...",
n = stuff.add_mainline_campaigns()
print "%d mainline campaigns found." % n
stuff.is_mainline_campaign = {}
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).
print "Parsing mainline units ...",
sys.stdout.flush()
WML = stuff.parser.parse("{core/units.cfg}")
n = stuff.add_units(WML, "mainline")
print n, "mainline units found."
# Now we read each mainline campaign in turn to get its units.
cnames = stuff.campaign_lookup.keys()
for cname in cnames:
print "Parsing %s units ..." % cname,
sys.stdout.flush()
campaign = stuff.campaign_lookup[cname]
define = campaign.get_text_val("define")
WML = stuff.parser.parse("""
#define %s\n#enddef
{campaigns}""" % define,
ignore_macros = lambda x: x.find("/scenarios") == -1)
n = stuff.add_units(WML, cname)
image_collector.add_binary_pathes_from_WML(cname, WML)
print n, "units found."
sys.stderr.flush()
if userdir:
print "Parsing addons ...",
sys.stdout.flush()
n = stuff.add_addons(image_collector)
print "%d 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 stuff.is_mainline_campaign: continue
campaign = stuff.campaign_lookup[cname]
print "Parsing %s units ..." % cname,
sys.stdout.flush()
define = campaign.get_text_val("define")
WML = stuff.parser.parse("""
#define %s\n#enddef
{~add-ons}""" % define,
ignore_macros = lambda x: x.find("/scenarios") == -1)
n = stuff.add_units(WML, cname)
image_collector.add_binary_pathes_from_WML(cname, WML)
print n, "units found."
stuff.find_unit_factions()
print "Generating report for %s." % (isocode)
# Report generation
if not os.path.isdir(options.output):
os.mkdir(options.output)
write_index(options.output)
campaigns = stuff.campaign_lookup.keys()
@ -940,8 +962,6 @@ def output(isocode):
# Single unit reports.
generate_single_unit_reports(options.output, isocode, stuff)
return stuff
if __name__ == '__main__':
# We change the process name to "wmlunits"
@ -954,25 +974,29 @@ if __name__ == '__main__':
global options
global image_collector
import optparse
gc.disable()
op = optparse.OptionParser()
op.add_option("-l", "--language", default = "all",
help = "Specify a language.")
help = "Specify a language to use. Else outputs is produced for all languages.")
op.add_option("-o", "--output",
help = "Specify output directory.")
help = "Specify the output directory.")
op.add_option("-n", "--nocopy", action = "store_true",
help = "No copying of files.")
op.add_option("-d", "--datadir",
help = "Specify Wesnoth's data to use. Default is <current directory>/data.")
op.add_option("-u", "--userdir",
help = "Specify user data dir to use, which is automatically scanned "+\
"for addons. For example -u ~/.wesnoth/data")
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 which has a po subfolder for translations. "+\
"(Defaults to the current directory.)")
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:
@ -981,17 +1005,11 @@ if __name__ == '__main__':
options.output = os.path.expanduser(options.output)
if not options.datadir:
#wmltools.pop_to_top("wmlunits")
datadir = os.getcwd() + "/data"
else:
datadir = options.datadir
if not options.wesnoth:
options.wesnoth = "wesnoth"
userdir = options.userdir
if options.transdir:
transdir = options.transdir
else:
transdir = os.getcwd()
if not options.transdir:
options.transdir = os.getcwd()
if options.language == "all":
languages = find_languages().keys()
@ -999,57 +1017,53 @@ if __name__ == '__main__':
else:
languages = [options.language]
image_collector = helpers.ImageCollector(datadir, userdir)
image_collector = helpers.ImageCollector(options.wesnoth)
once_without_translation = False
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:
wesnoth = output(isocode)
if not once_without_translation:
copy_images()
once_without_translation = True
for u in wesnoth.unit_lookup.values():
r = u.race
if r: r = r.get_text_val("id", "none")
else: r = "none"
# Store rid and id for each units so we have it easier
u.rid = r
u.id = u.get_text_val("id")
generate_report(wesnoth, isocode)
# 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()
# Kill all references, this makes sure the reference counting
# kicks in and throws away all data for the previous
# translation - saving a bit of memory since otherwise two
# translations would be kept at a time.
wesnoth = None
# We run a GC collection here, this ensures that also data
# which still are referenced but not reachable (yes, yes,
# this shouldn't happen) are freed.
gc.collect()
if not options.nocopy:
copy_images()