Label bar in the magnifier tray: seller's claims per label, point out, edit with a labeler

Six labels (model, fan, contacts, bracket, back print, sticker). Dealers
state claims (honest tell the truth, middlemen hide 70%, scammers hide
everything and name the stamped model); the price follows claims until a
label is pointed out, then follows the real state. Pointing at a fake's
evidence exposes the model label too. A wrong accusation costs reputation.
Owned cards can have claims edited freely with a labeler present; a buyer
spots each hidden flaw with 35% chance, which exposes it and costs
reputation. Dimensions are additive percent adjustments on the model price.

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:50:24 +08:00
1 parent f93dbc6eec
commit b359a00789
9 files changed
+452 -174

No files matched your search

+140 -69
View File
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Decentralized.Domain;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -6,22 +8,26 @@ using UnityEngine.UI;
namespace Decentralized
{
/// <summary>
/// 放大镜检视显卡时的大图:和香烟一样把物品放大摆在检视台上,正反面各一张,看外观自己判断真假。
/// 放大镜检视显卡:和香烟一样把物品放大摆在检视台上,正反面各一张,下面是标签栏。
/// 标签栏每行一个标签(型号、风扇、金手指、挡板、丝印、贴纸):左边是卖家的说法,右边的按钮"指出"——
/// 说法属实就没事,隐瞒了问题价格回到真实情况,指错了掉信誉。归自己的卡,有标签机时按钮改成"改",随便改说法。
/// 游戏没有给 mod 开放检视界面,这里不打补丁,只在检视台(InspectionUIManager.panel)下面挂一个自己的界面,
/// 用一个常驻的 MonoBehaviour 每帧看一眼是否正在检视显卡。
/// </summary>
public sealed class GpuInspectView : MonoBehaviour
{
private const float RowH = 34f, CardScale = 3f;
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;
private Transform buttonTemplate;
private TMP_FontAsset font;
private readonly TextMeshProUGUI[] rowName = new TextMeshProUGUI[6], rowText = new TextMeshProUGUI[6];
private readonly Button[] rowButton = new Button[6];
private readonly TextMeshProUGUI[] rowButtonText = new TextMeshProUGUI[6];
public static void Ensure()
{
@@ -51,19 +57,19 @@ 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);
string sig = Items.LookKeys(item, keys) + "|" + string.Join(",", keys) + "|" + item.unitValue + "|" + Items.Exposed(item) + "|" + (Items.GetClaim(item)?.Encode() ?? "") + "|" + GeneralHelper.IsItemOwned(item) + "|" + HasLabeler();
if (item != shown || sig != shownSig)
{
shown = item;
shownSig = sig;
Redraw(item);
UpdateRows(item);
}
});
}
/// <summary>检视大图的叠加层。五个维度各自对应一种或几种细节,组合时再加几处同源的小细节(磕碰、开过壳的螺丝、重新粘过的封条)。</summary>
/// <summary>检视台上的叠加层:每个标签的每个取值对应一种细节,一一对应。</summary>
public static List<string> BigOverlays(GameItem item)
{
var o = new List<string>();
@@ -71,7 +77,6 @@ namespace Decentralized
if (u.Dead) return o;
var l = u.Appearance;
string P = "gpu_big_ov_";
// 每个维度的取值对应一种细节,一一对应,不靠组合触发
if (!Items.IsBack(item))
{
if (l.Fan == 1) o.Add(P + "fan_dust2");
@@ -91,6 +96,46 @@ namespace Decentralized
return o;
}
private static bool HasLabeler() => EmporiumEntry.Instance != null && EmporiumEntry.Instance.GetAllOwnedItemsByTag("LABELER_TAG").Count > 0;
private TextMeshProUGUI MakeText(Transform parent, string name, Vector2 pos, Vector2 size, TextAlignmentOptions align, float fontSize)
{
var go = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI));
var rt = go.GetComponent<RectTransform>();
rt.SetParent(parent, false);
rt.sizeDelta = size;
rt.localPosition = pos;
var t = go.GetComponent<TextMeshProUGUI>();
if (font != null) t.font = font;
t.fontSize = fontSize;
t.alignment = align;
t.enableWordWrapping = false;
t.overflowMode = TextOverflowModes.Ellipsis;
t.raycastTarget = false;
return t;
}
private Button MakeButton(Transform parent, string name, Vector2 pos, Vector2 size, out TextMeshProUGUI label)
{
var go = Instantiate(buttonTemplate.gameObject, parent, false);
go.name = name;
var rt = (RectTransform)go.transform;
rt.sizeDelta = size;
rt.localPosition = pos;
var b = go.GetComponent<Button>();
b.onClick = new Button.ButtonClickedEvent();
label = go.GetComponentInChildren<TextMeshProUGUI>();
label.enableAutoSizing = true; label.fontSizeMin = 12; label.fontSizeMax = 24;
// 去掉复制来的按钮上驱动文字的本地化组件,文字由这里设置
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);
}
return b;
}
private void Build(InspectionUIManager m)
{
root = new GameObject("_PdGpuInspection", typeof(RectTransform)).GetComponent<RectTransform>();
@@ -99,83 +144,110 @@ namespace Decentralized
root.localPosition = Vector3.zero;
cardRoot = new GameObject("Card", typeof(RectTransform)).GetComponent<RectTransform>();
cardRoot.SetParent(root, false);
cardRoot.localPosition = new Vector3(0, 0, 0);
// 翻面按钮:复制鉴定面板里"发送"按钮的外观
var template = m.appraisalPanel.transform.Find("buttonSend");
if (template != null)
cardRoot.localPosition = new Vector3(0, 85, 0);
buttonTemplate = m.appraisalPanel.transform.Find("buttonSend");
if (buttonTemplate == null) return;
font = buttonTemplate.GetComponentInChildren<TextMeshProUGUI>()?.font;
flip = MakeButton(root, "Flip", new Vector2(-230, 312), new Vector2(160, 34), out flipText);
flip.onClick.AddListener(() => { if (shown != null) Items.Flip(shown); });
for (int i = 0; i < 6; i++)
{
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, -290, 0);
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);
}
int row = CardLabels.Rows[i];
float y = -128 - i * RowH;
rowName[i] = MakeText(root, "RowName" + i, new Vector2(-262, y), new Vector2(110, RowH), TextAlignmentOptions.Left, 22);
rowText[i] = MakeText(root, "RowText" + i, new Vector2(-30, y), new Vector2(330, RowH), TextAlignmentOptions.Left, 22);
rowButton[i] = MakeButton(root, "RowButton" + i, new Vector2(245, y), new Vector2(110, RowH - 4), out rowButtonText[i]);
rowButton[i].onClick.AddListener(() => Log.Guard("标签栏", () => OnRowButton(row)));
}
}
/// <summary>对峙:卖家的报价装作成色没问题,玩家逐项指出问题;只在还没买下、卖家在场、卖家报价的卡上出现。</summary>
private void UpdatePointButtons(GameItem item)
private static string ValueText(int row, int value) =>
row == CardLabels.RowSpec ? "" : L.T("lbl." + (row == CardLabels.RowFan ? "fan" : row == CardLabels.RowFingers ? "fingers" : row == CardLabels.RowBracket ? "bracket" : row == CardLabels.RowPrint ? "print" : "sticker") + "." + value);
private static string RowName(int row) => L.T("lbl.row." + (row == CardLabels.RowSpec ? "spec" : row == CardLabels.RowFan ? "fan" : row == CardLabels.RowFingers ? "fingers" : row == CardLabels.RowBracket ? "bracket" : row == CardLabels.RowPrint ? "print" : "sticker"));
/// <summary>现场有没有卖家 / 鉴定客户在:决定"指出"按钮是否出现。</summary>
private static bool CanPoint(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);
if (client == null || GeneralHelper.IsItemOwned(item)) return false;
if (client.clientIntent == StoreClient.ClientIntent.APPRAISAL_SERVICE) return true;
return client.clientIntent == StoreClient.ClientIntent.SELL && !InspectionUIManager.IsConfrontLocked();
}
private void OnPoint(int bit)
private void UpdateRows(GameItem item)
{
if (rowName[0] == null) return;
var u = Items.ToUnit(item);
var real = Claim.Truth(u);
var claim = Items.GetClaim(item);
int exposed = Items.Exposed(item);
bool owned = GeneralHelper.IsItemOwned(item);
bool canPoint = CanPoint(item), canEdit = owned && HasLabeler();
for (int i = 0; i < 6; i++)
{
int row = CardLabels.Rows[i];
rowName[i].text = RowName(row);
string text;
Color color = Color.white;
if (claim == null)
{
text = row == CardLabels.RowSpec ? DisplayModel(real.Model) : ValueText(row, real.Get(row));
}
else if ((exposed & row) != 0)
{
int r = row == CardLabels.RowSpec ? 0 : real.Get(row);
text = row == CardLabels.RowSpec ? DisplayModel(real.Model) : ValueText(row, r);
bool lied = row == CardLabels.RowSpec ? claim.Value.Model != real.Model : claim.Value.Get(row) != r;
if (lied) { color = new Color(0.95f, 0.5f, 0.45f); text += L.T("lbl.said", row == CardLabels.RowSpec ? DisplayModel(claim.Value.Model) : ValueText(row, claim.Value.Get(row))); }
else color = new Color(0.55f, 0.85f, 0.55f);
}
else
{
text = row == CardLabels.RowSpec ? DisplayModel(claim.Value.Model) : ValueText(row, claim.Value.Get(row));
}
rowText[i].text = text;
rowText[i].color = color;
bool show = row != CardLabels.RowSpec && ((canPoint && claim != null && (exposed & row) == 0) || canEdit || (canPoint && claim == null));
bool point = canPoint && !canEdit;
rowButton[i].gameObject.SetActive(row != CardLabels.RowSpec ? ((canPoint && (exposed & row) == 0) || canEdit) : canEdit);
rowButtonText[i].text = L.T(canEdit ? "lbl.edit" : "lbl.point");
}
if (flipText != null) flipText.text = L.T(Items.IsBack(item) ? "inspect.flip.front" : "inspect.flip.back");
}
private static string DisplayModel(string model) => Items.DisplayModel(model);
private void OnRowButton(int row)
{
if (shown == null) return;
var item = shown;
int kind = Items.Point(item, bit);
var d = new Dialogue();
if (GeneralHelper.IsItemOwned(item))
{
// 标签机:改说法,每按一次换下一个取值(型号是在九个型号里轮换)
var u = Items.ToUnit(item);
var cur = Items.GetClaim(item) ?? Claim.Truth(u);
if (row == CardLabels.RowSpec)
{
var models = Hardware.Specs.Values.Where(s => !s.Asic).Select(s => s.Model).ToList();
Items.EditClaim(item, row, 0, models[(models.IndexOf(cur.Model) + 1) % models.Count]);
}
else Items.EditClaim(item, row, (cur.Get(row) + 1) % 3);
EmporiumAudioPlayer.Instance?.audioLabeler?.Play();
return;
}
int kind = Items.Point(item, row);
Log.Info($"指出 {row},结果 {kind},报价 {item.unitValue}");
if (kind == 0)
{
d.SetText("CLIENT", L.T("point.none." + Random.Range(1, 3)));
InspectionUIManager.Instance.OnConfrontFailed();
DialogUIManager.Instance.StartDialogue(new Dialogue().SetText("CLIENT", L.T("point.none." + Random.Range(1, 3))));
}
else
{
d.SetText("CLIENT", L.T(kind == 2 ? "point.fake." + Random.Range(1, 3) : "point.worn." + Random.Range(1, 3)));
InspectionUIManager.Instance.OnConfrontSuccess();
DialogUIManager.Instance.StartDialogue(new Dialogue().SetText("CLIENT", L.T(kind == 2 ? "point.fake." + Random.Range(1, 3) : "point.worn." + Random.Range(1, 3))));
}
Log.Info($"对峙:指出 {bit},结果 {kind},报价 {item.unitValue}");
DialogUIManager.Instance.StartDialogue(d);
InspectionUIManager.Instance.CloseUI();
}
private void Redraw(GameItem item)
@@ -193,12 +265,11 @@ namespace Decentralized
var rt = go.GetComponent<RectTransform>();
rt.SetParent(cardRoot, false);
rt.sizeDelta = new Vector2(sprite.rect.width, sprite.rect.height);
rt.localScale = Vector3.one * 4f;
rt.localScale = Vector3.one * CardScale;
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");
}
}
}
+93 -44
View File
@@ -21,7 +21,7 @@ namespace Decentralized
public const string FakeTag = "PD_FAKE_OF";
public const string RefurbTag = "PD_REFURBISHED";
public const string DeadTag = "PD_DEAD";
public const string LookTag = "PD_LOOK", BackTag = "PD_BACK", SeedTag = "PD_SEED", QuotedTag = "PD_QUOTED", MarkupTag = "PD_MARKUP", PointedTag = "PD_POINTED", ExposedTag = "PD_EXPOSED";
public const string LookTag = "PD_LOOK", BackTag = "PD_BACK", SeedTag = "PD_SEED", ClaimTag = "PD_CLAIM", ExposedTag = "PD_EXPOSED", MarkupTag = "PD_MARKUP";
public static Action OpenTerminal;
@@ -81,69 +81,120 @@ namespace Decentralized
Flip(toggled);
});
item.getItemSpriteModifierForItemFunc = Overlays;
// 摆给顾客的时候,顾客可能看出说法与实物对不上
item.onSlotAddItemFunc = (Action<GameItem, GameInventory, SlotMarker>)Delegate.Combine(item.onSlotAddItemFunc,
(Action<GameItem, GameInventory, SlotMarker>)((it, _, __) => Log.Guard("顾客验货", () => OnOffered(it))));
ModHelper.SetSource(item, ModInfo.Id, ModInfo.Name);
Refresh(item);
return item;
}
/// <summary>按给定的状态造一张卡(二手卡、假卡):写入标签并生成外观。</summary>
public static GameItem NewCard(string model, double health, string fakeOf, Rng rng)
public static GameItem NewCard(string model, double health, string fakeOf, Rng rng, DealerKind? seller = null, int markupPct = 0)
{
var item = ModHelper.SpawnItem(GpuId(model));
var u = new Unit { Model = model, FakeOf = fakeOf, Health = health, Look = CardLook.Generate(rng, health, fakeOf != null) };
Store(item, u);
if (seller != null) SetClaim(item, CardLabels.DealerClaim(u, seller.Value, rng), 0, markupPct);
return item;
}
/// <summary>贩子的柜台报价:在物品还不属于玩家时,按 <see cref="CardValue.Quote"/> 算(装作成色没问题,玩家指出一项才扣一项);
/// 归玩家之后立刻只值真实价值。老实人的卡不报价。</summary>
public static void SetQuote(GameItem item, int markupPct)
// ---------- 标签栏:卖家的说法与指出 ----------
private static object checkedClient;
private static readonly HashSet<int> checkedItems = new HashSet<int>();
public const double CustomerSpotChance = 0.35;
/// <summary>
/// 自己的卡摆给想买的顾客:说法里隐瞒的每一项,顾客有 35% 的概率看出来;看出来就拆穿这一项(价格回到真实情况),
/// 并且掉一点信誉。每位顾客对每张卡只看一次。说法比实际更差(把好卡说差了)顾客不会去指。
/// </summary>
private static void OnOffered(GameItem item)
{
item.EnableTag(QuotedTag);
item.ModifyTag(MarkupTag, t => t.Enable().SetInt(markupPct));
if (item.IsTag(DeadTag) || !GeneralHelper.IsItemOwned(item) || !item.IsRootedInBackInv()) return;
var ci = PlayerStore.Instance.currentClientInstance;
var client = ci?.GetClientBlueprint();
if (client == null || (client.clientIntent != StoreClient.ClientIntent.BUY && client.clientIntent != StoreClient.ClientIntent.SELLNBUY)) return;
var claim = GetClaim(item);
if (claim == null) return;
if (!ReferenceEquals(checkedClient, ci)) { checkedClient = ci; checkedItems.Clear(); }
if (!checkedItems.Add(item.uniqueId)) return;
var u = ToUnit(item);
int exposed = Exposed(item);
int lies = CardLabels.Lies(u, claim.Value, exposed);
if (lies == 0) return;
var rng = Session.Data.Rng;
int found = 0;
foreach (int r in CardLabels.Rows)
if ((lies & r) != 0 && rng.Chance(CustomerSpotChance))
{
found |= r;
if (r != CardLabels.RowSpec && CardLabels.IsFakeSign(r, Claim.Truth(u).Get(r))) found |= CardLabels.RowSpec;
}
if (found == 0) return;
item.ModifyTag(ExposedTag, t => t.Enable().SetInt(exposed | found));
Refresh(item);
string faction = string.IsNullOrEmpty(client.clientFaction) || StoreReputation.GetStoreReputation(client.clientFaction) == null ? "FACTION_LOWER_LEVEL" : client.clientFaction;
StoreReputation.ModReputation(faction, -2);
EmporiumAudioPlayer.Instance?.audioFail?.Play();
Log.Info($"顾客看出标签不实:{found},价格 {item.unitValue},信誉 −2");
DialogUIManager.Instance.StartDialogue(new Dialogue().SetText("CLIENT", L.T((found & CardLabels.RowSpec) != 0 ? "cust.fake." + rng.Range(1, 2) : "cust.worn." + rng.Range(1, 2))));
}
/// <summary>标签上的说法;没有说法的卡(自己挖出来、正规渠道的新卡)一律按真实情况算。</summary>
public static Claim? GetClaim(GameItem item)
{
if (!item.IsTag(ClaimTag)) return null;
var c = Claim.Decode(item.GetTagReadonly(ClaimTag).GetString());
return string.IsNullOrEmpty(c.Model) ? (Claim?)null : c;
}
public static int Exposed(GameItem item) => item.IsTag(ExposedTag) ? item.GetTagReadonly(ExposedTag).GetInt() : 0;
public static void SetClaim(GameItem item, Claim claim, int exposed, int markupPct = 0)
{
item.ModifyTag(ClaimTag, t => t.Enable().SetString(claim.Encode()));
item.ModifyTag(ExposedTag, t => t.Enable().SetInt(exposed));
if (markupPct != 0) item.ModifyTag(MarkupTag, t => t.Enable().SetInt(markupPct));
Refresh(item);
}
public static int Pointed(GameItem item) => item.IsTag(PointedTag) ? item.GetTagReadonly(PointedTag).GetInt() : 0;
/// <summary>现在标签上写的型号:说法里的型号,被拆穿之后是真实芯片。</summary>
public static string BelievedModel(GameItem item, Unit u)
{
var c = GetClaim(item);
if (c == null) return u.Model;
return (Exposed(item) & CardLabels.RowSpec) != 0 ? (u.FakeOf ?? u.Model) : c.Value.Model;
}
/// <summary>玩家对峙时能指出的五项:风扇、金手指、挡板、丝印、贴纸。</summary>
public const int PointFan = 1, PointFingers = 2, PointBracket = 4, PointPrint = 8, PointSticker = 16;
/// <summary>指出过的项换算成报价里扣价的维度位(丝印、接口数、手写序列号不扣价,只拆穿假货)。</summary>
private static int PricedDims(int pointed, CardLook l) =>
((pointed & PointFan) != 0 ? CardLook.DimFan : 0) | ((pointed & PointFingers) != 0 ? CardLook.DimFingers : 0)
| ((pointed & PointBracket) != 0 ? CardLook.DimScratch : 0) | ((pointed & PointSticker) != 0 ? CardLook.DimSeal : 0);
/// <summary>这一项有没有问题;返回 0 没问题,1 老化类问题(扣价),2 假货迹象(拆穿假货)。</summary>
public static int Defect(GameItem item, int point)
/// <summary>玩家指出一个标签:记下来,说法改回真实情况;返回 0 说法属实、1 隐瞒了不利的情况、2 拆穿假货。</summary>
public static int Point(GameItem item, int row)
{
var u = ToUnit(item);
var l = u.Appearance;
switch (point)
{
case PointFan: return l.Fan > 0 ? 1 : 0;
case PointFingers: return l.Fingers > 0 ? 1 : 0;
case PointBracket: return l.Bracket == 2 ? 2 : l.Bracket == 1 ? 1 : 0;
case PointPrint: return l.Print != 0 ? 2 : 0;
case PointSticker: return l.Sticker == 1 ? 2 : l.Sticker == 2 ? 1 : 0;
}
return 0;
var claim = GetClaim(item) ?? Claim.Truth(u);
int exposed = Exposed(item);
int kind = CardLabels.Point(u, claim, row, ref exposed);
item.ModifyTag(ExposedTag, t => t.Enable().SetInt(exposed));
if (!item.IsTag(ClaimTag)) item.ModifyTag(ClaimTag, t => t.Enable().SetString(claim.Encode()));
Refresh(item);
if (item.IsTag("APPRAISAL_ITEM")) AppraisalHelper.SetDefaultAppraisalValue(item, (int)item.unitValue);
return kind;
}
/// <summary>玩家指出一项:记下来,有问题就按维度扣价或拆穿假货;返回这一项的情况(0 没问题,1 扣价,2 拆穿)。</summary>
public static int Point(GameItem item, int point)
/// <summary>用标签机改说法(随便改):这一项重新算作"没人指出过"。</summary>
public static void EditClaim(GameItem item, int row, int value, string model = null)
{
int kind = Defect(item, point);
item.ModifyTag(PointedTag, t => t.Enable().SetInt(Pointed(item) | point));
if (kind == 2) item.EnableTag(ExposedTag);
Refresh(item);
return kind;
var u = ToUnit(item);
var claim = GetClaim(item) ?? Claim.Truth(u);
claim = row == CardLabels.RowSpec ? new Claim { Model = model ?? claim.Model, Fan = claim.Fan, Fingers = claim.Fingers, Bracket = claim.Bracket, Print = claim.Print, Sticker = claim.Sticker } : claim.With(row, value);
int exposed = Exposed(item) & ~row;
SetClaim(item, claim, exposed);
}
/// <summary>说明:算力和耗电按标称写;成色在归玩家之前只给粗略等级(要看外观自己判断),归玩家之后给精确健康度。</summary>
private static string Desc(GameItem item, Unit u)
{
var label = u.Label;
var label = Hardware.Specs[BelievedModel(item, u)];
string cond = GeneralHelper.IsItemOwned(item)
? Mathf.RoundToInt((float)u.Health) + "%"
: L.T(u.Health >= 85 ? "item.gpu.grade.1" : u.Health >= 60 ? "item.gpu.grade.2" : u.Health >= 35 ? "item.gpu.grade.3" : "item.gpu.grade.4");
@@ -153,12 +204,13 @@ namespace Decentralized
private static void SyncValue(GameItem item)
{
if (item.IsTag(DeadTag)) return;
string desc = Desc(item, ToUnit(item));
if (item.shortDescription != desc) item.shortDescription = desc;
var u = ToUnit(item);
long value = item.IsTag(QuotedTag) && !GeneralHelper.IsItemOwned(item)
? CardValue.Quote(u, item.IsTag(MarkupTag) ? item.GetTagReadonly(MarkupTag).GetInt() : 0, PricedDims(Pointed(item), u.Appearance), item.IsTag(ExposedTag))
: CardValue.True(u);
string desc = Desc(item, u);
if (item.shortDescription != desc) item.shortDescription = desc;
string name = L.T("item.gpu.name", Hardware.Specs[BelievedModel(item, u)].Brand, DisplayModel(BelievedModel(item, u)));
if (item.name != name) item.name = name;
long value = CardLabels.Value(u, GetClaim(item), Exposed(item));
if (!GeneralHelper.IsItemOwned(item) && item.IsTag(MarkupTag)) value = Math.Max(1, (long)Math.Round(value * (1 + item.GetTagReadonly(MarkupTag).GetInt() / 100.0)));
if (item.unitValue != value) item.unitValue = value;
}
@@ -202,9 +254,6 @@ namespace Decentralized
return;
}
item.SetSpriteAndShapeFromMod(ModInfo.Id, (label.Brand == Hardware.Probidia ? "gpu_probidia" : "gpu_pmd") + (item.IsTag(BackTag) ? "_b" : "_f"));
item.name = L.T("item.gpu.name", label.Brand, DisplayModel(label.Model));
item.shortDescription = Desc(item, u);
item.unitValue = CardValue.True(u);
SyncValue(item);
}
+28 -5
View File
@@ -72,14 +72,37 @@ item.gpu.grade.1,"like new"
item.gpu.grade.2,"decent"
item.gpu.grade.3,"clearly worn"
item.gpu.grade.4,"badly worn"
point.fan,"Point out fan"
point.fingers,"Point out contacts"
point.bracket,"Point out bracket"
point.print,"Point out print"
point.sticker,"Point out sticker"
point.none.1,"There's nothing wrong with it. You misread it."
point.none.2,"Where? Don't make things up if you're not buying."
point.worn.1,"...Fine, it has that. I'll knock something off."
point.worn.2,"Sharp eyes. All right, this price."
point.fake.1,"...You caught it. It's a reflashed one. I'll price it for what it is."
point.fake.2,"Tch, not the original chip. Fine, the real chip's price."
lbl.row.spec,"Model"
lbl.row.fan,"Fans"
lbl.row.fingers,"Contacts"
lbl.row.bracket,"Bracket"
lbl.row.print,"Back print"
lbl.row.sticker,"Sticker"
lbl.fan.0,"Blades clean"
lbl.fan.1,"Blades dusty"
lbl.fan.2,"Blade chipped"
lbl.fingers.0,"Bright"
lbl.fingers.1,"Dull"
lbl.fingers.2,"Oxidised"
lbl.bracket.0,"Intact, three ports"
lbl.bracket.1,"Scratched"
lbl.bracket.2,"Wrong port count"
lbl.print.0,"Matches the model"
lbl.print.1,"Another model printed"
lbl.print.2,"Reprinted, crooked"
lbl.sticker.0,"Printed serial, seal intact"
lbl.sticker.1,"Handwritten serial"
lbl.sticker.2,"Seal missing"
lbl.said," (seller said: {0})"
lbl.point,"Point out"
lbl.edit,"Edit"
cust.fake.1,"That's not what the label says. You trying to pass off a fake?"
cust.fake.2,"The model doesn't match. That isn't the chip it claims."
cust.worn.1,"That's not what you wrote. You left out the flaws."
cust.worn.2,"The condition on the label is false. Not at this price."
1 # Probably Decentralized localization table: English (en)
72 item.gpu.grade.2
73 item.gpu.grade.3
74 item.gpu.grade.4
point.fan
point.fingers
point.bracket
point.print
point.sticker
75 point.none.1
76 point.none.2
77 point.worn.1
78 point.worn.2
79 point.fake.1
80 point.fake.2
81 lbl.row.spec
82 lbl.row.fan
83 lbl.row.fingers
84 lbl.row.bracket
85 lbl.row.print
86 lbl.row.sticker
87 lbl.fan.0
88 lbl.fan.1
89 lbl.fan.2
90 lbl.fingers.0
91 lbl.fingers.1
92 lbl.fingers.2
93 lbl.bracket.0
94 lbl.bracket.1
95 lbl.bracket.2
96 lbl.print.0
97 lbl.print.1
98 lbl.print.2
99 lbl.sticker.0
100 lbl.sticker.1
101 lbl.sticker.2
102 lbl.said
103 lbl.point
104 lbl.edit
105 cust.fake.1
106 cust.fake.2
107 cust.worn.1
108 cust.worn.2
+28 -5
View File
@@ -71,14 +71,37 @@ item.gpu.grade.1,"接近全新"
item.gpu.grade.2,"成色尚可"
item.gpu.grade.3,"明显旧了"
item.gpu.grade.4,"磨损严重"
point.fan,"指出风扇"
point.fingers,"指出金手指"
point.bracket,"指出挡板"
point.print,"指出丝印"
point.sticker,"指出贴纸"
point.none.1,"没毛病,你看错了吧。"
point.none.2,"这儿哪有问题?不买别瞎说。"
point.worn.1,"……行,这点毛病是有,算你便宜点。"
point.worn.2,"眼睛挺毒。好吧,这个价。"
point.fake.1,"……被你看出来了。是翻新的货,按真的价给你。"
point.fake.2,"啧,不是原装的。那就按芯片本来的价吧。"
lbl.row.spec,"型号"
lbl.row.fan,"风扇"
lbl.row.fingers,"金手指"
lbl.row.bracket,"挡板"
lbl.row.print,"背面丝印"
lbl.row.sticker,"贴纸封条"
lbl.fan.0,"叶片干净"
lbl.fan.1,"叶片积灰"
lbl.fan.2,"叶片缺角"
lbl.fingers.0,"光亮"
lbl.fingers.1,"发暗"
lbl.fingers.2,"氧化发绿"
lbl.bracket.0,"完好,接口三个"
lbl.bracket.1,"有划痕"
lbl.bracket.2,"接口数不对"
lbl.print.0,"与型号一致"
lbl.print.1,"印着另一个型号"
lbl.print.2,"重印过,字歪"
lbl.sticker.0,"印刷序列号,封条完好"
lbl.sticker.1,"手写序列号"
lbl.sticker.2,"封条已缺失"
lbl.said,"(卖家说:{0})"
lbl.point,"指出"
lbl.edit,"改"
cust.fake.1,"这不是它标的那块。你拿假货糊弄我?"
cust.fake.2,"型号对不上,里面的芯片根本不是这个。"
cust.worn.1,"跟你写的不一样,毛病你没说。"
cust.worn.2,"标签上的成色是假的吧,这个价我不要。"
1 # Probably Decentralized 本地化表:简体中文(zh)
71 item.gpu.grade.2
72 item.gpu.grade.3
73 item.gpu.grade.4
point.fan
point.fingers
point.bracket
point.print
point.sticker
74 point.none.1
75 point.none.2
76 point.worn.1
77 point.worn.2
78 point.fake.1
79 point.fake.2
80 lbl.row.spec
81 lbl.row.fan
82 lbl.row.fingers
83 lbl.row.bracket
84 lbl.row.print
85 lbl.row.sticker
86 lbl.fan.0
87 lbl.fan.1
88 lbl.fan.2
89 lbl.fingers.0
90 lbl.fingers.1
91 lbl.fingers.2
92 lbl.bracket.0
93 lbl.bracket.1
94 lbl.bracket.2
95 lbl.print.0
96 lbl.print.1
97 lbl.print.2
98 lbl.sticker.0
99 lbl.sticker.1
100 lbl.sticker.2
101 lbl.said
102 lbl.point
103 lbl.edit
104 cust.fake.1
105 cust.fake.2
106 cust.worn.1
107 cust.worn.2
+4 -5
View File
@@ -68,8 +68,8 @@ namespace Decentralized
int day = StoreStation.GetDayCounter();
var cards = UsedDealers.Cards(DealerKind.Middleman, day, rng);
var c = cards[0];
var item = Items.NewCard(c.Model, c.Health, c.FakeOf, rng);
AppraisalHelper.InitAppraisalItem(item, (int)CardValue.Of(c.Model, c.Health));
var item = Items.NewCard(c.Model, c.Health, c.FakeOf, rng, DealerKind.Middleman); // 带一份说法:鉴定时玩家要指出哪些不实
AppraisalHelper.InitAppraisalItem(item, (int)item.unitValue);
return item;
}
@@ -99,10 +99,9 @@ namespace Decentralized
var placed = new List<string>();
foreach (var c in cards)
{
var item = Items.NewCard(c.Model, c.Health, c.FakeOf, rng);
if (c.Quoted) Items.SetQuote(item, c.MarkupPct);
var item = Items.NewCard(c.Model, c.Health, c.FakeOf, rng, kind, c.MarkupPct);
PlayerStore.Instance.AddDirectSellingItemToTable(item);
placed.Add($"{c.Model}{(c.FakeOf != null ? "←" + c.FakeOf : "")} {c.Health:0}% {(c.Quoted ? c.MarkupPct + "%" : "不报价")} 柜台价 {item.unitValue}(真实 {CardValue.True(Items.ToUnit(item))})");
placed.Add($"{c.Model}{(c.FakeOf != null ? "←" + c.FakeOf : "")} {c.Health:0}% 加价 {c.MarkupPct}% 柜台价 {item.unitValue}(真实 {CardValue.True(Items.ToUnit(item))})");
}
Log.Info($"二手贩子({kind}):{string.Join(",", placed)}");
});