Decompiled source of Madness Zero Chests v0.1.0

plugins/MadnessZeroChests/MadnessZeroChests.dll

Decompiled 5 days ago
using 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.Versioning;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MadnessZeroChests")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("MadnessZeroChests")]
[assembly: AssemblyTitle("MadnessZeroChests")]
[assembly: AssemblyVersion("0.1.0.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 MadnessZeroChests
{
	public static class ChestIl
	{
		public static readonly MethodInfo GetNgPlus = AccessTools.Method(typeof(AtOManager), "GetNgPlus", new Type[1] { typeof(bool) }, (Type[])null);

		public static readonly MethodInfo Normalize = AccessTools.Method(typeof(ChestIl), "AsMadnessZero", (Type[])null, (Type[])null);

		public static int AsMadnessZero(int actualMadness)
		{
			return 0;
		}

		public static List<CodeInstruction> NormalizeNgCalls(IEnumerable<CodeInstruction> instructions)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			List<CodeInstruction> list2 = list.Where((CodeInstruction i) => CodeInstructionExtensions.Calls(i, GetNgPlus)).ToList();
			Require(list2.Count == 1, "Expected one chest-related GetNgPlus call.");
			list.Insert(list.IndexOf(list2[0]) + 1, new CodeInstruction(OpCodes.Call, (object)Normalize));
			return list;
		}

		public static List<CodeInstruction> NormalizeRunEligibility(IEnumerable<CodeInstruction> instructions)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			FieldInfo field = AccessTools.Field(typeof(PlayerRun), "MadnessLevel");
			List<CodeInstruction> list2 = list.Where((CodeInstruction i) => CodeInstructionExtensions.LoadsField(i, field, false)).ToList();
			Require(list2.Count == 1, "Expected one saved-run chest eligibility check.");
			list.Insert(list.IndexOf(list2[0]) + 1, new CodeInstruction(OpCodes.Call, (object)Normalize));
			return list;
		}

		public static List<CodeInstruction> NormalizeFinishReward(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Expected O, but got Unknown
			List<CodeInstruction> code = instructions.ToList();
			int num = code.FindIndex((CodeInstruction i) => CodeInstructionExtensions.Calls(i, GetNgPlus));
			Require(num >= 0 && code.Count((CodeInstruction i) => CodeInstructionExtensions.Calls(i, GetNgPlus)) == 1, "Expected one finish reward GetNgPlus call.");
			int local = LocalIndex(code[num + 1], load: false);
			Require(local >= 0, "Expected the actual Madness to be stored in a local.");
			FieldInfo fieldInfo = AccessTools.Field(typeof(PlayerRun), "MadnessLevel");
			List<int> list = (from i in Enumerable.Range(0, code.Count)
				where LocalIndex(code[i], load: true) == local
				select i).ToList();
			Require(list.Count == 4, "Expected three reward checks and one history read.");
			Require(CodeInstructionExtensions.LoadsConstant(code[list[0] + 1], 0L) && IsBranch(code[list[0] + 2], OpCodes.Ble, OpCodes.Ble_S), "Missing reward lower bound.");
			Require(CodeInstructionExtensions.LoadsConstant(code[list[1] + 1], 3L) && IsBranch(code[list[1] + 2], OpCodes.Bge, OpCodes.Bge_S), "Missing reward upper bound.");
			Require(CodeInstructionExtensions.LoadsConstant(code[list[2] + 1], 2L) && IsBranch(code[list[2] + 2], OpCodes.Bgt, OpCodes.Bgt_S), "Missing reward visibility bound.");
			Require(CodeInstructionExtensions.StoresField(code[list[3] + 1], fieldInfo), "Actual Madness history write changed.");
			foreach (int item in list.Take(3).Reverse())
			{
				code.Insert(item + 1, new CodeInstruction(OpCodes.Call, (object)Normalize));
			}
			return code;
		}

		private static bool IsBranch(CodeInstruction instruction, OpCode normal, OpCode shortForm)
		{
			if (!(instruction.opcode == normal))
			{
				return instruction.opcode == shortForm;
			}
			return true;
		}

		private static int LocalIndex(CodeInstruction i, bool load)
		{
			if (i.opcode == (load ? OpCodes.Ldloc_0 : OpCodes.Stloc_0))
			{
				return 0;
			}
			if (i.opcode == (load ? OpCodes.Ldloc_1 : OpCodes.Stloc_1))
			{
				return 1;
			}
			if (i.opcode == (load ? OpCodes.Ldloc_2 : OpCodes.Stloc_2))
			{
				return 2;
			}
			if (i.opcode == (load ? OpCodes.Ldloc_3 : OpCodes.Stloc_3))
			{
				return 3;
			}
			if (i.opcode != (load ? OpCodes.Ldloc : OpCodes.Stloc) && i.opcode != (load ? OpCodes.Ldloc_S : OpCodes.Stloc_S))
			{
				return -1;
			}
			if (i.operand is LocalBuilder localBuilder)
			{
				return localBuilder.LocalIndex;
			}
			if (i.operand is LocalVariableInfo localVariableInfo)
			{
				return localVariableInfo.LocalIndex;
			}
			return Convert.ToInt32(i.operand);
		}

		private static void Require(bool condition, string message)
		{
			if (!condition)
			{
				throw new InvalidOperationException("Madness Zero Chests: " + message);
			}
		}
	}
	[HarmonyPatch(typeof(TownManager), "ShowButtons")]
	internal static class ChestVisibilityPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ChestIl.NormalizeNgCalls(instructions);
		}
	}
	[HarmonyPatch]
	internal static class EarnedChestPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(TownManager), "SetTreasures", (Type[])null, (Type[])null));
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ChestIl.NormalizeRunEligibility(instructions);
		}
	}
	[HarmonyPatch(typeof(CurrencyManager), "GetInitResources")]
	internal static class SoloStartingResourcesPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ChestIl.NormalizeNgCalls(instructions);
		}
	}
	[HarmonyPatch(typeof(CurrencyManager), "GetInitResourcesMP")]
	internal static class CoopStartingResourcesPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ChestIl.NormalizeNgCalls(instructions);
		}
	}
	[HarmonyPatch(typeof(FinishRunManager), "CalculateFinishRunReward")]
	internal static class FinishRewardPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ChestIl.NormalizeFinishReward(instructions);
		}
	}
	[HarmonyPatch(typeof(Functions), "GetMadnessBonusText")]
	internal static class MadnessDescriptionPatch
	{
		private static void Postfix(int value, ref string __result)
		{
			if (value >= 3 && value <= 9)
			{
				string[] array = new string[7] { "800", "700", "600", "500", "400", "300", "250" };
				string oldValue = string.Format(Texts.Instance.GetText("madnessNoChest", ""), array[value - 3]);
				__result = __result.Replace(oldValue, "Starting chests and starting resources follow Madness 0 rules (mod).");
			}
		}
	}
	[BepInPlugin("cheyenne.acrosstheobelisk.madnesszerochests", "Madness Zero Chests", "0.1.0")]
	[BepInProcess("AcrossTheObelisk.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "cheyenne.acrosstheobelisk.madnesszerochests";

		private Harmony harmony;

		private void Awake()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			harmony = new Harmony("cheyenne.acrosstheobelisk.madnesszerochests");
			try
			{
				harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Madness Zero Chests 0.1.0 active: Madness 0 chest/resource rules. Install on every co-op player.");
			}
			catch (Exception ex)
			{
				harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)("Madness Zero Chests disabled: game code did not match expected patches. " + ex));
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
}