More actions
Python script example for making sweeping changes to the progressions |
Formating |
||
Line 5: | Line 5: | ||
import uuid | import uuid | ||
from pathlib import Path | from pathlib import Path | ||
# This function takes an xpath, which is a query language for xml structures, and tries to respect the load order to give you the most up to date result | # This function takes an xpath, which is a query language for xml structures, and tries to respect the load order to give you the most up to date result | ||
def run_xpath_group( xpath, progressions ): | def run_xpath_group( xpath, progressions ): | ||
Line 13: | Line 13: | ||
ret[item.xpath('attribute[@id="UUID"]')[0].attrib['value']] = item | ret[item.xpath('attribute[@id="UUID"]')[0].attrib['value']] = item | ||
return ret.values() | return ret.values() | ||
# This is just an helper function used later for conditionally extending a list | # This is just an helper function used later for conditionally extending a list | ||
def or_add( target, add ): | def or_add( target, add ): | ||
target.extend(add) | target.extend(add) | ||
return add | return add | ||
# This sets up how we can interact with this script via the command line to actually get the work done | # This sets up how we can interact with this script via the command line to actually get the work done | ||
arg_parser = argparse.ArgumentParser(description='Progressions manipulation for BG3') | arg_parser = argparse.ArgumentParser(description='Progressions manipulation for BG3') | ||
Line 27: | Line 27: | ||
run.add_argument('output_template') | run.add_argument('output_template') | ||
run.add_argument('output') | run.add_argument('output') | ||
parsed_args = arg_parser.parse_args() | parsed_args = arg_parser.parse_args() | ||
if parsed_args.command == 'run': | if parsed_args.command == 'run': | ||
# Given a path to where you've extracted the .pak files we'll collect all versions of Progressions.lsx | # Given a path to where you've extracted the .pak files we'll collect all versions of Progressions.lsx | ||
Line 38: | Line 38: | ||
base_classes = run_xpath_group( '//node[@id="ClassDescription" and not(attribute[@id="ParentGuid"])]',class_descriptions) | base_classes = run_xpath_group( '//node[@id="ClassDescription" and not(attribute[@id="ParentGuid"])]',class_descriptions) | ||
sub_classes = run_xpath_group( '//node[@id="ClassDescription" and attribute[@id="ParentGuid"]]',class_descriptions) | sub_classes = run_xpath_group( '//node[@id="ClassDescription" and attribute[@id="ParentGuid"]]',class_descriptions) | ||
progressions.sort( key=lambda x: x['tree'].xpath('//version')[0].attrib['build'] ) # Sort list such as the versions with highest build number is last | progressions.sort( key=lambda x: x['tree'].xpath('//version')[0].attrib['build'] ) # Sort list such as the versions with highest build number is last | ||
xp = '//node[@id="Progression" and attribute[@id="ProgressionType" and @value="0"] and attribute[@id="Name" and ( @value="MulticlassSpellSlots" or '+' or '.join( '@value="'+x+'"' for x in (x.xpath('attribute[@id="Name"]')[0].attrib['value'] for x in base_classes) )+ ' ) ] ]' | xp = '//node[@id="Progression" and attribute[@id="ProgressionType" and @value="0"] and attribute[@id="Name" and ( @value="MulticlassSpellSlots" or '+' or '.join( '@value="'+x+'"' for x in (x.xpath('attribute[@id="Name"]')[0].attrib['value'] for x in base_classes) )+ ' ) ] ]' | ||
Line 49: | Line 49: | ||
node.xpath('attribute[@id="UUID"]')[0].attrib['value'] = str(uuid.uuid4()) # The copies needs a new UUID | node.xpath('attribute[@id="UUID"]')[0].attrib['value'] = str(uuid.uuid4()) # The copies needs a new UUID | ||
nodes_base_classes += level_13s | nodes_base_classes += level_13s | ||
for progress in nodes_base_classes: # Give perk to all levels for base classes | for progress in nodes_base_classes: # Give perk to all levels for base classes | ||
allow_feat = progress.xpath('attribute[@id="AllowImprovement"]') or or_add( progress, [ etree.Element('attribute', {'id':'AllowImprovement', 'value' : 'true','type' : 'bool' }) ] ) | allow_feat = progress.xpath('attribute[@id="AllowImprovement"]') or or_add( progress, [ etree.Element('attribute', {'id':'AllowImprovement', 'value' : 'true','type' : 'bool' }) ] ) | ||
allow_feat[0].attrib['value'] = 'true' | allow_feat[0].attrib['value'] = 'true' | ||
xp = '//node[@id="Progression" and attribute[@id="ProgressionType" and @value="1"] and attribute[@id="Name" and ( '+' or '.join( '@value="'+x+'"' for x in (x.xpath('attribute[@id="Name"]')[0].attrib['value'] for x in sub_classes) )+ ' ) ] ]' | xp = '//node[@id="Progression" and attribute[@id="ProgressionType" and @value="1"] and attribute[@id="Name" and ( '+' or '.join( '@value="'+x+'"' for x in (x.xpath('attribute[@id="Name"]')[0].attrib['value'] for x in sub_classes) )+ ' ) ] ]' | ||
nodes_sub_classes = [ copy.deepcopy(x) for x in run_xpath_group( xp, progressions ) ] | nodes_sub_classes = [ copy.deepcopy(x) for x in run_xpath_group( xp, progressions ) ] | ||
# Get every class & sub class level which has a Boosts child, which contains the point improvements for that level | # Get every class & sub class level which has a Boosts child, which contains the point improvements for that level | ||
for check_item in [item for sublist in [ *(x.xpath('//attribute[@id="Boosts"]') for x in nodes_base_classes), *(x.xpath('//attribute[@id="Boosts"]') for x in nodes_sub_classes ) ] for item in sublist]: | for check_item in [item for sublist in [ *(x.xpath('//attribute[@id="Boosts"]') for x in nodes_base_classes), *(x.xpath('//attribute[@id="Boosts"]') for x in nodes_sub_classes ) ] for item in sublist]: | ||
Line 64: | Line 64: | ||
cur_val = re.sub( rf"(ActionResource\({res},)(\d)", lambda x: x.group(1)+str(int(x.group(2))*3), cur_val ) | cur_val = re.sub( rf"(ActionResource\({res},)(\d)", lambda x: x.group(1)+str(int(x.group(2))*3), cur_val ) | ||
check_item.attrib['value'] = cur_val | check_item.attrib['value'] = cur_val | ||
# Parse the xml template provided | # Parse the xml template provided | ||
output = etree.parse(parsed_args.output_template, etree.XMLParser(remove_blank_text=True)) | output = etree.parse(parsed_args.output_template, etree.XMLParser(remove_blank_text=True)) |