Tumbler 扩展:治安部探员与搜查、密钥泄露还原出款污点、审判预测盘、黑市跑路、报复、长期联系人、混币器换皮重开
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0f26d88c03
commit
a37dd4ca7c
19 files changed
+904
-20
No files matched your search
@@ -27,8 +27,12 @@ namespace Decentralized.Domain
|
||||
/// <summary>混币器:手续费、匿名集(NPC 混币量)、跑路概率的倍率;被举报后永久关闭(在途的照常提回,不能再存)。</summary>
|
||||
public double MixerFee = 1, MixerVolume = 1, MixerRug = 1;
|
||||
public bool MixerClosed;
|
||||
/// <summary>混币器换皮重开过(换了名字、地址);重开的是治安部的钓鱼混币器(提回的币一律被标记)。</summary>
|
||||
public bool MixerRebranded, MixerHoneypot;
|
||||
/// <summary>本 mod 自己的剧情线:混币器运营者 Tumbler。</summary>
|
||||
public Tumbler Tumbler = new Tumbler();
|
||||
/// <summary>Tumbler 四次上门之后的剧情:探员、泄露、审判、黑市跑路、报复、长期联系人、换皮重开。</summary>
|
||||
public TumblerAfter After = new TumblerAfter();
|
||||
|
||||
public bool Has(string key) => Applied.Contains(key);
|
||||
|
||||
@@ -85,10 +89,23 @@ namespace Decentralized.Domain
|
||||
case "tumbler_sold":
|
||||
MixerFee *= Tuning.TumblerSoldFee; MixerRug *= Tuning.TumblerSoldRug;
|
||||
break;
|
||||
case "tumbler_bm_rug":
|
||||
MixerClosed = true;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>混币器换皮重开:重新开放,费率、匿名集、跑路倍率回到 1;钓鱼混币器的费率打折(引人上钩)。</summary>
|
||||
public void Rebrand(bool honeypot)
|
||||
{
|
||||
MixerClosed = false;
|
||||
MixerRebranded = true;
|
||||
MixerHoneypot = honeypot;
|
||||
MixerFee = honeypot ? Tuning.TumblerHoneypotFee : 1;
|
||||
MixerVolume = 1; MixerRug = 1;
|
||||
}
|
||||
|
||||
/// <summary>SPARK 在接下来几晚里一共涨到 total 倍(每晚乘相同的倍数)。</summary>
|
||||
private void PushSpark(double total)
|
||||
{
|
||||
@@ -124,11 +141,16 @@ namespace Decentralized.Domain
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>今天该上门的是第几次(1–4),没有就返回 0(第 4 次举报过的话由接入层改成新闻:他被抓了)。</summary>
|
||||
public int Due(int day)
|
||||
/// <summary>
|
||||
/// 今天该上门的是第几次(1–4),没有就返回 0(第 4 次举报过的话由接入层改成新闻:他被抓了)。
|
||||
/// after 不为空时:第 3 次要等治安部探员来过(第二天起),配合过探员又选了藏的,第 4 次要等搜查过(第二天起)。
|
||||
/// </summary>
|
||||
public int Due(int day, TumblerAfter after = null)
|
||||
{
|
||||
if (Done || StartDay < 0 || Stage >= Offsets.Length || VisitedDay == day) return 0;
|
||||
if (day < NextVisitDay) return 0;
|
||||
if (after != null && Stage == 2 && (after.Agent == 0 || day <= after.AgentDay)) return 0;
|
||||
if (after != null && Stage == 3 && after.NeedSearch(this) && (!after.Searched || day <= after.SearchDay)) return 0;
|
||||
return Stage + 1;
|
||||
}
|
||||
|
||||
@@ -140,4 +162,95 @@ namespace Decentralized.Domain
|
||||
Done = Stage >= Offsets.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tumbler 四次上门之后的剧情(原来的进度类不动):
|
||||
/// 治安部探员在第 2、3 次上门之间来一次(配合 / 否认);配合过又选了藏的,第 4 次前一天来搜查;
|
||||
/// 举报、被搜走、第 4 次盘不在手上 → 密钥落到治安部,混币记录泄露;举报 → 审判盘;卖给黑市 → 黑市跑路;
|
||||
/// 无罪获释、黑市跑路 → Tumbler 报复;藏好归还 → 长期联系人;混币器关停若干天后换皮重开。只记进度,对话、物品、夜间效果在接入层。
|
||||
/// </summary>
|
||||
public sealed class TumblerAfter
|
||||
{
|
||||
public const int Cooperate = 1, Deny = 2;
|
||||
/// <summary>探员上门的结果(0 = 还没来)和那一天。</summary>
|
||||
public int Agent, AgentDay = -1;
|
||||
/// <summary>探员的外观(问话和搜查是同一个人)。</summary>
|
||||
public string AgentSprite;
|
||||
/// <summary>搜查过(不管有没有搜到)、搜走了密钥盘、哪一天。</summary>
|
||||
public bool Searched, Seized;
|
||||
public int SearchDay = -1;
|
||||
/// <summary>哪一晚泄露(-1 = 没安排);已经泄露过(整局一次)。</summary>
|
||||
public int LeakDay = -1;
|
||||
public bool Leaked;
|
||||
/// <summary>藏好并归还:成为长期联系人;已经提示过的镇压开始日。</summary>
|
||||
public bool Contact;
|
||||
public List<int> Tipped = new List<int>();
|
||||
/// <summary>审判:开盘日、宣判日、判决(1 定罪,0 无罪,-1 没有审判);每晚的庭审新闻偏向(true = 控方占上风);最后一条新闻是哪晚。</summary>
|
||||
public int TrialOpenDay = -1, TrialDay = -1, Verdict = -1, HintDay = -1;
|
||||
public List<bool> Hints = new List<bool>();
|
||||
public bool TrialMarket, TrialDone;
|
||||
/// <summary>报复在哪一晚(-1 = 没有)、已经报复过。</summary>
|
||||
public int RevengeDay = -1;
|
||||
public bool Revenged;
|
||||
/// <summary>黑市卷款跑路在哪一晚(-1 = 没有)、已经跑路。</summary>
|
||||
public int BmRugDay = -1;
|
||||
public bool BmRugged;
|
||||
/// <summary>混币器关停的那天(-1 = 没关停);换皮重开过。</summary>
|
||||
public int ClosedDay = -1;
|
||||
public bool Rebranded;
|
||||
|
||||
/// <summary>探员今天该不该来:第 2 次上门之后的第二天起,还没来过。</summary>
|
||||
public bool AgentDue(Tumbler t, int day) => !t.Done && t.Stage == 2 && Agent == 0 && day > t.VisitedDay;
|
||||
|
||||
/// <summary>配合过探员、又选了藏,第 4 次上门前要先来搜查。</summary>
|
||||
public bool NeedSearch(Tumbler t) => Agent == Cooperate && t.Choice == Tumbler.Hide;
|
||||
|
||||
/// <summary>搜查今天该不该来:第 4 次上门的前一天起(第 3 次之后的第二天起)。</summary>
|
||||
public bool SearchDue(Tumbler t, int day) => !t.Done && t.Stage == 3 && NeedSearch(t) && !Searched && day >= t.NextVisitDay - 1 && day > t.VisitedDay;
|
||||
|
||||
/// <summary>安排今晚泄露(整局一次)。</summary>
|
||||
public void LeakTonight(int day)
|
||||
{
|
||||
if (!Leaked && LeakDay < 0) LeakDay = day;
|
||||
}
|
||||
|
||||
/// <summary>举报:掷出判决,安排开盘和宣判;混币器从今天起关停。</summary>
|
||||
public void StartTrial(int day, Rng rng)
|
||||
{
|
||||
if (Verdict >= 0) return;
|
||||
Verdict = rng.Chance(Tuning.TumblerTrialConvict) ? 1 : 0;
|
||||
TrialOpenDay = day + Tuning.TumblerTrialOpen;
|
||||
TrialDay = day + Tuning.TumblerTrialDays;
|
||||
ClosedDay = day;
|
||||
}
|
||||
|
||||
/// <summary>今晚的庭审新闻(开盘前一晚起到宣判前一晚,每晚一条):偏向真实判决的概率 HintAcc。没有就返回 null。</summary>
|
||||
public bool? Hint(int day, Rng rng)
|
||||
{
|
||||
if (Verdict < 0 || TrialDone || day < TrialOpenDay - 1 || day >= TrialDay || HintDay >= day) return null;
|
||||
HintDay = day;
|
||||
bool truth = Verdict == 1;
|
||||
bool h = rng.Chance(Tuning.TumblerTrialHintAcc) ? truth : !truth;
|
||||
Hints.Add(h);
|
||||
return h;
|
||||
}
|
||||
|
||||
/// <summary>公开信息下的定罪概率:先验按已经登出的庭审新闻做贝叶斯更新。</summary>
|
||||
public double TrialPublic()
|
||||
{
|
||||
double acc = Tuning.TumblerTrialHintAcc, lr = Math.Log(acc / (1 - acc)), p0 = Tuning.TumblerTrialConvict;
|
||||
double l = Math.Log(p0 / (1 - p0));
|
||||
foreach (var h in Hints) l += h ? lr : -lr;
|
||||
return 1 / (1 + Math.Exp(-l));
|
||||
}
|
||||
|
||||
/// <summary>卖给黑市:安排黑市卷款跑路的那一晚。</summary>
|
||||
public void StartSold(int day, Rng rng)
|
||||
{
|
||||
if (BmRugDay < 0) BmRugDay = day + rng.Range(Tuning.TumblerBmRugMin, Tuning.TumblerBmRugMax);
|
||||
}
|
||||
|
||||
/// <summary>今晚该不该换皮重开:关停满 RebrandDays 天,只一次。</summary>
|
||||
public bool RebrandDue(int day) => !Rebranded && ClosedDay >= 0 && day >= ClosedDay + Tuning.TumblerRebrandDays;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user