LANTERN P1 step 2.5: client feel layer migrated to the socket kit
Per Phase1_Combat_Gym_Build_Spec §8 (the "KEEP as-is" feel layer was really a migration): - PlayerAnimationDriveSystem: both jobs (Local/Remote) now read SocketCooldown + DynamicBuffer<AbilitySocket>/<EffectiveSocketStats> instead of the single AbilityCooldown/ AbilityRef/EffectiveAbilityStats; shared SocketFireAndCone helper (any-socket-firing model: IsFiring if any socketed Spark's per-socket cooldown window is mid-fire; IsCone if any such is Cone). - CombatFeedbackSystem: the muzzle-flash + cone-cue blocks unified into one per-socket fire-edge loop over SocketCooldown (per-socket uint cache); non-Cone -> muzzle flash, Cone -> the aimed slash-arc cue. Dropped the now-dead single-cooldown latches. L1 clean; L2 494/494 (no regression). Deferred to the AbilityRef-removal cleanup (surfaced, NOT silent): the FrameId server-writer + the 2 class-HUD re-points (ClassPrepPortalHudSystem/MetaShopHudSystem) + HudSystem's ability bar. Rationale: AbilityRef is still baked + set (EquipSystem/ClassSwapUtil), so ClassForAbility remains a valid class signal through the transition; FrameId is baked ready. No feel regression. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -137,6 +137,30 @@ namespace ProjectM.Client
|
||||
return start.IsValid && end.IsValid && !start.IsNewerThan(serverTick) && end.IsNewerThan(serverTick);
|
||||
}
|
||||
|
||||
// LANTERN 4-socket fire/cone anim resolution (any-socket model): firing if ANY socketed Spark's
|
||||
// per-socket cooldown window is mid-fire; cone if any such active socket holds a Cone-archetype Spark.
|
||||
// Replaces the single-ability FireActive + IsCone reads (AbilityCooldown/EffectiveAbilityStats/AbilityRef).
|
||||
static void SocketFireAndCone(in SocketCooldown cd, DynamicBuffer<AbilitySocket> sockets,
|
||||
DynamicBuffer<EffectiveSocketStats> effSockets, BlobAssetReference<AbilityDatabaseBlob> abilityDb,
|
||||
NetworkTick serverTick, uint animTicks, out bool firing, out bool cone)
|
||||
{
|
||||
firing = false; cone = false;
|
||||
int n = math.min(SocketId.Count, math.min(sockets.Length, effSockets.Length));
|
||||
bool haveDb = abilityDb.IsCreated;
|
||||
for (int sk = 0; sk < n; sk++)
|
||||
{
|
||||
byte sid = sockets[sk].SparkId;
|
||||
if (sid == 0) continue;
|
||||
if (!FireActive(cd.Get(sk), effSockets[sk].CooldownTicks, serverTick, animTicks)) continue;
|
||||
firing = true;
|
||||
if (haveDb)
|
||||
{
|
||||
ref var adb = ref abilityDb.Value;
|
||||
if (adb.TryGetAbility(sid, out var d) && d.Archetype == (byte)AbilityArchetype.Cone) cone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LOCAL: GhostOwnerIsLocal ENABLED -> exactly the owned player. WithPresent<Dead> so alive
|
||||
// (Dead-disabled) players are visited. NOTE: GhostOwnerIsLocal as a WithAll filter respects the
|
||||
// enable bit; do NOT take it as an `in` parameter (that matches on presence -> drives remotes too).
|
||||
@@ -157,9 +181,9 @@ namespace ProjectM.Client
|
||||
in EffectiveCharacterStats stats,
|
||||
in KinematicCharacterBody body,
|
||||
in MeleeCombo melee,
|
||||
in AbilityCooldown fireCooldown,
|
||||
in EffectiveAbilityStats abilityStats,
|
||||
in AbilityRef abilityRef,
|
||||
in SocketCooldown socketCd,
|
||||
[ReadOnly] DynamicBuffer<AbilitySocket> sockets,
|
||||
[ReadOnly] DynamicBuffer<EffectiveSocketStats> effSockets,
|
||||
in DashState dashState,
|
||||
EnabledRefRO<Dead> dead)
|
||||
{
|
||||
@@ -167,8 +191,8 @@ namespace ProjectM.Client
|
||||
float3 p = AnimParamMath.LocomotionParams(body.RelativeVelocity, facing.Direction, stats.MoveSpeed);
|
||||
Write(ref a, p, dead.ValueRO, moveX, moveZ, speed, isDead);
|
||||
if (a.HasParameter(isAttacking)) a.SetParameterValue(isAttacking, SwingActive(melee, serverTick, attackTicks));
|
||||
if (a.HasParameter(isFiring)) a.SetParameterValue(isFiring,
|
||||
FireActive(fireCooldown.NextFireTick, abilityStats.CooldownTicks, serverTick, attackTicks));
|
||||
SocketFireAndCone(socketCd, sockets, effSockets, abilityDb, serverTick, attackTicks, out bool firingL, out bool coneL);
|
||||
if (a.HasParameter(isFiring)) a.SetParameterValue(isFiring, firingL);
|
||||
// Dash lean: the LOCAL player's predicted DashState window [StartTick, IFrameUntilTick+tail).
|
||||
bool dashActive = false;
|
||||
if (dashState.StartTick != 0u && dashState.IFrameUntilTick != 0u && serverTick.IsValid)
|
||||
@@ -179,13 +203,7 @@ namespace ProjectM.Client
|
||||
}
|
||||
if (a.HasParameter(isDashing)) a.SetParameterValue(isDashing, dashActive);
|
||||
if (a.HasParameter(comboStep)) a.SetParameterValue(comboStep, (int)melee.Step);
|
||||
bool cone = false;
|
||||
if (abilityDb.IsCreated)
|
||||
{
|
||||
ref var adb = ref abilityDb.Value;
|
||||
if (adb.TryGetAbility(abilityRef.Id, out var adef)) cone = adef.Archetype == (byte)AbilityArchetype.Cone;
|
||||
}
|
||||
if (a.HasParameter(isCone)) a.SetParameterValue(isCone, cone);
|
||||
if (a.HasParameter(isCone)) a.SetParameterValue(isCone, coneL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,9 +230,9 @@ namespace ProjectM.Client
|
||||
in PlayerFacing facing,
|
||||
in EffectiveCharacterStats stats,
|
||||
in MeleeCombo melee,
|
||||
in AbilityCooldown fireCooldown,
|
||||
in EffectiveAbilityStats abilityStats,
|
||||
in AbilityRef abilityRef,
|
||||
in SocketCooldown socketCd,
|
||||
[ReadOnly] DynamicBuffer<AbilitySocket> sockets,
|
||||
[ReadOnly] DynamicBuffer<EffectiveSocketStats> effSockets,
|
||||
EnabledRefRO<Dead> dead)
|
||||
{
|
||||
seen.Add(e);
|
||||
@@ -228,17 +246,11 @@ namespace ProjectM.Client
|
||||
float3 p = AnimParamMath.LocomotionParams(vel, facing.Direction, stats.MoveSpeed);
|
||||
Write(ref a, p, dead.ValueRO, moveX, moveZ, speed, isDead);
|
||||
if (a.HasParameter(isAttacking)) a.SetParameterValue(isAttacking, SwingActive(melee, serverTick, attackTicks));
|
||||
if (a.HasParameter(isFiring)) a.SetParameterValue(isFiring,
|
||||
FireActive(fireCooldown.NextFireTick, abilityStats.CooldownTicks, serverTick, attackTicks));
|
||||
SocketFireAndCone(socketCd, sockets, effSockets, abilityDb, serverTick, attackTicks, out bool firingR, out bool coneR);
|
||||
if (a.HasParameter(isFiring)) a.SetParameterValue(isFiring, firingR);
|
||||
if (a.HasParameter(isDashing)) a.SetParameterValue(isDashing, false); // DashState is not replicated to remotes
|
||||
if (a.HasParameter(comboStep)) a.SetParameterValue(comboStep, (int)melee.Step);
|
||||
bool cone = false;
|
||||
if (abilityDb.IsCreated)
|
||||
{
|
||||
ref var adb = ref abilityDb.Value;
|
||||
if (adb.TryGetAbility(abilityRef.Id, out var adef)) cone = adef.Archetype == (byte)AbilityArchetype.Cone;
|
||||
}
|
||||
if (a.HasParameter(isCone)) a.SetParameterValue(isCone, cone);
|
||||
if (a.HasParameter(isCone)) a.SetParameterValue(isCone, coneR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user