Fire control Cut 6b: a shot's dice belong to the shot, and airburst resolves in the simulation

6.1 (Soul finding 6): Commit builds a local CultMath.Random from (Zone.CombatSeed,
shot.ShotId) instead of reading and writing the shared ItemManager.Random stream.
Zone exposes CombatSeed (the same expression that already seeds its own _random),
so a shot's outcome is a pure function of zone identity and shot id -- immune to
whatever else drew from the shared stream in between.

6.2 (Soul finding 5): airburst is wired into the simulation instead of back into
Projectile.cs. PendingShot gains BurstPosition/BurstRadius, frozen at Fire time
from PredictedIntercept and the weapon's authored WeaponItemData.AirburstRange
when WeaponModifiers carries Airburst. FireControl.Step resolves such a shot via
Splash instead of Apply, never both. Projectile.cs/ProjectileManager.cs drop the
dead AirburstDistance/AirburstRange fields and write-back.

Tests: FireControlCut6Tests.cs pins both rules (208 baseline + 5 new, all green).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-19 18:34:04 +02:00
commit 39ad4bfc80
6 changed files with 358 additions and 12 deletions

View file

@ -489,6 +489,13 @@ public class WeaponItemData : GearData
[Inspectable, JsonProperty("modifiers"), Key(28)]
public WeaponModifiers WeaponModifiers;
// Cut 6b (docs/fire-control-cut.md, 6.2): the authored blast radius an Airburst-flagged weapon's shot
// splashes with at arrival, frozen into PendingShot.BurstRadius at fire. Zero (and unread) for a weapon
// without the Airburst flag. No authored value exists yet -- the flag is wired and unexercised until Cut
// 6a or a later authoring pass sets it.
[Inspectable, JsonProperty("airburstRange"), Key(29)]
public float AirburstRange;
}
[CultDocument("aetheria.hulldata", "1"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]