Skip to content

Rift Bolt detonation: weapons mark, the Rift Bolt cashes in

Status: approved in brainstorming 2026-10-12 (Chris). Next: implementation plan.

Every weapon hit calls ElementalSystem.apply_element() (sim/elemental_system.gd), which tints the enemy with the weapon’s element, sets stacks = 1 and a 4 s aura_remaining. Only two of the 14 elements do anything with it: Fire (burn, 2 HP/s DoT) and Lightning (shock, +15 % damage taken), live since 2026-09-23. The other weapon elements in play (kinetic, void, poison, light, blood, time, cold, aether) are pure tint, and stacks never exceed 1. To the player the element colours read as decoration with no mechanic.

Every weapon hit leaves an elemental mark (stacks). The Rift Bolt (WeaponAim, the always-on right-stick weapon) detonates marks on the enemies it physically hits, for damage that scales with stacks. The aimed weapon becomes the skill-shot payoff for the auto weapons’ setup.

Lesson carried from the archived reactions mode (2026-07-09): every-hit reactions were wildly lethal (~350 kills/min, player untouched). Here only the aimed Rift Bolt can trigger, which is the brake.

Question Choice Deferred alternatives
Payoff shape ONE generic detonation; element only changes colour per-element payoffs; generic + bespoke headline elements (test later)
Power budget Shift, don’t add: lower Rift Bolt base damage so a typical marked hit ≈ today’s damage pure bonus; Rift Bolt as pure trigger
Stack building ANY weapon hit adds a stack (cap stacks_max = 6), colour = last element same-element-only stacks; binary mark
What detonates Direct hit + pierce victims only; the impact splash does NOT detonate splash detonates; chain detonations (kept for a Rift Bolt upgrade / Railspike evolution)
  • In code these are “element stacks”, NOT “marks”: mark_* is already the Railgun’s marked debuff (mark_timer/mark_mult). “Mark” below is design shorthand only.
  • An enemy is stacked when it carries an applied (non-innate) aura: aura_element != -1 and aura_remaining < EnemyPool.BASE_AURA_PERSIST * 0.5 and stacks > 0 — the same test fx/enemy_aura_look.gd already uses. (An exact != BASE_AURA_PERSIST test is wrong: Elemental.decay counts innate auras down too.) An innate base aura is never stacked.
  • apply_element(sim, ei, el) with el >= 0 on a live enemy:
    • if already stacked: stacks = min(stacks + 1, ElementalSystem.STACKS_MAX), else stacks = 1;
    • aura_element = el (last element wins the colour);
    • aura_remaining = aura_decay_s of el (refresh).
    • A hit whose element equals the enemy’s innate element still stacks (stacked is decided by the aura_remaining test, not by element difference).
  • ElementalSystem.STACKS_MAX is a tunable defaulting to 6 (= bible stacks_max).
  • Expiry is unchanged (Elemental.decay): when the window runs out the whole stack drops at once and the enemy reverts to its innate aura (or none). No per-stack decay.
  • ModState.stack_bonus / aura_duration_mult stay unoffered (CRYSTALS_DEAD_EFFECTS).

Status effects stay at single-stack magnitude

Section titled “Status effects stay at single-stack magnitude”

burn DoT (apply_status_and_decay) and shock vulnerability (vuln_mult) currently multiply by stacks. With accumulation that would silently 6x both. They are pinned to 1-stack magnitude regardless of stack count. Stacks feed detonation only. The existing foreign-aura gate on status (el != base_element) is unchanged.

  • Trigger: a Rift Bolt projectile (ProjPool.KIND_AIM, including the evolved Railspike) collides with an enemy in resolve_collisions — the first hit and every pierce hit. The impact burst (burst_radius splash) deals its normal damage and does NOT detonate.
  • A Rift Bolt hit does NOT add a stack (no apply_element for KIND_AIM); otherwise a fresh enemy is stacked by the shot meant to cash in.
  • If the hit enemy is stacked with n stacks:
    • detonation damage D = bolt_hit_damage * WeaponAim.DETONATE_MULT_PER_STACK * n, where bolt_hit_damage is the projectile’s own damage (already includes damage_mult, Rift Bolt power picks and the shot’s crit roll — so detonation scales with Rift Bolt upgrades and no second crit is rolled);
    • the target takes D; every other enemy within WeaponAim.DETONATE_RADIUS takes D * DETONATE_SPLASH_FRAC; any boss (target or splash) takes DETONATE_BOSS_FRAC of its share;
    • the target’s stacks are consumed: revert exactly as decay does (innate aura or none);
    • the whole detonation is ONE begin_aoe_group()/end_aoe_group() hit (Wyrm shared pool rule).
  • All damage goes through damage_enemy (deferred death sweep; no removal mid-tick), so the existing dmgnum floating number shows it automatically.
  • Emits a rift_detonate fx event {pos, radius, element, stacks} (render-only, excluded from checksums like all fx_events).

