ThreeKingdomsSlingshot/Assets/Scripts/OutGame/Arena/ArenaScreen.cs

455 lines
29 KiB
C#
Raw Permalink 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>
/// 연무장(로비 출정 우측 · 기획 S13 · 목업 img09 · PD #1093 C) — 첫 페이지 = 모드 카드 4(규칙 · 보상 · 입장) → 카드 탭 = 모드 페이지:
/// ① 천하제일 탑(다음 층 · 적 템플릿 · 배율 · 보상 · 입장권 + 광고) ② 일일 던전 3(요일 · 남은 입장 · 보상) ③ 투기장(메달 · 주간 순위 · 샘플 상대 10 · 메달 상점)
/// ④ 적장 토벌(이번 주 보스 · 누적 피해 · 하루 1). 도전 = OutGameRoot.StartMode → BattleManager.StartMode(적 편성 · 배율 · 제한 턴) → 결과 패널 → 탭 = 이 페이지로.
/// </summary>
public class ArenaScreen : OutGameScreen
{
public static readonly string[] ModeNames = { "천하제일 탑", "일일 던전 3", "투기장", "적장 토벌" };
static readonly string[] Ribbons = { "ribbon_blue", "ribbon_purple", "ribbon_red", "ribbon_orange" }; // 카드 리본(설계서 §3⑧ · 테마 스킨 id)
int page;
RectTransform root;
/// <summary>0 = 카드 · 1 탑 · 2 던전 · 3 투기장 · 4 적장 토벌.</summary>
public int Page
{
get { return page; }
}
public override void Build()
{
if (UISkin.On) UiKit.Paint(UiKit.Fill("ListBg", Rt), UiKit.Col("list_bg", UiKit.Bg), false); // 목록형 바탕(Fable §5-3)
Header("연무장", "탑 · 일일 던전 · 투기장 · 적장 토벌", true);
root = Body();
}
public void SetPage(int p)
{
page = Mathf.Clamp(p, 0, 4);
Refresh();
}
public override void OnShow()
{
S.Do(() =>
{
ArenaRules.RollDaily(S.Db, S.Save, S.Now);
ArenaRules.EnsureSeason(S.Db, S.Save, S.Now, S.Rng);
ArenaRules.RaidBoss(S.Db, S.Save, S.Now);
return null;
});
}
public override bool OnBack()
{
if (page == 0) return false;
SetPage(0);
return true;
}
public override void Refresh()
{
UiKit.Clear(root);
switch (page)
{
case 0: BuildCards(); break;
case 1: BuildTower(); break;
case 2: BuildDungeons(); break;
case 3: BuildArena(); break;
case 4: BuildRaid(); break;
}
}
// ---------- 카드 4 ----------
void BuildCards()
{
var db = S.Db;
var d = S.Save;
var e = db.Economy;
var now = S.Now;
string boss = db.Heroes.Get(ArenaRules.RaidBoss(db, d, now)) != null ? db.Heroes.Get(ArenaRules.RaidBoss(db, d, now)).nameKo : "";
string[,] text =
{
{ "100층 · 층마다 고정 편성 적 · 10층마다 보스", "보옥 · 도면 · 10층마다 보물 금 상자", "입장권 하루 " + e.TowerTicketsDaily + "(광고 +" + e.TowerAdTickets + ") · 진행 저장", "다음 " + ArenaRules.TowerNext(db, d) + "층 · 입장권 " + d.arena.towerTickets },
{ "군량고(골드) · 대장간(승급 장비·운철) · 사당(스킬서·영웅 조각) — 요일 순환", "던전별 재화", "각 하루 " + (db.Dungeons.dungeons.Count > 0 ? db.Dungeons.dungeons[0].entriesPerDay : 0) + "(패스 +1 준비 중)", "오늘 열림 " + OpenCount(now) + "곳" },
{ "비동기 5v5 · 상대 편성은 AI 가 조작(적 AI 재사용) · 주간 랭킹", "투기장 메달 → 영웅 조각 상점", "하루 " + db.Arena.EntriesPerDay, "메달 " + d.arena.medals + " · " + ArenaRules.Rank(d) + "위 · 남은 " + ArenaRules.ArenaLeft(db, d, now) },
{ "주간 보스(여포·조조 …) · 누적 피해", "보물 명장 상자", "하루 " + e.RaidDaily, "이번 주 " + boss + " · 누적 " + d.arena.raidDamage.ToString("N0") },
};
float w = 508f, h = 700f;
if (UISkin.Has("card4_bg") && UISkin.Has("btn_l_yellow"))
{
CardsKit(text, w, h);
return;
}
for (int i = 0; i < 4; i++)
{
int k = i;
float x = 24f + (i % 2) * (w + 16f);
float y = -18f - (i / 2) * (h + 20f);
var card = UiKit.Btn("Card" + i, root, "", 30, UiKit.Paper, UiKit.Ink, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, y), new Vector2(w, h), () => SetPage(k + 1));
if (UISkin.Has("card4_bg") && UISkin.Has(Ribbons[i]))
{
// 카드(설계서 §3⑧) = 팩 CardFrame04 카드색 + 빛 + 테두리 · 글자 판 · 리본 제목
var ci = card.GetComponent<Image>();
UISkin.Apply(ci, "card4_bg");
ci.color = UiKit.Col("arena_" + i, UiKit.Paper);
UiKit.Layer(card.transform, "Glow", "card4_glow", UiKit.Col("arena_glow", UiKit.White), 0f, 0f, 0f, h * 0.5f);
UiKit.Layer(card.transform, "Inner", "panel12", UiKit.Col("arena_inner", UiKit.Paper), 14f, 112f, 14f, 100f);
UiKit.Layer(card.transform, "Border", "card4_border", UiKit.Col("card_border", UiKit.Line));
var rib = UiKit.Skin(UiKit.Rect("Ribbon", card.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 4f), new Vector2(w - 40f, 96f)), Ribbons[i], UiKit.White);
var rt = UiKit.Title(UiKit.FillLabel(rib.transform, ModeNames[i] + (i == 3 ? " <size=24>(이벤트)</size>" : ""), 42, UiKit.White, TextAnchor.MiddleCenter, true, 40f), true);
rt.rectTransform.offsetMin = new Vector2(rt.rectTransform.offsetMin.x, 16f);
}
else
{
UiKit.Border(card.GetComponent<Image>(), UiKit.Line, 3f);
var head = UiKit.TopBand("Head", card.transform, 0f, 110f);
UiKit.Paint(head, UiKit.Red);
UiKit.FillLabel(head, ModeNames[i] + (i == 3 ? "\n<size=26>(이벤트)</size>" : ""), 44, UiKit.White);
}
string[] tags = { "규칙", "보상", "입장" };
for (int t = 0; t < 3; t++)
{
float ty = -128f - t * 160f;
var tag = UiKit.Box("Tag" + t, card.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, ty), new Vector2(96f, 44f), UiKit.Dark);
tag.raycastTarget = false;
UiKit.FillLabel(tag.transform, tags[t], 24, UiKit.White);
UiKit.Label(card.transform, text[i, t], 28, UiKit.Ink, TextAnchor.UpperLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, ty - 52f), new Vector2(w - 40f, 104f), false);
}
var st = UiKit.Box("Status", card.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 16f), new Vector2(w - 32f, 70f), UiKit.Dark);
st.raycastTarget = false;
UiKit.FillLabel(st.transform, text[i, 3], 26, UiKit.Red);
}
}
// 카드 그림 · 요약 · 보상 아이콘(테마 id · Fable §4-6 · 일러 N4 전까지 픽토그램 임시)
static readonly string[] Arts = { "arena_art_0", "arena_art_1", "arena_art_2", "arena_art_3" };
static readonly string[] Briefs = { "층마다 고정 편성 · 10층마다 보스", "요일 순환 재화 던전", "비동기 5v5 · 주간 랭킹", "주간 보스 · 누적 피해" };
static readonly string[][] RewardIcons =
{
new[] { "cur_gem", "cur_blueprint", "chest_gold" },
new[] { "cur_gold", "cur_iron", "cur_skillbook" },
new[] { "gear_flag", "cur_shard" },
new[] { "chest_general" },
};
static readonly string[] EntryIcons = { "cur_ticket", "lobby_event", "sword", "fire" };
/// <summary>
/// 모드 카드(Fable 제안서 §4-6 · #1104) = CardFrame04 카드색 + 리본 제목(유지) · 위 일러 자리(픽토그램 220 흰 25 %) + 요약 한 줄 ·
/// 아래 어두운 판에 칩 2줄(입장 = 아이콘 + 값 · 보상 = 아이템 칸 72) · 상태 칩 · 규칙 문장 → ⓘ 팝업 · 이벤트 = 오른쪽 위 뱃지.
/// </summary>
void CardsKit(string[,] text, float w, float h)
{
var d = S.Save;
var db = S.Db;
var now = S.Now;
string[] entry =
{
"입장권 " + d.arena.towerTickets + " / " + db.Economy.TowerTicketsDaily,
"오늘 " + OpenCount(now) + "곳 · 각 " + (db.Dungeons.dungeons.Count > 0 ? db.Dungeons.dungeons[0].entriesPerDay : 0) + "회",
"남은 " + ArenaRules.ArenaLeft(db, d, now) + " / " + db.Arena.EntriesPerDay,
"하루 " + db.Economy.RaidDaily + "회",
};
var soft = UiKit.Col("text_soft", UiKit.InkSoft);
for (int i = 0; i < 4; i++)
{
int k = i;
float x = 24f + (i % 2) * (w + 16f);
float y = 18f + (i / 2) * (h + 20f);
var ci = UiKit.Skin(UiKit.At(root, "Card" + i, x, y, w, h), "card4_bg", UiKit.Col("arena_" + i, UiKit.Paper), true);
var card = ci.transform;
card.gameObject.AddComponent<Button>().onClick.AddListener(() => SetPage(k + 1));
UiKit.Layer(card, "Glow", "card4_glow", UiKit.Col("arena_glow", UiKit.White), 0f, 0f, 0f, h * 0.5f);
// 일러 자리 = 픽토그램(흰 25 %)
var art = UiKit.Icon(card, Arts[i], new Vector2(0.5f, 1f), new Vector2(0.5f, 0.5f), new Vector2(0f, -232f), 220f);
if (art != null) art.color = UiKit.Col("arena_art", new Color(1f, 1f, 1f, 0.25f));
var brief = UiKit.Title(UiKit.Label(card, Briefs[i], 28, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -356f), new Vector2(w - 40f, 44f)), true);
brief.horizontalOverflow = HorizontalWrapMode.Overflow;
// 칩 2줄 판
var board = UiKit.Skin(UiKit.At(card, "Inner", 14f, 414f, w - 28f, 182f), "panel12", UiKit.Col("arena_inner", UiKit.Paper)).transform;
UiKit.Label(board, "입장", 26, soft, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, -14f), new Vector2(80f, 56f));
UiKit.InfoChip(board, "Entry", 96f, 16f, w - 28f - 96f - 18f, 52f, EntryIcons[i], entry[i], 26);
UiKit.Label(board, "보상", 26, soft, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, -98f), new Vector2(80f, 72f));
for (int r = 0; r < RewardIcons[i].Length; r++)
UiKit.RewardTile(board, "Rw" + r, 96f + r * 86f, 92f, 76f, "gear_white_", RewardIcons[i][r], null, false);
// 상태 칩
var st = UiKit.Skin(UiKit.At(card, "Status", 16f, h - 86f, w - 32f, 66f), "chip", UiKit.Col("chip_dark", UiKit.Dark));
var stt = UiKit.Title(UiKit.FillLabel(st.transform, text[i, 3], 26, UiKit.Col("lv_fill", UiKit.Coin), TextAnchor.MiddleCenter, true, 16f), true);
stt.horizontalOverflow = HorizontalWrapMode.Overflow;
// 리본 제목(맨 위) · 이벤트 뱃지 · ⓘ
var rib = UiKit.Skin(UiKit.Rect("Ribbon", card, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 4f), new Vector2(w - 40f, 96f)), Ribbons[i], UiKit.White);
var rt = UiKit.Title(UiKit.FillLabel(rib.transform, ModeNames[i], 42, UiKit.White, TextAnchor.MiddleCenter, true, 40f), true);
rt.rectTransform.offsetMin = new Vector2(rt.rectTransform.offsetMin.x, 16f);
if (i == 3)
{
// 「이벤트」 = 카드 안 오른쪽 위 꼬리 칩(카드 밖 돌출 0 · 검수2 결함 7)
var ev = UiKit.Skin(UiKit.Rect("Event", card, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-20f, -102f), new Vector2(120f, 44f)), "tail", UiKit.Col("tail_red", UiKit.Red));
UiKit.Title(UiKit.FillLabel(ev.transform, "이벤트", 22, UiKit.White), true);
}
var info = UiKit.Rect("Info", card, new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(46f, -122f), new Vector2(60f, 60f));
UiKit.Skin(info, "badge_circle", UiKit.Col("chip_dark", UiKit.Dark), true);
if (UiKit.Icon(info, "info", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 32f) == null) UiKit.Title(UiKit.FillLabel(info, "i", 30, UiKit.White), true);
string rules = "<b>규칙</b> " + text[i, 0] + "\n\n<b>보상</b> " + text[i, 1] + "\n\n<b>입장</b> " + text[i, 2];
info.gameObject.AddComponent<Button>().onClick.AddListener(() => Root.Modal(ModeNames[k], rules, new OutGameRoot.ModalButton("확인", UiKit.Dark, null)));
}
}
int OpenCount(System.DateTime now)
{
int n = 0;
foreach (var dg in S.Db.Dungeons.dungeons)
if (ArenaRules.OpenToday(dg, now)) n++;
return n;
}
RectTransform Panel(string name, float top, float h)
{
var band = UiKit.TopBand(name, root, top, h, 24f, 24f);
var img = UiKit.Paint(band, UiKit.Paper);
UiKit.Border(img, UiKit.Line, 3f);
return band;
}
void Title(string t)
{
var band = UiKit.TopBand("Title", root, 12f, 96f, 24f, 24f);
UiKit.Paint(band, UiKit.Red);
UiKit.FillLabel(band, t, 48, UiKit.White);
}
// ---------- ① 천하제일 탑 ----------
void BuildTower()
{
var db = S.Db;
var d = S.Save;
Title("천하제일 탑");
int next = ArenaRules.TowerNext(db, d);
var f = db.Tower.Get(next);
var stage = ArenaRules.TowerStage(db, Root.Battle.Config, next);
var info = Panel("Floor", 124f, 520f);
UiKit.Label(info, next + "층", 110, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -20f), new Vector2(600f, 140f));
var sb = new System.Text.StringBuilder();
sb.Append("클리어 최고 ").Append(d.arena.towerFloor).Append("층 / ").Append(db.Tower.Top).Append("층\n");
if (f != null)
{
sb.Append("적 편성 = ").Append(f.templateStage).Append(stage != null ? " 「" + stage.subtitle + "」 · " + stage.enemies.Count + "기" : " (템플릿 없음)").Append("\n");
sb.Append("적 HP ×").Append(f.hpMult.ToString("0.00")).Append(" · 공격 ×").Append(f.atkMult.ToString("0.00")).Append("\n");
sb.Append("보상 보옥 ").Append(f.rewardGem).Append(" · 도면 ").Append(f.rewardBlueprint);
if (f.chest.Length > 0) sb.Append(" · ").Append(db.Chests.Get(f.chest).nameKo);
}
UiKit.Label(info, sb.ToString(), 34, UiKit.Ink, TextAnchor.UpperCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -180f), new Vector2(980f, 320f), false).lineSpacing = 1.2f;
var tk = Panel("Tickets", 664f, 150f);
UiKit.Label(tk, "입장권 " + d.arena.towerTickets + " <size=28>(매일 " + db.Economy.TowerTicketsDaily + "장까지 채움 · 입장 = 1장)</size>", 40, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(24f, 0f), new Vector2(720f, 80f));
var ad = UiKit.Btn("Ad", tk, "광고 +" + db.Economy.TowerAdTickets + "\n<size=24>(시험 · " + d.dailyC.towerAd + "/1)</size>", 32, UiKit.Grain, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-16f, 0f), new Vector2(240f, 110f), () =>
Root.Toast(S.Do(() => ArenaRules.TowerAd(S.Db, S.Save, S.Now)) ?? "입장권 +" + S.Db.Economy.TowerAdTickets + " (광고 SDK 미연동 · 시험 지급)"));
ad.interactable = d.dailyC.towerAd < 1;
var why = ArenaRules.CanTower(db, d, S.Now);
if (stage == null && why == null) why = "템플릿 스테이지 없음";
var go = UiKit.Btn("Go", root, why == null ? "도전 — " + next + "층" : why, 48, why == null ? UiKit.Red : UiKit.Disabled, UiKit.White, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -840f), new Vector2(1032f, 150f), () =>
{
var w = S.Do(() => ArenaRules.EnterTower(S.Db, S.Save, S.Now));
if (w != null)
{
Root.Toast(w);
return;
}
Root.StartMode(ArenaRules.Tower, "", next, ArenaRules.TowerStage(S.Db, Root.Battle.Config, next), 0, 1);
});
go.interactable = why == null;
UiKit.Label(root, "진행 저장 · 패배해도 층 유지(입장권만 소모)", 28, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -1010f), new Vector2(1000f, 50f), false);
}
// ---------- ② 일일 던전 ----------
void BuildDungeons()
{
var db = S.Db;
var d = S.Save;
var now = S.Now;
Title("일일 던전");
var today = DungeonDb.Weekdays[(int)now.ToLocalTime().DayOfWeek];
UiKit.Label(root, "오늘 = " + DayKo(today) + "요일 · 요일 순환 · 승리 시 입장 1회 소모", 30, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -116f), new Vector2(1000f, 50f), false);
for (int i = 0; i < db.Dungeons.dungeons.Count; i++)
{
var dg = db.Dungeons.dungeons[i];
bool open = ArenaRules.OpenToday(dg, now);
int left = ArenaRules.DungeonLeft(db, d, dg, now);
var box = Panel("Dungeon_" + dg.id, 180f + i * 330f, 310f);
box.GetComponent<Image>().color = open ? UiKit.Paper : UiKit.Slot;
UiKit.Label(box, dg.nameKo, 52, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -16f), new Vector2(400f, 76f));
var days = new List<string>();
foreach (var w in dg.weekdays) days.Add(w == today ? "<color=#b0301e>" + DayKo(w) + "</color>" : DayKo(w));
UiKit.Label(box, "요일 " + string.Join(" · ", days.ToArray()), 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -100f), new Vector2(600f, 50f), false);
var rw = new List<string>();
foreach (var k in dg.rewardKinds) rw.Add(RewardKo(k) + " " + dg.Amount(k));
UiKit.Label(box, "보상 " + string.Join(" · ", rw.ToArray()), 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -152f), new Vector2(640f, 50f), false);
UiKit.Label(box, "적 = " + dg.templateStage + " 편성 · HP ×" + dg.hpMult.ToString("0.0#"), 28, UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -204f), new Vector2(640f, 50f), false);
string id = dg.id;
var why = ArenaRules.CanDungeon(db, d, id, now);
var b = UiKit.Btn("Go", box, open ? "입장\n<size=26>남은 " + left + "/" + dg.entriesPerDay + "</size>" : "닫힘", 40, why == null ? UiKit.Red : UiKit.Disabled, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-24f, 0f), new Vector2(300f, 170f), () =>
{
var w = ArenaRules.CanDungeon(S.Db, S.Save, id, S.Now);
if (w != null)
{
Root.Toast(w);
return;
}
Root.StartMode(ArenaRules.Dungeon, id, 0, ArenaRules.DungeonStage(S.Db, Root.Battle.Config, id), 0, 2);
});
b.interactable = why == null;
}
}
static string DayKo(string w)
{
switch (w)
{
case "sun": return "일";
case "mon": return "월";
case "tue": return "화";
case "wed": return "수";
case "thu": return "목";
case "fri": return "금";
case "sat": return "토";
}
return w;
}
static string RewardKo(string k)
{
switch (k)
{
case "gold": return "골드";
case "gear": return "승급 장비";
case "iron": return "운철";
case "skill_book": return "스킬서";
case "shards": return "영웅 조각";
}
return k;
}
// ---------- ③ 투기장 ----------
void BuildArena()
{
var db = S.Db;
var d = S.Save;
var now = S.Now;
Title("투기장");
var me = Panel("Me", 124f, 200f);
int left = ArenaRules.ArenaLeft(db, d, now);
UiKit.Label(me, "메달 " + d.arena.medals + " · 주간 점수 " + d.arena.seasonScore + " · " + ArenaRules.Rank(d) + "위 / " + (d.arena.samples.Count + 1), 36, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -14f), new Vector2(980f, 56f));
var start = SaveStore.ParseIso(d.arena.seasonStart, now);
int daysLeft = Mathf.Max(0, db.Arena.SeasonDays - (int)(now - start).TotalDays);
UiKit.Label(me, "전적 " + d.arena.wins + "승 " + d.arena.losses + "패 · 오늘 남은 도전 " + left + "/" + db.Arena.EntriesPerDay + " · 시즌 " + daysLeft + "일 남음 · 승 +" + db.Arena.MedalWin + " / 패 +" + db.Arena.MedalLose + "\n" +
"<size=26>순위 보상 1~10위 보옥 " + db.Arena.RewardTop10Gem + " · 그 밖 " + db.Arena.RewardTop100Gem + (d.arena.lastSeason.Length > 0 ? " · " + d.arena.lastSeason : "") + "</size>",
30, UiKit.InkSoft, TextAnchor.UpperLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -76f), new Vector2(980f, 110f), false);
UiKit.Btn("Shop", me, "메달 상점", 30, UiKit.Jade, UiKit.White, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-16f, -12f), new Vector2(200f, 60f), MedalShop);
ScrollRect sr;
var list = UiKit.ScrollGrid("Opponents", root, 24f, 24f, 344f, 16f, new Vector2(1032f, 130f), new Vector2(0f, 12f), 1, out sr);
for (int i = 0; i < d.arena.samples.Count; i++)
{
var s = d.arena.samples[i];
int k = i;
var cell = UiKit.Cell(list, "Opp" + i, UiKit.Paper, null);
UiKit.Border(cell, UiKit.Line, 2f);
int power = 0;
for (int j = 0; j < s.heroes.Count; j++)
{
var h = db.Heroes.Get(s.heroes[j]);
if (h == null) continue;
power += HeroRules.Compute(db, Root.Battle.Config, h, new HeroState { id = h.id, lv = s.lv, grade = db.Promotion.FirstGrade, star = Mathf.Clamp(h.rarity, 1, db.Evolve.MaxStar) }).power;
UiKit.Portrait("H" + j, cell.transform, h, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(16f + j * 92f, 0f), new Vector2(84f, 104f), false, 1.8f, new Color(0.9f, 0.86f, 0.8f));
}
UiKit.Label(cell.transform, s.name + "\n<size=26>전투력 " + power.ToString("N0") + " · 점수 " + s.score + "</size>", 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(490f, 0f), new Vector2(330f, 110f));
var why = ArenaRules.CanArena(db, d, k, now);
var b = UiKit.Btn("Go", cell.transform, "도전", 34, why == null ? UiKit.Red : UiKit.Disabled, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-16f, 0f), new Vector2(180f, 96f), () =>
{
var w = ArenaRules.CanArena(S.Db, S.Save, k, S.Now);
if (w != null)
{
Root.Toast(w);
return;
}
Root.StartMode(ArenaRules.Arena, "", k, ArenaRules.ArenaStage(S.Db, Root.Battle.Config, S.Save.arena.samples[k]), 0, 3);
});
b.interactable = why == null;
}
}
public void MedalShop()
{
var db = S.Db;
var d = S.Save;
var content = Root.ModalCustom("메달 상점 — 영웅 조각", 1500f, new OutGameRoot.ModalButton("닫기", UiKit.Dark, null));
UiKit.Label(content, "메달 " + d.arena.medals + " · " + db.Arena.ShopPrice + "메달 = 조각 " + db.Arena.ShopShards + " · 오늘 " + d.dailyC.arenaShop + "/" + db.Arena.ShopDaily, 30, UiKit.Ink, TextAnchor.MiddleCenter,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(860f, 50f));
ScrollRect sr;
var grid = UiKit.ScrollGrid("Heroes", content, 0f, 0f, 60f, 0f, new Vector2(200f, 260f), new Vector2(12f, 12f), 4, out sr);
foreach (var hs in d.heroes)
{
var h = db.Heroes.Get(hs.id);
if (h == null) continue;
string id = h.id;
var cell = UiKit.Cell(grid, "Buy_" + id, UiKit.Paper, () =>
{
var w = S.Do(() => ArenaRules.BuyShards(S.Db, S.Save, id, S.Now));
Root.Toast(w ?? h.nameKo + " 조각 +" + S.Db.Arena.ShopShards);
if (w == null) MedalShop();
});
UiKit.Portrait("Face", cell.transform, h, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -8f), new Vector2(150f, 150f), false, 1.8f, new Color(0.9f, 0.86f, 0.8f));
UiKit.Label(cell.transform, h.nameKo + "\n<size=22>조각 " + hs.shards + " · ★" + hs.star + "</size>", 26, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 8f), new Vector2(190f, 90f));
}
}
// ---------- ④ 적장 토벌 ----------
void BuildRaid()
{
var db = S.Db;
var d = S.Save;
var now = S.Now;
var e = db.Economy;
Title("적장 토벌 (이벤트)");
string bossId = ArenaRules.RaidBoss(db, d, now);
var h = db.Heroes.Get(bossId);
var stage = ArenaRules.RaidStage(db, Root.Battle.Config, bossId);
var box = Panel("Boss", 124f, 640f);
UiKit.Portrait("Boss", box, h, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -20f), new Vector2(320f, 320f), false, 1f, new Color(0.85f, 0.8f, 0.75f));
var sb = new System.Text.StringBuilder();
sb.Append("<size=52><b>이번 주 보스 — ").Append(h != null ? h.nameKo : bossId).Append("</b></size>\n");
sb.Append("보스 HP ×").Append(e.RaidHpMult.ToString("0.#")).Append(" · ").Append(e.RaidTurns).Append("턴 동안 피해를 누적").Append(stage != null ? " · 적 " + stage.enemies.Count + "기" : "").Append("\n");
sb.Append("이번 주 누적 피해 ").Append(d.arena.raidDamage.ToString("N0")).Append(" (").Append(d.arena.raidWeek).Append(" 주)\n");
sb.Append("보상 ").Append(db.Chests.Get(e.RaidChest).nameKo).Append(" · 천기 보물 확률 ").Append((e.RaidArcanaPct * 100f).ToString("0.#")).Append("%");
UiKit.Label(box, sb.ToString(), 32, UiKit.Ink, TextAnchor.UpperCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -360f), new Vector2(980f, 270f), false).lineSpacing = 1.15f;
var why = ArenaRules.CanRaid(db, d, now);
if (stage == null && why == null) why = "보스 편성 없음";
var go = UiKit.Btn("Go", root, why == null ? "토벌 — 남은 " + ArenaRules.RaidLeft(db, d, now) + "/" + e.RaidDaily : why, 48, why == null ? UiKit.Red : UiKit.Disabled, UiKit.White,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -790f), new Vector2(1032f, 150f), () =>
{
var w = ArenaRules.CanRaid(S.Db, S.Save, S.Now);
if (w != null)
{
Root.Toast(w);
return;
}
Root.StartMode(ArenaRules.Raid, bossId, 0, ArenaRules.RaidStage(S.Db, Root.Battle.Config, bossId), S.Db.Economy.RaidTurns, 4);
});
go.interactable = why == null;
UiKit.Label(root, "주간 보스 = 여포·조조 순환 · 주가 바뀌면 누적 초기화 · 천기 = 적장 토벌 전용", 28, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -960f), new Vector2(1000f, 50f), false);
}
}
}