Used cards: counter quote tag instead of client-temporary feature; item type Material

Client-temporary features are stripped when an item sits on the front
counter, so the scam markup never applied. The quote now lives in a tag and
counts only while the item is not owned. GPU item type ELECTRONIC has no
translation in the game, so cards are typed as Material.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Sonnet 5.5 committed 2026-11-03 12:00:39 +08:00
1 parent 9b48d7bf0e
commit de7d5caafe
4 files changed
+30 -22

No files matched your search

+18 -2
View File
@@ -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"; public const string LookTag = "PD_LOOK", BackTag = "PD_BACK", QuoteTag = "PD_QUOTE";
public static Action OpenTerminal; public static Action OpenTerminal;
@@ -69,7 +69,7 @@ namespace Decentralized
private static GameItem CreateGpu(HardwareSpec spec) private static GameItem CreateGpu(HardwareSpec spec)
{ {
var item = ItemDirectory.CreateEmptyItem(GpuId(spec.Model)); var item = ItemDirectory.CreateEmptyItem(GpuId(spec.Model));
item.SetGameItemType("ELECTRONIC"); item.SetGameItemType("MATERIAL");
item.onLoaded = Refresh; item.onLoaded = Refresh;
// 右键"切换"翻面;外观差异用叠加图画在同一张底图上 // 右键"切换"翻面;外观差异用叠加图画在同一张底图上
item.EnableTag(ToggleAppearanceHelper.TOGGLE_APPEARANCE_TAG); item.EnableTag(ToggleAppearanceHelper.TOGGLE_APPEARANCE_TAG);
@@ -95,6 +95,20 @@ namespace Decentralized
return item; return item;
} }
/// <summary>贩子的柜台报价:只在物品还不属于玩家时按报价算,归玩家之后立刻只值真实价值。</summary>
public static void SetQuote(GameItem item, long quote)
{
item.ModifyTag(QuoteTag, t => t.Enable().SetLong(quote));
Refresh(item);
}
private static void SyncValue(GameItem item)
{
if (item.IsTag(DeadTag)) return;
long value = item.IsTag(QuoteTag) && !GeneralHelper.IsItemOwned(item) ? item.GetTagReadonly(QuoteTag).GetLong() : CardValue.True(ToUnit(item));
if (item.unitValue != value) item.unitValue = value;
}
public static Unit ToUnit(GameItem item) public static Unit ToUnit(GameItem item)
{ {
string model = item.identifier.Substring(GpuPrefix.Length); string model = item.identifier.Substring(GpuPrefix.Length);
@@ -135,6 +149,7 @@ namespace Decentralized
item.name = L.T("item.gpu.name", label.Brand, DisplayModel(label.Model)); item.name = L.T("item.gpu.name", label.Brand, DisplayModel(label.Model));
item.shortDescription = L.T("item.gpu.desc", Fmt.Num(label.DisplayHash), Fmt.Num(label.Kj), Mathf.RoundToInt((float)u.Health)); item.shortDescription = L.T("item.gpu.desc", Fmt.Num(label.DisplayHash), Fmt.Num(label.Kj), Mathf.RoundToInt((float)u.Health));
item.unitValue = CardValue.True(u); item.unitValue = CardValue.True(u);
SyncValue(item);
} }
private static Sprite ModSprite(string key) private static Sprite ModSprite(string key)
@@ -147,6 +162,7 @@ namespace Decentralized
private static List<ItemSpriteModifier> Overlays(GameItem item) private static List<ItemSpriteModifier> Overlays(GameItem item)
{ {
var list = new List<ItemSpriteModifier>(); var list = new List<ItemSpriteModifier>();
SyncValue(item); // 归属变化(买下)后价格随之变回真实价值
if (item.IsTag(DeadTag)) return list; if (item.IsTag(DeadTag)) return list;
var u = ToUnit(item); var u = ToUnit(item);
var keys = new List<string>(); var keys = new List<string>();
@@ -66,4 +66,3 @@ dealer.line1.3,"Picked these up on the road. First-hand price."
dealer.line2.1,"See for yourself, turn them over and check the back." dealer.line2.1,"See for yourself, turn them over and check the back."
dealer.line2.2,"No warranty. Look before you take." dealer.line2.2,"No warranty. Look before you take."
dealer.line2.3,"Price is negotiable if you want them." dealer.line2.3,"Price is negotiable if you want them."
dealer.markup,"Used-goods markup"
1 # Probably Decentralized localization table: English (en)
66 dealer.line2.1
67 dealer.line2.2
68 dealer.line2.3
dealer.markup
@@ -65,4 +65,3 @@ dealer.line1.3,"路上收来的货,一手价。"
dealer.line2.1,"自己看,背面也翻过来看。" dealer.line2.1,"自己看,背面也翻过来看。"
dealer.line2.2,"不保修,看好了再拿。" dealer.line2.2,"不保修,看好了再拿。"
dealer.line2.3,"要的话价钱好商量。" dealer.line2.3,"要的话价钱好商量。"
dealer.markup,"二手加价"
1 # Probably Decentralized 本地化表:简体中文(zh)
65 dealer.line2.1
66 dealer.line2.2
67 dealer.line2.3
dealer.markup
+12 -18
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Decentralized.Domain; using Decentralized.Domain;
@@ -5,7 +6,7 @@ namespace Decentralized
{ {
/// <summary> /// <summary>
/// 二手显卡贩子:偶尔上门,把几张卡摆上柜台,玩家按外观自己判断(翻面看背面)。 /// 二手显卡贩子:偶尔上门,把几张卡摆上柜台,玩家按外观自己判断(翻面看背面)。
/// 老实人按真实价值卖,中间商加价,骗子夹带假卡并按标称型号报价;价格是柜台上的临时加价,买下之后卡只值真实价值。 /// 老实人按真实价值卖,中间商加价,骗子夹带假卡并按标称型号报价;柜台价写在物品的报价标签上,物品归玩家之后只值真实价值。
/// 另外把显卡登记为鉴定公会的鉴定物品,正确价值与实际售价是同一个数。 /// 另外把显卡登记为鉴定公会的鉴定物品,正确价值与实际售价是同一个数。
/// </summary> /// </summary>
public static class UsedDealerClient public static class UsedDealerClient
@@ -19,7 +20,12 @@ namespace Decentralized
ModHook.OnGenerateCustomerNormal += OnGenerate; ModHook.OnGenerateCustomerNormal += OnGenerate;
ModHelper.AddAppraisalItem(ModInfo.Id, Appraisal, Items.GpuId("RZX50"), 2, CreateForAppraisal, u => (int)CardValue.True(Items.ToUnit(u)), ModHelper.AddAppraisalItem(ModInfo.Id, Appraisal, Items.GpuId("RZX50"), 2, CreateForAppraisal, u => (int)CardValue.True(Items.ToUnit(u)),
Items.IsGpu, null, () => L.T("appraisal.gpu")); Items.IsGpu, null, () => L.T("appraisal.gpu"));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Used dealer visit (test)", () => PlayerStore.Instance.storeClientManager?.AddClient(Create(DealerKind.Scammer))); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Used dealer visit (test)", () => Log.Guard("二手贩子(测试)", () =>
{
var m = PlayerStore.Instance.storeClientManager;
m.AddNextClient(Create(DealerKind.Scammer, 40));
Log.Info($"二手贩子(测试)已排入,队列 {m.clientStack.Count} 人");
}));
} }
public static void Unsubscribe() public static void Unsubscribe()
@@ -52,9 +58,9 @@ namespace Decentralized
return item; return item;
} }
private static StoreClient Create(DealerKind kind) private static StoreClient Create(DealerKind kind, int? atDay = null)
{ {
int day = StoreStation.GetDayCounter(); int day = atDay ?? StoreStation.GetDayCounter();
var rng = Session.Data.Rng; var rng = Session.Data.Rng;
var cards = UsedDealers.Cards(kind, day, rng); var cards = UsedDealers.Cards(kind, day, rng);
var client = new StoreClient var client = new StoreClient
@@ -73,9 +79,9 @@ namespace Decentralized
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);
if (c.MarkupPct != 0) item.AddItemFeature(Markup(c.MarkupPct)); if (c.MarkupPct != 0) Items.SetQuote(item, (long)Math.Round(CardValue.True(Items.ToUnit(item)) * (1 + c.MarkupPct / 100.0)));
PlayerStore.Instance.AddDirectSellingItemToTable(item); PlayerStore.Instance.AddDirectSellingItemToTable(item);
placed.Add($"{c.Model}{(c.FakeOf != null ? "←" + c.FakeOf : "")} {c.Health:0}% {c.MarkupPct:+0;-0}%"); placed.Add($"{c.Model}{(c.FakeOf != null ? "←" + c.FakeOf : "")} {c.Health:0}% {c.MarkupPct:+0;-0}% 柜台价 {item.unitValue}(真实 {CardValue.True(Items.ToUnit(item))})");
} }
Log.Info($"二手贩子({kind}):{string.Join(",", placed)}"); Log.Info($"二手贩子({kind}):{string.Join(",", placed)}");
})); }));
@@ -83,17 +89,5 @@ namespace Decentralized
client.CompleteClientCreation(); client.CompleteClientCreation();
return client; return client;
} }
/// <summary>柜台上的临时加价:离开贩子之后自动去掉(游戏按客户临时特征处理)。</summary>
private static ItemFeature Markup(int pct)
{
var f = new ItemFeature(ItemFeature.FeatureType.ClientTemporary) { identifier = "pd_used_markup", category = "markup", valueStage = ItemFeature.ValueStage.Market };
string text = L.T("dealer.markup");
f.SetPublicDisplay(text);
f.SetActualDisplay(text);
f.isFeatureMatch = true;
f.SetPreExposedValueMod(pct);
return f;
}
} }
} }