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

275 lines
16 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 System.Text;
using UnityEngine;
using UnityEngine.UI;
namespace TKS.OutGame
{
/// <summary>
/// 전투 결과 패널(기획서 §6 · 비주얼 = Fable 제안서 §4-2 #1104) — 별 3(받침 원 + 등장 튀김) · 조건 행 3 · 보상 타일(골드 · 경험 · 보옥 · 장비 · 상자) ·
/// 버튼 = 「다시」(Tertiary · 같은 스테이지 재도전) + 「확인」(Primary 노랑) · 승리면 리본 뒤 금빛.
/// 전투 HUD 의 승리/패배 판(sortingOrder 100) 위에 덮는다 · 화면 아무 데나 탭 = 기존 그대로(SlingshotInput → 월드맵 복귀 · 「다시」 위만 제외).
/// </summary>
public class BattleResultPanel
{
// 패널 배치(UI px · 패널 왼쪽 위 기준)
const float PW = 920f;
const float PH = 1180f;
const float Side = 60f;
const float RowW = PW - Side * 2f;
const float RowH = 64f;
const float Tile = 132f;
const float BtnY = PH - 40f - 110f;
readonly OutGameRoot root;
readonly GameObject go;
readonly Text title;
readonly Image ribbon;
readonly Image glow;
readonly RectTransform starRow;
readonly Image[] starImg = new Image[3];
readonly RectTransform body;
readonly Text plain;
readonly Button retry;
readonly Button ok;
string rewardsText = "";
public bool Visible
{
get { return go.activeSelf; }
}
public string RewardsText
{
get { return rewardsText; }
}
public BattleResultPanel(OutGameRoot r, RectTransform canvas)
{
root = r;
var dim = UiKit.Fill("Result", canvas);
UiKit.Paint(dim, UISkin.Col("dim", new Color(0f, 0f, 0f, 0.55f)), false);
go = dim.gameObject;
Image panel;
if (UISkin.Has("popup"))
{
// 리본 뒤 금빛(승리만 · 팩 Glow_Circle01 · 호흡) → 팩 팝업 틀 → 제목 리본(승리 붉은 · 패배 회색)
if (UISkin.Has("glow_circle"))
{
glow = UiKit.Skin(UiKit.Rect("Glow", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 20f + PH * 0.5f + 90f), new Vector2(1240f, 1240f)), "glow_circle", UiKit.Col("ribbon_glow", Color.clear));
var pulse = glow.gameObject.AddComponent<UiPulse>();
pulse.min = 0.65f;
pulse.period = 1.6f;
}
panel = UiKit.Skin(UiKit.Rect("Panel", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 20f), new Vector2(PW, PH)), "popup", UiKit.Paper);
var rib = UiKit.Rect("Ribbon", panel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 0.5f), new Vector2(0f, 10f), new Vector2(1000f, 200f));
ribbon = UiKit.Skin(rib, "ribbon", Color.white);
title = UiKit.Title(UiKit.FillLabel(rib, "", 88, UiKit.White, TextAnchor.MiddleCenter, true, 150f), true);
title.rectTransform.offsetMin = new Vector2(150f, 34f);
// 별 3 = 받침 원(Circle78 chip_dark) + 별(가운데 크게 · 위로) · 0.15 s 간격 튀김
starRow = UiKit.At(panel.transform, "StarRow", 0f, 120f, PW, 190f);
for (int i = 0; i < 3; i++)
{
bool mid = i == 1;
float plate = mid ? 150f : 120f;
var pos = new Vector2((i - 1) * 190f, mid ? 12f : -14f);
UiKit.Skin(UiKit.Rect("Plate" + i, starRow, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), pos, new Vector2(plate, plate)), "circle_frame", UiKit.Col("chip_dark", UiKit.Dark));
starImg[i] = UiKit.Icon(starRow, "star_on_l", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), pos + new Vector2(0f, 4f), mid ? 132f : 106f);
if (starImg[i] == null) continue;
UiKit.DropShadow(starImg[i], 5f);
starImg[i].gameObject.AddComponent<UiPop>().delay = 0.15f * i;
}
body = UiKit.Fill("Body", panel.transform);
retry = UiKit.SkinBtn(panel.transform, "Retry", "round08", UiKit.Col("menu_btn", UiKit.Dark), "다시", 40, UiKit.White, Side, BtnY, 260f, 110f, () => root.RetryStage());
ok = UiKit.SkinBtn(panel.transform, "Ok", UISkin.Has("btn_l_yellow") ? "btn_l_yellow" : "button", UISkin.Has("btn_l_yellow") ? UiKit.White : UiKit.Coin, "확인", 46, UiKit.Col("text_on_yellow", UiKit.Ink), PW - Side - 520f, BtnY, 520f, 110f, null);
}
else
{
panel = UiKit.Box("Panel", dim, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 40f), new Vector2(PW, PH), UiKit.Paper);
UiKit.Border(panel, UiKit.Line, 4f);
var head = UiKit.TopBand("Head", panel.transform, 0f, 170f);
UiKit.Paint(head, UiKit.Dark);
title = UiKit.FillLabel(head, "", 96, UiKit.White);
plain = UiKit.Label(panel.transform, "", 36, UiKit.Ink, TextAnchor.UpperLeft, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -200f), new Vector2(760f, 860f), false);
plain.lineSpacing = 1.15f;
UiKit.Label(panel.transform, "화면을 탭하면 계속", 34, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 36f), new Vector2(800f, 60f), false);
}
go.SetActive(false);
}
public void SetVisible(bool on)
{
if (go.activeSelf != on) go.SetActive(on);
}
/// <summary>화면 좌표가 「다시」 버튼 위인가 — 화면 탭(월드맵 복귀)을 이 버튼에서는 건너뛴다.</summary>
public bool PointerOnRetry(Vector2 screen)
{
if (retry == null || !go.activeInHierarchy || !retry.gameObject.activeSelf) return false;
var c = go.GetComponentInParent<Canvas>();
return RectTransformUtility.RectangleContainsScreenPoint((RectTransform)retry.transform, screen, c != null ? c.worldCamera : null);
}
public void Fill(StageReward r)
{
var s = root.Session;
rewardsText = r.win ? RewardText(s, r) : "보상 없음";
title.text = (r.win ? "승리" : "패배") + " <size=44>" + r.chapter + "-" + r.stage + (r.boss ? " 보스" : "") + "</size>";
ShowStars(r.win ? r.stars : 0, r.win);
SetButtons(true);
if (body == null)
{
plain.text = (r.win ? "별 " + UiKit.Stars(r.stars, 3) + "\n\n" : "아군이 전멸했습니다\n\n") + rewardsText;
return;
}
UiKit.Clear(body);
float y = 330f;
if (r.win)
{
string[] cond = { "스테이지 클리어", "아군 전원 생존", r.starTurns + "턴 이내 클리어 <size=26>(" + r.turns + "턴)</size>" };
for (int i = 0; i < 3; i++)
UiKit.CondRow(body, "Cond" + i, Side, y + i * (RowH + 8f), RowW, RowH, r.conds[i] ? "check" : "x_small", r.conds[i], cond[i], 30);
}
else
{
UiKit.CondRow(body, "Cond0", Side, y, RowW, RowH, "x_small", false, "아군이 전멸했습니다", 30);
UiKit.CondRow(body, "Cond1", Side, y + RowH + 8f, RowW, RowH, "cur_stamina", false, "군량 " + r.stamina + " 소모 · 별 없음", 30, false);
}
y = 566f;
UiKit.SubTitle(body, "보상", Side, y, RowW);
y += 64f;
if (!r.win)
{
var none = UiKit.Title(UiKit.Label(body, "보상 없음", 40, UiKit.Col("text_soft", UiKit.InkSoft), TextAnchor.MiddleCenter, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(Side, -y - 10f), new Vector2(RowW, 70f)));
none.horizontalOverflow = HorizontalWrapMode.Overflow;
UiKit.Label(body, "영웅 레벨업 · 승급 · 편성을 바꿔 다시 도전", 30, UiKit.Col("text_soft", UiKit.InkSoft), TextAnchor.MiddleCenter, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(Side, -y - 90f), new Vector2(RowW, 50f), false);
return;
}
Tiles(s, r, y);
Notes(s, r, y + Tile + 60f);
}
/// <summary>보상 타일 행(가운데 정렬 · 칸 아래 이름) — 골드 · 경험 · 보옥(있을 때) · 장비(등급 칸 · 있을 때) · 상자(있을 때).</summary>
void Tiles(OutGameSession s, StageReward r, float y)
{
var list = new List<string[]>(); // 아이콘 · 수량 · 칸 색 접두 · 이름
list.Add(new[] { "cur_gold", r.gold.ToString("N0"), "gear_white_", "골드" });
list.Add(new[] { "cur_exp", r.exp.ToString("N0"), "gear_white_", "경험 · " + r.expTo.Count + "명" });
if (r.gem > 0) list.Add(new[] { "cur_gem", r.gem.ToString("N0"), "gear_blue_", "보옥" });
if (!string.IsNullOrEmpty(r.gearId))
{
var g = s.Db.Gear.Get(r.gearId);
string grade = g != null && !string.IsNullOrEmpty(g.grade) ? g.grade : "white";
list.Add(new[] { g != null ? "gear_" + g.kind : "cur_gear", "", "gear_" + grade + "_", g != null ? g.nameKo : r.gearId });
}
if (!string.IsNullOrEmpty(r.chest) && s.Db.Chests != null && s.Db.Chests.Get(r.chest) != null)
list.Add(new[] { "chest_" + r.chest, "", "gear_purple_", s.Db.Chests.Get(r.chest).nameKo });
float gap = list.Count >= 5 ? 22f : 30f;
float x = (PW - list.Count * Tile - (list.Count - 1) * gap) * 0.5f;
for (int i = 0; i < list.Count; i++)
{
var e = list[i];
float tx = x + i * (Tile + gap);
UiKit.RewardTile(body, "Tile" + i, tx, y, Tile, e[2], e[0], e[1], e[2] != "gear_white_");
var n = UiKit.Label(body, e[3], 26, UiKit.Col("text_soft", UiKit.InkSoft), TextAnchor.UpperCenter, new Vector2(0f, 1f), new Vector2(0.5f, 1f), new Vector2(tx + Tile * 0.5f, -y - Tile - 12f), new Vector2(Tile + gap, 36f), false);
n.horizontalOverflow = HorizontalWrapMode.Overflow;
}
}
/// <summary>타일 아래 한두 줄 — 첫 클리어/반복 배율 · 레벨업 가능 영웅 · 소탕.</summary>
void Notes(OutGameSession s, StageReward r, float y)
{
var e = s.Db.Economy;
var sb = new StringBuilder(r.first ? "첫 클리어 보상" + (r.boss ? " + 보스" : "") : "반복 보상 ×" + e.RepeatRewardMult);
if (r.sweep) sb.Append(" · 소탕(군량 ").Append(r.stamina).Append(")");
var ready = ReadyNames(s, r);
if (ready.Length > 0) sb.Append("\n<color=#").Append(ColorUtility.ToHtmlStringRGB(UiKit.Col("alert_red", UiKit.Red))).Append(">레벨업 가능 ").Append(ready).Append("</color>");
var t = UiKit.Label(body, sb.ToString(), 30, UiKit.White, TextAnchor.UpperCenter, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(Side, -y), new Vector2(RowW, 100f), false);
t.lineSpacing = 1.2f;
}
/// <summary>연무장 결과(탑 · 던전 · 투기장 · 적장 토벌 · PD #1093 C) — 별 없음 · 결과 행 · 탭 = 연무장.</summary>
public void FillMode(ModeResult r)
{
SetButtons(false);
rewardsText = r != null ? string.Join("\n", r.lines.ToArray()) : "";
if (r == null)
{
title.text = "연무장";
ShowStars(-1, true);
if (body != null) UiKit.Clear(body);
else plain.text = "";
return;
}
bool win = r.win || r.kind == ArenaRules.Raid;
title.text = r.kind == ArenaRules.Raid ? "토벌 종료" : r.win ? "승리" : "패배";
ShowStars(-1, win);
if (body == null)
{
plain.text = "<size=44>" + r.title + "</size>\n\n" + rewardsText;
return;
}
UiKit.Clear(body);
var head = UiKit.Title(UiKit.Label(body, r.title, 44, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(Side, -150f), new Vector2(RowW, 110f)), true);
head.horizontalOverflow = HorizontalWrapMode.Overflow;
UiKit.SubTitle(body, "결과", Side, 290f, RowW);
float y = 354f;
for (int i = 0; i < r.lines.Count && y + RowH <= BtnY - 20f; i++, y += RowH + 8f)
UiKit.CondRow(body, "Line" + i, Side, y, RowW, RowH, null, true, r.lines[i], 30);
}
/// <summary>스테이지 = 「다시」 + 「확인」 · 연무장 = 「확인」 가운데.</summary>
void SetButtons(bool stage)
{
if (retry == null) return;
retry.gameObject.SetActive(stage);
((RectTransform)ok.transform).anchoredPosition = new Vector2(stage ? PW - Side - 520f : (PW - 520f) * 0.5f, -BtnY);
}
/// <summary>별 아이콘(n &lt; 0 = 숨김) · 리본 색(승리 붉은 · 패배 회색) · 금빛 = 승리만.</summary>
void ShowStars(int n, bool win)
{
if (glow != null) glow.gameObject.SetActive(win);
if (starRow == null) return;
starRow.gameObject.SetActive(n >= 0);
for (int i = 0; i < 3; i++)
if (starImg[i] != null) starImg[i].sprite = UISkin.Icon(i < n ? "star_on_l" : "star_off") ?? starImg[i].sprite;
if (ribbon != null && !UISkin.Apply(ribbon, win ? "ribbon" : "ribbon_gray")) ribbon.color = win ? Color.white : UiKit.Disabled;
}
static string ReadyNames(OutGameSession s, StageReward r)
{
var ready = new StringBuilder();
foreach (var id in r.expTo)
{
var lv = s.Save.Level(id);
if (HeroRules.CanLevelUp(s.Db, s.Save, lv) != null) continue;
if (ready.Length > 0) ready.Append(" · ");
ready.Append(s.Def(id).nameKo);
}
return ready.ToString();
}
/// <summary>보상 줄(소탕 팝업 · 테마 없을 때 결과 화면 공용).</summary>
public static string RewardText(OutGameSession s, StageReward r)
{
var e = s.Db.Economy;
var sb = new StringBuilder();
sb.Append("골드 +").Append(r.gold.ToString("N0")).Append(r.first ? " <size=28>(첫 클리어)</size>" : " <size=28>(반복 ×" + e.RepeatRewardMult + ")</size>");
sb.Append("\n경험 +").Append(r.exp).Append(" <size=28>출전 ").Append(r.expTo.Count).Append("명</size>");
if (r.gem > 0) sb.Append("\n보옥 +").Append(r.gem).Append(r.boss ? " <size=28>(첫 클리어 + 보스)</size>" : " <size=28>(첫 클리어)</size>");
if (!string.IsNullOrEmpty(r.gearId))
{
var g = s.Db.Gear.Get(r.gearId);
sb.Append("\n장비 ").Append(g != null ? g.nameKo + " (" + UiKit.GradeKo(g.grade) + ")" : r.gearId).Append(" 획득");
}
else sb.Append("\n<size=30>장비 드랍 없음</size>");
if (!string.IsNullOrEmpty(r.chest) && s.Db.Chests != null && s.Db.Chests.Get(r.chest) != null) sb.Append("\n").Append(s.Db.Chests.Get(r.chest).nameKo).Append(" 획득 <size=28>(보물 > 상자)</size>");
if (r.sweep) sb.Append("\n<size=30>군량 ").Append(r.stamina).Append(" 소모 · 소탕(전투 없음)</size>");
var ready = ReadyNames(s, r);
if (ready.Length > 0) sb.Append("\n<color=#a03a22><size=30>레벨업 가능: ").Append(ready).Append("</size></color>");
return sb.ToString();
}
}
}