Stage 9.1: story links (Serpico, Eleanor, Officer Lun) and newspaper importance fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
96e91a4884
commit
62bd641283
15 files changed
+240
-17
No files matched your search
@@ -55,7 +55,7 @@ namespace Decentralized.Domain
|
||||
History.RemoveAll(h => h.Key <= day - Tuning.AmlWindowDays);
|
||||
double recent = History.Sum(h => h.Value);
|
||||
double value = amount * price(Assets.CRC);
|
||||
r.HitChance = HitChance(value, taint, recent, rent, fromMixer || wallet.Flagged);
|
||||
r.HitChance = Math.Min(1, HitChance(value, taint, recent, rent, fromMixer || wallet.Flagged) * StoryMods.Current.Aml(day));
|
||||
wallet.Add(Assets.CRC, -amount);
|
||||
wallet.Taint -= taint;
|
||||
wallet.Basis -= basis;
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace Decentralized.Domain
|
||||
/// <summary>镇压事件开始:有一定概率被制裁,持续若干天。</summary>
|
||||
public void OnEvent(MarketEvent ev, int day, Rng rng)
|
||||
{
|
||||
if (ev == MarketEvent.Crackdown && rng.Chance(Tuning.MixerSanctionChance)) SanctionedUntil = day + Tuning.MixerSanctionDays;
|
||||
if (ev == MarketEvent.Crackdown && rng.Chance(Math.Min(1, Tuning.MixerSanctionChance * StoryMods.Current.Sanction(day)))) SanctionedUntil = day + Tuning.MixerSanctionDays;
|
||||
}
|
||||
|
||||
/// <summary>每晚:到期的存入提回钱包(污点按轮数降低),在途的各有小概率跑路。</summary>
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace Decentralized.Domain
|
||||
bool insider = PosAll(m.Id).Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap;
|
||||
if (pos.Yes) m.QYes -= pos.Shares; else m.QNo -= pos.Shares;
|
||||
Positions.Remove(pos);
|
||||
if (rng != null && insider && pos.Insider > 0 && got > pos.Cost && rng.Chance(Tuning.PredictInsiderCatch))
|
||||
if (rng != null && insider && pos.Insider > 0 && got > pos.Cost && rng.Chance(Math.Min(1, Tuning.PredictInsiderCatch * StoryMods.Current.Enforce(day))))
|
||||
{
|
||||
fraud = Math.Min(Tuning.PredictFraudCap, (got - pos.Cost) * Tuning.PredictFraudShare);
|
||||
got = 0;
|
||||
@@ -241,7 +241,7 @@ namespace Decentralized.Domain
|
||||
st.Count++; st.SumP0 += m.P0; if (res.Value) st.Hits++;
|
||||
var winners = all.Where(x => x.Yes == res.Value).ToList();
|
||||
// 操纵:行情盘上,赢家的方向和玩家推价的方向一致时按推价幅度被查——本金和收益都没收,收益的一半记罪证
|
||||
if (m.Kind == PredictKind.Price && winners.Count > 0 && winners.Any(x => x.Yes ? push > 0 : push < 0) && rng.Chance(Market.ManipChance(push)))
|
||||
if (m.Kind == PredictKind.Price && winners.Count > 0 && winners.Any(x => x.Yes ? push > 0 : push < 0) && rng.Chance(Math.Min(1, Market.ManipChance(push) * StoryMods.Current.Enforce(day))))
|
||||
{
|
||||
double profitM = winners.Sum(x => x.Shares) - cost;
|
||||
evs.Add(new PredictEvent { Kind = "manip", Title = m.Title, Market = m, Amount = cost, Fraud = Math.Min(Tuning.PredictFraudCap, Math.Max(0, profitM) * Tuning.PredictFraudShare) });
|
||||
@@ -257,7 +257,7 @@ namespace Decentralized.Domain
|
||||
{
|
||||
double payout = winners.Sum(x => x.Shares), profit = payout - cost;
|
||||
// 内幕持仓按盘口合计(拆到几个钱包里也一样)
|
||||
if (all.Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap && rng.Chance(Tuning.PredictInsiderCatch))
|
||||
if (all.Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap && rng.Chance(Math.Min(1, Tuning.PredictInsiderCatch * StoryMods.Current.Enforce(day))))
|
||||
{
|
||||
// 查实:本金和收益全部没收
|
||||
evs.Add(new PredictEvent { Kind = "caught", Title = m.Title, Market = m, Amount = payout, Fraud = Math.Min(Tuning.PredictFraudCap, Math.Max(0, profit) * Tuning.PredictFraudShare) });
|
||||
|
||||
@@ -34,13 +34,13 @@ namespace Decentralized.Domain
|
||||
/// <summary>减记发生时返回 CRC 持有量要乘的系数(其余情况为 1)。</summary>
|
||||
public double OnEventStart(MarketEvent ev, int day, Rng rng)
|
||||
{
|
||||
if (ev == MarketEvent.RevRaidUpper && CrcDepegEnd < 0 && rng.Chance(Tuning.CrcDepegChance))
|
||||
if (ev == MarketEvent.RevRaidUpper && CrcDepegEnd < 0 && rng.Chance(Tuning.CrcDepegChance * StoryMods.Current.CrcDepeg))
|
||||
{
|
||||
Crc = rng.Range(Tuning.CrcDepegMin, Tuning.CrcDepegMax);
|
||||
CrcDepegEnd = day + rng.Range(Tuning.CrcDepegDaysMin, Tuning.CrcDepegDaysMax);
|
||||
CrcHaircutPending = rng.Chance(Tuning.CrcHaircutChance);
|
||||
CrcHaircutPending = rng.Chance(Math.Min(1, Tuning.CrcHaircutChance * StoryMods.Current.CrcHaircut));
|
||||
}
|
||||
if ((ev == MarketEvent.GangWar || ev == MarketEvent.Crackdown) && CrtDepegEnd < 0 && rng.Chance(Tuning.CrtDepegChance))
|
||||
if ((ev == MarketEvent.GangWar || ev == MarketEvent.Crackdown) && CrtDepegEnd < 0 && rng.Chance(Tuning.CrtDepegChance * StoryMods.Current.CrtDepeg))
|
||||
{
|
||||
Crt = rng.Range(Tuning.CrtDepegMin, Tuning.CrtDepegMax);
|
||||
CrtDepegEnd = day + rng.Range(Tuning.CrtDepegDaysMin, Tuning.CrtDepegDaysMax);
|
||||
@@ -52,7 +52,7 @@ namespace Decentralized.Domain
|
||||
|
||||
/// <summary>镇压开始时,玩家持有的 CRT 被冻结一半的概率:黑市声望 0 时 40%,50 以上为 0。</summary>
|
||||
public static double CrtFreezeChance(double blackMarketRep) =>
|
||||
Math.Max(0, Tuning.CrtFreezeBase - blackMarketRep * Tuning.CrtFreezePerRep);
|
||||
Math.Max(0, Tuning.CrtFreezeBase - blackMarketRep * Tuning.CrtFreezePerRep) * StoryMods.Current.CrtFreeze;
|
||||
|
||||
/// <summary>镇压开始时是否冻结玩家的 CRT:按黑市声望,声望越低越容易被黑市"出卖"。</summary>
|
||||
public static bool CrtFrozenByCrackdown(double blackMarketRep, Rng rng) => rng.Chance(CrtFreezeChance(blackMarketRep));
|
||||
@@ -111,7 +111,7 @@ namespace Decentralized.Domain
|
||||
{
|
||||
CraSubsidy -= (Tuning.CraNpcStake + playerStaked) * CraRate;
|
||||
bool revFalling = RevHistory.Count == 7 && RevPower < RevHistory[0] - 5;
|
||||
double weekly = Tuning.CraSpiralWeekly * (revFalling ? 3 : 1);
|
||||
double weekly = Tuning.CraSpiralWeekly * (revFalling ? 3 : 1) * StoryMods.Current.CraSpiral(day);
|
||||
double daily = 1 - Math.Pow(1 - weekly, 1.0 / 7);
|
||||
if (CraWarnDay < 0 && (rng.Chance(daily) || CraSubsidy <= 0))
|
||||
{
|
||||
@@ -128,7 +128,7 @@ namespace Decentralized.Domain
|
||||
}
|
||||
else
|
||||
{
|
||||
Spark *= Math.Exp(rng.Gauss() * Tuning.SparkVol);
|
||||
Spark *= Math.Exp(rng.Gauss() * Tuning.SparkVol) * StoryMods.Current.TakeSparkPush();
|
||||
Spark += (Tuning.SparkStart - Spark) * Tuning.SparkRevert;
|
||||
Spark = Math.Max(Tuning.SparkStart * Tuning.SparkMinMult, Math.Min(Tuning.SparkStart * Tuning.SparkMaxMult, Spark));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Decentralized.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏剧情选择对本 mod 世界的影响:一组倍率和期限,随存档保存。各处的概率读 <see cref="Current"/>(接入层读档时指向存档里的这一份)。
|
||||
/// 每个选择只生效一次(Applied 记下键)。
|
||||
/// </summary>
|
||||
public sealed class StoryMods
|
||||
{
|
||||
/// <summary>当前生效的那一份。接入层读档时设成存档里的;测试里可以换。</summary>
|
||||
public static StoryMods Current = new StoryMods();
|
||||
|
||||
public List<string> Applied = new List<string>();
|
||||
/// <summary>CRC 脱锚、CRC 托管银行减记、CRT 脱锚、镇压时 CRT 被出卖的概率倍率(永久)。</summary>
|
||||
public double CrcDepeg = 1, CrcHaircut = 1, CrtDepeg = 1, CrtFreeze = 1;
|
||||
/// <summary>到这一天之前 CRA 螺旋概率减半。</summary>
|
||||
public int CraSafeUntil = -1;
|
||||
/// <summary>治安部整顿到这一天:出金审查命中率、混币器制裁概率、操纵与内幕查处概率上调。</summary>
|
||||
public int SecUntil = -1;
|
||||
/// <summary>伦警官被捕后的立威期到这一天:出金审查、操纵、内幕查处概率上调。</summary>
|
||||
public int LunUntil = -1;
|
||||
/// <summary>SPARK 每晚额外乘的倍数,还剩几晚。</summary>
|
||||
public double SparkPush = 1;
|
||||
public int SparkPushNights;
|
||||
|
||||
public bool Has(string key) => Applied.Contains(key);
|
||||
|
||||
public double CraSpiral(int day) => day < CraSafeUntil ? Tuning.StoryCraSpiralMult : 1;
|
||||
public double Aml(int day) => (day < SecUntil ? Tuning.StorySecAmlMult : 1) * (day < LunUntil ? Tuning.StoryLunMult : 1);
|
||||
public double Sanction(int day) => day < SecUntil ? Tuning.StorySecSanctionMult : 1;
|
||||
/// <summary>操纵检出、内幕查处的概率倍率。</summary>
|
||||
public double Enforce(int day) => (day < SecUntil ? Tuning.StorySecEnforceMult : 1) * (day < LunUntil ? Tuning.StoryLunMult : 1);
|
||||
|
||||
/// <summary>每晚取 SPARK 的额外倍数(推完就是 1)。</summary>
|
||||
public double TakeSparkPush()
|
||||
{
|
||||
if (SparkPushNights <= 0) return 1;
|
||||
SparkPushNights--;
|
||||
return SparkPush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记下一个剧情选择并设置倍率。返回 false 表示已经生效过。CRA 补贴池的注资由接入层做(这里只管倍率)。
|
||||
/// key:serpico_rev / serpico_bm / serpico_sec / eleanor_leak / eleanor_return / lun_arrested。
|
||||
/// </summary>
|
||||
public bool Apply(string key, int day)
|
||||
{
|
||||
if (Has(key)) return false;
|
||||
Applied.Add(key);
|
||||
switch (key)
|
||||
{
|
||||
case "serpico_rev":
|
||||
CraSafeUntil = day + Tuning.StorySecDays;
|
||||
PushSpark(Tuning.StorySerpicoSpark);
|
||||
break;
|
||||
case "serpico_bm":
|
||||
CrtDepeg *= Tuning.StoryBmCrtMult; CrtFreeze *= Tuning.StoryBmCrtMult;
|
||||
break;
|
||||
case "serpico_sec":
|
||||
SecUntil = day + Tuning.StorySecDays;
|
||||
break;
|
||||
case "eleanor_leak":
|
||||
CrcHaircut *= Tuning.StoryLeakHaircutMult;
|
||||
PushSpark(Tuning.StoryLeakSpark);
|
||||
break;
|
||||
case "eleanor_return":
|
||||
CrcDepeg *= Tuning.StoryReturnCrcMult;
|
||||
break;
|
||||
case "lun_arrested":
|
||||
LunUntil = day + Tuning.StoryLunDays;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>SPARK 在接下来几晚里一共涨到 total 倍(每晚乘相同的倍数)。</summary>
|
||||
private void PushSpark(double total)
|
||||
{
|
||||
int n = Tuning.StorySparkNights;
|
||||
// 已经在推:把剩下的叠上去
|
||||
double left = SparkPushNights > 0 ? Math.Pow(SparkPush, SparkPushNights) : 1;
|
||||
SparkPush = Math.Pow(left * total, 1.0 / n);
|
||||
SparkPushNights = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,6 +235,17 @@ namespace Decentralized.Domain
|
||||
public const double FakeSupportChance = 0.04, FakeTransferShare = 0.08;
|
||||
public const int ScamMinDay = 10;
|
||||
|
||||
/// <summary>剧情联动:塞尔皮科证据交给革命军(CRA 补贴池注资、螺旋概率倍率与期限、SPARK 几晚内涨到的倍数)、交给黑市(CRT 脱锚与被出卖的倍率)、
|
||||
/// 交给治安部(期限内出金审查命中、混币器制裁、操纵与内幕查处的倍率);埃莉诺泄露数据盘(CRC 托管减记倍率、SPARK 涨幅)、交回(CRC 脱锚倍率);
|
||||
/// 伦警官被捕(期限与查处倍率);SPARK 推涨分几晚走完。</summary>
|
||||
public const double StoryRevSubsidy = 10000, StoryCraSpiralMult = 0.5, StorySerpicoSpark = 1.2;
|
||||
public const double StoryBmCrtMult = 0.5;
|
||||
public const int StorySecDays = 30;
|
||||
public const double StorySecAmlMult = 1.5, StorySecSanctionMult = 1.5, StorySecEnforceMult = 1.3;
|
||||
public const double StoryLeakHaircutMult = 2, StoryLeakSpark = 1.15, StoryReturnCrcMult = 0.5;
|
||||
public const int StoryLunDays = 14, StorySparkNights = 5;
|
||||
public const double StoryLunMult = 1.3;
|
||||
|
||||
/// <summary>行情图的 NPC 成交量:每天的基础量占主池深度的比例(按币种),价格波动越大成交越多(|涨跌幅| × 倍数),再乘 0.6–1.4 的随机。</summary>
|
||||
public const double ChartVolPsc = 0.12, ChartVolCrc = 0.10, ChartVolCrt = 0.06, ChartVolCra = 0.04, ChartVolSpark = 0.03, ChartVolPonero = 0.01, ChartVolMove = 8;
|
||||
|
||||
|
||||
Reference in new issue
Block a user