using System;
using System.Collections;
using UnityEngine;
namespace TKS
{
/// 간단한 연출(이동 트윈·돌진·화살·피해 숫자). 코루틴 실행자는 BattleManager. 가상 실행(Sim.Active · 적 AI 예측) 중 = 이동·대기 즉시 · 팝업·터짐 없음.
public static class Fx
{
public static MonoBehaviour Runner;
public static Coroutine Run(IEnumerator routine)
{
if (Sim.Active)
{
Sim.Run(routine); // 가상 실행 = 그 자리에서 끝까지(병렬 밀기 등)
return null;
}
if (Runner == null) return null;
return Runner.StartCoroutine(routine);
}
public static IEnumerator Wait(float seconds)
{
if (Sim.Active) yield break;
float t = 0f;
while (t < seconds)
{
t += Time.deltaTime;
yield return null;
}
}
/// from → to 이동(기본 = 감속 · easeOut false = 등속 → 부딪히는 순간까지 속도를 유지). onDistance 에는 지금까지 지나온 거리를 넘긴다.
public static IEnumerator MoveAlong(Transform tr, Vector2 from, Vector2 to, float speed, Action onDistance, bool easeOut = true)
{
float total = Vector2.Distance(from, to);
if (total < 1e-4f) yield break;
if (Sim.Active)
{
tr.position = new Vector3(to.x, to.y, 0f);
if (onDistance != null) onDistance(total);
yield break;
}
float duration = Mathf.Max(0.12f, total / Mathf.Max(0.1f, speed));
float el = 0f;
while (el < duration)
{
el += Time.deltaTime;
float k = Mathf.Clamp01(el / duration);
float e = easeOut ? 1f - (1f - k) * (1f - k) : k;
var p = Vector2.Lerp(from, to, e);
tr.position = new Vector3(p.x, p.y, 0f);
if (onDistance != null) onDistance(e * total);
yield return null;
}
tr.position = new Vector3(to.x, to.y, 0f);
if (onDistance != null) onDistance(total);
}
/// 근접 공격 돌진(짧게 찌르고 복귀).
public static IEnumerator Lunge(Unit a, Vector2 toward)
{
if (Sim.Active) yield break;
Vector2 start = a.Position;
Vector2 dir = (toward - start);
if (dir.sqrMagnitude < 1e-4f) yield break;
dir.Normalize();
Vector2 peak = start + dir * 0.3f;
yield return MoveAlong(a.transform, start, peak, 6f, null);
yield return MoveAlong(a.transform, peak, start, 4f, null);
a.Position = start;
}
public static readonly Color ArrowColor = new Color(1f, 0.95f, 0.75f);
public static readonly Color StrategistShotColor = new Color(0.75f, 0.55f, 1f);
public static readonly Color SplashColor = new Color(0.7f, 0.45f, 1f, 0.45f);
public static readonly Color FriendlyFireColor = new Color(1f, 0.3f, 0.3f);
/// 화살(가늘고 긴 사각형) 날리기.
public static IEnumerator Arrow(Vector2 from, Vector2 to)
{
return Arrow(from, to, ArrowColor);
}
/// 투사체(가늘고 긴 사각형) 날리기 — 궁병 화살 · 책사 계략(색만 다름).
public static IEnumerator Arrow(Vector2 from, Vector2 to, Color color)
{
if (Sim.Active) yield break;
var go = new GameObject("Arrow");
var sr = go.AddComponent();
sr.sprite = ProceduralSprites.Square;
sr.color = color;
sr.sortingOrder = 8;
Vector2 d = to - from;
float ang = Mathf.Atan2(d.y, d.x) * Mathf.Rad2Deg;
go.transform.rotation = Quaternion.Euler(0f, 0f, ang);
go.transform.localScale = new Vector3(0.5f, 0.06f, 1f);
float dur = Mathf.Max(0.12f, d.magnitude / 14f);
float t = 0f;
while (t < dur)
{
t += Time.deltaTime;
var p = Vector2.Lerp(from, to, Mathf.Clamp01(t / dur));
go.transform.position = new Vector3(p.x, p.y, 0f);
yield return null;
}
UnityEngine.Object.Destroy(go);
}
/// 광역 터짐(반지름 radius 원이 짧게 커지며 사라짐 · 책사 광역).
public static void Burst(Vector2 center, float radius, Color color)
{
if (Sim.Active) return;
Run(BurstRoutine(center, radius, color));
}
static IEnumerator BurstRoutine(Vector2 center, float radius, Color color)
{
var sr = ProceduralSprites.NewSprite(null, "Burst", ProceduralSprites.Circle, color, radius * 2f, 7);
sr.transform.position = new Vector3(center.x, center.y, 0f);
float t = 0f;
const float dur = 0.35f;
while (t < dur)
{
t += Time.deltaTime;
float k = Mathf.Clamp01(t / dur);
float s = radius * 2f * (0.6f + 0.4f * k);
sr.transform.localScale = new Vector3(s, s, 1f);
var c = color;
c.a = color.a * (1f - k);
sr.color = c;
yield return null;
}
UnityEngine.Object.Destroy(sr.gameObject);
}
public static void Popup(Vector2 pos, string text, Color color)
{
if (Sim.Active) return;
Run(PopupRoutine(pos, text, color));
}
static IEnumerator PopupRoutine(Vector2 pos, string text, Color color)
{
var tm = TextUtil.CreateWorldText(null, text, 0.42f, color, 20);
tm.gameObject.name = "Popup";
var tr = tm.transform;
float t = 0f;
const float dur = 0.75f;
while (t < dur)
{
t += Time.deltaTime;
float k = Mathf.Clamp01(t / dur);
tr.position = new Vector3(pos.x, pos.y + 0.55f + 0.7f * k, 0f);
var c = color;
c.a = 1f - k * k;
tm.color = c;
yield return null;
}
UnityEngine.Object.Destroy(tm.gameObject);
}
}
}