using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("AutoQueueBuild")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("AutoQueueBuild")] [assembly: AssemblyTitle("AutoQueueBuild")] [assembly: AssemblyVersion("1.1.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AutoQueueBuild { internal static class ForgeInfo { internal static int QueuedCrafts(MechaForge forge, int recipeId) { List list = forge?.tasks; if (list == null) { return 0; } int num = 0; for (int i = 0; i < list.Count; i++) { ForgeTask val = list[i]; if (val != null && val.parentTaskIndex < 0 && val.recipeId == recipeId) { num += val.count; } } return num; } internal static float SecondsUntilDone(MechaForge forge, Mecha mecha, int recipeId) { List list = forge?.tasks; if (list == null || mecha == null) { return 0f; } int num = -1; for (int i = 0; i < list.Count; i++) { ForgeTask val = list[i]; if (val != null && val.parentTaskIndex < 0 && val.recipeId == recipeId) { num = i; } } if (num < 0) { return 0f; } double num2 = 0.0; for (int j = 0; j <= num && j < list.Count; j++) { ForgeTask val2 = list[j]; if (val2 != null) { num2 += (double)val2.tickSpend * (double)val2.count - (double)val2.tick; } } float num3 = mecha.replicateSpeed; if (num3 <= 0.0001f) { num3 = 1f; } return (float)(num2 / 600000.0 / (double)num3); } internal static string FormatSeconds(float s) { if (s <= 0f) { return ""; } if (s < 1f) { return "<1s"; } if (s < 60f) { return Mathf.RoundToInt(s) + "s"; } int num = Mathf.FloorToInt(s / 60f); if (num < 60) { return num + "m"; } return num / 60 + "h"; } } [HarmonyPatch(typeof(UIBuildMenu))] internal static class BuildBarOverlayPatch { private const int Slots = 12; private static readonly Text[] Overlays = (Text[])(object)new Text[12]; private static UIBuildMenu _boundTo; private static long _lastUpdateTick = long.MinValue; private static readonly Color OverlayColor = new Color(1f, 0.72f, 0.2f, 1f); private static bool _loggedShow; private static bool _loggedCreate; [HarmonyPostfix] [HarmonyPatch("_OnUpdate")] private static void OnUpdate_Postfix(UIBuildMenu __instance) { if (Plugin.Enabled == null || !Plugin.Enabled.Value) { HideAll(); return; } if (Plugin.ShowBarOverlay == null || !Plugin.ShowBarOverlay.Value) { HideAll(); return; } Player mainPlayer = GameMain.mainPlayer; if (mainPlayer == null || mainPlayer.mecha == null || mainPlayer.mecha.forge == null) { HideAll(); return; } EnsureOverlays(__instance); int currentCategory = __instance.currentCategory; if (currentCategory < 1 || currentCategory > 9) { HideAll(); return; } long gameTick = GameMain.gameTick; if (_lastUpdateTick != long.MinValue) { long num = gameTick - _lastUpdateTick; if (num >= 0 && num < 6) { return; } } _lastUpdateTick = gameTick; ItemProto[,] array = BuildMenuClickPatch.ProtosValue(); if (array == null) { return; } MechaForge forge = mainPlayer.mecha.forge; int length = array.GetLength(1); for (int i = 0; i < 12; i++) { Text val = Overlays[i]; if ((Object)(object)val == (Object)null) { continue; } ItemProto val2 = ((currentCategory < array.GetLength(0) && i < length) ? array[currentCategory, i] : null); RecipeProto val3 = val2?.handcraft; if (val3 == null) { val.text = ""; continue; } int num2 = ForgeInfo.QueuedCrafts(forge, ((Proto)val3).ID); if (num2 <= 0) { val.text = ""; continue; } string text = ForgeInfo.FormatSeconds(ForgeInfo.SecondsUntilDone(forge, mainPlayer.mecha, ((Proto)val3).ID)); val.text = ((text.Length > 0) ? ("+" + num2 + " (" + text + ")") : ("+" + num2)); if (Plugin.DebugLog != null && Plugin.DebugLog.Value && !_loggedShow) { _loggedShow = true; Plugin.Log.LogInfo((object)("[overlay] slot " + i + " item " + ((Proto)val2).ID + " -> \"" + val.text.Replace("\n", " ") + "\"")); } } } private static void EnsureOverlays(UIBuildMenu menu) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_boundTo == (Object)(object)menu && (Object)(object)Overlays[0] != (Object)null) { return; } if ((Object)(object)_boundTo != (Object)(object)menu) { for (int i = 0; i < 12; i++) { Overlays[i] = null; } _boundTo = menu; } Text[] childNumTexts = menu.childNumTexts; if (childNumTexts == null) { return; } for (int j = 0; j < 12 && j < childNumTexts.Length; j++) { if (!((Object)(object)Overlays[j] != (Object)null)) { Text val = childNumTexts[j]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.font == (Object)null)) { GameObject val2 = new GameObject("aqb-queue-overlay", new Type[2] { typeof(RectTransform), typeof(CanvasRenderer) }); val2.transform.SetParent(((Component)val).transform.parent, false); Text val3 = val2.AddComponent(); val3.font = val.font; val3.fontStyle = (FontStyle)1; val3.fontSize = Mathf.Max(2, val.fontSize - 1); ((Graphic)val3).color = OverlayColor; val3.alignment = (TextAnchor)7; val3.horizontalOverflow = (HorizontalWrapMode)1; val3.verticalOverflow = (VerticalWrapMode)1; ((Graphic)val3).raycastTarget = false; val3.text = ""; RectTransform val4 = (RectTransform)val2.transform; val4.sizeDelta = new Vector2(90f, 18f); val4.anchorMin = new Vector2(0.5f, 1f); val4.anchorMax = new Vector2(0.5f, 1f); val4.pivot = new Vector2(0.5f, 0f); val4.anchoredPosition = new Vector2(0f, 2f); Outline obj = val2.AddComponent(); ((Shadow)obj).effectColor = new Color(0f, 0f, 0f, 0.9f); ((Shadow)obj).effectDistance = new Vector2(1.2f, -1.2f); Overlays[j] = val3; } } } if (Plugin.DebugLog == null || !Plugin.DebugLog.Value || _loggedCreate) { return; } _loggedCreate = true; int num = 0; for (int k = 0; k < 12; k++) { if ((Object)(object)Overlays[k] != (Object)null) { num++; } } Plugin.Log.LogInfo((object)("[overlay] created " + num + "/" + 12 + " overlay texts (baseTexts=" + ((childNumTexts != null) ? childNumTexts.Length.ToString() : "null") + ")")); } private static void HideAll() { for (int i = 0; i < 12; i++) { if ((Object)(object)Overlays[i] != (Object)null) { Overlays[i].text = ""; } } } } [HarmonyPatch(typeof(UIBuildMenu))] internal static class BuildMenuClickPatch { private static readonly FieldInfo ProtosField = AccessTools.Field(typeof(UIBuildMenu), "protos"); private static readonly FieldRef DblClickIndexRef = AccessTools.FieldRefAccess("dblClickIndex"); private static readonly FieldRef DblClickTimeRef = AccessTools.FieldRefAccess("dblClickTime"); private const double DoubleClickWindow = 0.33000001311302185; internal static ItemProto[,] ProtosValue() { return ProtosField.GetValue(null) as ItemProto[,]; } [HarmonyPrefix] [HarmonyPatch("OnChildButtonClick")] private static bool OnChildButtonClick_Prefix(UIBuildMenu __instance, int index) { //IL_021e: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Enabled == null || !Plugin.Enabled.Value) { return true; } Player mainPlayer = GameMain.mainPlayer; if (mainPlayer == null || mainPlayer.package == null) { return true; } if (!(ProtosField.GetValue(null) is ItemProto[,] array)) { return true; } int currentCategory = __instance.currentCategory; if (currentCategory < 0 || currentCategory >= array.GetLength(0) || index < 0 || index >= array.GetLength(1)) { return true; } ItemProto val = array[currentCategory, index]; if (val == null) { return true; } int iD = ((Proto)val).ID; if (!__instance.showButtonsAnyways && !GameMain.history.ItemUnlocked(iD)) { return true; } if (mainPlayer.package.GetItemCount(iD) > 0) { return true; } if (currentCategory == 9 && index == 1) { return true; } if (index == DblClickIndexRef.Invoke(__instance) && GameMain.gameTime - DblClickTimeRef.Invoke(__instance) < 0.33000001311302185) { return true; } RecipeProto handcraft = val.handcraft; if (handcraft == null || !GameMain.history.RecipeUnlocked(((Proto)handcraft).ID)) { return true; } Mecha mecha = mainPlayer.mecha; if (mecha == null || mecha.forge == null) { return true; } int num = CraftsForClick(); MechaForge forge = mecha.forge; int num2 = ForgeInfo.QueuedCrafts(forge, ((Proto)handcraft).ID); int num3 = forge.PredictTaskCount(((Proto)handcraft).ID, num, false); if (num3 > num) { num3 = num; } if (num3 > 0) { forge.AddTask(((Proto)handcraft).ID, num3); } int num4 = ForgeInfo.QueuedCrafts(forge, ((Proto)handcraft).ID); if (num4 <= 0) { return true; } DblClickIndexRef.Invoke(__instance) = index; DblClickTimeRef.Invoke(__instance) = GameMain.gameTime; if (Plugin.ShowTip == null || Plugin.ShowTip.Value) { string text = ForgeInfo.FormatSeconds(ForgeInfo.SecondsUntilDone(forge, mecha, ((Proto)handcraft).ID)); string text2 = ((num4 > num2) ? "Added to queue" : "In queue"); ShowTipAtCursor((text.Length > 0) ? (text2 + " (" + text + ")") : text2); } if (Plugin.PlaySound == null || Plugin.PlaySound.Value) { VFAudio.Create("build-menu-child", (Transform)null, Vector3.zero, false, 0, -1, -1L).Play(); } return false; } private static int CraftsForClick() { if (VFInput.shift) { if (Plugin.ShiftClickCrafts == null) { return 5; } return Mathf.Max(1, Plugin.ShiftClickCrafts.Value); } if (VFInput.control) { if (Plugin.CtrlClickCrafts == null) { return 1; } return Mathf.Max(1, Plugin.CtrlClickCrafts.Value); } if (Plugin.CraftsPerClick == null) { return 1; } return Mathf.Max(1, Plugin.CraftsPerClick.Value); } private static void ShowTipAtCursor(string text) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) UIGame val = (((Object)(object)UIRoot.instance != (Object)null) ? UIRoot.instance.uiGame : null); if ((Object)(object)val == (Object)null || (Object)(object)val.generalTips == (Object)null) { UIRealtimeTip.Popup(text, false, 0); return; } Rect panelRect = val.generalTips.panelRect; float num = Input.mousePosition.x / (float)Screen.width * ((Rect)(ref panelRect)).width; float num2 = Input.mousePosition.y / (float)Screen.height * ((Rect)(ref panelRect)).height + 40f; UIRealtimeTip.Popup(text, new Vector2(num, num2), false); } } [HarmonyPatch(typeof(PlayerAction_Build))] internal static class PlacementCraftPatch { private static long _lastLogTick = long.MinValue; private static bool Dbg { get { if (Plugin.DebugLog != null) { return Plugin.DebugLog.Value; } return false; } } private static void Log(long tick, string msg) { if (Dbg && (_lastLogTick == long.MinValue || tick - _lastLogTick < 0 || tick - _lastLogTick >= 30)) { _lastLogTick = tick; Plugin.Log.LogInfo((object)("[placement] " + msg)); } } [HarmonyPostfix] [HarmonyPatch("GameTick")] private static void GameTick_Postfix(PlayerAction_Build __instance, long timei) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Invalid comparison between Unknown and I4 if (Plugin.Enabled == null || !Plugin.Enabled.Value || Plugin.PlacementCraft == null || !Plugin.PlacementCraft.Value) { return; } int num = ((Plugin.PlacementCheckInterval != null) ? Plugin.PlacementCheckInterval.Value : 10); if (num > 1 && timei % num != 0L) { return; } Player player = ((PlayerAction)__instance).player; if (player == null || player.package == null) { return; } int inhandItemId = player.inhandItemId; if (inhandItemId <= 0 || player.package.GetItemCount(inhandItemId) > 0) { return; } BuildTool val = (BuildTool)(((object)__instance.activeTool) ?? ((object)__instance.noneTool)); List list = ((val != null) ? val.buildPreviews : null); bool flag = false; int num2 = 0; if (list != null) { for (int i = 0; i < list.Count; i++) { BuildPreview val2 = list[i]; if (val2 != null && (int)val2.condition == 2) { num2++; if (val2.item != null && ((Proto)val2.item).ID == inhandItemId) { flag = true; } } } } if (Dbg) { Log(timei, string.Format("hand={0} have=0 tool={1} previews={2} notEnough={3} shortForHand={4}", inhandItemId, (val != null) ? ((object)val).GetType().Name : "null", list?.Count ?? (-1), num2, flag)); } if (!flag) { return; } RecipeProto val3 = ((ProtoSet)(object)LDB.items).Select(inhandItemId)?.handcraft; if (val3 == null || !GameMain.history.RecipeUnlocked(((Proto)val3).ID)) { Log(timei, "no unlocked handcraft recipe for item " + inhandItemId); return; } Mecha mecha = player.mecha; if (mecha == null || mecha.forge == null) { return; } int num3 = ((Plugin.PlacementBuffer != null) ? Plugin.PlacementBuffer.Value : 5) - ForgeInfo.QueuedCrafts(mecha.forge, ((Proto)val3).ID); if (num3 <= 0) { return; } int num4 = mecha.forge.PredictTaskCount(((Proto)val3).ID, num3, false); if (num4 > num3) { num4 = num3; } if (num4 <= 0) { Log(timei, "recipe " + ((Proto)val3).ID + " wanted " + num3 + " but affordable=0 (missing raw mats)"); return; } ForgeTask val4 = mecha.forge.AddTask(((Proto)val3).ID, num4); if (Plugin.DebugLog != null && Plugin.DebugLog.Value) { Plugin.Log.LogInfo((object)("[placement] queued " + num4 + " x recipe " + ((Proto)val3).ID + " (task=" + ((val4 != null) ? "ok" : "null") + ")")); } } } [BepInPlugin("com.livinginstinkt.dsp.autoqueuebuild", "AutoQueueBuild", "1.1.1")] public class Plugin : BaseUnityPlugin { public const string Guid = "com.livinginstinkt.dsp.autoqueuebuild"; public const string Name = "AutoQueueBuild"; public const string Version = "1.1.1"; internal static ManualLogSource Log; internal static ConfigEntry Enabled; internal static ConfigEntry CraftsPerClick; internal static ConfigEntry ShiftClickCrafts; internal static ConfigEntry CtrlClickCrafts; internal static ConfigEntry ShowTip; internal static ConfigEntry PlaySound; internal static ConfigEntry PlacementCraft; internal static ConfigEntry PlacementBuffer; internal static ConfigEntry PlacementCheckInterval; internal static ConfigEntry ShowBarOverlay; internal static ConfigEntry DebugLog; private Harmony _harmony; private void Awake() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master switch. When off, the build bar behaves exactly like vanilla."); CraftsPerClick = ((BaseUnityPlugin)this).Config.Bind("General", "CraftsPerClick", 1, new ConfigDescription("How many handcraft executions to queue per plain click (1 = one craft of the building).", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); CtrlClickCrafts = ((BaseUnityPlugin)this).Config.Bind("General", "CtrlClickCrafts", 1, new ConfigDescription("How many to queue on Ctrl+click.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); ShiftClickCrafts = ((BaseUnityPlugin)this).Config.Bind("General", "ShiftClickCrafts", 5, new ConfigDescription("How many to queue on Shift+click.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 100), Array.Empty())); ShowTip = ((BaseUnityPlugin)this).Config.Bind("Feedback", "ShowTip", true, "Show an on-screen tip when a build is queued to the replicator."); PlaySound = ((BaseUnityPlugin)this).Config.Bind("Feedback", "PlaySound", true, "Play the build-menu click sound when a build is queued."); PlacementCraft = ((BaseUnityPlugin)this).Config.Bind("Placement", "AutoCraftWhilePlacing", false, "OPT-IN. When true, running out of a building mid-placement auto-queues more (up to PlacementBuffer) so you can keep building. Off by default; the build-bar click is the only automatic queue."); PlacementBuffer = ((BaseUnityPlugin)this).Config.Bind("Placement", "PlacementBuffer", 5, new ConfigDescription("How many of the building to keep queued in the replicator while placing on empty inventory.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 50), Array.Empty())); PlacementCheckInterval = ((BaseUnityPlugin)this).Config.Bind("Placement", "CheckIntervalTicks", 10, new ConfigDescription("Game ticks between placement top-up checks (higher = less frequent, cheaper).", (AcceptableValueBase)(object)new AcceptableValueRange(1, 60), Array.Empty())); ShowBarOverlay = ((BaseUnityPlugin)this).Config.Bind("UI", "ShowBarOverlay", true, "Show queued quantity and build ETA on the build-bar icons."); DebugLog = ((BaseUnityPlugin)this).Config.Bind("Debug", "DebugLog", false, "Verbose diagnostic logging to the BepInEx console/log. Leave off for normal play."); _harmony = new Harmony("com.livinginstinkt.dsp.autoqueuebuild"); _harmony.PatchAll(); Log.LogInfo((object)"AutoQueueBuild v1.1.1 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; } } }