Ram Mechanics
Body-contact damage when the ship moves into an enemy fast enough. Resolved each frame inside CollisionResolver.resolveShipEnemyBodyCollisions (engine/combat/collision-resolver.ts). Ram fields are per-ship and ship from data/ships.ts.
Trigger
A ram fires when the ship’s body overlaps an enemy collision circle AND the ship’s current speed (hypot(ship.vx, ship.vy)) is greater than or equal to ramThreshold. Default ramThreshold = 150 u/s. Below that speed the contact is treated as a low-speed bump (mass-weighted decel + light/heavy juice cue, no damage).
The narrow-phase check uses circleIntersectsConvexPolygon against the ship’s hullPoly so the enemy must actually touch the hull, not just the bounding circle.
Damage
Ram damage scales linearly with speed between two anchors:
t = clamp01((shipSpeed - ramThreshold) / (5000 - ramThreshold))
ramDmg = round((ramDamageLo + t * (ramDamageHi - ramDamageLo)) * meleeMult)
ramDamageLo— damage at the threshold. Default40.ramDamageHi— damage at or above5000u/s (the hard-codedRAM_SPD_HI). Default1000.meleeMult— per-ship melee multiplier applied to the final number. Default1.0.
Damage routes through damageEnemy() so armor, shields, and on-hit signals all fire normally.
Per-enemy contact cooldown
After any ram or low-speed contact, the touched enemy gets _contactCooldown = ship.contactCooldown (default 0.25 s). While the cooldown is non-zero that enemy is skipped by the ram pass, so a single sustained overlap can only deal one ram hit every cooldown tick. The cooldown is decremented in CollisionResolver.resolve once per frame.
This is what prevents drive-through enemies from getting shredded by per-frame damage and keeps ram balanced against multi-enemy pileups.
Speed bleed
On a successful ram the ship’s velocity is scaled by a mass-weighted bleed factor:
ramBleed = 1 - clamp(shipFrac, 0.15, 0.85)
ship.vx *= ramBleed
ship.vy *= ramBleed
Where shipFrac = enemyMass / (shipMass + enemyMass). Heavier enemy = closer to a full stop; lighter enemy = plow through with little speed loss. The clamp guarantees you always lose at least 15% and never lose more than 85%.
ramSpeedBleed is a data field on ShipDef (default 0.50) intended for future per-ship override of this bleed; the live formula in resolveShipEnemyBodyCollisions currently derives the bleed from the mass ratio rather than reading this field directly.
Below ramThreshold but above 30 u/s, a separate contactBleed = 1 - clamp(shipFrac * 0.8, 0.1, 0.7) applies — gentler decel for low-speed bumps.
T-bone label
If the enemy sits in the ship’s forward arc (within ~40° of the heading vector) at the moment of the ram, the hit is tagged as a T-bone: spawns the T-BONED damage label and fires tbone_hit with the tbone payload. Off-axis rams still fire tbone_hit but with the ram payload — used by juice / effect systems to distinguish a clean front-axis hit from a glancing side-swipe.
Related fields
ramThreshold,ramDamageLo,ramDamageHi,ramSpeedBleed,meleeMult,contactCooldown— all onShipDef(data/ships.ts).shipClassandshipScale— feedshipMass(heavy=3, medium=2, light=1, multiplied by scale) which drives push-apart and bleed.- Enemy mass derives from collision radius via
ENEMY_MASS_SCALE = 0.07.