Vault Re-Alignment
This commit is contained in:
@@ -25,6 +25,12 @@ namespace ProjectM.Server
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
|
||||
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
|
||||
[UpdateAfter(typeof(ProjectileDamageSystem))]
|
||||
// Pin the drain AFTER DashSystem: a same-tick player-sourced projectile (ProjectileDamageSystem stamps
|
||||
// SourceTick = now and this system drains the SAME tick) must see a dash window STARTED this tick —
|
||||
// without the edge the negation at src == StartTick is an unconstrained sorter tiebreak. The Dash chain
|
||||
// (StatRecompute→PlayerControl→Dash) and the projectile chain (PlayerAim→AbilityFire→ProjectileMove→
|
||||
// ProjectileDamage→here) are otherwise disjoint, so this edge cannot form a cycle (Play-validated).
|
||||
[UpdateAfter(typeof(DashSystem))]
|
||||
[BurstCompile]
|
||||
public partial struct HealthApplyDamageSystem : ISystem
|
||||
{
|
||||
@@ -33,6 +39,8 @@ namespace ProjectM.Server
|
||||
{
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
bool haveTick = SystemAPI.TryGetSingleton<NetworkTime>(out var netTime);
|
||||
uint negatedThisTick = 0;
|
||||
uint punishesThisTick = 0;
|
||||
|
||||
foreach (var (health, dmg, entity) in
|
||||
SystemAPI.Query<RefRW<Health>, DynamicBuffer<DamageEvent>>()
|
||||
@@ -63,10 +71,55 @@ namespace ProjectM.Server
|
||||
}
|
||||
}
|
||||
|
||||
bool hasDash = haveTick && netTime.ServerTick.IsValid && SystemAPI.HasComponent<DashState>(entity);
|
||||
DashState ds = hasDash ? SystemAPI.GetComponent<DashState>(entity) : default;
|
||||
|
||||
bool isCharger = haveTick && netTime.ServerTick.IsValid && SystemAPI.HasComponent<LungeState>(entity);
|
||||
uint negatedForThisEntity = 0u;
|
||||
float total = 0f;
|
||||
for (int i = 0; i < dmg.Length; i++)
|
||||
{
|
||||
uint src = dmg[i].SourceTick;
|
||||
if (hasDash && src != 0u && ds.IFrameUntilTick != 0u)
|
||||
{
|
||||
var srcTick = new NetworkTick(src);
|
||||
var startTick = new NetworkTick(ds.StartTick);
|
||||
var untilTick = new NetworkTick(ds.IFrameUntilTick);
|
||||
// Dash i-frames cover the HALF-OPEN window [StartTick, IFrameUntilTick): negate iff src >= start AND src < until.
|
||||
bool atOrAfterStart = srcTick.IsValid && startTick.IsValid && !startTick.IsNewerThan(srcTick);
|
||||
bool beforeUntil = untilTick.IsValid && untilTick.IsNewerThan(srcTick);
|
||||
if (atOrAfterStart && beforeUntil)
|
||||
{
|
||||
negatedThisTick++;
|
||||
negatedForThisEntity++;
|
||||
continue; // dash i-frame negates this hit (per-element, not a whole-buffer clear)
|
||||
}
|
||||
}
|
||||
total += dmg[i].Amount;
|
||||
|
||||
// MC-1 punish scoring: a player-sourced hit (SourceNetworkId >= 0) landing inside a Charger's
|
||||
// whiff-stagger window counts ONCE — zeroing StaggerUntilTick keeps punishes:windows <= 1.
|
||||
if (isCharger && dmg[i].SourceNetworkId >= 0)
|
||||
{
|
||||
var lunge = SystemAPI.GetComponent<LungeState>(entity);
|
||||
if (lunge.StaggerUntilTick != 0u)
|
||||
{
|
||||
var stag = new NetworkTick(lunge.StaggerUntilTick);
|
||||
if (stag.IsValid && stag.IsNewerThan(netTime.ServerTick))
|
||||
{
|
||||
punishesThisTick++;
|
||||
lunge.StaggerUntilTick = 0u;
|
||||
SystemAPI.SetComponent(entity, lunge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dmg.Clear();
|
||||
if (negatedForThisEntity != 0u)
|
||||
{
|
||||
ds.NegatedCount += negatedForThisEntity; // server-side spam signal; DashSystem reads it at window-close
|
||||
SystemAPI.SetComponent(entity, ds);
|
||||
}
|
||||
|
||||
float newHp = health.ValueRO.Current - total;
|
||||
|
||||
@@ -83,6 +136,12 @@ namespace ProjectM.Server
|
||||
if (health.ValueRO.Current <= 0f && (SystemAPI.HasComponent<TrainingDummyTag>(entity) || SystemAPI.HasComponent<EnemyTag>(entity)))
|
||||
ecb.DestroyEntity(entity);
|
||||
}
|
||||
if ((negatedThisTick != 0u || punishesThisTick != 0u) && SystemAPI.HasSingleton<DevTelemetry>())
|
||||
{
|
||||
var telem = SystemAPI.GetSingletonRW<DevTelemetry>();
|
||||
telem.ValueRW.DashIFrameNegatedHits += negatedThisTick;
|
||||
telem.ValueRW.ChargerWhiffPunishesLanded += punishesThisTick;
|
||||
}
|
||||
|
||||
ecb.Playback(state.EntityManager);
|
||||
ecb.Dispose();
|
||||
|
||||
Reference in New Issue
Block a user