slow area persists as an area rather than instantaneously applying once to enemies in the area

This commit is contained in:
Ian Woods 2026-07-18 21:58:34 -07:00
parent e25a691ad9
commit 7419e9a140
2 changed files with 97 additions and 20 deletions

View file

@ -9,11 +9,16 @@ namespace TD.Gameplay.BuilderSpells
/// Builder spell #2 — slow every enemy within an area for a duration.
/// </summary>
/// <remarks>
/// <see cref="BuilderSpellDefinition.TargetType"/> is <see cref="SpellTargetType.AreaOfEffect"/>
/// <para><see cref="BuilderSpellDefinition.TargetType"/> is <see cref="SpellTargetType.AreaOfEffect"/>
/// — the cast controller previews <see cref="BuilderSpellDefinition.Radius"/> as a decal
/// while aiming, and this spell resolves against that same radius (no separate field).
/// Reuses the existing slow/DoT system (<see cref="EnemyStatus.ApplyEffect"/>) rather than
/// introducing a new status mechanism.
/// </para>
/// <para><b>Zone, not a snapshot.</b> <see cref="ServerCast"/> spawns a <see
/// cref="SlowZoneRuntime"/> that keeps re-checking the area for <see cref="EffectDuration"/>
/// seconds, so only enemies currently standing inside the radius are ever slowed — walking
/// out drops the slow almost immediately. Always succeeds and starts the cooldown, even if
/// no enemy is in the radius at the moment of cast, since the zone can still catch one later.
/// </para>
/// </remarks>
[CreateAssetMenu(fileName = "SlowAreaSpell", menuName = "TD/Builder Spells/Slow Area")]
public class SlowAreaSpellDefinition : BuilderSpellDefinition
@ -35,7 +40,8 @@ namespace TD.Gameplay.BuilderSpells
[Range(0f, 1f)]
public float SlowFactor = 0.5f;
[Tooltip("Seconds the slow lasts. Re-casting on an already-slowed enemy refreshes it.")]
[Tooltip("Seconds the zone itself persists. Enemies are only slowed while standing " +
"inside it — walking out drops the slow almost immediately.")]
[Min(0f)]
public float EffectDuration = 3f;
@ -43,23 +49,14 @@ namespace TD.Gameplay.BuilderSpells
{
PlayerSlot owner = PlayerMatchState.SlotForClient(clientId);
int count = Physics.OverlapSphereNonAlloc(
targetPoint, Mathf.Max(Radius, 0.01f), s_overlapBuffer, enemyLayerMask);
var zoneObject = new GameObject("SlowZone (server)");
zoneObject.AddComponent<SlowZoneRuntime>().Initialize(
targetPoint, Mathf.Max(Radius, 0.01f), SlowFactor, EffectDuration,
enemyLayerMask, owner);
bool hitAny = false;
for (int i = 0; i < count; i++)
{
var enemyHealth = s_overlapBuffer[i].GetComponent<EnemyHealth>();
if (enemyHealth == null || enemyHealth.IsDead) continue;
var enemyStatus = s_overlapBuffer[i].GetComponent<EnemyStatus>();
if (enemyStatus == null) continue;
enemyStatus.ApplyEffect(DamageType.Cold, SlowFactor, EffectDuration, owner);
hitAny = true;
}
return hitAny;
// Always succeeds — placing the zone starts the cooldown even if no enemy is
// standing in it yet, since it keeps slowing whoever walks in for EffectDuration.
return true;
}
public override void ClientSpawnVfx(Vector3 targetPoint)