Open Source Adventures: Episode 45: How BATTLETECH Game Stores Data
Tomasz Wegrzanowski
Posted on April 28, 2022
Recently I've been playing a lot of BATTLETECH.
Most similar games like let's say XCOM2 or Civ5 have fairly limited amount of character classes, weapons, so it's very easy to learn the difference between a Muton and a Sectoid, or a rocket launcher and a flamethrower, or between a Pikeman and a Catapult (special units in Civ5 are generally just base units with some specific bonuses).
Due to its origins as figurine game, BATTLETECH has far too many different equipment, with trivial differences.
There's 103 mechs, 172 weapons, and a lot of other equipment. It's really difficult to figure out which builds are good and which are not.
I've seen some Google Spreadsheets with mechs and weapons, but they're really satisfactory, and often only cover base weapons, not the whole set.
Where BATTLETECH game stores data
If you have the Steam version, by default the game will be installed to C:\Program Files (x86)\Steam\steamapps\common\BATTLETECH
. I'm not sure
Inside that folder, you need to dig in further into BattleTech_Data\StreamingAssets\data
.
Historically most games coded their data in custom formats, so the first part of any such project would be writing some custom parsers. Most of these formats were fairly simple, but occasionally you'd run into some crazy complex data format - or even worse game data hardcoded into code.
But we live in the future, and the data is largely JSON.
Data of interest
The data that interest me the most are two folders - weapon
and mech
/chassis
.
There's a few extra folders that contain a bit of extra data like ammunition
, ammunitionBox
, and maybe heatsinks
, upgrades
etc. Fortunately all of it is JSON.
Some of the other data (like loot tables) are in .csv
. There's some binary data in there too, like maps and audio assets, but pretty much everything we want is JSON.
Example Mech JSON
Here's an example of chassis/chassisdef_kintaro_KTO-18.json
(automatically reformatted for readability):
{
"Description": {
"Cost": 5200000,
"Rarity": 3,
"Purchasable": true,
"Manufacturer": "",
"Model": "",
"UIName": "Kintaro",
"Id": "chassisdef_kintaro_KTO-18",
"Name": "Kintaro",
"Details": "The Kintaro 18 is… a beast of a machine. It's fast for its size, mounts heavy armor, and can literally shred most other 'Mechs with its almost ridiculous amount of close-range weaponry. But, it runs hotter than Hell's saunas.",
"Icon": "uixTxrIcon_kintaro"
},
"MovementCapDefID": "movedef_kintaro_KTO-18",
"PathingCapDefID": "pathingdef_medium",
"HardpointDataDefID": "hardpointdatadef_kintaro",
"PrefabIdentifier": "chrPrfMech_kintaroBase-001",
"PrefabBase": "kintaro",
"Tonnage": 55,
"InitialTonnage": 27,
"weightClass": "MEDIUM",
"BattleValue": 5148000,
"Heatsinks": 0,
"TopSpeed": 80,
"TurnRadius": 90,
"MaxJumpjets": 5,
"Stability": 130,
"StabilityDefenses": [
0,
0,
0,
0,
0,
0
],
"SpotterDistanceMultiplier": 1,
"VisibilityMultiplier": 1,
"SensorRangeMultiplier": 1,
"Signature": 0,
"Radius": 5,
"PunchesWithLeftArm": false,
"MeleeDamage": 70,
"MeleeInstability": 55,
"MeleeToHitModifier": 0,
"DFADamage": 70,
"DFAToHitModifier": 0,
"DFASelfDamage": 70,
"DFAInstability": 70,
"Locations": [
{
"Location": "Head",
"Hardpoints": [],
"Tonnage": 0,
"InventorySlots": 1,
"MaxArmor": 45,
"MaxRearArmor": -1,
"InternalStructure": 16
},
{
"Location": "LeftArm",
"Hardpoints": [
{
"WeaponMount": "Missile",
"Omni": false
},
{
"WeaponMount": "Missile",
"Omni": false
},
{
"WeaponMount": "Energy",
"Omni": false
}
],
"Tonnage": 0,
"InventorySlots": 8,
"MaxArmor": 90,
"MaxRearArmor": -1,
"InternalStructure": 45
},
{
"Location": "LeftTorso",
"Hardpoints": [],
"Tonnage": 0,
"InventorySlots": 10,
"MaxArmor": 130,
"MaxRearArmor": 65,
"InternalStructure": 65
},
{
"Location": "CenterTorso",
"Hardpoints": [
{
"WeaponMount": "Missile",
"Omni": false
}
],
"Tonnage": 0,
"InventorySlots": 4,
"MaxArmor": 180,
"MaxRearArmor": 90,
"InternalStructure": 90
},
{
"Location": "RightTorso",
"Hardpoints": [
{
"WeaponMount": "Missile",
"Omni": false
},
{
"WeaponMount": "Missile",
"Omni": false
}
],
"Tonnage": 0,
"InventorySlots": 10,
"MaxArmor": 130,
"MaxRearArmor": 65,
"InternalStructure": 65
},
{
"Location": "RightArm",
"Hardpoints": [
{
"WeaponMount": "Energy",
"Omni": false
}
],
"Tonnage": 0,
"InventorySlots": 8,
"MaxArmor": 90,
"MaxRearArmor": -1,
"InternalStructure": 45
},
{
"Location": "LeftLeg",
"Hardpoints": [],
"Tonnage": 0,
"InventorySlots": 4,
"MaxArmor": 130,
"MaxRearArmor": -1,
"InternalStructure": 65
},
{
"Location": "RightLeg",
"Hardpoints": [],
"Tonnage": 0,
"InventorySlots": 4,
"MaxArmor": 130,
"MaxRearArmor": -1,
"InternalStructure": 65
}
],
"LOSSourcePositions": [
{
"x": 0,
"y": 15,
"z": 0
},
{
"x": 3,
"y": 15,
"z": 0
},
{
"x": -3,
"y": 15,
"z": 0
}
],
"LOSTargetPositions": [
{
"x": 0,
"y": 15,
"z": 0
},
{
"x": 3,
"y": 15,
"z": 0
},
{
"x": -3,
"y": 15,
"z": 0
},
{
"x": 3,
"y": 5,
"z": 0
},
{
"x": -3,
"y": 5,
"z": 0
}
],
"VariantName": "KTO-18",
"ChassisTags": {
"items": [],
"tagSetSourceFile": ""
},
"StockRole": "Juggernaut & Close Assault",
"YangsThoughts": "The Kintaro 18 is… a beast of a machine. It's fast for its size, mounts heavy armor, and can literally shred most other 'Mechs with its almost ridiculous amount of close-range weaponry. But, it runs hotter than Hell's saunas."
}
As you can see it's not complete definition, as it refers to movedef_kintaro_KTO-18
, and hardpointdatadef_kintaro
for further details, but it's still just more JSONs.
Example Weapon JSON
Here's an example of weapon JSON - weapon/Weapon_SRM_SRM6_0-STOCK.json
.
It lacks one thing we want, number of times it can fire per 1ton ammo box, other than that it's all in one JSON:
{
"Category": "Missile",
"Type": "SRM",
"WeaponSubType": "SRM6",
"MinRange": 0,
"MaxRange": 270,
"RangeSplit": [
180,
180,
270
],
"ammoCategoryID": "SRM",
"StartingAmmoCapacity": 0,
"HeatGenerated": 12,
"Damage": 8,
"OverheatedDamageMultiplier": 0,
"EvasiveDamageMultiplier": 0,
"EvasivePipsIgnored": 0,
"DamageVariance": 0,
"HeatDamage": 0,
"AccuracyModifier": 0,
"CriticalChanceMultiplier": 1,
"AOECapable": false,
"IndirectFireCapable": false,
"RefireModifier": 0,
"ShotsWhenFired": 6,
"ProjectilesPerShot": 1,
"AttackRecoil": 1,
"Instability": 3,
"WeaponEffectID": "WeaponEffect-Weapon_SRM6",
"Description": {
"Cost": 90000,
"Rarity": 0,
"Purchasable": true,
"Manufacturer": "TharHes",
"Model": "Short-Range Missile Launcher",
"UIName": "SRM6",
"Id": "Weapon_SRM_SRM6_0-STOCK",
"Name": "SRM6",
"Details": "SRM6s are the largest available rack of Short-Range Missiles and pack considerable punch while also being lightweight. As for all SRMs, SRM6 shots deal above-average stability impact per hit compared to other weapons.",
"Icon": "uixSvgIcon_weapon_Missile"
},
"BonusValueA": "",
"BonusValueB": "",
"ComponentType": "Weapon",
"ComponentSubType": "Weapon",
"PrefabIdentifier": "srm6",
"BattleValue": 0,
"InventorySize": 2,
"Tonnage": 3,
"AllowedLocations": "All",
"DisallowedLocations": "All",
"CriticalComponent": false,
"statusEffects": [],
"ComponentTags": {
"items": [
"component_type_stock",
"range_standard"
],
"tagSetSourceFile": ""
}
}
Coming next
In the next few episodes, we'll write some command line tool to load the game data and output some analysis.
The first thing we'll want to explore is weapons' damage per ton rankings.
Posted on April 28, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.