Decompiled source of OneHand v0.2.3

BepInEx/plugins/OneHand/OneHand.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("OneHand")]
[assembly: AssemblyDescription("One-hand challenge for White Knuckle Beta 0.61")]
[assembly: AssemblyFileVersion("0.2.3.0")]
[assembly: AssemblyInformationalVersion("0.2.3-beta.061")]
[assembly: AssemblyVersion("0.2.3.0")]
namespace WhiteKnuckle.OneHand;

internal sealed class GameSlotStore : IQuickSlotStore<Item>
{
	private sealed class PocketDefaults
	{
		internal int Capacity;

		internal int Large;

		internal bool NonPouchable;
	}

	private readonly OneHandSession session;

	private readonly OneHandPlugin plugin;

	private readonly Dictionary<Pouch, PocketDefaults> originals = new Dictionary<Pouch, PocketDefaults>();

	internal Inventory Inventory
	{
		get
		{
			if (!((Object)(object)session.Player != (Object)null))
			{
				return null;
			}
			return ENT_Player.GetInventory();
		}
	}

	public Item Held
	{
		get
		{
			Inventory inventory = Inventory;
			if (!((Object)(object)inventory != (Object)null) || inventory.itemHands == null || inventory.itemHands.Length <= session.HandId)
			{
				return null;
			}
			return inventory.itemHands[session.HandId].currentItem;
		}
	}

	public bool CanEquip
	{
		get
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			ENT_Player player = session.Player;
			if ((Object)(object)player == (Object)null || player.hands == null || player.hands.Length < 2)
			{
				return false;
			}
			Hand val = player.hands[session.HandId];
			if (!val.IsLocked() && !val.lockHand && (int)val.interactState == 0 && val.currentInteract == null)
			{
				return (Object)(object)val.grabTarget == (Object)null;
			}
			return false;
		}
	}

	internal GameSlotStore(OneHandSession session, OneHandPlugin plugin)
	{
		this.session = session;
		this.plugin = plugin;
	}

	internal Pouch Pocket(int slot)
	{
		Inventory inventory = Inventory;
		if (!((Object)(object)inventory != (Object)null) || inventory.pockets == null || slot < 0 || slot >= 2 || inventory.pockets.Count <= slot)
		{
			return null;
		}
		return inventory.pockets[slot].pouch;
	}

	public Item Peek(int slot)
	{
		Pouch val = Pocket(slot);
		if (val == null)
		{
			return null;
		}
		foreach (Item pouchItem in val.pouchItems)
		{
			if (pouchItem != null && !pouchItem.hasBeenDestroyed)
			{
				return pouchItem;
			}
		}
		return null;
	}

	internal bool IsQuickPocket(Pouch pouch)
	{
		if (session.Active)
		{
			if (pouch != Pocket(0))
			{
				return pouch == Pocket(1);
			}
			return true;
		}
		return false;
	}

	internal void ConfigurePockets()
	{
		if (!session.Active)
		{
			return;
		}
		for (int i = 0; i < 2; i++)
		{
			Pouch val = Pocket(i);
			if (val != null)
			{
				if (!originals.ContainsKey(val))
				{
					originals[val] = new PocketDefaults
					{
						Capacity = val.maxCapacity,
						Large = val.maxLargeCapacity,
						NonPouchable = val.allowNonPouchable
					};
				}
				val.maxCapacity = Math.Max(1, val.maxCapacity);
				val.maxLargeCapacity = Math.Max(1, val.maxLargeCapacity);
				val.allowNonPouchable = true;
			}
		}
	}

	internal void RestorePockets()
	{
		foreach (KeyValuePair<Pouch, PocketDefaults> original in originals)
		{
			original.Key.maxCapacity = original.Value.Capacity;
			original.Key.maxLargeCapacity = original.Value.Large;
			original.Key.allowNonPouchable = original.Value.NonPouchable;
		}
		originals.Clear();
	}

	internal int FindPocket(Item item)
	{
		if (item == null)
		{
			return -1;
		}
		for (int i = 0; i < 2; i++)
		{
			if (Pocket(i) != null && Pocket(i).pouchItems.Contains(item))
			{
				return i;
			}
		}
		return -1;
	}

	private void DetachFromPouches(Item item)
	{
		foreach (Pouch allPouch in Inventory.GetAllPouches())
		{
			if (allPouch.pouchItems.Contains(item))
			{
				allPouch.RemoveItemFromPouch(item);
			}
		}
	}

	private void PutInPocket(Item item, int slot)
	{
		if (slot < 0 || slot > 1 || item == null || item.hasBeenDestroyed)
		{
			return;
		}
		ConfigurePockets();
		Pouch val = Pocket(slot);
		if (val != null && !val.pouchItems.Contains(item) && Peek(slot) == null)
		{
			DetachFromPouches(item);
			if (val.CanAddItemToPouch(item))
			{
				val.AddItemToPouch(item, true);
			}
		}
	}

	public bool Stow(Item item, int slot)
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		Inventory inventory = Inventory;
		if ((Object)(object)inventory == (Object)null || Held != item || (Object)(object)item.GetHandItem() == (Object)null)
		{
			return false;
		}
		item.GetHandItem().OnTryPocket();
		Vector3 val = ((slot >= 0 && inventory.pockets.Count > slot) ? inventory.pockets[slot].transform.position : (((Component)session.Player.cam).transform.position + ((Component)session.Player.cam).transform.forward));
		if (!inventory.DropItemFromHand(val, session.HandId, true, false, true))
		{
			return false;
		}
		if (Held == item)
		{
			return false;
		}
		if (item.hasBeenDestroyed)
		{
			return true;
		}
		DetachFromPouches(item);
		PutInPocket(item, slot);
		return true;
	}

	public bool Equip(Item item, int slot)
	{
		Inventory inventory = Inventory;
		if ((Object)(object)inventory == (Object)null || item == null || item.hasBeenDestroyed || Held != null || !CanEquip)
		{
			return false;
		}
		try
		{
			inventory.AddItemToHand(item, session.Player.hands[session.HandId]);
			return Held == item;
		}
		catch (Exception ex)
		{
			plugin.Warn("Could not equip quick-slot item: " + ex);
			return false;
		}
	}

	internal bool BeforeAdd(Item item, ref Hand hand, out int sourceSlot)
	{
		sourceSlot = FindPocket(item);
		if (item == null || !session.Active || hand == null)
		{
			return true;
		}
		int id = hand.id;
		hand = session.Player.hands[session.HandId];
		if (Held == item)
		{
			sourceSlot = session.Slots.EquippedSlot;
			return false;
		}
		if (Held == null)
		{
			return true;
		}
		if (!Inventory.bagItems.Contains(item))
		{
			Inventory.AddItemToInventoryCenter(item);
		}
		PutInPocket(item, id);
		return false;
	}

	internal void AfterAdd(Item item, int sourceSlot)
	{
		if (session.Active && Held == item)
		{
			session.Slots.Observe(item, sourceSlot);
		}
	}

	internal void MoveExcludedItem()
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: 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)
		Inventory inventory = Inventory;
		if ((Object)(object)inventory == (Object)null)
		{
			return;
		}
		Item currentItem = inventory.itemHands[session.OtherId].currentItem;
		if (currentItem != null)
		{
			Vector3 val = ((Component)session.Player.cam).transform.position + ((Component)session.Player.cam).transform.forward;
			if ((Object)(object)currentItem.GetHandItem() != (Object)null && inventory.DropItemFromHand(val, session.OtherId, true, false, true))
			{
				PutInPocket(currentItem, session.OtherId);
			}
			else if ((Object)(object)currentItem.GetHandItem() != (Object)null)
			{
				((Component)currentItem.GetHandItem()).gameObject.SetActive(false);
			}
		}
	}
}
internal sealed class HandChoiceSound
{
	private const string ResourceName = "WhiteKnuckle.OneHand.HandChoice.wav";

	private readonly OneHandPlugin plugin;

	private AudioClip clip;

	private AudioSource source;

	private int startedFrame;

	internal HandChoiceSound(OneHandPlugin plugin)
	{
		this.plugin = plugin;
	}

