DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

Open Source Adventures: Episode 54: BATTLETECH Weapon Ranking App

Let's start coding the app. For now I'll hardcode some static assumptions like:

  • we don't care about crit damage
  • we don't care about stability damage
  • 10 ammo per weapon
  • 100% of heat compensated by single heat sinks, so 1 extra ton per 3 heat

Lodash sortBy

JavaScript standard library has an embarassing lack of a lot of basic functionality. I have little patience for writing it all myself every time, so we'll be using lodash for its sortBy function. Its API is quite inconvenient, but it works.


  import data from "./data.json"
  import Row from "./Row.svelte"
  import {sortBy} from "lodash"

  let round100 = (v) => Math.round(v * 100) / 100

  for (let row of data) {
    row.value = row.shots * row.baseDamage
    row.ammoWeight = round100(10 * row.ammoTonnagePerShot)
    row.cost = round100(row.tonnage + row.ammoWeight + row.heat/3.0)
    row.ratio = round100(row.value / row.cost)

  let sortedData = sortBy(data, [(x) => -x.ratio, (x) =>])

<h1>BATTLETECH Weapons Data</h1>

    <th>Ammo Weight</th>
  {#each sortedData as row}
    <Row data={row} />

:global(body) {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
table :global(tr):nth-child(even) {
  background-color: #f2f2f2;
table :global(tr):nth-child(odd) {
  background-color: #e0e0e0;
Enter fullscreen mode Exit fullscreen mode

Here's the main component. We statically precalculate various derived data here. These assumptions will turn into sliders.

Some other points:

  • there's fairly messy :global to make odd/even shading work with tables across component boundary; it's not the only way, but it will do
  • let sortedData = sortBy(data, [(x) => -x.ratio, (x) =>]) is some really awkward API


This component is much easier. Damage and Range column need some custom formatting. Range column could sure be a lot better with some colored bars instead.

Rounding to 2 decimal places could arguably be handled here instead of in the App component.

  export let data

  let {name, heat, shots, baseDamage, tonnage, minRange, maxRange, value, cost, ratio, ammoWeight} = data
  let damage
  if (shots == 1) {
    damage = baseDamage
  } else {
    damage = `${shots}x${baseDamage}`
  let range = `${minRange}-${maxRange}`

Enter fullscreen mode Exit fullscreen mode

Story so far

All the code is on GitHub.

I deployed this on GitHub Pages, you can see it here.

Coming next

In the next episode I'll add some sliders to control assumptions made.

Top comments (0)