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:
1 parent
6bc479f710
commit
e44a4439ed
12 files changed
+294
-243
No files matched your search
+4
-1
@@ -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)进储藏区。
|
||||
- 还没做:蜜罐、受控交付(第三批)。
|
||||
|
||||
### 第三阶段:数值调优(进行中)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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."
|
||||
|
@@ -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 兑出的币来路不明,出金审查会加重。"
|
||||
|
@@ -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 })
|
||||
{
|
||||
|
||||
@@ -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 治理社区");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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} 失败");
|
||||
|
||||
Reference in new issue
Block a user