Black market: useful goods only, player selling with price-dependent speed and taint

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Sonnet 5.5 committed 2026-11-03 20:23:21 +08:00
1 parent 5b57f3eedc
commit 502366e7c4
10 files changed
+363 -21

No files matched your search

+5
View File
@@ -20,6 +20,11 @@
- 挖矿报告并进 Web3 页的"挖矿"子页,终端顶栏不再有"报告"。
- 游戏内验证:受控交付的对话与两个选项显示,拒签后快递员离开、下一位顾客进来。没在游戏里验证:签收的罪证、蜜罐与落网的夜间追查(只有单元测试)。
- 货架重做:违禁品池只留能转手的货(枪械、钥匙卡、密码解序器芯片、爆炸物),弹药、毒品、药品、化学品、种子全部移除;高科技物品池换成矿机能用的东西(7 种系统模组、能量电池、能量阵列、高级模组提取器、矿架);显卡与高科技各占 30%,高科技不再需要中档声望。
- 玩家出售:黑市页新增"出售"子页。储藏区里的违禁品、高科技物品和显卡做成货架,选标价倍率 0.6–1.8(相对参考价;显卡按标称型号与成色)上架,收 5% 上架费。每晚按期望天数成交:≤0.9 倍 1 天,1.0 倍 2 天,1.8 倍 14 天,之间插值。成交扣 8% 抽成,货款托管 3 天后以 CRT 进钱包。货款带污点的概率和比例随倍率从 20%/20% 涨到 90%/80%。
- 出售的风险:假卡有 50% 被买家看穿退货,好评 −10(好评影响成交速度,每笔成交 +1);平台跑路时上架的货和未放款的货款都没了;蜜罐期间成交的货款和近期买家落网会被追查记罪证;镇压期间平台不成交。"我的订单与情报"页多出"我的上架",可以撤下。
- 游戏内验证:货架显示显卡、模组、能量电池;买一张卡后出售页列出储藏区里的货,上架(标价 400,上架费 20),过夜成交(货款 368),3 天后到账(CRT 2804 → 3172,这次随机没带污点)。没有在游戏里验证:假卡退货、跑路时的出售损失(只有单元测试)。
### 第三阶段:数值调优(进行中)
- 混币器每轮手续费 6% → 4%;用币付款的顾客概率提到上层 30%、黑市 50%、革命派 50%、下层 25%,并多付 12%(溢价只加在币数上,污点仍按货值算)。
+11 -12
View File
@@ -37,23 +37,22 @@ namespace Decentralized.Domain
public double DeskToday;
public int DeskDay = -1;
/// <summary>违禁品池:游戏物品 id 与违禁等级(1 低、2 中、3 高)。全部来自游戏已有的违禁品。</summary>
/// <summary>违禁品池:游戏物品 id 与违禁等级(1 低、2 中、3 高)。都是能转手的货(枪械、钥匙卡、密码解序器芯片、爆炸物);弹药、毒品、药品玩家用不上,不上架。</summary>
public static readonly KeyValuePair<string, int>[] Pool =
{
P("small_pistol_ammo", 1), P("heavy_pistol_ammo", 1), P("handmade_pistol", 1), P("printed_gun", 1), P("stun_baton", 1),
P("dream_cap_seed", 1), P("eng_keycard", 1), P("med_keycard", 1), P("ser_keycard", 1), P("sup_keycard", 1),
P("crypto_module_ser", 1), P("crypto_module_sup", 1),
P("revolver", 2), P("heavy_handmade_pistol", 2), P("small_pistol_ammo_p", 2), P("heavy_pistol_ammo_p", 2),
P("flashbang_grenade", 2), P("smoke_grenade", 2), P("dream_dust", 2), P("restricted_chemical", 2),
P("sci_keycard", 2), P("sec_keycard", 2), P("crypto_module_eng", 2), P("crypto_module_med", 2), P("unlicensed_phagimycin_pill", 2),
P("shotgun", 3), P("smg", 3), P("c4", 3), P("fanta_pill", 3), P("nightmare_dust", 3), P("cmd_keycard", 3), P("crypto_module_sec", 3),
P("handmade_pistol", 1), P("printed_gun", 1), P("stun_baton", 1),
P("eng_keycard", 1), P("med_keycard", 1), P("ser_keycard", 1), P("sup_keycard", 1), P("crypto_module_ser", 1), P("crypto_module_sup", 1),
P("revolver", 2), P("heavy_handmade_pistol", 2), P("flashbang_grenade", 2), P("smoke_grenade", 2),
P("sci_keycard", 2), P("sec_keycard", 2), P("crypto_module_eng", 2), P("crypto_module_med", 2),
P("shotgun", 3), P("smg", 3), P("c4", 3), P("cmd_keycard", 3), P("crypto_module_sec", 3),
};
/// <summary>高科技物品池(不是违禁品,黑市上要加价)。</summary>
/// <summary>高科技物品池(不是违禁品,黑市上要加价):矿机能用上的系统模组、能量电池、能量阵列,和矿架本身。</summary>
public static readonly string[] TechPool =
{
"hand", "aug_chip", "energy_credit_hyp", "energy_credit_breeder", "makeshift_reactor", "mirage_projector", "printer",
"system_module_overclock", "system_module_performance", "system_module_quality", "module_extractor_advanced", "powerblock",
"system_module_overclock", "system_module_performance", "system_module_quality", "system_module_eco", "system_module_efficiancy",
"system_module_fineness", "system_module_streamlining", "energy_credit_ext", "energy_credit_hyp", "energy_credit_breeder",
"powerblock", "module_extractor_advanced", "pd_rig",
};
private static KeyValuePair<string, int> P(string id, int level) => new KeyValuePair<string, int>(id, level);
@@ -84,7 +83,7 @@ namespace Decentralized.Domain
var c = UsedDealers.Card(gpus, Tuning.BmGpuFake, 0, rng);
goods.Add(new BmGood { Item = "gpu:" + c.Model, Kind = GoodKind.Gpu, Level = 2, Model = c.Model, FakeOf = c.FakeOf, Health = c.Health, Price = price(CardValue.Of(c.Model, c.Health), GoodKind.Gpu) });
}
else if (r < Tuning.BmGpuChance + Tuning.BmTechChance && rep >= Tuning.BmMidRep && tech.Count > 0)
else if (r < Tuning.BmGpuChance + Tuning.BmTechChance && tech.Count > 0)
{
int k = rng.Range(0, tech.Count - 1);
string id = tech[k];
+10 -3
View File
@@ -59,6 +59,7 @@ namespace Decentralized.Domain
{
public string Kind; // ready / scam_refund / scam_loss / rug / intercept / reopen
public BmOrder Order;
public BmSale Sale;
public double Amount;
}
@@ -84,7 +85,7 @@ namespace Decentralized.Domain
public bool Closed(int day) => day <= ClosedUntil;
/// <summary>托管中和已提前放款但尚未交付的资金(信用点),平台跑路的概率随它增加。</summary>
public double Held => Orders.Where(o => o.State == OrderState.Pending).Sum(o => (double)o.Price);
public double Held => Orders.Where(o => o.State == OrderState.Pending).Sum(o => (double)o.Price) + HeldSales;
public static int OrderPrice(long baseValue, GoodKind k = GoodKind.Contraband) => (int)Math.Max(1, Math.Round(baseValue * (1 + Extra(k)) * (1 - Tuning.BmOrderDiscount)));
public static int EscrowFee(int price) => (int)Math.Ceiling(price * Tuning.BmEscrowFee);
@@ -151,18 +152,23 @@ namespace Decentralized.Domain
}
/// <summary>每晚推进订单:平台跑路、到期的订单(卖家跑路、被截获、死投被挖走)、转入可取状态。镇压期间到期顺延一天。</summary>
public List<BmEvent> Night(int day, bool crackdown, Wallet w, Rng rng, int secRep = 0)
public List<BmEvent> Night(int day, bool crackdown, Wallet w, Rng rng, int secRep = 0, Func<string, double> price = null)
{
var ev = new List<BmEvent>();
if (!Honeypot(day) && rng.Chance(HoneypotChance(secRep))) { HoneypotUntil = day + Tuning.BmHoneypotDays; if (!Forum.Contains("easy")) Forum.Add("easy"); }
if (ClosedUntil == day) ev.Add(new BmEvent { Kind = "reopen" });
if (!Closed(day) && Orders.Any(o => o.State == OrderState.Pending) && rng.Chance(RugChance))
if (!Closed(day) && (Orders.Any(o => o.State == OrderState.Pending) || Sales.Any(x => x.State == SaleState.Listed || x.State == SaleState.Sold)) && rng.Chance(RugChance))
{
foreach (var o in Orders.Where(o => o.State == OrderState.Pending))
{
o.State = OrderState.Lost; o.Why = "rug";
ev.Add(new BmEvent { Kind = "rug", Order = o, Amount = o.Price });
}
foreach (var sl in Sales.Where(x => x.State == SaleState.Listed || x.State == SaleState.Sold))
{
ev.Add(new BmEvent { Kind = "sale_rug", Sale = sl, Amount = sl.State == SaleState.Sold ? sl.Proceeds : sl.ListPrice });
sl.State = SaleState.Lost; sl.Why = "rug";
}
ClosedUntil = day + Tuning.BmRugClosedDays;
Offers.Clear(); Catalog.Clear(); Sellers.Clear();
return ev;
@@ -186,6 +192,7 @@ namespace Decentralized.Domain
o.State = OrderState.Ready;
ev.Add(new BmEvent { Kind = "ready", Order = o });
}
if (price != null) SalesNight(day, crackdown, w, price, rng, ev);
// 卖家落网:近期成交过的卖家各有小概率被抓,他们留存的订单记录之后会连累买家
foreach (var name in Orders.Where(x => x.State == OrderState.Done && day - x.DueDay <= 14).Select(x => x.Seller).Distinct().ToList())
{
+118
View File
@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Decentralized.Domain
{
public enum SaleState { Listed, Sold, Paid, Lost, Delisted }
/// <summary>玩家在黑市上架的一件货。货交给平台(从储藏区拿走),成交后货款托管几天再到钱包,可能带污点。</summary>
public sealed class BmSale
{
public string Item;
public string Model, FakeOf;
public double Health;
/// <summary>标价倍率与标价(信用点)。</summary>
public double Mult;
public int ListPrice;
public int ListedDay, PayDay;
public SaleState State = SaleState.Listed;
/// <summary>成交后扣掉抽成的货款(信用点)与其中带的污点。</summary>
public int Proceeds;
public double Taint;
/// <summary>终态原因:fake(买家看穿假卡退货)/ rug / delist。</summary>
public string Why;
}
public sealed partial class BlackMarket
{
public List<BmSale> Sales = new List<BmSale>();
/// <summary>玩家作为卖家的好评(%):影响成交速度;卖假卡被看穿会下降,每笔成交回升一点。</summary>
public int Rating = Tuning.BmRatingStart;
/// <summary>期望成交天数:倍率 ≤0.9 一天,1.0 两天,1.8 十四天,之间插值(0.9–1.0 线性,1.0 以上指数)。</summary>
public static double ExpectedDays(double m)
{
if (m <= 0.9) return Tuning.BmSellDaysFast;
if (m < 1.0) return Tuning.BmSellDaysFast + (m - 0.9) / 0.1 * (Tuning.BmSellDays1 - Tuning.BmSellDaysFast);
return Tuning.BmSellDays1 * Math.Pow(Tuning.BmSellDaysMax / Tuning.BmSellDays1, (m - 1.0) / (Tuning.BmSellMax - 1.0));
}
private static double T(double m) => Math.Max(0, Math.Min(1, (m - Tuning.BmSellMin) / (Tuning.BmSellMax - Tuning.BmSellMin)));
/// <summary>货款带污点的概率与(带的时候)污点占货款的比例:都随标价倍率上升。</summary>
public static double TaintChance(double m) => Tuning.BmSaleTaintChanceMin + T(m) * (Tuning.BmSaleTaintChanceMax - Tuning.BmSaleTaintChanceMin);
public static double TaintRatio(double m) => Tuning.BmSaleTaintRatioMin + T(m) * (Tuning.BmSaleTaintRatioMax - Tuning.BmSaleTaintRatioMin);
/// <summary>每晚成交的概率:1 / 期望天数,乘好评系数(好评 90 为 1,封顶 1.1),最多 1。</summary>
public double SaleChance(double m) => Math.Min(1, 1 / ExpectedDays(m) * Math.Min(1.1, Rating / (double)Tuning.BmRatingStart));
public static int ListPriceOf(long refValue, double m) => (int)Math.Max(1, Math.Round(refValue * m));
public static int ListFee(int listPrice) => (int)Math.Ceiling(listPrice * Tuning.BmListFee);
/// <summary>上架:交付一件货,收上架费(付给 feeAsset 币,污点与成本按比例带走)。倍率超出范围、平台关闭或余额不足返回 null。</summary>
public BmSale List(string item, string model, string fakeOf, double health, long refValue, double m, Wallet w, string feeAsset, int day, Func<string, double> price)
{
if (m < Tuning.BmSellMin - 1e-9 || m > Tuning.BmSellMax + 1e-9 || refValue <= 0 || Closed(day) || feeAsset == Assets.CRC) return null;
int listPrice = ListPriceOf(refValue, m);
double p = price(feeAsset);
if (p <= 0) return null;
double qty = ListFee(listPrice) / p;
if (w.Avail(feeAsset) + 1e-9 < qty) return null;
double total = w.Value(price);
double share = total > 0 ? Math.Min(1, ListFee(listPrice) / total) : 0;
w.Taint -= w.Taint * share; w.Basis -= w.Basis * share;
w.Add(feeAsset, -qty);
var s = new BmSale { Item = item, Model = model, FakeOf = fakeOf, Health = health, Mult = m, ListPrice = listPrice, ListedDay = day };
Sales.Add(s);
return s;
}
public void Delist(BmSale s) { s.State = SaleState.Delisted; s.Why = "delist"; }
/// <summary>未到账的货款(信用点):跑路时一并归零,也推高跑路概率。</summary>
public double HeldSales => Sales.Where(x => x.State == SaleState.Sold).Sum(x => (double)x.Proceeds);
/// <summary>每晚推进出售:上架的货按概率成交(假卡可能被看穿退货),成交的货款托管到期后进钱包。镇压期间平台下线,不推进。</summary>
private void SalesNight(int day, bool crackdown, Wallet w, Func<string, double> price, Rng rng, List<BmEvent> ev)
{
if (crackdown) return;
foreach (var s in Sales.Where(x => x.State == SaleState.Listed && x.ListedDay < day).ToList())
{
if (!rng.Chance(SaleChance(s.Mult))) continue;
if (s.FakeOf != null && rng.Chance(Tuning.BmFakeDetect))
{
s.State = SaleState.Delisted; s.Why = "fake";
Rating = Math.Max(0, Rating - Tuning.BmRatingFakeLoss);
ev.Add(new BmEvent { Kind = "sale_fake", Sale = s });
continue;
}
s.State = SaleState.Sold;
s.Proceeds = (int)Math.Round(s.ListPrice * (1 - Tuning.BmCommission));
s.Taint = rng.Chance(TaintChance(s.Mult)) ? s.Proceeds * TaintRatio(s.Mult) : 0;
s.PayDay = day + Tuning.BmSaleHold;
Rating = Math.Min(100, Rating + 1);
if (Honeypot(day)) Traps.Add(new BmTrap { Day = day + Tuning.BmTrapDelay, Amount = s.Proceeds, Why = "honeypot", Item = s.Item });
ev.Add(new BmEvent { Kind = "sale_sold", Sale = s, Amount = s.Proceeds });
}
foreach (var s in Sales.Where(x => x.State == SaleState.Sold && day >= x.PayDay).ToList())
{
double p = price(Assets.CRT);
if (p <= 0) continue;
w.Add(Assets.CRT, s.Proceeds / p);
w.Taint += s.Taint;
w.Basis += s.Proceeds;
s.State = SaleState.Paid;
ev.Add(new BmEvent { Kind = "sale_paid", Sale = s, Amount = s.Proceeds });
}
// 买家落网:近期到账的出售记录会连累卖家(玩家)
var recent = Sales.Where(x => x.State == SaleState.Paid && day - x.PayDay <= 14).ToList();
if (recent.Count > 0 && rng.Chance(Tuning.BmSellerBust))
{
foreach (var s in recent) Traps.Add(new BmTrap { Day = day + Tuning.BmTrapDelay, Amount = s.Proceeds, Why = "seller", Item = s.Item });
Forum.Add("busted_buyer");
}
Sales.RemoveAll(x => (x.State == SaleState.Paid || x.State == SaleState.Delisted || x.State == SaleState.Lost) && day - Math.Max(x.ListedDay, x.PayDay) > 14);
}
}
}
+7 -1
View File
@@ -99,7 +99,13 @@ namespace Decentralized.Domain
public const double BmHoneypotBase = 0.01, BmHoneypotPerSecRep = 0.0004, BmHoneypotMax = 0.06, BmControlled = 0.35, BmSellerBust = 0.008;
public const int BmHoneypotDays = 6, BmTrapDelay = 4;
/// <summary>货架上除违禁品外的两类货:高科技物品(需中档声望)和显卡的出现概率、加价(在现货溢价 / 订单价之外再加),显卡里夹假卡的比例。</summary>
public const double BmTechChance = 0.15, BmGpuChance = 0.20, BmTechMarkup = 0.5, BmGpuMarkup = 0.2, BmGpuFake = 0.20;
public const double BmTechChance = 0.30, BmGpuChance = 0.30, BmTechMarkup = 0.5, BmGpuMarkup = 0.2, BmGpuFake = 0.20;
/// <summary>玩家上架出售:标价倍率范围(相对参考价)、上架费、成交抽成、成交后货款托管天数、假卡被买家看穿的概率;
/// 倍率 ≤0.9 / 1.0 / 1.8 时的期望成交天数(之间插值);货款带污点的概率与比例随倍率从下限涨到上限;卖家好评的初始值、看穿一次扣的点数。</summary>
public const double BmListFee = 0.05, BmCommission = 0.08, BmSellMin = 0.6, BmSellMax = 1.8, BmFakeDetect = 0.5;
public const double BmSellDaysFast = 1, BmSellDays1 = 2, BmSellDaysMax = 14;
public const double BmSaleTaintChanceMin = 0.2, BmSaleTaintChanceMax = 0.9, BmSaleTaintRatioMin = 0.2, BmSaleTaintRatioMax = 0.8;
public const int BmSaleHold = 3, BmRatingStart = 90, BmRatingFakeLoss = 10;
/// <summary>黑市人脉升级的价格。</summary>
public const int BmUnlockPrice = 1500;
/// <summary>单个金融违规事件记入的罪证上限。</summary>
+43 -1
View File
@@ -48,6 +48,31 @@ namespace Decentralized
return n;
}
// ---- 玩家出售:从储藏区挑能上架的货 ----
/// <summary>这件物品在黑市上的货 id(显卡是 "gpu:型号",其余是物品 id);不能上架返回 null。</summary>
public static string SellId(GameItem it)
{
if (it == null || it.identifier == null) return null;
if (Items.IsGpu(it)) return "gpu:" + Items.ToUnit(it).Model;
string id = it.identifier;
return BlackMarket.Pool.Any(x => x.Key == id) || BlackMarket.TechPool.Contains(id) ? id : null;
}
public static System.Collections.Generic.List<GameItem> Sellable() =>
GraphUtils.FindAllChildrenType<GameItem>(EmporiumEntry.Instance.invElement, i => GeneralHelper.IsItemOwned(i) && SellId(i) != null, i => i.identifier != Items.Rig);
/// <summary>参考价:显卡按标称型号与成色,其余按游戏基础价。</summary>
public static long RefValue(GameItem it)
{
string id = SellId(it);
if (id == null) return 0;
if (Items.IsGpu(it)) { var u = Items.ToUnit(it); return CardValue.Of(u.Model, u.Health); }
return Value(id);
}
public static GoodKind KindOf(string id) => id.StartsWith("gpu:") ? GoodKind.Gpu : BlackMarket.TechPool.Contains(id) ? GoodKind.Tech : GoodKind.Contraband;
/// <summary>每周刷新:现货与订单目录一起。返回是否刷新了。</summary>
public static bool Restock(SaveData d, int day, bool force = false)
{
@@ -62,8 +87,25 @@ namespace Decentralized
public static void Night(SaveData d, int day, bool crackdown)
{
var ps = PlayerStore.Instance;
foreach (var e in d.Black.Night(day, crackdown, d.Hot, d.Rng, StoreReputation.GetSecReputation()))
foreach (var e in d.Black.Night(day, crackdown, d.Hot, d.Rng, StoreReputation.GetSecReputation(), Session.Price))
{
if (e.Sale != null)
{
string sname = ItemName(e.Sale.Item);
Log.Info($"黑市出售事件:{e.Kind} {e.Sale.Item} ×{e.Sale.Mult} 货款 {e.Amount:0} 污点 {e.Sale.Taint:0}");
switch (e.Kind)
{
case "sale_sold": ps.AddNightLog(L.T("nightlog.bm.sale_sold", sname, Fmt.Num(e.Amount), e.Sale.PayDay), "99bb66"); break;
case "sale_paid": ps.AddNightLog(L.T("nightlog.bm.sale_paid", sname, Fmt.Num(e.Amount), Fmt.Num(e.Sale.Taint)), "99bb66"); break;
case "sale_fake":
Items.Give(Make(e.Sale.Item, e.Sale.Model, e.Sale.FakeOf, e.Sale.Health));
ps.AddNightLog(L.T("nightlog.bm.sale_fake", sname), "cc7755");
break;
case "sale_rug": ps.AddNightLog(L.T("nightlog.bm.sale_rug", sname, Fmt.Num(e.Amount)), "cc7755"); break;
}
if (e.Kind == "sale_rug") News.Show("pd_news_bmrug_" + day, L.T("news.bmrug.title"), L.T("news.bmrug.desc"), 4);
continue;
}
string name = e.Order != null && e.Kind != "trap" ? ItemName(e.Order.Item) : null;
Log.Info($"黑市订单事件:{e.Kind} {e.Order?.Item} {e.Order?.Seller} {e.Order?.Mode} 受控 {e.Order?.Controlled} 金额 {e.Amount:0}");
switch (e.Kind)
+24 -1
View File
@@ -270,7 +270,7 @@ black.kind.Contraband.3,Contraband (high)
black.kind.Tech,High-tech (markup)
nightlog.bm.ready,"Black market: {0} arrived; a courier will come by once you open."
item.web3guide.name,Web3 Handbook
item.web3guide.desc,"Bank: depositing turns credits into CRC; withdrawing turns CRC back into credits and goes through compliance screening. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometimes turns into enforcement at your door: signing logs the crime on the spot and seizes the goods, refusing only costs the payment. Ponero desk: Ponero is a privacy coin and swapping in wipes the taint on that value; 8% fee each way with a daily limit; coin swapped out of Ponero has no origin and cash-out screening gets stricter."
item.web3guide.desc,"Bank: depositing turns credits into CRC; withdrawing turns CRC back into credits and goes through compliance screening. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometimes turns into enforcement at your door: signing logs the crime on the spot and seizes the goods, refusing only costs the payment. Selling: on the black market's Sell page, list contraband, high-tech goods and GPUs from storage at 0.6x to 1.8x the reference price. The higher the multiplier, the slower the sale (below 0.9x about a day, 1.0x about two, 1.8x about fourteen), and the likelier and larger the taint on the payout; 5% listing fee, 8% commission, payout held 3 days before reaching your wallet; a fake card may be spotted and returned, hurting your rating; if the platform rugs, listed goods and unreleased payouts are gone. Ponero desk: Ponero is a privacy coin and swapping in wipes the taint on that value; 8% fee each way with a daily limit; coin swapped out of Ponero has no origin and cash-out screening gets stricter."
courier.odd1,...Package is here.
courier.odd2,"(His hand stays in his pocket.) Sign here."
courier.sign,Sign
@@ -286,3 +286,26 @@ nightlog.bm.trap.seller,"Black market: a seller was arrested and the order recor
web3.sub.mining,Mining
courier.sign.say,I'll sign.
courier.refuse.say,Not taking it. Send it back.
black.forum.busted_buyer,"Word is a batch of buyers got arrested, and the sale records are still on the platform."
nightlog.bm.sale_sold,"Black market: your {0} sold for {1}; payout on day {2}."
nightlog.bm.sale_paid,"Black market: payout of {1} for {0} arrived (CRT), taint {2}."
nightlog.bm.sale_fake,"Black market: the buyer saw that {0} is a fake and returned it; your rating dropped."
nightlog.bm.sale_rug,"Black market: the platform rugged; your {0} and the payout ({1}) are gone."
black.sub.sell,Sell
black.sell.h,Sell
black.sell.mult,"Price x{0}"
black.sell.info,"Price {0} ~{1} days to sell payout tainted {2}% of the time ({3}% of it)"
black.sell.list,List
black.sell.none,Nothing sellable in storage.
black.sell.ref,"Ref {0}"
black.sell.listed,"Listed: {0} at {1}."
black.sell.delist,Take down
black.sell.rating,"Rating {0}%"
black.mine.sales,My listings
black.mine.sale,"{0} x{1}: {2}"
black.sale.Listed,listed
black.sale.Sold,"sold, payout day {0}"
black.sale.Paid,paid
black.sale.Lost,platform rugged, gone
black.sale.Delisted,taken down
black.sale.Delisted.fake,buyer spotted the fake and returned it
1 # Probably Decentralized localization table: English (en)
270 black.kind.Tech
271 nightlog.bm.ready
272 item.web3guide.name
273 item.web3guide.desc
274 courier.odd1
275 courier.odd2
276 courier.sign
286 web3.sub.mining
287 courier.sign.say
288 courier.refuse.say
289 black.forum.busted_buyer
290 nightlog.bm.sale_sold
291 nightlog.bm.sale_paid
292 nightlog.bm.sale_fake
293 nightlog.bm.sale_rug
294 black.sub.sell
295 black.sell.h
296 black.sell.mult
297 black.sell.info
298 black.sell.list
299 black.sell.none
300 black.sell.ref
301 black.sell.listed
302 black.sell.delist
303 black.sell.rating
304 black.mine.sales
305 black.mine.sale
306 black.sale.Listed
307 black.sale.Sold
308 black.sale.Paid
309 black.sale.Lost
310 black.sale.Delisted
311 black.sale.Delisted.fake
+24 -1
View File
@@ -269,7 +269,7 @@ black.kind.Contraband.3,违禁品(高)
black.kind.Tech,高科技物品(加价)
nightlog.bm.ready,"黑市:{0} 到货,开店后会有快递员上门。"
item.web3guide.name,Web3 手册
item.web3guide.desc,"银行:存入把信用点换成 CRC;提取把 CRC 换回信用点,提取要过合规审查。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。Ponero 兑换处:Ponero 是隐私币,兑入时这部分价值的污点被清除;兑入、兑出各收 8% 手续费,每天有额度;从 Ponero 兑出的币来路不明,出金审查会加重。"
item.web3guide.desc,"银行:存入把信用点换成 CRC;提取把 CRC 换回信用点,提取要过合规审查。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的出售页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高;上架收 5% 上架费,成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了。Ponero 兑换处:Ponero 是隐私币,兑入时这部分价值的污点被清除;兑入、兑出各收 8% 手续费,每天有额度;从 Ponero 兑出的币来路不明,出金审查会加重。"
courier.odd1,……东西到了。
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
courier.sign,签收
@@ -285,3 +285,26 @@ nightlog.bm.trap.seller,"黑市:卖家落网,他留存的订单记录连累
web3.sub.mining,挖矿
courier.sign.say,签收。
courier.refuse.say,不收,退回去。
black.forum.busted_buyer,"有人说最近一批买家被抓了,成交记录还在平台上。"
nightlog.bm.sale_sold,"黑市:你上架的 {0} 卖出了,货款 {1},第 {2} 天放款。"
nightlog.bm.sale_paid,"黑市:{0} 的货款 {1} 到账(CRT),其中污点 {2}。"
nightlog.bm.sale_fake,"黑市:买家看穿了 {0} 是假卡,退了货,你的好评下降。"
nightlog.bm.sale_rug,"黑市:平台跑路,你的 {0} 和货款({1})没了。"
black.sub.sell,出售
black.sell.h,出售
black.sell.mult,"标价 ×{0}"
black.sell.info,"标价 {0} 约 {1} 天成交 货款带污点 {2}%(比例 {3}%)"
black.sell.list,上架
black.sell.none,储藏区里没有能卖的货。
black.sell.ref,"参考价 {0}"
black.sell.listed,"已上架:{0},标价 {1}。"
black.sell.delist,撤下
black.sell.rating,"好评 {0}%"
black.mine.sales,我的上架
black.mine.sale,"{0} ×{1}:{2}"
black.sale.Listed,上架中
black.sale.Sold,"已成交,第 {0} 天放款"
black.sale.Paid,已到账
black.sale.Lost,平台跑路,没了
black.sale.Delisted,已撤下
black.sale.Delisted.fake,买家看穿假卡退货
1 # Probably Decentralized 本地化表:简体中文(zh)
269 black.kind.Tech
270 nightlog.bm.ready
271 item.web3guide.name
272 item.web3guide.desc
273 courier.odd1
274 courier.odd2
275 courier.sign
285 web3.sub.mining
286 courier.sign.say
287 courier.refuse.say
288 black.forum.busted_buyer
289 nightlog.bm.sale_sold
290 nightlog.bm.sale_paid
291 nightlog.bm.sale_fake
292 nightlog.bm.sale_rug
293 black.sub.sell
294 black.sell.h
295 black.sell.mult
296 black.sell.info
297 black.sell.list
298 black.sell.none
299 black.sell.ref
300 black.sell.listed
301 black.sell.delist
302 black.sell.rating
303 black.mine.sales
304 black.mine.sale
305 black.sale.Listed
306 black.sale.Sold
307 black.sale.Paid
308 black.sale.Lost
309 black.sale.Delisted
310 black.sale.Delisted.fake
+73 -2
View File
@@ -267,7 +267,7 @@ namespace Decentralized
});
}
private enum BlackTab { Shop, Orders, Mine, Desk }
private enum BlackTab { Shop, Orders, Sell, Mine, Desk }
private static BlackTab _blackTab = BlackTab.Shop;
private static PayMode _mode = PayMode.Escrow;
private static string _deskAsset = Assets.PSC;
@@ -312,6 +312,7 @@ namespace Decentralized
if (d.Black.Closed(day)) { b.AddLabel(L.T("black.rugged", d.Black.ClosedUntil - day + 1)); return; }
if (_blackTab == BlackTab.Desk && desk) { DeskPage(b, rep, day); return; }
if (_blackTab == BlackTab.Mine) { MinePage(b, day); return; }
if (_blackTab == BlackTab.Sell) { SellPage(b, day); return; }
bool orders = _blackTab == BlackTab.Orders;
var w = d.Hot;
b.AddLabel(L.T("black.rep", rep, BlackMarket.MaxLevel(rep)) + " " + L.T("mixer.balance", _payAsset, Fmt.Num(w.Avail(_payAsset))) + (w.Taint > 0.5 ? " " + L.T("wallet.taint", Fmt.Credits(w.Taint)) : ""));
@@ -358,6 +359,60 @@ namespace Decentralized
}
}
private static double _sellMult = 1.0;
private static readonly double[] SellMults = { 0.6, 0.8, 0.9, 1.0, 1.2, 1.5, 1.8 };
/// <summary>出售:储藏区里能上架的货做成货架,选标价倍率后上架。</summary>
private static void SellPage(CustomUIBuilder b, int day)
{
var d = Session.Data;
b.BeginRow();
foreach (var m in SellMults)
{
double mult = m;
string n = m.ToString("0.0", CultureInfo.InvariantCulture);
b.AddButton(Math.Abs(mult - _sellMult) < 1e-9 ? $"[×{n}]" : "×" + n, () => { _sellMult = mult; Open(); });
}
b.End();
b.AddLabel(L.T("black.sell.info", "×" + _sellMult.ToString("0.0", CultureInfo.InvariantCulture), (int)Math.Round(BlackMarket.ExpectedDays(_sellMult)), (int)Math.Round(BlackMarket.TaintChance(_sellMult) * 100), (int)Math.Round(BlackMarket.TaintRatio(_sellMult) * 100)) + " " + L.T("black.sell.rating", d.Black.Rating));
b.AddLabel(L.T("mixer.balance", _payAsset, Fmt.Num(d.Hot.Avail(_payAsset))));
b.AddSeparator();
var items = BlackDelivery.Sellable();
if (items.Count == 0) { b.AddLabel(L.T("black.sell.none")); return; }
Shelf(b, items.Select(it =>
{
string id = BlackDelivery.SellId(it);
var kind = BlackDelivery.KindOf(id);
var g = new BmGood { Item = id, Kind = kind, Level = kind == GoodKind.Contraband ? BlackMarket.Pool.First(x => x.Key == id).Value : 3 };
if (kind == GoodKind.Gpu) g.Health = Items.ToUnit(it).Health;
g.Price = BlackMarket.ListPriceOf(BlackDelivery.RefValue(it), _sellMult);
return new { g, it };
}).Select(x => { SellCards[x.g] = x.it; return x.g; }).ToList(), (c, g) =>
{
var it = SellCards[g];
GoodHead(c, g, L.T("black.sell.ref", BlackDelivery.RefValue(it)));
c.AddLabel(L.T("black.price", g.Price));
c.AddFlexibleSpace();
c.AddButton(L.T("black.sell.list"), () => ListItem(it));
});
}
private static readonly System.Collections.Generic.Dictionary<BmGood, GameItem> SellCards = new System.Collections.Generic.Dictionary<BmGood, GameItem>();
private static void ListItem(GameItem it)
{
var d = Session.Data;
string id = BlackDelivery.SellId(it);
if (id == null) { Open(); return; }
var u = Items.IsGpu(it) ? Items.ToUnit(it) : null;
var s = d.Black.List(id, u?.Model, u?.FakeOf, u?.Health ?? 0, BlackDelivery.RefValue(it), _sellMult, d.Hot, _payAsset, StoreStation.GetDayCounter(), Session.Price);
if (s == null) { _message = L.T("msg.balance"); Open(); return; }
it.Destroy();
_message = L.T("black.sell.listed", ItemName(id), s.ListPrice);
Session.Save();
Open();
}
private const int ShelfCols = 4;
private const float CardW = 172f, CardH = 268f;
@@ -411,6 +466,15 @@ namespace Decentralized
Open();
}
private static void Delist(BmSale s)
{
var d = Session.Data;
d.Black.Delist(s);
Items.Give(BlackDelivery.Make(s.Item, s.Model, s.FakeOf, s.Health));
Session.Save();
Open();
}
/// <summary>我的订单与论坛情报。</summary>
private static void MinePage(CustomUIBuilder b, int day)
{
@@ -418,7 +482,14 @@ namespace Decentralized
Columns(b, l =>
{
l.AddHeader(L.T("black.mine.h"));
if (d.Black.Orders.Count == 0) l.AddLabel(L.T("black.mine.none"));
if (d.Black.Orders.Count == 0 && d.Black.Sales.Count == 0) l.AddLabel(L.T("black.mine.none"));
foreach (var sl in d.Black.Sales.Where(x => x.State != SaleState.Delisted || x.Why == "fake").OrderByDescending(x => x.ListedDay))
{
var sale = sl;
string st = sale.State == SaleState.Sold ? L.T("black.sale.Sold", sale.PayDay) : L.T("black.sale." + sale.State + (sale.State == SaleState.Delisted && sale.Why != null ? "." + sale.Why : ""));
l.AddLabel(L.T("black.mine.sale", ItemName(sale.Item), sale.Mult.ToString("0.0", CultureInfo.InvariantCulture), st));
if (sale.State == SaleState.Listed) l.AddButton(L.T("black.sell.delist"), () => Delist(sale));
}
foreach (var o in d.Black.Orders.OrderByDescending(x => x.PlacedDay))
l.AddLabel(L.T("black.mine.line", ItemName(o.Item), o.Seller, L.T("black.mode." + o.Mode), L.T("black.state." + o.State + (o.Why != null && o.State != OrderState.Done ? "." + o.Why : "")), o.DueDay));
}, r =>
@@ -654,6 +654,54 @@ internal static class Program
Check(r.Closed(40) && !r.Closed(41) && r.Place(new BmListing { Seller = 0, Item = "x", Price = 1 }, PayMode.Escrow, rw, Assets.CRT, 30, price) == null, "关闭期间不能下单");
});
Test("黑市出售", () =>
{
Func<string, double> price = a => a == Assets.CRT ? 1 : 100;
Check(Near(BlackMarket.ExpectedDays(0.6), 1, 1e-9) && Near(BlackMarket.ExpectedDays(0.9), 1, 1e-9) && Near(BlackMarket.ExpectedDays(1.0), 2, 1e-9) && Near(BlackMarket.ExpectedDays(1.8), 14, 1e-6), "期望成交天数:0.9 以下 1 天,1.0 两天,1.8 十四天");
Check(BlackMarket.ExpectedDays(1.2) > BlackMarket.ExpectedDays(1.0) && BlackMarket.ExpectedDays(1.5) > BlackMarket.ExpectedDays(1.2), "倍率越高越慢");
Check(BlackMarket.TaintChance(1.8) > BlackMarket.TaintChance(0.6) && BlackMarket.TaintRatio(1.8) > BlackMarket.TaintRatio(0.6) && Near(BlackMarket.TaintChance(0.6), Tuning.BmSaleTaintChanceMin, 1e-9), "倍率越高,带污点的概率和比例越高");
var bm = new BlackMarket();
var w = new Wallet(); w.Add(Assets.CRT, 1000);
Check(bm.List("c4", null, null, 0, 500, 2.0, w, Assets.CRT, 1, price) == null && bm.List("c4", null, null, 0, 500, 0.5, w, Assets.CRT, 1, price) == null, "倍率超出 0.6–1.8 不能上架");
var s = bm.List("c4", null, null, 0, 500, 1.0, w, Assets.CRT, 1, price);
Check(s != null && s.ListPrice == 500 && Near(w.Get(Assets.CRT), 1000 - 25, 1e-9), "上架:标价 500,上架费 5%");
int sold1 = 0, sold18 = 0;
for (int i = 0; i < 2000; i++) { if (new Rng((ulong)i + 1).Chance(bm.SaleChance(0.8))) sold1++; if (new Rng((ulong)i + 1).Chance(bm.SaleChance(1.8))) sold18++; }
Check(sold1 == 2000 && sold18 > 80 && sold18 < 220, $"每晚成交概率:0.8 倍必成交,1.8 倍约 1/14:{sold18}/2000");
// 成交与到账:夜晚 1 成交(0.8 倍必成交),托管 3 天后到钱包
var b2 = new BlackMarket();
var w2 = new Wallet(); w2.Add(Assets.CRT, 1000);
var s2 = b2.List("c4", null, null, 0, 500, 0.8, w2, Assets.CRT, 1, price);
double crt0 = w2.Get(Assets.CRT);
var e1 = b2.Night(2, false, w2, new Rng(7), 0, price);
Check(s2.State == SaleState.Sold && s2.Proceeds == (int)Math.Round(400 * (1 - Tuning.BmCommission)) && s2.PayDay == 2 + Tuning.BmSaleHold && Near(w2.Get(Assets.CRT), crt0, 1e-9), "成交:扣抽成,货款先托管");
b2.Night(4, false, w2, new Rng(8), 0, price);
Check(s2.State == SaleState.Sold, "没到放款日不动");
b2.Night(5, false, w2, new Rng(9), 0, price);
Check(s2.State == SaleState.Paid && Near(w2.Get(Assets.CRT), crt0 + s2.Proceeds, 1e-9) && Near(w2.Taint, s2.Taint, 1e-9) && (s2.Taint == 0 || Near(s2.Taint, s2.Proceeds * BlackMarket.TaintRatio(0.8), 1e-6)), "放款:货款进钱包,污点按比例");
// 假卡被看穿
int fake = 0;
for (int i = 0; i < 400; i++)
{
var b3 = new BlackMarket(); var w3 = new Wallet(); w3.Add(Assets.CRT, 1000);
var s3 = b3.List("gpu:RTX70", "RTX70", "RTX60", 60, 500, 0.8, w3, Assets.CRT, 1, price);
b3.Night(2, false, w3, new Rng((ulong)i + 3), 0, price);
if (s3.State == SaleState.Delisted && s3.Why == "fake") { fake++; if (b3.Rating >= Tuning.BmRatingStart) throw new Exception("好评没降"); }
}
Check(fake > 150 && fake < 250, $"假卡约一半被看穿退货:{fake}/400");
// 平台跑路:上架的货与未放款货款都没了
var b4 = new BlackMarket(); var w4 = new Wallet(); w4.Add(Assets.CRT, 100000);
for (int i = 0; i < 30; i++) b4.List("c4", null, null, 0, 5000, 1.8, w4, Assets.CRT, 1, price);
bool rugged = false;
for (int d = 2; d < 400 && !rugged; d++) { var ev = b4.Night(d, false, w4, new Rng((ulong)d * 31), 0, price); rugged = ev.Any(e => e.Kind == "sale_rug"); }
Check(rugged && b4.Sales.All(x => x.State != SaleState.Listed && x.State != SaleState.Sold), "跑路:上架与未放款全部归零");
// 镇压期间不推进
var b5 = new BlackMarket(); var w5 = new Wallet(); w5.Add(Assets.CRT, 1000);
var s5 = b5.List("c4", null, null, 0, 500, 0.8, w5, Assets.CRT, 1, price);
b5.Night(2, true, w5, new Rng(1), 0, price);
Check(s5.State == SaleState.Listed, "镇压期间平台下线,不成交");
});
Console.WriteLine($"\n{_passed} 通过,{_failed} 失败");
return _failed == 0 ? 0 : 1;
}