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); } } }