UnityTowerDefense/Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs

78 lines
3.4 KiB
C#

// Assets/_Project/Scripts/Gameplay/BuilderSpells/SlowAreaSpellDefinition.cs
using UnityEngine;
using TD.Audio;
using TD.Core;
namespace TD.Gameplay.BuilderSpells
{
/// <summary>
/// Builder spell #2 — slow every enemy within an area for a duration.
/// </summary>
/// <remarks>
/// <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>
/// The cast spawns a <see cref="SlowAreaZone"/> that lingers for
/// <see cref="EffectDuration"/> 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.
/// </para>
/// </remarks>
[CreateAssetMenu(fileName = "SlowAreaSpell", menuName = "TD/Builder Spells/Slow Area")]
public class SlowAreaSpellDefinition : BuilderSpellDefinition
{
public override BuilderSpellKind Kind => BuilderSpellKind.SlowArea;
[Header("Area VFX")]
[Tooltip("Prefab spawned at the target point on a successful cast, authored at a 1-unit " +
"radius — scaled up to match Radius here. Lives for EffectDuration, matching " +
"how long the slow itself lasts. Optional — leave empty for no visual.")]
[SerializeField] private GameObject areaVfxPrefab;
[Tooltip("Sound played on every peer when this spell resolves successfully.")]
[SerializeField] private SoundConfig areaSound;
[Header("Slow Area")]
[Tooltip("Speed multiplier applied to affected enemies for EffectDuration " +
"(e.g. 0.5 = half speed).")]
[Range(0f, 1f)]
public float SlowFactor = 0.5f;
[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;
public override bool ServerCast(ulong clientId, Vector3 targetPoint)
{
PlayerSlot owner = PlayerMatchState.SlotForClient(clientId);
SlowAreaZone.Spawn(targetPoint, Mathf.Max(Radius, 0.01f), SlowFactor,
EffectDuration, enemyLayerMask, owner);
return true;
}
public override void ClientSpawnVfx(Vector3 targetPoint)
{
if (areaVfxPrefab != null)
{
var instance = Instantiate(areaVfxPrefab, targetPoint, Quaternion.identity);
instance.transform.localScale *= Mathf.Max(Radius, 0.01f);
Destroy(instance, EffectDuration);
}
}
public override void ClientPlayImpact(Vector3 targetPoint)
{
// Instant spell (ImpactDelay = 0), so this fires on cast alongside ClientSpawnVfx.
if (areaSound.clip != null)
AudioManager.Instance?.Play(areaSound.clip, AudioCategory.Combat,
areaSound.RandomPitch(), areaSound.volume);
}
}
}