mirror of
https://github.com/soarqin/DSP_Mods.git
synced 2026-08-05 20:50:25 +08:00
refactor(UXAssist): split LogisticsPatch into focused classes
This commit is contained in:
@@ -0,0 +1,142 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using BepInEx.Configuration;
|
||||||
|
using HarmonyLib;
|
||||||
|
using UnityEngine;
|
||||||
|
using UXAssist.Common;
|
||||||
|
|
||||||
|
namespace UXAssist.Patches.Logistics;
|
||||||
|
|
||||||
|
internal class AutoConfigLogistics : PatchImpl<AutoConfigLogistics>
|
||||||
|
{
|
||||||
|
protected override void OnEnable()
|
||||||
|
{
|
||||||
|
ToggleLimitAutoReplenishCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisable()
|
||||||
|
{
|
||||||
|
ToggleLimitAutoReplenishCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ToggleLimitAutoReplenishCount()
|
||||||
|
{
|
||||||
|
LimitAutoReplenishCount.Enable(LogisticsPatch.AutoConfigLogisticsEnabled.Value && LogisticsPatch.AutoConfigLimitAutoReplenishCount.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LimitAutoReplenishCount : PatchImpl<LimitAutoReplenishCount>
|
||||||
|
{
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(PlanetFactory), nameof(PlanetFactory.EntityAutoReplenishIfNeeded))]
|
||||||
|
[HarmonyPatch(typeof(PlanetFactory), nameof(PlanetFactory.StationAutoReplenishIfNeeded))]
|
||||||
|
private static IEnumerable<CodeInstruction> PlanetFactory_StationAutoReplenishIfNeeded_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
// Patch dispenser courier count
|
||||||
|
matcher.Start().MatchForward(false,
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(DispenserComponent), nameof(DispenserComponent.workCourierDatas))),
|
||||||
|
new CodeMatch(OpCodes.Ldlen),
|
||||||
|
new CodeMatch(OpCodes.Conv_I4)
|
||||||
|
);
|
||||||
|
matcher.Repeat(m => m.Advance(4).InsertAndAdvance(
|
||||||
|
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(LogisticsPatch), nameof(LogisticsPatch.AutoConfigDispenserCourierCount))),
|
||||||
|
new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry<int>), nameof(ConfigEntry<int>.Value))),
|
||||||
|
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Math), nameof(Math.Min), [typeof(int), typeof(int)]))
|
||||||
|
));
|
||||||
|
|
||||||
|
// Patch PLS/ILS drone count
|
||||||
|
matcher.Start().MatchForward(false,
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.workDroneDatas))),
|
||||||
|
new CodeMatch(OpCodes.Ldlen),
|
||||||
|
new CodeMatch(OpCodes.Conv_I4)
|
||||||
|
);
|
||||||
|
matcher.Repeat(m =>
|
||||||
|
{
|
||||||
|
var instr = m.Instruction;
|
||||||
|
m.Advance(4).InsertAndAdvance(
|
||||||
|
instr,
|
||||||
|
Transpilers.EmitDelegate((int x, StationComponent station)
|
||||||
|
=> Math.Min(x, station.isStellar ? LogisticsPatch.AutoConfigILSDroneCount.Value : LogisticsPatch.AutoConfigPLSDroneCount.Value))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Patch ILS ship count
|
||||||
|
matcher.Start().MatchForward(false,
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.workShipDatas))),
|
||||||
|
new CodeMatch(OpCodes.Ldlen),
|
||||||
|
new CodeMatch(OpCodes.Conv_I4)
|
||||||
|
);
|
||||||
|
matcher.Repeat(m => m.Advance(4).InsertAndAdvance(
|
||||||
|
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(LogisticsPatch), nameof(LogisticsPatch.AutoConfigILSShipCount))),
|
||||||
|
new CodeInstruction(OpCodes.Call, AccessTools.PropertyGetter(typeof(ConfigEntry<int>), nameof(ConfigEntry<int>.Value))),
|
||||||
|
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Math), nameof(Math.Min), [typeof(int), typeof(int)]))
|
||||||
|
));
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(BuildTool_Addon), nameof(BuildTool_Addon.SetDefaultParams))]
|
||||||
|
private static void BuildTool_Addon_SetDefaultParams_Postfix(BuildTool_Addon __instance, int bpIndex)
|
||||||
|
{
|
||||||
|
if (__instance.handPrefabDesc.isDispenser)
|
||||||
|
{
|
||||||
|
__instance.handBpParams[bpIndex][2] = (int)(long)(5000.0 * LogisticsPatch.AutoConfigDispenserChargePower.Value + 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.NewStationComponent))]
|
||||||
|
private static void PlanetTransport_NewStationComponent_Postfix(PlanetTransport __instance, StationComponent __result)
|
||||||
|
{
|
||||||
|
LogisticsPatch.DoConfigStation(__instance.factory, __result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(DefenseSystem), nameof(DefenseSystem.NewBattleBaseComponent))]
|
||||||
|
private static void DefenseSystem_NewBattleBaseComponent_Postfix(DefenseSystem __instance, int __result)
|
||||||
|
{
|
||||||
|
if (__result <= 0) return;
|
||||||
|
LogisticsPatch.BattleBaseSetChargePower(__instance.factory, __instance.battleBases[__result]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.NewDispenserComponent))]
|
||||||
|
private static void PlanetTransport_NewDispenserComponent_Postfix(PlanetTransport __instance, int __result)
|
||||||
|
{
|
||||||
|
if (__result <= 0) return;
|
||||||
|
LogisticsPatch.DispenserFillCouriers(__instance.factory, __instance.dispenserPool[__result]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AutoConfigLogisticsSetDefaultRemoteLogicToStorage : PatchImpl<AutoConfigLogisticsSetDefaultRemoteLogicToStorage>
|
||||||
|
{
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnItemPickerReturn))]
|
||||||
|
[HarmonyPatch(typeof(UIStationStorage), nameof(UIStationStorage.OnItemPickerReturn))]
|
||||||
|
private static IEnumerable<CodeInstruction> UIStationStorage_OnItemPickerReturn_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
matcher.MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Ldarg_0),
|
||||||
|
new CodeMatch(OpCodes.Ldfld),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.isStellar))),
|
||||||
|
new CodeMatch(ci => ci.opcode == OpCodes.Brtrue_S || ci.opcode == OpCodes.Brtrue),
|
||||||
|
new CodeMatch(OpCodes.Ldc_I4_0),
|
||||||
|
new CodeMatch(ci => ci.opcode == OpCodes.Br_S || ci.opcode == OpCodes.Br),
|
||||||
|
new CodeMatch(OpCodes.Ldc_I4_1),
|
||||||
|
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(GameMain), nameof(GameMain.mainPlayer)))
|
||||||
|
);
|
||||||
|
if (matcher.IsValid)
|
||||||
|
{
|
||||||
|
matcher.RemoveInstructions(7).InsertAndAdvance(
|
||||||
|
new CodeInstruction(OpCodes.Ldc_I4_0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,419 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using HarmonyLib;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UXAssist.Common;
|
||||||
|
|
||||||
|
namespace UXAssist.Patches.Logistics;
|
||||||
|
|
||||||
|
internal class LogisticsCapacityTweaks : PatchImpl<LogisticsCapacityTweaks>
|
||||||
|
{
|
||||||
|
private static KeyCode _lastKey = KeyCode.None;
|
||||||
|
private static long _nextKeyTick;
|
||||||
|
private static bool _skipNextUIStationStorageEvent;
|
||||||
|
private static bool _skipNextUIControlPanelStationStorageEvent;
|
||||||
|
private static bool _refreshingUIStationStorage;
|
||||||
|
private static bool _refreshingUIControlPanelStationStorage;
|
||||||
|
|
||||||
|
private static bool UpdateKeyPressed(KeyCode code)
|
||||||
|
{
|
||||||
|
if (!Input.GetKey(code))
|
||||||
|
return false;
|
||||||
|
var main = GameMain.instance;
|
||||||
|
if (main == null)
|
||||||
|
return false;
|
||||||
|
if (code != _lastKey)
|
||||||
|
{
|
||||||
|
_lastKey = code;
|
||||||
|
_nextKeyTick = main.timei + 30;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currTick = main.timei;
|
||||||
|
if (_nextKeyTick > currTick) return false;
|
||||||
|
_nextKeyTick = currTick + 4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void UpdateInput()
|
||||||
|
{
|
||||||
|
if (_lastKey != KeyCode.None && Input.GetKeyUp(_lastKey))
|
||||||
|
{
|
||||||
|
_lastKey = KeyCode.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VFInput.shift) return;
|
||||||
|
var ctrl = VFInput.control;
|
||||||
|
var alt = VFInput.alt;
|
||||||
|
if (ctrl && alt) return;
|
||||||
|
int delta;
|
||||||
|
if (UpdateKeyPressed(KeyCode.LeftArrow))
|
||||||
|
{
|
||||||
|
if (ctrl)
|
||||||
|
delta = -100000;
|
||||||
|
else if (alt)
|
||||||
|
delta = -1000;
|
||||||
|
else
|
||||||
|
delta = -10;
|
||||||
|
}
|
||||||
|
else if (UpdateKeyPressed(KeyCode.RightArrow))
|
||||||
|
{
|
||||||
|
if (ctrl)
|
||||||
|
delta = 100000;
|
||||||
|
else if (alt)
|
||||||
|
delta = 1000;
|
||||||
|
else
|
||||||
|
delta = 10;
|
||||||
|
}
|
||||||
|
else if (UpdateKeyPressed(KeyCode.DownArrow))
|
||||||
|
{
|
||||||
|
if (ctrl)
|
||||||
|
delta = -1000000;
|
||||||
|
else if (alt)
|
||||||
|
delta = -10000;
|
||||||
|
else
|
||||||
|
delta = -100;
|
||||||
|
}
|
||||||
|
else if (UpdateKeyPressed(KeyCode.UpArrow))
|
||||||
|
{
|
||||||
|
if (ctrl)
|
||||||
|
delta = 1000000;
|
||||||
|
else if (alt)
|
||||||
|
delta = 10000;
|
||||||
|
else
|
||||||
|
delta = 100;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targets = new List<RaycastResult>();
|
||||||
|
EventSystem.current.RaycastAll(new PointerEventData(EventSystem.current) { position = Input.mousePosition }, targets);
|
||||||
|
foreach (var target in targets)
|
||||||
|
{
|
||||||
|
StationComponent station = null;
|
||||||
|
int index = -1;
|
||||||
|
PlanetFactory planetFactory = null;
|
||||||
|
var stationStorage = target.gameObject.GetComponentInParent<UIStationStorage>();
|
||||||
|
bool isControlPanelStationStorage = false;
|
||||||
|
if (stationStorage != null)
|
||||||
|
{
|
||||||
|
station = stationStorage.station;
|
||||||
|
index = stationStorage.index;
|
||||||
|
planetFactory = stationStorage.stationWindow?.factory;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var controlPanelStationStorage = target.gameObject.GetComponentInParent<UIControlPanelStationStorage>();
|
||||||
|
if (controlPanelStationStorage == null) continue;
|
||||||
|
station = controlPanelStationStorage.station;
|
||||||
|
index = controlPanelStationStorage.index;
|
||||||
|
planetFactory = controlPanelStationStorage.factory;
|
||||||
|
isControlPanelStationStorage = true;
|
||||||
|
}
|
||||||
|
if (station?.storage is null) continue;
|
||||||
|
ref var storage = ref station.storage[index];
|
||||||
|
var oldMax = storage.max;
|
||||||
|
var newMax = oldMax + delta;
|
||||||
|
if (newMax < 0)
|
||||||
|
{
|
||||||
|
newMax = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int itemCountMax;
|
||||||
|
if (LogisticsPatch.AllowOverflowInLogisticsEnabled.Value)
|
||||||
|
{
|
||||||
|
itemCountMax = 90000000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (planetFactory == null || station.entityId <= 0 || station.entityId >= planetFactory.entityCursor) continue;
|
||||||
|
var modelProto = LDB.models.Select(planetFactory.entityPool[station.entityId].modelIndex);
|
||||||
|
itemCountMax = modelProto == null ? 0 : modelProto.prefabDesc.stationMaxItemCount;
|
||||||
|
itemCountMax += station.isStellar && !station.isCollector ? GameMain.history.remoteStationExtraStorage : GameMain.history.localStationExtraStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newMax > itemCountMax)
|
||||||
|
{
|
||||||
|
newMax = itemCountMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.max = newMax;
|
||||||
|
if (isControlPanelStationStorage)
|
||||||
|
{
|
||||||
|
_skipNextUIControlPanelStationStorageEvent = oldMax / 100 != newMax / 100;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_skipNextUIStationStorageEvent = oldMax / 100 != newMax / 100;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(UIStationStorage), "RefreshValues")]
|
||||||
|
private static void UIStationStorage_RefreshValues_Prefix()
|
||||||
|
{
|
||||||
|
_refreshingUIStationStorage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(UIStationStorage), "RefreshValues")]
|
||||||
|
private static void UIStationStorage_RefreshValues_Postfix()
|
||||||
|
{
|
||||||
|
_refreshingUIStationStorage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(UIControlPanelStationStorage), "RefreshValues")]
|
||||||
|
private static void UIControlPanelStationStorage_RefreshValues_Prefix()
|
||||||
|
{
|
||||||
|
_refreshingUIControlPanelStationStorage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(UIControlPanelStationStorage), "RefreshValues")]
|
||||||
|
private static void UIControlPanelStationStorage_RefreshValues_Postfix()
|
||||||
|
{
|
||||||
|
_refreshingUIControlPanelStationStorage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(UIStationStorage), nameof(UIStationStorage.OnMaxSliderValueChange))]
|
||||||
|
private static bool UIStationStorage_OnMaxSliderValueChange_Prefix()
|
||||||
|
{
|
||||||
|
if (!_refreshingUIStationStorage && !_skipNextUIStationStorageEvent) return true;
|
||||||
|
_skipNextUIStationStorageEvent = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnMaxSliderValueChange))]
|
||||||
|
private static bool UIControlPanelStationStorage_OnMaxSliderValueChange_Prefix()
|
||||||
|
{
|
||||||
|
if (!_refreshingUIControlPanelStationStorage && !_skipNextUIControlPanelStationStorageEvent) return true;
|
||||||
|
_skipNextUIControlPanelStationStorageEvent = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.OnTechFunctionUnlocked))]
|
||||||
|
private static bool PlanetTransport_OnTechFunctionUnlocked_Prefix(PlanetTransport __instance, int _funcId, double _valuelf, int _level)
|
||||||
|
{
|
||||||
|
switch (_funcId)
|
||||||
|
{
|
||||||
|
case 30:
|
||||||
|
{
|
||||||
|
var stationPool = __instance.stationPool;
|
||||||
|
var factory = __instance.factory;
|
||||||
|
var history = GameMain.history;
|
||||||
|
for (var i = __instance.stationCursor - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
if (stationPool[i] == null || stationPool[i].id != i || (stationPool[i].isStellar && !stationPool[i].isCollector && !stationPool[i].isVeinCollector)) continue;
|
||||||
|
var modelIndex = factory.entityPool[stationPool[i].entityId].modelIndex;
|
||||||
|
var maxCount = LDB.models.Select(modelIndex).prefabDesc.stationMaxItemCount;
|
||||||
|
var oldMaxCount = maxCount + history.localStationExtraStorage - _valuelf;
|
||||||
|
if (oldMaxCount < 1.0) continue;
|
||||||
|
var intOldMaxCount = (int)Math.Round(oldMaxCount);
|
||||||
|
var intNewMaxCount = maxCount + history.localStationExtraStorage;
|
||||||
|
var ratio = intNewMaxCount / oldMaxCount;
|
||||||
|
var storage = stationPool[i].storage;
|
||||||
|
for (var j = storage.Length - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
var max = storage[j].max;
|
||||||
|
if (max + 10 < intOldMaxCount || max >= intNewMaxCount) continue;
|
||||||
|
storage[j].max = Mathf.RoundToInt((float)(max * ratio / 50.0)) * 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 31:
|
||||||
|
{
|
||||||
|
var stationPool = __instance.stationPool;
|
||||||
|
var factory = __instance.factory;
|
||||||
|
var history = GameMain.history;
|
||||||
|
for (var i = __instance.stationCursor - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
if (stationPool[i] == null || stationPool[i].id != i || !stationPool[i].isStellar || stationPool[i].isCollector || stationPool[i].isVeinCollector) continue;
|
||||||
|
var modelIndex = factory.entityPool[stationPool[i].entityId].modelIndex;
|
||||||
|
var maxCount = LDB.models.Select(modelIndex).prefabDesc.stationMaxItemCount;
|
||||||
|
var oldMaxCount = maxCount + history.remoteStationExtraStorage - _valuelf;
|
||||||
|
if (oldMaxCount < 1.0) continue;
|
||||||
|
var intOldMaxCount = (int)Math.Round(oldMaxCount);
|
||||||
|
var intNewMaxCount = maxCount + history.remoteStationExtraStorage;
|
||||||
|
var ratio = intNewMaxCount / oldMaxCount;
|
||||||
|
var storage = stationPool[i].storage;
|
||||||
|
for (var j = storage.Length - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
var max = storage[j].max;
|
||||||
|
if (max + 10 < intOldMaxCount || max >= intNewMaxCount) continue;
|
||||||
|
storage[j].max = Mathf.RoundToInt((float)(max * ratio / 100.0)) * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class GreaterPowerUsageInLogistics : PatchImpl<GreaterPowerUsageInLogistics>
|
||||||
|
{
|
||||||
|
protected override void OnEnable()
|
||||||
|
{
|
||||||
|
var window = UIRoot.instance?.uiGame?.stationWindow;
|
||||||
|
if (window == null) return;
|
||||||
|
window._Close();
|
||||||
|
window.maxMiningSpeedSlider.maxValue = 27f;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDisable()
|
||||||
|
{
|
||||||
|
var window = UIRoot.instance?.uiGame?.stationWindow;
|
||||||
|
if (window == null) return;
|
||||||
|
window._Close();
|
||||||
|
window.maxMiningSpeedSlider.maxValue = 20f;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(UIStationWindow), nameof(UIStationWindow.OnStationIdChange))]
|
||||||
|
private static IEnumerable<CodeInstruction> UIStationWindow_OnStationIdChange_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
matcher.Start().Insert(
|
||||||
|
new CodeInstruction(OpCodes.Ldarg_0),
|
||||||
|
Transpilers.EmitDelegate((UIStationWindow window) =>
|
||||||
|
{
|
||||||
|
window.maxMiningSpeedSlider.maxValue = 27f;
|
||||||
|
})
|
||||||
|
).MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Ldarg_0),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStationWindow), nameof(UIStationWindow.maxChargePowerSlider))),
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4 && ci.OperandIs(0xC350)),
|
||||||
|
new CodeMatch(OpCodes.Conv_I8)
|
||||||
|
);
|
||||||
|
var pos = matcher.Pos + 1;
|
||||||
|
matcher.Advance(5).MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Conv_R4),
|
||||||
|
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertySetter(typeof(Slider), nameof(Slider.value)))
|
||||||
|
);
|
||||||
|
var pos2 = matcher.Pos + 2;
|
||||||
|
matcher.Start().Advance(pos);
|
||||||
|
var ldvar = matcher.InstructionAt(1).Clone();
|
||||||
|
var locWorkEnergyPerTick = matcher.InstructionAt(-2).operand;
|
||||||
|
matcher.RemoveInstructions(pos2 - pos).InsertAndAdvance(
|
||||||
|
ldvar,
|
||||||
|
new CodeInstruction(OpCodes.Ldloc_S, locWorkEnergyPerTick),
|
||||||
|
Transpilers.EmitDelegate((UIStationWindow window, long maxWorkEnergy, long workEnergyPerTick) =>
|
||||||
|
{
|
||||||
|
var maxSliderValue = maxWorkEnergy / 50000L;
|
||||||
|
window.maxChargePowerSlider.maxValue = maxSliderValue + 9;
|
||||||
|
window.maxChargePowerSlider.minValue = maxWorkEnergy / 500000L;
|
||||||
|
if (workEnergyPerTick <= maxWorkEnergy)
|
||||||
|
window.maxChargePowerSlider.Set(workEnergyPerTick / 50000L, false);
|
||||||
|
else
|
||||||
|
window.maxChargePowerSlider.Set(maxSliderValue + (workEnergyPerTick - 1) / maxWorkEnergy + 1, false);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
matcher.MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Ldarg_0),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStationWindow), nameof(UIStationWindow.maxMiningSpeedSlider))),
|
||||||
|
new CodeMatch(OpCodes.Ldarg_0),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStationWindow), nameof(UIStationWindow.factorySystem))),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.minerPool))),
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.minerId))),
|
||||||
|
new CodeMatch(OpCodes.Ldelema, typeof(MinerComponent)),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(MinerComponent), nameof(MinerComponent.speed)))
|
||||||
|
);
|
||||||
|
pos = matcher.Pos + 9;
|
||||||
|
matcher.Advance(5).MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Conv_R4),
|
||||||
|
new CodeMatch(OpCodes.Callvirt, AccessTools.PropertySetter(typeof(Slider), nameof(Slider.value)))
|
||||||
|
);
|
||||||
|
pos2 = matcher.Pos;
|
||||||
|
matcher.Start().Advance(pos).RemoveInstructions(pos2 - pos).Insert(
|
||||||
|
Transpilers.EmitDelegate((int speed) =>
|
||||||
|
{
|
||||||
|
if (speed <= 30000)
|
||||||
|
return (speed - 10000) / 1000;
|
||||||
|
return (speed - 30000) / 10000 + 20;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(UIStationWindow), nameof(UIStationWindow.OnMaxMiningSpeedChange))]
|
||||||
|
private static IEnumerable<CodeInstruction> UIStationWindow_OnMaxMiningSpeedChange_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
matcher.MatchForward(false,
|
||||||
|
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4 && ci.OperandIs(10000)),
|
||||||
|
new CodeMatch(OpCodes.Ldarg_1)
|
||||||
|
);
|
||||||
|
var pos = matcher.Pos;
|
||||||
|
matcher.MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Stloc_1)
|
||||||
|
);
|
||||||
|
var pos2 = matcher.Pos;
|
||||||
|
matcher.Start().Advance(pos);
|
||||||
|
var labels = matcher.Labels;
|
||||||
|
matcher.RemoveInstructions(pos2 - pos);
|
||||||
|
matcher.Insert(
|
||||||
|
new CodeInstruction(OpCodes.Ldarg_1).WithLabels(labels),
|
||||||
|
Transpilers.EmitDelegate((float value) =>
|
||||||
|
{
|
||||||
|
var intval = (int)(value + 0.5f);
|
||||||
|
if (intval <= 20)
|
||||||
|
return intval * 1000 + 10000;
|
||||||
|
return (intval - 20) * 10000 + 30000;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(UIStationWindow), nameof(UIStationWindow.OnMaxChargePowerSliderValueChange))]
|
||||||
|
private static IEnumerable<CodeInstruction> UIStationWindow_OnMaxChargePowerSliderValueChange_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
matcher.MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Ldarg_0),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIStationWindow), nameof(UIStationWindow.factory))),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlanetFactory), nameof(PlanetFactory.powerSystem))),
|
||||||
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerSystem), nameof(PowerSystem.consumerPool)))
|
||||||
|
);
|
||||||
|
var labels = matcher.Labels;
|
||||||
|
matcher.Labels = null;
|
||||||
|
matcher.Insert(
|
||||||
|
new CodeInstruction(OpCodes.Ldarg_0).WithLabels(labels),
|
||||||
|
new CodeInstruction(OpCodes.Ldarg_1),
|
||||||
|
Transpilers.EmitDelegate((UIStationWindow window, float value) =>
|
||||||
|
{
|
||||||
|
float prevMax = window.workEnergyPrefab * 5L / 50000L;
|
||||||
|
if (value <= prevMax)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prevMax * (value - prevMax + 1);
|
||||||
|
}),
|
||||||
|
new CodeInstruction(OpCodes.Starg_S, 1)
|
||||||
|
);
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,340 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using BepInEx.Configuration;
|
||||||
|
using HarmonyLib;
|
||||||
|
using UnityEngine;
|
||||||
|
using UXAssist.Common;
|
||||||
|
using UXAssist.Common.ModFeatures;
|
||||||
|
using GameLogicProc = UXAssist.Common.GameLogic;
|
||||||
|
|
||||||
|
namespace UXAssist.Patches.Logistics;
|
||||||
|
|
||||||
|
[ModFeature("Logistics", Order = 11)]
|
||||||
|
public static class LogisticsPatch
|
||||||
|
{
|
||||||
|
public static ConfigEntry<bool> AutoConfigLogisticsEnabled;
|
||||||
|
public static ConfigEntry<bool> AutoConfigLimitAutoReplenishCount;
|
||||||
|
// Dispenser config
|
||||||
|
public static ConfigEntry<int> AutoConfigDispenserChargePower; // 3~30, display as 300000.0 * value
|
||||||
|
public static ConfigEntry<int> AutoConfigDispenserCourierCount; // 0~10
|
||||||
|
// Battlefield Analysis Base
|
||||||
|
public static ConfigEntry<int> AutoConfigBattleBaseChargePower; // 4~40, display as 300000.0 * value
|
||||||
|
// PLS config
|
||||||
|
public static ConfigEntry<int> AutoConfigPLSChargePower; // 2~20, display as 3000000.0 * value
|
||||||
|
public static ConfigEntry<int> AutoConfigPLSMaxTripDrone; // 1~180, by degress
|
||||||
|
public static ConfigEntry<int> AutoConfigPLSDroneMinDeliver; // 0~10; 0 = 1%, 1~10 = 10% *value
|
||||||
|
public static ConfigEntry<int> AutoConfigPLSMinPilerValue; // 0~4; 0 = Maximum in tech, 1~4 = piler stacking count
|
||||||
|
public static ConfigEntry<int> AutoConfigPLSDroneCount; // 0~50
|
||||||
|
// ILS config
|
||||||
|
public static ConfigEntry<bool> SetDefaultRemoteLogicToStorage;
|
||||||
|
public static ConfigEntry<int> AutoConfigILSChargePower; // 2~20, display as 15000000.0 * value
|
||||||
|
public static ConfigEntry<int> AutoConfigILSMaxTripDrone; // 1~180, by degress
|
||||||
|
public static ConfigEntry<int> AutoConfigILSMaxTripShip; // 1~41; 1~20 = value LY, 21-40 = 2*value-20LY, 41 = Unlimited
|
||||||
|
public static ConfigEntry<int> AutoConfigILSWarperDistance; // 2~21; 2~7 = value * 0.5 - 0.5AU, 8~16 = value - 4AU, 17~20 = value * 2 - 20AU, 21 = 60AU
|
||||||
|
public static ConfigEntry<int> AutoConfigILSDroneMinDeliver; // 0~10; 0 = 1%, 1~10 = 10% *value
|
||||||
|
public static ConfigEntry<int> AutoConfigILSShipMinDeliver; // 0~10; 0 = 1%, 1~10 = 10% *value
|
||||||
|
public static ConfigEntry<int> AutoConfigILSMinPilerValue; // 0~4; 0 = Maximum in tech, 1~4 = piler stacking count
|
||||||
|
public static ConfigEntry<bool> AutoConfigILSIncludeOrbitCollector;
|
||||||
|
public static ConfigEntry<bool> AutoConfigILSWarperNecessary;
|
||||||
|
public static ConfigEntry<int> AutoConfigILSDroneCount; // 0~100
|
||||||
|
public static ConfigEntry<int> AutoConfigILSShipCount; // 0~10
|
||||||
|
// Vein collector config
|
||||||
|
public static ConfigEntry<int> AutoConfigVeinCollectorHarvestSpeed; // 0-20, 100% + 10% * value
|
||||||
|
public static ConfigEntry<int> AutoConfigVeinCollectorMinPilerValue; // 0~4; 0 = Maximum in tech, 1~4 = piler stacking count
|
||||||
|
|
||||||
|
public static ConfigEntry<bool> LogisticsCapacityTweaksEnabled;
|
||||||
|
public static ConfigEntry<bool> AllowOverflowInLogisticsEnabled;
|
||||||
|
public static ConfigEntry<bool> GreaterPowerUsageInLogisticsEnabled;
|
||||||
|
public static ConfigEntry<bool> LogisticsConstrolPanelImprovementEnabled;
|
||||||
|
public static ConfigEntry<bool> RealtimeLogisticsInfoPanelEnabled;
|
||||||
|
public static ConfigEntry<bool> RealtimeLogisticsInfoPanelBarsEnabled;
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
AutoConfigLogisticsEnabled.SettingChanged += (_, _) => AutoConfigLogistics.Enable(AutoConfigLogisticsEnabled.Value);
|
||||||
|
AutoConfigLimitAutoReplenishCount.SettingChanged += (_, _) => AutoConfigLogistics.ToggleLimitAutoReplenishCount();
|
||||||
|
SetDefaultRemoteLogicToStorage.SettingChanged += (_, _) => AutoConfigLogisticsSetDefaultRemoteLogicToStorage.Enable(SetDefaultRemoteLogicToStorage.Value);
|
||||||
|
LogisticsCapacityTweaksEnabled.SettingChanged += (_, _) => LogisticsCapacityTweaks.Enable(LogisticsCapacityTweaksEnabled.Value);
|
||||||
|
AllowOverflowInLogisticsEnabled.SettingChanged += (_, _) => AllowOverflowInLogistics.Enable(AllowOverflowInLogisticsEnabled.Value);
|
||||||
|
GreaterPowerUsageInLogisticsEnabled.SettingChanged += (_, _) => GreaterPowerUsageInLogistics.Enable(GreaterPowerUsageInLogisticsEnabled.Value);
|
||||||
|
LogisticsConstrolPanelImprovementEnabled.SettingChanged += (_, _) => LogisticsConstrolPanelImprovement.Enable(LogisticsConstrolPanelImprovementEnabled.Value);
|
||||||
|
RealtimeLogisticsInfoPanelEnabled.SettingChanged += (_, _) => RealtimeLogisticsInfoPanel.Enable(RealtimeLogisticsInfoPanelEnabled.Value);
|
||||||
|
RealtimeLogisticsInfoPanelBarsEnabled.SettingChanged += (_, _) => RealtimeLogisticsInfoPanel.EnableBars(RealtimeLogisticsInfoPanelBarsEnabled.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Start()
|
||||||
|
{
|
||||||
|
AutoConfigLogistics.Enable(AutoConfigLogisticsEnabled.Value);
|
||||||
|
AutoConfigLogisticsSetDefaultRemoteLogicToStorage.Enable(SetDefaultRemoteLogicToStorage.Value);
|
||||||
|
LogisticsCapacityTweaks.Enable(LogisticsCapacityTweaksEnabled.Value);
|
||||||
|
AllowOverflowInLogistics.Enable(AllowOverflowInLogisticsEnabled.Value);
|
||||||
|
GreaterPowerUsageInLogistics.Enable(GreaterPowerUsageInLogisticsEnabled.Value);
|
||||||
|
LogisticsConstrolPanelImprovement.Enable(LogisticsConstrolPanelImprovementEnabled.Value);
|
||||||
|
RealtimeLogisticsInfoPanel.Enable(RealtimeLogisticsInfoPanelEnabled.Value);
|
||||||
|
RealtimeLogisticsInfoPanel.EnableBars(RealtimeLogisticsInfoPanelBarsEnabled.Value);
|
||||||
|
|
||||||
|
GameLogicProc.OnGameBegin += RealtimeLogisticsInfoPanel.OnGameBegin;
|
||||||
|
GameLogicProc.OnGameEnd += RealtimeLogisticsInfoPanel.OnGameEnd;
|
||||||
|
GameLogicProc.OnDataLoaded += RealtimeLogisticsInfoPanel.OnDataLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Uninit()
|
||||||
|
{
|
||||||
|
GameLogicProc.OnDataLoaded -= RealtimeLogisticsInfoPanel.OnDataLoaded;
|
||||||
|
GameLogicProc.OnGameEnd -= RealtimeLogisticsInfoPanel.OnGameEnd;
|
||||||
|
GameLogicProc.OnGameBegin -= RealtimeLogisticsInfoPanel.OnGameBegin;
|
||||||
|
|
||||||
|
AutoConfigLogistics.Enable(false);
|
||||||
|
AutoConfigLogisticsSetDefaultRemoteLogicToStorage.Enable(false);
|
||||||
|
LogisticsCapacityTweaks.Enable(false);
|
||||||
|
AllowOverflowInLogistics.Enable(false);
|
||||||
|
GreaterPowerUsageInLogistics.Enable(false);
|
||||||
|
LogisticsConstrolPanelImprovement.Enable(false);
|
||||||
|
RealtimeLogisticsInfoPanel.Enable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OnUpdate() => RealtimeInfoPanelPatch.OnUpdate();
|
||||||
|
|
||||||
|
public static void OnInputUpdate()
|
||||||
|
{
|
||||||
|
if (VFInput.onGUI && LogisticsCapacityTweaksEnabled.Value)
|
||||||
|
{
|
||||||
|
LogisticsCapacityTweaks.UpdateInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Apply auto-config values to existing facilities on the current planet
|
||||||
|
|
||||||
|
private enum StationKind
|
||||||
|
{
|
||||||
|
Pls,
|
||||||
|
Ils,
|
||||||
|
VeinCollector
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Per-field setters (single source of truth, shared by auto-config-on-build and apply-to-planet) ===
|
||||||
|
|
||||||
|
internal static void StationSetChargePower(PlanetFactory factory, StationComponent station) =>
|
||||||
|
factory.powerSystem.consumerPool[station.pcId].workEnergyPerTick =
|
||||||
|
(long)((station.isStellar ? 250000.0 * AutoConfigILSChargePower.Value : 50000.0 * AutoConfigPLSChargePower.Value) + 0.5);
|
||||||
|
|
||||||
|
internal static void StationSetTripRangeDrones(PlanetFactory factory, StationComponent station) =>
|
||||||
|
station.tripRangeDrones = Math.Cos((station.isStellar ? AutoConfigILSMaxTripDrone.Value : AutoConfigPLSMaxTripDrone.Value) / 180.0 * Math.PI);
|
||||||
|
|
||||||
|
internal static void StationSetDeliveryDrones(PlanetFactory factory, StationComponent station)
|
||||||
|
{
|
||||||
|
var v = station.isStellar ? AutoConfigILSDroneMinDeliver.Value : AutoConfigPLSDroneMinDeliver.Value;
|
||||||
|
station.deliveryDrones = v == 0 ? 1 : v * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void StationSetPilerCount(PlanetFactory factory, StationComponent station) =>
|
||||||
|
station.pilerCount = station.isStellar ? AutoConfigILSMinPilerValue.Value : AutoConfigPLSMinPilerValue.Value;
|
||||||
|
|
||||||
|
internal static void StationFillDrones(PlanetFactory factory, StationComponent station)
|
||||||
|
{
|
||||||
|
var target = station.isStellar ? AutoConfigILSDroneCount.Value : AutoConfigPLSDroneCount.Value;
|
||||||
|
var toFill = Math.Max(0, target - station.idleDroneCount - station.workDroneCount);
|
||||||
|
if (toFill > 0) station.idleDroneCount += GameMain.data.mainPlayer.package.TakeItem((int)KnownItemId.Drone, toFill, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void StationSetTripRangeShips(PlanetFactory factory, StationComponent station) =>
|
||||||
|
station.tripRangeShips = AutoConfigILSMaxTripShip.Value switch
|
||||||
|
{
|
||||||
|
<= 20 => AutoConfigILSMaxTripShip.Value,
|
||||||
|
<= 40 => AutoConfigILSMaxTripShip.Value * 2 - 20,
|
||||||
|
_ => 10000,
|
||||||
|
} * 2400000.0;
|
||||||
|
|
||||||
|
internal static void StationSetWarpDistance(PlanetFactory factory, StationComponent station) =>
|
||||||
|
station.warpEnableDist = AutoConfigILSWarperDistance.Value switch
|
||||||
|
{
|
||||||
|
<= 7 => AutoConfigILSWarperDistance.Value * 0.5 - 0.5,
|
||||||
|
<= 16 => AutoConfigILSWarperDistance.Value - 4.0,
|
||||||
|
<= 20 => AutoConfigILSWarperDistance.Value * 2 - 20.0,
|
||||||
|
_ => 60.0,
|
||||||
|
} * 40000.0;
|
||||||
|
|
||||||
|
internal static void StationSetDeliveryShips(PlanetFactory factory, StationComponent station)
|
||||||
|
{
|
||||||
|
var v = AutoConfigILSShipMinDeliver.Value;
|
||||||
|
station.deliveryShips = v == 0 ? 1 : v * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void StationFillShips(PlanetFactory factory, StationComponent station)
|
||||||
|
{
|
||||||
|
var toFill = Math.Max(0, AutoConfigILSShipCount.Value - station.idleShipCount - station.workShipCount);
|
||||||
|
if (toFill > 0) station.idleShipCount += GameMain.data.mainPlayer.package.TakeItem((int)KnownItemId.Ship, toFill, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void StationSetIncludeOrbitCollector(PlanetFactory factory, StationComponent station) =>
|
||||||
|
station.includeOrbitCollector = AutoConfigILSIncludeOrbitCollector.Value;
|
||||||
|
|
||||||
|
internal static void StationSetWarperNecessary(PlanetFactory factory, StationComponent station) =>
|
||||||
|
station.warperNecessary = AutoConfigILSWarperNecessary.Value;
|
||||||
|
|
||||||
|
/* station.minerId may not be set yet on freshly built collectors, so resolve the minerId from the EntityData. */
|
||||||
|
internal static bool VeinCollectorSetHarvestSpeed(PlanetFactory factory, StationComponent station)
|
||||||
|
{
|
||||||
|
ref var entity = ref factory.entityPool[station.entityId];
|
||||||
|
if (entity.id != station.entityId || entity.minerId <= 0 || entity.minerId >= factory.factorySystem.minerCursor) return false;
|
||||||
|
factory.factorySystem.minerPool[entity.minerId].speed = 10000 + AutoConfigVeinCollectorHarvestSpeed.Value * 1000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void VeinCollectorSetPilerCount(PlanetFactory factory, StationComponent station) =>
|
||||||
|
station.pilerCount = AutoConfigVeinCollectorMinPilerValue.Value;
|
||||||
|
|
||||||
|
internal static void DispenserSetChargePower(PlanetFactory factory, DispenserComponent dispenser) =>
|
||||||
|
factory.powerSystem.consumerPool[dispenser.pcId].workEnergyPerTick = (long)(5000.0 * AutoConfigDispenserChargePower.Value + 0.5);
|
||||||
|
|
||||||
|
internal static void DispenserFillCouriers(PlanetFactory factory, DispenserComponent dispenser)
|
||||||
|
{
|
||||||
|
var toFill = Math.Max(0, AutoConfigDispenserCourierCount.Value - dispenser.idleCourierCount - dispenser.workCourierCount);
|
||||||
|
if (toFill > 0) dispenser.idleCourierCount += GameMain.data.mainPlayer.package.TakeItem((int)KnownItemId.Bot, toFill, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void BattleBaseSetChargePower(PlanetFactory factory, BattleBaseComponent battleBase) =>
|
||||||
|
factory.powerSystem.consumerPool[battleBase.pcId].workEnergyPerTick = (long)(5000.0 * AutoConfigBattleBaseChargePower.Value + 0.5);
|
||||||
|
|
||||||
|
// === Per-facility "apply all settings" (also used as auto-config-on-build entry point) ===
|
||||||
|
|
||||||
|
internal static void DoConfigStation(PlanetFactory factory, StationComponent station)
|
||||||
|
{
|
||||||
|
if (station.isCollector) return;
|
||||||
|
if (station.isVeinCollector)
|
||||||
|
{
|
||||||
|
if (VeinCollectorSetHarvestSpeed(factory, station))
|
||||||
|
VeinCollectorSetPilerCount(factory, station);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!station.isStellar)
|
||||||
|
{
|
||||||
|
StationSetChargePower(factory, station);
|
||||||
|
StationSetTripRangeDrones(factory, station);
|
||||||
|
StationSetDeliveryDrones(factory, station);
|
||||||
|
StationSetPilerCount(factory, station);
|
||||||
|
StationFillDrones(factory, station);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StationSetChargePower(factory, station);
|
||||||
|
StationSetTripRangeDrones(factory, station);
|
||||||
|
StationSetTripRangeShips(factory, station);
|
||||||
|
StationSetWarpDistance(factory, station);
|
||||||
|
StationSetDeliveryDrones(factory, station);
|
||||||
|
StationSetDeliveryShips(factory, station);
|
||||||
|
StationSetPilerCount(factory, station);
|
||||||
|
StationSetIncludeOrbitCollector(factory, station);
|
||||||
|
StationSetWarperNecessary(factory, station);
|
||||||
|
StationFillDrones(factory, station);
|
||||||
|
StationFillShips(factory, station);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Iterate over the current planet's facilities of a given kind ===
|
||||||
|
|
||||||
|
private static void ForEachStation(StationKind kind, Action<PlanetFactory, StationComponent> action)
|
||||||
|
{
|
||||||
|
var factory = GameMain.localPlanet?.factory;
|
||||||
|
var transport = factory?.transport;
|
||||||
|
var stationPool = transport?.stationPool;
|
||||||
|
if (stationPool == null) return;
|
||||||
|
for (var i = transport.stationCursor - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
var station = stationPool[i];
|
||||||
|
if (station == null || station.id != i || station.isCollector) continue;
|
||||||
|
var skip = kind switch
|
||||||
|
{
|
||||||
|
StationKind.VeinCollector => !station.isVeinCollector,
|
||||||
|
StationKind.Ils => station.isVeinCollector || !station.isStellar,
|
||||||
|
StationKind.Pls => station.isVeinCollector || station.isStellar,
|
||||||
|
_ => true
|
||||||
|
};
|
||||||
|
if (skip) continue;
|
||||||
|
action(factory, station);
|
||||||
|
}
|
||||||
|
RefreshOpenLogisticsWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ForEachDispenser(Action<PlanetFactory, DispenserComponent> action)
|
||||||
|
{
|
||||||
|
var factory = GameMain.localPlanet?.factory;
|
||||||
|
var transport = factory?.transport;
|
||||||
|
var dispenserPool = transport?.dispenserPool;
|
||||||
|
if (dispenserPool == null) return;
|
||||||
|
for (var i = transport.dispenserCursor - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
var dispenser = dispenserPool[i];
|
||||||
|
if (dispenser == null || dispenser.id != i) continue;
|
||||||
|
action(factory, dispenser);
|
||||||
|
}
|
||||||
|
RefreshOpenLogisticsWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ForEachBattleBase(Action<PlanetFactory, BattleBaseComponent> action)
|
||||||
|
{
|
||||||
|
var factory = GameMain.localPlanet?.factory;
|
||||||
|
var battleBases = factory?.defenseSystem?.battleBases;
|
||||||
|
if (battleBases?.buffer == null) return;
|
||||||
|
for (var i = battleBases.cursor - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
var battleBase = battleBases.buffer[i];
|
||||||
|
if (battleBase == null || battleBase.id != i) continue;
|
||||||
|
action(factory, battleBase);
|
||||||
|
}
|
||||||
|
RefreshOpenLogisticsWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-populate any open logistic facility detail window so applied values show immediately.
|
||||||
|
internal static void RefreshOpenLogisticsWindows()
|
||||||
|
{
|
||||||
|
var uiRoot = UIRoot.instance;
|
||||||
|
if (!uiRoot) return;
|
||||||
|
var uiGame = uiRoot.uiGame;
|
||||||
|
if (!uiGame) return;
|
||||||
|
var stationWindow = uiGame.stationWindow;
|
||||||
|
if (stationWindow && stationWindow.active) stationWindow.OnStationIdChange();
|
||||||
|
var dispenserWindow = uiGame.dispenserWindow;
|
||||||
|
if (dispenserWindow && dispenserWindow.active) dispenserWindow.OnDispenserIdChange();
|
||||||
|
var battleBaseWindow = uiGame.battleBaseWindow;
|
||||||
|
if (battleBaseWindow && battleBaseWindow.active) battleBaseWindow.OnBattleBaseIdChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Public entry points invoked by the config panel buttons ===
|
||||||
|
|
||||||
|
// Dispenser
|
||||||
|
public static void ApplyDispenserChargePower() => ForEachDispenser(DispenserSetChargePower);
|
||||||
|
public static void ApplyDispenserCourierCount() => ForEachDispenser(DispenserFillCouriers);
|
||||||
|
public static void ApplyAllDispenser() => ForEachDispenser((f, d) => { DispenserSetChargePower(f, d); DispenserFillCouriers(f, d); });
|
||||||
|
|
||||||
|
// Battlefield Analysis Base
|
||||||
|
public static void ApplyBattleBaseChargePower() => ForEachBattleBase(BattleBaseSetChargePower);
|
||||||
|
public static void ApplyAllBattleBase() => ForEachBattleBase(BattleBaseSetChargePower);
|
||||||
|
|
||||||
|
// PLS
|
||||||
|
public static void ApplyPLSChargePower() => ForEachStation(StationKind.Pls, StationSetChargePower);
|
||||||
|
public static void ApplyPLSTripRangeDrones() => ForEachStation(StationKind.Pls, StationSetTripRangeDrones);
|
||||||
|
public static void ApplyPLSDroneMinDeliver() => ForEachStation(StationKind.Pls, StationSetDeliveryDrones);
|
||||||
|
public static void ApplyPLSMinPilerValue() => ForEachStation(StationKind.Pls, StationSetPilerCount);
|
||||||
|
public static void ApplyPLSDroneCount() => ForEachStation(StationKind.Pls, StationFillDrones);
|
||||||
|
public static void ApplyAllPLS() => ForEachStation(StationKind.Pls, DoConfigStation);
|
||||||
|
|
||||||
|
// ILS
|
||||||
|
public static void ApplyILSChargePower() => ForEachStation(StationKind.Ils, StationSetChargePower);
|
||||||
|
public static void ApplyILSTripRangeDrones() => ForEachStation(StationKind.Ils, StationSetTripRangeDrones);
|
||||||
|
public static void ApplyILSTripRangeShips() => ForEachStation(StationKind.Ils, StationSetTripRangeShips);
|
||||||
|
public static void ApplyILSWarpDistance() => ForEachStation(StationKind.Ils, StationSetWarpDistance);
|
||||||
|
public static void ApplyILSDroneMinDeliver() => ForEachStation(StationKind.Ils, StationSetDeliveryDrones);
|
||||||
|
public static void ApplyILSShipMinDeliver() => ForEachStation(StationKind.Ils, StationSetDeliveryShips);
|
||||||
|
public static void ApplyILSMinPilerValue() => ForEachStation(StationKind.Ils, StationSetPilerCount);
|
||||||
|
public static void ApplyILSDroneCount() => ForEachStation(StationKind.Ils, StationFillDrones);
|
||||||
|
public static void ApplyILSShipCount() => ForEachStation(StationKind.Ils, StationFillShips);
|
||||||
|
public static void ApplyILSIncludeOrbitCollector() => ForEachStation(StationKind.Ils, StationSetIncludeOrbitCollector);
|
||||||
|
public static void ApplyILSWarperNecessary() => ForEachStation(StationKind.Ils, StationSetWarperNecessary);
|
||||||
|
public static void ApplyAllILS() => ForEachStation(StationKind.Ils, DoConfigStation);
|
||||||
|
|
||||||
|
// Vein Collector (Advanced Mining Machine)
|
||||||
|
public static void ApplyVeinCollectorHarvestSpeed() => ForEachStation(StationKind.VeinCollector, (f, s) => VeinCollectorSetHarvestSpeed(f, s));
|
||||||
|
public static void ApplyVeinCollectorMinPilerValue() => ForEachStation(StationKind.VeinCollector, VeinCollectorSetPilerCount);
|
||||||
|
public static void ApplyAllVeinCollector() => ForEachStation(StationKind.VeinCollector, DoConfigStation);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using HarmonyLib;
|
||||||
|
using UnityEngine;
|
||||||
|
using UXAssist.Common;
|
||||||
|
|
||||||
|
namespace UXAssist.Patches.Logistics;
|
||||||
|
|
||||||
|
internal class AllowOverflowInLogistics : PatchImpl<AllowOverflowInLogistics>
|
||||||
|
{
|
||||||
|
private static bool _blueprintPasting;
|
||||||
|
|
||||||
|
// Do not check for overflow when try to send hand items into storages
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(UIControlPanelStationStorage), nameof(UIControlPanelStationStorage.OnItemIconMouseDown))]
|
||||||
|
[HarmonyPatch(typeof(UIStationStorage), nameof(UIStationStorage.OnItemIconMouseDown))]
|
||||||
|
private static IEnumerable<CodeInstruction> UIStationStorage_OnItemIconMouseDown_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
matcher.MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(LDB), nameof(LDB.items))),
|
||||||
|
new CodeMatch(OpCodes.Ldarg_0)
|
||||||
|
);
|
||||||
|
var pos = matcher.Pos;
|
||||||
|
matcher.MatchForward(false,
|
||||||
|
new CodeMatch(OpCodes.Ldloc_S),
|
||||||
|
new CodeMatch(OpCodes.Stloc_S)
|
||||||
|
);
|
||||||
|
var inst = matcher.InstructionAt(1).Clone();
|
||||||
|
var pos2 = matcher.Pos + 2;
|
||||||
|
matcher.Start().Advance(pos);
|
||||||
|
var labels = matcher.Labels;
|
||||||
|
matcher.RemoveInstructions(pos2 - pos).Insert(
|
||||||
|
new CodeInstruction(OpCodes.Ldloc_1).WithLabels(labels),
|
||||||
|
new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Player), nameof(Player.inhandItemCount))),
|
||||||
|
inst
|
||||||
|
);
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove storage limit check
|
||||||
|
[HarmonyTranspiler]
|
||||||
|
[HarmonyPatch(typeof(PlanetTransport), nameof(PlanetTransport.SetStationStorage))]
|
||||||
|
private static IEnumerable<CodeInstruction> PlanetTransport_SetStationStorage_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
||||||
|
{
|
||||||
|
var matcher = new CodeMatcher(instructions, generator);
|
||||||
|
matcher.MatchForward(false,
|
||||||
|
new CodeMatch(ci => ci.IsLdarg()),
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(OpCodes.Add),
|
||||||
|
new CodeMatch(ci => ci.Branches(out _)),
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(ci => ci.IsLdloc()),
|
||||||
|
new CodeMatch(OpCodes.Add),
|
||||||
|
new CodeMatch(ci => ci.IsStarg())
|
||||||
|
);
|
||||||
|
var label = generator.DefineLabel();
|
||||||
|
var oldLabels = matcher.Labels;
|
||||||
|
matcher.Labels = [];
|
||||||
|
matcher.InsertAndAdvance(
|
||||||
|
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(AllowOverflowInLogistics), nameof(_blueprintPasting))).WithLabels(oldLabels),
|
||||||
|
new CodeInstruction(OpCodes.Brfalse, label)
|
||||||
|
);
|
||||||
|
matcher.Advance(9).Labels.Add(label);
|
||||||
|
return matcher.InstructionEnumeration();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
[HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CreatePrebuilds))]
|
||||||
|
private static void BuildTool_BlueprintPaste_CreatePrebuilds_Prefix()
|
||||||
|
{
|
||||||
|
_blueprintPasting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPostfix]
|
||||||
|
[HarmonyPatch(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.CreatePrebuilds))]
|
||||||
|
private static void BuildTool_BlueprintPaste_CreatePrebuilds_Postfix()
|
||||||
|
{
|
||||||
|
_blueprintPasting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ using UXAssist.Functions;
|
|||||||
using UXAssist.ModsCompat;
|
using UXAssist.ModsCompat;
|
||||||
using UXAssist.Patches;
|
using UXAssist.Patches;
|
||||||
using UXAssist.Patches.Factory;
|
using UXAssist.Patches.Factory;
|
||||||
|
using UXAssist.Patches.Logistics;
|
||||||
using UXAssist.UI;
|
using UXAssist.UI;
|
||||||
|
|
||||||
namespace UXAssist;
|
namespace UXAssist;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using UXAssist.Common.ModFeatures;
|
|||||||
using UXAssist.Functions;
|
using UXAssist.Functions;
|
||||||
using UXAssist.Patches;
|
using UXAssist.Patches;
|
||||||
using UXAssist.Patches.Factory;
|
using UXAssist.Patches.Factory;
|
||||||
|
using UXAssist.Patches.Logistics;
|
||||||
using UXAssist.UI;
|
using UXAssist.UI;
|
||||||
using Util = UXAssist.Common.Util;
|
using Util = UXAssist.Common.Util;
|
||||||
using GameLogicProc = UXAssist.Common.GameLogic;
|
using GameLogicProc = UXAssist.Common.GameLogic;
|
||||||
|
|||||||
Reference in New Issue
Block a user