// combat/balance.js (Lane B) — every combat number. Sibling of flight/tuning.js: that file
// decides how ENDO-1 handles, this one decides what the fight is worth.
//
// Sized against the stub esophagus (flow 14 u/s, playable disc radius ~7..10 u) and against
// the player's own numbers: disc top speed 13 u/s, coat 100 with 8/s regen after 2.5 s.

export const BALANCE = {
  // Lysozyme cannon: infinite ammo, heat-limited (GDD). Heat is the whole design — the gun
  // is free, so the cost has to be time. Sustained fire nets +25 heat/s (11 shots/s × 5.5,
  // less 30/s cooling) => ~4 s of held fire before lockout, then ~2 s to recover to resumeAt.
  // Burst and you never see the meter; hold and you're disarmed exactly when it's busiest.
  cannon: {
    rof: 11,                 // shots/s
    speed: 95,               // u/s in world space (closing speed ~75 with player flow)
    life: 1.15,              // s => ~109 u range, comfortably past the fog
    damage: 12,
    radius: 0.35,
    heatPerShot: 5.5,
    coolRate: 30,            // /s
    overheatAt: 100,
    resumeAt: 40,            // hysteresis — no stutter-firing on the edge of lockout
    spread: 0.004,           // rad, seeded — a hair of life, not a handicap
    pool: 96,                // rof × life = ~13 in flight; 96 covers spikes with room
  },

  // Antacid torpedo: the dual-use secondary (GDD). Also neutralizes an acid zone for ~10 s
  // via the level:neutralize bus event — A/C consume it. Ammo-limited, pickup-fed (round 2).
  torpedo: {
    rof: 0.8, speed: 42, life: 3.2, damage: 45, splash: 9, radius: 0.7,
    ammo: 6, ammoMax: 6,
    neutralizeRadius: 14, neutralizeDuration: 10,
    pool: 8,
  },

  enemies: {
    // amylase droplet — slow floater with a corrosive contact aura. A soft obstacle: it
    // doesn't chase, it taxes the lane it sits in. Aura ticks discretely (see enemies.js).
    floater: { hp: 24, radius: 2.2, score: 100, drift: 1.6, bob: 0.5,
               auraRadius: 4.2, auraDps: 18, auraTick: 0.2, pool: 24 },

    // pepsin — small fast seeker, dissolves on contact. Chases in spline space (s,x,y), so
    // a pursuit costs three floats. speed 19 > player flow 14: it WILL catch you from
    // behind, and the answer is to shoot it or boost, not to outrun it at throttle 1.4.
    seeker: { hp: 14, radius: 1.0, score: 150, speed: 19, turn: 2.4, contact: 16,
              aggroS: 60, pool: 32 },

    // antibody turret (Peyer's patch) — wall-mounted, fires homing darts. Dart turn radius
    // is speed/turn = 24/1.8 = 13.3 u, wider than the tube itself: it can never corner you,
    // only punish a straight line. Dodgeable by design (charter).
    turret: { hp: 40, radius: 1.6, score: 250, fireEvery: 1.9, rangeS: 55,
              dartSpeed: 24, dartTurn: 1.8, dartLife: 4.5, dartDamage: 14, pool: 12 },
  },

  darts: { pool: 48, radius: 0.45 },

  // --- hazards (round 2) -------------------------------------------------------------
  // C authors WHERE and HOW MUCH in the level JSON (speed/period/amplitude/span/warn); these
  // are the mechanical constants those params drive. C's params always win where they overlap.
  hazards: {
    // reflux_surge — the rear chaser. C's finale: speed 21 vs flow 20, spawning 40 u behind
    // and chasing 600 u. Lethal on contact by C's authoring (`lethal: true`); the s=1700
    // teaching burp is `lethal: false` and only grazes. Antacid stalls it (`neutralizable`).
    surge: {
      graze: 26,            // coat damage/tick for the NON-lethal teaching version
      grazeTick: 0.35,      // s between graze ticks while inside it
      stallFactor: 0.0,     // speed multiplier while neutralised — 0 = fully parked.
                            // C's design leans on this: "fired backward it stalls the surge
                            // ~10 s" (their finale maths assumes a real stop, not a slow).
      proximityRange: 220,  // u within which we emit hazard:proximity (E's rear indicator)
      catchRadius: 3.0,     // u of s-overlap that counts as being caught
    },
    // aortic_squeeze — DIRECTIONAL by design (C): the arch presses from `theta` only and the
    // opposite arc is the answer. Non-lethal, and C's teach-then-test ledger depends on that
    // staying true (LANE_C_NOTES: "if B makes squeeze contact lethal, this breaks — tell me").
    squeeze: {
      graze: 14,            // coat damage per contact tick
      grazeTick: 0.3,
      shove: 9,             // u/s pushed off the bulging wall — the hazard MOVES you
      arc: 1.9,             // rad of the cross-section the bulge occupies (~110°, one side)
    },
    // ring_gate — a POINT, therefore timeable: the player modulates throttle to arrive on an
    // open beat. Closed contact is expensive but never lethal (C: it is a skill check, and
    // a flow-locked player cannot stop to wait).
    gate: {
      hit: 30,              // coat damage for arriving on a closed beat
      shove: 6,             // u/s shove toward the centreline (the iris squeezes you through)
      irisMin: 0.12,        // fraction of the lumen still open when fully "sealed" — never a
                            // true wall: a flow-locked player physically cannot stop, so a
                            // real seal would be an unavoidable toll rather than a skill check
    },
  },

  // --- pickups (round 2) -------------------------------------------------------------
  // C owns placement + counts (level JSON); B owns what each one is WORTH. C's round-2 task
  // is to hand over an economy — until it lands these are B's proposal, documented in NOTES.
  pickups: {
    radius: 1.5,            // generous collect radius: pickups are a reward, not a dexterity test
    spin: 1.4,              // rad/s idle rotation
    nutrient_orb:  { score: 50,  boost: 0.6, note: 'score + boost recharge (shaves cooldown)' },
    mucin_glob:    { coat: 35 },
    b12_cell:      { hull: 30 },
    antacid_ammo:  { ammo: 1 },
    biopsy_sample: { score: 500, sample: 1 },
  },

  // Chained kills feed the combo meter (GDD: score × style multiplier; E's music layer
  // listens). Window is generous enough to cross a fold, tight enough to mean something.
  combo: { window: 2.5 },
};
