using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
namespace TKS
{
///
/// IB(쿠로네코) 슬링샷 UI 이식 값 표(Resources/Skills/ib_ui.csv · C45) + 진행 중 캐릭터 연출(IbUnitMarks)이 쓰는 공용 도우미.
///
public static class IbUi
{
public const string TablePath = "Skills/ib_ui";
static Dictionary values;
public static void Reload()
{
values = null;
}
static void Ensure()
{
if (values != null) return;
values = new Dictionary();
var ta = Resources.Load(TablePath);
if (ta == null) return;
bool head = true;
foreach (var raw in ta.text.Replace("\r", "").Split('\n'))
{
var line = raw.TrimStart('');
if (line.Trim().Length == 0 || line.TrimStart().StartsWith("#")) continue;
if (head)
{
head = false;
continue;
}
var c = SkillVfx.SplitCsv(line);
if (c.Length >= 2) values[c[0].Trim()] = c[1].Trim();
}
}
public static string Str(string key, string def)
{
Ensure();
string v;
return values.TryGetValue(key, out v) && v.Length > 0 ? v : def;
}
public static float Num(string key, float def)
{
float f;
return float.TryParse(Str(key, ""), NumberStyles.Float, CultureInfo.InvariantCulture, out f) ? f : def;
}
/// IB 이펙트·UI 배율 = 이 게임 유닛 반지름 / IB 유닛 반지름.
public static float Scale(Unit u)
{
return u == null ? 1f : u.Radius / Mathf.Max(0.01f, Num("ib_unit_radius", 0.5f));
}
/// 진영 → IB 속성(water · tree · fire · dark · glow).
public static string Element(Unit u)
{
string camp = u != null && !string.IsNullOrEmpty(u.Camp) ? u.Camp.ToLowerInvariant() : "none";
return Str("element_" + camp, Str("element_none", "dark"));
}
/// IB 사본 하나 띄우기(유닛에 붙임 · 배율 · 렌더러 정렬).
public static GameObject Attach(string fxName, Unit u, Vector2 local, float scale, int order)
{
var prefab = SkillVfx.LoadFx(fxName);
if (prefab == null || u == null) return null;
var go = Object.Instantiate(prefab, u.transform);
go.name = fxName;
go.transform.localPosition = new Vector3(local.x, local.y, 0f);
go.transform.localRotation = Quaternion.identity;
var ls = u.transform.lossyScale;
go.transform.localScale = new Vector3(scale / Mathf.Max(0.001f, Mathf.Abs(ls.x)), scale / Mathf.Max(0.001f, Mathf.Abs(ls.y)), 1f);
foreach (var r in go.GetComponentsInChildren(true)) r.sortingOrder = order;
return go;
}
}
///
/// 슬링샷 진행 중 캐릭터 연출 — IB 이식(PD 2026-11-10 「선택된 캐릭터 주위 연출이 누락」):
/// 행동할 유닛(선택된 아군 · 행동 가능) = 발밑 도는 원(ef_target_circle · IB BottomPosition) + 몸 둘레 빛(ef_attack_ready_standard · IB CenterPosition) — IB ShowUserTurnMark ·
/// 조준 중(누름 ~ 놓음) = 모든 유닛에 속성 방울(ef_attribute_<속성> · 배율 0.9) — IB SetActiveBarrierVisualEffect.
///
public class IbUnitMarks : MonoBehaviour
{
Unit turnUnit;
readonly List turnFx = new List();
readonly List bubbles = new List();
public int BubbleCount
{
get { return bubbles.Count; }
}
public bool TurnMarkVisible
{
get { return turnFx.Count > 0; }
}
public void ShowTurn(Unit u)
{
if (u == turnUnit && turnFx.Count > 0) return;
Clear(turnFx);
turnUnit = u;
if (u == null) return;
float k = IbUi.Scale(u);
int order = Mathf.RoundToInt(IbUi.Num("order_ground", -8f));
Add(turnFx, IbUi.Attach("TKS_Fx_IB_TargetCircle", u, new Vector2(0f, -u.Radius * 0.55f), k, order)); // 발(아트 발 = 원 아래쪽 −0.55 R)
Add(turnFx, IbUi.Attach("TKS_Fx_IB_AttackReady", u, Vector2.zero, k, order));
}
public void ShowBubbles(IEnumerable units)
{
Clear(bubbles);
if (units == null) return;
int order = Mathf.RoundToInt(IbUi.Num("order_bubble", 12f));
float bs = IbUi.Num("barrier_scale", 0.9f);
foreach (var u in units)
{
if (u == null || !u.IsAlive) continue;
Add(bubbles, IbUi.Attach("TKS_Fx_IB_Attribute_" + IbUi.Element(u), u, Vector2.zero, IbUi.Scale(u) * bs, order));
}
}
public void HideBubbles()
{
Clear(bubbles);
}
static void Add(List list, GameObject go)
{
if (go != null) list.Add(go);
}
static void Clear(List list)
{
foreach (var g in list)
if (g != null) Destroy(g);
list.Clear();
}
void OnDestroy()
{
Clear(turnFx);
Clear(bubbles);
}
}
}