541 lines
32 KiB
C#
541 lines
32 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
namespace TKS.OutGame
|
||
{
|
||
/// <summary>
|
||
/// 거래(img11 · 기획 S15) — ① 영웅·유닛 뽑기(픽업 · 천장 n/80 · 확률 공개 · 1회/10연 · 첫 10연 무료 · 결과 = 카드 순차 공개)
|
||
/// ② 보물 상자(PD #1093 C) = 상자 5종 · 보유 수 · 구매(chests.csv buy_gold/buy_gem/buy_points · 거래 할인 개발) · 열기는 보물 > 상자 탭
|
||
/// ③ 상점·패스 = 골드 승급 장비 상점(흰/녹 · 일일 · 새로고침 보옥 · 시장 개설 무료) · 성장 패스·월정액·챕터 팩·첫 결제 팩 = 「준비 중」 스텁(과금 없음).
|
||
/// </summary>
|
||
public class TradeScreen : OutGameScreen
|
||
{
|
||
int page;
|
||
readonly List<Button> tabs = new List<Button>();
|
||
readonly List<RectTransform> pages = new List<RectTransform>();
|
||
|
||
Text pityText;
|
||
RectTransform pityBar;
|
||
GameObject freeChip;
|
||
GameObject freeChip2;
|
||
Button singleBtn;
|
||
Button tenBtn;
|
||
RectTransform pickupArt;
|
||
|
||
RectTransform shopGrid;
|
||
ScrollRect shopScroll;
|
||
Text shopTimer;
|
||
Button refreshBtn;
|
||
|
||
GameObject resultRoot;
|
||
readonly List<GameObject> resultCards = new List<GameObject>();
|
||
Coroutine revealing;
|
||
|
||
public List<GachaPull> LastPulls { get; private set; }
|
||
|
||
public int Page
|
||
{
|
||
get { return page; }
|
||
}
|
||
|
||
public bool ResultVisible
|
||
{
|
||
get { return resultRoot != null && resultRoot.activeSelf; }
|
||
}
|
||
|
||
/// <summary>장비 상점 칸 수(스킬서 임시 판매 칸 제외).</summary>
|
||
public int ShopCardCount
|
||
{
|
||
get
|
||
{
|
||
int n = 0;
|
||
foreach (Transform c in shopGrid)
|
||
if (c.name != SkillBookCellName) n++;
|
||
return n;
|
||
}
|
||
}
|
||
|
||
public const string SkillBookCellName = "SkillBook";
|
||
|
||
/// <summary>스킬서 판매 칸이 있는가(PD #1093 B).</summary>
|
||
public bool SkillBookCellVisible
|
||
{
|
||
get { return shopGrid != null && shopGrid.Find(SkillBookCellName) != null; }
|
||
}
|
||
|
||
public override void Build()
|
||
{
|
||
Tab = 4;
|
||
if (UISkin.On) UiKit.Paint(UiKit.Fill("ListBg", Rt), UiKit.Col("list_bg", UiKit.Bg), false); // 목록형 바탕(Fable §5-3)
|
||
Header("거래", "뽑기 · 상자 · 상점 · 패스", true);
|
||
var body = Body();
|
||
string[] names = { "영웅·유닛 뽑기", "보물 상자", "상점·패스" };
|
||
for (int i = 0; i < names.Length; i++)
|
||
{
|
||
int k = i;
|
||
tabs.Add(UISkin.On
|
||
? UiKit.SubTab(body, "Tab" + i, names[i], 24f + i * 348f, 18f, 332f, 84f, () => SetPage(k))
|
||
: UiKit.Btn("Tab" + i, body, names[i], 32, UiKit.Paper, UiKit.Ink, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f + i * 348f, -18f), new Vector2(332f, 84f), () => SetPage(k)));
|
||
pages.Add(UiKit.Fill("Page" + i, body, 0f, 0f, 118f, 0f));
|
||
}
|
||
if (UISkin.Has("panel_field") && UISkin.Has("btn_l_yellow")) BuildGachaKit(pages[0]);
|
||
else BuildGacha(pages[0]);
|
||
BuildShop(pages[2]);
|
||
}
|
||
|
||
// ---------- 뽑기(Fable 제안서 §4-4 · #1104) ----------
|
||
|
||
const float GaugeW = 820f;
|
||
Text pityBadge;
|
||
Text tenCost;
|
||
Text singleCost;
|
||
|
||
/// <summary>
|
||
/// 뽑기 = 배너(틀 유지 · 아래 문양 띠 흰 8 % · 오른쪽 위 「픽업」 뱃지) → 천장 섹션(레벨형 게이지 + 노랑 배지) →
|
||
/// 확률 섹션(제목 띠 · 행 3 = 별 아이콘 ×n + 바 + % Sen) → 1회(Secondary 하늘 + 보옥 비용) · 10연(Primary 노랑 + 「첫 무료」 꼬리 칩) · 각주 = ⓘ 팝업.
|
||
/// </summary>
|
||
void BuildGachaKit(RectTransform p)
|
||
{
|
||
var g = S.Db.Gacha;
|
||
// 배너
|
||
var banner = UiKit.At(p, "Banner", 24f, 12f, 1032f, 600f);
|
||
UiKit.Skin(banner, "banner", Color.white);
|
||
if (UISkin.Has("banner_pattern"))
|
||
{
|
||
var pat = UiKit.Skin(UiKit.At(banner, "Pattern", 18f, 600f - 112f, 1032f - 36f, 90f), "banner_pattern", UiKit.Col("banner_pattern", Color.clear));
|
||
pat.type = Image.Type.Tiled;
|
||
}
|
||
UiKit.InfoChip(banner, "Period", 30f, 26f, 180f, 52f, null, "기간 2주", 28); // 「픽업」 = 오른쪽 위 뱃지 하나만(검수 결함 3)
|
||
UiKit.Title(UiKit.Label(banner, "영웅·유닛 뽑기", 60, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(36f, -100f), new Vector2(600f, 90f)), true);
|
||
UiKit.Label(banner, "모든 영웅·병사 유닛\n= 뽑기로 획득", 32, UiKit.Col("text_soft", UiKit.InkSoft), TextAnchor.UpperLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(36f, -200f), new Vector2(520f, 110f), false);
|
||
var free = UiKit.Skin(UiKit.At(banner, "Free", 36f, 600f - 104f, 300f, 64f), "tail", UiKit.Col("tail_red", UiKit.Red));
|
||
UiKit.Title(UiKit.FillLabel(free.transform, "첫 10연 무료", 30, UiKit.White), true);
|
||
freeChip = free.gameObject;
|
||
pickupArt = UiKit.Rect("PickupArt", banner, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-20f, 20f), new Vector2(560f, 560f));
|
||
var hot = UiKit.Icon(banner, "badge_hot", new Vector2(1f, 1f), new Vector2(0.5f, 0.5f), new Vector2(-52f, -44f), 88f); // 배너 안쪽(검수 결함 3)
|
||
if (hot != null)
|
||
{
|
||
UiKit.DropShadow(hot, 4f);
|
||
UiKit.Title(UiKit.FillLabel(hot.transform, "픽업", 24, UiKit.White), true);
|
||
}
|
||
|
||
// 천장 게이지
|
||
RectTransform pr;
|
||
var pf = UiKit.SectionPanel(p, "Pity", 24f, 632f, 1032f, 176f, false, false, out pr);
|
||
UiKit.Title(UiKit.Label(pf, "천장", 34, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(26f, -12f), new Vector2(200f, 50f)), true);
|
||
pityText = UiKit.Label(pf, "", 28, UiKit.Col("text_soft", UiKit.InkSoft), TextAnchor.MiddleRight, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-26f, -12f), new Vector2(620f, 50f), false);
|
||
pityBar = UiKit.Gauge(pf, "Gauge", 20f, 62f, GaugeW + 60f, "lv_badge_y", "lv_fill", out pityBadge);
|
||
|
||
// 확률 공개
|
||
RectTransform probRoot;
|
||
var prob = UiKit.SectionPanel(p, "Prob", 24f, 828f, 1032f, 400f, false, false, out probRoot);
|
||
UiKit.SectionTitle(probRoot, "확률 공개");
|
||
var info = UiKit.Rect("Info", probRoot, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-16f, -12f), new Vector2(64f, 64f));
|
||
UiKit.Skin(info, "badge_circle", UiKit.Col("chip_dark", UiKit.Dark), true);
|
||
var ii = UiKit.Icon(info, "info", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 34f);
|
||
if (ii == null) UiKit.Title(UiKit.FillLabel(info, "i", 34, UiKit.White), true);
|
||
info.gameObject.AddComponent<Button>().onClick.AddListener(ShowGachaRules);
|
||
for (int t = 3; t >= 1; t--)
|
||
{
|
||
float y = 88f + (3 - t) * 98f;
|
||
var row = UiKit.Skin(UiKit.At(prob, "Row" + t, 20f, y, 1032f - 12f - 40f, 80f), "panel12", UiKit.Col("row_on_field", UiKit.Paper)).transform;
|
||
for (int s = 0; s < t; s++)
|
||
{
|
||
var st = UiKit.Icon(row, "star_on", new Vector2(0f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(40f + s * 40f, 0f), 40f);
|
||
if (st != null) UiKit.DropShadow(st, 2f);
|
||
}
|
||
var fill = UiKit.Bar("P" + t, row, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(180f, 0f), new Vector2(500f, 30f), UiKit.Slot, t == 3 ? UiKit.Col("grade_orange_card_bg", UiKit.Coin) : t == 2 ? UiKit.Col("grade_purple_card_bg", UiKit.Purple) : UiKit.Col("grade_blue_card_bg", UiKit.Jade));
|
||
UiKit.SetBar(fill, g.P(t));
|
||
var pt = UiKit.Title(UiKit.Label(row, (g.P(t) * 100f).ToString("0.#") + "%", 32, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(700f, 0f), new Vector2(140f, 50f)), true);
|
||
pt.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||
if (t == 1) UiKit.Label(row, "병사 포함", 24, UiKit.Col("text_soft", UiKit.InkSoft), TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-20f, 0f), new Vector2(140f, 40f), false);
|
||
}
|
||
|
||
// 1회 · 10연
|
||
singleBtn = UiKit.SkinBtn(p, "Single", "btn_sky", UiKit.White, "1회", 42, UiKit.White, 24f, 1288f, 420f, 116f, () => DoDraw(1)); // 버튼 행 = 탭 바 위 24 고정(검수 결함 4)
|
||
singleCost = UiKit.BtnCost(singleBtn, "cur_gem", "", 44f, 36);
|
||
tenBtn = UiKit.SkinBtn(p, "Ten", "btn_l_yellow", UiKit.White, g.TenCount + "연", 46, UiKit.Col("text_on_yellow", UiKit.Ink), 1056f - 580f, 1282f, 580f, 122f, () => DoDraw(g.TenCount));
|
||
tenCost = UiKit.BtnCost(tenBtn, "cur_gem", "", 44f, 36);
|
||
var f2 = UiKit.Skin(UiKit.Rect("Free2", tenBtn.transform, new Vector2(1f, 1f), new Vector2(1f, 0.5f), new Vector2(-14f, 2f), new Vector2(200f, 50f)), "tail", UiKit.Col("tail_red", UiKit.Red));
|
||
UiKit.Title(UiKit.FillLabel(f2.transform, "첫 무료", 26, UiKit.White), true);
|
||
freeChip2 = f2.gameObject;
|
||
}
|
||
|
||
/// <summary>뽑기 규칙(구 각주 → ⓘ 팝업 · Fable §4-4).</summary>
|
||
void ShowGachaRules()
|
||
{
|
||
var e = S.Db.Economy;
|
||
var g = S.Db.Gacha;
|
||
Root.Modal("뽑기 규칙",
|
||
"중복 = 영웅 조각\n★★★ " + e.ShardDup(3) + " · ★★ " + e.ShardDup(2) + " · ★ " + e.ShardDup(1) + "\n병사 중복 = 골드 " + e.SoldierDupGold +
|
||
"\n\n" + g.TenCount + "연 = ★★ 이상 1 보장\n천장 " + g.Pity + "회 = ★★★ 확정\n확률·천장 전부 공개",
|
||
new OutGameRoot.ModalButton("확인", UiKit.Dark, null));
|
||
}
|
||
|
||
public void SetPage(int p)
|
||
{
|
||
page = Mathf.Clamp(p, 0, pages.Count - 1);
|
||
Refresh();
|
||
}
|
||
|
||
public void OpenShop()
|
||
{
|
||
Root.Show(this);
|
||
SetPage(2);
|
||
}
|
||
|
||
public override void OnShow()
|
||
{
|
||
S.EnsureShop();
|
||
}
|
||
|
||
// ---------- 뽑기 ----------
|
||
|
||
void BuildGacha(RectTransform p)
|
||
{
|
||
var g = S.Db.Gacha;
|
||
var banner = UiKit.TopBand("Banner", p, 12f, 440f, 24f, 24f);
|
||
if (UISkin.Has("banner")) UiKit.Skin(banner, "banner", Color.white); // 팩 배너 틀(PD #1100)
|
||
else UiKit.Paint(banner, new Color(0.27f, 0.14f, 0.12f));
|
||
var pick = UiKit.Box("Pickup", banner, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(30f, -24f), new Vector2(230f, 62f), UiKit.Red);
|
||
UiKit.FillLabel(pick.transform, "픽업 (2주)", 30, UiKit.White);
|
||
UiKit.Title(UiKit.Label(banner, "영웅·유닛 뽑기", 60, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(36f, -110f), new Vector2(600f, 90f)), true);
|
||
UiKit.Label(banner, "모든 영웅·병사 유닛\n= 뽑기로 획득", 32, new Color(0.9f, 0.82f, 0.78f), TextAnchor.UpperLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(36f, -210f), new Vector2(520f, 110f), false);
|
||
var free = UiKit.Box("Free", banner, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(36f, 28f), new Vector2(300f, 78f), new Color(0.93f, 0.75f, 0.24f));
|
||
UiKit.FillLabel(free.transform, "첫 10연 무료", 34, UiKit.Ink);
|
||
freeChip = free.gameObject;
|
||
pickupArt = UiKit.Rect("PickupArt", banner, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-20f, 10f), new Vector2(440f, 400f));
|
||
|
||
var pity = UiKit.TopBand("Pity", p, 468f, 130f, 24f, 24f);
|
||
var pimg = UiKit.Paint(pity, UiKit.Paper);
|
||
UiKit.Border(pimg, UiKit.Line, 3f);
|
||
UiKit.Label(pity, "천장 게이지", 36, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -10f), new Vector2(400f, 56f));
|
||
pityText = UiKit.Label(pity, "", 30, UiKit.Red, TextAnchor.MiddleRight, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-24f, -10f), new Vector2(560f, 56f));
|
||
pityBar = UiKit.Bar("PityBar", pity, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 18f), new Vector2(980f, 32f), UiKit.Slot, UiKit.Red);
|
||
|
||
var prob = UiKit.TopBand("Prob", p, 614f, 270f, 24f, 24f);
|
||
var primg = UiKit.Paint(prob, UiKit.Paper);
|
||
UiKit.Border(primg, UiKit.Line, 3f);
|
||
UiKit.Label(prob, "확률 공개", 36, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -10f), new Vector2(400f, 56f));
|
||
UiKit.Label(prob, "확률·천장 전부 공개", 26, UiKit.InkSoft, TextAnchor.MiddleRight, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-24f, -10f), new Vector2(400f, 56f), false);
|
||
for (int t = 3; t >= 1; t--)
|
||
{
|
||
float y = -84f - (3 - t) * 58f;
|
||
UiKit.Label(prob, UiKit.Stars(t, t), 38, UiKit.Star, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, y), new Vector2(160f, 50f));
|
||
var fill = UiKit.Bar("P" + t, prob, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(200f, y - 12f), new Vector2(380f, 28f), UiKit.Slot, t == 3 ? UiKit.Red : new Color(0.47f, 0.38f, 0.32f));
|
||
UiKit.SetBar(fill, g.P(t));
|
||
UiKit.Label(prob, (g.P(t) * 100f).ToString("0.#") + " %" + (t == 1 ? " (병사 유닛 포함)" : ""), 32, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(600f, y), new Vector2(420f, 50f));
|
||
}
|
||
|
||
var btns = UiKit.TopBand("Buttons", p, 904f, 170f, 24f, 24f);
|
||
singleBtn = UiKit.Btn("Single", btns, "", 40, UiKit.Dark, UiKit.White, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), Vector2.zero, new Vector2(500f, 160f), () => DoDraw(1));
|
||
tenBtn = UiKit.Btn("Ten", btns, "", 40, UiKit.Red, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), Vector2.zero, new Vector2(500f, 160f), () => DoDraw(g.TenCount));
|
||
var f2 = UiKit.Box("Free2", tenBtn.transform, new Vector2(1f, 1f), new Vector2(1f, 0.5f), new Vector2(-10f, 0f), new Vector2(240f, 56f), new Color(0.93f, 0.75f, 0.24f));
|
||
UiKit.FillLabel(f2.transform, "첫 10연 무료", 26, UiKit.Ink);
|
||
freeChip2 = f2.gameObject;
|
||
|
||
var e = S.Db.Economy;
|
||
UiKit.Label(p, "중복 = 영웅 조각(★★★ " + e.ShardDup(3) + " · ★★ " + e.ShardDup(2) + " · ★ " + e.ShardDup(1) + ") · 병사 중복 = 골드 " + e.SoldierDupGold +
|
||
" · 10연 = ★★ 이상 1 보장", 26, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -1090f), new Vector2(1020f, 80f), false);
|
||
}
|
||
|
||
void RefreshGacha()
|
||
{
|
||
var g = S.Db.Gacha;
|
||
var d = S.Save;
|
||
bool free = GachaRules.FirstTenFree(S.Db, d);
|
||
freeChip.SetActive(free);
|
||
freeChip2.SetActive(free);
|
||
if (pityBadge != null)
|
||
{
|
||
pityText.text = d.gacha.pity + " / " + g.Pity + " · " + g.Pity + "회째 ★★★ 확정";
|
||
pityBadge.text = d.gacha.pity.ToString();
|
||
UiKit.SetGauge(pityBar, d.gacha.pity / (float)Mathf.Max(1, g.Pity), GaugeW + 60f);
|
||
singleCost.text = g.CostSingle.ToString("N0");
|
||
tenCost.text = free ? "무료" : g.CostTen.ToString("N0");
|
||
var ic = tenCost.transform.parent.GetComponentInChildren<Image>(true);
|
||
if (ic != null) ic.gameObject.SetActive(!free);
|
||
}
|
||
else
|
||
{
|
||
pityText.text = d.gacha.pity + " / " + g.Pity + " · " + g.Pity + "회 ★★★ 확정";
|
||
UiKit.SetBar(pityBar, d.gacha.pity / (float)Mathf.Max(1, g.Pity));
|
||
UiKit.LabelOf(singleBtn).text = "1회\n<size=32>보옥 " + g.CostSingle.ToString("N0") + "</size>";
|
||
UiKit.LabelOf(tenBtn).text = g.TenCount + "연\n<size=32>" + (free ? "무료" : "보옥 " + g.CostTen.ToString("N0")) + "</size>";
|
||
}
|
||
UiKit.Clear(pickupArt);
|
||
var picks = new List<HeroDb.Hero>();
|
||
foreach (var pe in g.pool)
|
||
if (pe.pickup && pe.inPool && S.Def(pe.heroId) != null) picks.Add(S.Def(pe.heroId));
|
||
if (pityBadge != null)
|
||
{
|
||
var arts = picks.FindAll(h => UiKit.ArtSprite(h.art) != null);
|
||
for (int i = 0; i < arts.Count && i < 2; i++)
|
||
UiKit.Portrait("Pick" + i, pickupArt, arts[i], new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-10f - i * 250f, 70f), new Vector2(300f, 440f), false, 1f, new Color(1f, 1f, 1f, 0f));
|
||
for (int i = 0; i < picks.Count && i < 2; i++)
|
||
UiKit.InfoChip(pickupArt, "Name" + i, 560f - (i + 1) * 250f, 498f, 230f, 50f, null, picks[i].nameKo + " ★" + picks[i].rarity, 26);
|
||
return;
|
||
}
|
||
for (int i = 0; i < picks.Count && i < 2; i++)
|
||
{
|
||
UiKit.Portrait("Pick" + i, pickupArt, picks[i], new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-i * 210f, 50f), new Vector2(230f, 330f), false, 1f, new Color(1f, 1f, 1f, 0f));
|
||
UiKit.Label(pickupArt, picks[i].nameKo + " ★" + picks[i].rarity, 26, UiKit.White, TextAnchor.MiddleCenter, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-i * 210f, 6f), new Vector2(230f, 44f));
|
||
}
|
||
}
|
||
|
||
public void DoDraw(int count)
|
||
{
|
||
var why = GachaRules.CanDraw(S.Db, S.Save, count);
|
||
if (why != null)
|
||
{
|
||
Root.Toast(why);
|
||
return;
|
||
}
|
||
LastPulls = S.Draw(count);
|
||
ShowResult(LastPulls);
|
||
}
|
||
|
||
// ---------- 뽑기 결과(카드 순차 공개) ----------
|
||
|
||
void ShowResult(List<GachaPull> pulls)
|
||
{
|
||
CloseResult();
|
||
var dim = UiKit.Fill("GachaResult", Root.TopRoot);
|
||
UiLayer.Nest(dim.gameObject, UiRate.Event, true); // 위 덮개 = 자기 캔버스(재화 바와 따로 · PD 2026-11-10)
|
||
var dimg = UiKit.Paint(dim, new Color(0.08f, 0.05f, 0.04f, 0.9f), false, true);
|
||
dimg.raycastTarget = true;
|
||
var skip = dim.gameObject.AddComponent<Button>();
|
||
skip.transition = Selectable.Transition.None;
|
||
skip.onClick.AddListener(RevealAll);
|
||
resultRoot = dim.gameObject;
|
||
UiKit.Label(dim, "뽑기 결과", 64, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -200f), new Vector2(900f, 100f));
|
||
int cols = Mathf.Min(5, Mathf.Max(1, pulls.Count));
|
||
int rows = (pulls.Count + cols - 1) / cols;
|
||
float cw = 196f, chh = 330f;
|
||
for (int i = 0; i < pulls.Count; i++)
|
||
{
|
||
int r = i / cols, c = i % cols;
|
||
int inRow = Mathf.Min(cols, pulls.Count - r * cols);
|
||
float x = (c - (inRow - 1) / 2f) * (cw + 12f);
|
||
float y = 120f - r * (chh + 20f) + (rows - 1) * (chh + 20f) / 2f;
|
||
resultCards.Add(ResultCard(dim, pulls[i], new Vector2(x, y), new Vector2(cw, chh)));
|
||
}
|
||
UiKit.Btn("Ok", dim, "확인", 40, UiKit.Red, UiKit.White, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 180f), new Vector2(420f, 110f), CloseResult);
|
||
revealing = Root.StartCoroutine(Reveal());
|
||
}
|
||
|
||
GameObject ResultCard(RectTransform parent, GachaPull p, Vector2 pos, Vector2 size)
|
||
{
|
||
var h = S.Def(p.heroId);
|
||
var back = UiKit.Box("Card", parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), pos, size, new Color(0.35f, 0.22f, 0.18f));
|
||
UiKit.Border(back, new Color(0.8f, 0.62f, 0.3f), 3f);
|
||
UiKit.FillLabel(back.transform, "?", 90, new Color(0.8f, 0.62f, 0.3f));
|
||
var front = UiKit.Fill("Front", back.transform);
|
||
UiKit.Paint(front, p.tier == 3 ? UiKit.Col("grade_orange_card_bg", UiKit.Coin) : p.tier == 2 ? UiKit.Purple : UiKit.Paper);
|
||
UiKit.Portrait("Face", front, h, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -10f), new Vector2(176f, 176f), false, 1.8f, new Color(0.9f, 0.86f, 0.8f));
|
||
UiKit.Label(front, UiKit.Stars(p.tier, p.tier), 30, UiKit.Star, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -188f), new Vector2(190f, 40f));
|
||
UiKit.Label(front, h != null ? h.nameKo : p.heroId, 32, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -226f), new Vector2(190f, 44f));
|
||
string tag = p.isNew ? "<color=#b0301e>NEW</color>" : p.shards > 0 ? "조각 +" + p.shards : p.gold > 0 ? "골드 +" + p.gold : "";
|
||
if (p.pity) tag += " 천장";
|
||
if (p.pickup) tag += " 픽업";
|
||
UiKit.Label(front, tag, 26, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 10f), new Vector2(190f, 44f));
|
||
front.gameObject.SetActive(false);
|
||
return front.gameObject;
|
||
}
|
||
|
||
IEnumerator Reveal()
|
||
{
|
||
foreach (var c in resultCards)
|
||
{
|
||
yield return new WaitForSecondsRealtime(0.14f);
|
||
if (c != null) c.SetActive(true);
|
||
}
|
||
revealing = null;
|
||
}
|
||
|
||
public void RevealAll()
|
||
{
|
||
if (revealing != null) Root.StopCoroutine(revealing);
|
||
revealing = null;
|
||
foreach (var c in resultCards)
|
||
if (c != null) c.SetActive(true);
|
||
}
|
||
|
||
public void CloseResult()
|
||
{
|
||
if (revealing != null) Root.StopCoroutine(revealing);
|
||
revealing = null;
|
||
resultCards.Clear();
|
||
if (resultRoot != null)
|
||
{
|
||
resultRoot.SetActive(false);
|
||
Object.Destroy(resultRoot);
|
||
resultRoot = null;
|
||
}
|
||
Refresh();
|
||
}
|
||
|
||
public int RevealedCount
|
||
{
|
||
get
|
||
{
|
||
int n = 0;
|
||
foreach (var c in resultCards)
|
||
if (c != null && c.activeSelf) n++;
|
||
return n;
|
||
}
|
||
}
|
||
|
||
// ---------- 장비 상점 ----------
|
||
|
||
void BuildShop(RectTransform p)
|
||
{
|
||
var head = UiKit.TopBand("ShopHead", p, 12f, 110f, 24f, 24f);
|
||
UiKit.Label(head, "승급 장비 상점", 44, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), Vector2.zero, new Vector2(400f, 70f));
|
||
var e = S.Db.Economy;
|
||
var grades = new List<string>();
|
||
foreach (var g in e.ShopGrades) grades.Add(UiKit.GradeKo(g));
|
||
UiKit.Label(head, "골드 · " + string.Join("/", grades.ToArray()) + " · 매일 갱신", 26, UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(330f, -4f), new Vector2(400f, 50f), false);
|
||
shopTimer = UiKit.Label(head, "", 28, UiKit.Red, TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), Vector2.zero, new Vector2(320f, 50f));
|
||
shopGrid = UiKit.ScrollGrid("ShopGrid", p, 24f, 24f, 136f, 280f, new Vector2(326f, 400f), new Vector2(17f, 20f), 3, out shopScroll);
|
||
refreshBtn = UiKit.Btn("Refresh", p, "", 36, UiKit.Jade, UiKit.White, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 156f), new Vector2(640f, 96f), () =>
|
||
{
|
||
var why = S.RefreshShop();
|
||
Root.Toast(why ?? "상점 새로고침");
|
||
});
|
||
// 패스·팩 = 스텁(과금 없음 · PD #1093 C · 기획 S15)
|
||
string[] packs = { "성장 패스", "월정액", "챕터 팩", "첫 결제 팩" };
|
||
for (int i = 0; i < packs.Length; i++)
|
||
{
|
||
string name = packs[i];
|
||
UiKit.Btn("Pack" + i, p, name + "\n<size=22>준비 중</size>", 28, UiKit.Purple, UiKit.White, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(24f + i * 262f, 30f), new Vector2(246f, 108f),
|
||
() => Root.Modal(name, "준비 중입니다.\n(과금·패스 = 스텁 · 결제 없음 · 시스템 기획 S15)", new OutGameRoot.ModalButton("확인", UiKit.Dark, null)));
|
||
}
|
||
}
|
||
|
||
void RefreshShop()
|
||
{
|
||
S.EnsureShop();
|
||
var d = S.Save;
|
||
var left = EconomyRules.UntilNextDay(S.Now);
|
||
shopTimer.text = "갱신까지 " + ((int)left.TotalHours).ToString("00") + ":" + left.Minutes.ToString("00");
|
||
int free = EconomyRules.FreeRefreshesLeft(S.Db, d);
|
||
UiKit.LabelOf(refreshBtn).text = free > 0 ? "새로고침 · 무료 " + free + "회(시장 개설)" : "새로고침 · 보옥 " + S.Db.Economy.ShopRefreshGem;
|
||
UiKit.Clear(shopGrid);
|
||
for (int i = 0; i < d.shop.items.Count; i++)
|
||
{
|
||
var it = d.shop.items[i];
|
||
var g = S.Db.Gear.Get(it.gearId);
|
||
int k = i;
|
||
var cell = UiKit.Cell(shopGrid, "Item" + i, it.sold ? UiKit.Slot : UiKit.Paper, null);
|
||
UiKit.Border(cell, UiKit.Line, 2f);
|
||
var strip = UiKit.TopBand("Grade", cell.transform, 0f, 64f);
|
||
UiKit.Paint(strip, UiKit.GradeColor(g != null ? g.grade : ""));
|
||
UiKit.FillLabel(strip, (g != null ? UiKit.GradeKo(g.grade) : "?") + " 등급", 28, UiKit.White);
|
||
UiKit.Label(cell.transform, g != null ? g.nameKo : it.gearId, 40, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -86f), new Vector2(310f, 70f));
|
||
UiKit.Label(cell.transform, "보유 " + d.inv.GearCount(it.gearId) + " · 필요 영웅 " + NeedCount(it.gearId), 24, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -160f), new Vector2(310f, 44f), false);
|
||
UiKit.Label(cell.transform, "골드 " + it.price.ToString("N0"), 36, UiKit.Coin, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 120f), new Vector2(310f, 56f));
|
||
var b = UiKit.Btn("Buy", cell.transform, it.sold ? "품절" : "구매", 34, it.sold ? UiKit.Disabled : UiKit.Red, UiKit.White, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 20f), new Vector2(260f, 84f), () =>
|
||
{
|
||
var why = S.BuyShop(k);
|
||
Root.Toast(why ?? (g != null ? g.nameKo : "") + " 구매 → 가방");
|
||
});
|
||
b.interactable = !it.sold;
|
||
}
|
||
// 스킬서 임시 판매(B)는 제거 — 정식 획득처 = 사당 던전(PD #1093 C)
|
||
}
|
||
|
||
/// <summary>그 장비를 지금 등급 칸에서 기다리는(아직 안 채운) 보유 영웅 수.</summary>
|
||
int NeedCount(string gearId)
|
||
{
|
||
int n = 0;
|
||
foreach (var hs in S.Save.heroes)
|
||
{
|
||
var h = S.Def(hs.id);
|
||
var list = h != null ? S.Db.Heroes.GearFor(h, hs.grade) : null;
|
||
if (list == null) continue;
|
||
for (int i = 0; i < list.Count; i++)
|
||
if (list[i] == gearId && string.IsNullOrEmpty(hs.gear[i]))
|
||
{
|
||
n++;
|
||
break;
|
||
}
|
||
}
|
||
return n;
|
||
}
|
||
|
||
// ---------- 보물 상자(PD #1093 C) ----------
|
||
|
||
/// <summary>상자 구매 칸 수(테스트).</summary>
|
||
public int ChestRowCount { get; private set; }
|
||
|
||
void RefreshChests()
|
||
{
|
||
var p = pages[1];
|
||
UiKit.Clear(p);
|
||
var db = S.Db;
|
||
var d = S.Save;
|
||
UiKit.Label(p, "보물 상자 구매", 44, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -16f), new Vector2(500f, 70f));
|
||
UiKit.Label(p, "상자 포인트 " + d.inv.chestPoints + " · 열기 = 보물 > 상자", 28, UiKit.InkSoft, TextAnchor.MiddleRight, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-28f, -24f), new Vector2(560f, 54f), false);
|
||
ChestRowCount = 0;
|
||
for (int i = 0; i < db.Chests.chests.Count; i++)
|
||
{
|
||
var c = db.Chests.chests[i];
|
||
var row = UiKit.TopBand("Chest_" + c.id, p, 104f + i * 236f, 220f, 24f, 24f);
|
||
var img = UiKit.Paint(row, UiKit.Paper);
|
||
UiKit.Border(img, UiKit.Line, 3f);
|
||
var icon = UiKit.Box("Icon", row, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(20f, 0f), new Vector2(170f, 140f), TreasureScreen.ChestColor(c.id));
|
||
if (UiKit.Icon(icon.transform, "chest_" + c.id, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 8f), 120f) != null)
|
||
UiKit.Title(UiKit.Label(icon.transform, "×" + TreasureRules.ChestCount(d, c.id), 36, UiKit.White, TextAnchor.LowerRight, new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-8f, 2f), new Vector2(150f, 50f)), true);
|
||
else UiKit.FillLabel(icon.transform, "×" + TreasureRules.ChestCount(d, c.id), 40, UiKit.White);
|
||
var probs = new List<string>();
|
||
for (int g = 0; g < ChestDb.ProbGrades.Length; g++)
|
||
if (c.p[g] > 0f) probs.Add(TreasureRules.GradeKo(ChestDb.ProbGrades[g]) + " " + (c.p[g] * 100f).ToString("0.#") + "%");
|
||
UiKit.Label(row, c.nameKo, 40, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(210f, -16f), new Vector2(420f, 60f));
|
||
UiKit.Label(row, string.Join(" · ", probs.ToArray()) + "\n포인트 +" + c.points + " · 획득처 " + c.source.Replace(";", "·"), 24, UiKit.InkSoft, TextAnchor.UpperLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(210f, -80f), new Vector2(440f, 120f), false);
|
||
int col = 0;
|
||
foreach (var cur in new[] { "gold", "gem", "points" })
|
||
{
|
||
int price = TreasureRules.Price(db, d, c, cur);
|
||
if (price <= 0) continue;
|
||
string id = c.id, currency = cur;
|
||
string label = (cur == "gold" ? "골드 " : cur == "gem" ? "보옥 " : "포인트 ") + price.ToString("N0");
|
||
var b = UiKit.Btn("Buy_" + cur, row, label, 28, cur == "gold" ? UiKit.Coin : cur == "gem" ? UiKit.Jade : UiKit.Grain, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-16f - col * 186f, 0f), new Vector2(176f, 96f), () =>
|
||
{
|
||
var why = S.Do(() => TreasureRules.Buy(S.Db, S.Save, id, currency));
|
||
Root.Toast(why ?? S.Db.Chests.Get(id).nameKo + " +1 (보물 > 상자에서 열기)");
|
||
});
|
||
col++;
|
||
}
|
||
if (col == 0) UiKit.Label(row, c.source.Contains("event") ? "이벤트(적장 토벌)" : "판매 안 함", 28, UiKit.InkSoft, TextAnchor.MiddleRight, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-24f, 0f), new Vector2(300f, 60f), false);
|
||
ChestRowCount++;
|
||
}
|
||
UiKit.Btn("ToTreasure", p, "보물 > 상자 열기", 34, UiKit.Dark, UiKit.White, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 24f), new Vector2(560f, 96f), () =>
|
||
{
|
||
Root.Show(Root.Treasures);
|
||
Root.Treasures.SetPage(2);
|
||
});
|
||
}
|
||
|
||
public override void Refresh()
|
||
{
|
||
for (int i = 0; i < tabs.Count; i++)
|
||
{
|
||
bool on = i == page;
|
||
if (UISkin.On) UiKit.SetSubTab(tabs[i], on);
|
||
else
|
||
{
|
||
tabs[i].GetComponent<Image>().color = on ? UiKit.Red : UiKit.Paper;
|
||
UiKit.LabelOf(tabs[i]).color = on ? UiKit.White : UiKit.Ink;
|
||
}
|
||
pages[i].gameObject.SetActive(on);
|
||
}
|
||
if (page == 0) RefreshGacha();
|
||
else if (page == 1) RefreshChests();
|
||
else if (page == 2) RefreshShop();
|
||
}
|
||
}
|
||
}
|