#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: import struct from ftexplorer.data import Data for game in ['BL2', 'TPS']: linkids = {} data = Data(game) bpd_names = [] bpd_names.extend(data.get_all_by_type('BehaviorProviderDefinition')) bpd_names.extend(data.get_all_by_type('AIBehaviorProviderDefinition')) for bpd_name in sorted(bpd_names): bpd = data.get_struct_by_full_object(bpd_name) for bs_idx, bs in enumerate(bpd['BehaviorSequences']): for cold_idx, cold in enumerate(bs['ConsolidatedOutputLinkData']): link = int(cold['LinkIdAndLinkedBehavior']) packed = struct.pack('>i', link) linkid = packed[0] if packed[1] != 0: print( '{} BehaviorSequences[{}].ConsolidatedOutputLinkData[{}] - {} second byte is: {}' .format( bpd_name, bs_idx, cold_idx, link, packed[1], )) if linkid == 255: linkid = -1
mod_name = 'TPS Skinpool Reassignments' mod_version = '1.1.0' output_filename = '{}.blcm'.format(mod_name) output_source_filename = '{}-source.txt'.format(mod_name) ### ### Processing the mod ### data = Data('TPS') free_count = 0 prefix = ' ' * (2 * 4) hotfix_output = [] saved_pools = [] for keyed in sorted(data.get_all_by_type('KeyedItemPoolDefinition')): structure = data.get_node_by_full_object(keyed).get_structure() for (bi_idx, item) in enumerate(structure['BalancedItems']): (junk, pool, junk2) = item['ItmPoolDefinition'].split("'") saved_pools.append(pool) innerpool = data.get_node_by_full_object(pool).get_structure() if len(innerpool['BalancedItems']) != 1: raise Exception('Inner pool {} has {} items'.format( pool, len(innerpool['BalancedItems']))) (junk, actualcustom, junk2 ) = innerpool['BalancedItems'][0]['InvBalanceDefinition'].split("'") # Hotfix to unlink the intermediate pool hotfix_output.append( '{}level None set {} BalancedItems[{}].ItmPoolDefinition None'. format(
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: # Finds all objects which exist inside classes which include a # *Rate* attribute somewhere in there whose object names contain # the given string. Used when trying to track down where in # the world the speed of Sanctuary's main gate is coming from. # # Requires having run find_classes_with_rate.py previously. from ftexplorer.data import Data #search_for = 'Gate' search_for = 'Sanct' classes = [] with open('rate_classes.txt') as df: for line in df: classes.append(line.strip()) data = Data('BL2') for classname in classes: for obj_name in data.get_all_by_type(classname): if search_for in obj_name: print(obj_name)
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: import sys import argparse from ftexplorer.data import Data data = Data('TPS') for obj_name in data.get_all_by_type('WeaponNamePartDefinition'): obj = data.get_node_by_full_object(obj_name).get_structure() if 'PartName' not in obj or obj['PartName'] == '' or obj[ 'PartName'] == 'None': print(obj_name)
print( 'FastTravelStationDefinition objects imported into FT/BLCMM Explorer') print('as well, which will be a hassle if you\'re Not Me. Sorry for the') print('bother!') print( '********************************************************************') print('') sys.exit(1) # Control Vars mod_name = 'BL2 Sorted Fast Travel' mod_version = '1.0.1' output_filename = '{}.blcm'.format(mod_name) data = Data('BL2') ftdefs = data.get_all_by_type('FastTravelStationDefinition') if len(ftdefs) == 0: raise Exception( 'No FastTravelStationDefinitions found! FT/BLCMM Explorer does not have those by default...' ) # Construct the mod lines = [] lines.append('BL2') lines.append('#<{}>'.format(mod_name)) lines.append('') lines.append(' # {} v{}'.format(mod_name, mod_version)) lines.append(' # by Apocalyptech') lines.append(' # Licensed under Public Domain / CC0 1.0 Universal') lines.append(' #') lines.append(
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: import sys from ftexplorer.data import Data data = Data('BL2') baldef_names = data.get_all_by_type('InventoryBalanceDefinition') + \ data.get_all_by_type('WeaponBalanceDefinition') for baldef_name in sorted(baldef_names): baldef = data.get_struct_by_full_object(baldef_name) if ('Manufacturers' in baldef and baldef['Manufacturers'] is not None and baldef['Manufacturers'] != ''): for (man_idx, man) in enumerate(baldef['Manufacturers']): if ('Grades' in man and man['Grades'] is not None and man['Grades'] != ''): for (grade_idx, grade) in enumerate(man['Grades']): if ('GameStageRequirement' in grade and grade['GameStageRequirement'] is not None and grade['GameStageRequirement'] != ''): req = grade['GameStageRequirement'] min_stage = req['MinGameStage'] if int(min_stage) > 1: print( 'set {} Manufacturers[{}].Grades[{}].GameStageRequirement.MinGameStage 1' .format( baldef_name, man_idx, grade_idx, ))
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: import sys import argparse from ftexplorer.data import Data data = Data('BL2') objects = [] objects.extend(data.get_all_by_type('AIBehaviorProviderDefinition')) objects.extend(data.get_all_by_type('BehaviorProviderDefinition')) found_event_names = {} for bpd_name in objects: bpd = data.get_node_by_full_object(bpd_name).get_structure() if 'BehaviorSequences' in bpd and bpd[ 'BehaviorSequences'] != 'None' and bpd['BehaviorSequences'] != '': for seq in bpd['BehaviorSequences']: if 'EventData2' in seq and seq['EventData2'] != 'None' and seq[ 'EventData2'] != '': for event in seq['EventData2']: if 'UserData' in event and event[ 'UserData'] != 'None' and event['UserData'] != '': event_name = event['UserData']['EventName'] if 'NPC' in event_name: if event_name not in found_event_names: found_event_names[event_name] = set() found_event_names[event_name].add(bpd_name)
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: # Finds all objects which exist inside classes which include a # *Rate* attribute somewhere in there whose object names contain # the given string. Used when trying to track down where in # the world the speed of Sanctuary's main gate is coming from. # # Requires having run find_classes_with_rate.py previously. from ftexplorer.data import Data data = Data('BL2') print('type,speed,recovery,up,down,left,right,minvert,minhoriz') for obj_name in data.get_all_by_type('WeaponTypeDefinition'): obj = data.get_struct_by_full_object(obj_name) print('{},{},{},{},{},{},{},{},{}'.format( obj_name, obj['WeaponKickSpeed'], obj['WeaponKickRecoveryTime'], obj['WeaponKickUp'], obj['WeaponKickDown'], obj['WeaponKickLeft'], obj['WeaponKickRight'], obj['MinimumVerticalPercentage'], obj['MinimumHorizontalPercentage'], ))
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: from ftexplorer.data import Data data = Data('BL2') for aidef in data.get_all_by_type('AIClassDefinition'): structure = data.get_node_by_full_object(aidef).get_structure() chance_modifier = None duration_modifier = None damage_impact_modifier = None damage_status_modifier = None report = False if 'BaseShockChanceResistanceModifier' in structure: value = round( float(structure['BaseShockChanceResistanceModifier'] ['BaseValueConstant']), 6) if value != 1: chance_modifier = value report = True if 'BaseShockDurationResistanceModifier' in structure: value = round( float(structure['BaseShockDurationResistanceModifier'] ['BaseValueConstant']), 6) if value != 1: duration_modifier = value report = True if 'BaseShockDamageModifiers' in structure: value = round( float(structure['BaseShockDamageModifiers']['ResistanceToImpact'] ['BaseValueConstant']), 6)
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: # This util loops through all AIPawnBalanceDefinitions and dumps out # a list of what loot/equip pools are used by which of those definitions. # Basically intended to be pruned manually, edited, and then passed # to cdh.py to generate replacement data for Cold Dead Hands. Was not # used until updating BL2 CDH for DLC5... import sys from ftexplorer.data import Data pools = {} data = Data('BL2') for baldef_name in data.get_all_by_type('AIPawnBalanceDefinition'): baldef = data.get_struct_by_full_object(baldef_name) if 'PlayThroughs' in baldef and baldef['PlayThroughs'] != 'None' and baldef['PlayThroughs'] != '': for pt in baldef['PlayThroughs']: if 'CustomItemPoolList' in pt and pt['CustomItemPoolList'] != 'None' and pt['CustomItemPoolList'] != '': for pool in pt['CustomItemPoolList']: pool_name = Data.get_attr_obj(pool['ItemPool']) if pool_name not in pools: pools[pool_name] = set() pools[pool_name].add(baldef_name) if 'DefaultItemPoolList' in baldef and baldef['DefaultItemPoolList'] != 'None' and baldef['DefaultItemPoolList'] != '': for pool in baldef['DefaultItemPoolList']: pool_name = Data.get_attr_obj(pool['ItemPool']) if pool_name not in pools:
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: from ftexplorer.data import Data data = Data('TPS') for bpd_name in sorted(data.get_all_by_type('BehaviorProviderDefinition')): bpd = data.get_struct_by_full_object(bpd_name) found_disabled = False for seq_idx, seq in enumerate(bpd['BehaviorSequences']): if found_disabled: break for event_idx, event in enumerate(seq['EventData2']): enabled = event['UserData']['bEnabled'] name = event['UserData']['EventName'] if enabled != 'True': found_disabled = True print('{}: [{}] {} [{}]'.format(bpd_name, seq_idx, name, event_idx)) break
import os import sys from ftexplorer.data import Data def striptype(obj_name): (junk1, name, junk2) = obj_name.split("'", 2) return name def dotnode(obj_name): name_parts = obj_name.split('.') return name_parts[-1].lower().replace('-', '_') data = Data('BL2') with open('b2missions.dot', 'w') as df: print('digraph b2 {', file=df) for mission_obj in data.get_all_by_type('MissionDefinition'): node = data.get_node_by_full_object(mission_obj) mission = node.get_structure() dot_name = dotnode(mission_obj) print(' {} [label=<{}>];'.format(dot_name, mission['MissionName']), file=df) print('{} ({})'.format(mission['MissionName'], mission_obj)) if 'Dependencies' in mission: main_deps = set() for dep in mission['Dependencies']: print(' * {}'.format(dep)) if dep[:17] != 'MissionDefinition': raise Exception('Unknown mission dep type: {}'.format(dep)) depname = striptype(dep) main_deps.add(depname)
# Finding all AttributeStartingValues attributes in use by game pawns. asvs = set() def get_asvs(obj, asvs): if 'AttributeStartingValues' in obj and obj[ 'AttributeStartingValues'] != '' and obj[ 'AttributeStartingValues'] != 'None': for asv in obj['AttributeStartingValues']: asv_val = Data.get_struct_attr_obj(asv, 'Attribute') if asv_val is not None: asvs.add(asv_val) data = Data('TPS') for classdef_name in data.get_all_by_type('AIClassDefinition'): get_asvs(data.get_struct_by_full_object(classdef_name), asvs) for pawnbal_name in data.get_all_by_type('AIPawnBalanceDefinition'): pawnbal = data.get_struct_by_full_object(pawnbal_name) if 'PlayThroughs' in pawnbal and pawnbal['PlayThroughs'] != '' and pawnbal[ 'PlayThroughs'] != 'None': for pt in pawnbal['PlayThroughs']: get_asvs(pt, asvs) print('ASVs found:') print('') for asv in sorted(asvs): print(asv) print('')
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: from ftexplorer.data import Data #data = Data('BL2') data = Data('TPS') affected = {} for challenge_def in data.get_all_by_type('ChallengeDefinition'): challenge_struct = data.get_node_by_full_object( challenge_def).get_structure() if 'Levels' in challenge_struct: for (idx, level) in enumerate(challenge_struct['Levels']): reward = Data.get_struct_attr_obj(level, 'RewardItemPool') if reward: affected[reward] = ( challenge_struct['ChallengeName'], challenge_struct['Description'], idx + 1, ) print('Pool | Name | Level | Description') print('--- | --- | --- | ---') for reward in sorted(affected.keys()): (name, desc, num) = affected[reward] print('`{}` | {} | {} | {}'.format(reward, name, num, desc))
import sys from ftexplorer.data import Data run_station_check = False #game = 'BL2' game = 'TPS' input_file = 'save_index_{}.txt'.format(game.lower()) data = Data(game) # Get a list of all the last-visited locations locations = {} for obj_type in [ 'FastTravelStationDefinition', 'LevelTravelStationDefinition' ]: for obj_name in data.get_all_by_type(obj_type): obj_struct = data.get_struct_by_full_object(obj_name) (_, identifier) = obj_name.rsplit('.', 1) if obj_struct['StationLevelName'] != 'None': if run_station_check and identifier.lower() in locations: print('Overwriting {}: {} -> {}'.format( identifier.lower(), locations[identifier.lower()], obj_struct['StationLevelName'].lower(), )) if run_station_check and game == 'TPS' and identifier.lower( ) == 'cliffs': print('Warning: "cliffs" found in TPS...') locations[ identifier.lower()] = obj_struct['StationLevelName'].lower() if run_station_check:
# Given the specified popdefs, make them practically guaranteed in # any mix that they happen to belong to. popdef_names = set([ #'GD_Population_Engineer.Balance.PawnBalance_HyperionHawk', #'GD_Population_Midget.Balance.PawnBalance_MidgetRat', #'GD_Allium_PsychoSnow_Midget.Balance.PawnBalance_PsychoSnow_Midget', #'GD_Population_Marauder.Balance.PawnBalance_MarauderGrunt', #'GD_Population_Marauder.Balance.PawnBalance_MarauderIntro', #'GD_Population_Midget.Balance.PawnBalance_MidgetShotgun', #'GD_Population_Nomad.Balance.PawnBalance_NomadPyro', #'GD_Aster_Pop_Knights.Balance.PawnBalance_Knight_Paladin', 'GD_Population_SpiderAnt.Balance.PawnBalance_SpiderantChubby', #'GD_Population_PrimalBeast.Population.Unique.PopDef_PrimalBeast_KingMong', ]) wpd_cache = {} for pfbap_name in data.get_all_by_type('PopulationFactoryBalancedAIPawn'): pfbap = data.get_struct_by_full_object(pfbap_name) pawn_name = Data.get_struct_attr_obj(pfbap, 'PawnBalanceDefinition') if pawn_name in popdef_names: (mix_name, junk) = pfbap_name.split(':', 1) if mix_name not in wpd_cache: wpd_cache[mix_name] = data.get_struct_by_full_object(mix_name) for (aal_idx, aal) in enumerate(wpd_cache[mix_name]['ActorArchetypeList']): sf_name = Data.get_struct_attr_obj(aal, 'SpawnFactory') if sf_name == pfbap_name: print('set {} ActorArchetypeList[{}].Probability (BaseValueConstant=200000,BaseValueAttribute=None,InitializationDefinition=None,BaseValueScaleConstant=1)'.format( mix_name, aal_idx ))
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: from ftexplorer.data import Data # Attempts to find some cases where we might have a BPD -> Seq -> BPD link. # *shudder* data = Data('BL2') # First gather our BPD EventName mappings print('Gathering BPD EventName mappings') bpd_events = {} events_bpd = {} for bpd_name in sorted(data.get_all_by_type('BehaviorProviderDefinition')): bpd_events[bpd_name.lower()] = set() bpd_struct = data.get_struct_by_full_object(bpd_name) if 'BehaviorSequences' in bpd_struct and bpd_struct[ 'BehaviorSequences'] != '': for seq in bpd_struct['BehaviorSequences']: if 'EventData2' in seq and seq['EventData2'] != '': for event in seq['EventData2']: event_name = event['UserData']['EventName'].replace( '"', '') if event_name.lower() not in events_bpd: events_bpd[event_name.lower()] = set() events_bpd[event_name.lower()].add(bpd_name.lower()) bpd_events[bpd_name.lower()].add(event_name.lower()) # Now gather our Kismet EventName mappings print('Gathering Kismet EventName mappings')
# Finding damage sources which use WillowGame.WillowDmgSource_Skill as # a DamageSource but don't have a damage type defined. This causes the # damage to come across just as "Skill" damage which can't be usefully # blocked (at least by enemy pawns) without merging in # D_Attributes.DamageSourceModifiers.ReceivedSkillDamageModifier # somehow, which only exists for Sal and Zer0, I believe. # Data types here were chosen just by searching for WillowGame.WillowDmgSource_Skill print('Classes which appear to cause *only* Skill damage:') print('') data = Data('BL2') found_types = set() names = data.get_all_by_type('Behavior_CauseDamage') names.extend(data.get_all_by_type('Behavior_FireBeam')) names.extend(data.get_all_by_type('MeleeDefinition')) names.extend(data.get_all_by_type('WillowDamageArea')) for cd_name in names: cd = data.get_struct_by_full_object(cd_name) if 'WillowGame.WillowDmgSource_Skill' in cd['DamageSource']: if cd['DamageTypeDefinition'] == 'None': print(' * {}'.format(cd_name)) else: found_types.add(cd['DamageTypeDefinition']) for exp_name in data.get_all_by_type('Behavior_Explode'): exp = data.get_struct_by_full_object(exp_name) if 'WillowGame.WillowDmgSource_Skill' in exp['DamageSource']:
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: from ftexplorer.data import Data def get_twos_compliment(val): val = int(val) one = val >> 16 two = val & 0xFF return (one, two) data = Data('BL2') for obj_name in sorted(data.get_all_by_type('InteractiveObjectDefinition')): obj_struct = data.get_struct_by_full_object(obj_name) bpd_name = Data.get_struct_attr_obj(obj_struct, 'BehaviorProviderDefinition') shown_title = False if bpd_name: bpd_node = data.get_node_by_full_object(bpd_name) # Output a statement to ensure that attached items are immediately # available children = list( bpd_node.get_children_with_name('behavior_attachitems')) if len(children) > 1: print('{}: {}'.format(bpd_name, len(children)))
data.append(self.disable_phaselock) if self.phaselock_time is None: data.append('') else: data.append(self.phaselock_time) data.extend(self.fire.get_data()) data.extend(self.corrosive.get_data()) data.extend(self.shock.get_data()) data.extend(self.slag.get_data()) return data startvals = {} ret_list = [] shown_header = False writer = csv.writer(sys.stdout) for pawnbal_name in sorted(data.get_all_by_type('AIPawnBalanceDefinition')): pawnbal = data.get_struct_by_full_object(pawnbal_name) if 'PlayThroughs' in pawnbal: aipawn = data.get_struct_attr_obj_real(pawnbal, 'AIPawnArchetype') allegiance = Data.get_attr_obj(aipawn['Allegiance']) if allegiance not in friendly_allegiances: aiclass_name = Data.get_attr_obj(aipawn['AIClass']) aiclass = data.get_struct_attr_obj_real(aipawn, 'AIClass') # Loop through all playthroughs to gather stats pawn_stats = [] for (idx, pt) in enumerate(pawnbal['PlayThroughs']): display_name = get_display_name(pt['DisplayName']) pawn_stats.append(AttributeSet(display_name, pawnbal_name, idx+1, pawnbal, aiclass)) # Loop through playthroughs one more time to combine any playthroughs
'GD_Weap_Pistol.A_Weapons_Unique.Pistol_Dahl_Starter', 'GD_Weap_SMG.A_Weapons_Unique.SMG_Gearbox_1', 'GD_Weap_SniperRifles.A_Weapons_Unique.Sniper_Gearbox_1', 'gd_cork_weap_assaultrifle.A_Weapons_Unique.AR_Starter_Vladof_Athena', # Boo, updated data packages don't like these now: 'GD_Cork_Weap_SniperRifles.A_Weapons_Unique.Sniper_Jakobs_3_Trespasser', 'GD_Weap_Pistol.A_Weapons_Unique.Pistol_Maliwan_3_Rubi', ]), } for game in ['BL2', 'TPS']: total_count = 0 total_count_no_luneshine = 0 data = Data(game) for baldef_name in sorted(data.get_all_by_type('WeaponBalanceDefinition')): # Don't process anything in the blacklist if baldef_name in blacklist[game]: continue baldef_struct = data.get_struct_by_full_object(baldef_name) partlist_name = Data.get_struct_attr_obj(baldef_struct, 'RuntimePartListCollection') if partlist_name: partlist = data.get_struct_by_full_object(partlist_name) # First get our CAID data caids = {} for (idx, caid) in enumerate(partlist['ConsolidatedAttributeInitData']):
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: # Trying to find references to D_Attributes.Balance.PlayThroughCount inside # AttributeInitializationDefinition objects which reference something other # than 1 and 2. from ftexplorer.data import Data, Weight data = Data('BL2') for aid_name in sorted( data.get_all_by_type('AttributeInitializationDefinition')): aid = data.get_struct_by_full_object(aid_name) ci = aid['ConditionalInitialization'] if ci['bEnabled'] == 'True': for ce in ci['ConditionalExpressionList']: for exp in ce['Expressions']: if 'PlayThroughCount' in exp['AttributeOperand1']: numval = float(exp['ConstantOperand2']) if numval != 1 and numval != 2: print('{}: {}'.format(aid_name, numval)) elif numval == 2: print('{} - 2'.format(aid_name))
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: # Attempting to find some pattern to the bandit coolers near the # Denial Subroutine battle which don't actually work. import sys from ftexplorer.data import Data, Weight data = Data('TPS') cooler_points = [] points = data.get_all_by_type('WillowPopulationOpportunityPoint') for point_name in sorted(points): if point_name.startswith( 'Ma_RightCluster_Combat.TheWorld:PersistentLevel'): point = data.get_struct_by_full_object(point_name) popdef_name = Data.get_struct_attr_obj(point, 'PopulationDef') if popdef_name and popdef_name == 'GD_Population_Treasure.Lootables.BanditCooler': cooler_points.append(point) if point[ 'PhysicsVolume'] == "BehaviorVolume'Ma_RightCluster_Combat.TheWorld:PersistentLevel.BehaviorVolume_6'": #print("set {} PhysicsVolume DefaultPhysicsVolume'Loader.TheWorld:PersistentLevel.DefaultPhysicsVolume_2'".format(point_name)) print( "set {} PopulationDef PopulationDefinition'GD_Ma_Population_Treasure.Lootables.BanditAmmo_Marigold'" .format(point_name)) print('Found {} coolers'.format(len(cooler_points))) print('') keys = {} for point in cooler_points: for key in point.keys():
def investigate_pool(pool, report): """ Looks into a pool to see if we should report it. """ if (pool['ItemPool'] != 'None' and 'AmmoAndResourcePools' not in pool['ItemPool'] and 'EnemyUse' not in pool['ItemPool'] and 'Pool_GunsAndGear_Weighted' not in pool['ItemPool'] and 'ItemPool_MoxxiPicture' not in pool['ItemPool'] and 'Pool_GrenadeMods_All' not in pool['ItemPool'] and 'GD_CustomItemPools' not in pool['ItemPool'] and 'Pool_SpellGrenade' not in pool['ItemPool'] and 'Pool_Shields_All_01_Common' not in pool['ItemPool'] ): prob = pool['PoolProbability'] if is_fixed(prob): print(report) for classname in data.get_all_by_type('AIPawnBalanceDefinition'): pawn = data.get_node_by_full_object(classname).get_structure() if 'DefaultItemPoolList' in pawn: for (dipl, pool) in enumerate(pawn['DefaultItemPoolList']): investigate_pool(pool, '{} DefaultItemPoolList[{}]'.format(classname, dipl)) if 'PlayThroughs' in pawn: for (pt_idx, pt) in enumerate(pawn['PlayThroughs']): if ('CustomItemPoolList' in pt and pt['CustomItemPoolList'] is not None and pt['CustomItemPoolList'] != ''): for (cipl, pool) in enumerate(pt['CustomItemPoolList']): investigate_pool(pool, '{} PlayThroughs[{}].CustomItemPoolList[{}]'.format(classname, pt_idx, cipl))
grenade_unlock_list.append( 'level None set GD_GrenadeMods.A_Item.GM_{}_4_VeryRare Manufacturers[{}].Grades[0].GameStageRequirement.MinGameStage 0' .format( gm_type, man_num, )) prefix = ' ' * (4 * 2) grenade_unlock = "\n\n".join( ['{}{}'.format(prefix, s) for s in grenade_unlock_list]) # Generate an exhaustive list of part unlocks, using my ft-explorer # data introspection routines. exhaustive_unlocks_list = [] data = Data('TPS') classnames = sorted( data.get_all_by_type('WeaponPartListCollectionDefinition') + data.get_all_by_type('ItemPartListCollectionDefinition'), key=str.lower) for classname in classnames: obj_struct = data.get_struct_by_full_object(classname) if 'ConsolidatedAttributeInitData' not in obj_struct: # Should maybe keep track of which of these doesn't have it... continue # Figure out our caid values caid = obj_struct['ConsolidatedAttributeInitData'] caid_values = [] for caid_val in caid: caid_values.append(float(caid_val['BaseValueConstant']))
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: from ftexplorer.data import Data data = Data('BL2') for gbdef in data.get_all_by_type('GameBalanceDefinition'): gb = data.get_struct_by_full_object(gbdef) if 'BalanceByRegion' in gb: for (balidx, bal) in enumerate(gb['BalanceByRegion']): print('set {} BalanceByRegion[{}].MaxDefaultGameStage.BaseValueConstant 80'.format( gbdef, balidx, )) if 'MissionOverrides' in bal and bal['MissionOverrides'] is not None and bal['MissionOverrides'] != '' and bal['MissionOverrides'] != 'None': for (overidx, over) in enumerate(bal['MissionOverrides']): print('set {} BalanceByRegion[{}].MissionOverrides[{}].MaxGameStage.BaseValueConstant 80'.format( gbdef, balidx, overidx, ))
if get_customs: customs |= get_presentations(data, namepart) if get_customs: return (list(names), list(customs)) else: return list(names) for game in games: data = Data(game) # Weapons if True: for baldef_name in sorted( data.get_all_by_type('WeaponBalanceDefinition')): # Don't process anything in the blacklist if baldef_name in blacklist[game]: continue baldef_struct = data.get_struct_by_full_object(baldef_name) try: partlist = data.get_struct_attr_obj_real( baldef_struct, 'RuntimePartListCollection') except KeyError: partlist = data.get_struct_attr_obj_real( baldef_struct, 'WeaponPartListCollection') # Only really interested in the Barrel, since I'm after red-text guns. for part_dict in partlist['BarrelPartData']['WeightedParts']: partdef = data.get_struct_attr_obj_real(part_dict, 'Part')
mod_name = 'TPS Skinpool Reassignments' mod_version = '1.0.0' output_filename = '{}.txt'.format(mod_name) ### ### Processing the mod ### data = Data('TPS') hfs = Hotfixes() free_count = 0 prefix = ' '*(2*4) hotfix_output = [] saved_pools = [] for keyed in sorted(data.get_all_by_type('KeyedItemPoolDefinition')): structure = data.get_node_by_full_object(keyed).get_structure() for (bi_idx, item) in enumerate(structure['BalancedItems']): (junk, pool, junk2) = item['ItmPoolDefinition'].split("'") saved_pools.append(pool) innerpool = data.get_node_by_full_object(pool).get_structure() if len(innerpool['BalancedItems']) != 1: raise Exception('Inner pool {} has {} items'.format(pool, len(innerpool['BalancedItems']))) (junk, actualcustom, junk2) = innerpool['BalancedItems'][0]['InvBalanceDefinition'].split("'") # Hotfix to unlink the intermediate pool hf_id = 'unlink_{}'.format(free_count) hfs.add_level_hotfix(hf_id, 'TPSSkinpool', ',{},BalancedItems[{}].ItmPoolDefinition,,None'.format( keyed, bi_idx,