Phase 2: customers paying in coin (CRC/CRT/CRA/PSC), taint from stolen goods
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1ba0652705
commit
090a973d23
10 files changed
+149
No files matched your search
@@ -2,6 +2,10 @@
|
||||
|
||||
## 未发布
|
||||
|
||||
### 第二阶段(进行中)
|
||||
|
||||
- 用币付款的顾客:排队的买家里,上层付 CRC(12%)、黑市付 CRT(25%)、革命派付 CRA(25%)、下层付 PSC(10%)。成交时扣回信用点,按当日币价换成币进热钱包;赃物款项不记销赃,改成币带污点(货值 × 赃物热度%);夜间日志汇总当天用币结算的额度。开发者模式 F9 有测试入口。游戏内成交流程尚未实测(测试存档里顾客不上柜台),机制本身在技术验证里通过。
|
||||
|
||||
### 第一阶段完成(入门、挖矿、显卡鉴定、终端)
|
||||
|
||||
以下条目取代下面较早条目里已过时的部分(型号、入口价、终端页面):
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace Decentralized.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// 顾客用币付款:成交的信用点在店里扣回,按当前币价换成币进热钱包。
|
||||
/// 收赃物的款项不再记销赃,改为币带污点:货值 × 赃物热度%。
|
||||
/// </summary>
|
||||
public static class CoinPayment
|
||||
{
|
||||
/// <summary>各派系用什么币付款(其余派系不用币)。</summary>
|
||||
public static string AssetFor(string faction)
|
||||
{
|
||||
switch (faction)
|
||||
{
|
||||
case "FACTION_UPPER_LEVEL": return Assets.CRC;
|
||||
case "FACTION_BLACK_MARKET": return Assets.CRT;
|
||||
case "FACTION_REVOLUTION": return Assets.CRA;
|
||||
case "FACTION_LOWER_LEVEL": return Assets.PSC;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>一位顾客用币付款的概率。</summary>
|
||||
public static double Chance(string asset)
|
||||
{
|
||||
switch (asset)
|
||||
{
|
||||
case Assets.CRC: return Tuning.CoinPayCrc;
|
||||
case Assets.CRT: return Tuning.CoinPayCrt;
|
||||
case Assets.CRA: return Tuning.CoinPayCra;
|
||||
default: return Tuning.CoinPayPsc;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>收款:返回收到的币数。heatPct 是物品的赃物热度(0–100)。</summary>
|
||||
public static double Receive(Wallet w, string asset, double credits, double price, double heatPct)
|
||||
{
|
||||
if (credits <= 0 || price <= 0) return 0;
|
||||
double qty = credits / price;
|
||||
w.Add(asset, qty);
|
||||
w.Taint += credits * Math.Max(0, Math.Min(100, heatPct)) / 100.0;
|
||||
w.Basis += credits;
|
||||
return qty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,8 @@ namespace Decentralized.Domain
|
||||
public const int AmlWindowDays = 7;
|
||||
public const double AmlTaintRatio = 0.10, AmlHeavyRatio = 0.30, AmlMixerBonus = 0.30, AmlMaxChance = 0.9;
|
||||
public const double TaxRate = 0.08;
|
||||
/// <summary>各派系顾客用币付款的概率。</summary>
|
||||
public const double CoinPayCrc = 0.12, CoinPayCrt = 0.25, CoinPayCra = 0.25, CoinPayPsc = 0.10;
|
||||
/// <summary>单个金融违规事件记入的罪证上限。</summary>
|
||||
public const double CrimeEventCap = 400;
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Collections.Generic;
|
||||
using Decentralized.Domain;
|
||||
|
||||
namespace Decentralized
|
||||
{
|
||||
/// <summary>
|
||||
/// 用币付款的顾客:每天排队的买家里,按派系(上层 CRC、黑市 CRT、革命派 CRA、下层 PSC)有一小部分改用币结算。
|
||||
/// 成交时在店里扣回刚入账的信用点,换成币进热钱包;赃物的款项不记销赃,改成币带污点(货值 × 赃物热度%)。
|
||||
/// </summary>
|
||||
public static class CoinCustomers
|
||||
{
|
||||
private static readonly HashSet<StoreClient> Marked = new HashSet<StoreClient>();
|
||||
private static int markDay = -1;
|
||||
|
||||
public static void Subscribe()
|
||||
{
|
||||
ModHook.OnGenerateCustomerLate += OnGenerate;
|
||||
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("WATER"); c.clientBuyingTagList.Add("BREVAGE");
|
||||
c.mainDialogue.SetText(c.displayName, "CRT.").SetEndAction(() => { });
|
||||
c.AddBasicDialog();
|
||||
c.CompleteClientCreation();
|
||||
Mark(c, Assets.CRT);
|
||||
PlayerStore.Instance.currentClientInstance = StoreClientInstance.CreateClientInstance(c); // 测试:直接换成这位顾客
|
||||
}));
|
||||
}
|
||||
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;
|
||||
c.isNoContributeToEvidence = true;
|
||||
c.onItemBought = (item, cost) =>
|
||||
{
|
||||
before?.Invoke(item, cost);
|
||||
Log.Guard("用币付款成交", () => Pay(asset, item, cost));
|
||||
};
|
||||
}
|
||||
|
||||
private static void Pay(string asset, GameItem item, int cost)
|
||||
{
|
||||
var d = Session.Data;
|
||||
double price = Session.Price(asset);
|
||||
if (cost <= 0 || price <= 0) return;
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Decentralized
|
||||
Night.Subscribe();
|
||||
DealerStock.Subscribe();
|
||||
UsedDealerClient.Subscribe();
|
||||
CoinCustomers.Subscribe();
|
||||
GpuInspectView.Ensure();
|
||||
ModHook.OnGameLoadedLate += Session.Load;
|
||||
ModHook.OnNewGameStarted += OnNewGame;
|
||||
@@ -28,6 +29,7 @@ namespace Decentralized
|
||||
Night.Unsubscribe();
|
||||
DealerStock.Unsubscribe();
|
||||
UsedDealerClient.Unsubscribe();
|
||||
CoinCustomers.Unsubscribe();
|
||||
GpuInspectView.Remove();
|
||||
ModHook.OnGameLoadedLate -= Session.Load;
|
||||
ModHook.OnNewGameStarted -= OnNewGame;
|
||||
|
||||
@@ -50,6 +50,7 @@ report.burnt,Cards burnt out: {0}
|
||||
report.price,PSC price: {0} credits
|
||||
nightlog.mined,Mining rigs produced {0} PSC.
|
||||
nightlog.burnt,A {0} burnt out.
|
||||
nightlog.coinpay,Customers paying in coin settled {0} credits; the coins went to the hot wallet.
|
||||
nightlog.unpowered,{0} mining rig(s) had no power.
|
||||
news.open.title,Poinbase Lists PSC
|
||||
news.open.desc,The Upper Level exchange Poinbase now lists PSC against CRC. Graphics cards are in demand across the station.
|
||||
|
||||
|
@@ -49,6 +49,7 @@ report.burnt,烧毁的显卡:{0}
|
||||
report.price,PSC 价格:{0} 信用点
|
||||
nightlog.mined,矿架挖出 {0} PSC。
|
||||
nightlog.burnt,一张 {0} 烧毁了。
|
||||
nightlog.coinpay,用币付款的顾客共结算 {0} 信用点,已换成币进了热钱包。
|
||||
nightlog.unpowered,{0} 个矿架没有电。
|
||||
news.open.title,Poinbase 上架 PSC
|
||||
news.open.desc,上层交易所 Poinbase 上架 PSC/CRC 交易对。全站显卡需求上升。
|
||||
|
||||
|
@@ -120,6 +120,8 @@ namespace Decentralized
|
||||
foreach (var name in report.Burnt) ps.AddNightLog(L.T("nightlog.burnt", name), "cc7755");
|
||||
int unpowered = report.Rigs - report.PoweredRigs;
|
||||
if (unpowered > 0 && units.Count + unpowered > 0) ps.AddNightLog(L.T("nightlog.unpowered", unpowered), "cc7755");
|
||||
if (d.CoinSalesToday > 0) ps.AddNightLog(L.T("nightlog.coinpay", Fmt.Credits(d.CoinSalesToday)));
|
||||
d.CoinSalesToday = 0;
|
||||
Session.Save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace Decentralized
|
||||
public List<MarketEvent> ActiveEvents = new List<MarketEvent>();
|
||||
public NightReport LastNight;
|
||||
public bool OpeningNewsShown;
|
||||
/// <summary>当天用币付款的成交额(信用点),夜间清零。</summary>
|
||||
public double CoinSalesToday;
|
||||
}
|
||||
|
||||
/// <summary>当前这一局的状态:读档 / 新游戏时载入,睡觉结算后写回。</summary>
|
||||
|
||||
@@ -488,6 +488,16 @@ internal static class Program
|
||||
Check(Near(back.Market.Price, m.Price, 1e-9), "读回后继续推进,行情与原来完全相同");
|
||||
});
|
||||
|
||||
Test("用币付款:派系对应的币、按币价换算、污点与成本", () =>
|
||||
{
|
||||
var w = new Wallet { Address = "t" };
|
||||
Check(CoinPayment.AssetFor("FACTION_BLACK_MARKET") == Assets.CRT && CoinPayment.AssetFor("FACTION_SECURITY") == null, "派系对应的币");
|
||||
double q = CoinPayment.Receive(w, Assets.CRT, 200, 0.5, 40);
|
||||
Check(Near(q, 400, 1e-9) && Near(w.Get(Assets.CRT), 400, 1e-9), "按币价换算数量");
|
||||
Check(Near(w.Taint, 80, 1e-9) && Near(w.Basis, 200, 1e-9), "污点 = 货值 × 热度%,成本 = 货值");
|
||||
Check(CoinPayment.Receive(w, Assets.CRC, 0, 1, 10) == 0, "零金额不收");
|
||||
});
|
||||
|
||||
Console.WriteLine($"\n{_passed} 通过,{_failed} 失败");
|
||||
return _failed == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user