Ghosts, Radial Bullets, and the Curious Case of LIFETIME
This week's update touched three different systems, and all three turned out to have the same energy: "this works perfectly... except for one specific case nobody thought about."
MULTISHOT: 360 degrees of chaos, minus one
The new MULTISHOT upgrade fires extra projectiles in a radial spread — stack it 20 times and you fire 20 bullets, evenly spaced, 18° apart, in a full circle around your ship. Very satisfying. Very "I am become death, destroyer of cubes."
There's one rule though: the first bullet always fires straight ahead, no matter how many stacks you have. Every other bullet fans out symmetrically around it. So instead of a perfectly random-feeling spray, the spread always has a clean "forward" bullet anchoring the pattern — which, unsurprisingly, feels way better to aim than a circle with no obvious "front." We built this with a small rotation-matrix helper that computes each bullet's direction from the stack count, and even hand-drew the upgrade icon in Python — six little yellow bolts radiating from an orange core. Look, not every asset needs Photoshop.
The enemies that wouldn't stay dead
Here's a fun one: ricochet projectiles were bouncing off enemies after they died. The enemy's sprite was gone, the health bar said zero, the game clearly considered it deceased — and yet bullets kept richocheting off its corpse like it had unfinished business.
The cause: marking an enemy as PreventCollision on death wasn't enough, because its collider was still sitting there in the physics world, fully solid, just... invisible. The fix was to explicitly call collider.clear() the moment an enemy dies, on top of the collision-type change. While we were in there, we also tightened up the win condition — it used to check status !== 'alive', which technically counts enemies that haven't even spawned in yet as "not alive" (true, but not helpful). Now it specifically checks for 'dying' or 'dead', so the stage doesn't declare victory just because half your enemies are still loading in.
LIFETIME: the upgrade that didn't read the rulebook
Every upgrade in the game follows the same scaling rule: roughly ±5% per stack, nice and predictable. Except LIFETIME, the upgrade that extends how long your projectiles stay alive before despawning. That one scales by a flat +250ms per stack, starting from a 750ms base, capping out at 5500ms at the maximum 19 stacks.
Why? Because "5% of a projectile's lifetime" is a deeply unsatisfying number to reason about while playing, and a flat time bonus is something a player can actually feel. So LIFETIME gets its own special case in the scaling code, with its own named constant, and a comment that basically amounts to "yes, this one is different, no, we're not fixing it, it's a feature." We also found — and fixed — a bug where the post-win upgrade picker was being handed a brand new, empty upgrade-tracking object instead of your actual one, so it kept offering upgrades you'd already maxed out at 19 stacks. Nobody needs a 20th LIFETIME. Nobody.
Next time: an actual Options menu, volume sliders that apply live, and a small quality-of-life fix for the worst feeling in any roguelike — losing a run and getting dumped back at square one.