using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace DSPOutputCap;
[BepInPlugin("dsp.outputcap", "DSP Output Stack Cap", "9.0.0")]
public class OutputCapPlugin : BaseUnityPlugin
{
public const string GUID = "dsp.outputcap";
internal static ManualLogSource Log;
internal static ConfigEntry<int> LimitSmelt;
internal static ConfigEntry<int> LimitAssemble;
internal static ConfigEntry<int> LimitOther;
internal static ConfigEntry<bool> VerboseLogging;
internal static ConfigEntry<KeyCode> PanelKey;
internal static ConfigEntry<bool> ShowPanelOnStart;
internal static int LimSmelt = 5;
internal static int LimAssemble = 5;
internal static int LimOther = 5;
private Harmony _harmony;
private static FieldInfo _asmProducedField;
private static FieldInfo _asmReplicatingField;
internal static int BlockedCycles;
private bool _panelVisible;
private bool _panelShownOnce;
private Rect _panelRect = new Rect(24f, 24f, 390f, 0f);
private bool _configDirty;
private GUIStyle _boxStyle;
[ThreadStatic]
private static HashSet<int> _stoppedEntityIds;
private static int _powerZeroed;
private static bool _powerDumped;
private static int _powerPostfixCalls;
[ThreadStatic]
private static PlanetFactory _cachedFactory;
private void Awake()
{
//IL_0159: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
LimitSmelt = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "Smelt", 5, "冶炼类设备(电弧熔炉等,ERecipeType.Smelt)的堆积上限。达到该数量即进入游戏自带的「产物堆积」状态:停产、停止耗电,物品被取走后自动恢复。原版容量为 100 个。");
LimitAssemble = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "Assemble", 5, "制造台的堆积上限(ERecipeType.Assemble)。原版容量为 productCounts x 9。");
LimitOther = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "Other", 5, "其他配方类型的堆积上限(化工厂、精炼厂、粒子对撞机等)。原版容量为 productCounts x 19。");
VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Verbose", false, "输出更详细的日志,仅供排查使用。");
PanelKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("InGamePanel", "ToggleKey", (KeyCode)288, "显示/隐藏游戏内设置面板的按键。");
ShowPanelOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("InGamePanel", "ShowOnStart", false, "读档后自动显示设置面板。");
SyncLimits();
_asmProducedField = typeof(AssemblerComponent).GetField("produced");
_asmReplicatingField = typeof(AssemblerComponent).GetField("replicating");
Log.LogInfo((object)("字段缓存 -- produced=" + (_asmProducedField != null) + ", replicating=" + (_asmReplicatingField != null)));
_harmony = new Harmony("dsp.outputcap");
PatchAll();
Log.LogInfo((object)("已加载 v9.0.0(公式改写 + 停产时电力归零)。限制值 -- 冶炼=" + LimSmelt + "(未生效), 制造=" + LimAssemble + ", 其他=" + LimOther));
}
private static void SyncLimits()
{
LimSmelt = LimitSmelt.Value;
LimAssemble = LimitAssemble.Value;
LimOther = LimitOther.Value;
}
private void PatchAll()
{
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Expected O, but got Unknown
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Expected O, but got Unknown
//IL_018f: Unknown result type (might be due to invalid IL or missing references)
//IL_019c: Expected O, but got Unknown
MethodInfo methodInfo = FindInstanceMethod(typeof(AssemblerComponent), "InternalUpdate");
if (methodInfo == null)
{
Log.LogError((object)"未找到 AssemblerComponent.InternalUpdate,插件无法工作。");
return;
}
try
{
MethodInfo method = typeof(OutputCapPlugin).GetMethod("CapTranspiler", BindingFlags.Static | BindingFlags.NonPublic);
if (method != null)
{
_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null);
Log.LogInfo((object)"公式改写已应用: AssemblerComponent.InternalUpdate");
}
else
{
Log.LogWarning((object)"未找到 CapTranspiler,跳过公式改写。");
}
}
catch (Exception ex)
{
Log.LogError((object)("公式改写失败(前缀仍会工作): " + ex.Message));
}
try
{
MethodInfo method2 = typeof(OutputCapPlugin).GetMethod("AssemblerPrefix", BindingFlags.Static | BindingFlags.NonPublic);
if (method2 != null)
{
_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Log.LogInfo((object)"停产前缀已应用: AssemblerComponent.InternalUpdate");
}
else
{
Log.LogWarning((object)"未找到 AssemblerPrefix。");
}
}
catch (Exception ex2)
{
Log.LogError((object)("停产前缀挂载失败: " + ex2.Message));
}
try
{
MethodInfo methodInfo2 = FindMethod(typeof(PowerConsumerComponent), "SetRequiredEnergy", new Type[2]
{
typeof(bool),
typeof(int)
});
if (methodInfo2 != null)
{
MethodInfo method3 = typeof(OutputCapPlugin).GetMethod("PowerPostfix", BindingFlags.Static | BindingFlags.NonPublic);
if (method3 != null)
{
_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(method3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Log.LogInfo((object)"电力归零补丁已应用: PowerConsumerComponent.SetRequiredEnergy(bool,int)");
}
}
else
{
Log.LogWarning((object)"未找到 SetRequiredEnergy(bool,int),电力归零不可用。");
}
}
catch (Exception ex3)
{
Log.LogError((object)("电力归零补丁挂载失败: " + ex3.Message));
}
}
private static MethodInfo FindMethod(Type type, string name, Type[] paramTypes)
{
try
{
return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null);
}
catch
{
return null;
}
}
private static void PowerPostfix(ref PowerConsumerComponent __instance)
{
if (_stoppedEntityIds == null || !_stoppedEntityIds.Contains(__instance.entityId))
{
return;
}
try
{
__instance.requiredEnergy = 0L;
_powerZeroed++;
_powerPostfixCalls++;
}
catch
{
}
}
private static void DumpPowerOnce(int entityId)
{
if (_powerDumped || VerboseLogging == null || !VerboseLogging.Value)
{
return;
}
try
{
PlanetFactory val = FindFactoryFor(entityId);
if (val == null)
{
return;
}
PowerSystem powerSystem = val.powerSystem;
if (powerSystem == null)
{
return;
}
PowerConsumerComponent[] consumerPool = powerSystem.consumerPool;
if (consumerPool == null)
{
return;
}
for (int i = 0; i < consumerPool.Length; i++)
{
if (consumerPool[i].entityId == entityId)
{
_powerDumped = true;
Log.LogInfo((object)("【电力诊断】entityId=" + entityId + " 运转档=" + consumerPool[i].workEnergyPerTick + " 待机档=" + consumerPool[i].idleEnergyPerTick + " 当前需求=" + consumerPool[i].requiredEnergy));
break;
}
}
}
catch
{
}
}
private static MethodInfo FindInstanceMethod(Type type, string name)
{
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int i = 0; i < methods.Length; i++)
{
if (methods[i].Name == name)
{
return methods[i];
}
}
return null;
}
private static IEnumerable<CodeInstruction> CapTranspiler(IEnumerable<CodeInstruction> instructions)
{
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Expected O, but got Unknown
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
MethodInfo method = typeof(OutputCapPlugin).GetMethod("LimitForAssemble", BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo method2 = typeof(OutputCapPlugin).GetMethod("LimitForOther", BindingFlags.Static | BindingFlags.NonPublic);
int num = 0;
int num2 = 0;
for (int i = 0; i < list.Count - 1; i++)
{
CodeInstruction val = list[i];
if (!(val.opcode != OpCodes.Ldc_I4_S) && !(list[i + 1].opcode != OpCodes.Mul))
{
int num3;
try
{
num3 = Convert.ToInt32(val.operand);
}
catch
{
continue;
}
if (num3 == 9 && method != null)
{
list[i] = new CodeInstruction(OpCodes.Call, (object)method);
num++;
}
else if (num3 == 19 && method2 != null)
{
list[i] = new CodeInstruction(OpCodes.Call, (object)method2);
num2++;
}
}
}
Log.LogInfo((object)("IL 改写完成:制造乘数 " + num + " 处,默认乘数 " + num2 + " 处。"));
return list;
}
private static int LimitForAssemble()
{
return LimAssemble;
}
private static int LimitForOther()
{
return LimOther;
}
private static PlanetFactory FindFactoryFor(int entityId)
{
try
{
if (_cachedFactory != null)
{
FactorySystem factorySystem = _cachedFactory.factorySystem;
if (factorySystem != null && OwnsEntity(factorySystem, entityId))
{
return _cachedFactory;
}
}
GameData data = GameMain.data;
if (data == null || data.factories == null)
{
return null;
}
for (int i = 0; i < data.factories.Length; i++)
{
PlanetFactory val = data.factories[i];
if (val == null)
{
continue;
}
FactorySystem factorySystem2 = val.factorySystem;
if (factorySystem2 == null)
{
continue;
}
AssemblerComponent[] assemblerPool = factorySystem2.assemblerPool;
if (assemblerPool == null)
{
continue;
}
for (int j = 0; j < assemblerPool.Length; j++)
{
if (assemblerPool[j].id != 0 && assemblerPool[j].entityId == entityId)
{
_cachedFactory = val;
return val;
}
}
}
}
catch (Exception ex)
{
Log.LogWarning((object)("定位星球工厂失败: " + ex.Message));
}
return null;
}
private static bool OwnsEntity(FactorySystem fs, int entityId)
{
AssemblerComponent[] assemblerPool = fs.assemblerPool;
if (assemblerPool == null)
{
return false;
}
for (int i = 0; i < assemblerPool.Length; i++)
{
if (assemblerPool[i].id != 0 && assemblerPool[i].entityId == entityId)
{
return true;
}
}
return false;
}
private static void ZeroPower(int entityId)
{
try
{
PlanetFactory val = FindFactoryFor(entityId);
if (val == null)
{
return;
}
PowerSystem powerSystem = val.powerSystem;
if (powerSystem == null)
{
return;
}
PowerConsumerComponent[] consumerPool = powerSystem.consumerPool;
if (consumerPool == null)
{
return;
}
for (int i = 0; i < consumerPool.Length; i++)
{
if (consumerPool[i].id != 0 && consumerPool[i].entityId == entityId)
{
consumerPool[i].requiredEnergy = 0L;
break;
}
}
}
catch (Exception ex)
{
Log.LogWarning((object)("电力归零失败: " + ex.Message));
}
}
private static bool AssemblerPrefix(AssemblerComponent __instance)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
try
{
int[] produced = GetProduced(__instance);
if (produced == null)
{
return true;
}
if (_stoppedEntityIds == null)
{
_stoppedEntityIds = new HashSet<int>();
}
_stoppedEntityIds.Remove(__instance.entityId);
int slots = ProductSlots(__instance.recipeExecuteData, produced);
int num = ResolveLimit(__instance.recipeType);
if (num < 0)
{
return true;
}
if (!IsAtLimit(produced, slots, num))
{
return true;
}
SetReplicatingFalse(__instance);
ZeroPower(__instance.entityId);
if (_stoppedEntityIds == null)
{
_stoppedEntityIds = new HashSet<int>();
}
_stoppedEntityIds.Add(__instance.entityId);
DumpPowerOnce(__instance.entityId);
BlockedCycles++;
if (VerboseLogging != null && VerboseLogging.Value)
{
Log.LogInfo((object)string.Concat("停产:类型=", __instance.recipeType, " 已达上限 ", num, ",电力已归零。"));
}
return false;
}
catch (Exception ex)
{
Log.LogWarning((object)("停产判定异常(已放行): " + ex.Message));
return true;
}
}
private static void SetReplicatingFalse(AssemblerComponent instance)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
try
{
if (!(_asmReplicatingField == null))
{
object obj = instance;
_asmReplicatingField.SetValue(obj, false);
}
}
catch
{
}
}
private static int ResolveLimit(ERecipeType type)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Invalid comparison between Unknown and I4
if ((int)type == 1)
{
return LimSmelt;
}
if ((int)type == 4)
{
return LimAssemble;
}
return LimOther;
}
private static int[] GetProduced(AssemblerComponent instance)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
try
{
if (_asmProducedField == null)
{
return null;
}
return (int[])_asmProducedField.GetValue(instance);
}
catch (Exception ex)
{
Log.LogWarning((object)("读取 produced 失败: " + ex.Message));
return null;
}
}
private static int ProductSlots(RecipeExecuteData red, int[] produced)
{
if (produced == null)
{
return 0;
}
if (red != null && red.products != null && red.products.Length > 0)
{
return Math.Min(red.products.Length, produced.Length);
}
return produced.Length;
}
private static bool IsAtLimit(int[] produced, int slots, int limit)
{
for (int i = 0; i < slots; i++)
{
if (produced[i] >= limit)
{
return true;
}
}
return false;
}
private void Update()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
try
{
KeyCode value = PanelKey.Value;
if ((int)value != 0 && Input.GetKeyDown(value))
{
_panelVisible = !_panelVisible;
Log.LogInfo((object)("设置面板 " + (_panelVisible ? "已显示" : "已隐藏")));
}
if (ShowPanelOnStart.Value && !_panelShownOnce && GameMain.data != null)
{
_panelShownOnce = true;
_panelVisible = true;
}
}
catch (Exception ex)
{
Log.LogWarning((object)("热键处理出错: " + ex.Message));
PanelKey.Value = (KeyCode)0;
}
if (_configDirty)
{
_configDirty = false;
try
{
SyncLimits();
((BaseUnityPlugin)this).Config.Save();
}
catch (Exception ex2)
{
Log.LogWarning((object)("配置保存失败: " + ex2.Message));
}
}
}
private void OnGUI()
{
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Expected O, but got Unknown
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Expected O, but got Unknown
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Expected O, but got Unknown
if (!_panelVisible)
{
return;
}
try
{
if (_boxStyle == null)
{
_boxStyle = new GUIStyle(GUI.skin.box);
_boxStyle.normal.textColor = Color.white;
_boxStyle.padding = new RectOffset(12, 12, 10, 10);
_boxStyle.alignment = (TextAnchor)0;
}
_panelRect = GUILayout.Window(1146310723, _panelRect, new WindowFunction(DrawPanel), string.Concat("输出堆叠上限 [", PanelKey.Value, " 隐藏]"), (GUILayoutOption[])(object)new GUILayoutOption[0]);
}
catch (Exception ex)
{
_panelVisible = false;
Log.LogWarning((object)("面板绘制出错: " + ex.Message));
}
}
private void DrawPanel(int windowId)
{
//IL_0144: Unknown result type (might be due to invalid IL or missing references)
GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[0]);
GUILayout.Label("达到上限后设备显示「产物堆积」并停止生产与耗电,", (GUILayoutOption[])(object)new GUILayoutOption[0]);
GUILayout.Label("物品被运走后自动恢复。改动立即生效。", (GUILayoutOption[])(object)new GUILayoutOption[0]);
GUILayout.Space(8f);
DrawLimitRow("冶炼炉", LimitSmelt, "原版容量 100 个");
DrawLimitRow("制造台", LimitAssemble, "原版容量 = 单次产出 x 9");
DrawLimitRow("化工厂 / 精炼厂等", LimitOther, "原版容量 = 单次产出 x 19");
GUILayout.Space(8f);
GUILayout.Label("快捷设定(三类同时生效):", (GUILayoutOption[])(object)new GUILayoutOption[0]);
GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
if (GUILayout.Button("全部设为 5", (GUILayoutOption[])(object)new GUILayoutOption[0]))
{
SetAllLimits(5);
}
if (GUILayout.Button("10", (GUILayoutOption[])(object)new GUILayoutOption[0]))
{
SetAllLimits(10);
}
if (GUILayout.Button("20", (GUILayoutOption[])(object)new GUILayoutOption[0]))
{
SetAllLimits(20);
}
if (GUILayout.Button("50", (GUILayoutOption[])(object)new GUILayoutOption[0]))
{
SetAllLimits(50);
}
GUILayout.EndHorizontal();
GUILayout.Space(8f);
GUILayout.Label("配置文件:BepInEx\\config\\dsp.outputcap.cfg", (GUILayoutOption[])(object)new GUILayoutOption[0]);
GUILayout.EndVertical();
GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _panelRect)).width, 22f));
}
private void DrawLimitRow(string label, ConfigEntry<int> entry, string hint)
{
GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[0]);
GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
if (GUILayout.Button("-1", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }))
{
StepLimit(entry, -1);
}
if (GUILayout.Button("-10", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(46f) }))
{
StepLimit(entry, -10);
}
GUILayout.Label(" " + entry.Value + " ", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) });
if (GUILayout.Button("+1", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }))
{
StepLimit(entry, 1);
}
if (GUILayout.Button("+10", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(46f) }))
{
StepLimit(entry, 10);
}
if (GUILayout.Button("+100", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(52f) }))
{
StepLimit(entry, 100);
}
GUILayout.EndHorizontal();
GUILayout.Label(" " + hint, (GUILayoutOption[])(object)new GUILayoutOption[0]);
GUILayout.Space(6f);
}
private void StepLimit(ConfigEntry<int> entry, int delta)
{
int num = entry.Value + delta;
if (num < 1)
{
num = 1;
}
if (num > 100000)
{
num = 100000;
}
entry.Value = num;
_configDirty = true;
}
private void SetAllLimits(int value)
{
LimitSmelt.Value = value;
LimitAssemble.Value = value;
LimitOther.Value = value;
_configDirty = true;
}
}