Stage 7.3: NPC scams (wicks, phishing airdrops, rug pools, fake support, forged transfers)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Opus 5.5 committed 2026-11-04 08:28:43 +08:00
1 parent 4279d48374
commit 6c5db17758
13 files changed
+691 -11

No files matched your search

+56 -5
View File
@@ -11,7 +11,7 @@ namespace Decentralized
public static class CoinCustomers
{
private static readonly HashSet<StoreClient> Marked = new HashSet<StoreClient>();
private static int markDay = -1;
private static int markDay = -1, supportDay = -1;
public static void Subscribe()
{
@@ -152,7 +152,7 @@ namespace Decentralized
c.mainDialogue.SetText(c.displayName, "CRT.").SetEndAction(() => { });
c.AddBasicDialog();
c.CompleteClientCreation();
Mark(c, Assets.CRT);
Mark(c, Assets.CRT, false);
Log.Info($"用币付款顾客(测试)已设置,钱包污点 {Session.Data.Hot.Taint:0.##},当前顾客 {PlayerStore.Instance.currentClientInstance?.GetClientBlueprint()?.identifier},队列 {PlayerStore.Instance.storeClientManager.clientStack.Count} 人");
PlayerStore.Instance.currentClientInstance = null; // 测试:丢掉在场的顾客(开局的高利贷者不可遣散)
PlayerStore.Instance.storeClientManager.clientStack.Insert(0, c);
@@ -171,29 +171,80 @@ namespace Decentralized
{
int day = StoreStation.GetDayCounter();
if (day != markDay) { Marked.Clear(); markDay = day; }
// 假客服:一天最多一次
if (day >= Tuning.ScamMinDay && supportDay != day)
{
supportDay = day;
if (Session.Data.Rng.Chance(Tuning.FakeSupportChance)) manager.AddClient(FakeSupport());
}
foreach (var c in manager.clientStack)
{
if (c == null || c.isSecurity || Marked.Contains(c) || c.clientIntent != StoreClient.ClientIntent.BUY || c.isNoContributeToEvidence) continue;
Marked.Add(c);
string asset = CoinPayment.AssetFor(c.clientFaction);
if (asset == null || !Session.Data.Rng.Chance(CoinPayment.Chance(asset))) continue;
Mark(c, asset);
Mark(c, asset, day >= Tuning.ScamMinDay && Session.Data.Rng.Chance(Tuning.FakeTransferShare));
}
});
}
private static void Mark(StoreClient c, string asset)
/// <summary>
/// 标成用币付款的顾客。开口先说用什么币付:正常的说"链上确认要一会儿";骗子(伪造转账)说"已经转了,给你看截图,快给货"——
/// 成交后信用点照样被扣回,币却不会到账。玩家能做的就是听清这句话,不卖给他。
/// </summary>
private static void Mark(StoreClient c, string asset, bool fake)
{
var before = c.onItemBought;
double total = 0; // 这位顾客这一单一共付了多少币
c.isNoContributeToEvidence = true;
var intro = new Dialogue().SetText(c.displayName, L.T(fake ? "coinpay.intro.fake" : "coinpay.intro", asset));
intro.SetNextDialogue(c.mainDialogue);
c.mainDialogue = intro;
c.onItemBought = (item, cost) =>
{
before?.Invoke(item, cost);
Log.Guard("用币付款成交", () => { total += Pay(c, asset, item, cost); Say(c, asset, total); });
Log.Guard("用币付款成交", () =>
{
if (fake) { FakePay(c, asset, item, cost); total += cost / Session.Price(asset); }
else total += Pay(c, asset, item, cost);
Say(c, asset, total);
});
};
}
/// <summary>伪造转账:信用点扣回(顾客没真付),币不会到账,晨间日志说明。</summary>
private static void FakePay(StoreClient client, string asset, GameItem item, int cost)
{
if (cost <= 0) return;
ModHelper.AddCredit(-cost, false);
Session.Data.FakePaidToday.Add(L.T("coinpay.fake.entry", client.displayName, item.name, cost));
Log.Info($"伪造转账:{client.displayName} 拿走 {item.name}({cost} 信用点),{asset} 不会到账");
}
/// <summary>假客服:自称 Poinbase 客服,说账户被风控,要把 CRC 转到"安全地址"。Poinbase 从不上门,也从不要求转账;转过去的币就没了。</summary>
public static StoreClient FakeSupportForTest() => FakeSupport();
private static StoreClient FakeSupport()
{
var d = Session.Data;
string addr = ChainAddress.Make(d.Rng.NextUInt());
d.Scams.ScamAddrs.Add(addr);
int amount = 100 * d.Rng.Range(3, 15);
var c = new StoreClient
{
identifier = "pd_fake_support", displayName = L.T("scam.support.name"), spriteName = SpriteDict.GetRandomUpperSpriteName(),
clientIntent = StoreClient.ClientIntent.DIALOGUE,
};
c.mainDialogue.SetText(c.displayName, L.T("scam.support.1")).NextDialogue()
.SetText(c.displayName, L.T("scam.support.2", amount, addr)).NextDialogue()
.SetText(c.displayName, L.T("scam.support.3"));
ModHelper.SetClientSource(c, ModInfo.Id);
c.AddBasicDialog();
c.CompleteClientCreation();
Log.Info($"假客服上门:要 {amount} CRC 转到 {addr}");
return c;
}
/// <summary>成交后顾客说的话:告诉玩家这单是币到账,不是信用点。</summary>
private static void Say(StoreClient client, string asset, double total)
{