The Level Only Moves Three Numbers
Back when every tower learned to level from 1 to 100, I authored two stat blocks per tower and let a straight line fill in the other 98. Good system. But it quietly scaled everything: a leveled Frost tower slowed harder, a leveled Tesla split more generously, a leveled poison dart poisoned longer, all as a side effect of the same level curve. This week I drew a firm line through the middle of that, added a whole upgrade family, dropped a sandstorm on one of the maps, and rebuilt the stage-select screen. A grab-bag week.
Level scales three numbers, nothing else
The rule now: a tower's level scales only damage, range and interval. Those three, and only those three. The two endpoint blocks a tower authors, level1 and level100, hold exactly those stats and the linear interpolation fills in the rest, same as before.
Everything else a tower carries, its actual personality, moved out into a separate towerEffects.json: splash radius, slow power, burn DPS, poison duration, bounce count, the Tesla's target count and split efficiency, execute threshold, aura multipliers, all of it. These are fixed across levels. A level-100 Frost tower slows for exactly the same amount as a level-1 one; what leveling buys it is more damage, more range, and a faster interval.
Why bother? Because it makes the two axes mean different things. Leveling is the "make this tower bigger" axis. Upgrades are the "make this tower special" axis. Before, they were tangled: you could not push a tower's raw power without also inflating its slow or its burn, which made balance a guessing game. Now the only way to grow an effect is to pick the matching upgrade card, which is a deliberate choice you spend a level-up on. The inspect panel and the upgrade card sort themselves out from this: the card only ever shows damage, range and fire rate changing per level, and the special stats show up on the inspect panel where they belong.
The nice part is how little broke. towerLevelDef interpolates the three core stats and then merges the fixed effects on top, returning the same full definition every system already reads. Every lvl.splashRadius and lvl.slowPower in the codebase kept working untouched; they just read a constant now instead of an interpolated value.
Electricity: the Tesla finally has its own upgrades
With effects frozen at the tower level, the only way to grow the Tesla's star zap is upgrades, so I added two, worded around "electricity" rather than the tower:
- Chain Lightning grows the number of enemies each shot arcs to at once.
- Superconductor raises the split efficiency, so the damage divided across those targets keeps more of its value per enemy instead of thinning out.
Both are levelable cards, both cost a little gold to unlock, and both are the only way to push the Tesla past its base 3 targets and even split. The bolt count drawn on screen reads from the same value, so an upgraded Tesla visibly throws more lightning. This is exactly the shape I want every tower to eventually have: a modest base and a couple of upgrades that define how it grows.
While I was in enemy flags I also added bounceImmune: an enemy with it set absorbs a ricochet disc instead of passing it along. The disc still deals its damage, it just stops there rather than chaining onward, so a well-placed immune enemy can be a wall that kills a Ricochet build's whole gimmick.
Sirocco: a map that blinds you
One of the stages, Sirocco, got a proper sandstorm hazard. A wall of dust sweeps in from the left, holds covering the entire screen for eight seconds, then slides off to the right, on a repeating cadence with a top-right countdown telling you when the next one lands and how long the current one lasts. While the cloud is over a tower, its range is cut, so the storm is a real tactical event, not just decoration: you place with the storm in mind or you go blind and short-ranged for a third of the fight.
Getting it to feel like weather instead of a gray rectangle took a few passes. The leading and trailing edges undulate in an "S" wave, each row offset a little, so the front looks like billowing sand rather than a sliding bar. The dust opacity rides a triangular envelope, faint as it arrives, densest at the midpoint, faint again as it leaves, all from a tiny pure stormHaze helper. And the camera does a soft flag-waving wobble under the cloud, via a new cameraOffset stage hook, so the whole screen breathes with the wind. Playtest feedback trimmed the hold from ten seconds to eight and softened the edges; it reads much better as actual weather now.
Endless never runs out of bosses
Endless mode used to go quiet on bosses once you passed a stage's last authored boss wave. Now it keeps throwing them, on a decreasing cadence: 25 waves after the last one, then 17, 14, 12, and tightening down to a boss every 2 waves forever. The pick is a random boss off the seeded RNG, so co-op stays deterministic and everyone fights the same monster. Deep endless runs finally feel like an escalating siege instead of an endless normie parade.
Stage select, redone
The stage-select screen got flattened into clean rows: name, inline medals, max wave reached, and a single PLAY or BUY button at the end. Locked stages open a buy-confirmation card that greys out the BUY when you cannot afford it (clicking the dead button plays a soft "denied" sound and keeps the card open, rather than doing nothing silently). Difficulty selection became a modal card over the dimmed stage list: a 2x2 grid of medal tiers with lit and greyed icons and metal-versus-diamond active backgrounds. Endless now sits behind the gold medal like every other tier, so it is a thing you earn rather than a thing that is just there.
The HUD grew a couple of small wave-stat lines in the bottom-right, above the toolbar buttons: how many enemies remain in the active wave, or a countdown to the next wave once the current one is cleared, plus a second line that only appears when more than one wave is running at once. The counting is a small pure helper; the HUD just renders it. And the various phase-event countdowns all moved to that same bottom-right corner so the eye has one place to check for "what happens next."
Three axes cleanly separated, a tower family that finally has its own upgrades, a map that fights back, and a menu that reads at a glance. Back to the board.