-
Notifications
You must be signed in to change notification settings - Fork 0
Changelog
Generated from the thetowersdk repository. Edits made here are overwritten on the next push — change the source file instead.
Breaking: one website's plumbing is out of the package. thetowersdk/internal/* is gone,
and so is everything else that described how one site stores and moves data rather than what
the game is: per-calculator local state, persisted settings schemas, the cross-page input hub,
save-import payloads, previews and merge rules, dropdown builders, perk preferences, lab menus,
number-format options and the misplaced-save warning. The facts those modules were built from
stay. The save readers are all still here, and readPerkChoicesFromSaveRoot returns
SavePerkChoices, exactly what the save holds.
Breaking: thetowersdk/data no longer computes. estimateBotUptimeFraction and the Bot Bot
amplification estimates are in thetowersdk/mechanics, as is the utility level-skip chance.
MODULE_INFO_CATALOG moved the other way, from the save catalogs into data.
New entry point: thetowersdk/calculators. The calculator registry is its own domain, and
its generated specs no longer reach into knowledge.
Renames. TOOL_LAB_OVERVIEW_CATEGORY_ORDER is LAB_CATEGORY_NAMES,
isToolLabOverviewCategoryLabel is isLabCategoryName, VaultV29TrackerTarget is
VaultV29LevelTarget, and GUILD_SEASON_RELIC_TOKEN_COST — a lookup, not a price — is
GUILD_SEASON_RELIC_TOKEN_COST_BY_RARITY.
Breaking: deprecated aliases removed. BC_COUNTER_LAB_SLUGS,
CHAIN_THUNDER_LAB_MAX_FRACTION_PER_LEVEL, chainLightningPlusDamageFraction,
chainLightningPlusHpDamage, computeChainLightningPlusTotalHpDamage,
waveInfoEliteDoubleSpawnChancePct and HEAT_BC_INDEX_ELS_REDUCTION_NATIVE each had a
replacement under its real name. FLEET_ENEMY_LAB_BENEFIT_PER_LEVEL held a placeholder value, and
getTournamentEnemyLevelSkipHeatSubtract returned 0 for every league because the game has no such
subtract.
Synchronicity is read from the save. countBotSyncUnlocksFromSaveRoot looked for two fields
the game never writes, so it returned 0 for every save and the stones-spent total left
synchronicity out. It now reads synchronicityUnlocked and synchronicityLevel, and
readBotsFromSaveRoot carries a synchronicity block. Slots are priced the way the game prices
them: 1500 to unlock, the first two slots free, 1500 for each after.
ordinaryCoinWeightAtWave(tier, wave) is ordinaryCoinWeightAtTier(tier). The coin mix
depends on the tier alone, and the wave argument was never read.
Project Funding is named correctly, no longer counts twice, and is charted under Utility Dissonance.
Source layout. Each folder's exported types live in its types.ts; tables transcribed from
the game are named *.data.ts; every module opens with a docblock saying what it is for; and
disassembly detail and dead modules are gone from the published source.
Bot tables pinned to the game's numbers, including which four stats belong to which bot. They are not the regular layout the save's twenty slots suggest.
Bot stats are formulas with typed units ({ base, perLevel, maxLevel, unit }) instead of
thirty-odd display strings each, so no consumer has to strip a suffix to get a number.
Two card corrections reverted. Death Ray and Energy Shield are back to the values the card screen shows. The knowledge compartments now record why the stored benefit table misled.
Breaking: one website's vocabulary is out of the public API. Names built on "tracker", which
meant the pages of the site this package came from, now describe what they do.
sharedToolsCatalog, a route table for that site, is gone from thetowersdk/data.
data and save are grouped into folders by the rule mechanics already followed.
Calculators and the MCP server work from an installed copy. Both previously loaded only in the monorepo. The generated specs imported tooling that does not ship, and the server required a file the package excludes.
Fixes.
-
D,AB,AIand every exact power like them can be printed again;1e33had printed as1000N. -
contributionsandsave-decoderresolve as entry points. - A truncated save is explained rather than surfacing a
TypeError. -
DATA_TABLE_SCHEMASno longer claims to cover every table when it covered eleven of 138.
Reproducible builds. Generated artifacts no longer stamp the wall clock, so two releases of the
same source are byte-identical. Every tree in the package is mapped, and tests take a real save
from TOWER_TEST_SAVE.
Community guides are credited by name. thetowersdk/contributions now carries the guides the
knowledge graph draws its contextual claims from — minionek, McBlue, Evan, DrAudacious, ColbyJack,
Kitchen Salt, 1410c and NanaSeiYuri — each with the exact ref the graph cites it by, and a test
that checks the graph still cites it. A guide credited but unused would put someone's name on work
this package never took.
NanaSeiYuri's dissonance sheet is the one that states arithmetic rather than strategy, so the package is checked against it: the +400% and +200% caps, the 5000-wave maximum, x13.40 and x7.20 with every tier maxed, and the rule that a tier is excluded from its own echo. All six agree.
Extraction detail removed from JSON and from a filename. The scrubber walked .ts and .mjs
only, so a JSON file kept three citations naming a symbol and an address, and a test file's own
name carried the toolchain into every graph that indexes by path. Both are in scope now.
Counts corrected. The site claimed 1,642 graph claims against 1,641, and 315 nodes beside 316. Both are guarded.
Docs. A page on running the package from any language over WebAssembly, the client-side save
decoder written up properly, the Google service-account walkthrough moved to the spreadsheet docs
where it belongs, and a section on what normalize does with input nobody checked.
Two new entry points.
-
thetowersdk/save-decoder— the save reader with no Node imports, for decodingplayerInfo.datin a browser tab. Gunzip comes fromDecompressionStream; the NRBF reader is the same onethetowersdk/nodeuses. A save never has to leave the machine it is on. -
thetowersdk/contributions— the roster of whose work this package carries, as data, so a tool can render the credit it is actually using.
Calculator labels are the game's. Every builder field is now named the way the Run Tracker and
the community name it: Enable Chrono Field (CF), CF Reduction (%), Defense Absolute Value,
Current Level. The workshop picker offered WSP_SUPER_CRIT_MULTI and now offers Super Crit Mult, read from the enhancement definitions the package already carries.
Thorns is the calculator players use. It asked for an enemy factor, a thorn multiplier and a
module benefit — parameters of a formula, none of which a player can read anywhere — and returned
damage per hit. It now takes base thorns, tier, plasma cannon and its mastery, the BC reduction
labs and Sharp Fortitude, and answers in hits to kill. thornDamageOnHit is unchanged in
thetowersdk/mechanics for a caller who has the formula's own inputs.
The MCP scratchpad works outside this repository. sdk_sandbox_run shelled out to a script in
the development monorepo, so for anyone who installed the package it was advertised and failed to
spawn. It is now built from what the package ships — list, export, calc, format, decode —
and the tools that genuinely need the monorepo are no longer advertised where they cannot run.
Removed: thetowersdk/inputs. It held the account-state shapes the Run Tracker's own pages
persist — hub merge helpers, storage compaction, per-page local-state schemas. That is one
website's plumbing, not a description of The Tower, and it does not belong in a package about the
game.
Nothing is lost. Every calculator already describes the inputs it takes:
import { CALCULATOR_BUILDERS } from 'thetowersdk/builders'
const calc = CALCULATOR_BUILDERS.find((entry) => entry.id === 'assist.stones')
calc.fields // [{ key: 'currentLevel', label: 'Current level', kind: 'number', min: 0, max: 69 }, …]
calc.defaults // { currentLevel: 0, targetLevel: 10 }
calc.normalize // whatever you have stored -> a complete, valid input record
calc.compute // the resultThat is the same guarantee — pass what you have, get back a complete record with defaults filled in — for all fifteen calculators, described per calculator rather than as one shared blob.
Twelve entry points now. Everything else is unchanged.
Nine new entry points. Nothing was removed or renamed, so this is additive for anything already on 0.5.2.
Skip 0.5.3. It reached npm before any of this landed, carrying the 0.5.2 export set, and a published version cannot be replaced. Everything below ships as 0.5.4.
Enough to build a tool end to end without writing any of the plumbing:
-
thetowersdk/builders— the calculators as data. Each one declares its fields, their units and their caps, so a UI, a bot command or a test can be generated from the same declaration rather than hand-written three times. -
thetowersdk/bot—createTowerBot()andcalculatorCommands(), which turn every builder into a slash command. Transport-agnostic: it produces commands and replies, and never touches a Discord client. -
thetowersdk/sheets— a Google Sheets reader that knows the two ways a sheet lies to you. A spilled cell carries no formula, and the API truncates trailing empties, so a short row means "unknown", not "zero". -
thetowersdk/inputs— the shared input vocabulary the builders parse, including a decimal separator that follows the reader's locale. -
thetowersdk/assets— which image file is a given module or card. A module's file is named after its INITIALS and rarity (Om Chipat Epic isepic_oc.png), which is not guessable from its name. Every function returnsnullfor something with no art, so a caller can tell that apart from a wrong path. The artwork itself is NOT in this package: it belongs to TechTree Games and ships separately.
Conventions that now hold across every calculator, because each was a real bug
first: a level past the end of a cost table is refused rather than priced at
zero, caps come from each curve rather than a shared constant, and any lookup
keyed by a name from a save, a sheet or a URL uses own-key access — ?? cannot
reject an inherited function.
And the game data that used to live in the consuming application.
-
thetowersdk/charts— the shared chart registry, its data, and the links from each chart to the mechanics it documents. A chart is a view over game data, so its definition is game data. -
thetowersdk/knowledge— the Tower Oracle graph: mechanics, how they relate, and the specific ways each has been misread.
Moved into thetowersdk/data from @tmrxjd/platform, which now re-exports them
so existing consumers keep working:
- Relic unlock methods and the published bonus-total categories.
- Theme category definitions, the passive coin-bonus coefficients, and the full theme catalog. The formula string and the 169 per-item bonus literals are gone — both are derived from one rate per category now.
- Vault tree summaries, derived from the node lists rather than transcribed.
- Daily mission tier and weekly reward tables, with weekly totals derived from the rows they sum.
- The module pull simulator: pity counters, rarity rates, pool sizes, RNG walk.
@tmrxjd/platform depends on this version for thetowersdk/charts and
thetowersdk/knowledge; neither existed in 0.5.2.
Keep node:fs modules (repo-root, planner codegen, debug-graph session/trace, coverage
citations, graph tooling, builders) off the public thetowersdk/mechanics barrel so
browser bundlers do not crash when importing wave/EP formulas.
Stop shipping the monorepo docs-gen/doctor/kernel chain through builders so the
published package no longer requires @tmrxjd/governance-engine at install time.
Labs catalog refresh, card gem costs, Effective Paths economy stone mastery / UW cooldown helpers,
and the public graph surfaces (ep-graph, sdk-graph, save-graph, debug-graph, coverage,
planner-engine, mcp-contract).
Doctor / kernel / governance-adapter / registry / sandbox / docs-gen / lsp stay in the monorepo
source tree for AGS tooling but are not part of the published thetowersdk/mechanics barrel —
they need @tmrxjd/governance-engine, which is not a public npm dependency of this package.
sharedToolsCatalog gains an Effective Paths entry, so a consumer building links or a menu from the
catalog gets the tool rather than a gap.
README: a worked example of planning for a real player — what a config is versus levels, how to fill
a zero record from the candidate lists, and the fact that mapping a save onto them is the caller's
job rather than something this package does. The MCP section now lists the tools and says which two
change how an agent works: plan_effective_path and wiki_page.
Converts the community wiki's Fandom wikitext to Markdown, and fetches a page:
import { fetchFandomPageAsMarkdown } from 'thetowersdk/wiki'
const markdown = await fetchFandomPageAsMarkdown('Cards')Wiki text is CC-BY-SA and this package is MIT, so pages are fetched rather than bundled.
The MCP server gains wiki_search and wiki_page, so an agent can confirm how a mechanic behaves
instead of inferring it from a table. Set TOWER_WIKI_DIR to a directory of slug.md pages to serve
them offline; every response reports whether it came from local, cache or fandom.
planEffectiveHealthPath, planEffectiveEconomyPath and planEffectiveRegenPath now carry
issues alongside steps and excluded, as planEffectiveDamagePath already did. Each checks its
levels before planning and returns an empty path with a populated issues rather than computing
against a record it cannot use.
This is additive for callers reading a plan, and a new field for anyone constructing one. If you
destructure a plan result, issues is EffectiveLevelsIssue[] — { path, message }.
Levels are held to completeness and finiteness, not magnitude. Negative and fractional levels are accepted deliberately: the source sheet carries a negative stone level of its own.
planPath checked each candidate's value for finiteness but not the value it ranked them against.
With a non-finite baseline every ROI was NaN, NaN > NaN is false, and the first candidate
examined won every step — a path in declaration order presented as a recommendation. Those
candidates are now skipped as unevaluable and reported.
-
mechanics/coverage.ts→mechanics/formula-coverage.ts. Same exports, unchanged. The deploy build of a consuming app rejects any file namedcoverage.tsas a test artefact. -
EffectiveRegenLevelsandZERO_EFFECTIVE_REGEN_LEVELSnow live inmechanics/effective-paths-regen-levels.ts, matching where the damage and economy models keep theirs. Both are still re-exported from the planner, so no import needs to change.
58 label builders became formatters: buildWorkshopStatFieldLabel is now
formatWorkshopStatFieldLabel, and so on for every build*Label that takes one
argument and returns a string. Turning one value into words for a human to read
is formatting, not assembly.
Scoped deliberately. 64 exported build* take one argument and return a string,
but six of them build keys -- buildTrackerRunFingerprint,
buildBattleRunDeduplicationKey -- and a key is assembled, not formatted. Those
kept build.
Breaking. Every lab table moved, and 562 exported functions were renamed. If you are on 0.2.0 the compiler will point at each one; the rules below say what to rename them to.
generatedLabs and labs are gone. LAB_CATALOG replaces both.
// before
import { generatedLabs, labs } from 'thetowersdk/data'
// after
import { LAB_CATALOG } from 'thetowersdk/data'They were two files split by category with no overlap — 150 labs in one, 75 in
the other — and, worse, two units. labs stored cost pre-scaled with a
currency of B/T/q/Q, so cost: 1.1 meant 1.1 quadrillion, while
generatedLabs stored coins. Nothing in the shape said which you had.
cost is now absolute coins everywhere, and currency no longer exists. A
lab that costs 1.1 quadrillion reads 1.1e15, so two labs can be added together
without checking where either came from. If you were multiplying by a currency
factor, delete that. If you were formatting with the suffix, formatCompact
already renders 1.1q on its own.
value moved from the level to the lab. It was identical on every level and
only the first was ever read. unit moved with it.
Six labs gained cost data they never had (the three enemy Health labs and Wave
Skip Mastery among them), and super_tower_bonus had 25 corrupt levels
corrected against the community Effective Paths tables.
One verb per idea. The retired ones and what they became:
| Was | Now | Why |
|---|---|---|
resolve* |
get*, find*, compute*
|
meant three different things |
derive*, extract*
|
read*, compute*
|
reading a save vs calculating |
calculate* |
compute* |
same thing, two words |
create* |
build* |
same thing, two words |
find* returns T \| null \| undefined; get* always returns a value; read*
pulls structure out of a save or file; compute* calculates. The split was made
from return types read out of the compiler, not from guessing at names — 124 of
the 265 resolve* functions turned out to be doing arithmetic.
34 names also lost a redundant From<Source> tail, but only where no sibling
function differed by source: readDissonanceCalculatorStateFromSaveRoot is now
readDissonanceCalculatorState, while readLabsFromSave keeps its tail because
readLabsFromReport exists beside it.
The rules are in docs/NAMING.md.
The 297 hand-curated community acronyms — what players actually type, dmg+,
zerk, aspd+ — are merged in. The glossary goes from 233 entries to 500 and
covers all 297, asserted by a test.
Entries now carry source. catalog means the expansion names something in the
shipped data and is held to it; community means real player shorthand the
catalogs have no row for. An expansion that resolves to nothing and is not
marked community fails the build, so nothing can quietly become a claim about
the game.
GC now returns two entries — Galaxy Compressor from the module table and glass
cannon from the community — flagged ambiguous rather than the SDK picking one.
-
computeModuleStat(wascalculateModuleStat) is unchanged in behaviour; module stats were checked against the Effective Paths tables and all 60 level-1 values agree. - A guardian cooldown max level and a Death Wave stone cost were corrected.
Lab catalog consolidation, first cut. Superseded by 0.3.0 — prefer that.
Initial public release.
Generated from TheTowerSDK — do not edit here. Docs and live demos: https://tmrxjd.github.io/TheTowerSDK/
- Quick Start
- Entry Points
- How It Fits Together
- Getting a Save File
- Reading a Save
- Reading The Community Wiki
- Formulas
- Builders
- Charts
- Effective Paths
- Examples
- Templates
- Building a Bot On This
- Google Sheets
- Desktop and Mobile
- Optional Add-ons
- Using It With An AI Agent
- Using Your Own Artwork
- Names And Acronyms
- Patch Notes
- Accuracy
- Versioning
- Where the Docs Live
- Contributing
- Credits
- License
Guides
Examples
- 01-browse-game-data.ts
- 02-read-a-save-file.ts
- 03-plan-upgrades-from-a-save.ts
- 04-generate-a-chart.ts
- 05-generate-a-cost-table.ts
- 06-read-the-community-wiki.ts
- 07-format-like-the-game.ts
- 08-build-a-calculator.ts
- 09-build-a-bot.ts
- 10-read-a-sheet.ts
- 11-build-a-knowledge-base.ts
- 12-show-module-and-card-art.ts
Templates