Oppenheimer Energy Lance

by Snerpes

Built with Claude Opus. (WIP) -- The Oppenheimer Energy Lance is a prototype mega weapon capable of annihilating entire bases in a single shot, hence the name. It operates purely on electricity. It has energy discharge options that you select using the built-in HUD, capable of producing anything from a 30 tile radius crater up to a 2000 tile radius one. At maximum charge it draws 36 GW from the grid for 16 seconds, then releases that energy over 5.7 seconds, creating an unstable ball of energy.

Content
8 hours ago
2.0
11
Combat

Changelog

Version: 0.31.0
Date: 2026-09-14
  Changes:
    - 36 CAPACITOR BANKS. Each corner cluster is now 3x3, nine to operate
      (one full cluster), thirty-six for the top of the dial. Each bank
      holds a thirty-sixth of the charge instead of a sixteenth. The pad is
      55 tiles across.
    - THE FIRING RING IS HAZARD CONCRETE AGAIN. A two-tile band under the
      drawn conduit ring, flowing round the gun between the clusters and
      straight across the four lanes. The rest of the hardstand is
      unchanged.
    - The Lance Substations stand further out, at the new corners, and
      reach 14 tiles to cover a whole 3x3 cluster.
    - 32 runway lamps, adding a pair at the substation row on each lane.
    - Oppenheimer turrets must now be 56 tiles apart (was 48), so two pads
      never overlap.
    - Existing installations: every bank is still in its slot (the old
      slots are part of the new layout); the twenty new slots are ghosted.
      Each substation is moved out to its new corner with its wires kept,
      because the old spot is now a bank slot; one that cannot move is
      reported. Lamps move and the pad is re-laid. Schema 14 -> 15.
Version: 0.30.0
Date: 2026-09-14
  Changes:
    - THE CHARGING SPHERE, RELIT. User, from a screenshot at 139 tiles/5%
      charge: "sloppy, not imposing" -- the same complaint 0.29.0 fixed for
      the pad, this time for the ball. Four fixes, all in implode.draw_orb
      (shared by the growth phase and the collapse):
    - Shading banding fixed: shade.layers 6 -> 16 at an equivalent total
      alpha (0.15 -> 0.06) and a linear colour ease (shade.ease = 1, was a
      bare quadratic u*u) instead of packing the gradient into the last two
      layers.
    - THE RIM HALO. A single hard stroke around a filled disc reads as this
      mod's own danger-preview rings (render.preview, also filled=false) --
      an information overlay, not a hot edge. Replaced with ball.halo_steps
      fading, widening strokes stepping outward from the true edge.
    - THE BALL CASTS LIGHT (ball.light, rendering.draw_light, scaled off the
      current radius, capped). Everything before this was confined to the
      ball's own silhouette no matter how hot it got.
    - Infall streaks (implode.draw_streaks_at, both the growth and collapse
      phases) now draw a small hot dot at the leading end -- the one closest
      to the ball -- instead of one flat colour top to bottom.
    - No schema change: config and rendering only, nothing persisted.
Version: 0.29.0
Date: 2026-09-14
  Changes:
    - THE PAD IS A HARDSTAND. Straight lines only: a two-tile hazard rim,
      a refined-concrete field, a concrete cross along the four beam lanes
      that breaks out through the rim, and a two-tile hazard frame around a
      square concrete stage under the gun. The tile grid can only draw a
      circle as uneven steps, so the tiled firing ring, the stepped moat and
      the round stage are gone; the drawn conduit ring is the one circle.
    - The hazard stripes mitre at the corners (left and right variants).
    - The pad is one tile wider on each side (47 across), so the masts no
      longer crowd the rim.
    - RUNWAY LIGHTS. The lamps stand in pairs either side of each beam
      lane, one pair per bank row and mast row, so every lamp lines up with
      the hardware beside it. 24 lamps, up from 20. The four that sat on the
      moat next to the stage are gone.
    - Only the bank and mast slots keep a marked socket, hidden once built.
    - The strike flash is square, to match the stage.
    - Existing lamps are moved onto the new slots and the four new ones
      ghosted; existing pads are re-laid. Schema 12 -> 13.
    - A lamp slot that would stand under a capacitor bank's art is now a
      load error naming the slot.
    - THE BUFFERED AND FUEL-CELL BANK TIERS ARE REMOVED. Only the standard
      Capacitor Bank remains -- their items, recipes, technology unlocks
      and locale entries are gone. Existing banks of either tier did not
      survive the update (the engine drops entities of a deleted prototype
      before any migration runs); their slots are re-ghosted for the
      standard bank on load.
Version: 0.28.2
Date: 2026-09-14
  Bugfixes:
    - A TOTAL POWER CUT MID-CHARGE STILL FIRED THE GUN. Losing all incoming
      power partway through the 16-second charge correctly froze the chamber
      gauge -- no energy was arriving -- but nothing aborted the sequence:
      the brownout/DARK detector that exists for exactly this case
      explicitly skips the FIRE state, and beam.fire has no power floor of
      its own. The tape played out in full and the gun discharged anyway, at
      whatever charge it actually held, including zero. Zero joules banked
      at the strike now reports a misfire instead of lighting a lance at
      power 0.
