1017 lines
59 KiB
C#
1017 lines
59 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
namespace TKS.OutGame
|
||
{
|
||
/// <summary>
|
||
/// 영웅·병사 카드(가로형 · UI 상업용 재설계 ② · 설계서 §3② · 팩 CardFrame01 5층 등급색) — 초상(뒤 빛) · 병종 원 · Lv 배지 · 세력 깃발 · 출전 꼬리 칩 ·
|
||
/// 아래 이름 띠(이름 · ★ · 등급 칩) · 미보유 = 빈 카드(문양 + 초상 어둡게). 목록 · 편성 보유 행 · 도감 공용.
|
||
/// </summary>
|
||
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 배지 · 세력 깃발 · (출전 꼬리 칩)
|
||
// 병과 = 단색 심볼만(배경 틀 없음 · PD 2026-11-09)
|
||
var ring = UiKit.Rect("Type", t, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(142f, -12f), new Vector2(52f, 52f));
|
||
var cls = UiKit.Icon(ring, UiKit.TypeSymbolId(h.type), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 50f);
|
||
if (cls == null) UiKit.Title(UiKit.FillLabel(ring, 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;
|
||
}
|
||
UiKit.DropShadow(UiKit.FactionBadge(t, h.faction, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(264f, -12f), 46f), 2f); // 진영 원 배지(PD 2026-11-09)
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 영웅 목록 세로 카드(PD 2026-11-09 레퍼런스 = 팩 데모 Collection_List · 칸 320×431) — 등급색 바탕(마스크) · 그라데이션 · 일러 뒤 빛 · 일러 · 아래 어두운 띠 · 테두리 ·
|
||
/// 이름(왼쪽 위) · 병종 원 · 레벨 배지(왼쪽 아래 · 최고 = 노랑) · ★(오른쪽 아래) · 미보유 = 빈 카드(문양 + 얼굴 실루엣). 수치 = 팩 프리팹 RectTransform 그대로.
|
||
/// </summary>
|
||
public static class CollectionCards
|
||
{
|
||
public static readonly Vector2 CellSize = new Vector2(320f, 431f);
|
||
public static readonly Vector2 Spacing = new Vector2(22f, 20f);
|
||
/// <summary>크기 표 size_px(머리 크기 기준 표준 키)가 카드에서 차지할 px — 배율 = UnitPx ÷ size_px(모든 유닛 머리 크기 같음) · 머리 끝 위치(카드 위에서 px).</summary>
|
||
const float UnitPx = 360f; // 450 → 360 = 유닛 20 % 작게(PD 2026-11-09 「현재 비율보다 20 % 정도 작게」)
|
||
const float HeadTop = 50f;
|
||
/// <summary>걷는 유닛(보병·창병·궁병·책사) = 머리 끝을 더 내려 발이 카드 아래로 숨게(PD 2026-11-09).</summary>
|
||
const float FootDrop = 36f;
|
||
/// <summary>말 탄 유닛(크기 표 mounted) = 걷는 유닛보다 작게 · 말 얼굴이 카드 오른쪽 경계에 닿을 만큼(PD 2026-11-09 0.72 → 「조금 더 크게 · 내려도」).</summary>
|
||
const float MountedScale = 0.85f;
|
||
/// <summary>말 탄 유닛 머리 끝 내림(카드 px · PD 2026-11-09).</summary>
|
||
const float MountedDrop = 24f;
|
||
/// <summary>말 탄 유닛 전신(발 ~ 머리 끝) 높이 상한(카드 px) — 크기 표 실측이 튀는 그림(예 여포 size_px 449)도 다른 기병과 비슷하게.</summary>
|
||
const float MountedMaxBody = 460f;
|
||
/// <summary>눈 줄(카드 위에서 px · 크기 표 eye_up_px 가 있는 유닛) — 머리 끝 맞춤이면 관·투구가 낮은 유닛(전위·태사자)의 얼굴이 위로 뜸 →
|
||
/// 눈 높이를 맞춘다(PD 2026-11-10 「너무 위쪽에 등장하는 영웅은 내려」) · 값 = PD 확인 기존 20종의 머리 끝 맞춤 때 눈 높이 중앙값(걷는 216.5 · 기병 210.3).</summary>
|
||
const float EyeLine = 216f;
|
||
const float EyeLineMounted = 210f;
|
||
|
||
/// <param name="owned">false = 미보유(PD 2026-11-10 「유닛 목록에 장수들의 모습이 보이도록」) — 일러를 테마 card_locked_art 로 조금 어둡게 · 「미보유」 꼬리 칩 · 레벨 배지·성장 점 없음</param>
|
||
public static Image Card(Transform content, OutGameSession s, HeroDb.Hero h, bool deployed, System.Action onTap, bool owned = true)
|
||
{
|
||
var cell = UiKit.Cell(content, "Card_" + h.id, Color.clear, onTap);
|
||
cell.sprite = null; // 바닥 = 클릭 판(투명) · 그림은 층으로
|
||
var t = cell.transform;
|
||
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 bg = UiKit.Layer(t, "Bg", "card_bg", UiKit.Col(G + "card_bg", UiKit.Paper), 2f, 2f, 2f, 2f);
|
||
bg.gameObject.AddComponent<Mask>().showMaskGraphic = true;
|
||
var b = bg.transform;
|
||
UiKit.Layer(b, "Gradient", "card_grad", UiKit.Col(G + "card_grad", UiKit.White), 0f, 0f, 0f, 44.6f);
|
||
UiKit.Skin(UiKit.Rect("Glow", b, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(226f, 226f)), "card_glow", UiKit.Col(G + "card_glow", UiKit.White)).enabled = UISkin.Has("card_glow");
|
||
var sp = UiKit.ArtSprite(h.art);
|
||
if (sp != null)
|
||
{
|
||
// 머리 크기 통일(PD 2026-11-09 「캐릭터 크기가 동일하게 보이도록」) = 배율 UnitPx ÷ 크기 표 size_px(눈 간격 기준 · 말 탄 유닛은 탄 사람 기준) ·
|
||
// 머리 끝(발 + bodyHeightPx · 무기 제외)을 카드 위 HeadTop 에 맞춤 → 다리·말은 아래 띠 뒤로 잘림(레퍼런스 흉상 느낌) · 메타가 없으면 칸 맞춤
|
||
var a = UnitArt.Load(h.art);
|
||
var m = a != null ? a.Meta : null;
|
||
float body = a != null ? a.BodyPx : 0f;
|
||
float size = a != null && a.SizePx > 0.5f ? a.SizePx : body;
|
||
Image art;
|
||
if (body > 0.5f && m.pivot != null && m.pivot.Length > 1)
|
||
{
|
||
float hgt = sp.rect.height;
|
||
bool mounted = a.Mounted;
|
||
float k = UnitPx / size * (mounted ? MountedScale : 1f);
|
||
if (mounted) k = Mathf.Min(k, MountedMaxBody / body);
|
||
float pivotY, y;
|
||
if (a.EyeUpPx > 0.5f)
|
||
{
|
||
pivotY = Mathf.Clamp01((m.pivot[1] * hgt + a.EyeUpPx) / hgt); // 눈 줄 = 프레임 아래에서의 비율
|
||
y = mounted ? EyeLineMounted : EyeLine;
|
||
}
|
||
else
|
||
{
|
||
pivotY = Mathf.Clamp01((m.pivot[1] * hgt + body) / hgt); // 머리 끝 = 프레임 아래에서의 비율(눈 높이 없는 그림)
|
||
y = HeadTop + (mounted ? MountedDrop : FootDrop);
|
||
}
|
||
art = UiKit.Rect("Character", b, new Vector2(0.5f, 1f), new Vector2(0.5f, pivotY), new Vector2(0f, -y), sp.rect.size * k).gameObject.AddComponent<Image>();
|
||
}
|
||
else
|
||
{
|
||
art = UiKit.Rect("Character", b, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -56f), new Vector2(376f, 368f)).gameObject.AddComponent<Image>();
|
||
art.preserveAspect = true;
|
||
}
|
||
art.sprite = sp;
|
||
art.raycastTarget = false;
|
||
if (!owned) art.color = UiKit.Col("card_locked_art", UiKit.InkSoft);
|
||
}
|
||
else
|
||
{
|
||
var dot = UiKit.Dot("Shape", b, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -10f), 200f, UiKit.TypeColor(h.type));
|
||
UiKit.Title(UiKit.FillLabel(dot.transform, UnitTypeInfo.Mark(h.type), 100, UiKit.White), true);
|
||
}
|
||
var bottom = UiKit.Rect("Bottom", b, new Vector2(0.5f, 0f), new Vector2(0.5f, 0.5f), new Vector2(0f, 21.7f), new Vector2(0f, 91.5f));
|
||
bottom.anchorMin = new Vector2(0f, 0f);
|
||
bottom.anchorMax = new Vector2(1f, 0f);
|
||
bottom.sizeDelta = new Vector2(52.6f, 91.5f);
|
||
UiKit.Skin(bottom, "card_bottom", UiKit.Col("card_bottom", UiKit.Dark)).enabled = UISkin.Has("card_bottom");
|
||
UiKit.Layer(t, "Border", "card_border", UiKit.Col("card_border", UiKit.Line));
|
||
|
||
// ★(오른쪽 아래 · 42×45 · 28 간격 겹침)
|
||
int star = hs != null ? hs.star : h.rarity;
|
||
for (int i = star - 1; i >= 0; i--)
|
||
UiKit.Icon(t, "star_on", new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(-15.5f - i * 28f, 64.8f), 44f);
|
||
// 레벨 배지(왼쪽 아래 · 최고 레벨 = 노랑 · 미보유 = 없음)
|
||
int lvn = lv != null ? lv.lv : 1;
|
||
bool maxLv = s.Db.Levels != null && lvn >= s.Db.Levels.MaxLevel;
|
||
var badge = owned ? UiKit.Icon(t, maxLv ? "lv_badge_max" : "lv_badge_s", new Vector2(0f, 0f), new Vector2(0.5f, 0.5f), new Vector2(33.9f, 36.7f), 79f) : null;
|
||
if (badge != null)
|
||
{
|
||
var bt = UiKit.Title(UiKit.Label(badge.transform, lvn.ToString(), 30, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(1f, -3f), new Vector2(74f, 50f)), true);
|
||
bt.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||
}
|
||
// PD 커스터마이징(2026-11-09): 왼쪽 위 = 진영 깃발 · 그 오른쪽 = 이름(45 · 긴 이름 자동 축소) · 깃발 아래(왼쪽 가장자리) = 출전 · 오른쪽 위 = 병종 원(#35374E 58×60)
|
||
// 진영 = 원 배지(위·촉·오·군 · PD 2026-11-09 · 구 세력 깃발 대체)
|
||
UiKit.DropShadow(UiKit.FactionBadge(t, h.faction, new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(44f, -42f), 62f), 3f);
|
||
var name = UiKit.Title(UiKit.Label(t, h.nameKo, 45, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(80f, -40.5f), new Vector2(172f, 66f)), true);
|
||
name.resizeTextForBestFit = true;
|
||
name.resizeTextMinSize = 26;
|
||
name.resizeTextMaxSize = 45;
|
||
if (deployed || !owned)
|
||
{
|
||
var tail = UiKit.Skin(UiKit.Rect(owned ? "Deployed" : "Locked", t, new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(10f, -96f), new Vector2(owned ? 84f : 104f, 38f)), "tail",
|
||
owned ? UiKit.Col("tail_red", UiKit.Red) : UiKit.Col("tail_locked", UiKit.Dark));
|
||
UiKit.Title(UiKit.FillLabel(tail.transform, owned ? "출전" : "미보유", 24, UiKit.White), true);
|
||
}
|
||
// 병과 = 단색 심볼만(배경 틀 없음 · PD 2026-11-09 「심볼은 배경 프레임 없이 단색」)
|
||
var role = UiKit.Rect("Role", t, new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-42f, -42f), new Vector2(60f, 60f));
|
||
var sym = UiKit.Icon(role, UiKit.TypeSymbolId(h.type), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 60f);
|
||
if (sym == null) UiKit.Title(UiKit.FillLabel(role, UnitTypeInfo.Mark(h.type), 28, UiKit.White), true);
|
||
if (owned && s.CanGrowAny(h.id)) UiKit.Icon(t, "new_dot", new Vector2(0f, 0f), new Vector2(0.5f, 0.5f), new Vector2(64f, 70f), 30f); // 성장 가능 = 레벨 배지 오른쪽 위
|
||
return cell;
|
||
}
|
||
|
||
/// <summary>원래 크기(320×431) 카드를 holder 가운데에 배율 k 로 넣는다(편성 자리 · 편성 보유 목록 · 영웅 목록 미보유 = owned false).</summary>
|
||
public static Image CardIn(Transform holder, OutGameSession s, HeroDb.Hero h, bool deployed, System.Action onTap, float k, bool owned = true)
|
||
{
|
||
return Fit(Card(holder, s, h, deployed, onTap, owned), k);
|
||
}
|
||
|
||
/// <summary>미보유·도감 안내 한 줄(이름(한자) · 병종 · ★ · 보유/미보유).</summary>
|
||
public static string InfoText(HeroDb.Hero h, bool own)
|
||
{
|
||
return h.nameKo + (string.IsNullOrEmpty(h.hanja) ? "" : "(" + h.hanja + ")") + " · " + UnitTypeInfo.Korean(h.type) + " · ★" + h.rarity +
|
||
(h.isSoldier ? " · 병사 유닛" : "") + (own ? " · 보유" : " · 미보유 — 거래 > 뽑기");
|
||
}
|
||
|
||
public static Image EmptyIn(Transform holder, float k)
|
||
{
|
||
return Fit(Empty(holder), k);
|
||
}
|
||
|
||
static Image Fit(Image card, float k)
|
||
{
|
||
var rt = card.rectTransform;
|
||
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0.5f);
|
||
rt.pivot = new Vector2(0.5f, 0.5f);
|
||
rt.sizeDelta = CellSize;
|
||
rt.anchoredPosition = Vector2.zero;
|
||
rt.localScale = new Vector3(k, k, 1f);
|
||
return card;
|
||
}
|
||
|
||
/// <summary>빈 카드(미보유 자리) = 어두운 바탕 + 문양 + 테두리 + 오른쪽 아래 얼굴 실루엣(팩 CardFrame01_Empty).</summary>
|
||
public static Image Empty(Transform content)
|
||
{
|
||
var cell = UiKit.Cell(content, "Empty", Color.clear, null);
|
||
cell.sprite = null;
|
||
var t = cell.transform;
|
||
// 팩 CardFrame01_Empty 그대로: Bg(#303146 · 마스크) 안에 Bg_d_Pattern 타일(#2D2F42) → 테두리 → 얼굴
|
||
var bg = UiKit.Layer(t, "Bg", "card_bg", UiKit.Col("card_empty", UiKit.Slot), 2f, 2f, 2f, 2f);
|
||
bg.gameObject.AddComponent<Mask>().showMaskGraphic = true;
|
||
var pat = UiKit.Layer(bg.transform, "Pattern", "card_empty_pattern", UiKit.Col("card_empty_pattern", UiKit.Slot));
|
||
pat.type = Image.Type.Tiled;
|
||
pat.pixelsPerUnitMultiplier = 1f;
|
||
UiKit.Layer(t, "Border", "card_border", UiKit.Col("card_empty_border", UiKit.Line));
|
||
var face = UiKit.Icon(t, "face", new Vector2(1f, 0f), new Vector2(0.5f, 0.5f), new Vector2(-87f, 90.8f), 127f);
|
||
if (face != null) face.color = UiKit.Col("card_empty_face", UiKit.Dark);
|
||
return cell;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 영웅 탭 = 영웅 목록(PD 2026-11-09 레퍼런스 = 팩 Collection_List) — 헤더(제목 · 장수/병사 고르기 ▼ · 사선 칸 「보유 / 전체」) ·
|
||
/// 세로 카드(보유 = 레벨 높은 순 → 탭 = 영웅 상세) + 미보유 = 그 영웅 카드(조금 어둡게 · 「미보유」 · 탭 = 획득 안내 · PD 2026-11-10).
|
||
/// </summary>
|
||
public class HeroListScreen : OutGameScreen
|
||
{
|
||
bool soldiers;
|
||
RectTransform content;
|
||
ScrollRect scroll;
|
||
Text filterText;
|
||
Text countText;
|
||
int ownedCards;
|
||
int lockedCards;
|
||
UiAutoGrid autoGrid;
|
||
|
||
public bool ShowingSoldiers
|
||
{
|
||
get { return soldiers; }
|
||
}
|
||
|
||
public override void Build()
|
||
{
|
||
Tab = 0;
|
||
// 바탕 = 진남(레퍼런스 실측) + 재화 바 뒤 띠
|
||
UiKit.Paint(UiKit.Fill("ListBg", Rt), UiKit.Col("list_bg", UiKit.Bg), false);
|
||
UiKit.Paint(UiKit.TopBand("ListTop", Rt, 0f, OutGameRoot.TopBarH), UiKit.Col("band_top", UiKit.Bg), false);
|
||
var head = Header("영웅", "", true);
|
||
|
||
// 오른쪽 사선 칸(보유 / 전체) — 팩 PanelFrame03_Oblique #D8E8FF 239 폭
|
||
var ob = UiKit.Rect("Oblique", head, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), Vector2.zero, new Vector2(239f, OutGameRoot.HeaderH - 7f));
|
||
UiKit.Skin(ob, "oblique", UiKit.Col("oblique", UiKit.White));
|
||
countText = UiKit.Title(UiKit.Label(ob, "", 52, UiKit.Col("count_text", UiKit.Dark), TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(12f, 0f), new Vector2(220f, 80f)));
|
||
countText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||
|
||
// 장수 / 병사 고르기(팩 「Property ▼」 자리 · 탭 = 전환)
|
||
var pick = UiKit.Rect("Filter", head, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-252f, 0f), new Vector2(240f, 90f));
|
||
var hit = pick.gameObject.AddComponent<Image>();
|
||
hit.color = Color.clear;
|
||
pick.gameObject.AddComponent<Button>().onClick.AddListener(() => SetFilter(!soldiers));
|
||
filterText = UiKit.Title(UiKit.Label(pick, "", 38, UiKit.InkSoft, TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-52f, 2f), new Vector2(180f, 70f)));
|
||
var arrow = UiKit.Icon(pick, "arrow_down", new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-6f, 0f), 38f);
|
||
UiKit.DropShadow(arrow, 3f);
|
||
|
||
// 본문 = 화면 폭 전체(해상도 대응 · PD 2026-11-09) · 열 수·카드 배율 = UiAutoGrid(기준 1080 = 4열 · 좁으면 3 · 넓으면 5 이상)
|
||
var body = UiKit.Fill("ListBody", Rt, 0f, 0f, OutGameRoot.TopBarH + OutGameRoot.HeaderH, OutGameRoot.TabBarH);
|
||
content = UiKit.ScrollGrid("List", body, 24f, 24f, 20f, 0f, CollectionCards.CellSize, CollectionCards.Spacing, 4, out scroll);
|
||
autoGrid = content.gameObject.AddComponent<UiAutoGrid>();
|
||
autoGrid.design = CollectionCards.CellSize;
|
||
}
|
||
|
||
/// <summary>열 수(해상도 대응 · 테스트·캡처).</summary>
|
||
public int Columns
|
||
{
|
||
get { return autoGrid != null ? autoGrid.Columns : 0; }
|
||
}
|
||
|
||
/// <summary>격자 칸(holder) — 원래 크기 카드를 UiAutoGrid 배율로 넣는다.</summary>
|
||
Transform Slot()
|
||
{
|
||
var go = new GameObject("Slot", typeof(RectTransform));
|
||
go.transform.SetParent(content, false);
|
||
return go.transform;
|
||
}
|
||
|
||
public void SetFilter(bool soldier)
|
||
{
|
||
soldiers = soldier;
|
||
Refresh();
|
||
}
|
||
|
||
public override void Refresh()
|
||
{
|
||
filterText.text = soldiers ? "병사" : "장수";
|
||
UiKit.Clear(content);
|
||
var owned = new List<HeroDb.Hero>(S.Owned(soldiers));
|
||
// 보유 = 레벨 높은 순 → ★ 높은 순
|
||
owned.Sort((a, b) =>
|
||
{
|
||
int la = S.Save.Level(a.id) != null ? S.Save.Level(a.id).lv : 1, lb = S.Save.Level(b.id) != null ? S.Save.Level(b.id).lv : 1;
|
||
if (la != lb) return lb.CompareTo(la);
|
||
var ha = S.Save.Hero(a.id);
|
||
var hb = S.Save.Hero(b.id);
|
||
return (hb != null ? hb.star : b.rarity).CompareTo(ha != null ? ha.star : a.rarity);
|
||
});
|
||
ownedCards = 0;
|
||
foreach (var h in owned)
|
||
{
|
||
var id = h.id;
|
||
CollectionCards.CardIn(Slot(), S, h, S.InActivePreset(id), () => Root.ShowHero(id), autoGrid.Scale);
|
||
ownedCards++;
|
||
}
|
||
// 미보유 = 빈 카드 대신 그 영웅 카드(일러 조금 어둡게 · 「미보유」 · 탭 = 획득 안내) · ★ 높은 순 → 표 순서
|
||
// PD 2026-11-10 「왜 유닛 목록에 장수들의 모습이 보이지 않는거지? 이번에 추가된 리소스가 반영되도록」
|
||
// (상세 화면은 레벨·세이브가 있는 보유 영웅 전제라 미보유는 열지 않는다)
|
||
var locked = new List<HeroDb.Hero>();
|
||
var all = S.Db.Heroes.heroes;
|
||
foreach (var h in all)
|
||
if (h.isSoldier == soldiers && !S.Save.Owns(h.id)) locked.Add(h);
|
||
locked.Sort((a, b) => a.rarity != b.rarity ? b.rarity.CompareTo(a.rarity) : all.IndexOf(a).CompareTo(all.IndexOf(b)));
|
||
lockedCards = 0;
|
||
foreach (var h in locked)
|
||
{
|
||
var hero = h;
|
||
CollectionCards.CardIn(Slot(), S, h, false, () => Root.Toast(CollectionCards.InfoText(hero, false)), autoGrid.Scale, false);
|
||
lockedCards++;
|
||
}
|
||
countText.text = owned.Count + "/" + (owned.Count + locked.Count);
|
||
autoGrid.Apply();
|
||
}
|
||
|
||
/// <summary>보유 카드 수(미보유 제외).</summary>
|
||
public int CardCount
|
||
{
|
||
get { return ownedCards; }
|
||
}
|
||
|
||
/// <summary>미보유 영웅 카드 수(그림 카드 · PD 2026-11-10).</summary>
|
||
public int LockedCount
|
||
{
|
||
get { return lockedCards; }
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 영웅 상세(UI 상업용 재설계 ① · PD 2026-11-09 확정 목업 hero_detail.html = 좌표·부품·색 SOT · 본문 y = 목업 y − 224) —
|
||
/// 등급 색 바탕(글로우 + 문양 · 화면 전체) · 전투력 칩 · 일러(좌우 밀기 = 영웅 전환) + 양옆 장비 6칸 · 레벨 행(레벨업 · 승급) · 이름 띠(세력 · 병종 · 등급 · ★) ·
|
||
/// 스킬 패널(액티브 · 패시브 · 행마다 레벨업) · 메뉴 4버튼(정보 = 상세정보 + 도감 · 진화 = 조각 팝업 · 각성 · 외형 = 준비 중). 색·스프라이트 = 테마 표(ui_theme.csv) id.
|
||
/// </summary>
|
||
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<string> order = new List<string>();
|
||
|
||
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;
|
||
RectTransform factionRoot;
|
||
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<Button>().onClick.AddListener(() => onClick());
|
||
return img;
|
||
}
|
||
|
||
static Image Ico(Transform p, string icon, float x, float y, float w, float h, float shadow = 0f)
|
||
{
|
||
var img = At(p, "Icon", x, y, w, h).gameObject.AddComponent<Image>();
|
||
img.preserveAspect = true;
|
||
img.raycastTarget = false;
|
||
SetIcon(img, icon, 1f);
|
||
if (shadow > 0f) Drop(img, shadow);
|
||
return img;
|
||
}
|
||
|
||
/// <summary>테마 아이콘(팩 임시 = 표 tint · 신규 제작 = 원색) · alpha = 곱.</summary>
|
||
static void SetIcon(Image img, string icon, float alpha)
|
||
{
|
||
var e = UISkin.Theme != null && icon != null ? UISkin.Theme.IconEntryOf(icon) : null;
|
||
img.sprite = e != null ? e.Sprite : null;
|
||
var c = e != null && e.samguk == null ? e.tint : Color.white;
|
||
c.a *= alpha;
|
||
img.color = c;
|
||
img.enabled = img.sprite != null;
|
||
}
|
||
|
||
static void Drop(Graphic g, float d)
|
||
{
|
||
var s = g.gameObject.AddComponent<Shadow>();
|
||
s.effectColor = Col("icon_shadow");
|
||
s.effectDistance = new Vector2(0f, -d);
|
||
}
|
||
|
||
/// <summary>목업 text() — y = 글자 윗변 · title = Sen(+ 한글 폴백) · false = 맑은 고딕 · stroke < 0 = 기본(크기 × 0.06) · 0 = 없음 · 그 밖 = 목업 px(절반).</summary>
|
||
static Text Txt(Transform p, string s, float x, float y, int size, string color, TextAnchor align = TextAnchor.MiddleLeft, float w = 0f, bool title = true, float stroke = -1f, bool shadow = true)
|
||
{
|
||
float bw = w > 0f ? w : 900f;
|
||
float px = align == TextAnchor.MiddleCenter ? 0.5f : align == TextAnchor.MiddleRight ? 1f : 0f;
|
||
var rt = UiKit.Rect("Text", p, new Vector2(0f, 1f), new Vector2(px, 0.5f), new Vector2(x + bw * px, -(y + size * 0.5f)), new Vector2(bw, size * 1.6f));
|
||
var t = rt.gameObject.AddComponent<Text>();
|
||
t.font = title ? UISkin.TitleFont : TextUtil.Font;
|
||
t.fontSize = size;
|
||
t.fontStyle = FontStyle.Bold;
|
||
t.alignment = align;
|
||
t.color = Col(color);
|
||
t.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||
t.verticalOverflow = VerticalWrapMode.Overflow;
|
||
t.supportRichText = true;
|
||
t.raycastTarget = false;
|
||
t.text = s;
|
||
if (stroke != 0f) UISkin.Stroke(t, Col("stroke"), stroke < 0f ? Mathf.Max(2f, size * 0.06f) : stroke * 0.5f);
|
||
if (shadow)
|
||
{
|
||
var sh = t.gameObject.AddComponent<Shadow>();
|
||
sh.effectColor = Col("text_shadow");
|
||
sh.effectDistance = new Vector2(0f, -Mathf.Round(size * 0.07f));
|
||
}
|
||
return t;
|
||
}
|
||
|
||
static Text Desc(Transform p, float x, float y, float w)
|
||
{
|
||
var t = Txt(p, "", x, y, 26, "text_desc", TextAnchor.UpperLeft, w, false, 0f, false);
|
||
var rt = t.rectTransform;
|
||
rt.pivot = new Vector2(0f, 1f);
|
||
rt.anchoredPosition = new Vector2(x, -y);
|
||
rt.sizeDelta = new Vector2(w, 70f);
|
||
t.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||
t.lineSpacing = 1.05f;
|
||
return t;
|
||
}
|
||
|
||
static void PlaceX(Graphic g, float x)
|
||
{
|
||
var rt = g.rectTransform;
|
||
rt.anchoredPosition = new Vector2(x, rt.anchoredPosition.y);
|
||
}
|
||
|
||
// ---------- 화면 ----------
|
||
|
||
public override void Build()
|
||
{
|
||
Tab = 0;
|
||
// ① 바탕(화면 전체 · 재화 바·헤더 뒤까지) = 등급 색 + 세로 글로우 + 문양 타일
|
||
bgFill = UiKit.Fill("GradeBg", Rt).gameObject.AddComponent<Image>();
|
||
bgFill.raycastTarget = false;
|
||
bgGlow = UiKit.Skin(UiKit.Fill("GradeGlow", Rt), "bg_glow", Color.white);
|
||
bgGlow.enabled = UISkin.Has("bg_glow");
|
||
var pat = UiKit.Skin(UiKit.Fill("GradePattern", Rt), "bg_pattern", Col("grade_pattern"));
|
||
pat.type = Image.Type.Tiled;
|
||
pat.enabled = UISkin.Has("bg_pattern");
|
||
|
||
var head = Header("영웅", "", true);
|
||
var own = UiKit.Rect("Owned", head, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-24f, 0f), new Vector2(244f, 62f));
|
||
UiKit.Skin(own, "chip", Col("chip_dark"));
|
||
ownedText = Txt(own, "", 0f, 15f, 32, "white", TextAnchor.MiddleCenter, 244f);
|
||
|
||
// 본문 = 목업 1080 폭 틀(화면이 낮아 본문이 줄면서 가로로 늘어나도 가운데 고정)
|
||
var body = UiKit.Rect("Mock", Body(), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(OutGameRoot.Reference.x, OutGameRoot.BodyH));
|
||
|
||
// ② 일러 = 빛 원 + 발밑 그림자 + 그림(좌우 밀기 = 이전/다음 영웅)
|
||
Spr(body, "ArtGlow", "glow_circle", 210f, 14f, 660f, 660f, "hero_glow");
|
||
Spr(body, "ArtShadow", "oval_shadow", 310f, 550f, 460f, 64f, "hero_shadow");
|
||
art = At(body, "Art", 260f, 94f, 560f, 500f);
|
||
art.gameObject.AddComponent<Image>().color = Color.clear;
|
||
var drag = art.gameObject.AddComponent<TapDrag>();
|
||
drag.OnDragStart = e => dragX = 0f;
|
||
drag.OnDragMove = e => dragX += e.delta.x;
|
||
drag.OnDragEnd = e => Swipe(dragX / ScaleOf(art));
|
||
|
||
// 전투력 칩(두 장비 열 사이 위)
|
||
var pw = Spr(body, "Power", "chip", 380f, 24f, 320f, 70f, "chip_dark").transform;
|
||
Spr(pw, "Line", "chip_line", 6f, 6f, 308f, 58f, "chip_line");
|
||
Ico(pw, "sword", 18f, 4f, 62f, 62f);
|
||
Txt(pw, "전투력", 88f, 23f, 28, "text_soft", TextAnchor.MiddleLeft, 0f, true, 0f, false);
|
||
power = Txt(pw, "", 180f, 11f, 50, "white");
|
||
|
||
// ◀ ▶ = 장비 칸 바깥(장비 열 세로 가운데)
|
||
Arrow(body, "Prev", "arrow_prev", 10f, -1);
|
||
Arrow(body, "Next", "arrow_next", 1026f, 1);
|
||
|
||
// 장비 6칸(182×178 · ItemFrame 5층 · 장비 등급 색) — 탭 = 장착 · 필요 장비
|
||
for (int i = 0; i < 6; i++)
|
||
{
|
||
int k = i;
|
||
var s = At(body, "Slot" + i, i < 3 ? 66f : 832f, 24f + (i % 3) * 206f, 182f, 178f);
|
||
slotRoot[i] = s.gameObject;
|
||
slotGlow[i] = Spr(s, "Glow", "item_glow", -8f, -8f, 198f, 194f, "white");
|
||
slotBg[i] = Spr(s, "Bg", "item_bg", 0f, 0f, 182f, 178f, "white", true);
|
||
slotDeco[i] = Spr(s, "Deco", "item_deco", 4f, 4f, 174f, 170f, "white");
|
||
slotLight[i] = Spr(s, "Light", "item_light", 8f, 6f, 166f, 82f, "white");
|
||
Spr(s, "Border", "item_border", 0f, 0f, 182f, 178f, "gear_border");
|
||
slotIcon[i] = Ico(s, null, 41f, 17f, 100f, 100f, 5f);
|
||
Spr(s, "Plate", "plate12", 10f, 130f, 162f, 38f, "gear_plate");
|
||
slotName[i] = Txt(s, "", 10f, 135f, 26, "white", TextAnchor.MiddleCenter, 162f, false, 0f, false);
|
||
slotCheck[i] = Ico(s, "check", 138f, -12f, 54f, 52f, 3f);
|
||
slotBag[i] = Ico(s, "up_green", 142f, -14f, 48f, 56f, 3f);
|
||
s.gameObject.AddComponent<Button>().onClick.AddListener(() => OnSlot(k));
|
||
}
|
||
|
||
// ③ 레벨 행 = 배지 + 레벨 바 · 레벨업(녹 Primary + 골드) · 승급(하늘)
|
||
Ico(body, "lv_badge", 30f, 650f, 92f, 96f, 3f);
|
||
lvBadge = Txt(body, "", 30f, 674f, 46, "white", TextAnchor.MiddleCenter, 92f);
|
||
lvText = Txt(body, "", 132f, 648f, 34, "white");
|
||
expText = Txt(body, "", 400f, 654f, 28, "text_desc", TextAnchor.MiddleRight, 190f, false, 3f);
|
||
Spr(body, "LvBar", "lv_bg", 116f, 694f, 478f, 54f, "white");
|
||
expFill = (RectTransform)Spr(body, "LvFill", "lv_fill", 126f, 700f, 458f, 38f, "lv_fill").transform;
|
||
levelImg = Press(body, "LevelUp", "btn_l_green", 612f, 640f, 208f, 112f, "white", OnLevelUp);
|
||
levelLabel = Txt(levelImg.transform, "레벨업", 0f, 16f, 38, "text_on_green", TextAnchor.MiddleCenter, 208f, false, 0f, false);
|
||
goldIcon = Ico(levelImg.transform, "cur_gold", 48f, 56f, 40f, 40f);
|
||
levelCost = Txt(levelImg.transform, "", 92f, 60f, 32, "text_on_green", TextAnchor.MiddleLeft, 0f, true, 0f, false);
|
||
promoteImg = Press(body, "Promote", "btn_sky", 834f, 640f, 222f, 112f, "white", OnPromote);
|
||
Txt(promoteImg.transform, "승급", 0f, 14f, 38, "white", TextAnchor.MiddleCenter, 222f, false);
|
||
promoteSub = Txt(promoteImg.transform, "", 0f, 60f, 28, "white", TextAnchor.MiddleCenter, 222f, false, 3f);
|
||
|
||
// ④ 이름 띠 = 어두운 띠 + 세력 깃발 · 병종 원 · 등급 칩 · 이름 · 병종 · ★
|
||
bandImg = Spr(body, "NameBand", "card", 24f, 776f, 1032f, 114f, "white");
|
||
Spr(body, "BandLight", "band_light", 40f, 782f, 1000f, 12f, "band_light");
|
||
factionRoot = UiKit.At(body, "Faction", 44f, 789f, 88f, 88f); // 진영 원 배지 자리(PD 2026-11-09 · 구 세력 깃발)
|
||
clsIcon = Ico(body, null, 140f, 794f, 78f, 78f); // 병과 = 평면 심볼만(배경 틀 · 그림자 없음 · PD 2026-11-09)
|
||
paraImg = Spr(body, "Grade", "para_green", 232f, 805f, 96f, 56f, "white");
|
||
gradeText = Txt(body, "", 232f, 811f, 38, "white", TextAnchor.MiddleCenter, 96f);
|
||
nameText = Txt(body, "", 344f, 797f, 66, "white");
|
||
typeText = Txt(body, "", 500f, 819f, 30, "text_type", TextAnchor.MiddleLeft, 0f, false, 0f, false);
|
||
starRow = At(body, "Stars", 742f, 803f, 290f, 60f);
|
||
|
||
// ⑤ 스킬 패널 = 제목(액티브 + 쿨 칩 · 맨 오른쪽 Lv 칩 / 패시브 + Lv 칩) · 행 = 틀 + 병종 글리프 · 이름 · 한자 · 설명 · 레벨업
|
||
panelImg = Spr(body, "SkillPanel", "panel12", 24f, 920f, 1032f, 444f, "white");
|
||
for (int i = 0; i < 2; i++)
|
||
{
|
||
int k = i;
|
||
float ty = 934f + i * 214f, y = ty + 54f;
|
||
Txt(body, i == 0 ? "액티브" : "패시브", 52f, ty + 4f, 36, "white");
|
||
if (i == 0)
|
||
{
|
||
var cc = Spr(body, "Cool", "chip", 184f, ty, 112f, 46f, "chip_cool");
|
||
coolChip = cc.gameObject;
|
||
coolText = Txt(cc.transform, "", 0f, 9f, 26, "white", TextAnchor.MiddleCenter, 112f, false, 3f);
|
||
}
|
||
var lc = Spr(body, "SkillLv" + i, "chip", 912f, ty, 112f, 46f, "chip_dark");
|
||
skLv[i] = Txt(lc.transform, "", 0f, 9f, 26, "white", TextAnchor.MiddleCenter, 112f, true, 3f);
|
||
rowImg[i] = Spr(body, "SkillRow" + i, "panel12", 44f, y, 992f, 146f, "white");
|
||
skFrame[i] = Ico(body, null, 58f, y + 10f, 126f, 126f, 4f);
|
||
skCls[i] = Ico(body, null, 80f, y + 32f, 82f, 82f);
|
||
skName[i] = Txt(body, "", 200f, y + 18f, 42, "white");
|
||
skHanja[i] = Txt(body, "", 200f, y + 30f, 28, "white", TextAnchor.MiddleLeft, 0f, false, 0f, false);
|
||
skDesc[i] = Desc(body, 200f, y + 74f, 600f);
|
||
skBtn[i] = Press(body, "SkillUp" + i, "btn_purple", 818f, y + 18f, 204f, 110f, "white", () => OnSkillRow(k));
|
||
var bt = skBtn[i].transform;
|
||
skBtnText[i] = Txt(bt, "", 0f, 12f, 34, "white", TextAnchor.MiddleCenter, 204f, false);
|
||
skBtnSub[i] = Txt(bt, "", 0f, 58f, 24, "text_max_sub", TextAnchor.MiddleCenter, 204f, false, 3f);
|
||
var cost = At(bt, "Cost", 0f, 0f, 204f, 110f);
|
||
skCost[i] = cost.gameObject;
|
||
Ico(cost, "cur_skillbook", 22f, 56f, 38f, 38f);
|
||
skBooks[i] = Txt(cost, "", 62f, 60f, 28, "white", TextAnchor.MiddleLeft, 0f, true, 3f);
|
||
Ico(cost, "cur_gold", 98f, 59f, 32f, 32f);
|
||
skGold[i] = Txt(cost, "", 132f, 63f, 22, "text_book", TextAnchor.MiddleLeft, 0f, true, 3f);
|
||
}
|
||
|
||
// ⑥ 메뉴 4버튼 = 정보 · 진화 · 각성 · 외형(진화 가능 = 알림 점)
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
int k = i;
|
||
var b = Press(body, "Menu" + i, "round08", 34f + i * 258f, 1390f, 236f, 108f, "menu_btn", () => OnMenu(k));
|
||
Spr(b.transform, "Light", "band_light", 14f, 8f, 208f, 12f, "menu_light");
|
||
var sz = MenuIconSize[i];
|
||
Ico(b.transform, MenuIcons[i], 30f + (64f - sz.x) / 2f, 54f - sz.y / 2f, sz.x, sz.y, 3f);
|
||
Txt(b.transform, MenuNames[i], 104f, 34f, 40, "white", TextAnchor.MiddleLeft, 0f, false);
|
||
}
|
||
evolveDot = Ico(body, "new_dot", 498f, 1380f, 36f, 36f);
|
||
}
|
||
|
||
void Arrow(Transform body, string name, string icon, float x, int d)
|
||
{
|
||
var hit = At(body, name, x - 18f, 249f, 80f, 140f);
|
||
hit.gameObject.AddComponent<Image>().color = Color.clear;
|
||
hit.gameObject.AddComponent<Button>().onClick.AddListener(() => Step(d));
|
||
Ico(hit, icon, 18f, 27f, 44f, 86f);
|
||
}
|
||
|
||
static float ScaleOf(Transform t)
|
||
{
|
||
var cv = t.GetComponentInParent<Canvas>();
|
||
return cv != null && cv.scaleFactor > 0f ? cv.scaleFactor : 1f;
|
||
}
|
||
|
||
/// <summary>일러 밀기(기준 px · 왼쪽 = 다음 · 오른쪽 = 이전 · 80 px 미만 = 무시).</summary>
|
||
public void Swipe(float dx)
|
||
{
|
||
if (Mathf.Abs(dx) >= SwipeMin) Step(dx < 0f ? 1 : -1);
|
||
}
|
||
|
||
void Step(int d)
|
||
{
|
||
if (order.Count == 0) return;
|
||
int i = Mathf.Max(0, order.IndexOf(id));
|
||
id = order[(i + d + order.Count) % order.Count];
|
||
Refresh();
|
||
}
|
||
|
||
public override void Refresh()
|
||
{
|
||
var h = S.Def(id);
|
||
if (h == null) return;
|
||
var db = S.Db;
|
||
var lv = S.Save.Level(id);
|
||
var hs = S.Save.Hero(id);
|
||
var st = S.StatsOf(id);
|
||
string g = h.isSoldier || hs == null || string.IsNullOrEmpty(hs.grade) ? "white" : hs.grade;
|
||
string G = "grade_" + g + "_";
|
||
bgFill.color = Col(G + "bg");
|
||
bgGlow.color = Col(G + "glow");
|
||
bandImg.color = Col(G + "band");
|
||
panelImg.color = Col(G + "stat");
|
||
for (int i = 0; i < 2; i++) rowImg[i].color = Col(G + "row");
|
||
if (UISkin.Apply(paraImg, "para_" + g)) paraImg.color = Col(G + "chip");
|
||
paraImg.gameObject.SetActive(!h.isSoldier);
|
||
gradeText.gameObject.SetActive(!h.isSoldier);
|
||
gradeText.text = UiKit.GradeKo(g);
|
||
|
||
int total = 0;
|
||
foreach (var o in db.Heroes.heroes)
|
||
if (o.isSoldier == h.isSoldier) total++;
|
||
ownedText.text = "보유 " + S.Owned(h.isSoldier).Count + " / " + total;
|
||
power.text = st.power.ToString("N0");
|
||
|
||
UiKit.Clear(art);
|
||
UiKit.Portrait("Hero", art, h, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(560f, 500f), false, 1f, Color.clear);
|
||
|
||
// 장비: 장착 = 체크 · 가방 보유 = 초록 화살표 · 없음 = 흐림
|
||
for (int i = 0; i < 6; i++)
|
||
{
|
||
string need = h.isSoldier ? null : HeroRules.RequiredGear(db, h, hs, i);
|
||
slotRoot[i].SetActive(need != null);
|
||
if (need == null) continue;
|
||
var gd = db.Gear.Get(need);
|
||
bool on = hs != null && i < hs.gear.Count && !string.IsNullOrEmpty(hs.gear[i]);
|
||
bool bag = !on && S.Save.inv.GearCount(need) > 0;
|
||
bool lit = on || bag;
|
||
string gg = "gear_" + (gd != null && !string.IsNullOrEmpty(gd.grade) ? gd.grade : "white") + "_";
|
||
slotGlow[i].color = lit ? Col(gg + "glow") : Color.clear;
|
||
slotBg[i].color = Col(gg + "bg");
|
||
slotDeco[i].color = Col(gg + "deco");
|
||
slotLight[i].color = Col(gg + "light");
|
||
SetIcon(slotIcon[i], gd != null ? "gear_" + gd.kind : null, lit ? 1f : Col("gear_off_icon").a);
|
||
slotIcon[i].GetComponent<Shadow>().enabled = lit;
|
||
slotName[i].text = gd != null ? gd.nameKo : need;
|
||
slotName[i].color = Col(lit ? "white" : "text_off");
|
||
slotCheck[i].gameObject.SetActive(on && slotCheck[i].sprite != null);
|
||
slotBag[i].gameObject.SetActive(bag && slotBag[i].sprite != null);
|
||
}
|
||
|
||
// 레벨 · 승급 — 구조적으로 불가(최고 · 병사) = 회색 · 조건 부족 = 원래 색 + 누르면 사유 토스트
|
||
int lvCap = HeroRules.LevelCap(db, S.Save);
|
||
var row = db.Levels.Get(lv.lv);
|
||
bool maxLv = lv.lv >= db.Levels.MaxLevel || row == null;
|
||
float pct = row != null && row.expToNext > 0 ? Mathf.Clamp01(lv.exp / (float)row.expToNext) : 1f;
|
||
lvBadge.text = lv.lv.ToString();
|
||
lvText.text = "Lv " + lv.lv + " / " + lvCap;
|
||
expText.text = maxLv ? "최고 레벨" : "경험치 " + (pct * 100f).ToString("0.##") + "%";
|
||
expFill.sizeDelta = new Vector2(458f * pct, expFill.sizeDelta.y);
|
||
expFill.gameObject.SetActive(pct > 0.01f);
|
||
levelImg.color = Col(maxLv ? "disabled" : "white");
|
||
levelLabel.color = levelCost.color = Col(maxLv ? "disabled_text" : "text_on_green");
|
||
levelCost.text = maxLv ? "최고" : row.goldCost.ToString("N0");
|
||
goldIcon.gameObject.SetActive(!maxLv);
|
||
|
||
var gearList = h.isSoldier || hs == null ? null : db.Heroes.GearFor(h, hs.grade);
|
||
promoteSub.text = h.isSoldier ? "병사 없음" : gearList == null ? "최고 등급" : "장비 " + HeroRules.EquippedCount(hs) + "/" + gearList.Count;
|
||
promoteImg.color = Col(gearList == null ? "disabled" : "white");
|
||
evolveDot.gameObject.SetActive(!h.isSoldier && hs != null && evolveDot.sprite != null && HeroRules.CanEvolve(db, h, hs) == null);
|
||
|
||
// 이름 띠
|
||
UiKit.Clear(factionRoot);
|
||
UiKit.DropShadow(UiKit.FactionBadge(factionRoot, h.faction, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 84f), 3f);
|
||
SetIcon(clsIcon, UiKit.TypeSymbolId(h.type), 1f);
|
||
nameText.text = h.nameKo;
|
||
typeText.text = UnitTypeInfo.Korean(h.type);
|
||
PlaceX(typeText, 344f + nameText.preferredWidth + 24f);
|
||
UiKit.Clear(starRow);
|
||
int max = h.isSoldier ? 3 : db.Evolve.MaxStar;
|
||
int n = h.isSoldier ? h.rarity : hs != null ? hs.star : 1;
|
||
for (int i = 0; i < max; i++) Ico(starRow, i < n ? "star_l" : "star_off", i * 58f, 0f, 58f, 60f, i < n ? 3f : 0f);
|
||
|
||
// 스킬 — 상한(등급·★3) 또는 최고 = 회색 「최대」 · 그 밖 = 보라 레벨업(스킬서 · 골드)
|
||
string tk = UiKit.TypeKey(h.type);
|
||
for (int i = 0; i < 2; i++)
|
||
{
|
||
var sk = SkillRules.Skill(db, h, i);
|
||
bool has = sk != null && !h.isSoldier && hs != null;
|
||
SetIcon(skFrame[i], tk != null ? "skill_" + tk + (i == 0 ? "_a" : "_p") : null, has ? 1f : 0.5f);
|
||
SetIcon(skCls[i], UiKit.TypeSymbolId(h.type), has ? 1f : 0.5f);
|
||
skLv[i].transform.parent.gameObject.SetActive(has);
|
||
skBtn[i].gameObject.SetActive(has);
|
||
if (i == 0) coolChip.SetActive(has);
|
||
if (!has)
|
||
{
|
||
skName[i].text = h.isSoldier ? "스킬 없음" : "—";
|
||
skHanja[i].text = "";
|
||
skDesc[i].text = h.isSoldier ? "병사 유닛은 스킬이 없습니다" : "";
|
||
continue;
|
||
}
|
||
int slv = SkillRules.Level(hs, i), cap = SkillRules.Cap(db, h, hs, i);
|
||
skName[i].text = sk.nameKo;
|
||
skHanja[i].text = sk.hanja ?? "";
|
||
skHanja[i].color = Col(G + "label");
|
||
PlaceX(skHanja[i], 200f + skName[i].preferredWidth + 8f);
|
||
skDesc[i].text = SkillInfoDb.DisplayDesc(sk);
|
||
skLv[i].text = "Lv " + slv + "/" + cap;
|
||
if (i == 0) coolText.text = "쿨 " + sk.cooldown + "턴";
|
||
bool top = slv >= cap;
|
||
UISkin.Apply(skBtn[i], top ? "btn_gray" : "btn_purple");
|
||
skBtn[i].color = Color.white;
|
||
skBtnText[i].text = top ? "최대" : "레벨업";
|
||
skBtnText[i].color = Col(top ? "text_max" : "white");
|
||
skBtnSub[i].gameObject.SetActive(top);
|
||
skBtnSub[i].text = slv >= sk.lvMax ? "최고 레벨" : "등급 상한";
|
||
skCost[i].SetActive(!top);
|
||
var cost = db.Skills.CostTo(slv + 1);
|
||
skBooks[i].text = cost != null ? cost.books.ToString() : "-";
|
||
skGold[i].text = cost != null ? cost.gold.ToString("N0") : "-";
|
||
}
|
||
}
|
||
|
||
/// <summary>메뉴 4버튼: 0 정보(상세정보 + 도감) · 1 진화(조각 팝업) · 2 각성 · 3 외형(준비 중).</summary>
|
||
public void OnMenu(int i)
|
||
{
|
||
switch (i)
|
||
{
|
||
case 0: ShowStats(); break;
|
||
case 1: ShowEvolve(); break;
|
||
case 2: Root.ComingSoon("각성"); break;
|
||
case 3: Root.ComingSoon("외형"); break;
|
||
}
|
||
}
|
||
|
||
/// <summary>스킬 행 레벨업 = 그 스킬만(스킬서 + 골드 · 불가 = 사유 토스트).</summary>
|
||
public void OnSkillRow(int i)
|
||
{
|
||
var h = S.Def(id);
|
||
var hs = S.Save.Hero(id);
|
||
if (h == null || hs == null || h.isSoldier)
|
||
{
|
||
Root.Toast("병사 유닛은 스킬이 없습니다");
|
||
return;
|
||
}
|
||
var sk = SkillRules.Skill(S.Db, h, i);
|
||
if (sk == null) return;
|
||
var r = SkillRules.LevelUp(S.Db, S.Save, h, hs, i);
|
||
if (r == null) S.Commit();
|
||
Refresh();
|
||
Root.Toast(r ?? sk.nameKo + " Lv " + SkillRules.Level(hs, i) + "!");
|
||
}
|
||
|
||
/// <summary>진화 팝업 = ★ 줄 + 영웅 조각 바 + 다음 ★ · 진화 버튼(기존 진화 규칙).</summary>
|
||
public void ShowEvolve()
|
||
{
|
||
var h = S.Def(id);
|
||
var hs = S.Save.Hero(id);
|
||
if (h == null || hs == null || h.isSoldier)
|
||
{
|
||
Root.Toast("병사 유닛 — 진화 없음 (중복 = 골드)");
|
||
return;
|
||
}
|
||
var db = S.Db;
|
||
int need = HeroRules.ShardsForNext(db, hs);
|
||
bool top = hs.star >= db.Evolve.MaxStar;
|
||
var why = HeroRules.CanEvolve(db, h, hs);
|
||
var c = Root.ModalCustom("진화 — " + h.nameKo, 600f,
|
||
new OutGameRoot.ModalButton(top ? "최고 ★" : "진화", why == null ? UiKit.Red : UiKit.Disabled, OnEvolve, why != null),
|
||
new OutGameRoot.ModalButton("닫기", UiKit.Dark, null));
|
||
UiKit.StarRow(c, hs.star, db.Evolve.MaxStar, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -4f), 64f);
|
||
UiKit.IconOrDot(c, "cur_shard", "조", UiKit.Purple, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, -90f), 60f);
|
||
UiKit.Label(c, "영웅 조각", 34, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(92f, -90f), new Vector2(400f, 60f));
|
||
var bar = UiKit.Bar("ShardBar", c, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -164f), new Vector2(820f, 46f), UiKit.Slot, UiKit.Purple);
|
||
UiKit.SetBar(bar, need > 0 ? Mathf.Clamp01(hs.shards / (float)need) : 1f);
|
||
UiKit.Title(UiKit.Label(bar.parent, need > 0 ? hs.shards + " / " + need : hs.shards + " · 최고 ★", 30, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(800f, 46f)), true);
|
||
UiKit.Label(c, top ? "최고 ★ 달성" : "다음 ★" + (hs.star + 1) + " · 필요 조각 " + need + (why != null ? "\n" + why : ""), 28, UiKit.InkSoft, TextAnchor.UpperCenter,
|
||
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -226f), new Vector2(840f, 84f), false);
|
||
}
|
||
|
||
void OnSlot(int i)
|
||
{
|
||
var h = S.Def(id);
|
||
var hs = S.Save.Hero(id);
|
||
if (h == null || hs == null) return;
|
||
var need = HeroRules.RequiredGear(S.Db, h, hs, i);
|
||
var g = S.Db.Gear.Get(need);
|
||
if (need == null) return;
|
||
if (!string.IsNullOrEmpty(hs.gear[i]))
|
||
{
|
||
Root.Toast("장착됨: " + (g != null ? g.nameKo : need) + " — 6칸을 모두 채우면 승급");
|
||
return;
|
||
}
|
||
var why = S.Equip(id, i);
|
||
if (why == null)
|
||
{
|
||
Root.Toast((g != null ? g.nameKo : need) + " 장착 · 1칸 = 1강(누적 " + hs.gearTotal + ")");
|
||
return;
|
||
}
|
||
string from = g != null ? g.dropFromChapter + "장~ 스테이지 드랍" : "";
|
||
Root.Modal("필요 장비", (g != null ? g.nameKo + " (" + UiKit.GradeKo(g.grade) + " 등급)" : need) + "\n보유 0\n\n획득: " + from + " · 거래 > 상점(흰/녹)" +
|
||
(g != null && g.craftCount > 0 ? "\n합성 = 하위 " + g.craftCount + "개 + 도면 " + g.craftBlueprint + " (2차)" : ""),
|
||
new OutGameRoot.ModalButton("상점 가기", UiKit.Red, () => Root.Trade.OpenShop()),
|
||
new OutGameRoot.ModalButton("닫기", UiKit.Dark, null));
|
||
}
|
||
|
||
void OnLevelUp()
|
||
{
|
||
var why = S.LevelUp(id);
|
||
Root.Toast(why ?? S.Def(id).nameKo + " Lv " + S.Save.Level(id).lv + "!");
|
||
}
|
||
|
||
void OnPromote()
|
||
{
|
||
var h = S.Def(id);
|
||
var why = S.Promote(id);
|
||
Root.Toast(why ?? h.nameKo + " 승급 → " + UiKit.GradeKo(S.Save.Hero(id).grade) + " 등급 · 새 장비 6칸");
|
||
}
|
||
|
||
/// <summary>스킬 레벨업(PD #1093 B): 액티브·패시브 중 고르기 → 스킬서 + 골드(skill_levels.csv) · 상한 = 등급 + ★3 액티브 +1.</summary>
|
||
public void OnSkillUp()
|
||
{
|
||
var h = S.Def(id);
|
||
var hs = S.Save.Hero(id);
|
||
if (h == null || hs == null || h.isSoldier)
|
||
{
|
||
Root.Toast("병사 유닛은 스킬이 없습니다");
|
||
return;
|
||
}
|
||
var db = S.Db;
|
||
var buttons = new List<OutGameRoot.ModalButton>();
|
||
var body = new System.Text.StringBuilder("보유 스킬서 " + S.Save.inv.books.skill + " · 골드 " + S.Save.gold.ToString("N0") + "\n");
|
||
for (int i = 0; i < 2; i++)
|
||
{
|
||
int k = i;
|
||
var sk = SkillRules.Skill(db, h, i);
|
||
if (sk == null) continue;
|
||
int lv = SkillRules.Level(hs, i);
|
||
var cost = db.Skills.CostTo(lv + 1);
|
||
var why = SkillRules.CanLevelUp(db, S.Save, h, hs, i);
|
||
body.Append("\n").Append(i == 0 ? "액티브 " : "패시브 ").Append(sk.nameKo).Append(" Lv ").Append(lv).Append(" / ").Append(SkillRules.Cap(db, h, hs, i));
|
||
if (cost != null && lv < sk.lvMax) body.Append(" → 스킬서 ").Append(cost.books).Append(" · 골드 ").Append(cost.gold.ToString("N0"));
|
||
if (why != null) body.Append("\n <color=#a03a22>").Append(why).Append("</color>");
|
||
buttons.Add(new OutGameRoot.ModalButton((i == 0 ? "액티브" : "패시브") + " Lv+1", why == null ? UiKit.Red : UiKit.Disabled, () =>
|
||
{
|
||
var r = SkillRules.LevelUp(S.Db, S.Save, h, hs, k);
|
||
if (r == null) S.Commit();
|
||
Refresh();
|
||
if (r == null) OnSkillUp(); // 이어서 올릴 수 있게 다시 연다
|
||
Root.Toast(r ?? sk.nameKo + " Lv " + SkillRules.Level(hs, k) + "!");
|
||
}));
|
||
}
|
||
buttons.Add(new OutGameRoot.ModalButton("닫기", UiKit.Dark, null));
|
||
Root.Modal("스킬 레벨업 — " + h.nameKo, body.ToString(), buttons.ToArray());
|
||
}
|
||
|
||
void OnEvolve()
|
||
{
|
||
var why = S.Evolve(id);
|
||
Root.Toast(why ?? S.Def(id).nameKo + " 진화 → ★" + S.Save.Hero(id).star);
|
||
}
|
||
|
||
/// <summary>정보 = 상세정보(스탯 내역) + 도감 버튼.</summary>
|
||
public void ShowStats()
|
||
{
|
||
var h = S.Def(id);
|
||
var st = S.StatsOf(id);
|
||
var e = S.Db.Economy;
|
||
var hs = S.Save.Hero(id);
|
||
string body =
|
||
"기본(" + UnitTypeInfo.Korean(h.type) + ") HP " + st.baseHp + " · 공 " + st.baseAtk + " · 방 " + st.baseDef + "\n" +
|
||
"영웅 배율 ×" + st.hpMult + " · ×" + st.atkMult + " · ×" + st.defMult + "\n" +
|
||
"레벨 Lv " + S.Save.Level(id).lv + " +" + st.lvPct.ToString("0.#") + "%\n" +
|
||
"승급 " + (hs != null ? UiKit.GradeKo(hs.grade) : "—") + " +" + st.promoPct.ToString("0.#") + "% · 장비 누적 " + (hs != null ? hs.gearTotal : 0) + "개 +" + st.gearPct.ToString("0.#") + "%\n" +
|
||
"진화 " + (hs != null ? "★" + hs.star : "—") + " +" + st.starPct.ToString("0.#") + "%\n" +
|
||
"훈련(" + TrainingRules.RankName(S.Db, TrainingRules.Rank(S.Save, h.type)) + ") HP +" + st.trainHpPct.ToString("0.#") + "% · 공 +" + st.trainPct.ToString("0.#") + "% · 방 +" + st.trainDefPct.ToString("0.#") + "%\n" +
|
||
"보물(수집품·세트) HP +" + st.treasureHpPct.ToString("0.#") + "% · 공 +" + st.treasureAtkPct.ToString("0.#") + "% · 방 +" + st.treasureDefPct.ToString("0.#") + "%\n\n" +
|
||
"<size=40>HP " + st.Hp + " · 공 " + st.Atk + " · 방 " + st.Def + "</size>\n" +
|
||
"전투력 " + st.power.ToString("N0") + " = HP/" + e.PowerHpDiv + " + 공×" + e.PowerAtkW + " + 방×" + e.PowerDefW;
|
||
Root.Modal("상세정보 — " + h.nameKo, body,
|
||
new OutGameRoot.ModalButton("도감", UiKit.Dark, () => ShowCodex(Root)),
|
||
new OutGameRoot.ModalButton("닫기", UiKit.Dark, null));
|
||
}
|
||
|
||
/// <summary>도감 = 전 영웅·병사(미보유 회색).</summary>
|
||
public static void ShowCodex(OutGameRoot root)
|
||
{
|
||
var s = root.Session;
|
||
int owned = 0;
|
||
foreach (var h in s.Db.Heroes.heroes)
|
||
if (s.Save.Owns(h.id)) owned++;
|
||
var content = root.ModalCustom("도감 " + owned + " / " + s.Db.Heroes.heroes.Count, 1560f, new OutGameRoot.ModalButton("닫기", UiKit.Dark, null));
|
||
ScrollRect sr;
|
||
var grid = UiKit.ScrollGrid("Codex", content, 0f, 0f, 0f, 0f, new Vector2(420f, 140f), new Vector2(20f, 14f), 2, out sr);
|
||
foreach (var h in s.Db.Heroes.heroes)
|
||
{
|
||
bool own = s.Save.Owns(h.id);
|
||
var hero = h;
|
||
HeroCards.Card(grid, s, h, false, own, () => root.Toast(CollectionCards.InfoText(hero, own)));
|
||
}
|
||
}
|
||
}
|
||
}
|