using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace TKS.OutGame { /// /// 영웅·병사 카드(가로형 · UI 상업용 재설계 ② · 설계서 §3② · 팩 CardFrame01 5층 등급색) — 초상(뒤 빛) · 병종 원 · Lv 배지 · 세력 깃발 · 출전 꼬리 칩 · /// 아래 이름 띠(이름 · ★ · 등급 칩) · 미보유 = 빈 카드(문양 + 초상 어둡게). 목록 · 편성 보유 행 · 도감 공용. /// public static class HeroCards { public static Image Card(Transform content, OutGameSession s, HeroDb.Hero h, bool deployed, bool owned, System.Action onTap) { var cell = UiKit.Cell(content, "Card_" + h.id, Color.clear, onTap); cell.sprite = null; // 바닥 = 클릭 판(투명) · 그림은 층으로 var lv = s.Save.Level(h.id); var hs = s.Save.Hero(h.id); string g = h.isSoldier || hs == null || string.IsNullOrEmpty(hs.grade) ? "white" : hs.grade; string G = "grade_" + g + "_"; var t = cell.transform; if (owned) { UiKit.Layer(t, "Bg", "card_bg", UiKit.Col(G + "card_bg", UiKit.Paper)); UiKit.Layer(t, "Grad", "card_grad", UiKit.Col(G + "card_grad", UiKit.White), 0f, 6f, 0f, 0f); var glow = UiKit.Skin(UiKit.Rect("Glow", t, new Vector2(0f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(72f, 6f), new Vector2(190f, 190f)), "card_glow", UiKit.Col(G + "card_glow", UiKit.White)); glow.enabled = UISkin.Has("card_glow"); } else { UiKit.Layer(t, "Bg", "card_empty", UiKit.Col("card_empty", UiKit.Slot)); var pat = UiKit.Layer(t, "Pattern", "card_empty_pattern", UiKit.Col("card_empty_pattern", UiKit.Slot), 6f, 6f, 6f, 6f); pat.type = Image.Type.Tiled; } UiKit.Portrait("Face", t, h, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(12f, 6f), new Vector2(120f, 116f), false, 2f, Color.clear); if (!owned) UiKit.Paint(UiKit.Rect("Dim", t, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(12f, 6f), new Vector2(120f, 116f)), UiKit.Col("card_dim", UiKit.Dark), false); var band = UiKit.Rect("Band", t, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), Vector2.zero, new Vector2(0f, 62f)); band.anchorMin = new Vector2(0f, 0f); band.anchorMax = new Vector2(1f, 0f); band.sizeDelta = new Vector2(0f, 62f); UiKit.Skin(band, "card_bottom", UiKit.Col("card_bottom", UiKit.Dark)).enabled = UISkin.Has("card_bottom"); UiKit.Layer(t, "Border", "card_border", UiKit.Col(owned ? "card_border" : "card_empty_border", UiKit.Line)); int star = hs != null ? hs.star : h.rarity; UiKit.StarRow(t, star, star, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(16f, 8f), 24f); var name = UiKit.Title(UiKit.Label(t, h.nameKo, 40, owned ? UiKit.White : UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(142f, 4f), new Vector2(260f, 56f)), true); name.horizontalOverflow = HorizontalWrapMode.Overflow; // 위 줄: 병종 원 · Lv 배지 · 세력 깃발 · (출전 꼬리 칩) var ring = UiKit.Skin(UiKit.Rect("Type", t, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(142f, -12f), new Vector2(52f, 52f)), "dot", UiKit.White); ring.preserveAspect = true; var cls = UiKit.Icon(ring.transform, UiKit.TypeIconId(h.type), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 36f); if (cls == null) UiKit.Title(UiKit.FillLabel(ring.transform, UnitTypeInfo.Mark(h.type), 26, UiKit.White), true); if (owned) { var badge = UiKit.Icon(t, "lv_badge_s", new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(202f, -8f), 56f); var lt = UiKit.Title(UiKit.Label(badge != null ? badge.transform : t, (lv != null ? lv.lv : 1).ToString(), 26, UiKit.White, TextAnchor.MiddleCenter, badge != null ? new Vector2(0.5f, 0.5f) : new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), badge != null ? new Vector2(0f, 2f) : new Vector2(230f, -36f), new Vector2(56f, 40f)), true); lt.horizontalOverflow = HorizontalWrapMode.Overflow; } var flag = UiKit.Icon(t, UISkin.Icon("flag_" + h.faction) != null ? "flag_" + h.faction : "flag_other", new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(264f, -14f), 44f); UiKit.DropShadow(flag, 2f); if (deployed) { var tail = UiKit.Skin(UiKit.Rect("Deployed", t, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-10f, -10f), new Vector2(104f, 46f)), "tail", UiKit.Col("tail_red", UiKit.Red)); UiKit.Title(UiKit.FillLabel(tail.transform, "출전", 26, UiKit.White), true); } if (owned && hs != null && !h.isSoldier && UISkin.Has("para_" + g)) { var chip = UiKit.Skin(UiKit.Rect("Grade", t, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-14f, 11f), new Vector2(66f, 40f)), "para_" + g, UiKit.Col(G + "chip", UiKit.White)); UiKit.Title(UiKit.FillLabel(chip.transform, UiKit.GradeKo(g), 24, UiKit.White), true); } if (owned && s.CanGrowAny(h.id) && UiKit.Icon(t, "new_dot", new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(14f, -10f), 34f) == null) UiKit.Dot("New", t, new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(14f, -10f), 26f, UiKit.Red); return cell; } } /// 영웅 탭 = 보유 영웅·병사 목록(장수|병사 · 2줄 카드) → 탭 = 영웅 상세 · 도감. public class HeroListScreen : OutGameScreen { bool soldiers; RectTransform content; ScrollRect scroll; Button tabHero; Button tabSoldier; public bool ShowingSoldiers { get { return soldiers; } } public override void Build() { Tab = 0; Header("영웅", "", true); // 부제 삭제(설계서 §2-5) var body = Body(); tabHero = UiKit.Btn("TabHero", body, "장수", 36, UiKit.Red, UiKit.White, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -20f), new Vector2(220f, 84f), () => SetFilter(false)); tabSoldier = UiKit.Btn("TabSoldier", body, "병사", 36, UiKit.Paper, UiKit.Ink, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(258f, -20f), new Vector2(220f, 84f), () => SetFilter(true)); UiKit.Btn("Codex", body, "도감", 36, UiKit.Dark, UiKit.White, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-24f, -20f), new Vector2(220f, 84f), () => HeroDetailScreen.ShowCodex(Root)); content = UiKit.ScrollGrid("List", body, 24f, 24f, 124f, 16f, new Vector2(506f, 150f), new Vector2(20f, 18f), 2, out scroll); } public void SetFilter(bool soldier) { soldiers = soldier; Refresh(); } public override void Refresh() { tabHero.GetComponent().color = soldiers ? UiKit.Paper : UiKit.Red; UiKit.LabelOf(tabHero).color = soldiers ? UiKit.Ink : UiKit.White; tabSoldier.GetComponent().color = soldiers ? UiKit.Red : UiKit.Paper; UiKit.LabelOf(tabSoldier).color = soldiers ? UiKit.White : UiKit.Ink; UiKit.LabelOf(tabHero).text = "장수 " + S.Owned(false).Count; UiKit.LabelOf(tabSoldier).text = "병사 " + S.Owned(true).Count; UiKit.Clear(content); foreach (var h in S.Owned(soldiers)) { var id = h.id; HeroCards.Card(content, S, h, S.InActivePreset(id), true, () => Root.ShowHero(id)); } } public int CardCount { get { return content.childCount; } } } /// /// 영웅 상세(UI 상업용 재설계 ① · PD 2026-11-09 확정 목업 hero_detail.html = 좌표·부품·색 SOT · 본문 y = 목업 y − 224) — /// 등급 색 바탕(글로우 + 문양 · 화면 전체) · 전투력 칩 · 일러(좌우 밀기 = 영웅 전환) + 양옆 장비 6칸 · 레벨 행(레벨업 · 승급) · 이름 띠(세력 · 병종 · 등급 · ★) · /// 스킬 패널(액티브 · 패시브 · 행마다 레벨업) · 메뉴 4버튼(정보 = 상세정보 + 도감 · 진화 = 조각 팝업 · 각성 · 외형 = 준비 중). 색·스프라이트 = 테마 표(ui_theme.csv) id. /// public class HeroDetailScreen : OutGameScreen { const float SwipeMin = 80f; // 일러 밀기 = 가로 80 px 이상(기준 해상도 px) static readonly string[] MenuNames = { "정보", "진화", "각성", "외형" }; static readonly string[] MenuIcons = { "info", "star_l", "fire", "top" }; static readonly Vector2[] MenuIconSize = { new Vector2(26f, 60f), new Vector2(58f, 62f), new Vector2(64f, 50f), new Vector2(62f, 46f) }; string id; readonly List order = new List(); Image bgFill; Image bgGlow; Text ownedText; Text power; RectTransform art; float dragX; readonly GameObject[] slotRoot = new GameObject[6]; readonly Image[] slotGlow = new Image[6]; readonly Image[] slotBg = new Image[6]; readonly Image[] slotDeco = new Image[6]; readonly Image[] slotLight = new Image[6]; readonly Image[] slotIcon = new Image[6]; readonly Text[] slotName = new Text[6]; readonly Image[] slotCheck = new Image[6]; readonly Image[] slotBag = new Image[6]; Text lvBadge; Text lvText; Text expText; RectTransform expFill; Image levelImg; Text levelLabel; Text levelCost; Image goldIcon; Image promoteImg; Text promoteSub; Image bandImg; Image flagIcon; Image clsIcon; Image paraImg; Text gradeText; Text nameText; Text typeText; RectTransform starRow; Image panelImg; GameObject coolChip; Text coolText; readonly Image[] rowImg = new Image[2]; readonly Image[] skFrame = new Image[2]; readonly Image[] skCls = new Image[2]; readonly Text[] skName = new Text[2]; readonly Text[] skHanja = new Text[2]; readonly Text[] skDesc = new Text[2]; readonly Text[] skLv = new Text[2]; readonly Image[] skBtn = new Image[2]; readonly Text[] skBtnText = new Text[2]; readonly Text[] skBtnSub = new Text[2]; readonly GameObject[] skCost = new GameObject[2]; readonly Text[] skBooks = new Text[2]; readonly Text[] skGold = new Text[2]; Image evolveDot; public string HeroId { get { return id; } } public void Select(string heroId) { id = heroId; var h = S.Def(heroId); order.Clear(); if (h != null) foreach (var o in S.Owned(h.isSoldier)) order.Add(o.id); } // ---------- 목업 좌표 도우미(부모 왼쪽 위 기준 · y 아래로) ---------- static Color Col(string colorId) { return UISkin.Col(colorId, Color.white); } static RectTransform At(Transform p, string name, float x, float y, float w, float h) { return UiKit.Rect(name, p, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, -y), new Vector2(w, h)); } static Image Spr(Transform p, string name, string skin, float x, float y, float w, float h, string color, bool raycast = false) { return UiKit.Skin(At(p, name, x, y, w, h), skin, Col(color), raycast); } Image Press(Transform p, string name, string skin, float x, float y, float w, float h, string color, System.Action onClick) { var img = Spr(p, name, skin, x, y, w, h, color, true); img.gameObject.AddComponent