LANTERN P1 step 6: manual-aim windups (SoD readable telegraphs)
AbilityDefBlob/AbilityDefinition gain WindupTicks. AbilityFireSystem's socket loop resolves each socket at the historical command (serverTick - WindupTicks) via GetDataAtTick — a pure fn of replicated data on both worlds (Build Spec §4/ DB-6: no local fireAtTick that would diverge under reprediction; fireCount from that command, spawn uses the current tick's aim/pos; history gap = no-fire). WindupTicks set on the 4 non-Blink Sparks (Blink is movement, resolved by BlinkSystem). 497 EditMode green (no regression). Client telegraph VFX + the reprediction L3 test are operator-eyes / netcode-world follow-ups. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ namespace ProjectM.Simulation
|
||||
public int CooldownTicks;
|
||||
public byte EffectFlags; // ProjectileEffectFlag bits stamped at spawn (the Reel bit ⇒ HookPull homes the hit target to the caster). Config, not replicated.
|
||||
public int DurationTicks; // Aoe/zone + decoy lifetime, ticks (0 = system default). Reel leash is a system const.
|
||||
public int WindupTicks; // manual-aim telegraph delay (ticks) before the ability resolves; 0 = instant (Build Spec §4/DB-6)
|
||||
public FixedString64Bytes Name;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,9 +131,8 @@ namespace ProjectM.Simulation
|
||||
BoonEffects bfx = m_BoonEffectsLookup.HasComponent(entity) ? m_BoonEffectsLookup[entity] : default;
|
||||
bool pull = (bfx.Flags & BoonFlag.KnockToPull) != 0;
|
||||
|
||||
// Replicated command buffer at this tick (per-socket fire count for the SpawnId + scheme for aim assist).
|
||||
// Replicated command buffer (windup resolve + per-socket fire count for the SpawnId + scheme for aim assist).
|
||||
var inputBuffer = SystemAPI.GetBuffer<InputBufferData<PlayerInput>>(entity);
|
||||
bool haveApplied = inputBuffer.GetDataAtTick(serverTick, out var applied);
|
||||
|
||||
bool cdDirty = false;
|
||||
int socketCount = math.min(SocketId.Count, sockets.Length);
|
||||
@@ -141,7 +140,23 @@ namespace ProjectM.Simulation
|
||||
{
|
||||
byte sparkId = sockets[sk].SparkId;
|
||||
if (sparkId == 0) continue; // empty socket
|
||||
if (!input.ValueRO.GetSocket(sk).IsSet) continue; // this socket not fired this tick
|
||||
|
||||
EffectiveSocketStats es = sk < effSockets.Length ? effSockets[sk] : default;
|
||||
byte archetype = adb.TryGetAbility(sparkId, out var adef) ? adef.Archetype : (byte)AbilityArchetype.Projectile;
|
||||
|
||||
// MANUAL-AIM WINDUP (Build Spec §4/DB-6): resolve when the command WindupTicks ago had this socket
|
||||
// set, so the resolve tick is a pure fn of replicated data on both worlds (no local fireAtTick that
|
||||
// diverges under reprediction). fireCount comes from THAT command; the spawn uses the CURRENT tick's
|
||||
// aim/position. WindupTicks == 0 = instant. A history gap (older than the buffer window) = no-fire.
|
||||
NetworkTick resolveTick = serverTick;
|
||||
if (adef.WindupTicks > 0)
|
||||
{
|
||||
uint riNow = serverTick.TickIndexForValidTick;
|
||||
if (riNow <= (uint)adef.WindupTicks) continue; // not enough history yet -> no-fire
|
||||
resolveTick = new NetworkTick(riNow - (uint)adef.WindupTicks);
|
||||
}
|
||||
if (!inputBuffer.GetDataAtTick(resolveTick, out var applied)) continue; // history gap -> no-fire
|
||||
if (!applied.InternalInput.GetSocket(sk).IsSet) continue; // socket not fired at the resolve tick
|
||||
|
||||
// Per-socket cooldown gate (0 = ready).
|
||||
uint nextFireRaw = cd.Get(sk);
|
||||
@@ -151,9 +166,6 @@ namespace ProjectM.Simulation
|
||||
if (nextTick.IsValid && nextTick.IsNewerThan(serverTick)) continue;
|
||||
}
|
||||
|
||||
EffectiveSocketStats es = sk < effSockets.Length ? effSockets[sk] : default;
|
||||
byte archetype = adb.TryGetAbility(sparkId, out var adef) ? adef.Archetype : (byte)AbilityArchetype.Projectile;
|
||||
|
||||
// CONE: no projectile ghost. Predict the cooldown on both worlds; apply server-only cleave.
|
||||
if (archetype == (byte)AbilityArchetype.Cone)
|
||||
{
|
||||
@@ -261,7 +273,6 @@ namespace ProjectM.Simulation
|
||||
if (abilityPrefabs[i].Id == sparkId) { prefab = abilityPrefabs[i].Prefab; break; }
|
||||
}
|
||||
if (prefab == Entity.Null) continue;
|
||||
if (!haveApplied) continue;
|
||||
|
||||
uint socketFireCount = applied.InternalInput.GetSocket(sk).Count;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user