Version: 0.28.1
Date: 2026-09-14
  Changes:
    - Updated description further
Version: 0.28.0
Date: 2026-09-14
  Features:
    - CAPACITOR ARCS. Once a charge passes 10% of the order, red lightning pings
      from bank to bank, faster and thicker as the charge climbs. Mostly within
      a cluster; some jump to the neighbouring cluster, never across the gun.
    - THE BROWNOUT BEAT. On the strike every lamp, bank glow and the conduit
      ring drop almost to black for 14 ticks, then slam back.
    - THE IGNITION SHOCK. On the strike a ring of dust races across the pad
      from the stage to the rim.
    - BARREL VENTING. After a shot, steam pours off the muzzle, from the point
      the lance left, thinning out over about 5.5 seconds.
    - THE BALL IS A SPHERE. The growing sphere and the collapse are shaded
      with a stack of offset discs lit from the upper left, and the ground
      shadow is pushed away from the light to match. The shading is drawn in
      the world only, not on the map.
  Bugfixes:
    - THE LAMPS CLIPPED THE CAPACITOR BANKS, ALL TWENTY OF THEM. The offsets
      cleared the bank's 3-tile footprint, not its art, which is 4.06 tiles wide
      and 5.9 tall; and the per-bank lamps stood 2.5 tiles out along a diagonal,
      only 1.77 tiles per axis. The lamps now stand in an L-bracket on each
      cluster's gun-facing edges, cleared against the bank art itself.
    - THE CONTACT EXPLOSIONS PLAYED UNDER THE BALL. They spawned at the aim
      point, which the ball's opaque fill covers within a few ticks. They now
      land where the drawn lance meets the ball's near face.
  Changes:
    - Existing lamps are moved onto the new slots on load, not deleted; ghosts
      are re-placed. Schema 11 -> 12.
    - Fixed the formatting of the 0.27.1 to 0.27.3 entries (tabs).
Version: 0.27.3
Date: 2026-09-14
  Changes:
    - Updated description reflecting energy reading calculations
Version: 0.27.2
Date: 2026-09-14
  Changes:
    - Updated thumbnail
Version: 0.27.1
Date: 2026-09-14
  Changes:
    - Updated description
Version: 0.27.0
Date: 2026-09-14
  Features:
    - THE PAD IS ITS OWN CROSSHAIR. The floor steps light to dark toward the
      gun: a light refined-concrete stage under it, a hazard frame, a dark
      stone-path moat, a concrete field with four light lanes on the axes, a
      two-tile hazard firing ring at radius 17 running through the bank
      clusters, then a dark curb and the hazard rim. Every slot keeps a light
      pad. Slot geometry is unchanged. Only mineable base tiles are used.
    - The conduit ring is now drawn on the concrete firing ring.
    - GROUNDBREAKING IS A SEQUENCE. Placing the gun draws an outline of the
      pad, ghosts the masts, then each cluster's banks clockwise, then pours
      the floor outward from the centre over about 3.5 seconds. A save taken
      mid-pour finishes it.
    - COMMISSIONING. When the last bank and mast link, the masts light one
      after another, then the conduit ring draws itself around the loop, each
      spur appearing as the arc reaches it.
    - FIRING. A dim aim line runs from the gun to the rim and swings with the
      barrel. During the charge, 24 bulbs run round the firing ring and spin
      up with it. Just before the strike they race to where the aim line
      crosses the ring and stream down it into the barrel. On the strike the
      stage flashes white and keeps a scorch mark, up to six.
  Changes:
    - Existing pads are re-laid in the new plan on load, and an existing
      complete installation commissions once. Schema 10 -> 11, render STYLE
      9 -> 10.
