Decompiled source of ToggleQol v0.6.0

BepInEx/plugins/ToggleQol/ToggleQol.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ToggleQol;

internal abstract class QolFeature : IDisposable
{
	private readonly ConfigEntry<bool> setting;

	private readonly ToggleState state;

	public string Title { get; private set; }

	public string Description { get; private set; }

	public string Status { get; protected set; }

	public bool Available { get; private set; }

	public bool Running { get; private set; }

	public bool Enabled => state.Enabled;

	protected QolFeature(ConfigFile config, string key, string title, string description)
	{
		Title = title;
		Description = description;
		setting = config.Bind<bool>("Features", key, true, description);
		state = new ToggleState(setting.Value, OnToggle);
		Available = true;
		Running = true;
		Status = "";
	}

	public void SetEnabled(bool value)
	{
		if (Available && state.Set(value))
		{
			setting.Value = value;
		}
	}

	public virtual void Initialize()
	{
	}

	public void Tick()
	{
		if (Available && Running && Enabled)
		{
			OnTick();
		}
	}

	protected virtual void OnTick()
	{
	}

	protected virtual void OnToggle(bool next)
	{
	}

	public void Fail(Exception error)
	{
		Available = false;
		Status = "Unavailable: " + error.Message;
		Dispose();
	}

	public void Dispose()
	{
		if (Running)
		{
			Running = false;
			OnStop();
		}
	}

	protected virtual void OnStop()
	{
	}
}
internal sealed class AutoDoorsFeature : QolFeature
{
	private sealed class Entry
	{
		public bool Opened;

		public bool SawOpen;

		public float Requested;

		public readonly DoorClearance Clearance = new DoorClearance();

		public bool ManualOverride;

		public float ManualUntil;
	}

	private static AutoDoorsFeature instance;

	private Harmony harmony;

	private static MethodInfo canInteract;

	private readonly Dictionary<Door, Entry> tracked = new Dictionary<Door, Entry>();

	private bool automaticInteraction;

	private float nextPrune;

	public AutoDoorsFeature(ConfigFile config)
		: base(config, "AutoDoors", "Auto-open and close doors", "Open ordinary doors within 2.5 metres. Close only doors opened automatically after all players move 4 metres away. Respects wards; key-locked doors stay manual.")
	{
	}

	public override void Initialize()
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Expected O, but got Unknown
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Expected O, but got Unknown
		instance = this;
		canInteract = AccessTools.Method(typeof(Door), "CanInteract", Type.EmptyTypes, (Type[])null);
		if (canInteract == null)
		{
			throw new MissingMethodException("Door.CanInteract changed.");
		}
		harmony = new Harmony("benowy.qol.autodoors");
		harmony.Patch((MethodBase)AccessTools.Method(typeof(Door), "UpdateState", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(AutoDoorsFeature), "UpdateDoor", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(Door), "Interact", (Type[])null, (Type[])null), new HarmonyMethod(typeof(AutoDoorsFeature), "ManualInteraction", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static void UpdateDoor(Door __instance)
	{
		if (instance == null || !instance.Running || !instance.Enabled)
		{
			return;
		}
		try
		{
			instance.Visit(__instance);
		}
		catch (Exception error)
		{
			instance.Fail(error);
		}
	}

	private static void ManualInteraction(Door __instance)
	{
		if (instance != null && instance.Running && instance.Enabled && !instance.automaticInteraction)
		{
			instance.tracked[__instance] = new Entry
			{
				ManualOverride = true
			};
		}
	}

	private void Visit(Door door)
	{
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: 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_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0280: Unknown result type (might be due to invalid IL or missing references)
		//IL_0285: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if (!Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || !Object.op_Implicit((Object)(object)door) || !((Behaviour)door).isActiveAndEnabled || Object.op_Implicit((Object)(object)door.m_keyItem) || door.m_canNotBeClosed)
		{
			return;
		}
		ZNetView component = ((Component)door).GetComponent<ZNetView>();
		if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
		{
			return;
		}
		Vector3 position = ((Component)door).transform.position;
		float num = Vector3.Distance(((Component)localPlayer).transform.position, position);
		tracked.TryGetValue(door, out var value);
		if (num > 12f && value == null)
		{
			return;
		}
		if (value != null && value.ManualOverride)
		{
			if (num < 4f)
			{
				return;
			}
			tracked.Remove(door);
			value = null;
		}
		if (value != null && Time.time < value.ManualUntil)
		{
			return;
		}
		int num2 = component.GetZDO().GetInt(ZDOVars.s_state, 0);
		if ((door.m_checkGuardStone && !PrivateArea.CheckAccess(position, 0f, false, false)) || !(bool)canInteract.Invoke(door, null))
		{
			return;
		}
		if (value != null && value.Opened)
		{
			if (num2 == 0)
			{
				if (value.SawOpen || Time.time - value.Requested > 3f)
				{
					tracked[door] = new Entry
					{
						ManualUntil = Time.time + 5f
					};
				}
				return;
			}
			value.SawOpen = true;
			bool occupied = false;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (Object.op_Implicit((Object)(object)allPlayer) && !((Character)allPlayer).IsDead() && Vector3.Distance(((Component)allPlayer).transform.position, position) < 4f)
				{
					occupied = true;
					break;
				}
			}
			if (num > 12f)
			{
				tracked.Remove(door);
			}
			else if (value.Clearance.Ready(occupied, Time.time))
			{
				Use(door, localPlayer);
				tracked[door] = new Entry
				{
					ManualUntil = Time.time + 3f
				};
			}
		}
		else
		{
			if (num2 != 0 || num > 2.5f || ((Character)localPlayer).IsAttached())
			{
				return;
			}
			foreach (Player allPlayer2 in Player.GetAllPlayers())
			{
				if (Object.op_Implicit((Object)(object)allPlayer2) && !((Object)(object)allPlayer2 == (Object)(object)localPlayer) && !((Character)allPlayer2).IsDead())
				{
					float num3 = Vector3.Distance(((Component)allPlayer2).transform.position, position);
					if (num3 < num || (Mathf.Approximately(num3, num) && allPlayer2.GetPlayerID() < localPlayer.GetPlayerID()))
					{
						return;
					}
				}
			}
			tracked[door] = new Entry
			{
				Opened = true,
				Requested = Time.time
			};
			Use(door, localPlayer);
		}
	}

	private void Use(Door door, Player player)
	{
		automaticInteraction = true;
		try
		{
			door.Interact((Humanoid)(object)player, false, false);
		}
		finally
		{
			automaticInteraction = false;
		}
	}

	protected override void OnTick()
	{
		if (Time.time < nextPrune)
		{
			return;
		}
		nextPrune = Time.time + 5f;
		List<Door> list = new List<Door>();
		foreach (KeyValuePair<Door, Entry> item in tracked)
		{
			if (!Object.op_Implicit((Object)(object)item.Key) || (!item.Value.ManualOverride && !item.Value.Opened && Time.time > item.Value.ManualUntil))
			{
				list.Add(item.Key);
			}
		}
		foreach (Door item2 in list)
		{
			tracked.Remove(item2);
		}
	}

	protected override void OnToggle(bool next)
	{
		if (!next)
		{
			tracked.Clear();
		}
	}

	protected override void OnStop()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		tracked.Clear();
	}
}
internal sealed class AutoRunFeature : QolFeature
{
	private static AutoRunFeature instance;

	private Harmony harmony;

	private static MethodInfo takeInput;

	public AutoRunFeature(ConfigFile config)
		: base(config, "AutoRunSteering", "Auto-run camera steering", "Steer auto-run by turning the camera and keep auto-running when jumping. Movement, attacks and dodging still cancel normally.")
	{
	}

