I wanted to add some functionality to the app, but for that I need a bit more data. So here are the changes I needed to do to the data exporter:
- filter out Flamers and Infernos
- split namefield (likeSRM6+ (+2 Dmg)) intobaseName(likeSRM6+) andbonus(+2 Dmg)
- added category
- added baseStabDamage
I might need to do some more changes in the future, for now this will do.
One thing to note is that bonus field is purely visual, and all the bonus information is included in the data already.
Generated JSON example
{
  "baseName": "Gauss Rifle+",
  "bonus": "-2 Tons",
  "tonnage": 13,
  "heat": 5,
  "shots": 1,
  "baseDamage": 75,
  "ammoTonnagePerShot": 0.125,
  "minRange": 180,
  "maxRange": 660,
  "indirectFire": false,
  "baseStabDamage": 40,
  "category": "Ballistic"
}
Script
The changes from previous version are fairly trivial.
#!/usr/bin/env ruby
require "json"
require "memoist"
require "pathname"
require "pry"
class String
  def camelize
    gsub(/_([a-z])/) { $1.upcase }
  end
end
class AmmoBox
  extend Memoist
  attr_reader :data, :path
  def initialize(path)
    @path = path
    @data = JSON.parse(path.read)
  end
  memoize def id
    @data["AmmoID"].sub(/\AAmmunition_/, "")
  end
  def tonnage
    @data["Tonnage"]
  end
  def capacity
    @data["Capacity"]
  end
end
class Weapon
  extend Memoist
  attr_reader :game, :data, :path
  def initialize(game, path)
    @game = game
    @path = path
    @data = JSON.parse(path.read)
  end
  memoize def name
    if has_bonus?
      "#{base_name} (#{bonus})"
    else
      base_name
    end
  end
  memoize def has_bonus?
    [bonus_a, bonus_b].any?
  end
  memoize def bonus
    [bonus_a, bonus_b].compact.join(", ")
  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\./, "").gsub(/[\+\-]\K /, "")
  end
  memoize def bonus_b
    data["BonusValueB"] == "" ? nil : data["BonusValueB"].gsub(/[a-z]\K\./, "").gsub(/[\+\-]\K /, "")
  end
  def category
    @data["Category"]
  end
  def subtype
    @data["WeaponSubType"]
  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 ammo_per_shot
    @data["ShotsWhenFired"] * data["ProjectilesPerShot"]
  end
  def heat_tonnage
    heat / 3.0
  end
  # 10 rounds of shootnig at target
  def ammo_tonnage_per_shot
    @game.ammo_weights.fetch(ammo_category) * ammo_per_shot
  end
  def total_tonnage
    tonnage + heat_tonnage + ammo_tonnage
  end
  def ammo_category
    @data["ammoCategoryID"] || @data["AmmoCategory"]
  end
  def purchasable?
    @data["Description"]["Purchasable"]
  end
  def weapon_effect
    @data["WeaponEffectID"]
  end
  def ignore?
    [
      category == "Melee",
      name == "AI Laser",
      base_name =~ /Flamer/,
      base_name =~ /Infernos/,
      subtype == "TAG",
      subtype == "Narc",
      subtype =~ /\ACOIL/,
      weapon_effect == "WeaponEffect-Artillery_MechMortar",
      weapon_effect == "WeaponEffect-Artillery_Thumper",
    ].any?
  end
  def min_range
    @data["MinRange"]
  end
  def max_range
    @data["MaxRange"]
  end
  def indirect_fire
    @data["IndirectFireCapable"]
  end
  def base_stab_damage
    @data["Instability"]
  end
  def as_json
    {
      base_name:,
      bonus:,
      tonnage:,
      heat:,
      shots:,
      base_damage:,
      ammo_tonnage_per_shot:,
      min_range:,
      max_range:,
      indirect_fire:,
      base_stab_damage:,
      category:,
    }.transform_keys(&:to_s).transform_keys(&:camelize)
  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
  def roots
    [data_root, *@dlc_roots]
  end
  memoize def weapon_files
    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(self, path)}
  end
  memoize def ammobox_files
    roots
      .flat_map{|root| root.glob("ammunitionBox/*.json")}
      .select{|n| n.basename.to_s != "AmmoBoxTemplate.json"}
  end
  memoize def ammoboxes
    ammobox_files.map{|path| AmmoBox.new(path)}
  end
  memoize def ammo_weights
    # MG box occurs twice, but with same ratio
    ammoboxes.to_h{|a| [a.id, a.tonnage.to_f/a.capacity]}.merge("NotSet" => 0.0)
  end
  def inspect
    "BattechGame"
  end
  def as_json
    weapons
      .reject(&:ignore?)
      .map(&:as_json)
  end
end
game = BattleTechGame.new(*ARGV)
puts JSON.pretty_generate(game.as_json)
Story so far
Coming next
In the next episode I'll use these changes to add extra functionality to the app.
 

 
    
Top comments (0)