DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

4 3

Open Source Adventures: Episode 48: Extracting DLC data from BATTLETECH

I tried various tools to extract data from heavymetal DLC.

DisUnity

First I tried DisUnity - but that's an abandoned project, and it doesn't work with current version.

Unity Asset Bundle Extractor

Next I tried Unity Asset Bundle Extractor which is very nonintuitive but it extracted what I want. "Select All" doesn't work, I needed to select first item, then shift click on the last item. Then I pressed "Export Raw".

It unfortunately created files like: weapon_autocannon_lb10x_0_stock.json-CAB_81519cd4dc355e215c567f814d1dd677--9084477644472635856.dat instead of weapon/autocannon_lb10x_0_stock.json.

They also some weird crap at start and end of each file. It wouldn't be too difficult to strip, but I hoped for just the proper data.

"Dump as text file" and "Dump as json file" options were even worse. Finally "Plugins > Export to .txt" worked.

Unity Asset Bundle Extractor has all information necessary to get it right, it just choses not to.

Organizing the files

The files still had stupid names like weapon_autocannon_lb10x_0_stock.json-CAB_81519cd4dc355e215c567f814d1dd677--9084477644472635856.txt.

So I did a quick:

$ rename 's/-CAB_.*//' *.txt
Enter fullscreen mode Exit fullscreen mode

