diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs
index bd4cd8b..4885848 100644
--- a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs
+++ b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs
@@ -14,6 +14,12 @@ namespace TD.Gameplay.BuilderSpells
/// while aiming, and this spell resolves against that same radius (no separate field).
/// Reuses the existing slow/DoT system () rather than
/// introducing a new status mechanism.
+ ///
+ /// The cast spawns a that lingers for
+ /// and keeps re-applying the slow to anything standing in
+ /// the radius, so enemies walking through the area get slowed rather than only whatever
+ /// happened to be standing there at the exact instant of cast.
+ ///
///
[CreateAssetMenu(fileName = "SlowAreaSpell", menuName = "TD/Builder Spells/Slow Area")]
public class SlowAreaSpellDefinition : BuilderSpellDefinition
@@ -35,7 +41,9 @@ 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 slow lasts on an affected enemy, and how long the area itself " +
+ "lingers, re-slowing anything that walks through it. Re-casting on an " +
+ "already-slowed enemy refreshes it.")]
[Min(0f)]
public float EffectDuration = 3f;
@@ -43,25 +51,12 @@ namespace TD.Gameplay.BuilderSpells
{
PlayerSlot owner = PlayerMatchState.SlotForClient(clientId);
- int count = Physics.OverlapSphereNonAlloc(
- targetPoint, Mathf.Max(Radius, 0.01f), s_overlapBuffer, enemyLayerMask);
+ SlowAreaZone.Spawn(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();
- if (enemyHealth == null || enemyHealth.IsDead) continue;
-
- var enemyStatus = s_overlapBuffer[i].GetComponent();
- if (enemyStatus == null) continue;
-
- enemyStatus.ApplyEffect(DamageType.Cold, SlowFactor, EffectDuration, owner);
- hitAny = true;
- }
-
- return hitAny;
+ return true;
}
-
+
public override void ClientSpawnVfx(Vector3 targetPoint)
{
if (areaVfxPrefab != null)
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs
new file mode 100644
index 0000000..de1c76a
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs
@@ -0,0 +1,77 @@
+// Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs
+using UnityEngine;
+using TD.Core;
+
+namespace TD.Gameplay.BuilderSpells
+{
+ ///
+ /// Server-only, non-networked lingering area spawned by
+ /// . Re-scans its radius on an
+ /// interval for its lifetime and keeps refreshing the slow on anything standing
+ /// inside, so enemies get slowed as they walk through rather than only at the
+ /// instant of cast.
+ ///
+ ///
+ /// Never spawned as a — it runs
+ /// server-side only, and the slow it applies already reaches clients through
+ /// 's own NetworkVariable, the same as an instant hit.
+ ///
+ public class SlowAreaZone : MonoBehaviour
+ {
+ private const float TickInterval = 0.2f;
+
+ private float radius;
+ private float slowFactor;
+ private float effectDuration;
+ private LayerMask enemyLayerMask;
+ private PlayerSlot owner;
+
+ private readonly Collider[] overlapBuffer = new Collider[64];
+ private float tickTimer;
+
+ public static SlowAreaZone Spawn(Vector3 position, float radius, float slowFactor,
+ float effectDuration, LayerMask enemyLayerMask, PlayerSlot owner)
+ {
+ var zoneObject = new GameObject("SlowAreaZone (Server)");
+ zoneObject.transform.position = position;
+
+ var zone = zoneObject.AddComponent();
+ zone.radius = radius;
+ zone.slowFactor = slowFactor;
+ zone.effectDuration = effectDuration;
+ zone.enemyLayerMask = enemyLayerMask;
+ zone.owner = owner;
+ zone.ApplyToOverlapping();
+
+ Destroy(zoneObject, effectDuration);
+ return zone;
+ }
+
+ private void Update()
+ {
+ tickTimer -= Time.deltaTime;
+ if (tickTimer > 0f) return;
+
+ ApplyToOverlapping();
+ }
+
+ private void ApplyToOverlapping()
+ {
+ tickTimer = TickInterval;
+
+ int count = Physics.OverlapSphereNonAlloc(
+ transform.position, radius, overlapBuffer, enemyLayerMask);
+
+ for (int i = 0; i < count; i++)
+ {
+ var enemyHealth = overlapBuffer[i].GetComponent();
+ if (enemyHealth == null || enemyHealth.IsDead) continue;
+
+ var enemyStatus = overlapBuffer[i].GetComponent();
+ if (enemyStatus == null) continue;
+
+ enemyStatus.ApplyEffect(DamageType.Cold, slowFactor, effectDuration, owner);
+ }
+ }
+ }
+}
diff --git a/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs.meta b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs.meta
new file mode 100644
index 0000000..7c880a9
--- /dev/null
+++ b/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaZone.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 7b00a1f57e8be13cd91c0ed5f515bfc8
\ No newline at end of file