// Assets/_Project/Scripts/Combat/TeslaArcVisual.cs
using System.Collections.Generic;
using UnityEngine;
namespace TD.Combat
{
///
/// Local-only electrical-arc visual for
/// towers (Tesla Coil). Subscribes to and draws a
/// jagged line from the tower's emitter point to every enemy hit, flashed for a moment.
///
///
/// Pure visualization — runs on every peer from the replicated fire broadcast; nothing
/// here is networked. Arcs are pooled s reused across ticks.
///
[RequireComponent(typeof(TowerCombat))]
public class TeslaArcVisual : MonoBehaviour
{
[Tooltip("Point the arcs originate from — the central top of the coil. " +
"If unset, falls back to one unit above the tower root.")]
[SerializeField] private Transform emitter;
[Tooltip("Material for the arc lines. Assign an unlit/additive material for a glow. " +
"If unset, a fallback Sprites/Default material is created at runtime.")]
[SerializeField] private Material arcMaterial;
[SerializeField] private Color arcColor = new Color(0.6f, 0.85f, 1f, 1f);
[SerializeField] private float arcWidth = 0.06f;
[Tooltip("How long each arc flash stays visible, in seconds.")]
[SerializeField] private float arcDuration = 0.12f;
[Tooltip("Number of points per arc. More = more jagged detail.")]
[SerializeField] private int segmentsPerArc = 6;
[Tooltip("Random offset applied to interior arc points for the lightning look.")]
[SerializeField] private float jitter = 0.2f;
private TowerCombat combat;
private readonly List pool = new List();
private float hideAt = -1f;
private void Awake() => combat = GetComponent();
private void OnEnable()
{
if (combat != null) combat.OnAreaFired += HandleAreaFired;
}
private void OnDisable()
{
if (combat != null) combat.OnAreaFired -= HandleAreaFired;
}
private void Update()
{
if (hideAt >= 0f && Time.time >= hideAt)
{
for (int i = 0; i < pool.Count; i++) pool[i].enabled = false;
hideAt = -1f;
}
}
private void HandleAreaFired(Vector3[] positions)
{
if (positions == null) return;
Vector3 origin = emitter != null ? emitter.position : transform.position + Vector3.up;
for (int i = 0; i < positions.Length; i++)
{
var lr = GetLine(i);
DrawArc(lr, origin, positions[i]);
lr.enabled = true;
}
// Disable any leftover lines from a previous, larger tick.
for (int i = positions.Length; i < pool.Count; i++)
pool[i].enabled = false;
hideAt = Time.time + arcDuration;
}
private LineRenderer GetLine(int index)
{
while (pool.Count <= index)
{
var go = new GameObject($"Arc_{pool.Count}");
go.transform.SetParent(transform, worldPositionStays: false);
var lr = go.AddComponent();
lr.useWorldSpace = true;
lr.widthMultiplier = arcWidth;
lr.numCapVertices = 2;
lr.textureMode = LineTextureMode.Stretch;
lr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
lr.material = arcMaterial != null
? arcMaterial
: new Material(Shader.Find("Sprites/Default"));
lr.startColor = lr.endColor = arcColor;
lr.enabled = false;
pool.Add(lr);
}
return pool[index];
}
private void DrawArc(LineRenderer lr, Vector3 a, Vector3 b)
{
int n = Mathf.Max(2, segmentsPerArc);
lr.positionCount = n;
for (int s = 0; s < n; s++)
{
float t = s / (float)(n - 1);
Vector3 p = Vector3.Lerp(a, b, t);
// Jitter interior points only — endpoints stay anchored to the coil and enemy.
if (s != 0 && s != n - 1)
p += Random.insideUnitSphere * jitter;
lr.SetPosition(s, p);
}
}
}
}