	internal void Play()
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Expected O, but got Unknown
		try
		{
			if ((Object)(object)clip == (Object)null)
			{
				clip = LoadClip();
			}
			Stop();
			GameObject val = new GameObject("OneHand.ChoiceAudio");
			Object.DontDestroyOnLoad((Object)(object)val);
			source = val.AddComponent<AudioSource>();
			source.playOnAwake = false;
			source.loop = false;
			source.spatialBlend = 0f;
			source.volume = 1f;
			source.pitch = 1f;
			source.ignoreListenerPause = true;
			source.bypassReverbZones = true;
			source.reverbZoneMix = 0f;
			if ((Object)(object)AudioManager.instance != (Object)null)
			{
				source.outputAudioMixerGroup = AudioManager.instance.UIMixer;
			}
			source.clip = clip;
			startedFrame = Time.frameCount;
			source.Play();
		}
		catch (Exception ex)
		{
			Stop();
			plugin.Warn("Could not play the hand-choice sound; starting without it. " + ex);
		}
	}

	private static AudioClip LoadClip()
	{
		using Stream stream = typeof(HandChoiceSound).Assembly.GetManifestResourceStream("WhiteKnuckle.OneHand.HandChoice.wav");
		if (stream == null)
		{
			throw new InvalidDataException("Embedded hand-choice WAV is missing.");
		}
		using BinaryReader binaryReader = new BinaryReader(stream, Encoding.ASCII);
		if (Tag(binaryReader) != "RIFF")
		{
			throw new InvalidDataException("Not a RIFF file.");
		}
		binaryReader.ReadUInt32();
		if (Tag(binaryReader) != "WAVE")
		{
			throw new InvalidDataException("Not a WAV file.");
		}
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		byte[] array = null;
		while (stream.Position + 8 <= stream.Length)
		{
			string text = Tag(binaryReader);
			int num6 = checked((int)binaryReader.ReadUInt32());
			long num7 = stream.Position + num6;
			if (num7 > stream.Length)
			{
				throw new InvalidDataException("Truncated WAV chunk.");
			}
			if (text == "fmt " && num6 >= 16)
			{
				num = binaryReader.ReadUInt16();
				num2 = binaryReader.ReadUInt16();
				num3 = checked((int)binaryReader.ReadUInt32());
				binaryReader.ReadUInt32();
				num5 = binaryReader.ReadUInt16();
				num4 = binaryReader.ReadUInt16();
			}
			else if (text == "data")
			{
				array = binaryReader.ReadBytes(num6);
			}
			stream.Position = num7 + (num6 & 1);
		}
		if (num != 1 || num4 != 24 || num2 < 1 || num2 > 2 || num3 <= 0 || num5 != num2 * 3 || array == null || array.Length == 0 || array.Length % num5 != 0)
		{
			throw new InvalidDataException("Expected the embedded 24-bit PCM hand-choice WAV.");
		}
		float[] array2 = new float[array.Length / 3];
		int num8 = 0;
		int num9 = 0;
		while (num8 < array2.Length)
		{
			int num10 = array[num9] | (array[num9 + 1] << 8) | (array[num9 + 2] << 16);
			if ((num10 & 0x800000) != 0)
			{
				num10 -= 16777216;
			}
			array2[num8] = (float)num10 / 8388608f;
			num8++;
			num9 += 3;
		}
		AudioClip val = AudioClip.Create("OneHand.HandChoice", array2.Length / num2, num2, num3, false);
		if (!val.SetData(array2, 0))
		{
			Object.Destroy((Object)(object)val);
			throw new InvalidDataException("Unity could not fill the hand-choice AudioClip.");
		}
		return val;
	}

	private static string Tag(BinaryReader reader)
	{
		return Encoding.ASCII.GetString(reader.ReadBytes(4));
	}

	internal void Tick()
	{
		if ((Object)(object)source != (Object)null && Time.frameCount > startedFrame && !source.isPlaying)
		{
			Stop();
		}
	}

	internal void Stop()
	{
		if ((Object)(object)source != (Object)null)
		{
			source.Stop();
			Object.Destroy((Object)(object)((Component)source).gameObject);
		}
		source = null;
	}

	internal void Dispose()
	{
		Stop();
		if ((Object)(object)clip != (Object)null)
		{
			Object.Destroy((Object)(object)clip);
		}
		clip = null;
	}
}
public enum SelectedHand
{
	Left,
	Right
}
[BepInPlugin("ilya.whiteknuckle.onehand", "OneHand", "0.2.3")]
public sealed class OneHandPlugin : BaseUnityPlugin
{
	public const string Id = "ilya.whiteknuckle.onehand";

	public const string Version = "0.2.3";

	internal static OneHandPlugin Instance;

	internal ConfigEntry<bool> ModeEnabled;

	internal ConfigEntry<SelectedHand> HandChoice;

	internal ConfigEntry<KeyCode> SlotOneKey;

	internal ConfigEntry<KeyCode> SlotTwoKey;

	internal OneHandSession Session;

	private Harmony harmony;

	private OneHandUi ui;

	private int lastSlotFrame = -1;

	private Item deferredItem;

	private int deferredSlot = -1;

	private string notice = "";

	private float noticeUntil;

	internal bool PatchesReady { get; private set; }

	internal string Notice
	{
		get
		{
			if (!(Time.unscaledTime < noticeUntil))
			{
				return "";
			}
			return notice;
		}
	}

	private void Awake()
	{
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Expected O, but got Unknown
		Instance = this;
		ModeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Run", "Enabled", true, "Enable OneHand for the next run. Locked during a run; saved OneHand runs retain their choice.");
		HandChoice = ((BaseUnityPlugin)this).Config.Bind<SelectedHand>("Run", "Hand", SelectedHand.Right, "Last remaining hand: Left or Right. A fresh run asks after Play; saved runs retain their choice.");
		SlotOneKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Slot1", (KeyCode)49, "Additional shortcut for quick slot 1. Native pocket controls also work.");
		SlotTwoKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "Slot2", (KeyCode)50, "Additional shortcut for quick slot 2. Native pocket controls also work.");
		Session = new OneHandSession(this);
		ui = new OneHandUi(this);
		harmony = new Harmony("ilya.whiteknuckle.onehand");
		try
		{
			harmony.PatchAll(typeof(OneHandPlugin).Assembly);
			PatchesReady = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"OneHand 0.2.3 patches installed. Built against the supplied White Knuckle Beta 0.61 assemblies.");
		}
		catch (Exception ex)
		{
			harmony.UnpatchSelf();
			((BaseUnityPlugin)this).Logger.LogError((object)("OneHand could not install its patches; the challenge is disabled. " + ex));
			Tell("OneHand could not load. See LogOutput.log.", 30f);
		}
	}

	private void Update()
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		if (ui != null)
		{
			ui.Tick();
		}
		if (!PatchesReady)
		{
			return;
		}
		Session.Tick();
		if (Session.CanUseControls)
		{
			if (Input.GetKeyDown(SlotOneKey.Value))
			{
				RequestSlot(0);
			}
			else if (Input.GetKeyDown(SlotTwoKey.Value))
			{
				RequestSlot(1);
			}
		}
	}

	private void LateUpdate()
	{
		if (PatchesReady)
		{
			Session.HideOtherHand();
		}
		if (ui != null)
		{
			ui.Animate();
		}
	}

	internal bool BeforeStart(UI_GamemodeScreen screen)
	{
		if (ui != null)
		{
			return ui.BeforeStart(screen);
		}
		return true;
	}

	internal void RequestSlot(int slot, bool deferred = false)
	{
		if (!Session.CanUseControls || (!deferred && lastSlotFrame == Time.frameCount))
		{
			return;
		}
		lastSlotFrame = Time.frameCount;
		try
		{
			SlotResult slotResult = Session.Slots.Activate(slot);
			switch (slotResult)
			{
			case SlotResult.Busy:
				deferredItem = Session.Store.Held;
				deferredSlot = ((deferredItem != null) ? slot : (-1));
				Tell("Let go of the hold or finish using the item first.");
				break;
			case SlotResult.Empty:
				Tell("Empty slot. Pick up an item and press this slot's key to store it.", 4f);
				break;
			case SlotResult.EquipFailed:
				Tell("Could not equip the item. It is still in your inventory.");
				break;
			}
			if (slotResult != SlotResult.Busy)
			{
				deferredItem = null;
				deferredSlot = -1;
			}
			Session.StoreRunFlags();
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Quick-slot operation failed: " + ex));
			Tell("Could not switch items. See LogOutput.log.");
		}
	}

	internal void SwapSlot()
	{
		Session.Slots.Reconcile();
		RequestSlot((Session.Slots.EquippedSlot >= 0) ? (1 - Session.Slots.EquippedSlot) : ((Session.Store.Peek(0) == null) ? 1 : 0));
	}

	internal void Tell(string text, float seconds = 3f)
	{
		notice = text;
		noticeUntil = Time.unscaledTime + seconds;
	}

	internal void Warn(string text)
	{
		((BaseUnityPlugin)this).Logger.LogWarning((object)text);
	}

	internal void OnRunStarted()
	{
		if (ui != null)
		{
			ui.OnRunStarted();
		}
	}

	internal int TakeDeferredSlot(Item item, int fallback)
	{
		int result = ((item == deferredItem && deferredSlot >= 0) ? deferredSlot : fallback);
		deferredItem = null;
		deferredSlot = -1;
		return result;
	}

	internal IEnumerator FinishPiton(HandItem_Piton piton)
	{
		yield return null;
		yield return null;
		if ((Object)(object)piton != (Object)null && Session.Active && Session.IsChosen(((Hand_Base)piton).hand) && ((HandItem)piton).used && Session.Store.Held == ((HandItem)piton).item)
		{
			((HandItem)piton).RemoveItem();
			Session.Slots.Observe(null, -1);
			Tell("Piton placed. Take out your hammer and secure it.");
		}
	}

	private void OnDestroy()
	{
		if (Session != null)
		{
			Session.Restore();
		}
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		if (ui != null)
		{
			ui.Dispose();
		}
		if (Instance == this)
		{
			Instance = null;
		}
	}
}
internal static class OneHandRules
{
	public const float Multiplier = 2.5f;

