using Unity.Entities; using Unity.Mathematics; namespace ProjectM.Simulation { /// /// MC-2 — a hostile Spitter projectile: a server-spawned, OWNERLESS INTERPOLATED ghost moved server-only in the /// plain SimulationSystemGroup (NOT predicted — like the Husks that fire it). It replicates ONLY the stock /// LocalTransform (no hand-written [GhostField]); this component is server-only state. It deliberately carries NO /// Health, so it is invisible to every WithAll<Health> target loop (player melee/projectile hit-tests can /// never see it — fork 2a: spits are pure dodge/dash checks, NOT shootable). Integrated by /// EnemyProjectileMoveSystem and swept-hit-tested against players + structures by EnemyProjectileDamageSystem. /// public struct EnemyProjectile : IComponentData { /// Planar heading (world XZ -> float2 x,y), unit length, locked at spawn. public float2 Direction; /// Travel speed (world units/second). public float Speed; /// Damage applied to the first valid same-region target hit. public float Damage; /// Max travel distance before it expires (world units). public float Range; /// Accumulated travelled distance (server-only; drives range-expiry). public float DistanceTravelled; /// Distance moved on the LAST tick (= Speed * the server fixed step). The damage system rebuilds the /// swept segment as cur - Direction*LastStep — NEVER a fresh SystemAPI.Time.DeltaTime (this system runs in the /// PLAIN group where that dt is the wall-frame delta, not the fixed step). Prevents high-speed tunnelling. public float LastStep; /// Region byte (RegionId.Base/Expedition), copied from the firing Spitter. The damage system skips any /// target whose RegionTag.Region != this — relevancy hides cross-region ghosts from CLIENTS, but the SERVER /// world holds base + expedition players 1000u apart, so server damage needs its OWN region guard. public byte Region; } /// /// Baked subscene singleton: the Spitter projectile ghost prefab + the concurrent soft-cap. The server reads it /// via GetSingleton (the prefab Entity lives HERE, never per-Spitter — mirrors AbilityDatabase / WaveEnemyPrefab). /// MaxLiveProjectiles bounds the RegionRelevancySystem O(ghosts x conn)/tick loop: a Spitter at/over the cap /// soft-fails its shot (no cooldown burn — the EB-2 turret soft-fail pattern). /// public struct SpitterProjectilePrefab : IComponentData { public Entity Prefab; public int MaxLiveProjectiles; } }