Mod: mining rig, graphics cards, trading terminal, nightly settlement

First playable slice, tested in game on the test save:
- Network upgrade "Mining Access" (300) delivers a trading terminal, a
  mining rig and an RZX 50.
- The rig is a machine with a battery slot and four card slots; each night
  it draws its total power from the battery and its cards mine PSCoin.
  Cards show hashrate, power draw and condition, wear every night and can
  burn out. All state lives on item tags or in the mod save data.
- Trading terminal with wallet, trade (four pairs, deposit credits as CRC,
  withdraw CRC through the compliance desk) and report pages.
- Nightly log lines; exchange opening and halving news on the paper.
- English and Chinese text; sprites generated by art/sprites.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Opus 5.5 committed 2026-11-03 09:11:46 +08:00
1 parent efc58888f3
commit 750b26e4b3
21 files changed
+1040

No files matched your search

+140
View File
@@ -0,0 +1,140 @@
using System.Collections.Generic;
using System.Linq;
using Decentralized.Domain;
namespace Decentralized
{
/// <summary>
/// 每天的推进:开门时日内波动和 NPC 大户;睡觉时(游戏 EndNight 的最后)挖矿、损耗与暴毙、全网、稳定币、行情结算,写晨间日志,存档。
/// 矿架自己在夜间早段扣电并登记显卡(Items.NightDraw),这里只结算。
/// </summary>
public static class Night
{
public static void Subscribe()
{
ModHook.OnShutterOpenedLate += OnOpen;
ModHook.OnGoingSleepEarly += OnSleepEarly;
ModHook.OnGoingSleepLate += OnSleepLate;
}
public static void Unsubscribe()
{
ModHook.OnShutterOpenedLate -= OnOpen;
ModHook.OnGoingSleepEarly -= OnSleepEarly;
ModHook.OnGoingSleepLate -= OnSleepLate;
}
private static bool Active => Session.Data != null && Unlock.IsUnlocked;
/// <summary>游戏新闻事件与派系行动 → 行情事件。</summary>
public static List<MarketEvent> ActiveEvents()
{
var list = new List<MarketEvent>();
var mgr = StoreStation.Instance?.storeEventManager;
if (mgr == null) return list;
foreach (var e in mgr.GetActiveEvents())
{
switch (e.identifier)
{
case "stationWideBlackout": case "powerShortage": case "powerPlantMaintenance": list.Add(MarketEvent.Blackout); break;
case "crackdown": case "sec_martial_law": list.Add(MarketEvent.Crackdown); break;
case "bm_gang_war": list.Add(MarketEvent.GangWar); break;
case "rev_raid_upper_level": list.Add(MarketEvent.RevRaidUpper); break;
case "ll_riot": case "ll_worker_strike": list.Add(MarketEvent.LowerUnrest); break;
}
}
return list.Distinct().ToList();
}
private static void OnOpen()
{
if (!Active) return;
Log.Guard("开门行情", () =>
{
var d = Session.Data;
int day = StoreStation.GetDayCounter();
d.Market.Open(ActiveEvents(), d.Rng, day);
d.Market.Whale(d.Rng);
if (!d.OpeningNewsShown)
{
News.Show("pd_news_open", L.T("news.open.title"), L.T("news.open.desc"), 2);
d.OpeningNewsShown = true;
}
Session.Save();
});
}
private static void OnSleepEarly()
{
Session.TonightCards.Clear();
Session.TonightRigs = Session.TonightPoweredRigs = 0;
Session.TonightKj = 0;
}
private static void OnSleepLate()
{
if (!Active) return;
Log.Guard("夜间结算", Settle);
}
private static void Settle()
{
var d = Session.Data;
int day = StoreStation.GetDayCounter();
var events = ActiveEvents();
foreach (var ev in events.Where(e => !d.ActiveEvents.Contains(e))) d.Stable.OnEventStart(ev, day, d.Rng);
d.ActiveEvents = events;
d.Network.Availability = (events.Contains(MarketEvent.Blackout) ? 0.8 : 1) * (events.Contains(MarketEvent.LowerUnrest) ? 0.9 : 1);
var units = Session.TonightCards.Select(c => (item: c, unit: Items.ToUnit(c))).ToList();
double hash = units.Sum(x => x.unit.Hash(false));
double mined = d.Network.Mine(hash);
if (mined > 0) d.Hot.Add(Assets.PSC, mined);
var report = new NightReport
{
Day = day, Rigs = Session.TonightRigs, PoweredRigs = Session.TonightPoweredRigs, Kj = Session.TonightKj,
Hash = hash, NetworkHash = d.Network.NpcHash * d.Network.Availability + hash, Mined = mined,
};
foreach (var (item, unit) in units)
{
bool died = unit.Night(false, d.Rng);
Items.Store(item, unit);
if (died) report.Burnt.Add(item.name);
}
double rewardBefore = d.Network.Reward;
d.Network.Night(day, d.Market.Price, hash);
if (d.Network.Reward < rewardBefore) News.Show("pd_news_halving_" + day, L.T("news.halving.title"), L.T("news.halving.desc"), 2);
double crcFactor = d.Stable.Night(day, d.Rng, 0);
if (crcFactor < 1) d.Hot.Balances[Assets.CRC] = d.Hot.Get(Assets.CRC) * crcFactor;
d.Market.Night(events, d.Rng, day, Session.Price);
report.Price = d.Market.Price;
d.LastNight = report;
d.Day = day;
var ps = PlayerStore.Instance;
if (mined > 0) ps.AddNightLog(L.T("nightlog.mined", Fmt.Num(mined)));
foreach (var name in report.Burnt) ps.AddNightLog(L.T("nightlog.burnt", name), "cc7755");
int unpowered = report.Rigs - report.PoweredRigs;
if (unpowered > 0 && units.Count + unpowered > 0) ps.AddNightLog(L.T("nightlog.unpowered", unpowered), "cc7755");
Session.Save();
}
}
/// <summary>mod 新闻:不带价格修正的新闻事件,和原版新闻一样出现在报纸上。</summary>
public static class News
{
public static void Show(string id, string title, string text, int days)
{
var mgr = StoreStation.Instance?.storeEventManager;
if (mgr == null || mgr.IsEventActive(id)) return;
mgr.ActivateEvent(new StoreEvent
{
identifier = id, newsName = title, displayName = title, newsDescription = text,
duration = days, eventType = StoreEvent.EventType.COSMETIC, eventArea = StoreEvent.EventArea.ALL,
importance = 2, cooldown = 0, negociationDatas = new List<NegociationData>(),
});
}
}
}