2aebc37115
Cone/SpecialSlam damage lands at its visual contact via ConeContactPending (knob 32, 0=legacy; early-flush + resolve re-validate; death + BOTH class-swap paths drop armed pendings — fixes the shipped melee death-strand in the same stroke). Zones: [GhostField] Caster/Radius/NextTick + ZoneTelegraphSystem (Geyser latch contract on InterpolationTick; rim = true radius; arm grows, persistent phase drains). Cone cues latch to contact (FireStartRaw, C14); TuningConfig.Defaults() fallback at client cue sites (release-build timing fix). G4: SaturationMath ally-FX degrade (living-enemy census, solo-exempt; enemy telegraphs structurally exempt) + CombatStressDebug + overlay saturation rows. Reviews wf_98bf1268 (13 confirmed folded) / wf_9757d214 (5 confirmed fixed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
3.7 KiB
C#
65 lines
3.7 KiB
C#
using Unity.Entities;
|
|
using Unity.NetCode;
|
|
|
|
namespace ProjectM.Simulation
|
|
{
|
|
/// <summary>
|
|
/// Periodic-AoE state on a player-cast zone ghost (Vortex / LightZone) — the LANTERN Aoe/zone archetype.
|
|
/// The zone is a server-spawned INTERPOLATED ownerless ghost; position replicates via the stock LocalTransform
|
|
/// variant. 07-21 G6 (design review wf_98bf1268): THREE fields are now <c>[GhostField]</c> so the client can
|
|
/// draw an HONEST fill telegraph (ZoneTelegraphSystem): <see cref="CasterNetworkId"/> (G3 ownership tint),
|
|
/// <see cref="Radius"/> (rim = the TRUE folded damage radius) and <see cref="NextTick"/> (fill arrival = the
|
|
/// damage moment — server mutations propagate to all clients on an ownerless ghost). Damage schedule/amount
|
|
/// stay server-only. <c>ZonePulseSystem</c> borrows the <c>GeyserEruptSystem</c> skeleton (the INVERTED
|
|
/// invalid-tick guard so a born-0 tick never storm-fires + the <c>= now + period</c> reschedule) but is
|
|
/// ENEMY-ONLY (no friendly fire), stamps <see cref="DamageEvent.SourceNetworkId"/> = <see cref="CasterNetworkId"/>
|
|
/// (kills credit the caster, feeding KillRewardSystem — NOT the -1 environment convention), and has NO region
|
|
/// gate (a no-world gym). DestroyEntity when <see cref="ExpireTick"/> elapses.
|
|
/// </summary>
|
|
public struct ZoneEffect : IComponentData
|
|
{
|
|
/// <summary>Pulse cadence in ticks — single source for ZonePulseSystem (damage) and ZoneTelegraphSystem
|
|
/// (the client fill window). Hoisted 07-21 G6 (was ZonePulseSystem.k_PulsePeriodTicks).</summary>
|
|
public const uint PulsePeriodTicks = 30;
|
|
|
|
/// <summary>NetworkId of the casting player (damage/kill attribution + the client's mine-vs-ally tint).</summary>
|
|
[GhostField] public int CasterNetworkId;
|
|
|
|
/// <summary>Planar (XZ) damage radius, world units. Replicated so the drawn rim is the TRUE folded radius.</summary>
|
|
[GhostField(Quantization = 100)] public float Radius;
|
|
|
|
/// <summary>Raw next-pulse tick (NonZero). <c>0</c> = unscheduled → lazy-stamped born-correct (GeyserErupt H2
|
|
/// rule, never storm-fires). Replicated for the client fill: ride the ABSOLUTE tick + a value-latch + a
|
|
/// was-counting-down arm-guard (the Geyser ★ rule — never edge-detect the re-stamp).</summary>
|
|
[GhostField] public uint NextTick;
|
|
|
|
/// <summary>Damage dealt to each living enemy in radius per pulse. Server-only.</summary>
|
|
public float DamagePerPulse;
|
|
|
|
/// <summary>Raw tick the zone despawns (NonZero). Active while .IsNewerThan(serverTick). Server-only.</summary>
|
|
public uint ExpireTick;
|
|
|
|
/// <summary>See <see cref="ZoneEffectFlag"/>. bit0 = Vortex (also pull enemies toward the zone center each pulse).</summary>
|
|
public byte Flags;
|
|
}
|
|
|
|
/// <summary>Bit masks for <see cref="ZoneEffect.Flags"/>.</summary>
|
|
public static class ZoneEffectFlag
|
|
{
|
|
public const byte Vortex = 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// SERVER-ONLY decoy-wisp state (the Aoe/spawn Spark) — a server-spawned INTERPOLATED ownerless ghost that
|
|
/// draws Husk aggro. <c>EnemyAISystem</c> gathers DecoyTag entities into its target set so Husks path to and
|
|
/// strike the decoy (soaking hits via its Health/DamageEvent buffer); it despawns on <c>Health <= 0</c> or
|
|
/// when <see cref="ExpireTick"/> elapses (both handled where EnemyAISystem already iterates decoys). Position
|
|
/// replicates via the stock LocalTransform variant — NOT a <c>[GhostField]</c>.
|
|
/// </summary>
|
|
public struct DecoyTag : IComponentData
|
|
{
|
|
/// <summary>Raw tick the decoy despawns (NonZero). <c>0</c> = no timeout; active while .IsNewerThan(serverTick).</summary>
|
|
public uint ExpireTick;
|
|
}
|
|
}
|