Decompiled source of Vasodilation v2.0.0

tony4twentys-Vasodilation.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyTitle("Vasodilation")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Vasodilation")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("15fa7750-965f-4249-837f-3d54157b5401")]
[assembly: AssemblyFileVersion("2.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.0.0")]
[module: UnverifiableCode]
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace VasodilationMod
{
	internal enum WarmthSource
	{
		None,
		Sprint,
		Jump,
		SprintJump,
		ClimbJump,
		WallClimb,
		RopeClimb,
		VineClimb
	}
	[BepInPlugin("tony4twentys.Vasodilation", "Vasodilation", "2.0.0")]
	[BepInProcess("PEAK.exe")]
	public class VasodilationPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "tony4twentys.Vasodilation";

		public const string PluginName = "Vasodilation";

		public const string PluginVersion = "2.0.0";

		internal const byte EventCode = 162;

		internal const int ProtocolVersion = 3;

		internal const string EventMagic = "tony4twentys.Vasodilation";

		internal const int ConfigPayloadLength = 13;

		internal static ConfigEntry<float> SprintRatio;

		internal static ConfigEntry<float> JumpRatio;

		internal static ConfigEntry<float> SprintJumpRatio;

		internal static ConfigEntry<float> ClimbJumpRatio;

		internal static ConfigEntry<float> WallClimbRatio;

		internal static ConfigEntry<float> RopeClimbRatio;

		internal static ConfigEntry<float> VineClimbRatio;

		internal static ConfigEntry<float> EmoteColdPerSecond;

		internal static ConfigEntry<float> GateOn;

		internal static ConfigEntry<float> GateOff;

		internal static ConfigEntry<bool> VerboseLogs;

		internal static float syncedSprintRatio = 0.5f;

		internal static float syncedJumpRatio = 0.25f;

		internal static float syncedSprintJumpRatio = 0.25f;

		internal static float syncedClimbJumpRatio = 0.25f;

		internal static float syncedWallClimbRatio = 0.5f;

		internal static float syncedRopeClimbRatio = 1f;

		internal static float syncedVineClimbRatio = 0.75f;

		internal static float syncedEmoteColdPerSecond = 0.01f;

		internal static float syncedGateOn = 0.025f;

		internal static float syncedGateOff = 0f;

		internal static bool isHandshakeValid = true;

		private Harmony _harmony;

		private bool _warmingActive;

		private bool _loggedTickError;

		private bool _loggedStaminaError;

		private float _nextEmoteLog;

		private static readonly float[] NextSourceLog = new float[8];

		private static float _alpineCachedAt = -999f;

		private static bool _alpineCached;

		internal static VasodilationPlugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static bool IsGameplayEnabled
		{
			get
			{
				try
				{
					if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)
					{
						return true;
					}
				}
				catch
				{
					return isHandshakeValid;
				}
				return isHandshakeValid;
			}
		}

		internal static bool DebugLogs
		{
			get
			{
				if (VerboseLogs != null)
				{
					return VerboseLogs.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			ApplyLocalConfig();
			if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)
			{
				isHandshakeValid = true;
			}
			_harmony = new Harmony("tony4twentys.Vasodilation");
			PatchAllSafe();
			VasoNet.Ensure();
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			Log.LogInfo((object)string.Format("{0} v{1} loaded. Photon {2}.", "Vasodilation", "2.0.0", (byte)162));
		}

		private void Update()
		{
			try
			{
				TickEmoteWarmth(Time.deltaTime);
			}
			catch (Exception ex)
			{
				LogOnce(ref _loggedTickError, "[Vasodilation] Emote warmth tick failed.", ex);
			}
		}

		private void BindConfig()
		{
			if ((0u | (DropOldConfig("General", "ColdPerSecond") ? 1u : 0u) | (DropOldConfig("General", "TickSeconds") ? 1u : 0u) | (DropOldConfig("General", "StaminaToColdRatio") ? 1u : 0u) | (DropOldConfig("General", "EmoteColdPerSecond") ? 1u : 0u)) != 0)
			{
				((BaseUnityPlugin)this).Config.Save();
			}
			SprintRatio = BindWarmthRatio("Sprint", 0.5f, "Grounded sprint.");
			JumpRatio = BindWarmthRatio("Jump", 0.25f, "Standing jump.");
			SprintJumpRatio = BindWarmthRatio("SprintJump", 0.25f, "Jump while sprinting.");
			ClimbJumpRatio = BindWarmthRatio("ClimbJump", 0.25f, "Wall sprint-boost. Alpine walls are ignored.");
			WallClimbRatio = BindWarmthRatio("WallClimb", 0.5f, "Wall hang, move, or re-grab. Alpine walls are ignored.");
			RopeClimbRatio = BindWarmthRatio("RopeClimb", 1f, "Climbing up a rope. Hang and climb-down are ignored.");
			VineClimbRatio = BindWarmthRatio("VineClimb", 0.75f, "Climbing a vine. Hang, slide, and grab are ignored.");
			EmoteColdPerSecond = ((BaseUnityPlugin)this).Config.Bind<float>("Warmth", "EmoteColdPerSecond", 0.01f, "Host only. Cold removed per second during Dance, Crashout, Shimmy, Backflip, Wave, or Bully. Emotes do not use stamina. Host value is synced to the room.");
			GateOn = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ColdGateOn", 0.025f, "Host only. Start warming once Cold is above this. Host value is synced to the room.");
			GateOff = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ColdGateOff", 0f, "Host only. Stop warming once Cold is at or below this. Host value is synced to the room.");
			VerboseLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogs", false, "Local only. Log warmth about once per second per move. Not host-synced.");
		}

		private ConfigEntry<float> BindWarmthRatio(string key, float defaultValue, string what)
		{
			return ((BaseUnityPlugin)this).Config.Bind<float>("Warmth", key, defaultValue, "Host only. Cold removed per stamina spent: " + what + " 0.5 = half, 1 = 1:1. Host value is synced to the room.");
		}

		private bool DropOldConfig(string section, string key)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			ConfigDefinition val = new ConfigDefinition(section, key);
			if (!((BaseUnityPlugin)this).Config.ContainsKey(val))
			{
				return false;
			}
			((BaseUnityPlugin)this).Config.Remove(val);
			return true;
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			if (args == null || (object)args.ChangedSetting != VerboseLogs)
			{
				VasoNet.OnLocalSettingChanged();
			}
		}

		internal static void ApplyLocalConfig()
		{
			ApplySyncedValues(GateOn.Value, GateOff.Value, EmoteColdPerSecond.Value, SprintRatio.Value, JumpRatio.Value, SprintJumpRatio.Value, ClimbJumpRatio.Value, WallClimbRatio.Value, RopeClimbRatio.Value, VineClimbRatio.Value);
		}

		internal static object[] BuildConfigPayload(byte sub)
		{
			return new object[13]
			{
				"tony4twentys.Vasodilation", 3, sub, GateOn.Value, GateOff.Value, EmoteColdPerSecond.Value, SprintRatio.Value, JumpRatio.Value, SprintJumpRatio.Value, ClimbJumpRatio.Value,
				WallClimbRatio.Value, RopeClimbRatio.Value, VineClimbRatio.Value
			};
		}

		internal static void ApplyRemoteConfig(object[] arr)
		{
			if (arr == null || arr.Length < 13)
			{
				throw new InvalidOperationException("Vasodilation config payload too short.");
			}
			ApplySyncedValues(Convert.ToSingle(arr[3]), Convert.ToSingle(arr[4]), Convert.ToSingle(arr[5]), Convert.ToSingle(arr[6]), Convert.ToSingle(arr[7]), Convert.ToSingle(arr[8]), Convert.ToSingle(arr[9]), Convert.ToSingle(arr[10]), Convert.ToSingle(arr[11]), Convert.ToSingle(arr[12]));
		}

		private static void ApplySyncedValues(float gateOn, float gateOff, float emote, float sprint, float jump, float sprintJump, float climbJump, float wall, float rope, float vine)
		{
			syncedGateOn = Mathf.Max(0f, gateOn);
			syncedGateOff = Mathf.Max(0f, gateOff);
			syncedEmoteColdPerSecond = Mathf.Max(0f, emote);
			syncedSprintRatio = Mathf.Max(0f, sprint);
			syncedJumpRatio = Mathf.Max(0f, jump);
			syncedSprintJumpRatio = Mathf.Max(0f, sprintJump);
			syncedClimbJumpRatio = Mathf.Max(0f, climbJump);
			syncedWallClimbRatio = Mathf.Max(0f, wall);
			syncedRopeClimbRatio = Mathf.Max(0f, rope);
			syncedVineClimbRatio = Mathf.Max(0f, vine);
			if (syncedGateOff > syncedGateOn)
			{
				syncedGateOff = syncedGateOn;
			}
		}

		internal static void DebugLog(string message)
		{
			if (DebugLogs)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}

		internal static void LogOnce(ref bool flag, string message, Exception ex)
		{
			if (flag)
			{
				return;
			}
			flag = true;
			if (ex != null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)(message + " " + ex));
				}
			}
			else
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)message);
				}
			}
		}

		internal static void OnStaminaSpent(Character character, float spent)
		{
			VasodilationPlugin instance = Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			try
			{
				instance.TryWarmFromStamina(character, spent);
			}
			catch (Exception ex)
			{
				LogOnce(ref instance._loggedStaminaError, "[Vasodilation] Stamina warmth failed.", ex);
			}
		}

		private void TryWarmFromStamina(Character character, float spent)
		{
			if (spent <= 1E-05f || !IsGameplayEnabled || !CanWarm(character))
			{
				return;
			}
			WarmthSource warmthSource = GetWarmthSource(character);
			if (warmthSource != WarmthSource.None && UpdateWarmingGate(character))
			{
				float num = spent * GetSyncedRatio(warmthSource);
				if (!(num <= 0f))
				{
					character.refs.afflictions.SubtractStatus((STATUSTYPE)2, num, false, false);
					MaybeLogWarmth(warmthSource, spent, num);
				}
			}
		}

		private void TickEmoteWarmth(float deltaTime)
		{
			if (deltaTime <= 0f || syncedEmoteColdPerSecond <= 0f || !IsGameplayEnabled)
			{
				return;
			}
			Character localCharacter = Character.localCharacter;
			if (!EmoteWarmth.IsWarming(localCharacter) || !CanWarm(localCharacter) || !UpdateWarmingGate(localCharacter))
			{
				return;
			}
			float num = syncedEmoteColdPerSecond * deltaTime;
			if (!(num <= 0f))
			{
				localCharacter.refs.afflictions.SubtractStatus((STATUSTYPE)2, num, false, false);
				if (DebugLogs && Time.unscaledTime >= _nextEmoteLog)
				{
					_nextEmoteLog = Time.unscaledTime + 1f;
					DebugLog("[Vasodilation] emote coldDelta=" + num.ToString("0.####"));
				}
			}
		}

		private bool CanWarm(Character character)
		{
			if ((Object)(object)character == (Object)null || !character.IsLocal)
			{
				return false;
			}
			if ((Object)(object)character.data == (Object)null || character.refs == null || (Object)(object)character.refs.afflictions == (Object)null)
			{
				return false;
			}
			if (character.data.dead)
			{
				return false;
			}
			return true;
		}

		private bool UpdateWarmingGate(Character character)
		{
			float currentStatus = character.refs.afflictions.GetCurrentStatus((STATUSTYPE)2);
			if (currentStatus <= syncedGateOff)
			{
				_warmingActive = false;
			}
			else if (currentStatus > syncedGateOn)
			{
				_warmingActive = true;
			}
			return _warmingActive;
		}

		internal static WarmthSource GetWarmthSource(Character character)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null)
			{
				return WarmthSource.None;
			}
			CharacterData data = character.data;
			if (WarmthSpendMark.Ignore)
			{
				return WarmthSource.None;
			}
			if (WarmthSpendMark.ClimbJump)
			{
				if (!IsAlpine())
				{
					return WarmthSource.ClimbJump;
				}
				return WarmthSource.None;
			}
			if (data.isJumping)
			{
				if (!data.isSprinting)
				{
					return WarmthSource.Jump;
				}
				return WarmthSource.SprintJump;
			}
			if (data.isRopeClimbing)
			{
				if (!IsClimbingRopeUp(character))
				{
					return WarmthSource.None;
				}
				return WarmthSource.RopeClimb;
			}
			if (data.isVineClimbing)
			{
				if (!IsClimbingVine(character))
				{
					return WarmthSource.None;
				}
				return WarmthSource.VineClimb;
			}
			if (data.isClimbing)
			{
				if (!IsAlpine())
				{
					return WarmthSource.WallClimb;
				}
				return WarmthSource.None;
			}
			if (data.isSprinting)
			{
				return WarmthSource.Sprint;
			}
			return WarmthSource.None;
		}

		internal static float GetSyncedRatio(WarmthSource source)
		{
			return source switch
			{
				WarmthSource.Sprint => syncedSprintRatio, 
				WarmthSource.Jump => syncedJumpRatio, 
				WarmthSource.SprintJump => syncedSprintJumpRatio, 
				WarmthSource.ClimbJump => syncedClimbJumpRatio, 
				WarmthSource.WallClimb => syncedWallClimbRatio, 
				WarmthSource.RopeClimb => syncedRopeClimbRatio, 
				WarmthSource.VineClimb => syncedVineClimbRatio, 
				_ => 0f, 
			};
		}

		private static bool IsClimbingRopeUp(Character character)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)character.input == (Object)null)
			{
				return false;
			}
			return character.input.movementInput.y > 0.01f;
		}

		private static bool IsClimbingVine(Character character)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)character.input == (Object)null)
			{
				return false;
			}
			if (character.refs != null && (Object)(object)character.refs.vineClimbing != (Object)null && character.refs.vineClimbing.Sliding())
			{
				return false;
			}
			return Mathf.Abs(character.input.movementInput.y) >= 0.01f;
		}

		private void MaybeLogWarmth(WarmthSource source, float spent, float amount)
		{
			if (!DebugLogs)
			{
				return;
			}
			if (source >= WarmthSource.None && (int)source < NextSourceLog.Length)
			{
				float unscaledTime = Time.unscaledTime;
				if (!(spent < 0.02f) || !(unscaledTime < NextSourceLog[(int)source]))
				{
					NextSourceLog[(int)source] = unscaledTime + 1f;
					DebugLog($"[Vasodilation] stam={spent:0.####} coldDelta={amount:0.####} source={source}");
				}
			}
		}

		internal static bool IsAlpine()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Invalid comparison between Unknown and I4
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime - _alpineCachedAt < 0.5f)
			{
				return _alpineCached;
			}
			_alpineCachedAt = unscaledTime;
			try
			{
				MapHandler instance = Singleton<MapHandler>.Instance;
				_alpineCached = (Object)(object)instance != (Object)null && (int)instance.GetCurrentBiome() == 2;
			}
			catch
			{
				_alpineCached = false;
			}
			return _alpineCached;
		}

		private void PatchAllSafe()
		{
			Type[] types = typeof(VasodilationPlugin).Assembly.GetTypes();
			foreach (Type type in types)
			{
				if (!(type == null) && type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0)
				{
					try
					{
						_harmony.CreateClassProcessor(type).Patch();
					}
					catch (Exception ex)
					{
						Log.LogError((object)("[Vasodilation] Patch failed on " + type.Name + ": " + ex.Message));
					}
				}
			}
		}
	}
	internal sealed class VasoNet : MonoBehaviourPunCallbacks, IOnEventCallback
	{
		private const byte SubConfig = 1;

		private const byte SubConfigRequest = 2;

		private const float HostSyncTimeoutSeconds = 12f;

		private const float HostSyncRetrySeconds = 2f;

		internal static bool HostModConfirmed;

		private Coroutine _debounce;

		private Coroutine _hostSyncWatch;

		private bool _syncingFromNet;

		internal static VasoNet Instance { get; private set; }

		internal static void Ensure()
		{
			//IL_0013: 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_001e: Expected O, but got Unknown
			if ((Object)(object)Instance != (Object)null)
			{
				return;
			}
			GameObject val = new GameObject("Vasodilation_Net");
			Object.DontDestroyOnLoad((Object)val);
			Instance = val.AddComponent<VasoNet>();
			try
			{
				if (PhotonNetwork.InRoom)
				{
					Instance.HandleJoinedRoom();
				}
			}
			catch
			{
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		public override void OnEnable()
		{
			((MonoBehaviourPunCallbacks)this).OnEnable();
			PhotonNetwork.AddCallbackTarget((object)this);
		}

		public override void OnDisable()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			((MonoBehaviourPunCallbacks)this).OnDisable();
		}

		public override void OnJoinedRoom()
		{
			HandleJoinedRoom();
		}

		public override void OnLeftRoom()
		{
			StopHostSyncWatch();
			HostModConfirmed = false;
			VasodilationPlugin.isHandshakeValid = true;
			VasodilationPlugin.ApplyLocalConfig();
		}

		public override void OnMasterClientSwitched(Player newMasterClient)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				HostModConfirmed = true;
				VasodilationPlugin.isHandshakeValid = true;
				VasodilationPlugin.ApplyLocalConfig();
				BroadcastNow();
				StopHostSyncWatch();
			}
			else
			{
				HostModConfirmed = false;
				VasodilationPlugin.isHandshakeValid = false;
				RequestHostSync();
				RestartHostSyncWatch();
			}
		}

		public override void OnPlayerEnteredRoom(Player newPlayer)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				((MonoBehaviour)this).StartCoroutine(BroadcastAfterDelay(0.5f));
			}
		}

		public void OnEvent(EventData photonEvent)
		{
			if (photonEvent == null || photonEvent.Code != 162 || !(photonEvent.CustomData is object[] array) || array.Length < 3 || !(array[0] is string text) || text != "tony4twentys.Vasodilation" || ToInt(array[1]) != 3)
			{
				return;
			}
			byte b = ToByte(array[2]);
			if (b == 2 && PhotonNetwork.IsMasterClient)
			{
				BroadcastNow();
			}
			else
			{
				if (b != 1 || PhotonNetwork.IsMasterClient)
				{
					return;
				}
				try
				{
					_syncingFromNet = true;
					VasodilationPlugin.ApplyRemoteConfig(array);
					StopHostSyncWatch();
					HostModConfirmed = true;
					VasodilationPlugin.isHandshakeValid = true;
					VasodilationPlugin.Log.LogInfo((object)"[Vasodilation] Host config sync applied.");
				}
				catch (Exception ex)
				{
					VasodilationPlugin.Log.LogError((object)("[Vasodilation] Config sync failed: " + ex.Message));
				}
				finally
				{
					_syncingFromNet = false;
				}
			}
		}

		internal static void OnLocalSettingChanged()
		{
			if (!((Object)(object)Instance != (Object)null) || !Instance._syncingFromNet)
			{
				if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)
				{
					VasodilationPlugin.ApplyLocalConfig();
				}
				ScheduleBroadcast();
			}
		}

		internal static void ScheduleBroadcast()
		{
			if (!((Object)(object)Instance == (Object)null) && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient)
			{
				if (Instance._debounce != null)
				{
					((MonoBehaviour)Instance).StopCoroutine(Instance._debounce);
				}
				Instance._debounce = ((MonoBehaviour)Instance).StartCoroutine(Instance.DebounceBroadcast());
			}
		}

		private void HandleJoinedRoom()
		{
			if (PhotonNetwork.IsMasterClient)
			{
				HostModConfirmed = true;
				VasodilationPlugin.isHandshakeValid = true;
				VasodilationPlugin.ApplyLocalConfig();
				BroadcastNow();
				StopHostSyncWatch();
			}
			else
			{
				HostModConfirmed = false;
				VasodilationPlugin.isHandshakeValid = false;
				RequestHostSync();
				RestartHostSyncWatch();
			}
		}

		private static void RequestHostSync()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient)
			{
				PhotonNetwork.RaiseEvent((byte)162, (object)new object[3]
				{
					"tony4twentys.Vasodilation",
					3,
					(byte)2
				}, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)2
				}, SendOptions.SendReliable);
			}
		}

		private void RestartHostSyncWatch()
		{
			StopHostSyncWatch();
			if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient)
			{
				_hostSyncWatch = ((MonoBehaviour)this).StartCoroutine(WatchForHostSync());
			}
		}

		private void StopHostSyncWatch()
		{
			if (_hostSyncWatch != null)
			{
				((MonoBehaviour)this).StopCoroutine(_hostSyncWatch);
				_hostSyncWatch = null;
			}
		}

		private IEnumerator WatchForHostSync()
		{
			float elapsed = 0f;
			float nextRetry = 2f;
			while (elapsed < 12f)
			{
				if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient || HostModConfirmed)
				{
					yield break;
				}
				elapsed += Time.unscaledDeltaTime;
				if (elapsed >= nextRetry)
				{
					RequestHostSync();
					nextRetry += 2f;
				}
				yield return null;
			}
			if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient && !HostModConfirmed)
			{
				VasodilationPlugin.isHandshakeValid = false;
				VasodilationPlugin.Log.LogWarning((object)"[Vasodilation] Host does not appear to have Vasodilation — warmth disabled.");
			}
			_hostSyncWatch = null;
		}

		private IEnumerator DebounceBroadcast()
		{
			yield return (object)new WaitForSeconds(0.25f);
			BroadcastNow();
			_debounce = null;
		}

		private IEnumerator BroadcastAfterDelay(float seconds)
		{
			yield return (object)new WaitForSeconds(seconds);
			BroadcastNow();
		}

		private static void BroadcastNow()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0030: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient)
			{
				PhotonNetwork.RaiseEvent((byte)162, (object)VasodilationPlugin.BuildConfigPayload(1), new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				}, SendOptions.SendReliable);
			}
		}

		private static int ToInt(object value)
		{
			return Convert.ToInt32(value);
		}

		private static byte ToByte(object value)
		{
			return Convert.ToByte(value);
		}
	}
	internal static class EmoteWarmth
	{
		internal static bool LastIsWarming;

		private static readonly string[] WarmTokens = new string[6] { "dance", "crashout", "shimmy", "backflip", "wave", "bully" };

		internal static bool IsWarmingName(string emoteName)
		{
			if (string.IsNullOrEmpty(emoteName))
			{
				return false;
			}
			string text = emoteName.ToLowerInvariant();
			for (int i = 0; i < WarmTokens.Length; i++)
			{
				if (text.IndexOf(WarmTokens[i], StringComparison.Ordinal) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		internal static bool IsWarming(Character character)
		{
			if (!LastIsWarming)
			{
				return false;
			}
			if ((Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.animations == (Object)null)
			{
				return false;
			}
			return character.refs.animations.emoting;
		}

		internal static void Remember(CharacterAnimations animations, string emoteName)
		{
			if (!((Object)(object)animations == (Object)null) && !string.IsNullOrEmpty(emoteName))
			{
				Character character = animations.character;
				if (!((Object)(object)character == (Object)null) && character.IsLocal)
				{
					LastIsWarming = IsWarmingName(emoteName);
				}
			}
		}
	}
	internal static class WarmthSpendMark
	{
		internal static bool Ignore;

		internal static bool ClimbJump;
	}
	[HarmonyPatch(typeof(CharacterVineClimbing), "GrabVineRpc")]
	internal static class VineGrabSkipPatch
	{
		private static void Prefix()
		{
			WarmthSpendMark.Ignore = true;
		}

		private static Exception Finalizer(Exception __exception)
		{
			WarmthSpendMark.Ignore = false;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(CharacterGrabbing), "KickCast")]
	internal static class KickSkipPatch
	{
		private static void Prefix()
		{
			WarmthSpendMark.Ignore = true;
		}

		private static Exception Finalizer(Exception __exception)
		{
			WarmthSpendMark.Ignore = false;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(CharacterClimbing), "RPCA_ClimbJump")]
	internal static class ClimbJumpMarkPatch
	{
		private static void Prefix()
		{
			WarmthSpendMark.ClimbJump = true;
		}

		private static Exception Finalizer(Exception __exception)
		{
			WarmthSpendMark.ClimbJump = false;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Character), "UseStamina")]
	internal static class UseStaminaWarmthPatch
	{
		private static bool _logged;

		private static void Prefix(Character __instance, out float __state)
		{
			__state = -1f;
			try
			{
				if (!((Object)(object)__instance == (Object)null) && __instance.IsLocal)
				{
					__state = __instance.GetTotalStamina();
				}
			}
			catch (Exception ex)
			{
				VasodilationPlugin.LogOnce(ref _logged, "[Vasodilation] UseStamina prefix failed.", ex);
			}
		}

		private static void Postfix(Character __instance, float __state)
		{
			if (__state < 0f || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			try
			{
				float num = __state - __instance.GetTotalStamina();
				if (num > 1E-05f)
				{
					VasodilationPlugin.OnStaminaSpent(__instance, num);
				}
			}
			catch (Exception ex)
			{
				VasodilationPlugin.LogOnce(ref _logged, "[Vasodilation] UseStamina postfix failed.", ex);
			}
		}
	}
	[HarmonyPatch(typeof(CharacterAnimations), "RPCA_PlayRemove")]
	internal static class EmotePlayPatch
	{
		private static bool _logged;

		private static void Postfix(CharacterAnimations __instance, string emoteName)
		{
			try
			{
				EmoteWarmth.Remember(__instance, emoteName);
			}
			catch (Exception ex)
			{
				VasodilationPlugin.LogOnce(ref _logged, "[Vasodilation] Emote postfix failed.", ex);
			}
		}
	}
	[HarmonyPatch(typeof(CharacterAnimations), "PlayEmote")]
	internal static class EmoteLocalPlayPatch
	{
		private static bool _logged;

		private static void Postfix(CharacterAnimations __instance, string emoteName)
		{
			try
			{
				EmoteWarmth.Remember(__instance, emoteName);
			}
			catch (Exception ex)
			{
				VasodilationPlugin.LogOnce(ref _logged, "[Vasodilation] PlayEmote postfix failed.", ex);
			}
		}
	}
}