Version: 0.26.4
Date: 2026-09-14
  Bugfixes:
    - THE STANDBY HUM HAD A ~0.5s GAP EVERY LOOP. It was `ambient_sounds`, an
      atmosphere-effect system built for occasional environmental noise --
      `average_pause_seconds` tunes the average gap between REPLAYS of a
      one-shot clip, it does not make one loop gaplessly, so even at 0 it
      audibly re-triggered with a dead patch every cycle.
    - Switched to `working_sound`, which the engine loops natively. Merged
      it with #77's own working_sound (the charge whine) into one
      `main_sounds` array, since a prototype has exactly one working_sound
      key -- the same pattern vanilla's own accumulator uses to layer two
      sounds on one entity.
  Info:
    - The old ambient_sounds choice reasoned that working_sound "is driven
      by the entity being working", so it would never start on a turret
      that never works. `EntityPrototype.working_sound`'s own doc text says
      otherwise ("will also work on entities that don't actually do work",
      confirmed via apiq), and #77 was already live proof of it, fifty
      lines below the hum's old ambient_sounds block the whole time.
    - NOT run in game. If the hum stays silent specifically in STANDBY
      (unlike #77's charging state), main_sounds needs some activity
      signal after all -- fallback is a scripted loop timed off this file's
      own measured duration (sound/README.md).
Version: 0.26.3
Date: 2026-09-14
  Bugfixes:
    - THE DOUBLE BARREL. Reported in game as a second turret sprite pasted on at
      a weird angle. Two bugs stacked. The native folded_animation was still
      drawn, frozen at one facing, under the 0.26.0 script head -- a
      transparent sprite cannot hide a different facing of itself. And the
      script head was a single frame: AnimationPrototype has no
      direction_count, so the 64-facing sheet loaded as its north frame only,
      and writing the render object's orientation spun that bitmap.
    - The native rotating layers are now a transparent 1x1 frame and
      energy_glow_animation is removed. The head is a 64-frame strip whose
      facing is chosen by animation_offset; its shadow and lens glow are
      script-drawn alongside it on the same frame (C.turret.head).
    - Render STYLE 8 -> 9, so every save rebuilds its head, seeded from the
      entity's real orientation rather than north.
Version: 0.26.2
Date: 2026-09-14
  Changes:
    - THE BEAM MUZZLE, SETTLED LIVE. muzzle_forward_mult/muzzle_lift_mult had
      sat at neutral placeholders since the 0.24.0 chassis swap -- a linear
      scale of vanilla laser-turret's own source_offset, never checked
      against this turret's real ~3x scale-up and added tower. Read off
      /oppenheimer-muzzle's marker against the actual barrel tip: 0.7
      forward, 1.1 lift.
  Info:
    - Traced vanilla's laser-turret prototype directly while investigating:
      it carries TWO separate, undocumented source_offset fields (one on
      attack_parameters paired with source_direction_count = 64, one nested
      in the beam's own action_delivery). Neither has doc text in the
      shipped spec, so which one actually governs the on-screen origin was
      only ever going to be settled by looking, not by reading.
Version: 0.26.1
Date: 2026-09-14
  Bugfixes:
    - THE BARREL WAS NEVER ACTUALLY TURNING -- A DEAD WRITE, NOT A RENDER
      GAP. write_orientation wrote relative_turret_orientation first and only
      fell through to orientation if that pcall threw. It never throws on
      this chassis: confirmed via apiq against the shipped spec, writing it
      "does nothing if the vehicle doesn't have a turret" -- accepted
      silently, not an error, so the fallback line that actually moves the
      barrel was never reached. orientation is now written directly. The
      0.26.0 script-drawn head was wired correctly all along; there was
      simply nothing changing for it to follow.
    - THE STALL DETECTOR COULD NEVER HAVE CAUGHT IT EITHER. turret.slew's
      "did the last command take" check compared this tick's reading
      against the PREVIOUS WRITE TARGET rather than the previous READING --
      a frozen barrel produces the same comparison every tick under that
      test, so the 20-second give-up could never fire. Now compares
      consecutive readings, which is what "stuck" means.
  Info:
    - Found by tracing why the fire-control panel's "N degrees to target"
      held at the same number across three separate CONFIRM attempts, then
      confirming the mechanism against the on-disk API spec rather than
      assuming the write behaves like the read.
Version: 0.26.0
Date: 2026-09-14
  Changes:
    - THE ROTATING BARREL IS NOW SCRIPT-DRAWN, NOT AN ENTITY-GRAPHICS FIELD.
      Gate 09 (0.24.0's rotation-fix attempt, folded_animation_is_stateless =
      true) came back negative in the field: the engine did not re-render the
      64-direction sheet against turret.slew's scripted orientation writes
      while genuinely folded. A standalone animation prototype
      (N.anim.turret_head, the identical sheet) is now drawn with
      rendering.draw_animation and its own .orientation written every tick
      from turret.slew, fully covering the frozen native layer beneath it.
      The shadow layer is left native and static -- rendering.draw_animation
      has no draw_as_shadow parameter, and this was not worth guessing at for
      a shadow when the barrel itself is the thing actually reported broken.
  Info:
    - render.lua's render-bag STYLE bumped 7 -> 8 for the new head object, so
      an installation built before this version rebuilds its render bag
      rather than sitting there without one.
Version: 0.25.0
Date: 2026-09-13
  Changes:
    - THE INSTALLATION'S STATUS SIGNAL NOW LIVES ON THE REAL GROUND LAMPS. Used
      to be a separate rendering.draw_light floating near each bank/mast --
      reported as reading like disconnected FX rather than the hardware itself.
      render.lights() now paints colour straight onto every real N.ground_lamp
      entity's own .color instead (found by position each repaint; still not
      tracked in schema/storage). The floating per-bank/mast lights are gone
      with it.
    - POSITION SET / READY (aim, barrel aligned and terrain confirmed) is now
      solid AMBER, its own hue -- it used to borrow the discharge red at a
      gentler flash. The charge ramp below it is unchanged: dark red climbing
      to bright red with a growing flicker as the banks fill, exactly as it
      was.
    - THE LANCE LIT (the 5.7 s beam sustain) now holds steady at peak
      brightness instead of flashing roughly 3.3 times a second -- reported as
      "looks dumb" over what should read as the charge ramp's own peak, held.
    - The barrel's electric-arc crackle FX (scripts/barrelfx.lua, the three
      node pairs that sparked red during the charge cue) is removed outright,
      not just disabled. The unrelated standby muzzle glow (also barrel-
      mounted) is unchanged.
  Bugfixes:
    - GROUND LAMPS WERE NEVER ACTUALLY BROKEN, ONLY DARKNESS-GATED. Reported as
      "none of the lights work" in daylight -- vanilla's own darkness_for_all_
      lamps_on doing exactly what it always does on a lamp with no always_on
      set. always_on = true now, so the fixture's day/night state stops mattering.
    - THE PER-BANK LAMPS CLIPPED THEIR OWN BATTERIES. Root cause: all four lamps
      in a 2x2 cluster shared the cluster's one outward direction, so the two
      inner-corner banks pushed their lamps into their own cluster-mates
      instead of open ground. Each of the sixteen bank lamps now gets its own
      inward direction, computed from its own offset toward the turret --
      guaranteed not to collide with a sibling bank. Also reverted the lamp
      sprite from the turret's own 3x scale to vanilla's native size, and
      raised light_when_colored's intensity off vanilla small-lamp's own dim
      default (0), which is what read as "so dim the running lights overpower
      them" once a lamp is in coloured mode essentially all the time.
  Info:
    - render.lua's render-bag STYLE bumped 6 -> 7 so an installation from
      before this version has its old floating status-light objects torn down
      and rebuilt without them, rather than left behind, dark and unmaintained.
    - C.lights.bank/mast, C.barrelfx.emitters, and C.sound.arc_cue_start/_end
      are deleted, not left as dead knobs -- nothing reads them any more.
    - changelog.txt history before 0.20.0 (0.1.0-0.19.1, the pre-beam
      artillery-shell build and the early lance versions) is removed from this
      file -- it was no longer being read, only carried.
