Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Cheese v1.0.0
plugins/Cheese.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Cheese")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Cheese")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] namespace AutoAttack; [BepInPlugin("Cheese", "Cheese", "1.0.0")] public class AutoAttack : BaseUnityPlugin { public const string PluginGuid = "Cheese"; public const string PluginName = "Cheese"; public const string PluginVersion = "1.0.0"; public static ConfigEntry<bool> Enabled; public static ConfigEntry<KeyboardShortcut> ToggleKey; public static ConfigEntry<string> GamepadToggleButtons; public static ConfigEntry<bool> CancelOnWeaponChange; public static ConfigEntry<int> BowReleaseAtDraw; public static ConfigEntry<int> PauseAtResource; public static ConfigEntry<int> ResumeAtResource; public static ConfigEntry<int> PauseAtHealth; public static ConfigEntry<int> ResumeAtHealth; public static ConfigEntry<AutoMode> Mode; public static ConfigEntry<KeyboardShortcut> CycleModeKey; public static ConfigEntry<bool> ReturnToStart; public static ConfigEntry<float> ReturnTolerance; public static ConfigEntry<int> ReEatAtRemaining; public static ConfigEntry<int> RepairAtDurability; public static ConfigEntry<int> CraftAmmoBelow; public static ConfigEntry<int> CraftAmmoUpTo; public static ConfigEntry<bool> RestWhenNotRested; public static ConfigEntry<int> RestTimeout; public static ConfigEntry<bool> ShowOnScreenMessage; public static ConfigEntry<MessageType> ScreenMessagePosition; public static ConfigEntry<int> ScreenMessageSize; public static ConfigEntry<string> MessageOn; public static ConfigEntry<string> MessageOff; public static ConfigEntry<string> MessageCancelled; public static ConfigEntry<string> MessageWeaponChanged; internal static AutoAttack Instance; private static ManualLogSource _log; private Harmony _harmony; private static string[] _gamepadChord = new string[0]; private static string _gamepadChordRaw; private static bool _gamepadChordHeldLastFrame; private static bool _gamepadChordValidated; internal static void LogStatic(string message) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)message); } } private void Awake() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_011b: 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_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Expected O, but got Unknown //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Expected O, but got Unknown //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Expected O, but got Unknown //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Expected O, but got Unknown //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Expected O, but got Unknown //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03d5: Expected O, but got Unknown //IL_0470: Unknown result type (might be due to invalid IL or missing references) //IL_047a: Expected O, but got Unknown Instance = this; _log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for the mod. When disabled, continuous attack is forced off and the game behaves exactly as vanilla (no toggle key, no pause/cancel buttons, no jump or weapon-swap adjustments)."); ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keyboard", "Toggle auto-attack key", new KeyboardShortcut((KeyCode)324, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Keyboard key combination that starts/stops continuous attacking (equivalent to holding down the attack button). Written as Unity KeyCode names - first the key to press, then the modifiers held with it, separated by + or , (default Mouse1 + LeftAlt). Names are case-sensitive (Mouse1, not mouse1), and the combination only fires while no other keyboard key is held (mouse buttons don't count). Mouse buttons: Mouse0 (left) to Mouse6. Full list of key names: https://docs.unity3d.com/6000.0/Documentation/ScriptReference/KeyCode.html"); CancelOnWeaponChange = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Cancel on weapon change", true, "Cancel continuous attack if the equipped weapon changes while it is active (swapping to another weapon, unequipping, or the weapon being destroyed)."); BowReleaseAtDraw = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Bow release at draw %", 100, new ConfigDescription("Draw percentage at which a bow shot is released. 100 = full draw (vanilla damage). Lower values fire sooner; the shot still counts as an attack. 1 = release as soon as the draw starts.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); PauseAtResource = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Pause at resource %", 1, new ConfigDescription("When stamina (or eitr, for weapons that cost eitr) falls to this percentage of its current maximum, continuous attack waits. Uses the live value, so food and buffs are accounted for. 0 = never wait.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); ResumeAtResource = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Resume at resource %", 50, new ConfigDescription("While waiting, continuous attack resumes once stamina (or eitr) has refilled to this percentage of its current maximum.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); PauseAtHealth = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Pause at health %", 40, new ConfigDescription("When health falls to this percentage of its maximum, the automated action waits (e.g. Block or Dodge with a dummy hitting you). 0 = never wait for health.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); ResumeAtHealth = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Resume at health %", 90, new ConfigDescription("While waiting for health, the automated action resumes once health has refilled to this percentage of its maximum.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); Mode = ((BaseUnityPlugin)this).Config.Bind<AutoMode>("General", "Mode", AutoMode.Attack, "What the toggle automates. Attack = hold attack (all weapons, tools and staffs). Run = sprint forward (into a wall). Jump = jump repeatedly. Sneak = crouch-walk forward. Block = hold block. Dodge = dodge repeatedly. Can also be cycled in game with the cycle mode key."); ReturnToStart = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Return to start position", true, "Walk back to where the toggle was turned on when attacks, dodges or hits push the character away (Attack, Jump, Block and Dodge modes; Run and Sneak are meant to move)."); ReturnTolerance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Return tolerance meters", 0.3f, new ConfigDescription("How far the character may drift from the start position before walking back.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>())); ReEatAtRemaining = ((BaseUnityPlugin)this).Config.Bind<int>("Maintenance", "Re-eat food at % remaining", 10, new ConfigDescription("While the toggle is on, a food is eaten again (the same food, from your inventory) once its timer has this percentage left. Foods that run out completely are re-eaten too. The game only allows re-eating below 50%. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>())); RepairAtDurability = ((BaseUnityPlugin)this).Config.Bind<int>("Maintenance", "Repair at durability %", 20, new ConfigDescription("When the equipped weapon drops to this durability percentage, the character interacts with the crafting station in reach (stand within 2 m of it). Repairing on interact needs a mod such as ComfyAutoRepair. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 99), Array.Empty<object>())); CraftAmmoBelow = ((BaseUnityPlugin)this).Config.Bind<int>("Maintenance", "Craft ammo below", 20, new ConfigDescription("When the ammo last used drops below this count, the character interacts with the crafting station in reach and crafts more of the same ammo. Materials in nearby chests count if a mod such as Craft From Chests is installed. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); CraftAmmoUpTo = ((BaseUnityPlugin)this).Config.Bind<int>("Maintenance", "Craft ammo up to", 100, new ConfigDescription("How much ammo to have after crafting (stops early if materials or inventory space run out).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>())); RestWhenNotRested = ((BaseUnityPlugin)this).Config.Bind<bool>("Maintenance", "Rest when not rested", true, "When the Rested buff is missing, the action waits and the character sits down until the game grants Rested again (needs a fire nearby and no enemy sensing you)."); RestTimeout = ((BaseUnityPlugin)this).Config.Bind<int>("Maintenance", "Rest timeout seconds", 90, new ConfigDescription("Give up sitting after this many seconds without getting Rested, carry on without it, and try again 5 minutes later.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 600), Array.Empty<object>())); CycleModeKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keyboard", "Cycle mode key", new KeyboardShortcut((KeyCode)325, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Keyboard key combination that switches to the next mode (default Mouse2 + LeftAlt = Left Alt + middle mouse button). Same name rules as the toggle key."); ShowOnScreenMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Show on screen message", true, "Show a short on-screen message when continuous attack turns on, off, or gets cancelled."); ScreenMessagePosition = ((BaseUnityPlugin)this).Config.Bind<MessageType>("HUD", "Screen message position", (MessageType)2, "Position of the screen message."); ScreenMessageSize = ((BaseUnityPlugin)this).Config.Bind<int>("HUD", "Screen message size %", 100, new ConfigDescription("Text size of the screen message, as a percentage of the game's normal size for that position.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 400), Array.Empty<object>())); MessageOn = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "Turned on", "Continuous attack: ON", "Text shown when the toggle turns on. Leave empty to hide this message."); MessageOff = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "Turned off", "Continuous attack: OFF", "Text shown when the toggle turns off. Leave empty to hide this message."); MessageCancelled = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "Cancelled", "Continuous attack: cancelled", "Text shown when a cancel button stops continuous attack. Leave empty to hide this message."); MessageWeaponChanged = ((BaseUnityPlugin)this).Config.Bind<string>("Messages", "Cancelled (weapon changed)", "Continuous attack: cancelled (weapon changed)", "Text shown when continuous attack stops because the equipped weapon changed (only when \"Cancel on weapon change\" is enabled). Leave empty to hide this message."); _harmony = new Harmony("Cheese"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Cheese 1.0.0 loaded."); } private void Update() { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) if (DynamicButtonConfig.CancelEntries.Count == 0) { DynamicButtonConfig.Generate(((BaseUnityPlugin)this).Config); } bool flag = GamepadToggleDown(); if (!Enabled.Value) { AutoAttackState.Reset(); return; } if ((Object)(object)Player.m_localPlayer == (Object)null) { AutoAttackState.Reset(); return; } AutoAttackState.Paused = DynamicButtonConfig.AnyHeld(DynamicButtonConfig.PauseEntries); bool flag2 = false; KeyboardShortcut value; if (!Minimap.InTextInput()) { value = CycleModeKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { AutoMode[] array = (AutoMode[])Enum.GetValues(typeof(AutoMode)); Mode.Value = array[(Array.IndexOf(array, Mode.Value) + 1) % array.Length]; ResourceWait.Reset(); ShowMessage("Mode: " + Mode.Value); flag2 = true; } } if (!Minimap.InTextInput()) { value = ToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() || flag) { bool flag3 = AutoAttackState.Toggle(); ShowMessage((flag3 ? MessageOn.Value : MessageOff.Value) + " (" + Mode.Value.ToString() + ")"); flag2 = true; } } if (!flag2 && AutoAttackState.Active && DynamicButtonConfig.AnyDown(DynamicButtonConfig.CancelEntries)) { AutoAttackState.Reset(); ShowMessage(MessageCancelled.Value); } if (!flag2 && AutoAttackState.Active && CancelOnWeaponChange.Value && !AutoMaintain.StationBusy && ((Humanoid)Player.m_localPlayer).GetCurrentWeapon() != AutoAttackState.ActiveWeapon) { AutoAttackState.Reset(); ShowMessage(MessageWeaponChanged.Value); } if (AutoAttackState.Active) { AutoMaintain.Tick(Player.m_localPlayer); } } internal void ShowMessage(string text) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(text) && ShowOnScreenMessage.Value && (Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage(ScreenMessagePosition.Value, "<size=" + ScreenMessageSize.Value + "%>" + text + "</size>", 0, (Sprite)null, false, true); } } private bool GamepadToggleDown() { if (GamepadToggleButtons == null) { return false; } string value = GamepadToggleButtons.Value; if (!string.Equals(value, _gamepadChordRaw, StringComparison.Ordinal)) { _gamepadChordRaw = value; _gamepadChord = (from part in (value ?? string.Empty).Split(new char[2] { ',', '+' }, StringSplitOptions.RemoveEmptyEntries) select part.Trim() into part where part.Length > 0 select part).ToArray(); _gamepadChordHeldLastFrame = false; _gamepadChordValidated = false; } bool flag = _gamepadChord.Length != 0; int num = 0; while (flag && num < _gamepadChord.Length) { flag = ZInput.GetButton(_gamepadChord[num]); num++; } bool result = flag && !_gamepadChordHeldLastFrame; _gamepadChordHeldLastFrame = flag; if (!_gamepadChordValidated && DynamicButtonConfig.CancelEntries.Count > 0) { _gamepadChordValidated = true; string[] gamepadChord = _gamepadChord; foreach (string text in gamepadChord) { if (!DynamicButtonConfig.IsRegisteredButton(text)) { LogStatic("Gamepad toggle: ZInput has no button named \"" + text + "\" - it can never complete the combo. Check [Gamepad] Toggle auto-attack buttons."); } } } return result; } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class DynamicButtonConfig { public static readonly Dictionary<string, ConfigEntry<bool>> CancelEntries = new Dictionary<string, ConfigEntry<bool>>(); public static readonly Dictionary<string, ConfigEntry<bool>> PauseEntries = new Dictionary<string, ConfigEntry<bool>>(); private static readonly FieldRef<ZInput, Dictionary<string, ButtonDef>> ButtonsRef = AccessTools.FieldRefAccess<ZInput, Dictionary<string, ButtonDef>>("m_buttons"); public static bool IsRegisteredButton(string name) { ZInput instance = ZInput.instance; if (instance != null) { return ButtonsRef.Invoke(instance).ContainsKey(name); } return false; } public static void Generate(ConfigFile config) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Invalid comparison between Unknown and I4 if (CancelEntries.Count > 0) { return; } ZInput instance = ZInput.instance; if (instance == null) { return; } List<ButtonDef> list = ButtonsRef.Invoke(instance).Values.Where((ButtonDef b) => b.Rebindable || (int)b.Source == 180).OrderBy<ButtonDef, string>((ButtonDef b) => b.Name, StringComparer.OrdinalIgnoreCase).ToList(); if (AutoAttack.GamepadToggleButtons == null) { AutoAttack.GamepadToggleButtons = config.Bind<string>("Gamepad", "Toggle auto-attack buttons", "JoyLBumper, JoyRBumper", BuildGamepadChordDescription(list)); } foreach (ButtonDef item in list) { string name = item.Name; bool flag = (int)item.Source == 180; string text = DescribeButton(instance, name); string text2 = (flag ? "Gamepad cancel buttons" : "Keyboard cancel buttons"); string text3 = (flag ? "Gamepad pause buttons" : "Keyboard pause buttons"); bool flag3; if (flag) { bool flag2; switch (name) { case "JoyAttack": case "JoySecondaryAttack": case "JoyBlock": case "JoyUse": case "JoyCrouch": case "JoySit": flag2 = true; break; default: flag2 = false; break; } flag3 = flag2; } else { bool flag2; switch (name) { case "Attack": case "Crouch": case "Sit": case "Use": case "SecondaryAttack": case "Block": case "AltDodge": case "AutoRun": flag2 = true; break; default: flag2 = false; break; } flag3 = flag2; } bool flag4 = flag3; CancelEntries[name] = config.Bind<bool>(text2, name, flag4, flag ? ("Pressing " + text + " fully cancels continuous attack. Raw name \"" + name + "\", also usable in [Gamepad] Toggle auto-attack buttons.") : ("Pressing " + text + " fully cancels continuous attack.")); if (name == "Attack" || name == "JoyAttack") { continue; } if (flag) { bool flag2; switch (name) { case "JoyLStickDown": case "JoyLStickLeft": case "JoySecondaryAttack": case "JoyJump": case "JoyBlock": case "JoyLStickUp": case "JoyLStickRight": flag2 = true; break; default: flag2 = false; break; } flag3 = flag2; } else { bool flag2; switch (name) { case "Jump": case "Left": case "Block": case "Right": case "SecondaryAttack": case "Forward": case "Backward": flag2 = true; break; default: flag2 = false; break; } flag3 = flag2; } bool flag5 = flag3; PauseEntries[name] = config.Bind<bool>(text3, name, flag5, flag ? ("Holding " + text + " temporarily pauses continuous attack; it resumes automatically on release. Raw name \"" + name + "\", also usable in [Gamepad] Toggle auto-attack buttons.") : ("Holding " + text + " temporarily pauses continuous attack; it resumes automatically on release.")); } AutoAttack.LogStatic($"Generated {CancelEntries.Count} cancel / {PauseEntries.Count} pause button entries from ZInput (keyboard + gamepad)."); } private static string BuildGamepadChordDescription(List<ButtonDef> buttons) { string[] array = (from b in buttons where (int)b.Source == 180 select b.Name).ToArray(); string text = "Gamepad buttons that must all be held together to start/stop continuous attacking, as comma or plus separated ZInput button names (default JoyLBumper, JoyRBumper = L1 + R1). The combo fires on the press that completes it, like the keyboard combination. Leave empty to disable the gamepad toggle.\n"; if (array.Length == 0) { return text; } return text + "\nAll button names available in this game, generated from ZInput at startup (controller layout: " + ((object)Unsafe.As<InputLayout, InputLayout>(ref ZInput.InputLayout)/*cast due to .constrained prefix*/).ToString() + "; restart the game after changing the layout). Each name is also listed with its in-game label and current binding under [Gamepad cancel buttons]:\n" + WrapNameList(array); } private static string WrapNameList(string[] names) { List<string> list = new List<string>(); string text = null; int num = 0; foreach (string text2 in names) { int num2 = text2.Length + ((text != null) ? 2 : 0); if (text != null && num + num2 > 100) { list.Add(text); text = null; num = 0; num2 = text2.Length; } text = ((text == null) ? text2 : (text + ", " + text2)); num += num2; } if (text != null) { list.Add(text); } return string.Join("\n", list); } private static string DescribeButton(ZInput instance, string name) { string text = null; try { if (Localization.instance != null) { text = Localization.instance.Localize("$settings_" + name.ToLower()); } } catch (Exception ex) { AutoAttack.LogStatic("Could not localize button \"" + name + "\": " + ex.Message); } string text2 = null; try { text2 = instance.GetBoundKeyString(name, true); if (!string.IsNullOrEmpty(text2) && Localization.instance != null) { text2 = Localization.instance.Localize(text2); } } catch (Exception ex2) { AutoAttack.LogStatic("Could not read bound key for \"" + name + "\": " + ex2.Message); } bool flag = !string.IsNullOrEmpty(text) && text != name; bool flag2 = !string.IsNullOrEmpty(text2); if (flag && flag2) { return "\"" + name + "\" (" + text + ", currently bound to: " + text2 + ")"; } if (flag) { return "\"" + name + "\" (" + text + ")"; } if (flag2) { return "\"" + name + "\" (currently bound to: " + text2 + ")"; } return "\"" + name + "\""; } public static bool AnyDown(Dictionary<string, ConfigEntry<bool>> entries) { foreach (KeyValuePair<string, ConfigEntry<bool>> entry in entries) { if (entry.Value.Value && ZInput.GetButtonDown(entry.Key)) { return true; } } return false; } public static bool AnyHeld(Dictionary<string, ConfigEntry<bool>> entries) { foreach (KeyValuePair<string, ConfigEntry<bool>> entry in entries) { if (entry.Value.Value && ZInput.GetButton(entry.Key)) { return true; } } return false; } } [HarmonyPatch(typeof(ZInput), "Initialize")] internal static class ZInput_Initialize_GenerateButtonConfig { private static void Postfix() { if ((Object)(object)AutoAttack.Instance != (Object)null) { DynamicButtonConfig.Generate(((BaseUnityPlugin)AutoAttack.Instance).Config); } } } internal static class AutoAttackState { public static bool Active { get; private set; } public static bool Paused { get; set; } public static ItemData ActiveWeapon { get; private set; } public static Vector3 Anchor { get; private set; } public static bool Toggle() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) Active = !Active; if (Active && (Object)(object)Player.m_localPlayer != (Object)null) { Anchor = ((Component)Player.m_localPlayer).transform.position; } Paused = false; ResourceWait.Reset(); AutoMaintain.Reset(); object activeWeapon; if (!Active) { activeWeapon = null; } else { Player localPlayer = Player.m_localPlayer; activeWeapon = ((localPlayer != null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null); } ActiveWeapon = (ItemData)activeWeapon; return Active; } public static void Reset() { if (Active) { AutoMaintain.Reset(); } Active = false; Paused = false; ActiveWeapon = null; } } [HarmonyPatch(typeof(ZInput), "GetButton", new Type[] { typeof(string) })] internal static class ZInput_GetButton_ForceAttackHold { private static readonly FieldRef<Humanoid, float> AttackDrawTimeRef = AccessTools.FieldRefAccess<Humanoid, float>("m_attackDrawTime"); private static void Postfix(string name, ref bool __result) { if (!__result && AutoAttackState.Active && !AutoAttackState.Paused && !(name != "Attack") && AutoAttack.Mode.Value == AutoMode.Attack && !ResourceWait.Waiting() && !ReloadPending() && !BowDrawStuck() && !BowFullyDrawn()) { __result = true; } } private static bool BowDrawStuck() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon(); if (currentWeapon == null || !currentWeapon.m_shared.m_attack.m_bowDraw) { return false; } return AttackDrawTimeRef.Invoke((Humanoid)(object)localPlayer) < 0f; } private static bool ReloadPending() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon(); if (currentWeapon != null && currentWeapon.m_shared.m_attack.m_requiresReload) { return !((Humanoid)localPlayer).IsWeaponLoaded(); } return false; } private static bool BowFullyDrawn() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon(); if (currentWeapon == null || !currentWeapon.m_shared.m_attack.m_bowDraw) { return false; } if (((Character)localPlayer).InAttack()) { return false; } float attackDrawPercentage = ((Humanoid)localPlayer).GetAttackDrawPercentage(); if (attackDrawPercentage > 0f) { return attackDrawPercentage >= (float)AutoAttack.BowReleaseAtDraw.Value / 100f; } return false; } } internal static class ResourceWait { private static bool _waiting; private static bool _healthWaiting; private static float _lastValue = -1f; private static float _actionCost; private static float _lastMax; private static bool _lastUsesEitr; public static bool Waiting() { return HealthWaiting() | ResourceWaiting() | AutoMaintain.Resting | AutoMaintain.StationBusy; } private static bool HealthWaiting() { Player localPlayer = Player.m_localPlayer; int value = AutoAttack.PauseAtHealth.Value; if ((Object)(object)localPlayer == (Object)null || value <= 0 || ((Character)localPlayer).GetMaxHealth() <= 0f) { return _healthWaiting = false; } float num = ((Character)localPlayer).GetHealth() / ((Character)localPlayer).GetMaxHealth() * 100f; if (_healthWaiting) { _healthWaiting = num < (float)AutoAttack.ResumeAtHealth.Value; } else { _healthWaiting = num <= (float)value; } return _healthWaiting; } private static bool ResourceWaiting() { Player localPlayer = Player.m_localPlayer; int value = AutoAttack.PauseAtResource.Value; if ((Object)(object)localPlayer == (Object)null || value <= 0) { return _waiting = false; } ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon(); bool flag = currentWeapon != null && currentWeapon.m_shared.m_attack.m_attackEitr > 0f; float num = (flag ? localPlayer.GetEitr() : localPlayer.GetStamina()); float num2 = (flag ? ((Character)localPlayer).GetMaxEitr() : ((Character)localPlayer).GetMaxStamina()); if (num2 <= 0f) { return _waiting = false; } if (flag != _lastUsesEitr) { _lastUsesEitr = flag; _lastValue = -1f; _actionCost = 0f; } if (_lastValue >= 0f && Mathf.Approximately(num2, _lastMax) && _lastValue - num > _actionCost) { _actionCost = _lastValue - num; } _lastValue = num; _lastMax = num2; float num3 = num / num2 * 100f; if (_waiting) { _waiting = num3 < (float)AutoAttack.ResumeAtResource.Value; } else { _waiting = num3 <= (float)value || num < _actionCost; } return _waiting; } public static void Reset() { _waiting = (_healthWaiting = false); _lastValue = -1f; _actionCost = 0f; } } public enum AutoMode { Attack, Run, Jump, Sneak, Block, Dodge } [HarmonyPatch(typeof(Player), "SetControls")] internal static class Player_SetControls_DriveMode { private static readonly FieldRef<Player, bool> CrouchToggledRef = AccessTools.FieldRefAccess<Player, bool>("m_crouchToggled"); private static bool _blockWasFaked; private static void ReturnToAnchor(Player player, AutoMode mode, ref Vector3 movedir) { //IL_003e: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player != (Object)(object)Player.m_localPlayer) && AutoAttackState.Active && !AutoAttackState.Paused && AutoAttack.ReturnToStart.Value && mode != AutoMode.Run && mode != AutoMode.Sneak && !AutoMaintain.Resting && !AutoMaintain.StationBusy) { Vector3 val = AutoAttackState.Anchor - ((Component)player).transform.position; val.y = 0f; if (!(((Vector3)(ref val)).magnitude <= AutoAttack.ReturnTolerance.Value)) { Vector3 lookDir = ((Character)player).GetLookDir(); lookDir.y = 0f; ((Vector3)(ref lookDir)).Normalize(); Vector3 val2 = Vector3.Cross(Vector3.up, lookDir); ((Vector3)(ref val)).Normalize(); movedir = new Vector3(Vector3.Dot(val, val2), 0f, Vector3.Dot(val, lookDir)); } } } private static void Prefix(Player __instance, ref Vector3 movedir, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch, ref bool run, ref bool dodge) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) AutoMode value = AutoAttack.Mode.Value; ReturnToAnchor(__instance, value, ref movedir); int num; if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && AutoAttackState.Active && !AutoAttackState.Paused && value != AutoMode.Attack) { if (!ResourceWait.Waiting()) { num = 1; goto IL_0057; } if (value == AutoMode.Block && !AutoMaintain.Resting) { num = ((!AutoMaintain.StationBusy) ? 1 : 0); if (num != 0) { goto IL_0057; } } else { num = 0; } } else { num = 0; } goto IL_005b; IL_005b: _blockWasFaked = false; goto IL_0061; IL_0061: if (num == 0) { return; } switch (value) { case AutoMode.Run: movedir = Vector3.forward; run = true; break; case AutoMode.Sneak: if (!CrouchToggledRef.Invoke(__instance)) { crouch = true; } movedir = Vector3.forward; break; case AutoMode.Jump: if (!blockHold && !CrouchToggledRef.Invoke(__instance)) { jump = true; } break; case AutoMode.Dodge: dodge = true; break; case AutoMode.Block: block = !_blockWasFaked; blockHold = true; _blockWasFaked = true; break; } return; IL_0057: if (value != AutoMode.Block) { goto IL_005b; } goto IL_0061; } } [HarmonyPatch(typeof(Character), "Jump", new Type[] { typeof(bool) })] internal static class Character_Jump_AllowWhileAutoAttack { private static void Prefix(Character __instance, ref bool force) { if (!force && AutoAttackState.Active && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && (ZInput.GetButton("Jump") || ZInput.GetButton("JoyJump"))) { force = true; } } } internal static class EquipDuringAutoAttack { private static readonly MethodInfo BlocksEquipMethod = AccessTools.Method(typeof(EquipDuringAutoAttack), "BlocksEquip", (Type[])null, (Type[])null); private static readonly MethodInfo BlocksQueuedActionMethod = AccessTools.Method(typeof(EquipDuringAutoAttack), "BlocksQueuedAction", (Type[])null, (Type[])null); private static readonly FieldRef<Player, List<MinorActionData>> ActionQueueRef = AccessTools.FieldRefAccess<Player, List<MinorActionData>>("m_actionQueue"); private static bool BypassEnabled(Humanoid self) { if (AutoAttackState.Active) { return (Object)(object)self == (Object)(object)Player.m_localPlayer; } return false; } public static bool BlocksEquip(Humanoid self) { if (!BypassEnabled(self)) { return ((Character)self).InAttack(); } return false; } public static bool BlocksQueuedAction(Humanoid self) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Invalid comparison between Unknown and I4 if (BypassEnabled(self)) { List<MinorActionData> list = ActionQueueRef.Invoke((Player)self); if (list.Count > 0) { ActionType type = list[0].m_type; if ((int)type == 0 || (int)type == 1) { return false; } } } return ((Character)self).InAttack(); } public static IEnumerable<CodeInstruction> TranspileBlocksEquip(IEnumerable<CodeInstruction> instructions) { return ReplaceInAttackCall(instructions, BlocksEquipMethod); } public static IEnumerable<CodeInstruction> TranspileBlocksQueuedAction(IEnumerable<CodeInstruction> instructions) { return ReplaceInAttackCall(instructions, BlocksQueuedActionMethod); } private static IEnumerable<CodeInstruction> ReplaceInAttackCall(IEnumerable<CodeInstruction> instructions, MethodInfo replacement) { foreach (CodeInstruction instruction in instructions) { if ((instruction.opcode == OpCodes.Callvirt || instruction.opcode == OpCodes.Call) && instruction.operand is MethodInfo { Name: "InAttack" }) { instruction.opcode = OpCodes.Call; instruction.operand = replacement; } yield return instruction; } } } [HarmonyPatch(typeof(Player), "ToggleEquipped")] internal static class Player_ToggleEquipped_AllowSwapDuringAutoAttack { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return EquipDuringAutoAttack.TranspileBlocksEquip(instructions); } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] internal static class Humanoid_EquipItem_AllowSwapDuringAutoAttack { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return EquipDuringAutoAttack.TranspileBlocksEquip(instructions); } } [HarmonyPatch(typeof(Player), "UpdateActionQueue")] internal static class Player_UpdateActionQueue_AllowQueuedSwapDuringAutoAttack { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return EquipDuringAutoAttack.TranspileBlocksQueuedAction(instructions); } } [HarmonyPatch(typeof(Player), "Update")] internal static class Player_Update_AllowHideDuringAutoAttack { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return EquipDuringAutoAttack.TranspileBlocksEquip(instructions); } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class Player_OnDeath_ResetAutoAttack { private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { AutoAttackState.Reset(); } } } internal static class AutoMaintain { private const float TickInterval = 0.5f; private const float StationRetryDelay = 30f; private const float RestRetryDelay = 300f; private const float NoMaterialsRetryDelay = 300f; private static float _nextTick; private static float _nextStationTry; private static float _nextRestTry; private static float _restStarted; private static float _hideGuiAt = -1f; private static float _resumeAt = -1f; private static Quaternion _savedRotation; private static Vector3 _savedLookDir; private static string _lastAmmoName; private static readonly HashSet<string> KnownFoods = new HashSet<string>(); public static bool Resting { get; private set; } public static bool StationBusy { get; private set; } public static void Reset() { if (Resting && (Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).StopEmote(); } Resting = false; if (StationBusy && (Object)(object)Player.m_localPlayer != (Object)null) { LeaveStation(Player.m_localPlayer); } StationBusy = false; _nextStationTry = (_nextRestTry = 0f); _hideGuiAt = (_resumeAt = -1f); KnownFoods.Clear(); if (!((Object)(object)Player.m_localPlayer != (Object)null)) { return; } foreach (Food food in Player.m_localPlayer.GetFoods()) { KnownFoods.Add(food.m_item.m_shared.m_name); } } public static void Tick(Player player) { float time = Time.time; if (_hideGuiAt >= 0f && time >= _hideGuiAt) { _hideGuiAt = -1f; LeaveStation(player); _resumeAt = time + 1f; } if (_resumeAt >= 0f && time >= _resumeAt) { _resumeAt = -1f; RestoreFacing(player); StationBusy = false; } if (!(time < _nextTick)) { _nextTick = time + 0.5f; Eat(player); Rest(player, time); if (!Resting && !StationBusy && time >= _nextStationTry && !((Character)player).InAttack()) { UseStation(player, time); } } } private static void Eat(Player player) { int value = AutoAttack.ReEatAtRemaining.Value; if (value <= 0) { return; } List<Food> foods = player.GetFoods(); string text = null; foreach (Food item2 in foods) { KnownFoods.Add(item2.m_item.m_shared.m_name); if (text == null && item2.m_time <= item2.m_item.m_shared.m_foodBurnTime * (float)value / 100f) { text = item2.m_item.m_shared.m_name; } } if (text == null) { foreach (string name in KnownFoods) { if (!foods.Any((Food f) => f.m_item.m_shared.m_name == name)) { text = name; break; } } } if (text != null) { Inventory inventory = ((Humanoid)player).GetInventory(); ItemData item = inventory.GetItem(text, -1, false); if (item != null && player.CanEat(item, false)) { ((Humanoid)player).ConsumeItem(inventory, item, false); } } } private static void Rest(Player player, float now) { bool flag = ((Character)player).GetSEMan().HaveStatusEffect(SEMan.s_statusEffectRested); if (Resting) { if (flag || now - _restStarted > (float)AutoAttack.RestTimeout.Value) { if (!flag) { _nextRestTry = now + 300f; AutoAttack.Instance.ShowMessage("Could not get Rested (fire nearby? enemy sensing you?)"); } Resting = false; ((Character)player).StopEmote(); } else if (!((Character)player).IsSitting()) { player.StartEmote("sit", false); } } else if (!(!AutoAttack.RestWhenNotRested.Value || flag) && !(now < _nextRestTry)) { Resting = true; _restStarted = now; AutoAttack.Instance.ShowMessage("Resting until Rested"); } } private static void UseStation(Player player, float now) { //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); bool num = AutoAttack.RepairAtDurability.Value > 0 && currentWeapon != null && currentWeapon.m_shared.m_useDurability && currentWeapon.m_durability <= currentWeapon.GetMaxDurability() * (float)AutoAttack.RepairAtDurability.Value / 100f; ItemData ammoItem = ((Humanoid)player).GetAmmoItem(); if (ammoItem != null) { _lastAmmoName = ammoItem.m_shared.m_name; } Inventory inventory = ((Humanoid)player).GetInventory(); bool flag = AutoAttack.CraftAmmoBelow.Value > 0 && _lastAmmoName != null && currentWeapon != null && !string.IsNullOrEmpty(currentWeapon.m_shared.m_ammoType) && inventory.CountItems(_lastAmmoName, -1, true) < AutoAttack.CraftAmmoBelow.Value; if (!num && !flag) { return; } _nextStationTry = now + 30f; CraftingStation val = ((IEnumerable<CraftingStation>)CraftingStation.m_allStations).FirstOrDefault((Func<CraftingStation, bool>)((CraftingStation s) => (Object)(object)s != (Object)null && s.InUseDistance((Humanoid)(object)player))); if ((Object)(object)val == (Object)null) { AutoAttack.Instance.ShowMessage("No crafting station within reach (2 m)"); return; } _savedRotation = ((Component)player).transform.rotation; _savedLookDir = ((Character)player).GetLookDir(); StationBusy = true; val.Interact((Humanoid)(object)player, false, false); if (flag) { CraftAmmo(player, inventory); } _hideGuiAt = now + 1f; } private static void LeaveStation(Player player) { if (InventoryGui.IsVisible()) { InventoryGui.instance.Hide(); } player.SetCraftingStation((CraftingStation)null); ((Humanoid)player).ShowHandItems(false, true); RestoreFacing(player); } private static void RestoreFacing(Player player) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) ((Component)player).transform.rotation = _savedRotation; ((Character)player).SetLookDir(_savedLookDir, 0f); } private static void CraftAmmo(Player player, Inventory inventory) { Recipe val = ((IEnumerable<Recipe>)ObjectDB.instance.m_recipes).FirstOrDefault((Func<Recipe, bool>)((Recipe r) => (Object)(object)r != (Object)null && r.m_enabled && (Object)(object)r.m_item != (Object)null && r.m_item.m_itemData.m_shared.m_name == _lastAmmoName)); if ((Object)(object)val == (Object)null) { return; } int num = 0; for (int num2 = 0; num2 < 100; num2++) { if (inventory.CountItems(_lastAmmoName, -1, true) >= AutoAttack.CraftAmmoUpTo.Value) { break; } if (!player.HaveRequirements(val, false, 1, 1)) { break; } if (!inventory.CanAddItem(((Component)val.m_item).gameObject, val.m_amount)) { break; } player.ConsumeResources(val.m_resources, 1, -1, 1); inventory.AddItem(((Component)val.m_item).gameObject, val.m_amount); num += val.m_amount; } if (((Humanoid)player).GetAmmoItem() == null) { ItemData item = inventory.GetItem(_lastAmmoName, -1, false); if (item != null) { ((Humanoid)player).EquipItem(item, true); } } if (num == 0) { _nextStationTry = Time.time + 300f; } AutoAttack.Instance.ShowMessage((num > 0) ? ("Crafted " + num + " ammo") : "Could not craft ammo (materials?)"); } }