	public override void Initialize()
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Expected O, but got Unknown
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Expected O, but got Unknown
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Expected O, but got Unknown
		instance = this;
		takeInput = AccessTools.Method(typeof(Player), "TakeInput", Type.EmptyTypes, (Type[])null);
		if (takeInput == null || AccessTools.Field(typeof(Character), "m_moveDir") == null)
		{
			throw new MissingMemberException("Auto-run input fields changed.");
		}
		harmony = new Harmony("benowy.qol.autorun");
		Harmony obj = harmony;
		MethodInfo methodInfo = AccessTools.Method(typeof(Player), "SetControls", (Type[])null, (Type[])null);
		HarmonyMethod val = new HarmonyMethod(typeof(AutoRunFeature), "KeepJumpRunning", (Type[])null);
		obj.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(AutoRunFeature), "Steer", (Type[])null), val, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static bool JumpCancels(bool jump, Player player, bool block, bool blockHold)
	{
		if (jump)
		{
			if (!block && !blockHold && instance != null && instance.Running && instance.Enabled && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsBlocking() && !((Character)player).IsCrouching() && !((Character)player).IsAttached())
			{
				return player.GetDoodadController() != null;
			}
			return true;
		}
		return false;
	}

	private static IEnumerable<CodeInstruction> KeepJumpRunning(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
	{
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Expected O, but got Unknown
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Expected O, but got Unknown
		//IL_0185: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Expected O, but got Unknown
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		int num = Array.FindIndex(__originalMethod.GetParameters(), (ParameterInfo p) => p.Name == "jump") + 1;
		int num2 = Array.FindIndex(__originalMethod.GetParameters(), (ParameterInfo p) => p.Name == "dodge") + 1;
		int num3 = Array.FindIndex(__originalMethod.GetParameters(), (ParameterInfo p) => p.Name == "block") + 1;
		int num4 = Array.FindIndex(__originalMethod.GetParameters(), (ParameterInfo p) => p.Name == "blockHold") + 1;
		if (num < 1 || num2 < 1 || num3 < 1 || num4 < 1)
		{
			throw new MissingMemberException("Control parameters changed.");
		}
		int num5 = 0;
		for (int num6 = 1; num6 + 2 < list.Count; num6++)
		{
			if (CodeInstructionExtensions.IsLdarg(list[num6 - 1], (int?)2) && CodeInstructionExtensions.IsLdarg(list[num6], (int?)num) && !(list[num6 + 1].opcode != OpCodes.Or) && CodeInstructionExtensions.IsLdarg(list[num6 + 2], (int?)num2))
			{
				list.Insert(num6 + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list.Insert(num6 + 2, new CodeInstruction(OpCodes.Ldarg, (object)num3));
				list.Insert(num6 + 3, new CodeInstruction(OpCodes.Ldarg, (object)num4));
				list.Insert(num6 + 4, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(AutoRunFeature), "JumpCancels", (Type[])null, (Type[])null)));
				num5++;
				num6 += 4;
			}
		}
		if (num5 != 1)
		{
			throw new InvalidOperationException("Auto-run jump cancellation site changed.");
		}
		return list;
	}

	private static void Steer(Player __instance, ref Vector3 ___m_moveDir)
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		if (instance == null || !instance.Running || !instance.Enabled || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
		{
			return;
		}
		try
		{
			if (__instance.m_autoRun && !((Character)__instance).IsDead() && !((Character)__instance).IsAttached() && __instance.GetDoodadController() == null && (bool)takeInput.Invoke(__instance, null))
			{
				Vector3 lookDir = ((Character)__instance).GetLookDir();
				lookDir.y = 0f;
				if (((Vector3)(ref lookDir)).sqrMagnitude > 0.0001f)
				{
					___m_moveDir = ((Vector3)(ref lookDir)).normalized;
				}
			}
		}
		catch (Exception error)
		{
			instance.Fail(error);
		}
	}

	protected override void OnStop()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal sealed class BuildSearchFixFeature : QolFeature
{
	private static BuildSearchFixFeature instance;

	private static FieldInfo searchField;

	private Harmony harmony;

	public BuildSearchFixFeature(ConfigFile config)
		: base(config, "BuildSearchTypingFix", "Fix: build-menu search typing", "Typing B (or your rebound build-menu key) in the search box no longer closes the build menu. Escape still closes it.")
	{
	}

	public override void Initialize()
	{
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Expected O, but got Unknown
		instance = this;
		searchField = AccessTools.Field(typeof(BuildUi), "m_searchField");
		MethodInfo methodInfo = AccessTools.Method(typeof(BuildUi), "NavigationUpdate", Type.EmptyTypes, (Type[])null);
		if (searchField == null || !typeof(TMP_InputField).IsAssignableFrom(searchField.FieldType) || methodInfo == null)
		{
			throw new MissingMemberException("Build-menu search input changed.");
		}
		harmony = new Harmony("benowy.qol.buildsearchfix");
		harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BuildSearchFixFeature), "ProtectSearch", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static bool ReadBuildMenuButton(string button, BuildUi ui)
	{
		if (instance != null && instance.Running && instance.Enabled && Object.op_Implicit((Object)(object)ui))
		{
			object? value = searchField.GetValue(ui);
			TMP_InputField val = (TMP_InputField)((value is TMP_InputField) ? value : null);
			if (Object.op_Implicit((Object)(object)val) && ((Behaviour)val).isActiveAndEnabled && val.isFocused)
			{
				return false;
			}
		}
		return ZInput.GetButtonDown(button);
	}

	private static IEnumerable<CodeInstruction> ProtectSearch(IEnumerable<CodeInstruction> instructions)
	{
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		MethodInfo methodInfo = AccessTools.Method(typeof(ZInput), "GetButtonDown", new Type[1] { typeof(string) }, (Type[])null);
		int num = 0;
		for (int i = 1; i < list.Count; i++)
		{
			if (CodeInstructionExtensions.Calls(list[i], methodInfo) && !(list[i - 1].opcode != OpCodes.Ldstr) && object.Equals(list[i - 1].operand, "BuildMenu"))
			{
				num++;
				CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
				val.labels.AddRange(list[i].labels);
				list[i].labels.Clear();
				val.blocks.AddRange(list[i].blocks);
				list[i].blocks.Clear();
				list.Insert(i++, val);
				list[i].opcode = OpCodes.Call;
				list[i].operand = AccessTools.Method(typeof(BuildSearchFixFeature), "ReadBuildMenuButton", (Type[])null, (Type[])null);
			}
		}
		if (num != 1)
		{
			throw new InvalidOperationException("Expected one build-menu close shortcut; found " + num);
		}
		return list;
	}

	protected override void OnStop()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal static class ClockTime
{
	public static string Format(float dayFraction)
	{
		return Format(dayFraction, useAmPm: false);
	}

	public static string Format(float dayFraction, bool useAmPm)
	{
		if (float.IsNaN(dayFraction) || float.IsInfinity(dayFraction))
		{
			return "--:--";
		}
		double num = (double)dayFraction - Math.Floor(dayFraction);
		int num2 = (int)Math.Floor(num * 48.0) * 30;
		if (useAmPm)
		{
			int num3 = num2 / 60 % 12;
			return ((num3 == 0) ? 12 : num3).ToString(CultureInfo.InvariantCulture) + ":" + (num2 % 60).ToString("00", CultureInfo.InvariantCulture) + ((num2 < 720) ? " AM" : " PM");
		}
		return (num2 / 60).ToString("00", CultureInfo.InvariantCulture) + ":" + (num2 % 60).ToString("00", CultureInfo.InvariantCulture);
	}
}
internal sealed class CrossbowReloadFeature : QolFeature
{
	private static CrossbowReloadFeature instance;

	private static FieldInfo queue;

	private static MethodInfo clearQueue;

	private Harmony harmony;

	public CrossbowReloadFeature(ConfigFile config)
		: base(config, "MobileCrossbowReload", "Reload crossbows on the move", "Reload crossbows while sprinting or jumping. Normal reload time, stamina and ammo requirements apply. Dodging and switching weapons still interrupt reloading.")
	{
	}

	public override void Initialize()
	{
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Expected O, but got Unknown
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Expected O, but got Unknown
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Expected O, but got Unknown
		instance = this;
		clearQueue = AccessTools.Method(typeof(Humanoid), "ClearActionQueue", Type.EmptyTypes, (Type[])null);
		if (clearQueue == null)
		{
			throw new MissingMethodException("ClearActionQueue changed.");
		}
		queue = AccessTools.Field(typeof(Player), "m_actionQueue");
		if (queue == null || queue.FieldType != typeof(List<MinorActionData>))
		{
			throw new MissingMemberException("Reload queue changed.");
		}
		harmony = new Harmony("benowy.qol.crossbowreload");
		string[] array = new string[2] { "CheckRun", "OnJump" };
		foreach (string text in array)
		{
			harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Player), text, (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(CrossbowReloadFeature), "KeepReload", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
		}
		harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "InMinorActionSlowdown", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(CrossbowReloadFeature), "AllowMovement", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static bool Eligible(Player player)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Invalid comparison between Unknown and I4
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Invalid comparison between Unknown and I4
		if (instance == null || !instance.Running || !instance.Enabled || !Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead())
		{
			return false;
		}
		List<MinorActionData> list = (List<MinorActionData>)queue.GetValue(player);
		if (list.Count != 1 || (int)list[0].m_type != 2)
		{
			return false;
		}
		ItemData item = list[0].m_item;
		if (item != null && item == ((Humanoid)player).GetCurrentWeapon() && (int)item.m_shared.m_skillType == 14)
		{
			return item.m_shared.m_attack.m_requiresReload;
		}
		return false;
	}

	private static void ClearUnlessReloading(Humanoid human)
	{
		if (!Eligible((Player)(object)((human is Player) ? human : null)))
		{
			clearQueue.Invoke(human, null);
		}
	}

	private static void AllowMovement(Player __instance, ref bool __result)
	{
		if (__result && Eligible(__instance))
		{
			__result = false;
		}
	}

	private static IEnumerable<CodeInstruction> KeepReload(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		MethodInfo methodInfo = AccessTools.Method(typeof(Humanoid), "ClearActionQueue", Type.EmptyTypes, (Type[])null);
		int num = 0;
		foreach (CodeInstruction item in list)
		{
			if (CodeInstructionExtensions.Calls(item, methodInfo))
			{
				item.opcode = OpCodes.Call;
				item.operand = AccessTools.Method(typeof(CrossbowReloadFeature), "ClearUnlessReloading", (Type[])null, (Type[])null);
				num++;
			}
		}
		if (num != 1)
		{
			throw new InvalidOperationException("Expected exactly one movement queue-clear site.");
		}
		return list;
	}

	protected override void OnStop()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal sealed class DoorClearance
{
	private float clearSince = -1f;

	public bool Ready(bool occupied, float now)
	{
		if (occupied)
		{
			clearSince = -1f;
			return false;
		}
		if (clearSince < 0f || now < clearSince)
		{
			clearSince = now;
			return false;
		}
		return now - clearSince >= 1f;
	}
}
internal static class FoodFactorPolicy
{
	public static float Apply(float nativeFactor, bool active)
	{
		if (!active || !(nativeFactor > 0f))
		{
			return nativeFactor;
		}
		return 1f;
	}
}
internal sealed class FullFoodFeature : QolFeature
{
	private const string Id = "benowy.qol.food";

	private static FullFoodFeature instance;

	private Harmony harmony;

	public FullFoodFeature(ConfigFile config)
		: base(config, "FullDurationFood", "Full-duration food", "Personal: full food health, stamina and Eitr benefits until normal expiry. Changes apply on the next normal food tick; timers and regeneration stay normal.")
	{
	}

	public override void Initialize()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		instance = this;
		harmony = new Harmony("benowy.qol.food");
		MethodInfo methodInfo = AccessTools.Method(typeof(Player), "UpdateFood", new Type[2]
		{
			typeof(float),
			typeof(bool)
		}, (Type[])null);
		if (methodInfo == null)
		{
			throw new MissingMethodException("Player.UpdateFood changed.");
		}
		harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(FullFoodFeature), "RemoveDecay", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static float AdjustFactor(float nativeFactor, Player player)
	{
		bool active = instance != null && instance.Running && instance.Available && instance.Enabled && Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer;
		return FoodFactorPolicy.Apply(nativeFactor, active);
	}

	private static IEnumerable<CodeInstruction> RemoveDecay(IEnumerable<CodeInstruction> instructions)
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Expected O, but got Unknown
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		int num = -1;
		for (int i = 11; i + 25 < list.Count; i++)
		{
			if (MatchesDecay(list, i))
			{
				if (num >= 0)
				{
					throw new InvalidOperationException("Multiple food-decay sites; refusing an ambiguous patch.");
				}
				num = i;
			}
		}
		if (num < 0)
		{
			throw new InvalidOperationException("Expected food-decay calculation not found; game or another food mod changed it.");
		}
		list.Insert(num + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
		list.Insert(num + 2, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(FullFoodFeature), "AdjustFactor", (Type[])null, (Type[])null)));
		return list;
	}

	private static bool MatchesDecay(List<CodeInstruction> code, int i)
	{
		if (!Calls(code[i], typeof(Mathf), "Pow") || code[i - 1].opcode != OpCodes.Ldc_R4 || !(code[i - 1].operand is float) || (float)code[i - 1].operand != 0.3f || !Calls(code[i - 4], typeof(Mathf), "Clamp01") || code[i - 5].opcode != OpCodes.Div || !Field(code[i - 6], OpCodes.Ldfld, typeof(SharedData), "m_foodBurnTime") || !Field(code[i - 10], OpCodes.Ldfld, typeof(Food), "m_time"))
		{
			return false;
		}
		int num = Local(code[i + 1], store: true);
		if (num < 0 || Local(code[i - 3], store: true) != num || Local(code[i - 2], store: false) != num)
		{
			return false;
		}
		string[] array = new string[3] { "m_food", "m_foodStamina", "m_foodEitr" };
		string[] array2 = new string[3] { "m_health", "m_stamina", "m_eitr" };
		for (int j = 0; j < 3; j++)
		{
			int num2 = i + j * 8;
			if (!Field(code[num2 + 6], OpCodes.Ldfld, typeof(SharedData), array[j]) || Local(code[num2 + 7], store: false) != num || code[num2 + 8].opcode != OpCodes.Mul || !Field(code[num2 + 9], OpCodes.Stfld, typeof(Food), array2[j]))
			{
				return false;
			}
		}
		return true;
	}

	private static bool Calls(CodeInstruction instruction, Type type, string name)
	{
		MethodInfo methodInfo = instruction.operand as MethodInfo;
		if (instruction.opcode == OpCodes.Call && methodInfo != null && methodInfo.DeclaringType == type)
		{
			return methodInfo.Name == name;
		}
		return false;
	}

	private static bool Field(CodeInstruction instruction, OpCode opcode, Type type, string name)
	{
		FieldInfo fieldInfo = instruction.operand as FieldInfo;
		if (instruction.opcode == opcode && fieldInfo != null && fieldInfo.DeclaringType == type)
		{
			return fieldInfo.Name == name;
		}
		return false;
	}

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

	protected override void OnStop()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		if (instance == this)
		{
			instance = null;
		}
	}
}
internal sealed class InputGuard : IDisposable
{
	private Harmony harmony;

