From 99df583923b1c7ea2a1abf8543d36b3f2591d5c5 Mon Sep 17 00:00:00 2001 From: starfish <50672801+starfi5h@users.noreply.github.com> Date: Wed, 4 Sep 2024 22:35:16 +0800 Subject: [PATCH] Sync UIControlPanelObjectEntry --- .../LCPAdvancedMinerEntryUpdate.cs | 100 ++++++++++ .../ControlPanel/LCPDispenserEntryUpdate.cs | 82 ++++++++ .../ControlPanel/LCPObjectEntryEntityInfo.cs | 21 +++ .../ControlPanel/LCPObjectEntryRequest.cs | 23 +++ .../ControlPanel/LCPStationEntryUpdate.cs | 93 +++++++++ .../LCPAdvancedMinerEntryUpdateProcessor.cs | 137 ++++++++++++++ .../LCPDispenserEntryUpdateProcessor.cs | 130 +++++++++++++ .../LCPObjectEntryEntityInfoProcessor.cs | 111 +++++++++++ .../LCPObjectEntryRequestProcessor.cs | 74 ++++++++ .../LCPStationEntryUpdateProcessor.cs | 176 ++++++++++++++++++ .../UIControlPanelAdvancedMinerEntry_Patch.cs | 49 +++++ .../UIControlPanelDispenserEntry_Patch.cs | 48 +++++ .../UIControlPanelObjectEntry_Patch.cs | 28 +++ .../UIControlPanelPlanetEntry_Patch.cs | 52 ++++++ .../UIControlPanelStationEntry_Patch.cs | 57 ++++++ .../Dynamic/UIControlPanelWindow_Patch.cs | 53 ++++++ NebulaWorld/Logistics/StationUIManager.cs | 15 +- 17 files changed, 1247 insertions(+), 2 deletions(-) create mode 100644 NebulaModel/Packets/Logistics/ControlPanel/LCPAdvancedMinerEntryUpdate.cs create mode 100644 NebulaModel/Packets/Logistics/ControlPanel/LCPDispenserEntryUpdate.cs create mode 100644 NebulaModel/Packets/Logistics/ControlPanel/LCPObjectEntryEntityInfo.cs create mode 100644 NebulaModel/Packets/Logistics/ControlPanel/LCPObjectEntryRequest.cs create mode 100644 NebulaModel/Packets/Logistics/ControlPanel/LCPStationEntryUpdate.cs create mode 100644 NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPAdvancedMinerEntryUpdateProcessor.cs create mode 100644 NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPDispenserEntryUpdateProcessor.cs create mode 100644 NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPObjectEntryEntityInfoProcessor.cs create mode 100644 NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPObjectEntryRequestProcessor.cs create mode 100644 NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPStationEntryUpdateProcessor.cs create mode 100644 NebulaPatcher/Patches/Dynamic/UIControlPanelObjectEntry_Patch.cs create mode 100644 NebulaPatcher/Patches/Dynamic/UIControlPanelPlanetEntry_Patch.cs diff --git a/NebulaModel/Packets/Logistics/ControlPanel/LCPAdvancedMinerEntryUpdate.cs b/NebulaModel/Packets/Logistics/ControlPanel/LCPAdvancedMinerEntryUpdate.cs new file mode 100644 index 000000000..ab4c1bfc1 --- /dev/null +++ b/NebulaModel/Packets/Logistics/ControlPanel/LCPAdvancedMinerEntryUpdate.cs @@ -0,0 +1,100 @@ +using UnityEngine; + +namespace NebulaModel.Packets.Logistics.ControlPanel; + +public class LCPAdvancedMinerEntryUpdate +{ + public LCPAdvancedMinerEntryUpdate() { } + + public void Set(int index, StationComponent station, PlanetFactory factory) + { + Index = index; + + if (!station.isCollector) + { + var powerSystem = factory.powerSystem; + var powerConsumerComponent = powerSystem.consumerPool[station.pcId]; + var networkId = powerConsumerComponent.networkId; + var powerNetwork = powerSystem.netPool[networkId]; + ConsumerRatio = (float)powerNetwork.consumerRatio; + RequirePower = (long)((powerConsumerComponent.requiredEnergy * 60L) * ConsumerRatio); + WorkEnergyPerTick = powerSystem.consumerPool[station.pcId].workEnergyPerTick; + PowerRound = station.energy / (float)station.energyMax; + } + + var minerComponent = factory.factorySystem.minerPool[station.minerId]; + if (minerComponent.id == station.minerId) + { + var veinPool = factory.veinPool; + var lowVeinCount = 0; + var emptyVeinCount = 0; + if (minerComponent.veinCount > 0) + { + for (var i = 0; i < minerComponent.veinCount; i++) + { + var id = minerComponent.veins[i]; + if (id > 0 && veinPool[id].id == id) + { + var amount = veinPool[id].amount; + if (amount < 1000) + { + if (amount > 0) + { + lowVeinCount++; + } + else + { + emptyVeinCount++; + } + } + } + } + } + VeinCount = (short)minerComponent.veinCount; + LowVeinCount = (short)lowVeinCount; + EmptyVeinCount = (short)emptyVeinCount; + + var veinId = ((minerComponent.veinCount == 0) ? 0 : minerComponent.veins[minerComponent.currentVeinIndex]); + VeinProtoId = (int)veinPool[veinId].type; + var veinProto = LDB.veins.Select((int)veinPool[veinId].type); + if (veinProto != null) + { + minerComponent.GetTotalVeinAmount(veinPool); + TotalVeinAmount = minerComponent.totalVeinAmount; + } + } + + ItemId = 0; + if (station.storage == null || station.storage.Length == 0) return; + ref var store = ref station.storage[0]; + ItemId = store.itemId; + ItemCount = store.count; + LocalOrder = (short)Mathf.Clamp(store.localOrder, -20000, 20000); + RemoteOrder = (short)Mathf.Clamp(store.remoteOrder, -20000, 20000); + StoreMax = store.max; + LocalLogic = (byte)store.localLogic; + RemoteLogic = (byte)store.remoteLogic; + } + + public static readonly LCPAdvancedMinerEntryUpdate Instance = new(); + + public int Index { get; set; } + public float ConsumerRatio { get; set; } + public long RequirePower { get; set; } + public long WorkEnergyPerTick { get; set; } + public float PowerRound { get; set; } + + public int VeinProtoId { get; set; } + public long TotalVeinAmount { get; set; } + public short VeinCount { get; set; } + public short LowVeinCount { get; set; } + public short EmptyVeinCount { get; set; } + + public int ItemId { get; set; } + public int ItemCount { get; set; } + public short LocalOrder { get; set; } + public short RemoteOrder { get; set; } + public int StoreMax { get; set; } + public byte LocalLogic { get; set; } + public byte RemoteLogic { get; set; } +} diff --git a/NebulaModel/Packets/Logistics/ControlPanel/LCPDispenserEntryUpdate.cs b/NebulaModel/Packets/Logistics/ControlPanel/LCPDispenserEntryUpdate.cs new file mode 100644 index 000000000..04a88be76 --- /dev/null +++ b/NebulaModel/Packets/Logistics/ControlPanel/LCPDispenserEntryUpdate.cs @@ -0,0 +1,82 @@ +namespace NebulaModel.Packets.Logistics.ControlPanel; + +public class LCPDispenserEntryUpdate +{ + public LCPDispenserEntryUpdate() { } + + public void Set(int index, DispenserComponent dispenser, PlanetFactory factory) + { + Index = index; + + var powerSystem = factory.powerSystem; + var powerConsumerComponent = powerSystem.consumerPool[dispenser.pcId]; + var networkId = powerConsumerComponent.networkId; + var powerNetwork = powerSystem.netPool[networkId]; + ConsumerRatio = (float)powerNetwork.consumerRatio; + RequirePower = (long)((powerConsumerComponent.requiredEnergy * 60L) * ConsumerRatio); + WorkEnergyPerTick = powerSystem.consumerPool[dispenser.pcId].workEnergyPerTick; + PowerRound = dispenser.energy / (float)dispenser.energyMax; + + Filter = dispenser.filter; + PlayerMode = (short)dispenser.playerMode; + StorageMode = (short)dispenser.storageMode; + IdleCourierCount = dispenser.idleCourierCount; + WorkCourierCount = dispenser.workCourierCount; + + var status = 0; + if (dispenser.playerMode == EPlayerDeliveryMode.None && dispenser.storageMode == EStorageDeliveryMode.None) + { + status = 1; + } + else if (dispenser.filter == 0) + { + status = 2; + } + else if (dispenser.idleCourierCount + dispenser.workCourierCount == 0) + { + status = 3; + } + else if (dispenser.holdupItemCount > 0) + { + status = 5; + } + else if (ConsumerRatio < 0.0001f && dispenser.energy < 100000L) + { + status = 4; + } + WarningFlag = status > 0; + + var count = 0; + var inc = 0; + var loop = 0; + if (dispenser.storage != null && dispenser.filter > 0) + { + var storageComponent = dispenser.storage; + do + { + count += storageComponent.GetItemCount(dispenser.filter, out var num); + inc += num; + storageComponent = storageComponent.previousStorage; + if (loop++ > 20) break; + } + while (storageComponent != null); + } + ItemCount = count; + } + + public static readonly LCPDispenserEntryUpdate Instance = new(); + + public int Index { get; set; } + public float ConsumerRatio { get; set; } + public long RequirePower { get; set; } + public long WorkEnergyPerTick { get; set; } + public float PowerRound { get; set; } + + public int Filter { get; set; } + public short PlayerMode { get; set; } + public short StorageMode { get; set; } + public int IdleCourierCount { get; set; } + public int WorkCourierCount { get; set; } + public bool WarningFlag { get; set; } + public int ItemCount { get; set; } +} diff --git a/NebulaModel/Packets/Logistics/ControlPanel/LCPObjectEntryEntityInfo.cs b/NebulaModel/Packets/Logistics/ControlPanel/LCPObjectEntryEntityInfo.cs new file mode 100644 index 000000000..0651064d3 --- /dev/null +++ b/NebulaModel/Packets/Logistics/ControlPanel/LCPObjectEntryEntityInfo.cs @@ -0,0 +1,21 @@ +namespace NebulaModel.Packets.Logistics.ControlPanel; + +public class LCPObjectEntryEntityInfo +{ + public LCPObjectEntryEntityInfo() { } + + public void Set(int index, int protoId, int id, string name) + { + Index = index; + ProtoId = protoId; + Id = id; + Name = name; + } + + public static readonly LCPObjectEntryEntityInfo Instance = new(); + + public int Index { get; set; } + public int ProtoId { get; set; } + public int Id { get; set; } + public string Name { get; set; } +} diff --git a/NebulaModel/Packets/Logistics/ControlPanel/LCPObjectEntryRequest.cs b/NebulaModel/Packets/Logistics/ControlPanel/LCPObjectEntryRequest.cs new file mode 100644 index 000000000..e327f7b4f --- /dev/null +++ b/NebulaModel/Packets/Logistics/ControlPanel/LCPObjectEntryRequest.cs @@ -0,0 +1,23 @@ +namespace NebulaModel.Packets.Logistics.ControlPanel; + +public class LCPObjectEntryRequest +{ + public LCPObjectEntryRequest() { } + + public void Set(UIControlPanelObjectEntry controlPanelFilter, bool isInit = false) + { + // isInit: Request for entityData.protoId, name + Index = controlPanelFilter.index; + EntryType = (short)controlPanelFilter.entryType; + AstroId = controlPanelFilter.target.astroId; + // Use sign of ObjId to note the state of request + ObjId = isInit ? -controlPanelFilter.target.objId : controlPanelFilter.target.objId; + } + + public static readonly LCPObjectEntryRequest Instance = new(); + + public int Index { get; set; } + public short EntryType { get; set; } + public int AstroId { get; set; } + public int ObjId { get; set; } +} diff --git a/NebulaModel/Packets/Logistics/ControlPanel/LCPStationEntryUpdate.cs b/NebulaModel/Packets/Logistics/ControlPanel/LCPStationEntryUpdate.cs new file mode 100644 index 000000000..161cb2ee0 --- /dev/null +++ b/NebulaModel/Packets/Logistics/ControlPanel/LCPStationEntryUpdate.cs @@ -0,0 +1,93 @@ +using UnityEngine; + +namespace NebulaModel.Packets.Logistics.ControlPanel; + +public class LCPStationEntryUpdate +{ + public LCPStationEntryUpdate() { } + + public void Set(int index, StationComponent station, PlanetFactory factory) + { + Index = index; + + if (!station.isCollector) + { + var powerSystem = factory.powerSystem; + var powerConsumerComponent = powerSystem.consumerPool[station.pcId]; + var networkId = powerConsumerComponent.networkId; + var powerNetwork = powerSystem.netPool[networkId]; + ConsumerRatio = (float)powerNetwork.consumerRatio; + RequirePower = (long)((powerConsumerComponent.requiredEnergy * 60L) * ConsumerRatio); + WorkEnergyPerTick = powerSystem.consumerPool[station.pcId].workEnergyPerTick; + PowerRound = station.energy / (float)station.energyMax; + } + else + { + ConsumerRatio = 1.0f; + RequirePower = 0; + WorkEnergyPerTick = 0; + PowerRound = 0; + } + + IdleDroneCount = (short)station.idleDroneCount; + WorkDroneCount = (short)station.workDroneCount; + IdleShipCount = (short)station.idleShipCount; + WorkShipCount = (short)station.workShipCount; + WarperCount = (short)station.warperCount; + + if (ItemId == null) + { + ItemId = new int[5]; + ItemCount = new int[5]; + LocalOrder = new short[5]; + RemoteOrder = new short[5]; + StoreMax = new int[5]; + LocalLogic = new byte[5]; + RemoteLogic = new byte[5]; + } + var i = 0; + if (station.storage != null) + { + var storeLength = station.storage.Length; + if (storeLength > 5) storeLength = 5; + for (; i < storeLength; i++) + { + ref var store = ref station.storage[i]; + ItemId[i] = store.itemId; + ItemCount[i] = store.count; + LocalOrder[i] = (short)Mathf.Clamp(store.localOrder, -20000, 20000); + RemoteOrder[i] = (short)Mathf.Clamp(store.remoteOrder, -20000, 20000); + StoreMax[i] = store.max; + LocalLogic[i] = (byte)store.localLogic; + RemoteLogic[i] = (byte)store.remoteLogic; + } + } + for (; i < 5; i++) + { + ItemId[i] = 0; + ItemCount[i] = 0; + } + } + + public static readonly LCPStationEntryUpdate Instance = new(); + + public int Index { get; set; } + public float ConsumerRatio { get; set; } + public long RequirePower { get; set; } + public long WorkEnergyPerTick { get; set; } + public float PowerRound { get; set; } + + public short IdleDroneCount { get; set; } + public short WorkDroneCount { get; set; } + public short IdleShipCount { get; set; } + public short WorkShipCount { get; set; } + public short WarperCount { get; set; } + + public int[] ItemId { get; set; } + public int[] ItemCount { get; set; } + public short[] LocalOrder { get; set; } + public short[] RemoteOrder { get; set; } + public int[] StoreMax { get; set; } + public byte[] LocalLogic { get; set; } + public byte[] RemoteLogic { get; set; } +} diff --git a/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPAdvancedMinerEntryUpdateProcessor.cs b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPAdvancedMinerEntryUpdateProcessor.cs new file mode 100644 index 000000000..e87e42c4b --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPAdvancedMinerEntryUpdateProcessor.cs @@ -0,0 +1,137 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Logistics.ControlPanel; +using UnityEngine; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Logistics.ControlPanel; + +[RegisterPacketProcessor] +public class LCPAdvancedMinerEntryUpdateProcessor : PacketProcessor +{ + static readonly StationStore[] stationStores = new StationStore[1]; + + protected override void ProcessPacket(LCPAdvancedMinerEntryUpdate packet, NebulaConnection conn) + { + if (IsHost) return; + + var objectEntryPool = UIRoot.instance.uiGame.controlPanelWindow.objectEntryPool; + for (var i = 0; i < objectEntryPool.Count; i++) + { + if (objectEntryPool[i] != null && objectEntryPool[i].index == packet.Index) + { + var entry = objectEntryPool[i] as UIControlPanelAdvancedMinerEntry; + UpdateEntry(entry, packet); + return; + } + } + } + + private static void UpdateEntry(UIControlPanelAdvancedMinerEntry entry, LCPAdvancedMinerEntryUpdate packet) + { + if (entry == null) return; + + // Modify from UIControlPanelAdvancedMinerEntry._OnUpdate + var isInfiniteResource = GameMain.data.gameDesc.isInfiniteResource; + var isAllEmpty = packet.VeinCount == packet.EmptyVeinCount; + entry.warningItemCanvasGroup.alpha = ((packet.LowVeinCount > 0 && !isInfiniteResource) || isAllEmpty) ? 1 : 0; + entry.warningText.text = (isAllEmpty ? "矿脉全部耗尽警告".Translate() : string.Format("矿脉耗尽警告".Translate(), packet.LowVeinCount)); + + var veinProto = LDB.veins.Select(packet.VeinProtoId); + if (!isAllEmpty && veinProto != null) + { + entry.minerItemImage.sprite = veinProto.iconSprite; + entry.minerItemImage.color = Color.white; + StringBuilderUtility.WriteKMG(entry.veinAmountSB, 8, packet.TotalVeinAmount, false); + entry.veinAmountText.text = (isInfiniteResource ? ("(" + packet.VeinCount + ")") : string.Concat(new object[] + { + entry.veinAmountSB.ToString(), + "(", + packet.VeinCount, + ")" + })); + } + else + { + entry.minerItemImage.sprite = null; + entry.minerItemImage.color = Color.clear; + entry.veinAmountText.text = ""; + } + + // Update item UI + ref var store = ref stationStores[0]; + store.itemId = packet.ItemId; + store.count = packet.ItemCount; + store.localOrder = packet.LocalOrder; + store.remoteOrder = packet.RemoteOrder; + store.max = packet.StoreMax; + store.localLogic = (ELogisticStorage)packet.LocalLogic; + store.remoteLogic = (ELogisticStorage)packet.RemoteLogic; + var tmp = entry.station.storage; + entry.station.storage = stationStores; + // expand UpdateItems() + entry.storageItem.SetVisible(stationStores[0].itemId > 0); + entry.storageItem._Update(); + entry.station.storage = tmp; + + // Update power UI + entry.powerGroupGo.SetActive(true); + var consumerRatio = (float)packet.ConsumerRatio; + int powerStatusCode; + if (consumerRatio >= 1f) + { + entry.powerCircleFg.fillAmount = 1f; + powerStatusCode = 1; + } + else + { + entry.powerCircleFg.fillAmount = consumerRatio; + powerStatusCode = ((consumerRatio >= 0.1f) ? 2 : 3); + } + StringBuilderUtility.WriteKMG1000(entry.sbw, 8, packet.RequirePower, true); + StringBuilderUtility.WriteKMG1000(entry.sbw2, 8, packet.WorkEnergyPerTick * 60L, true); + entry.powerText.text = entry.sbw.ToString(); + entry.maxChargePowerValue.text = entry.sbw2.ToString(); + entry.powerRoundFg.fillAmount = packet.PowerRound; + switch (powerStatusCode) + { + + case 1: + entry.powerSignImage.color = entry.masterWindow.powerSignColor1; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor1; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor1; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor1; + entry.powerText.color = entry.masterWindow.powerTextColor1; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor1; + break; + case 2: + entry.powerSignImage.color = entry.masterWindow.powerSignColor2; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor2; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor2; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor2; + entry.powerText.color = entry.masterWindow.powerTextColor2; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor2; + break; + case 3: + entry.powerSignImage.color = entry.masterWindow.powerSignColor3; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor3; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor3; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor3; + entry.powerText.color = entry.masterWindow.powerTextColor3; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor3; + break; + default: + entry.powerSignImage.color = entry.masterWindow.powerSignColor0; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor0; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor0; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor0; + entry.powerText.color = entry.masterWindow.powerTextColor0; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor0; + break; + } + } +} diff --git a/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPDispenserEntryUpdateProcessor.cs b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPDispenserEntryUpdateProcessor.cs new file mode 100644 index 000000000..7bc9d2d17 --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPDispenserEntryUpdateProcessor.cs @@ -0,0 +1,130 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Logistics.ControlPanel; +using UnityEngine; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Logistics.ControlPanel; + +[RegisterPacketProcessor] +public class LCPDispenserEntryUpdateProcessor : PacketProcessor +{ + protected override void ProcessPacket(LCPDispenserEntryUpdate packet, NebulaConnection conn) + { + if (IsHost) return; + + var objectEntryPool = UIRoot.instance.uiGame.controlPanelWindow.objectEntryPool; + for (var i = 0; i < objectEntryPool.Count; i++) + { + if (objectEntryPool[i] != null && objectEntryPool[i].index == packet.Index) + { + var entry = objectEntryPool[i] as UIControlPanelDispenserEntry; + UpdateEntry(entry, packet); + return; + } + } + } + + private static void UpdateEntry(UIControlPanelDispenserEntry entry, LCPDispenserEntryUpdate packet) + { + if (entry == null) return; + + // Modify from UIControlPanelDispenserEntry._OnUpdate + var filter = packet.Filter; + var playerMode = (EPlayerDeliveryMode)packet.PlayerMode; + var storageMode = (EStorageDeliveryMode)packet.StorageMode; + + if (playerMode == EPlayerDeliveryMode.Recycle && filter < 0) + { + entry.transitItemGroup.alpha = 0f; + entry.transitItemGroup.blocksRaycasts = false; + entry.recycleAllText.color = entry.masterWindow.recycleAllColor; + } + else + { + entry.transitItemGroup.alpha = 1f; + entry.transitItemGroup.blocksRaycasts = true; + entry.transitItemText.color = entry.masterWindow.transitItemTextColor; + entry.recycleAllText.color = Color.clear; + var itemProto = LDB.items.Select(filter); + if (itemProto != null) + { + entry.transitItemImage.sprite = itemProto.iconSprite; + entry.transitItemButton.tips.itemId = filter; + entry.transitItemButton.tips.itemInc = 0; + entry.transitItemButton.tips.itemCount = 0; + entry.transitItemButton.tips.type = UIButton.ItemTipType.Other; + entry.transitItemText.text = packet.ItemCount.ToString(); + } + else + { + entry.transitItemGroup.alpha = 0f; + entry.transitItemGroup.blocksRaycasts = false; + entry.recycleAllText.color = Color.clear; + } + } + entry.SetPlayerDeliveryActiveModes(playerMode, entry.mechaDeliverySupply, entry.mechaDeliveryDemand, entry.mechaDeliverySupplyDemand); + entry.SetStorageDeliveryActiveModes(storageMode, entry.storageDeliverySupply, entry.storageDeliveryDemand); + entry.courierCountText.text = packet.IdleCourierCount.ToString() + "/" + (packet.IdleCourierCount + packet.WorkCourierCount).ToString(); + + // Update power UI + var consumerRatio = (float)packet.ConsumerRatio; + int powerStatusCode; + if (consumerRatio >= 1f) + { + entry.powerCircleFg.fillAmount = 1f; + powerStatusCode = 1; + } + else + { + entry.powerCircleFg.fillAmount = consumerRatio; + powerStatusCode = ((consumerRatio >= 0.1f) ? 2 : 3); + } + StringBuilderUtility.WriteKMG1000(entry.sbw, 8, packet.RequirePower, true); + StringBuilderUtility.WriteKMG1000(entry.sbw2, 8, packet.WorkEnergyPerTick * 60L, true); + entry.powerText.text = entry.sbw.ToString(); + entry.maxChargePowerValue.text = entry.sbw2.ToString(); + entry.powerRoundFg.fillAmount = packet.PowerRound; + switch (powerStatusCode) + { + + case 1: + entry.powerSignImage.color = entry.masterWindow.powerSignColor1; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor1; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor1; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor1; + entry.powerText.color = entry.masterWindow.powerTextColor1; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor1; + break; + case 2: + entry.powerSignImage.color = entry.masterWindow.powerSignColor2; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor2; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor2; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor2; + entry.powerText.color = entry.masterWindow.powerTextColor2; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor2; + break; + case 3: + entry.powerSignImage.color = entry.masterWindow.powerSignColor3; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor3; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor3; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor3; + entry.powerText.color = entry.masterWindow.powerTextColor3; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor3; + break; + default: + entry.powerSignImage.color = entry.masterWindow.powerSignColor0; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor0; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor0; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor0; + entry.powerText.color = entry.masterWindow.powerTextColor0; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor0; + break; + } + entry.warningItemCanvasGroup.alpha = packet.WarningFlag ? 1 : 0; + } +} diff --git a/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPObjectEntryEntityInfoProcessor.cs b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPObjectEntryEntityInfoProcessor.cs new file mode 100644 index 000000000..8818786bd --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPObjectEntryEntityInfoProcessor.cs @@ -0,0 +1,111 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Logistics.ControlPanel; +using UITools; +using UnityEngine; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Logistics.ControlPanel; + +[RegisterPacketProcessor] +public class LCPObjectEntryEntityInfoProcessor : PacketProcessor +{ + protected override void ProcessPacket(LCPObjectEntryEntityInfo packet, NebulaConnection conn) + { + if (IsHost) return; + + var objectEntryPool = UIRoot.instance.uiGame.controlPanelWindow.objectEntryPool; + for (var i = 0; i < objectEntryPool.Count; i++) + { + if (objectEntryPool[i] != null && objectEntryPool[i].index == packet.Index) + { + UpdateEntry(objectEntryPool[i], packet); + return; + } + } + } + + private static void UpdateEntry(UIControlPanelObjectEntry objectEntry, LCPObjectEntryEntityInfo packet) + { + if (objectEntry == null) return; + + if (objectEntry is UIControlPanelStationEntry) + { + var entry = objectEntry as UIControlPanelStationEntry; + + entry.storageItem0.station = entry.station; + entry.storageItem1.station = entry.station; + entry.storageItem2.station = entry.station; + entry.storageItem3.station = entry.station; + entry.storageItem4.station = entry.station; + var itemProto = LDB.items.Select(packet.ProtoId); + entry.stationIcon.sprite = itemProto?.iconSprite; + entry.stationIdText.text = "#" + packet.Id; + entry.stationIconButton.tips.itemId = packet.ProtoId; + entry.stationIconButton.tips.itemInc = 0; + entry.stationIconButton.tips.itemCount = 0; + entry.stationIconButton.tips.type = UIButton.ItemTipType.Other; + + entry.id = packet.Id; + var text = "无名称编号".Translate(); + if (packet.Name == "") + { + text = string.Format(text, entry.id.ToString()); + entry.stationNameText.color = entry.masterWindow.unnamedColor; + } + else + { + text = packet.Name; + entry.stationNameText.color = entry.masterWindow.renamedColor; + } + Utils.UITextTruncateShow(entry.stationNameText, text, entry.stationNameTextWidthLimit, ref entry.stationNameTextSettings); + } + else if (objectEntry is UIControlPanelAdvancedMinerEntry) + { + var entry = objectEntry as UIControlPanelAdvancedMinerEntry; + + entry.storageItem.station = entry.station; + var itemProto = LDB.items.Select(packet.ProtoId); + entry.stationIcon.sprite = (itemProto?.iconSprite); + entry.stationIdText.text = "#" + packet.Id; + entry.stationIconButton.tips.itemId = packet.ProtoId; + entry.stationIconButton.tips.itemInc = 0; + entry.stationIconButton.tips.itemCount = 0; + entry.stationIconButton.tips.type = UIButton.ItemTipType.Other; + + entry.id = packet.Id; + var text = "无名称编号".Translate(); + if (packet.Name == "") + { + text = string.Format(text, entry.id.ToString()); + entry.stationNameText.color = entry.masterWindow.unnamedColor; + } + else + { + text = packet.Name; + entry.stationNameText.color = entry.masterWindow.renamedColor; + } + Utils.UITextTruncateShow(entry.stationNameText, text, entry.stationNameTextWidthLimit, ref entry.stationNameTextSettings); + } + else if (objectEntry is UIControlPanelDispenserEntry) + { + var entry = objectEntry as UIControlPanelDispenserEntry; + + entry.id = packet.Id; + var itemProto = LDB.items.Select(packet.ProtoId); + entry.dispenserIcon.sprite = itemProto?.iconSprite; + entry.dispenserIdText.text = "#" + entry.id.ToString(); + entry.dispenserIconButton.tips.itemId = packet.ProtoId; + entry.dispenserIconButton.tips.itemInc = 0; + entry.dispenserIconButton.tips.itemCount = 0; + entry.dispenserIconButton.tips.type = UIButton.ItemTipType.Other; + + var text = "无名称编号".Translate(); + entry.dispenserNameText.text = string.Format(text, entry.id.ToString()); + } + } +} diff --git a/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPObjectEntryRequestProcessor.cs b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPObjectEntryRequestProcessor.cs new file mode 100644 index 000000000..57111261a --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPObjectEntryRequestProcessor.cs @@ -0,0 +1,74 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Logistics.ControlPanel; +using UnityEngine; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Logistics.ControlPanel; + +[RegisterPacketProcessor] +public class LCPObjectEntryRequestProcessor : PacketProcessor +{ + protected override void ProcessPacket(LCPObjectEntryRequest packet, NebulaConnection conn) + { + if (IsClient) return; + var factory = GameMain.galaxy.PlanetById(packet.AstroId)?.factory; + if (factory == null) return; + + var isInit = false; + var objId = packet.ObjId; + if (objId < 0) + { + objId = -objId; + isInit = true; + } + if (objId <= 0 || objId > factory.entityPool.Length) return; + ref var entity = ref factory.entityPool[objId]; + if (entity.id != objId) return; + + switch ((EControlPanelEntryType)packet.EntryType) + { + case EControlPanelEntryType.InterstellarStation: + case EControlPanelEntryType.OrbitCollector: + case EControlPanelEntryType.LocalStation: + if (entity.stationId == 0) return; + if (isInit) + { + LCPObjectEntryEntityInfo.Instance.Set(packet.Index, entity.protoId, entity.stationId, factory.ReadExtraInfoOnEntity(objId)); + conn.SendPacket(LCPObjectEntryEntityInfo.Instance); + } + var station = factory.transport.stationPool[entity.stationId]; + LCPStationEntryUpdate.Instance.Set(packet.Index, station, factory); + conn.SendPacket(LCPStationEntryUpdate.Instance); + break; + + case EControlPanelEntryType.VeinCollector: + if (entity.stationId == 0) return; + if (isInit) + { + LCPObjectEntryEntityInfo.Instance.Set(packet.Index, entity.protoId, entity.stationId, factory.ReadExtraInfoOnEntity(objId)); + conn.SendPacket(LCPObjectEntryEntityInfo.Instance); + } + var veinCollector = factory.transport.stationPool[entity.stationId]; + LCPAdvancedMinerEntryUpdate.Instance.Set(packet.Index, veinCollector, factory); + conn.SendPacket(LCPAdvancedMinerEntryUpdate.Instance); + break; + + case EControlPanelEntryType.Dispenser: + if (entity.dispenserId == 0) return; + if (isInit) + { + LCPObjectEntryEntityInfo.Instance.Set(packet.Index, entity.protoId, entity.dispenserId, factory.ReadExtraInfoOnEntity(objId)); + conn.SendPacket(LCPObjectEntryEntityInfo.Instance); + } + var dispenser = factory.transport.dispenserPool[entity.dispenserId]; + LCPDispenserEntryUpdate.Instance.Set(packet.Index, dispenser, factory); + conn.SendPacket(LCPDispenserEntryUpdate.Instance); + break; + } + } +} diff --git a/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPStationEntryUpdateProcessor.cs b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPStationEntryUpdateProcessor.cs new file mode 100644 index 000000000..9b53a1f91 --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Logistics/ControlPanel/LCPStationEntryUpdateProcessor.cs @@ -0,0 +1,176 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Logistics.ControlPanel; +using UnityEngine; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Logistics.ControlPanel; + +[RegisterPacketProcessor] +public class LCPStationEntryUpdateProcessor : PacketProcessor +{ + static readonly StationStore[] stationStores = new StationStore[5]; + + protected override void ProcessPacket(LCPStationEntryUpdate packet, NebulaConnection conn) + { + if (IsHost) return; + + var objectEntryPool = UIRoot.instance.uiGame.controlPanelWindow.objectEntryPool; + for (var i = 0; i < objectEntryPool.Count; i++) + { + if (objectEntryPool[i] != null && objectEntryPool[i].index == packet.Index) + { + var entry = objectEntryPool[i] as UIControlPanelStationEntry; + UpdateEntry(entry, packet); + return; + } + } + } + + private static void UpdateEntry(UIControlPanelStationEntry entry, LCPStationEntryUpdate packet) + { + if (entry == null) return; + + // Modify from UIControlPanelStationEntry._OnUpdate + // Update item UI + for (var i = 0; i < 5; i++) + { + ref var store = ref stationStores[i]; + store.itemId = packet.ItemId[i]; + store.count = packet.ItemCount[i]; + store.localOrder = packet.LocalOrder[i]; + store.remoteOrder = packet.RemoteOrder[i]; + store.max = packet.StoreMax[i]; + store.localLogic = (ELogisticStorage)packet.LocalLogic[i]; + store.remoteLogic = (ELogisticStorage)packet.RemoteLogic[i]; + } + var tmp = entry.station.storage; + entry.station.storage = stationStores; + // expand entry.UpdateItems() + entry.storageItem0.SetVisible(stationStores[0].itemId > 0); + entry.storageItem0._Update(); + entry.storageItem1.SetVisible(stationStores[1].itemId > 0); + entry.storageItem1._Update(); + entry.storageItem2.SetVisible(stationStores[2].itemId > 0); + entry.storageItem2._Update(); + entry.storageItem3.SetVisible(stationStores[3].itemId > 0); + entry.storageItem3._Update(); + entry.storageItem4.SetVisible(stationStores[4].itemId > 0); + entry.storageItem4._Update(); + entry.station.storage = tmp; + + // Update drone and ship count UI + switch (entry.target.entryType) + { + case EControlPanelEntryType.InterstellarStation: + entry.SetDeliveryVisible(true); + entry.droneCountSB.Append(packet.IdleDroneCount); + entry.droneCountSB.Append('/'); + entry.droneCountSB.Append(packet.IdleDroneCount + packet.WorkDroneCount); + entry.droneCountText.text = entry.droneCountSB.ToString(); + entry.droneCountSB.Clear(); + entry.shipCountSB.Append(packet.IdleShipCount); + entry.shipCountSB.Append('/'); + entry.shipCountSB.Append(packet.IdleShipCount + packet.WorkShipCount); + entry.shipCountText.text = entry.shipCountSB.ToString(); + entry.shipCountSB.Clear(); + entry.warperCountText.text = packet.WarperCount.ToString(); + entry.fillNecessaryButton.button.interactable = true; + entry.fillNecessaryImage.raycastTarget = true; + break; + + case EControlPanelEntryType.OrbitCollector: + entry.SetDeliveryVisible(false); + entry.droneCountText.text = ""; + entry.shipCountText.text = ""; + entry.warperCountText.text = ""; + entry.fillNecessaryButton.button.interactable = false; + entry.fillNecessaryImage.raycastTarget = false; + break; + + case EControlPanelEntryType.VeinCollector: + entry.SetDeliveryVisible(false); + entry.droneIconImage.raycastTarget = true; + entry.droneIconImage.color = entry.masterWindow.deliveryIconColor; + entry.droneCountText.color = entry.masterWindow.deliveryTextColor; + entry.droneCountSB.Append(packet.IdleDroneCount); + entry.droneCountSB.Append('/'); + entry.droneCountSB.Append(packet.IdleDroneCount + packet.WorkDroneCount); + entry.droneCountText.text = entry.droneCountSB.ToString(); + entry.droneCountSB.Clear(); + entry.shipCountText.text = ""; + entry.warperCountText.text = ""; + entry.fillNecessaryButton.button.interactable = true; + entry.fillNecessaryImage.raycastTarget = true; + break; + + case EControlPanelEntryType.LocalStation: + entry.SetDeliveryVisible(false); + entry.droneCountText.text = ""; + entry.shipCountText.text = ""; + entry.warperCountText.text = ""; + entry.fillNecessaryButton.button.interactable = false; + entry.fillNecessaryImage.raycastTarget = false; + break; + } + + // Update power UI + entry.powerGroupGo.SetActive(entry.target.entryType != EControlPanelEntryType.OrbitCollector); + var consumerRatio = (float)packet.ConsumerRatio; + int powerStatusCode; + if (consumerRatio >= 1f) + { + entry.powerCircleFg.fillAmount = 1f; + powerStatusCode = 1; + } + else + { + entry.powerCircleFg.fillAmount = consumerRatio; + powerStatusCode = ((consumerRatio >= 0.1f) ? 2 : 3); + } + StringBuilderUtility.WriteKMG1000(entry.sbw, 8, packet.RequirePower, true); + StringBuilderUtility.WriteKMG1000(entry.sbw2, 8, packet.WorkEnergyPerTick * 60L, true); + entry.powerText.text = entry.sbw.ToString(); + entry.maxChargePowerValue.text = entry.sbw2.ToString(); + entry.powerRoundFg.fillAmount = packet.PowerRound; + switch (powerStatusCode) + { + case 1: + entry.powerSignImage.color = entry.masterWindow.powerSignColor1; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor1; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor1; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor1; + entry.powerText.color = entry.masterWindow.powerTextColor1; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor1; + break; + case 2: + entry.powerSignImage.color = entry.masterWindow.powerSignColor2; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor2; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor2; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor2; + entry.powerText.color = entry.masterWindow.powerTextColor2; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor2; + break; + case 3: + entry.powerSignImage.color = entry.masterWindow.powerSignColor3; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor3; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor3; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor3; + entry.powerText.color = entry.masterWindow.powerTextColor3; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor3; + break; + default: + entry.powerSignImage.color = entry.masterWindow.powerSignColor0; + entry.powerCircleBg.color = entry.masterWindow.powerCircleBgColor0; + entry.powerCircleFg.color = entry.masterWindow.powerCircleFgColor0; + entry.powerRoundFg.color = entry.masterWindow.powerRoundFgColor0; + entry.powerText.color = entry.masterWindow.powerTextColor0; + entry.maxChargePowerValue.color = entry.masterWindow.powerTextColor0; + break; + } + } +} diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelAdvancedMinerEntry_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelAdvancedMinerEntry_Patch.cs index f5931a3f2..c8b4cc8b2 100644 --- a/NebulaPatcher/Patches/Dynamic/UIControlPanelAdvancedMinerEntry_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelAdvancedMinerEntry_Patch.cs @@ -1,6 +1,7 @@ #region using HarmonyLib; +using NebulaModel.Packets.Logistics.ControlPanel; using NebulaWorld; #endregion @@ -10,6 +11,54 @@ namespace NebulaPatcher.Patches.Dynamic; [HarmonyPatch(typeof(UIControlPanelAdvancedMinerEntry))] internal class UIControlPanelAdvancedMinerEntry_Patch { + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelAdvancedMinerEntry.OnSetTarget))] + public static bool OnSetTarget_Prefix(UIControlPanelAdvancedMinerEntry __instance) + { + if (!Multiplayer.IsActive || Multiplayer.Session.IsServer) return true; + var factory = GameMain.data.galaxy.PlanetById(__instance.target.astroId).factory; + if (factory == null) + { + LCPObjectEntryRequest.Instance.Set(__instance, true); + Multiplayer.Session.Client.SendPacket(LCPObjectEntryRequest.Instance); + __instance.station = Multiplayer.Session.StationsUI.DummyStationStoreContainer; + __instance.storageItem.station = __instance.station; + __instance.storageItem.SetVisible(false); + return false; + } + return true; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelAdvancedMinerEntry._OnUpdate))] + public static bool Update_Prefix(UIControlPanelAdvancedMinerEntry __instance) + { + if (!Multiplayer.IsActive || Multiplayer.Session.IsServer) return true; + if (__instance.factory != null) return true; + + UIControlPanelObjectEntry_Patch.OnUpdate(__instance); + __instance.viewToTargetButton.button.interactable = __instance.isLocal; + if (UIControlPanelWindow_Patch.UpdateTimer % 60 == 0) + { + // Request content update every 1s + LCPObjectEntryRequest.Instance.Set(__instance, false); + Multiplayer.Session.Client.SendPacket(LCPObjectEntryRequest.Instance); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelAdvancedMinerEntry.isLocal), MethodType.Getter)] + public static bool IsLocal_Prefix(UIControlPanelAdvancedMinerEntry __instance, ref bool __result) + { + if (__instance.factory == null) + { + __result = false; + return false; + } + return true; + } + [HarmonyPrefix] [HarmonyPatch(nameof(UIControlPanelAdvancedMinerEntry.OnFillNecessaryButtonClick))] public static bool OnFillNecessaryButtonClick_Prefix() diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelDispenserEntry_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelDispenserEntry_Patch.cs index ab272fb0f..01d32d85f 100644 --- a/NebulaPatcher/Patches/Dynamic/UIControlPanelDispenserEntry_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelDispenserEntry_Patch.cs @@ -1,6 +1,7 @@ #region using HarmonyLib; +using NebulaModel.Packets.Logistics.ControlPanel; using NebulaWorld; #endregion @@ -10,6 +11,53 @@ namespace NebulaPatcher.Patches.Dynamic; [HarmonyPatch(typeof(UIControlPanelDispenserEntry))] internal class UIControlPanelDispenserEntry_Patch { + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelDispenserEntry.OnSetTarget))] + public static bool OnSetTarget_Prefix(UIControlPanelDispenserEntry __instance) + { + if (!Multiplayer.IsActive || Multiplayer.Session.IsServer) return true; + var planet = GameMain.data.galaxy.PlanetById(__instance.target.astroId); + var factory = planet.factory; + if (factory == null) + { + LCPObjectEntryRequest.Instance.Set(__instance, true); + Multiplayer.Session.Client.SendPacket(LCPObjectEntryRequest.Instance); + return false; + } + return true; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelDispenserEntry._OnUpdate))] + public static bool Update_Prefix(UIControlPanelDispenserEntry __instance) + { + if (!Multiplayer.IsActive || Multiplayer.Session.IsServer) return true; + if (__instance.factory != null) return true; + + UIControlPanelObjectEntry_Patch.OnUpdate(__instance); + __instance.viewToTargetButton.button.interactable = __instance.isLocal; + if (UIControlPanelWindow_Patch.UpdateTimer % 60 == 0) + { + // Request content update every 1s + LCPObjectEntryRequest.Instance.Set(__instance, false); + Multiplayer.Session.Client.SendPacket(LCPObjectEntryRequest.Instance); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelDispenserEntry.isLocal), MethodType.Getter)] + public static bool IsLocal_Prefix(UIControlPanelDispenserEntry __instance, ref bool __result) + { + if (__instance.factory == null) + { + __result = false; + return false; + } + return true; + } + + [HarmonyPrefix] [HarmonyPatch(nameof(UIControlPanelDispenserEntry.OnFillNecessaryButtonClick))] public static bool OnFillNecessaryButtonClick_Prefix(UIControlPanelDispenserEntry __instance) diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelObjectEntry_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelObjectEntry_Patch.cs new file mode 100644 index 000000000..8dd621217 --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelObjectEntry_Patch.cs @@ -0,0 +1,28 @@ +#region + +using System.Collections.Generic; +using HarmonyLib; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; +#pragma warning disable IDE0060 // Remove unused parameter + +[HarmonyPatch(typeof(UIControlPanelObjectEntry))] +internal class UIControlPanelObjectEntry_Patch +{ + [HarmonyReversePatch(HarmonyReversePatchType.Original)] + [HarmonyPatch(nameof(UIControlPanelObjectEntry._OnUpdate))] + public static void OnUpdate(UIControlPanelObjectEntry entry) + { + // Use HarmonyReversePatch to call base._OnUpdate as base is not available in static function + _ = Transpiler(null); + return; + + static IEnumerable Transpiler(IEnumerable instructions) + { + return instructions; + } + } +} + diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelPlanetEntry_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelPlanetEntry_Patch.cs new file mode 100644 index 000000000..ea9a2404d --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelPlanetEntry_Patch.cs @@ -0,0 +1,52 @@ +#region + +using HarmonyLib; +using NebulaWorld; +using UITools; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; + +[HarmonyPatch(typeof(UIControlPanelPlanetEntry))] +internal class UIControlPanelPlanetEntry_Patch +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelPlanetEntry.UpdateBanner))] + public static bool UpdateBanner_Prefix(UIControlPanelPlanetEntry __instance) + { + if (!Multiplayer.IsActive || Multiplayer.Session.IsServer) return true; + + // Copy from vanilla code, except that ref to planet.factory is removed + if (!__instance.isTargetDataValid) + { + return false; + } + if (__instance.isLocal) + { + __instance.distanceText.text = "当前星球".Translate(); + __instance.distanceText.font = __instance.masterWindow.FONT_SAIRASB; + } + else + { + var uPosition = __instance.planet.uPosition; + var magnitude = (GameMain.mainPlayer.uPosition - uPosition).magnitude; // No need to access planet.factory in vanilla code + var distanceLY = magnitude / 2400000.0; + if (distanceLY < 0.10000000149011612) + { + var distanceAU = magnitude / 40000.0; + __instance.distanceText.text = string.Format("距伊卡洛斯距离提示".Translate(), distanceAU.ToString("F1") + " AU"); + } + else + { + __instance.distanceText.text = string.Format("距伊卡洛斯距离提示".Translate(), distanceLY.ToString("F1") + " ly"); + } + __instance.distanceText.font = __instance.masterWindow.FONT_DIN; + } + __instance.planetTypeText.text = __instance.planet.typeString; + var displayName = __instance.planet.displayName; + Utils.UITextTruncateShow(__instance.planetNameText, displayName, __instance.planetNameTextWidthLimit, ref __instance.planetNameTextSettings); + + return false; + } +} diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelStationEntry_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelStationEntry_Patch.cs index 64d7ee990..45137bfb7 100644 --- a/NebulaPatcher/Patches/Dynamic/UIControlPanelStationEntry_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelStationEntry_Patch.cs @@ -1,6 +1,7 @@ #region using HarmonyLib; +using NebulaModel.Packets.Logistics.ControlPanel; using NebulaWorld; #endregion @@ -10,6 +11,62 @@ namespace NebulaPatcher.Patches.Dynamic; [HarmonyPatch(typeof(UIControlPanelStationEntry))] internal class UIControlPanelStationEntry_Patch { + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelStationEntry.OnSetTarget))] + public static bool OnSetTarget_Prefix(UIControlPanelStationEntry __instance) + { + if (!Multiplayer.IsActive || Multiplayer.Session.IsServer) return true; + var factory = GameMain.data.galaxy.PlanetById(__instance.target.astroId).factory; + if (factory == null) + { + LCPObjectEntryRequest.Instance.Set(__instance, true); + Multiplayer.Session.Client.SendPacket(LCPObjectEntryRequest.Instance); + __instance.station = Multiplayer.Session.StationsUI.DummyStationStoreContainer; + __instance.storageItem0.station = __instance.station; + __instance.storageItem1.station = __instance.station; + __instance.storageItem2.station = __instance.station; + __instance.storageItem3.station = __instance.station; + __instance.storageItem4.station = __instance.station; + __instance.storageItem0.SetVisible(false); + __instance.storageItem1.SetVisible(false); + __instance.storageItem2.SetVisible(false); + __instance.storageItem3.SetVisible(false); + __instance.storageItem4.SetVisible(false); + return false; + } + return true; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelStationEntry._OnUpdate))] + public static bool Update_Prefix(UIControlPanelStationEntry __instance) + { + if (!Multiplayer.IsActive || Multiplayer.Session.IsServer) return true; + if (__instance.factory != null) return true; + + UIControlPanelObjectEntry_Patch.OnUpdate(__instance); + __instance.viewToTargetButton.button.interactable = __instance.isLocal; + if (UIControlPanelWindow_Patch.UpdateTimer % 60 == 0) + { + // Request content update every 1s + LCPObjectEntryRequest.Instance.Set(__instance, false); + Multiplayer.Session.Client.SendPacket(LCPObjectEntryRequest.Instance); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelStationEntry.isLocal), MethodType.Getter)] + public static bool IsLocal_Prefix(UIControlPanelAdvancedMinerEntry __instance, ref bool __result) + { + if (__instance.factory == null) + { + __result = false; + return false; + } + return true; + } + [HarmonyPrefix] [HarmonyPatch(nameof(UIControlPanelStationEntry.OnFillNecessaryButtonClick))] public static bool OnFillNecessaryButtonClick_Prefix() diff --git a/NebulaPatcher/Patches/Dynamic/UIControlPanelWindow_Patch.cs b/NebulaPatcher/Patches/Dynamic/UIControlPanelWindow_Patch.cs index c977335a1..927917a1f 100644 --- a/NebulaPatcher/Patches/Dynamic/UIControlPanelWindow_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/UIControlPanelWindow_Patch.cs @@ -11,6 +11,8 @@ namespace NebulaPatcher.Patches.Dynamic; [HarmonyPatch(typeof(UIControlPanelWindow))] internal class UIControlPanelWindow_Patch { + public static int UpdateTimer { get; private set; } + [HarmonyPrefix, HarmonyPriority(Priority.Last)] [HarmonyPatch(nameof(UIControlPanelWindow.DetermineFilterResults))] public static bool DetermineFilterResults_Prefix(UIControlPanelWindow __instance) @@ -22,4 +24,55 @@ public static bool DetermineFilterResults_Prefix(UIControlPanelWindow __instance Multiplayer.Session.Client.SendPacket(new LCPFilterResultsRequest(__instance.filter)); return false; } + + [HarmonyPrefix] + [HarmonyPatch(nameof(UIControlPanelWindow.DetermineSelectionInspector))] + public static bool DetermineSelectionInspectorPrefix(UIControlPanelWindow __instance) + { + if (!Multiplayer.IsActive) return true; + + // In MP, open the local station window instead of inspector temporarily + // TODO: Enable Inspector in client for remote entry and sync + __instance.needDetermineSelectionInspector = false; + var planet = GameMain.galaxy.PlanetById(__instance.selection.astroId); + var factory = planet?.factory; + if (factory == null || GameMain.localPlanet != planet) return false; + switch (__instance.selection.entryType) + { + case EControlPanelEntryType.InterstellarStation: + case EControlPanelEntryType.OrbitCollector: + case EControlPanelEntryType.LocalStation: + case EControlPanelEntryType.VeinCollector: + // Close station window first so it can stay on top + UIRoot.instance.uiGame.ShutStationWindow(); + var minerId = factory.entityPool[__instance.selection.objId].minerId; + var stationId = factory.entityPool[__instance.selection.objId].stationId; + UIRoot.instance.uiGame.stationWindow.veinCollectorPanel.minerId = minerId; + UIRoot.instance.uiGame.stationWindow.stationId = stationId; + if (UIRoot.instance.uiGame.inspectStationId == 0 && stationId > 0) + { + UIRoot.instance.uiGame.OpenStationWindow(); + } + break; + + case EControlPanelEntryType.Dispenser: + // Close station window first so it can stay on top + UIRoot.instance.uiGame.ShutDispenserWindow(); + var dispenserId = factory.entityPool[__instance.selection.objId].dispenserId; + UIRoot.instance.uiGame.dispenserWindow.dispenserId = dispenserId; + if (UIRoot.instance.uiGame.inspectDispenserId == 0 && dispenserId > 0) + { + UIRoot.instance.uiGame.OpenDispenserWindow(); + } + break; + } + return false; + } + + [HarmonyPostfix] + [HarmonyPatch(nameof(UIControlPanelWindow._OnUpdate))] + public static void OnUpdate_Postfix() + { + UpdateTimer = (++UpdateTimer) % 600; + } } diff --git a/NebulaWorld/Logistics/StationUIManager.cs b/NebulaWorld/Logistics/StationUIManager.cs index 2e39a104a..fd3f629d3 100644 --- a/NebulaWorld/Logistics/StationUIManager.cs +++ b/NebulaWorld/Logistics/StationUIManager.cs @@ -27,8 +27,19 @@ public int StorageMaxChangeId public ToggleSwitch IsIncomingRequest { get; set; } = new(); + public StationComponent DummyStationStoreContainer { get; private set; } // For UIControlPanelStorageItem + + public StationUIManager() + { + DummyStationStoreContainer = new() + { + storage = new StationStore[5] + }; + } + public void Dispose() { + DummyStationStoreContainer = null; GC.SuppressFinalize(this); } @@ -162,7 +173,7 @@ private static void UpdateSettingsUI(StationComponent stationComponent, ref Stat } case StationUI.EUISettings.MaxTripVessel: { - double value = packet.SettingValue; + var value = packet.SettingValue; value = value switch { > 40.5 => 10000.0, @@ -186,7 +197,7 @@ private static void UpdateSettingsUI(StationComponent stationComponent, ref Stat } case StationUI.EUISettings.WarpDistance: { - double value = packet.SettingValue; + var value = packet.SettingValue; switch (value) { case < 1.5: