engine/combat
PURPOSE — Resolves all damage and collision events for the live frame: applies incoming damage to the player and to enemies/destructibles through the canonical damage chain, runs the broad- and narrow-phase collision passes between player bullets / enemy bullets / ship body / enemy bodies, and stages the kill-side bookkeeping (streaks, milestones, elite/boss bonuses, knockback, deferred death VFX, signal emission).
OWNS
- Enemy mutable combat fields:
hp,shieldHp,flashAmount,vx/vyknockback,_hitImmune,_contactCooldown,_immuneTextCooldown,_tbonedTextCooldown,_stunTimer,_dmgSquashT,_frozenForLag,_lagHp,_deathVfxT/_deathVfxMax,_explodeDebrisNow,_lastDmgTag,_pendingXpCount/_pendingXpGame,_chargerPhaseimmunity flash latch. - Destructible mutable combat fields:
hp,flashAmount,alive. - Ship damage-chain side-effects:
hp,shield,_hitFlash,_hitFlashColor,_dmgBlueFlash,_dmgWhiteFlash,_hullPulse,shieldHitTimer,shieldHitIntensity,_shieldHitAngle,_shieldRecovering,_shieldBrokenTimer,_shieldBackgroundRegen,_shieldBreakPulse,shieldRegenTimer,invulnerable,invulnTimer, post-knockbackvx/vy,alive. GameStatecombat-cycle counters:killStreak,bestStreak,killStreakTimer,lastStreakMilestone,stats.totalKills/eliteKills/damageDealt/damageTaken,tracking.damageTaken/lowestHpPercent/deathDefianceUsed,dmgFlash,_hullFlash,_hitFreezeTimer,_invertScreenTimer,timeDilation(hit-stop),_pendingBarDamage,_lastBossDeathX/Y,_wcSpawnedThisLevel,phase,deathTimer.- Module-private state: combo-kill rolling window and shake cooldown, deferred-death-VFX queue, last-impact-shake throttle timestamp.
ReviveSystemsingleton (active flag + countdown timer for the Death Defiance grace window).- The per-frame enemy spatial-grid contents (rebuilt at the top of
resolve). - Loot spawns produced as a kill consequence: XP orbs pushed into
world.xpOrbs, weapon and artifact boxes pushed intoworld.weaponBoxes/world.artifactBoxes, floating damage numbers pushed intoworld.dmgNumbers.
READS FROM
engine/coreforWorldState/ShipState/GameState,CFG, signal bus, set pool.engine/core/spatial-grid(enemyGrid) for broad-phase enemy queries on every bullet, beam, line, chain, body-contact, and warp-line pass.engine/enemiesforEnemyBehaviorsonDeath callbacks,GameMaster(AI director pressure), andENEMY_PROJ_ARM_DISTANCE.engine/boss/encounterfor the shared boss VFX kit and per-defonBodyDeath/onDeathcinematic hooks viaBOSS_DEFS.engine/affixesfor the damage-filter chain and on-death affix chain.engine/effects/enemy-statusfor the shred multiplier andengine/effects/custom-handlersfor flame-zone spawning on incendiary echoes.engine/player/statesfor the Star Power exclusive-state check (read viahasExclusiveState, available to dependents).engine/world/artifactsfor weapon knockback force, stun duration, and the knockback-notify hook.engine/world/pickupsandengine/world/xp-orbsfor kill-consequence spawns.engine/physics/collision(nearbyTerrain) andengine/physics/rapier-ship/rapier-worldto keep the Rapier body synced after a ship-body collision push.data/weapons(WEAPON_MAP) fordamageTag/secondaryDamageTag/hitShakeon impact.data/enemies(getEnemyCollisionRadius) for collision sizing.data/bosses(BOSS_DEFS) for boss-def death cinematics.data/kill-streaksfor streak milestone thresholds and elite/boss kill bonuses.
PUSHES TO
engine/vfx— particles, damage-number/HP/shield/XP/notification accumulators, juice cues, explosion FX, AoE explosion, sonar ring shards, post-FX rings/scars/arcs/exhausts/streaks, player glow flashes.engine/rendering— camera shake, HP flash trigger, tutorial trigger, hit-direction indicator, shield visual radius lookup.engine/audio— sampled shield-hit and hull-hit cues, archetype-specific death audio.engine/telemetry— damage-chain trace record on everydamagePlayercall, weapon-hit records on every player-bullet collision.engine/combat(self) —damageEnemyis re-entered from beam, chain, line, projectile, body-ram, AoE-blast, warp-line, and chain-explosion sites.- Signal bus — see “Signals fired” below.
DOES NOT
- Pick targets, spawn projectiles, run per-bullet flight behavior, apply weapon stat resolution, or own bullet allocation — that lives in
engine/weapons. - Move enemies, run AI behavior, or own enemy lifecycle outside of the damage→frozen-for-lag→dying transition (the dying→removed step is in the bridge).
- Render anything — only writes state the renderers read.
- Run the fixed-step game loop, schedule the per-frame pipeline, or update timers that aren’t combat-scoped (the ship’s invuln timer is decremented elsewhere; combat only sets it).
- Decide weapon stats, scaling curves, damage tags, archetypes, or hit-shake values — consumes them from
data/weapons. - Own the boss encounter state machine, arena, or director — only fires the body-kill / final-sharer signals and dispatches the configured death cinematic.
- Define affix behavior or scripted DoT — only routes incoming damage through the filter chain and the on-death chain.
- Reconcile the Rapier physics simulation step — only re-syncs ship position/velocity into Rapier after a body-collision push-out.
- Persist or post-process damage-chain telemetry — hands it to the collector and returns.
Signals fired
damage_dealt— every enemy damage event past gates and caps, carries damage amount, hit position, last damage tag.bullet_hit— every player-bullet hit on an enemy, carries enemy eid, damage, and damage tag (fires once per primary tag, plus once per secondary tag when the weapon spec has one).enemy_kill— non-anchor, non-shared-health enemy killed.tagged_kill— same kill as above, carries the last damage tag for type-conditional artifacts.kill_streak_milestone— when the streak crosses a new threshold, carries the milestone kill count.boss_anchor_destroyed— anchor-typed boss body killed (skips the regular enemy_kill path).boss_body_kill— shared-health boss body killed.boss_kill— final-sharer body killed, ending the encounter.shield_hit— any hit that the player’s shield absorbs.shield_break— the hit that drops the shield to zero.hull_damage— any hit that lands hull damage.player_damage— fired on both branches with a'shield'/'hull'tag.tbone_hit— ship-body ram contact, tagged'tbone'(in forward arc) or'ram'.
Signals watched — none. The module reacts to call sites, not signals.
Entry points
CollisionResolver.resolve— top-of-frame collision pass: rebuilds the enemy spatial grid, runs the bullet / enemy-bullet / ship-body subpasses, and ticks per-enemy combat cooldown timers.CollisionResolver.rebuildEnemyGrid— populates the shared enemy grid for this frame, filtering out spawn-protected, dying, and frozen-for-lag enemies.CollisionResolver.resolvePlayerBulletEnemyCollisions— dispatches each player bullet to the correct collision sub-path by_collisionMode(beam trace, chain arc, line, target-only, first-hit, pierce-all) and runs first-hit collision plus pierce decrement.CollisionResolver.resolveBeamTrace— projects enemies onto the beam line and damages all within beam width along the segment.CollisionResolver.resolveLineCollision— point-to-segment damage between two ball endpoints with per-enemy contact cooldowns (Tesla line family).CollisionResolver.resolveChainArc— acquires the first chain-arc target, applies the chain-explosion stack at the strike point, and seeds the chain state the per-bullet behavior consumes on subsequent jumps.CollisionResolver.resolveEnemyBulletPlayerCollisions— broad-phase circle check against the ship’s outer radius, narrow-phase circle-vs-hull-polygon test againstship.hullPoly, then dispatchesdamagePlayerwith the hit angle and the source-specific hp multiplier.CollisionResolver.resolveShipEnemyBodyCollisions— mass-weighted body contact: push-out, knockback, speed bleed, ram damage when above the threshold (with forward-arc T-bone tagging), per-contact cooldown, post-resolution Rapier sync.CollisionResolver.separateEnemies— boid-style pairwise separation force using the spatial grid (with an O(n²) fallback for empty-grid call sites; not invoked fromresolveitself in the current build).updateCollision— compatibility wrapper that callsCollisionResolver.resolve.damageEnemy— canonical enemy damage entry: handles charger lunge immunity, boss hit-immunity windows, shield absorption, shred multiplier, affix damage-filter chain, shared-health boss cap, generic boss cap + per-part / global cooldowns, applies the damage, firesdamage_dealt, runs knockback / stun / life-steal, and on lethal damage transitions the enemy into the frozen-for-lag state while firing kill signals, running affix on-death, dispatching boss-def death hooks, deferring XP-orb spawn, and selecting the archetype-flavored kill juice cue.damagePlayer— canonical player damage chain (invuln gate → flat DR → threshold DR → final damage → universal knockback → shield-vs-hull route → shield-break grace), records one trace row per call intotelemetry.recordDamageChain.damageDestructible— applies damage to a destructible target and adds a grey floating damage number.warpLineDamage— corridor damage along a warp line for nearby enemies with perpendicular knockback.computePlayerDamage— applies the global player damage multiplier to a base value.spawnXPOrbs— kill-consequence XP-orb spawn around an enemy, with depth/tier scaling and the global XP nerf factor, pushed off overlapping terrain.pushOffTerrain— radial push to escape any overlapping asteroid bounding circle, used by every kill-side spawn site.pointToLineDistance— point-to-segment distance helper, used bywarpLineDamageand consumed externally.applyKnockback— generic impulse helper.updateDeathVfx— drains the deferred death-VFX queue (capped per frame), called by the bridge with raw dt so VFX play through hit-stop.ReviveSystem.check/.update/.reset— Death Defiance grace-window state machine;checkconsumes a token on death and starts the timer,updateadvances it,resetclears it on respawn.applyChainExplosion— exported AoE step invoked from chain-arc hits (currently a no-op stub pending the artifact rework).transformHullPoly— applies the ship’s per-frame scale/rotate/translate to the unit-space hull polygon, returning a shared buffer that downstream collision tests consume.pointInConvexPolygon— CCW cross-product winding test for point-in-polygon.circleIntersectsConvexPolygon— center-in-polygon test plus per-edge nearest-point-on-segment distance test against the radius.nearestEdgeNormal— push-out normal and distance from a convex polygon to an external point, used by bounce/separation physics.
Pattern notes
- Lethal damage is a two-stage transition:
damageEnemyflips the enemy into_frozenForLag(kill bookkeeping and signals fire immediately, HP bar collapses, debris and shrink-VFX latch is set), and the bridge later promotes_frozenForLag→_dyingwhen the white lag bar drains. The deferred-death VFX queue exists so a simultaneous wave of kills spreads its cosmetic spawns across frames without skipping gameplay-critical effects. - Combat re-entrancy: AoE blasts, beam traces, chain arcs, warp lines, and ship-body rams all call back into
damageEnemy. Boss caps and affix filters short-circuit redundant chunking; pack leaders and lunging chargers short-circuit knockback / stun / damage entirely. - Boss damage capping is layered: shared-health bodies cap against the live pool (sum of bar-contributing siblings), non-shared bosses cap against their own
hpMaxand consume a per-part immunity window after every hit. Non-player damage sources are filtered out for shared-health bodies. damagePlayeris the single source of truth for player damage. Every stage outcome lands in a telemetry record (including the invuln-gate early-return path) so “knocked around but no damage” symptoms can be traced back to which stage zeroed the result.- Shield never bleeds through to HP on the same hit. When a hit breaks the shield, a short invuln window is set before the next collision pass; the shield’s background regen restarts from zero.
- Bullet vs. player narrow-phase uses the hull polygon (
circleIntersectsConvexPolygon) rather than a single radius, gated by a cheaper bounding-circle broad-phase. - Ship vs. body collision is mass-weighted: ship mass derives from
shipClass×shipScale, enemy mass from collision radius. Mass ratio decides who absorbs the push-out, who keeps speed, and how the speed-bleed clamp interpolates between plow-through and bounce. After resolution the Rapier kinematic body is teleported and re-velocitied to match the resolved values. - Combo-kill camera shake uses a rolling window plus a hard cooldown so rapid kills produce one scaled hit-stop rather than strobing the camera.
- The hit-shake throttle in the impact-VFX path is a module-level
performance.now()latch shared across all bullets, not a per-bullet timer. - The damage-filter chain is the chokepoint for shielded / gated / absorbed enemy affixes — if the chain returns zero, the local body still flashes a “ping” so the player sees the hit registered, but no bar-drain, no shake, no kill bookkeeping fires.
pushOffTerrainis applied to every kill-side spawn (XP orbs, elite reward drops, defunct weapon-chest drop site) so loot never lands inside an asteroid.- The polygon utilities assume CCW convex hulls —
circleIntersectsConvexPolygonrelies onpointInConvexPolygon’s sign convention, andtransformHullPolyreuses a module-level output buffer that callers must consume before the next call.