	private static InputGuard current;

	private readonly MenuInputState state = new MenuInputState();

	private KeyCode releaseKey;

	private static bool readingMenuInput;

	public bool Blocking => state.Blocking;

	private static bool Block
	{
		get
		{
			if (current != null && current.Blocking)
			{
				return !readingMenuInput;
			}
			return false;
		}
	}

	public void Install()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		current = this;
		harmony = new Harmony("benowy.qol.menu");
		Patch(typeof(Player), "TakeInput", Type.EmptyTypes, "BlockBoolean");
		Patch(typeof(PlayerController), "TakeInput", new Type[1] { typeof(bool) }, "BlockBoolean");
		Patch(typeof(GameCamera), "UpdateMouseCapture", Type.EmptyTypes, "CaptureCursor");
		string[] array = new string[3] { "GetButton", "GetButtonDown", "GetButtonUp" };
		foreach (string method in array)
		{
			Patch(typeof(ZInput), method, new Type[1] { typeof(string) }, "BlockBoolean");
		}
		string[] array2 = new string[3] { "GetMouseButton", "GetMouseButtonDown", "GetMouseButtonUp" };
		foreach (string method2 in array2)
		{
			Patch(typeof(ZInput), method2, new Type[1] { typeof(int) }, "BlockBoolean");
		}
		string[] array3 = new string[3] { "GetKey", "GetKeyDown", "GetKeyUp" };
		foreach (string method3 in array3)
		{
			Patch(typeof(ZInput), method3, new Type[2]
			{
				typeof(KeyCode),
				typeof(bool)
			}, "BlockBoolean");
		}
		string[] array4 = new string[4] { "GetJoyLeftStickX", "GetJoyLeftStickY", "GetJoyRightStickX", "GetJoyRightStickY" };
		foreach (string name in array4)
		{
			PatchStick(name, typeof(float), "BlockFloat");
		}
		string[] array5 = new string[2] { "GetJoyLeftStick", "GetJoyRightStick" };
		foreach (string name2 in array5)
		{
			PatchStick(name2, typeof(Vector2), "BlockVector");
		}
		string[] array6 = new string[3] { "GetMouseScrollWheel", "GetJoyLTrigger", "GetJoyRTrigger" };
		foreach (string method4 in array6)
		{
			Patch(typeof(ZInput), method4, Type.EmptyTypes, "BlockFloat");
		}
		Patch(typeof(ZInput), "GetMouseDelta", Type.EmptyTypes, "BlockVector");
	}

	private void PatchStick(string name, Type result, string prefix)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		int num = 0;
		MethodInfo[] methods = typeof(ZInput).GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo methodInfo in methods)
		{
			if (!(methodInfo.Name != name) && !(methodInfo.ReturnType != result))
			{
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length == 0 || (parameters.Length == 1 && parameters[0].ParameterType == typeof(bool)))
				{
					harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(InputGuard), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
			}
		}
		if (num == 0)
		{
			throw new MissingMethodException("No supported controller input method: ZInput." + name);
		}
	}

	private void Patch(Type type, string method, Type[] args, string prefix)
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.Method(type, method, args, (Type[])null);
		if (methodInfo == null)
		{
			throw new MissingMethodException(type.Name, method);
		}
		harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(InputGuard), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	public bool KeyDown(KeyCode key)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		readingMenuInput = true;
		try
		{
			return ZInput.GetKeyDown(key, true);
		}
		finally
		{
			readingMenuInput = false;
		}
	}

	public void SetOpen(bool value, KeyCode key)
	{
		//IL_001b: 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)
		state.SetOpen(value, Time.unscaledTime);
		if (value)
		{
			Unlock();
			return;
		}
		releaseKey = key;
		PlayerController.SetTakeInputDelay(0.2f);
		ZInput.IgnoreMouseInputForFrames(2);
	}

	public void Update()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		if (state.Open)
		{
			Unlock();
		}
		else if (state.Blocking)
		{
			readingMenuInput = true;
			try
			{
				bool closingInputHeld = ZInput.GetKey(releaseKey, true) || ZInput.GetKey((KeyCode)27, true) || ZInput.GetMouseButton(0) || ZInput.GetMouseButton(1) || ZInput.GetMouseButton(2) || ZInput.GetButton("Attack") || ZInput.GetButton("SecondaryAttack") || ZInput.GetJoyLTrigger() > 0.2f || ZInput.GetJoyRTrigger() > 0.2f;
				state.Update(Time.unscaledTime, closingInputHeld);
			}
			finally
			{
				readingMenuInput = false;
			}
		}
	}

	private static void Unlock()
	{
		ZCursor.LockState = (CursorLockMode)0;
		ZCursor.Show();
	}

	private static bool CaptureCursor()
	{
		if (!Block)
		{
			return true;
		}
		Unlock();
		return false;
	}

	private static bool BlockBoolean(ref bool __result)
	{
		if (!Block)
		{
			return true;
		}
		__result = false;
		return false;
	}

	private static bool BlockFloat(ref float __result)
	{
		if (!Block)
		{
			return true;
		}
		__result = 0f;
		return false;
	}

	private static bool BlockVector(ref Vector2 __result)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		if (!Block)
		{
			return true;
		}
		__result = Vector2.zero;
		return false;
	}

	public void Dispose()
	{
		state.Reset();
		if (harmony != null)
		{
			harmony.UnpatchSelf();
			harmony = null;
		}
		if (current == this)
		{
			current = null;
		}
	}
}
internal sealed class MenuInputState
{
	private bool pending;

	private float releaseAfter;

	public bool Open { get; private set; }

	public bool Blocking
	{
		get
		{
			if (!Open)
			{
				return pending;
			}
			return true;
		}
	}

	public void SetOpen(bool value, float now)
	{
		Open = value;
		pending = !value;
		releaseAfter = now + 0.2f;
	}

	public void Update(float now, bool closingInputHeld)
	{
		if (!Open && pending && now >= releaseAfter && !closingInputHeld)
		{
			pending = false;
		}
	}

	public void Reset()
	{
		Open = false;
		pending = false;
	}
}
internal sealed class MinimapClockFeature : QolFeature
{
	private TextMeshProUGUI label;

	private TextMeshProUGUI timeLabel;

	private Minimap owner;

	private readonly ConfigEntry<bool> useAmPm;

	public bool UseAmPm => useAmPm.Value;

	public void ToggleFormat()
	{
		useAmPm.Value = !useAmPm.Value;
		if (base.Enabled && base.Running)
		{
			OnTick();
		}
	}

	public MinimapClockFeature(ConfigFile config)
		: base(config, "MinimapClock", "Minimap day and time", "In-game day and clock in half-hour steps. Choose 24-hour time or AM/PM using the adjacent button.")
	{
		useAmPm = config.Bind<bool>("Clock", "UseAmPm", false, "Use 12-hour AM/PM time instead of 24-hour time.");
	}

	protected override void OnTick()
	{
		//IL_0411: Unknown result type (might be due to invalid IL or missing references)
		//IL_0416: Unknown result type (might be due to invalid IL or missing references)
		//IL_0430: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Expected O, but got Unknown
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_020b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0220: Unknown result type (might be due to invalid IL or missing references)
		//IL_0235: Unknown result type (might be due to invalid IL or missing references)
		//IL_024a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Unknown result type (might be due to invalid IL or missing references)
		//IL_0278: Expected O, but got Unknown
		//IL_0312: Unknown result type (might be due to invalid IL or missing references)
		//IL_0328: Unknown result type (might be due to invalid IL or missing references)
		//IL_0387: Unknown result type (might be due to invalid IL or missing references)
		//IL_039c: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
		Minimap instance = Minimap.instance;
		if ((Object)(object)owner != (Object)(object)instance)
		{
			Clear();
		}
		if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance.m_smallRoot) || !Object.op_Implicit((Object)(object)instance.m_mapImageSmall) || !Object.op_Implicit((Object)(object)instance.m_biomeNameSmall))
		{
			return;
		}
		if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)EnvMan.instance) || !Object.op_Implicit((Object)(object)ZNet.instance) || !Object.op_Implicit((Object)(object)Hud.instance) || !Hud.instance.IsVisible() || !instance.m_smallRoot.activeInHierarchy)
		{
			if (Object.op_Implicit((Object)(object)label))
			{
				((Component)label).gameObject.SetActive(false);
			}
			return;
		}
		if (!Object.op_Implicit((Object)(object)label))
		{
			owner = instance;
			GameObject val = new GameObject("ToggleQolClock", new Type[1] { typeof(RectTransform) });
			val.SetActive(false);
			val.transform.SetParent(((Component)instance.m_mapImageSmall).transform, false);
			label = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)label).font = instance.m_biomeNameSmall.font;
			((TMP_Text)label).fontSharedMaterial = instance.m_biomeNameSmall.fontSharedMaterial;
			((TMP_Text)label).fontSize = 20f;
			((TMP_Text)label).enableAutoSizing = true;
			((TMP_Text)label).fontSizeMin = 12f;
			((TMP_Text)label).fontSizeMax = 20f;
			((TMP_Text)label).fontStyle = (FontStyles)1;
			((Graphic)label).color = Color.white;
			((TMP_Text)label).alignment = (TextAlignmentOptions)513;
			((Graphic)label).raycastTarget = false;
			((MaskableGraphic)label).maskable = false;
			((TMP_Text)label).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)label).overflowMode = (TextOverflowModes)0;
			RectTransform rectTransform = ((TMP_Text)label).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 1f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.pivot = new Vector2(0.5f, 0f);
			rectTransform.sizeDelta = new Vector2(-12f, 26f);
			rectTransform.anchoredPosition = new Vector2(0f, 4f);
			GameObject val2 = new GameObject("ToggleQolTime", new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(val.transform, false);
			timeLabel = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)timeLabel).font = ((TMP_Text)label).font;
			((TMP_Text)timeLabel).fontSharedMaterial = ((TMP_Text)label).fontSharedMaterial;
			((TMP_Text)timeLabel).fontSize = ((TMP_Text)label).fontSize;
			((TMP_Text)timeLabel).enableAutoSizing = true;
			((TMP_Text)timeLabel).fontSizeMin = 12f;
			((TMP_Text)timeLabel).fontSizeMax = 20f;
			((TMP_Text)timeLabel).fontStyle = ((TMP_Text)label).fontStyle;
			((Graphic)timeLabel).color = ((Graphic)label).color;
			((TMP_Text)timeLabel).alignment = (TextAlignmentOptions)516;
			((Graphic)timeLabel).raycastTarget = false;
			((MaskableGraphic)timeLabel).maskable = false;
			((TMP_Text)timeLabel).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)timeLabel).overflowMode = (TextOverflowModes)0;
			((TMP_Text)timeLabel).rectTransform.anchorMin = new Vector2(0.45f, 0f);
			((TMP_Text)timeLabel).rectTransform.anchorMax = Vector2.one;
			((TMP_Text)timeLabel).rectTransform.offsetMin = Vector2.zero;
			((TMP_Text)timeLabel).rectTransform.offsetMax = Vector2.zero;
		}
		bool flag = !((Component)label).gameObject.activeSelf;
		((Component)label).gameObject.SetActive(true);
		TextMeshProUGUI obj = label;
		Rect rect = ((TMP_Text)label).rectTransform.rect;
		((TMP_Text)obj).margin = new Vector4(0f, 0f, ((Rect)(ref rect)).width * 0.55f + 6f, 0f);
		((TMP_Text)label).text = "Day " + EnvMan.instance.GetDay().ToString(CultureInfo.InvariantCulture);
		((TMP_Text)timeLabel).text = ClockTime.Format(EnvMan.instance.GetDayFraction(), UseAmPm);
		if (flag)
		{
			label.canvasRenderer.cull = false;
			((Graphic)label).SetAllDirty();
			((TMP_Text)label).ForceMeshUpdate(false, false);
			timeLabel.canvasRenderer.cull = false;
			((Graphic)timeLabel).SetAllDirty();
			((TMP_Text)timeLabel).ForceMeshUpdate(false, false);
		}
	}

	protected override void OnToggle(bool next)
	{
		if (next)
		{
			OnTick();
		}
		else if (Object.op_Implicit((Object)(object)label))
		{
			((Component)label).gameObject.SetActive(false);
		}
	}

	protected override void OnStop()
	{
		Clear();
	}

	private void Clear()
	{
		if (Object.op_Implicit((Object)(object)label))
		{
			((Component)label).gameObject.SetActive(false);
			Object.Destroy((Object)(object)((Component)label).gameObject);
		}
		label = null;
		timeLabel = null;
		owner = null;
	}
}
internal sealed class OrderedSummonsFeature : QolFeature
{
	private sealed class Cursor
	{
		public ConfigEntry<int> Saved;

		public SummonCycleState State;
	}

	private sealed class Ticket
	{
		public long Caster;

		public int SelectedIndex;

		public GameObject Created;
	}

	private static OrderedSummonsFeature instance;

	private static readonly FieldInfo Owner = AccessTools.Field(typeof(SpawnAbility), "m_owner");

	private static readonly FieldInfo Weapon = AccessTools.Field(typeof(SpawnAbility), "m_weapon");

	private readonly ConfigFile config;

	private readonly Action<string> warn;

	private Harmony harmony;

	private readonly Dictionary<long, Cursor> cursors = new Dictionary<long, Cursor>();

	private readonly Dictionary<SpawnAbility, Ticket> tickets = new Dictionary<SpawnAbility, Ticket>();

	private readonly List<SpawnAbility> expired = new List<SpawnAbility>();

	public OrderedSummonsFeature(ConfigFile config, Action<string> warn)
		: base(config, "OrderedSpiritSummons", "Ordered Spirit Caller summons", "Personal cast order: boar > wolf > bear > moose. Advances after a successful spawn; native caps/costs stay normal. Saves per character. In-flight casts finish their selected mode.")
	{
		this.config = config;
		this.warn = warn;
	}

	public override void Initialize()
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Expected O, but got Unknown
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Expected O, but got Unknown
		instance = this;
		if (Owner == null || Weapon == null)
		{
			throw new MissingFieldException("SpawnAbility caster/item fields changed.");
		}
		MethodInfo methodInfo = AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(SpawnAbility), "Spawn", (Type[])null, (Type[])null));
		if (methodInfo == null)
		{
			throw new MissingMethodException("Spawn coroutine changed.");
		}
		harmony = new Harmony("benowy.qol.orderedspirits");
		harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(OrderedSummonsFeature), "PatchSpawn", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private Cursor GetCursor(long id)
	{
		if (!cursors.TryGetValue(id, out var value))
		{
			ConfigEntry<int> val = config.Bind<int>("SpiritCycle", "Character_" + id.ToString(CultureInfo.InvariantCulture), 0, "Next ordered spirit for this character: 0 boar, 1 wolf, 2 bear, 3 moose. Saved after successful ordered spawns; independent of world and army composition.");
			Cursor cursor = new Cursor();
			cursor.Saved = val;
			cursor.State = new SummonCycleState(val.Value);
			value = cursor;
			cursors.Add(id, value);
		}
		return value;
	}

	private static bool Caster(SpawnAbility ability, out Player player)
	{
		player = null;
		if (!Object.op_Implicit((Object)(object)ability) || PrefabName(((Component)ability).gameObject) != "staff_SpiritCaller_spawn" || ability.m_minToSpawn != 1 || ability.m_maxToSpawn != 1)
		{
			return false;
		}
		object? value = Owner.GetValue(ability);
		player = (Player)((value is Player) ? value : null);
		object? value2 = Weapon.GetValue(ability);
		ItemData val = (ItemData)((value2 is ItemData) ? value2 : null);
		if (Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer && player.GetPlayerID() != 0 && val != null && Object.op_Implicit((Object)(object)val.m_dropPrefab))
		{
			return ((Object)val.m_dropPrefab).name == "StaffSpiritCaller";
		}
		return false;
	}

	private static GameObject[] Prefabs(SpawnAbility ability)
	{
		if (ability.m_spawnPrefab == null || ability.m_spawnPrefab.Length != 4)
		{
			return null;
		}
		GameObject[] array = (GameObject[])(object)new GameObject[4];
		GameObject[] spawnPrefab = ability.m_spawnPrefab;
		foreach (GameObject val in spawnPrefab)
		{
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			int num = Array.IndexOf(SummonCycleState.PrefabOrder, ((Object)val).name);
			if (num < 0 || Object.op_Implicit((Object)(object)array[num]))
			{
				return null;
			}
			array[num] = val;
		}
		return array;
	}

	private static string PrefabName(GameObject obj)
	{
		string name = ((Object)obj).name;
		if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
		{
			return name;
		}
		return name.Substring(0, name.Length - 7);
	}

	private static GameObject Select(GameObject nativeRandom, SpawnAbility ability)
	{
		OrderedSummonsFeature orderedSummonsFeature = instance;
		if (orderedSummonsFeature == null || !orderedSummonsFeature.Running || !orderedSummonsFeature.Available)
		{
			return nativeRandom;
		}
		orderedSummonsFeature.Prune();
		orderedSummonsFeature.tickets.Remove(ability);
		if (!orderedSummonsFeature.Enabled)
		{
			return nativeRandom;
		}
		try
		{
			if (!Caster(ability, out var player))
			{
				return nativeRandom;
			}
			GameObject[] array = Prefabs(ability);
			if (array == null)
			{
				return nativeRandom;
			}
			long playerID = player.GetPlayerID();
			int nextIndex = orderedSummonsFeature.GetCursor(playerID).State.NextIndex;
			orderedSummonsFeature.tickets[ability] = new Ticket
			{
				Caster = playerID,
				SelectedIndex = nextIndex
			};
			return array[nextIndex];
		}
		catch (Exception ex)
		{
			orderedSummonsFeature.warn("Ordered selection skipped: " + ex.Message);
			return nativeRandom;
		}
	}

	private static GameObject Create(GameObject selected, Vector3 position, Quaternion rotation, SpawnAbility ability, Vector3 target)
	{
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		OrderedSummonsFeature orderedSummonsFeature = instance;
		Ticket value = null;
		GameObject val = selected;
		if (orderedSummonsFeature != null && orderedSummonsFeature.Running && orderedSummonsFeature.Available && orderedSummonsFeature.tickets.TryGetValue(ability, out value))
		{
			try
			{
				GameObject[] array = Prefabs(ability);
				if (!Caster(ability, out var player) || player.GetPlayerID() != value.Caster || array == null)
				{
					orderedSummonsFeature.tickets.Remove(ability);
					value = null;
				}
				else
				{
					value.SelectedIndex = orderedSummonsFeature.GetCursor(value.Caster).State.NextIndex;
					val = array[value.SelectedIndex];
					if (ability.m_maxSpawned > 0 && SpawnSystem.GetNrOfInstances(val, target, 0f, false, false) >= ability.m_maxSpawned)
					{
						orderedSummonsFeature.tickets.Remove(ability);
						((Character)player).Message((MessageType)2, ability.m_maxSummonReached, 0, (Sprite)null, false);
						return null;
					}
				}
			}
			catch (Exception ex)
			{
				orderedSummonsFeature.tickets.Remove(ability);
				value = null;
				val = selected;
				orderedSummonsFeature.warn("Ordered spawn preparation skipped: " + ex.Message);
			}
		}
		GameObject val2 = Object.Instantiate<GameObject>(val, position, rotation);
		if (value != null)
		{
			value.Created = val2;
		}
		return val2;
	}

	private static void Commit(GameObject created, SpawnAbility ability)
	{
		OrderedSummonsFeature orderedSummonsFeature = instance;
		if (orderedSummonsFeature == null || !orderedSummonsFeature.tickets.TryGetValue(ability, out var value))
		{
			return;
		}
		orderedSummonsFeature.tickets.Remove(ability);
		try
		{
			if (!Object.op_Implicit((Object)(object)created) || (Object)(object)created != (Object)(object)value.Created || !Object.op_Implicit((Object)(object)created.GetComponent<Character>()) || PrefabName(created) != SummonCycleState.PrefabOrder[value.SelectedIndex])
			{
				return;
			}
			ZNetView component = created.GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				return;
			}
			Cursor cursor = orderedSummonsFeature.GetCursor(value.Caster);
			if (!cursor.State.Commit(value.SelectedIndex))
			{
				orderedSummonsFeature.warn("Ordered cycle changed unexpectedly during spawn; cursor was not advanced.");
				return;
			}
			cursor.Saved.Value = cursor.State.NextIndex;
			try
			{
				orderedSummonsFeature.config.Save();
			}
			catch (Exception ex)
			{
				orderedSummonsFeature.warn("Cycle advanced for this session, but could not save: " + ex.Message);
			}
		}
		catch (Exception ex2)
		{
			orderedSummonsFeature.warn("Could not record successful ordered spawn: " + ex2.Message);
		}
	}

	protected override void OnTick()
	{
		Prune();
	}

	private void Prune()
	{
		expired.Clear();
		foreach (KeyValuePair<SpawnAbility, Ticket> ticket in tickets)
		{
			if (!Object.op_Implicit((Object)(object)ticket.Key))
			{
				expired.Add(ticket.Key);
			}
		}
		foreach (SpawnAbility item in expired)
		{
			tickets.Remove(item);
		}
	}

	protected override void OnStop()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		tickets.Clear();
		if (instance == this)
		{
			instance = null;
		}
	}

	private static IEnumerable<CodeInstruction> PatchSpawn(IEnumerable<CodeInstruction> instructions, MethodBase original, ILGenerator generator)
	{
		//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e3: Expected O, but got Unknown
		//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f5: Expected O, but got Unknown
		//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0407: Expected O, but got Unknown
		//IL_040f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0419: Expected O, but got Unknown
		//IL_0458: Unknown result type (might be due to invalid IL or missing references)
		//IL_0462: Expected O, but got Unknown
		//IL_046a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0474: Expected O, but got Unknown
		//IL_0491: Unknown result type (might be due to invalid IL or missing references)
		//IL_049b: Expected O, but got Unknown
		//IL_04a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b3: Expected O, but got Unknown
		//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_04cb: Expected O, but got Unknown
		//IL_04d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_04dd: Expected O, but got Unknown
		//IL_04e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f0: Expected O, but got Unknown
		//IL_04fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0508: Expected O, but got Unknown
		//IL_0510: Unknown result type (might be due to invalid IL or missing references)
		//IL_051a: Expected O, but got Unknown
		//IL_0522: Unknown result type (might be due to invalid IL or missing references)
		//IL_052c: Expected O, but got Unknown
		//IL_0549: Unknown result type (might be due to invalid IL or missing references)
		//IL_0553: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		FieldInfo fieldInfo = AccessTools.Field(original.DeclaringType, "<>4__this");
		FieldInfo fieldInfo2 = AccessTools.Field(original.DeclaringType, "<targetPosition>5__5");
		int num = -1;
		int num2 = -1;
		int num3 = -1;
		object obj = null;
		int num4 = 0;
		int num5 = 0;
		int num6 = 0;
		int num7 = 0;
		for (int i = 1; i + 1 < list.Count; i++)
		{
			MethodInfo methodInfo = list[i].operand as MethodInfo;
			FieldInfo fieldInfo3 = list[i].operand as FieldInfo;
			if (list[i].opcode == OpCodes.Ldelem_Ref && CodeInstructionExtensions.Calls(list[i - 1], AccessTools.Method(typeof(Random), "Range", new Type[2]
			{
				typeof(int),
				typeof(int)
			}, (Type[])null)) && list[i + 1].opcode == OpCodes.Stfld && ((FieldInfo)list[i + 1].operand).Name == "<prefab>5__9")
			{
				num = i;
				num4++;
			}
			if (list[i].opcode == OpCodes.Call && methodInfo != null && methodInfo.DeclaringType == typeof(Object) && methodInfo.Name == "Instantiate" && methodInfo.IsGenericMethod && methodInfo.GetGenericArguments()[0] == typeof(GameObject) && methodInfo.GetParameters().Length == 3)
			{
				num2 = i;
				num5++;
			}
			if (fieldInfo3 != null && fieldInfo3.DeclaringType == typeof(SpawnAbility) && fieldInfo3.Name == "m_preSpawnEffects" && i >= 2 && (list[i - 2].opcode == OpCodes.Br || list[i - 2].opcode == OpCodes.Br_S))
			{
				obj = list[i - 2].operand;
				num7++;
			}
			if (fieldInfo3 != null && fieldInfo3.DeclaringType == typeof(SpawnAbility) && fieldInfo3.Name == "m_spawnDelay" && i >= 3 && list[i - 2].opcode == OpCodes.Pop && list[i - 3].operand is MethodInfo && ((MethodInfo)list[i - 3].operand).DeclaringType == typeof(EffectList) && ((MethodInfo)list[i - 3].operand).Name == "Create")
			{
				num3 = i - 2;
				num6++;
			}
		}
		if (fieldInfo == null || fieldInfo2 == null || num4 != 1 || num5 != 1 || num6 != 1 || num7 != 1 || num >= num2 || num2 >= num3 || !(obj is Label) || list[num2 + 1].opcode != OpCodes.Stloc_2 || list[num2].labels.Count != 0 || list[num2].blocks.Count != 0)
		{
			throw new InvalidOperationException("Spirit spawn structure changed; ordered feature not patched.");
		}
		Label label = generator.DefineLabel();
		list[num2 + 1].labels.Add(label);
		List<CodeInstruction> list2 = new List<CodeInstruction>();
		for (int j = 0; j < list.Count; j++)
		{
			if (j == num2)
			{
				list2.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo));
				list2.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo2));
				list[j].operand = AccessTools.Method(typeof(OrderedSummonsFeature), "Create", (Type[])null, (Type[])null);
			}
			list2.Add(list[j]);
			if (j == num)
			{
				list2.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo));
				list2.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(OrderedSummonsFeature), "Select", (Type[])null, (Type[])null)));
			}
			if (j == num2)
			{
				list2.Add(new CodeInstruction(OpCodes.Dup, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Brtrue, (object)label));
				list2.Add(new CodeInstruction(OpCodes.Pop, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Br, obj));
			}
			if (j == num3)
			{
				list2.Add(new CodeInstruction(OpCodes.Ldloc_2, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				list2.Add(new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo));
				list2.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(OrderedSummonsFeature), "Commit", (Type[])null, (Type[])null)));
			}
		}
		return list2;
	}
}
[BepInIncompatibility("local.food.fullduration")]
[DefaultExecutionOrder(-10000)]
[BepInPlugin("benowy.qol", "ToggleQol", "0.6.0")]
[BepInIncompatibility("local.spiritcaller.passthrough")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Id = "benowy.qol";

	private readonly List<QolFeature> features = new List<QolFeature>();

	private ConfigEntry<KeyCode> menuKey;

	private SettingsMenu menu;

	private InputGuard input;

	private bool ready;

	private void Awake()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Invalid comparison between Unknown and I4
		((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
		menuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Menu", "Hotkey", (KeyCode)289, "Keyboard key for the ToggleQol menu. Also changeable in-game. Default F8.");
		if ((int)menuKey.Value == 0 || (int)menuKey.Value == 27)
		{
			menuKey.Value = (KeyCode)289;
		}
		features.Add(new SpiritCollisionFeature(((BaseUnityPlugin)this).Config));
		features.Add(new FullFoodFeature(((BaseUnityPlugin)this).Config));
		features.Add(new MinimapClockFeature(((BaseUnityPlugin)this).Config));
		features.Add(new AutoDoorsFeature(((BaseUnityPlugin)this).Config));
		features.Add(new AutoRunFeature(((BaseUnityPlugin)this).Config));
		features.Add(new CrossbowReloadFeature(((BaseUnityPlugin)this).Config));
		features.Add(new SwimmingToolsFeature(((BaseUnityPlugin)this).Config));
		features.Add(new OrderedSummonsFeature(((BaseUnityPlugin)this).Config, delegate(string message)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)message);
		}));
		features.Add(new BuildSearchFixFeature(((BaseUnityPlugin)this).Config));
		foreach (QolFeature feature in features)
		{
			try
			{
				feature.Initialize();
			}
			catch (Exception ex)
			{
				feature.Fail(ex);
				((BaseUnityPlugin)this).Logger.LogError((object)(feature.Title + ": " + ex));
			}
		}
		try
		{
			input = new InputGuard();
			input.Install();
			menu = new SettingsMenu(features, menuKey, SaveSettings, input);
		}
		catch (Exception ex2)
		{
			if (input != null)
			{
				input.Dispose();
			}
			((BaseUnityPlugin)this).Logger.LogError((object)("Menu input protection could not initialize; menu disabled. Features remain configurable in benowy.qol.cfg. " + ex2));
		}
		ready = true;
		SaveSettings();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"ToggleQol ready. F8 by default opens settings in-game. Collision requires matching settings on every client and dedicated server; food is personal.");
	}

	private string SaveSettings()
	{
		try
		{
			((BaseUnityPlugin)this).Config.Save();
			return "Saved. Food changes apply on the next normal food tick.";
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Settings active but could not save: " + ex.Message));
			return "Active, but could not save settings: " + ex.Message;
		}
	}

	private void Update()
	{
		if (ready && menu != null)
		{
			menu.Update();
		}
	}

	private void FixedUpdate()
	{
		if (!ready)
		{
			return;
		}
		foreach (QolFeature feature in features)
		{
			try
			{
				feature.Tick();
			}
			catch (Exception ex)
			{
				feature.Fail(ex);
				((BaseUnityPlugin)this).Logger.LogError((object)(feature.Title + " stopped safely: " + ex));
			}
		}
	}

	private void OnGUI()
	{
		if (ready && menu != null)
		{
			menu.Draw();
		}
	}

	private void OnDisable()
	{
		ready = false;
		if (menu != null)
		{
			menu.Dispose();
		}
		if (input != null)
		{
			input.Dispose();
		}
		foreach (QolFeature feature in features)
		{
			feature.Dispose();
		}
	}

	private void OnDestroy()
	{
		OnDisable();
	}
}
internal sealed class SettingsMenu : IDisposable
{
	private readonly List<QolFeature> features;

	private readonly ConfigEntry<KeyCode> hotkey;

	private readonly Func<string> save;

	private readonly InputGuard input;

	private readonly UiShield shield = new UiShield();

	private bool open;

	private bool capturing;

	private Player menuPlayer;

	private string status = "Changes save automatically";

	private Rect window;

	private Vector2 scroll;

	private GUIStyle windowStyle;

	private GUIStyle titleStyle;

	private GUIStyle labelStyle;

	private GUIStyle headingStyle;

	private GUIStyle nameStyle;

	private GUIStyle buttonStyle;

	private GUIStyle smallStyle;

	private GUIStyle stateStyle;

	private Texture2D background;

	private Texture2D pill;

	private Texture2D knob;

	private Texture2D buttonTexture;

	private Texture2D hoverTexture;

	private Texture2D selectedTexture;

	private readonly Color gold = new Color(0.76f, 0.59f, 0.34f);

	private readonly Color teal = new Color(0.3f, 0.78f, 0.7f);

	private readonly Color muted = new Color(0.61f, 0.67f, 0.68f);

	public SettingsMenu(List<QolFeature> features, ConfigEntry<KeyCode> hotkey, Func<string> save, InputGuard input)
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: 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_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		this.features = new List<QolFeature>(features);
		this.features.Sort((QolFeature a, QolFeature b) => Order(a).CompareTo(Order(b)));
		this.hotkey = hotkey;
		this.save = save;
		this.input = input;
	}

	private static int Order(QolFeature f)
	{
		if (f is CrossbowReloadFeature)
		{
			return 2;
		}
		if (f is AutoDoorsFeature)
		{
			return 0;
		}
		if (f is AutoRunFeature)
		{
			return 1;
		}
		if (f is SwimmingToolsFeature)
		{
			return 2;
		}
		if (f is MinimapClockFeature)
		{
			return 3;
		}
		if (f is FullFoodFeature)
		{
			return 4;
		}
		if (f is SpiritCollisionFeature)
		{
			return 5;
		}
		if (f is OrderedSummonsFeature)
		{
			return 6;
		}
		if (f is BuildSearchFixFeature)
		{
			return 7;
		}
		return 8;
	}

	public void Update()
	{
		//IL_0087: 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_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		input.Update();
		shield.SetActive(input.Blocking);
		Player localPlayer = Player.m_localPlayer;
		if (open && (!Object.op_Implicit((Object)(object)localPlayer) || (Object)(object)localPlayer != (Object)(object)menuPlayer || ((Character)localPlayer).IsDead()))
		{
			Close();
		}
		else
		{
			if (capturing)
			{
				return;
			}
			if (open && input.KeyDown((KeyCode)27))
			{
				Close();
			}
			else if (input.KeyDown(hotkey.Value))
			{
				if (open)
				{
					Close();
				}
				else if (Object.op_Implicit((Object)(object)localPlayer) && !((Character)localPlayer).IsDead() && !Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible() && !StoreGui.IsVisible() && !Minimap.IsOpen() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()))
				{
					open = true;
					menuPlayer = localPlayer;
					float num = Math.Min(1080, Screen.width - 24);
					float num2 = Math.Min(860, Screen.height - 24);
					window = new Rect(((float)Screen.width - num) / 2f, ((float)Screen.height - num2) / 2f, num, num2);
					input.SetOpen(value: true, hotkey.Value);
					shield.SetActive(value: true);
				}
			}
		}
	}

	private void Close()
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		open = false;
		capturing = false;
		input.SetOpen(value: false, hotkey.Value);
	}

	private void Save()
	{
		string text = save();
		status = (text.StartsWith("Saved.") ? "Changes saved" : text);
	}

	public void Draw()
	{
		//IL_0093: 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_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Expected O, but got Unknown
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Invalid comparison between Unknown and I4
		//IL_0027: 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_0036: Invalid comparison between Unknown and I4
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		if (!open)
		{
			return;
		}
		EnsureStyles();
		Event current = Event.current;
		if (capturing && (int)current.type == 4 && (int)current.keyCode != 0)
		{
			if ((int)current.keyCode == 27)
			{
				capturing = false;
				status = "Key change cancelled";
			}
			else if (ZInput.IsKeyCodeValid(current.keyCode))
			{
				hotkey.Value = current.keyCode;
				capturing = false;
				Save();
			}
			current.Use();
		}
		Fill(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), new Color(0f, 0f, 0f, 0.55f));
		((Rect)(ref window)).width = Math.Min(1080, Screen.width - 24);
		((Rect)(ref window)).height = Math.Min(860, Screen.height - 24);
		((Rect)(ref window)).x = Mathf.Clamp(((Rect)(ref window)).x, 0f, Math.Max(0f, (float)Screen.width - ((Rect)(ref window)).width));
		((Rect)(ref window)).y = Mathf.Clamp(((Rect)(ref window)).y, 0f, Math.Max(0f, (float)Screen.height - ((Rect)(ref window)).height));
		window = GUI.Window(194732, window, new WindowFunction(DrawWindow), GUIContent.none, windowStyle);
	}

	private void DrawWindow(int id)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: 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_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: 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_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0225: Unknown result type (might be due to invalid IL or missing references)
		//IL_0230: Unknown result type (might be due to invalid IL or missing references)
		//IL_023f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_030a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0311: Unknown result type (might be due to invalid IL or missing references)
		//IL_0316: Unknown result type (might be due to invalid IL or missing references)
		//IL_036d: Unknown result type (might be due to invalid IL or missing references)
		//IL_039b: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0466: Unknown result type (might be due to invalid IL or missing references)
		//IL_046c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0477: Unknown result type (might be due to invalid IL or missing references)
		//IL_0482: Unknown result type (might be due to invalid IL or missing references)
		//IL_0491: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04de: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_051a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0403: Unknown result type (might be due to invalid IL or missing references)
		//IL_0566: Unknown result type (might be due to invalid IL or missing references)
		//IL_056b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0587: Unknown result type (might be due to invalid IL or missing references)
		//IL_05bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_05ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_05ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_0622: Unknown result type (might be due to invalid IL or missing references)
		//IL_058f: Unknown result type (might be due to invalid IL or missing references)
		float width = ((Rect)(ref window)).width;
		float height = ((Rect)(ref window)).height;
		GUI.DrawTexture(new Rect(0f, 0f, width, height), (Texture)(object)background);
		Frame(new Rect(1f, 1f, width - 2f, height - 2f), gold);
		Frame(new Rect(5f, 5f, width - 10f, height - 10f), new Color(gold.r, gold.g, gold.b, 0.25f));
		Corner(12f, 12f, 1, 1);
		Corner(width - 12f, 12f, -1, 1);
		Corner(12f, height - 12f, 1, -1);
		Corner(width - 12f, height - 12f, -1, -1);
		Frame(new Rect(32f, 25f, 44f, 48f), gold);
		Fill(new Rect(43f, 36f, 22f, 3f), gold);
		Fill(new Rect(53f, 36f, 3f, 27f), gold);
		GUI.Label(new Rect(96f, 24f, width - 270f, 54f), "TOGGLE QOL", titleStyle);
		GUI.Label(new Rect(width - 142f, 34f, 66f, 32f), ((object)hotkey.Value).ToString(), buttonStyle);
		if (GUI.Button(new Rect(width - 62f, 30f, 36f, 38f), "×", buttonStyle))
		{
			Close();
		}
		Fill(new Rect(32f, 91f, width - 64f, 1f), new Color(gold.r, gold.g, gold.b, 0.55f));
		Rect val = default(Rect);
		((Rect)(ref val))..ctor(28f, 104f, width - 56f, Math.Max(80f, height - 225f));
		float num = ((Rect)(ref val)).width - 20f;
		float num2 = 0f;
		for (int i = 0; i < 4; i++)
		{
			num2 += 43f;
			foreach (QolFeature feature in features)
			{
				if (Category(feature) == i)
				{
					num2 += RowHeight(feature, num);
				}
			}
			num2 += 5f;
		}
		scroll = GUI.BeginScrollView(val, scroll, new Rect(0f, 0f, num, num2), false, false);
		float num3 = 0f;
		string[] array = new string[4] { "GENERAL", "HUD", "FOOD & SUMMONS", "FIXES" };
		for (int j = 0; j < 4; j++)
		{
			GUI.Label(new Rect(4f, num3, num - 8f, 28f), array[j], headingStyle);
			Fill(new Rect(4f, num3 + 31f, num - 8f, 1f), new Color(0.42f, 0.44f, 0.41f, 0.6f));
			num3 += 43f;
			foreach (QolFeature feature2 in features)
			{
				if (Category(feature2) == j)
				{
					float num4 = RowHeight(feature2, num);
					DrawRow(feature2, new Rect(0f, num3, num, num4));
					num3 += num4;
				}
			}
			num3 += 5f;
		}
		GUI.EndScrollView();
		float num5 = height - 107f;
		Fill(new Rect(32f, num5, width - 64f, 1f), new Color(gold.r, gold.g, gold.b, 0.5f));
		GUI.Label(new Rect(34f, num5 + 17f, 125f, 34f), "Menu shortcut", labelStyle);
		GUI.Label(new Rect(160f, num5 + 17f, 85f, 34f), ((object)hotkey.Value).ToString(), buttonStyle);
		if (GUI.Button(new Rect(254f, num5 + 17f, 118f, 34f), capturing ? "Press key…" : "Change", buttonStyle))
		{
			capturing = !capturing;
		}
		string text = (capturing ? "Press a key · Esc cancels" : status);
		Color contentColor = GUI.contentColor;
		GUI.contentColor = ((status == "Changes saved" && !capturing) ? teal : Color.white);
		GUI.Label(new Rect(390f, num5 + 12f, Math.Max(100f, width - 426f), 57f), text, smallStyle);
		GUI.contentColor = contentColor;
		GUI.Label(new Rect(34f, height - 34f, width - 68f, 22f), "The world keeps running while this menu is open.", smallStyle);
		GUI.DragWindow(new Rect(85f, 8f, Math.Max(1f, width - 250f), 76f));
	}

	private static int Category(QolFeature f)
	{
		if (f is MinimapClockFeature)
		{
			return 1;
		}
		if (f is FullFoodFeature || f is SpiritCollisionFeature || f is OrderedSummonsFeature)
		{
			return 2;
		}
		if (f is BuildSearchFixFeature)
		{
			return 3;
		}
		return 0;
	}

	private static string Name(QolFeature f)
	{
		if (f is CrossbowReloadFeature)
		{
			return "Mobile crossbow reload";
		}
		if (f is AutoDoorsFeature)
		{
			return "Auto doors";
		}
		if (f is AutoRunFeature)
		{
			return "Auto-run steering";
		}
		if (f is SwimmingToolsFeature)
		{
			return "Tools while swimming";
		}
		if (f is MinimapClockFeature)
		{
			return "Day & time";
		}
		if (f is FullFoodFeature)
		{
			return "No food decay";
		}
		if (f is SpiritCollisionFeature)
		{
			return "Friendly summon collision";
		}
		if (f is OrderedSummonsFeature)
		{
			return "Ordered summons";
		}
		if (f is BuildSearchFixFeature)
		{
			return "Build-menu search";
		}
		return f.Title;
	}

	private static string Summary(QolFeature f)
	{
		if (f is CrossbowReloadFeature)
		{
			return "Reload while sprinting and jumping.";
		}
		if (f is AutoDoorsFeature)
		{
			return "Open nearby doors and close them behind you.";
		}
		if (f is AutoRunFeature)
		{
			return "Steer with the camera; jumping keeps auto-run active.";
		}
		if (f is SwimmingToolsFeature)
		{
			return "Build, chop and mine without leaving the water.";
		}
		if (f is MinimapClockFeature)
		{
			return "Day counter and clock in half-hour steps.";
		}
		if (f is FullFoodFeature)
		{
			return "Keep food bonuses at full strength until expiry.";
		}
		if (f is SpiritCollisionFeature)
		{
			return "Walk through friendly Spirit Caller summons.";
		}
		if (f is OrderedSummonsFeature)
		{
			return "Boar → Wolf → Bear → Moose";
		}
		if (f is BuildSearchFixFeature)
		{
			return "Type freely without triggering the menu shortcut.";
		}
		return f.Description;
	}

	private float RowHeight(QolFeature f, float width)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Expected O, but got Unknown
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Expected O, but got Unknown
		bool flag = width < 850f || f is MinimapClockFeature;
		float val = (flag ? (width - 122f) : (width - 424f));
		float num = labelStyle.CalcHeight(new GUIContent(Summary(f)), Math.Max(80f, val));
		float num2 = Math.Max(flag ? 70 : 50, num + (float)(flag ? 36 : 18));
		if (!f.Available)
		{
			num2 += smallStyle.CalcHeight(new GUIContent(f.Status), Math.Max(80f, width - 20f)) + 8f;
		}
		return num2;
	}

	private void DrawRow(QolFeature feature, Rect row)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_0334: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0376: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Expected O, but got Unknown
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_024f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0273: Unknown result type (might be due to invalid IL or missing references)
		//IL_027a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0291: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a0: Expected O, but got Unknown
		bool flag = ((Rect)(ref row)).width < 850f || feature is MinimapClockFeature;
		if (((Rect)(ref row)).Contains(Event.current.mousePosition))
		{
			Fill(row, new Color(1f, 1f, 1f, 0.025f));
		}
		float num = ((Rect)(ref row)).width - 100f;
		MinimapClockFeature minimapClockFeature = feature as MinimapClockFeature;
		GUI.Label(new Rect(4f, ((Rect)(ref row)).y + 2f, (minimapClockFeature != null) ? (((Rect)(ref row)).width - 300f) : (flag ? (num - 10f) : 295f), 30f), new GUIContent(Name(feature), feature.Description), nameStyle);
		GUI.Label(new Rect((float)(flag ? 4 : 308), ((Rect)(ref row)).y + (float)(flag ? 33 : 5), flag ? (num - 14f) : (num - 320f), ((Rect)(ref row)).height - (float)(flag ? 36 : 8)), Summary(feature), labelStyle);
		bool enabled = GUI.enabled;
		GUI.enabled = enabled && feature.Available;
		Rect val = default(Rect);
		((Rect)(ref val))..ctor(num, ((Rect)(ref row)).y + 5f, 96f, 30f);
		Color color = (feature.Enabled ? new Color(0.16f, 0.44f, 0.45f) : new Color(0.22f, 0.26f, 0.28f));
		TintTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y + 1f, 56f, 28f), pill, color);
		TintTexture(new Rect(((Rect)(ref val)).x + (float)(feature.Enabled ? 30 : 3), ((Rect)(ref val)).y + 4f, 22f, 22f), knob, (Color)(feature.Available ? new Color(0.91f, 0.95f, 0.93f) : muted));
		Color contentColor = GUI.contentColor;
		GUI.contentColor = (feature.Enabled ? teal : muted);
		GUI.Label(new Rect(((Rect)(ref val)).x + 63f, ((Rect)(ref val)).y, 35f, 30f), feature.Enabled ? "ON" : "OFF", stateStyle);
		GUI.contentColor = contentColor;
		if (GUI.Button(val, new GUIContent("", "Toggle " + Name(feature)), GUIStyle.none))
		{
			try
			{
				feature.SetEnabled(!feature.Enabled);
				Save();
			}
			catch (Exception ex)
			{
				feature.Fail(ex);
				status = "Could not change feature: " + ex.Message;
			}
		}
		if (minimapClockFeature != null)
		{
			float num2 = num - 166f;
			FormatButton(minimapClockFeature, new Rect(num2, ((Rect)(ref row)).y + 3f, 70f, 32f), amPm: false, "24h");
			FormatButton(minimapClockFeature, new Rect(num2 + 70f, ((Rect)(ref row)).y + 3f, 83f, 32f), amPm: true, "AM/PM");
		}
		GUI.enabled = enabled;
		if (!feature.Available)
		{
			GUI.Label(new Rect(4f, ((Rect)(ref row)).yMax - 36f, ((Rect)(ref row)).width - 8f, 36f), feature.Status, smallStyle);
		}
		Fill(new Rect(4f, ((Rect)(ref row)).yMax - 2f, ((Rect)(ref row)).width - 8f, 1f), new Color(1f, 1f, 1f, 0.035f));
	}

	private void FormatButton(MinimapClockFeature clock, Rect rect, bool amPm, string text)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		bool flag = clock.UseAmPm == amPm;
		Color backgroundColor = GUI.backgroundColor;
		if (flag)
		{
			GUI.backgroundColor = new Color(0.45f, 0.95f, 0.95f);
		}
		if (GUI.Button(rect, text, buttonStyle) && !flag)
		{
			try
			{
				clock.ToggleFormat();
				Save();
			}
			catch (Exception ex)
			{
				clock.Fail(ex);
				status = "Could not change clock: " + ex.Message;
			}
		}
		GUI.backgroundColor = backgroundColor;
		if (flag)
		{
			Frame(rect, new Color(0.33f, 0.66f, 0.66f));
		}
	}

	private static void Fill(Rect rect, Color color)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		TintTexture(rect, Texture2D.whiteTexture, color);
	}

	private static void TintTexture(Rect rect, Texture2D texture, Color color)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		Color color2 = GUI.color;
		GUI.color = color;
		GUI.DrawTexture(rect, (Texture)(object)texture);
		GUI.color = color2;
	}

	private static void Frame(Rect r, Color c)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: 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_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, 1f), c);
		Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - 1f, ((Rect)(ref r)).width, 1f), c);
		Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, 1f, ((Rect)(ref r)).height), c);
		Fill(new Rect(((Rect)(ref r)).xMax - 1f, ((Rect)(ref r)).y, 1f, ((Rect)(ref r)).height), c);
	}

	private void Corner(float x, float y, int dx, int dy)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: 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_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		Fill(new Rect((dx > 0) ? x : (x - 22f), y, 22f, 1f), gold);
		Fill(new Rect(x, (dy > 0) ? y : (y - 22f), 1f, 22f), gold);
		Frame(new Rect((dx > 0) ? (x + 4f) : (x - 12f), (dy > 0) ? (y + 4f) : (y - 12f), 8f, 8f), gold);
	}

	private Texture2D Solid(Color color)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		Texture2D val = new Texture2D(1, 1);
		val.SetPixel(0, 0, color);
		val.Apply();
		return val;
	}

	private Texture2D Rounded(int width, int height)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Expected O, but got Unknown
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		Texture2D val = new Texture2D(width, height, (TextureFormat)4, false);
		((Texture)val).wrapMode = (TextureWrapMode)1;
		((Texture)val).filterMode = (FilterMode)1;
		float num = (float)height / 2f - 1f;
		for (int i = 0; i < height; i++)
		{
			for (int j = 0; j < width; j++)
			{
				float num2 = Mathf.Clamp((float)j + 0.5f, num + 1f, (float)width - num - 1f);
				float num3 = Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), new Vector2(num2, (float)height / 2f));
				val.SetPixel(j, i, new Color(1f, 1f, 1f, Mathf.Clamp01(num + 0.5f - num3)));
			}
		}
		val.Apply();
		return val;
	}

	private GUIStyle Text(int size, Color color, FontStyle weight)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		GUIStyle val = new GUIStyle(GUI.skin.label);
		val.fontSize = size;
		val.fontStyle = weight;
		val.wordWrap = true;
		GUIStyle val2 = val;
		val2.normal.textColor = color;
		return val2;
	}

	private void EnsureStyles()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		//IL_00c4: 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)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Expected O, but got Unknown
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Expected O, but got Unknown
		//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0200: Expected O, but got Unknown
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_0262: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: 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)
		if (windowStyle != null)
		{
			return;
		}
		background = new Texture2D(128, 128);
		Random random = new Random(418);
		for (int i = 0; i < 128; i++)
		{
			for (int j = 0; j < 128; j++)
			{
				float num = (float)random.NextDouble() * 0.018f;
				background.SetPixel(j, i, new Color(0.045f + num, 0.068f + num, 0.078f + num, 0.985f));
			}
		}
		background.Apply();
		pill = Rounded(112, 56);
		knob = Rounded(44, 44);
		buttonTexture = Solid(new Color(0.12f, 0.15f, 0.16f, 0.7f));
		hoverTexture = Solid(new Color(0.2f, 0.28f, 0.29f, 0.9f));
		selectedTexture = Solid(new Color(0.14f, 0.38f, 0.4f));
		windowStyle = new GUIStyle(GUIStyle.none);
		titleStyle = Text(36, gold, (FontStyle)0);
		headingStyle = Text(16, gold, (FontStyle)1);
		nameStyle = Text(19, new Color(0.94f, 0.95f, 0.92f), (FontStyle)1);
		labelStyle = Text(16, new Color(0.78f, 0.81f, 0.8f), (FontStyle)0);
		smallStyle = Text(13, muted, (FontStyle)0);
		stateStyle = Text(13, Color.white, (FontStyle)1);
		stateStyle.alignment = (TextAnchor)3;
		GUIStyle val = new GUIStyle(GUI.skin.button);
		val.fontSize = 15;
		val.alignment = (TextAnchor)4;
		val.border = new RectOffset(0, 0, 0, 0);
		buttonStyle = val;
		buttonStyle.normal.background = buttonTexture;
		buttonStyle.normal.textColor = new Color(0.88f, 0.92f, 0.9f);
		buttonStyle.hover.background = hoverTexture;
		buttonStyle.hover.textColor = Color.white;
		buttonStyle.active.background = selectedTexture;
		buttonStyle.active.textColor = Color.white;
	}

	public void Dispose()
	{
		if (open)
		{
			Close();
		}
		Texture2D[] array = (Texture2D[])(object)new Texture2D[6] { background, pill, knob, buttonTexture, hoverTexture, selectedTexture };
		foreach (Texture2D val in array)
		{
			if (Object.op_Implicit((Object)(object)val))
			{
				Object.Destroy((Object)(object)val);
			}
		}
		shield.Dispose();
	}
}
internal sealed class SpiritCollisionFeature : QolFeature
{
	private sealed class Pair
	{
		public Collider A;

		public Collider B;

		public bool PreviouslyIgnored;
	}

	private sealed class Bodies
	{
		public Collider Root;

		public Rigidbody Body;

		public float RefreshAt;

		public readonly List<Collider> Colliders = new List<Collider>();
	}

	private struct Node
	{
		public Character Character;

		public bool Spirit;
	}

	private static readonly HashSet<int> SpiritPrefabs = new HashSet<int>
	{
		StringExtensionMethods.GetStableHashCode("Boar_spiritcaller"),
		StringExtensionMethods.GetStableHashCode("Wolf_spiritcaller"),
		StringExtensionMethods.GetStableHashCode("Moose_spiritcaller"),
		StringExtensionMethods.GetStableHashCode("Bjorn_spiritcaller")
	};

	private readonly Dictionary<ulong, Pair> pairs = new Dictionary<ulong, Pair>();

	private readonly HashSet<ulong> seen = new HashSet<ulong>();

	private readonly List<ulong> removedPairs = new List<ulong>();

	private readonly Dictionary<Character, Bodies> bodyCache = new Dictionary<Character, Bodies>();

	private readonly HashSet<Character> live = new HashSet<Character>();

	private readonly List<Character> removedCharacters = new List<Character>();

	private readonly List<Node> characters = new List<Node>();