	public static float Gain(float amount)
	{
		if (!(amount > 0f))
		{
			return amount;
		}
		return amount * 2.5f;
	}

	public static float Tick(float before, float proposed, float capacity)
	{
		if (proposed <= before)
		{
			return proposed;
		}
		return Math.Min(capacity, before + (proposed - before) * 2.5f);
	}
}
internal interface IQuickSlotStore<T> where T : class
{
	T Held { get; }

	bool CanEquip { get; }

	T Peek(int slot);

	bool Stow(T item, int slot);

	bool Equip(T item, int slot);
}
internal enum SlotResult
{
	Empty,
	Equipped,
	Stored,
	Busy,
	EquipFailed,
	Invalid
}
internal sealed class QuickSlots<T> where T : class
{
	private readonly IQuickSlotStore<T> store;

	private T tracked;

	public int EquippedSlot { get; private set; } = -1;

	public bool InTransaction { get; private set; }

	public QuickSlots(IQuickSlotStore<T> store)
	{
		this.store = store;
	}

	public void Observe(T item, int slot)
	{
		tracked = item;
		EquippedSlot = ((item == null || slot < 0 || slot > 1) ? (-1) : slot);
	}

	public void Reconcile()
	{
		if (tracked != store.Held)
		{
			Observe(store.Held, -1);
		}
	}

	public SlotResult Activate(int slot)
	{
		if (slot < 0 || slot > 1)
		{
			return SlotResult.Invalid;
		}
		if (InTransaction)
		{
			return SlotResult.Busy;
		}
		Reconcile();
		T held = store.Held;
		T val = store.Peek(slot);
		if (held == null && val == null)
		{
			return SlotResult.Empty;
		}
		if (held == null && !store.CanEquip)
		{
			return SlotResult.Busy;
		}
		InTransaction = true;
		try
		{
			if (held != null)
			{
				int num = EquippedSlot;
				if (num < 0)
				{
					num = ((val == null) ? slot : ((store.Peek(1 - slot) == null) ? (1 - slot) : (-1)));
				}
				if (num >= 0 && store.Peek(num) != null)
				{
					num = -1;
				}
				if (!store.Stow(held, num))
				{
					return SlotResult.Busy;
				}
				Observe(null, -1);
			}
			if (val == null || val == held)
			{
				return SlotResult.Stored;
			}
			if (!store.Equip(val, slot))
			{
				return SlotResult.EquipFailed;
			}
			Observe(val, slot);
			return SlotResult.Equipped;
		}
		finally
		{
			InTransaction = false;
		}
	}
}
internal sealed class OneHandSession
{
	internal const string RunFlag = "onehand.v1.run";

	internal const string SlotFlag = "onehand.v1.slot";

	private static readonly Func<ENT_Player, bool> InCutscene = (Func<ENT_Player, bool>)Delegate.CreateDelegate(typeof(Func<ENT_Player, bool>), AccessTools.Method(typeof(ENT_Player), "IsInCutsceneMode", (Type[])null, (Type[])null));

	private readonly OneHandPlugin plugin;

	private readonly Dictionary<GameObject, bool> hiddenObjects = new Dictionary<GameObject, bool>();

	private readonly Dictionary<Behaviour, bool> hiddenUi = new Dictionary<Behaviour, bool>();

	private float originalCapacity;

	private bool started;

	private int loadedSlot = -1;

	internal ENT_Player Player { get; private set; }

	internal bool Active { get; private set; }

	internal int HandId { get; private set; }

	internal int OtherId => 1 - HandId;

	internal GameSlotStore Store { get; private set; }

	internal QuickSlots<Item> Slots { get; private set; }

	internal bool HasPlayer => (Object)(object)Player != (Object)null;

	internal bool CanUseControls
	{
		get
		{
			if (Active && started && (Object)(object)Player != (Object)null && !((GameEntity)Player).dead && (Object)(object)CL_GameManager.gMan != (Object)null && !CL_GameManager.IsLoading() && !CL_GameManager.gMan.isPaused && !Player.IsInputLocked() && !Player.IsInventoryLocked() && !InCutscene(Player) && !OS_Manager.inUse && !CommandConsole.IsConsoleVisible() && !CL_GameManager.gMan.lockPlayer)
			{
				return !CL_GameManager.gMan.freecam;
			}
			return false;
		}
	}

	internal OneHandSession(OneHandPlugin plugin)
	{
		this.plugin = plugin;
		Store = new GameSlotStore(this, plugin);
		Slots = new QuickSlots<Item>(Store);
	}

	internal void Attach(ENT_Player player)
	{
		if (Player != player)
		{
			Restore();
			Player = player;
			originalCapacity = player.gripStrengthTimer;
			HandId = (int)plugin.HandChoice.Value;
			Active = plugin.PatchesReady && plugin.ModeEnabled.Value;
			started = false;
			Slots.Observe(null, -1);
			ApplyCapacity();
		}
	}

	internal void Started(ENT_Player player)
	{
		if (Player != player)
		{
			Attach(player);
		}
		started = true;
		plugin.OnRunStarted();
		Store.ConfigurePockets();
		HideOtherHand();
		if (!CL_SaveManager.loadSessionSaveOnLoad)
		{
			StoreRunFlags();
		}
	}

	private void ApplyCapacity()
	{
		if ((Object)(object)Player == (Object)null)
		{
			return;
		}
		float gripStrengthTimer = Player.gripStrengthTimer;
		float num = originalCapacity * (Active ? 2.5f : 1f);
		Player.gripStrengthTimer = num;
		if (!started || !(gripStrengthTimer > 0f) || Player.hands == null)
		{
			return;
		}
		Hand[] hands = Player.hands;
		foreach (Hand val in hands)
		{
			if (val != null)
			{
				val.gripStrength *= num / gripStrengthTimer;
			}
		}
	}

	internal bool IsChosen(Hand hand)
	{
		if (Active && (Object)(object)Player != (Object)null && Player.hands != null && Player.hands.Length > HandId)
		{
			return Player.hands[HandId] == hand;
		}
		return false;
	}

	internal bool IsExcluded(Hand hand)
	{
		if (Active && (Object)(object)Player != (Object)null && Player.hands != null && Player.hands.Length > OtherId)
		{
			return Player.hands[OtherId] == hand;
		}
		return false;
	}

	internal bool IsOwner(ENT_Player player)
	{
		if (Active)
		{
			return Player == player;
		}
		return false;
	}

