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:
1 parent
f93dbc6eec
commit
b359a00789
9 files changed
+452
-174
No files matched your search
@@ -0,0 +1,134 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Decentralized.Domain
|
||||||
|
{
|
||||||
|
/// <summary>卖家的说法:每个标签一项。型号是文字,其余五项取值与 <see cref="CardLook"/> 的取值一致。</summary>
|
||||||
|
public struct Claim
|
||||||
|
{
|
||||||
|
public string Model;
|
||||||
|
public int Fan, Fingers, Bracket, Print, Sticker;
|
||||||
|
|
||||||
|
public int Get(int row) => row == CardLabels.RowFan ? Fan : row == CardLabels.RowFingers ? Fingers : row == CardLabels.RowBracket ? Bracket : row == CardLabels.RowPrint ? Print : Sticker;
|
||||||
|
|
||||||
|
public Claim With(int row, int value)
|
||||||
|
{
|
||||||
|
var c = this;
|
||||||
|
if (row == CardLabels.RowFan) c.Fan = value; else if (row == CardLabels.RowFingers) c.Fingers = value;
|
||||||
|
else if (row == CardLabels.RowBracket) c.Bracket = value; else if (row == CardLabels.RowPrint) c.Print = value; else c.Sticker = value;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Encode() => $"{Model}:{Fan}{Fingers}{Bracket}{Print}{Sticker}";
|
||||||
|
|
||||||
|
public static Claim Decode(string s)
|
||||||
|
{
|
||||||
|
var p = (s ?? "").Split(':');
|
||||||
|
if (p.Length != 2 || p[1].Length != 5 || !Hardware.Specs.ContainsKey(p[0])) return default;
|
||||||
|
int D(int i) => Math.Max(0, Math.Min(2, p[1][i] - '0'));
|
||||||
|
return new Claim { Model = p[0], Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>按真实情况写出的说法(没有隐瞒)。</summary>
|
||||||
|
public static Claim Truth(Unit u)
|
||||||
|
{
|
||||||
|
var l = u.Appearance;
|
||||||
|
return new Claim { Model = u.FakeOf ?? u.Model, Fan = l.Fan, Fingers = l.Fingers, Bracket = l.Bracket, Print = l.Print, Sticker = l.Sticker };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标签栏的数据:六个标签(型号、风扇、金手指、挡板、丝印、贴纸),每个标签有卖家的说法和真实情况。
|
||||||
|
/// 一个标签被指出(拆穿)之后,说法改回真实情况;价格按「已指出的标签取真实情况,其余取说法」算,
|
||||||
|
/// 所以卖家隐瞒的问题要玩家一项项指出来才能把价格拉回真实价值。
|
||||||
|
/// </summary>
|
||||||
|
public static class CardLabels
|
||||||
|
{
|
||||||
|
public const int RowSpec = 1, RowFan = 2, RowFingers = 4, RowBracket = 8, RowPrint = 16, RowSticker = 32, AllRows = 63;
|
||||||
|
public static readonly int[] Rows = { RowSpec, RowFan, RowFingers, RowBracket, RowPrint, RowSticker };
|
||||||
|
|
||||||
|
/// <summary>各标签各取值对价格的影响(百分比,相加):风扇积灰 −4%、缺角 −12%;金手指发暗 −4%、氧化 −12%;挡板划痕 −2%;封条缺失 −6%。
|
||||||
|
/// 接口数不对、丝印、手写序列号本身不扣价,它们是假货的证据。</summary>
|
||||||
|
public static double Pct(int row, int value)
|
||||||
|
{
|
||||||
|
switch (row)
|
||||||
|
{
|
||||||
|
case RowFan: return value == 1 ? -4 : value == 2 ? -12 : 0;
|
||||||
|
case RowFingers: return value == 1 ? -4 : value == 2 ? -12 : 0;
|
||||||
|
case RowBracket: return value == 1 ? -2 : 0;
|
||||||
|
case RowSticker: return value == 2 ? -6 : 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>这一项的真实取值是不是假货的证据。</summary>
|
||||||
|
public static bool IsFakeSign(int row, int realValue) =>
|
||||||
|
(row == RowBracket && realValue == 2) || (row == RowPrint && realValue != 0) || (row == RowSticker && realValue == 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 价值:claim 为空表示没有人说过什么,一律按真实情况;exposed 里的位表示已指出的标签(取真实情况)。
|
||||||
|
/// 型号取「说法里的型号」,型号标签被拆穿之后取真实芯片。价值 = 型号定价 ×(0.25 + 0.75 × 健康度)×(1 + 各标签折扣之和)。
|
||||||
|
/// </summary>
|
||||||
|
public static long Value(Unit u, Claim? claim, int exposed)
|
||||||
|
{
|
||||||
|
var real = Claim.Truth(u);
|
||||||
|
Claim believed = claim == null ? real : claim.Value;
|
||||||
|
double sum = 0;
|
||||||
|
foreach (int r in Rows)
|
||||||
|
{
|
||||||
|
if (r == RowSpec) continue;
|
||||||
|
int v = claim == null || (exposed & r) != 0 ? real.Get(r) : believed.Get(r);
|
||||||
|
sum += Pct(r, v);
|
||||||
|
}
|
||||||
|
string model = claim == null || (exposed & RowSpec) != 0 ? real.Model : believed.Model;
|
||||||
|
double value = CardValue.Of(model, u.Health) * Math.Max(0, 1 + sum / 100.0);
|
||||||
|
return Math.Max(1, (long)Math.Round(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>指出一个标签:返回 0 没问题(说法属实)、1 隐瞒了对价格不利的情况、2 拆穿了假货。
|
||||||
|
/// 指出假货的证据(接口数、丝印、手写序列号)时,型号标签一并拆穿。</summary>
|
||||||
|
public static int Point(Unit u, Claim claim, int row, ref int exposed)
|
||||||
|
{
|
||||||
|
var real = Claim.Truth(u);
|
||||||
|
bool wasExposed = (exposed & row) != 0;
|
||||||
|
exposed |= row;
|
||||||
|
if (row == RowSpec) return claim.Model != real.Model ? 2 : 0;
|
||||||
|
if (IsFakeSign(row, real.Get(row)) && claim.Get(row) != real.Get(row))
|
||||||
|
{
|
||||||
|
exposed |= RowSpec;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (wasExposed) return 0;
|
||||||
|
return claim.Get(row) != real.Get(row) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>说法里隐瞒了什么:说法比真实情况价值高的标签,以及型号不符。</summary>
|
||||||
|
public static int Lies(Unit u, Claim claim, int exposed)
|
||||||
|
{
|
||||||
|
var real = Claim.Truth(u);
|
||||||
|
int lies = 0;
|
||||||
|
foreach (int r in Rows)
|
||||||
|
{
|
||||||
|
if ((exposed & r) != 0) continue;
|
||||||
|
if (r == RowSpec) { if (claim.Model != real.Model) lies |= r; }
|
||||||
|
else if (claim.Get(r) != real.Get(r) && (Pct(r, claim.Get(r)) > Pct(r, real.Get(r)) || IsFakeSign(r, real.Get(r)))) lies |= r;
|
||||||
|
}
|
||||||
|
return lies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>卖家怎么说:老实人如实说;中间商每项有三成概率隐瞒;骗子全部说成没问题。假卡的型号一律说成标称型号。</summary>
|
||||||
|
public static Claim DealerClaim(Unit u, DealerKind kind, Rng rng)
|
||||||
|
{
|
||||||
|
var c = Claim.Truth(u);
|
||||||
|
c.Model = u.Model;
|
||||||
|
if (kind == DealerKind.Honest) { c.Model = u.FakeOf ?? u.Model; return c; }
|
||||||
|
double hide = kind == DealerKind.Scammer ? 1.0 : 0.7;
|
||||||
|
foreach (int r in Rows)
|
||||||
|
{
|
||||||
|
if (r == RowSpec) continue;
|
||||||
|
if (rng.Chance(hide)) c = c.With(r, 0);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,20 +62,9 @@ namespace Decentralized.Domain
|
|||||||
return new CardLook { Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4) };
|
return new CardLook { Fan = D(0), Fingers = D(1), Bracket = D(2), Print = D(3), Sticker = D(4) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 外观维度对应卡的真实状态:价格、损耗、暴毙概率都随之变化,所以看外观就是在估价
|
// 外观维度对应卡的真实状态:损耗、暴毙概率随之变化,价值走标签(见 CardLabels)
|
||||||
private static readonly double[] FanValue = { 1.00, 0.96, 0.88 }, FanWear = { 1.00, 1.10, 1.30 }, FanDeath = { 1.00, 1.00, 1.20 };
|
private static readonly double[] FanWear = { 1.00, 1.10, 1.30 }, FanDeath = { 1.00, 1.00, 1.20 };
|
||||||
private static readonly double[] FingerValue = { 1.00, 0.96, 0.88 }, FingerDeath = { 1.00, 1.10, 1.30 };
|
private static readonly double[] FingerDeath = { 1.00, 1.10, 1.30 };
|
||||||
|
|
||||||
/// <summary>外观对价值的折扣:风扇积灰 −4%、缺角 −12%;金手指发暗 −4%、氧化 −12%;挡板划痕 −2%;封条缺失(开过封)−6%。</summary>
|
|
||||||
public double ValueFactor => ValueFactorOf(AllDims);
|
|
||||||
|
|
||||||
/// <summary>外观折扣的维度位:1 风扇、2 金手指、4 挡板划痕、8 封条缺失。</summary>
|
|
||||||
public const int DimFan = 1, DimFingers = 2, DimScratch = 4, DimSeal = 8, AllDims = 15;
|
|
||||||
|
|
||||||
/// <summary>只计入 mask 里的维度的折扣:贩子报价时先装作这些问题都没有,玩家指出一项才扣一项。</summary>
|
|
||||||
public double ValueFactorOf(int mask) =>
|
|
||||||
((mask & DimFan) != 0 ? FanValue[Fan] : 1.0) * ((mask & DimFingers) != 0 ? FingerValue[Fingers] : 1.0)
|
|
||||||
* ((mask & DimScratch) != 0 && Bracket == 1 ? 0.98 : 1.0) * ((mask & DimSeal) != 0 && Sticker == 2 ? 0.94 : 1.0);
|
|
||||||
|
|
||||||
/// <summary>每晚损耗倍数:风扇积灰散热差 ×1.1,缺角 ×1.3。</summary>
|
/// <summary>每晚损耗倍数:风扇积灰散热差 ×1.1,缺角 ×1.3。</summary>
|
||||||
public double WearFactor => FanWear[Fan];
|
public double WearFactor => FanWear[Fan];
|
||||||
@@ -131,22 +120,8 @@ namespace Decentralized.Domain
|
|||||||
return Math.Max(1, (long)Math.Round(spec.Price * f));
|
return Math.Max(1, (long)Math.Round(spec.Price * f));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>真实价值(芯片型号 + 健康度 + 外观折扣)。</summary>
|
/// <summary>真实价值:全部标签都按真实情况算。</summary>
|
||||||
public static long True(Unit u) => Math.Max(1, (long)Math.Round(Of(u.FakeOf ?? u.Model, u.Health) * u.Appearance.ValueFactor));
|
public static long True(Unit u) => CardLabels.Value(u, null, 0);
|
||||||
|
|
||||||
public const double ScamDiscount = 0.90;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 贩子的报价:装作成色没问题(外观折扣只计玩家已指出的维度),在此之上加价 markupPct%。
|
|
||||||
/// 没拆穿的假卡按标称型号的价再打九折;拆穿之后按真实芯片算,和真卡一样。
|
|
||||||
/// </summary>
|
|
||||||
public static long Quote(Unit u, int markupPct, int pointedDims, bool exposed)
|
|
||||||
{
|
|
||||||
bool fakeHidden = u.FakeOf != null && !exposed;
|
|
||||||
double v = Of(fakeHidden ? u.Model : (u.FakeOf ?? u.Model), u.Health) * u.Appearance.ValueFactorOf(pointedDims);
|
|
||||||
v *= fakeHidden ? ScamDiscount : 1 + markupPct / 100.0;
|
|
||||||
return Math.Max(1, (long)Math.Round(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>标称价:贩子报的价,按标称型号算;二手卡也折旧,但只按贩子"自称"的健康度。</summary>
|
/// <summary>标称价:贩子报的价,按标称型号算;二手卡也折旧,但只按贩子"自称"的健康度。</summary>
|
||||||
public static long Quoted(Unit u, double claimedHealth) => Of(u.Model, claimedHealth);
|
public static long Quoted(Unit u, double claimedHealth) => Of(u.Model, claimedHealth);
|
||||||
|
|||||||
@@ -7,20 +7,18 @@ namespace Decentralized.Domain
|
|||||||
/// <summary>二手显卡贩子的类型:老实人按真实价卖;中间商加价;骗子夹带假卡,按标称型号的价卖。</summary>
|
/// <summary>二手显卡贩子的类型:老实人按真实价卖;中间商加价;骗子夹带假卡,按标称型号的价卖。</summary>
|
||||||
public enum DealerKind { Honest, Middleman, Scammer }
|
public enum DealerKind { Honest, Middleman, Scammer }
|
||||||
|
|
||||||
/// <summary>贩子带来的一张卡。老实人不报价(按真实价值卖);其他贩子 <see cref="Quoted"/> 为真,按 <see cref="CardValue.Quote"/> 报价,
|
/// <summary>贩子带来的一张卡:型号、健康度、是不是假卡。怎么说由 <see cref="CardLabels.DealerClaim"/> 决定,中间商另加 <see cref="MarkupPct"/>% 的价。</summary>
|
||||||
/// <see cref="MarkupPct"/> 是加价百分比,玩家指出外观问题或假货迹象就按维度扣回去。</summary>
|
|
||||||
public sealed class UsedCard
|
public sealed class UsedCard
|
||||||
{
|
{
|
||||||
public string Model;
|
public string Model;
|
||||||
public string FakeOf;
|
public string FakeOf;
|
||||||
public double Health;
|
public double Health;
|
||||||
public int MarkupPct;
|
public int MarkupPct;
|
||||||
public bool Quoted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UsedDealers
|
public static class UsedDealers
|
||||||
{
|
{
|
||||||
public const double MiddlemanMarkup = 0.08, ScamDiscount = CardValue.ScamDiscount;
|
public const double MiddlemanMarkup = 0.08;
|
||||||
public const double FakeShareMiddleman = 0.10, FakeShareScammer = 0.45;
|
public const double FakeShareMiddleman = 0.10, FakeShareScammer = 0.45;
|
||||||
|
|
||||||
/// <summary>今天来的贩子的类型:骗子越往后越多。</summary>
|
/// <summary>今天来的贩子的类型:骗子越往后越多。</summary>
|
||||||
@@ -52,7 +50,6 @@ namespace Decentralized.Domain
|
|||||||
var card = new UsedCard { Model = label, Health = 25 + rng.NextDouble() * 60 };
|
var card = new UsedCard { Model = label, Health = 25 + rng.NextDouble() * 60 };
|
||||||
var cheaper = Hardware.Specs.Values.Where(s => !s.Asic && s.Price * 2 <= Hardware.Specs[label].Price).Select(s => s.Model).ToArray();
|
var cheaper = Hardware.Specs.Values.Where(s => !s.Asic && s.Price * 2 <= Hardware.Specs[label].Price).Select(s => s.Model).ToArray();
|
||||||
if (cheaper.Length > 0 && rng.Chance(share)) card.FakeOf = cheaper[rng.Range(0, cheaper.Length - 1)];
|
if (cheaper.Length > 0 && rng.Chance(share)) card.FakeOf = cheaper[rng.Range(0, cheaper.Length - 1)];
|
||||||
card.Quoted = kind != DealerKind.Honest;
|
|
||||||
card.MarkupPct = kind == DealerKind.Middleman ? (int)Math.Round(MiddlemanMarkup * 100) : 0;
|
card.MarkupPct = kind == DealerKind.Middleman ? (int)Math.Round(MiddlemanMarkup * 100) : 0;
|
||||||
cards.Add(card);
|
cards.Add(card);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Decentralized.Domain;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
@@ -6,22 +8,26 @@ using UnityEngine.UI;
|
|||||||
namespace Decentralized
|
namespace Decentralized
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 放大镜检视显卡时的大图:和香烟一样把物品放大摆在检视台上,正反面各一张,看外观自己判断真假。
|
/// 放大镜检视显卡:和香烟一样把物品放大摆在检视台上,正反面各一张,下面是标签栏。
|
||||||
|
/// 标签栏每行一个标签(型号、风扇、金手指、挡板、丝印、贴纸):左边是卖家的说法,右边的按钮"指出"——
|
||||||
|
/// 说法属实就没事,隐瞒了问题价格回到真实情况,指错了掉信誉。归自己的卡,有标签机时按钮改成"改",随便改说法。
|
||||||
/// 游戏没有给 mod 开放检视界面,这里不打补丁,只在检视台(InspectionUIManager.panel)下面挂一个自己的界面,
|
/// 游戏没有给 mod 开放检视界面,这里不打补丁,只在检视台(InspectionUIManager.panel)下面挂一个自己的界面,
|
||||||
/// 用一个常驻的 MonoBehaviour 每帧看一眼是否正在检视显卡。
|
/// 用一个常驻的 MonoBehaviour 每帧看一眼是否正在检视显卡。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class GpuInspectView : MonoBehaviour
|
public sealed class GpuInspectView : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
private const float RowH = 34f, CardScale = 3f;
|
||||||
private static GpuInspectView instance;
|
private static GpuInspectView instance;
|
||||||
private RectTransform root, cardRoot;
|
private RectTransform root, cardRoot;
|
||||||
private Button flip;
|
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 TextMeshProUGUI flipText;
|
||||||
private GameItem shown;
|
private GameItem shown;
|
||||||
private string shownSig;
|
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()
|
public static void Ensure()
|
||||||
{
|
{
|
||||||
@@ -51,19 +57,19 @@ namespace Decentralized
|
|||||||
{
|
{
|
||||||
if (root == null) Build(m);
|
if (root == null) Build(m);
|
||||||
if (!root.gameObject.activeSelf) root.gameObject.SetActive(true);
|
if (!root.gameObject.activeSelf) root.gameObject.SetActive(true);
|
||||||
UpdatePointButtons(item);
|
|
||||||
var keys = new List<string>();
|
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)
|
if (item != shown || sig != shownSig)
|
||||||
{
|
{
|
||||||
shown = item;
|
shown = item;
|
||||||
shownSig = sig;
|
shownSig = sig;
|
||||||
Redraw(item);
|
Redraw(item);
|
||||||
|
UpdateRows(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>检视大图的叠加层。五个维度各自对应一种或几种细节,组合时再加几处同源的小细节(磕碰、开过壳的螺丝、重新粘过的封条)。</summary>
|
/// <summary>检视台上的叠加层:每个标签的每个取值对应一种细节,一一对应。</summary>
|
||||||
public static List<string> BigOverlays(GameItem item)
|
public static List<string> BigOverlays(GameItem item)
|
||||||
{
|
{
|
||||||
var o = new List<string>();
|
var o = new List<string>();
|
||||||
@@ -71,7 +77,6 @@ namespace Decentralized
|
|||||||
if (u.Dead) return o;
|
if (u.Dead) return o;
|
||||||
var l = u.Appearance;
|
var l = u.Appearance;
|
||||||
string P = "gpu_big_ov_";
|
string P = "gpu_big_ov_";
|
||||||
// 每个维度的取值对应一种细节,一一对应,不靠组合触发
|
|
||||||
if (!Items.IsBack(item))
|
if (!Items.IsBack(item))
|
||||||
{
|
{
|
||||||
if (l.Fan == 1) o.Add(P + "fan_dust2");
|
if (l.Fan == 1) o.Add(P + "fan_dust2");
|
||||||
@@ -91,6 +96,46 @@ namespace Decentralized
|
|||||||
return o;
|
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)
|
private void Build(InspectionUIManager m)
|
||||||
{
|
{
|
||||||
root = new GameObject("_PdGpuInspection", typeof(RectTransform)).GetComponent<RectTransform>();
|
root = new GameObject("_PdGpuInspection", typeof(RectTransform)).GetComponent<RectTransform>();
|
||||||
@@ -99,83 +144,110 @@ namespace Decentralized
|
|||||||
root.localPosition = Vector3.zero;
|
root.localPosition = Vector3.zero;
|
||||||
cardRoot = new GameObject("Card", typeof(RectTransform)).GetComponent<RectTransform>();
|
cardRoot = new GameObject("Card", typeof(RectTransform)).GetComponent<RectTransform>();
|
||||||
cardRoot.SetParent(root, false);
|
cardRoot.SetParent(root, false);
|
||||||
cardRoot.localPosition = new Vector3(0, 0, 0);
|
cardRoot.localPosition = new Vector3(0, 85, 0);
|
||||||
// 翻面按钮:复制鉴定面板里"发送"按钮的外观
|
buttonTemplate = m.appraisalPanel.transform.Find("buttonSend");
|
||||||
var template = m.appraisalPanel.transform.Find("buttonSend");
|
if (buttonTemplate == null) return;
|
||||||
if (template != null)
|
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>();
|
int row = CardLabels.Rows[i];
|
||||||
pointRow.SetParent(root, false);
|
float y = -128 - i * RowH;
|
||||||
pointRow.localPosition = new Vector3(0, 312, 0);
|
rowName[i] = MakeText(root, "RowName" + i, new Vector2(-262, y), new Vector2(110, RowH), TextAlignmentOptions.Left, 22);
|
||||||
for (int i = 0; i < point.Length; i++)
|
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]);
|
||||||
int bit = PointBits[i];
|
rowButton[i].onClick.AddListener(() => Log.Guard("标签栏", () => OnRowButton(row)));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>对峙:卖家的报价装作成色没问题,玩家逐项指出问题;只在还没买下、卖家在场、卖家报价的卡上出现。</summary>
|
private static string ValueText(int row, int value) =>
|
||||||
private void UpdatePointButtons(GameItem item)
|
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();
|
var client = PlayerStore.Instance.currentClientInstance?.GetClientBlueprint();
|
||||||
bool show = client != null && client.clientIntent == StoreClient.ClientIntent.SELL && item.IsTag(Items.QuotedTag)
|
if (client == null || GeneralHelper.IsItemOwned(item)) return false;
|
||||||
&& !GeneralHelper.IsItemOwned(item) && !InspectionUIManager.IsConfrontLocked();
|
if (client.clientIntent == StoreClient.ClientIntent.APPRAISAL_SERVICE) return true;
|
||||||
pointRow.gameObject.SetActive(show);
|
return client.clientIntent == StoreClient.ClientIntent.SELL && !InspectionUIManager.IsConfrontLocked();
|
||||||
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)
|
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;
|
if (shown == null) return;
|
||||||
var item = shown;
|
var item = shown;
|
||||||
int kind = Items.Point(item, bit);
|
if (GeneralHelper.IsItemOwned(item))
|
||||||
var d = new Dialogue();
|
{
|
||||||
|
// 标签机:改说法,每按一次换下一个取值(型号是在九个型号里轮换)
|
||||||
|
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)
|
if (kind == 0)
|
||||||
{
|
{
|
||||||
d.SetText("CLIENT", L.T("point.none." + Random.Range(1, 3)));
|
|
||||||
InspectionUIManager.Instance.OnConfrontFailed();
|
InspectionUIManager.Instance.OnConfrontFailed();
|
||||||
|
DialogUIManager.Instance.StartDialogue(new Dialogue().SetText("CLIENT", L.T("point.none." + Random.Range(1, 3))));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
d.SetText("CLIENT", L.T(kind == 2 ? "point.fake." + Random.Range(1, 3) : "point.worn." + Random.Range(1, 3)));
|
|
||||||
InspectionUIManager.Instance.OnConfrontSuccess();
|
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)
|
private void Redraw(GameItem item)
|
||||||
@@ -193,12 +265,11 @@ namespace Decentralized
|
|||||||
var rt = go.GetComponent<RectTransform>();
|
var rt = go.GetComponent<RectTransform>();
|
||||||
rt.SetParent(cardRoot, false);
|
rt.SetParent(cardRoot, false);
|
||||||
rt.sizeDelta = new Vector2(sprite.rect.width, sprite.rect.height);
|
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>();
|
var img = go.GetComponent<Image>();
|
||||||
img.sprite = sprite;
|
img.sprite = sprite;
|
||||||
img.raycastTarget = false;
|
img.raycastTarget = false;
|
||||||
}
|
}
|
||||||
if (flipText != null) flipText.text = L.T(Items.IsBack(item) ? "inspect.flip.front" : "inspect.flip.back");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+93
-44
@@ -21,7 +21,7 @@ namespace Decentralized
|
|||||||
public const string FakeTag = "PD_FAKE_OF";
|
public const string FakeTag = "PD_FAKE_OF";
|
||||||
public const string RefurbTag = "PD_REFURBISHED";
|
public const string RefurbTag = "PD_REFURBISHED";
|
||||||
public const string DeadTag = "PD_DEAD";
|
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;
|
public static Action OpenTerminal;
|
||||||
|
|
||||||
@@ -81,69 +81,120 @@ namespace Decentralized
|
|||||||
Flip(toggled);
|
Flip(toggled);
|
||||||
});
|
});
|
||||||
item.getItemSpriteModifierForItemFunc = Overlays;
|
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);
|
ModHelper.SetSource(item, ModInfo.Id, ModInfo.Name);
|
||||||
Refresh(item);
|
Refresh(item);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>按给定的状态造一张卡(二手卡、假卡):写入标签并生成外观。</summary>
|
/// <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 item = ModHelper.SpawnItem(GpuId(model));
|
||||||
var u = new Unit { Model = model, FakeOf = fakeOf, Health = health, Look = CardLook.Generate(rng, health, fakeOf != null) };
|
var u = new Unit { Model = model, FakeOf = fakeOf, Health = health, Look = CardLook.Generate(rng, health, fakeOf != null) };
|
||||||
Store(item, u);
|
Store(item, u);
|
||||||
|
if (seller != null) SetClaim(item, CardLabels.DealerClaim(u, seller.Value, rng), 0, markupPct);
|
||||||
return item;
|
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);
|
if (item.IsTag(DeadTag) || !GeneralHelper.IsItemOwned(item) || !item.IsRootedInBackInv()) return;
|
||||||
item.ModifyTag(MarkupTag, t => t.Enable().SetInt(markupPct));
|
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);
|
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>
|
/// <summary>玩家指出一个标签:记下来,说法改回真实情况;返回 0 说法属实、1 隐瞒了不利的情况、2 拆穿假货。</summary>
|
||||||
public const int PointFan = 1, PointFingers = 2, PointBracket = 4, PointPrint = 8, PointSticker = 16;
|
public static int Point(GameItem item, int row)
|
||||||
|
|
||||||
/// <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)
|
|
||||||
{
|
{
|
||||||
var u = ToUnit(item);
|
var u = ToUnit(item);
|
||||||
var l = u.Appearance;
|
var claim = GetClaim(item) ?? Claim.Truth(u);
|
||||||
switch (point)
|
int exposed = Exposed(item);
|
||||||
{
|
int kind = CardLabels.Point(u, claim, row, ref exposed);
|
||||||
case PointFan: return l.Fan > 0 ? 1 : 0;
|
item.ModifyTag(ExposedTag, t => t.Enable().SetInt(exposed));
|
||||||
case PointFingers: return l.Fingers > 0 ? 1 : 0;
|
if (!item.IsTag(ClaimTag)) item.ModifyTag(ClaimTag, t => t.Enable().SetString(claim.Encode()));
|
||||||
case PointBracket: return l.Bracket == 2 ? 2 : l.Bracket == 1 ? 1 : 0;
|
Refresh(item);
|
||||||
case PointPrint: return l.Print != 0 ? 2 : 0;
|
if (item.IsTag("APPRAISAL_ITEM")) AppraisalHelper.SetDefaultAppraisalValue(item, (int)item.unitValue);
|
||||||
case PointSticker: return l.Sticker == 1 ? 2 : l.Sticker == 2 ? 1 : 0;
|
return kind;
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>玩家指出一项:记下来,有问题就按维度扣价或拆穿假货;返回这一项的情况(0 没问题,1 扣价,2 拆穿)。</summary>
|
/// <summary>用标签机改说法(随便改):这一项重新算作"没人指出过"。</summary>
|
||||||
public static int Point(GameItem item, int point)
|
public static void EditClaim(GameItem item, int row, int value, string model = null)
|
||||||
{
|
{
|
||||||
int kind = Defect(item, point);
|
var u = ToUnit(item);
|
||||||
item.ModifyTag(PointedTag, t => t.Enable().SetInt(Pointed(item) | point));
|
var claim = GetClaim(item) ?? Claim.Truth(u);
|
||||||
if (kind == 2) item.EnableTag(ExposedTag);
|
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);
|
||||||
Refresh(item);
|
int exposed = Exposed(item) & ~row;
|
||||||
return kind;
|
SetClaim(item, claim, exposed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>说明:算力和耗电按标称写;成色在归玩家之前只给粗略等级(要看外观自己判断),归玩家之后给精确健康度。</summary>
|
/// <summary>说明:算力和耗电按标称写;成色在归玩家之前只给粗略等级(要看外观自己判断),归玩家之后给精确健康度。</summary>
|
||||||
private static string Desc(GameItem item, Unit u)
|
private static string Desc(GameItem item, Unit u)
|
||||||
{
|
{
|
||||||
var label = u.Label;
|
var label = Hardware.Specs[BelievedModel(item, u)];
|
||||||
string cond = GeneralHelper.IsItemOwned(item)
|
string cond = GeneralHelper.IsItemOwned(item)
|
||||||
? Mathf.RoundToInt((float)u.Health) + "%"
|
? 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");
|
: 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)
|
private static void SyncValue(GameItem item)
|
||||||
{
|
{
|
||||||
if (item.IsTag(DeadTag)) return;
|
if (item.IsTag(DeadTag)) return;
|
||||||
string desc = Desc(item, ToUnit(item));
|
|
||||||
if (item.shortDescription != desc) item.shortDescription = desc;
|
|
||||||
var u = ToUnit(item);
|
var u = ToUnit(item);
|
||||||
long value = item.IsTag(QuotedTag) && !GeneralHelper.IsItemOwned(item)
|
string desc = Desc(item, u);
|
||||||
? CardValue.Quote(u, item.IsTag(MarkupTag) ? item.GetTagReadonly(MarkupTag).GetInt() : 0, PricedDims(Pointed(item), u.Appearance), item.IsTag(ExposedTag))
|
if (item.shortDescription != desc) item.shortDescription = desc;
|
||||||
: CardValue.True(u);
|
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;
|
if (item.unitValue != value) item.unitValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,9 +254,6 @@ namespace Decentralized
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
item.SetSpriteAndShapeFromMod(ModInfo.Id, (label.Brand == Hardware.Probidia ? "gpu_probidia" : "gpu_pmd") + (item.IsTag(BackTag) ? "_b" : "_f"));
|
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);
|
SyncValue(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,14 +72,37 @@ item.gpu.grade.1,"like new"
|
|||||||
item.gpu.grade.2,"decent"
|
item.gpu.grade.2,"decent"
|
||||||
item.gpu.grade.3,"clearly worn"
|
item.gpu.grade.3,"clearly worn"
|
||||||
item.gpu.grade.4,"badly 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.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.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.1,"...Fine, it has that. I'll knock something off."
|
||||||
point.worn.2,"Sharp eyes. All right, this price."
|
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.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."
|
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."
|
||||||
|
@@ -71,14 +71,37 @@ item.gpu.grade.1,"接近全新"
|
|||||||
item.gpu.grade.2,"成色尚可"
|
item.gpu.grade.2,"成色尚可"
|
||||||
item.gpu.grade.3,"明显旧了"
|
item.gpu.grade.3,"明显旧了"
|
||||||
item.gpu.grade.4,"磨损严重"
|
item.gpu.grade.4,"磨损严重"
|
||||||
point.fan,"指出风扇"
|
|
||||||
point.fingers,"指出金手指"
|
|
||||||
point.bracket,"指出挡板"
|
|
||||||
point.print,"指出丝印"
|
|
||||||
point.sticker,"指出贴纸"
|
|
||||||
point.none.1,"没毛病,你看错了吧。"
|
point.none.1,"没毛病,你看错了吧。"
|
||||||
point.none.2,"这儿哪有问题?不买别瞎说。"
|
point.none.2,"这儿哪有问题?不买别瞎说。"
|
||||||
point.worn.1,"……行,这点毛病是有,算你便宜点。"
|
point.worn.1,"……行,这点毛病是有,算你便宜点。"
|
||||||
point.worn.2,"眼睛挺毒。好吧,这个价。"
|
point.worn.2,"眼睛挺毒。好吧,这个价。"
|
||||||
point.fake.1,"……被你看出来了。是翻新的货,按真的价给你。"
|
point.fake.1,"……被你看出来了。是翻新的货,按真的价给你。"
|
||||||
point.fake.2,"啧,不是原装的。那就按芯片本来的价吧。"
|
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,"标签上的成色是假的吧,这个价我不要。"
|
||||||
|
@@ -68,8 +68,8 @@ namespace Decentralized
|
|||||||
int day = StoreStation.GetDayCounter();
|
int day = StoreStation.GetDayCounter();
|
||||||
var cards = UsedDealers.Cards(DealerKind.Middleman, day, rng);
|
var cards = UsedDealers.Cards(DealerKind.Middleman, day, rng);
|
||||||
var c = cards[0];
|
var c = cards[0];
|
||||||
var item = Items.NewCard(c.Model, c.Health, c.FakeOf, rng);
|
var item = Items.NewCard(c.Model, c.Health, c.FakeOf, rng, DealerKind.Middleman); // 带一份说法:鉴定时玩家要指出哪些不实
|
||||||
AppraisalHelper.InitAppraisalItem(item, (int)CardValue.Of(c.Model, c.Health));
|
AppraisalHelper.InitAppraisalItem(item, (int)item.unitValue);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,10 +99,9 @@ namespace Decentralized
|
|||||||
var placed = new List<string>();
|
var placed = new List<string>();
|
||||||
foreach (var c in cards)
|
foreach (var c in cards)
|
||||||
{
|
{
|
||||||
var item = Items.NewCard(c.Model, c.Health, c.FakeOf, rng);
|
var item = Items.NewCard(c.Model, c.Health, c.FakeOf, rng, kind, c.MarkupPct);
|
||||||
if (c.Quoted) Items.SetQuote(item, c.MarkupPct);
|
|
||||||
PlayerStore.Instance.AddDirectSellingItemToTable(item);
|
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)}");
|
Log.Info($"二手贩子({kind}):{string.Join(",", placed)}");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ internal static class Program
|
|||||||
Check(CardValue.True(u) == CardValue.Of("RZX60", 80) && CardValue.Quoted(u, 80) == CardValue.Of("RZX90", 80), "真实价值按芯片型号,标称价按标称型号");
|
Check(CardValue.True(u) == CardValue.Of("RZX60", 80) && CardValue.Quoted(u, 80) == CardValue.Of("RZX90", 80), "真实价值按芯片型号,标称价按标称型号");
|
||||||
Check(CardValue.Quoted(u, 80) > CardValue.True(u) * 8, $"假 RZX 90 的标称价 {CardValue.Quoted(u, 80)} 远高于真值 {CardValue.True(u)}");
|
Check(CardValue.Quoted(u, 80) > CardValue.True(u) * 8, $"假 RZX 90 的标称价 {CardValue.Quoted(u, 80)} 远高于真值 {CardValue.True(u)}");
|
||||||
var worn = new Unit { Model = "RZX70", Health = 100, Look = new CardLook { Fan = 2, Fingers = 2, Bracket = 1, Sticker = 2 } };
|
var worn = new Unit { Model = "RZX70", Health = 100, Look = new CardLook { Fan = 2, Fingers = 2, Bracket = 1, Sticker = 2 } };
|
||||||
Check(Near(CardLook.Decode("22102").ValueFactor, 0.88 * 0.88 * 0.98 * 0.94, 1e-9) && CardValue.True(worn) == (long)Math.Round(800 * 0.88 * 0.88 * 0.98 * 0.94), "外观折扣乘进真实价值:风扇缺角 + 金手指氧化 + 划痕 + 封条缺失");
|
Check(CardValue.True(worn) == (long)Math.Round(800 * (1 - 0.12 - 0.12 - 0.02 - 0.06)), "各标签折扣相加进真实价值:风扇缺角 + 金手指氧化 + 划痕 + 封条缺失");
|
||||||
var clean = new Unit { Model = "RZX70", Health = 80 };
|
var clean = new Unit { Model = "RZX70", Health = 80 };
|
||||||
var dusty = new Unit { Model = "RZX70", Health = 80, Look = new CardLook { Fan = 2, Fingers = 2 } };
|
var dusty = new Unit { Model = "RZX70", Health = 80, Look = new CardLook { Fan = 2, Fingers = 2 } };
|
||||||
Check(dusty.DeathChance(ModuleBonus.None) > clean.DeathChance(ModuleBonus.None) * 1.5 && Near(CardLook.Decode("20000").WearFactor, 1.3, 1e-9), "风扇缺角、金手指氧化抬高暴毙概率与损耗");
|
Check(dusty.DeathChance(ModuleBonus.None) > clean.DeathChance(ModuleBonus.None) * 1.5 && Near(CardLook.Decode("20000").WearFactor, 1.3, 1e-9), "风扇缺角、金手指氧化抬高暴毙概率与损耗");
|
||||||
@@ -346,7 +346,7 @@ internal static class Program
|
|||||||
int fakes = 0, cards = 0;
|
int fakes = 0, cards = 0;
|
||||||
for (int i = 0; i < 300; i++)
|
for (int i = 0; i < 300; i++)
|
||||||
foreach (var c in UsedDealers.Cards(DealerKind.Honest, 40, rng))
|
foreach (var c in UsedDealers.Cards(DealerKind.Honest, 40, rng))
|
||||||
Check(c.FakeOf == null && !c.Quoted, "老实人的卡是真卡且不报价");
|
Check(c.FakeOf == null, "老实人的卡是真卡");
|
||||||
for (int i = 0; i < 300; i++)
|
for (int i = 0; i < 300; i++)
|
||||||
foreach (var c in UsedDealers.Cards(DealerKind.Scammer, 40, rng))
|
foreach (var c in UsedDealers.Cards(DealerKind.Scammer, 40, rng))
|
||||||
{
|
{
|
||||||
@@ -357,15 +357,22 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
Check(fakes > cards * 0.2 && fakes < cards * 0.5, $"骗子的假卡占两成多(入门卡便宜到没法冒充):{fakes}/{cards}");
|
Check(fakes > cards * 0.2 && fakes < cards * 0.5, $"骗子的假卡占两成多(入门卡便宜到没法冒充):{fakes}/{cards}");
|
||||||
Check(UsedDealers.Pool(5).All(m => Dealer.Basic.Contains(m)) && UsedDealers.Pool(30).Contains("RZX90"), "型号按天数放开");
|
Check(UsedDealers.Pool(5).All(m => Dealer.Basic.Contains(m)) && UsedDealers.Pool(30).Contains("RZX90"), "型号按天数放开");
|
||||||
// 报价:假 RZX 70(芯片 RZX 50)、健康 60、风扇缺角、金手指氧化
|
// 标签:假 RZX 70(芯片 RZX 50)、健康 60、风扇缺角、金手指氧化、丝印印着真实型号
|
||||||
var u = new Unit { Model = "RZX70", FakeOf = "RZX50", Health = 60, Look = new CardLook { Fan = 2, Fingers = 2 } };
|
var u = new Unit { Model = "RZX70", FakeOf = "RZX50", Health = 60, Look = new CardLook { Fan = 2, Fingers = 2, Print = 1 } };
|
||||||
long label = CardValue.Of("RZX70", 60), real = CardValue.True(u);
|
var claim = CardLabels.DealerClaim(u, DealerKind.Scammer, new Rng(3));
|
||||||
Check(CardValue.Quote(u, 0, 0, false) == (long)Math.Round(label * 0.9), "没拆穿的假卡:标称型号的价打九折,外观问题还没指出就不扣");
|
Check(claim.Model == "RZX70" && claim.Fan == 0 && claim.Fingers == 0 && claim.Print == 0, "骗子的说法:标称型号,外观全是没问题");
|
||||||
Check(CardValue.Quote(u, 0, CardLook.DimFan, false) == (long)Math.Round(label * 0.88 * 0.9), "指出风扇缺角:扣 12%");
|
long hidden = CardLabels.Value(u, claim, 0), real = CardValue.True(u);
|
||||||
Check(CardValue.Quote(u, 0, CardLook.AllDims, true) == real, "外观问题都指出并拆穿假货:报价等于真实价值");
|
Check(hidden == CardValue.Of("RZX70", 60) && real == (long)Math.Round(CardValue.Of("RZX50", 60) * 0.76), "没指出时按说法算(标称型号满价),真实价值按芯片和外观算");
|
||||||
var honest = new Unit { Model = "RZX70", Health = 60, Look = new CardLook { Fan = 1 } };
|
int ex = 0;
|
||||||
Check(CardValue.Quote(honest, 8, 0, false) == (long)Math.Round(CardValue.Of("RZX70", 60) * 1.08), "中间商:加价 8%,外观问题没指出就不扣");
|
Check(CardLabels.Point(u, claim, CardLabels.RowFan, ref ex) == 1 && CardLabels.Value(u, claim, ex) == (long)Math.Round(CardValue.Of("RZX70", 60) * 0.88), "指出风扇缺角:扣 12%");
|
||||||
Check(CardValue.Quote(honest, 8, CardLook.AllDims, false) == (long)Math.Round(CardValue.Of("RZX70", 60) * 0.96 * 1.08), "指出积灰:扣 4%");
|
Check(CardLabels.Point(u, claim, CardLabels.RowPrint, ref ex) == 2 && (ex & CardLabels.RowSpec) != 0, "指出丝印印着真实型号:拆穿假货,型号标签一并拆穿");
|
||||||
|
Check(CardLabels.Point(u, claim, CardLabels.RowFingers, ref ex) == 1 && CardLabels.Value(u, claim, ex) == real, "全部指出之后价格回到真实价值");
|
||||||
|
int ex2 = 0;
|
||||||
|
var honestUnit = new Unit { Model = "RZX70", Health = 60, Look = new CardLook { Fan = 1 } };
|
||||||
|
Check(CardLabels.Point(honestUnit, CardLabels.DealerClaim(honestUnit, DealerKind.Honest, new Rng(1)), CardLabels.RowFan, ref ex2) == 0, "老实人说的是实话,指出去没用");
|
||||||
|
Check(CardLabels.Lies(u, claim, 0) == (CardLabels.RowSpec | CardLabels.RowFan | CardLabels.RowFingers | CardLabels.RowPrint), "骗子隐瞒了型号、风扇、金手指、丝印");
|
||||||
|
Check(CardLabels.Lies(u, claim, ex) == 0, "指出之后不再算隐瞒");
|
||||||
|
Check(Claim.Decode(claim.Encode()).Encode() == claim.Encode(), "说法能存取");
|
||||||
});
|
});
|
||||||
|
|
||||||
Test("外观随损耗重算:越旧外观越差;种子固定;算力受风扇与金手指影响", () =>
|
Test("外观随损耗重算:越旧外观越差;种子固定;算力受风扇与金手指影响", () =>
|
||||||
|
|||||||
Reference in new issue
Block a user