2026-11-03 09:11:46 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using Decentralized.Domain;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Decentralized
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>上一晚的挖矿报告(终端"报告"页和晨间日志)。</summary>
|
|
|
|
|
|
public sealed class NightReport
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Day;
|
|
|
|
|
|
public int Rigs, PoweredRigs;
|
|
|
|
|
|
public double Hash, NetworkHash, Mined, Price, Kj;
|
|
|
|
|
|
public List<string> Burnt = new List<string>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-03 23:29:54 +08:00
|
|
|
|
/// <summary>地址投毒的计划:某个钱包转给某地址后,到了这一天收到一笔来自相似地址的 0 币转入。</summary>
|
|
|
|
|
|
public sealed class PoisonPlan
|
|
|
|
|
|
{
|
|
|
|
|
|
public string Wallet, Target;
|
|
|
|
|
|
public int Day;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-03 09:11:46 +08:00
|
|
|
|
/// <summary>一局的 mod 状态。整体序列化成 JSON 存进游戏的 mod 存档数据;显卡的健康度等存在显卡物品的标签上。</summary>
|
|
|
|
|
|
public sealed class SaveData
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Version = 1;
|
|
|
|
|
|
public int Day;
|
|
|
|
|
|
public Rng Rng = new Rng((ulong)DateTime.UtcNow.Ticks);
|
|
|
|
|
|
public Market Market;
|
|
|
|
|
|
public Stablecoins Stable = new Stablecoins();
|
2026-11-04 00:06:16 +08:00
|
|
|
|
/// <summary>革命军 CRA 质押池里玩家各钱包的仓位。</summary>
|
|
|
|
|
|
public Staking Staking = new Staking();
|
2026-11-04 00:26:50 +08:00
|
|
|
|
/// <summary>Poinbase 永续合约账本(保证金用 Poinbase 账户的 CRC)。</summary>
|
|
|
|
|
|
public PerpBook Perps = new PerpBook();
|
2026-11-04 07:26:27 +08:00
|
|
|
|
/// <summary>预测市场:盘口、持仓、观测记录(下注资金用 Poinbase 账户的 CRC)。</summary>
|
|
|
|
|
|
public PredictBook Predict = new PredictBook();
|
2026-11-04 08:11:03 +08:00
|
|
|
|
/// <summary>高息理财(庞氏):一个存档只能开一次。</summary>
|
|
|
|
|
|
public Ponzi Ponzi = new Ponzi();
|
2026-11-04 08:28:43 +08:00
|
|
|
|
/// <summary>NPC 骗局:空投与授权、第三方收益池、骗子地址。</summary>
|
|
|
|
|
|
public Scams Scams = new Scams();
|
2026-11-04 08:47:34 +08:00
|
|
|
|
/// <summary>日 K 历史(PSC,Poinbase 主池价格)。</summary>
|
|
|
|
|
|
public CandleBook Candles = new CandleBook();
|
2026-11-04 09:04:50 +08:00
|
|
|
|
/// <summary>游戏剧情选择对本 mod 世界的影响。</summary>
|
|
|
|
|
|
public StoryMods Story = new StoryMods();
|
2026-11-04 08:28:43 +08:00
|
|
|
|
/// <summary>今天被伪造转账骗走的(晨间日志用):顾客名 + 信用点。</summary>
|
|
|
|
|
|
public List<string> FakePaidToday = new List<string>();
|
2026-11-04 07:53:12 +08:00
|
|
|
|
/// <summary>近几天的 gas 指数(每晚记明天的),情报线索里显示;最近一份局势简报。</summary>
|
|
|
|
|
|
public List<double> GasHist = new List<double>();
|
|
|
|
|
|
public List<string> Brief = new List<string>();
|
|
|
|
|
|
public int BriefDay = -1;
|
2026-11-04 00:06:16 +08:00
|
|
|
|
/// <summary>质押日息下调的新闻已经发过。</summary>
|
|
|
|
|
|
public bool CraLowNews;
|
2026-11-03 09:11:46 +08:00
|
|
|
|
public Network Network = new Network();
|
2026-11-03 22:58:24 +08:00
|
|
|
|
/// <summary>链上钱包(硬件钱包,第一个是主钱包),各自的余额、污点、成本、冻结独立。</summary>
|
|
|
|
|
|
public List<Wallet> Wallets = new List<Wallet>();
|
|
|
|
|
|
/// <summary>终端当前连接的钱包:Web3 页的交易、黑市、混币器都用它,用币付款的顾客、挖矿收益也进它。</summary>
|
|
|
|
|
|
public int Current;
|
|
|
|
|
|
/// <summary>Poinbase 托管账户:银行存取、Poinbase 交易、出金审查都在这里。</summary>
|
|
|
|
|
|
public Wallet Poinbase = new Wallet { Address = "poinbase" };
|
|
|
|
|
|
|
|
|
|
|
|
[JsonIgnore] public Wallet Hot => Wallets[Math.Max(0, Math.Min(Wallets.Count - 1, Current))];
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>按地址找自己的钱包;找不到(钱包没了)退回当前钱包。</summary>
|
|
|
|
|
|
public Wallet WalletAt(string address) => Wallets.Find(w => ChainAddress.Same(w.Address, address)) ?? Hot;
|
|
|
|
|
|
|
|
|
|
|
|
public Wallet Own(string address) => Wallets.Find(w => ChainAddress.Same(w.Address, address));
|
|
|
|
|
|
|
2026-11-03 23:05:13 +08:00
|
|
|
|
/// <summary>钱包编号:每添加一个加一,销毁后不回收,名字不会错位。</summary>
|
|
|
|
|
|
public int WalletSeq;
|
|
|
|
|
|
|
2026-11-03 22:58:24 +08:00
|
|
|
|
public Wallet AddWallet(bool poisoned)
|
|
|
|
|
|
{
|
2026-11-03 23:05:13 +08:00
|
|
|
|
var w = new Wallet { Address = ChainAddress.Make(Rng.NextUInt()), Poisoned = poisoned, Name = (++WalletSeq).ToString() };
|
2026-11-03 22:58:24 +08:00
|
|
|
|
Wallets.Add(w);
|
|
|
|
|
|
return w;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>链上资产合计(全部钱包)。</summary>
|
|
|
|
|
|
public double ChainValue(Func<string, double> price) { double v = 0; foreach (var w in Wallets) v += w.Value(price); return v; }
|
2026-11-03 09:11:46 +08:00
|
|
|
|
public Exchange Exchange = new Exchange();
|
2026-11-03 16:39:40 +08:00
|
|
|
|
public Mixer Mixer = new Mixer();
|
2026-11-03 19:35:48 +08:00
|
|
|
|
public BlackMarket Black = new BlackMarket();
|
2026-11-03 09:11:46 +08:00
|
|
|
|
public List<MarketEvent> ActiveEvents = new List<MarketEvent>();
|
|
|
|
|
|
public NightReport LastNight;
|
|
|
|
|
|
public bool OpeningNewsShown;
|
2026-11-03 21:31:50 +08:00
|
|
|
|
/// <summary>各个收款方的地址(平台、混币器、Ponero 兑换处等),第一次用到时随机生成并存档。付款前要手输。</summary>
|
|
|
|
|
|
public Dictionary<string, string> Addresses = new Dictionary<string, string>();
|
|
|
|
|
|
|
|
|
|
|
|
public string Address(string key)
|
|
|
|
|
|
{
|
2026-11-03 22:29:42 +08:00
|
|
|
|
if (!Addresses.TryGetValue(key, out var a) || !ChainAddress.Valid(a)) { a = ChainAddress.Make(Rng.NextUInt()); Addresses[key] = a; }
|
2026-11-03 21:31:50 +08:00
|
|
|
|
return a;
|
|
|
|
|
|
}
|
2026-11-03 23:29:54 +08:00
|
|
|
|
/// <summary>网络拥堵的起止天(含)。</summary>
|
|
|
|
|
|
public int CongestedFrom = -1, CongestedUntil = -1;
|
2026-11-03 23:32:50 +08:00
|
|
|
|
/// <summary>当天的 gas 指数(夜间结算算出第二天的)与这一局的周期相位。</summary>
|
|
|
|
|
|
public double Gas = 1;
|
|
|
|
|
|
public int GasPhase = -1;
|
2026-11-03 23:29:54 +08:00
|
|
|
|
public List<PoisonPlan> PoisonPlans = new List<PoisonPlan>();
|
|
|
|
|
|
|
2026-11-03 22:29:42 +08:00
|
|
|
|
/// <summary>链上的区块高度:每签一笔交易往上走几块。</summary>
|
|
|
|
|
|
public long Block = 1204331;
|
2026-11-03 16:01:20 +08:00
|
|
|
|
/// <summary>当天用币付款的成交额(信用点),夜间清零。</summary>
|
|
|
|
|
|
public double CoinSalesToday;
|
2026-11-03 09:11:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>当前这一局的状态:读档 / 新游戏时载入,睡觉结算后写回。</summary>
|
|
|
|
|
|
public static class Session
|
|
|
|
|
|
{
|
|
|
|
|
|
private const string Key = "state";
|
|
|
|
|
|
public static SaveData Data;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>今晚有电的矿架上的显卡(矿架在夜间早段扣电时登记,结算时用)。</summary>
|
2026-11-03 09:16:46 +08:00
|
|
|
|
public static readonly List<KeyValuePair<GameItem, ModuleBonus>> TonightCards = new List<KeyValuePair<GameItem, ModuleBonus>>();
|
2026-11-03 09:11:46 +08:00
|
|
|
|
public static int TonightRigs, TonightPoweredRigs;
|
|
|
|
|
|
public static double TonightKj;
|
|
|
|
|
|
|
|
|
|
|
|
public static void Load()
|
|
|
|
|
|
{
|
|
|
|
|
|
Data = null;
|
2026-11-03 22:58:24 +08:00
|
|
|
|
WalletWindow.Reset();
|
2026-11-03 09:11:46 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
string json = ModHelper.GetModData(ModInfo.Id, Key);
|
|
|
|
|
|
if (!string.IsNullOrEmpty(json)) Data = JsonConvert.DeserializeObject<SaveData>(json);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex) { Log.Error($"读取存档数据失败,重新开始:{ex.Message}"); }
|
|
|
|
|
|
if (Data == null) Data = new SaveData();
|
|
|
|
|
|
if (Data.Market == null) Data.Market = Market.CreateDefault(StoreStation.GetDayCounter());
|
2026-11-03 22:29:42 +08:00
|
|
|
|
// 旧存档里的商家地址没有校验位:换成合法地址
|
|
|
|
|
|
foreach (var s in Data.Black.Sellers) if (!ChainAddress.Valid(s.Name)) s.Name = ChainAddress.Make(Data.Rng.NextUInt());
|
2026-11-03 22:58:24 +08:00
|
|
|
|
if (Data.Wallets.Count == 0) Data.AddWallet(false);
|
2026-11-04 09:04:50 +08:00
|
|
|
|
if (Data.Story == null) Data.Story = new StoryMods();
|
|
|
|
|
|
StoryMods.Current = Data.Story;
|
2026-11-03 09:36:12 +08:00
|
|
|
|
Log.Info($"状态已载入(第 {Data.Day} 天,PSC {Data.Market.Price:0.00})");
|
2026-11-03 09:11:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void Save()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Data == null) return;
|
|
|
|
|
|
try { ModHelper.SetModData(ModInfo.Id, Key, JsonConvert.SerializeObject(Data)); }
|
|
|
|
|
|
catch (Exception ex) { Log.Error($"写入存档数据失败:{ex.Message}"); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>链上资产的信用点价格。</summary>
|
|
|
|
|
|
public static double Price(string asset)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (asset == Assets.PSC) return Data.Market.Price;
|
|
|
|
|
|
var spec = Assets.Get(asset);
|
2026-11-03 19:35:48 +08:00
|
|
|
|
if (asset == Assets.Ponero) return BlackMarket.PoneroPrice(Data.Market.Price);
|
2026-11-04 00:06:16 +08:00
|
|
|
|
if (asset == Assets.SPARK) return Data.Stable.Spark;
|
2026-11-03 09:11:46 +08:00
|
|
|
|
if (spec != null && spec.Stable) return Data.Stable.Price(asset);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static double Rent() => PlayerStore.Instance != null ? PlayerStore.Instance.rentValue : 200;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|