	internal bool IsInventory(Inventory inventory)
	{
		if (Active)
		{
			return Store.Inventory == inventory;
		}
		return false;
	}

	internal void Tick()
	{
		if ((Object)(object)Player == (Object)null)
		{
			if (Active || started)
			{
				Restore();
			}
		}
		else if (Active && started)
		{
			Store.ConfigurePockets();
			Slots.Reconcile();
		}
	}

	internal void HideOtherHand()
	{
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		if (Active && started && !((Object)(object)Player == (Object)null) && Player.hands != null && Player.hands.Length >= 2)
		{
			Hand val = Player.hands[OtherId];
			Hide(((Object)(object)val.handModel != (Object)null) ? ((Component)val.handModel).gameObject : null);
			Hide(((Object)(object)val.handSprite != (Object)null) ? ((Component)val.handSprite).gameObject : null);
			Hide(((Object)(object)val.handForegroundSprite != (Object)null) ? ((Component)val.handForegroundSprite).gameObject : null);
			Hide(((Object)(object)val.handBase != (Object)null) ? ((Component)val.handBase).gameObject : null);
			Hide(((Object)(object)val.accessoryBase != (Object)null) ? ((Component)val.accessoryBase).gameObject : null);
			HideUi((Behaviour)(object)val.uiInteract);
			HideUi((Behaviour)(object)val.uiInteractOverlay);
			if ((int)val.interactState != 0 || val.currentInteract != null || (Object)(object)val.grabTarget != (Object)null)
			{
				Player.StopInteraction(OtherId, "", false);
			}
			Inventory inventory = Store.Inventory;
			if ((Object)(object)inventory != (Object)null && inventory.itemHands != null && inventory.itemHands.Length > OtherId && inventory.itemHands[OtherId].currentItem != null)
			{
				Store.MoveExcludedItem();
			}
		}
	}

	private void Hide(GameObject obj)
	{
		if (!((Object)(object)obj == (Object)null))
		{
			if (!hiddenObjects.ContainsKey(obj))
			{
				hiddenObjects[obj] = obj.activeSelf;
			}
			if (obj.activeSelf)
			{
				obj.SetActive(false);
			}
		}
	}

	private void HideUi(Behaviour obj)
	{
		if (!((Object)(object)obj == (Object)null))
		{
			if (!hiddenUi.ContainsKey(obj))
			{
				hiddenUi[obj] = obj.enabled;
			}
			obj.enabled = false;
		}
	}

	private void RestoreVisuals()
	{
		foreach (KeyValuePair<GameObject, bool> hiddenObject in hiddenObjects)
		{
			if ((Object)(object)hiddenObject.Key != (Object)null)
			{
				hiddenObject.Key.SetActive(hiddenObject.Value);
			}
		}
		foreach (KeyValuePair<Behaviour, bool> item in hiddenUi)
		{
			if ((Object)(object)item.Key != (Object)null)
			{
				item.Key.enabled = item.Value;
			}
		}
		hiddenObjects.Clear();
		hiddenUi.Clear();
	}

	internal void StoreRunFlags()
	{
		if (!((Object)(object)Player == (Object)null) && !((Object)(object)CL_GameManager.gMan == (Object)null) && started)
		{
			CL_GameManager.SetGameFlag("onehand.v1.run", Active, HandId.ToString(), false, false);
			CL_GameManager.SetGameFlag("onehand.v1.slot", Active, Slots.EquippedSlot.ToString(), false, false);
		}
	}

	internal void ReadSavedRun(SessionSave save)
	{
		if (save == null || save.sessionFlags == null || (Object)(object)Player == (Object)null)
		{
			return;
		}
		SessionFlag val = save.sessionFlags.Find((SessionFlag f) => f.name == "onehand.v1.run");
		SessionFlag val2 = save.sessionFlags.Find((SessionFlag f) => f.name == "onehand.v1.slot");
		loadedSlot = -1;
		if (val2 != null && int.TryParse(val2.data, out var result) && result >= 0 && result <= 1)
		{
			loadedSlot = result;
		}
		if (val != null)
		{
			RestoreVisuals();
			Active = plugin.PatchesReady && val.state;
			if (int.TryParse(val.data, out var result2) && (result2 == 0 || result2 == 1))
			{
				HandId = result2;
			}
			ApplyCapacity();
			Slots.Observe(null, -1);
			if (Active)
			{
				Store.ConfigurePockets();
			}
			else
			{
				Store.RestorePockets();
			}
		}
	}

	internal void InventoryLoaded()
	{
		if (Active)
		{
			Store.ConfigurePockets();
			Slots.Observe(Store.Held, loadedSlot);
			loadedSlot = -1;
			HideOtherHand();
		}
	}

	internal void Restore()
	{
		RestoreVisuals();
		if ((Object)(object)Player != (Object)null && Active)
		{
			float gripStrengthTimer = Player.gripStrengthTimer;
			Player.gripStrengthTimer = originalCapacity;
			if (started && gripStrengthTimer > 0f && Player.hands != null)
			{
				Hand[] hands = Player.hands;
				foreach (Hand val in hands)
				{
					if (val != null)
					{
						val.gripStrength *= originalCapacity / gripStrengthTimer;
					}
				}
			}
		}
		Store.RestorePockets();
		Active = false;
		started = false;
		Player = null;
		loadedSlot = -1;
		Slots.Observe(null, -1);
	}
}
internal sealed class OneHandUi
{
	private readonly OneHandPlugin plugin;

	private readonly HandChoiceSound choiceSound;

	private readonly RunStartGate start = new RunStartGate();

	private readonly List<Selectable> blockedControls = new List<Selectable>();

	private readonly Button[] buttons = (Button[])(object)new Button[2];

	private readonly HandChoiceLetterMotion[] motion = new HandChoiceLetterMotion[2];

	private const float ConfirmationHold = 0.2f;

	private const float ConfirmationFade = 1.5f;

	private HandChoiceLetterMotion confirmationMotion;

	private float confirmationStarted;

	private AsyncOperation sceneLoad;

	private Button backButton;

	private TextMeshProUGUI backText;

	private GameObject root;

	private GameObject content;

	private GameObject previousSelection;

	private GameObject noticeRoot;

	private EventSystem eventSystem;

	private UI_GamemodeScreen screen;

	private TMP_FontAsset font;

	private Material fontMaterial;

	private TextMeshProUGUI noticeText;

	private bool closing;

	private bool keyboardNavigation;

	private bool capturedCursor;

	private bool oldShowCursor;

	private CursorLockMode oldCursorLock;

	private int hovered = -1;

	private int openedFrame;

	private Vector3 previousMouse;

	internal OneHandUi(OneHandPlugin plugin)
	{
		this.plugin = plugin;
		choiceSound = new HandChoiceSound(plugin);
		SceneManager.sceneLoaded += SceneLoaded;
	}

	internal bool BeforeStart(UI_GamemodeScreen target)
	{
		if (sceneLoad != null && !sceneLoad.isDone)
		{
			return false;
		}
		if (start.Resuming)
		{
			M_Gamemode gamemode = target.GetGamemode();
			if ((Object)(object)gamemode == (Object)null)
			{
				throw new InvalidOperationException("No gamemode selected.");
			}
			CL_GameManager.gamemode = gamemode;
			sceneLoad = SceneManager.LoadSceneAsync(gamemode.gamemodeScene);
			if (sceneLoad == null)
			{
				throw new InvalidOperationException("Scene loading did not start.");
			}
			sceneLoad.allowSceneActivation = false;
			if ((Object)(object)target.loadingText != (Object)null)
			{
				target.loadingText.SetActive(true);
			}
			return false;
		}
		bool waiting = start.Waiting;
		if (!start.Hold(plugin.PatchesReady && plugin.ModeEnabled.Value, CL_SaveManager.loadSessionSaveOnLoad, delegate
		{
			target.LoadGamemode();
		}))
		{
			RememberFont(target);
			return true;
		}
		if (waiting)
		{
			return false;
		}
		try
		{
			screen = target;
			RememberFont(target);
			OpenSelection();
			return false;
		}
		catch (Exception ex)
		{
			CloseSelection(restoreFocus: true);
			plugin.Warn("Could not open the hand selector; using the configured hand. " + ex);
			return true;
		}
	}

