Files
kronic 56cf60cce3 Slice Combat Depth (MC-2): enemy-variety server spine — Spitter, Swarmer, 4-type mix (DR-041)
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>
2026-06-24 20:06:56 -07:00

45 lines
1.9 KiB
C#

using ProjectM.Simulation;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
namespace ProjectM.Authoring
{
/// <summary>
/// MC-2 — authoring for the hostile Spitter projectile prefab (an ownerless INTERPOLATED ghost, duplicated from an
/// existing interpolated ghost so the GhostAuthoringComponent comes free). Bakes <see cref="EnemyProjectile"/> with
/// the spit's default Speed/Damage/Range; the firing Spitter OVERRIDES Direction + Speed + Damage + Region at spawn
/// and ADDS the <c>RegionTag</c> (so this prefab MUST NOT bake RegionTag — AddComponent would throw on a baked one).
/// NO Health (so it is invisible to every player hit-test) and NO <c>[GhostField]</c> beyond the stock LocalTransform.
/// </summary>
public class EnemyProjectileAuthoring : MonoBehaviour
{
[Min(0f), Tooltip("Default muzzle speed (the firing Spitter overrides this per-variant).")]
public float Speed = 11f;
[Min(0f), Tooltip("Default damage (the firing Spitter overrides this from its AttackDamage).")]
public float Damage = 8f;
[Min(0f), Tooltip("Max travel distance before the spit expires (world units).")]
public float Range = 16f;
private class EnemyProjectileBaker : Baker<EnemyProjectileAuthoring>
{
public override void Bake(EnemyProjectileAuthoring authoring)
{
var entity = GetEntity(authoring, TransformUsageFlags.Dynamic);
AddComponent(entity, new EnemyProjectile
{
Speed = authoring.Speed,
Damage = authoring.Damage,
Range = authoring.Range,
Direction = new float2(0f, 1f),
DistanceTravelled = 0f,
LastStep = 0f,
Region = 0,
});
}
}
}
}