diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2278215..4fef2a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@
- 收尾核算(`research/sim/bm_econ.py`,按 Tuning 常量算期望):Ponero 来回 15.4%,与混币器洗掉 80% 污点的 15–18% 相当,但当天完成、清得彻底、不受制裁,所以手续费 8% → 10%(来回 19%),日额度 1000 → 600(日额度按钱包污点占比估,约够清掉稳态的污点流入,后期会成为瓶颈);出售的最优倍率取决于玩家的资金机会成本:每天 1% 时 ×1.8,2–3% 时 ×1.5,倍率不是越高越好;订单:托管期望净 +0.02、提前放款 +0.07(现货 −0.30),所以订单是便宜进货,不是赚钱路线;订单进货再挂高价出售的套利每件约 +0.13–0.18(20 天一轮,资金占用),量小,暂不限制上架数。
- 订单改成商家货架(用户意见):黑市页"订单"改名"商家",每周从 6 种类型里抽 5 家(全新矿卡经销商、二手矿卡贩子、模组高科技、军火、证件芯片、药物),另有 25% 的周出现一家大宗商家(库存 10–25 件、价格系数 0.55–0.75、送货 6 天)。每家有自己的价格系数(订单价 = 基础价 × 系数,一般 0.70–0.95,全新卡 0.85–1.00,二手卡 0.65–0.90),每家 4 种货、库存 3–6 件。一次订 5 件以上 95 折、10 件以上 9 折。界面:件数按钮 ×1 / ×5 / ×10,商家按钮,所选商家的货架。药物(药片、梦尘、管制化学品)只在药物商家和大宗商家出现,也能转手出售。每周货架总值在游戏里抽样 60 周平均 13.2k(目标 1.4 万)。
+- 商家类型扩到 18 种 + 大宗(用户意见,对照游戏的物品目录和玩家职业):全新矿卡经销商、二手矿卡贩子、矿架与能源、模组、军火(枪、手雷、炸药、刀具、电击枪)、枪匠配件(枪械配件、3D 打印芯片与模组、枪械零件)、证件芯片、药物(药片、注射器、血袋、手术工具)、化学品(化学用品、助熔剂、化学设备模组、量筒试剂瓶)、酿酒(酵母、荧光莓、啤酒、酒瓶)、水培农场(种子、水培模组、植物分析仪)、养殖(鱼卵、鱼子酱、循环水养殖系统、血清、屠宰机)、矿工(矿石、金属锭、电子元件、拾荒扫描仪)、水务(滤水、净水、储水、检测)、仓储(走私暗格、储藏区扩建、军用背包)、工具(涡轮增压器、等离子切割机、X 射线扫描仪、心智重编程器)、奢侈品(香水、精华液、香烟)。每周 7 家:2 家从矿卡 / 矿架 / 模组 4 类里抽(保证矿工路线总有货),其余 5 家从其余类型里抽;每家 3 种货,库存 2–5 件,单件基础价超过 4000 的不上。游戏里抽样 60 周货架总值平均 13.2k(目标 1.4 万)。药物改成并入药物商家,现货柜台的货不变。所有货都能转手出售。
- 同时上架最多 10 件(用户意见),出售页显示"上架 n/10",超出时提示。
- 出售:货款污点随近 7 天成交额加重(成交额达 1.4 万时概率与比例各加 30%);出售页加"全部上架"按钮。套利核算(`bm_econ.py`,商家系数均值 0.825 再加批量折扣):普通商家提前放款进货 + ×1.5 出售每件约 +21%,大宗商家约 +33%,一周可买的货约 1.3 万,带污点的货款约够吃满 Ponero 日额度。
- 调整(用户意见):Ponero 手续费 10% → 6%(一进一出约 12%),日额度 600 → 1000;混币器每轮手续费 4% → 3%。黑市货架上的显卡有 15% 是全新卡(成色 100,封条完好,不会是假卡),标签显示"全新"。全新卡只有单元测试,没有在游戏里见过。
diff --git a/src/Decentralized.Domain/BlackMarket.cs b/src/Decentralized.Domain/BlackMarket.cs
index e4cb754..460dabe 100644
--- a/src/Decentralized.Domain/BlackMarket.cs
+++ b/src/Decentralized.Domain/BlackMarket.cs
@@ -5,7 +5,7 @@ using System.Linq;
namespace Decentralized.Domain
{
/// 货的种类:违禁品、高科技物品、显卡。
- public enum GoodKind { Contraband, Tech, Gpu }
+ public enum GoodKind { Contraband, Tech, Gpu, Goods }
/// 黑市货架上的一种货。显卡的 Item 是 "gpu:型号",Model 是标称型号,FakeOf 非空表示背后是别的芯片(买家看不到)。
public class BmGood
@@ -47,12 +47,6 @@ namespace Decentralized.Domain
P("shotgun", 3), P("smg", 3), P("c4", 3), P("cmd_keycard", 3), P("crypto_module_sec", 3),
};
- /// 药品池:只有药物商家卖,也能转手出售。
- public static readonly KeyValuePair[] PharmaPool =
- {
- P("unlicensed_phagimycin_pill", 2), P("dream_dust", 2), P("restricted_chemical", 2), P("fanta_pill", 3), P("nightmare_dust", 3),
- };
-
/// 高科技物品池(不是违禁品,黑市上要加价):矿机能用上的系统模组、能量电池、能量阵列,和矿架本身。
public static readonly string[] TechPool =
{
diff --git a/src/Decentralized.Domain/BmMerchants.cs b/src/Decentralized.Domain/BmMerchants.cs
new file mode 100644
index 0000000..6f1938e
--- /dev/null
+++ b/src/Decentralized.Domain/BmMerchants.cs
@@ -0,0 +1,70 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Decentralized.Domain
+{
+ /// 商家货架上的一项货:游戏物品 id、货的种类、违禁等级(只对违禁品有意义,随黑市声望放开)。
+ public struct BmEntry
+ {
+ public string Id;
+ public GoodKind Kind;
+ public int Level;
+ }
+
+ ///
+ /// 商家类型的货表。除了矿卡,其余都按玩家能走的路线配货:矿工、酿酒师、化学师、药剂师、枪械师、水培农场主、养殖户、水务、仓储、工具、奢侈品,
+ /// 加上违禁品(军火、证件芯片)。大宗商家什么都卖。
+ ///
+ public static class Merchants
+ {
+ private static BmEntry C(string id, int level) => new BmEntry { Id = id, Kind = GoodKind.Contraband, Level = level };
+ private static BmEntry T(string id) => new BmEntry { Id = id, Kind = GoodKind.Tech, Level = 3 };
+ private static BmEntry G(string id) => new BmEntry { Id = id, Kind = GoodKind.Goods, Level = 0 };
+ private static BmEntry[] Gs(params string[] ids) => ids.Select(G).ToArray();
+
+ public static readonly Dictionary Table = new Dictionary
+ {
+ [MerchantType.Rigs] = new[] { T("pd_rig"), T("energy_credit_ext"), T("energy_credit_hyp"), T("energy_credit_breeder"), T("powerblock"), T("recharger"), T("current_converter") },
+ [MerchantType.Modules] = new[] { T("system_module_overclock"), T("system_module_performance"), T("system_module_quality"), T("system_module_eco"), T("system_module_efficiancy"),
+ T("system_module_fineness"), T("system_module_streamlining"), T("module_extractor_advanced"), T("module_bay_expansion_kit") },
+ [MerchantType.Arms] = new[] { C("handmade_pistol", 1), C("printed_gun", 1), C("stun_baton", 1), C("revolver", 2), C("heavy_handmade_pistol", 2), C("flashbang_grenade", 2), C("smoke_grenade", 2),
+ C("shotgun", 3), C("smg", 3), C("c4", 3), G("combat_knife"), G("combat_machete"), G("hatchet"), G("crowbar"), G("kitchen_cleaver"), G("skinning_knife"), G("stun_gun"), G("tazer") },
+ [MerchantType.GunMods] = Gs("silencer2", "laser2", "match_barrel", "trigger_group", "mag_10mm_high", "mag_22_high", "mag_10mm_smg_high", "rds", "compensator", "stock", "grip", "bipod", "barrel",
+ "printer_chip_gun_part", "printer_chip_silencer", "printer_chip_handmade_pistol_frame", "printer_chip_heavy_handmade_pistol_frame", "printer_module_cnc", "printer_module_metal", "gun_part", "printer_plastic"),
+ [MerchantType.Docs] = new[] { C("eng_keycard", 1), C("med_keycard", 1), C("ser_keycard", 1), C("sup_keycard", 1), C("crypto_module_ser", 1), C("crypto_module_sup", 1),
+ C("sci_keycard", 2), C("sec_keycard", 2), C("crypto_module_eng", 2), C("crypto_module_med", 2), C("cmd_keycard", 3), C("crypto_module_sec", 3) },
+ [MerchantType.Pharma] = new[] { C("unlicensed_phagimycin_pill", 2), C("dream_dust", 2), C("fanta_pill", 3), C("nightmare_dust", 3), C("zyanide_pill", 2),
+ G("black_injector"), G("pure_white_injector"), G("large_purple_injector"), G("pink_injector"), G("pink_alt_injector"), G("blue_blood_bag"), G("surgery_tool"), G("zerochew"), G("phagimycin_pill") },
+ [MerchantType.Chem] = new[] { C("restricted_chemical", 2), G("common_chemical"), G("flux_agent"), G("advanced_flux_agent"), G("chem_module"), G("evaporator_module_solvent_recovery"),
+ G("chem_scanner"), G("med_bottle_blue"), G("med_bottle_orange"), G("med_bottle_red"), G("med_bottle_small_pink"), G("med_bottle_violet"), G("100ml_cylinder"), G("5ml_dropper") },
+ [MerchantType.Brewer] = new[] { G("wine_yeast"), G("wine_superyeast"), G("wine_yeast_infinite"), C("wine_yeast_red", 1), G("wine_berry"), G("bloomberry_seed"), G("nudka"), G("red_beer"), G("beerCase"), G("wine_bottle") },
+ [MerchantType.Farm] = new[] { G("nutrifruit_seed"), G("kotton_seed"), G("fillerweed_seed"), G("hydroreed_seed"), G("bloomberry_seed"), C("dream_cap_seed", 1),
+ G("farm_module_nutrifruit"), G("farm_module_kotton_loom"), G("farm_module_fillerweed_production"), G("farm_module_hydroreed_filter"), G("farm_module_dream_cap"), G("farm_module_smuggler"),
+ G("plant_analyser"), G("plant_dna_disk"), G("nutrient_tablet"), G("hydroreed_filter") },
+ [MerchantType.Husbandry] = Gs("tilapia_roe", "sturgeon_roe", "caviar", "ras", "serum_green", "serum_red",
+ "livestock_feed_dispenser", "auto_butcher", "industrial_fish_feed", "animal_cage"),
+ [MerchantType.Miner] = Gs("common_ore", "rare_ore", "metal_ingot", "scrap_metal", "common_electronic", "rare_electronic", "hypertech_electronic", "metal_scanner", "wire", "advanced_flux_agent"),
+ [MerchantType.Water] = Gs("water_filter_adv", "portable_water_purifier", "water_tank", "uv_filter", "water_tablet_container", "water_test_container", "aquascan", "bottled_water_premium", "water_jug", "water_ration"),
+ [MerchantType.Storage] = Gs("smuggler_bay", "smuggler_bay_mod", "smuggler_bay_mini", "storage_bay_large", "storage_bay_expansion_kit", "hypertech_bay", "machine_bay_ext", "storage_bay_chem",
+ "storage_bay_gun", "backpack_large_military"),
+ [MerchantType.Tools] = Gs("turbo_booster", "turbo_booster_adv", "turbo_booster_hypertech", "welder", "xray_scanner", "magnifier", "labeler_blue", "toolbox", "wire_cutter", "mind_reprogrammer"),
+ [MerchantType.Luxury] = new[] { C("pheromone_perfume", 2), G("neuroactive_perfume"), G("self_refilling_neuroactive_perfume"), G("skincare_cream"), G("packet_red_cigarette"), G("gamblers_coin"), G("kotton_fabric"), G("armor_lining") },
+ };
+
+ private static readonly Dictionary ById = BuildIndex();
+
+ private static Dictionary BuildIndex()
+ {
+ var d = new Dictionary();
+ foreach (var e in Table.Values.SelectMany(x => x)) d[e.Id] = e;
+ foreach (var kv in BlackMarket.Pool) d[kv.Key] = new BmEntry { Id = kv.Key, Kind = GoodKind.Contraband, Level = kv.Value };
+ foreach (var id in BlackMarket.TechPool) d[id] = new BmEntry { Id = id, Kind = GoodKind.Tech, Level = 3 };
+ return d;
+ }
+
+ /// 这个物品 id 在黑市上是不是一件货(现货柜台、商家货架里出现过的,都能转手出售)。
+ public static bool IsGood(string id) => ById.ContainsKey(id);
+
+ public static BmEntry Info(string id) => ById[id];
+ }
+}
diff --git a/src/Decentralized.Domain/BmOrders.cs b/src/Decentralized.Domain/BmOrders.cs
index 0a6f9e7..16197ac 100644
--- a/src/Decentralized.Domain/BmOrders.cs
+++ b/src/Decentralized.Domain/BmOrders.cs
@@ -5,8 +5,8 @@ using System.Linq;
namespace Decentralized.Domain
{
public enum PayMode { Escrow, Advance }
- /// 商家类型:全新矿卡经销商、二手矿卡贩子、模组高科技、军火、证件芯片、药物,大宗商家随机出现。
- public enum MerchantType { GpuNew, GpuUsed, Mods, Arms, Docs, Pharma, Bulk }
+ /// 商家类型:矿卡(全新 / 二手)、矿架能源、模组、军火、枪匠配件、证件芯片、药物、化学、酿酒、水培、养殖、矿工、水务、仓储、工具、奢侈品;大宗商家随机出现。
+ public enum MerchantType { GpuNew, GpuUsed, Rigs, Modules, Arms, GunMods, Docs, Pharma, Chem, Brewer, Farm, Husbandry, Miner, Water, Storage, Tools, Luxury, Bulk }
public enum OrderState { Pending, Ready, Done, Refunded, Lost }
/// 黑市上的卖家:成交数与好评率公开;是不是骗子是隐藏的。
@@ -104,33 +104,28 @@ namespace Decentralized.Domain
public double RugChance => Math.Min(Tuning.BmRugMax, Tuning.BmRugBase + Held / 10000.0 * Tuning.BmRugPerTenK);
- private static readonly MerchantType[] Regular = { MerchantType.GpuNew, MerchantType.GpuUsed, MerchantType.Mods, MerchantType.Arms, MerchantType.Docs, MerchantType.Pharma };
+ /// 每周必有的"矿工路线"商家类型(从中抽 2 家),其余按全部类型抽。
+ private static readonly MerchantType[] Core = { MerchantType.GpuNew, MerchantType.GpuUsed, MerchantType.Rigs, MerchantType.Modules };
+ private static readonly MerchantType[] Others = { MerchantType.Arms, MerchantType.GunMods, MerchantType.Docs, MerchantType.Pharma, MerchantType.Chem, MerchantType.Brewer,
+ MerchantType.Farm, MerchantType.Husbandry, MerchantType.Miner, MerchantType.Water, MerchantType.Storage, MerchantType.Tools, MerchantType.Luxury };
private static double F(Rng rng, double lo, double hi) => lo + rng.NextDouble() * (hi - lo);
- /// 商家类型对应的货(含货种、库存),按声望放开违禁等级。
+ /// 商家类型对应的货(含货种、库存),违禁品按声望放开等级。
private List MerchantGoods(int idx, BmSeller s, int day, int rep, Rng rng, Func value)
{
var list = new List();
int n = s.Type == MerchantType.Bulk ? 2 : Tuning.BmMerchantListings;
- var contra = new List>();
- switch (s.Type)
- {
- case MerchantType.Arms: contra = Pool.Where(x => ArmsIds.Contains(x.Key)).ToList(); break;
- case MerchantType.Docs: contra = Pool.Where(x => !ArmsIds.Contains(x.Key)).ToList(); break;
- case MerchantType.Pharma: contra = PharmaPool.ToList(); break;
- case MerchantType.Bulk: contra = Pool.Concat(PharmaPool).ToList(); break;
- }
- contra = contra.Where(x => x.Value <= MaxLevel(rep)).ToList();
- var tech = TechPool.ToList();
+ bool gpuKind = s.Type == MerchantType.GpuNew || s.Type == MerchantType.GpuUsed;
+ var entries = s.Type == MerchantType.Bulk ? Merchants.Table.Values.SelectMany(x => x).GroupBy(x => x.Id).Select(g => g.First()).ToList()
+ : gpuKind ? new List() : Merchants.Table[s.Type].ToList();
+ entries = entries.Where(e => e.Kind != GoodKind.Contraband || e.Level <= MaxLevel(rep)).ToList();
var gpus = UsedDealers.Pool(day);
for (int i = 0; i < n; i++)
{
int qty = s.Type == MerchantType.Bulk ? rng.Range(Tuning.BmBulkQtyMin, Tuning.BmBulkQtyMax) : s.Type == MerchantType.GpuNew ? rng.Range(Math.Max(1, Tuning.BmQtyMin - 1), Tuning.BmQtyMax - 2) : rng.Range(Tuning.BmQtyMin, Tuning.BmQtyMax);
double jitter = F(rng, 0.97, 1.03);
- BmListing l = null;
- bool gpuKind = s.Type == MerchantType.GpuNew || s.Type == MerchantType.GpuUsed;
- if (s.Type == MerchantType.Bulk) gpuKind = false;
+ BmListing l;
if (gpuKind)
{
bool fresh = s.Type == MerchantType.GpuNew;
@@ -138,23 +133,14 @@ namespace Decentralized.Domain
if (fresh) c.Health = 100;
l = new BmListing { Item = "gpu:" + c.Model, Kind = GoodKind.Gpu, Level = 2, Model = c.Model, FakeOf = c.FakeOf, Health = c.Health, Price = (int)Math.Max(1, Math.Round(CardValue.Of(c.Model, c.Health) * s.Factor * jitter)) };
}
- else if (s.Type == MerchantType.Mods || (s.Type == MerchantType.Bulk && tech.Count > 0 && (contra.Count == 0 || rng.Chance(0.4))))
- {
- if (tech.Count == 0) continue;
- int k = rng.Range(0, tech.Count - 1);
- string id = tech[k]; tech.RemoveAt(k);
- long v = value(id);
- if (v <= 0) continue;
- l = new BmListing { Item = id, Kind = GoodKind.Tech, Level = 3, Price = (int)Math.Max(1, Math.Round(v * s.Factor * jitter)) };
- }
else
{
- if (contra.Count == 0) continue;
- int k = rng.Range(0, contra.Count - 1);
- var pick = contra[k]; contra.RemoveAt(k);
- long v = value(pick.Key);
- if (v <= 0) continue;
- l = new BmListing { Item = pick.Key, Kind = GoodKind.Contraband, Level = pick.Value, Price = (int)Math.Max(1, Math.Round(v * s.Factor * jitter)) };
+ if (entries.Count == 0) break;
+ int k = rng.Range(0, entries.Count - 1);
+ var e = entries[k]; entries.RemoveAt(k);
+ long v = value(e.Id);
+ if (v <= 0 || v > Tuning.BmMaxUnitValue) { i--; continue; }
+ l = new BmListing { Item = e.Id, Kind = e.Kind, Level = e.Level, Price = (int)Math.Max(1, Math.Round(v * s.Factor * jitter)) };
}
l.Seller = idx; l.Qty = qty;
list.Add(l);
@@ -162,13 +148,12 @@ namespace Decentralized.Domain
return list;
}
- private static readonly HashSet ArmsIds = new HashSet { "handmade_pistol", "printed_gun", "stun_baton", "revolver", "heavy_handmade_pistol", "flashbang_grenade", "smoke_grenade", "shotgun", "smg", "c4" };
-
/// 每周重新生成商家、货架和论坛情报(与现货同一天刷新):从 6 种类型里抽 BmMerchantCount 家,另有概率出现一家大宗商家。
public void RefreshOrders(int day, int rep, Rng rng, Func value)
{
Sellers.Clear(); Catalog.Clear(); Forum.Clear();
- var types = Regular.OrderBy(_ => rng.NextDouble()).Take(Tuning.BmMerchantCount).ToList();
+ var types = Core.OrderBy(_ => rng.NextDouble()).Take(Tuning.BmCoreMerchants).ToList();
+ types.AddRange(Others.OrderBy(_ => rng.NextDouble()).Take(Tuning.BmMerchantCount - Tuning.BmCoreMerchants));
if (rng.Chance(Tuning.BmBulkChance)) types.Add(MerchantType.Bulk);
foreach (var t in types)
{
diff --git a/src/Decentralized.Domain/Tuning.cs b/src/Decentralized.Domain/Tuning.cs
index 482e6db..1ebef79 100644
--- a/src/Decentralized.Domain/Tuning.cs
+++ b/src/Decentralized.Domain/Tuning.cs
@@ -106,10 +106,12 @@ namespace Decentralized.Domain
public const double BmSellDaysFast = 1, BmSellDays1 = 2, BmSellDaysMax = 14;
public const double BmSaleTaintChanceMin = 0.2, BmSaleTaintChanceMax = 0.9, BmSaleTaintRatioMin = 0.2, BmSaleTaintRatioMax = 0.8;
public const int BmSaleHold = 3, BmRatingStart = 90, BmRatingFakeLoss = 10, BmMaxListings = 10;
- /// 商家货架:每周的商家数(从 6 种类型里抽)、每家的货种数、每种货的库存范围;大宗商家出现概率、库存范围、送货天数;
+ /// 商家货架:每周的商家数(其中 2 家从矿卡 / 矿架 / 模组 4 类里抽,其余从其他类型里抽)、每家的货种数、每种货的库存范围;大宗商家出现概率、库存范围、送货天数;
/// 各类商家的价格系数范围(订单价 = 基础价 × 系数);批量折扣档(件数、折扣)。
- public const int BmMerchantCount = 5, BmMerchantListings = 4, BmQtyMin = 3, BmQtyMax = 6, BmBulkQtyMin = 10, BmBulkQtyMax = 25, BmBulkDays = 6;
+ public const int BmMerchantCount = 7, BmCoreMerchants = 2, BmMerchantListings = 3, BmQtyMin = 2, BmQtyMax = 5, BmBulkQtyMin = 10, BmBulkQtyMax = 25, BmBulkDays = 6;
public const double BmBulkChance = 0.25, BmFactorMin = 0.70, BmFactorMax = 0.95, BmFactorNewMin = 0.85, BmFactorNewMax = 1.00, BmFactorUsedMin = 0.65, BmFactorUsedMax = 0.90, BmFactorBulkMin = 0.55, BmFactorBulkMax = 0.75;
+ /// 商家货架上单件基础价的上限(过贵的货不上,免得一件把周转量撑歪)。
+ public const long BmMaxUnitValue = 4000;
public const int BmBulk1Count = 5, BmBulk2Count = 10;
public const double BmBulk1Discount = 0.05, BmBulk2Discount = 0.10;
/// 出售量越大货款越容易带污点:近 7 天成交额达到参考量时,污点概率与比例各加最多 BmVolumeTaintMax。
diff --git a/src/Decentralized/BlackDelivery.cs b/src/Decentralized/BlackDelivery.cs
index b11d333..20a0e2a 100644
--- a/src/Decentralized/BlackDelivery.cs
+++ b/src/Decentralized/BlackDelivery.cs
@@ -56,7 +56,7 @@ namespace Decentralized
if (it == null || it.identifier == null) return null;
if (Items.IsGpu(it)) return "gpu:" + Items.ToUnit(it).Model;
string id = it.identifier;
- return BlackMarket.Pool.Any(x => x.Key == id) || BlackMarket.PharmaPool.Any(x => x.Key == id) || BlackMarket.TechPool.Contains(id) ? id : null;
+ return Merchants.IsGood(id) ? id : null;
}
public static System.Collections.Generic.List Sellable() =>
@@ -71,7 +71,7 @@ namespace Decentralized
return Value(id);
}
- public static GoodKind KindOf(string id) => id.StartsWith("gpu:") ? GoodKind.Gpu : BlackMarket.TechPool.Contains(id) ? GoodKind.Tech : GoodKind.Contraband;
+ public static GoodKind KindOf(string id) => id.StartsWith("gpu:") ? GoodKind.Gpu : Merchants.Info(id).Kind;
/// 每周刷新:现货与订单目录一起。返回是否刷新了。
public static bool Restock(SaveData d, int day, bool force = false)
diff --git a/src/Decentralized/CoinCustomers.cs b/src/Decentralized/CoinCustomers.cs
index 89b802c..1ad0d02 100644
--- a/src/Decentralized/CoinCustomers.cs
+++ b/src/Decentralized/CoinCustomers.cs
@@ -69,6 +69,10 @@ namespace Decentralized
BlackDelivery.Restock(d, day, true);
vals.Add(d.Black.CatalogValue);
}
+ var missing = Merchants.Table.SelectMany(kv => kv.Value.Select(e => (kv.Key, e.Id))).Where(x => BlackDelivery.Value(x.Item2) <= 0).Select(x => x.Item1 + ":" + x.Item2).ToList();
+ var costs = Merchants.Table.SelectMany(kv => kv.Value.Select(e => (kv.Key, e.Id))).Select(x => BlackDelivery.Value(x.Item2)).Where(v => v > 0).OrderBy(v => v).ToList();
+ Log.Info("黑市抽样:基础价 >2500 的货:" + string.Join(", ", Merchants.Table.SelectMany(kv => kv.Value.Select(e => (kv.Key, e.Id))).Select(x => (id: x.Item2, v: BlackDelivery.Value(x.Item2))).Where(x => x.v > 2500).Select(x => x.id + "=" + x.v)));
+ Log.Info($"黑市抽样:无法造出或价格为 0 的货 {missing.Count} 个:{string.Join(", ", missing)};基础价中位 {costs[costs.Count / 2]},最高 {costs.Last()}");
Log.Info($"黑市抽样:60 周货架总值 平均 {vals.Average():0},最小 {vals.Min():0},最大 {vals.Max():0}");
}));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Black market events (test)", () => Log.Guard("黑市事件(测试)", () =>
diff --git a/src/Decentralized/ModFiles/Localization/en.csv b/src/Decentralized/ModFiles/Localization/en.csv
index 109cd70..20e1c3a 100644
--- a/src/Decentralized/ModFiles/Localization/en.csv
+++ b/src/Decentralized/ModFiles/Localization/en.csv
@@ -310,7 +310,6 @@ black.sale.Delisted.fake,buyer spotted the fake and returned it
black.kind.gpunew,GPU brand new
merchant.GpuNew,New GPUs
merchant.GpuUsed,Used GPUs
-merchant.Mods,Mods & tech
merchant.Arms,Arms
merchant.Docs,Keycards & chips
merchant.Pharma,Pharma
@@ -320,3 +319,16 @@ black.sell.all,List all
black.sell.listedall,"Listed {0} items."
black.sell.slots,"Listed {0}/{1}"
black.sell.full,"At most {0} listings at a time."
+merchant.Rigs,Rigs & power
+merchant.Modules,Modules
+merchant.GunMods,Gunsmith parts
+merchant.Chem,Chemicals
+merchant.Brewer,Brewing
+merchant.Farm,Hydroponics
+merchant.Husbandry,Husbandry
+merchant.Miner,Mining supplies
+merchant.Water,Water
+merchant.Storage,Storage
+merchant.Tools,Tools
+merchant.Luxury,Luxury
+black.kind.Goods,Goods
diff --git a/src/Decentralized/ModFiles/Localization/zh.csv b/src/Decentralized/ModFiles/Localization/zh.csv
index 4e9c81e..3ef9ee3 100644
--- a/src/Decentralized/ModFiles/Localization/zh.csv
+++ b/src/Decentralized/ModFiles/Localization/zh.csv
@@ -309,7 +309,6 @@ black.sale.Delisted.fake,买家看穿假卡退货
black.kind.gpunew,显卡 全新
merchant.GpuNew,全新矿卡
merchant.GpuUsed,二手矿卡
-merchant.Mods,模组高科技
merchant.Arms,军火
merchant.Docs,证件芯片
merchant.Pharma,药物
@@ -319,3 +318,16 @@ black.sell.all,全部上架
black.sell.listedall,"已上架 {0} 件。"
black.sell.slots,"上架 {0}/{1}"
black.sell.full,"最多同时上架 {0} 件。"
+merchant.Rigs,矿架与能源
+merchant.Modules,模组
+merchant.GunMods,枪匠配件
+merchant.Chem,化学品
+merchant.Brewer,酿酒
+merchant.Farm,水培农场
+merchant.Husbandry,养殖
+merchant.Miner,矿工
+merchant.Water,水务
+merchant.Storage,仓储
+merchant.Tools,工具
+merchant.Luxury,奢侈品
+black.kind.Goods,商品
diff --git a/src/Decentralized/TerminalWindow.cs b/src/Decentralized/TerminalWindow.cs
index f711850..de84183 100644
--- a/src/Decentralized/TerminalWindow.cs
+++ b/src/Decentralized/TerminalWindow.cs
@@ -370,7 +370,7 @@ namespace Decentralized
{
string id = BlackDelivery.SellId(it);
var kind = BlackDelivery.KindOf(id);
- var g = new BmGood { Item = id, Kind = kind, Level = kind == GoodKind.Contraband ? BlackMarket.Pool.Concat(BlackMarket.PharmaPool).First(x => x.Key == id).Value : 3 };
+ var g = new BmGood { Item = id, Kind = kind, Level = kind == GoodKind.Gpu ? 2 : Merchants.Info(id).Level };
if (kind == GoodKind.Gpu) g.Health = Items.ToUnit(it).Health;
g.Price = BlackMarket.ListPriceOf(BlackDelivery.RefValue(it), _sellMult);
return new { g, it };
diff --git a/tests/Decentralized.Domain.Tests/Program.cs b/tests/Decentralized.Domain.Tests/Program.cs
index 6543283..94cbd87 100644
--- a/tests/Decentralized.Domain.Tests/Program.cs
+++ b/tests/Decentralized.Domain.Tests/Program.cs
@@ -737,10 +737,10 @@ internal static class Program
}
double avg = sum / n;
Check(avg > 8000 && avg < 40000, $"每周货架总值(游戏里实测平均约 1.4 万,这里用统一基础价 250):平均 {avg:0}");
- Check(bulk > n * 0.15 && bulk < n * 0.35 && types.Count == 7, $"大宗商家约 {Tuning.BmBulkChance:P0} 的周出现:{bulk}/{n};七种类型都出现过");
+ Check(bulk > n * 0.15 && bulk < n * 0.35 && types.Count >= 18, $"大宗商家约 {Tuning.BmBulkChance:P0} 的周出现:{bulk}/{n};七种类型都出现过");
// 批量下单:折扣、库存扣减
Func price = a => 1;
- var m = new BlackMarket(); m.Sellers.Add(new BmSeller { Name = "a", Trades = 100, Type = MerchantType.Mods, Factor = 0.8 });
+ var m = new BlackMarket(); m.Sellers.Add(new BmSeller { Name = "a", Trades = 100, Type = MerchantType.Modules, Factor = 0.8 });
var l = new BmListing { Seller = 0, Item = "x", Price = 100, Qty = 8 };
m.Catalog.Add(l);
var w = new Wallet(); w.Add(Assets.CRT, 5000);
@@ -748,7 +748,7 @@ internal static class Program
Check(o != null && o.Count == 5 && o.Price == (int)Math.Round(475 * 0.9) && l.Qty == 3 && m.Catalog.Contains(l), "订 5 件:95 折,再按提前放款打折,库存扣 5");
Check(m.Place(l, 4, PayMode.Escrow, w, Assets.CRT, 1, price) == null && m.Place(l, 3, PayMode.Escrow, w, Assets.CRT, 1, price) != null && !m.Catalog.Contains(l), "超过库存不能订;订完下架");
var bs = new BmSeller { Type = MerchantType.Bulk };
- Check(bs.Days == Tuning.BmBulkDays && new BmSeller { Type = MerchantType.Mods }.Days == Tuning.BmDoorDays, "大宗商家送货更久");
+ Check(bs.Days == Tuning.BmBulkDays && new BmSeller { Type = MerchantType.Modules }.Days == Tuning.BmDoorDays, "大宗商家送货更久");
// 出售量推高污点
var b2 = new BlackMarket();
double v0 = b2.VolumeTaint(10);