slow area persists as an area rather than instantaneously applying once to enemies in the area
This commit is contained in:
parent
e25a691ad9
commit
7419e9a140
2 changed files with 97 additions and 20 deletions
|
|
@ -0,0 +1,80 @@
|
|||
// Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowZoneRuntime.cs
|
||||
using UnityEngine;
|
||||
using TD.Core;
|
||||
|
||||
namespace TD.Gameplay.BuilderSpells
|
||||
{
|
||||
/// <summary>
|
||||
/// Server-only runtime object for a Slow Area cast. Re-scans its radius every frame for the
|
||||
/// rest of the zone's lifetime, continuously refreshing the Cold status on whatever enemies
|
||||
/// currently overlap it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Zone, not a snapshot.</b> Replaces <see cref="SlowAreaSpellDefinition"/>'s old
|
||||
/// single <c>OverlapSphereNonAlloc</c> call at cast time, which slowed whoever was standing
|
||||
/// there for a fixed duration regardless of whether they walked out. This ticks continuously
|
||||
/// instead, so only enemies currently inside the radius are ever affected.</para>
|
||||
///
|
||||
/// <para><b>Refresh, not enter/exit events.</b> Each tick re-applies
|
||||
/// <see cref="EnemyStatus.ApplyEffect"/> with a short fixed window (<see cref="RefreshWindow"/>),
|
||||
/// not the zone's remaining lifetime. An enemy that walks out of the zone simply stops being
|
||||
/// refreshed and the slow expires on its own <see cref="RefreshWindow"/> seconds later —
|
||||
/// no explicit exit detection needed.</para>
|
||||
///
|
||||
/// <para><b>Not a NetworkObject.</b> It only ever runs where <see
|
||||
/// cref="SlowAreaSpellDefinition.ServerCast"/> runs (the server), and it never needs to exist
|
||||
/// on clients — the zone's visual is spawned independently on every peer via
|
||||
/// <see cref="SlowAreaSpellDefinition.ClientSpawnVfx"/>.</para>
|
||||
/// </remarks>
|
||||
public class SlowZoneRuntime : MonoBehaviour
|
||||
{
|
||||
// Comfortably longer than one frame so a single missed tick doesn't let the slow
|
||||
// visibly drop while an enemy is still inside, but short enough that leaving the
|
||||
// zone reads as an almost-immediate loss of the slow.
|
||||
private const float RefreshWindow = 0.35f;
|
||||
|
||||
private static readonly Collider[] s_overlapBuffer = new Collider[64];
|
||||
|
||||
private float radius;
|
||||
private float slowFactor;
|
||||
private float remainingLifetime;
|
||||
private LayerMask enemyLayerMask;
|
||||
private PlayerSlot owner;
|
||||
|
||||
/// <summary>Positions and configures the zone. Call immediately after AddComponent.</summary>
|
||||
public void Initialize(Vector3 position, float radius, float slowFactor, float lifetime,
|
||||
LayerMask enemyLayerMask, PlayerSlot owner)
|
||||
{
|
||||
transform.position = position;
|
||||
this.radius = radius;
|
||||
this.slowFactor = slowFactor;
|
||||
this.remainingLifetime = lifetime;
|
||||
this.enemyLayerMask = enemyLayerMask;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
remainingLifetime -= Time.deltaTime;
|
||||
if (remainingLifetime <= 0f)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = Physics.OverlapSphereNonAlloc(
|
||||
transform.position, radius, s_overlapBuffer, enemyLayerMask);
|
||||
|
||||
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, RefreshWindow, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue