Decompiled source of WeaponCycle v0.2.3

plugins/MycopunkWeaponCycle/MycopunkWeaponCycle.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MycopunkShared;
using Pigeon.Movement;
using UnityEngine;
using UnityEngine.InputSystem;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MycopunkWeaponCycle")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.3.0")]
[assembly: AssemblyInformationalVersion("0.2.3+fe8cef1291afd0a7f7ea8a9ddfd28d3607c4cdd5")]
[assembly: AssemblyProduct("MycopunkWeaponCycle")]
[assembly: AssemblyTitle("MycopunkWeaponCycle")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.3.0")]
[module: UnverifiableCode]
[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 MycopunkWeaponCycle
{
	internal static class MenuText
	{
		private static readonly Dictionary<string, Dictionary<string, string>> Names = new Dictionary<string, Dictionary<string, string>>
		{
			["en"] = new Dictionary<string, string>
			{
				["cycle.heavy"] = "Heavy weapon in the weapon cycle",
				["cycle.skip_empty"] = "Wheel skips weapons out of ammo"
			},
			["ja"] = new Dictionary<string, string>
			{
				["cycle.heavy"] = "武器の切り替えに重量武器を含める",
				["cycle.skip_empty"] = "ホイールで弾切れの武器を飛ばす"
			}
		};

		private static readonly Dictionary<string, Dictionary<string, string>> Descriptions = new Dictionary<string, Dictionary<string, string>> { ["ja"] = new Dictionary<string, string>
		{
			["cycle.heavy"] = "しまってある重量武器を武器の切り替えに含めます。ホイールは 武器 1 → 武器 2 → 重量武器 の順に回り、武器 1 キーも同じ順に切り替えます。武器 2 キーはそのままです。この変更は即時に反映されます。",
			["cycle.skip_empty"] = "ホイールで切り替えるとき、弾倉と予備の弾がどちらも 0 の武器を飛ばします。弾が無くても撃てる武器は飛ばしません。ほかの武器が全部弾切れのときは、切り替えずに今の武器のままにします。武器のキーはそのままです。この変更は即時に反映されます。"
		} };

		public static object[] Label(string key)
		{
			return ModsMenuTag.Label(key, (IDictionary<string, Dictionary<string, string>>)Names, (IDictionary<string, Dictionary<string, string>>)Descriptions);
		}
	}
	[BepInPlugin("t4t5u0.mycopunk.weaponcycle", "Mycopunk Weapon Cycle", "0.2.3")]
	[BepInDependency("doku_macaron.mycopunk.macaroncore", "1.1.0")]
	[MycoMod(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "t4t5u0.mycopunk.weaponcycle";

		public const string Name = "Mycopunk Weapon Cycle";

		public const string Version = "0.2.3";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> SkipEmpty;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, new ConfigDescription("Put the stored heavy weapon into the weapon cycle: the scroll wheel goes weapon 1 → weapon 2 → heavy, and each press of the weapon 1 key does the same. Weapon 2 key unchanged. Takes effect at once.", (AcceptableValueBase)null, MenuText.Label("cycle.heavy")));
			SkipEmpty = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SkipEmptyOnWheel", true, new ConfigDescription("The scroll wheel skips a weapon whose magazine and reserve are both empty (not one that fires without ammo). When every other weapon is empty it stays on the current weapon. The weapon keys are unchanged. Takes effect at once.", (AcceptableValueBase)null, MenuText.Label("cycle.skip_empty")));
			_harmony = new Harmony("t4t5u0.mycopunk.weaponcycle");
			ModSetup.Try(Log, "weapon cycle", (Action)delegate
			{
				_harmony.CreateClassProcessor(typeof(SlotPatches.WheelPatch)).Patch();
				_harmony.CreateClassProcessor(typeof(SlotPatches.Weapon1Patch)).Patch();
			});
			ModSetup.Try(Log, "MODS tab", (Action)delegate
			{
				ModsMenu.Install(_harmony, (BaseUnityPlugin)(object)this);
			});
			ModSetup.Try(Log, "hotkey report", (Action)delegate
			{
				Hotkeys.ReportConflicts((BaseUnityPlugin)(object)this);
			});
			Log.LogInfo((object)("Mycopunk Weapon Cycle 0.2.3 loaded (enabled=" + Enabled.Value + ")."));
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class SlotPatches
	{
		[HarmonyPatch(typeof(Player), "OnSwitchSlot")]
		internal static class WheelPatch
		{
			[HarmonyTranspiler]
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
			{
				return ReplaceSlot(instructions, generator, wheel: true);
			}
		}

		[HarmonyPatch(typeof(Player), "OnSlot1Pressed")]
		internal static class Weapon1Patch
		{
			[HarmonyTranspiler]
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
			{
				return ReplaceSlot(instructions, generator, wheel: false);
			}
		}

		private static readonly FieldInfo LabelsField = AccessTools.Field(typeof(CodeInstruction), "labels");

		private static IEnumerable<CodeInstruction> ReplaceSlot(IEnumerable<CodeInstruction> instructions, ILGenerator generator, bool wheel)
		{
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Expected O, but got Unknown
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Expected O, but got Unknown
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Expected O, but got Unknown
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: 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_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Expected O, but got Unknown
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Expected O, but got Unknown
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Expected O, but got Unknown
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Expected O, but got Unknown
			List<CodeInstruction> code = instructions.ToList();
			MethodInfo swap = AccessTools.Method(typeof(Player), "SwapToSlot", new Type[4]
			{
				typeof(int),
				typeof(IGear),
				typeof(bool),
				typeof(float)
			}, (Type[])null);
			List<int> list = (from i in Enumerable.Range(3, Math.Max(0, code.Count - 3))
				where swap != null && CodeInstructionExtensions.Calls(code[i], swap) && code[i - 3].opcode == OpCodes.Ldnull && code[i - 2].opcode == OpCodes.Ldc_I4_0 && code[i - 1].opcode == OpCodes.Ldc_R4
				select i).ToList();
			if (list.Count != 1 || LabelsField == null || code[list[0] - 3].blocks.Count != 0)
			{
				Plugin.Log.LogWarning((object)("Weapon cycle: expected " + (wheel ? "wheel" : "weapon 1") + " code not found; unchanged."));
				return code;
			}
			int index = list[0] - 3;
			List<CodeInstruction> list2 = new List<CodeInstruction>
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null)
			};
			IList list3 = (IList)LabelsField.GetValue(code[index]);
			IList list4 = (IList)LabelsField.GetValue(list2[0]);
			foreach (object item in list3)
			{
				list4.Add(item);
			}
			list3.Clear();
			if (wheel)
			{
				list2.Add(new CodeInstruction(OpCodes.Ldarg_1, (object)null));
			}
			list2.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(SlotPatches), wheel ? "WheelSlot" : "Weapon1Slot", (Type[])null, (Type[])null)));
			if (wheel)
			{
				Label label = generator.DefineLabel();
				list2.Add(new CodeInstruction(OpCodes.Dup, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(SlotPatches), "SkipWheelSwap", (Type[])null, (Type[])null)));
				list2.Add(new CodeInstruction(OpCodes.Brfalse, (object)label));
				list2.Add(new CodeInstruction(OpCodes.Pop, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Pop, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Ret, (object)null));
				list3.Add(label);
			}
			code.InsertRange(index, list2);
			return code;
		}

		private static int WheelSlot(int original, Player player, CallbackContext context)
		{
			if (!Plugin.Enabled.Value)
			{
				return original;
			}
			try
			{
				float num = ((CallbackContext)(ref context)).ReadValue<float>();
				return Plugin.SkipEmpty.Value ? WeaponCycle.Wheel(player.SelectedGearSlot, num > 0f, player.IsGearStored(), (int slot) => IsOutOfAmmo(player, slot)) : WeaponCycle.Wheel(player.SelectedGearSlot, num > 0f, player.IsGearStored());
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Weapon cycle (wheel) failed; using the game's: " + ex));
				return original;
			}
		}

		private static bool SkipWheelSwap(int slot, Player player)
		{
			if (Plugin.Enabled.Value)
			{
				return slot == player.SelectedGearSlot;
			}
			return false;
		}

		private static int Weapon1Slot(int original, Player player)
		{
			if (!Plugin.Enabled.Value)
			{
				return original;
			}
			try
			{
				return WeaponCycle.Weapon1Key(player.SelectedGearSlot, player.IsGearStored());
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Weapon cycle (weapon 1 key) failed; using the game's: " + ex));
				return original;
			}
		}

		private static bool IsOutOfAmmo(Player player, int slot)
		{
			IGear[] gear = player.Gear;
			Gun val = (Gun)((gear != null && slot >= 1 && slot <= gear.Length) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val != (Object)null && !val.CanFireWithoutAmmo)
			{
				return val.IsMagazineAndReserveEmpty;
			}
			return false;
		}
	}
	internal static class WeaponCycle
	{
		public const int Weapon1 = 1;

		public const int Weapon2 = 2;

		public const int Stored = 6;

		public static int Wheel(int current, bool forward, bool hasStored)
		{
			int result = (hasStored ? 6 : 2);
			switch (current)
			{
			case 1:
				if (!forward)
				{
					return result;
				}
				return 2;
			case 2:
				if (!forward)
				{
					return 1;
				}
				if (!hasStored)
				{
					return 1;
				}
				return 6;
			case 6:
				if (hasStored)
				{
					if (!forward)
					{
						return 2;
					}
					return 1;
				}
				break;
			}
			if (!forward)
			{
				return result;
			}
			return 1;
		}

		public static int Wheel(int current, bool forward, bool hasStored, Func<int, bool> skip)
		{
			int num = Wheel(current, forward, hasStored);
			int num2 = num;
			for (int i = 0; i < 3; i++)
			{
				if (num2 != current && !skip(num2))
				{
					return num2;
				}
				num2 = Wheel(num2, forward, hasStored);
			}
			if (current != 1 && current != 2 && !(current == 6 && hasStored))
			{
				return num;
			}
			return current;
		}

		public static int Weapon1Key(int current, bool hasStored)
		{
			switch (current)
			{
			case 1:
				return 2;
			case 2:
				if (!hasStored)
				{
					return 1;
				}
				return 6;
			default:
				return 1;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}