Decompiled source of PeakTwitchIntegration v0.8.6

plugins/PeakTwitchIntegration.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.WebSockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Peak.UI;
using Photon.Pun;
using Photon.Realtime;
using Photon.Voice;
using Photon.Voice.Unity;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PeakTwitchIntegration")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.8.6.0")]
[assembly: AssemblyInformationalVersion("0.8.6")]
[assembly: AssemblyProduct("PeakTwitchIntegration")]
[assembly: AssemblyTitle("PeakTwitchIntegration")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.8.6.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PeakTwitchIntegration
{
	internal static class CannonLaunchController
	{
		private const byte FallProtectionEventCode = 173;

		private const byte CountdownEventCode = 174;

		private const byte TeammatePushFallProtectionEventCode = 175;

		private const string NetworkMarker = "com.local.peaktwitchintegration:cannon-launch:v1";

		private const string CountdownNetworkMarker = "com.local.peaktwitchintegration:cannon-countdown:v1";

		private const string TeammatePushProtectionNetworkMarker = "com.local.peaktwitchintegration:teammate-push-protection:v1";

		private const float FallProtectionSeconds = 300f;

		private const int CountdownSeconds = 3;

		private const float ForwardForceRatio = 0.15f;

		private static bool _initialized;

		private static CannonLaunchRuntime? _runtime;

		internal static void Initialize(MonoBehaviour host)
		{
			if (!_initialized)
			{
				_runtime = ((Component)host).GetComponent<CannonLaunchRuntime>() ?? ((Component)host).gameObject.AddComponent<CannonLaunchRuntime>();
				PhotonNetwork.NetworkingClient.EventReceived += OnPhotonEvent;
				_initialized = true;
			}
		}

		internal static void Shutdown()
		{
			if (_initialized)
			{
				PhotonNetwork.NetworkingClient.EventReceived -= OnPhotonEvent;
				FallDamageSafety.Clear();
				_runtime?.ClearCountdowns();
				_runtime = null;
				_initialized = false;
			}
		}

		internal static void HandleRedemption(CannonLaunchRedemption redemption)
		{
			if (!Plugin.CannonLaunchEnabled)
			{
				Reject(redemption, "Cannon Launch is disabled; Channel Points were refunded.");
				return;
			}
			try
			{
				if (!TryFindPlayer(redemption.PlayerName, out Character target, out string problem))
				{
					Reject(redemption, problem + " Channel Points were refunded.");
					return;
				}
				if ((Object)(object)target == (Object)null || (Object)(object)target.data == (Object)null || target.data.dead || target.data.fullyPassedOut)
				{
					Reject(redemption, "'" + redemption.PlayerName + "' cannot be launched right now; Channel Points were refunded.");
					return;
				}
				int ownerActorNr = ((MonoBehaviourPun)target).photonView.OwnerActorNr;
				string bestName = GetBestName(target);
				if ((Object)(object)_runtime == (Object)null)
				{
					throw new InvalidOperationException("Cannon Launch runtime is unavailable.");
				}
				ShowLocalCountdown(ownerActorNr, 3, "[PEAK] Cannon Launch");
				BroadcastCountdown(ownerActorNr, 3, "[PEAK] Cannon Launch");
				string text = $"{redemption.Viewer} selected {bestName}; Cannon Launch in {3} seconds.";
				Plugin.Log.LogInfo((object)text);
				Plugin.Twitch.ReportCannonLaunchResult(text);
				((MonoBehaviour)_runtime).StartCoroutine(LaunchAfterCountdown(redemption, ownerActorNr, bestName));
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Cannon Launch failed: {arg}");
				Reject(redemption, "Cannon Launch failed; Channel Points were refunded.");
			}
		}

		internal static void ProtectFromTeammatePush(Character target, float seconds)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)target == (Object)null) && !((Object)(object)((MonoBehaviourPun)target).photonView == (Object)null))
			{
				int ownerActorNr = ((MonoBehaviourPun)target).photonView.OwnerActorNr;
				FallDamageSafety.Begin(ownerActorNr, seconds, "Teammate Push");
				if (PhotonNetwork.InRoom)
				{
					object[] array = new object[3]
					{
						"com.local.peaktwitchintegration:teammate-push-protection:v1",
						ownerActorNr,
						Mathf.Clamp(seconds, 1f, 30f)
					};
					RaiseEventOptions val = new RaiseEventOptions
					{
						Receivers = (ReceiverGroup)1
					};
					PhotonNetwork.RaiseEvent((byte)175, (object)array, val, SendOptions.SendReliable);
				}
			}
		}

		private static IEnumerator LaunchAfterCountdown(CannonLaunchRedemption redemption, int actorNumber, string resolvedName)
		{
			yield return (object)new WaitForSecondsRealtime(3f);
			CompleteLaunch(redemption, actorNumber, resolvedName);
		}

		private static void CompleteLaunch(CannonLaunchRedemption redemption, int actorNumber, string previousName)
		{
			//IL_0092: 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_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.CannonLaunchEnabled)
			{
				Reject(redemption, "Cannon Launch was disabled during the countdown; Channel Points were refunded.");
				return;
			}
			try
			{
				if (!TryFindPlayer(actorNumber, out Character target) || (Object)(object)target == (Object)null)
				{
					Reject(redemption, "'" + previousName + "' left before Cannon Launch; Channel Points were refunded.");
					return;
				}
				if ((Object)(object)target.data == (Object)null || target.data.dead || target.data.fullyPassedOut)
				{
					Reject(redemption, "'" + previousName + "' cannot be launched now; Channel Points were refunded.");
					return;
				}
				BeginLocalProtection(actorNumber);
				BroadcastFallProtection(actorNumber);
				Vector3 val = BuildLaunchForce(target);
				if (PhotonNetwork.InRoom && (Object)(object)target.refs.view != (Object)null)
				{
					target.refs.view.RPC("RPCA_Fall", (RpcTarget)0, new object[2] { 1.25f, 0f });
					target.refs.view.RPC("RPCA_AddForceToBodyPart", (RpcTarget)0, new object[3]
					{
						(object)(BodypartType)2,
						Vector3.zero,
						val
					});
				}
				else
				{
					target.RPCA_Fall(1.25f, 0f);
					target.RPCA_AddForceToBodyPart((BodypartType)2, Vector3.zero, val);
				}
				string bestName = GetBestName(target);
				string text = redemption.Viewer + " launched " + bestName + " with Cannon Launch.";
				Plugin.Log.LogInfo((object)text);
				Plugin.Twitch.ReportCannonLaunchResult(text);
				Plugin.Twitch.CompleteCannonLaunch(redemption, fulfilled: true);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Cannon Launch failed after countdown: {arg}");
				Reject(redemption, "Cannon Launch failed; Channel Points were refunded.");
			}
		}

		private static Vector3 BuildLaunchForce(Character target)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)target).transform.forward;
			if (target.refs != null && (Object)(object)target.refs.view != (Object)null)
			{
				val = ((Component)target.refs.view).transform.forward;
			}
			val = Vector3.ProjectOnPlane(val, Vector3.up);
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = Vector3.forward;
			}
			((Vector3)(ref val)).Normalize();
			return (Vector3.up + val * 0.15f) * Plugin.CannonLaunchForce;
		}

		private static void Reject(CannonLaunchRedemption redemption, string message)
		{
			Plugin.Log.LogWarning((object)message);
			Plugin.Twitch.ReportCannonLaunchResult(message);
			Plugin.Twitch.CompleteCannonLaunch(redemption, fulfilled: false);
		}

		private static bool TryFindPlayer(string requestedName, out Character? target, out string problem)
		{
			target = null;
			string text = NormalizeName(requestedName);
			if (text.Length == 0)
			{
				problem = "No PEAK player name was entered.";
				return false;
			}
			List<Character> list = new List<Character>();
			foreach (Character allCharacter in Character.AllCharacters)
			{
				if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.isBot && !allCharacter.isZombie && !allCharacter.isScoutmaster)
				{
					string text2 = NormalizeName(allCharacter.characterName);
					PhotonView photonView = ((MonoBehaviourPun)allCharacter).photonView;
					object obj;
					if (photonView == null)
					{
						obj = null;
					}
					else
					{
						Player owner = photonView.Owner;
						obj = ((owner != null) ? owner.NickName : null);
					}
					if (obj == null)
					{
						obj = string.Empty;
					}
					string text3 = NormalizeName((string)obj);
					if (text2 == text || text3 == text)
					{
						list.Add(allCharacter);
					}
				}
			}
			if (list.Count == 1)
			{
				target = list[0];
				problem = string.Empty;
				return true;
			}
			if (list.Count > 1)
			{
				problem = "The name '" + requestedName + "' is ambiguous in this lobby.";
				return false;
			}
			problem = "No active PEAK player named '" + requestedName + "' was found.";
			return false;
		}

		private static bool TryFindPlayer(int actorNumber, out Character? target)
		{
			target = null;
			foreach (Character allCharacter in Character.AllCharacters)
			{
				if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.isBot && !allCharacter.isZombie && !allCharacter.isScoutmaster && !((Object)(object)((MonoBehaviourPun)allCharacter).photonView == (Object)null) && ((MonoBehaviourPun)allCharacter).photonView.OwnerActorNr == actorNumber)
				{
					target = allCharacter;
					return true;
				}
			}
			return false;
		}

		private static string NormalizeName(string name)
		{
			string text = (name ?? string.Empty).Trim();
			while (text.StartsWith("@", StringComparison.Ordinal))
			{
				text = text.Substring(1).TrimStart();
			}
			return text.ToUpperInvariant();
		}

		private static string GetBestName(Character character)
		{
			PhotonView photonView = ((MonoBehaviourPun)character).photonView;
			object obj;
			if (photonView == null)
			{
				obj = null;
			}
			else
			{
				Player owner = photonView.Owner;
				obj = ((owner != null) ? owner.NickName : null);
			}
			if (obj == null)
			{
				obj = string.Empty;
			}
			string text = (string)obj;
			string text2;
			if (string.IsNullOrWhiteSpace(text))
			{
				text2 = character.characterName;
				if (text2 == null)
				{
					return "Unknown player";
				}
			}
			else
			{
				text2 = text;
			}
			return text2;
		}

		private static void BroadcastFallProtection(int actorNumber)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.InRoom)
			{
				object[] array = new object[3] { "com.local.peaktwitchintegration:cannon-launch:v1", actorNumber, 300f };
				RaiseEventOptions val = new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)1
				};
				PhotonNetwork.RaiseEvent((byte)173, (object)array, val, SendOptions.SendReliable);
			}
		}

		private static void ShowLocalCountdown(int actorNumber, int seconds, string rewardTitle)
		{
			_runtime?.ShowCountdown(actorNumber, seconds, rewardTitle);
		}

		private static void BroadcastCountdown(int actorNumber, int seconds, string rewardTitle)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.InRoom)
			{
				object[] array = new object[4] { "com.local.peaktwitchintegration:cannon-countdown:v1", actorNumber, seconds, rewardTitle };
				RaiseEventOptions val = new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				};
				PhotonNetwork.RaiseEvent((byte)174, (object)array, val, SendOptions.SendReliable);
			}
		}

		private static void OnPhotonEvent(EventData eventData)
		{
			if (!(eventData.CustomData is object[] array))
			{
				return;
			}
			object obj;
			if (array.Length == 3 && eventData.Code == 173 && array[0] is string text && text == "com.local.peaktwitchintegration:cannon-launch:v1")
			{
				obj = array[1];
				if (obj is int)
				{
					int protectedActorNumber = (int)obj;
					obj = array[2];
					if (obj is float)
					{
						float protectionSeconds = (float)obj;
						Plugin.QueueMainThreadAction(delegate
						{
							FallDamageSafety.Begin(protectedActorNumber, protectionSeconds, "Twitch Cannon Launch");
						});
						return;
					}
				}
			}
			if (array.Length == 3 && eventData.Code == 175 && array[0] is string text2 && text2 == "com.local.peaktwitchintegration:teammate-push-protection:v1")
			{
				obj = array[1];
				if (obj is int)
				{
					int pushedActorNumber = (int)obj;
					obj = array[2];
					if (obj is float)
					{
						float pushProtectionSeconds = (float)obj;
						Plugin.QueueMainThreadAction(delegate
						{
							FallDamageSafety.Begin(pushedActorNumber, pushProtectionSeconds, "Teammate Push");
						});
						return;
					}
				}
			}
			if (array.Length != 4 || eventData.Code != 174 || !(array[0] is string text3) || !(text3 == "com.local.peaktwitchintegration:cannon-countdown:v1"))
			{
				return;
			}
			obj = array[1];
			if (!(obj is int))
			{
				return;
			}
			int countdownActorNumber = (int)obj;
			obj = array[2];
			if (!(obj is int))
			{
				return;
			}
			int countdownSeconds = (int)obj;
			obj = array[3];
			string rewardTitle = obj as string;
			if (rewardTitle != null)
			{
				Plugin.QueueMainThreadAction(delegate
				{
					ShowLocalCountdown(countdownActorNumber, countdownSeconds, rewardTitle);
				});
			}
		}

		private static void BeginLocalProtection(int actorNumber)
		{
			FallDamageSafety.Begin(actorNumber, 300f, "Twitch Cannon Launch");
		}
	}
	internal sealed class CannonLaunchRuntime : MonoBehaviour
	{
		private readonly List<float> _countdownDeadlines = new List<float>();

		private GameObject? _overlayRoot;

		private TextMeshProUGUI? _countdownLabel;

		private GameObject? _rewardPopup;

		private RectTransform? _rewardPopupTransform;

		private CanvasGroup? _rewardPopupCanvasGroup;

		private TextMeshProUGUI? _rewardPopupLabel;

		private int _lastNumber;

		private float _numberChangedAt;

		private float _popupShownAt;

		private float _popupUntil;

		internal void ShowCountdown(int actorNumber, int seconds, string rewardTitle)
		{
			Character localCharacter = Character.localCharacter;
			if (!((Object)(object)localCharacter == (Object)null) && !((Object)(object)((MonoBehaviourPun)localCharacter).photonView == (Object)null) && ((MonoBehaviourPun)localCharacter).photonView.OwnerActorNr == actorNumber)
			{
				EnsureOverlay();
				float unscaledTime = Time.unscaledTime;
				int num = Mathf.Clamp(seconds, 1, 10);
				_countdownDeadlines.Add(unscaledTime + (float)num);
				_popupShownAt = unscaledTime;
				_popupUntil = Mathf.Max(_popupUntil, unscaledTime + (float)num + 1.5f);
				if ((Object)(object)_rewardPopupLabel != (Object)null)
				{
					((TMP_Text)_rewardPopupLabel).text = "TWITCH REWARD\n" + (rewardTitle ?? "UNKNOWN REWARD").ToUpperInvariant();
				}
				GameObject? rewardPopup = _rewardPopup;
				if (rewardPopup != null)
				{
					rewardPopup.SetActive(true);
				}
				GameObject? overlayRoot = _overlayRoot;
				if (overlayRoot != null)
				{
					overlayRoot.SetActive(true);
				}
			}
		}

		internal void ClearCountdowns()
		{
			_countdownDeadlines.Clear();
			_lastNumber = 0;
			_popupUntil = 0f;
			GameObject? rewardPopup = _rewardPopup;
			if (rewardPopup != null)
			{
				rewardPopup.SetActive(false);
			}
			GameObject? overlayRoot = _overlayRoot;
			if (overlayRoot != null)
			{
				overlayRoot.SetActive(false);
			}
		}

		private void Update()
		{
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: 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)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_overlayRoot == (Object)null || (Object)(object)_countdownLabel == (Object)null)
			{
				return;
			}
			float now = Time.unscaledTime;
			_countdownDeadlines.RemoveAll((float deadline) => deadline <= now);
			bool flag = _countdownDeadlines.Count > 0;
			if (!flag)
			{
				_lastNumber = 0;
				((Component)_countdownLabel).gameObject.SetActive(false);
			}
			else
			{
				((Component)_countdownLabel).gameObject.SetActive(true);
				float num = _countdownDeadlines[0];
				for (int num2 = 1; num2 < _countdownDeadlines.Count; num2++)
				{
					num = Mathf.Min(num, _countdownDeadlines[num2]);
				}
				int num3 = Mathf.Clamp(Mathf.CeilToInt(num - now), 1, 3);
				if (num3 != _lastNumber)
				{
					_lastNumber = num3;
					_numberChangedAt = now;
					((TMP_Text)_countdownLabel).text = num3.ToString();
					TextMeshProUGUI countdownLabel = _countdownLabel;
					((Graphic)countdownLabel).color = Color32.op_Implicit((Color32)(num3 switch
					{
						3 => new Color32((byte)83, (byte)224, (byte)121, byte.MaxValue), 
						2 => new Color32(byte.MaxValue, (byte)215, (byte)64, byte.MaxValue), 
						_ => new Color32(byte.MaxValue, (byte)75, (byte)72, byte.MaxValue), 
					}));
				}
				float num4 = Mathf.Clamp01((now - _numberChangedAt) / 0.24f);
				float num5 = Mathf.Lerp(1.28f, 1f, num4);
				((Transform)((TMP_Text)_countdownLabel).rectTransform).localScale = new Vector3(num5, num5, 1f);
			}
			bool flag2 = now < _popupUntil;
			GameObject? rewardPopup = _rewardPopup;
			if (rewardPopup != null)
			{
				rewardPopup.SetActive(flag2);
			}
			if (flag2 && (Object)(object)_rewardPopupCanvasGroup != (Object)null && (Object)(object)_rewardPopupTransform != (Object)null)
			{
				float num6 = Mathf.Clamp01((now - _popupShownAt) / 0.18f);
				float num7 = Mathf.Clamp01((_popupUntil - now) / 0.35f);
				_rewardPopupCanvasGroup.alpha = Mathf.Min(num6, num7);
				_rewardPopupTransform.anchoredPosition = new Vector2(Mathf.Lerp(18f, 42f, num6), 42f);
			}
			_overlayRoot.SetActive(flag || flag2);
		}

		private void EnsureOverlay()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_00b7: 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_010a: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Expected O, but got Unknown
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0305: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_0417: Unknown result type (might be due to invalid IL or missing references)
			//IL_0459: Unknown result type (might be due to invalid IL or missing references)
			//IL_045f: Expected O, but got Unknown
			//IL_047d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0519: Unknown result type (might be due to invalid IL or missing references)
			//IL_051e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0546: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_overlayRoot != (Object)null))
			{
				TextMeshProUGUI obj = Object.FindAnyObjectByType<TextMeshProUGUI>();
				TMP_FontAsset font = ((obj != null) ? ((TMP_Text)obj).font : null) ?? TMP_Settings.defaultFontAsset;
				_overlayRoot = new GameObject("PeakTwitchIntegration_CannonCountdown", new Type[3]
				{
					typeof(RectTransform),
					typeof(Canvas),
					typeof(CanvasScaler)
				});
				_overlayRoot.transform.SetParent(((Component)this).transform, false);
				Canvas component = _overlayRoot.GetComponent<Canvas>();
				component.renderMode = (RenderMode)0;
				component.sortingOrder = 250;
				CanvasScaler component2 = _overlayRoot.GetComponent<CanvasScaler>();
				component2.uiScaleMode = (ScaleMode)1;
				component2.referenceResolution = new Vector2(1920f, 1080f);
				component2.screenMatchMode = (ScreenMatchMode)0;
				component2.matchWidthOrHeight = 0.5f;
				GameObject val = new GameObject("CountdownNumber", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(TextMeshProUGUI)
				});
				val.transform.SetParent(_overlayRoot.transform, false);
				RectTransform component3 = val.GetComponent<RectTransform>();
				component3.anchorMin = new Vector2(0.5f, 0.62f);
				component3.anchorMax = new Vector2(0.5f, 0.62f);
				component3.pivot = new Vector2(0.5f, 0.5f);
				component3.anchoredPosition = Vector2.zero;
				component3.sizeDelta = new Vector2(320f, 170f);
				_countdownLabel = val.GetComponent<TextMeshProUGUI>();
				((TMP_Text)_countdownLabel).font = font;
				((TMP_Text)_countdownLabel).fontSize = 112f;
				((TMP_Text)_countdownLabel).fontStyle = (FontStyles)1;
				((TMP_Text)_countdownLabel).alignment = (TextAlignmentOptions)514;
				((Graphic)_countdownLabel).color = new Color(0.92f, 1f, 0.9f, 1f);
				((TMP_Text)_countdownLabel).outlineWidth = 0.22f;
				((TMP_Text)_countdownLabel).outlineColor = new Color32((byte)12, (byte)18, (byte)14, byte.MaxValue);
				((Graphic)_countdownLabel).raycastTarget = false;
				((TMP_Text)_countdownLabel).text = string.Empty;
				_rewardPopup = new GameObject("TwitchRewardPopup", new Type[4]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(Image),
					typeof(CanvasGroup)
				});
				_rewardPopup.transform.SetParent(_overlayRoot.transform, false);
				_rewardPopupTransform = _rewardPopup.GetComponent<RectTransform>();
				_rewardPopupTransform.anchorMin = Vector2.zero;
				_rewardPopupTransform.anchorMax = Vector2.zero;
				_rewardPopupTransform.pivot = Vector2.zero;
				_rewardPopupTransform.anchoredPosition = new Vector2(42f, 42f);
				_rewardPopupTransform.sizeDelta = new Vector2(430f, 76f);
				Image component4 = _rewardPopup.GetComponent<Image>();
				((Graphic)component4).color = Color32.op_Implicit(new Color32((byte)12, (byte)18, (byte)14, (byte)224));
				((Graphic)component4).raycastTarget = false;
				_rewardPopupCanvasGroup = _rewardPopup.GetComponent<CanvasGroup>();
				GameObject val2 = new GameObject("Accent", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(Image)
				});
				val2.transform.SetParent(_rewardPopup.transform, false);
				RectTransform component5 = val2.GetComponent<RectTransform>();
				component5.anchorMin = new Vector2(0f, 0f);
				component5.anchorMax = new Vector2(0f, 1f);
				component5.pivot = new Vector2(0f, 0.5f);
				component5.anchoredPosition = Vector2.zero;
				component5.sizeDelta = new Vector2(7f, 0f);
				Image component6 = val2.GetComponent<Image>();
				((Graphic)component6).color = Color32.op_Implicit(new Color32((byte)83, (byte)224, (byte)121, byte.MaxValue));
				((Graphic)component6).raycastTarget = false;
				GameObject val3 = new GameObject("RewardName", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(TextMeshProUGUI)
				});
				val3.transform.SetParent(_rewardPopup.transform, false);
				RectTransform component7 = val3.GetComponent<RectTransform>();
				component7.anchorMin = Vector2.zero;
				component7.anchorMax = Vector2.one;
				component7.offsetMin = new Vector2(22f, 8f);
				component7.offsetMax = new Vector2(-14f, -8f);
				_rewardPopupLabel = val3.GetComponent<TextMeshProUGUI>();
				((TMP_Text)_rewardPopupLabel).font = font;
				((TMP_Text)_rewardPopupLabel).fontSize = 22f;
				((TMP_Text)_rewardPopupLabel).fontStyle = (FontStyles)1;
				((TMP_Text)_rewardPopupLabel).alignment = (TextAlignmentOptions)4097;
				((Graphic)_rewardPopupLabel).color = Color32.op_Implicit(new Color32((byte)235, byte.MaxValue, (byte)230, byte.MaxValue));
				((TMP_Text)_rewardPopupLabel).outlineWidth = 0.12f;
				((TMP_Text)_rewardPopupLabel).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue);
				((Graphic)_rewardPopupLabel).raycastTarget = false;
				((TMP_Text)_rewardPopupLabel).text = string.Empty;
				_rewardPopup.SetActive(false);
				_overlayRoot.SetActive(false);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_overlayRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_overlayRoot);
			}
		}
	}
	internal static class FallDamageSafety
	{
		private static int _protectedActorNumber = -1;

		private static float _protectedUntil;

		private static string _reason = "mod movement effect";

		internal static string Reason => _reason;

		internal static void Begin(int actorNumber, float seconds, string reason)
		{
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null || (Object)(object)((MonoBehaviourPun)localCharacter).photonView == (Object)null || ((MonoBehaviourPun)localCharacter).photonView.OwnerActorNr != actorNumber || (Object)(object)localCharacter.data == (Object)null)
			{
				return;
			}
			float num = Time.unscaledTime + Mathf.Clamp(seconds, 1f, 300f);
			int num2;
			if (_protectedActorNumber == actorNumber)
			{
				num2 = ((Time.unscaledTime <= _protectedUntil) ? 1 : 0);
				if (num2 != 0 && !(num >= _protectedUntil))
				{
					goto IL_008b;
				}
			}
			else
			{
				num2 = 0;
			}
			_reason = (string.IsNullOrWhiteSpace(reason) ? "mod movement effect" : reason);
			goto IL_008b;
			IL_008b:
			_protectedActorNumber = actorNumber;
			_protectedUntil = ((num2 != 0) ? Mathf.Max(_protectedUntil, num) : num);
			localCharacter.data.launchedByCannon = true;
		}

		internal static bool IsProtected(Character? character)
		{
			if ((Object)(object)character == (Object)null || !character.IsLocal || (Object)(object)((MonoBehaviourPun)character).photonView == (Object)null || ((MonoBehaviourPun)character).photonView.OwnerActorNr != _protectedActorNumber)
			{
				return false;
			}
			if (Time.unscaledTime <= _protectedUntil)
			{
				return true;
			}
			Clear();
			return false;
		}

		internal static void OnLanded(Character? character)
		{
			if (IsProtected(character))
			{
				if ((Object)(object)character != (Object)null && (Object)(object)character.data != (Object)null)
				{
					character.data.launchedByCannon = false;
					character.data.fallSeconds = 0f;
				}
				Clear();
			}
		}

		internal static void Clear()
		{
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter != (Object)null && (Object)(object)localCharacter.data != (Object)null && (Object)(object)((MonoBehaviourPun)localCharacter).photonView != (Object)null && ((MonoBehaviourPun)localCharacter).photonView.OwnerActorNr == _protectedActorNumber)
			{
				localCharacter.data.launchedByCannon = false;
			}
			_protectedActorNumber = -1;
			_protectedUntil = 0f;
			_reason = "mod movement effect";
		}
	}
	[HarmonyPatch(typeof(CharacterMovement), "CheckFallDamage")]
	internal static class ModFallDamagePatch
	{
		private static bool Prefix(Character ___character)
		{
			if (!FallDamageSafety.IsProtected(___character))
			{
				return true;
			}
			Plugin.Log.LogInfo((object)("Prevented fall damage caused by " + FallDamageSafety.Reason + "."));
			return false;
		}
	}
	[HarmonyPatch(typeof(CharacterMovement), "Land")]
	internal static class ModEffectLandingPatch
	{
		private static void Prefix(Character ___character, out bool __state)
		{
			__state = (Object)(object)___character != (Object)null && (Object)(object)___character.data != (Object)null && ___character.data.sinceGrounded > 0.5f && FallDamageSafety.IsProtected(___character);
		}

		private static void Postfix(Character ___character, bool __state)
		{
			if (__state)
			{
				FallDamageSafety.OnLanded(___character);
			}
		}
	}
	internal static class CredentialProtector
	{
		private struct DataBlob
		{
			internal int Size;

			internal IntPtr Data;
		}

		private const int CryptProtectUiForbidden = 1;

		[DllImport("Crypt32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool CryptProtectData(ref DataBlob dataIn, string description, IntPtr optionalEntropy, IntPtr reserved, IntPtr prompt, int flags, out DataBlob dataOut);

		[DllImport("Crypt32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool CryptUnprotectData(ref DataBlob dataIn, out IntPtr description, IntPtr optionalEntropy, IntPtr reserved, IntPtr prompt, int flags, out DataBlob dataOut);

		[DllImport("Kernel32.dll")]
		private static extern IntPtr LocalFree(IntPtr memory);

		internal static string Protect(string plaintext)
		{
			return Convert.ToBase64String(Transform(Encoding.UTF8.GetBytes(plaintext), protect: true));
		}

		internal static string Unprotect(string protectedBase64)
		{
			byte[] bytes = Transform(Convert.FromBase64String(protectedBase64), protect: false);
			return Encoding.UTF8.GetString(bytes);
		}

		private static byte[] Transform(byte[] input, bool protect)
		{
			DataBlob dataIn = default(DataBlob);
			DataBlob dataOut = default(DataBlob);
			IntPtr description = IntPtr.Zero;
			try
			{
				dataIn.Size = input.Length;
				dataIn.Data = Marshal.AllocHGlobal(Math.Max(input.Length, 1));
				if (input.Length != 0)
				{
					Marshal.Copy(input, 0, dataIn.Data, input.Length);
				}
				if (!(protect ? CryptProtectData(ref dataIn, "PEAK Twitch Integration OAuth credentials", IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 1, out dataOut) : CryptUnprotectData(ref dataIn, out description, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 1, out dataOut)))
				{
					throw new Win32Exception(Marshal.GetLastWin32Error());
				}
				byte[] array = new byte[dataOut.Size];
				if (array.Length != 0)
				{
					Marshal.Copy(dataOut.Data, array, 0, array.Length);
				}
				return array;
			}
			finally
			{
				if (dataIn.Data != IntPtr.Zero)
				{
					Marshal.FreeHGlobal(dataIn.Data);
				}
				if (dataOut.Data != IntPtr.Zero)
				{
					LocalFree(dataOut.Data);
				}
				if (description != IntPtr.Zero)
				{
					LocalFree(description);
				}
			}
		}
	}
	internal static class DeveloperDummyHudFactory
	{
		private const float VerticalOffset = 72f;

		internal static GameObject? TryCreate(Character target, string displayName)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: 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)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			StaminaBar val = GUIManager.instance?.bar;
			if ((Object)(object)target == (Object)null || (Object)(object)val == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null)
			{
				return null;
			}
			GameObject val2 = null;
			try
			{
				val2 = new GameObject("PeakTwitchIntegration_DeveloperDummyHud", new Type[1] { typeof(RectTransform) });
				RectTransform component = val2.GetComponent<RectTransform>();
				((Transform)component).SetParent(((Component)val).transform.parent, false);
				component.anchorMin = Vector2.zero;
				component.anchorMax = Vector2.one;
				component.pivot = new Vector2(0.5f, 0.5f);
				component.offsetMin = Vector2.zero;
				component.offsetMax = Vector2.zero;
				((Transform)component).SetSiblingIndex(((Component)val).transform.GetSiblingIndex() + 1);
				GameObject val3 = Object.Instantiate<GameObject>(((Component)val).gameObject, (Transform)(object)component, false);
				((Object)val3).name = "Developer Dummy Stamina Bar";
				RectTransform component2 = val3.GetComponent<RectTransform>();
				component2.anchoredPosition += new Vector2(0f, 72f);
				if ((Object)(object)val3.GetComponent<StaminaBar>() == (Object)null)
				{
					Object.Destroy((Object)(object)val2);
					return null;
				}
				GameObject val4 = new GameObject("Developer Dummy Status Label", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(TextMeshProUGUI)
				});
				val4.transform.SetParent(val3.transform, false);
				RectTransform component3 = val4.GetComponent<RectTransform>();
				component3.anchorMin = new Vector2(0f, 1f);
				component3.anchorMax = new Vector2(0f, 1f);
				component3.pivot = new Vector2(0f, 0f);
				component3.anchoredPosition = new Vector2(0f, 5f);
				component3.sizeDelta = new Vector2(520f, 30f);
				TextMeshProUGUI component4 = val4.GetComponent<TextMeshProUGUI>();
				Canvas componentInParent = ((Component)val).GetComponentInParent<Canvas>();
				object obj;
				if (componentInParent == null)
				{
					obj = null;
				}
				else
				{
					TextMeshProUGUI componentInChildren = ((Component)componentInParent).GetComponentInChildren<TextMeshProUGUI>(true);
					obj = ((componentInChildren != null) ? ((TMP_Text)componentInChildren).font : null);
				}
				if (obj == null)
				{
					TextMeshProUGUI obj2 = Object.FindAnyObjectByType<TextMeshProUGUI>();
					obj = ((obj2 != null) ? ((TMP_Text)obj2).font : null) ?? TMP_Settings.defaultFontAsset;
				}
				((TMP_Text)component4).font = (TMP_FontAsset)obj;
				((TMP_Text)component4).fontSize = 18f;
				((TMP_Text)component4).fontStyle = (FontStyles)1;
				((TMP_Text)component4).alignment = (TextAlignmentOptions)1025;
				((Graphic)component4).color = Color32.op_Implicit(DeveloperDummyHudMarker.NormalColor);
				((TMP_Text)component4).outlineWidth = 0.14f;
				((TMP_Text)component4).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue);
				((Graphic)component4).raycastTarget = false;
				val3.AddComponent<DeveloperDummyHudMarker>().Initialize(target, component4, displayName);
				return val2;
			}
			catch
			{
				if ((Object)(object)val2 != (Object)null)
				{
					Object.Destroy((Object)(object)val2);
				}
				throw;
			}
		}
	}
	internal sealed class DeveloperDummyHudMarker : MonoBehaviour
	{
		internal static readonly Color32 NormalColor = new Color32((byte)235, byte.MaxValue, (byte)230, byte.MaxValue);

		private static readonly Color32 DamageColor = new Color32(byte.MaxValue, (byte)82, (byte)72, byte.MaxValue);

		private Character? _target;

		private TextMeshProUGUI? _label;

		private string _displayName = "PUSH TEST DUMMY";

		private float _lastInjury;

		private float _damageAmount;

		private float _damageMessageUntil;

		internal Character? Target => _target;

		internal void Initialize(Character target, TextMeshProUGUI label, string displayName)
		{
			_target = target;
			_label = label;
			_displayName = (string.IsNullOrWhiteSpace(displayName) ? "PUSH TEST DUMMY" : displayName);
			_lastInjury = ReadInjury();
			RefreshLabel();
		}

		private void Update()
		{
			if ((Object)(object)_target == (Object)null || (Object)(object)_target.data == (Object)null || (Object)(object)_target.refs?.afflictions == (Object)null)
			{
				if ((Object)(object)((Component)this).gameObject != (Object)null)
				{
					((Component)this).gameObject.SetActive(false);
				}
				return;
			}
			float num = ReadInjury();
			if (num > _lastInjury + 0.0001f)
			{
				_damageAmount = num - _lastInjury;
				_damageMessageUntil = Time.unscaledTime + 2.75f;
			}
			_lastInjury = num;
			RefreshLabel();
		}

		private float ReadInjury()
		{
			Character? target = _target;
			float? obj;
			if (target == null)
			{
				obj = null;
			}
			else
			{
				CharacterRefs refs = target.refs;
				if (refs == null)
				{
					obj = null;
				}
				else
				{
					CharacterAfflictions afflictions = refs.afflictions;
					obj = ((afflictions != null) ? new float?(afflictions.GetCurrentStatus((STATUSTYPE)0)) : ((float?)null));
				}
			}
			float? num = obj;
			return num.GetValueOrDefault();
		}

		private void RefreshLabel()
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_label == (Object)null) && !((Object)(object)_target == (Object)null) && !((Object)(object)_target.data == (Object)null))
			{
				int num = Mathf.RoundToInt(Mathf.Clamp01(_target.data.currentStamina) * 100f);
				int num2 = Mathf.RoundToInt(Mathf.Clamp01(_lastInjury) * 100f);
				if (Time.unscaledTime < _damageMessageUntil)
				{
					int num3 = Mathf.Max(1, Mathf.RoundToInt(_damageAmount * 100f));
					((TMP_Text)_label).text = $"{_displayName}   DAMAGE +{num3}%   INJURY {num2}%";
					((Graphic)_label).color = Color32.op_Implicit(DamageColor);
				}
				else
				{
					string text = (_target.data.dead ? "DEAD" : (_target.data.fullyPassedOut ? "PASSED OUT" : $"STAMINA {num}%   INJURY {num2}%"));
					((TMP_Text)_label).text = _displayName + "   " + text;
					((Graphic)_label).color = Color32.op_Implicit(NormalColor);
				}
			}
		}
	}
	internal static class DeveloperDummyHudContext
	{
		private static Character? _target;

		internal static Character? Target => _target;

		internal static bool Begin(StaminaBar bar)
		{
			DeveloperDummyHudMarker component = ((Component)bar).GetComponent<DeveloperDummyHudMarker>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.Target == (Object)null)
			{
				return false;
			}
			_target = component.Target;
			bar.ChangeBar();
			return true;
		}

		internal static void End()
		{
			_target = null;
		}
	}
	[HarmonyPatch(typeof(StaminaBar), "Update")]
	internal static class DeveloperDummyStaminaBarUpdatePatch
	{
		private static void Prefix(StaminaBar __instance, out bool __state)
		{
			__state = DeveloperDummyHudContext.Begin(__instance);
		}

		private static Exception? Finalizer(Exception? __exception, bool __state)
		{
			if (__state)
			{
				DeveloperDummyHudContext.End();
			}
			return __exception;
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class DeveloperDummyObservedCharacterPatch
	{
		private static void Postfix(ref Character __result)
		{
			Character target = DeveloperDummyHudContext.Target;
			if ((Object)(object)target != (Object)null)
			{
				__result = target;
			}
		}
	}
	internal sealed class DeveloperToolsController : MonoBehaviour
	{
		private const string DummyName = "[DEV] PUSH TEST DUMMY";

		private readonly List<GameObject> _spawnedRoots = new List<GameObject>();

		private Character? _activeDummy;

		private GameObject? _dummyHudRoot;

		private string _status = "Enable the test-bot spawner, then press the configured key.";

		internal string Status => _status;

		internal bool CanUseDeveloperTools
		{
			get
			{
				if (PhotonNetwork.InRoom)
				{
					return PhotonNetwork.IsMasterClient;
				}
				return true;
			}
		}

		internal string AccessStatus
		{
			get
			{
				if (!CanUseDeveloperTools)
				{
					return "HOST ONLY: The lobby host controls the test-bot spawner.";
				}
				if (!PhotonNetwork.InRoom)
				{
					return "Available outside a multiplayer room and to the lobby host.";
				}
				return "Host access confirmed. Only you can spawn the local test bot.";
			}
		}

		internal int ActiveDummyCount
		{
			get
			{
				RemoveMissingReferences();
				return _spawnedRoots.Count;
			}
		}

		private void Update()
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!CanUseDeveloperTools)
			{
				if (_spawnedRoots.Count > 0 || (Object)(object)_activeDummy != (Object)null || (Object)(object)_dummyHudRoot != (Object)null)
				{
					RemoveTestBots();
				}
				_status = "Test-bot spawning is locked to the lobby host.";
			}
			else if (Plugin.DeveloperToolsEnabled)
			{
				RemoveMissingReferences();
				if ((Object)(object)_activeDummy != (Object)null && (Object)(object)_dummyHudRoot == (Object)null)
				{
					_dummyHudRoot = TryCreateDummyHud(_activeDummy);
				}
				Keyboard current = Keyboard.current;
				if (current != null && ((ButtonControl)current[Plugin.DeveloperTestBotSpawnKey]).wasPressedThisFrame)
				{
					SpawnOrReplaceTestBot();
				}
			}
		}

		internal void SpawnOrReplaceTestBot()
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			if (!CanUseDeveloperTools)
			{
				_status = "Test-bot spawning is locked to the lobby host.";
				Plugin.Log.LogWarning((object)_status);
				return;
			}
			if (!Plugin.DeveloperToolsEnabled)
			{
				_status = "Test-bot spawning is disabled.";
				return;
			}
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null || localCharacter.data.dead || localCharacter.data.fullyPassedOut)
			{
				_status = "Enter a playable PEAK level before spawning a test bot.";
				return;
			}
			BotSpawner val = FindBotSpawner();
			if ((Object)(object)val == (Object)null || (Object)(object)val.botPrefab == (Object)null)
			{
				_status = "This level has no loaded PEAK bot prefab.";
				Plugin.Log.LogWarning((object)_status);
				return;
			}
			RemoveTestBots();
			try
			{
				Vector3 val2 = FindSpawnPosition(localCharacter);
				GameObject val3 = Object.Instantiate<GameObject>(val.botPrefab, val2, Quaternion.identity);
				((Object)val3).name = "PEAK Developer Push Test Dummy";
				Character componentInChildren = val3.GetComponentInChildren<Character>(true);
				if ((Object)(object)componentInChildren == (Object)null)
				{
					Object.Destroy((Object)(object)val3);
					_status = "PEAK's bot prefab did not contain a Character component.";
					Plugin.Log.LogWarning((object)_status);
					return;
				}
				(((Component)componentInChildren).gameObject.GetComponent<DeveloperTestBotMarker>() ?? ((Component)componentInChildren).gameObject.AddComponent<DeveloperTestBotMarker>()).SpawnedRoot = val3;
				componentInChildren.isBot = true;
				componentInChildren.isZombie = false;
				componentInChildren.isScoutmaster = false;
				AccessTools.Field(typeof(Character), "<characterName>k__BackingField")?.SetValue(componentInChildren, "[DEV] PUSH TEST DUMMY");
				DisableBotControl(val3);
				_spawnedRoots.Add(val3);
				_activeDummy = componentInChildren;
				_dummyHudRoot = TryCreateDummyHud(componentInChildren);
				_status = (((Object)(object)_dummyHudRoot != (Object)null) ? string.Format("Spawned {0} with a live damage/status bar. Look at it and press {1}.", "[DEV] PUSH TEST DUMMY", Plugin.TeammatePushKey) : "Spawned [DEV] PUSH TEST DUMMY. Its damage/status bar will appear when PEAK's HUD is ready.");
				Plugin.Log.LogInfo((object)_status);
			}
			catch (Exception arg)
			{
				_status = "Could not spawn the PEAK test bot. See the BepInEx log.";
				Plugin.Log.LogError((object)$"Developer test-bot spawn failed: {arg}");
			}
		}

		internal void RemoveTestBots()
		{
			RemoveDummyHud();
			_activeDummy = null;
			foreach (GameObject spawnedRoot in _spawnedRoots)
			{
				if ((Object)(object)spawnedRoot != (Object)null)
				{
					Object.Destroy((Object)(object)spawnedRoot);
				}
			}
			_spawnedRoots.Clear();
			DeveloperTestBotMarker[] array = Resources.FindObjectsOfTypeAll<DeveloperTestBotMarker>();
			foreach (DeveloperTestBotMarker developerTestBotMarker in array)
			{
				if (!((Object)(object)developerTestBotMarker == (Object)null))
				{
					GameObject val = (GameObject)(((Object)(object)developerTestBotMarker.SpawnedRoot != (Object)null) ? ((object)developerTestBotMarker.SpawnedRoot) : ((object)((Component)developerTestBotMarker).gameObject));
					if ((Object)(object)val != (Object)null)
					{
						Object.Destroy((Object)(object)val);
					}
				}
			}
			_status = "Removed all developer test bots.";
		}

		private static GameObject? TryCreateDummyHud(Character target)
		{
			try
			{
				return DeveloperDummyHudFactory.TryCreate(target, "[DEV] PUSH TEST DUMMY");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogWarning((object)$"Developer dummy HUD creation failed: {arg}");
				return null;
			}
		}

		private void RemoveDummyHud()
		{
			if ((Object)(object)_dummyHudRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_dummyHudRoot);
			}
			_dummyHudRoot = null;
		}

		private static BotSpawner? FindBotSpawner()
		{
			BotSpawner val = Object.FindAnyObjectByType<BotSpawner>();
			if ((Object)(object)val != (Object)null && (Object)(object)val.botPrefab != (Object)null)
			{
				return val;
			}
			return ((IEnumerable<BotSpawner>)Resources.FindObjectsOfTypeAll<BotSpawner>()).FirstOrDefault((Func<BotSpawner, bool>)((BotSpawner candidate) => (Object)(object)candidate != (Object)null && (Object)(object)candidate.botPrefab != (Object)null));
		}

		private static Vector3 FindSpawnPosition(Character local)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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_0070: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: 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)
			//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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: 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)
			Vector3 val = Vector3.ProjectOnPlane(((Component)local).transform.forward, Vector3.up);
			if ((Object)(object)MainCamera.instance != (Object)null)
			{
				val = Vector3.ProjectOnPlane(((Component)MainCamera.instance).transform.forward, Vector3.up);
			}
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = Vector3.forward;
			}
			((Vector3)(ref val)).Normalize();
			Vector3 val2 = local.Center + val;
			RaycastHit[] array = Physics.RaycastAll(val2 + Vector3.up * 3f, Vector3.down, 10f, -5, (QueryTriggerInteraction)1);
			Array.Sort(array, (RaycastHit left, RaycastHit right) => ((RaycastHit)(ref left)).distance.CompareTo(((RaycastHit)(ref right)).distance));
			RaycastHit[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				RaycastHit val3 = array2[num];
				if (!((Object)(object)((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent<Character>() == (Object)(object)local))
				{
					return ((RaycastHit)(ref val3)).point + Vector3.up * 0.2f;
				}
			}
			return val2 + Vector3.up * 0.5f;
		}

		private static void DisableBotControl(GameObject spawnedRoot)
		{
			MonoBehaviour[] componentsInChildren = spawnedRoot.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !(val is DeveloperTestBotMarker))
				{
					string name = ((object)val).GetType().Name;
					if (val is Bot || val is BotToCharacterTranslator || val is BotMoverRagdoll || name.StartsWith("Bot", StringComparison.Ordinal) || name == "Navigator")
					{
						((Behaviour)val).enabled = false;
					}
				}
			}
		}

		private void RemoveMissingReferences()
		{
			_spawnedRoots.RemoveAll((GameObject root) => (Object)(object)root == (Object)null);
			if ((Object)(object)_activeDummy == (Object)null)
			{
				_activeDummy = null;
				RemoveDummyHud();
			}
		}

		private void OnDestroy()
		{
			RemoveTestBots();
		}
	}
	internal sealed class DeveloperTestBotMarker : MonoBehaviour
	{
		internal GameObject? SpawnedRoot { get; set; }
	}
	internal sealed class IncomingVoiceProcessor : IDisposable
	{
		private readonly object _sync = new object();

		private readonly int _sampleRate;

		private readonly int _channels;

		private readonly LoudnessLeveler _leveler;

		private RnNoiseDenoiser? _denoiser;

		private bool _disposed;

		internal IncomingVoiceProcessor(int sampleRate, int channels)
		{
			_sampleRate = Math.Max(sampleRate, 1);
			_channels = Math.Max(channels, 1);
			_leveler = new LoudnessLeveler(_sampleRate, _channels);
			try
			{
				_denoiser = new RnNoiseDenoiser(_sampleRate, _channels);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("RNNoise could not be initialized. Loudness leveling will still work. " + ex.Message));
			}
		}

		internal void Process(float[] samples, ProcessorSettings settings)
		{
			lock (_sync)
			{
				if (!_disposed && settings.Enabled)
				{
					float voiceProbability = -1f;
					if (settings.NoiseSuppression && _denoiser != null)
					{
						voiceProbability = _denoiser.Process(samples, settings.SuppressionStrength);
					}
					if (settings.AutoLevel)
					{
						_leveler.Process(samples, settings, voiceProbability);
					}
					else
					{
						_leveler.Reset();
					}
				}
			}
		}

		public void Dispose()
		{
			lock (_sync)
			{
				if (!_disposed)
				{
					_disposed = true;
					_denoiser?.Dispose();
					_denoiser = null;
				}
			}
		}
	}
	internal readonly struct ProcessorSettings
	{
		internal bool Enabled { get; }

		internal bool NoiseSuppression { get; }

		internal float SuppressionStrength { get; }

		internal bool AutoLevel { get; }

		internal float TargetRmsDb { get; }

		internal float EqualizedVolumePercent { get; }

		internal float MinimumGainDb { get; }

		internal float MaximumGainDb { get; }

		internal float SpeechGateDb { get; }

		internal ProcessorSettings(bool enabled, bool noiseSuppression, float suppressionStrength, bool autoLevel, float targetRmsDb, float equalizedVolumePercent, float minimumGainDb, float maximumGainDb, float speechGateDb)
		{
			Enabled = enabled;
			NoiseSuppression = noiseSuppression;
			SuppressionStrength = Clamp(suppressionStrength, 0f, 1f);
			AutoLevel = autoLevel;
			TargetRmsDb = Clamp(targetRmsDb, -30f, -10f);
			EqualizedVolumePercent = Clamp(equalizedVolumePercent, 0f, 200f);
			MinimumGainDb = Clamp(minimumGainDb, -30f, 0f);
			MaximumGainDb = Clamp(maximumGainDb, 0f, 30f);
			SpeechGateDb = Clamp(speechGateDb, -80f, -30f);
		}

		private static float Clamp(float value, float minimum, float maximum)
		{
			if (!(value < minimum))
			{
				if (!(value > maximum))
				{
					return value;
				}
				return maximum;
			}
			return minimum;
		}
	}
	internal sealed class LoudnessLeveler
	{
		private const float Epsilon = 1E-07f;

		private const float LimiterCeiling = 0.96f;

		private const float VoiceProbabilityThreshold = 0.45f;

		private readonly int _sampleRate;

		private readonly int _channels;

		private float _estimatedSpeechDb;

		private float _gainDb;

		private float _limiterGain = 1f;

		private bool _hasSpeechEstimate;

		internal float CurrentGainDb => _gainDb + LinearToDb(_limiterGain);

		internal LoudnessLeveler(int sampleRate, int channels)
		{
			_sampleRate = Math.Max(sampleRate, 1);
			_channels = Math.Max(channels, 1);
		}

		internal void Process(float[] samples, ProcessorSettings settings, float voiceProbability)
		{
			if (samples.Length == 0)
			{
				return;
			}
			double num = 0.0;
			foreach (float num2 in samples)
			{
				num += (double)(num2 * num2);
			}
			float num3 = LinearToDb((float)Math.Sqrt(num / (double)samples.Length));
			float durationSeconds = (float)samples.Length / (float)(_sampleRate * _channels);
			if (voiceProbability >= 0.45f || num3 >= settings.SpeechGateDb)
			{
				if (!_hasSpeechEstimate)
				{
					_estimatedSpeechDb = num3;
					_hasSpeechEstimate = true;
				}
				else
				{
					float timeConstantSeconds = ((num3 > _estimatedSpeechDb) ? 0.08f : 0.9f);
					float num4 = TimeBlend(durationSeconds, timeConstantSeconds);
					_estimatedSpeechDb += (num3 - _estimatedSpeechDb) * num4;
				}
				float num5 = Clamp(settings.TargetRmsDb - _estimatedSpeechDb, settings.MinimumGainDb, settings.MaximumGainDb);
				float timeConstantSeconds2 = ((num5 < _gainDb) ? 0.05f : 0.6f);
				float num6 = TimeBlend(durationSeconds, timeConstantSeconds2);
				_gainDb += (num5 - _gainDb) * num6;
			}
			float num7 = DbToLinear(_gainDb);
			float num8 = settings.EqualizedVolumePercent / 100f;
			float num9 = num7 * num8;
			float num10 = 0f;
			for (int j = 0; j < samples.Length; j++)
			{
				num10 = Math.Max(num10, Math.Abs(samples[j]) * num9);
			}
			float num11 = ((num10 > 0.96f) ? (0.96f / num10) : 1f);
			if (num11 < _limiterGain)
			{
				_limiterGain = num11;
			}
			else
			{
				float num12 = TimeBlend(durationSeconds, 0.25f);
				_limiterGain += (num11 - _limiterGain) * num12;
			}
			float num13 = num9 * _limiterGain;
			for (int k = 0; k < samples.Length; k++)
			{
				samples[k] = Clamp(samples[k] * num13, -1f, 1f);
			}
		}

		internal void Reset()
		{
			_estimatedSpeechDb = 0f;
			_gainDb = 0f;
			_limiterGain = 1f;
			_hasSpeechEstimate = false;
		}

		private static float TimeBlend(float durationSeconds, float timeConstantSeconds)
		{
			return 1f - (float)Math.Exp((0f - durationSeconds) / Math.Max(timeConstantSeconds, 0.001f));
		}

		private static float DbToLinear(float decibels)
		{
			return (float)Math.Pow(10.0, (double)decibels / 20.0);
		}

		private static float LinearToDb(float linear)
		{
			return 20f * (float)Math.Log10(Math.Max(linear, 1E-07f));
		}

		private static float Clamp(float value, float minimum, float maximum)
		{
			if (!(value < minimum))
			{
				if (!(value > maximum))
				{
					return value;
				}
				return maximum;
			}
			return minimum;
		}
	}
	[HarmonyPatch(typeof(MainMenu), "Start")]
	internal static class MainMenuTwitchIntegrationPatch
	{
		private const string RootName = "PeakTwitchIntegration_Menu";

		[HarmonyPostfix]
		private static void Postfix(MainMenu __instance)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Scene scene = ((Component)__instance).gameObject.scene;
			if (!((Scene)(ref scene)).IsValid() || HasMenu(scene))
			{
				return;
			}
			try
			{
				AddMenu(scene);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Could not add the PEAK Twitch Integration menu: {arg}");
			}
		}

		private static bool HasMenu(Scene scene)
		{
			GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
			for (int i = 0; i < rootGameObjects.Length; i++)
			{
				if (((Object)rootGameObjects[i]).name == "PeakTwitchIntegration_Menu")
				{
					return true;
				}
			}
			return false;
		}

		private static void AddMenu(Scene menuScene)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Expected O, but got Unknown
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: 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_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = CreateTwitchLogoTexture();
			Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			((Object)val2).name = "PEAK Twitch logo";
			GameObject val3 = new GameObject("PeakTwitchIntegration_Menu", new Type[6]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(GraphicRaycaster),
				typeof(TwitchLogoAssetOwner),
				typeof(TwitchIntegrationMenuController)
			});
			SceneManager.MoveGameObjectToScene(val3, menuScene);
			Canvas component = val3.GetComponent<Canvas>();
			component.renderMode = (RenderMode)0;
			component.sortingOrder = 100;
			CanvasScaler component2 = val3.GetComponent<CanvasScaler>();
			component2.uiScaleMode = (ScaleMode)1;
			component2.referenceResolution = new Vector2(1920f, 1080f);
			component2.screenMatchMode = (ScreenMatchMode)0;
			component2.matchWidthOrHeight = 0.5f;
			TwitchLogoAssetOwner component3 = val3.GetComponent<TwitchLogoAssetOwner>();
			component3.Texture = val;
			component3.Sprite = val2;
			GameObject val4 = new GameObject("OpenTwitchIntegration", new Type[4]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image),
				typeof(Button)
			});
			val4.transform.SetParent(val3.transform, false);
			RectTransform component4 = val4.GetComponent<RectTransform>();
			component4.anchorMin = new Vector2(1f, 1f);
			component4.anchorMax = new Vector2(1f, 1f);
			component4.pivot = new Vector2(1f, 1f);
			component4.anchoredPosition = new Vector2(-22f, -22f);
			component4.sizeDelta = new Vector2(64f, 64f);
			Image component5 = val4.GetComponent<Image>();
			component5.sprite = val2;
			component5.preserveAspect = true;
			((Graphic)component5).raycastTarget = true;
			Button component6 = val4.GetComponent<Button>();
			((Selectable)component6).targetGraphic = (Graphic)(object)component5;
			ColorBlock colors = ((Selectable)component6).colors;
			((ColorBlock)(ref colors)).normalColor = new Color(1f, 1f, 1f, 0.82f);
			((ColorBlock)(ref colors)).highlightedColor = Color.white;
			((ColorBlock)(ref colors)).pressedColor = new Color(0.78f, 0.68f, 0.96f, 1f);
			((ColorBlock)(ref colors)).selectedColor = ((ColorBlock)(ref colors)).highlightedColor;
			((ColorBlock)(ref colors)).fadeDuration = 0.08f;
			((Selectable)component6).colors = colors;
			val3.GetComponent<TwitchIntegrationMenuController>().Initialize(component6, val4);
			Plugin.Log.LogInfo((object)"Added the compact Twitch logo button to the main menu.");
		}

		private static Texture2D CreateTwitchLogoTexture()
		{
			//IL_004a: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Expected O, but got Unknown
			Color32 value = default(Color32);
			((Color32)(ref value))..ctor((byte)0, (byte)0, (byte)0, (byte)0);
			Color32 color = default(Color32);
			((Color32)(ref color))..ctor((byte)145, (byte)70, byte.MaxValue, byte.MaxValue);
			Color32 color2 = default(Color32);
			((Color32)(ref color2))..ctor(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
			Color32[] array = (Color32[])(object)new Color32[65536];
			Array.Fill(array, value);
			Vector2[] polygon = (Vector2[])(object)new Vector2[8]
			{
				new Vector2(20f, 236f),
				new Vector2(228f, 236f),
				new Vector2(228f, 88f),
				new Vector2(180f, 40f),
				new Vector2(124f, 40f),
				new Vector2(88f, 4f),
				new Vector2(88f, 40f),
				new Vector2(20f, 40f)
			};
			Vector2[] polygon2 = (Vector2[])(object)new Vector2[8]
			{
				new Vector2(48f, 208f),
				new Vector2(200f, 208f),
				new Vector2(200f, 104f),
				new Vector2(164f, 68f),
				new Vector2(112f, 68f),
				new Vector2(76f, 32f),
				new Vector2(76f, 68f),
				new Vector2(48f, 68f)
			};
			FillPolygon(array, 256, polygon, color);
			FillPolygon(array, 256, polygon2, color2);
			FillRectangle(array, 256, 88, 112, 112, 176, color);
			FillRectangle(array, 256, 144, 112, 168, 176, color);
			Texture2D val = new Texture2D(256, 256, (TextureFormat)4, false)
			{
				name = "PEAK Twitch logo",
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			val.SetPixels32(array);
			val.Apply(false, true);
			return val;
		}

		private static void FillPolygon(Color32[] pixels, int textureSize, Vector2[] polygon, Color32 color)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < textureSize; i++)
			{
				for (int j = 0; j < textureSize; j++)
				{
					if (ContainsPoint(polygon, (float)j + 0.5f, (float)i + 0.5f))
					{
						pixels[i * textureSize + j] = color;
					}
				}
			}
		}

		private static bool ContainsPoint(Vector2[] polygon, float x, float y)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			int num = 0;
			int num2 = polygon.Length - 1;
			while (num < polygon.Length)
			{
				Vector2 val = polygon[num];
				Vector2 val2 = polygon[num2];
				if (val.y > y != val2.y > y && x < (val2.x - val.x) * (y - val.y) / (val2.y - val.y) + val.x)
				{
					flag = !flag;
				}
				num2 = num++;
			}
			return flag;
		}

		private static void FillRectangle(Color32[] pixels, int textureSize, int left, int bottom, int right, int top, Color32 color)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			for (int i = bottom; i < top; i++)
			{
				for (int j = left; j < right; j++)
				{
					pixels[i * textureSize + j] = color;
				}
			}
		}
	}
	[HarmonyPatch(typeof(PauseMenuMainPage), "Start")]
	internal static class PauseMenuTwitchIntegrationPatch
	{
		private const string ButtonName = "PeakTwitchIntegration_PauseMenuButton";

		[HarmonyPostfix]
		private static void Postfix(PauseMenuMainPage __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).GetComponent<TwitchIntegrationMenuController>() != (Object)null)
			{
				return;
			}
			try
			{
				object? obj = AccessTools.Field(typeof(PauseMenuMainPage), "m_settingsButton")?.GetValue(__instance);
				Button val = (Button)((obj is Button) ? obj : null);
				if (val == null)
				{
					val = __instance.resumeButton;
				}
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Could not add the Twitch Integration button because the pause menu had no usable button template.");
					return;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent, false);
				((Object)val2).name = "PeakTwitchIntegration_PauseMenuButton";
				val2.transform.SetSiblingIndex(Mathf.Min(((Component)val).transform.GetSiblingIndex() + 1, val2.transform.parent.childCount - 1));
				LocalizedText[] componentsInChildren = val2.GetComponentsInChildren<LocalizedText>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					((Behaviour)componentsInChildren[i]).enabled = false;
				}
				TMP_Text[] componentsInChildren2 = val2.GetComponentsInChildren<TMP_Text>(true);
				for (int i = 0; i < componentsInChildren2.Length; i++)
				{
					componentsInChildren2[i].text = "TWITCH INTEGRATION";
				}
				Button component = val2.GetComponent<Button>();
				((UnityEventBase)component.onClick).RemoveAllListeners();
				((Selectable)component).interactable = true;
				ConfigureNavigation(val, component);
				((Component)__instance).gameObject.AddComponent<TwitchIntegrationMenuController>().Initialize(component, val2);
				val2.SetActive(true);
				Plugin.Log.LogInfo((object)"Added the Twitch Integration button to the pause menu.");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Could not add the PEAK Twitch Integration button to the pause menu: {arg}");
			}
		}

		private static void ConfigureNavigation(Button template, Button button)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_0058: 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_0061: Invalid comparison between Unknown and I4
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			Navigation navigation = ((Selectable)template).navigation;
			if ((int)((Navigation)(ref navigation)).mode != 4)
			{
				((Selectable)button).navigation = navigation;
				return;
			}
			Selectable selectOnDown = ((Navigation)(ref navigation)).selectOnDown;
			Navigation navigation2 = navigation;
			((Navigation)(ref navigation2)).selectOnUp = (Selectable)(object)template;
			((Navigation)(ref navigation2)).selectOnDown = selectOnDown;
			((Selectable)button).navigation = navigation2;
			((Navigation)(ref navigation)).selectOnDown = (Selectable)(object)button;
			((Selectable)template).navigation = navigation;
			if ((Object)(object)selectOnDown != (Object)null)
			{
				Navigation navigation3 = selectOnDown.navigation;
				if ((int)((Navigation)(ref navigation3)).mode == 4)
				{
					((Navigation)(ref navigation3)).selectOnUp = (Selectable)(object)button;
					selectOnDown.navigation = navigation3;
				}
			}
		}
	}
	[HarmonyPatch(typeof(PauseMenuHandler), "Update")]
	internal static class PauseMenuHandlerUpdatePatch
	{
		[HarmonyPrefix]
		private static bool Prefix()
		{
			return !TwitchIntegrationMenuController.ShouldBlockPauseMenuUpdate;
		}
	}
	internal sealed class TwitchIntegrationMenuController : MonoBehaviour
	{
		private enum MenuTab
		{
			Integration,
			DeveloperTools
		}

		private const int WindowIdBase = 781245;

		private static int _openWindowCount;

		private static int _suppressPauseMenuThroughFrame = -1;

		private Button _openButton;

		private GameObject _openLabel;

		private bool _windowOpen;

		private bool _initialized;

		private string _clientIdDraft = string.Empty;

		private string _cannonCostDraft = string.Empty;

		private Rect _windowRect;

		private Vector2 _scroll;

		private MenuTab _selectedTab;

		private Texture2D? _windowBackground;

		private Texture2D? _panelBackground;

		private GUIStyle? _windowStyle;

		private GUIStyle? _panelStyle;

		private GUIStyle? _sectionHeaderStyle;

		internal static bool ShouldBlockPauseMenuUpdate
		{
			get
			{
				if (_openWindowCount <= 0)
				{
					return Time.frameCount <= _suppressPauseMenuThroughFrame;
				}
				return true;
			}
		}

		internal void Initialize(Button openButton, GameObject openLabel)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			_openButton = openButton;
			_openLabel = openLabel;
			((UnityEvent)_openButton.onClick).AddListener(new UnityAction(ToggleWindow));
			_clientIdDraft = Plugin.TwitchClientId;
			_cannonCostDraft = Plugin.CannonLaunchCost.ToString();
			_initialized = true;
		}

		private void ToggleWindow()
		{
			SetWindowOpen(!_windowOpen);
		}

		private void SetWindowOpen(bool open)
		{
			if (_windowOpen != open)
			{
				_windowOpen = open;
				_openWindowCount = Mathf.Max(0, _openWindowCount + (open ? 1 : (-1)));
				if ((Object)(object)_openButton != (Object)null)
				{
					((Component)_openButton).gameObject.SetActive(!open);
				}
				if ((Object)(object)_openLabel != (Object)null)
				{
					_openLabel.SetActive(!open);
				}
				if (open)
				{
					CenterWindow();
				}
			}
		}

		private void Update()
		{
			if (_windowOpen && Input.GetKeyDown((KeyCode)27))
			{
				_suppressPauseMenuThroughFrame = Time.frameCount;
				SetWindowOpen(open: false);
			}
		}

		private void OnGUI()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			if (_windowOpen)
			{
				if (((Rect)(ref _windowRect)).width <= 0f || ((Rect)(ref _windowRect)).height <= 0f)
				{
					CenterWindow();
				}
				EnsureStyles();
				GUI.depth = -1000;
				Color color = GUI.color;
				Color contentColor = GUI.contentColor;
				Color backgroundColor = GUI.backgroundColor;
				GUI.color = Color.white;
				GUI.contentColor = new Color(0.94f, 1f, 0.97f, 1f);
				GUI.backgroundColor = new Color(0.3f, 0.72f, 0.52f, 1f);
				_windowRect = GUI.Window(0xBEBBD ^ ((Object)this).GetInstanceID(), _windowRect, new WindowFunction(DrawWindow), "PEAK TWITCH INTEGRATION", _windowStyle);
				GUI.color = color;
				GUI.contentColor = contentColor;
				GUI.backgroundColor = backgroundColor;
			}
		}

		private void EnsureStyles()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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_005e: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Expected O, but got Unknown
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Expected O, but got Unknown
			if (_windowStyle == null)
			{
				_windowBackground = MakeSolidTexture("PEAK Twitch Integration opaque window", new Color32((byte)16, (byte)31, (byte)27, byte.MaxValue));
				_panelBackground = MakeSolidTexture("PEAK Twitch Integration panel", new Color32((byte)28, (byte)53, (byte)45, byte.MaxValue));
				_windowStyle = new GUIStyle(GUI.skin.window);
				_windowStyle.normal.background = _windowBackground;
				_windowStyle.onNormal.background = _windowBackground;
				_windowStyle.normal.textColor = new Color(0.78f, 1f, 0.88f, 1f);
				_windowStyle.fontStyle = (FontStyle)1;
				_windowStyle.border = new RectOffset(0, 0, 0, 0);
				_windowStyle.padding = new RectOffset(16, 16, 30, 14);
				_panelStyle = new GUIStyle(GUI.skin.box);
				_panelStyle.normal.background = _panelBackground;
				_panelStyle.normal.textColor = Color.white;
				_panelStyle.border = new RectOffset(0, 0, 0, 0);
				_panelStyle.padding = new RectOffset(12, 12, 10, 10);
				_panelStyle.margin = new RectOffset(2, 2, 4, 4);
				_sectionHeaderStyle = new GUIStyle(GUI.skin.box);
				_sectionHeaderStyle.normal.background = _panelBackground;
				_sectionHeaderStyle.normal.textColor = new Color(0.72f, 1f, 0.84f, 1f);
				_sectionHeaderStyle.border = new RectOffset(0, 0, 0, 0);
				_sectionHeaderStyle.fontStyle = (FontStyle)1;
				_sectionHeaderStyle.alignment = (TextAnchor)4;
				_sectionHeaderStyle.padding = new RectOffset(8, 8, 7, 7);
			}
		}

		private static Texture2D MakeSolidTexture(string textureName, Color32 color)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false)
			{
				name = textureName,
				hideFlags = (HideFlags)61,
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)0
			};
			val.SetPixel(0, 0, Color32.op_Implicit(color));
			val.Apply(false, true);
			return val;
		}

		private void CenterWindow()
		{
			//IL_0057: 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)
			float num = Mathf.Clamp((float)Screen.width - 80f, 480f, 720f);
			float num2 = Mathf.Clamp((float)Screen.height - 80f, 520f, 720f);
			_windowRect = new Rect(((float)Screen.width - num) * 0.5f, ((float)Screen.height - num2) * 0.5f, num, num2);
		}

		private void DrawWindow(int windowId)
		{
			//IL_0020: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (GUI.Button(new Rect(((Rect)(ref _windowRect)).width - 34f, 4f, 28f, 24f), "X"))
			{
				SetWindowOpen(open: false);
			}
			GUILayout.Space(12f);
			DrawTabs();
			GUILayout.Space(8f);
			_scroll = GUILayout.BeginScrollView(_scroll, Array.Empty<GUILayoutOption>());
			if (_selectedTab == MenuTab.DeveloperTools)
			{
				DrawDeveloperTools();
			}
			else
			{
				DrawTwitchConnection();
				GUILayout.Space(16f);
				DrawFeatures();
			}
			GUILayout.EndScrollView();
			GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width - 42f, 30f));
		}

		private void DrawTabs()
		{
			//IL_000a: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			Color backgroundColor = GUI.backgroundColor;
			GUI.backgroundColor = ((_selectedTab == MenuTab.Integration) ? new Color(0.36f, 0.82f, 0.6f, 1f) : new Color(0.22f, 0.42f, 0.36f, 1f));
			if (GUILayout.Button("INTEGRATION", Array.Empty<GUILayoutOption>()))
			{
				SelectTab(MenuTab.Integration);
			}
			GUI.backgroundColor = ((_selectedTab == MenuTab.DeveloperTools) ? new Color(0.86f, 0.62f, 0.22f, 1f) : new Color(0.42f, 0.34f, 0.2f, 1f));
			if (GUILayout.Button("DEVELOPER TOOLS", Array.Empty<GUILayoutOption>()))
			{
				SelectTab(MenuTab.DeveloperTools);
			}
			GUI.backgroundColor = backgroundColor;
			GUILayout.EndHorizontal();
		}

		private void SelectTab(MenuTab tab)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (_selectedTab != tab)
			{
				_selectedTab = tab;
				_scroll = Vector2.zero;
			}
		}

		private void DrawTwitchConnection()
		{
			TwitchConnectionSnapshot snapshot = Plugin.Twitch.Snapshot;
			GUILayout.Label("TWITCH CONNECTION", _sectionHeaderStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label(snapshot.Status, Array.Empty<GUILayoutOption>());
			if (!string.IsNullOrEmpty(snapshot.AccountName))
			{
				GUILayout.Label("Account: " + snapshot.AccountName, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(8f);
			GUILayout.Label("Public Twitch Client ID:", Array.Empty<GUILayoutOption>());
			_clientIdDraft = GUILayout.TextField(_clientIdDraft ?? string.Empty, 128, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			bool enabled = GUI.enabled;
			GUI.enabled = !snapshot.IsBusy && !snapshot.IsConnected;
			if (GUILayout.Button("CONNECT", Array.Empty<GUILayoutOption>()))
			{
				Plugin.SetTwitchClientId(_clientIdDraft);
				Plugin.Twitch.Connect();
			}
			GUI.enabled = !snapshot.IsBusy;
			if (GUILayout.Button("NEW AUTHORIZATION", Array.Empty<GUILayoutOption>()))
			{
				Plugin.SetTwitchClientId(_clientIdDraft);
				Plugin.Twitch.Connect(forceNewAuthorization: true);
			}
			GUI.enabled = snapshot.IsBusy || snapshot.IsConnected;
			if (GUILayout.Button("DISCONNECT", Array.Empty<GUILayoutOption>()))
			{
				Plugin.Twitch.Disconnect(forgetAuthorization: false);
			}
			GUI.enabled = enabled;
			GUILayout.EndHorizontal();
			if (!string.IsNullOrEmpty(snapshot.UserCode))
			{
				GUILayout.Space(10f);
				GUILayout.Label("Activation code: " + snapshot.UserCode, _panelStyle, Array.Empty<GUILayoutOption>());
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				if (GUILayout.Button("OPEN TWITCH ACTIVATION", Array.Empty<GUILayoutOption>()))
				{
					Application.OpenURL(snapshot.VerificationUri);
				}
				if (GUILayout.Button("COPY CODE", Array.Empty<GUILayoutOption>()))
				{
					GUIUtility.systemCopyBuffer = snapshot.UserCode;
				}
				GUILayout.EndHorizontal();
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("CREATE / FIND CLIENT ID", Array.Empty<GUILayoutOption>()))
			{
				Application.OpenURL("https://dev.twitch.tv/console/apps");
			}
			if (GUILayout.Button("FORGET SAVED ACCOUNT", Array.Empty<GUILayoutOption>()))
			{
				Plugin.Twitch.Disconnect(forgetAuthorization: true);
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(6f);
			GUILayout.Label("The Client ID is public. OAuth tokens are encrypted for your Windows account. The integration listens directly to Twitch EventSub while PEAK is running.", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Last redemption: " + snapshot.LastRedemption, Array.Empty<GUILayoutOption>());
		}

		private void DrawFeatures()
		{
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e6: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label("FEATURES", _sectionHeaderStyle, Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical(_panelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label("PEAK VOICE BALANCE", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Noise suppression, per-player automatic loudness leveling and a shared output level.", Array.Empty<GUILayoutOption>());
			bool flag = GUILayout.Toggle(Plugin.ProcessingEnabled, Plugin.ProcessingEnabled ? "ENABLED" : "DISABLED", Array.Empty<GUILayoutOption>());
			if (flag != Plugin.ProcessingEnabled)
			{
				Plugin.SetProcessingEnabled(flag);
			}
			GUILayout.Label($"Shared output: {Mathf.RoundToInt(Plugin.EqualizedVolumePercent)}%", Array.Empty<GUILayoutOption>());
			float num = GUILayout.HorizontalSlider(Plugin.EqualizedVolumePercent, 0f, 200f, Array.Empty<GUILayoutOption>());
			if (Mathf.Abs(num - Plugin.EqualizedVolumePercent) >= 0.5f)
			{
				Plugin.SetEqualizedVolumePercent(Mathf.Round(num));
			}
			GUILayout.EndVertical();
			GUILayout.Space(8f);
			GUILayout.BeginVertical(_panelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label("CANNON LAUNCH", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Automatically manages the '[PEAK] Cannon Launch' Channel Points reward. The viewer must enter an exact PEAK player name.", Array.Empty<GUILayoutOption>());
			bool flag2 = GUILayout.Toggle(Plugin.CannonLaunchEnabled, Plugin.CannonLaunchEnabled ? "ENABLED" : "DISABLED", Array.Empty<GUILayoutOption>());
			if (flag2 != Plugin.CannonLaunchEnabled)
			{
				Plugin.SetCannonLaunchEnabled(flag2);
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Reward cost:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
			_cannonCostDraft = GUILayout.TextField(_cannonCostDraft ?? string.Empty, 9, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) });
			if (GUILayout.Button("APPLY", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }))
			{
				if (int.TryParse(_cannonCostDraft, out var result) && result > 0)
				{
					Plugin.SetCannonLaunchCost(result);
					_cannonCostDraft = Plugin.CannonLaunchCost.ToString();
				}
				else
				{
					_cannonCostDraft = Plugin.CannonLaunchCost.ToString();
				}
			}
			if (GUILayout.Button("SYNC REWARD", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }))
			{
				Plugin.Twitch.SynchronizeCannonReward();
			}
			GUILayout.EndHorizontal();
			GUILayout.Label($"Launch power: {Mathf.RoundToInt(Plugin.CannonLaunchForce)}", Array.Empty<GUILayoutOption>());
			float num2 = Mathf.Round(GUILayout.HorizontalSlider(Plugin.CannonLaunchForce, 250f, 5000f, Array.Empty<GUILayoutOption>()) / 50f) * 50f;
			if (Mathf.Abs(num2 - Plugin.CannonLaunchForce) >= 25f)
			{
				Plugin.SetCannonLaunchForce(num2);
			}
			GUILayout.Label(Plugin.Twitch.Snapshot.CannonRewardStatus, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Unknown, missing, ambiguous, dead or unavailable players are rejected and the viewer's points are refunded.", Array.Empty<GUILayoutOption>());
			GUILayout.Label("The launch reaches vanilla clients. Guaranteed no-fall-damage for a remote player requires this mod on that player's client too.", Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
			GUILayout.Space(8f);
			GUILayout.BeginVertical(_panelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label("TEAMMATE PUSH", Array.Empty<GUILayoutOption>());
			GUILayout.Label($"Look directly at a nearby teammate and press {Plugin.TeammatePushKey} to shove them backward and knock them over.", Array.Empty<GUILayoutOption>());
			bool flag3 = GUILayout.Toggle(Plugin.TeammatePushEnabled, Plugin.TeammatePushEnabled ? "ENABLED" : "DISABLED", Array.Empty<GUILayoutOption>());
			if (flag3 != Plugin.TeammatePushEnabled)
			{
				Plugin.SetTeammatePushEnabled(flag3);
			}
			GUILayout.Label($"Shared push force: {Mathf.RoundToInt(Plugin.EffectiveTeammatePushForce)}", Array.Empty<GUILayoutOption>());
			bool enabled = GUI.enabled;
			GUI.enabled = enabled && Plugin.CanEditTeammatePushForce;
			float num3 = Mathf.Round(GUILayout.HorizontalSlider(Plugin.EffectiveTeammatePushForce, 200f, 800f, Array.Empty<GUILayoutOption>()) / 25f) * 25f;
			if (Plugin.CanEditTeammatePushForce && Mathf.Abs(num3 - Plugin.EffectiveTeammatePushForce) >= 12.5f)
			{
				Plugin.SetTeammatePushForce(num3);
			}
			GUI.enabled = enabled;
			GUILayout.Label(Plugin.TeammatePushForceControlStatus, Array.Empty<GUILayoutOption>());
			GUILayout.Label($"Key: {Plugin.TeammatePushKey}   |   Range: {Plugin.TeammatePushRange:0.##} m   |   Fall protection: {Plugin.TeammatePushFallProtectionSeconds:0.#} s", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Fall protection ends at the first genuine landing. The pushed player needs this mod for that protection. No Twitch chat permission is used.", Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
		}

		private void DrawDeveloperTools()
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label("DEVELOPER TOOLS", _sectionHeaderStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Local test utilities for development. The push dummy is not a real Photon user and does not occupy a multiplayer slot.", Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.BeginVertical(_panelStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label("PUSH TEST BOT", Array.Empty<GUILayoutOption>());
			GUILayout.Label("Spawns one stationary PEAK Character/Ragdoll dummy one metre in front of you. A second PEAK-style bar shows its stamina, debuffs, and received damage.", Array.Empty<GUILayoutOption>());
			bool canU