	private void RememberFont(UI_GamemodeScreen target)
	{
		if ((Object)(object)target == (Object)null)
		{
			return;
		}
		UI_GamemodeText[] componentsInChildren = ((Component)target).GetComponentsInChildren<UI_GamemodeText>(true);
		foreach (UI_GamemodeText val in componentsInChildren)
		{
			if (((Component)val).gameObject.activeInHierarchy && UseFont(((Component)val).GetComponent<TMP_Text>()))
			{
				return;
			}
		}
		TMP_Text[] componentsInChildren2 = ((Component)target).GetComponentsInChildren<TMP_Text>(true);
		foreach (TMP_Text val2 in componentsInChildren2)
		{
			if (((Component)val2).gameObject.activeInHierarchy && UseFont(val2))
			{
				break;
			}
		}
	}

	private bool UseFont(TMP_Text source)
	{
		if ((Object)(object)source == (Object)null || (Object)(object)source.font == (Object)null)
		{
			return false;
		}
		font = source.font;
		fontMaterial = source.fontSharedMaterial;
		return true;
	}

	private bool EnsureFont()
	{
		if ((Object)(object)font != (Object)null)
		{
			return true;
		}
		TMP_Text[] array = Object.FindObjectsByType<TMP_Text>((FindObjectsSortMode)0);
		foreach (TMP_Text source in array)
		{
			if (UseFont(source))
			{
				return true;
			}
		}
		font = TMP_Settings.defaultFontAsset;
		if ((Object)(object)font == (Object)null)
		{
			return false;
		}
		fontMaterial = ((TMP_Asset)font).material;
		return true;
	}

