Page MenuHomeWildfire Games
Paste P267

Fix D4584.
ActivePublic

Authored by Freagarach on Mar 14 2022, 7:55 AM.
#!/usr/bin/env python3
from lxml import etree as ET
import fileinput
import glob
import json
import os
import pysvn
import sys
class StyleFixer:
def fix_template_style(template_path):
changes = [
["version='1.0'", 'version="1.0"'],
["'UTF-8'", '"utf-8"']
]
StyleFixer.sed(template_path, changes)
def sed(path, changes):
for line in fileinput.input(path, inplace=True):
for change in changes:
line = line.replace(change[0], change[1])
print(line, end="")
def simplify_requirements(reqs):
if len(reqs) != 1:
return
all_req = reqs.find("All")
if all_req == None:
return
reqs.remove(all_req)
for child in all_req:
reqs.append(child)
def sort_xml(node):
node[:] = sorted(node, key=lambda child: child.tag)
for child in node:
StyleFixer.sort_xml(child)
class TechnologyConverter:
def __init__(self, vfs_root):
self.scc = pysvn.Client()
self.tech_folder = os.path.join(vfs_root, 'simulation', 'data', 'technologies')
self.template_folder = os.path.join(vfs_root, 'simulation', 'templates')
self.tech_template_folder = os.path.join(self.template_folder, 'technologies')
self.civs = []
os.makedirs(self.tech_template_folder, exist_ok=True)
self.scc.add(self.tech_template_folder, force=True)
with os.scandir(os.path.join(self.template_folder, 'players')) as civ_files:
for civ_file in civ_files:
if civ_file.is_file():
civ = civ_file.name.removesuffix(".xml")
if civ == 'gaia':
continue
self.civs.append(civ)
civ_folder = os.path.join(self.tech_template_folder, civ)
os.makedirs(civ_folder, exist_ok=True)
self.scc.add(civ_folder, force=True)
def add_requirements(self, req_json, reqs):
if 'tech' in req_json:
techs = ET.SubElement(reqs, "Techs")
techs.set("datatype", "tokens")
techs.text = req_json['tech']
if 'entity' in req_json:
ent_req = ET.SubElement(ET.SubElement(reqs, "Entities"), req_json['entity']['class'])
if 'number' in req_json['entity']:
ET.SubElement(ent_req, "Count").text = str(req_json['entity']['number'])
if 'variants' in req_json['entity']:
ET.SubElement(ent_req, "Variants").text = str(req_json['entity']['variants'])
if 'all' in req_json:
all_req = ET.SubElement(reqs, "All")
for req in req_json['all']:
self.add_requirements(req, all_req)
if len(all_req) == 0:
reqs.remove(all_req)
if 'any' in req_json:
any_req = ET.SubElement(reqs, "Any")
for req in req_json['any']:
self.add_requirements(req, any_req)
if len(any_req) == 0:
reqs.remove(any_req)
def add_modifications(self, cmp_tech, tech_json):
mods = ET.SubElement(cmp_tech, "Modifiers")
i = 1
for mod in tech_json['modifications']:
modifier = ET.SubElement(mods, "modifier" + str(i))
i += 1
paths = ET.SubElement(modifier, "Paths")
paths.set("datatype", "tokens")
paths.text = mod['value']
if 'add' in mod:
ET.SubElement(modifier, "Add").text = str(mod['add'])
elif 'multiply' in mod:
ET.SubElement(modifier, "Multiply").text = str(mod['multiply'])
elif 'replace' in mod:
ET.SubElement(modifier, "Replace").text = str(mod['replace'])
affects = ET.SubElement(modifier, "Affects")
affects.set("datatype", "tokens")
if 'affects' in tech_json:
affects.text = tech_json['affects'][0].replace(" ", "+")
for affect in tech_json['affects'][1:]:
affects.text = affects.text + " " + affect.replace(" ", "+")
if 'affects' in mod:
if affects.text == None:
affects.text = mod['affects']
else:
affects.text = affects.text + "+" + mod['affects']
if affects.text == None:
modifier.remove(affects)
def create_civ_tech(self, tech_json, name, civ):
file_path = os.path.join(self.tech_template_folder, civ, name) + ".xml"
root = ET.Element("Technology")
root.set("parent", "template_technology_" + name)
cmp_identity = ET.SubElement(root, "Identity")
ET.SubElement(cmp_identity, "Civ").text = civ
if 'specificName' in tech_json and civ in tech_json['specificName']:
ET.SubElement(cmp_identity, "SpecificName").text = tech_json['specificName'][civ]
StyleFixer.sort_xml(root)
ET.ElementTree(root).write(file_path, xml_declaration=True, pretty_print=True, encoding='utf-8')
StyleFixer.fix_template_style(file_path)
self.scc.add(file_path)
if 'autoResearch' in tech_json:
player_template_path = os.path.join(self.template_folder, 'players', civ) + ".xml"
tree = ET.parse(player_template_path)
root = tree.getroot()
cmp_technology_manager = root.find('TechnologyManager')
if cmp_technology_manager == None:
cmp_technology_manager = ET.SubElement(root, "TechnologyManager")
auto_researched = cmp_technology_manager.find('AutoResearched')
if auto_researched == None:
auto_researched = ET.SubElement(cmp_technology_manager, "AutoResearched')
indentation = ' '
if auto_researched.text == None:
auto_researched.text = "\n " + indentation + "technologies/" + civ + name + "\n" + indentation
else:
auto_researched.text = auto_researched.text + " " + "technologies/" + civ + name + "\n" + indentation
StyleFixer.sort_xml(root)
ET.ElementTree(root).write(player_template_path, xml_declaration=True, pretty_print=True, encoding='utf-8')
StyleFixer.fix_template_style(player_template_path)
def convert_tech_to_template(self, tech_path, name):
tech_json = json.load(tech_path)
root = ET.Element("Technology")
cmp_tech = ET.SubElement(root, "Technology")
if 'top' in tech_json:
ET.SubElement(cmp_tech, "Choices").text = "technologies/{civ}/" + tech_json['top'] + " " + "technologies/{civ}/" + tech_json['bottom']
cmp_identity = ET.SubElement(root, "Identity")
ET.SubElement(cmp_identity, "GenericName").text = tech_json['genericName']
ET.SubElement(cmp_identity, "History").text = tech_json['description'] if 'description' in tech_json else ""
ET.SubElement(cmp_identity, "Icon").text = "technologies/" + tech_json['icon']
ET.SubElement(cmp_identity, "Tooltip").text = tech_json['tooltip']
# The identity spec requires this.
ET.SubElement(cmp_identity, "Undeletable").text = "true"
# It would be nice to not have this dual relationship.
if 'pair' in tech_json:
ET.SubElement(cmp_tech, "ChoiceRoot").text = tech_json['pair']
if 'phase' in name:
classes = ET.SubElement(cmp_identity, "Classes")
classes.set("datatype", "tokens")
classes.text = "Phase"
if 'supersedes' in tech_json:
ET.SubElement(cmp_tech, "Supersedes").text = "technologies/{civ}/" + tech_json['supersedes']
allowed_civs = []
disallowed_civs = []
if 'requirements' in tech_json:
reqs = ET.Element("Requirements")
self.add_requirements(tech_json['requirements'], reqs)
if 'all' in tech_json['requirements']:
for req in tech_json['requirements']['all']:
if 'civ' in req:
allowed_civs.append(req['civ'])
if 'notciv' in req:
disallowed_civs.append(req['notciv'])
if 'any' in req:
for civ_req in req['any']:
allowed_civs.append(civ_req['civ'])
elif 'any' in tech_json['requirements']:
for req in tech_json['requirements']['any']:
if 'civ' in req:
allowed_civs.append(req['civ'])
elif 'civ' in tech_json['requirements']:
allowed_civs.append(tech_json['requirements']['civ'])
StyleFixer.simplify_requirements(reqs)
if 'requirementsTooltip' in tech_json:
techs = reqs.find('Techs')
if len(reqs) != 1 or techs == None:
ET.SubElement(reqs, "Tooltip").text = tech_json['requirementsTooltip']
if len(reqs) > 0:
cmp_identity.append(reqs)
if 'cost' in tech_json:
cmp_cost = ET.SubElement(root, "Cost")
resource_cost = ET.SubElement(cmp_cost, "Resources")
for element in tech_json['cost']:
ET.SubElement(resource_cost, element).text = str(tech_json['cost'][element])
if 'researchTime' in tech_json:
ET.SubElement(cmp_cost, "BuildTime").text = str(tech_json['researchTime'])
# cmpCost expects this node.
else:
ET.SubElement(cmp_cost, "BuildTime").text = '0'
# cmpCost expects this node.
ET.SubElement(cmp_cost, "Population").text = '0'
if 'soundComplete' in tech_json:
ET.SubElement(ET.SubElement(ET.SubElement(root, "Sound"), "SoundGroups"), "completed").text = tech_json['soundComplete']
if 'modifications' in tech_json:
self.add_modifications(cmp_tech, tech_json)
if len(allowed_civs) > 0 and len(disallowed_civs) > 0:
print("Warning: complex (dis)allowed civs: " + name)
if len(disallowed_civs) > 0:
for civ in self.civs:
if not civ in disallowed_civs:
allowed_civs.append(civ)
elif len(allowed_civs) == 0:
allowed_civs = self.civs[:]
for civ in allowed_civs:
self.create_civ_tech(tech_json, name, civ)
StyleFixer.sort_xml(root)
ET.ElementTree(root).write(tech_path.name, xml_declaration=True, pretty_print=True, encoding='utf-8')
def run(self):
for tech_path in glob.iglob(self.tech_folder + '/**/*.json', recursive=True):
name = os.path.basename(tech_path).removesuffix(".json")
if 'phase' in name:
if 'generic' in name:
name = name.removesuffix("_generic")
else:
continue
tech_template_path = os.path.join(self.template_folder, "template_technology_" + name) + ".xml"
self.scc.move(tech_path, tech_template_path)
with open(tech_template_path, 'r') as file:
try:
self.convert_tech_to_template(file, name)
StyleFixer.fix_template_style(tech_template_path)
except Exception as e:
print("Error: Failed conversion: " + name)
print(e)
self.scc.move(tech_template_path, tech_path)
def convert_input_file(self, relative_path):
new_path = relative_path
if 'json' in relative_path:
new_path = relative_path.removesuffix(".json") + ".xml"
self.scc.move(relative_path, new_path)
name = os.path.basename(new_path).removesuffix(".xml")
with open(new_path, 'r') as file:
try:
self.convert_tech_to_template(file, name)
except Exception as e:
print("Error: Failed conversion: " + relative_path)
print(e)
StyleFixer.fix_template_style(new_path)
class TemplateFixer:
def __init__(self, vfs_root):
self.template_folder = os.path.join(vfs_root, 'simulation', 'templates')
def fix_template(self, template_path):
tree = ET.parse(template_path)
root = tree.getroot()
changed = False
if self.fix_researcher(root):
changed = True
if self.check_requirements(root):
changed = True
tree.write(template_path, xml_declaration=True, pretty_print=True, encoding='utf-8')
return changed
def fix_researcher(self, root):
cmp_researcher = root.find('Researcher')
if cmp_researcher == None:
return False
technologies = cmp_researcher.find('Technologies')
if technologies == None:
return False
self.fix_technology_names(technologies, ' ')
return True
def fix_technology_names(self, node, indentation):
new_names = []
for technology in node.text.split():
prefix = "technologies/{civ}/"
if technology[0] == '!':
prefix = '!' + prefix
technology = technology[1:]
if technology[0] == '-':
if technology[1] == '!':
prefix = '!' + prefix
technology = technology[1:]
prefix = '-' + prefix
technology = technology[1:]
if prefix in technology:
new_names.append(technology.removesuffix("_{civ}"))
else:
new_names.append(prefix + technology.removesuffix("_{civ}"))
node.text = "\n " + indentation + ('\n ' + indentation).join(sorted(new_names)) + "\n" + indentation
def check_requirements(self, root):
changed = False
cmp_identity = root.find('Identity')
if cmp_identity != None:
requirements = cmp_identity.find('Requirements')
if requirements != None and self.fix_requirements(requirements, ' '):
changed = True
cmp_upgrade = root.find('Upgrade')
if cmp_upgrade != None:
for upgrade in cmp_upgrade.findall('./'):
requirements = upgrade.find('Requirements')
if requirements != None and self.fix_requirements(requirements, ' '):
changed = True
cmp_entity_limits = root.find('EntityLimits')
if cmp_entity_limits != None:
limit_removers = cmp_entity_limits.find('LimitRemovers')
if limit_removers != None:
for class_entry in limit_removers:
req_techs = class_entry.find('RequiredTechs')
if req_techs != None:
self.fix_technology_names(req_techs, ' ')
changed = True
return changed
def fix_requirements(self, requirements, indentation):
technologies = requirements.find('Techs')
if technologies == None:
return False
self.fix_technology_names(technologies, indentation)
return True
def run(self):
for template_path in glob.iglob(self.template_folder + '/**/*.xml', recursive=True):
if self.fix_template(template_path):
StyleFixer.fix_template_style(template_path)
if __name__ == '__main__':
script_dir = os.path.dirname(os.path.realpath(__file__))
tech_converter = TechnologyConverter(script_dir)
if len(sys.argv) > 1:
for tech_path in sys.argv[1:]:
tech_converter.convert_input_file(tech_path)
else:
tech_converter.run()
template_fixer = TemplateFixer(script_dir)
template_fixer.run()

Event Timeline

Freagarach created this paste.Mar 14 2022, 7:55 AM
Freagarach created this object with visibility "No One".
Freagarach edited the content of this paste. (Show Details)Mar 14 2022, 5:18 PM
Freagarach edited the content of this paste. (Show Details)Mar 25 2022, 4:46 PM
Freagarach changed the title of this paste from Fix Dxxxx. to Fix D4584..Mar 31 2022, 8:24 AM
Freagarach edited the content of this paste. (Show Details)
Freagarach changed the visibility from "No One" to "Public (No Login Required)".
Freagarach edited the content of this paste. (Show Details)Apr 1 2022, 5:51 PM
Freagarach edited the content of this paste. (Show Details)Apr 9 2022, 9:35 AM
Freagarach edited the content of this paste. (Show Details)Apr 17 2022, 7:16 AM
Freagarach edited the content of this paste. (Show Details)Nov 25 2022, 8:47 AM
Freagarach edited the content of this paste. (Show Details)Mar 10 2023, 3:35 PM
Freagarach edited the content of this paste. (Show Details)Mar 13 2023, 4:07 PM
Freagarach edited the content of this paste. (Show Details)Mar 24 2023, 12:29 PM
Freagarach edited the content of this paste. (Show Details)Mar 24 2023, 2:23 PM
Freagarach edited the content of this paste. (Show Details)Mar 31 2023, 3:06 PM
Freagarach edited the content of this paste. (Show Details)Apr 4 2023, 3:37 PM