56cf60cce3
Adds the server-authoritative mechanics for three new enemy archetypes on top of the Grunt/Charger base, plus the weighted wave-composition that introduces them: - Spitter: a ranged Husk variant (SpitterState) that holds a preferred range-band (advance/retreat/hold via EnemyAIMath.BandVelocity) and fires a telegraphed, dodgeable EnemyProjectile. New server EnemyProjectileMoveSystem (integrate + store LastStep) + EnemyProjectileDamageSystem (region-filtered swept hit-test rebuilt from LastStep — DR-018 anti-tunnelling; players use HitRadius, structures a const radius; at-most-once destroy). Concurrent-spit soft cap, soft-fail retry. - Swarmer: marker tag + deterministic cluster spawn (1 slot = 1 pack; EnemyAIMath.ClusterOffset), MaxAlive counts ENTITIES so a pack defers if it won't fit. - 4-type weighted mix: MixBands -> ZoneEnemyMath.WaveSlots/KindForSlot/ PackSizeForSlot drives both the expedition director and (fork-4a) the base siege, with a mandatory MaxAlive cap. Legacy WaveSize/IsChargerSlot kept + parity-tested. - Discriminator stays component-presence (no enum in Bursted systems): query- partition guards keep each enemy moved by exactly one EnemyAISystem pass (sole-Position-writer). EnemyTelegraph.IsCharger -> Kind byte for the client cue. New authoring (Spitter/Swarmer/EnemyProjectile) + expanded director authorings with tunable mix/cluster defaults. 13 new EditMode tests (mix composition + legacy parity, band/cluster math, projectile move + cross-region + swept anti-tunnelling regressions); full suite green before commit. Dormant until the prefab/subscene wiring lands (next): the new systems guard on TryGetSingleton/RequireForUpdate, so with no prefabs wired the new types stay inert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
37 lines
2.2 KiB
C#
37 lines
2.2 KiB
C#
using Unity.Entities;
|
|
using Unity.NetCode;
|
|
|
|
namespace ProjectM.Simulation
|
|
{
|
|
/// <summary>
|
|
/// Replicated Husk attack-telegraph signal. While <see cref="WindUpUntilTick"/> is non-zero the Husk is
|
|
/// "winding up" to strike; EnemyAISystem sets it ~<see cref="Tuning.AttackWindupTicks"/> before the strike
|
|
/// lands, and the strike fires when the tick elapses. This is a [GhostField] (the only replicated Husk field
|
|
/// beyond the stock LocalTransform) so the CLIENT can play a ~0.3s pre-strike cue — the client has none of the
|
|
/// server-only timing inputs (EnemyStats / EnemyAttackCooldown), so the wind-up MUST be replicated. A uint tick
|
|
/// (not a [GhostEnabledBit]) so the cue can ramp/countdown and survive a missed snapshot (absolute, not an edge).
|
|
/// </summary>
|
|
public struct AttackWindup : IComponentData
|
|
{
|
|
/// <summary>Server tick the wind-up completes + the strike lands (0 = not winding up; scheduled via TickUtil.NonZero).</summary>
|
|
[GhostField] public uint WindUpUntilTick;
|
|
}
|
|
|
|
/// <summary>Client-safe BAKED telegraph metadata (NOT a [GhostField] — baked values are identical on
|
|
/// both worlds, so the client reads it directly to size the danger-cone ramp + pick the Charger look).
|
|
/// WindupTicks = the variant's wind-up lead in ticks (the client danger-ramp denominator: Grunt =
|
|
/// Tuning.AttackWindupTicks, Charger = ChargerWindupTicks); IsCharger distinguishes the variant since
|
|
/// LungeState is server-only. Both stored as byte (no enum on a client-read path — honours the Burst
|
|
/// byte-not-enum convention); never changes at runtime, so a missed snapshot is irrelevant. Baked by
|
|
/// EnemyAuthoring.EnemyBaker (the SOLE writer — reads sibling ChargerAuthoring to set the Charger values).</summary>
|
|
public struct EnemyTelegraph : IComponentData
|
|
{
|
|
/// <summary>Per-variant wind-up DURATION in ticks (the client danger-ramp denominator).</summary>
|
|
public byte WindupTicks;
|
|
|
|
/// <summary>Enemy kind for the client telegraph look: 0=Grunt, 1=Charger, 2=Spitter, 3=Swarmer (the
|
|
/// ZoneEnemyMath.Kind* bytes). Baked per variant by EnemyBaker (the SOLE writer); never a [GhostField].</summary>
|
|
public byte Kind;
|
|
}
|
|
}
|