Skip to content

Instantly share code, notes, and snippets.

@leper
Last active December 10, 2015 22:29
Show Gist options
  • Save leper/4502798 to your computer and use it in GitHub Desktop.
Save leper/4502798 to your computer and use it in GitHub Desktop.
get template data place in binaries/data/mods/public
#!/usr/bin/env python
import xml.etree.ElementTree as ET
import copy
import os
import errno
use_custom_xml_folder = False
template_path = "simulation/templates/"
save_path = "XML/"
# Used to cache the dicts produced by get_template_as_dict
templates = {}
def get_template_as_dict(template):
# Not already cached -> retrieve
if template not in templates:
tree = ET.parse(get_template_file(template))
if not tree:
# TODO: Raise exception?
print("Error reading file \"" + template + ".xml\"")
return {}
root = tree.getroot()
parent = root.get('parent')
# Get values top-down (parent first)
vals = {}
if parent:
# Get a deep copy, so we don't change the parent
vals = copy.deepcopy(get_template_as_dict(parent))
templates[template] = dict_temp_helper(root, vals)
return templates[template]
def dict_temp_helper(element, vals={}):
for el in list(element):
if "disable" in el.attrib:
# If this raises an exception, then the xml files
# are doing something wrong.
del vals[el.tag]
continue
if "datatype" in el.attrib and el.attrib["datatype"] == "tokens":
if not el.tag in vals:
vals[el.tag] = []
# Merge the tokens (including removal)
vals[el.tag].extend(dict_temp_helper(el, vals[el.tag]).split())
# NOTE: We use sorted just to find all values that should be
# removed (starting with '-'), but we don't want to change the
# order
for val in sorted(vals[el.tag]):
# No more values to remove
# TODO: Doesn't work with templates starting with
# characters < '-', but we assume nobody will
# feed this with such template names.
if val[0] != '-':
break
# Remove the entry to be removed
vals[el.tag].remove(val[1:])
# Remove the remover
vals[el.tag].remove(val)
else:
vals[el.tag] = dict_temp_helper(el, vals[el.tag] if el.tag in vals else {})
if not vals or not isinstance(vals, dict):
if element.text:
return element.text
else:
# To support eg. <Looter/>
return {}
return vals
# Returns None in case the element specified by list isn't found
def get_stat(stats, list):
list.reverse()
return get_stat_helper(stats, list)
def get_stat_helper(stats, list):
val = stats.get(list.pop())
if len(list) and val is not None:
return get_stat_helper(val, list)
return val
# Returns a list with the found elements
# or None
def get_stats(stats, list):
list.reverse()
return get_stats_helper(stats, list)
def get_stats_helper(stats, l):
key = l[-1]
if key is None:
ret = []
if len(l[:-1]):
for val in stats:
b = get_stats_helper(stats[val], l[:-1])
if b is not None:
if isinstance(b, list):
for a in b:
ret.append(a)
else:
ret.append(b)
else:
if isinstance(stats, dict):
for val in stats:
ret.append(stats[val])
else:
ret = stats
return ret
else:
val = stats.get(key)
if len(l[:-1]) and val is not None:
return get_stats_helper(val, l[:-1])
return val
def get_template_file(template):
if use_custom_xml_folder:
# TODO: add the retrieval code here
pass
else:
return template_path + template + '.xml'
def make_sure_path_exists(path):
try:
os.makedirs(path)
except OSError as exception:
if exception.errno != errno.EEXIST:
raise
def write_file(civ, unit, file, val):
path = save_path + civ + '/' + unit + '/'
make_sure_path_exists(path)
f = open(path + file, 'w', encoding="utf-8")
f.write(val)
f.close()
def printUnit(unit):
stats = get_template_as_dict(unit)
print("=========")
print(unit)
civ = get_stat(stats, ["Identity", "Civ"])
print(civ)
write_file(civ, unit, "Gen_Name.txt", get_stat(stats, ["Identity", "GenericName"]))
print("Specific Name:")
print(get_stat(stats, ["Identity", "SpecificName"]))
print("History:")
print(get_stat(stats, ["Identity", "History"]))
resources = get_stats(stats, ["Cost", "Resources"])
# Remove some useless values
# TODO: If this happens more than twice we should maybe make this a function, or part of get_stats
for res in list(resources):
if resources[res] == "0":
del resources[res]
write_file(civ, unit, "Resources.txt",
',\\,'.join(list('\\includegraphics[width=0.0225\\textwidth]{'+res+'.png}\\,'+resources[res]
for res in sorted(resources, key=["food", "wood", "stone", "metal"].index))))
# NOTE: Don't use the first None as we only have Melee, Ranged, Charge and Slaughter defined
# and some bonuses are defined in the same way for Melee and Charge
# Just use Melee and Ranged (Slaughter doesn't support bonuses and Charge isn't implemented yet)
print("Attack bonuses:")
for a in get_stats(stats, ["Attack", None, "Bonuses", None]):
print("Bonused vs "+a["Classes"]+" (x"+a["Multiplier"]+")")
print("Location of Portrait:")
print(get_stat(stats, ["Identity", "Icon"]))
print("Tooltip:")
print(get_stat(stats, ["Identity", "Tooltip"]))
print("Health:")
print(get_stat(stats, ["Health", "Max"]))
# Packed siege special case
if unit[-9:] == "_unpacked":
print("unpacked siege")
packed_stats = get_template_as_dict(unit[:-9]+"_packed")
print("Velocity(Walk):")
print(get_stat(packed_stats, ["UnitMotion", "WalkSpeed"]))
print("Velocity(Run):")
print(get_stat(packed_stats, ["UnitMotion", "Run", "Speed"]))
else:
print("Velocity(Walk):")
print(get_stat(stats, ["UnitMotion", "WalkSpeed"]))
print("Velocity(Run):")
print(get_stat(stats, ["UnitMotion", "Run", "Speed"]))
print("Armour")
armour = get_stats(stats, ["Armour"])
write_file(civ, unit, "Armour.txt",
',\\,'.join(list('\\includegraphics[width=0.0225\\textwidth]{'+a.lower()+'.png}\\,'+armour[a]
for a in sorted(armour))))
promotions = 0
promotion_stats = stats
while True:
promotions += 1
promotion = get_stat(promotion_stats, ["Promotion", "Entity"])
if promotion is None:
break
promotion_stats = get_template_as_dict(promotion)
print("Promotion "+str(promotions)+":")
print(promotion)
print("Health")
print(get_stat(promotion_stats, ["Health", "Max"]))
# NOTE: No special handling for packed siege here, as we
# don't have promotions for any packable units currently.
print("Velocity(Walk):")
print(get_stat(stats, ["UnitMotion", "WalkSpeed"]))
print("Velocity(Run):")
print(get_stat(stats, ["UnitMotion", "Run", "Speed"]))
# TODO:
#armour
#attack
#range
# TODO: Should we move the civ replacement somewhere else?
buildable = get_stats(stats, ["Builder", "Entities"])
if buildable is not None:
for i in range(len(buildable)):
buildable[i] = buildable[i].replace("{civ}", civ)
for building in buildable:
building_stats = get_template_as_dict(building)
print(get_stat(building_stats, ["Identity", "GenericName"]))
# TODO: ["ProductionQueue", "Entities"] (Analogous to Builder->Entities
# main
units = [
[ "units/rome_infantry_swordsman_b",
"units/rome_mechanical_siege_ballista_unpacked"
]
]
for civtemplates in units:
for unit in civtemplates:
printUnit(unit)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment