From de7d5caafe22ecf46af201542a450c49a0279d29 Mon Sep 17 00:00:00 2001 From: ZHU MINCHAO <48154958+MinchaoZhu@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:00:39 +0800 Subject: [PATCH] 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 --- src/Decentralized/Items.cs | 20 +++++++++++-- .../ModFiles/Localization/en.csv | 1 - .../ModFiles/Localization/zh.csv | 1 - src/Decentralized/UsedDealerClient.cs | 30 ++++++++----------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/Decentralized/Items.cs b/src/Decentralized/Items.cs index 7a354e0..1ebe294 100644 --- a/src/Decentralized/Items.cs +++ b/src/Decentralized/Items.cs @@ -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"; + public const string LookTag = "PD_LOOK", BackTag = "PD_BACK", QuoteTag = "PD_QUOTE"; public static Action OpenTerminal; @@ -69,7 +69,7 @@ namespace Decentralized private static GameItem CreateGpu(HardwareSpec spec) { var item = ItemDirectory.CreateEmptyItem(GpuId(spec.Model)); - item.SetGameItemType("ELECTRONIC"); + item.SetGameItemType("MATERIAL"); item.onLoaded = Refresh; // 右键"切换"翻面;外观差异用叠加图画在同一张底图上 item.EnableTag(ToggleAppearanceHelper.TOGGLE_APPEARANCE_TAG); @@ -95,6 +95,20 @@ namespace Decentralized return item; } + /// 贩子的柜台报价:只在物品还不属于玩家时按报价算,归玩家之后立刻只值真实价值。 + 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) { 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.shortDescription = L.T("item.gpu.desc", Fmt.Num(label.DisplayHash), Fmt.Num(label.Kj), Mathf.RoundToInt((float)u.Health)); item.unitValue = CardValue.True(u); + SyncValue(item); } private static Sprite ModSprite(string key) @@ -147,6 +162,7 @@ namespace Decentralized private static List Overlays(GameItem item) { var list = new List(); + SyncValue(item); // 归属变化(买下)后价格随之变回真实价值 if (item.IsTag(DeadTag)) return list; var u = ToUnit(item); var keys = new List(); diff --git a/src/Decentralized/ModFiles/Localization/en.csv b/src/Decentralized/ModFiles/Localization/en.csv index c0c87f1..c3dab31 100644 --- a/src/Decentralized/ModFiles/Localization/en.csv +++ b/src/Decentralized/ModFiles/Localization/en.csv @@ -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.2,"No warranty. Look before you take." dealer.line2.3,"Price is negotiable if you want them." -dealer.markup,"Used-goods markup" diff --git a/src/Decentralized/ModFiles/Localization/zh.csv b/src/Decentralized/ModFiles/Localization/zh.csv index cdfc4bb..783e40b 100644 --- a/src/Decentralized/ModFiles/Localization/zh.csv +++ b/src/Decentralized/ModFiles/Localization/zh.csv @@ -65,4 +65,3 @@ dealer.line1.3,"路上收来的货,一手价。" dealer.line2.1,"自己看,背面也翻过来看。" dealer.line2.2,"不保修,看好了再拿。" dealer.line2.3,"要的话价钱好商量。" -dealer.markup,"二手加价" diff --git a/src/Decentralized/UsedDealerClient.cs b/src/Decentralized/UsedDealerClient.cs index 5ce9753..dd31f52 100644 --- a/src/Decentralized/UsedDealerClient.cs +++ b/src/Decentralized/UsedDealerClient.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Decentralized.Domain; @@ -5,7 +6,7 @@ namespace Decentralized { /// /// 二手显卡贩子:偶尔上门,把几张卡摆上柜台,玩家按外观自己判断(翻面看背面)。 - /// 老实人按真实价值卖,中间商加价,骗子夹带假卡并按标称型号报价;价格是柜台上的临时加价,买下之后卡只值真实价值。 + /// 老实人按真实价值卖,中间商加价,骗子夹带假卡并按标称型号报价;柜台价写在物品的报价标签上,物品归玩家之后只值真实价值。 /// 另外把显卡登记为鉴定公会的鉴定物品,正确价值与实际售价是同一个数。 /// public static class UsedDealerClient @@ -19,7 +20,12 @@ namespace Decentralized ModHook.OnGenerateCustomerNormal += OnGenerate; ModHelper.AddAppraisalItem(ModInfo.Id, Appraisal, Items.GpuId("RZX50"), 2, CreateForAppraisal, u => (int)CardValue.True(Items.ToUnit(u)), 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() @@ -52,9 +58,9 @@ namespace Decentralized 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 cards = UsedDealers.Cards(kind, day, rng); var client = new StoreClient @@ -73,9 +79,9 @@ namespace Decentralized foreach (var c in cards) { 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); - 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)}"); })); @@ -83,17 +89,5 @@ namespace Decentralized client.CompleteClientCreation(); return client; } - - /// 柜台上的临时加价:离开贩子之后自动去掉(游戏按客户临时特征处理)。 - 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; - } } }