2026-11-03 09:27:28 +08:00
using System.Collections.Generic ;
using System.Linq ;
2026-11-03 12:08:51 +08:00
using UnityEngine ;
2026-11-03 09:27:28 +08:00
using Decentralized.Domain ;
namespace Decentralized
{
/// <summary>
/// 杰克逊博士进货时(夜间去他店里,或他每周来本店摆摊)加上挖矿设备。用游戏给 mod 的进货钩子和他自己的上架函数,
2026-11-03 09:36:12 +08:00
/// 价格是物品价值加上他的加价(难度规则与夜间事件决定)。加入 Web3 治理社区之后才上货。
2026-11-03 09:27:28 +08:00
/// </summary>
public static class DealerStock
{
public static void Subscribe ()
{
ModHook . OnPlaceInventorInventoryItemLate += OnPlace ;
// 开发者模式(F9)里的测试入口:像到了杰克逊店里一样,清空并扩大柜台后上货
2026-11-03 10:01:16 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Give rig x3 (test)" , () => { for ( int i = 0 ; i < 3 ; i ++) Items . Give ( ModHelper . SpawnItem ( Items . Rig )); });
2026-11-03 11:26:11 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Used cards x4 (test)" , () =>
{
var rng = Session . Data . Rng ;
2026-11-03 18:34:57 +08:00
Items . Give ( Items . NewCard ( "RTX80" , 40 , null , rng ));
Items . Give ( Items . NewCard ( "RTX80" , 70 , "RTX60" , rng ));
Items . Give ( Items . NewCard ( "RX800" , 30 , "RX70" , rng ));
Items . Give ( Items . NewCard ( "RX900" , 90 , "RX70" , rng ));
2026-11-03 11:26:11 +08:00
});
2026-11-03 12:08:51 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Dump inspection UI (test)" , () => Log . Guard ( "检视界面结构" , () =>
{
var sb = new System . Text . StringBuilder ( "\n" );
void Walk ( Transform t , int depth )
{
var rt = t as RectTransform ;
var img = t . GetComponent < UnityEngine . UI . Image >();
sb . AppendLine ( $"{new string(' ', depth * 2)}{t.name} active={t.gameObject.activeSelf} pos={t.localPosition} size={(rt != null ? rt.sizeDelta.ToString() : " - ")} scale={t.localScale}{(img != null ? " img = " + (img.sprite != null ? img.sprite.name : " null ") : "")}" );
if ( depth < 5 ) foreach ( Transform c in t ) Walk ( c , depth + 1 );
}
var m = InspectionUIManager . Instance ;
2026-11-03 13:20:11 +08:00
var ci = PlayerStore . Instance . currentClientInstance ;
sb . AppendLine ( "当前顾客:" + ( ci == null ? "无" : ci . GetClientBlueprint (). identifier + " 意图 " + ci . GetClientBlueprint (). clientIntent ));
sb . AppendLine ( "检视物品:" + ( m . currentInspectingItem ?. identifier ?? "无" ) + " APPRAISAL=" + ( m . currentInspectingItem ?. IsTag ( "APPRAISAL_ITEM" ) ?? false ));
2026-11-03 12:08:51 +08:00
Walk ( m . panel . transform , 0 );
Log . Info ( sb . ToString ());
}));
2026-11-03 14:57:28 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Edit claim + offer to buyer (test)" , () => Log . Guard ( "卖出侧测试" , () =>
{
var rng = Session . Data . Rng ;
// 自己的一张真卡:标签机改成旗舰型号并把成色说成没问题,再摆给一位买家
2026-11-03 18:34:57 +08:00
var item = Items . NewCard ( "RTX70" , 55 , null , rng );
2026-11-03 14:57:28 +08:00
Items . Give ( item );
var u = Items . ToUnit ( item );
2026-11-03 18:34:57 +08:00
var c = Claim . Truth ( u ); c . Model = "RTX90" ; c . Fan = 0 ; c . Fingers = 0 ; c . Bracket = 0 ; c . Sticker = 0 ;
2026-11-03 14:57:28 +08:00
item . EnableTag ( Items . EditedTag );
Items . SetClaim ( item , c , 0 );
var client = new StoreClient { identifier = "pd_test_buyer" , displayName = "买家" , spriteName = SpriteDict . GetRandomScavSpriteName (), clientIntent = StoreClient . ClientIntent . BUY };
client . CompleteClientCreation ();
PlayerStore . Instance . currentClientInstance = StoreClientInstance . CreateClientInstance ( client );
Log . Info ( $"测试:卡 #{item.uniqueId} 说法 {c.Encode()},真实 {Claim.Truth(u).Encode()},价格 {item.unitValue},信誉前" );
Items . OfferForTest ( item );
Log . Info ( $"测试:验货后 Exposed={Items.Exposed(item)} 价格 {item.unitValue}" );
}));
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Buy scam card (test)" , () => Log . Guard ( "买入侧测试" , () =>
{
var rng = Session . Data . Rng ;
2026-11-03 18:34:57 +08:00
var item = Items . NewCard ( "RTX90" , 60 , "RTX60" , rng , DealerKind . Scammer , 0 );
2026-11-03 14:57:28 +08:00
long before = item . unitValue ;
GeneralHelper . SetItemOwned ( item , true );
Items . Refresh ( item );
Items . Give ( item );
Log . Info ( $"买入测试:卡 #{item.uniqueId} 买前报价 {before},买下后价值 {item.unitValue},真实 {CardValue.True(Items.ToUnit(item))},说法 {(Items.GetClaim(item)?.Encode() ?? " 无 ")}" );
}));
2026-11-04 00:26:50 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Poinbase +5000 CRC, unlock (test)" , () => Log . Guard ( "永续测试" , () =>
{
var d = Session . Data ;
d . Poinbase . Add ( Assets . CRC , 5000 ); d . Poinbase . Basis += 5000 ;
if (! Unlock . IsUnlocked ) ModHelper . GetNetworkUpgrade ( Unlock . Id )?. Unlock ();
Log . Info ( $"测试:Poinbase CRC {d.Poinbase.Get(Assets.CRC):0},解锁 {Unlock.IsUnlocked}" );
}));
2026-11-04 00:58:14 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Give ASIC M1/M2/M3 (test)" , () => { foreach ( var m in new [] { "M1" , "M2" , "M3" }) Items . Give ( AsicItems . New ( m , 100 )); for ( int i = 0 ; i < 4 ; i ++) Items . Give ( ModHelper . SpawnItem ( "rare_electronic" )); });
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Era in 2 days (test)" , () => { var n = Session . Data . Network ; int day = StoreStation . GetDayCounter (); n . EraDay = day + 2 ; n . EraWarnDay = day ; n . EraWarned = false ; n . EraDone = false ; Log . Info ( $"测试:矿机时代第 {n.EraDay} 天,NPC 耗电 {n.NpcKj}" ); });
2026-11-04 08:03:44 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Predict: refill all kinds (test)" , () => Log . Guard ( "预测测试" , () =>
2026-11-04 07:26:27 +08:00
{
var d = Session . Data ; int day = StoreStation . GetDayCounter ();
d . Predict . Markets . Clear ();
2026-11-04 07:53:12 +08:00
var made = new List < PredictMarket >(); for ( int i = 0 ; i < 4 ; i ++) made . AddRange ( d . Predict . Refill ( day , d . Rng , PredictGame . Candidates ( day )));
2026-11-04 07:26:27 +08:00
Log . Info ( $"测试:新开 {made.Count} 个预测盘口(不看解锁日)" );
}));
2026-11-04 08:03:44 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Predict: add era and CRA (test)" , () =>
2026-11-04 07:53:12 +08:00
{
var d = Session . Data ; int day = StoreStation . GetDayCounter (); var bk = d . Predict ;
bk . Markets . Add ( PredictMarket . Create (++ bk . Seq , PredictKind . Era , "era" , L . T ( "predict.era" ), 0 , day , day + 1 , day + 3 , PredictMath . EraProb ( day , day + 1 , day + 3 , - 1 ) + 0.05 ));
double h = 1 - System . Math . Pow ( 1 - Tuning . CraSpiralWeekly , 1.0 / 7 );
bk . Markets . Add ( PredictMarket . Create (++ bk . Seq , PredictKind . Cra , "cra" , "CRA" , 0 , day , day + 1 , day + 3 , PredictMath . CraWarnProb ( d . Stable . CraSubsidy , ( Tuning . CraNpcStake + d . Staking . Earning ) * d . Stable . CraRate , day , day + 1 , day + 3 , h )));
Log . Info ( $"测试:矿机时代 {d.Network.EraDay}, CRA 补贴池 {d.Stable.CraSubsidy:0}" );
});
2026-11-04 07:26:27 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Predict: window = tomorrow (test)" , () => { foreach ( var m in Session . Data . Predict . Markets ) { m . FromDay = m . ToDay = StoreStation . GetDayCounter () + 1 ; } Log . Info ( "测试:所有盘口窗口改成明天" ); });
2026-11-04 00:26:50 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Perp fund 0 (test)" , () => { Session . Data . Perps . InsuranceFund = 0 ; Log . Info ( "测试:保险基金清零" ); });
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "SPARK x0.3 (test)" , () => { var st = Session . Data . Stable ; st . Spark *= 0.3 ; Log . Info ( $"测试:SPARK 价格 {st.Spark:0.00}" ); });
2026-11-04 07:53:12 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Wallet gift: CRC, CRA, PSC (test)" , () => { var w = Session . Data . Hot ; w . Add ( Assets . CRA , 1000 ); w . Add ( Assets . CRC , 1000 ); w . Add ( Assets . CRT , 3000 ); w . Add ( Assets . PSC , 20 ); w . Basis += 5000 ; Log . Info ( $"测试:{WalletWindow.NameOf(w)} CRA {w.Get(Assets.CRA):0}" ); });
2026-11-04 00:06:16 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "CRA spiral warning (test)" , () =>
{
var st = Session . Data . Stable ; int day = StoreStation . GetDayCounter ();
st . CraWarnDay = day ; st . CraSpiralDay = day + 3 ;
Log . Info ( $"测试:CRA 预兆今天,螺旋第 {st.CraSpiralDay} 天" );
});
2026-11-03 23:55:37 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Give terminal (test)" , () => { Items . Give ( ModHelper . SpawnItem ( Items . Terminal )); });
2026-11-03 22:58:24 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Hardware wallets (test)" , () => { Items . Give ( Items . NewHwWallet ( false )); Items . Give ( Items . NewHwWallet ( true )); Log . Info ( "测试:给了一个正品、一个被投毒的硬件钱包" ); });
2026-11-03 23:29:54 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Chain drama (test)" , () => Log . Guard ( "链上事件测试" , () =>
{
// 今天拥堵;当前钱包最近转出过的地址立刻收到相似地址的 0 币转入;报纸和论坛各来一条钓鱼
var d = Session . Data ;
int day = StoreStation . GetDayCounter ();
d . CongestedFrom = day ; d . CongestedUntil = day + 1 ;
var last = d . Hot . History . FirstOrDefault ( r => ! r . In );
if ( last != null ) TxRules . Record ( d . Hot . History , new TxRecord { Addr = ChainAddress . Lookalike ( last . Addr , d . Rng ), Asset = Assets . PSC , Amount = 0 , Day = day , In = true });
string fake = ChainAddress . Make ( d . Rng . NextUInt ());
News . Show ( "pd_news_phish_test_" + day , L . T ( "news.phish.title" ), L . T ( "news.phish.desc" , fake ), 2 );
d . Black . Forum . Add ( "phish:" + ChainAddress . Make ( d . Rng . NextUInt ()));
Log . Info ( $"测试:拥堵到第 {d.CongestedUntil} 天;投毒 {(last != null ? last.Addr : " 无转出记录 ")};钓鱼 {fake}" );
}));
2026-11-03 09:27:28 +08:00
ModHelper . AddDebugFunction ( ModInfo . Id , ModInfo . Name , "Dr. Jackson stock (test)" , () =>
{
EmporiumEntry . Instance . ClearFrontInv ();
EmporiumEntry . Instance . ExpandFrontCounterGrid ();
StoreClientList . PlaceInventorInventory ();
});
}
public static void Unsubscribe ()
{
ModHook . OnPlaceInventorInventoryItemLate -= OnPlace ;
ModHelper . RemoveDebugFunctions ( ModInfo . Id );
}
private static void OnPlace ( List < GameItem > owned )
{
if ( Session . Data == null || ! Unlock . IsUnlocked ) return ;
Log . Guard ( "杰克逊博士上货" , () =>
{
bool ownsRig = owned != null && owned . Any ( i => i . identifier == Items . Rig );
bool special = AfterhourEvents . IsActive ( "special_stock" );
var placed = new List < string >();
foreach ( var kind in Dealer . Stock ( StoreStation . GetDayCounter (), ownsRig , special , Session . Data . Rng ))
{
var item = ModHelper . SpawnItem ( kind == Dealer . Rig ? Items . Rig : Items . GpuId ( kind ));
bool ok = PlayerStore . Instance . TryAddDirectSellingItemToTable ( item );
placed . Add ( ok ? $"{kind}#{item.uniqueId}" : kind + "(柜台已满)" );
}
2026-11-03 22:58:24 +08:00
// 硬件钱包(正品):钱包和手上的硬件钱包加起来还没到上限时,有一定概率上一个
int wallets = Session . Data . Wallets . Count + ( owned ?. Count ( i => i . identifier == Items . HwWallet ) ?? 0 );
if ( wallets < Tuning . WalletMax && Session . Data . Rng . Chance ( Tuning . WalletJacksonChance ))
{
var hw = Items . NewHwWallet ( false );
placed . Add ( PlayerStore . Instance . TryAddDirectSellingItemToTable ( hw ) ? $"硬件钱包#{hw.uniqueId}" : "硬件钱包(柜台已满)" );
}
2026-11-03 09:27:28 +08:00
Log . Info ( "杰克逊博士上货:" + string . Join ( ", " , placed ));
});
}
}
}