using UnityEngine; using UnityEngine.UI; namespace TKS.OutGame { /// /// 영웅 상세 정보 팝업(PD 2026-11-13 명장 등용 「상세 정보를 누르면 해당 영웅의 풀 성장 스펙 및 스킬 정보를 볼 수 있는 팝업」) — /// 머리(초상 · 이름 · 진영 · 병종 · ★(등급만 켜짐) · 전투력) → 풀 성장 능력치(HP · 공격력 · 방어력 = HeroRules.Compute(FullGrowth) · 비교 = 처음 얻었을 때) → /// 스킬 2행(액티브 · 패시브 = 아이콘 · 이름 · 한자 · 쿨 · 최고 레벨 · 설명). 부품·색 = 영웅 상세 화면(HeroDetailScreen) 것 그대로 · 보유하지 않은 영웅도 열린다. /// 값 = 표(hero_levels · promotion · evolve · hero_gear · skills) — 훈련·보물 효과는 유저마다 달라 넣지 않는다. /// public static class HeroInfoPopup { const float W = 860f; // 팝업 내용 칸 폭(ModalCustom 940 − 좌우 40) const float RowIcon = 126f; /// 마지막으로 연 팝업의 풀 성장 수치(테스트·캡처 로그). public static HeroRules.Stats LastStats { get; private set; } public static string LastHeroId { get; private set; } public static bool Show(OutGameRoot root, string heroId) { var s = root.Session; var db = s.Db; var h = s.Def(heroId); if (h == null) return false; var full = HeroRules.FullGrowth(db, h); var st = HeroRules.Compute(db, s.Cfg, h, full); var first = HeroRules.Compute(db, s.Cfg, h, HeroRules.NewHero(db, h, 0)); LastStats = st; LastHeroId = heroId; var c = root.ModalCustom("상세 정보", 1200f, new OutGameRoot.ModalButton("닫기", UiKit.Dark, null)); string g = h.isSoldier ? "white" : full.grade; // ① 머리 = 초상(최고 등급 색 틀) · 이름 · 한자 · 진영 배지 · 병종 · ★ · 전투력 UiKit.Portrait("Face", c, h, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, -4f), new Vector2(204f, 204f), false, 1f, UiKit.Col("grade_" + g + "_card_bg", UiKit.Coin)); var name = HeroDetailScreen.Txt(c, h.nameKo, 228f, 6f, 60, "white"); if (!string.IsNullOrEmpty(h.hanja)) HeroDetailScreen.Txt(c, h.hanja, 228f + name.preferredWidth + 14f, 32f, 30, "text_soft", TextAnchor.MiddleLeft, 0f, false, 0f, false); UiKit.DropShadow(UiKit.FactionBadge(c, h.faction, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(228f, -88f), 64f), 3f); HeroDetailScreen.Ico(c, UiKit.TypeSymbolId(h.type), 304f, 90f, 60f, 60f); HeroDetailScreen.Txt(c, UnitTypeInfo.Korean(h.type), 372f, 104f, 30, "text_type", TextAnchor.MiddleLeft, 0f, false, 0f, false); // 머리 별 = 그 영웅의 등급(배너·카드와 같은 수) · 풀 성장 ★ 까지 남은 칸 = 꺼진 별(QA r1 M6 「배너 ★3 ↔ 팝업 ★5」) int stars = h.isSoldier ? h.rarity : full.star; for (int i = 0; i < stars; i++) HeroDetailScreen.Ico(c, i < h.rarity ? "star_l" : "star_off", 228f + i * 50f, 160f, 48f, 50f, i < h.rarity ? 3f : 0f); var pw = HeroDetailScreen.Spr(c, "Power", "chip", W - 316f, 150f, 316f, 66f, "chip_dark").transform; HeroDetailScreen.Ico(pw, "sword", 14f, 4f, 58f, 58f); HeroDetailScreen.Txt(pw, "전투력", 78f, 21f, 26, "text_soft", TextAnchor.MiddleLeft, 0f, true, 0f, false); HeroDetailScreen.Txt(pw, st.power.ToString("N0"), 164f, 12f, 44, "white"); // ② 풀 성장 능력치 = 기준 줄 + 칸 3(큰 값 = 풀 성장 · 아래 = 처음 얻었을 때) float y = 236f; UiKit.SubTitle(c, "풀 성장 능력치", 0f, y, W, 34); string basis = h.isSoldier ? "Lv " + full.lv + " 기준" : "Lv " + full.lv + " · " + UiKit.GradeKo(full.grade) + " 등급 · ★" + full.star + " · 승급 장비 " + full.gearTotal + "개 기준"; HeroDetailScreen.Txt(c, basis, 0f, y + 52f, 26, "text_soft", TextAnchor.MiddleCenter, W, false, 0f, false); y += 100f; string[] names = { "HP", "공격력", "방어력" }; string[] icons = { "dom_stat_hp", "dom_stat_atk", "dom_stat_def" }; int[] now = { st.Hp, st.Atk, st.Def }; int[] was = { first.Hp, first.Atk, first.Def }; const float tw = 276f, th = 150f; for (int i = 0; i < 3; i++) { var tile = HeroDetailScreen.Spr(c, "Stat" + i, "panel12", i * (tw + 16f), y, tw, th, "grade_" + g + "_row").transform; HeroDetailScreen.Ico(tile, icons[i], 14f, 14f, 52f, 52f, 3f); HeroDetailScreen.Txt(tile, names[i], 76f, 22f, 30, "white", TextAnchor.MiddleLeft, 0f, true); HeroDetailScreen.Txt(tile, now[i].ToString("N0"), 0f, 62f, 48, "white", TextAnchor.MiddleCenter, tw); HeroDetailScreen.Txt(tile, "처음 " + was[i].ToString("N0"), 0f, 116f, 22, "text_soft", TextAnchor.MiddleCenter, tw, false, 0f, false); } y += th + 14f; HeroDetailScreen.Txt(c, "내정 훈련 · 보물 효과는 포함하지 않은 값", 0f, y, 22, "text_soft", TextAnchor.MiddleCenter, W, false, 0f, false); y += 46f; // ③ 스킬 2행(액티브 · 패시브) — 행 높이 = 설명 줄 수에 맞춤 UiKit.SubTitle(c, "스킬", 0f, y, W, 34); y += 58f; if (h.isSoldier) { HeroDetailScreen.Txt(c, "병사 유닛은 스킬이 없습니다", 0f, y + 10f, 28, "text_soft", TextAnchor.MiddleCenter, W, false, 0f, false); y += 70f; } else { string tk = UiKit.TypeKey(h.type); for (int i = 0; i < 2; i++) { var sk = SkillRules.Skill(db, h, i); if (sk == null) continue; var row = HeroDetailScreen.Spr(c, "Skill" + i, "panel12", 0f, y, W, 160f, "grade_" + g + "_row"); var rt = row.transform; string iconId = "skill_" + sk.id; bool hasIcon = UISkin.Theme != null && UISkin.Theme.IconEntryOf(iconId) != null; // 아이콘 = 영웅 상세와 같은 규칙(256² 사각 아이콘 + 병종 얇은 틀 · 아이콘이 없으면 두꺼운 틀 + 병종 기호) if (hasIcon) { HeroDetailScreen.Ico(rt, iconId, 14f + 10.34f, 16f + 10.34f, 105.33f, 105.33f); if (tk != null) HeroDetailScreen.Ico(rt, "skill_ring_" + tk, 14f, 16f, RowIcon, RowIcon, 4f); } else { if (tk != null) HeroDetailScreen.Ico(rt, "skill_" + tk + "_a", 14f, 16f, RowIcon, RowIcon, 4f); HeroDetailScreen.Ico(rt, UiKit.TypeSymbolId(h.type), 36f, 38f, 82f, 82f); } float x = 158f; var kind = HeroDetailScreen.Spr(rt, "Kind", "chip", x, 14f, 116f, 44f, i == 0 ? "chip_cool" : "chip_dark"); HeroDetailScreen.Txt(kind.transform, i == 0 ? "액티브" : "패시브", 0f, 9f, 26, "white", TextAnchor.MiddleCenter, 116f, true, 3f); if (i == 0 && sk.cooldown > 0) { var cool = HeroDetailScreen.Spr(rt, "Cool", "chip", x + 126f, 14f, 124f, 44f, "chip_dark"); HeroDetailScreen.Txt(cool.transform, "쿨 " + sk.cooldown + "턴", 0f, 9f, 26, "white", TextAnchor.MiddleCenter, 124f, false, 3f); } var lv = HeroDetailScreen.Spr(rt, "Lv", "chip", W - 176f, 14f, 160f, 44f, "chip_dark"); HeroDetailScreen.Txt(lv.transform, "최고 Lv " + SkillRules.Level(full, i), 0f, 9f, 26, "white", TextAnchor.MiddleCenter, 160f, true, 3f); var nm = HeroDetailScreen.Txt(rt, sk.nameKo, x, 66f, 38, "white"); if (!string.IsNullOrEmpty(sk.hanja)) HeroDetailScreen.Txt(rt, sk.hanja, x + nm.preferredWidth + 10f, 76f, 26, "grade_" + g + "_label", TextAnchor.MiddleLeft, 0f, false, 0f, false); var desc = HeroDetailScreen.Desc(rt, x, 118f, W - x - 20f); desc.fontSize = 24; desc.text = SkillInfoDb.DisplayDesc(sk); desc.verticalOverflow = VerticalWrapMode.Overflow; float dh = Mathf.Ceil(desc.preferredHeight); desc.rectTransform.sizeDelta = new Vector2(desc.rectTransform.sizeDelta.x, dh); float rh = Mathf.Max(160f, 118f + dh + 18f); row.rectTransform.sizeDelta = new Vector2(W, rh); y += rh + 14f; } } // 팝업 높이 = 내용에 맞춤(제목 띠 130 + 아래 버튼 줄 160) var panel = (RectTransform)c.parent; panel.sizeDelta = new Vector2(panel.sizeDelta.x, Mathf.Clamp(y + 300f, 600f, 1620f)); return true; } } }