2026-11-03 12:08:51 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using TMPro;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
|
|
|
|
namespace Decentralized
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 放大镜检视显卡时的大图:和香烟一样把物品放大摆在检视台上,正反面各一张,看外观自己判断真假。
|
|
|
|
|
/// 游戏没有给 mod 开放检视界面,这里不打补丁,只在检视台(InspectionUIManager.panel)下面挂一个自己的界面,
|
|
|
|
|
/// 用一个常驻的 MonoBehaviour 每帧看一眼是否正在检视显卡。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed class GpuInspectView : MonoBehaviour
|
|
|
|
|
{
|
|
|
|
|
private static GpuInspectView instance;
|
|
|
|
|
private RectTransform root, cardRoot;
|
|
|
|
|
private Button flip;
|
2026-11-03 13:37:48 +08:00
|
|
|
private readonly Button[] point = new Button[5];
|
|
|
|
|
private RectTransform pointRow;
|
|
|
|
|
private static readonly int[] PointBits = { Items.PointFan, Items.PointFingers, Items.PointBracket, Items.PointPrint, Items.PointSticker };
|
|
|
|
|
private static readonly string[] PointKeys = { "point.fan", "point.fingers", "point.bracket", "point.print", "point.sticker" };
|
2026-11-03 12:08:51 +08:00
|
|
|
private TextMeshProUGUI flipText;
|
|
|
|
|
private GameItem shown;
|
|
|
|
|
private string shownSig;
|
|
|
|
|
|
|
|
|
|
public static void Ensure()
|
|
|
|
|
{
|
|
|
|
|
if (instance != null) return;
|
|
|
|
|
var go = new GameObject("pd_gpu_inspect_view");
|
|
|
|
|
DontDestroyOnLoad(go);
|
|
|
|
|
instance = go.AddComponent<GpuInspectView>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Remove()
|
|
|
|
|
{
|
|
|
|
|
if (instance != null) Destroy(instance.gameObject);
|
|
|
|
|
instance = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Update()
|
|
|
|
|
{
|
|
|
|
|
var m = InspectionUIManager.Instance;
|
|
|
|
|
var item = m != null && m.panel != null && m.panel.activeSelf ? m.currentInspectingItem : null;
|
|
|
|
|
if (item == null || !Items.IsGpu(item))
|
|
|
|
|
{
|
|
|
|
|
if (root != null && root.gameObject.activeSelf) root.gameObject.SetActive(false);
|
|
|
|
|
shown = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Log.Guard("显卡检视大图", () =>
|
|
|
|
|
{
|
|
|
|
|
if (root == null) Build(m);
|
|
|
|
|
if (!root.gameObject.activeSelf) root.gameObject.SetActive(true);
|
2026-11-03 13:37:48 +08:00
|
|
|
UpdatePointButtons(item);
|
2026-11-03 12:08:51 +08:00
|
|
|
var keys = new List<string>();
|
|
|
|
|
string sig = Items.LookKeys(item, keys) + "|" + string.Join(",", keys);
|
|
|
|
|
if (item != shown || sig != shownSig)
|
|
|
|
|
{
|
|
|
|
|
shown = item;
|
|
|
|
|
shownSig = sig;
|
|
|
|
|
Redraw(item);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-03 13:04:45 +08:00
|
|
|
/// <summary>检视大图的叠加层。五个维度各自对应一种或几种细节,组合时再加几处同源的小细节(磕碰、开过壳的螺丝、重新粘过的封条)。</summary>
|
|
|
|
|
public static List<string> BigOverlays(GameItem item)
|
|
|
|
|
{
|
|
|
|
|
var o = new List<string>();
|
|
|
|
|
var u = Items.ToUnit(item);
|
|
|
|
|
if (u.Dead) return o;
|
2026-11-03 13:37:48 +08:00
|
|
|
var l = u.Appearance;
|
2026-11-03 13:04:45 +08:00
|
|
|
string P = "gpu_big_ov_";
|
2026-11-03 13:37:48 +08:00
|
|
|
// 每个维度的取值对应一种细节,一一对应,不靠组合触发
|
2026-11-03 13:04:45 +08:00
|
|
|
if (!Items.IsBack(item))
|
|
|
|
|
{
|
2026-11-03 13:37:48 +08:00
|
|
|
if (l.Fan == 1) o.Add(P + "fan_dust2");
|
|
|
|
|
if (l.Fan == 2) { o.Add(P + "fan_dust1"); o.Add(P + "fan_chip"); }
|
2026-11-03 13:04:45 +08:00
|
|
|
if (l.Fingers == 1) o.Add(P + "fin_dim");
|
|
|
|
|
if (l.Fingers == 2) o.Add(P + "fin_oxide");
|
2026-11-03 13:37:48 +08:00
|
|
|
if (l.Bracket == 1) o.Add(P + "brk_scratch");
|
2026-11-03 13:04:45 +08:00
|
|
|
if (l.Bracket == 2) o.Add(P + "brk_port");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string model = (l.Print == 1 && u.FakeOf != null ? u.FakeOf : u.Model).ToLowerInvariant();
|
|
|
|
|
o.Add(P + "print_" + (l.Print == 2 ? "skew_" : l.Print == 1 ? "stamp_" : "normal_") + model);
|
|
|
|
|
if (l.Sticker == 1) o.Add(P + "sticker_hand");
|
2026-11-03 13:37:48 +08:00
|
|
|
if (l.Sticker == 2) o.Add(P + "seal_missing");
|
2026-11-03 13:04:45 +08:00
|
|
|
}
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-03 12:08:51 +08:00
|
|
|
private void Build(InspectionUIManager m)
|
|
|
|
|
{
|
|
|
|
|
root = new GameObject("_PdGpuInspection", typeof(RectTransform)).GetComponent<RectTransform>();
|
|
|
|
|
root.SetParent(m.panel.transform, false);
|
|
|
|
|
root.sizeDelta = new Vector2(1920, 1080);
|
|
|
|
|
root.localPosition = Vector3.zero;
|
|
|
|
|
cardRoot = new GameObject("Card", typeof(RectTransform)).GetComponent<RectTransform>();
|
|
|
|
|
cardRoot.SetParent(root, false);
|
2026-11-03 13:37:48 +08:00
|
|
|
cardRoot.localPosition = new Vector3(0, 0, 0);
|
2026-11-03 12:08:51 +08:00
|
|
|
// 翻面按钮:复制鉴定面板里"发送"按钮的外观
|
|
|
|
|
var template = m.appraisalPanel.transform.Find("buttonSend");
|
|
|
|
|
if (template != null)
|
|
|
|
|
{
|
2026-11-03 13:37:48 +08:00
|
|
|
pointRow = new GameObject("PointRow", typeof(RectTransform)).GetComponent<RectTransform>();
|
|
|
|
|
pointRow.SetParent(root, false);
|
|
|
|
|
pointRow.localPosition = new Vector3(0, 312, 0);
|
|
|
|
|
for (int i = 0; i < point.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
int bit = PointBits[i];
|
|
|
|
|
var pb = Instantiate(template.gameObject, pointRow, false);
|
|
|
|
|
pb.name = "Point" + i;
|
|
|
|
|
var prt = (RectTransform)pb.transform;
|
|
|
|
|
prt.sizeDelta = new Vector2(140, 40);
|
|
|
|
|
prt.localPosition = new Vector3((i - 2) * 148, 0, 0);
|
|
|
|
|
point[i] = pb.GetComponent<Button>();
|
|
|
|
|
point[i].onClick = new Button.ButtonClickedEvent();
|
|
|
|
|
point[i].onClick.AddListener(() => Log.Guard("指出问题", () => OnPoint(bit)));
|
|
|
|
|
var t = pb.GetComponentInChildren<TextMeshProUGUI>();
|
|
|
|
|
t.enableAutoSizing = true; t.fontSizeMin = 12; t.fontSizeMax = 26;
|
|
|
|
|
foreach (var mb in pb.GetComponentsInChildren<MonoBehaviour>(true))
|
|
|
|
|
{
|
|
|
|
|
string n = mb.GetType().Name;
|
|
|
|
|
if (n.Contains("Local") || n.Contains("TMPLocalizer")) Destroy(mb);
|
|
|
|
|
}
|
|
|
|
|
t.text = L.T(PointKeys[i]);
|
|
|
|
|
}
|
2026-11-03 12:08:51 +08:00
|
|
|
var go = Instantiate(template.gameObject, root, false);
|
|
|
|
|
go.name = "Flip";
|
2026-11-03 13:37:48 +08:00
|
|
|
go.transform.localPosition = new Vector3(0, -290, 0);
|
2026-11-03 12:08:51 +08:00
|
|
|
flip = go.GetComponent<Button>();
|
|
|
|
|
flip.onClick = new Button.ButtonClickedEvent();
|
|
|
|
|
flip.onClick.AddListener(() => { if (shown != null) Items.Flip(shown); });
|
|
|
|
|
flipText = go.GetComponentInChildren<TextMeshProUGUI>();
|
|
|
|
|
// 去掉复制来的按钮上驱动文字的组件(本地化 / 提交逻辑),文字由这里设置
|
|
|
|
|
foreach (var mb in go.GetComponentsInChildren<MonoBehaviour>(true))
|
|
|
|
|
{
|
|
|
|
|
string n = mb.GetType().Name;
|
|
|
|
|
if (mb is Button || mb is Image || mb is TextMeshProUGUI || mb is CanvasRenderer) continue;
|
|
|
|
|
if (n.Contains("Local") || n.Contains("Appraisal") || n.Contains("Send")) Destroy(mb);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-03 13:37:48 +08:00
|
|
|
/// <summary>对峙:卖家的报价装作成色没问题,玩家逐项指出问题;只在还没买下、卖家在场、卖家报价的卡上出现。</summary>
|
|
|
|
|
private void UpdatePointButtons(GameItem item)
|
|
|
|
|
{
|
|
|
|
|
if (pointRow == null) return;
|
|
|
|
|
var client = PlayerStore.Instance.currentClientInstance?.GetClientBlueprint();
|
|
|
|
|
bool show = client != null && client.clientIntent == StoreClient.ClientIntent.SELL && item.IsTag(Items.QuotedTag)
|
|
|
|
|
&& !GeneralHelper.IsItemOwned(item) && !InspectionUIManager.IsConfrontLocked();
|
|
|
|
|
pointRow.gameObject.SetActive(show);
|
|
|
|
|
if (!show) return;
|
|
|
|
|
for (int i = 0; i < point.Length; i++)
|
|
|
|
|
if (point[i] != null) point[i].gameObject.SetActive((Items.Pointed(item) & PointBits[i]) == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnPoint(int bit)
|
|
|
|
|
{
|
|
|
|
|
if (shown == null) return;
|
|
|
|
|
var item = shown;
|
|
|
|
|
int kind = Items.Point(item, bit);
|
|
|
|
|
var d = new Dialogue();
|
|
|
|
|
if (kind == 0)
|
|
|
|
|
{
|
|
|
|
|
d.SetText("CLIENT", L.T("point.none." + Random.Range(1, 3)));
|
|
|
|
|
InspectionUIManager.Instance.OnConfrontFailed();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
d.SetText("CLIENT", L.T(kind == 2 ? "point.fake." + Random.Range(1, 3) : "point.worn." + Random.Range(1, 3)));
|
|
|
|
|
InspectionUIManager.Instance.OnConfrontSuccess();
|
|
|
|
|
}
|
|
|
|
|
Log.Info($"对峙:指出 {bit},结果 {kind},报价 {item.unitValue}");
|
|
|
|
|
DialogUIManager.Instance.StartDialogue(d);
|
|
|
|
|
InspectionUIManager.Instance.CloseUI();
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-03 12:08:51 +08:00
|
|
|
private void Redraw(GameItem item)
|
|
|
|
|
{
|
|
|
|
|
foreach (Transform c in cardRoot) Destroy(c.gameObject);
|
|
|
|
|
var keys = new List<string>();
|
2026-11-03 13:04:45 +08:00
|
|
|
string baseKey = Items.LookKeys(item, keys); // 小图键名只用来取底图(品牌与正反面)
|
|
|
|
|
var layers = new List<string> { "gpu_big_" + baseKey.Substring("gpu_".Length) };
|
|
|
|
|
layers.AddRange(BigOverlays(item));
|
|
|
|
|
foreach (var k in layers)
|
2026-11-03 12:08:51 +08:00
|
|
|
{
|
|
|
|
|
var sprite = Items.ModSprite(k);
|
|
|
|
|
if (sprite == null) continue;
|
|
|
|
|
var go = new GameObject(k, typeof(RectTransform), typeof(Image));
|
|
|
|
|
var rt = go.GetComponent<RectTransform>();
|
|
|
|
|
rt.SetParent(cardRoot, false);
|
|
|
|
|
rt.sizeDelta = new Vector2(sprite.rect.width, sprite.rect.height);
|
2026-11-03 12:21:40 +08:00
|
|
|
rt.localScale = Vector3.one * 4f;
|
2026-11-03 12:08:51 +08:00
|
|
|
var img = go.GetComponent<Image>();
|
|
|
|
|
img.sprite = sprite;
|
|
|
|
|
img.raycastTarget = false;
|
|
|
|
|
}
|
|
|
|
|
if (flipText != null) flipText.text = L.T(Items.IsBack(item) ? "inspect.flip.front" : "inspect.flip.back");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|