using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace TKS.OutGame { /// 영웅·병사 카드(2줄 · 기획 S12 v4) — 좌 초상(★ 는 초상 아래) · 1줄 = 병종 배지 · Lv · 출전 중 · 2줄 = 이름. 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, owned ? UiKit.Paper : new Color(0.86f, 0.84f, 0.8f), onTap); UiKit.Border(cell, deployed ? UiKit.Red : UiKit.Line, deployed ? 4f : 2f); var lv = s.Save.Level(h.id); var hs = s.Save.Hero(h.id); UiKit.Portrait("Face", cell.transform, h, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(12f, 8f), new Vector2(116f, 108f), false, 2f, new Color(0.9f, 0.86f, 0.8f)); int star = hs != null ? hs.star : h.rarity; if (UISkin.Icon("star_on") != null) UiKit.StarRow(cell.transform, star, star, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(14f, 2f), 24f); else UiKit.Label(cell.transform, UiKit.Stars(star, star), 22, UiKit.Star, TextAnchor.MiddleCenter, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(12f, 2f), new Vector2(116f, 30f)); UiKit.TypeBadge(cell.transform, h.type, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(142f, -16f), 40f); UiKit.Title(UiKit.Label(cell.transform, owned ? "Lv " + (lv != null ? lv.lv : 1) : "미보유", 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(204f, -12f), new Vector2(150f, 48f))); if (deployed) { var b = UiKit.Box("Deployed", cell.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-12f, -14f), new Vector2(124f, 44f), UiKit.Red); UiKit.Title(UiKit.FillLabel(b.transform, "출전 중", 24, UiKit.White), true); } if (hs != null) { var g = UiKit.Box("Grade", cell.transform, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-12f, 14f), new Vector2(56f, 48f), UiKit.GradeColor(hs.grade)); UiKit.GradeFrame(g, hs.grade); UiKit.Title(UiKit.FillLabel(g.transform, UiKit.GradeKo(hs.grade), 24, UiKit.White), true); } var name = UiKit.Title(UiKit.Label(cell.transform, h.nameKo, 44, owned ? UiKit.Ink : UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(142f, 12f), new Vector2(290f, 66f))); name.horizontalOverflow = HorizontalWrapMode.Overflow; if (owned && s.CanGrowAny(h.id) && UiKit.Icon(cell.transform, "new_dot", new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(14f, -10f), 34f) == null) UiKit.Dot("New", cell.transform, 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); 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