	public SpiritCollisionFeature(ConfigFile config)
		: base(config, "FriendlySpiritCollisions", "Friendly Spirit Caller collisions", "Anyone's four Spirit Caller summons pass through friendly characters. Every client and dedicated server must use matching settings; PvP-enabled players retain collision.")
	{
	}

	protected override void OnTick()
	{
		UpdatePairs();
	}

	protected override void OnToggle(bool next)
	{
		if (!next)
		{
			Clear();
		}
	}

	protected override void OnStop()
	{
		Clear();
	}

	private void UpdatePairs()
	{
		seen.Clear();
		live.Clear();
		characters.Clear();
		foreach (Character allCharacter in Character.GetAllCharacters())
		{
			if (Object.op_Implicit((Object)(object)allCharacter) && ((Component)allCharacter).gameObject.activeInHierarchy && !allCharacter.IsDead())
			{
				ZNetView component = ((Component)allCharacter).GetComponent<ZNetView>();
				if (Object.op_Implicit((Object)(object)component) && component.IsValid())
				{
					live.Add(allCharacter);
					characters.Add(new Node
					{
						Character = allCharacter,
						Spirit = SpiritPrefabs.Contains(component.GetZDO().GetPrefab())
					});
				}
			}
		}
		for (int i = 0; i < characters.Count; i++)
		{
			Node node = characters[i];
			if (!node.Spirit)
			{
				continue;
			}
			for (int j = 0; j < characters.Count; j++)
			{
				Node node2 = characters[j];
				if (i == j || (node2.Spirit && j < i) || !Friendly(node.Character, node2.Character))
				{
					continue;
				}
				List<Collider> bodies = GetBodies(node.Character);
				List<Collider> bodies2 = GetBodies(node2.Character);
				foreach (Collider item in bodies)
				{
					foreach (Collider item2 in bodies2)
					{
						if (ActiveBody(item, node.Character) && ActiveBody(item2, node2.Character) && (Object)(object)item != (Object)(object)item2)
						{
							AddPair(item, item2);
						}
					}
				}
			}
		}
		removedPairs.Clear();
		foreach (KeyValuePair<ulong, Pair> pair in pairs)
		{
			if (!seen.Contains(pair.Key))
			{
				removedPairs.Add(pair.Key);
			}
		}
		foreach (ulong removedPair in removedPairs)
		{
			Restore(pairs[removedPair]);
			pairs.Remove(removedPair);
		}
		removedCharacters.Clear();
		foreach (KeyValuePair<Character, Bodies> item3 in bodyCache)
		{
			if (!live.Contains(item3.Key))
			{
				removedCharacters.Add(item3.Key);
			}
		}
		foreach (Character removedCharacter in removedCharacters)
		{
			bodyCache.Remove(removedCharacter);
		}
	}