Version: 0.24.0
Date: 2026-09-13
  Bugfixes:
    - THE 0.23.0 CHASSIS SWAP WAS ONLY HALF DONE. Reported in game: the
      installation still read as an artillery turret. graphics_set.base_
      visualisation -- the big, static plinth under the rotating housing --
      was never touched in 0.23.0; it was still artillery-turret-base.png
      (plus its shadow) verbatim, tinted to match. Only the small rotating
      housing on top had become laser-turret's. Now laser-turret-base.png /
      -base-shadow.png / -reflection.png, dimensions and native shift copied
      from base's own graphics_set/water_reflection. corpse and
      dying_explosion move to laser-turret's own remnants/explosion for the
      same reason -- a "fully replaced" chassis should not still leave an
      artillery wreck behind when it dies.
    - THE ROTATION BUG, DIAGNOSED AND GIVEN A TARGETED FIX. Reported in game:
      the barrel does not turn and fires in a fixed direction regardless of
      the designated target -- exactly the risk 0.23.0's own header flagged
      as unverified. Root cause, confirmed by reading vanilla's own
      laser-turret prototype rather than guessing: vanilla does NOT put its
      64-direction shooting sheet in folded_animation. folded_animation
      there is a single static frame (laser_turret_extension{frame_count=1});
      the rotating sheet is prepared_animation, which only renders once the
      turret's native AI has a real acquired target -- a state this mod can
      never reach without reintroducing the autonomous-firing risk the whole
      range-lock design exists to prevent. Fix attempt:
      folded_animation_is_stateless = true, a genuine TurretPrototype field
      (confirmed via apiq; the shipped spec carries no prose for it beyond
      name/type/default) whose name is a precise match for the symptom.
      UNVERIFIED -- Gate 09. If this does not resolve it, the fallback is a
      script-drawn rendering.draw_animation object with an explicit
      orientation this mod sets itself every tick, bypassing the
      prototype's animation-state machine entirely; not built pre-emptively.
    - THE BARREL GLOW AND CHARGE ARCS WERE ANCHORED ABOUT 2.6x TOO FAR FROM
      THE HOUSING. Reported in game as "light FX do not work" and arcs that
      "don't always show". Both scripts/render.lua's muzzle_glow_point and
      scripts/barrelfx.lua's nodes() anchor to beam.tip_distance(), which
      was still built from artillery's own projectile_creation_distance
      (1.6 tiles, a vanilla 3x3 ARTILLERY hull) with two multipliers stacked
      on top, tuned via /oppenheimer-muzzle for THAT geometry -- landing the
      origin 10.18 tiles out. C.gun.projectile_creation_distance is now
      laser-turret's own beam-source offset (vanilla's
      attack_parameters.ammo_type.action.action_delivery.source_offset,
      {0, -1.31439}, confirmed from base source) and the two multipliers
      that were compensating for the old wrong base are reset to neutral
      (1.0, 0) rather than re-guessed a third time -- re-tune live via
      /oppenheimer-muzzle before trusting either again.
  Changes:
    - C.barrelfx.emitters.color: pale blue -> red, matching the capacitor
      banks' own charge-arc colour (C.lights.colour.charging) -- asked for
      explicitly, so the barrel arcs read as the same system as the bank
      arcs instead of a second, differently-coloured one.
  Features:
    - A REAL LAMP AT EVERY CAPACITOR BANK, not only the four cluster-corner
      ground lamps. Asked for explicitly, alongside (not instead of) the
      existing per-bank charge-glow render object. Reuses N.ground_lamp at
      sixteen more auto-ghosted slots (C.bank_lamp_slot_defs,
      C.lamp.per_bank_enabled). First-guess placement, like every other
      offset in this mod: every bank in a 2x2 cluster shares its cluster's
      one outward direction, so the two inner-corner banks push toward
      their own cluster-mates rather than into open ground, and some of the
      sixteen ghosts may fail to place -- harmless when it happens, worth a
      look in game.
  Info:
    - NOT run in game. Highest-risk item, in order: whether
      folded_animation_is_stateless actually makes the barrel track
      orientation while folded (Gate 09); whether the reset muzzle geometry
      lands anywhere near correct before /oppenheimer-muzzle retuning;
      whether all sixteen per-bank lamp ghosts place cleanly.