	private static GameObject CanvasRoot(string name, int order)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Expected O, but got Unknown
		GameObject val = new GameObject(name, new Type[3]
		{
			typeof(RectTransform),
			typeof(Canvas),
			typeof(CanvasScaler)
		});
		Canvas component = val.GetComponent<Canvas>();
		component.renderMode = (RenderMode)0;
		component.sortingOrder = order;
		CanvasScaler component2 = val.GetComponent<CanvasScaler>();
		component2.uiScaleMode = (ScaleMode)1;
		component2.referenceResolution = new Vector2(1920f, 1080f);
		component2.matchWidthOrHeight = 0.5f;
		return val;
	}

	private static RectTransform Rect(string name, Transform parent, Vector2 size, Vector2 position)
	{
		//IL_0014: 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_0023: 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_002c: 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)
		//IL_003f: 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_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: 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_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Expected O, but got Unknown
		RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform;
		((Transform)val).SetParent(parent, false);
		Vector2 val2 = default(Vector2);
		((Vector2)(ref val2))..ctor(0.5f, 0.5f);
		val.pivot = val2;
		Vector2 anchorMin = (val.anchorMax = val2);
		val.anchorMin = anchorMin;
		val.sizeDelta = size;
		val.anchoredPosition = position;
		return val;
	}

	private TextMeshProUGUI Text(Transform parent, string value, float size, Vector2 bounds, Vector2 position)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0004: 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)
		TextMeshProUGUI val = ((Component)Rect(value, parent, bounds, position)).gameObject.AddComponent<TextMeshProUGUI>();
		((TMP_Text)val).font = font;
		if ((Object)(object)fontMaterial != (Object)null)
		{
			((TMP_Text)val).fontSharedMaterial = fontMaterial;
		}
		((TMP_Text)val).text = value;
		((TMP_Text)val).fontSize = size;
		((TMP_Text)val).fontStyle = (FontStyles)0;
		((Graphic)val).color = Color.white;
		((TMP_Text)val).alignment = (TextAlignmentOptions)514;
		((TMP_Text)val).enableAutoSizing = false;
		((TMP_Text)val).textWrappingMode = (TextWrappingModes)0;
		((TMP_Text)val).overflowMode = (TextOverflowModes)0;
		((Graphic)val).raycastTarget = false;
		return val;
	}

	private void OpenSelection()
	{
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0322: Unknown result type (might be due to invalid IL or missing references)
		//IL_032c: Expected O, but got Unknown
		//IL_0343: Unknown result type (might be due to invalid IL or missing references)
		//IL_0348: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0411: Unknown result type (might be due to invalid IL or missing references)
		//IL_043d: Unknown result type (might be due to invalid IL or missing references)
		//IL_045e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0463: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b0: 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_01eb: Expected O, but got Unknown
		//IL_0218: Unknown result type (might be due to invalid IL or missing references)
		//IL_021d: Unknown result type (might be due to invalid IL or missing references)
		if (!EnsureFont())
		{
			throw new InvalidOperationException("No native TextMeshPro font is loaded.");
		}
		eventSystem = EventSystem.current;
		if ((Object)(object)eventSystem == (Object)null)
		{
			throw new InvalidOperationException("The menu EventSystem is unavailable.");
		}
		previousSelection = eventSystem.currentSelectedGameObject;
		Selectable[] array = Object.FindObjectsByType<Selectable>((FindObjectsSortMode)0);
		foreach (Selectable val in array)
		{
			if (val.interactable)
			{
				blockedControls.Add(val);
				val.interactable = false;
			}
		}
		oldShowCursor = CL_GameManager.showCursor;
		oldCursorLock = Cursor.lockState;
		capturedCursor = true;
		Cursor.lockState = (CursorLockMode)0;
		CL_GameManager.showCursor = true;
		root = CanvasRoot("OneHand.HandChoice", 32767);
		root.AddComponent<GraphicRaycaster>();
		Image obj = root.AddComponent<Image>();
		((Graphic)obj).color = Color.black;
		((Graphic)obj).raycastTarget = true;
		content = ((Component)Rect("Choice", root.transform, new Vector2(1080f, 400f), Vector2.zero)).gameObject;
		Text(content.transform, "Which will you keep?", 52f, new Vector2(1060f, 100f), new Vector2(0f, 98f));
		for (int j = 0; j < 2; j++)
		{
			int hand = j;
			RectTransform val2 = Rect((j == 0) ? "Left" : "Right", content.transform, new Vector2(250f, 110f), new Vector2((float)((j == 0) ? (-220) : 220), -65f));
			Button val3 = ((Component)val2).gameObject.AddComponent<Button>();
			((Selectable)val3).transition = (Transition)0;
			((UnityEvent)val3.onClick).AddListener((UnityAction)delegate
			{
				Choose(hand);
			});
			buttons[j] = val3;
			TextMeshProUGUI val4 = Text((Transform)(object)val2, (j == 0) ? "left" : "right", 44f, new Vector2(250f, 110f), Vector2.zero);
			((TMP_Text)val4).fontStyle = (FontStyles)4;
			((Graphic)val4).raycastTarget = true;
			((Selectable)val3).targetGraphic = (Graphic)(object)val4;
			motion[j] = new HandChoiceLetterMotion(val4, 71347 + j * 997);
			HandChoiceInput handChoiceInput = ((Component)val2).gameObject.AddComponent<HandChoiceInput>();
			handChoiceInput.Owner = this;
			handChoiceInput.Control = j;
		}
		RectTransform val5 = Rect("Back", root.transform, new Vector2(170f, 70f), Vector2.zero);
		Vector2 val6 = default(Vector2);
		((Vector2)(ref val6))..ctor(0f, 1f);
		val5.pivot = val6;
		Vector2 anchorMin = (val5.anchorMax = val6);
		val5.anchorMin = anchorMin;
		val5.anchoredPosition = new Vector2(48f, -40f);
		backButton = ((Component)val5).gameObject.AddComponent<Button>();
		((Selectable)backButton).transition = (Transition)0;
		((UnityEvent)backButton.onClick).AddListener(new UnityAction(Cancel));
		backText = Text((Transform)(object)val5, "back", 32f, new Vector2(170f, 70f), Vector2.zero);
		((TMP_Text)backText).alignment = (TextAlignmentOptions)4097;
		((TMP_Text)backText).fontStyle = (FontStyles)4;
		((Graphic)backText).raycastTarget = true;
		((Selectable)backButton).targetGraphic = (Graphic)(object)backText;
		HandChoiceInput handChoiceInput2 = ((Component)val5).gameObject.AddComponent<HandChoiceInput>();
		handChoiceInput2.Owner = this;
		handChoiceInput2.Control = 2;
		Navigation navigation;
		for (int num = 0; num < 2; num++)
		{
			Button obj2 = buttons[num];
			navigation = default(Navigation);
			((Navigation)(ref navigation)).mode = (Mode)4;
			((Navigation)(ref navigation)).selectOnLeft = (Selectable)(object)buttons[1 - num];
			((Navigation)(ref navigation)).selectOnRight = (Selectable)(object)buttons[1 - num];
			((Navigation)(ref navigation)).selectOnUp = (Selectable)(object)backButton;
			((Selectable)obj2).navigation = navigation;
		}
		Button obj3 = backButton;
		navigation = default(Navigation);
		((Navigation)(ref navigation)).mode = (Mode)4;
		((Navigation)(ref navigation)).selectOnDown = (Selectable)(object)buttons[0];
		((Navigation)(ref navigation)).selectOnRight = (Selectable)(object)buttons[0];
		((Selectable)obj3).navigation = navigation;
		hovered = -1;
		closing = false;
		keyboardNavigation = InputManager.IsGamepad();
		previousMouse = Input.mousePosition;
		openedFrame = Time.frameCount;
		eventSystem.SetSelectedGameObject(((Component)buttons[(plugin.HandChoice.Value != SelectedHand.Left) ? 1u : 0u]).gameObject);
		if ((Object)(object)noticeRoot != (Object)null)
		{
			noticeRoot.SetActive(false);
		}
	}

	internal void Pointer(int control, bool entered)
	{
		keyboardNavigation = false;
		if (entered)
		{
			hovered = control;
		}
		else if (hovered == control)
		{
			hovered = -1;
		}
	}

	internal void Navigate()
	{
		keyboardNavigation = true;
	}

	private int FocusedControl()
	{
		if (!keyboardNavigation)
		{
			return hovered;
		}
		if ((Object)(object)eventSystem == (Object)null)
		{
			return -1;
		}
		for (int i = 0; i < 2; i++)
		{
			if ((Object)(object)buttons[i] != (Object)null && (Object)(object)eventSystem.currentSelectedGameObject == (Object)(object)((Component)buttons[i]).gameObject)
			{
				return i;
			}
		}
		if ((Object)(object)backButton != (Object)null && (Object)(object)eventSystem.currentSelectedGameObject == (Object)(object)((Component)backButton).gameObject)
		{
			return 2;
		}
		return -1;
	}

	internal void Tick()
	{
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: 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_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		choiceSound.Tick();
		if (start.Waiting && ((Object)(object)root == (Object)null || (Object)(object)screen == (Object)null))
		{
			CloseSelection(restoreFocus: false);
		}
		if ((Object)(object)root != (Object)null && (start.Waiting || sceneLoad != null))
		{
			foreach (Selectable blockedControl in blockedControls)
			{
				if ((Object)(object)blockedControl != (Object)null)
				{
					blockedControl.interactable = false;
				}
			}
		}
		if (sceneLoad != null && !sceneLoad.allowSceneActivation && Time.unscaledTime - confirmationStarted >= 1.7f)
		{
			sceneLoad.allowSceneActivation = true;
		}
		if ((Object)(object)root != (Object)null && start.Waiting && !closing)
		{
			Vector3 val = Input.mousePosition - previousMouse;
			if (((Vector3)(ref val)).sqrMagnitude > 1f)
			{
				keyboardNavigation = false;
			}
			previousMouse = Input.mousePosition;
			if (Time.frameCount > openedFrame)
			{
				if (Input.GetKeyDown((KeyCode)9))
				{
					int num = FocusedControl();
					bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
					int num2 = ((num >= 0) ? ((num + ((!flag) ? 1 : 2)) % 3) : (flag ? 2 : 0));
					keyboardNavigation = true;
					eventSystem.SetSelectedGameObject((num2 == 2) ? ((Component)backButton).gameObject : ((Component)buttons[num2]).gameObject);
				}
				if (Input.GetKeyDown((KeyCode)27) || InputManager.GetButton("UIBackButton").Down)
				{
					Cancel();
				}
			}
		}
		UpdateNotice();
	}

	internal void Animate()
	{
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)root != (Object)null && confirmationMotion != null)
		{
			float num = Mathf.Clamp01((Time.unscaledTime - confirmationStarted - 0.2f) / 1.5f);
			float opacity = 1f - Mathf.SmoothStep(0f, 1f, num);
			confirmationMotion.Animate(0, opacity);
		}
		else if (!((Object)(object)root == (Object)null) && start.Waiting && !closing)
		{
			int num2 = FocusedControl();
			for (int i = 0; i < 2; i++)
			{
				motion[i].Animate((num2 >= 0 && num2 != 2) ? ((num2 == i) ? 1 : 2) : 0);
			}
			if ((Object)(object)backText != (Object)null)
			{
				((Graphic)backText).color = Color.Lerp(((Graphic)backText).color, (num2 == 2) ? Color.yellow : Color.white, 1f - Mathf.Exp(-18f * Time.unscaledDeltaTime));
			}
		}
	}

	private void Choose(int hand)
	{
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		if (!start.Waiting || closing || Time.frameCount <= openedFrame)
		{
			return;
		}
		closing = true;
		plugin.HandChoice.Value = ((hand != 0) ? SelectedHand.Right : SelectedHand.Left);
		Button[] array = buttons;
		foreach (Button val in array)
		{
			if ((Object)(object)val != (Object)null)
			{
				((Selectable)val).interactable = false;
			}
		}
		if ((Object)(object)backButton != (Object)null)
		{
			((Component)backButton).gameObject.SetActive(false);
		}
		Object.DontDestroyOnLoad((Object)(object)root);
		content.SetActive(false);
		TextMeshProUGUI text = Text(root.transform, "Choice made", 52f, new Vector2(1060f, 100f), Vector2.zero);
		confirmationMotion = new HandChoiceLetterMotion(text, 98327);
		confirmationStarted = Time.unscaledTime;
		choiceSound.Play();
		((MonoBehaviour)plugin).StartCoroutine(StartAfterInput(root));
	}

	private IEnumerator StartAfterInput(GameObject choiceScreen)
	{
		yield return null;
		if ((Object)(object)choiceScreen == (Object)null || root != choiceScreen)
		{
			yield break;
		}
		if (!start.Waiting || (Object)(object)screen == (Object)null)
		{
			CloseSelection(restoreFocus: false);
			yield break;
		}
		try
		{
			start.Resume();
		}
		catch (Exception ex)
		{
			CloseSelection(restoreFocus: true);
			plugin.Warn("The native Play action failed: " + ex);
			plugin.Tell("Could not start the run. See LogOutput.log.");
		}
	}

	private void SceneLoaded(Scene scene, LoadSceneMode mode)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		if (sceneLoad != null && (int)mode == 0)
		{
			OnRunStarted();
		}
	}

	internal void OnRunStarted()
	{
		capturedCursor = false;
		CloseSelection(restoreFocus: false, stopSound: false);
	}

	private void Cancel()
	{
		if (!start.Waiting || closing || Time.frameCount <= openedFrame)
		{
			return;
		}
		closing = true;
		Button[] array = buttons;
		foreach (Button val in array)
		{
			if ((Object)(object)val != (Object)null)
			{
				((Selectable)val).interactable = false;
			}
		}
		if ((Object)(object)backButton != (Object)null)
		{
			((Selectable)backButton).interactable = false;
		}
		((MonoBehaviour)plugin).StartCoroutine(CancelAfterInput());
	}

	private IEnumerator CancelAfterInput()
	{
		yield return null;
		CloseSelection(restoreFocus: true);
	}

	private void RestoreMenu(bool restoreFocus)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		foreach (Selectable blockedControl in blockedControls)
		{
			if ((Object)(object)blockedControl != (Object)null)
			{
				blockedControl.interactable = true;
			}
		}
		blockedControls.Clear();
		if (capturedCursor)
		{
			Cursor.lockState = oldCursorLock;
			CL_GameManager.showCursor = oldShowCursor;
			capturedCursor = false;
		}
		if (restoreFocus && (Object)(object)eventSystem != (Object)null && (Object)(object)previousSelection != (Object)null && previousSelection.activeInHierarchy)
		{
			eventSystem.SetSelectedGameObject(previousSelection);
		}
	}

	internal void CloseSelection(bool restoreFocus, bool stopSound = true)
	{
		start.Cancel();
		if (stopSound)
		{
			choiceSound.Stop();
		}
		if (sceneLoad != null && !sceneLoad.isDone)
		{
			sceneLoad.allowSceneActivation = true;
		}
		sceneLoad = null;
		if ((Object)(object)root != (Object)null)
		{
			root.SetActive(false);
			Object.Destroy((Object)(object)root);
		}
		root = null;
		content = null;
		backButton = null;
		backText = null;
		confirmationMotion = null;
		RestoreMenu(restoreFocus);
		screen = null;
		closing = false;
		hovered = -1;
		for (int i = 0; i < 2; i++)
		{
			buttons[i] = null;
			motion[i] = null;
		}
	}

	private void UpdateNotice()
	{
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: 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)
		string notice = plugin.Notice;
		if ((Object)(object)root != (Object)null || notice.Length == 0)
		{
			if ((Object)(object)noticeRoot != (Object)null)
			{
				noticeRoot.SetActive(false);
			}
			return;
		}
		if ((Object)(object)noticeRoot == (Object)null)
		{
			if (!EnsureFont())
			{
				return;
			}
			noticeRoot = CanvasRoot("OneHand.Notice", 32760);
			noticeText = Text(noticeRoot.transform, "", 25f, new Vector2(1250f, 80f), Vector2.zero);
			RectTransform rectTransform = ((TMP_Text)noticeText).rectTransform;
			RectTransform rectTransform2 = ((TMP_Text)noticeText).rectTransform;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0f);
			rectTransform2.anchorMax = val;
			rectTransform.anchorMin = val;
			((TMP_Text)noticeText).rectTransform.anchoredPosition = new Vector2(0f, 142f);
			((TMP_Text)noticeText).textWrappingMode = (TextWrappingModes)1;
		}
		((TMP_Text)noticeText).text = notice;
		noticeRoot.SetActive(true);
	}

	internal void Dispose()
	{
		SceneManager.sceneLoaded -= SceneLoaded;
		CloseSelection(restoreFocus: true);
		choiceSound.Dispose();
		if ((Object)(object)noticeRoot != (Object)null)
		{
			Object.Destroy((Object)(object)noticeRoot);
		}
	}
}
internal sealed class HandChoiceLetterMotion
{
	private readonly TextMeshProUGUI text;

