--- title: Exploding_Barrels_Build_Spec type: note permalink: gamevault/03-design/exploding-barrels-build-spec --- # Exploding Barrels — Build Spec (Phase 1.5 hazard v1, design-review-hardened) Review: `wf_2cb10454-fdf` (3 lenses, 23 agents, 13 findings CONFIRMED / 3 refuted; the first run lost 8 verifiers to a session limit and was resumed from cache — the [[workflow-agent-quota-failures-look-clean]] protocol held). This spec is the fold; it supersedes the pre-review draft. ## The core mechanism: the FUSE (one fix for both HIGHs) On pop the barrel does NOT die. The server zeroes the barrel's REPLICATED `BlightClutter.Remaining` and adds a server-only `BarrelFuse { uint ExplodeTick }` (`TickUtil.NonZero(now + Tuning.BarrelFuseTicks≈36)`). The client therefore sees `Remaining==0` on a still-alive barrel across multiple snapshots → an unambiguous "about to blow" cue, and the boom-vs-puff choice at despawn keys on the CACHED `Remaining<=0` — a room- teardown despawn carries `Remaining>0` → **no false booms** (HIGH #1: despawn-inference is cause-blind; players stand in torn-down rooms through RouteSelect — RunDirectorSystem.cs:247-251, WorldFeedbackSystem prune :96-119). Detonation is the AUTHORING moment → `DamageEvent.SourceTick = TickUtil.NonZero(detonation tick)` honors the authored-tick contract dash i-frames negate against (HIGH #2; DamageEvent.cs:20-24). The fuse also: dissolves the melee self-hit complaint (pop → step out), creates the bait loop (lure enemies onto a lit barrel), and — because the fuse lives ON the RoomTag'd barrel — room teardown/Staging sweep clean fused barrels for free (the orphaned-ExplosionEvent lifecycle findings dissolve). ## Server - **Seeding** (RoomFieldSystem clutter loop): `bc.Variant = crng.NextFloat() < 0.25f ? (byte)3 : (byte)(i % 3)` (seeded rng, deterministic; variants 0-2 stay cosmetic). - **Pop sites** (BOTH: ResourceHarvestSystem projectile sweep; MeleeComboSystem isServer-gated harvest block): gate on `isClutter && Variant==3` INSIDE the destroyed-bitset guard (nodes share the packed arrays with Variant slot 0 — confirmed LOW). On rem<=0 for an explosive: mark destroyed[i] (unhittable this tick), `SetComponent(BlightClutter{Remaining=0,...})` + `ecb.AddComponent(BarrelFuse{ExplodeTick})` — NO destroy. Both target snapshots SKIP entries with `Remaining <= 0` (a fused barrel is not hittable → no double-pop). - **`HazardExplosionSystem`** (new, ProjectM.Server, plain `SimulationSystemGroup`, NO ordering attributes, NO lifecycle gating — a fused barrel must always drain (confirmed LOW: never RequireForUpdate-gate it); `TryGetSingleton` invalid → skip tick, fuse persists): query `(BarrelFuse, LocalTransform)`; when `ExplodeTick` elapses (NetworkTick.IsNewerThan semantics): radius scan (XZ, `Tuning.BarrelExplodeRadius ≈3.25`) — players via the BossAISystem:62-70 gather VERBATIM (`Health>0 && RegionTag==Expedition`; confirmed: dead players persist at death pos for DelayTicks) + enemies (`EnemyTag && Health>0`, excludes Dying corpses) → `AppendToBuffer(DamageEvent{ Amount=Tuning.BarrelExplodeDamage≈26, SourceNetworkId=-1, SourceTick= TickUtil.NonZero(now) })` → `ecb.DestroyEntity(barrel)` (single destroy site). **SourceNetworkId = -1** (environment convention — confirmed: a player id would score+consume Charger whiff-punish windows via HealthApplyDamageSystem:102, the sole reader). - **No chain reactions** v1 (clutter has Remaining, not Health). Damage numbers as `Tuning` consts v1; promote to TuningConfig knobs when the knob-table extension is worth it (confirmed-LOW tension, deferred deliberately — noted here per [[ask-before-deferring-during-implementation]]: the overlay knob table is a separate mechanical slice). ## Client (all new work — confirmed: NO existing per-variant path; Variant is read by zero client systems) - **WorldFeedbackSystem**: add `Variant` to the Cache struct; at prune, `IsClutter && Variant==3 && Remaining<=0` → BIG boom (burst + `DynamicLightSystem.RequestFlash` large orange + low boom SFX); else the existing small puff. Teardown despawns (Remaining>0) keep the puff. - **DynamicLightSystem**: Variant==3 barrels get a red glow light (node-glow pattern); when the cached `Remaining<=0` (fuse lit) the glow strobes hard — the flee cue. (Body tint via URPMaterialPropertyBaseColor on LEG children = polish, deferred.) ## Tests - ResourceHarvestSystemTests: explosive pop → BarrelFuse added, Remaining==0, entity ALIVE; fused barrel not re-hittable. - New HazardExplosionSystemTests: elapsed fuse → DamageEvent(Amount, SourceNetworkId=-1) on in-radius living enemy AND living player, dead player skipped, out-of-radius skipped, barrel destroyed once; un-elapsed fuse → nothing. ## Wire/bake classification ZERO new [GhostField]s / ghost prefabs / RPCs; `BarrelFuse` is server-only; Variant==3 rides the existing replicated byte. No new ordering edges. Re-bake: none (code + seeded-value only).