Version: 0.23.1
Date: 2026-09-13
  Bugfixes:
    - MASSIVE UPS STALL FIRING A 100% CHARGE SHOT. Reported in game: a
      sandbox-map, no-enemies test still froze hard on a max-yield shot.
      scripts/front.lua's acquire() -- the one mechanism the wave, the
      collapse and the fire wave all share -- called
      surface.force_generate_chunk_requests() once per MISSING CHUNK inside
      its per-row search loop. That call blocks and drains the entire pending
      generation queue regardless of what triggered it, so calling it once per
      chunk was never "bounded by one acquisition run" the way the comment
      claimed -- it was one full blocking terrain-generation flush per missing
      chunk in that run. The Sedov front is fastest in its first few ticks,
      exactly where a 100%-yield acquisition run is widest, so over
      unexplored sandbox terrain that meant dozens of synchronous generation
      flushes landing on a single tick. acquire() now walks its own geometry
      twice -- once to queue every missing chunk in the run and flush ONCE,
      once to actually search -- rather than interleaving the two. Two cheap
      arithmetic passes over each_ring_run, not two engine calls; the search
      pass is now also guaranteed every chunk it touches was already flushed,
      which the old interleaved version was not strictly guaranteed either
      (a chunk requested by an EARLIER row in the same run had no guarantee
      its own flush had landed before that row's own search ran).
    - NOT run in game. The freeze was reported before this fix; the fix itself
      has not yet been confirmed to resolve it.
Version: 0.23.0
Date: 2026-09-13
  Features:
    - THE CHASSIS CHANGED FROM artillery-turret TO electric-turret. A laser
      turret's whole prototype family is built for a beam leaving a housing;
      an artillery turret's is built around a shell's own ballistics, and this
      mod's beam was always that chassis aimed at nothing and fired by hand
      anyway. The rotating art now comes from laser-turret's own 64-direction
      shooting sheet (not its 4-way folded/raising one -- see
      prototypes/entity.lua's header for why that distinction matters), tinted
      dark gunmetal to match the installation's own plinth. The turning
      mechanism, the safety model, and everything scripts/beam.lua does are
      unaffected: LuaEntity.orientation is documented as "the orientation of
      the weapon" for non-artillery turrets, which is exactly the fallback
      scripts/turret.lua already had in place, and the native attack is kept
      permanently unreachable (attack_parameters.range near zero) the same
      way the old flare-based gate was replaced with an inert projectile --
      lock at the effect, not at the authority.
  Bugfixes:
    - PRE-EXISTING, FOUND WHILE REWRITING THIS BLOCK: prototypes/entity.lua set
      alert_icon_scale TWICE in one table literal (0 for #103, chart_scale for
      #62), so the second silently clobbered the first. alert_icon_scale
      controls entity alert badges, not chart icon size, so #62 was very
      likely never doing what its own comment claimed either way. Both are
      moot now regardless -- #103's reason (hide the "no ammo" alert) does not
      apply without an ammo inventory. Left unset.
  Known losses, accepted knowingly:
    - THE RESONANCE-CELL CHAMBER'S WORLD-VISIBLE ALT-MODE AMMO BADGE IS GONE.
      electric-turret has no ammo inventory at all, so scripts/turret.lua's
      sync_cells now writes rec.cells as a plain number instead of real N.shell
      items in a real inventory. The fire-control panel's own readout
      (scripts/gui.lua) is unaffected -- it reads rec.cells directly now,
      still the one place that number is written.
  Known regressions, not yet fixed:
    - logistics.circuit_disabled reads LuaControlBehavior.disabled, which
      belonged to LuaArtilleryTurretControlBehavior. get_control_behavior now
      returns a LuaTurretControlBehavior instead -- a different class, with no
      `disabled` field at all -- so this silently always reads false instead
      of erroring. LOW severity: nothing safety-critical depends on it, the
      installation just stops noticing when a player has circuit-disabled it.
      The 2.0 turret family's real enable/disable-by-circuit surface has not
      been identified yet.
    - NO MIGRATION STEP for existing saves' turrets. Same class of breakage as
      0.11.0's accumulator-to-EEI bank change (see that entry's
      banks-rebuilt message) -- an artillery-turret-typed entity in an old
      save will not survive becoming electric-turret-typed, and nothing yet
      detects that and reports it the way 0.11.0 did. Costs nothing today
      (testing has been in the map editor) but is a real gap before this ships
      to a save anyone is attached to.
  Info:
    - source_offset = {0, -3.423489/4} on vanilla's own laser-turret
      attack_parameters is the first non-guessed reference point this mod has
      had for where a beam should leave the housing -- noted in
      prototypes/entity.lua, NOT yet folded into C.beam.muzzle_forward_mult/
      _lift_mult. Those numbers were tuned for the old artillery barrel and
      almost certainly need re-tuning via /oppenheimer-muzzle against the new
      art before they mean anything again.
    - UNVERIFIED, HIGHEST-RISK ITEM IN THIS VERSION: whether the engine
      actually re-renders a scripted LuaEntity.orientation write against
      folded_animation's frames while the turret is genuinely folded (its
      permanent state here, since the native attack can never find a target).
      Vanilla itself never explores that combination in normal play. If it
      does not work, the barrel will not visibly turn during AIM even though
      turret.slew is writing orientation every tick -- Gate 09, first thing to
      check in game.
    - NOT run in game.
Version: 0.22.0
Date: 2026-09-13
  Features:
    - THE STATUS LIGHTS GOT A SECOND SUB-STATE. AIM is now yellow, flashing, while
      the barrel is still rotating onto a designated point; once it is aligned AND
      the ground under the shot has actually been confirmed generated, it switches
      to flashing red -- "ready to fire", distinguished from the FIRE state's own
      flashing red by pace and amplitude alone (gentler, capped at 1.0), not hue,
      since both are honestly "this gun is dangerous". STANDBY is solid blue now
      instead of breathing (C.lights.standby_solid, one flag to revert).
    - THE CAPACITOR BANKS' CHARGE GLOW FLICKERS. A small amount of plain
      math.random noise on top of the existing fill-proportional alpha, scaled by
      fill so an empty bank stays dark -- explicitly not a second precise state
      signal, just texture that reads as heavy energy draw. C.lights.bank_charge
      .flicker_amount.
    - THE BARREL HAS ITS OWN FX. A soft standby glow at the muzzle, on its own
      slow rhythm independent of the status lights (C.barrelfx.glow); and three
      pairs of emitter nodes flanking the barrel that crackle with forked arcs
      during the electrifying cue BeamFire.ogg's own charge sound carries around
      10.5-12 s in (C.sound.arc_cue_start/_end, C.barrelfx.emitters,
      scripts/barrelfx.lua). Node positions are a first guess, same as the
      muzzle origin was -- /oppenheimer-muzzle now draws them alongside the
      muzzle-tuning circles so both get settled in one session.
    - GROUND LAMPS. Four real vanilla small-lamp entities (cloned under this
      mod's own name so they can be auto-ghosted and told apart from a player's
      own lamps), one at the inside corner of each capacitor cluster -- the
      mirror of the masts, which stand at the outside corner. Purely decorative;
      vanilla's own lamp behaviour needs nothing from control.lua, so unlike the
      pylons and masts they carry no schema or adoption tracking at all.
  Bugfixes:
    - THE BOOM LANDED ABOUT A SECOND LATE EVEN STANDING ON TOP OF THE SHOT.
      Reported in game. Not thunder()'s distance delay (boom_delay_min floors at
      8 ticks, negligible) -- C.blast.implosion.beat_ticks, the deliberate pause
      between the collapse finishing and the release that every stage of the
      detonation is offset by, was 40 ticks (0.67 s) of felt silence between
      "the core has visibly finished collapsing" and "everything actually goes
      off". Cut to 15 -- C.blast.implosion.stamp_life_min, the floor below which
      the last collapsing shell's fade would outlive the flash it is supposed to
      finish before (stamp_life() clamps DOWN to beat_ticks before it clamps UP
      to stamp_life_min, so anything lower reopens that overlap in the other
      direction). Going tighter than 15 means shortening the shell fade itself,
      not this number alone.
