Cards: appearance follows wear, hash depends on fan and contacts, seller confrontation

Fan, contact and scratch levels are recomputed from health and a per-card
seed, so a card looks older as it wears. Fan and contacts scale hashrate.
Each inspection-tray detail now maps to exactly one dimension value. Quoting
dealers price the card as if its condition were fine; the player points out
fan, contacts, bracket, print or sticker in the tray to take the matching
discount or expose a fake (a wrong accusation costs reputation like the
vanilla confrontation). Unowned cards show a coarse grade and rated specs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Sonnet 5.5 committed 2026-11-03 13:37:48 +08:00
1 parent 68265095a8
commit f93dbc6eec
9 files changed
+279 -76

No files matched your search

+70 -8
View File
@@ -15,6 +15,10 @@ namespace Decentralized
private static GpuInspectView instance;
private RectTransform root, cardRoot;
private Button flip;
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" };
private TextMeshProUGUI flipText;
private GameItem shown;
private string shownSig;
@@ -47,6 +51,7 @@ namespace Decentralized
{
if (root == null) Build(m);
if (!root.gameObject.activeSelf) root.gameObject.SetActive(true);
UpdatePointButtons(item);
var keys = new List<string>();
string sig = Items.LookKeys(item, keys) + "|" + string.Join(",", keys);
if (item != shown || sig != shownSig)
@@ -64,24 +69,24 @@ namespace Decentralized
var o = new List<string>();
var u = Items.ToUnit(item);
if (u.Dead) return o;
var l = u.Look;
var l = u.Appearance;
string P = "gpu_big_ov_";
// 每个维度的取值对应一种细节,一一对应,不靠组合触发
if (!Items.IsBack(item))
{
if (l.Fan == 1) o.Add(P + (l.Fingers > 0 ? "fan_dust2" : "fan_dust1"));
if (l.Fan == 2) { o.Add(P + "fan_chip"); if (l.Fingers == 2) o.Add(P + "fan_oil"); }
if (l.Fan == 1) o.Add(P + "fan_dust2");
if (l.Fan == 2) { o.Add(P + "fan_dust1"); o.Add(P + "fan_chip"); }
if (l.Fingers == 1) o.Add(P + "fin_dim");
if (l.Fingers == 2) o.Add(P + "fin_oxide");
if (l.Bracket == 1) { o.Add(P + "brk_scratch"); if (l.Fan > 0) o.Add(P + "shroud_chip"); }
if (l.Bracket == 1) o.Add(P + "brk_scratch");
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.Print == 2) o.Add(P + "screw_opened"); // 重印过,说明拆开过
if (l.Sticker == 1) o.Add(P + "sticker_hand");
if (l.Sticker == 2) o.Add(P + (l.Print != 0 ? "seal_reglued" : "seal_missing"));
if (l.Sticker == 2) o.Add(P + "seal_missing");
}
return o;
}
@@ -94,14 +99,37 @@ namespace Decentralized
root.localPosition = Vector3.zero;
cardRoot = new GameObject("Card", typeof(RectTransform)).GetComponent<RectTransform>();
cardRoot.SetParent(root, false);
cardRoot.localPosition = new Vector3(0, 40, 0);
cardRoot.localPosition = new Vector3(0, 0, 0);
// 翻面按钮:复制鉴定面板里"发送"按钮的外观
var template = m.appraisalPanel.transform.Find("buttonSend");
if (template != null)
{
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]);
}
var go = Instantiate(template.gameObject, root, false);
go.name = "Flip";
go.transform.localPosition = new Vector3(0, -300, 0);
go.transform.localPosition = new Vector3(0, -290, 0);
flip = go.GetComponent<Button>();
flip.onClick = new Button.ButtonClickedEvent();
flip.onClick.AddListener(() => { if (shown != null) Items.Flip(shown); });
@@ -116,6 +144,40 @@ namespace Decentralized
}
}
/// <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();
}
private void Redraw(GameItem item)
{
foreach (Transform c in cardRoot) Destroy(c.gameObject);