	private readonly Random random;

	private readonly Vector2[] offsets = (Vector2[])(object)new Vector2[16];

	private float amplitude = 1.6f;

	private float nextNoise;

	internal HandChoiceLetterMotion(TextMeshProUGUI text, int seed)
	{
		this.text = text;
		random = new Random(seed);
	}

	internal void Animate(int state, float opacity = 1f)
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: 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_0196: 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_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)text == (Object)null)
		{
			return;
		}
		amplitude = Mathf.MoveTowards(amplitude, state switch
		{
			2 => 8f, 
			1 => 0.12f, 
			_ => 1.6f, 
		}, Time.unscaledDeltaTime * 48f);
		Color color = Color.Lerp(((Graphic)text).color, (Color)(state switch
		{
			2 => Color.red, 
			1 => Color.yellow, 
			_ => Color.white, 
		}), 1f - Mathf.Exp(-18f * Time.unscaledDeltaTime));
		color.a = Mathf.Clamp01(opacity);
		((Graphic)text).color = color;
		if (Time.unscaledTime >= nextNoise)
		{
			nextNoise = Time.unscaledTime + 1f / 30f;
			for (int i = 0; i < offsets.Length; i++)
			{
				offsets[i] = new Vector2((float)random.NextDouble() * 2f - 1f, (float)random.NextDouble() * 2f - 1f);
			}
		}
		((TMP_Text)text).ForceMeshUpdate(false, false);
		TMP_TextInfo textInfo = ((TMP_Text)text).textInfo;
		for (int j = 0; j < textInfo.characterCount; j++)
		{
			TMP_CharacterInfo val = textInfo.characterInfo[j];
			if (val.isVisible)
			{
				Vector3 val2 = Vector2.op_Implicit(offsets[j % offsets.Length] * amplitude);
				Vector3[] vertices = textInfo.meshInfo[val.materialReferenceIndex].vertices;
				for (int k = 0; k < 4; k++)
				{
					ref Vector3 reference = ref vertices[val.vertexIndex + k];
					reference += val2;
				}
			}
		}
		((TMP_Text)text).UpdateVertexData((TMP_VertexDataUpdateFlags)1);
	}
}
internal sealed class HandChoiceInput : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IMoveHandler
{
	internal OneHandUi Owner;

	internal int Control;

	public void OnPointerEnter(PointerEventData data)
	{
		Owner?.Pointer(Control, entered: true);
	}

	public void OnPointerExit(PointerEventData data)
	{
		Owner?.Pointer(Control, entered: false);
	}

	public void OnMove(AxisEventData data)
	{
		Owner?.Navigate();
	}
}
internal static class PatchContext
{
	internal static OneHandSession Session
	{
		get
		{
			if (!((Object)(object)OneHandPlugin.Instance != (Object)null))
			{
				return null;
			}
			return OneHandPlugin.Instance.Session;
		}
	}

	internal static bool Owner(ENT_Player player)
	{
		if (Session != null)
		{
			return Session.IsOwner(player);
		}
		return false;
	}

	internal static bool Inventory(Inventory inv)
	{
		if (Session != null)
		{
			return Session.IsInventory(inv);
		}
		return false;
	}

	internal static bool Excluded(Hand hand)
	{
		if (Session != null)
		{
			return Session.IsExcluded(hand);
		}
		return false;
	}