Version: 0.21.1
Date: 2026-09-13
  Bugfixes:
    - 0.21.0 ONLY SWAPPED HALF THE BOOM. The broadcast layer (scripts/detonate.lua's thunder(),
      audible from the map) got ImplosionBoom.ogg; a SECOND, independent copy of the old stock
      sound -- N.ex_boom in prototypes/vfx/explosions.lua, the near-field world-positional layer --
      still embedded __base__/large-explosion-1.ogg, at a flat volume 1.5, on its own fixed 100-tick
      delay unrelated to the broadcast's distance-based timing. Reported as a leftover explosion
      sound trailing the new one by about half a second for anyone standing near their own shot.
      N.ex_boom now carries the same ImplosionBoom.ogg, at C.sound.boom_volume_floor rather than a
      volume picked for a different file, and C.blast.stages.boom moved from 100 to 10 ticks so the
      near-field copy lands with the broadcast's own near-field floor instead of 1.67 s after it.
    - This also explains why the broadcast's energy-scaling looked like it wasn't doing anything up
      close: the near-field layer's flat, unscaled volume was the dominant thing a nearby listener
      heard, regardless of what the broadcast layer's yield-based volume was doing underneath it.
  Info:
    - N.ex_cap (the mushroom cap's embedded sound) still references stock large-explosion-1/2.ogg
      and was left alone -- confirmed dead code under C.blast.style = "implosion" (the current,
      only-used style); it never fires. Revisit only if the style is ever switched to "atomic".
    - Neither embedded layer scales with yield the way the broadcast does -- an ExplosionPrototype's
      `sound` is baked at data-stage load, no runtime hook. Exact per-yield scaling on the near-field
      copy would mean building one N.ex_boom variant per C.charge.yield_targets step, the way the
      flash and scorch decal already are. Not done; the near-field layer is now pinned to the
      broadcast's own volume floor instead, which is close rather than exact.
Version: 0.21.0
Date: 2026-09-13
  Bugfixes:
    - A BIG SHOT COULD STILL LEAVE NESTS UNTOUCHED PAST THE PREGEN DISC. 0.20.1/0.20.2 force-generate
      the near-epicentre ground before the fronts move, capped at 512 tiles to avoid a strike-tick
      hitch, and trust the wave's own chart_ahead backstop to keep the rest of a big shot's ground
      built as the front travels. Nothing ever verified that backstop actually kept up: front.lua's
      acquire() called find_entities_filtered on a chunk the instant its lead reached it, and an
      ungenerated chunk returns {} indistinguishable from a genuinely empty one -- so a chunk the
      async queue had not finished yet was acquired anyway, bin nothing, and the front never looks
      at it again. acquire() now checks is_chunk_generated per chunk and force-generates just that
      one chunk if it is missing -- the same trade impact.settle_terrain already makes at the
      strike, spread thin across the sweep instead of risked in one place. Shared by the wave, the
      implosion shock and the fire wave, since all three go through this one function.
    - THE DETONATION'S BOOM WAS A STOCK EFFECT SIZED FOR A FEW TILES, PLAYED OVER A BLAST UP TO 2000.
      __base__/sound/fight/large-explosion-1.ogg finished before the Sedov wave had covered more
      than a few percent of a big shot's radius, so most of the visible detonation played out in
      silence. Replaced with sound/ImplosionBoom.ogg (10.500 s = 630 ticks). Volume now scales with
      the shot's yield (C.sound.boom_volume_floor / boom_volume_exponent) -- duration cannot, since
      play_sound has no stop, mute or seek anywhere in the runtime API, so the clip always plays to
      the end regardless of what the shot paid for.
    - THE DESIGNATOR'S BLAST-RADIUS PREVIEW NEVER APPEARED ON THE CHART. render.preview has drawn
      four rings at the real damage-band radii since 0.14.0, but through a plain rendering.draw_
      circle -- invisible on the map, which is the exact mistake 0.16.0 already fixed once for the
      sphere. A player designating a long-range shot is usually standing at the gun, not at the
      target, so a multi-hundred-tile preview ring was frequently drawn somewhere nobody was
      looking. Now routed through mapdraw.circle, so it appears on the chart the same way the
      sphere, the wave and the collapse already do. mapdraw.circle itself gained a return value
      (world, chart render objects) and forwards `forces`/`players` to the chart copy, neither of
      which any prior caller needed.
    - Generation now starts at the first remote press (designate), not only at CONFIRM. Async and
      free -- request_to_generate_chunks on ground already requested or generated is a no-op -- and
      the barrel's own slew can hold AIM for up to ~21 s before CONFIRM is even legal, with no
      timeout after that either. All of it previously generated nothing.
    - Corrected stale timing in the turret's in-game description and the fire-control CONFIRM
      tooltip: both still said "21 seconds, strikes at 17, burns for 4", left over from before the
      audio's exact duration was measured. It is 21.7 s total, strike at 16 s, 5.7 s of sustain
      (config.lua's discharge_at / sequence_ticks, not rounded).
  Features:
    - THE PREVIEW NOW WARNS WHEN IT DOESN'T KNOW. The outer band samples is_chunk_generated at
      C.blast.rings.preview.coverage_samples points around its own circumference and switches to a
      distinct colour (`unconfirmed`) when fewer than coverage_warn_threshold are generated yet --
      a READOUT, not a lock, same rule as the existing danger colour and the dynamic standoff in
      turret.designate. Re-samples roughly every 3 seconds while a player sits in AIM, so it
      catches up as generation (now starting at designate) finishes in the background.
  Locale:
    - The capacitor bank description still said "top of the yield selector" -- the selector was
      replaced by the charge-rate dial in 0.13.0. Reworded.
    - The damage-multiplier startup setting's description claimed it scales detonation damage. It
      is fully inert: C.shell.damage_multiplier is written from the setting and never read anywhere
      else in the mod, a fossil from when the gun threw a shell. Reworded to say so, in the same
      spirit as the shell-speed technology's existing "does nothing" note.
Version: 0.20.2
Date: 2026-09-10
  Bugfixes:
    - CONFIRM DROPPED THE GAME TO 5 FPS FOR THE WHOLE FIRING SEQUENCE. 0.20.1 generated the entire
      blast disc up front, and the request radius scales with the blast: a full-dial shot asked the
      map generator for a 64-chunk radius -- 16641 chunks -- on a single CONFIRM. The async queue
      then churned through terrain, resource and enemy-base generation for the entire 21 second
      sequence, and the strike's force_generate_chunk_requests() flushed the remainder in one
      blocking tick on top of it. Selection was fine because designation only charts the fireball
      ring; the disc-wide request was new at CONFIRM.
    - The pregen request is now capped at C.blast.rings.pregen.max_chunks (16 = 512 tiles). Only the
      near-epicentre ground has to exist before the fronts move -- the fireball, the collapse shock
      (which is center-weighted and barely touches the rim), and the arcs off the small early ball.
      The wave already generates its own ground chunk by chunk as it travels, with ~28 seconds to
      do it, so everything past the cap is built by the front that reaches it. force_at_strike is
      kept but is now bounded by the same cap.
  Info:
    - C.blast.rings.pregen gains max_chunks. Raise it if a big shot on unexplored ground leaves rim
      nests standing -- though the wave sweep should be collecting those.
Version: 0.20.1
Date: 2026-09-10
  Bugfixes:
    - THE BLAST LEFT WHOLE NESTS UNTOUCHED INSIDE THE CRATER, worse the bigger the shot. 0.20.0
      revealed the map as the wave travelled and took terrain GENERATION with it -- but
      find_entities_filtered returns nothing for a chunk the generator has not built yet, so the
      wave payout, the collapse shock and the arcs off the growing ball all swept over ungenerated
      ground and the spawners and worms standing on it took zero damage. A small shot mostly lands
      on explored ground and only clipped its fringe; a megaton reached far enough into unbuilt map
      to spare most of what it covered.
    - The whole blast disc is now generated during the firing sequence: requested at CONFIRM, where
      the 17 seconds of charge before the beam lights is far more time than the generator needs,
      and force-completed at the strike so the last chunks cannot slip through. Map reveal
      (chart_ahead) stays lazy -- a map that fills in late is invisible; a nest that takes no
      damage is not.
  Info:
    - New config block C.blast.rings.pregen: enabled, margin_chunks, force_at_strike.
Version: 0.20.0
Date: 2026-09-10
  Major Features:
    - THE DIAL IS 1%..100% AND 100% IS 2000 TILES. The 8850 tile ceiling was a map-generation job
      with a trigger: every radius-squared bill in the mod was priced against it. Energy steps are
      unchanged (5% rungs, 1% slider); 100% is still 36 GJ/s, a 576 GJ trigger and 1.00 Mt.
    - THE WAVE IS SLOW, AND IT IS SEDOV. The front follows R ~ t^(2/5), the Sedov-Taylor point
      blast, at an average of 1.2 tiles a tick: out of the fireball fast, then a crawl, 2000 tiles
      in about 28 seconds. The ring is stamped whenever the front has moved a fraction of a puff's
      width instead of every tick, and each puff lives long enough to overlap the layer behind it,
      so a slow front is one continuous band of fire rather than sixty rings stacked on each other.
    - THE RINGS ARE OVERPRESSURE CONTOURS. Damage beyond the fireball follows Brode's (1955) fit for
      peak overpressure against scaled distance, with the blast edge at 0.1 bar, where that fit
      stops being valid. The fireball (>= 20 psi, inner 21.4%) is obliterated outright; the heavy,
      severe and blast rings follow the curve from 100% of the payload down to 7.3% at the rim.
    - ABSOLUTE DAMAGE. One payload in hit points (60000 at the fireball's edge), not a percentage of
      the victim's health, so the weapon scales with biter health: soft targets die to the rim,
      armoured ones only where the pressure is high enough. A new infinite technology, Oppenheimer
      Lance Payload, adds 20% a level to every stage.
    - EVERY STAGE HURTS. The growing ball's inner tenth is a fire wave that burns each thing once as
      it passes. Arcs off the rim hunt the band just outside it, strike what they find for an
      electric chip, and jump to the next target. The collapse is a converging shock: one physical
      hit as the shell crosses each thing, rising as r^-0.907 toward the centre.
    - THE BEAM VAPORIZES. Anything under the drawn beam, inside its drawn width, dies outright.
  Changes:
    - The map is revealed by the wave. Designation charts only the fireball ring; the rest is charted
      a few seconds ahead of the front as it travels, ungenerated chunks are requested explicitly,
      and the crater is re-charted behind the paint.
    - Tile paint happens only on the wave, and follows the front every tick instead of lagging it.
      The growth phase no longer paints its footprint.
    - The lance's map line now stops at the ball's near face, as the world beam does.
  Optimizations:
    - THE GROWTH PHASE WAS PAYING THREE AREA-PRICED BILLS AT ONCE, reported as 10 FPS on a 25%
      shot. It re-charted the whole blast box frame by frame (LuaForce.chart refreshes what it is
      given) on top of the same box charted at designation; it painted 9000 tiles every 4 ticks
      that the wave then repainted from radius zero; and the beam searched the whole bounding box
      of its line every 10 ticks, a square ~125000 tiles across for a diagonal 500 tile shot. The
      first two are gone and the third is a row of small boxes along the drawn beam.
    - scripts/front.lua: every moving front -- the fire wave, the collapse and the shock wave -- now
      finds its victims chunk by chunk just ahead of itself. The disc is searched once in total,
      spread across the sweep, instead of by full-disc queries.
    - The tile paint reuses its per-tile tables instead of allocating two per tile per tick.
  Bugfixes:
    - THE WAVE STARTED UP TO ~16% OF A COLLAPSE AFTER THE FLASH on any shot between two built yield
      steps. 0.18.1 moved the wave's start onto the shot's raw power, reasoning that the collapse ran
      on it; scripts/implode.lua in fact runs the collapse on the variant's clock, and the release
      stages in prototypes/vfx/waves.lua are baked on it too. The wave is back on the variant's
      clock, which all three now share.
  Info:
    - Migration: detonations and lances in flight in a 0.19.x save are ended, and any installation
      set above 100% is clamped to it.