Stage 6.3: RatDigg ASIC miners, miner dealers on the black market, miner era event
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b249747855
commit
7d13ae2fe8
18 files changed
+393
-17
No files matched your search
@@ -4,6 +4,11 @@
|
||||
|
||||
### 第六阶段:高收益与高风险(进行中)
|
||||
|
||||
- RatDigg 矿机(M1 / M2 / M3,11,000 / 27,000 / 72,000,50 / 120 / 300 H,60 / 110 / 220 kJ/天):独立的 Machinery,窗口里有电池槽、模块槽(和矿架同一套效果与上限)、修理槽,没有显卡槽;夜间和矿架一样先从电池扣电、有电才挖,算入全网算力。损耗每天 0.8,暴毙系数 0.5;没有外观、没有真假。暴毙后成"损坏的矿机"(残值 15%),往修理槽按顺序放电子元件修好(M1:4 普通 + 1 稀有;M2:2 稀有;M3:1 超科技 + 1 稀有,约机价的 4%),修好健康 −20。新画了三张像素图(M1 绿、M2 蓝、M3 金的指示灯与风扇轴心)。
|
||||
- 来源只在 Web3 黑市:新增商家类型"矿机商",声望 ≥ 40 起每周 25% 出现,一次一台,70% 全新、其余二手(健康 40–85,价格按健康折算);型号按声望放开(M1 40、M2 60、M3 80),出现权重 1 / 0.6 / 0.4;价格系数同尖端店铺 0.85–1.00。可以在"卖货"页转手,损坏的不能上架。
|
||||
- "矿机时代":第 50–90 天之间随机一天(存档接入得晚则至少留出预兆天数),提前 4–7 天上报纸预告,到点 NPC 矿工耗电效率翻倍(`NpcKj` 2.4 → 1.2),永久。NPC 愿意挖得更久,均衡算力上升,显卡矿工的收益被摊薄。
|
||||
- 复核(ops `research/asicsim`,真实 Network 与 Market,每组 150 局,玩家另有一个 20 H 的矿架,电费按电网价 1.5):买入后累计回本——M1 第 20 天买 20 天、第 60 天买 22 天(有矿机时代 28 天)、第 100 天买 32 天(有矿机时代 61 天);M2 21 / 23 / 34 天(有时代 28 / 59);M3 26 / 28 / 42 天(有时代 37 / 65)。矿机时代之后一台 M1 每天净赚约 140–210,仍然为正。M3 要 7.2 万现金和声望 80,实际上早期买不起。
|
||||
- 游戏内验证:三台矿机可以造出、放进柜台、打开窗口(电池 / 模块 / 修理三个面板);放自充电池后夜里 M1 挖出 11.83 PSC(= 50/410 × 100 × 0.97);手动标记损坏后,修理槽只收这一步需要的元件(M2 拒收普通元件,M1 收下 4 个普通元件进入下一步,M3 收下超科技元件进入下一步),M2 放两个稀有元件后"修好了,健康 80";"矿机时代"调试入口:第 3 天日志"矿机时代到来";黑市"矿机商"货架:「RatDigg M1 矿机 矿机 全新 剩 1 件 11148 信用点等值」带图标。没有在游戏里验证:真实暴毙(只有单元测试)、报纸上的两条矿机时代新闻、从黑市买下矿机后的收货、模块对矿机的效果。
|
||||
- Poinbase 永续合约(Poinbase 页"永续"子页):PSC-PERP 最高 5 倍、单仓名义上限 20000,SPARK-PERP 最高 3 倍、上限 5000;保证金用 Poinbase 账户的 CRC,一个品种一个净仓位(同品种不能多空同开),托管账户不用签名。成交价按名义价值占现货池深的比例滑点(深度倍数 3:名义 5000、池深 5 万时单边约 3.3%),开平仓各收 0.1%。
|
||||
- 标记价 = 当日均价(开盘价与收盘价的平均,SPARK 取当天价),强平只在夜间检查:权益低于名义价值的 10% 时强平,保证金没了,剩余权益(扣 2% 强平费后)进保险基金。每晚资金费 = 0.1 × (标记价 / 7 日均价 − 1),上下限 ±0.3%,价格偏高时多头付空头。开仓时按现价算权益若已低于强平线再加 2%,直接拒绝(滑点太大,开了就是被强平)。
|
||||
- 保险基金初始 10000,每晚 +150,手续费的 30% 进基金;NPC 在大行情(标记价日涨跌超过 6%)里穿仓会按涨跌幅的平方抽走基金(SPARK 崩盘能抽干)。基金见底时盈利仓位被自动减仓,少拿一半盈利。强平穿仓的缺口先由基金垫,不够的记成玩家的欠款。
|
||||
|
||||
@@ -607,6 +607,33 @@ def guide():
|
||||
return c
|
||||
|
||||
|
||||
def asic(model):
|
||||
"""RatDigg 矿机(48×32,3×2 格):宽扁的金属机箱,正面两个大风扇,顶上一条铭牌带三个指示灯和型号;
|
||||
指示灯与风扇轴心的颜色区分档次(M1 绿、M2 蓝、M3 金)。光从左上来。"""
|
||||
c = Canvas(48, 32)
|
||||
accent = {'M1': 'G', 'M2': 'u', 'M3': 'y'}[model]
|
||||
c.rect(3, 1, 44, 5, 'l') # 顶面
|
||||
c.rect(3, 1, 44, 1, 'h')
|
||||
c.rect(43, 2, 44, 5, 'b')
|
||||
c.rect(2, 6, 45, 29, 'm') # 正面
|
||||
c.rect(2, 6, 2, 29, 'b'); c.rect(45, 6, 45, 29, 'd'); c.rect(2, 29, 45, 29, 'd')
|
||||
c.rect(4, 7, 43, 10, 'n') # 铭牌带
|
||||
for x in (6, 9, 12):
|
||||
c.put(x, 8, accent); c.put(x, 9, accent if x == 6 else 'k')
|
||||
text(c, 32, 7, model.replace('M', 'M'), 'w')
|
||||
for fx in (14, 34): # 风扇:方框风道 + 圆扇
|
||||
c.rect(fx - 9, 12, fx + 9, 28, 'n')
|
||||
c.rect(fx - 9, 12, fx + 9, 12, 'g'); c.rect(fx - 9, 12, fx - 9, 28, 'g')
|
||||
fan(c, fx, 20, accent, R=8)
|
||||
c.rect(22, 12, 26, 28, 'd') # 两扇之间的散热栅
|
||||
for y in range(13, 28, 2):
|
||||
c.rect(23, y, 25, y, 'k')
|
||||
c.rect(4, 30, 9, 31, 'k'); c.rect(38, 30, 43, 31, 'k') # 脚垫
|
||||
c.rect(10, 30, 37, 30, 'd')
|
||||
outline(c)
|
||||
return c
|
||||
|
||||
|
||||
def hw_wallet():
|
||||
"""PLEDGER 硬件钱包(16×16,一格):竖放的石墨卡片,大块浅色墨水屏,右侧一个侧键。"""
|
||||
c = Canvas(16, 16)
|
||||
@@ -631,6 +658,9 @@ SPRITES = {
|
||||
'rig': rig,
|
||||
'terminal': terminal,
|
||||
'hw_wallet': hw_wallet,
|
||||
'asic_m1': lambda: asic('M1'),
|
||||
'asic_m2': lambda: asic('M2'),
|
||||
'asic_m3': lambda: asic('M3'),
|
||||
}
|
||||
for _tier in range(4):
|
||||
SPRITES['gpu_probidia_f_t%d' % _tier] = (lambda t=_tier: gpu_front('G', 'g2', tier=t))
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace Decentralized.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// RatDigg 矿机暴毙后的修理:往机器的修理槽里按顺序放电子元件,约占机价的 4%。每一步只收这一步需要的那种元件,攒够这一步再进下一步,
|
||||
/// 全部攒够就修好(健康扣 <see cref="Tuning.AsicRepairHealthCut"/> 点)。游戏里电子元件的价值是 33 / 350 / 2,500(普通 / 稀有 / 超科技)。
|
||||
/// </summary>
|
||||
public static class AsicRepair
|
||||
{
|
||||
public static (string Item, int Count)[] Steps(string model)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case "M1": return new[] { ("common_electronic", 4), ("rare_electronic", 1) };
|
||||
case "M2": return new[] { ("rare_electronic", 2) };
|
||||
case "M3": return new[] { ("hypertech_electronic", 1), ("rare_electronic", 1) };
|
||||
default: throw new ArgumentOutOfRangeException(nameof(model));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>当前这一步需要的元件;已修好(step 越界)返回 null。</summary>
|
||||
public static (string Item, int Count)? Need(string model, int step)
|
||||
{
|
||||
var steps = Steps(model);
|
||||
return step < steps.Length ? steps[step] : ((string, int)?)null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 投入 count 件 item:只收当前这一步需要的那种,攒够就进下一步。返回实际收下的件数;step 达到总步数表示修好了。
|
||||
/// </summary>
|
||||
public static int Insert(string model, ref int step, ref int progress, string item, int count)
|
||||
{
|
||||
var need = Need(model, step);
|
||||
if (need == null || count <= 0 || item != need.Value.Item) return 0;
|
||||
int take = Math.Min(count, need.Value.Count - progress);
|
||||
progress += take;
|
||||
if (progress >= need.Value.Count) { step++; progress = 0; }
|
||||
return take;
|
||||
}
|
||||
|
||||
public static bool Done(string model, int step) => step >= Steps(model).Length;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Decentralized.Domain
|
||||
{
|
||||
public enum PayMode { Escrow, Advance }
|
||||
/// <summary>商家类型:矿卡(全新 / 二手)、矿架能源、模组、军火、枪匠配件、证件芯片、药物、化学、酿酒、水培、养殖、矿工、水务、仓储、工具、奢侈品;大宗商家、尖端店铺(只卖贵货,概率低)、个人卖家(什么都卖,价格随心所欲)随机出现。</summary>
|
||||
public enum MerchantType { GpuNew, GpuUsed, Rigs, Modules, Arms, GunMods, Docs, Pharma, Chem, Brewer, Farm, Husbandry, Miner, Water, Storage, Tools, Luxury, Bulk, Elite, Personal }
|
||||
public enum MerchantType { GpuNew, GpuUsed, Rigs, Modules, Arms, GunMods, Docs, Pharma, Chem, Brewer, Farm, Husbandry, Miner, Water, Storage, Tools, Luxury, Bulk, Elite, Personal, Asic }
|
||||
public enum OrderState { Pending, Ready, Done, Refunded, Lost }
|
||||
|
||||
/// <summary>黑市上的卖家:成交数与好评率公开;是不是骗子是隐藏的。</summary>
|
||||
@@ -131,6 +131,7 @@ namespace Decentralized.Domain
|
||||
private List<BmListing> MerchantGoods(int idx, BmSeller s, int day, int rep, Rng rng, Func<string, long> value)
|
||||
{
|
||||
var list = new List<BmListing>();
|
||||
if (s.Type == MerchantType.Asic) return AsicGoods(idx, s, rep, rng);
|
||||
bool gpuKind = s.Type == MerchantType.GpuNew || s.Type == MerchantType.GpuUsed;
|
||||
int n = s.Type == MerchantType.Bulk ? 2 : s.Type == MerchantType.Elite ? Tuning.BmEliteListings : s.Type == MerchantType.Personal ? Tuning.BmPersonalListings : Tuning.BmMerchantListings;
|
||||
List<BmEntry> entries = gpuKind ? new List<BmEntry>() : s.Type == MerchantType.Bulk || s.Type == MerchantType.Personal || s.Type == MerchantType.Elite ? AllEntries() : Merchants.Table[s.Type].ToList();
|
||||
@@ -177,6 +178,27 @@ namespace Decentralized.Domain
|
||||
return list;
|
||||
}
|
||||
|
||||
public static int AsicRep(string model) => model == "M3" ? Tuning.AsicRepM3 : model == "M2" ? Tuning.AsicRepM2 : Tuning.AsicRepM1;
|
||||
private static double AsicWeight(string model) => model == "M3" ? Tuning.AsicWeightM3 : model == "M2" ? Tuning.AsicWeightM2 : Tuning.AsicWeightM1;
|
||||
|
||||
/// <summary>矿机商:一台 RatDigg 矿机,型号按黑市声望放开,新机(70%)或二手;价格 = 机价(二手按健康度折算)× 商家系数。</summary>
|
||||
private List<BmListing> AsicGoods(int idx, BmSeller s, int rep, Rng rng)
|
||||
{
|
||||
var models = new[] { "M1", "M2", "M3" }.Where(m => rep >= AsicRep(m)).ToList();
|
||||
if (models.Count == 0) return new List<BmListing>();
|
||||
double roll = rng.NextDouble() * models.Sum(AsicWeight);
|
||||
string model = models.Last();
|
||||
foreach (var m in models) { roll -= AsicWeight(m); if (roll <= 0) { model = m; break; } }
|
||||
bool fresh = rng.Chance(Tuning.AsicNewShare);
|
||||
double health = fresh ? 100 : rng.Range(Tuning.AsicUsedHealthMin, Tuning.AsicUsedHealthMax);
|
||||
double factor = s.Factor * F(rng, 1 - Tuning.BmPriceJitter, 1 + Tuning.BmPriceJitter);
|
||||
return new List<BmListing>
|
||||
{
|
||||
new BmListing { Seller = idx, Item = "gpu:" + model, Kind = GoodKind.Gpu, Level = 2, Model = model, Health = health, Qty = model == "M1" ? rng.Range(1, 2) : 1,
|
||||
Price = (int)Math.Max(1, Math.Round(CardValue.Of(model, health) * factor)) },
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>每周重新生成商家、货架和论坛情报(与现货同一天刷新):从 6 种类型里抽 BmMerchantCount 家,另有概率出现一家大宗商家。</summary>
|
||||
public void RefreshOrders(int day, int rep, Rng rng, Func<string, long> value, bool allTypes = false)
|
||||
{
|
||||
@@ -188,6 +210,7 @@ namespace Decentralized.Domain
|
||||
{
|
||||
if (rng.Chance(Tuning.BmBulkChance)) types.Add(MerchantType.Bulk);
|
||||
if (rng.Chance(Tuning.BmEliteChance)) types.Add(MerchantType.Elite);
|
||||
if (rep >= Tuning.AsicRepM1 && rng.Chance(Tuning.AsicChance)) types.Add(MerchantType.Asic);
|
||||
for (int i = 0; i < Tuning.BmPersonalCount; i++) types.Add(MerchantType.Personal);
|
||||
}
|
||||
foreach (var t in types)
|
||||
@@ -196,8 +219,8 @@ namespace Decentralized.Domain
|
||||
bool scam = rng.Chance(personal ? Tuning.BmPersonalScam : Tuning.BmScamSellerShare);
|
||||
// 骗子多半是新号,也有刷过好评的老号;个人卖家都是新号
|
||||
bool fresh = personal || (scam ? rng.Chance(Tuning.BmScamFresh) : rng.Chance(Tuning.BmHonestFresh));
|
||||
double lo = t == MerchantType.Elite ? Tuning.BmFactorEliteMin : t == MerchantType.GpuNew ? Tuning.BmFactorNewMin : t == MerchantType.GpuUsed ? Tuning.BmFactorUsedMin : t == MerchantType.Bulk ? Tuning.BmFactorBulkMin : Tuning.BmFactorMin;
|
||||
double hi = t == MerchantType.Elite ? Tuning.BmFactorEliteMax : t == MerchantType.GpuNew ? Tuning.BmFactorNewMax : t == MerchantType.GpuUsed ? Tuning.BmFactorUsedMax : t == MerchantType.Bulk ? Tuning.BmFactorBulkMax : Tuning.BmFactorMax;
|
||||
double lo = t == MerchantType.Elite || t == MerchantType.Asic ? Tuning.BmFactorEliteMin : t == MerchantType.GpuNew ? Tuning.BmFactorNewMin : t == MerchantType.GpuUsed ? Tuning.BmFactorUsedMin : t == MerchantType.Bulk ? Tuning.BmFactorBulkMin : Tuning.BmFactorMin;
|
||||
double hi = t == MerchantType.Elite || t == MerchantType.Asic ? Tuning.BmFactorEliteMax : t == MerchantType.GpuNew ? Tuning.BmFactorNewMax : t == MerchantType.GpuUsed ? Tuning.BmFactorUsedMax : t == MerchantType.Bulk ? Tuning.BmFactorBulkMax : Tuning.BmFactorMax;
|
||||
var s = new BmSeller
|
||||
{
|
||||
Name = ChainAddress.Make(rng.NextUInt()),
|
||||
|
||||
@@ -152,6 +152,10 @@ namespace Decentralized.Domain
|
||||
public sealed class Network
|
||||
{
|
||||
public double NpcHash = Tuning.NpcHash0;
|
||||
/// <summary>NPC 矿工每 H 的耗电(kJ):矿机时代到来后减半,他们能挖得更久,全网算力均衡点升高。</summary>
|
||||
public double NpcKj = Tuning.NpcKjPerH;
|
||||
public int EraDay = -1, EraWarnDay = -1;
|
||||
public bool EraWarned, EraDone;
|
||||
public double Reward = Tuning.BlockReward;
|
||||
/// <summary>停电、动荡等事件对 NPC 算力的可用比例。</summary>
|
||||
public double Availability = 1.0;
|
||||
@@ -164,6 +168,25 @@ namespace Decentralized.Domain
|
||||
return Reward * playerHash / total * (1 - Tuning.PoolFee);
|
||||
}
|
||||
|
||||
public enum EraStep { None, Warn, Start }
|
||||
|
||||
/// <summary>
|
||||
/// "矿机时代":第一次调用时在 EraMin–EraMax 天之间随机定一天(至少留出预兆的天数),提前 4–7 天预兆,到点 NPC 耗电效率翻倍,永久。
|
||||
/// 返回今晚发生的一步(预兆 / 开始)。
|
||||
/// </summary>
|
||||
public EraStep EraNight(int day, Rng rng)
|
||||
{
|
||||
if (EraDone) return EraStep.None;
|
||||
if (EraDay < 0)
|
||||
{
|
||||
EraDay = Math.Max(day + Tuning.EraWarnMax + 1, rng.Range(Tuning.EraMin, Tuning.EraMax));
|
||||
EraWarnDay = EraDay - rng.Range(Tuning.EraWarnMin, Tuning.EraWarnMax);
|
||||
}
|
||||
if (day >= EraDay) { NpcKj *= Tuning.EraKjMult; EraDone = true; return EraStep.Start; }
|
||||
if (!EraWarned && day >= EraWarnDay) { EraWarned = true; return EraStep.Warn; }
|
||||
return EraStep.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 夜间:第 HalvingDays 天的倍数时减半;NPC 矿工按"每 H 收益 / 电费"与目标比的偏离扩张或关机,每晚最多 ±5%。
|
||||
/// </summary>
|
||||
@@ -172,7 +195,7 @@ namespace Decentralized.Domain
|
||||
if (day > 0 && day % Tuning.HalvingDays == 0) Reward /= 2;
|
||||
double total = NpcHash * Availability + playerHash;
|
||||
double perH = Reward * price / total;
|
||||
double ratio = perH / (Tuning.NpcKjPerH * Tuning.ElecPrice);
|
||||
double ratio = perH / (NpcKj * Tuning.ElecPrice);
|
||||
double adj = Math.Max(-Tuning.NpcAdj, Math.Min(Tuning.NpcAdj, Tuning.NpcAdj * (ratio / Tuning.NpcTargetRatio - 1)));
|
||||
NpcHash *= 1 + adj;
|
||||
}
|
||||
|
||||
@@ -176,6 +176,18 @@ namespace Decentralized.Domain
|
||||
public const int PerpPscLev = 5, PerpSparkLev = 3;
|
||||
public const double PerpPscCap = 20000, PerpSparkCap = 5000;
|
||||
|
||||
/// <summary>RatDigg 矿机(ASIC):黑市矿机商每周出现的概率;M1 / M2 / M3 需要的黑市声望与出现权重;新机占比与二手的健康度范围。</summary>
|
||||
public const double AsicChance = 0.25, AsicNewShare = 0.7;
|
||||
public const int AsicRepM1 = 40, AsicRepM2 = 60, AsicRepM3 = 80;
|
||||
public const double AsicWeightM1 = 1.0, AsicWeightM2 = 0.6, AsicWeightM3 = 0.4;
|
||||
public const int AsicUsedHealthMin = 40, AsicUsedHealthMax = 85;
|
||||
/// <summary>修好之后健康扣的点数;损坏的机器剩余价值占机价的比例。</summary>
|
||||
public const int AsicRepairHealthCut = 20;
|
||||
public const double AsicDeadValue = 0.15;
|
||||
/// <summary>"矿机时代":第几天到第几天之间随机发生一次(永久),提前几天上报纸;NPC 矿工的耗电乘数(耗电效率翻倍 = 0.5)。</summary>
|
||||
public const int EraMin = 50, EraMax = 90, EraWarnMin = 4, EraWarnMax = 7;
|
||||
public const double EraKjMult = 0.5;
|
||||
|
||||
/// <summary>主交易对(信用点一侧)的深度随天数增长。</summary>
|
||||
public static double PoolDepth(int day) => PoolDepth0 * (1 + day / 60.0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Decentralized.Domain;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Decentralized
|
||||
{
|
||||
/// <summary>
|
||||
/// RatDigg 矿机(M1 / M2 / M3):独立的机器,窗口里有电池槽、模块槽(和矿架同一套效果)和修理槽,没有显卡槽。
|
||||
/// 夜间和矿架一样先从电池扣电、有电才挖;损耗与暴毙沿用 <see cref="Unit"/>(暴毙系数 0.5,每天损耗 0.8)。
|
||||
/// 暴毙后成了"损坏的矿机":往修理槽里按顺序放电子元件修好(见 <see cref="AsicRepair"/>),健康扣 20 点。
|
||||
/// </summary>
|
||||
public static class AsicItems
|
||||
{
|
||||
public const string Prefix = "pd_asic_";
|
||||
public const string StepTag = "PD_REPAIR_STEP", ProgTag = "PD_REPAIR_PROG";
|
||||
|
||||
public static string Id(string model) => Prefix + model.ToLowerInvariant();
|
||||
public static bool Is(GameItem it) => it != null && it.identifier != null && it.identifier.StartsWith(Prefix);
|
||||
|
||||
public static void Register(ModItemDirectory dir)
|
||||
{
|
||||
foreach (var spec in Hardware.Specs.Values.Where(s => s.Asic))
|
||||
{
|
||||
var s = spec;
|
||||
dir.Add(Id(s.Model), () => Create(s));
|
||||
}
|
||||
}
|
||||
|
||||
private static int Step(GameItem m) => m.IsTag(StepTag) ? m.GetTagReadonly(StepTag).GetInt() : 0;
|
||||
private static int Prog(GameItem m) => m.IsTag(ProgTag) ? m.GetTagReadonly(ProgTag).GetInt() : 0;
|
||||
|
||||
private static GameItem Create(HardwareSpec spec)
|
||||
{
|
||||
var battery = new GameSlotInventory();
|
||||
var repair = new GameSlotInventory();
|
||||
var modules = new GameGridInventory(MachineHelper.DEFAULT_MODULE_BAY_CONFIG, 7);
|
||||
var window = MachineHelper.CreateMachineryWindow(true,
|
||||
MachineHelper.PowerPanel(battery),
|
||||
MachineHelper.Panel(L.T("rig.label.modules"), modules, "module"),
|
||||
MachineHelper.Panel(L.T("asic.label.repair"), repair, "upgrade"));
|
||||
var m = ItemDirectory.CreateEmptyItem(Id(spec.Model)).SetContentWindow(window);
|
||||
m.SetSpriteAndShapeFromMod(ModInfo.Id, "asic_" + spec.Model.ToLowerInvariant());
|
||||
m.name = L.T("item.asic.name", spec.Model);
|
||||
m.unitValue = spec.Price;
|
||||
MachineHelper.SetupBatterySlot(battery, m);
|
||||
m.EnableTag("CONTAINER_TAG");
|
||||
m.EnableTag("STANDARD_MACHINE_TAG");
|
||||
MachineryHelper.InitMachinery(m, (int)Math.Ceiling(spec.Kj));
|
||||
MachineHelper.SetupModuleBay(modules, m, () => Update(m, modules));
|
||||
repair.mayInventoryAddItemFunc = (it, _) =>
|
||||
{
|
||||
var u = Items.ToUnit(m);
|
||||
var need = AsicRepair.Need(u.Model, Step(m));
|
||||
return GeneralHelper.IsItemOwned(it) && u.Dead && need != null && it.identifier == need.Value.Item;
|
||||
};
|
||||
repair.onSlotAddItemEndFunc = (it, _, _) => Log.Guard("矿机修理", () => TakeRepair(m, repair, modules, it));
|
||||
m.onLoaded = _ => Update(m, modules);
|
||||
m.onCycleEndEarlySlotItemFunc = (_, _, _) => Log.Guard("矿机扣电", () => NightDraw(m, battery, modules));
|
||||
ModHelper.SetSource(m, ModInfo.Id, ModInfo.Name);
|
||||
Update(m, modules);
|
||||
return m;
|
||||
}
|
||||
|
||||
/// <summary>名字、说明、价值与耗电(耗电按模块效果重算)。</summary>
|
||||
private static void Update(GameItem m, GameInventory modules)
|
||||
{
|
||||
var u = Items.ToUnit(m);
|
||||
var b = Items.Bonus(m, modules);
|
||||
int baseKj = (int)Math.Ceiling(u.Label.Kj);
|
||||
m.ModifyTag("MACHINERY_DRAWN_POWER_BASE", s => s.Enable().SetInt(baseKj));
|
||||
m.ModifyTag(MachineryHelper.MACHINERY_DRAWN_POWER, s => s.Enable().SetInt(u.Dead ? 0 : (int)Math.Ceiling(u.Kj(b))));
|
||||
Describe(m, u);
|
||||
}
|
||||
|
||||
/// <summary>按标签刷新名字、说明、价值(夜间损耗之后也走这里)。</summary>
|
||||
public static void Refresh(GameItem m) => Describe(m, Items.ToUnit(m));
|
||||
|
||||
private static void Describe(GameItem m, Unit u)
|
||||
{
|
||||
var spec = u.Label;
|
||||
m.name = L.T(u.Dead ? "item.asic.dead.name" : "item.asic.name", spec.Model);
|
||||
if (u.Dead)
|
||||
{
|
||||
var need = AsicRepair.Need(u.Model, Step(m));
|
||||
m.shortDescription = need == null ? L.T("item.asic.dead.fixed") : L.T("item.asic.dead.desc", L.T("mat." + need.Value.Item), need.Value.Count - Prog(m));
|
||||
m.unitValue = Math.Max(1, (long)Math.Round(spec.Price * Tuning.AsicDeadValue));
|
||||
return;
|
||||
}
|
||||
string cond = GeneralHelper.IsItemOwned(m)
|
||||
? Mathf.RoundToInt((float)u.Health) + "%"
|
||||
: L.T(u.Health >= 85 ? "item.gpu.grade.1" : u.Health >= 60 ? "item.gpu.grade.2" : u.Health >= 35 ? "item.gpu.grade.3" : "item.gpu.grade.4");
|
||||
m.shortDescription = L.T("item.asic.desc", Fmt.Num(spec.DisplayHash), Fmt.Num(spec.Kj), cond);
|
||||
m.unitValue = CardValue.Of(spec.Model, u.Health);
|
||||
}
|
||||
|
||||
private static void NightDraw(GameItem m, GameSlotInventory battery, GameInventory modules)
|
||||
{
|
||||
var u = Items.ToUnit(m);
|
||||
Session.TonightRigs++;
|
||||
if (u.Dead) return;
|
||||
var b = Items.Bonus(m, modules);
|
||||
int kj = (int)Math.Ceiling(u.Kj(b));
|
||||
if (battery.childItems.Count == 0 || !PowerHelper.DrawPowerSource(battery.childItem, kj)) return;
|
||||
Session.TonightPoweredRigs++;
|
||||
Session.TonightKj += kj;
|
||||
Session.TonightCards.Add(new KeyValuePair<GameItem, ModuleBonus>(m, b));
|
||||
Update(m, modules);
|
||||
}
|
||||
|
||||
private static void TakeRepair(GameItem m, GameSlotInventory slot, GameInventory modules, GameItem item)
|
||||
{
|
||||
var u = Items.ToUnit(m);
|
||||
if (!u.Dead) return;
|
||||
int step = Step(m), prog = Prog(m);
|
||||
int took = AsicRepair.Insert(u.Model, ref step, ref prog, item.identifier, item.unitCount);
|
||||
if (took <= 0) return;
|
||||
m.ModifyTag(StepTag, s => s.Enable().SetInt(step));
|
||||
m.ModifyTag(ProgTag, s => s.Enable().SetInt(prog));
|
||||
if (took >= item.unitCount) { slot.Expel(item); item.Destroy(); }
|
||||
else item.GetSplitItem(took).Destroy();
|
||||
if (AsicRepair.Done(u.Model, step))
|
||||
{
|
||||
u.Dead = false;
|
||||
u.Health = Math.Max(10, u.Health - Tuning.AsicRepairHealthCut);
|
||||
m.ModifyTag(Items.DeadTag, s => s.Disable());
|
||||
m.ModifyTag(StepTag, s => s.Enable().SetInt(0));
|
||||
m.ModifyTag(ProgTag, s => s.Enable().SetInt(0));
|
||||
Items.Store(m, u);
|
||||
EmporiumAudioPlayer.Instance?.ratchet?.Play();
|
||||
Log.Info($"矿机 {u.Model} 修好了,健康 {u.Health:0}");
|
||||
}
|
||||
Update(m, modules);
|
||||
}
|
||||
|
||||
/// <summary>造一台矿机:按健康度写标签。</summary>
|
||||
public static GameItem New(string model, double health)
|
||||
{
|
||||
var item = ModHelper.SpawnItem(Id(model));
|
||||
Items.Store(item, new Unit { Model = model, Health = health });
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,12 +33,12 @@ namespace Decentralized
|
||||
}
|
||||
|
||||
/// <summary>货架上的货对应的游戏物品 id:显卡是 "gpu:型号",对应本 mod 的显卡物品。</summary>
|
||||
public static string GameId(string id) => id.StartsWith("gpu:") ? Items.GpuId(id.Substring(4)) : id;
|
||||
public static string GameId(string id) => id.StartsWith("gpu:") ? (Hardware.Specs.TryGetValue(id.Substring(4), out var sp) && sp.Asic ? AsicItems.Id(sp.Model) : Items.GpuId(id.Substring(4))) : id;
|
||||
|
||||
/// <summary>造出这件货:显卡按成色与(可能的)假卡真身造,不带卖家的说法;其余直接造。</summary>
|
||||
public static GameItem Make(string id, string model, string fakeOf, double health)
|
||||
{
|
||||
if (id.StartsWith("gpu:")) return Items.NewCard(model, health, fakeOf, Session.Data.Rng);
|
||||
if (id.StartsWith("gpu:")) return Hardware.Specs[model].Asic ? AsicItems.New(model, health) : Items.NewCard(model, health, fakeOf, Session.Data.Rng);
|
||||
if (id == Items.HwWallet) return Items.NewHwWallet(Session.Data.Rng.Chance(Tuning.WalletPoisonShare));
|
||||
return ModHelper.SpawnItem(id);
|
||||
}
|
||||
@@ -61,7 +61,7 @@ namespace Decentralized
|
||||
public static string SellId(GameItem it)
|
||||
{
|
||||
if (it == null || it.identifier == null) return null;
|
||||
if (Items.IsGpu(it)) return "gpu:" + Items.ToUnit(it).Model;
|
||||
if (Items.IsGpu(it) || AsicItems.Is(it)) return "gpu:" + Items.ToUnit(it).Model;
|
||||
string id = it.identifier;
|
||||
return Merchants.IsGood(id) ? id : null;
|
||||
}
|
||||
@@ -70,14 +70,14 @@ namespace Decentralized
|
||||
private static readonly System.Collections.Generic.HashSet<string> StorageIds = new System.Collections.Generic.HashSet<string>(Merchants.Table[MerchantType.Storage].Select(e => e.Id));
|
||||
|
||||
public static System.Collections.Generic.List<GameItem> Sellable() =>
|
||||
GraphUtils.FindAllChildrenType<GameItem>(EmporiumEntry.Instance.invElement, i => GeneralHelper.IsItemOwned(i) && SellId(i) != null && !StorageIds.Contains(i.identifier) && i.FindAllChildItems().Count == 0, i => i.identifier != Items.Rig);
|
||||
GraphUtils.FindAllChildrenType<GameItem>(EmporiumEntry.Instance.invElement, i => GeneralHelper.IsItemOwned(i) && SellId(i) != null && !i.IsTag(Items.DeadTag) && !StorageIds.Contains(i.identifier) && i.FindAllChildItems().Count == 0, i => i.identifier != Items.Rig);
|
||||
|
||||
/// <summary>参考价:显卡按标称型号与成色,其余按游戏基础价。</summary>
|
||||
public static long RefValue(GameItem it)
|
||||
{
|
||||
string id = SellId(it);
|
||||
if (id == null) return 0;
|
||||
if (Items.IsGpu(it)) { var u = Items.ToUnit(it); return CardValue.Of(u.Model, u.Health); }
|
||||
if (Items.IsGpu(it) || AsicItems.Is(it)) { var u = Items.ToUnit(it); return CardValue.Of(u.Model, u.Health); }
|
||||
return Value(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,8 @@ namespace Decentralized
|
||||
if (!Unlock.IsUnlocked) ModHelper.GetNetworkUpgrade(Unlock.Id)?.Unlock();
|
||||
Log.Info($"测试:Poinbase CRC {d.Poinbase.Get(Assets.CRC):0},解锁 {Unlock.IsUnlocked}");
|
||||
}));
|
||||
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}"); });
|
||||
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}"); });
|
||||
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "CRA + PSC (test)", () => { var w = Session.Data.Hot; w.Add(Assets.CRA, 1000); w.Add(Assets.PSC, 20); w.Basis += 1000; Log.Info($"测试:{WalletWindow.NameOf(w)} CRA {w.Get(Assets.CRA):0}"); });
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace Decentralized
|
||||
dir.Add(Guide, CreateGuide);
|
||||
dir.Add(Web3Guide, CreateWeb3Guide);
|
||||
dir.Add(HwWallet, CreateHwWallet);
|
||||
AsicItems.Register(dir);
|
||||
foreach (var spec in Hardware.Specs.Values.Where(s => !s.Asic && !s.Scrap))
|
||||
{
|
||||
var s = spec;
|
||||
@@ -316,9 +317,17 @@ namespace Decentralized
|
||||
|
||||
public static Unit ToUnit(GameItem item)
|
||||
{
|
||||
string model = item.identifier.Substring(GpuPrefix.Length);
|
||||
model = Hardware.Specs.Keys.FirstOrDefault(k => k.ToLowerInvariant() == model) ?? "RTX60";
|
||||
bool asic = AsicItems.Is(item);
|
||||
string model = item.identifier.Substring(asic ? AsicItems.Prefix.Length : GpuPrefix.Length);
|
||||
model = Hardware.Specs.Keys.FirstOrDefault(k => k.ToLowerInvariant() == model) ?? (asic ? "M1" : "RTX60");
|
||||
var u = new Unit { Model = model, Health = 100 };
|
||||
if (asic)
|
||||
{
|
||||
// 矿机没有外观、没有真假:不记外观种子,算力、损耗、暴毙都按规格表
|
||||
if (item.IsTag(HealthTag)) u.Health = item.GetTagReadonly(HealthTag).GetDouble();
|
||||
u.Dead = item.IsTag(DeadTag);
|
||||
return u;
|
||||
}
|
||||
if (item.IsTag(HealthTag)) u.Health = item.GetTagReadonly(HealthTag).GetDouble();
|
||||
if (item.IsTag(FakeTag)) u.FakeOf = item.GetTagReadonly(FakeTag).GetString();
|
||||
u.Refurbished = item.IsTag(RefurbTag);
|
||||
@@ -335,14 +344,18 @@ namespace Decentralized
|
||||
if (u.FakeOf != null) item.ModifyTag(FakeTag, s => s.Enable().SetString(u.FakeOf));
|
||||
if (u.Refurbished) item.EnableTag(RefurbTag);
|
||||
if (u.Dead) item.EnableTag(DeadTag);
|
||||
if (!AsicItems.Is(item))
|
||||
{
|
||||
item.ModifyTag(LookTag, s => s.Enable().SetString(u.Look.Encode()));
|
||||
item.ModifyTag(SeedTag, s => s.Enable().SetInt((int)u.Look.Seed));
|
||||
}
|
||||
Refresh(item);
|
||||
}
|
||||
|
||||
/// <summary>按标签更新名字、说明、价值和图:显示的是标签上的型号(假卡也显示被冒充的型号)。</summary>
|
||||
public static void Refresh(GameItem item)
|
||||
{
|
||||
if (AsicItems.Is(item)) { AsicItems.Refresh(item); return; }
|
||||
var u = ToUnit(item);
|
||||
var label = u.Label;
|
||||
if (u.Dead)
|
||||
|
||||
@@ -53,7 +53,7 @@ nightlog.mined,Mining rigs produced {0} PSC.
|
||||
nightlog.burnt,A {0} burnt out.
|
||||
coinpay.line,Deal. {0} {1}.
|
||||
nightlog.coinpay,Customers paying in coin settled {0} credits; the coins went to the hot wallet.
|
||||
nightlog.unpowered,{0} mining rig(s) had no power.
|
||||
nightlog.unpowered,{0} mining rig(s) or miner(s) had no power.
|
||||
news.open.title,Poinbase Lists PSC
|
||||
news.open.desc,The Upper Level exchange Poinbase now lists PSC against CRC. Graphics cards are in demand across the station.
|
||||
news.halving.title,PSC Block Reward Halved
|
||||
@@ -268,7 +268,7 @@ black.kind.Contraband.3,Contraband (high)
|
||||
black.kind.Tech,High-tech (markup)
|
||||
nightlog.bm.ready,"Black market: {0} arrived; a courier will come by once you open."
|
||||
item.web3guide.name,Web3 Handbook
|
||||
item.web3guide.desc,"Bank: depositing turns credits into CRC in your Poinbase account; withdrawing turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint; open and connect them on the terminal's Wallets page. The Web3 page's trading, black market and mixer use the connected wallet, and coin-paying customers and mining rewards go into it too. Payments (black market, mixer, Ponero desk, listing fees) start as an invoice on the terminal; type the payee address on the wallet and hold ✓ to sign. The last two characters of an address are a checksum, so a one-character typo is usually caught; once in a while it happens to be valid and the coins go to an address nobody uses. DEX swaps also need a signature on the wallet. Perps: the Perps tab on Poinbase uses your account CRC as margin, up to 5× on PSC and 3× on SPARK, one position per product, never long and short at once. Fills are worse than the quote (more so for bigger orders, like the spot pool) and opening and closing both cost a fee. Liquidation is checked only overnight against the day's average price: equity under 10% of notional wipes the margin. There is also a nightly funding payment; when the price is above its 7-day average, longs pay shorts. If a liquidation loses more than the margin, the insurance fund covers the gap; when it runs dry (a SPARK collapse can do that) winning positions are auto-deleveraged and give up half their profit; anything beyond that becomes your debt. Debt grows 2% a day, is repaid first from CRC in the account every night, blocks new positions and cash-outs, and after seven days items in your shop are seized to cover it; whatever is still short is written off and the perp account is banned for 30 days. Poinbase is a custodial account and can withdraw straight to the connected wallet; depositing from a wallet means sending to Poinbase's deposit address, which takes only CRC and PSC. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to 5. The black market has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page once its coins are moved out. Transfers pay a fee: slow, normal or fast, and gas prices rise and fall over roughly a week; when the network is congested fees rise and slow transactions may be dropped from the mempool. Check the full address before sending. Staking: CRA staked in the Revolution pool earns a daily rate that compounds into the stake; withdrawing means requesting first and waiting four days, with no interest while waiting and no way to take it early. The pool pays from a subsidy fund; when it runs low the rate is cut and word of big holders pulling out may spread. Treat that as a warning: once it collapses CRA is worthless within three days and four days of unlocking is too slow. The balance token SPARK can be minted and burned against CRA at the going price (with a fee); its price swings hard and crashes with the collapse. Staking, minting and burning are contract calls and need a signature on the wallet. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; merchants sell by type at their own price factor, 5+ items get 5% off and 10+ get 10% off, and a bulk merchant appears at random with the lowest prices but six days of waiting; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometimes turns into enforcement at your door: signing logs the crime on the spot and seizes the goods, refusing only costs the payment. Selling: on the black market's Sell tab, list contraband, high-tech goods and GPUs from storage at 0.6x to 1.8x the reference price. The higher the multiplier, the slower the sale (below 0.9x about a day, 1.0x about two, 1.8x about fourteen), and the likelier and larger the taint on the payout, which also grows with how much you sold in the last 7 days; at most 10 listings at a time, 5% listing fee, repricing a listing costs 2% and the sale speed follows the new multiplier, 8% commission, payout held 3 days before reaching your wLine truncated
|
||||
item.web3guide.desc,"Bank: depositing turns credits into CRC in your Poinbase account; withdrawing turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint; open and connect them on the terminal's Wallets page. The Web3 page's trading, black market and mixer use the connected wallet, and coin-paying customers and mining rewards go into it too. Payments (black market, mixer, Ponero desk, listing fees) start as an invoice on the terminal; type the payee address on the wallet and hold ✓ to sign. The last two characters of an address are a checksum, so a one-character typo is usually caught; once in a while it happens to be valid and the coins go to an address nobody uses. DEX swaps also need a signature on the wallet. RatDigg miners (M1 / M2 / M3) are standalone machines with far more hash than a card and a lower power draw per hash; they need a battery, take modules and use no rig slots. Only the black market's miner dealers sell them, gated by black-market standing (M1 40, M2 60, M3 80), sometimes second-hand. When one burns out, repair it by putting electronic components in its repair slot in order; it comes back 20 health lower. Somewhere between day 50 and day 90 a "miner era" arrives, announced in the paper a few days earlier: the big farms halve their power costs, network hash climbs, GPU miners are diluted and low-end cards barely earn, while miners still do fine. Perps: the Perps tab on Poinbase uses your account CRC as margin, up to 5× on PSC and 3× on SPARK, one position per product, never long and short at once. Fills are worse than the quote (more so for bigger orders, like the spot pool) and opening and closing both cost a fee. Liquidation is checked only overnight against the day's average price: equity under 10% of notional wipes the margin. There is also a nightly funding payment; when the price is above its 7-day average, longs pay shorts. If a liquidation loses more than the margin, the insurance fund covers the gap; when it runs dry (a SPARK collapse can do that) winning positions are auto-deleveraged and give up half their profit; anything beyond that becomes your debt. Debt grows 2% a day, is repaid first from CRC in the account every night, blocks new positions and cash-outs, and after seven days items in your shop are seized to cover it; whatever is still short is written off and the perp account is banned for 30 days. Poinbase is a custodial account and can withdraw straight to the connected wallet; depositing from a wallet means sending to Poinbase's deposit address, which takes only CRC and PSC. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to 5. The black market has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page once its coins are moved out. Transfers pay a fee: slow, normal or fast, and gas prices rise and fall over roughly a week; when the network is congested fees rise and slow transactions may be dropped from the mempool. Check the full address before sending. Staking: CRA staked in the Revolution pool earns a daily rate that compounds into the stake; withdrawing means requesting first and waiting four days, with no interest while waiting and no way to take it early. The pool pays from a subsidy fund; when it runs low the rate is cut and word of big holders pulling out may spread. Treat that as a warning: once it collapses CRA is worthless within three days and four days of unlocking is too slow. The balance token SPARK can be minted and burned against CRA at the going price (with a fee); its price swings hard and crashes with the collapse. Staking, minting and burning are contract calls and need a signature on the wallet. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; merchants sell by type at their own price factor, 5+ items get 5% off and 10+ get 10% off, and a bulk merchant appears at random with the lowest prices but six days of waiting; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometLine truncated
|
||||
courier.odd1,...Package is here.
|
||||
courier.odd2,"(His hand stays in his pocket.) Sign here."
|
||||
courier.sign,Sign
|
||||
@@ -521,3 +521,17 @@ perp.slip.title,Perp debt seizure slip
|
||||
perp.preview2,"Fee {0} liq. {1}"
|
||||
nightlog.perp.repaid,"Perp deficit: {0} CRC was taken from the Poinbase account to repay it."
|
||||
perp.err.Thin,"Slippage on this order is so large it would be liquidated at once. Reduce the size or leverage."
|
||||
mat.hypertech_electronic,Hypertech electronic component
|
||||
asic.label.repair,Repair
|
||||
item.asic.name,RatDigg {0} miner
|
||||
item.asic.dead.name,Burnt-out RatDigg {0} miner
|
||||
item.asic.desc,"Rated {0} GH/s. Rated draw {1} kJ/day. Condition {2}. A standalone miner: needs a battery to run, takes modules."
|
||||
item.asic.dead.desc,Burnt out. Put electronic components in the repair slot: now {0} × {1}. Health drops by 20 once fixed.
|
||||
item.asic.dead.fixed,Already fixed.
|
||||
merchant.Asic,Miner dealer
|
||||
black.kind.asic,Miner condition {0}%
|
||||
black.kind.asicnew,Miner new
|
||||
news.erawarn.title,RatDigg is about to launch a new generation of miners
|
||||
news.erawarn.desc,"The dedicated-miner maker has announced new hardware, and the industry expects big farms to swap out en masse before long. Some say the good days of GPU mining are numbered."
|
||||
news.era.title,The miner era
|
||||
news.era.desc,A new generation of dedicated miners is in mass production and the big farms have halved their power costs. Network hash will climb and GPU miners' returns will be diluted.
|
||||
@@ -52,7 +52,7 @@ nightlog.mined,矿架挖出 {0} PSC。
|
||||
nightlog.burnt,一张 {0} 烧毁了。
|
||||
coinpay.line,成交,{0} {1}。
|
||||
nightlog.coinpay,用币付款的顾客共结算 {0} 信用点,已换成币进了热钱包。
|
||||
nightlog.unpowered,{0} 个矿架没有电。
|
||||
nightlog.unpowered,{0} 台矿架或矿机没有电。
|
||||
news.open.title,Poinbase 上架 PSC
|
||||
news.open.desc,上层交易所 Poinbase 上架 PSC/CRC 交易对。全站显卡需求上升。
|
||||
news.halving.title,PSC 出块奖励减半
|
||||
@@ -267,7 +267,7 @@ black.kind.Contraband.3,违禁品(高)
|
||||
black.kind.Tech,高科技物品(加价)
|
||||
nightlog.bm.ready,"黑市:{0} 到货,开店后会有快递员上门。"
|
||||
item.web3guide.name,Web3 手册
|
||||
item.web3guide.desc,"银行:存入把信用点换成 Poinbase 账户里的 CRC;提取把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点各算各的;在终端的钱包页打开、连接。Web3 页的交易、黑市、混币器用已连接的钱包,用币付款的顾客和挖矿收益也进它。付款(黑市、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。Poinbase 是托管账户,可以直接提到已连接的钱包;从钱包充值要转到 Poinbase 的充值地址,只收 CRC 和 PSC。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 5 个;黑市上也有更便宜的。不要的钱包可以在钱包页销毁,要先把币转走。转账要付手续费,分慢、普通、快三档,gas 价格大约一周一个起落;网络拥堵时手续费变贵,慢档可能被挤出内存池。转账前核对完整地址。质押:把 CRA 质押进革命军质押池每天有日息,利息并入本金;取出要先申请,等四天才到账,等待期间没有利息,也不能提前拿。质押池靠补贴池付息,补贴池见底会降息,有时还会传出大户撤出的消息,那是预兆:一旦崩盘,三天内 CRA 就一文不值,四天的解锁期来不及。平衡代币 SPARK 可以和 CRA 按价格互相铸销(收手续费),价格波动很大,崩盘时会跟着暴跌。质押、铸销都是合约调用,要在钱包上签名。永续合约:Poinbase 的"永续"页,用账户里的 CRC 当保证金,PSC 最高 5 倍、SPARK 最高 3 倍,一个品种只有一个仓位,不能同时多空。成交价比现价差一截(单越大差得越多,和现货池一样),开平仓还收手续费。强平只在夜里按当天均价检查,权益不到名义价值的 10% 就被强平,保证金没了;每晚还有资金费,价格比近 7 天均价高时多头付给空头。强平后亏光保证金还不够的缺口,先由保险基金垫,基金见底(SPARK 崩盘这类大行情会抽干它)后盈利的仓位会被自动减仓、少拿一半盈利,再不够就记成你的欠款:欠款每天涨 2%,账户里的 CRC 每晚先拿去还,没还清不能开仓、不能出金,七天不还就没收店里的物品抵债,抵不够的核销并拉黑永续账户 30 天。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;商家按类型卖货,价格系数各不相同,一次订 5 件以上 95 折、10 件以上 9 折,大宗商家随机出现,价格最低但要等六天;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的卖货页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高,近 7 天出售的量越大,污点越重;同时最多上架 10 件,上架收 5% 上架费,上架后可以改价(收 2% 改价费,标价按参考价重算,成交的快慢跟着新倍率走),成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了。匿名币兑换(Web3 交易页,要黑市声望):Ponero 是隐私币,兑入时这部分价值的污点被清除;兑入、兑出各收 6% 手续费,每天有额度;从 Ponero 兑出的币来路不明,出金审查会加重。"
|
||||
item.web3guide.desc,"银行:存入把信用点换成 Poinbase 账户里的 CRC;提取把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点各算各的;在终端的钱包页打开、连接。Web3 页的交易、黑市、混币器用已连接的钱包,用币付款的顾客和挖矿收益也进它。付款(黑市、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。Poinbase 是托管账户,可以直接提到已连接的钱包;从钱包充值要转到 Poinbase 的充值地址,只收 CRC 和 PSC。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 5 个;黑市上也有更便宜的。不要的钱包可以在钱包页销毁,要先把币转走。转账要付手续费,分慢、普通、快三档,gas 价格大约一周一个起落;网络拥堵时手续费变贵,慢档可能被挤出内存池。转账前核对完整地址。质押:把 CRA 质押进革命军质押池每天有日息,利息并入本金;取出要先申请,等四天才到账,等待期间没有利息,也不能提前拿。质押池靠补贴池付息,补贴池见底会降息,有时还会传出大户撤出的消息,那是预兆:一旦崩盘,三天内 CRA 就一文不值,四天的解锁期来不及。平衡代币 SPARK 可以和 CRA 按价格互相铸销(收手续费),价格波动很大,崩盘时会跟着暴跌。质押、铸销都是合约调用,要在钱包上签名。RatDigg 矿机(M1 / M2 / M3)是独立的机器,算力比显卡大得多、耗电更省,要放电池才挖,可以放模块,不占矿架槽位;只在黑市的矿机商那里有,要黑市声望(M1 40、M2 60、M3 80),偶尔有二手的。暴毙后要往修理槽里放电子元件按顺序修好,修好健康少 20。第 50 到 90 天之间会有一次"矿机时代",提前几天上报纸预告,之后大矿场的耗电成本砍半,全网算力涨上去,显卡矿工的收益被摊薄,低档显卡几乎不赚钱,矿机还行。永续合约:Poinbase 的"永续"页,用账户里的 CRC 当保证金,PSC 最高 5 倍、SPARK 最高 3 倍,一个品种只有一个仓位,不能同时多空。成交价比现价差一截(单越大差得越多,和现货池一样),开平仓还收手续费。强平只在夜里按当天均价检查,权益不到名义价值的 10% 就被强平,保证金没了;每晚还有资金费,价格比近 7 天均价高时多头付给空头。强平后亏光保证金还不够的缺口,先由保险基金垫,基金见底(SPARK 崩盘这类大行情会抽干它)后盈利的仓位会被自动减仓、少拿一半盈利,再不够就记成你的欠款:欠款每天涨 2%,账户里的 CRC 每晚先拿去还,没还清不能开仓、不能出金,七天不还就没收店里的物品抵债,抵不够的核销并拉黑永续账户 30 天。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;商家按类型卖货,价格系数各不相同,一次订 5 件以上 95 折、10 件以上 9 折,大宗商家随机出现,价格最低但要等六天;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的卖货页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高,近 7 天出售的量越大,污点越重;同时最多上架 10 件,上架收 5% 上架费,上架后可以改价(收 2% 改价费,标价按参考价重算,成交的快慢跟着新倍率走),成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了Line truncated
|
||||
courier.odd1,……东西到了。
|
||||
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
|
||||
courier.sign,签收
|
||||
@@ -520,3 +520,17 @@ perp.slip.title,永续欠款没收单
|
||||
perp.preview2,"手续费 {0} 强平价 {1}"
|
||||
nightlog.perp.repaid,"永续穿仓欠款,从 Poinbase 账户扣了 {0} CRC 还款。"
|
||||
perp.err.Thin,"这个单子滑点太大,开了就会被强平。减小金额或杠杆。"
|
||||
mat.hypertech_electronic,超科技电子元件
|
||||
asic.label.repair,修理
|
||||
item.asic.name,RatDigg {0} 矿机
|
||||
item.asic.dead.name,损坏的 RatDigg {0} 矿机
|
||||
item.asic.desc,标称算力 {0} GH/s。标称耗电 {1} kJ/天。成色 {2}。独立矿机:放电池才挖,可以放模块。
|
||||
item.asic.dead.desc,烧坏了。往修理槽里放电子元件修好:现在要 {0} × {1}。修好后健康 −20。
|
||||
item.asic.dead.fixed,已经修好。
|
||||
merchant.Asic,矿机商
|
||||
black.kind.asic,矿机 成色 {0}%
|
||||
black.kind.asicnew,矿机 全新
|
||||
news.erawarn.title,RatDigg 新一代矿机即将发布
|
||||
news.erawarn.desc,专用矿机厂商宣布新品,业内预计不久后大型矿场会成批换机。有人说显卡挖矿的好日子要到头了。
|
||||
news.era.title,矿机时代
|
||||
news.era.desc,新一代专用矿机开始量产,大型矿场的耗电成本砍掉一半。全网算力会涨上去,显卡矿工的收益要被摊薄。
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 495 B |
Binary file not shown.
|
After Width: | Height: | Size: 502 B |
Binary file not shown.
|
After Width: | Height: | Size: 500 B |
@@ -220,6 +220,11 @@ namespace Decentralized
|
||||
ps.AddNightLog(L.T("nightlog.drained", WalletWindow.NameOf(w), Fmt.Credits(v)), "cc7755");
|
||||
}
|
||||
ChainNight(d, day);
|
||||
switch (d.Network.EraNight(day, d.Rng))
|
||||
{
|
||||
case Network.EraStep.Warn: News.Show("pd_news_erawarn_" + day, L.T("news.erawarn.title"), L.T("news.erawarn.desc"), 5); break;
|
||||
case Network.EraStep.Start: News.Show("pd_news_era_" + day, L.T("news.era.title"), L.T("news.era.desc"), 5); Log.Info("矿机时代到来:NPC 耗电效率翻倍"); break;
|
||||
}
|
||||
foreach (var e in perpEvents)
|
||||
{
|
||||
string who = e.Product + " " + L.T(e.Long ? "perp.long" : "perp.short");
|
||||
|
||||
@@ -667,7 +667,7 @@ namespace Decentralized
|
||||
string id = BlackDelivery.SellId(it);
|
||||
var kind = BlackDelivery.KindOf(id);
|
||||
var g = new BmGood { Item = id, Kind = kind, Level = kind == GoodKind.Gpu ? 2 : Merchants.Info(id).Level };
|
||||
if (kind == GoodKind.Gpu) g.Health = Items.ToUnit(it).Health;
|
||||
if (kind == GoodKind.Gpu) { var uu = Items.ToUnit(it); g.Health = uu.Health; g.Model = uu.Model; }
|
||||
g.Price = BlackMarket.ListPriceOf(BlackDelivery.RefValue(it), _sellMult);
|
||||
return new { g, it };
|
||||
}).Select(x => { SellCards[x.g] = x.it; return x.g; }).ToList(), (c, g) =>
|
||||
@@ -771,7 +771,8 @@ namespace Decentralized
|
||||
var sprite = Icon(g.Item);
|
||||
if (sprite != null) c.AddImage(sprite, 64f, 64f); else c.AddSpace(64f);
|
||||
c.AddLabel("<b>" + ItemName(g.Item) + "</b>");
|
||||
c.AddLabel(g.Kind == GoodKind.Gpu ? (g.Health >= 100 ? L.T("black.kind.gpunew") : L.T("black.kind.gpu", (int)Math.Round(g.Health))) : L.T("black.kind." + g.Kind + (g.Kind == GoodKind.Contraband ? "." + g.Level : "")));
|
||||
bool asic = g.Kind == GoodKind.Gpu && g.Model != null && Hardware.Specs.TryGetValue(g.Model, out var gs) && gs.Asic;
|
||||
c.AddLabel(asic ? (g.Health >= 100 ? L.T("black.kind.asicnew") : L.T("black.kind.asic", (int)Math.Round(g.Health))) : g.Kind == GoodKind.Gpu ? (g.Health >= 100 ? L.T("black.kind.gpunew") : L.T("black.kind.gpu", (int)Math.Round(g.Health))) : L.T("black.kind." + g.Kind + (g.Kind == GoodKind.Contraband ? "." + g.Level : "")));
|
||||
if (!string.IsNullOrEmpty(line2)) c.AddLabel(line2);
|
||||
}
|
||||
|
||||
|
||||
@@ -808,6 +808,51 @@ internal static class Program
|
||||
Check(!ChainAddress.Valid("0x12345") && !ChainAddress.Valid("0xzzzzzzzz") && !ChainAddress.WellFormed("12345678ab"), "格式不对的地址不合法");
|
||||
});
|
||||
|
||||
Test("RatDigg 矿机:规格、维修、矿机时代、矿机商", () =>
|
||||
{
|
||||
var m1 = Hardware.Specs["M1"]; var m3 = Hardware.Specs["M3"];
|
||||
Check(m1.Asic && Math.Abs(m1.Hash - 50) < 1e-9 && m3.Hash == 300 && m1.Kj / m1.Hash < Hardware.Specs["RTX90"].Kj / Hardware.Specs["RTX90"].Hash, "矿机每 H 耗电低于最好的显卡");
|
||||
var u = new Unit { Model = "M2", Health = 100 };
|
||||
Check(Math.Abs(u.Hash(ModuleBonus.None) - 120) < 1e-9 && Math.Abs(u.Kj(ModuleBonus.None) - 110) < 1e-9, "M2 满健康 120 H、110 kJ");
|
||||
Check(Math.Abs(u.DeathChance(ModuleBonus.None) - Tuning.DeathBase * 0.5) < 1e-12 && Math.Abs(new Unit { Model = "RTX90", Health = 100 }.DeathChance(ModuleBonus.None) - Tuning.DeathBase * 0.7) < 1e-12, "矿机暴毙系数 0.5(Probidia 0.7)");
|
||||
Check(Math.Abs(CardValue.Of("M1", 100) - 11000) < 1 && CardValue.Of("M1", 0) < 11000 * 0.3, "矿机价值按健康折算,和显卡同一公式");
|
||||
// 维修:按顺序收元件,别的不收
|
||||
int step = 0, prog = 0;
|
||||
Check(AsicRepair.Insert("M1", ref step, ref prog, "rare_electronic", 1) == 0, "M1 先要普通元件,稀有的不收");
|
||||
Check(AsicRepair.Insert("M1", ref step, ref prog, "common_electronic", 3) == 3 && step == 0 && prog == 3, "收 3 个普通元件,进度 3/4");
|
||||
Check(AsicRepair.Insert("M1", ref step, ref prog, "common_electronic", 5) == 1 && step == 1 && prog == 0, "只再收 1 个,进下一步");
|
||||
Check(AsicRepair.Insert("M1", ref step, ref prog, "rare_electronic", 2) == 1 && AsicRepair.Done("M1", step), "再放 1 个稀有,修好;多的不收");
|
||||
double cost = new[] { "M1", "M2", "M3" }.Max(m => AsicRepair.Steps(m).Sum(x => x.Count * (x.Item == "common_electronic" ? 33 : x.Item == "rare_electronic" ? 350 : 2500)) / (double)Hardware.Specs[m].Price);
|
||||
Check(cost > 0.02 && cost < 0.06, $"修理约占机价 4%:最贵的 {cost:P1}");
|
||||
// 矿机时代:定一天、提前预兆、永久减半
|
||||
var net = new Network(); var rng = new Rng(3);
|
||||
double kj0 = net.NpcKj; int warns = 0, starts = 0, warnDay = -1, startDay = -1;
|
||||
for (int d = 1; d <= 120; d++) { var st = net.EraNight(d, rng); if (st == Network.EraStep.Warn) { warns++; warnDay = d; } if (st == Network.EraStep.Start) { starts++; startDay = d; } }
|
||||
Check(warns == 1 && starts == 1 && startDay >= Tuning.EraMin && startDay <= Tuning.EraMax && startDay - warnDay >= Tuning.EraWarnMin && startDay - warnDay <= Tuning.EraWarnMax, $"预兆第 {warnDay} 天,开始第 {startDay} 天");
|
||||
Check(Math.Abs(net.NpcKj - kj0 * Tuning.EraKjMult) < 1e-9 && net.EraNight(200, rng) == Network.EraStep.None, "NPC 耗电效率翻倍,只发生一次");
|
||||
var late = new Network(); Check(late.EraNight(200, new Rng(1)) == Network.EraStep.None && late.EraDay >= 200 + Tuning.EraWarnMax + 1, "存档很晚才接入:至少留出预兆天数");
|
||||
// 均衡:效率翻倍后 NPC 算力涨
|
||||
var a = new Network(); var b = new Network { NpcKj = Tuning.NpcKjPerH * Tuning.EraKjMult };
|
||||
for (int d = 1; d <= 200; d++) { a.Night(d, 100, 0); b.Night(d, 100, 0); a.Reward = b.Reward = 100; }
|
||||
Check(b.NpcHash > a.NpcHash * 1.5, $"耗电减半后均衡算力 {a.NpcHash:0} → {b.NpcHash:0}");
|
||||
// 矿机商:按声望放开型号
|
||||
int none = 0, m1only = 0, withM3 = 0, weeks = 600;
|
||||
for (int i = 0; i < weeks; i++)
|
||||
{
|
||||
foreach (var rep in new[] { 30, 50, 90 })
|
||||
{
|
||||
var bm = new BlackMarket();
|
||||
bm.RefreshOrders(10, rep, new Rng((ulong)(i * 7 + rep)), _ => 200);
|
||||
var asic = bm.Catalog.Where(l => bm.Sellers[l.Seller].Type == MerchantType.Asic).ToList();
|
||||
if (rep == 30 && asic.Count == 0) none++;
|
||||
if (rep == 50 && asic.Count > 0 && asic.All(l => l.Model == "M1")) m1only++;
|
||||
if (rep == 90 && asic.Any(l => l.Model == "M3")) withM3++;
|
||||
foreach (var l in asic) if (l.Model == "M3" && rep < Tuning.AsicRepM3 || l.Model == "M2" && rep < Tuning.AsicRepM2) throw new Exception("声望不够不该出现:" + l.Model + " " + rep);
|
||||
}
|
||||
}
|
||||
Check(none == weeks && m1only > weeks * 0.05 && withM3 > weeks * 0.01, $"声望 30 不出现;50 只有 M1({m1only}/{weeks} 周);90 有 M3({withM3}/{weeks} 周)");
|
||||
});
|
||||
|
||||
Test("永续合约:开平仓、强平、基金与欠款、自动减仓、没收", () =>
|
||||
{
|
||||
Func<string, double> pr = a => a == Assets.PSC ? 100 : 1;
|
||||
|
||||
Reference in new issue
Block a user