After that I moved weapon files to heavymetal/weapon/*.json. I might need to organize other files like ammo etc. later.

Fortunately that's the only DLC with extra weapons. If I wanted to analyze all mechs, I'd need to repeat the same steps with other DLCs.

Script

#!/usr/bin/env ruby

require "json"
require "memoist"
require "pathname"
require "pry"

class Weapon
  extend Memoist
  attr_reader :data

  def initialize(path)
    @data = JSON.parse(path.read)
  end

  memoize def name
    bonuses = [bonus_a, bonus_b].compact
    if bonuses.empty?
      base_name
    else
      "#{base_name} (#{bonuses.join(", ")})"
    end
  end

  memoize def base_name
    [
      data["Description"]["Name"],
      data["Description"]["UIName"],
    ].compact.last.gsub(" +", "+")
  end

  memoize def bonus_a
    data["BonusValueA"] == "" ? nil : data["BonusValueA"].gsub(/[a-z]\K\./, "")
  end

  memoize def bonus_b
    data["BonusValueB"] == "" ? nil : data["BonusValueB"].gsub(/[a-z]\K\./, "")
  end

  def category
    @data["Category"]
  end

  def tonnage
    @data["Tonnage"]
  end

  def damage
    shots * base_damage
  end

  def base_damage
    @data["Damage"]
  end

  def shots
    @data["ShotsWhenFired"]
  end

  def heat
    @data["HeatGenerated"]
  end

  def heat_tonnage
    heat / 3.0
  end

  def ammo_tonnage
    # TODO
    0
  end

  def total_tonnage
    tonnage + heat_tonnage + ammo_tonnage
  end

  def ammo_category
    @data["ammoCategoryID"]
  end

  def purchasable?
    @data["Description"]["Purchasable"]
  end
end

class BattleTechGame
  extend Memoist

  def initialize(game_root, *dlc_roots)
    @game_root = Pathname(game_root)
    @dlc_roots = dlc_roots.map{|path| Pathname(path)}
  end

  memoize def data_root
    @game_root + "BattleTech_Data/StreamingAssets/data"
  end

  memoize def weapon_files
    [data_root, *@dlc_roots]
      .flat_map{|root| root.glob("weapon/*.json")}
      .select{|n| n.basename.to_s != "WeaponTemplate.json"}
  end

  memoize def weapons
    weapon_files.map{|path| Weapon.new(path)}
  end

  def print_weapons_report
    weapons
      .select{|w| w.category != "Melee" and w.name != "AI Laser"}
      .sort_by{|w|
        [w.damage.to_f / w.total_tonnage, w.damage, w.name]
      }
      .reverse
      .each{|w|
        puts "* #{w.name}: #{w.damage} / #{w.total_tonnage.round(2)}"
      }
  end
end

game = BattleTechGame.new(*ARGV)
game.print_weapons_report
Enter fullscreen mode Exit fullscreen mode

To run the scirpt I use ./weapons /path/to/battletech /path/to/heavymetal

Result

Here's the result. As the script still doesn't count ammo, it overvalues ammo using weapons. It's also quite confused by any weapon that has special effects like all those TAGs and NARCs and COILs on the end of the list. It might be best to just exclude them the analysis.

  • MG++ (+ 5 Shots, - 0.5 Ton): 30 / 0.0
  • MG++ (+ 50% Crit, - 0.5 Ton): 15 / 0.0
  • MG+ (+ 5 Shots): 30 / 0.5
  • MG+ (+ 25% Crit): 15 / 0.5
  • MG: 15 / 0.5
  • LB 2-X++ (+ 2 Dmg, + 50% Crit): 72 / 5.67
  • S Laser+++ (+ 10 Dmg, + 50% Crit): 30 / 2.5
  • S Laser++ (+ 10 Dmg): 30 / 2.5
  • LB 2-X+ (+ 1 Dmg, + 25% Crit): 60 / 5.67
  • ER S Laser++ (+ 10 Dmg): 40 / 3.83
  • S Pulse++ (+ 10 Dmg, - 2 Heat): 45 / 4.33
  • SRM6+++ (+ 4 Dmg, + 2 StbDmg): 72 / 7.0
  • SRM6++ (+ 4 Dmg): 72 / 7.0
  • SRM4+++ (+ 4 Dmg, + 2 StbDmg): 48 / 4.67
  • SRM4++ (+ 4 Dmg): 48 / 4.67
  • SRM2+++ (+ 4 Dmg, + 2 StbDmg): 24 / 2.33
  • SRM2++ (+ 4 Dmg): 24 / 2.33
  • S Laser++ (+ 5 Dmg, + 25% Crit): 25 / 2.5
  • S Laser+ (+ 5 Dmg): 25 / 2.5
  • Flamer+ (* 4 Ammo, + 5 Dmg): 10 / 1.0
  • UAC/2++ (+ 10 Dmg, - 2 Tons): 70 / 7.67
  • ER S Laser+ (+ 5 Dmg): 35 / 3.83
  • SRM4++ (+ 2 Dmg, + 1 StbDmg): 40 / 4.67
  • SRM4+ (+ 2 Dmg): 40 / 4.67
  • S Pulse+ (+ 5 Dmg, - 1 Heat): 40 / 4.67
  • SRM2++ (+ 2 Dmg, + 1 StbDmg): 20 / 2.33
  • SRM2+ (+ 2 Dmg): 20 / 2.33
  • UAC/20++ (+ 20 Dmg, - 3 Tons): 240 / 28.0
  • SRM6++ (+ 2 Dmg, + 1 StbDmg): 60 / 7.0
  • SRM6+ (+ 2 Dmg): 60 / 7.0
  • LB 2-X: 48 / 5.67
  • S Laser++ (+ 50% Crit): 20 / 2.5
  • S Laser++ (+ 3 Acc): 20 / 2.5
  • S Laser+ (+ 25% Crit): 20 / 2.5
  • S Laser+ (+ 1 Acc): 20 / 2.5
  • S Laser: 20 / 2.5
  • ER S Laser: 30 / 3.83
  • LRM15++ (+ 2 Dmg): 90 / 11.67
  • LRM20++ (+ 2 Dmg): 120 / 16.0
  • LRM5++ (+ 2 Dmg): 30 / 4.0
  • UAC/20+ (+ 10 Dmg, - 1.5 Tons): 220 / 29.5
  • UAC/5++ (- 2 Recoil, - 2 Tons): 90 / 12.33
  • LB 10-X++ (+ 2 Dmg, - 1 Ton): 96 / 13.33
  • LRM10++ (+ 2 Dmg): 60 / 8.33
  • Snub PPC++ (+ 10 Dmg): 125 / 17.67
  • S Pulse: 35 / 5.0
  • M Laser++ (+ 10 Dmg): 35 / 5.0
  • UAC/2+ (+ 5 Dmg, - 1 Ton): 60 / 8.67
  • SRM4++ (+ 50% Crit): 32 / 4.67
  • SRM4+ (+ 25% Crit): 32 / 4.67
  • SRM4: 32 / 4.67
  • SRM2++ (+ 50% Crit): 16 / 2.33
  • SRM2+ (+ 25% Crit): 16 / 2.33
  • SRM2: 16 / 2.33
  • SRM6++ (+ 50% Crit): 48 / 7.0
  • SRM6+ (+ 25% Crit): 48 / 7.0
  • SRM6: 48 / 7.0
  • M Pulse++ (- 4 Heat, + 1 Acc): 50 / 7.33
  • UAC/5+ (- 1 Recoil, - 1 Ton): 90 / 13.33
  • UAC/10++ (+ 10 StbDmg, - 3 Tons): 120 / 18.0
  • LB 5-X++ (+ 4 StbDmg, - 1 Ton): 60 / 9.0
  • UAC/20: 200 / 31.0
  • LB 20-X++ (+ 4 StbDmg, - 2 Tons): 120 / 18.67
  • LRM15+ (+ 1 Dmg): 75 / 11.67
  • LB 10-X+ (+ 1 Dmg, - 0.5 Ton): 88 / 13.83
  • LB 5-X+ (+ 2 StbDmg, - 0.5 Ton): 60 / 9.5
  • UAC/5: 90 / 14.33
  • LRM20+ (+ 1 Dmg): 100 / 16.0
  • LRM5+ (+ 1 Dmg): 25 / 4.0
  • UAC/10+ (+ 5 StbDmg, - 1.5 Tons): 120 / 19.5
  • LB 20-X+ (+ 2 StbDmg, - 1 Ton): 120 / 19.67
  • LB 5-X: 60 / 10.0
  • LRM10+ (+ 1 Dmg): 50 / 8.33
  • M Laser+ (+ 5 Dmg): 30 / 5.0
  • ER M Laser++ (+ 10 Dmg): 45 / 7.67
  • LB 20-X: 120 / 20.67
  • UAC/10: 120 / 21.0
  • Snub PPC+ (+ 5 Dmg): 100 / 17.67
  • M Pulse+ (- 2 Heat): 45 / 8.0
  • LB 10-X: 80 / 14.33
  • AC/20+++ (+ 20 Dmg, + 20 StbDmg): 120 / 22.0
  • AC/20++ (+ 20 Dmg): 120 / 22.0
  • Snub PPC++ (- 10 Heat): 75 / 14.33
  • ER M Laser+ (+ 5 Dmg): 40 / 7.67
  • UAC/2: 50 / 9.67
  • AC/5++ (+ 10 Dmg): 55 / 10.67
  • LRM15+++ (+ 50% Crit, + 2 StbDmg): 60 / 11.67
  • LRM15++ (+ 50% Crit): 60 / 11.67
  • LRM15++ (+ 25% Crit, + 1 StbDmg): 60 / 11.67
  • LRM15++ (+ 2 StbDmg): 60 / 11.67
  • LRM15+ (+ 25% Crit): 60 / 11.67
  • LRM15+ (+ 1 StbDmg): 60 / 11.67
  • LRM15: 60 / 11.67
  • Gauss Rifle++ (- 2 Tons, - 1 Slot): 75 / 14.67
  • Gauss Rifle+ (- 2 Tons): 75 / 14.67
  • AC/20++ (+ 10 Dmg, + 10 StbDmg): 110 / 22.0
  • AC/20+ (+ 10 Dmg): 110 / 22.0
  • LRM20+++ (+ 50% Crit, + 2 StbDmg): 80 / 16.0
  • LRM20++ (+ 50% Crit): 80 / 16.0
  • LRM20++ (+ 25% Crit, + 1 StbDmg): 80 / 16.0
  • LRM20++ (+ 2 StbDmg): 80 / 16.0
  • LRM20+ (+ 25% Crit): 80 / 16.0
  • LRM20+ (+ 1 StbDmg): 80 / 16.0
  • LRM20: 80 / 16.0
  • M Laser+++ (+ 3 Acc, + 50% Crit): 25 / 5.0
  • M Laser++ (+3 Acc): 25 / 5.0
  • M Laser++ (+ 50% Crit): 25 / 5.0
  • M Laser++ (+ 1 Acc, + 25% Crit): 25 / 5.0
  • M Laser+ (+1 Acc): 25 / 5.0
  • M Laser+ (+ 25% Crit): 25 / 5.0
  • M Laser: 25 / 5.0
  • LRM5+++ (+ 50% Crit, + 2 StbDmg): 20 / 4.0
  • LRM5++ (+ 50% Crit): 20 / 4.0
  • LRM5++ (+ 25% Crit, + 1 StbDmg): 20 / 4.0
  • LRM5++ (+ 2 StbDmg): 20 / 4.0
  • LRM5+ (+ 25% Crit): 20 / 4.0
  • LRM5+ (+ 1 StbDmg): 20 / 4.0
  • LRM5: 20 / 4.0
  • Flamer+++ (* 40 Ammo): 5 / 1.0
  • Flamer++ (* 4 Ammo, + 5 Dmg (H)): 5 / 1.0
  • Flamer (* 4 Ammo): 5 / 1.0
  • LRM10+++ (+ 50% Crit, + 2 StbDmg): 40 / 8.33
  • LRM10++ (+ 50% Crit): 40 / 8.33
  • LRM10++ (+ 25% Crit, + 1 StbDmg): 40 / 8.33
  • LRM10++ (+ 2 StbDmg): 40 / 8.33
  • LRM10+ (+ 25% Crit): 40 / 8.33
  • LRM10+ (+ 1 StbDmg): 40 / 8.33
  • LRM10: 40 / 8.33
  • AC/2++ (+ 10 Dmg): 35 / 7.33
  • Snub PPC+ (- 5 Heat): 75 / 16.0
  • AC/5+ (+ 5 Dmg): 50 / 10.67
  • M Pulse: 40 / 8.67
  • ER M Laser: 35 / 7.67
  • AC/20+++ : 100 / 22.0
  • AC/20++ (+ 50% Crit): 100 / 22.0
  • AC/20++ (+ 4 Acc): 100 / 22.0
  • AC/20++ (+ 20 StbDmg): 100 / 22.0
  • AC/20+ (+ 25% Crit): 100 / 22.0
  • AC/20+ (+ 2 Acc): 100 / 22.0
  • AC/20+ (+ 10 StbDmg): 100 / 22.0
  • AC/20: 100 / 22.0
  • L Laser+++ (+ 10 Dmg, + 3 Acc): 50 / 11.0
  • L Laser++ (+ 10 Dmg): 50 / 11.0
  • Gauss Rifle: 75 / 16.67
  • AC/10++ (+ 10 Dmg): 70 / 16.0
  • Snub PPC++ (+ 10 StbDmg): 75 / 17.67
  • Snub PPC+ (+ 5 StbDmg): 75 / 17.67
  • Snub PPC: 75 / 17.67
  • AC/5+++ (+ 4 Acc, + 20 StbDmg): 45 / 10.67
  • AC/5++ (+ 50% Crit): 45 / 10.67
  • AC/5++ (+ 4 Acc): 45 / 10.67
  • AC/5++ (+ 20 StbDmg): 45 / 10.67
  • AC/5++ (+ 2 Acc, + 10 StbDmg): 45 / 10.67
  • AC/5+ (+ 25% Crit): 45 / 10.67
  • AC/5+ (+ 2 Acc): 45 / 10.67
  • AC/5+ (+ 10 StbDmg): 45 / 10.67
  • AC/5: 45 / 10.67
  • L Laser++ (+ 5 Dmg, + 1 Acc): 45 / 11.0
  • L Laser+ (+ 5 Dmg): 45 / 11.0
  • AC/2+ (+ 5 Dmg): 30 / 7.33
  • L Pulse++ (+ 10 Dmg, - 1 Ton): 65 / 16.0
  • AC/10+ (+ 5 Dmg): 65 / 16.0
  • ER L Laser++ (+ 5 Dmg, - 1 Ton): 50 / 12.33
  • L Pulse+ (+ 5 Dmg, - 1 Ton): 60 / 16.0
  • AC/10+++ (+ 50% Crit, + 20 StbDmg): 60 / 16.0
  • AC/10++ (+ 50% Crit): 60 / 16.0
  • AC/10++ (+ 4 Acc): 60 / 16.0
  • AC/10++ (+ 25% Crit, + 10 StbDmg): 60 / 16.0
  • AC/10++ (+ 20 StbDmg): 60 / 16.0
  • AC/10+ (+ 25% Crit): 60 / 16.0
  • AC/10+ (+ 2 Acc): 60 / 16.0
  • AC/10+ (+ 10 StbDmg): 60 / 16.0
  • AC/10: 60 / 16.0
  • ER L Laser+ (+ 5 Dmg): 50 / 13.33
  • L Laser++ (+ 50% Crit): 40 / 11.0
  • L Laser++ (+ 3 Acc): 40 / 11.0
  • L Laser+ (+ 25% Crit): 40 / 11.0
  • L Laser+ (+ 1 Acc): 40 / 11.0
  • L Laser: 40 / 11.0
  • AC/2+++ (+ 4 Acc, + 50% Crit): 25 / 7.33
  • AC/2++ (+ 50% Crit): 25 / 7.33
  • AC/2++ (+ 4 Acc): 25 / 7.33
  • AC/2++ (+ 20 StbDmg): 25 / 7.33
  • AC/2++ (+ 2 Acc, + 25% Crit): 25 / 7.33
  • AC/2+ (+ 25% Crit): 25 / 7.33
  • AC/2+ (+ 2 Acc): 25 / 7.33
  • AC/2+ (+ 10 StbDmg): 25 / 7.33
  • AC/2: 25 / 7.33
  • ER L Laser: 45 / 13.33
  • ER PPC++ (+ 10 Dmg, - 1 Ton): 70 / 21.0
  • L Pulse: 55 / 17.0
  • PPC++ (+ 10 Dmg): 60 / 18.67
  • ER PPC+ (+ 5 Dmg, - 1 Ton): 65 / 21.0
  • PPC+ (+ 5 Dmg): 55 / 18.67
  • ER PPC: 60 / 22.0
  • PPC++ (+ 4 Acc): 50 / 18.67
  • PPC++ (+ 30 StbDmg): 50 / 18.67
  • PPC+ (+ 2 Acc): 50 / 18.67
  • PPC+ (+ 15 StbDmg): 50 / 18.67
  • PPC: 50 / 18.67
  • AC/10: 30 / 21.0
  • COIL-L (Dmg X Evasion): 35 / 27.33
  • COIL-M (Dmg X Evasion): 25 / 19.67
  • COIL-S (Dmg X Evasion): 15 / 12.0
  • Infernos++ (* 8 Volleys, + 10 Dmg (H)): 4 / 4.0
  • Infernos+ (* 8 Volleys, + 5 Dmg (H)): 4 / 4.0
  • Infernos (* 8 Volleys): 4 / 4.0
  • AC/10: 30 / 32.0
  • TAG++ (+ 20% Dmg Mark, * Energy, Ballistic): 1 / 2.67
  • TAG+ (+ 15% Dmg Mark, * Energy, Ballistic): 1 / 2.67
  • TAG (+ 10% Dmg Mark, * Energy, Ballistic): 1 / 2.67
  • Narc Beacon++ (+ 75% Dmg Mark, * Missile): 1 / 3.0
  • Narc Beacon+ (+ 50% Dmg Mark, * Missile): 1 / 3.0
  • Narc Beacon (+ 25% Dmg Mark, * Missile): 1 / 3.0

Story so far

All the code is on GitHub.

Coming next

In the next few episodes, we'll finish the script by including ammo in the count.

Image of AssemblyAI

Automatic Speech Recognition with AssemblyAI

Experience near-human accuracy, low-latency performance, and advanced Speech AI capabilities with AssemblyAI's Speech-to-Text API. Sign up today and get $50 in API credit. No credit card required.

Try the API

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay