#!/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
# 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') kismet_events = {}
#!/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: 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, ))
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'])) # Now loop through all our items. caid_updates = set() for key, val in obj_struct.items(): if key[-8:] == 'PartData':
def find_has_shields(poollist): if poollist is None or poollist == '': return (False, None) for pool in poollist: if 'Pool_Shields_Standard_EnemyUse' in pool['ItemPool']: shieldweight = Weight(pool['PoolProbability']) if shieldweight.value > 0: return (True, shieldweight.value) return (False, None) data = Data('TPS') for classname in data.get_all_by_type('AIPawnBalanceDefinition'): # Get BalDef baldef = data.get_struct_by_full_object(classname) try: pawn_name = baldef['PlayThroughs'][0]['DisplayName'] except KeyError as e: #print('WARNING: No PlayThroughs for {}'.format(classname)) pawn_name = '(unknown)' # Find out if this enemy has a chance to use shields has_shields = False shield_loc = None shield_prob = None has_cipl = False if ('PlayThroughs' in baldef and baldef['PlayThroughs'] is not None and baldef['PlayThroughs'] != ''): if (len(baldef['PlayThroughs']) > 0 and 'CustomItemPoolList' in baldef['PlayThroughs'][0]
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 # which have identical data (this could probably be done in the
#!/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():
# 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 ))
# 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']: expdef = data.get_struct_attr_obj_real(exp, 'Definition') if expdef['DamageTypeDef'] == 'None': print(' * {}'.format(exp_name)) else: found_types.add(expdef['DamageTypeDef'])
# vim: set expandtab tabstop=4 shiftwidth=4: import sys from ftexplorer.data import Data print('This is just used to compare some basic info between versions') print('of guns. Only prints out some real basic info, does not include') print('things like firing mode definitions, BPDs, etc...') print('') print(sys.argv[1]) print('-' * len(sys.argv[1])) print('') data = Data('BL2') bal = data.get_struct_by_full_object(sys.argv[1]) plc = data.get_struct_attr_obj_real(bal, 'RuntimePartListCollection') for parttype in [ 'Body', 'Grip', 'Barrel', 'Sight', 'Stock', 'Elemental', 'Accessory1' ]: part_list = [] for pd in plc['{}PartData'.format(parttype)]['WeightedParts']: part_list.append(' * {}'.format(pd['Part'].split("'")[1])) print('{}:'.format(parttype)) for part in sorted(part_list): print(part) print('') # Get barrel effects
'GD_Population_Psycho.Population.PopDef_Psycho', 'GD_Population_Psycho.Population.PopDef_PsychoBurning', 'GD_Population_Psycho.Population.PopDef_PsychoSuicide', ] pawns = set([ 'GD_Population_Marauder.Balance.PawnBalance_MarauderElite', 'GD_Population_Marauder.Balance.PawnBalance_MarauderRegular', 'GD_Population_Marauder.Balance.PawnBalance_Scavenger', 'GD_Population_Midget.Balance.PawnBalance_MidgetShotgun' ]) commands = [] for popdef_name in popdefs: reported = False popdef = data.get_struct_by_full_object(popdef_name) for (archetype_idx, archetype) in enumerate(popdef['ActorArchetypeList']): prob = Weight(archetype['Probability']) if prob.value > 0: factory_name = Data.get_struct_attr_obj(archetype, 'SpawnFactory') factory = data.get_struct_by_full_object(factory_name) pawn_name = Data.get_struct_attr_obj(factory, 'PawnBalanceDefinition') if pawn_name in pawns: if not reported: print(popdef_name) reported = True print(' * [{}] {} @ {}'.format(archetype_idx, pawn_name, prob.value)) commands.append("""set {} ActorArchetypeList[{}].Probability ( BaseValueConstant=200000, BaseValueAttribute=None,
# 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 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)))
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: import sys from ftexplorer.data import Data game = sys.argv[1] obj = sys.argv[2] data = Data(game.upper()) val = data.get_struct_by_full_object(obj) print(val)
'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']): caids[idx] = Weight(caid).value # Now loop through gun_types = 1 gun_types_no_luneshine = 1 for attr in [
data = Data('BL2') popdef_name = 'GD_Population_Midget.Balance.PawnBalance_MidgetShotgun' aidef_name = 'GD_AI_DenDef.AIDenDef_Bandits' level_name = 'SouthernShelf_P' mixes = set() for (package_name, package) in data.get_level_package_nodes(level_name): for child in package: if child.name.startswith('PopulationOpportunityDen'): mix_name = Data.get_struct_attr_obj(child.get_structure(), 'PopulationDef') if mix_name: mixes.add(mix_name) for mix_name in mixes: print("set {} AIDef WillowAIDenDefinition'{}'".format( mix_name, aidef_name)) mix = data.get_struct_by_full_object(mix_name) for (aal_idx, aal) in enumerate(mix['ActorArchetypeList']): if aal_idx == 0: sf_name = Data.get_struct_attr_obj(aal, 'SpawnFactory') print("set {} PawnBalanceDefinition AIPawnBalanceDefinition'{}'". format(sf_name, popdef_name)) print( "set {} ActorArchetypeList[0].Probability (BaseValueConstant=1,BaseValueAttribute=None,InitializationDefinition=None,BaseValueScaleConstant=1)" .format(mix_name)) else: print( "set {} ActorArchetypeList[{}].Probability (BaseValueConstant=0,BaseValueAttribute=None,InitializationDefinition=None,BaseValueScaleConstant=0)" .format(mix_name, aal_idx))
lines.append( ' # the prefix "The" from all fast travel names, so they also sort') lines.append(' # *correctly.*') lines.append('') lines.append(' #<Alphabetical By Default>') lines.append('') lines.append( ' # Many thanks to Our Lord and Savior Gabe Newell for this!') lines.append('') lines.append(' set FastTravelStationGFxMovie SortMode 1') lines.append('') lines.append(' #</Alphabetical By Default>') lines.append('') lines.append(' #<Remove Level Prefixes>') lines.append('') for ftdef in ftdefs: ft = data.get_struct_by_full_object(ftdef) if ft['StationDisplayName'].startswith('The '): lines.append(' set {} StationDisplayName {}'.format( ftdef, ft['StationDisplayName'][4:], )) lines.append('') lines.append(' #</Remove Level Prefixes>') lines.append('') lines.append('#</{}>'.format(mod_name)) # Write out to the file mp.human_str_to_blcm_filename("\n".join(lines), output_filename) print('Wrote mod to {}'.format(output_filename))
def get_twos_compliment(val): val = int(val) one = val >> 16 two = val & 0xFF return (one, two) found_trees = set() bpd_stmts = [] disable_stmts = [] 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') if bpd_name: bpd_node = data.get_node_by_full_object(bpd_name) # Remove any delay if we have an immediate AttachItems behavior after # an Event. This may or may not catch everything. bpd_struct = bpd_node.get_structure() for seq_idx, seq in enumerate(bpd_struct['BehaviorSequences']): for cold_idx, cold in enumerate(seq['ConsolidatedOutputLinkData']): if float(cold['ActivateDelay']) > 0: bpd_stmts.append( 'set {} BehaviorSequences[{}].ConsolidatedOutputLinkData[{}].ActivateDelay {}' .format( bpd_name,
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') prefixes = get_names(data, partdef, 'PrefixList') (names, customs) = get_names(data, partdef, 'TitleList', get_customs=True) if len(prefixes) == 0:
#!/usr/bin/env python # vim: set expandtab tabstop=4 shiftwidth=4: import struct from ftexplorer.data import Data data = Data('BL2') #bpd = data.get_struct_by_full_object('GD_ButtStallion_Proto.Character.AIDef_ButtStallion_Proto:AIBehaviorProviderDefinition_1') bpd = data.get_struct_by_full_object( 'gd_slotmachine.SlotMachine:BehaviorProviderDefinition_0') behavior_linkids = {} for idx, cold in enumerate( bpd['BehaviorSequences'][1]['ConsolidatedOutputLinkData']): compliment = int(cold['LinkIdAndLinkedBehavior']) # Use big-endian just for clarity's sake packed = struct.pack('>i', compliment) #print('{:08b} {:08b} {:08b} {:08b}'.format(packed[0], packed[1], packed[2], packed[3])) #linkid = compliment >> 16 #linkid = compliment >> 24 linkid = packed[0] behavior = compliment & 0xFF if behavior not in behavior_linkids: behavior_linkids[behavior] = {} if linkid not in behavior_linkids[behavior]: behavior_linkids[behavior][linkid] = 1 else: behavior_linkids[behavior][linkid] += 1 print('[{:3d}] {:9d} ({:08b} {:08b} {:08b} {:08b}): {}, {}'.format( idx, compliment, packed[0], packed[1], packed[2], packed[3], linkid, behavior))