Stage 4: black market orders, escrow, delivery, seller scams, platform rug
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f43cd487e5
commit
6bc479f710
12 files changed
+592
-35
No files matched your search
+5
-1
@@ -8,7 +8,11 @@
|
||||
- 现货柜台:每周刷新一批违禁品(游戏已有的 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。
|
||||
- 还没做:订单(托管、送货、死投点)、平台跑路、包裹截获、蜜罐、受控交付。
|
||||
- 订单:黑市页新增"订单"和"我的订单与情报"。目录每周刷新,4 个卖家(成交数、好评率公开,15% 是骗子,多半是新号,也有刷过好评的老号),价格 = 基础价 × 0.85。托管多付 5%,收货后放款,卖家跑路时退款(污点与成本一并还回,不能借退款洗钱);提前放款再打九折,只有成交 ≥ 30 的卖家接受,骗子拿钱就跑。
|
||||
- 配送:送货上门 3 天,到货后开店时快递员作为顾客上门,对话结束把货放进储藏区;死投点 2 天,到货后晚上去垃圾场,货进外出背包。上门被截获 8%,死投 3%,死投另有 12% 被别人先挖走;镇压期间到期顺延、截获 +15%。
|
||||
- 平台跑路:每晚概率 0.2% + 在途款项每万信用点 0.4%(封顶 5%),跑路后在途款项归零、目录与现货清空、入口关闭 10 天。论坛情报:骗子卖家有一半概率被人点名,跑路风险升高时出现提币变慢的帖子。
|
||||
- 游戏内验证:下单后在途显示在"我的订单";过夜到期,两单到货,一单骗子卖家托管退款(CRT +143,污点 +57);快递员对话走完,货进储藏区,快递员自动离开。没有在游戏里验证:死投点取货(要去垃圾场)、平台跑路与截获的实际触发(只有单元测试)。
|
||||
- 还没做:蜜罐、受控交付(第三批)。
|
||||
|
||||
### 第三阶段:数值调优(进行中)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Decentralized.Domain
|
||||
/// 付出去的币连同按比例的污点一起留在卖家那边;Ponero 兑换处把任意币兑成无法追踪的 Ponero(污点留在原地),有每日额度和手续费。
|
||||
/// 镇压期间整个平台下线。
|
||||
/// </summary>
|
||||
public sealed class BlackMarket
|
||||
public sealed partial class BlackMarket
|
||||
{
|
||||
public List<BmOffer> Offers = new List<BmOffer>();
|
||||
public int RefreshedDay = -999;
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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>
|
||||
public sealed class BmSeller
|
||||
{
|
||||
public string Name;
|
||||
public int Trades;
|
||||
public int Rating; // 好评率 %
|
||||
public bool Scam;
|
||||
/// <summary>只有成交数够多的卖家才接受提前放款。</summary>
|
||||
public bool Advance => Trades >= Tuning.BmAdvanceMinTrades;
|
||||
}
|
||||
|
||||
/// <summary>订单目录里的一件货:卖家、货、价格(订单价,已折扣)。</summary>
|
||||
public sealed class BmListing
|
||||
{
|
||||
public int Seller;
|
||||
public string Item;
|
||||
public int Level;
|
||||
public int Price;
|
||||
}
|
||||
|
||||
public sealed class BmOrder
|
||||
{
|
||||
public string Item;
|
||||
public string Seller;
|
||||
public bool SellerScam;
|
||||
public PayMode Mode;
|
||||
public Delivery Delivery;
|
||||
public int Price;
|
||||
public string Asset;
|
||||
public double Qty;
|
||||
/// <summary>付款时留在卖家那边的污点与成本:退款时一并还回,免得"付脏币再退款"洗钱。</summary>
|
||||
public double Taint, Basis;
|
||||
public int PlacedDay, DueDay;
|
||||
public OrderState State = OrderState.Pending;
|
||||
/// <summary>终态的原因:done / scam / rug / intercept / theft / refund。</summary>
|
||||
public string Why;
|
||||
}
|
||||
|
||||
/// <summary>夜间发生的订单事件,供日志与新闻使用。</summary>
|
||||
public sealed class BmEvent
|
||||
{
|
||||
public string Kind; // ready / scam_refund / scam_loss / rug / intercept / theft / reopen
|
||||
public BmOrder Order;
|
||||
public double Amount;
|
||||
}
|
||||
|
||||
public sealed partial class BlackMarket
|
||||
{
|
||||
public List<BmSeller> Sellers = new List<BmSeller>();
|
||||
public List<BmListing> Catalog = new List<BmListing>();
|
||||
public List<BmOrder> Orders = new List<BmOrder>();
|
||||
public List<string> Forum = new List<string>();
|
||||
/// <summary>平台跑路后关闭到这一天(含)。</summary>
|
||||
public int ClosedUntil = -1;
|
||||
|
||||
private static readonly string[] NameA = { "rat", "grey", "static", "null", "dust", "iron", "pale", "wire", "salt", "ghost" };
|
||||
private static readonly string[] NameB = { "king", "mule", "broker", "hand", "cat", "fox", "tooth", "lamp", "nine", "dealer" };
|
||||
|
||||
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 static int OrderPrice(long baseValue) => (int)Math.Max(1, Math.Round(baseValue * (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)
|
||||
{
|
||||
Sellers.Clear(); Catalog.Clear(); Forum.Clear();
|
||||
for (int i = 0; i < Tuning.BmSellerCount; i++)
|
||||
{
|
||||
bool scam = rng.Chance(Tuning.BmScamSellerShare);
|
||||
// 骗子多半是新号,也有刷过好评的老号
|
||||
bool fresh = scam ? rng.Chance(0.6) : rng.Chance(0.25);
|
||||
var s = new BmSeller
|
||||
{
|
||||
Name = NameA[rng.Range(0, NameA.Length - 1)] + "_" + NameB[rng.Range(0, NameB.Length - 1)] + rng.Range(1, 99),
|
||||
Scam = scam,
|
||||
Trades = fresh ? rng.Range(1, 12) : rng.Range(25, 220),
|
||||
Rating = scam && !fresh ? rng.Range(94, 99) : fresh ? rng.Range(70, 100) : rng.Range(90, 99),
|
||||
};
|
||||
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) });
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (l == null || asset == Assets.CRC || Closed(day)) return null;
|
||||
var seller = Sellers[l.Seller];
|
||||
if (mode == PayMode.Advance && !seller.Advance) return null;
|
||||
double p = price(asset);
|
||||
if (p <= 0) return null;
|
||||
int credits = Pay(l.Price, mode);
|
||||
double qty = credits / p;
|
||||
if (w.Avail(asset) + 1e-9 < qty) return null;
|
||||
double total = w.Value(price);
|
||||
double share = total > 0 ? Math.Min(1, credits / total) : 0;
|
||||
double taint = w.Taint * share, basis = w.Basis * share;
|
||||
w.Taint -= taint; w.Basis -= basis;
|
||||
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),
|
||||
};
|
||||
Orders.Add(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
private static void Refund(Wallet w, BmOrder o)
|
||||
{
|
||||
w.Add(o.Asset, o.Qty);
|
||||
w.Taint += o.Taint; w.Basis += o.Basis;
|
||||
}
|
||||
|
||||
/// <summary>每晚推进订单:平台跑路、到期的订单(卖家跑路、被截获、死投被挖走)、转入可取状态。镇压期间到期顺延一天。</summary>
|
||||
public List<BmEvent> Night(int day, bool crackdown, Wallet w, Rng rng)
|
||||
{
|
||||
var ev = new List<BmEvent>();
|
||||
if (ClosedUntil == day) ev.Add(new BmEvent { Kind = "reopen" });
|
||||
if (!Closed(day) && Orders.Any(o => o.State == OrderState.Pending) && 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 });
|
||||
}
|
||||
ClosedUntil = day + Tuning.BmRugClosedDays;
|
||||
Offers.Clear(); Catalog.Clear(); Sellers.Clear();
|
||||
return ev;
|
||||
}
|
||||
foreach (var o in Orders.Where(x => x.State == OrderState.Pending).ToList())
|
||||
{
|
||||
if (day < o.DueDay) continue;
|
||||
if (crackdown) { o.DueDay = day + 1; continue; }
|
||||
if (o.SellerScam)
|
||||
{
|
||||
if (o.Mode == PayMode.Escrow) { Refund(w, o); o.State = OrderState.Refunded; o.Why = "scam"; ev.Add(new BmEvent { Kind = "scam_refund", Order = o, Amount = o.Price }); }
|
||||
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);
|
||||
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 });
|
||||
}
|
||||
Orders.RemoveAll(o => o.State != OrderState.Pending && o.State != OrderState.Ready && day - o.DueDay > 14);
|
||||
return ev;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,11 @@ 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 int BmUnlockPrice = 1500;
|
||||
/// <summary>单个金融违规事件记入的罪证上限。</summary>
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
using System.Linq;
|
||||
using Decentralized.Domain;
|
||||
|
||||
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)
|
||||
{
|
||||
var it = ModHelper.SpawnItem(id);
|
||||
if (it == null) return 0;
|
||||
long v = it.GetBaseValue();
|
||||
it.Destroy();
|
||||
return v;
|
||||
}
|
||||
|
||||
public static string ItemName(string id)
|
||||
{
|
||||
var it = ModHelper.SpawnItem(id);
|
||||
if (it == null) return id;
|
||||
string n = it.name;
|
||||
it.Destroy();
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>每周刷新:现货与订单目录一起。返回是否刷新了。</summary>
|
||||
public static bool Restock(SaveData d, int day, bool force = false)
|
||||
{
|
||||
int rep = StoreReputation.GetBMReputation();
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
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}");
|
||||
switch (e.Kind)
|
||||
{
|
||||
case "ready": ps.AddNightLog(L.T("nightlog.bm.ready." + e.Order.Delivery, 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;
|
||||
}
|
||||
if (e.Kind == "rug") Night_RugNews(day);
|
||||
}
|
||||
if (Unlock.BlackUnlocked && !crackdown) Restock(d, day);
|
||||
}
|
||||
|
||||
private static void Night_RugNews(int day) => News.Show("pd_news_bmrug_" + day, L.T("news.bmrug.title"), L.T("news.bmrug.desc"), 4);
|
||||
|
||||
// ---- 上门:开门后有到货的当天,快递员排进顾客队伍 ----
|
||||
private static int courierDay = -1;
|
||||
|
||||
private static void OnGenerate(StoreClientManager manager)
|
||||
{
|
||||
if (Session.Data == null || !Unlock.BlackUnlocked) return;
|
||||
Log.Guard("黑市快递员", () =>
|
||||
{
|
||||
int day = StoreStation.GetDayCounter();
|
||||
if (day == courierDay || !Session.Data.Black.ReadyFor(Delivery.Door).Any()) return;
|
||||
courierDay = day;
|
||||
manager.AddClient(CreateCourier());
|
||||
});
|
||||
}
|
||||
|
||||
public static StoreClient CreateCourier()
|
||||
{
|
||||
var rng = Session.Data.Rng;
|
||||
var c = new StoreClient
|
||||
{
|
||||
identifier = "pd_bm_courier",
|
||||
displayName = L.T("courier.name"),
|
||||
spriteName = SpriteDict.GetRandomScavSpriteName(),
|
||||
clientIntent = StoreClient.ClientIntent.SELL,
|
||||
};
|
||||
StoreClientFactionSetup.InitScav(c);
|
||||
c.mainDialogue.SetText(c.displayName, L.T("courier.line1." + rng.Range(1, 2))).NextDialogue().SetText(c.displayName, L.T("courier.line2." + rng.Range(1, 2)))
|
||||
.SetEndAction(() => Log.Guard("黑市快递交货", () => { DeliverDoor(); PlayerStore.Instance.DismissCurrentClient(); }));
|
||||
c.AddBasicDialog();
|
||||
c.CompleteClientCreation();
|
||||
return c;
|
||||
}
|
||||
|
||||
private static void DeliverDoor()
|
||||
{
|
||||
var d = Session.Data;
|
||||
foreach (var o in d.Black.ReadyFor(Delivery.Door).ToList())
|
||||
{
|
||||
Items.Give(ModHelper.SpawnItem(o.Item));
|
||||
d.Black.Deliver(o);
|
||||
Log.Info($"黑市快递:{o.Item} 已放进储藏区");
|
||||
}
|
||||
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(); }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,20 +51,20 @@ namespace Decentralized
|
||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Black market +50 rep, restock (test)", () => Log.Guard("黑市(测试)", () =>
|
||||
{
|
||||
StoreReputation.ModReputation("FACTION_BLACK_MARKET", 50);
|
||||
if (!Unlock.IsUnlocked) ModHelper.GetNetworkUpgrade(Unlock.Id)?.Unlock();
|
||||
if (!Unlock.BlackUnlocked) ModHelper.GetNetworkUpgrade(Unlock.BlackId)?.Unlock();
|
||||
var d = Session.Data;
|
||||
d.Black.RefreshedDay = -999;
|
||||
d.Hot.Add(Assets.PSC, 20); d.Hot.Add(Assets.CRT, 3000); d.Hot.Taint += 2000; d.Hot.Basis += 1000;
|
||||
d.Black.Refresh(StoreStation.GetDayCounter(), StoreReputation.GetBMReputation(), d.Rng, id =>
|
||||
{
|
||||
var it = ModHelper.SpawnItem(id);
|
||||
if (it == null) return 0;
|
||||
long v = it.GetBaseValue();
|
||||
it.Destroy();
|
||||
return v;
|
||||
});
|
||||
BlackDelivery.Restock(d, StoreStation.GetDayCounter(), true);
|
||||
Log.Info($"黑市:声望 {StoreReputation.GetBMReputation()},上架 {d.Black.Offers.Count} 种:{string.Join(", ", d.Black.Offers.Select(o => o.Item + "×" + o.Qty + "@" + o.Price))},解锁 {Unlock.BlackUnlocked}");
|
||||
}));
|
||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Courier (test)", () => Log.Guard("黑市快递(测试)", () =>
|
||||
{
|
||||
foreach (var o in Session.Data.Black.Orders.Where(x => x.State == OrderState.Pending)) { o.State = OrderState.Ready; }
|
||||
PlayerStore.Instance.currentClientInstance = null;
|
||||
PlayerStore.Instance.storeClientManager.clientStack.Insert(0, BlackDelivery.CreateCourier());
|
||||
PlayerStore.Instance.TryCallNextClient();
|
||||
}));
|
||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Coin buyer (test)", () => Log.Guard("用币付款顾客(测试)", () =>
|
||||
{
|
||||
var c = new StoreClient
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Decentralized
|
||||
DealerStock.Subscribe();
|
||||
UsedDealerClient.Subscribe();
|
||||
CoinCustomers.Subscribe();
|
||||
BlackDelivery.Subscribe();
|
||||
GpuInspectView.Ensure();
|
||||
ModHook.OnGameLoadedLate += Session.Load;
|
||||
ModHook.OnNewGameStarted += OnNewGame;
|
||||
@@ -30,6 +31,7 @@ namespace Decentralized
|
||||
DealerStock.Unsubscribe();
|
||||
UsedDealerClient.Unsubscribe();
|
||||
CoinCustomers.Unsubscribe();
|
||||
BlackDelivery.Unsubscribe();
|
||||
GpuInspectView.Remove();
|
||||
ModHook.OnGameLoadedLate -= Session.Load;
|
||||
ModHook.OnNewGameStarted -= OnNewGame;
|
||||
|
||||
@@ -230,3 +230,50 @@ black.desk.in,Into Ponero
|
||||
black.desk.out,Out of Ponero
|
||||
black.desk.go,Swap
|
||||
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.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.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.state.Pending,in transit
|
||||
black.state.Ready,arrived
|
||||
black.state.Done,picked up
|
||||
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
|
||||
news.bmrug.desc,"The black market's on-chain entrance went dark overnight, wiping out all escrow and advance funds. A new entrance will appear in a few days."
|
||||
courier.name,Courier
|
||||
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."
|
||||
|
@@ -229,3 +229,50 @@ black.desk.in,兑入 Ponero
|
||||
black.desk.out,兑出 Ponero
|
||||
black.desk.go,兑换
|
||||
black.desk.fail,兑换失败:余额、额度或数量不对。
|
||||
black.sub.orders,订单
|
||||
black.sub.mine,我的订单与情报
|
||||
black.rugged,"平台出事了,入口关闭,还要 {0} 天才会有人重新开张。"
|
||||
black.seller,"卖家 {0} 成交 {1} 好评 {2}%"
|
||||
black.orders.h,订单目录
|
||||
black.listing,"{0} 实付 {1} 信用点等值"
|
||||
black.order,下单
|
||||
black.noadvance,这个卖家不接受提前放款
|
||||
black.orders.hint,"比现货便宜,但要等几天,|路上可能出事。|托管:多付 {0}% 托管费,|收货后放款,卖家跑路时退款。|提前放款:再打 {1}% 折,|卖家若是骗子钱就没了;|成交 ≥ {2} 的卖家才接受。|平台自己也可能跑路,|托管和提前放款的钱都在它手里。"
|
||||
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.state.Pending,在途
|
||||
black.state.Ready,到货
|
||||
black.state.Done,已取
|
||||
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,黑市平台跑路
|
||||
news.bmrug.desc,"黑市的链上入口一夜之间下线,托管和提前放款的资金全部清零。几天后会有新的入口。"
|
||||
courier.name,快递员
|
||||
courier.line1.1,"东西到了。"
|
||||
courier.line1.2,"有你的包裹。"
|
||||
courier.line2.1,"别问我里面是什么,我也不想知道。"
|
||||
courier.line2.2,"货放这儿了,我走了。"
|
||||
|
@@ -101,15 +101,7 @@ namespace Decentralized
|
||||
foreach (var t in d.Hot.Thaw(day)) PlayerStore.Instance.AddNightLog(L.T("nightlog.thaw", Fmt.Num(t.Value), t.Key));
|
||||
if (!wasSanctioned && d.Mixer.Sanctioned(day) && Unlock.MixerUnlocked) News.Show("pd_news_sanction_" + day, L.T("news.sanction.title"), L.T("news.sanction.desc"), 3);
|
||||
d.ActiveEvents = events;
|
||||
if (Unlock.BlackUnlocked && !events.Contains(MarketEvent.Crackdown))
|
||||
d.Black.Refresh(day, StoreReputation.GetBMReputation(), d.Rng, id =>
|
||||
{
|
||||
var it = ModHelper.SpawnItem(id);
|
||||
if (it == null) return 0;
|
||||
long v = it.GetBaseValue();
|
||||
it.Destroy();
|
||||
return v;
|
||||
});
|
||||
if (Unlock.BlackUnlocked) BlackDelivery.Night(d, day, events.Contains(MarketEvent.Crackdown));
|
||||
d.Network.Availability = (events.Contains(MarketEvent.Blackout) ? 0.8 : 1) * (events.Contains(MarketEvent.LowerUnrest) ? 0.9 : 1);
|
||||
|
||||
var units = Session.TonightCards.Select(c => (item: c.Key, unit: Items.ToUnit(c.Key), bonus: c.Value)).ToList();
|
||||
|
||||
@@ -264,7 +264,10 @@ namespace Decentralized
|
||||
});
|
||||
}
|
||||
|
||||
private static bool _blackDesk;
|
||||
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;
|
||||
@@ -278,14 +281,19 @@ namespace Decentralized
|
||||
if (d.ActiveEvents.Contains(MarketEvent.Crackdown)) { b.AddLabel(L.T("black.offline")); return; }
|
||||
if (!BlackMarket.Open(rep)) { b.AddLabel(L.T("black.closed", Tuning.BmOpenRep, rep)); return; }
|
||||
bool desk = BlackMarket.DeskOpen(rep);
|
||||
if (desk)
|
||||
{
|
||||
b.BeginRow();
|
||||
b.AddButton(_blackDesk ? L.T("black.sub.shop") : $"[{L.T("black.sub.shop")}]", () => { _blackDesk = false; _message = null; Open(); });
|
||||
b.AddButton(_blackDesk ? $"[{L.T("black.sub.desk")}]" : L.T("black.sub.desk"), () => { _blackDesk = true; _message = null; Open(); });
|
||||
b.End();
|
||||
foreach (BlackTab t in Enum.GetValues(typeof(BlackTab)))
|
||||
{
|
||||
if (t == BlackTab.Desk && !desk) continue;
|
||||
var tab = t;
|
||||
string name = L.T("black.sub." + t.ToString().ToLowerInvariant());
|
||||
b.AddButton(tab == _blackTab ? $"[{name}]" : name, () => { _blackTab = tab; _message = null; Open(); });
|
||||
}
|
||||
if (desk && _blackDesk) { DeskPage(b, rep, day); return; }
|
||||
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; }
|
||||
var w = d.Hot;
|
||||
Columns(b, l =>
|
||||
{
|
||||
@@ -314,14 +322,7 @@ namespace Decentralized
|
||||
});
|
||||
}
|
||||
|
||||
private static string ItemName(string id)
|
||||
{
|
||||
var it = ModHelper.SpawnItem(id);
|
||||
if (it == null) return id;
|
||||
string n = it.name;
|
||||
it.Destroy();
|
||||
return n;
|
||||
}
|
||||
private static string ItemName(string id) => BlackDelivery.ItemName(id);
|
||||
|
||||
private static void BuyOffer(BmOffer o)
|
||||
{
|
||||
@@ -333,6 +334,87 @@ namespace Decentralized
|
||||
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);
|
||||
if (o == null) { _message = L.T("msg.balance"); Open(); return; }
|
||||
_message = L.T("black.ordered", ItemName(o.Item), o.DueDay);
|
||||
Session.Save();
|
||||
Open();
|
||||
}
|
||||
|
||||
/// <summary>我的订单与论坛情报。</summary>
|
||||
private static void MinePage(CustomUIBuilder b, int day)
|
||||
{
|
||||
var d = Session.Data;
|
||||
Columns(b, l =>
|
||||
{
|
||||
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") : ""));
|
||||
}, r =>
|
||||
{
|
||||
r.AddHeader(L.T("black.forum.h"));
|
||||
if (d.Black.Forum.Count == 0) r.AddLabel(L.T("black.forum.quiet"));
|
||||
foreach (var f in d.Black.Forum)
|
||||
r.AddLabel(f.StartsWith("scam:") ? L.T("black.forum.scam", f.Substring(5)) : L.T("black.forum." + f));
|
||||
});
|
||||
}
|
||||
|
||||
private static void DeskPage(CustomUIBuilder b, int rep, int day)
|
||||
{
|
||||
var d = Session.Data;
|
||||
|
||||
@@ -9,6 +9,8 @@ internal static class Program
|
||||
{
|
||||
private static int _failed, _passed;
|
||||
|
||||
private static double BlackMarketTestRug() => Tuning.BmRugBase * 3;
|
||||
|
||||
private static void Check(bool ok, string what)
|
||||
{
|
||||
if (ok) _passed++;
|
||||
@@ -583,6 +585,55 @@ internal static class Program
|
||||
Check(Near(back, 10 * 50 * (1 - Tuning.PoneroFee), 1e-9) && Near(w.Get(Assets.CRT), back, 1e-9), "兑出 Ponero");
|
||||
});
|
||||
|
||||
Test("黑市订单", () =>
|
||||
{
|
||||
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)");
|
||||
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 };
|
||||
bm.Sellers.Clear(); bm.Sellers.Add(good); bm.Sellers.Add(bad);
|
||||
var lg = new BmListing { Seller = 0, Item = "x", Price = 1000 };
|
||||
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");
|
||||
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));
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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, "关闭期间不能下单");
|
||||
});
|
||||
|
||||
Console.WriteLine($"\n{_passed} 通过,{_failed} 失败");
|
||||
return _failed == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user