Black market: elite shop for expensive goods, private sellers, weekly catalog value ~20k
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
61ec91b34d
commit
96d8b7cf3b
7 files changed
+88
-34
No files matched your search
@@ -5,8 +5,8 @@ using System.Linq;
|
||||
namespace Decentralized.Domain
|
||||
{
|
||||
public enum PayMode { Escrow, Advance }
|
||||
/// <summary>商家类型:矿卡(全新 / 二手)、矿架能源、模组、军火、枪匠配件、证件芯片、药物、化学、酿酒、水培、养殖、矿工、水务、仓储、工具、奢侈品;大宗商家随机出现。</summary>
|
||||
public enum MerchantType { GpuNew, GpuUsed, Rigs, Modules, Arms, GunMods, Docs, Pharma, Chem, Brewer, Farm, Husbandry, Miner, Water, Storage, Tools, Luxury, Bulk }
|
||||
/// <summary>商家类型:矿卡(全新 / 二手)、矿架能源、模组、军火、枪匠配件、证件芯片、药物、化学、酿酒、水培、养殖、矿工、水务、仓储、工具、奢侈品;大宗商家、尖端店铺(只卖贵货,概率低)、个人卖家(什么都卖,价格随心所欲)随机出现。</summary>
|
||||
public enum MerchantType { GpuNew, GpuUsed, Rigs, Modules, Arms, GunMods, Docs, Pharma, Chem, Brewer, Farm, Husbandry, Miner, Water, Storage, Tools, Luxury, Bulk, Elite, Personal }
|
||||
public enum OrderState { Pending, Ready, Done, Refunded, Lost }
|
||||
|
||||
/// <summary>黑市上的卖家:成交数与好评率公开;是不是骗子是隐藏的。</summary>
|
||||
@@ -19,7 +19,7 @@ namespace Decentralized.Domain
|
||||
public MerchantType Type;
|
||||
/// <summary>商家的价格系数(订单价 = 基础价 × 系数,各家不同)。</summary>
|
||||
public double Factor;
|
||||
public int Days => Type == MerchantType.Bulk ? Tuning.BmBulkDays : Tuning.BmDoorDays;
|
||||
public int Days => Type == MerchantType.Bulk ? Tuning.BmBulkDays : Type == MerchantType.Elite ? Tuning.BmEliteDays : Tuning.BmDoorDays;
|
||||
/// <summary>只有成交数够多的卖家才接受提前放款。</summary>
|
||||
public bool Advance => Trades >= Tuning.BmAdvanceMinTrades;
|
||||
}
|
||||
@@ -111,36 +111,53 @@ namespace Decentralized.Domain
|
||||
|
||||
private static double F(Rng rng, double lo, double hi) => lo + rng.NextDouble() * (hi - lo);
|
||||
|
||||
/// <summary>商家类型对应的货(含货种、库存),违禁品按声望放开等级。</summary>
|
||||
private static List<BmEntry> AllEntries() => Merchants.Table.Values.SelectMany(x => x).GroupBy(x => x.Id).Select(g => g.First()).ToList();
|
||||
|
||||
private static string[] HighGpus => Hardware.Specs.Values.Where(x => !x.Asic && !x.Scrap && x.Tier >= 3).Select(x => x.Model).ToArray();
|
||||
|
||||
/// <summary>商家类型对应的货(含货种、库存),违禁品按声望放开等级。个人卖家什么都卖、每件各自定价;尖端店铺只卖贵货。</summary>
|
||||
private List<BmListing> MerchantGoods(int idx, BmSeller s, int day, int rep, Rng rng, Func<string, long> value)
|
||||
{
|
||||
var list = new List<BmListing>();
|
||||
int n = s.Type == MerchantType.Bulk ? 2 : Tuning.BmMerchantListings;
|
||||
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<BmEntry>() : Merchants.Table[s.Type].ToList();
|
||||
int n = s.Type == MerchantType.Bulk ? 2 : s.Type == MerchantType.Elite ? Tuning.BmEliteListings : s.Type == MerchantType.Personal ? Tuning.BmPersonalListings : Tuning.BmMerchantListings;
|
||||
List<BmEntry> entries = gpuKind ? new List<BmEntry>() : s.Type == MerchantType.Bulk || s.Type == MerchantType.Personal || s.Type == MerchantType.Elite ? AllEntries() : 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;
|
||||
if (gpuKind)
|
||||
int qty = s.Type == MerchantType.Bulk ? rng.Range(Tuning.BmBulkQtyMin, Tuning.BmBulkQtyMax)
|
||||
: s.Type == MerchantType.Elite ? rng.Range(1, Tuning.BmEliteQtyMax)
|
||||
: s.Type == MerchantType.Personal ? rng.Range(1, Tuning.BmPersonalQtyMax)
|
||||
: s.Type == MerchantType.GpuNew ? rng.Range(Math.Max(1, Tuning.BmQtyMin - 1), Tuning.BmQtyMax - 2) : rng.Range(Tuning.BmQtyMin, Tuning.BmQtyMax);
|
||||
double factor = s.Type == MerchantType.Personal ? F(rng, Tuning.BmPersonalFactorMin, Tuning.BmPersonalFactorMax) : s.Factor * F(rng, 0.97, 1.03);
|
||||
BmListing l = null;
|
||||
bool eliteGpu = s.Type == MerchantType.Elite && rng.Chance(0.4);
|
||||
bool personalGpu = s.Type == MerchantType.Personal && rng.Chance(0.15);
|
||||
if (gpuKind || eliteGpu || personalGpu)
|
||||
{
|
||||
bool fresh = s.Type == MerchantType.GpuNew;
|
||||
var c = UsedDealers.Card(gpus, fresh ? 0 : Tuning.BmGpuFake, 0, rng);
|
||||
bool fresh = s.Type == MerchantType.GpuNew || eliteGpu || (personalGpu && rng.Chance(0.3));
|
||||
var pool = eliteGpu ? HighGpus : gpus;
|
||||
var c = UsedDealers.Card(pool, fresh ? 0 : Tuning.BmGpuFake, 0, rng);
|
||||
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)) };
|
||||
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) * factor)) };
|
||||
}
|
||||
else
|
||||
{
|
||||
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)) };
|
||||
bool wantElite = s.Type == MerchantType.Elite || (s.Type == MerchantType.Personal && rng.Chance(Tuning.BmPersonalEliteShare));
|
||||
BmEntry pick = default; long v = 0; bool found = false;
|
||||
for (int tries = 0; tries < 40 && entries.Count > 0 && !found; tries++)
|
||||
{
|
||||
int k = rng.Range(0, entries.Count - 1);
|
||||
var e = entries[k];
|
||||
long val = value(e.Id);
|
||||
if (val <= 0) { entries.RemoveAt(k); continue; }
|
||||
bool ok = s.Type == MerchantType.Elite ? val >= Tuning.BmEliteMinValue : wantElite ? val > Tuning.BmMaxUnitValue : val <= Tuning.BmMaxUnitValue;
|
||||
if (!ok) continue;
|
||||
pick = e; v = val; found = true; entries.RemoveAt(k);
|
||||
}
|
||||
if (!found) continue;
|
||||
l = new BmListing { Item = pick.Id, Kind = pick.Kind, Level = pick.Level, Price = (int)Math.Max(1, Math.Round(v * factor)) };
|
||||
}
|
||||
l.Seller = idx; l.Qty = qty;
|
||||
list.Add(l);
|
||||
@@ -155,18 +172,21 @@ namespace Decentralized.Domain
|
||||
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);
|
||||
if (rng.Chance(Tuning.BmEliteChance)) types.Add(MerchantType.Elite);
|
||||
for (int i = 0; i < Tuning.BmPersonalCount; i++) types.Add(MerchantType.Personal);
|
||||
foreach (var t in types)
|
||||
{
|
||||
bool scam = rng.Chance(Tuning.BmScamSellerShare);
|
||||
// 骗子多半是新号,也有刷过好评的老号
|
||||
bool fresh = scam ? rng.Chance(0.6) : rng.Chance(0.25);
|
||||
double lo = t == MerchantType.GpuNew ? Tuning.BmFactorNewMin : t == MerchantType.GpuUsed ? Tuning.BmFactorUsedMin : t == MerchantType.Bulk ? Tuning.BmFactorBulkMin : Tuning.BmFactorMin;
|
||||
double hi = t == MerchantType.GpuNew ? Tuning.BmFactorNewMax : t == MerchantType.GpuUsed ? Tuning.BmFactorUsedMax : t == MerchantType.Bulk ? Tuning.BmFactorBulkMax : Tuning.BmFactorMax;
|
||||
bool personal = t == MerchantType.Personal;
|
||||
bool scam = rng.Chance(personal ? Tuning.BmPersonalScam : Tuning.BmScamSellerShare);
|
||||
// 骗子多半是新号,也有刷过好评的老号;个人卖家都是新号
|
||||
bool fresh = personal || (scam ? rng.Chance(0.6) : rng.Chance(0.25));
|
||||
double lo = t == MerchantType.Elite ? Tuning.BmFactorEliteMin : t == MerchantType.GpuNew ? Tuning.BmFactorNewMin : t == MerchantType.GpuUsed ? Tuning.BmFactorUsedMin : t == MerchantType.Bulk ? Tuning.BmFactorBulkMin : Tuning.BmFactorMin;
|
||||
double hi = t == MerchantType.Elite ? Tuning.BmFactorEliteMax : t == MerchantType.GpuNew ? Tuning.BmFactorNewMax : t == MerchantType.GpuUsed ? Tuning.BmFactorUsedMax : t == MerchantType.Bulk ? Tuning.BmFactorBulkMax : Tuning.BmFactorMax;
|
||||
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, Type = t, Factor = F(rng, lo, hi),
|
||||
Trades = fresh ? rng.Range(1, 12) : rng.Range(25, 220),
|
||||
Scam = scam, Type = t, Factor = personal ? 1.0 : F(rng, lo, hi),
|
||||
Trades = fresh ? rng.Range(0, 12) : rng.Range(25, 220),
|
||||
Rating = scam && !fresh ? rng.Range(94, 99) : fresh ? rng.Range(70, 100) : rng.Range(90, 99),
|
||||
};
|
||||
Sellers.Add(s);
|
||||
|
||||
@@ -108,10 +108,17 @@ namespace Decentralized.Domain
|
||||
public const int BmSaleHold = 3, BmRatingStart = 90, BmRatingFakeLoss = 10, BmMaxListings = 10;
|
||||
/// <summary>商家货架:每周的商家数(其中 2 家从矿卡 / 矿架 / 模组 4 类里抽,其余从其他类型里抽)、每家的货种数、每种货的库存范围;大宗商家出现概率、库存范围、送货天数;
|
||||
/// 各类商家的价格系数范围(订单价 = 基础价 × 系数);批量折扣档(件数、折扣)。</summary>
|
||||
public const int BmMerchantCount = 7, BmCoreMerchants = 2, BmMerchantListings = 3, BmQtyMin = 2, BmQtyMax = 5, BmBulkQtyMin = 10, BmBulkQtyMax = 25, BmBulkDays = 6;
|
||||
public const int BmMerchantCount = 7, BmCoreMerchants = 2, BmMerchantListings = 3, BmQtyMin = 2, BmQtyMax = 6, 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;
|
||||
/// <summary>商家货架上单件基础价的上限(过贵的货不上,免得一件把周转量撑歪)。</summary>
|
||||
public const long BmMaxUnitValue = 4000;
|
||||
/// <summary>尖端店铺:每周出现概率、货种数、库存范围、单件基础价下限、价格系数范围、送货天数。</summary>
|
||||
public const double BmEliteChance = 0.15, BmFactorEliteMin = 0.85, BmFactorEliteMax = 1.00;
|
||||
public const int BmEliteListings = 2, BmEliteQtyMax = 2, BmEliteDays = 5;
|
||||
public const long BmEliteMinValue = 2000;
|
||||
/// <summary>个人卖家:每周家数、货种数、库存上限、每件的价格系数范围(各件独立随机)、是骗子的概率、混进尖端货的概率。</summary>
|
||||
public const int BmPersonalCount = 2, BmPersonalListings = 3, BmPersonalQtyMax = 2;
|
||||
public const double BmPersonalFactorMin = 0.50, BmPersonalFactorMax = 1.20, BmPersonalScam = 0.25, BmPersonalEliteShare = 0.10;
|
||||
public const int BmBulk1Count = 5, BmBulk2Count = 10;
|
||||
public const double BmBulk1Discount = 0.05, BmBulk2Discount = 0.10;
|
||||
/// <summary>出售量越大货款越容易带污点:近 7 天成交额达到参考量时,污点概率与比例各加最多 BmVolumeTaintMax。</summary>
|
||||
|
||||
Reference in new issue
Block a user