368 lines
27 KiB
C#
368 lines
27 KiB
C#
// ─────────────────────────────────────────────────────────────────────────────
|
||
// WLEquipGui.RefLayout.cs — PD 레퍼런스 레이아웃(#881 · 2026-10-16)
|
||
//
|
||
// PD 원문: 「장비 아이템에 텍스트가 너무 많아. 첨부한 레퍼런스 게임과 같이 장비 UI 팝업의 레이아웃을 수정해줘」
|
||
// + 「인벤토리 화면도 예시 이미지와 같이 레이아웃을 개선하도록 해.」
|
||
// - 장비 상세 팝업 V2: 등급 리본 · 제목 띠 · 아이콘 + 「레벨: n/max」 · 능력치(+다음 레벨 증가분) + 합성 · 강등 작은 버튼 · 짧은 설명 ·
|
||
// 「등급 스킬」 목록(등급 색 점 · 잠긴 등급 = 자물쇠 + 흐림 · 마지막 줄 = 시너지) · 비용 줄 · 버튼 3(장비 · 레벨업 · 일괄 레벨업).
|
||
// 예전 긴 글(FR 옵션 · 계산식 · 설명 문단)은 넣지 않는다.
|
||
// - 인벤토리 머리줄: 부위 탭 대신 「정렬 · 나의 장비 · 합성」(부위 거르기 = 빈 슬롯 누르기 · 제목 누르면 전체).
|
||
// 치수 · 색 · 글자 · 아이콘 = Resources/WL/WLEquipGui.json itemPopup · hub(C45).
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
using System.Collections.Generic;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using WL.PartSkill;
|
||
|
||
namespace WL.Equip
|
||
{
|
||
public partial class WLEquipGui
|
||
{
|
||
WLGuiItemPopup IP;
|
||
bool IpOn { get { return IP != null && IP.enabled != 0; } }
|
||
|
||
TextMeshProUGUI _ipLevel, _ipStat, _ipDesc, _ipSection, _ipGold, _ipStone;
|
||
Image _ipStatIcon, _ipGoldIcon, _ipStoneIcon;
|
||
RectTransform _ipRowsRoot, _ipStoneGroup;
|
||
sealed class IpRow { public RectTransform root; public Image dot, lockImg; public TextMeshProUGUI text; }
|
||
readonly List<IpRow> _ipRows = new List<IpRow>();
|
||
public static int ItemPopupV2Refreshes;
|
||
|
||
// ── 인벤토리 머리줄 · 정렬 ──────────────────────────────────────────
|
||
int _sortMode;
|
||
TextMeshProUGUI _sortLabel, _gridTitle;
|
||
|
||
void BuildGridHeader()
|
||
{
|
||
var hdr = NewRT(_hub, "GridHeader");
|
||
hdr.anchorMin = new Vector2(0f, 1f); hdr.anchorMax = new Vector2(1f, 1f); hdr.pivot = new Vector2(0.5f, 1f);
|
||
hdr.anchoredPosition = new Vector2(0f, -H.tabTop); hdr.sizeDelta = new Vector2(0f, H.headerH);
|
||
var sortBtn = NewButton(hdr, SortLabel(), Spr("btnPrimary"), Color.white, H.headerBtnW, H.headerH, () => { _sortMode = (_sortMode + 1) % Mathf.Max(1, H.sortLabels != null ? H.sortLabels.Length : 1); Refresh(); }, H.headerBtnFontPx, DarkCol());
|
||
Place((RectTransform)sortBtn.transform, new Vector2(0f, 0.5f), new Vector2(H.edge, 0f), new Vector2(H.headerBtnW, H.headerH), new Vector2(0f, 0.5f));
|
||
_sortLabel = sortBtn.GetComponentInChildren<TextMeshProUGUI>();
|
||
_composeOpenBtn = NewButton(hdr, "합성", Spr("btnPrimary"), Color.white, H.headerBtnW, H.headerH, () => OpenCompose(-1), H.headerBtnFontPx, DarkCol());
|
||
Place((RectTransform)_composeOpenBtn.transform, new Vector2(1f, 0.5f), new Vector2(-H.edge, 0f), new Vector2(H.headerBtnW, H.headerH), new Vector2(1f, 0.5f));
|
||
_gridTitle = NewText(hdr, "Title", H.headerFontPx, TextAlignmentOptions.Midline, TextCol());
|
||
_gridTitle.fontStyle = FontStyles.Bold; ApplyOutline(_gridTitle); _gridTitle.raycastTarget = true;
|
||
var trt = _gridTitle.rectTransform; trt.anchorMin = new Vector2(0f, 0f); trt.anchorMax = new Vector2(1f, 1f);
|
||
trt.offsetMin = new Vector2(H.edge + H.headerBtnW + 16f, 0f); trt.offsetMax = new Vector2(-(H.edge + H.headerBtnW + 16f), 0f);
|
||
var tb = _gridTitle.gameObject.AddComponent<Button>(); tb.targetGraphic = _gridTitle; tb.onClick.AddListener(() => { _tab = 0; Refresh(); }); // 제목 = 부위 거르기 해제
|
||
}
|
||
|
||
string SortLabel()
|
||
{
|
||
var l = H.sortLabels;
|
||
return l != null && l.Length > 0 ? l[Mathf.Clamp(_sortMode, 0, l.Length - 1)] : "정렬";
|
||
}
|
||
|
||
void RefreshGridHeader()
|
||
{
|
||
if (_sortLabel != null) _sortLabel.text = SortLabel();
|
||
if (_gridTitle != null) _gridTitle.text = H.gridTitle + (_tab != 0 ? " · " + WLEquipTables.SlotName((eFREquipmentType)_tab) : "");
|
||
}
|
||
|
||
static int TierOfItem(Item it)
|
||
{
|
||
WLGradeTier t; int plus;
|
||
return it != null && it.itemJson != null && it.itemJson.IsEquipment && WLEquipGrades.TryMap(it.itemJson, out t, out plus) ? t.tier * 100 + plus : 0;
|
||
}
|
||
|
||
/// <summary>그리드 목록 — 장착분 빼기(hub.gridHideEquipped) + 정렬(0 = 등급순 · 1 = 레벨순 · 장비 먼저 · 소모품 뒤).</summary>
|
||
List<Item> GridItems(List<Item> src)
|
||
{
|
||
if (H.headerMode == 0 && H.gridHideEquipped == 0) return src;
|
||
var list = new List<Item>(src.Count);
|
||
for (int i = 0; i < src.Count; i++)
|
||
{
|
||
var it = src[i];
|
||
if (H.gridHideEquipped != 0 && it.itemJson.IsEquipment && WLInventory.IsEquipped(it.rawItem._id)) continue;
|
||
list.Add(it);
|
||
}
|
||
if (H.headerMode == 0) return list;
|
||
int mode = _sortMode;
|
||
list.Sort((a, b) =>
|
||
{
|
||
bool qa = a.itemJson.IsEquipment, qb = b.itemJson.IsEquipment;
|
||
if (qa != qb) return qa ? -1 : 1;
|
||
int ta = TierOfItem(a), tb = TierOfItem(b), la = a.rawItem.level, lb = b.rawItem.level;
|
||
int c = mode == 1 ? lb.CompareTo(la) : tb.CompareTo(ta);
|
||
if (c != 0) return c;
|
||
c = mode == 1 ? tb.CompareTo(ta) : lb.CompareTo(la);
|
||
if (c != 0) return c;
|
||
c = a.itemJson.EquipmentType.CompareTo(b.itemJson.EquipmentType);
|
||
if (c != 0) return c;
|
||
return a.rawItem._id.CompareTo(b.rawItem._id);
|
||
});
|
||
return list;
|
||
}
|
||
|
||
// ── 장비 상세 팝업 V2 ────────────────────────────────────────────────
|
||
void BuildItemPopupV2()
|
||
{
|
||
_popup = new GameObject("DetailPopup", typeof(RectTransform)); _popup.layer = UILayer; _popup.transform.SetParent(_root, false);
|
||
var prt = (RectTransform)_popup.transform; Stretch(prt);
|
||
var dim = NewImage(prt, "Dim", null, Col("dim", new Color(0f, 0f, 0f, 0.7f)), Image.Type.Simple); Stretch(dim.rectTransform); dim.raycastTarget = true;
|
||
var dimBtn = dim.gameObject.AddComponent<Button>(); dimBtn.targetGraphic = dim; dimBtn.onClick.AddListener(() => _popup.SetActive(false));
|
||
var box = NewPopupBox(prt, "Box", new Vector2(IP.w, IP.h)); var b = box.rectTransform;
|
||
const float rim = 14f;
|
||
|
||
// 제목 띠 + 등급 리본 + 닫기
|
||
var titleBar = NewImage(b, "TitleBar", Spr("titleBar"), Col("popupTitleBar", new Color(1f, 0.78f, 0.2f, 1f)));
|
||
var tb = titleBar.rectTransform; tb.anchorMin = new Vector2(0f, 1f); tb.anchorMax = new Vector2(1f, 1f); tb.pivot = new Vector2(0.5f, 1f);
|
||
tb.offsetMin = new Vector2(rim, -IP.titleH - rim); tb.offsetMax = new Vector2(-rim, -rim);
|
||
_popupName = NewText(tb, "Name", IP.titleFontPx, TextAlignmentOptions.Midline, Col("popupTitleText", Color.white)); _popupName.fontStyle = FontStyles.Bold; ApplyOutline(_popupName);
|
||
Stretch(_popupName.rectTransform, 100f, 4f, 100f, 4f);
|
||
_popupRarityImg = NewImage(b, "Ribbon", Spr("gradeLabel"), Color.white);
|
||
Place(_popupRarityImg.rectTransform, new Vector2(0.5f, 1f), new Vector2(0f, IP.ribbonH * 0.35f), new Vector2(IP.ribbonW, IP.ribbonH), new Vector2(0.5f, 0.5f));
|
||
_popupRarity = NewText(_popupRarityImg.rectTransform, "Rarity", IP.ribbonFontPx, TextAlignmentOptions.Midline, Color.white); _popupRarity.fontStyle = FontStyles.Bold; ApplyOutline(_popupRarity); Stretch(_popupRarity.rectTransform);
|
||
var close = NewIconButton(b, "Close", "closeIcon", L.closeBtnW, () => _popup.SetActive(false), "closeBtnBg");
|
||
Place((RectTransform)close.transform, new Vector2(1f, 1f), new Vector2(10f, 10f), new Vector2(L.closeBtnW, L.closeBtnH), new Vector2(1f, 1f));
|
||
|
||
// 아이콘 + 레벨
|
||
float x0 = rim + IP.pad, y0 = rim + IP.titleH + IP.pad;
|
||
_popupForm = NewCell(b, "Form", IP.iconSize, L.cellIconInset, L.cellFontPx, true, false, null);
|
||
Place(_popupForm.root, new Vector2(0f, 1f), new Vector2(x0, -y0), new Vector2(IP.iconSize, IP.iconSize), new Vector2(0f, 1f));
|
||
_ipLevel = NewText(b, "Level", IP.levelFontPx, TextAlignmentOptions.Midline, TextCol()); _ipLevel.fontStyle = FontStyles.Bold;
|
||
Place(_ipLevel.rectTransform, new Vector2(0f, 1f), new Vector2(x0, -(y0 + IP.iconSize + 4f)), new Vector2(IP.iconSize, IP.levelFontPx + 12f), new Vector2(0f, 1f));
|
||
|
||
// 능력치 상자 + 작은 버튼(합성 · 강등)
|
||
float rx = x0 + IP.iconSize + IP.gap, rw = IP.w - rx - x0;
|
||
float statW = rw - (IP.smallBtn + IP.gap) * 2f;
|
||
var statBox = NewImage(b, "StatBox", Spr("innerBox"), Col("statBox", new Color(0.1f, 0.14f, 0.3f, 0.92f)));
|
||
Place(statBox.rectTransform, new Vector2(0f, 1f), new Vector2(rx, -y0), new Vector2(statW, IP.statH), new Vector2(0f, 1f));
|
||
_ipStatIcon = NewImage(statBox.rectTransform, "Icon", null, Color.white, Image.Type.Simple); _ipStatIcon.preserveAspect = true;
|
||
Place(_ipStatIcon.rectTransform, new Vector2(0f, 0.5f), new Vector2(10f, 0f), new Vector2(IP.statIcon, IP.statIcon), new Vector2(0f, 0.5f));
|
||
_ipStat = NewText(statBox.rectTransform, "Value", IP.statFontPx, TextAlignmentOptions.MidlineRight, TextCol()); _ipStat.fontStyle = FontStyles.Bold; ApplyOutline(_ipStat);
|
||
_ipStat.enableWordWrapping = false; _ipStat.overflowMode = TextOverflowModes.Ellipsis;
|
||
var srt = _ipStat.rectTransform; srt.anchorMin = Vector2.zero; srt.anchorMax = Vector2.one; srt.offsetMin = new Vector2(IP.statIcon + 18f, 0f); srt.offsetMax = new Vector2(-16f, 0f);
|
||
_composeBtn = NewButton(b, "합성", Spr("btnBlue"), Color.white, IP.smallBtn, IP.statH, () => OpenCompose(_selectedId), IP.smallBtnFontPx, TextCol());
|
||
Place((RectTransform)_composeBtn.transform, new Vector2(0f, 1f), new Vector2(rx + statW + IP.gap, -y0), new Vector2(IP.smallBtn, IP.statH), new Vector2(0f, 1f));
|
||
_downBtn = NewButton(b, "강등", Spr("btnDanger"), Color.white, IP.smallBtn, IP.statH, () => Act(() => WLInventory.Downgrade(_selectedId)), IP.smallBtnFontPx, TextCol());
|
||
Place((RectTransform)_downBtn.transform, new Vector2(0f, 1f), new Vector2(rx + statW + IP.gap * 2f + IP.smallBtn, -y0), new Vector2(IP.smallBtn, IP.statH), new Vector2(0f, 1f));
|
||
|
||
// 짧은 설명
|
||
float dy = y0 + IP.statH + IP.gap;
|
||
float dh = Mathf.Max(IP.descH, IP.iconSize + IP.levelFontPx + 16f - IP.statH - IP.gap);
|
||
var desc = NewImage(b, "Desc", Spr("innerBox"), Col("descBox", new Color(0f, 0f, 0f, 0.28f)));
|
||
Place(desc.rectTransform, new Vector2(0f, 1f), new Vector2(rx, -dy), new Vector2(rw, dh), new Vector2(0f, 1f));
|
||
_ipDesc = NewText(desc.rectTransform, "Text", IP.descFontPx, TextAlignmentOptions.MidlineLeft, Col("descText", new Color(0.92f, 0.94f, 1f, 1f)));
|
||
_ipDesc.enableWordWrapping = true; _ipDesc.overflowMode = TextOverflowModes.Ellipsis; Stretch(_ipDesc.rectTransform, 16f, 8f, 16f, 8f);
|
||
|
||
// 등급 스킬
|
||
float bottomBlock = rim + IP.pad + IP.btnH + IP.gap + IP.costH + IP.gap;
|
||
float sy = Mathf.Max(dy + dh, y0 + IP.iconSize + IP.levelFontPx + 16f) + IP.gap;
|
||
var sec = NewImage(b, "Section", Spr("innerBox"), Col("sectionBox", new Color(0f, 0f, 0f, 0.26f)));
|
||
var secRt = sec.rectTransform; secRt.anchorMin = Vector2.zero; secRt.anchorMax = Vector2.one; secRt.offsetMin = new Vector2(x0, bottomBlock); secRt.offsetMax = new Vector2(-x0, -sy);
|
||
var head = NewImage(secRt, "Head", Spr("innerBox"), Col("sectionHead", new Color(0f, 0f, 0f, 0.3f)));
|
||
var hrt = head.rectTransform; hrt.anchorMin = new Vector2(0f, 1f); hrt.anchorMax = new Vector2(1f, 1f); hrt.pivot = new Vector2(0.5f, 1f); hrt.offsetMin = new Vector2(0f, -IP.sectionTitleH); hrt.offsetMax = Vector2.zero;
|
||
_ipSection = NewText(hrt, "Title", IP.sectionTitleFontPx, TextAlignmentOptions.MidlineLeft, TextCol()); _ipSection.fontStyle = FontStyles.Bold; Stretch(_ipSection.rectTransform, 20f, 0f, 20f, 0f);
|
||
_ipSection.text = IP.sectionTitle;
|
||
_ipRowsRoot = NewRT(secRt, "Rows");
|
||
_ipRowsRoot.anchorMin = Vector2.zero; _ipRowsRoot.anchorMax = Vector2.one; _ipRowsRoot.offsetMin = new Vector2(16f, 8f); _ipRowsRoot.offsetMax = new Vector2(-16f, -IP.sectionTitleH - 8f);
|
||
var vl = _ipRowsRoot.gameObject.AddComponent<VerticalLayoutGroup>(); vl.spacing = 2f;
|
||
vl.childControlWidth = true; vl.childControlHeight = true; vl.childForceExpandWidth = true; vl.childForceExpandHeight = false;
|
||
var circle = WLPartSkillHud.CircleSprite();
|
||
for (int i = 0; i < 8; i++)
|
||
{
|
||
var r = new IpRow();
|
||
r.root = NewRT(_ipRowsRoot, "Row" + i); Pref(r.root, 0f, IP.rowH);
|
||
r.dot = NewImage(r.root, "Dot", circle, Color.white, Image.Type.Simple);
|
||
Place(r.dot.rectTransform, new Vector2(0f, 0.5f), new Vector2(6f + (IP.lockSize - IP.dotSize) * 0.5f, 0f), new Vector2(IP.dotSize, IP.dotSize), new Vector2(0f, 0.5f));
|
||
r.lockImg = NewImage(r.root, "Lock", Spr("lockIcon"), Col("lockTint", new Color(1f, 0.75f, 0.3f, 1f)), Image.Type.Simple); r.lockImg.preserveAspect = true;
|
||
Place(r.lockImg.rectTransform, new Vector2(0f, 0.5f), new Vector2(6f, 0f), new Vector2(IP.lockSize, IP.lockSize), new Vector2(0f, 0.5f));
|
||
r.text = NewText(r.root, "Text", IP.rowFontPx, TextAlignmentOptions.MidlineLeft, TextCol()); r.text.enableWordWrapping = false; r.text.overflowMode = TextOverflowModes.Ellipsis;
|
||
var t = r.text.rectTransform; t.anchorMin = Vector2.zero; t.anchorMax = Vector2.one; t.offsetMin = new Vector2(IP.lockSize + 20f, 0f); t.offsetMax = Vector2.zero;
|
||
_ipRows.Add(r);
|
||
}
|
||
|
||
// 비용 줄(골드 · 강화석)
|
||
var cost = NewRT(b, "Cost");
|
||
cost.anchorMin = new Vector2(0f, 0f); cost.anchorMax = new Vector2(1f, 0f); cost.pivot = new Vector2(0.5f, 0f);
|
||
cost.offsetMin = new Vector2(x0, rim + IP.pad + IP.btnH + IP.gap); cost.offsetMax = new Vector2(-x0, rim + IP.pad + IP.btnH + IP.gap + IP.costH);
|
||
var hl = cost.gameObject.AddComponent<HorizontalLayoutGroup>(); hl.spacing = 10f; hl.childAlignment = TextAnchor.MiddleCenter;
|
||
hl.childControlWidth = true; hl.childControlHeight = true; hl.childForceExpandWidth = false; hl.childForceExpandHeight = false;
|
||
_ipGoldIcon = NewImage(cost, "GoldIcon", Spr("goldIcon"), Color.white, Image.Type.Simple); _ipGoldIcon.preserveAspect = true; Pref(_ipGoldIcon.rectTransform, IP.costIcon, IP.costIcon);
|
||
_ipGold = NewText(cost, "Gold", IP.costFontPx, TextAlignmentOptions.MidlineLeft, TextCol()); _ipGold.fontStyle = FontStyles.Bold; ApplyOutline(_ipGold);
|
||
_ipGold.enableWordWrapping = false; var gle = _ipGold.gameObject.AddComponent<LayoutElement>(); gle.minHeight = IP.costH;
|
||
_ipStoneGroup = NewRT(cost, "StoneGroup");
|
||
var shl = _ipStoneGroup.gameObject.AddComponent<HorizontalLayoutGroup>(); shl.spacing = 10f; shl.padding = new RectOffset(30, 0, 0, 0); shl.childAlignment = TextAnchor.MiddleCenter;
|
||
shl.childControlWidth = true; shl.childControlHeight = true; shl.childForceExpandWidth = false; shl.childForceExpandHeight = false;
|
||
_ipStoneIcon = NewImage(_ipStoneGroup, "StoneIcon", Spr("stoneIcon"), Color.white, Image.Type.Simple); _ipStoneIcon.preserveAspect = true; Pref(_ipStoneIcon.rectTransform, IP.costIcon, IP.costIcon);
|
||
_ipStone = NewText(_ipStoneGroup, "Stone", IP.costFontPx, TextAlignmentOptions.MidlineLeft, TextCol()); _ipStone.fontStyle = FontStyles.Bold; ApplyOutline(_ipStone);
|
||
_ipStone.enableWordWrapping = false; var sle = _ipStone.gameObject.AddComponent<LayoutElement>(); sle.minHeight = IP.costH;
|
||
|
||
// 버튼 3 — 장비 · 레벨업 · 일괄 레벨업
|
||
var row = NewRT(b, "Buttons");
|
||
row.anchorMin = new Vector2(0f, 0f); row.anchorMax = new Vector2(1f, 0f); row.pivot = new Vector2(0.5f, 0f);
|
||
row.offsetMin = new Vector2(x0, rim + IP.pad); row.offsetMax = new Vector2(-x0, rim + IP.pad + IP.btnH);
|
||
var bl = row.gameObject.AddComponent<HorizontalLayoutGroup>(); bl.spacing = IP.gap;
|
||
bl.childControlWidth = true; bl.childControlHeight = true; bl.childForceExpandWidth = false; bl.childForceExpandHeight = true;
|
||
_equipBtn = NewButton(row, "장비", Spr("btnBlue"), Color.white, 0f, IP.btnH, OnEquipToggle, IP.btnFontPx, TextCol()); Flex((RectTransform)_equipBtn.transform, 1f);
|
||
_equipLabel = _equipBtn.GetComponentInChildren<TextMeshProUGUI>();
|
||
_lvBtn = NewButton(row, "레벨업", Spr("btnPrimary"), Color.white, 0f, IP.btnH, () => Act(() => WLInventory.ItemLevelUp(_selectedId, false)), IP.btnFontPx, DarkCol()); Flex((RectTransform)_lvBtn.transform, 1f);
|
||
_lvAllBtn = NewButton(row, "일괄 레벨업", Spr("btnSecondary"), Color.white, 0f, IP.btnH, () => Act(() => WLInventory.ItemLevelUp(_selectedId, true)), IP.btnFontPx, DarkCol()); Flex((RectTransform)_lvAllBtn.transform, 1f);
|
||
_popup.SetActive(false);
|
||
}
|
||
|
||
void RefreshItemPopupV2(Item sel)
|
||
{
|
||
ItemPopupV2Refreshes++;
|
||
var j = sel.itemJson;
|
||
bool isEq = j.IsEquipment;
|
||
BindCell(_popupForm, sel, false);
|
||
_popupName.text = j.Item_name;
|
||
int tier = 1, plus = 0; WLGradeTier t = null;
|
||
if (isEq && WLEquipGrades.TryMap(j, out t, out plus)) tier = t.tier;
|
||
_popupRarityImg.color = isEq ? TierCol(tier) : Col("frameEmpty", Color.gray);
|
||
_popupRarity.text = isEq ? WLEquipGrades.Label(j, false) : "소모품";
|
||
_ipLevel.text = isEq ? IP.levelPrefix + sel.rawItem.level + "/" + j.ItemMaxLv : "×" + sel.rawItem.count;
|
||
|
||
string iconKey = null;
|
||
_ipStat.text = isEq ? StatText(sel, out iconKey) : "";
|
||
string sprName; _ipStatIcon.sprite = iconKey != null && IP.statIcons != null && IP.statIcons.TryGetValue(iconKey, out sprName) ? WLEquipGuiAssets.Get(sprName) : null;
|
||
_ipStatIcon.color = _ipStatIcon.sprite != null ? Color.white : new Color(1f, 1f, 1f, 0f);
|
||
_ipDesc.text = DescText(sel);
|
||
FillGradeRows(sel, isEq ? tier : 0);
|
||
FillCost(sel, isEq);
|
||
|
||
bool equipped = isEq && WLInventory.IsEquipped(sel.rawItem._id);
|
||
_equipLabel.text = equipped ? "해제" : "장비";
|
||
SetEnabled(_equipBtn, isEq, "btnBlue");
|
||
bool canLv = isEq && WLInventory.NextLevelCost(sel) != null;
|
||
SetEnabled(_lvBtn, canLv, "btnPrimary"); SetEnabled(_lvAllBtn, canLv, "btnSecondary");
|
||
ItemComposeJson cj; ItemJson rj;
|
||
SetEnabled(_composeBtn, isEq && WLInventory.ComposeInfo(sel, out cj, out rj), "btnBlue");
|
||
SetEnabled(_downBtn, isEq && WLEquipTables.FindComposeByResult(j.ItemGrade, j.GradeLv) != null, "btnDanger");
|
||
}
|
||
|
||
/// <summary>능력치 한 줄 — 평평한 값 = 「7.0<초록>+0.3</초록>」(다음 레벨 증가분) · 반지 = 근접/원거리 · 신발 · 목걸이 = 배율 문구.</summary>
|
||
string StatText(Item sel, out string iconKey)
|
||
{
|
||
iconKey = null;
|
||
var part = WLEquipGrades.Part(sel.itemJson.EquipmentType);
|
||
if (part == null) return "";
|
||
if (WLEquipGrades.IsRateOutput(part)) { iconKey = part.output; return WLPartStats.ItemStatLine(sel, true); }
|
||
eStat o; double bv, bonus; string line;
|
||
if (!WLEquipStats.ItemValues(sel, WLEquipStats.CurrentMainStat(), out o, out bv, out bonus, out line)) return "";
|
||
double cur = bv + bonus;
|
||
iconKey = WLEquipGrades.IsRingOutput(part) ? "RING" : o.ToString();
|
||
string head = "";
|
||
if (WLEquipGrades.IsRingOutput(part))
|
||
{
|
||
string rn; bool ok = WLPartStats.RingMatches(sel, out rn);
|
||
head = (rn.Length > 0 ? rn.Split(' ')[0] : rn) + " ";
|
||
if (!ok) return "<color=#FF8A8A>" + head + cur.ToString("0.#") + "</color>";
|
||
}
|
||
string s = head + cur.ToString("0.#");
|
||
if (WLInventory.NextLevelCost(sel) != null)
|
||
{
|
||
eStat o2; double bv2, bonus2; string line2;
|
||
if (WLEquipStats.ItemValues(sel, WLEquipStats.CurrentMainStat(), out o2, out bv2, out bonus2, out line2, sel.rawItem.level + 1))
|
||
{
|
||
double d = bv2 + bonus2 - cur;
|
||
if (d > 0.05) s += "<color=#" + ColorUtility.ToHtmlStringRGB(Col("statDelta", new Color(0.49f, 0.92f, 0.38f, 1f))) + ">+" + d.ToString("0.#") + "</color>";
|
||
}
|
||
}
|
||
return s;
|
||
}
|
||
|
||
/// <summary>짧은 설명 — 파츠 스킬 설명 · 무기 = 주력/각성 스킬 이름 · 그 밖 = 표 설명.</summary>
|
||
string DescText(Item sel)
|
||
{
|
||
var j = sel.itemJson;
|
||
if (!j.IsEquipment) return j.Desc ?? "";
|
||
var d = WLPartSkillData.Enabled ? WLPartSkillData.DefForGroup(j.GroupNo) : null;
|
||
if (d != null && (d.kind == WLPartKind.Active || d.kind == WLPartKind.Passive)) return d.desc;
|
||
if (j.EquipmentType == eFREquipmentType.Weapon)
|
||
{
|
||
var rows = WL.Combat.Weapon.WLWeaponSkillTable.Rows(j.GroupNo);
|
||
if (rows != null && rows.Count > 0)
|
||
{
|
||
var sb = new System.Text.StringBuilder();
|
||
for (int i = 0; i < rows.Count; i++) { if (i > 0) sb.Append(" · "); sb.Append(rows[i].slot == 1 ? "주력 [" : "각성 [").Append(rows[i].skillName).Append(']'); }
|
||
return sb.ToString();
|
||
}
|
||
}
|
||
return j.Desc ?? "";
|
||
}
|
||
|
||
/// <summary>등급 스킬 줄 — 파츠 스킬 = 등급별 단계 · 무기 = 무기 스킬 등급표 · 마지막 = 시너지. 지금 등급보다 높은 줄 = 자물쇠 + 흐림.</summary>
|
||
void FillGradeRows(Item sel, int tier)
|
||
{
|
||
var j = sel.itemJson;
|
||
var texts = new List<string>(); var tiers = new List<int>(); var open = new List<bool>();
|
||
var d = j.IsEquipment && WLPartSkillData.Enabled ? WLPartSkillData.DefForGroup(j.GroupNo) : null;
|
||
int maxParts = Mathf.Max(1, IP.rowMaxParts);
|
||
if (d != null && (d.kind == WLPartKind.Active || d.kind == WLPartKind.Passive))
|
||
{
|
||
for (int t = 1; t <= 7; t++) { texts.Add(WLPartSkillText.TierRow(d, t, maxParts)); tiers.Add(t); open.Add(t <= tier); }
|
||
}
|
||
else if (j.IsEquipment && j.EquipmentType == eFREquipmentType.Weapon)
|
||
{
|
||
var rows = WL.Combat.Weapon.WLWeaponSkillTable.Rows(j.GroupNo);
|
||
string main = "", awaken = "";
|
||
if (rows != null) foreach (var r in rows) { if (r.slot == 1) main = r.skillName; else if (r.slot == 2) awaken = r.skillName; }
|
||
var wt = IP.weaponTierText;
|
||
if (rows != null && rows.Count > 0 && wt != null && wt.Length > 1)
|
||
for (int t = 1; t <= 7 && t < wt.Length; t++) { texts.Add(wt[t].Replace("{main}", main).Replace("{awaken}", awaken)); tiers.Add(t); open.Add(t <= tier); }
|
||
}
|
||
// 시너지 줄(파츠 액티브 · 무기 시너지 · 결합)
|
||
if (d != null && d.HasSynergy && d.kind != WLPartKind.Passive)
|
||
{
|
||
var s = SlotFor(sel, d);
|
||
var p = WLPartSkillData.Def(!string.IsNullOrEmpty(d.synKey) ? d.synKey : d.combineKey);
|
||
int need = WLPartSkillData.S.synergyLevel;
|
||
texts.Add("★ 시너지 [" + (string.IsNullOrEmpty(d.synName) ? d.name : d.synName) + "] · " + (p != null ? p.name : "?") + " " + need + "단계");
|
||
tiers.Add(0); open.Add(s != null && s.synergy);
|
||
}
|
||
for (int i = 0; i < _ipRows.Count; i++)
|
||
{
|
||
var r = _ipRows[i];
|
||
bool on = i < texts.Count;
|
||
SetOn(r.root, on || (i == 0 && texts.Count == 0));
|
||
if (!on)
|
||
{
|
||
if (i == 0 && texts.Count == 0) { SetOn(r.dot, false); SetOn(r.lockImg, false); r.text.text = IP.noSkillText; r.text.color = Col("textDim", Color.gray); }
|
||
continue;
|
||
}
|
||
bool isOpen = open[i];
|
||
SetOn(r.dot, isOpen); SetOn(r.lockImg, !isOpen);
|
||
r.dot.color = tiers[i] > 0 ? TierCol(tiers[i]) : Col("synergyOn", new Color(1f, 0.8f, 0.25f, 1f));
|
||
r.lockImg.color = tiers[i] > 0 ? TierCol(tiers[i]) : Col("synergyOn", new Color(1f, 0.8f, 0.25f, 1f));
|
||
r.text.text = texts[i];
|
||
var c = TextCol(); if (!isOpen) c.a = 0.45f;
|
||
r.text.color = c;
|
||
}
|
||
}
|
||
|
||
void FillCost(Item sel, bool isEq)
|
||
{
|
||
var cost = isEq ? WLInventory.NextLevelCost(sel) : null;
|
||
SetOn(_ipGoldIcon, cost != null);
|
||
if (cost == null) { _ipGold.text = isEq ? IP.maxLevelText : ""; SetOn(_ipStoneGroup, false); return; }
|
||
long g = WLInventory.GetGold();
|
||
string lack = "#" + ColorUtility.ToHtmlStringRGB(Col("costLack", new Color(1f, 0.45f, 0.45f, 1f)));
|
||
_ipGold.text = (g < cost.MoneyCountRequired ? "<color=" + lack + ">" : "") + Short(g) + "/" + Short(cost.MoneyCountRequired) + (g < cost.MoneyCountRequired ? "</color>" : "");
|
||
bool needStone = cost.ItemKeyRequired > 0 && cost.ItemCountRequired > 0;
|
||
SetOn(_ipStoneGroup, needStone);
|
||
if (needStone)
|
||
{
|
||
long have = WLInventory.GetItemCountByIndex(cost.ItemKeyRequired);
|
||
_ipStone.text = (have < cost.ItemCountRequired ? "<color=" + lack + ">" : "") + have + "/" + cost.ItemCountRequired + (have < cost.ItemCountRequired ? "</color>" : "");
|
||
}
|
||
}
|
||
|
||
public string ItemPopupStateForProbe
|
||
{
|
||
get
|
||
{
|
||
if (!IpOn || _popup == null) return "v2 off";
|
||
int rows = 0; foreach (var r in _ipRows) if (r.root.gameObject.activeSelf) rows++;
|
||
return "v2 open=" + _popup.activeSelf + " name='" + (_popupName != null ? _popupName.text : "") + "' stat='" + (_ipStat != null ? _ipStat.text : "") + "' rows=" + rows + " gold='" + (_ipGold != null ? _ipGold.text : "") + "'";
|
||
}
|
||
}
|
||
}
|
||
}
|