using System.Collections.Generic; using System.Linq; using Decentralized.Domain; namespace Decentralized { /// /// 用币付款的顾客:每天排队的买家里,按派系(上层 CRC、黑市 CRT、革命派 CRA、下层 PSC)有一小部分改用币结算。 /// 成交时在店里扣回刚入账的信用点,换成币进热钱包;赃物的款项不记销赃,改成币带污点(货值 × 赃物热度%)。 /// public static class CoinCustomers { private static readonly HashSet Marked = new HashSet(); private static int markDay = -1; public static void Subscribe() { ModHook.OnGenerateCustomerLate += OnGenerate; ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Run night (test)", () => Log.Guard("过夜(测试)", () => { // 走游戏自己的夜间流程(EndNight 会触发本 mod 的夜间结算),再进下一天并开门前的日程 // 游戏过夜会清空前柜台,先把自己的东西收进物品栏 foreach (var it in EmporiumEntry.Instance.frontInvinvElement.childItems.ToList()) if (GeneralHelper.IsItemOwned(it)) GraphUtils.TryAcceptAll(EmporiumEntry.Instance.invElement, it); PlayerStore.Instance.EndNight(); StoreStation.Instance.storeEventManager.OnDayEnd(); // 让事件按天数过期 StoreStation.Instance.StartDay(); Log.Info($"过夜完成,现在是第 {StoreStation.GetDayCounter()} 天"); })); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Morning report (test)", () => Log.Guard("晨间日志(测试)", () => StartOfDayUIManager.Instance.ShowMorningReport())); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Newspaper (test)", () => Log.Guard("报纸(测试)", () => NewsUIManager.Instance.ToggleUI())); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Start crackdown (test)", () => Log.Guard("镇压(测试)", () => { StoreStation.Instance.storeEventManager.ActivateEvent(OperationListSec.Crackdown()); Log.Info("镇压事件已激活"); })); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Frozen CRT (test)", () => Log.Guard("冻结 CRT(测试)", () => { var w = Session.Data.Hot; w.Add(Assets.CRT, 1000); double f = w.Freeze(Assets.CRT, Tuning.CrtFreezeShare, StoreStation.GetDayCounter() + Tuning.CrtUnfreezeAfter); Log.Info($"CRT {w.Get(Assets.CRT):0},冻结 {f:0},可动用 {w.Avail(Assets.CRT):0}"); })); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Tainted wallet (test)", () => Log.Guard("带污点的钱包(测试)", () => { var w = Session.Data.Hot; w.Add(Assets.PSC, 100); w.Add(Assets.CRT, 1000); w.Taint += 3000; w.Basis += 2000; Log.Info($"钱包:PSC {w.Get(Assets.PSC):0.##},污点 {w.Taint:0}"); })); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Terminal (test)", () => Log.Guard("终端(测试)", TerminalWindow.Open)); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Black market +50 rep, restock (test)", () => Log.Guard("黑市(测试)", () => { StoreReputation.ModReputation("FACTION_BLACK_MARKET", 50); if (!Unlock.IsUnlocked) ModHelper.GetNetworkUpgrade(Unlock.Id)?.Unlock(); if (!Unlock.BlackUnlocked) ModHelper.GetNetworkUpgrade(Unlock.BlackId)?.Unlock(); var d = Session.Data; d.Hot.Add(Assets.PSC, 20); d.Hot.Add(Assets.CRT, 3000); d.Hot.Taint += 2000; d.Hot.Basis += 1000; BlackDelivery.Restock(d, StoreStation.GetDayCounter(), true); Log.Info($"黑市:声望 {StoreReputation.GetBMReputation()},上架 {d.Black.Offers.Count} 种:{string.Join(", ", d.Black.Offers.Select(o => o.Item + "×" + o.Qty + "@" + o.Price))},解锁 {Unlock.BlackUnlocked}"); })); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Courier (test)", () => Log.Guard("黑市快递(测试)", () => { foreach (var o in Session.Data.Black.Orders.Where(x => x.State == OrderState.Pending)) { o.State = OrderState.Ready; } PlayerStore.Instance.currentClientInstance = null; PlayerStore.Instance.storeClientManager.clientStack.Insert(0, BlackDelivery.CreateCourier()); PlayerStore.Instance.TryCallNextClient(); })); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Courier trap (test)", () => Log.Guard("受控交付(测试)", () => { foreach (var o in Session.Data.Black.Orders.Where(x => x.State == OrderState.Pending)) { o.State = OrderState.Ready; o.Controlled = true; } PlayerStore.Instance.currentClientInstance = null; PlayerStore.Instance.storeClientManager.clientStack.Insert(0, BlackDelivery.CreateCourier()); PlayerStore.Instance.TryCallNextClient(); })); ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Coin buyer (test)", () => Log.Guard("用币付款顾客(测试)", () => { var c = new StoreClient { identifier = "pd_coin_test", displayName = "Coin buyer", spriteName = SpriteDict.GetRandomScavSpriteName(), clientFaction = "FACTION_BLACK_MARKET", useClientBudget = true, clientIntent = StoreClient.ClientIntent.BUY, canClientExposeFeature = ClientCanExposeFunc.BasicClientExposeCapacity, }; c.SetBudget(5000); c.clientBuyingTagList.Add("SEED"); c.mainDialogue.SetText(c.displayName, "CRT.").SetEndAction(() => { }); c.AddBasicDialog(); c.CompleteClientCreation(); Mark(c, Assets.CRT); Log.Info($"用币付款顾客(测试)已设置,钱包污点 {Session.Data.Hot.Taint:0.##},当前顾客 {PlayerStore.Instance.currentClientInstance?.GetClientBlueprint()?.identifier},队列 {PlayerStore.Instance.storeClientManager.clientStack.Count} 人"); PlayerStore.Instance.currentClientInstance = null; // 测试:丢掉在场的顾客(开局的高利贷者不可遣散) PlayerStore.Instance.storeClientManager.clientStack.Insert(0, c); PlayerStore.Instance.TryCallNextClient(); // 把后柜里的种子都标成赃物(热度 50%),买走后钱包的污点应为成交额的一半 foreach (var it in EmporiumEntry.Instance.backInvinvElement.childItems) if (it.IsTag("SEED")) it.ModifyTag("STOLEN_VALUE_INT", t => t.Enable().SetInt(50)); })); } public static void Unsubscribe() => ModHook.OnGenerateCustomerLate -= OnGenerate; private static void OnGenerate(StoreClientManager manager) { if (Session.Data == null || !Unlock.IsUnlocked) return; Log.Guard("用币付款", () => { int day = StoreStation.GetDayCounter(); if (day != markDay) { Marked.Clear(); markDay = day; } foreach (var c in manager.clientStack) { if (c == null || c.isSecurity || Marked.Contains(c) || c.clientIntent != StoreClient.ClientIntent.BUY || c.isNoContributeToEvidence) continue; Marked.Add(c); string asset = CoinPayment.AssetFor(c.clientFaction); if (asset == null || !Session.Data.Rng.Chance(CoinPayment.Chance(asset))) continue; Mark(c, asset); } }); } private static void Mark(StoreClient c, string asset) { var before = c.onItemBought; double total = 0; // 这位顾客这一单一共付了多少币 c.isNoContributeToEvidence = true; c.onItemBought = (item, cost) => { before?.Invoke(item, cost); Log.Guard("用币付款成交", () => { total += Pay(c, asset, item, cost); Say(c, asset, total); }); }; } /// 成交后顾客说的话:告诉玩家这单是币到账,不是信用点。 private static void Say(StoreClient client, string asset, double total) { client.acceptDealDialogue = new Dialogue().SetText(client.displayName, L.T("coinpay.line", Fmt.Num(total), asset)); client.acceptDealDialogue.priority = 4; client.acceptDealDialoguePlayed = false; } private static double Pay(StoreClient client, string asset, GameItem item, int cost) { var d = Session.Data; double price = Session.Price(asset); if (cost <= 0 || price <= 0) return 0; int heat = item.IsTag("STOLEN_VALUE_INT") ? item.GetTagReadonly("STOLEN_VALUE_INT").GetInt() : 0; ModHelper.AddCredit(-cost, false); double qty = CoinPayment.Receive(d.Hot, asset, cost, price, heat); d.CoinSalesToday += cost; Log.Info($"用币付款:{item.name} {cost} 信用点 → {qty:0.####} {asset},污点 +{cost * heat / 100.0:0}"); return qty; } } }