/** * Combat rules — the pure arithmetic shared by the sandbox's actors * (the player's armor, the enemy craft, and whatever system ports out of * the range into the real game). No Phaser, no scene, so it runs in bare * Node: dev/combat.test.mjs exercises exactly this file. * * An armor block is `{ shields, hull }` (both ≥ 0): a hit spends the * shields first, then the hull, and the craft is dead at hull ≤ 0 — the * same rule for the player (data/combat.json → playerShip) and the * hostiles (→ enemy). */ /** * Spend damage off an armor block (mutates it). * * @param {{shields: number, hull: number}} armor the block to hit * @param {number} amount damage (negative/NaN treated as 0) * @returns {{shieldHit: number, hullHit: number, dead: boolean}} * shieldHit/hullHit = how much each layer absorbed; dead = hull ≤ 0. */ export function applyDamage(armor, amount) { let rem = Math.max(0, Number(amount) || 0); const shields = Math.max(0, armor.shields ?? 0); const shieldHit = Math.min(shields, rem); rem -= shieldHit; const hull = Math.max(0, armor.hull ?? 0); const hullHit = Math.min(hull, rem); rem -= hullHit; armor.shields = shields - shieldHit; armor.hull = hull - hullHit; return { shieldHit, hullHit, dead: armor.hull <= 0 }; } /** * The render heading (rad) of a ship sprite: its rotation minus the * art's facing offset (ship.artOffset — the sheet art may not face east, * js/entities/Ship.js). A plain object with a .rotation is fine. */ export function headingOf(ship) { return ship.rotation - (ship.artOffset || 0); } /** * One step of the standoff-ring AI: advance the craft's orbit angle and * return the point on the ring it should steer to (pure — the craft * does the steering, js/combat/EnemyShip.js). * * @returns {{angle: number, x: number, y: number}} the new orbit angle + target */ export function orbitStep(px, py, orbitAngle, orbitDir, orbitSpeed, dt, orbitRadius) { const angle = orbitAngle + orbitDir * orbitSpeed * dt; return { angle, x: px + Math.cos(angle) * orbitRadius, y: py + Math.sin(angle) * orbitRadius, }; }