Formatting

The Tower writes numbers its own way — 18.759M, 4.77T, 1.34S — and players type them back the same way. Thirty-nine functions read and write that notation, plus durations, multipliers and rates, so what your tool shows matches what the game shows and what a player pastes in is understood.

Numbers The Way The Game Writes Them

import {
  formatNumberForDisplay,
  formatCoin,
  formatShard,
  formatCompact,
  formatLargeNumber,
  formatGroupedNumber
} from 'thetowersdk/formatting'

formatNumberForDisplay(18_759_000)     // '18.759M'
formatCoin(4_770_477_147_914)          // '4.77T'
formatShard(482_970)                   // '482.97K'
formatCompact(1_234_567)               // '1.23M'
formatLargeNumber(1.343e24)            // '1.34S'
formatGroupedNumber(1_234_567)         // '1,234,567'

The suffixes carry on well past T — the game reaches values a browser would otherwise print in exponential notation, and a player reading 1.343e+24 has to translate it before it means anything.

Reading What A Player Types

The same notation in reverse. A tracker that asks for a coin total gets 4.77T pasted in, not a digit string.

import {
  parseNumberInput,
  parseResource,
  parseValueWithUnit,
  standardizeNotation
} from 'thetowersdk/formatting'

parseNumberInput('18.759K')     // 18759
parseResource('4.77T')          // { value: 4770000000000 }
parseValueWithUnit('23s')       // { value: 23, unit: 's', parsed: true }

// Accepts what people actually type, then normalises the case.
standardizeNotation('1.5b')     // '1.5B'

parseValueWithUnit reports whether it understood the input, so a form can say "that does not look like a number" rather than quietly treating it as zero.

Durations

Research times, run lengths and cooldowns all arrive in different shapes. These read and write all of them.

import {
  formatDuration,
  formatHoursDuration,
  formatSecondsAsHoursMinutes,
  parseDuration,
  parseDurationToHours
} from 'thetowersdk/formatting'

formatDuration(3661)                 // '1h, 1m, 1s'
formatSecondsAsHoursMinutes(5400)    // '1h 30m'
formatHoursDuration(38.88)           // '1d 14h 52m 48s'

parseDuration('1h, 1m, 1s')          // 3661
parseDurationToHours('38:53:00')     // 38.8833…  — the catalog's own format

parseDurationToHours is the one to reach for with lab research times: the catalogs write them as 38:53:00, and this turns that into hours you can add up.

Multipliers And Rates

import {
  formatMultiplier,
  formatMultiplier3Decimals,
  computeHourlyRate
} from 'thetowersdk/formatting'

formatMultiplier(2.4)            // '2.400×'
formatMultiplier3Decimals(2.4)   // '2.400×'

// Coins per hour from a run's coins and its real time in seconds.
computeHourlyRate(4_770_477_147_914, 3600)   // '1.325B'

A Worked Example

A lab cost table, rendered the way a player would recognise it — costs in the game's notation, research time as a readable duration.

import { LAB_CATALOG } from 'thetowersdk/data'
import { formatNumberForDisplay, parseDurationToHours, formatHoursDuration } from 'thetowersdk/formatting'

const lab = LAB_CATALOG.find((entry) => entry.name === 'Attack Speed')

const next = lab.levels.slice(10, 20)
const coins = next.reduce((sum, level) => sum + level.cost, 0)
const hours = next.reduce((sum, level) => sum + parseDurationToHours(level.duration), 0)

console.log(formatNumberForDisplay(coins))   // '243.56K'
console.log(formatHoursDuration(hours))      // days, hours, minutes

The Rest

Also here: formatDateTimeForDisplay, formatDateToISO and parseSaveDateTimeToMs for the timestamps in a save; normalizeDecimalSeparator for input from locales that use a comma; sortByUnit and sortByConvertedDuration for ordering a table by a column of formatted values; and roundToDisplayPrecision and stripInsignificantDecimalZeros for matching the game's own rounding.

Catalogs → · Save Files → · Charts →