Refuse a fused weapon whose Range does not exceed its BlastRadius
Soul: a no-lock round bursts at Range, so Range <= BlastRadius (or a zero Range) lands the blast on the shooter. Made structurally impossible at the catalog rather than policed at runtime: StatValidation.ValidateFuse joins the checks CultRecordRefs.Validate runs on every Upsert, at the weakest value the Range stat can take. Only a weapon that will detonate (a Fuse and a BlastRadius above zero) is held to it. AetheriaStores.Open now calls that same Validate for every equippable and consumable document instead of carrying its own copy of the three checks, so Upsert and Open can no longer disagree. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
3e8ee02276
commit
e3c9cbb282
3 changed files with 74 additions and 16 deletions
|
|
@ -29,22 +29,10 @@ public static class AetheriaStores
|
|||
cache.AllStoredDocuments.All(stored => stored.Descriptor != descriptor));
|
||||
if (missing != null)
|
||||
throw new InvalidOperationException($"Catalog {catalogPath} has no {missing.SchemaName} record; catalog globals are authored, never invented.");
|
||||
// R-heat (docs/stats-and-power-cut.md): every equippable design's heat response must describe a
|
||||
// coherent range before anything reads it. Fails loudly, naming the item.
|
||||
foreach (var data in cache.GetAll<EquippableItemData>())
|
||||
{
|
||||
StatValidation.ValidateHeatResponse(data);
|
||||
StatValidation.ValidateStatModifiers(data.Name, data.Behaviors);
|
||||
// Cut 7 (docs/stats-and-power-cut.md): a stat naming a role its design lacks is refused.
|
||||
StatValidation.ValidateRoleUsage(data.Name, data.Roles, data.Behaviors);
|
||||
}
|
||||
// Cut 2 (docs/stats-and-power-cut.md): a consumable's own StatModifierData needs the same check;
|
||||
// consumables carry no heat response, so this is not folded into the loop above.
|
||||
foreach (var data in cache.GetAll<ConsumableItemData>())
|
||||
{
|
||||
StatValidation.ValidateStatModifiers(data.Name, data.Behaviors);
|
||||
StatValidation.ValidateRoleUsage(data.Name, data.Roles, data.Behaviors);
|
||||
}
|
||||
// Every catalog document is held to the one check Upsert applies on write: R-heat (docs/stats-and-power-
|
||||
// cut.md), stat modifiers, role usage (Cut 7), and the fuse range rule. Fails loudly, naming the item.
|
||||
foreach (var data in cache.GetAll<EquippableItemData>()) CultRecordRefs.Validate(data);
|
||||
foreach (var data in cache.GetAll<ConsumableItemData>()) CultRecordRefs.Validate(data);
|
||||
return cache;
|
||||
}
|
||||
catch
|
||||
|
|
@ -85,6 +73,7 @@ public static class CultRecordRefs
|
|||
StatValidation.ValidateHeatResponse(data);
|
||||
StatValidation.ValidateStatModifiers(data.Name, data.Behaviors);
|
||||
StatValidation.ValidateRoleUsage(data.Name, data.Roles, data.Behaviors);
|
||||
StatValidation.ValidateFuse(data);
|
||||
}
|
||||
if (document is ConsumableItemData consumable)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -752,6 +752,21 @@ public static class StatValidation
|
|||
"not exceed them");
|
||||
}
|
||||
|
||||
// Operator ruling 2026-09-30 (Soul): a round with a blast that could burst on its own shooter is refused at
|
||||
// the catalog rather than policed at runtime. A weapon that detonates (a fuse and a blast radius above zero,
|
||||
// the two FireControl.Fire freezes together) must reach farther than its blast, at the weakest value its Range
|
||||
// stat can take: a no-lock round bursts at Range, so Range <= BlastRadius would put the shooter inside the
|
||||
// burst, and Range <= 0 is a burst on the muzzle. A fuse without a radius is inert and needs no range.
|
||||
public static void ValidateFuse(EquippableItemData data)
|
||||
{
|
||||
if (!(data is WeaponItemData weapon) || weapon.Fuse == null || !(weapon.BlastRadius > 0f)) return;
|
||||
foreach (var behavior in weapon.Behaviors)
|
||||
if (behavior is WeaponData weaponData && weaponData.Range.Min <= weapon.BlastRadius.Value)
|
||||
throw new InvalidOperationException(
|
||||
$"{weapon.Name}: fused weapon's Range {weaponData.Range.Min} does not exceed its BlastRadius " +
|
||||
$"{weapon.BlastRadius.Value} -- a burst at max range would land on the shooter");
|
||||
}
|
||||
|
||||
// Cut 2 (docs/stats-and-power-cut.md): a StatReference names its target by (type name, field name) so a
|
||||
// modifier can point at any design or behaviour without a hard type reference -- but before this cut an
|
||||
// unresolvable reference failed silently: StatModifier.Initialize left `_stats` null and the first
|
||||
|
|
|
|||
Loading…
Reference in a new issue