Black market: useful goods only, player selling with price-dependent speed and taint
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5b57f3eedc
commit
502366e7c4
10 files changed
+363
-21
No files matched your search
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Decentralized.Domain
|
||||
{
|
||||
public enum SaleState { Listed, Sold, Paid, Lost, Delisted }
|
||||
|
||||
/// <summary>玩家在黑市上架的一件货。货交给平台(从储藏区拿走),成交后货款托管几天再到钱包,可能带污点。</summary>
|
||||
public sealed class BmSale
|
||||
{
|
||||
public string Item;
|
||||
public string Model, FakeOf;
|
||||
public double Health;
|
||||
/// <summary>标价倍率与标价(信用点)。</summary>
|
||||
public double Mult;
|
||||
public int ListPrice;
|
||||
public int ListedDay, PayDay;
|
||||
public SaleState State = SaleState.Listed;
|
||||
/// <summary>成交后扣掉抽成的货款(信用点)与其中带的污点。</summary>
|
||||
public int Proceeds;
|
||||
public double Taint;
|
||||
/// <summary>终态原因:fake(买家看穿假卡退货)/ rug / delist。</summary>
|
||||
public string Why;
|
||||
}
|
||||
|
||||
public sealed partial class BlackMarket
|
||||
{
|
||||
public List<BmSale> Sales = new List<BmSale>();
|
||||
/// <summary>玩家作为卖家的好评(%):影响成交速度;卖假卡被看穿会下降,每笔成交回升一点。</summary>
|
||||
public int Rating = Tuning.BmRatingStart;
|
||||
|
||||
/// <summary>期望成交天数:倍率 ≤0.9 一天,1.0 两天,1.8 十四天,之间插值(0.9–1.0 线性,1.0 以上指数)。</summary>
|
||||
public static double ExpectedDays(double m)
|
||||
{
|
||||
if (m <= 0.9) return Tuning.BmSellDaysFast;
|
||||
if (m < 1.0) return Tuning.BmSellDaysFast + (m - 0.9) / 0.1 * (Tuning.BmSellDays1 - Tuning.BmSellDaysFast);
|
||||
return Tuning.BmSellDays1 * Math.Pow(Tuning.BmSellDaysMax / Tuning.BmSellDays1, (m - 1.0) / (Tuning.BmSellMax - 1.0));
|
||||
}
|
||||
|
||||
private static double T(double m) => Math.Max(0, Math.Min(1, (m - Tuning.BmSellMin) / (Tuning.BmSellMax - Tuning.BmSellMin)));
|
||||
/// <summary>货款带污点的概率与(带的时候)污点占货款的比例:都随标价倍率上升。</summary>
|
||||
public static double TaintChance(double m) => Tuning.BmSaleTaintChanceMin + T(m) * (Tuning.BmSaleTaintChanceMax - Tuning.BmSaleTaintChanceMin);
|
||||
public static double TaintRatio(double m) => Tuning.BmSaleTaintRatioMin + T(m) * (Tuning.BmSaleTaintRatioMax - Tuning.BmSaleTaintRatioMin);
|
||||
|
||||
/// <summary>每晚成交的概率:1 / 期望天数,乘好评系数(好评 90 为 1,封顶 1.1),最多 1。</summary>
|
||||
public double SaleChance(double m) => Math.Min(1, 1 / ExpectedDays(m) * Math.Min(1.1, Rating / (double)Tuning.BmRatingStart));
|
||||
|
||||
public static int ListPriceOf(long refValue, double m) => (int)Math.Max(1, Math.Round(refValue * m));
|
||||
public static int ListFee(int listPrice) => (int)Math.Ceiling(listPrice * Tuning.BmListFee);
|
||||
|
||||
/// <summary>上架:交付一件货,收上架费(付给 feeAsset 币,污点与成本按比例带走)。倍率超出范围、平台关闭或余额不足返回 null。</summary>
|
||||
public BmSale List(string item, string model, string fakeOf, double health, long refValue, double m, Wallet w, string feeAsset, int day, Func<string, double> price)
|
||||
{
|
||||
if (m < Tuning.BmSellMin - 1e-9 || m > Tuning.BmSellMax + 1e-9 || refValue <= 0 || Closed(day) || feeAsset == Assets.CRC) return null;
|
||||
int listPrice = ListPriceOf(refValue, m);
|
||||
double p = price(feeAsset);
|
||||
if (p <= 0) return null;
|
||||
double qty = ListFee(listPrice) / p;
|
||||
if (w.Avail(feeAsset) + 1e-9 < qty) return null;
|
||||
double total = w.Value(price);
|
||||
double share = total > 0 ? Math.Min(1, ListFee(listPrice) / total) : 0;
|
||||
w.Taint -= w.Taint * share; w.Basis -= w.Basis * share;
|
||||
w.Add(feeAsset, -qty);
|
||||
var s = new BmSale { Item = item, Model = model, FakeOf = fakeOf, Health = health, Mult = m, ListPrice = listPrice, ListedDay = day };
|
||||
Sales.Add(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public void Delist(BmSale s) { s.State = SaleState.Delisted; s.Why = "delist"; }
|
||||
|
||||
/// <summary>未到账的货款(信用点):跑路时一并归零,也推高跑路概率。</summary>
|
||||
public double HeldSales => Sales.Where(x => x.State == SaleState.Sold).Sum(x => (double)x.Proceeds);
|
||||
|
||||
/// <summary>每晚推进出售:上架的货按概率成交(假卡可能被看穿退货),成交的货款托管到期后进钱包。镇压期间平台下线,不推进。</summary>
|
||||
private void SalesNight(int day, bool crackdown, Wallet w, Func<string, double> price, Rng rng, List<BmEvent> ev)
|
||||
{
|
||||
if (crackdown) return;
|
||||
foreach (var s in Sales.Where(x => x.State == SaleState.Listed && x.ListedDay < day).ToList())
|
||||
{
|
||||
if (!rng.Chance(SaleChance(s.Mult))) continue;
|
||||
if (s.FakeOf != null && rng.Chance(Tuning.BmFakeDetect))
|
||||
{
|
||||
s.State = SaleState.Delisted; s.Why = "fake";
|
||||
Rating = Math.Max(0, Rating - Tuning.BmRatingFakeLoss);
|
||||
ev.Add(new BmEvent { Kind = "sale_fake", Sale = s });
|
||||
continue;
|
||||
}
|
||||
s.State = SaleState.Sold;
|
||||
s.Proceeds = (int)Math.Round(s.ListPrice * (1 - Tuning.BmCommission));
|
||||
s.Taint = rng.Chance(TaintChance(s.Mult)) ? s.Proceeds * TaintRatio(s.Mult) : 0;
|
||||
s.PayDay = day + Tuning.BmSaleHold;
|
||||
Rating = Math.Min(100, Rating + 1);
|
||||
if (Honeypot(day)) Traps.Add(new BmTrap { Day = day + Tuning.BmTrapDelay, Amount = s.Proceeds, Why = "honeypot", Item = s.Item });
|
||||
ev.Add(new BmEvent { Kind = "sale_sold", Sale = s, Amount = s.Proceeds });
|
||||
}
|
||||
foreach (var s in Sales.Where(x => x.State == SaleState.Sold && day >= x.PayDay).ToList())
|
||||
{
|
||||
double p = price(Assets.CRT);
|
||||
if (p <= 0) continue;
|
||||
w.Add(Assets.CRT, s.Proceeds / p);
|
||||
w.Taint += s.Taint;
|
||||
w.Basis += s.Proceeds;
|
||||
s.State = SaleState.Paid;
|
||||
ev.Add(new BmEvent { Kind = "sale_paid", Sale = s, Amount = s.Proceeds });
|
||||
}
|
||||
// 买家落网:近期到账的出售记录会连累卖家(玩家)
|
||||
var recent = Sales.Where(x => x.State == SaleState.Paid && day - x.PayDay <= 14).ToList();
|
||||
if (recent.Count > 0 && rng.Chance(Tuning.BmSellerBust))
|
||||
{
|
||||
foreach (var s in recent) Traps.Add(new BmTrap { Day = day + Tuning.BmTrapDelay, Amount = s.Proceeds, Why = "seller", Item = s.Item });
|
||||
Forum.Add("busted_buyer");
|
||||
}
|
||||
Sales.RemoveAll(x => (x.State == SaleState.Paid || x.State == SaleState.Delisted || x.State == SaleState.Lost) && day - Math.Max(x.ListedDay, x.PayDay) > 14);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user