Combo Kills & Kill Streaks

A single chain counter rewards uninterrupted offence. Every enemy kill increments game.killStreak, and the counter survives any shield-only hit. Two things break the chain: any hull damage (shield-bleed-through is never possible, so the chain only dies when HP actually drops) and a 2-second silence between kills.

Streak counter

  • Increment site: damageEnemy in engine/combat/damage.ts, inside the non-anchor / non-shared-health kill branch.
  • On every counted kill: game.killStreak += 1, game.killStreakTimer = 2.0, game.bestStreak is updated if the new value beats it.
  • Decay: bridge.ts ticks killStreakTimer each frame; when it hits zero the streak and milestone index reset.
  • Break on hull: damagePlayer sets game.killStreak = 0 and game.lastStreakMilestone = -1 whenever hullDmg > 0. Pure shield absorption never touches those fields.
  • Anchors, shared-health boss bodies, and the boss kill itself route through separate signals and do not advance the streak counter.

Milestones

Defined in data/kill-streaks.ts as STREAK_MILESTONES. Each milestone fires once per streak — game.lastStreakMilestone stores the highest index already awarded so re-crossing thresholds inside the same chain is a no-op.

KillsLabelBonus XPColor
10STREAK ×1010#88ff88
25RAMPAGE ×2530#ffff44
50MASSACRE ×5080#ff8844
100UNSTOPPABLE ×100200#ff44ff
200GODLIKE ×200500#44ffff

Cumulative bonus XP for a full 200-kill chain: 820 XP (10 + 30 + 80 + 200 + 500). The loop in _checkStreakMilestone walks the table in order and breaks after the highest newly-reached milestone, so multi-step jumps (e.g. crossing 10 and 25 on consecutive frames) still award each tier on its own frame.

On-milestone effects

When a threshold fires, _checkStreakMilestone runs:

  • XpAccum.collect(m.bonusXp) — bonus XP routes through the standard XP accumulator and stacks with normal orb drops.
  • Notifications.add(m.label, m.color) — banner text in the milestone color.
  • Particles.burst(...) — celebratory spark burst at the enemy’s death position; particle count scales with milestone index (20 + i * 8), speed 80 + i * 30, color parsed from the milestone hex.
  • Juice.fire('elite_kill') — reuses the elite kill juice recipe.
  • Sig.fire('kill_streak_milestone', 0, 0, m.killCount, 0, '') — broadcast for any effect-system listener.

HUD readout

engine/rendering/hud.ts draws a minimal label below the FPS counter once game.killStreak >= 5:

  • Before the first milestone (5–9 kills): generic N× STREAK text in MedicalPalette.statusGood.
  • After a milestone fires: switches to STREAK_MILESTONES[lastStreakMilestone].label and .color, so the HUD tier-color matches the most recent threshold awarded.

No glow, no shake, no other on-screen flourish from the counter itself — the milestone banner + particle burst carry the celebration, the HUD label is just the running tally.

  • kill_streak_milestone — fired on every milestone crossing (kill count in dxa slot).
  • enemy_kill / tagged_kill — fired once per non-anchor, non-shared-body kill in the same branch that increments the streak.
  • hull_damage — fired by damagePlayer immediately before the streak is wiped, so any consumer that wants to react to “chain broken” can listen for hull damage instead of polling the counter.