using System; using System.Collections.Generic; using System.Linq; using Decentralized.Domain; using UnityEngine; namespace Decentralized { /// /// RatDigg 矿机(M1 / M2 / M3):独立的机器,窗口里有电池槽、模块槽(和矿架同一套效果)和修理槽,没有显卡槽。 /// 夜间和矿架一样先从电池扣电、有电才挖;损耗与暴毙沿用 (暴毙系数 0.5,每天损耗 0.8)。 /// 暴毙后成了"损坏的矿机":往修理槽里按顺序放电子元件修好(见 ),健康扣 20 点。 /// 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; } /// 名字、说明、价值与耗电(耗电按模块效果重算)。 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); } /// 按标签刷新名字、说明、价值(夜间损耗之后也走这里)。 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(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); } /// 造一台矿机:按健康度写标签。 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; } } }