ThreeKingdomsSlingshot/Assets/Scripts/OutGame/Treasure/TreasureScreen.cs

359 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace TKS.OutGame
{
/// <summary>
/// 보물(하단 탭 · 기획 S8 · 목업 img07·16·17 · 카피바라 수집품 구조 · PD #1093 C) — 상단 탭 3:
/// ① 보유 중 = 그리드 4열 · 등급 색 · ★ · 미보유 회색 · 탭 = 상세(기본 효과 · 조건 효과 n/max · 강화 ★)
/// ② 세트 = 세트마다 구성 · 조각 게이지 · [세트]/[3성]/[6성]/[10성] 효과 ③ 상자 = 상자 포인트 게이지 → 명장 상자 · 선택 상자 + 확률 · 보유 수 · 열기(상자는 이 탭에서만 연다).
/// </summary>
public class TreasureScreen : OutGameScreen
{
public static readonly string[] PageNames = { "보유 중", "세트", "상자" };
int page;
int chestIdx;
readonly List<Button> tabs = new List<Button>();
readonly List<RectTransform> pages = new List<RectTransform>();
public int Page
{
get { return page; }
}
public int GridCount { get; private set; }
public int SetCount { get; private set; }
public ChestResult LastOpen { get; private set; }
public string SelectedChest
{
get
{
var l = S.Db.Chests.chests;
return l.Count > 0 ? l[Mathf.Clamp(chestIdx, 0, l.Count - 1)].id : "";
}
}
public override void Build()
{
Tab = 1;
Header("보물", "수집품 · 보유만으로 상시 발동", true);
var body = Body();
for (int i = 0; i < PageNames.Length; i++)
{
int k = i;
tabs.Add(UiKit.Btn("Tab" + i, body, PageNames[i], 36, 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));
}
}
public void SetPage(int p)
{
page = Mathf.Clamp(p, 0, pages.Count - 1);
Refresh();
}
public void SelectChest(string id)
{
int i = S.Db.Chests.IndexOf(id);
if (i >= 0) chestIdx = i;
Refresh();
}
public override void Refresh()
{
for (int i = 0; i < tabs.Count; i++)
{
bool on = i == page;
tabs[i].GetComponent<Image>().color = on ? UiKit.Dark : UiKit.Paper;
UiKit.LabelOf(tabs[i]).color = on ? UiKit.White : UiKit.Ink;
pages[i].gameObject.SetActive(on);
}
UiKit.Clear(pages[page]);
if (page == 0) BuildOwned(pages[0]);
else if (page == 1) BuildSets(pages[1]);
else BuildChests(pages[2]);
}
public static Color GradeColor(string g)
{
switch (g)
{
case "rare": return new Color(0.27f, 0.45f, 0.75f);
case "epic": return new Color(0.55f, 0.33f, 0.66f);
case "legend": return new Color(0.86f, 0.45f, 0.16f);
case "myth": return new Color(0.75f, 0.2f, 0.18f);
case "arcana": return new Color(0.78f, 0.62f, 0.16f);
}
return UiKit.Slot;
}
/// <summary>보물 칸(아이콘 = 이름 첫 글자 · ★ · 등급 띠 · 미보유 = 회색 「?」).</summary>
void TreasureCell(Transform parent, TreasureDb.Item it, bool showShards)
{
var st = S.Save.inv.Treasure(it.id);
bool owned = st != null;
var cell = UiKit.Cell(parent, "T_" + it.id, owned ? UiKit.Paper : new Color(0.85f, 0.82f, 0.77f), owned ? (System.Action)(() => Detail(it.id)) : null);
UiKit.Border(cell, owned ? GradeColor(it.grade) : new Color(0.7f, 0.66f, 0.62f), 3f);
var icon = UiKit.Dot("Icon", cell.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -14f), 120f, owned ? new Color(0.94f, 0.9f, 0.83f) : new Color(0.78f, 0.74f, 0.69f));
UiKit.FillLabel(icon.transform, owned ? it.nameKo.Substring(0, 1) : "?", 60, owned ? GradeColor(it.grade) : new Color(0.6f, 0.56f, 0.52f));
if (owned)
{
if (showShards && st.star < it.starMax)
{
int need, iron;
TreasureRules.EnhanceCost(S.Db, it, st.star + 1, out need, out iron);
var fill = UiKit.Bar("Shards", cell.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -142f), new Vector2(150f, 26f), new Color(0.3f, 0.27f, 0.25f), new Color(0.38f, 0.62f, 0.86f));
UiKit.SetBar(fill, st.shards / (float)Mathf.Max(1, need));
UiKit.FillLabel(fill.parent, st.shards + "/" + need, 18, UiKit.White);
}
else UiKit.Label(cell.transform, st.star > 0 ? "★" + st.star : "☆", 32, UiKit.Star, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -140f), new Vector2(200f, 40f));
}
var band = UiKit.Box("Name", cell.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 10f), new Vector2(210f, 56f), owned ? GradeColor(it.grade) : new Color(0.62f, 0.58f, 0.55f));
UiKit.FillLabel(band.transform, owned ? it.nameKo : "미보유", 26, UiKit.White);
}
// ---------- ① 보유 중 ----------
void BuildOwned(RectTransform p)
{
var db = S.Db;
int owned = 0;
foreach (var it in db.Treasures.items)
if (S.Save.inv.Treasure(it.id) != null) owned++;
UiKit.Label(p, "보유 " + owned + " / " + db.Treasures.items.Count, 36, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -8f), new Vector2(360f, 56f));
var grades = db.Economy.TreasureGrades;
for (int i = 0; i < grades.Count; i++)
{
var chip = UiKit.Box("G" + i, p, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-24f - (grades.Count - 1 - i) * 118f, -12f), new Vector2(110f, 48f), GradeColor(grades[i].Key));
UiKit.FillLabel(chip.transform, TreasureRules.GradeKo(grades[i].Key), 24, UiKit.White);
}
ScrollRect sr;
var grid = UiKit.ScrollGrid("Grid", p, 24f, 24f, 76f, 64f, new Vector2(240f, 290f), new Vector2(16f, 16f), 4, out sr);
// 정렬 = 보유 먼저 · 등급 높은 순
var list = new List<TreasureDb.Item>(db.Treasures.items);
list.Sort((a, b) =>
{
bool oa = S.Save.inv.Treasure(a.id) != null, ob = S.Save.inv.Treasure(b.id) != null;
if (oa != ob) return oa ? -1 : 1;
return db.Economy.TreasureGradeOrder(b.grade).CompareTo(db.Economy.TreasureGradeOrder(a.grade));
});
GridCount = 0;
foreach (var it in list)
{
TreasureCell(grid, it, false);
GridCount++;
}
var bonus = new List<string>();
foreach (var s in TreasureDb.BaseStats)
{
float v = TreasureRules.StatPct(db, S.Save, s);
if (v > 0f) bonus.Add(TreasureRules.StatKo(s) + " +" + v.ToString("0.#") + "%");
}
UiKit.Label(p, bonus.Count > 0 ? "전체 효과 " + string.Join(" · ", bonus.ToArray()) : "보물을 모으면 전 유닛에 상시 효과", 26, UiKit.InkSoft, TextAnchor.MiddleCenter,
new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 8f), new Vector2(1030f, 50f), false);
}
public void Detail(string id)
{
var db = S.Db;
var d = S.Save;
var it = db.Treasures.Get(id);
var st = d.inv.Treasure(id);
if (it == null || st == null) return;
var sb = new System.Text.StringBuilder();
sb.Append(TreasureRules.GradeKo(it.grade)).Append(" · ★").Append(st.star).Append("/").Append(it.starMax);
var tiers = it.setId.Length > 0 ? db.TreasureSets.TiersOf(it.setId) : null;
if (tiers != null && tiers.Count > 0) sb.Append(" · 세트 「").Append(tiers[0].nameKo).Append("」");
sb.Append("\n\n<b>기본 효과</b> ").Append(TreasureRules.StatKo(it.baseStat)).Append(" +").Append(TreasureRules.BasePct(db, it, st.star).ToString("0.##")).Append("%");
int stacks = TreasureRules.CondStacks(d, it);
sb.Append("\n<b>조건 효과</b> ").Append(TreasureRules.CondKo(it.condKey)).Append(" ").Append(it.condEvery).Append("회마다 +").Append(it.condPct.ToString("0.##")).Append("% (")
.Append(stacks).Append("/").Append(it.condMax).Append(" · 누적 ").Append(TreasureRules.Counter(d, it.condKey)).Append(")");
sb.Append("\n<size=28>= 지금 ").Append(TreasureRules.StatKo(it.baseStat)).Append(" +").Append(TreasureRules.Value(db, d, it).ToString("0.##")).Append("%</size>");
string btn;
var why = TreasureRules.CanEnhance(db, d, id);
if (st.star < it.starMax)
{
int shards, iron;
TreasureRules.EnhanceCost(db, it, st.star + 1, out shards, out iron);
sb.Append("\n\n<b>강화 ★").Append(st.star + 1).Append("</b> 조각 ").Append(st.shards).Append("/").Append(shards).Append(" · 운철 ").Append(d.inv.iron).Append("/").Append(iron);
btn = why == null ? "강화" : why;
}
else btn = "최고 ★";
Root.Modal(it.nameKo, sb.ToString(),
new OutGameRoot.ModalButton(btn, why == null ? UiKit.Red : UiKit.Disabled, () =>
{
var w = S.Do(() => TreasureRules.Enhance(S.Db, S.Save, id));
if (w == null) Detail(id);
else Root.Toast(w);
}, true),
new OutGameRoot.ModalButton("닫기", UiKit.Dark, null));
}
// ---------- ② 세트 ----------
void BuildSets(RectTransform p)
{
var db = S.Db;
var d = S.Save;
ScrollRect sr;
var list = UiKit.ScrollGrid("Sets", p, 24f, 24f, 6f, 12f, new Vector2(1032f, 640f), new Vector2(0f, 24f), 1, out sr);
SetCount = 0;
foreach (var setId in db.TreasureSets.SetIds)
{
var tiers = db.TreasureSets.TiersOf(setId);
if (tiers.Count == 0) continue;
var head = tiers[0];
var block = UiKit.Cell(list, "Set_" + setId, new Color(0f, 0f, 0f, 0f), null);
block.raycastTarget = false;
var band = UiKit.Box("Band", block.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(700f, 70f), new Color(0.47f, 0.33f, 0.24f));
UiKit.FillLabel(band.transform, head.nameKo, 40, UiKit.White);
int owned = TreasureRules.OwnedIn(d, head);
UiKit.Label(block.transform, "보유 " + owned + "/" + head.members.Count, 30, owned == head.members.Count ? UiKit.Ink : UiKit.Red, TextAnchor.MiddleRight, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(0f, -10f), new Vector2(200f, 50f));
var row = UiKit.Rect("Members", block.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -84f), new Vector2(1032f, 290f));
var hl = row.gameObject.AddComponent<HorizontalLayoutGroup>();
hl.spacing = 12f;
hl.childAlignment = TextAnchor.UpperCenter;
hl.childControlWidth = hl.childControlHeight = false;
foreach (var m in head.members)
{
var it = db.Treasures.Get(m);
if (it == null) continue;
var holder = UiKit.Rect("M", row, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(196f, 290f));
TreasureCell(holder, it, true);
var c = holder.GetChild(0) as RectTransform;
c.anchorMin = Vector2.zero;
c.anchorMax = Vector2.one;
c.offsetMin = c.offsetMax = Vector2.zero;
}
var fx = UiKit.Box("Effects", block.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), Vector2.zero, new Vector2(1032f, 250f), new Color(0.96f, 0.93f, 0.87f));
UiKit.Border(fx, UiKit.Line, 2f);
var best = TreasureRules.SetTier(db, d, setId);
var sb = new System.Text.StringBuilder("<b>기본 효과</b> <size=26>(가장 높은 달성 단계 1개)</size>");
foreach (var t in tiers)
{
bool ok = TreasureRules.TierReached(d, t);
string label = t.stars == 0 ? "[세트]" : "[" + t.stars + "성]";
string stat = TreasureRules.StatKo(t.effectKey.Replace("_pct", ""));
sb.Append("\n").Append(ok ? (t == best ? "<color=#2c6a26><b>" : "<color=#2c6a26>") : "<color=#8a8078>").Append(label).Append(" ").Append(stat).Append(" +").Append(t.pct.ToString("0.#")).Append("%")
.Append(ok ? (t == best ? " ✔ 적용</b>" : " 달성") : " 미달성").Append("</color>");
}
UiKit.FillLabel(fx.transform, sb.ToString(), 30, UiKit.Ink, TextAnchor.MiddleLeft, false, 24f);
SetCount++;
}
}
// ---------- ③ 상자 ----------
void BuildChests(RectTransform p)
{
var db = S.Db;
var d = S.Save;
var e = db.Economy;
var goalBox = UiKit.TopBand("Points", p, 6f, 150f, 24f, 24f);
var gimg = UiKit.Paint(goalBox, UiKit.Paper);
UiKit.Border(gimg, UiKit.Line, 3f);
var pd = UiKit.Dot("P", goalBox, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(20f, 0f), 90f, UiKit.Coin);
UiKit.FillLabel(pd.transform, "P", 44, UiKit.Ink);
var reward = db.Chests.Get(e.ChestPointReward);
UiKit.Label(goalBox, (e.ChestPointGoal - d.inv.chestPoints) + "포인트 더 모으면 " + (reward != null ? reward.nameKo : ""), 34, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(130f, -12f), new Vector2(800f, 54f));
var fill = UiKit.Bar("Bar", goalBox, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(130f, 52f), new Vector2(700f, 30f), new Color(0.88f, 0.84f, 0.77f), UiKit.Coin);
UiKit.SetBar(fill, d.inv.chestPoints / (float)Mathf.Max(1, e.ChestPointGoal));
UiKit.Label(goalBox, "상자 포인트 " + d.inv.chestPoints + " / " + e.ChestPointGoal, 26, UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(130f, 8f), new Vector2(700f, 40f), false);
var c = db.Chests.Get(SelectedChest);
if (c == null) return;
int count = TreasureRules.ChestCount(d, c.id);
var banner = UiKit.Box("Banner", p, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -180f), new Vector2(560f, 470f), ChestColor(c.id));
UiKit.Border(banner, new Color(0.25f, 0.2f, 0.18f), 4f);
UiKit.Label(banner.transform, c.nameKo, 60, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -40f), new Vector2(520f, 90f));
int line = 0;
for (int i = 0; i < ChestDb.ProbGrades.Length; i++)
{
if (c.p[i] <= 0f) continue;
var chip = UiKit.Box("P" + i, banner.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -150f - line * 62f), new Vector2(360f, 54f), GradeColor(ChestDb.ProbGrades[i]));
UiKit.FillLabel(chip.transform, TreasureRules.GradeKo(ChestDb.ProbGrades[i]) + " " + (c.p[i] * 100f).ToString("0.#") + " %", 30, UiKit.White);
line++;
}
var extra = new List<string>();
if (c.goldMax > 0) extra.Add("골드 " + c.goldMin + "~" + c.goldMax);
if (c.gemMax > 0) extra.Add("보옥 " + c.gemMin + "~" + c.gemMax);
if (c.ironMax > 0) extra.Add("운철 " + c.ironMin + "~" + c.ironMax);
extra.Add("포인트 +" + c.points);
UiKit.Label(banner.transform, string.Join(" · ", extra.ToArray()), 26, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 18f), new Vector2(540f, 50f), false);
UiKit.Btn("Prev", p, "◁", 70, new Color(0f, 0f, 0f, 0f), UiKit.Ink, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(40f, -360f), new Vector2(120f, 140f), () => Step(-1));
UiKit.Btn("Next", p, "▷", 70, new Color(0f, 0f, 0f, 0f), UiKit.Ink, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-40f, -360f), new Vector2(120f, 140f), () => Step(1));
var open = UiKit.Btn("Open", p, count > 0 ? "열기\n<size=28>보유 " + count + "</size>" : "열기\n<size=28>상자 없음</size>", 46, count > 0 ? UiKit.Red : UiKit.Disabled, UiKit.White,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -680f), new Vector2(480f, 140f), () => DoOpen(SelectedChest));
open.interactable = count > 0;
UiKit.Label(p, "상자는 이 탭에서만 엽니다 · 중복 = 조각 · 천기 = 적장 토벌 전용", 26, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -836f), new Vector2(1000f, 44f), false);
int n = db.Chests.chests.Count;
float w = (1032f - (n - 1) * 12f) / n;
for (int i = 0; i < n; i++)
{
var ch = db.Chests.chests[i];
int k = i;
var b = UiKit.Btn("Chest_" + ch.id, p, "", 28, i == chestIdx ? new Color(1f, 0.95f, 0.8f) : UiKit.Paper, UiKit.Ink, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(24f + i * (w + 12f), 24f), new Vector2(w, 220f), () =>
{
chestIdx = k;
Refresh();
});
UiKit.Border(b.GetComponent<Image>(), i == chestIdx ? UiKit.Coin : UiKit.Line, i == chestIdx ? 5f : 2f);
var box = UiKit.Box("Icon", b.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -20f), new Vector2(w * 0.7f, 90f), ChestColor(ch.id));
box.raycastTarget = false;
UiKit.Label(b.transform, ch.nameKo + "\n<size=34>×" + TreasureRules.ChestCount(d, ch.id) + "</size>", 26, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 14f), new Vector2(w, 100f));
}
}
void Step(int dir)
{
int n = Mathf.Max(1, S.Db.Chests.chests.Count);
chestIdx = ((chestIdx + dir) % n + n) % n;
Refresh();
}
public void DoOpen(string id)
{
var r = S.OpenChest(id);
LastOpen = r;
if (r == null)
{
Root.Toast("상자 없음");
return;
}
var it = r.treasureId != null ? S.Db.Treasures.Get(r.treasureId) : null;
var sb = new System.Text.StringBuilder();
if (it != null)
sb.Append("<size=48><b>").Append(it.nameKo).Append("</b></size>\n").Append(TreasureRules.GradeKo(it.grade)).Append(r.gradeUp ? " (상위 등급!)" : "")
.Append(r.isNew ? " <color=#b0301e>NEW</color>" : " 조각 +" + r.shards).Append("\n\n");
var parts = new List<string>();
if (r.gold > 0) parts.Add("골드 +" + r.gold.ToString("N0"));
if (r.gem > 0) parts.Add("보옥 +" + r.gem.ToString("N0"));
if (r.iron > 0) parts.Add("운철 +" + r.iron);
parts.Add("상자 포인트 +" + r.points);
sb.Append(string.Join(" · ", parts.ToArray()));
if (r.bonusChests > 0) sb.Append("\n<color=#b0301e>포인트 달성 → ").Append(S.Db.Chests.Get(S.Db.Economy.ChestPointReward).nameKo).Append(" +").Append(r.bonusChests).Append("</color>");
Root.Modal(S.Db.Chests.Get(id).nameKo + " 열기", sb.ToString(),
new OutGameRoot.ModalButton("한 번 더", UiKit.Red, () => DoOpen(id)),
new OutGameRoot.ModalButton("확인", UiKit.Dark, null));
}
public static Color ChestColor(string id)
{
switch (id)
{
case "bronze": return new Color(0.69f, 0.47f, 0.3f);
case "silver": return new Color(0.66f, 0.68f, 0.72f);
case "gold": return new Color(0.88f, 0.7f, 0.22f);
case "general": return new Color(0.62f, 0.25f, 0.22f);
case "gem": return new Color(0.3f, 0.65f, 0.57f);
}
return UiKit.Slot;
}
}
}