WeaponAim.DEF_DAMAGE drops below today’s 6.0 and DETONATE_MULT_PER_STACK is set so a hit on a typical stacked enemy (≈2-3 stacks, measured in soak, not assumed) lands about today’s damage. Starting values (4.0 base, 0.25/stack → 2 stacks = 6.0) are placeholders; calibrated from soak stack-count data + a paired-seed economy-sim comparison (skill bh-economy-sim): target is run power ≈ level vs. main, with stacked hits above and unstacked hits below today’s output.

Tunables (live console, per the console-tunable-by-default rule)

Section titled “Tunables (live console, per the console-tunable-by-default rule)”

WeaponAim.DEF_DAMAGE (already one), ElementalSystem.STACKS_MAX, WeaponAim.DETONATE_MULT_PER_STACK, WeaponAim.DETONATE_RADIUS, WeaponAim.DETONATE_SPLASH_FRAC, WeaponAim.DETONATE_BOSS_FRAC, EnemyAuraLook.RINGS_MAX, AudioManager.RIFT_DETONATE_SFX_DB/_PITCH — registered static vars with labels in tools/tunable_docs/docs.d/*.json (skill bh-tunable-batch).

stacks is in state_checksum/snapshot_string, and stack accumulation plus the damage shift change the seed-1234 baselines. Expected: re-pin via skill bh-determinism-repin after confirming the shift comes only from this change. No new RNG draws.

  • Stack read: fx/enemy_aura_look.gd already draws one extra ring per stack but caps at the const MAX_RINGS = 3. It becomes a live tunable RINGS_MAX (default 6, range 1-6), so 3 vs. 6 rings can be compared live on iPhone + ATV from the console (the switchable comparison) before settling the default.
  • Detonation VFX: new rift_detonate FxManager kind — element-coloured ring at DETONATE_RADIUS plus a hot flash, brighter with stacks consumed; ESSENTIAL (damage lands) with its own per-frame cap. New entry + score + preview in tools/vfx_library/ (pinned by tests/test_vfx_library_manifest.gd).
  • Damage number: the existing dmgnum path (see Detonation).
  • Audio: rift_detonate reuses the scatter_explosion asset via cue_shape with its own RIFT_DETONATE_SFX_DB/_PITCH tunables (low-risk mix change, flagged for a listen). Pitch-by-stacks is deferred.
  • Teaching: the existing tut_riftbolt dialogue (data/story.json; the name “Rift Bolt” is already live there) gains a line on detonating stacked enemies.

SkilledAutoPlayer.compute_aim_for (ai/skilled_auto_player.gd): when no enemy is inside a danger radius of the pilot, aim at the in-range enemy with the most stacks (≥ a minimum); otherwise keep today’s threat-cluster aim. Without this the soak / economy-sim numbers under-rate the mechanic (the shift lowers unstacked damage).

Unit:

  • any weapon hit adds a stack up to STACKS_MAX; colour follows the last element;
  • refresh on hit; whole mark drops at expiry; innate aura restored;
  • innate aura alone is not stacked (no detonation on an untouched elemental enemy);
  • burn DoT and shock vuln are identical at 1 and 6 stacks;
  • Rift Bolt direct hit and pierce hit detonate; splash victims do not; Rift Bolt hit adds no stack;
  • detonation damage = bolt hit × per-stack mult × stacks (target), splash fraction, boss fraction; stacks consumed.

End-to-end: real spawn → weapons stack an enemy → Rift Bolt kills it via _sweep_dead → XP awarded (the biomass trap rule: assert awarded XP through the real path).

Then: determinism re-pin, economy-sim paired comparison, scripts/gate.sh, tests/test_vfx_library_manifest.gd, and a Dev install for on-device feel with the tunables live.

Per-element payoffs (options 2/3), chain detonations (future Rift Bolt upgrade / Railspike path), re-enabling stack/duration crystals, the dead Elemental.apply reactions code.