LANTERN purge B6: delete the legacy single-ability path (sockets are THE ability model)

AbilityRef, AbilityCooldown, EffectiveAbilityStats, DefaultAbility deleted.
GoInGameServerSystem seeds the per-frame 4-socket Spark loadout UNCONDITIONALLY
(was gym-only); ClassSelectReceiveSystem + DebugOp.SetClass swap FrameId +
re-seed sockets + zero SocketCooldown; ClassSwapUtil.Apply drops newAbilityId;
EquipSystem weapons become stat-sticks (GrantedAbilityId removed from the item
blob/authoring); StatRecomputeSystem folds CharacterStatsRef + sockets only;
HUD cooldown bar reads socket 0 of SocketCooldown/EffectiveSocketStats; class
HUD readers are FrameId-only (ClassForAbility/AbilityFor deleted);
DebugModifierInjectionSystem drops CycleAbility.

390 tests green; Play-verified: a non-gym spawn gets frame=2 with Sparks
[7,8,6,9] and no console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 15:47:12 -07:00
parent b34945c2d2
commit 4a8220ad3e
27 changed files with 137 additions and 275 deletions
@@ -7,8 +7,9 @@ namespace ProjectM.Simulation
{
/// <summary>
/// Folds each modifiable entity's authored base stats (from the AbilityDatabase blob, keyed by
/// AbilityRef / CharacterStatsRef) with its replicated StatModifier buffer into the
/// EffectiveAbilityStats / EffectiveCharacterStats components - every predicted tick, on both worlds.
/// CharacterStatsRef / the AbilitySocket loadout) with its replicated StatModifier buffer into the
/// EffectiveCharacterStats / EffectiveSocketStats components - every predicted tick, on both worlds.
/// (The legacy single AbilityRef -> EffectiveAbilityStats fold is deleted — LANTERN purge.)
///
/// Runs at the head of the predicted group (UpdateBefore PlayerAimSystem;
/// AbilityFireSystem runs after PlayerAimSystem, so it sees fresh values too). Recompute is
@@ -34,24 +35,11 @@ namespace ProjectM.Simulation
var database = SystemAPI.GetSingleton<AbilityDatabase>();
ref var db = ref database.Value.Value;
foreach (var (abilityRef, charRef, mods, effAbility, effChar) in
SystemAPI.Query<RefRO<AbilityRef>, RefRO<CharacterStatsRef>, DynamicBuffer<StatModifier>,
RefRW<EffectiveAbilityStats>, RefRW<EffectiveCharacterStats>>()
foreach (var (charRef, mods, effChar) in
SystemAPI.Query<RefRO<CharacterStatsRef>, DynamicBuffer<StatModifier>,
RefRW<EffectiveCharacterStats>>()
.WithAll<Simulate>())
{
if (db.TryGetAbility(abilityRef.ValueRO.Id, out var a))
{
effAbility.ValueRW = new EffectiveAbilityStats
{
Damage = StatMath.Apply(a.Damage, StatTarget.Damage, mods),
ProjectileSpeed = StatMath.Apply(a.ProjectileSpeed, StatTarget.ProjectileSpeed, mods),
Range = StatMath.Apply(a.Range, StatTarget.Range, mods),
AutoTargetRange = StatMath.Apply(a.AutoTargetRange, StatTarget.AutoTargetRange, mods),
AutoTargetConeRadians = StatMath.Apply(a.AutoTargetConeRadians, StatTarget.AutoTargetConeRadians, mods),
CooldownTicks = (int)math.round(StatMath.Apply(a.CooldownTicks, StatTarget.CooldownTicks, mods)),
};
}
if (db.TryGetCharacter(charRef.ValueRO.Id, out var c))
{
effChar.ValueRW = new EffectiveCharacterStats
@@ -65,8 +53,7 @@ namespace ProjectM.Simulation
// LANTERN Phase 1 (Step 1b): per-socket fold - each socket's Spark base folded with the SHARED
// StatModifier band into its EffectiveSocketStats row (uniform band; per-Spark warping is Phase 4).
// Separate query so this system stays under the 7-arg cap; the legacy single fold above stays
// until AbilityFireSystem + the feel layer migrate to the buffer (steps 2/2.5).
// Separate query so this system stays under the 7-arg cap.
foreach (var (sockets, socketMods, effSockets) in
SystemAPI.Query<DynamicBuffer<AbilitySocket>, DynamicBuffer<StatModifier>, DynamicBuffer<EffectiveSocketStats>>()
.WithAll<Simulate>())