	public static void StaminaWrite(Hand hand, float next)
	{
		if (Session != null && Session.IsChosen(hand))
		{
			next = OneHandRules.Tick(hand.gripStrength, next, hand.GetGripStrengthMax());
		}
		hand.gripStrength = next;
	}
}
[HarmonyPatch(typeof(UI_GamemodeScreen), "LoadGamemode")]
internal static class PreRunChoicePatch
{
	private static bool Prefix(UI_GamemodeScreen __instance)
	{
		if (!((Object)(object)OneHandPlugin.Instance == (Object)null))
		{
			return OneHandPlugin.Instance.BeforeStart(__instance);
		}
		return true;
	}
}
[HarmonyPatch(typeof(ENT_Player), "Awake")]
internal static class AttachPlayerPatch
{
	private static void Prefix(ENT_Player __instance)
	{
		PatchContext.Session?.Attach(__instance);
	}
}
[HarmonyPatch(typeof(ENT_Player), "Start")]
internal static class StartPlayerPatch
{
	private static void Postfix(ENT_Player __instance)
	{
		PatchContext.Session?.Started(__instance);
	}
}
[HarmonyPatch(typeof(ENT_Player), "StartInteraction")]
internal static class OneInteractionPatch
{
	private static bool Prefix(ENT_Player __instance, int hand)
	{
		if (PatchContext.Owner(__instance))
		{
			return hand == PatchContext.Session.HandId;
		}
		return true;
	}
}
[HarmonyPatch(typeof(Hand), "CanInteract")]
internal static class HandCanInteractPatch
{
	private static void Postfix(Hand __instance, ref bool __result)
	{
		if (PatchContext.Excluded(__instance))
		{
			__result = false;
		}
	}
}
[HarmonyPatch]
internal static class BlockScriptedGrabPatch
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		yield return typeof(Hand).GetMethod("GrabHold");
		yield return typeof(Hand).GetMethod("GrabTarget");
	}

	private static bool Prefix(Hand __instance)
	{
		return !PatchContext.Excluded(__instance);
	}
}
[HarmonyPatch(typeof(HandItem), "TryUse")]
internal static class BlockGhostItemPatch
{
	private static bool Prefix(HandItem __instance)
	{
		return !PatchContext.Excluded(((Hand_Base)__instance).hand);
	}
}
[HarmonyPatch(typeof(Hand), "AddGripStrength")]
internal static class StaminaGainPatch
{
	private static void Prefix(Hand __instance, ref float amount)
	{
		if (PatchContext.Session != null && PatchContext.Session.IsChosen(__instance))
		{
			amount = OneHandRules.Gain(amount);
		}
	}
}
[HarmonyPatch(typeof(ENT_Player), "HandStamina")]
internal static class StaminaTickPatch
{
	internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		FieldInfo field = typeof(Hand).GetField("gripStrength");
		MethodInfo method = typeof(PatchContext).GetMethod("StaminaWrite", BindingFlags.Static | BindingFlags.Public);
		int num = 0;
		List<CodeInstruction> list = new List<CodeInstruction>();
		foreach (CodeInstruction instruction in instructions)
		{
			if (instruction.opcode == OpCodes.Stfld && object.Equals(instruction.operand, field))
			{
				instruction.opcode = OpCodes.Call;
				instruction.operand = method;
				num++;
			}
			list.Add(instruction);
		}
		if (num != 8)
		{
			throw new InvalidOperationException("OneHand expected 8 stamina writes in Beta 0.61, found " + num + ". Refusing a partial stamina patch.");
		}
		return list;
	}
}
[HarmonyPatch(typeof(ENT_Player), "GetLowestGripStrength", new Type[] { })]
internal static class LowestGripPatch
{
	private static bool Prefix(ENT_Player __instance, ref float __result)
	{
		if (!PatchContext.Owner(__instance))
		{
			return true;
		}
		__result = __instance.hands[PatchContext.Session.HandId].gripStrength;
		return false;
	}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
internal static class LowestGripWithIdPatch
{
	private static bool Prefix(ENT_Player __instance, ref int handID, ref float __result)
	{
		if (!PatchContext.Owner(__instance))
		{
			return true;
		}
		handID = PatchContext.Session.HandId;
		__result = __instance.hands[handID].gripStrength;
		return false;
	}
}
[HarmonyPatch(typeof(ENT_Player), "GetAverageGripStrength")]
internal static class AverageGripPatch
{
	private static bool Prefix(ENT_Player __instance, bool onlyHangingHands, ref float __result)
	{
		if (!PatchContext.Owner(__instance))
		{
			return true;
		}
		Hand val = __instance.hands[PatchContext.Session.HandId];
		__result = ((!onlyHangingHands || val.IsHanging()) ? val.gripStrength : __instance.GetCurrentGripStrengthTimer(val.id));
		return false;
	}
}
[HarmonyPatch(typeof(Inventory), "Initialize")]
internal static class InventoryInitPatch
{
	private static void Postfix(Inventory __instance)
	{
		if (PatchContext.Inventory(__instance))
		{
			PatchContext.Session.Store.ConfigurePockets();
		}
	}
}
[HarmonyPatch(typeof(Inventory), "PushItemsApart")]
internal static class PocketSizePatch
{
	private static void Postfix(Inventory __instance)
	{
		if (PatchContext.Inventory(__instance))
		{
			PatchContext.Session.Store.ConfigurePockets();
		}
	}
}
[HarmonyPatch(typeof(Pouch), "CanAddItemToPouch")]
internal static class QuickPocketCapacityPatch
{
	private static bool Prefix(Pouch __instance, Item item, ref bool __result)
	{
		if (PatchContext.Session == null || !PatchContext.Session.Store.IsQuickPocket(__instance))
		{
			return true;
		}
		__result = item != null && !item.hasBeenDestroyed && (Object)(object)item.handItemAsset != (Object)null && !item.HasTag("unpurchased") && __instance.pouchItems.Count == 0;
		return false;
	}
}
[HarmonyPatch(typeof(Inventory), "TryPocketItemInHand")]
internal static class QuickPocketInputPatch
{
	private static bool Prefix(Inventory __instance, int handID)
	{
		if (!PatchContext.Inventory(__instance))
		{
			return true;
		}
		OneHandPlugin.Instance.RequestSlot(handID);
		return false;
	}
}
[HarmonyPatch(typeof(Inventory), "SwapHands")]
internal static class SwapInputPatch
{
	private static bool Prefix(Inventory __instance)
	{
		if (!PatchContext.Inventory(__instance))
		{
			return true;
		}
		OneHandPlugin.Instance.SwapSlot();
		return false;
	}
}
[HarmonyPatch(typeof(HandItem), "PocketHandItem")]
internal static class DeferredPocketPatch
{
	private static bool Prefix(HandItem __instance)
	{
		OneHandSession session = PatchContext.Session;
		if (session == null || !session.IsChosen(((Hand_Base)__instance).hand))
		{
			return true;
		}
		session.Slots.Reconcile();
		OneHandPlugin.Instance.RequestSlot(OneHandPlugin.Instance.TakeDeferredSlot(__instance.item, (session.Slots.EquippedSlot >= 0) ? session.Slots.EquippedSlot : session.HandId), deferred: true);
		return false;
	}
}
[HarmonyPatch(typeof(Inventory), "AddItemToHand", new Type[]
{
	typeof(Item),
	typeof(Hand)
})]
internal static class RouteItemPatch
{
	private static bool Prefix(Inventory __instance, Item i, ref Hand playerHand, out int __state)
	{
		__state = -1;
		if (PatchContext.Inventory(__instance))
		{
			return PatchContext.Session.Store.BeforeAdd(i, ref playerHand, out __state);
		}
		return true;
	}

	private static void Postfix(Inventory __instance, Item i, int __state)
	{
		if (PatchContext.Inventory(__instance))
		{
			PatchContext.Session.Store.AfterAdd(i, __state);
		}
	}
}
[HarmonyPatch(typeof(HandItem_Piton), "PitonHit")]
internal static class OneHandPitonPatch
{
	private struct State
	{
		internal bool Applies;

		internal bool AutoGrab;

		internal bool Used;
	}

	private static void Prefix(HandItem_Piton __instance, out State __state)
	{
		__state = new State
		{
			Applies = (PatchContext.Session != null && PatchContext.Session.IsChosen(((Hand_Base)__instance).hand)),
			AutoGrab = __instance.autograbOnPlaceIfUsing,
			Used = ((HandItem)__instance).used
		};
		if (__state.Applies)
		{
			__instance.autograbOnPlaceIfUsing = false;
		}
	}

	private static void Postfix(HandItem_Piton __instance, State __state)
	{
		if (__state.Applies && !__state.Used && ((HandItem)__instance).used)
		{
			((MonoBehaviour)OneHandPlugin.Instance).StartCoroutine(OneHandPlugin.Instance.FinishPiton(__instance));
		}
	}

	private static Exception Finalizer(HandItem_Piton __instance, State __state, Exception __exception)
	{
		if (__state.Applies && (Object)(object)__instance != (Object)null)
		{
			__instance.autograbOnPlaceIfUsing = __state.AutoGrab;
		}
		return __exception;
	}
}
[HarmonyPatch(typeof(CL_SaveManager), "SaveSession")]
internal static class SaveModePatch
{
	private static void Prefix()
	{
		PatchContext.Session?.StoreRunFlags();
	}
}
[HarmonyPatch(typeof(CL_SaveManager), "LoadSave")]
internal static class LoadModePatch
{
	private static void Prefix(SessionSave sessionSave)
	{
		PatchContext.Session?.ReadSavedRun(sessionSave);
	}
}
[HarmonyPatch(typeof(SaveData), "LoadDataIntoPlayer")]
internal static class LoadedInventoryPatch
{
	private static void Postfix(ENT_Player player, bool loadInventory)
	{
		if (loadInventory && PatchContext.Owner(player))
		{
			PatchContext.Session.InventoryLoaded();
		}
	}
}
internal sealed class RunStartGate
{
	private Action pending;

	private bool resuming;

	internal bool Waiting => pending != null;

	internal bool Resuming => resuming;

	internal bool Hold(bool enabled, bool continuing, Action start)
	{
		if (resuming)
		{
			return false;
		}
		if (Waiting)
		{
			return true;
		}
		if (!enabled || continuing)
		{
			return false;
		}
		pending = start ?? throw new ArgumentNullException("start");
		return true;
	}

	internal bool Resume()
	{
		Action action = pending;
		if (action == null)
		{
			return false;
		}
		pending = null;
		resuming = true;
		try
		{
			action();
		}
		finally
		{
			resuming = false;
		}
		return true;
	}

	internal void Cancel()
	{
		pending = null;
	}
}