ThreeKingdomsSlingshot/Assets/Scripts/OutGame/UI/TradeScreen.cs

383 lines
21 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연 무료 · 결과 = 카드 순차 공개)
/// ② 보물 상자 = 준비 중 ③ 상점·패스 = 골드 승급 장비 상점(흰/녹 · 일일 · 새로고침 보옥) · 패스 = 준비 중.
/// </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;
Header("거래", "뽑기 · 상자 · 상점 · 패스", true);
var body = Body();
string[] names = { "영웅·유닛 뽑기", "보물 상자", "상점·패스" };
for (int i = 0; i < names.Length; i++)
{
int k = i;
tabs.Add(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));
}
BuildGacha(pages[0]);
var chest = pages[1];
UiKit.Label(chest, "보물 상자\n<size=34>청동 · 은 · 금 · 명장 · 보옥 상자</size>\n\n<size=36>준비 중 (2차 · 시스템 기획 §8)</size>", 56, UiKit.Ink, TextAnchor.MiddleCenter,
new Vector2(0.5f, 0.6f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(900f, 500f));
BuildShop(pages[2]);
}
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);
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.Label(banner, "영웅·유닛 뽑기", 60, UiKit.White, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(36f, -110f), new Vector2(600f, 90f));
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), new Color(0.86f, 0.82f, 0.75f), 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), new Color(0.86f, 0.82f, 0.75f), 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;
pityText.text = d.gacha.pity + " / " + g.Pity + " · " + g.Pity + "회 ★★★ 확정";
UiKit.SetBar(pityBar, d.gacha.pity / (float)Mathf.Max(1, g.Pity));
bool free = GachaRules.FirstTenFree(S.Db, d);
freeChip.SetActive(free);
freeChip2.SetActive(free);
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));
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);
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 ? new Color(1f, 0.93f, 0.75f) : p.tier == 2 ? new Color(0.92f, 0.9f, 0.98f) : 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, 150f), new Vector2(640f, 104f), () =>
{
var why = S.RefreshShop();
Root.Toast(why ?? "상점 새로고침");
});
UiKit.Label(p, "청 이상 장비(보옥) · 성장 패스 · 월정액 · 챕터 팩 = 준비 중", 28, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 60f), new Vector2(1000f, 60f), false);
}
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");
UiKit.LabelOf(refreshBtn).text = "새로고침 · 보옥 " + 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 ? new Color(0.86f, 0.84f, 0.8f) : 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;
}
AddSkillBookCell(d);
}
/// <summary>스킬서 임시 판매(PD #1093 B · 하루 skill_book_shop_daily 개 · 골드 skill_book_price · 정식 획득처 = 사당 던전(C) · PD 확인 대상).</summary>
void AddSkillBookCell(SaveData d)
{
var e = S.Db.Economy;
int left = Mathf.Max(0, e.SkillBookShopDaily - SkillRules.BooksBoughtToday(d, S.Now));
var cell = UiKit.Cell(shopGrid, SkillBookCellName, left > 0 ? UiKit.Paper : new Color(0.86f, 0.84f, 0.8f), null);
UiKit.Border(cell, UiKit.Line, 2f);
var strip = UiKit.TopBand("Grade", cell.transform, 0f, 64f);
UiKit.Paint(strip, UiKit.Purple);
UiKit.FillLabel(strip, "스킬서 · 임시 판매", 28, UiKit.White);
UiKit.Label(cell.transform, "스킬서", 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.books.skill + " · 오늘 " + left + "/" + e.SkillBookShopDaily, 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, "골드 " + e.SkillBookPrice.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("BuyBook", cell.transform, left > 0 ? "구매" : "품절", 34, left > 0 ? UiKit.Red : UiKit.Disabled, UiKit.White, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 20f), new Vector2(260f, 84f), () =>
{
var why = SkillRules.BuyBook(S.Db, S.Save, S.Now);
if (why == null) S.Commit();
Root.Toast(why ?? "스킬서 +1 (보유 " + S.Save.inv.books.skill + ")");
Refresh();
});
b.interactable = left > 0;
}
/// <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;
}
public override void Refresh()
{
for (int i = 0; i < tabs.Count; i++)
{
bool on = i == page;
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 == 2) RefreshShop();
}
}
}