@@ -10,93 +10,195 @@ def generate_build_data(balance_data_path):
1010 abilities = {}
1111 units = {}
1212
13+ ability_lookup = {}
14+
1315 for xml_file_path in glob .glob (os .path .join (balance_data_path , "*.xml" )):
1416 tree = xml .etree .ElementTree .parse (xml_file_path )
1517 root = tree .getroot ()
1618
1719 for ability_element in root .findall ("./abilities/ability" ):
18- if ability_element .get ("index" ) and ability_element .get ("id" ):
19- abilities [ability_element .get ("index" )] = ability_element .get ("id" )
20+ ability_name = ability_element .get ("id" )
21+ if ability_element .get ("index" ) and ability_name :
22+ abilities [ability_element .get ("index" )] = ability_name
23+
24+ if ability_name not in ability_lookup :
25+ ability_lookup [ability_name ] = []
26+
27+ for command_element in ability_element .findall ("./command" ):
28+ command_id = command_element .get ("id" )
29+ command_index_str = command_element .get ("index" )
30+
31+ if command_id and command_index_str :
32+ command_index = int (command_index_str )
2033
21- unit_name = root .get ("id" )
34+ # Pad potential gaps in command indices with empty strings
35+ while len (ability_lookup [ability_name ]) <= command_index :
36+ ability_lookup [ability_name ].append ("" )
37+
38+ command_name = command_id if command_id != "Execute" else ability_name
39+ ability_lookup [ability_name ][command_index ] = command_name
40+
41+ unit_id = root .get ("id" )
2242
2343 meta_element = root .find ("./meta" )
2444
25- if unit_name and meta_element is not None and meta_element .get ("index" ):
26- units [meta_element .get ("index" )] = unit_name
45+ if unit_id and meta_element is not None and meta_element .get ("index" ):
46+ units [meta_element .get ("index" )] = unit_id
2747
28- build_unit_element = root .find ("./builds/unit" )
29- if build_unit_element is not None :
30- build_ability_index = build_unit_element .get ("ability" )
48+ build_unit_elements = root .findall ("./builds/unit" )
49+ if build_unit_elements :
50+ build_ability_index = build_unit_elements [ 0 ] .get ("ability" )
3151
32- if unit_name == "SCV" :
52+ if unit_id == "SCV" :
3353 build_ability_name = "TerranBuild"
34- elif unit_name == "Probe" :
54+ elif unit_id == "Probe" :
3555 build_ability_name = "ProtossBuild"
36- elif unit_name == "Drone" :
56+ elif unit_id == "Drone" :
3757 build_ability_name = "ZergBuild"
3858 else :
39- build_ability_name = "{}Build" .format (unit_name )
59+ build_ability_name = "{}Build" .format (unit_id )
4060
4161 if build_ability_index :
4262 abilities [build_ability_index ] = build_ability_name
4363
64+ if build_ability_name not in ability_lookup :
65+ ability_lookup [build_ability_name ] = []
66+
67+ for element in build_unit_elements :
68+ built_unit_id = element .get ("id" )
69+ command_index_str = element .get ("index" )
70+
71+ if built_unit_id and command_index_str :
72+ command_index = int (command_index_str )
73+
74+ # Pad potential gaps in command indices with empty strings
75+ while len (ability_lookup [build_ability_name ]) <= command_index :
76+ ability_lookup [build_ability_name ].append ("" )
77+
78+ build_command_name = "Build{}" .format (built_unit_id )
79+ ability_lookup [build_ability_name ][command_index ] = build_command_name
80+
4481 train_unit_elements = root .findall ("./trains/unit" )
4582 if train_unit_elements :
4683 train_ability_index = train_unit_elements [0 ].get ("ability" )
4784
4885 if train_ability_index :
49- abilities [train_ability_index ] = "{}Train" .format (unit_name )
86+ train_ability_name = "{}Train" .format (unit_id )
87+ abilities [train_ability_index ] = train_ability_name
88+
89+ if train_ability_name not in ability_lookup :
90+ ability_lookup [train_ability_name ] = []
5091
51- # Handle cases where a unit can train other units using multiple ability indices.
52- # The Nexus is currently the only known example.
53- for element in train_unit_elements [1 :]:
92+ for element in train_unit_elements :
5493 element_ability_index = element .get ("ability" )
5594 trained_unit_name = element .get ("id" )
5695
57- if element_ability_index != train_ability_index and trained_unit_name :
58- train_ability_index = element_ability_index
96+ if trained_unit_name :
97+ # Handle cases where a unit can train other units using multiple ability indices.
98+ # The Nexus is currently the only known example.
99+ if element_ability_index != train_ability_index :
100+ train_ability_index = element_ability_index
101+
102+ train_ability_name = "{}Train{}" .format (unit_id , trained_unit_name )
103+ abilities [train_ability_index ] = train_ability_name
104+
105+ if train_ability_name not in ability_lookup :
106+ ability_lookup [train_ability_name ] = []
107+
108+ command_index_str = element .get ("index" )
109+
110+ if command_index_str :
111+ command_index = int (command_index_str )
59112
60- abilities [train_ability_index ] = "{}Train{}" .format (unit_name , trained_unit_name )
113+ # Pad potential gaps in command indices with empty strings
114+ while len (ability_lookup [train_ability_name ]) <= command_index :
115+ ability_lookup [train_ability_name ].append ("" )
61116
62- research_upgrade_element = root .find ("./researches/upgrade" )
63- if research_upgrade_element is not None :
64- research_ability_index = research_upgrade_element .get ("ability" )
65- research_ability_name = "{}Research" .format (unit_name )
117+ ability_lookup [train_ability_name ][command_index ] = train_ability_name
118+ else :
119+ command_index_str = element .get ("index" )
120+
121+ if command_index_str :
122+ command_index = int (command_index_str )
123+
124+ # Pad potential gaps in command indices with empty strings
125+ while len (ability_lookup [train_ability_name ]) <= command_index :
126+ ability_lookup [train_ability_name ].append ("" )
127+
128+ train_command_name = "Train{}" .format (trained_unit_name )
129+ ability_lookup [train_ability_name ][command_index ] = train_command_name
130+
131+ research_upgrade_elements = root .findall ("./researches/upgrade" )
132+ if research_upgrade_elements :
133+ research_ability_index = research_upgrade_elements [0 ].get ("ability" )
134+ research_ability_name = "{}Research" .format (unit_id )
66135
67136 abilities [research_ability_index ] = research_ability_name
68137
69- sorted_abilities = collections .OrderedDict (sorted (abilities .items (), key = lambda x : int (x [0 ])))
138+ if research_ability_name not in ability_lookup :
139+ ability_lookup [research_ability_name ] = []
140+
141+ for element in research_upgrade_elements :
142+ researched_upgrade_id = element .get ("id" )
143+ command_index_str = element .get ("index" )
144+
145+ if researched_upgrade_id and command_index_str :
146+ command_index = int (command_index_str )
147+
148+ # Pad potential gaps in command indices with empty strings
149+ while len (ability_lookup [research_ability_name ]) <= command_index :
150+ ability_lookup [research_ability_name ].append ("" )
151+
152+ research_command_name = "Research{}" .format (researched_upgrade_id )
153+ ability_lookup [research_ability_name ][command_index ] = research_command_name
154+
70155 sorted_units = collections .OrderedDict (sorted (units .items (), key = lambda x : int (x [0 ])))
156+ sorted_abilities = collections .OrderedDict (sorted (abilities .items (), key = lambda x : int (x [0 ])))
157+
158+ unit_lookup = [[unit_name , unit_name ] for _ , unit_name in sorted_units .items ()]
71159
72- return sorted_units , sorted_abilities
160+ return sorted_units , sorted_abilities , unit_lookup , ability_lookup
73161
74162
75163def main ():
76- parser = argparse .ArgumentParser (description = 'Generate [BUILD_VERSION]_abilities.csv and [BUILD_VERSION]_units.csv'
77- ' files from exported balance data.' )
164+ parser = argparse .ArgumentParser (description = 'Generate [BUILD_VERSION]_abilities.csv, [BUILD_VERSION]_units.csv,'
165+ ' ability_lookup.csv and unit_lookup.csv files from exported balance'
166+ ' data.' )
78167 parser .add_argument ('build_version' , metavar = 'BUILD_VERSION' , type = int ,
79168 help = 'the build version of the balance data export' )
80169 parser .add_argument ('balance_data_path' , metavar = 'BALANCE_DATA_PATH' , type = str ,
81170 help = 'the path to the balance data export' )
82171
83172 args = parser .parse_args ()
84173
85- units , abilities = generate_build_data (args .balance_data_path )
174+ units , abilities , unit_lookup , ability_lookup = generate_build_data (args .balance_data_path )
86175
87176 if not units or not abilities :
177+ parser .print_help ()
178+ print ("\n " )
179+
88180 raise ValueError ("No balance data found at provided balance data path." )
89181
90182 with open ('{}_units.csv' .format (args .build_version ), 'w' , newline = '' ) as csvfile :
91- csv_writer = csv .writer (csvfile , delimiter = ',' )
183+ csv_writer = csv .writer (csvfile , delimiter = ',' , lineterminator = os . linesep )
92184 for unit_index , unit_name in units .items ():
93185 csv_writer .writerow ([unit_index , unit_name ])
94186
95187 with open ('{}_abilities.csv' .format (args .build_version ), 'w' , newline = '' ) as csvfile :
96- csv_writer = csv .writer (csvfile , delimiter = ',' )
188+ csv_writer = csv .writer (csvfile , delimiter = ',' , lineterminator = os . linesep )
97189 for ability_index , ability_name in abilities .items ():
98190 csv_writer .writerow ([ability_index , ability_name ])
99191
192+ with open ('unit_lookup.csv' .format (args .build_version ), 'w' , newline = '' ) as csvfile :
193+ csv_writer = csv .writer (csvfile , delimiter = ',' , lineterminator = os .linesep )
194+ for lookup_entry in unit_lookup :
195+ csv_writer .writerow (lookup_entry )
196+
197+ with open ('ability_lookup.csv' .format (args .build_version ), 'w' , newline = '' ) as csvfile :
198+ csv_writer = csv .writer (csvfile , delimiter = ',' , lineterminator = os .linesep )
199+ for ability_name , commands in ability_lookup .items ():
200+ csv_writer .writerow ([ability_name ] + commands )
201+
100202
101203if __name__ == "__main__" :
102204 main ()
0 commit comments