	private static bool Friendly(Character a, Character b)
	{
		if ((a.IsPlayer() && a.IsPVPEnabled()) || (b.IsPlayer() && b.IsPVPEnabled()))
		{
			return false;
		}
		if (!BaseAI.IsEnemy(a, b))
		{
			return !BaseAI.IsEnemy(b, a);
		}
		return false;
	}

	private List<Collider> GetBodies(Character character)
	{
		if (!bodyCache.TryGetValue(character, out var value))
		{
			value = new Bodies();
			bodyCache.Add(character, value);
		}
		Collider collider = (Collider)(object)character.GetCollider();
		Rigidbody val = (Object.op_Implicit((Object)(object)collider) ? collider.attachedRigidbody : null);
		if ((Object)(object)value.Root != (Object)(object)collider || (Object)(object)value.Body != (Object)(object)val || Time.time >= value.RefreshAt)
		{
			value.Root = collider;
			value.Body = val;
			value.RefreshAt = Time.time + 0.5f;
			value.Colliders.Clear();
			if (Object.op_Implicit((Object)(object)collider))
			{
				value.Colliders.Add(collider);
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				Collider[] componentsInChildren = ((Component)character).GetComponentsInChildren<Collider>(true);
				foreach (Collider val2 in componentsInChildren)
				{
					if ((Object)(object)val2 != (Object)(object)collider && (Object)(object)val2.attachedRigidbody == (Object)(object)val && (Object)(object)((Component)val2).GetComponentInParent<Character>() == (Object)(object)character)
					{
						value.Colliders.Add(val2);
					}
				}
			}
		}
		return value.Colliders;
	}

	private static bool ActiveBody(Collider collider, Character character)
	{
		if (!Object.op_Implicit((Object)(object)collider) || !collider.enabled || !((Component)collider).gameObject.activeInHierarchy || collider.isTrigger)
		{
			return false;
		}
		Collider collider2 = (Collider)(object)character.GetCollider();
		if ((Object)(object)collider == (Object)(object)collider2)
		{
			return true;
		}
		if (Object.op_Implicit((Object)(object)collider2) && Object.op_Implicit((Object)(object)collider2.attachedRigidbody) && (Object)(object)collider.attachedRigidbody == (Object)(object)collider2.attachedRigidbody)
		{
			return (Object)(object)((Component)collider).GetComponentInParent<Character>() == (Object)(object)character;
		}
		return false;
	}

	private void AddPair(Collider a, Collider b)
	{
		int num = ((Object)a).GetInstanceID();
		int num2 = ((Object)b).GetInstanceID();
		if (num > num2)
		{
			Collider val = a;
			a = b;
			b = val;
			int num3 = num;
			num = num2;
			num2 = num3;
		}
		ulong num4 = ((ulong)(uint)num << 32) | (uint)num2;
		seen.Add(num4);
		if (pairs.TryGetValue(num4, out var value) && ((Object)(object)value.A != (Object)(object)a || (Object)(object)value.B != (Object)(object)b))
		{
			Restore(value);
			pairs.Remove(num4);
		}
		if (!pairs.TryGetValue(num4, out value))
		{
			Pair pair = new Pair();
			pair.A = a;
			pair.B = b;
			pair.PreviouslyIgnored = Physics.GetIgnoreCollision(a, b);
			value = pair;
			pairs.Add(num4, value);
		}
		if (!Physics.GetIgnoreCollision(a, b))
		{
			Physics.IgnoreCollision(a, b, true);
		}
	}

	private static void Restore(Pair pair)
	{
		if (Object.op_Implicit((Object)(object)pair.A) && Object.op_Implicit((Object)(object)pair.B) && !pair.PreviouslyIgnored)
		{
			Physics.IgnoreCollision(pair.A, pair.B, false);
		}
	}

	private void Clear()
	{
		foreach (Pair value in pairs.Values)
		{
			Restore(value);
		}
		pairs.Clear();
		bodyCache.Clear();
		seen.Clear();
		live.Clear();
		characters.Clear();
	}
}
internal sealed class SummonCycleState
{
	public static readonly string[] PrefabOrder = new string[4] { "Boar_spiritcaller", "Wolf_spiritcaller", "Bjorn_spiritcaller", "Moose_spiritcaller" };

	public int NextIndex { get; private set; }

	public SummonCycleState(int savedIndex)
	{
		NextIndex = ((savedIndex >= 0 && savedIndex < 4) ? savedIndex : 0);
	}

	public bool Commit(int createdIndex)
	{
		if (createdIndex != NextIndex)
		{
			return false;
		}
		NextIndex = (NextIndex + 1) % 4;
		return true;
	}
}
internal sealed class SwimmingToolsFeature : QolFeature
{
	private static SwimmingToolsFeature instance;

	private Harmony harmony;

	private static FieldInfo hiddenRight;

	private static FieldInfo hiddenLeft;

	private static FieldInfo rightItem;

	private static FieldInfo leftItem;

	public SwimmingToolsFeature(ConfigFile config)
		: base(config, "SwimmingTools", "Use tools while swimming", "Build, repair, chop and mine while swimming with hammers, hoes, cultivators, axes and pickaxes. Other weapons stay restricted; normal costs and swim stamina apply.")
	{
	}

	public override void Initialize()
	{
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Expected O, but got Unknown
		instance = this;
		hiddenRight = AccessTools.Field(typeof(Humanoid), "m_hiddenRightItem");
		hiddenLeft = AccessTools.Field(typeof(Humanoid), "m_hiddenLeftItem");
		rightItem = AccessTools.Field(typeof(Humanoid), "m_rightItem");
		leftItem = AccessTools.Field(typeof(Humanoid), "m_leftItem");
		if (hiddenRight == null || hiddenLeft == null || rightItem == null || leftItem == null)
		{
			throw new MissingMemberException("Hidden equipment fields changed.");
		}
		harmony = new Harmony("benowy.qol.swimmingtools");
		Patch(typeof(Humanoid), "EquipItem", "EquipCheck");
		Patch(typeof(Humanoid), "UpdateEquipment", "EquipmentCheck");
		Patch(typeof(Player), "Update", "RestoreCheck");
	}

	private void Patch(Type type, string name, string patch)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.Method(type, name, (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			throw new MissingMethodException(type.Name, name);
		}
		harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SwimmingToolsFeature), patch, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static bool Active(Character character)
	{
		if (instance != null && instance.Running && instance.Enabled && Object.op_Implicit((Object)(object)character))
		{
			return (Object)(object)character == (Object)(object)Player.m_localPlayer;
		}
		return false;
	}

	private static bool Allowed(ItemData item)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Invalid comparison between Unknown and I4
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Invalid comparison between Unknown and I4
		if (item == null || item.m_shared == null)
		{
			return false;
		}
		if (!Object.op_Implicit((Object)(object)item.m_shared.m_buildPieces) && (int)item.m_shared.m_skillType != 7)
		{
			return (int)item.m_shared.m_skillType == 12;
		}
		return true;
	}

	private static bool HandsAllowed(Humanoid human, bool hidden)
	{
		//IL_0020: 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)
		//IL_0026: Expected O, but got Unknown
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Expected O, but got Unknown
		ItemData val = (hidden ? ((ItemData)hiddenRight.GetValue(human)) : ((ItemData)rightItem.GetValue(human)));
		ItemData val2 = (hidden ? ((ItemData)hiddenLeft.GetValue(human)) : ((ItemData)leftItem.GetValue(human)));
		if ((val != null || val2 != null) && (val == null || Allowed(val)))
		{
			if (val2 != null)
			{
				return Allowed(val2);
			}
			return true;
		}
		return false;
	}

	private static bool EquipSwimming(Character character, ItemData item)
	{
		if (character.IsSwimming())
		{
			if (Active(character))
			{
				return !Allowed(item);
			}
			return true;
		}
		return false;
	}

	private static bool EquipmentSwimming(Character character)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected O, but got Unknown
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Expected O, but