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;
}
}