Black market in Web3 page, shelf UI, no dead drop, tech and GPU goods; rules moved to Web3 handbook

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:06:32 +08:00
1 parent 6bc479f710
commit e44a4439ed
12 files changed
+294 -243

No files matched your search

+4 -1
View File
@@ -4,7 +4,7 @@
### 第四阶段:Web3 黑市与 Ponero(进行中)
- 人脉网络新增"Web3 黑市"(前置:Web3 治理社区,1500):黑市派系在链上的实体;终端多一页"黑市",需要黑市声望 ≥ 10,镇压期间下线。
- 人脉网络新增"Web3 黑市"(前置:Web3 治理社区,1500):黑市派系在链上的实体;放在终端 Web3 页的"黑市"子页里,需要黑市声望 ≥ 10,镇压期间下线。
- 现货柜台:每周刷新一批违禁品(游戏已有的 32 种,低 / 中 / 高三档随声望 10 / 40 / 80 放开;件数 4 + 声望/40),价格 = 游戏基础价 × 1.3,只收 PSC / CRT / CRA / Ponero,不收 CRC。付出去的币连同按比例的污点留在卖家那边,货放进储藏区,不带赃物标签。
- Ponero 兑换处(声望 ≥ 30):任意币兑入 Ponero 时这部分价值的污点留在原地,兑入、兑出各收 8%,每天额度 1000 信用点;Ponero 价 = PSC × 0.5。从 Ponero 兑出的币在出金审查里按"刚出混币器"加重。
- 游戏内验证:声望 75 时上架 5 种;用 CRT 买一把左轮 312 点,余额 3000 → 2688,污点 2000 → 1875(按付款占钱包的比例);5 PSC 兑入得 9.2 Ponero(500 × 0.92 / 50),当天额度剩 500。
@@ -12,6 +12,9 @@
- 配送:送货上门 3 天,到货后开店时快递员作为顾客上门,对话结束把货放进储藏区;死投点 2 天,到货后晚上去垃圾场,货进外出背包。上门被截获 8%,死投 3%,死投另有 12% 被别人先挖走;镇压期间到期顺延、截获 +15%。
- 平台跑路:每晚概率 0.2% + 在途款项每万信用点 0.4%(封顶 5%),跑路后在途款项归零、目录与现货清空、入口关闭 10 天。论坛情报:骗子卖家有一半概率被人点名,跑路风险升高时出现提币变慢的帖子。
- 游戏内验证:下单后在途显示在"我的订单";过夜到期,两单到货,一单骗子卖家托管退款(CRT +143,污点 +57);快递员对话走完,货进储藏区,快递员自动离开。没有在游戏里验证:死投点取货(要去垃圾场)、平台跑路与截获的实际触发(只有单元测试)。
- 商城改成货架式:每行 4 张商品卡(游戏物品贴图、名称、类别、库存或卖家、价格、按钮);删掉死投点,只剩送货上门;货架上除违禁品外偶尔有高科技物品(声望 ≥ 40,加价 50%)和显卡(加价 20%,20% 是假卡)。
- 界面不放说明文字:银行、交易、混币器、黑市、Ponero 的规则说明都移进新物品"Web3 手册"(购买 Web3 治理社区时一并交付)。
- 游戏内验证:货架显示贴图与成色,用 CRT 买一张 RTX 60(232)进储藏区。
- 还没做:蜜罐、受控交付(第三批)。
### 第三阶段:数值调优(进行中)
+66 -19
View File
@@ -4,18 +4,29 @@ using System.Linq;
namespace Decentralized.Domain
{
/// <summary>黑市现货柜台上的一件货:游戏物品 id、剩余件数、含溢价的单价(信用点)。</summary>
public sealed class BmOffer
/// <summary>货的种类:违禁品、高科技物品、显卡。</summary>
public enum GoodKind { Contraband, Tech, Gpu }
/// <summary>黑市货架上的一种货。显卡的 Item 是 "gpu:型号",Model 是标称型号,FakeOf 非空表示背后是别的芯片(买家看不到)。</summary>
public class BmGood
{
public string Item;
public int Qty;
public int Price;
public GoodKind Kind;
public int Level;
public int Price;
public string Model, FakeOf;
public double Health;
}
/// <summary>现货柜台上的一件货:剩余件数、含溢价的单价(信用点)。</summary>
public sealed class BmOffer : BmGood
{
public int Qty;
}
/// <summary>
/// Web3 黑市:黑市派系在链上的实体。现货柜台每周刷新一批违禁品(档次随黑市声望放开),只收币,脏币也收,
/// 付出去的币连同按比例的污点一起留在卖家那边;Ponero 兑换处把任意币兑成无法追踪的 Ponero(污点留在原地),有每日额度和手续费。
/// Web3 黑市:黑市派系在链上的实体。货架上主要是违禁品(档次随黑市声望放开),偶尔有高科技物品和显卡(加价),
/// 现货只收币,脏币也收,付出去的币连同按比例的污点一起留在卖家那边;Ponero 兑换处把任意币兑成无法追踪的 Ponero(这部分价值的污点被清除),有每日额度和手续费。
/// 镇压期间整个平台下线。
/// </summary>
public sealed partial class BlackMarket
@@ -26,7 +37,7 @@ 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),
@@ -38,6 +49,13 @@ namespace Decentralized.Domain
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),
};
/// <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",
};
private static KeyValuePair<string, int> P(string id, int level) => new KeyValuePair<string, int>(id, level);
public static bool Open(int rep) => rep >= Tuning.BmOpenRep;
@@ -45,26 +63,55 @@ namespace Decentralized.Domain
public static bool DeskOpen(int rep) => rep >= Tuning.PoneroOpenRep;
public static int StockCount(int rep) => Tuning.BmStockBase + (int)Math.Max(0, rep / Tuning.BmStockPerRep);
/// <summary>溢价后的单价(信用点)。</summary>
public static int PriceOf(long baseValue) => (int)Math.Max(1, Math.Round(baseValue * (1 + Tuning.BmPremium)));
/// <summary>货的加价:违禁品 0,高科技、显卡各有额外加价(在现货溢价 / 订单折扣之外)。</summary>
public static double Extra(GoodKind k) => k == GoodKind.Tech ? Tuning.BmTechMarkup : k == GoodKind.Gpu ? Tuning.BmGpuMarkup : 0;
/// <summary>每周刷新一次(或首次开放时):按声望开放的档次抽 StockCount 种货,每种 1–3 件。value 给出物品的游戏基础价。</summary>
public bool Refresh(int day, int rep, Rng rng, Func<string, long> value)
/// <summary>现货单价(信用点):基础价 ×(1 + 溢价 + 货的加价)。</summary>
public static int PriceOf(long baseValue, GoodKind k = GoodKind.Contraband) => (int)Math.Max(1, Math.Round(baseValue * (1 + Tuning.BmPremium + Extra(k))));
/// <summary>抽 count 种货:高科技(中档声望起)和显卡按概率换掉违禁品。value 给出游戏物品的基础价;显卡的基础价按标称型号与成色算。</summary>
private List<BmGood> Roll(int count, int day, int rep, Rng rng, Func<string, long> value, Func<long, GoodKind, int> price)
{
if (!Open(rep) || day - RefreshedDay < Tuning.BmRefreshDays) return false;
RefreshedDay = day;
Offers.Clear();
var goods = new List<BmGood>();
var pool = Pool.Where(x => x.Value <= MaxLevel(rep)).ToList();
int n = Math.Min(StockCount(rep), pool.Count);
for (int i = 0; i < n; i++)
var tech = TechPool.ToList();
var gpus = UsedDealers.Pool(day);
for (int i = 0; i < count; i++)
{
double r = rng.NextDouble();
if (r < Tuning.BmGpuChance)
{
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)
{
int k = rng.Range(0, tech.Count - 1);
string id = tech[k];
tech.RemoveAt(k);
long v = value(id);
if (v > 0) goods.Add(new BmGood { Item = id, Kind = GoodKind.Tech, Level = 3, Price = price(v, GoodKind.Tech) });
}
else if (pool.Count > 0)
{
int k = rng.Range(0, pool.Count - 1);
var pick = pool[k];
pool.RemoveAt(k);
long v = value(pick.Key);
if (v <= 0) continue;
Offers.Add(new BmOffer { Item = pick.Key, Level = pick.Value, Qty = pick.Value == 3 ? 1 : rng.Range(1, 3), Price = PriceOf(v) });
if (v > 0) goods.Add(new BmGood { Item = pick.Key, Kind = GoodKind.Contraband, Level = pick.Value, Price = price(v, GoodKind.Contraband) });
}
}
return goods;
}
/// <summary>每周刷新一次(或首次开放时):按声望开放的档次抽 StockCount 种货,每种 1–3 件(高档与显卡、高科技 1 件)。</summary>
public bool Refresh(int day, int rep, Rng rng, Func<string, long> value)
{
if (!Open(rep) || day - RefreshedDay < Tuning.BmRefreshDays) return false;
RefreshedDay = day;
Offers.Clear();
foreach (var g in Roll(StockCount(rep), day, rep, rng, value, PriceOf))
Offers.Add(new BmOffer { Item = g.Item, Kind = g.Kind, Level = g.Level, Price = g.Price, Model = g.Model, FakeOf = g.FakeOf, Health = g.Health, Qty = g.Kind != GoodKind.Contraband || g.Level == 3 ? 1 : rng.Range(1, 3) });
return true;
}
@@ -91,7 +138,7 @@ namespace Decentralized.Domain
public double DeskLeft(int day) => Math.Max(0, Tuning.PoneroDailyCap - (DeskDay == day ? DeskToday : 0));
/// <summary>兑入 Ponero:from 币 amount 个,扣手续费后按 Ponero 价到账;这部分价值带的污点留在原地(不跟着 Ponero)。返回到账数量,额度或余额不足返回 -1。</summary>
/// <summary>兑入 Ponero:from 币 amount 个,扣手续费后按 Ponero 价到账;这部分价值带的污点被清除(不跟着 Ponero)。返回到账数量,额度或余额不足返回 -1。</summary>
public double ToPonero(Wallet w, string from, double amount, int day, Func<string, double> price)
{
double value = amount * price(from);
+14 -26
View File
@@ -5,7 +5,6 @@ using System.Linq;
namespace Decentralized.Domain
{
public enum PayMode { Escrow, Advance }
public enum Delivery { Door, Drop }
public enum OrderState { Pending, Ready, Done, Refunded, Lost }
/// <summary>黑市上的卖家:成交数与好评率公开;是不是骗子是隐藏的。</summary>
@@ -20,12 +19,9 @@ namespace Decentralized.Domain
}
/// <summary>订单目录里的一件货:卖家、货、价格(订单价,已折扣)。</summary>
public sealed class BmListing
public sealed class BmListing : BmGood
{
public int Seller;
public string Item;
public int Level;
public int Price;
}
public sealed class BmOrder
@@ -34,7 +30,8 @@ namespace Decentralized.Domain
public string Seller;
public bool SellerScam;
public PayMode Mode;
public Delivery Delivery;
public string Model, FakeOf;
public double Health;
public int Price;
public string Asset;
public double Qty;
@@ -49,7 +46,7 @@ namespace Decentralized.Domain
/// <summary>夜间发生的订单事件,供日志与新闻使用。</summary>
public sealed class BmEvent
{
public string Kind; // ready / scam_refund / scam_loss / rug / intercept / theft / reopen
public string Kind; // ready / scam_refund / scam_loss / rug / intercept / reopen
public BmOrder Order;
public double Amount;
}
@@ -71,16 +68,15 @@ namespace Decentralized.Domain
/// <summary>托管中和已提前放款但尚未交付的资金(信用点),平台跑路的概率随它增加。</summary>
public double Held => Orders.Where(o => o.State == OrderState.Pending).Sum(o => (double)o.Price);
public static int OrderPrice(long baseValue) => (int)Math.Max(1, Math.Round(baseValue * (1 - Tuning.BmOrderDiscount)));
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);
/// <summary>下单实付(信用点):托管 = 订单价 + 托管费;提前放款 = 订单价打折。</summary>
public static int Pay(int price, PayMode m) => m == PayMode.Escrow ? price + EscrowFee(price) : (int)Math.Round(price * (1 - Tuning.BmAdvanceDiscount));
public static int Days(Delivery d) => d == Delivery.Door ? Tuning.BmDoorDays : Tuning.BmDropDays;
public double RugChance => Math.Min(Tuning.BmRugMax, Tuning.BmRugBase + Held / 10000.0 * Tuning.BmRugPerTenK);
/// <summary>每周重新生成卖家、目录和论坛情报(与现货同一天刷新)。</summary>
public void RefreshOrders(int rep, Rng rng, Func<string, long> value)
public void RefreshOrders(int day, int rep, Rng rng, Func<string, long> value)
{
Sellers.Clear(); Catalog.Clear(); Forum.Clear();
for (int i = 0; i < Tuning.BmSellerCount; i++)
@@ -98,21 +94,14 @@ namespace Decentralized.Domain
Sellers.Add(s);
if (scam && rng.Chance(0.5)) Forum.Add("scam:" + s.Name);
}
var pool = Pool.Where(x => x.Value <= MaxLevel(rep)).ToList();
for (int i = 0; i < Tuning.BmSellerCount * 2 && pool.Count > 0; i++)
{
int k = rng.Range(0, pool.Count - 1);
var pick = pool[k];
pool.RemoveAt(k);
long v = value(pick.Key);
if (v <= 0) continue;
Catalog.Add(new BmListing { Seller = i % Sellers.Count, Item = pick.Key, Level = pick.Value, Price = OrderPrice(v) });
}
int n = 0;
foreach (var g in Roll(Tuning.BmSellerCount * 2, day, rep, rng, value, OrderPrice))
Catalog.Add(new BmListing { Seller = n++ % Sellers.Count, Item = g.Item, Kind = g.Kind, Level = g.Level, Price = g.Price, Model = g.Model, FakeOf = g.FakeOf, Health = g.Health });
if (RugChance > Tuning.BmRugBase * 3) Forum.Add("rug");
}
/// <summary>下单:钱包里的币按实付折算扣除,污点与成本按比例留在卖家那边(记在订单上)。成功返回订单。</summary>
public BmOrder Place(BmListing l, PayMode mode, Delivery del, Wallet w, string asset, int day, Func<string, double> price)
public BmOrder Place(BmListing l, PayMode mode, Wallet w, string asset, int day, Func<string, double> price)
{
if (l == null || asset == Assets.CRC || Closed(day)) return null;
var seller = Sellers[l.Seller];
@@ -129,8 +118,8 @@ namespace Decentralized.Domain
w.Add(asset, -qty);
var o = new BmOrder
{
Item = l.Item, Seller = seller.Name, SellerScam = seller.Scam, Mode = mode, Delivery = del, Price = credits, Asset = asset, Qty = qty,
Taint = taint, Basis = basis, PlacedDay = day, DueDay = day + Days(del),
Item = l.Item, Seller = seller.Name, SellerScam = seller.Scam, Mode = mode, Model = l.Model, FakeOf = l.FakeOf, Health = l.Health, Price = credits, Asset = asset, Qty = qty,
Taint = taint, Basis = basis, PlacedDay = day, DueDay = day + Tuning.BmDoorDays,
};
Orders.Add(o);
return o;
@@ -168,9 +157,8 @@ namespace Decentralized.Domain
else { o.State = OrderState.Lost; o.Why = "scam"; ev.Add(new BmEvent { Kind = "scam_loss", Order = o, Amount = o.Price }); }
continue;
}
double ic = (o.Delivery == Delivery.Door ? Tuning.BmInterceptDoor : Tuning.BmInterceptDrop) + (crackdown ? Tuning.BmInterceptCrackdown : 0);
double ic = Tuning.BmInterceptDoor + (crackdown ? Tuning.BmInterceptCrackdown : 0);
if (rng.Chance(ic)) { o.State = OrderState.Lost; o.Why = "intercept"; ev.Add(new BmEvent { Kind = "intercept", Order = o, Amount = o.Price }); continue; }
if (o.Delivery == Delivery.Drop && rng.Chance(Tuning.BmDropTheft)) { o.State = OrderState.Lost; o.Why = "theft"; ev.Add(new BmEvent { Kind = "theft", Order = o, Amount = o.Price }); continue; }
o.State = OrderState.Ready;
ev.Add(new BmEvent { Kind = "ready", Order = o });
}
@@ -180,6 +168,6 @@ namespace Decentralized.Domain
public void Deliver(BmOrder o) { o.State = OrderState.Done; o.Why = "done"; }
public IEnumerable<BmOrder> ReadyFor(Delivery d) => Orders.Where(o => o.State == OrderState.Ready && o.Delivery == d);
public IEnumerable<BmOrder> Ready => Orders.Where(o => o.State == OrderState.Ready);
}
}
+7 -5
View File
@@ -89,11 +89,13 @@ namespace Decentralized.Domain
/// <summary>Ponero 兑换处:开放所需黑市声望、每次兑入 / 兑出的手续费、每天可兑换的额度(信用点)、对 PSC 的价格比。</summary>
public const int PoneroOpenRep = 30;
public const double PoneroFee = 0.08, PoneroDailyCap = 1000, PoneroPscRatio = 0.5;
/// <summary>黑市订单:订单价相对游戏基础价的折扣、托管费、提前放款再折扣、提前放款所需卖家成交数、上门 / 死投的天数、
/// 上门 / 死投被截获的概率(镇压期间再加)、死投被别人先挖走的概率、骗子卖家占比、每晚平台跑路基础概率与每万信用点托管资金加的概率(封顶)、跑路后关闭的天数。</summary>
public const double BmOrderDiscount = 0.15, BmEscrowFee = 0.05, BmAdvanceDiscount = 0.10, BmInterceptDoor = 0.08, BmInterceptDrop = 0.03, BmInterceptCrackdown = 0.15;
public const double BmDropTheft = 0.12, BmScamSellerShare = 0.15, BmRugBase = 0.002, BmRugPerTenK = 0.004, BmRugMax = 0.05;
public const int BmAdvanceMinTrades = 30, BmDoorDays = 3, BmDropDays = 2, BmRugClosedDays = 10, BmSellerCount = 4;
/// <summary>黑市订单:订单价相对游戏基础价的折扣、托管费、提前放款再折扣、提前放款所需卖家成交数、上门的天数、
/// 上门被截获的概率(镇压期间再加)、骗子卖家占比、每晚平台跑路基础概率与每万信用点托管资金加的概率(封顶)、跑路后关闭的天数。</summary>
public const double BmOrderDiscount = 0.15, BmEscrowFee = 0.05, BmAdvanceDiscount = 0.10, BmInterceptDoor = 0.08, BmInterceptCrackdown = 0.15;
public const double BmScamSellerShare = 0.15, BmRugBase = 0.002, BmRugPerTenK = 0.004, BmRugMax = 0.05;
public const int BmAdvanceMinTrades = 30, BmDoorDays = 3, BmRugClosedDays = 10, BmSellerCount = 4;
/// <summary>货架上除违禁品外的两类货:高科技物品(需中档声望)和显卡的出现概率、加价(在现货溢价 / 订单价之外再加),显卡里夹假卡的比例。</summary>
public const double BmTechChance = 0.15, BmGpuChance = 0.20, BmTechMarkup = 0.5, BmGpuMarkup = 0.2, BmGpuFake = 0.20;
/// <summary>黑市人脉升级的价格。</summary>
public const int BmUnlockPrice = 1500;
/// <summary>单个金融违规事件记入的罪证上限。</summary>
+11 -6
View File
@@ -38,6 +38,16 @@ namespace Decentralized.Domain
return list.ToArray();
}
/// <summary>从池里抽一张:随机型号与成色;按 fakeShare 的概率是假卡(背后是便宜得多的真实芯片,废片只给低档标称)。</summary>
public static UsedCard Card(string[] pool, double fakeShare, int markupPct, Rng rng)
{
var label = pool[rng.Range(0, pool.Length - 1)];
var card = new UsedCard { Model = label, Health = 25 + rng.NextDouble() * 60, MarkupPct = markupPct };
var cheaper = Hardware.Specs.Values.Where(s => !s.Asic && s.Price * 2 <= Hardware.Specs[label].Price && (!s.Scrap || Hardware.Specs[label].Tier == 0)).Select(s => s.Model).ToArray();
if (cheaper.Length > 0 && rng.Chance(fakeShare)) card.FakeOf = cheaper[rng.Range(0, cheaper.Length - 1)];
return card;
}
public static List<UsedCard> Cards(DealerKind kind, int day, Rng rng)
{
var pool = Pool(day);
@@ -46,12 +56,7 @@ namespace Decentralized.Domain
for (int i = 0; i < count; i++)
{
double share = kind == DealerKind.Scammer ? FakeShareScammer : kind == DealerKind.Middleman ? FakeShareMiddleman : 0;
var label = pool[rng.Range(0, pool.Length - 1)];
var card = new UsedCard { Model = label, Health = 25 + rng.NextDouble() * 60 };
var cheaper = Hardware.Specs.Values.Where(s => !s.Asic && s.Price * 2 <= Hardware.Specs[label].Price && (!s.Scrap || Hardware.Specs[label].Tier == 0)).Select(s => s.Model).ToArray();
if (cheaper.Length > 0 && rng.Chance(share)) card.FakeOf = cheaper[rng.Range(0, cheaper.Length - 1)];
card.MarkupPct = kind == DealerKind.Middleman ? (int)Math.Round(MiddlemanMarkup * 100) : 0;
cards.Add(card);
cards.Add(Card(pool, share, kind == DealerKind.Middleman ? (int)Math.Round(MiddlemanMarkup * 100) : 0, rng));
}
return cards;
}
+19 -32
View File
@@ -5,20 +5,18 @@ namespace Decentralized
{
/// <summary>
/// Web3 黑市的货物进出:每周刷新现货与订单目录,夜间推进订单并写夜间日志,
/// 上门(快递员当顾客上门)和死投(去垃圾场取货)把到货交给玩家。
/// 上门(快递员当顾客上门)把到货交给玩家。
/// </summary>
public static class BlackDelivery
{
public static void Subscribe()
{
ModHook.OnGenerateCustomerNormal += OnGenerate;
ModHook.OnAfterhourArriveLate += OnArrive;
}
public static void Unsubscribe()
{
ModHook.OnGenerateCustomerNormal -= OnGenerate;
ModHook.OnAfterhourArriveLate -= OnArrive;
}
public static long Value(string id)
@@ -30,9 +28,19 @@ namespace Decentralized
return v;
}
/// <summary>货架上的货对应的游戏物品 id:显卡是 "gpu:型号",对应本 mod 的显卡物品。</summary>
public static string GameId(string id) => id.StartsWith("gpu:") ? Items.GpuId(id.Substring(4)) : id;
/// <summary>造出这件货:显卡按成色与(可能的)假卡真身造,不带卖家的说法;其余直接造。</summary>
public static GameItem Make(string id, string model, string fakeOf, double health)
{
if (id.StartsWith("gpu:")) return Items.NewCard(model, health, fakeOf, Session.Data.Rng);
return ModHelper.SpawnItem(id);
}
public static string ItemName(string id)
{
var it = ModHelper.SpawnItem(id);
var it = ModHelper.SpawnItem(GameId(id));
if (it == null) return id;
string n = it.name;
it.Destroy();
@@ -46,7 +54,7 @@ namespace Decentralized
if (d.Black.Closed(day)) return false;
if (force) d.Black.RefreshedDay = -999;
if (!d.Black.Refresh(day, rep, d.Rng, Value)) return false;
d.Black.RefreshOrders(rep, d.Rng, Value);
d.Black.RefreshOrders(day, rep, d.Rng, Value);
return true;
}
@@ -56,14 +64,13 @@ namespace Decentralized
foreach (var e in d.Black.Night(day, crackdown, d.Hot, d.Rng))
{
string name = e.Order != null ? ItemName(e.Order.Item) : null;
Log.Info($"黑市订单事件:{e.Kind} {e.Order?.Item} {e.Order?.Seller} {e.Order?.Mode}/{e.Order?.Delivery} 金额 {e.Amount:0}");
Log.Info($"黑市订单事件:{e.Kind} {e.Order?.Item} {e.Order?.Seller} {e.Order?.Mode} 金额 {e.Amount:0}");
switch (e.Kind)
{
case "ready": ps.AddNightLog(L.T("nightlog.bm.ready." + e.Order.Delivery, name), "99bb66"); break;
case "ready": ps.AddNightLog(L.T("nightlog.bm.ready", name), "99bb66"); break;
case "scam_refund": ps.AddNightLog(L.T("nightlog.bm.scam_refund", e.Order.Seller, name), "cc7755"); break;
case "scam_loss": ps.AddNightLog(L.T("nightlog.bm.scam_loss", e.Order.Seller, name, Fmt.Num(e.Amount)), "cc7755"); break;
case "intercept": ps.AddNightLog(L.T("nightlog.bm.intercept", name, Fmt.Num(e.Amount)), "cc7755"); break;
case "theft": ps.AddNightLog(L.T("nightlog.bm.theft", name, Fmt.Num(e.Amount)), "cc7755"); break;
case "rug": ps.AddNightLog(L.T("nightlog.bm.rug", name, Fmt.Num(e.Amount)), "cc7755"); break;
case "reopen": ps.AddNightLog(L.T("nightlog.bm.reopen"), "99bb66"); break;
}
@@ -83,7 +90,7 @@ namespace Decentralized
Log.Guard("黑市快递员", () =>
{
int day = StoreStation.GetDayCounter();
if (day == courierDay || !Session.Data.Black.ReadyFor(Delivery.Door).Any()) return;
if (day == courierDay || !Session.Data.Black.Ready.Any()) return;
courierDay = day;
manager.AddClient(CreateCourier());
});
@@ -110,33 +117,13 @@ namespace Decentralized
private static void DeliverDoor()
{
var d = Session.Data;
foreach (var o in d.Black.ReadyFor(Delivery.Door).ToList())
foreach (var o in d.Black.Ready.ToList())
{
Items.Give(ModHelper.SpawnItem(o.Item));
Items.Give(Make(o.Item, o.Model, o.FakeOf, o.Health));
d.Black.Deliver(o);
Log.Info($"黑市快递:{o.Item} 已放进储藏区");
Log.Info($"黑市快递:{o.Item}{(o.FakeOf != null ? "(假卡,真身 " + o.FakeOf + ")" : "")} 已放进储藏区");
}
Session.Save();
}
// ---- 死投:晚上去垃圾场,货在那里 ----
private static void OnArrive(string locationId)
{
if (locationId != "dumping_ground" || Session.Data == null || !Unlock.BlackUnlocked) return;
Log.Guard("黑市死投", () =>
{
var d = Session.Data;
var list = d.Black.ReadyFor(Delivery.Drop).ToList();
foreach (var o in list)
{
var item = ModHelper.SpawnItem(o.Item);
GeneralHelper.SetItemOwned(item, true);
GraphUtils.EmporiumTryAdd(EmporiumEntry.Instance.afterhourInventory, item);
d.Black.Deliver(o);
Log.Info($"黑市死投:{o.Item} 已放进外出背包");
}
if (list.Count > 0) { Session.Save(); }
});
}
}
}
+16
View File
@@ -16,6 +16,7 @@ namespace Decentralized
public const string Terminal = "pd_terminal";
public const string Rig = "pd_rig";
public const string Guide = "pd_gpu_guide";
public const string Web3Guide = "pd_web3_guide";
public const string GpuPrefix = "pd_gpu_";
public const string HealthTag = "PD_HEALTH";
@@ -38,6 +39,7 @@ namespace Decentralized
dir.Add(Terminal, CreateTerminal);
dir.Add(Rig, CreateRig);
dir.Add(Guide, CreateGuide);
dir.Add(Web3Guide, CreateWeb3Guide);
foreach (var spec in Hardware.Specs.Values.Where(s => !s.Asic && !s.Scrap))
{
var s = spec;
@@ -77,6 +79,20 @@ namespace Decentralized
return item;
}
/// <summary>Web3 手册:银行、交易、混币器、黑市与 Ponero 的规则。界面上不放说明,规则都在这里。</summary>
private static GameItem CreateWeb3Guide()
{
var item = ItemDirectory.CreateEmptyItem(Web3Guide);
item.SetSpriteAndShapeFromMod(ModInfo.Id, "gpu_guide");
item.name = L.T("item.web3guide.name");
item.shortDescription = L.T("item.web3guide.desc");
item.unitValue = 0;
item.EnableTag(GeneralHelper.IMPORTANT_TAG);
item.EnableTag(TypeHelper.NOT_FOR_RESALE);
ModHelper.SetSource(item, ModInfo.Id, ModInfo.Name);
return item;
}
// ---------- 显卡 ----------
/// <summary>RTX70 → "RTX 70",RX800 → "RX 800"。</summary>
+18 -24
View File
@@ -164,8 +164,6 @@ bank.ops,"Deposit / withdraw"
poin.account.h,"Poinbase account"
trade.h,"Trade"
web3.wallet.h,"On-chain wallet"
trade.hint,"Buying pays in the quote coin; |selling sells the traded coin."
bank.hint,"Deposit turns credits into CRC; |withdrawing turns CRC back into credits |and passes compliance review."
col.peg,"Price"
col.status,"Status"
stable.ok,"Pegged"
@@ -184,7 +182,6 @@ mixer.col.taint,Taint
mixer.col.ready,Ready
mixer.day,Day {0}
mixer.deposit,Deposit
mixer.hint,"Pick asset, amount and rounds.|Each round: fee, less taint.|Rounds = days of waiting.|Coins return to this address.|Bigger deposits clean worse."
mixer.balance,Balance: {1} {0}
mixer.rounds,Rounds
mixer.preview,"Expect {0} {1} back, taint down about {2}%, ready on day {3}."
@@ -206,23 +203,20 @@ nightlog.crt_freeze,The black market handed over addresses during the crackdown:
nightlog.thaw,Frozen {0} {1} thawed.
news.crtfreeze.title,Black market freezes some CRT addresses for the crackdown
news.crtfreeze.desc,"Word is the black market gave security a list of CRT addresses in exchange for being left alone. Holders with low standing are likelier to be frozen."
tab.black,Black market
black.upgrade.title,Web3 Black Market
black.upgrade.desc,"Unlocks the terminal's black-market page: the black-market faction's on-chain arm. A spot counter restocks weekly with contraband, payable only in coin (PSC, CRT, CRA, Ponero), tainted coin accepted, at a premium; higher standing also opens a Ponero desk. Needs black-market standing; offline during crackdowns."
black.upgrade.desc,"Unlocks the black market in the Web3 page: the black-market faction's on-chain arm. Weekly shelves of contraband, occasionally high-tech goods and GPUs (at a markup); buy spot, or order and wait for a courier. Coin only (PSC, CRT, CRA, Ponero), tainted coin accepted. Needs black-market standing; offline during crackdowns."
black.offline,The black market's on-chain entrance is offline during the crackdown.
black.closed,"You need black-market standing of {0} before anyone lets you in (currently {1})."
black.sub.shop,Spot counter
black.sub.desk,Ponero desk
black.shop.h,Spot counter
black.rep,"Black-market standing {0}, goods tier {1}/3 visible. Restocks weekly."
black.rep,"Black-market standing {0}"
black.empty,Sold out for this week.
black.offer,"{0} x{1} {2} credits"
black.buy,"Buy with coin ({0})"
black.buy,Buy
black.bought,"Deal done: {0} is in storage."
black.pay.h,Payment
black.hint,"Coin only, no CRC.|Prices are 30% above normal channels.|The coin you pay leaves its share of taint with the seller; the goods come without a stolen tag."
black.desk.h,Ponero desk
black.desk.hint,"Ponero is a privacy coin; it cannot be traced on-chain.|Swapping any coin into Ponero leaves that value's taint behind.|{0}% fee each way, with a daily limit.|Coin swapped out of Ponero has no origin; cash-out screening gets stricter."
black.desk.left,"Left today: {0} (limit {1})"
black.desk.price,"Ponero price: {0}"
black.desk.swap,Swap
@@ -233,23 +227,15 @@ black.desk.fail,Swap failed: balance, limit or amount is wrong.
black.sub.orders,Orders
black.sub.mine,My orders & intel
black.rugged,"The platform is down; nobody will reopen for {0} more days."
black.seller,"Seller {0} trades {1} rating {2}%"
black.seller,"{0}, {1} trades, {2}%"
black.orders.h,Order catalog
black.listing,"{0} pay {1} credits"
black.order,Order
black.noadvance,This seller does not take advance release
black.orders.hint,"Cheaper than spot, but it takes days|and things can go wrong.|Escrow: {0}% fee, released on delivery;|refunded if the seller runs.|Advance: {1}% off, but a scammer keeps it;|only sellers with >= {2} trades accept.|The platform can rug too; escrow and|advance money both sit with it."
black.order,"Order ({0} days)"
black.mode.Escrow,Escrow
black.mode.Advance,Advance release
black.del.Door,Door delivery
black.del.Drop,Dead drop
black.del.hint.Door,"A courier comes by in {0} days (as a customer when you open); higher interception chance."
black.del.hint.Drop,"Left at the dumping ground in {0} days, pick it up at night; low interception chance, but someone may dig it up first."
black.ordered,"Ordered: {0}, due day {1}."
black.mine.h,My orders
black.mine.none,No orders yet.
black.mine.line,"{0} ({1}, {2}): {3}, day {4}{5}"
black.mine.drop,", pick up at the dumping ground"
black.mine.line,"{0} ({1}, {2}): {3}, day {4}"
black.state.Pending,in transit
black.state.Ready,arrived
black.state.Done,picked up
@@ -257,17 +243,13 @@ black.state.Refunded.scam,seller ran, refunded
black.state.Lost.scam,seller ran, money gone
black.state.Lost.rug,platform rugged, money gone
black.state.Lost.intercept,intercepted
black.state.Lost.theft,dug up by someone else
black.forum.h,Forum intel
black.forum.quiet,Quiet lately.
black.forum.scam,"Someone says {0} takes the money and never ships."
black.forum.rug,"Withdrawals have been slow lately, the site is running promos, and a lot of complaint threads got deleted."
nightlog.bm.ready.Door,"Black market: {0} arrived; a courier will come by once you open."
nightlog.bm.ready.Drop,"Black market: {0} is at the dumping ground, go get it at night."
nightlog.bm.scam_refund,"Black market: seller {0} ran; the escrow for {1} was refunded."
nightlog.bm.scam_loss,"Black market: seller {0} took the advance and ran; {1} is gone ({2} credits)."
nightlog.bm.intercept,"Black market: the parcel with {0} was intercepted; {1} credits of payment lost."
nightlog.bm.theft,"Black market: {0} at the dumping ground was dug up by someone else; {1} credits lost."
nightlog.bm.rug,"Black market: the platform rugged; the in-transit funds for {0} ({1}) are gone."
nightlog.bm.reopen,Black market: someone reopened at the old spot.
news.bmrug.title,Black-market platform rugged
@@ -277,3 +259,15 @@ courier.line1.1,"Delivery's here."
courier.line1.2,"Package for you."
courier.line2.1,"Don't tell me what's inside. I don't want to know."
courier.line2.2,"Dropped it here. I'm gone."
web3.sub.black,Black market
black.stock,"{0} left"
black.price,"{0} credits"
black.noadvance,No advance release
black.kind.gpu,"GPU condition {0}%"
black.kind.Contraband.1,Contraband (low)
black.kind.Contraband.2,Contraband (mid)
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. 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."
1 # Probably Decentralized localization table: English (en)
164 poin.account.h
165 trade.h
166 web3.wallet.h
trade.hint
bank.hint
167 col.peg
168 col.status
169 stable.ok
182 mixer.col.ready
183 mixer.day
184 mixer.deposit
mixer.hint
185 mixer.balance
186 mixer.rounds
187 mixer.preview
203 nightlog.thaw
204 news.crtfreeze.title
205 news.crtfreeze.desc
tab.black
206 black.upgrade.title
207 black.upgrade.desc
208 black.offline
209 black.closed
210 black.sub.shop
211 black.sub.desk
212 black.shop.h
213 black.rep
214 black.empty
215 black.offer
216 black.buy
217 black.bought
218 black.pay.h
black.hint
219 black.desk.h
black.desk.hint
220 black.desk.left
221 black.desk.price
222 black.desk.swap
227 black.sub.orders
228 black.sub.mine
229 black.rugged
230 black.seller
231 black.orders.h
232 black.listing black.order
black.order
black.noadvance
black.orders.hint
233 black.mode.Escrow
234 black.mode.Advance
black.del.Door
black.del.Drop
black.del.hint.Door
black.del.hint.Drop
235 black.ordered
236 black.mine.h
237 black.mine.none
238 black.mine.line
black.mine.drop
239 black.state.Pending
240 black.state.Ready
241 black.state.Done
243 black.state.Lost.scam
244 black.state.Lost.rug
245 black.state.Lost.intercept
black.state.Lost.theft
246 black.forum.h
247 black.forum.quiet
248 black.forum.scam
249 black.forum.rug
nightlog.bm.ready.Door
nightlog.bm.ready.Drop
250 nightlog.bm.scam_refund
251 nightlog.bm.scam_loss
252 nightlog.bm.intercept
nightlog.bm.theft
253 nightlog.bm.rug
254 nightlog.bm.reopen
255 news.bmrug.title
259 courier.line1.2
260 courier.line2.1
261 courier.line2.2
262 web3.sub.black
263 black.stock
264 black.price
265 black.noadvance
266 black.kind.gpu
267 black.kind.Contraband.1
268 black.kind.Contraband.2
269 black.kind.Contraband.3
270 black.kind.Tech
271 nightlog.bm.ready
272 item.web3guide.name
273 item.web3guide.desc
+18 -24
View File
@@ -163,8 +163,6 @@ bank.ops,"存取"
poin.account.h,"Poinbase 账户"
trade.h,"交易"
web3.wallet.h,"链上钱包"
trade.hint,"买入按计价币的数量支付;|卖出按所交易币的数量卖出。"
bank.hint,"存入把信用点换成 CRC;|提取把 CRC 换回信用点,|提取要过合规审查。"
col.peg,"价格"
col.status,"状态"
stable.ok,"锚定正常"
@@ -183,7 +181,6 @@ mixer.col.taint,污点
mixer.col.ready,到期
mixer.day,第 {0} 天
mixer.deposit,存入
mixer.hint,"选币种、填数量和轮数。|每轮扣手续费并降低污点。|轮数即等待天数。|到期提回同一个地址。|存入越多,清洗越差。"
mixer.balance,余额:{1} {0}
mixer.rounds,轮数
mixer.preview,"预计提回 {0} {1},污点约降低 {2}%,第 {3} 天到期。"
@@ -205,23 +202,20 @@ nightlog.crt_freeze,镇压期间黑市交出了一批地址:你的 {0} CRT 被
nightlog.thaw,冻结的 {0} {1} 已解冻。
news.crtfreeze.title,黑市配合镇压冻结部分 CRT 地址
news.crtfreeze.desc,"传闻黑市为换取放行,向安保交出了一批 CRT 地址。声望低的持有者更容易被冻结。"
tab.black,黑市
black.upgrade.title,Web3 黑市
black.upgrade.desc,"解锁终端的黑市页:黑市派系在链上的实体。现货柜台每周上一批违禁品,只收币(PSC、CRT、CRA、Ponero),脏币也收,价格比正常渠道高;声望够高还有 Ponero 兑换处。需要黑市声望,镇压期间下线。"
black.upgrade.desc,"解锁 Web3 页里的黑市:黑市派系在链上的实体。货架上每周上一批违禁品,偶尔有高科技物品和显卡(要加价);可以直接买现货,也可以下订单等快递员上门。只收币(PSC、CRT、CRA、Ponero),脏币也收。需要黑市声望,镇压期间下线。"
black.offline,镇压期间,黑市的链上入口已经下线。
black.closed,"黑市声望达到 {0} 才会有人带你进去(现在 {1})。"
black.sub.shop,现货柜台
black.sub.desk,Ponero 兑换处
black.shop.h,现货柜台
black.rep,"黑市声望 {0},可见货物档次 {1}/3。每周刷新。"
black.rep,"黑市声望 {0}"
black.empty,这周的货卖完了。
black.offer,"{0} ×{1} {2} 信用点等值"
black.buy,"用币购买({0})"
black.buy,购买
black.bought,"成交:{0} 已放进储藏区。"
black.pay.h,付款
black.hint,"只收币,不收 CRC。|价格比正常渠道高 30%。|付出去的币带着按比例的污点一起留在卖家那边,拿到手的货不带赃物标签。"
black.desk.h,Ponero 兑换处
black.desk.hint,"Ponero 是隐私币,链上无法追踪。|任何币兑入 Ponero 时,这部分价值的污点留在原地。|兑入、兑出各收 {0}% 手续费,每天有额度。|从 Ponero 兑出的币来路不明,出金审查会加重。"
black.desk.left,"今天还能兑换 {0}(上限 {1})"
black.desk.price,"Ponero 价格:{0}"
black.desk.swap,兑换
@@ -232,23 +226,15 @@ black.desk.fail,兑换失败:余额、额度或数量不对。
black.sub.orders,订单
black.sub.mine,我的订单与情报
black.rugged,"平台出事了,入口关闭,还要 {0} 天才会有人重新开张。"
black.seller,"卖家 {0} 成交 {1} 好评 {2}%"
black.seller,"{0},成交 {1},好评 {2}%"
black.orders.h,订单目录
black.listing,"{0} 实付 {1} 信用点等值"
black.order,下单
black.noadvance,这个卖家不接受提前放款
black.orders.hint,"比现货便宜,但要等几天,|路上可能出事。|托管:多付 {0}% 托管费,|收货后放款,卖家跑路时退款。|提前放款:再打 {1}% 折,|卖家若是骗子钱就没了;|成交 ≥ {2} 的卖家才接受。|平台自己也可能跑路,|托管和提前放款的钱都在它手里。"
black.order,"下单({0} 天到)"
black.mode.Escrow,托管
black.mode.Advance,提前放款
black.del.Door,"送货上门"
black.del.Drop,死投点
black.del.hint.Door,"{0} 天后快递员上门(开店时来个顾客),被截获的概率较高。"
black.del.hint.Drop,"{0} 天后货放在垃圾场,晚上去取;被截获的概率低,但可能被别人先挖走。"
black.ordered,"已下单:{0},预计第 {1} 天到。"
black.mine.h,我的订单
black.mine.none,还没有订单。
black.mine.line,"{0}({1},{2}):{3},第 {4} 天{5}"
black.mine.drop,,去垃圾场取
black.mine.line,"{0}({1},{2}):{3},第 {4} 天"
black.state.Pending,在途
black.state.Ready,到货
black.state.Done,已取
@@ -256,17 +242,13 @@ black.state.Refunded.scam,卖家跑路,已退款
black.state.Lost.scam,卖家跑路,钱没了
black.state.Lost.rug,平台跑路,钱没了
black.state.Lost.intercept,被截获
black.state.Lost.theft,被别人先挖走
black.forum.h,论坛情报
black.forum.quiet,最近很安静。
black.forum.scam,"有人说 {0} 收了钱不发货。"
black.forum.rug,"最近提币变慢了,全场还在搞促销,投诉帖被删了不少。"
nightlog.bm.ready.Door,"黑市:{0} 到货,开店后会有快递员上门。"
nightlog.bm.ready.Drop,"黑市:{0} 已放在垃圾场,晚上去取。"
nightlog.bm.scam_refund,"黑市:卖家 {0} 跑路了,{1} 的托管款已退回。"
nightlog.bm.scam_loss,"黑市:卖家 {0} 拿了提前放款的钱跑路,{1} 没有了({2} 信用点等值)。"
nightlog.bm.intercept,"黑市:{0} 的包裹被截获,{1} 信用点等值的货款没了。"
nightlog.bm.theft,"黑市:垃圾场的 {0} 被别人先挖走了,{1} 信用点等值的货款没了。"
nightlog.bm.rug,"黑市:平台跑路,{0} 的在途款项({1})归零。"
nightlog.bm.reopen,黑市:有人在老地方重新开张了。
news.bmrug.title,黑市平台跑路
@@ -276,3 +258,15 @@ courier.line1.1,"东西到了。"
courier.line1.2,"有你的包裹。"
courier.line2.1,"别问我里面是什么,我也不想知道。"
courier.line2.2,"货放这儿了,我走了。"
web3.sub.black,黑市
black.stock,"剩 {0} 件"
black.price,"{0} 信用点等值"
black.noadvance,不接受提前放款
black.kind.gpu,"显卡 成色 {0}%"
black.kind.Contraband.1,违禁品(低)
black.kind.Contraband.2,违禁品(中)
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 兑出的币来路不明,出金审查会加重。"
1 # Probably Decentralized 本地化表:简体中文(zh)
163 poin.account.h
164 trade.h
165 web3.wallet.h
trade.hint
bank.hint
166 col.peg
167 col.status
168 stable.ok
181 mixer.col.ready
182 mixer.day
183 mixer.deposit
mixer.hint
184 mixer.balance
185 mixer.rounds
186 mixer.preview
202 nightlog.thaw
203 news.crtfreeze.title
204 news.crtfreeze.desc
tab.black
205 black.upgrade.title
206 black.upgrade.desc
207 black.offline
208 black.closed
209 black.sub.shop
210 black.sub.desk
211 black.shop.h
212 black.rep
213 black.empty
214 black.offer
215 black.buy
216 black.bought
217 black.pay.h
black.hint
218 black.desk.h
black.desk.hint
219 black.desk.left
220 black.desk.price
221 black.desk.swap
226 black.sub.orders
227 black.sub.mine
228 black.rugged
229 black.seller
230 black.orders.h
231 black.listing black.order
black.order
black.noadvance
black.orders.hint
232 black.mode.Escrow
233 black.mode.Advance
black.del.Door
black.del.Drop
black.del.hint.Door
black.del.hint.Drop
234 black.ordered
235 black.mine.h
236 black.mine.none
237 black.mine.line
black.mine.drop
238 black.state.Pending
239 black.state.Ready
240 black.state.Done
242 black.state.Lost.scam
243 black.state.Lost.rug
244 black.state.Lost.intercept
black.state.Lost.theft
245 black.forum.h
246 black.forum.quiet
247 black.forum.scam
248 black.forum.rug
nightlog.bm.ready.Door
nightlog.bm.ready.Drop
249 nightlog.bm.scam_refund
250 nightlog.bm.scam_loss
251 nightlog.bm.intercept
nightlog.bm.theft
252 nightlog.bm.rug
253 nightlog.bm.reopen
254 news.bmrug.title
258 courier.line1.2
259 courier.line2.1
260 courier.line2.2
261 web3.sub.black
262 black.stock
263 black.price
264 black.noadvance
265 black.kind.gpu
266 black.kind.Contraband.1
267 black.kind.Contraband.2
268 black.kind.Contraband.3
269 black.kind.Tech
270 nightlog.bm.ready
271 item.web3guide.name
272 item.web3guide.desc
+108 -94
View File
@@ -13,7 +13,7 @@ namespace Decentralized
public static class TerminalWindow
{
public const string Id = "pd_terminal";
private enum Page { Home, Bank, Poinbase, Web3, Black, Report }
private enum Page { Home, Bank, Poinbase, Web3, Report }
private static Page _page = Page.Home;
private static string _amount = "";
@@ -24,25 +24,23 @@ namespace Decentralized
if (Session.Data == null) return;
var b = CustomUIManager.Window(Id, L.T("term.title"));
if (b == null) return;
b.SetSize(780, 580).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle();
b.SetSize(780, 660).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle();
b.BeginRow();
foreach (Page p in Enum.GetValues(typeof(Page)))
{
if (p == Page.Black && !Unlock.BlackUnlocked) continue;
var page = p;
string name = L.T("tab." + p.ToString().ToLowerInvariant());
b.AddButton(page == _page ? $"[{name}]" : name, () => { _page = page; _message = null; Open(); });
}
b.End();
b.AddSeparator();
b.BeginScroll(450);
b.BeginScroll(540);
switch (_page)
{
case Page.Home: HomePage(b); break;
case Page.Bank: BankPage(b); break;
case Page.Poinbase: ExchangePage(b, Venue.Exchange); break;
case Page.Web3: Web3Page(b); break;
case Page.Black: BlackPage(b); break;
default: ReportPage(b); break;
}
b.End();
@@ -139,7 +137,6 @@ namespace Decentralized
}, r =>
{
r.AddHeader(L.T("bank.ops"));
foreach (var line in L.T("bank.hint").Split('|')) r.AddLabel(line);
AmountRow(r);
AmlPreview(r);
r.AddButton(L.T("trade.deposit"), Deposit);
@@ -229,7 +226,6 @@ namespace Decentralized
}, r =>
{
r.AddHeader(L.T("trade.h"));
foreach (var line in L.T("trade.hint").Split('|')) r.AddLabel(line);
AmountRow(r);
TradeRows(r, venue);
});
@@ -239,13 +235,21 @@ namespace Decentralized
private static void Web3Page(CustomUIBuilder b)
{
var w = Session.Data.Hot;
if (Unlock.MixerUnlocked)
if (Unlock.MixerUnlocked || Unlock.BlackUnlocked)
{
if (_web3Tab == Web3Tab.Mixer && !Unlock.MixerUnlocked) _web3Tab = Web3Tab.Trade;
if (_web3Tab == Web3Tab.Black && !Unlock.BlackUnlocked) _web3Tab = Web3Tab.Trade;
b.BeginRow();
b.AddButton(_web3Mixer ? L.T("web3.sub.trade") : $"[{L.T("web3.sub.trade")}]", () => { _web3Mixer = false; _message = null; Open(); });
b.AddButton(_web3Mixer ? $"[{L.T("web3.sub.mixer")}]" : L.T("web3.sub.mixer"), () => { _web3Mixer = true; _message = null; Open(); });
foreach (Web3Tab t in Enum.GetValues(typeof(Web3Tab)))
{
if ((t == Web3Tab.Mixer && !Unlock.MixerUnlocked) || (t == Web3Tab.Black && !Unlock.BlackUnlocked)) continue;
var tab = t;
string name = L.T("web3.sub." + t.ToString().ToLowerInvariant());
b.AddButton(tab == _web3Tab ? $"[{name}]" : name, () => { _web3Tab = tab; _message = null; Open(); });
}
b.End();
if (_web3Mixer) { MixerPage(b); return; }
if (_web3Tab == Web3Tab.Mixer) { MixerPage(b); return; }
if (_web3Tab == Web3Tab.Black) { BlackPage(b); return; }
}
Columns(b, l =>
{
@@ -258,7 +262,6 @@ namespace Decentralized
}, r =>
{
r.AddHeader(L.T("trade.h"));
foreach (var line in L.T("trade.hint").Split('|')) r.AddLabel(line);
AmountRow(r);
TradeRows(r, Venue.Dex);
});
@@ -267,12 +270,28 @@ namespace Decentralized
private enum BlackTab { Shop, Orders, Mine, Desk }
private static BlackTab _blackTab = BlackTab.Shop;
private static PayMode _mode = PayMode.Escrow;
private static Delivery _delivery = Delivery.Door;
private static string _deskAsset = Assets.PSC;
private static bool _deskIn = true;
private static string _payAsset = Assets.CRT;
private static readonly System.Collections.Generic.Dictionary<string, Sprite> IconCache = new System.Collections.Generic.Dictionary<string, Sprite>();
/// <summary>Web3 黑市:黑市派系的链上实体。现货柜台(只收币)与 Ponero 兑换处。</summary>
/// <summary>货架上的商品图:游戏物品自己的贴图(本 mod 的显卡用本 mod 的贴图)。</summary>
private static Sprite Icon(string id)
{
if (IconCache.TryGetValue(id, out var cached)) return cached;
Sprite sprite = null;
var it = ModHelper.SpawnItem(BlackDelivery.GameId(id));
if (it != null)
{
try { sprite = it.useSpriteFromMod ? Items.ModSprite(it.spritePath) : RenderHandler.LoadFromAtlas(it.spriteAtlasPath, it.spritePath); }
catch (Exception ex) { Log.Info($"商品图读取失败 {id}:{ex.Message}"); }
it.Destroy();
}
IconCache[id] = sprite;
return sprite;
}
/// <summary>Web3 页里的黑市:黑市派系的链上实体。货架式商城(现货、订单)、我的订单与情报、Ponero 兑换处。</summary>
private static void BlackPage(CustomUIBuilder b)
{
int rep = StoreReputation.GetBMReputation();
@@ -292,104 +311,100 @@ namespace Decentralized
b.End();
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.Orders) { OrdersPage(b, day); return; }
if (_blackTab == BlackTab.Mine) { MinePage(b, day); return; }
bool orders = _blackTab == BlackTab.Orders;
var w = d.Hot;
Columns(b, l =>
{
l.AddHeader(L.T("black.shop.h"));
l.AddLabel(L.T("black.rep", rep, BlackMarket.MaxLevel(rep)));
if (d.Black.Offers.Count == 0) l.AddLabel(L.T("black.empty"));
foreach (var o in d.Black.Offers.ToList())
{
var offer = o;
l.AddLabel(L.T("black.offer", ItemName(offer.Item), offer.Qty, offer.Price));
l.AddButton(L.T("black.buy", offer.Price), () => BuyOffer(offer));
}
}, r =>
{
r.AddHeader(L.T("black.pay.h"));
foreach (var line in L.T("black.hint").Split('|')) r.AddLabel(line);
r.BeginRow();
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)) : ""));
b.BeginRow();
foreach (var a in new[] { Assets.PSC, Assets.CRT, Assets.CRA, Assets.Ponero })
{
var asset = a;
r.AddButton(asset == _payAsset ? $"[{asset}]" : asset, () => { _payAsset = asset; Open(); });
b.AddButton(asset == _payAsset ? $"[{asset}]" : asset, () => { _payAsset = asset; Open(); });
}
r.End();
r.AddLabel(L.T("mixer.balance", _payAsset, Fmt.Num(w.Avail(_payAsset))));
if (w.Taint > 0.5) r.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)));
if (orders)
foreach (PayMode m in Enum.GetValues(typeof(PayMode)))
{
var mode = m;
string n = L.T("black.mode." + m);
b.AddButton(mode == _mode ? $"[{n}]" : n, () => { _mode = mode; Open(); });
}
b.End();
b.AddSeparator();
if (!orders)
{
if (d.Black.Offers.Count == 0) b.AddLabel(L.T("black.empty"));
Shelf(b, d.Black.Offers.Cast<BmGood>().ToList(), (c, g) =>
{
var offer = (BmOffer)g;
GoodHead(c, g, L.T("black.stock", offer.Qty));
c.AddLabel(L.T("black.price", offer.Price));
c.AddFlexibleSpace();
c.AddButton(L.T("black.buy"), () => BuyOffer(offer));
});
}
else
{
if (d.Black.Catalog.Count == 0) b.AddLabel(L.T("black.empty"));
Shelf(b, d.Black.Catalog.Cast<BmGood>().ToList(), (c, g) =>
{
var listing = (BmListing)g;
var seller = d.Black.Sellers[listing.Seller];
GoodHead(c, g, L.T("black.seller", seller.Name, seller.Trades, seller.Rating));
c.AddLabel(L.T("black.price", BlackMarket.Pay(listing.Price, _mode)));
c.AddFlexibleSpace();
bool ok = _mode == PayMode.Escrow || seller.Advance;
c.AddButton(ok ? L.T("black.order", Tuning.BmDoorDays) : L.T("black.noadvance"), () => { if (ok) PlaceOrder(listing); });
});
}
}
private const int ShelfCols = 4;
private const float CardW = 172f, CardH = 268f;
/// <summary>货架:一行 4 张商品卡。</summary>
private static void Shelf(CustomUIBuilder b, System.Collections.Generic.List<BmGood> goods, Action<CustomUIBuilder, BmGood> card)
{
for (int i = 0; i < goods.Count; i += ShelfCols)
{
b.BeginRow(6f);
for (int k = i; k < Math.Min(goods.Count, i + ShelfCols); k++)
{
var g = goods[k];
b.BeginPanel(CardW, CardH, TextAnchor.UpperCenter, 6f, 3f);
card(b, g);
b.End();
}
b.End();
}
}
private static void GoodHead(CustomUIBuilder c, BmGood g, string line2)
{
var sprite = Icon(g.Item);
if (sprite != null) c.AddImage(sprite, 64f, 64f); else c.AddSpace(64f);
c.AddLabel("<b>" + ItemName(g.Item) + "</b>");
c.AddLabel(g.Kind == GoodKind.Gpu ? L.T("black.kind.gpu", (int)Math.Round(g.Health)) : L.T("black.kind." + g.Kind + (g.Kind == GoodKind.Contraband ? "." + g.Level : "")));
c.AddLabel(line2);
}
private static string ItemName(string id) => BlackDelivery.ItemName(id);
private static void BuyOffer(BmOffer o)
{
var d = Session.Data;
string item = o.Item, model = o.Model, fake = o.FakeOf;
double health = o.Health;
if (!d.Black.Buy(o, d.Hot, _payAsset, Session.Price)) { _message = L.T("msg.balance"); Open(); return; }
Items.Give(ModHelper.SpawnItem(o.Item));
_message = L.T("black.bought", ItemName(o.Item));
Items.Give(BlackDelivery.Make(item, model, fake, health));
_message = L.T("black.bought", ItemName(item));
Session.Save();
Open();
}
private static string SellerText(BmSeller s) => L.T("black.seller", s.Name, s.Trades, s.Rating);
/// <summary>订单目录:比现货便宜,要等几天,可能被截获、被骗。</summary>
private static void OrdersPage(CustomUIBuilder b, int day)
{
var d = Session.Data;
var w = d.Hot;
Columns(b, l =>
{
l.AddHeader(L.T("black.orders.h"));
if (d.Black.Catalog.Count == 0) l.AddLabel(L.T("black.empty"));
foreach (var x in d.Black.Catalog.ToList())
{
var listing = x;
var seller = d.Black.Sellers[listing.Seller];
l.AddLabel(L.T("black.listing", ItemName(listing.Item), BlackMarket.Pay(listing.Price, _mode)));
l.AddLabel(" " + SellerText(seller));
bool ok = _mode == PayMode.Escrow || seller.Advance;
l.AddButton(ok ? L.T("black.order") : L.T("black.noadvance"), () => { if (ok) PlaceOrder(listing); });
}
}, r =>
{
r.AddHeader(L.T("black.pay.h"));
foreach (var line in L.T("black.orders.hint", (int)Math.Round(Tuning.BmEscrowFee * 100), (int)Math.Round(Tuning.BmAdvanceDiscount * 100), Tuning.BmAdvanceMinTrades).Split('|')) r.AddLabel(line);
r.BeginRow();
foreach (PayMode m in Enum.GetValues(typeof(PayMode)))
{
var mode = m;
string n = L.T("black.mode." + m);
r.AddButton(mode == _mode ? $"[{n}]" : n, () => { _mode = mode; Open(); });
}
r.End();
r.BeginRow();
foreach (Delivery dl in Enum.GetValues(typeof(Delivery)))
{
var del = dl;
string n = L.T("black.del." + dl, Tuning.BmDoorDays);
r.AddButton(del == _delivery ? $"[{n}]" : n, () => { _delivery = del; Open(); });
}
r.End();
r.AddLabel(L.T("black.del.hint." + _delivery, BlackMarket.Days(_delivery)));
r.BeginRow();
foreach (var a in new[] { Assets.PSC, Assets.CRT, Assets.CRA, Assets.Ponero })
{
var asset = a;
r.AddButton(asset == _payAsset ? $"[{asset}]" : asset, () => { _payAsset = asset; Open(); });
}
r.End();
r.AddLabel(L.T("mixer.balance", _payAsset, Fmt.Num(w.Avail(_payAsset))));
});
}
private static void PlaceOrder(BmListing l)
{
var d = Session.Data;
var o = d.Black.Place(l, _mode, _delivery, d.Hot, _payAsset, StoreStation.GetDayCounter(), Session.Price);
var o = d.Black.Place(l, _mode, d.Hot, _payAsset, StoreStation.GetDayCounter(), Session.Price);
if (o == null) { _message = L.T("msg.balance"); Open(); return; }
_message = L.T("black.ordered", ItemName(o.Item), o.DueDay);
Session.Save();
@@ -405,7 +420,7 @@ namespace Decentralized
l.AddHeader(L.T("black.mine.h"));
if (d.Black.Orders.Count == 0) l.AddLabel(L.T("black.mine.none"));
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, o.Delivery == Delivery.Drop && o.State == OrderState.Ready ? L.T("black.mine.drop") : ""));
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 =>
{
r.AddHeader(L.T("black.forum.h"));
@@ -422,7 +437,6 @@ namespace Decentralized
Columns(b, l =>
{
l.AddHeader(L.T("black.desk.h"));
foreach (var line in L.T("black.desk.hint", (int)Math.Round(Tuning.PoneroFee * 100)).Split('|')) l.AddLabel(line);
l.AddLabel(L.T("black.desk.left", Fmt.Credits(d.Black.DeskLeft(day)), Fmt.Credits(Tuning.PoneroDailyCap)));
l.AddLabel(L.T("black.desk.price", Fmt.Num(Session.Price(Assets.Ponero))));
Holdings(l, w, a => a == Assets.Ponero || a == _deskAsset);
@@ -460,7 +474,8 @@ namespace Decentralized
Open();
}
private static bool _web3Mixer;
private enum Web3Tab { Trade, Mixer, Black }
private static Web3Tab _web3Tab = Web3Tab.Trade;
private static string _mixAsset = Assets.PSC;
private static int _mixRounds = 2;
private static readonly float[] MixW = { 70f, 100f, 100f, 80f };
@@ -488,7 +503,6 @@ namespace Decentralized
}, r =>
{
r.AddHeader(L.T("mixer.deposit"));
foreach (var line in L.T("mixer.hint").Split('|')) r.AddLabel(line);
r.BeginRow();
foreach (var a in new[] { Assets.PSC, Assets.CRC, Assets.CRT, Assets.CRA })
{
+1
View File
@@ -44,6 +44,7 @@ namespace Decentralized
Items.Give(ModHelper.SpawnItem(Items.Rig));
Items.Give(ModHelper.SpawnItem(Items.GpuId("RTX60")));
Items.Give(ModHelper.SpawnItem(Items.Guide));
Items.Give(ModHelper.SpawnItem(Items.Web3Guide));
Log.Info("已加入 Web3 治理社区");
}
}
+12 -12
View File
@@ -567,7 +567,7 @@ internal static class Program
Func<string, double> price = a => a == Assets.Ponero ? 50 : a == Assets.PSC ? 100 : 1;
var bm = new BlackMarket();
Check(!bm.Refresh(10, 5, new Rng(1), id => 100) && bm.Offers.Count == 0, "声望不够不开放");
Check(bm.Refresh(10, 45, new Rng(1), id => 100) && bm.Offers.Count > 0 && bm.Offers.All(o => o.Level <= 2 && o.Price == 130), "声望 45 开放中档,溢价 30%");
Check(bm.Refresh(10, 45, new Rng(1), id => 100) && bm.Offers.Count > 0 && bm.Offers.Where(o => o.Kind == GoodKind.Contraband).All(o => o.Level <= 2 && o.Price == 130), "声望 45 开放中档,溢价 30%");
Check(!bm.Refresh(12, 45, new Rng(2), id => 100) && bm.Refresh(17, 45, new Rng(2), id => 100), "每周刷新");
var w = new Wallet { Address = "t" };
w.Add(Assets.PSC, 10); w.Add(Assets.CRT, 1000); w.Taint = 400; w.Basis = 800; // 总价值 2000
@@ -590,8 +590,8 @@ internal static class Program
Func<string, double> price = a => a == Assets.CRT ? 1 : 100;
Func<string, long> value = id => 200;
var bm = new BlackMarket();
bm.RefreshOrders(75, new Rng(5), value);
Check(bm.Sellers.Count == Tuning.BmSellerCount && bm.Catalog.Count > 0 && bm.Catalog.All(l => l.Price == 170), "目录:卖家与订单价(基础价 × 0.85)");
bm.RefreshOrders(10, 75, new Rng(5), value);
Check(bm.Sellers.Count == Tuning.BmSellerCount && bm.Catalog.Count > 0 && bm.Catalog.Where(l => l.Kind == GoodKind.Contraband).All(l => l.Price == 170), "目录:卖家与订单价(基础价 × 0.85)");
Check(BlackMarket.Pay(170, PayMode.Escrow) == 170 + 9 && BlackMarket.Pay(170, PayMode.Advance) == 153, "托管多付 5%,提前放款打九折");
var good = new BmSeller { Name = "g", Trades = 100, Rating = 95 };
var bad = new BmSeller { Name = "b", Trades = 100, Rating = 95, Scam = true };
@@ -600,38 +600,38 @@ internal static class Program
var lb = new BmListing { Seller = 1, Item = "x", Price = 1000 };
var w = new Wallet { Address = "t" };
w.Add(Assets.CRT, 10000); w.Taint = 4000; w.Basis = 5000;
var o = bm.Place(lb, PayMode.Escrow, Delivery.Door, w, Assets.CRT, 10, price);
Check(o != null && Near(w.Get(Assets.CRT), 10000 - 1050, 1e-9) && w.Taint < 4000 && bm.Place(lg, PayMode.Escrow, Delivery.Door, w, Assets.CRC, 10, price) == null, "托管下单:扣币、污点留在订单上;不收 CRC");
var o = bm.Place(lb, PayMode.Escrow, w, Assets.CRT, 10, price);
Check(o != null && Near(w.Get(Assets.CRT), 10000 - 1050, 1e-9) && w.Taint < 4000 && bm.Place(lg, PayMode.Escrow, w, Assets.CRC, 10, price) == null, "托管下单:扣币、污点留在订单上;不收 CRC");
double tBefore = 4000;
var ev = bm.Night(13, false, w, new Rng(1));
Check(ev.Any(e => e.Kind == "scam_refund") && Near(w.Get(Assets.CRT), 10000, 1e-6) && Near(w.Taint, tBefore, 1e-6) && Near(w.Basis, 5000, 1e-6), "骗子卖家 + 托管:退款,污点与成本一并还回(不能借退款洗钱)");
var o2 = bm.Place(lb, PayMode.Advance, Delivery.Drop, w, Assets.CRT, 20, price);
var ev2 = bm.Night(22, false, w, new Rng(2));
var o2 = bm.Place(lb, PayMode.Advance, w, Assets.CRT, 20, price);
var ev2 = bm.Night(23, false, w, new Rng(2));
Check(o2.State == OrderState.Lost && Near(w.Get(Assets.CRT), 10000 - 900, 1e-6), "骗子卖家 + 提前放款:钱没了");
var o3 = bm.Place(lg, PayMode.Escrow, Delivery.Door, w, Assets.CRT, 30, price);
var o3 = bm.Place(lg, PayMode.Escrow, w, Assets.CRT, 30, price);
Check(bm.Night(31, false, w, new Rng(3)).Count == 0 && o3.State == OrderState.Pending, "没到期不动");
int ready = 0, lost = 0;
for (int i = 0; i < 400; i++)
{
var t = new BlackMarket(); t.Sellers.Add(good);
var ww = new Wallet(); ww.Add(Assets.CRT, 5000);
var oo = t.Place(new BmListing { Seller = 0, Item = "x", Price = 100 }, PayMode.Escrow, Delivery.Door, ww, Assets.CRT, 1, price);
var oo = t.Place(new BmListing { Seller = 0, Item = "x", Price = 100 }, PayMode.Escrow, ww, Assets.CRT, 1, price);
t.Night(oo.DueDay, false, ww, new Rng((ulong)i + 7));
if (oo.State == OrderState.Ready) ready++; else lost++;
}
Check(ready > 340 && lost > 10, $"上门:约 {Tuning.BmInterceptDoor:P0} 被截获(跑路另算):到货 {ready},丢失 {lost}");
var c = new BlackMarket(); c.Sellers.Add(good);
var cw = new Wallet(); cw.Add(Assets.CRT, 5000);
var co = c.Place(new BmListing { Seller = 0, Item = "x", Price = 100 }, PayMode.Escrow, Delivery.Door, cw, Assets.CRT, 1, price);
var co = c.Place(new BmListing { Seller = 0, Item = "x", Price = 100 }, PayMode.Escrow, cw, Assets.CRT, 1, price);
c.Night(co.DueDay, true, cw, new Rng(1));
Check(co.State == OrderState.Pending && co.DueDay == 5, "镇压期间到期顺延一天");
// 跑路:托管资金多则概率高;跑路后关闭
var r = new BlackMarket(); r.Sellers.Add(good);
var rw = new Wallet(); rw.Add(Assets.CRT, 1000000);
for (int i = 0; i < 20; i++) r.Place(new BmListing { Seller = 0, Item = "x", Price = 10000 }, PayMode.Escrow, Delivery.Door, rw, Assets.CRT, 1, price);
for (int i = 0; i < 20; i++) r.Place(new BmListing { Seller = 0, Item = "x", Price = 10000 }, PayMode.Escrow, rw, Assets.CRT, 1, price);
Check(r.RugChance > BlackMarketTestRug(), "托管资金越多越容易跑路");
r.ClosedUntil = 40;
Check(r.Closed(40) && !r.Closed(41) && r.Place(new BmListing { Seller = 0, Item = "x", Price = 1 }, PayMode.Escrow, Delivery.Door, rw, Assets.CRT, 30, price) == null, "关闭期间不能下单");
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, "关闭期间不能下单");
});
Console.WriteLine($"\n{_passed} 通过,{_failed} 失败");