Decompiled source of SecondAscent v1.1.0

SecondAscent.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DG.Tweening;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json.Linq;
using Peak;
using Peak.Afflictions;
using Photon.Pun;
using Photon.Realtime;
using SecondAscent.Death;
using SecondAscent.Domain;
using SecondAscent.Effects;
using SecondAscent.Runtime;
using SecondAscent.Skeletons;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Core;

[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("SecondAscent")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+bbaa751e8111a0c149634c10396bd8e0fdbef8ca")]
[assembly: AssemblyProduct("SecondAscent")]
[assembly: AssemblyTitle("SecondAscent")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 SecondAscent
{
	internal static class GameBindings
	{
		private static MethodInfo _reviveCharacter;

		private static MethodInfo _endGame;

		private static MethodInfo _die;

		private static MethodInfo _warpPlayer;

		private static MethodInfo _fall;

		private static MethodInfo _stick;

		private static MethodInfo _stickPartExists;

		private static MethodInfo _getBodypart;

		private static MethodInfo _getBodypartRig;

		private static MethodInfo _moveBodypartTowardsPoint;

		private static MethodInfo _updateWeight;

		private static FieldInfo _lowGravAmount;

		private static FieldInfo _isInvincible;

		private static FieldInfo _skeletonFlag;

		private static FieldInfo _reviveUsed;

		private static FieldInfo _waterJumpModifier;

		private static MethodInfo _throwAchievement;

		internal static FieldInfo ConstructingField { get; private set; }

		internal static FieldInfo CurrentConstructHitField { get; private set; }

		internal static bool TryInitialize(out string reason)
		{
			_reviveCharacter = AccessTools.Method(typeof(Character), "ReviveCharacter", new Type[1] { typeof(bool) }, (Type[])null);
			_endGame = AccessTools.Method(typeof(Character), "EndGame", (Type[])null, (Type[])null);
			_die = AccessTools.Method(typeof(Character), "RPCA_Die", (Type[])null, (Type[])null);
			_warpPlayer = AccessTools.Method(typeof(Character), "WarpPlayer", new Type[2]
			{
				typeof(Vector3),
				typeof(bool)
			}, (Type[])null);
			_fall = AccessTools.Method(typeof(Character), "Fall", new Type[2]
			{
				typeof(float),
				typeof(float)
			}, (Type[])null);
			_stick = AccessTools.Method(typeof(Character), "RPCA_Stick", new Type[5]
			{
				typeof(BodypartType),
				typeof(Vector3),
				typeof(Vector3),
				typeof(STATUSTYPE),
				typeof(float)
			}, (Type[])null);
			_stickPartExists = AccessTools.Method(typeof(Character), "StickPartExists", new Type[1] { typeof(Bodypart) }, (Type[])null);
			_getBodypart = AccessTools.Method(typeof(Character), "GetBodypart", new Type[1] { typeof(BodypartType) }, (Type[])null);
			_getBodypartRig = AccessTools.Method(typeof(Character), "GetBodypartRig", new Type[1] { typeof(BodypartType) }, (Type[])null);
			_moveBodypartTowardsPoint = AccessTools.Method(typeof(Character), "MoveBodypartTowardsPoint", new Type[4]
			{
				typeof(BodypartType),
				typeof(Vector3),
				typeof(float),
				typeof(float)
			}, (Type[])null);
			_updateWeight = AccessTools.Method(typeof(CharacterAfflictions), "UpdateWeight", (Type[])null, (Type[])null);
			_lowGravAmount = AccessTools.Field(typeof(CharacterData), "lowGravAmount");
			_isInvincible = AccessTools.Field(typeof(CharacterData), "isInvincible");
			_skeletonFlag = AccessTools.Field(typeof(CharacterData), "_isSkeleton");
			_reviveUsed = AccessTools.Field(typeof(RespawnChest), "ReviveUsed");
			_waterJumpModifier = AccessTools.Field(typeof(CharacterMovement), "waterJumpModifier");
			_throwAchievement = AccessTools.Method(typeof(AchievementManager), "ThrowAchievement", new Type[1] { typeof(ACHIEVEMENTTYPE) }, (Type[])null);
			ConstructingField = AccessTools.Field(typeof(Constructable), "constructing");
			CurrentConstructHitField = AccessTools.Field(typeof(Constructable), "currentConstructHit");
			if (_reviveCharacter == null)
			{
				reason = "Character.ReviveCharacter(bool) was not found in this PEAK build.";
				return false;
			}
			if (_endGame == null)
			{
				reason = "Character.EndGame() was not found in this PEAK build.";
				return false;
			}
			if (_die == null || _warpPlayer == null)
			{
				reason = "Character death/warp methods were not found in this PEAK build.";
				return false;
			}
			if (ConstructingField == null || CurrentConstructHitField == null)
			{
				reason = "Constructable construction fields were not found in this PEAK build.";
				return false;
			}
			reason = null;
			return true;
		}

		internal static void ReviveForReanimation(Character character)
		{
			Invoke(_reviveCharacter, character, false);
		}

		internal static void Revive(Character character, bool applyStatus)
		{
			Invoke(_reviveCharacter, character, applyStatus);
		}

		internal static void RunVanillaDeath(Character character)
		{
			Invoke(_die, character);
		}

		internal static void Warp(Character character, Vector3 position, bool poof)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			Invoke(_warpPlayer, character, position, poof);
		}

		internal static void Fall(Character character, float seconds, float screenShake = 0f)
		{
			if ((Object)(object)character != (Object)null && _fall != null)
			{
				Invoke(_fall, character, seconds, screenShake);
			}
		}

		internal static void SlipLikeBanana(Character character)
		{
			//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_004d: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)character == (Object)null))
			{
				Vector3 lookDirection_Flat = character.data.lookDirection_Flat;
				Rigidbody bodypartRig = GetBodypartRig(character, (BodypartType)16);
				Rigidbody bodypartRig2 = GetBodypartRig(character, (BodypartType)13);
				Rigidbody bodypartRig3 = GetBodypartRig(character, (BodypartType)0);
				Rigidbody bodypartRig4 = GetBodypartRig(character, (BodypartType)4);
				Fall(character, 2f);
				if (bodypartRig != null)
				{
					bodypartRig.AddForce((lookDirection_Flat + Vector3.up) * 200f, (ForceMode)1);
				}
				if (bodypartRig2 != null)
				{
					bodypartRig2.AddForce((lookDirection_Flat + Vector3.up) * 200f, (ForceMode)1);
				}
				if (bodypartRig3 != null)
				{
					bodypartRig3.AddForce(Vector3.up * 1500f, (ForceMode)1);
				}
				if (bodypartRig4 != null)
				{
					bodypartRig4.AddForce(lookDirection_Flat * -300f, (ForceMode)1);
				}
				GameAssetCatalog.PlayBananaSlipSound(character.Center);
			}
		}

		internal static void EndGame(Character character)
		{
			Invoke(_endGame, character);
		}

		internal static bool IsConstructing(ScoutEffigy effigy)
		{
			return (bool)ConstructingField.GetValue(effigy);
		}

		internal static RaycastHit GetConstructHit(ScoutEffigy effigy)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			return (RaycastHit)CurrentConstructHitField.GetValue(effigy);
		}

		internal static bool StickToMovingBodypart(Character victim, Character source, BodypartType victimPart, BodypartType sourcePart, Vector3 worldAnchor)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			ConfigurableJoint added;
			return StickToMovingBodypart(victim, source, victimPart, sourcePart, worldAnchor, out added);
		}

		internal static bool StickToMovingBodypart(Character victim, Character source, BodypartType victimPart, BodypartType sourcePart, Vector3 worldAnchor, out ConfigurableJoint added)
		{
			//IL_0026: 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_005e: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			added = null;
			if (_stick == null || (Object)(object)victim == (Object)null || (Object)(object)source == (Object)null)
			{
				return false;
			}
			Rigidbody bodypartRig = GetBodypartRig(victim, victimPart);
			Rigidbody bodypartRig2 = GetBodypartRig(source, sourcePart);
			if ((Object)(object)bodypartRig == (Object)null || (Object)(object)bodypartRig2 == (Object)null)
			{
				return false;
			}
			ConfigurableJoint[] components = ((Component)bodypartRig).GetComponents<ConfigurableJoint>();
			Invoke(_stick, victim, victimPart, ((Component)bodypartRig).transform.position, worldAnchor, (object)(STATUSTYPE)9, 0f);
			ConfigurableJoint[] components2 = ((Component)bodypartRig).GetComponents<ConfigurableJoint>();
			for (int i = 0; i < components2.Length; i++)
			{
				bool flag = false;
				for (int j = 0; j < components.Length; j++)
				{
					if ((Object)(object)components2[i] == (Object)(object)components[j])
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					added = components2[i];
					break;
				}
			}
			if ((Object)(object)added == (Object)null)
			{
				return false;
			}
			((Joint)added).autoConfigureConnectedAnchor = false;
			((Joint)added).connectedBody = bodypartRig2;
			((Joint)added).connectedAnchor = ((Component)bodypartRig2).transform.InverseTransformPoint(worldAnchor);
			return true;
		}

		internal static bool CanStickBodypart(Character victim, Bodypart bodypart)
		{
			if ((Object)(object)victim == (Object)null || (Object)(object)bodypart == (Object)null || victim.data.sinceUnstuck < 3f)
			{
				return false;
			}
			try
			{
				return _stickPartExists == null || !(bool)_stickPartExists.Invoke(victim, new object[1] { bodypart });
			}
			catch (Exception arg)
			{
				ManualLogSource logSource = Plugin.LogSource;
				if (logSource != null)
				{
					logSource.LogWarning((object)$"Cactus stick validation failed safely.\n{arg}");
				}
				return false;
			}
		}

		internal static Bodypart GetBodypart(Character character, BodypartType type)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			if (!((Object)(object)character == (Object)null) && !(_getBodypart == null))
			{
				return (Bodypart)_getBodypart.Invoke(character, new object[1] { type });
			}
			return null;
		}

		internal static Rigidbody GetBodypartRig(Character character, BodypartType type)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			if (!((Object)(object)character == (Object)null) && !(_getBodypartRig == null))
			{
				return (Rigidbody)_getBodypartRig.Invoke(character, new object[1] { type });
			}
			return null;
		}

		internal static void MoveBodypartTowardsPoint(Character character, BodypartType type, Vector3 point, float force, float clampDistance = 1f)
		{
			//IL_0025: 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)
			if (!((Object)(object)character == (Object)null) && !(_moveBodypartTowardsPoint == null))
			{
				_moveBodypartTowardsPoint.Invoke(character, new object[4] { type, point, force, clampDistance });
			}
		}

		internal static int GetLowGravityAmount(Character character)
		{
			if (!((Object)(object)character == (Object)null) && !(_lowGravAmount == null))
			{
				return (int)_lowGravAmount.GetValue(character.data);
			}
			return 0;
		}

		internal static float GetWaterJumpModifier(CharacterMovement movement)
		{
			if (!((Object)(object)movement == (Object)null) && !(_waterJumpModifier == null))
			{
				return (float)_waterJumpModifier.GetValue(movement);
			}
			return 1f;
		}

		internal static void UpdateWeight(Character character)
		{
			if ((Object)(object)character?.refs?.afflictions != (Object)null && _updateWeight != null)
			{
				Invoke(_updateWeight, character.refs.afflictions);
			}
		}

		internal static bool IsInvincible(Character character)
		{
			if ((Object)(object)character != (Object)null && _isInvincible != null)
			{
				return (bool)_isInvincible.GetValue(character.data);
			}
			return false;
		}

		internal static void SetVanillaSkeletonFlag(Character character, bool active)
		{
			if ((Object)(object)character?.data != (Object)null && _skeletonFlag != null)
			{
				_skeletonFlag.SetValue(character.data, active);
			}
		}

		internal static void NotifyReviveChestUsed(RespawnChest chest)
		{
			if ((Object)(object)chest != (Object)null && _reviveUsed?.GetValue(chest) is Action<RespawnChest> action)
			{
				action(chest);
			}
		}

		internal static void AwardResourcefulness()
		{
			AchievementManager val = Object.FindFirstObjectByType<AchievementManager>();
			if ((Object)(object)val != (Object)null && _throwAchievement != null)
			{
				Invoke(_throwAchievement, val, (object)(ACHIEVEMENTTYPE)40);
			}
		}

		private static void Invoke(MethodInfo method, object instance, params object[] arguments)
		{
			try
			{
				method.Invoke(instance, arguments);
			}
			catch (TargetInvocationException ex) when (ex.InnerException != null)
			{
				throw ex.InnerException;
			}
		}
	}
	internal static class LegacyInvincibilityCleanup
	{
		internal static void Tick()
		{
			Affliction val2 = default(Affliction);
			foreach (SkeletonSession activeSession in SkeletonRegistry.ActiveSessions)
			{
				Character val = activeSession?.Character;
				if ((Object)(object)val == (Object)null || (Object)(object)((MonoBehaviourPun)val).photonView == (Object)null || !((MonoBehaviourPun)val).photonView.IsMine || (Object)(object)val.refs?.afflictions == (Object)null)
				{
					continue;
				}
				CharacterAfflictions afflictions = val.refs.afflictions;
				if (afflictions.HasAfflictionType((AfflictionType)16, ref val2) && val2.totalTime == float.MaxValue)
				{
					Affliction_Invincibility val3 = (Affliction_Invincibility)(object)((val2 is Affliction_Invincibility) ? val2 : null);
					if (val3 != null && val3.isFromMilk)
					{
						afflictions.RemoveAffliction(val2, false, true);
						val.data.RecalculateInvincibility();
					}
				}
			}
		}
	}
	internal static class NetworkSession
	{
		internal const string ProtocolProperty = "SSR.Protocol";

		internal const string UndeadProperty = "SSR.Undead";

		internal const string TraitProperty = "SSR.Trait";

		internal const string DeathSequenceProperty = "SSR.DeathSequence";

		internal const string CostumeProperty = "SSR.Costume";

		internal const string Protocol = "18";

		private static int _publishedActor = -1;

		internal static void PublishCompatibility()
		{
			//IL_005b: 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_006f: Expected O, but got Unknown
			if (!PhotonNetwork.InRoom || PhotonNetwork.LocalPlayer == null)
			{
				_publishedActor = -1;
				return;
			}
			int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
			string text = ((Plugin.ModEnabled != null && Plugin.ModEnabled.Value) ? "18" : "disabled");
			if (_publishedActor != actorNumber || !PropertyEquals(PhotonNetwork.LocalPlayer, "SSR.Protocol", text))
			{
				Hashtable val = new Hashtable { [(object)"SSR.Protocol"] = text };
				PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
				_publishedActor = actorNumber;
			}
		}

		internal static bool CanCreateSupportSkeleton(Character character, bool warnLocalPlayer)
		{
			if (Plugin.ModEnabled == null || !Plugin.ModEnabled.Value || (Object)(object)character == (Object)null || character.isBot || character.gameOver)
			{
				return false;
			}
			if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null)
			{
				return false;
			}
			bool flag = PhotonNetwork.CurrentRoom.PlayerCount <= 1 && HostSettings.Mode == GameMode.Arcade;
			if (PhotonNetwork.CurrentRoom.PlayerCount <= 1 && !flag)
			{
				return false;
			}
			PublishCompatibility();
			Player[] playerList = PhotonNetwork.PlayerList;
			foreach (Player val in playerList)
			{
				if (val != PhotonNetwork.LocalPlayer && !PropertyEquals(val, "SSR.Protocol", "18"))
				{
					if (warnLocalPlayer && character.IsLocal)
					{
						Plugin.LogSource.LogWarning((object)string.Format("Normal death used because player #{0} does not advertise {1} protocol {2}. Install the mod on every client.", val.ActorNumber, "Second Ascent", "18"));
					}
					return false;
				}
			}
			return playerList.Length > 1 || flag;
		}

		internal static bool AllPlayersCompatible()
		{
			if (Plugin.ModEnabled == null || !Plugin.ModEnabled.Value || !PhotonNetwork.InRoom || PhotonNetwork.LocalPlayer == null)
			{
				return false;
			}
			PublishCompatibility();
			Player[] playerList = PhotonNetwork.PlayerList;
			for (int i = 0; i < playerList.Length; i++)
			{
				if (!PropertyEquals(playerList[i], "SSR.Protocol", "18"))
				{
					return false;
				}
			}
			return true;
		}

		internal static bool PropertyIsTrue(Player player, string key)
		{
			bool flag = default(bool);
			int num;
			if (player != null && player.CustomProperties != null && ((Dictionary<object, object>)(object)player.CustomProperties).TryGetValue((object)key, out object value))
			{
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		internal static void SetLocalUndeadProperty(bool undead)
		{
			SetLocalSkeletonState(undead, SkeletonKind.None, 0);
		}

		internal static void SetLocalSkeletonState(bool undead, SkeletonKind kind, int deathSequence)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null)
			{
				Hashtable val = new Hashtable
				{
					[(object)"SSR.Undead"] = undead,
					[(object)"SSR.Trait"] = (byte)(undead ? kind : SkeletonKind.None),
					[(object)"SSR.DeathSequence"] = (undead ? deathSequence : 0)
				};
				PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
			}
		}

		internal static void SetLocalCostumeState(bool costumed, SkeletonKind kind)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0041: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null)
			{
				Hashtable val = new Hashtable
				{
					[(object)"SSR.Costume"] = costumed,
					[(object)"SSR.Trait"] = (byte)(costumed ? kind : SkeletonKind.None)
				};
				PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
			}
		}

		internal static void SetLocalArcadeSkeletonState(SkeletonKind kind)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0040: 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_0063: Expected O, but got Unknown
			if (PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null)
			{
				Hashtable val = new Hashtable
				{
					[(object)"SSR.Undead"] = false,
					[(object)"SSR.Costume"] = true,
					[(object)"SSR.Trait"] = (byte)kind,
					[(object)"SSR.DeathSequence"] = 0
				};
				PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
			}
		}

		internal static SkeletonKind GetSkeletonKind(Player player)
		{
			if (((player != null) ? player.CustomProperties : null) == null || !((Dictionary<object, object>)(object)player.CustomProperties).TryGetValue((object)"SSR.Trait", out object value))
			{
				return SkeletonKind.None;
			}
			if (value is byte b && Enum.IsDefined(typeof(SkeletonKind), b))
			{
				return (SkeletonKind)b;
			}
			if (value is int num && Enum.IsDefined(typeof(SkeletonKind), num))
			{
				return (SkeletonKind)num;
			}
			return SkeletonKind.None;
		}

		internal static int GetDeathSequence(Player player)
		{
			if (((player != null) ? player.CustomProperties : null) != null && ((Dictionary<object, object>)(object)player.CustomProperties).TryGetValue((object)"SSR.DeathSequence", out object value) && value is int)
			{
				return (int)value;
			}
			return 0;
		}

		private static bool PropertyEquals(Player player, string key, string expected)
		{
			if (player != null && player.CustomProperties != null && ((Dictionary<object, object>)(object)player.CustomProperties).TryGetValue((object)key, out object value) && value is string text)
			{
				return text == expected;
			}
			return false;
		}
	}
	[BepInPlugin("com.dimas.peak.supportskeletonsreborn", "Second Ascent", "1.0.0")]
	[BepInIncompatibility("com.github.BDSayers.SupportSkeletons")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "com.dimas.peak.supportskeletonsreborn";

		public const string Name = "Second Ascent";

		public const string Version = "1.0.0";

		private Harmony _harmony;

		private static Texture2D _crosshairTexture;

		private static readonly Color CrosshairNeutral = Color.white;

		private static readonly Color CrosshairTargeted = new Color(1f, 0.86f, 0.1f);

		private static readonly Color CrosshairPullsTarget = new Color(0.35f, 0.75f, 1f);

		private static Texture2D _holdRadialTexture;

		private static float _holdRadialProgress = -1f;

		private readonly UguiMenuWindow _selectionMenu = new UguiMenuWindow();

		private int _consumeInputThroughFrame = -1;

		internal static ManualLogSource LogSource { get; private set; }

		internal static ConfigEntry<bool> ModEnabled { get; private set; }

		internal static string PluginDirectory { get; private set; }

		internal static bool ModMenuOpen { get; private set; }

		private void Awake()
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			LogSource = ((BaseUnityPlugin)this).Logger;
			PluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable support-skeleton deaths in compatible multiplayer sessions.");
			LocalKeybinds.Bind(((BaseUnityPlugin)this).Config);
			HostSettings.Bind(((BaseUnityPlugin)this).Config);
			SkeletonShrine.OpenSelection = delegate
			{
				_selectionMenu.OpenSelection();
			};
			GoldenSkeletonIdolBehaviour.OpenSelection = delegate(int itemViewId)
			{
				if (SettingsWindowRouter.IsOpen)
				{
					SettingsWindowRouter.Close();
				}
				_selectionMenu.OpenFromGoldenIdol(itemViewId);
			};
			GoldenIdolSelectionService.LocalCommitted = _selectionMenu.OnGoldenIdolCommitted;
			GoldenIdolSelectionService.LocalRejected = _selectionMenu.OnGoldenIdolRejected;
			if (Chainloader.PluginInfos.ContainsKey("com.github.BDSayers.SupportSkeletons"))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Second Ascent will not start while the obsolete SupportSkeletons plugin is loaded. Remove ringtail_mods-SupportSkeletons first.");
				return;
			}
			if (!GameBindings.TryInitialize(out var reason))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Second Ascent disabled: " + reason));
				return;
			}
			_harmony = new Harmony("com.dimas.peak.supportskeletonsreborn");
			try
			{
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
			}
			catch (Exception arg)
			{
				_harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)string.Format("{0} could not apply every required patch and was disabled cleanly.\n{1}", "Second Ascent", arg));
				return;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Second Ascent 1.0.0 loaded. The feature activates only when every player has the same protocol.");
		}

		private void Update()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			RuntimeScheduler.Tick();
			BoneItemRegistry.EnsureRegistered();
			GoldenSkeletonIdolRegistry.EnsureRegistered();
			if (BoneItemRegistry.IsReady && GoldenSkeletonIdolRegistry.IsReady)
			{
				NetworkSession.PublishCompatibility();
			}
			VanillaUiFactory.Tick();
			if (SettingsWindowRouter.HandleKeyCapture())
			{
				_consumeInputThroughFrame = Time.frameCount;
			}
			else if (SettingsWindowRouter.IsOpen && Input.GetKeyDown((KeyCode)27))
			{
				_consumeInputThroughFrame = Time.frameCount;
				SettingsWindowRouter.Close();
			}
			else if (_selectionMenu.IsOpen && Input.GetKeyDown((KeyCode)27))
			{
				_consumeInputThroughFrame = Time.frameCount;
				_selectionMenu.Close();
			}
			else if (Input.GetKeyDown(LocalKeybinds.SettingsMenuKey))
			{
				if (_selectionMenu.IsOpen)
				{
					_selectionMenu.Close();
				}
				SettingsWindowRouter.Toggle();
				_consumeInputThroughFrame = Time.frameCount;
			}
			else if (Input.GetKeyDown(LocalKeybinds.SelectionMenuKey))
			{
				Character localCharacter = Character.localCharacter;
				SceneSwitchingStatus val = default(SceneSwitchingStatus);
				bool flag = GameHandler.TryGetStatus<SceneSwitchingStatus>(ref val);
				if ((Object)(object)localCharacter != (Object)null && !flag)
				{
					if (SettingsWindowRouter.IsOpen)
					{
						SettingsWindowRouter.Close();
					}
					_selectionMenu.Toggle();
					_consumeInputThroughFrame = Time.frameCount;
				}
			}
			else if (!SettingsWindowRouter.IsOpen && !_selectionMenu.IsOpen && !IsTypingIntoUi())
			{
				TriggerManualEffectHotkey();
			}
			SettingsWindowRouter.Tick();
			_selectionMenu.Tick();
			ModMenuOpen = SettingsWindowRouter.IsOpen || _selectionMenu.IsOpen || _consumeInputThroughFrame >= Time.frameCount;
		}

		private static void TriggerManualEffectHotkey()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (!HostSettings.ManualEffectHotkeysEnabled)
			{
				return;
			}
			Character localCharacter = Character.localCharacter;
			SceneSwitchingStatus val = default(SceneSwitchingStatus);
			if ((Object)(object)localCharacter == (Object)null || GameHandler.TryGetStatus<SceneSwitchingStatus>(ref val) || !SkeletonRegistry.TryGetSession(localCharacter, out var session) || session.State != LifeState.Undead)
			{
				return;
			}
			RandomEffectKind[] allowedEffects = RandomEffectRuntime.GetAllowedEffects(session.Kind);
			for (int i = 0; i < allowedEffects.Length; i++)
			{
				if (Input.GetKeyDown(LocalKeybinds.GetEffectKey(allowedEffects[i])))
				{
					RandomEffectRuntime.TriggerNow(allowedEffects[i]);
					break;
				}
			}
		}

		private static bool IsTypingIntoUi()
		{
			EventSystem current = EventSystem.current;
			GameObject val = ((current != null) ? current.currentSelectedGameObject : null);
			if ((Object)(object)val != (Object)null)
			{
				if (!((Object)(object)val.GetComponent<TMP_InputField>() != (Object)null))
				{
					return (Object)(object)val.GetComponent<InputField>() != (Object)null;
				}
				return true;
			}
			return false;
		}

		private void FixedUpdate()
		{
			RuntimeScheduler.FixedTick();
		}

		private void LateUpdate()
		{
			_selectionMenu.KeepCursorAvailable();
			SettingsWindowRouter.KeepCursorAvailable();
			RuntimeScheduler.LateTick();
		}

		private void OnGUI()
		{
			//IL_003e: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			float holdProgress = VanillaGhostService.HoldProgress;
			if (holdProgress > 0f && holdProgress < 1f)
			{
				float num = 320f;
				float num2 = 34f;
				GUI.Box(new Rect(((float)Screen.width - num) * 0.5f, (float)Screen.height * 0.72f, num, num2), Strings.Format("ghost.hold", LocalKeybinds.DisplayName(LocalKeybinds.BecomeGhostKey), holdProgress * 5f));
			}
			if (PoisonDrainService.IsAimHeld)
			{
				DrawCrosshair(PoisonDrainService.IsAimingAtValidTarget ? CrosshairTargeted : CrosshairNeutral);
			}
			else if (WebGrappleService.IsAimHeld)
			{
				DrawCrosshair((!WebGrappleService.HasTargetInRange) ? CrosshairNeutral : (WebGrappleService.IsAimingAtPullableTarget ? CrosshairPullsTarget : CrosshairTargeted));
			}
			float holdProgress2 = StonePetrifyService.HoldProgress;
			if (holdProgress2 > 0f)
			{
				DrawHoldRadial(holdProgress2);
			}
			HungerPreySnifferService.DrawMarkers();
		}

		private static void DrawHoldRadial(float progress)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			GUI.DrawTexture(new Rect(((float)Screen.width - 64f) * 0.5f, ((float)Screen.height - 64f) * 0.5f - 40f, 64f, 64f), (Texture)(object)GetHoldRadialTexture(progress));
		}

		private static Texture2D GetHoldRadialTexture(float progress)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: 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)
			if (Mathf.Approximately(_holdRadialProgress, progress) && (Object)(object)_holdRadialTexture != (Object)null)
			{
				return _holdRadialTexture;
			}
			if ((Object)(object)_holdRadialTexture == (Object)null)
			{
				_holdRadialTexture = new Texture2D(64, 64, (TextureFormat)4, false);
			}
			Color val = default(Color);
			((Color)(ref val))..ctor(0f, 0f, 0f, 0f);
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0f, 0f, 0f, 0.35f);
			Color val3 = default(Color);
			((Color)(ref val3))..ctor(0.92f, 0.92f, 0.88f, 0.95f);
			float num = progress * 360f;
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					float num2 = (float)j + 0.5f - 32f;
					float num3 = (float)i + 0.5f - 32f;
					float num4 = Mathf.Sqrt(num2 * num2 + num3 * num3);
					if (num4 < 20f || num4 > 28f)
					{
						_holdRadialTexture.SetPixel(j, i, val);
						continue;
					}
					float num5 = Mathf.Atan2(num2, 0f - num3) * 57.29578f;
					if (num5 < 0f)
					{
						num5 += 360f;
					}
					_holdRadialTexture.SetPixel(j, i, (num5 <= num) ? val3 : val2);
				}
			}
			_holdRadialTexture.Apply();
			_holdRadialProgress = progress;
			return _holdRadialTexture;
		}

		private static void DrawCrosshair(Color color)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			Rect val = new Rect(((float)Screen.width - 24f) * 0.5f, ((float)Screen.height - 24f) * 0.5f, 24f, 24f);
			Color color2 = GUI.color;
			GUI.color = color;
			GUI.DrawTexture(val, (Texture)(object)GetCrosshairTexture());
			GUI.color = color2;
		}

		private static Texture2D GetCrosshairTexture()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_crosshairTexture != (Object)null)
			{
				return _crosshairTexture;
			}
			Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false);
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(1f, 1f, 1f, 0f);
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 32; j++)
				{
					val.SetPixel(j, i, val2);
				}
			}
			FillBar(val, 15, 3, 17, 12);
			FillBar(val, 15, 20, 17, 29);
			FillBar(val, 3, 15, 12, 17);
			FillBar(val, 20, 15, 29, 17);
			val.Apply();
			_crosshairTexture = val;
			return _crosshairTexture;
		}

		private static void FillBar(Texture2D texture, int x0, int y0, int x1, int y1)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			for (int i = y0; i <= y1; i++)
			{
				if (i < 0 || i >= ((Texture)texture).height)
				{
					continue;
				}
				for (int j = x0; j <= x1; j++)
				{
					if (j >= 0 && j < ((Texture)texture).width)
					{
						texture.SetPixel(j, i, Color.white);
					}
				}
			}
		}

		private void OnDestroy()
		{
			GoldenSkeletonIdolBehaviour.OpenSelection = null;
			GoldenIdolSelectionService.LocalCommitted = null;
			GoldenIdolSelectionService.LocalRejected = null;
			_selectionMenu.Close();
			SkeletonRegistry.ResetForCompletedRun();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class SkeletonRegistry
	{
		private static readonly Dictionary<int, SkeletonSession> Sessions = new Dictionary<int, SkeletonSession>();

		private static readonly HashSet<int> KnownLivingViews = new HashSet<int>();

		private static readonly HashSet<int> DeathsInProgress = new HashSet<int>();

		private static readonly HashSet<int> ArcadeReanimationsInProgress = new HashSet<int>();

		private static readonly HashSet<int> RevivesInProgress = new HashSet<int>();

		private static readonly List<int> PruneBuffer = new List<int>();

		private static bool _runEnding;

		private static int _runEndingSceneHandle = -1;

		internal static bool IsRunEnding => _runEnding;

		internal static int ActiveCount => Sessions.Count;

		internal static IEnumerable<SkeletonSession> ActiveSessions => Sessions.Values;

		internal static bool IsUndead(Character character)
		{
			if (!TryGetViewId(character, out var viewId))
			{
				return false;
			}
			if (RevivesInProgress.Contains(viewId))
			{
				return false;
			}
			if (Sessions.ContainsKey(viewId))
			{
				return true;
			}
			if (KnownLivingViews.Contains(viewId))
			{
				return false;
			}
			if (!NetworkSession.PropertyIsTrue(((MonoBehaviourPun)character).photonView.Owner, "SSR.Undead"))
			{
				return false;
			}
			SkeletonKind skeletonKind = NetworkSession.GetSkeletonKind(((MonoBehaviourPun)character).photonView.Owner);
			if (skeletonKind == SkeletonKind.None)
			{
				skeletonKind = SkeletonKind.Trauma;
			}
			int deathSequence = NetworkSession.GetDeathSequence(((MonoBehaviourPun)character).photonView.Owner);
			SkeletonSession value = new SkeletonSession(character, skeletonKind, deathSequence)
			{
				State = LifeState.Undead
			};
			Sessions[viewId] = value;
			return true;
		}

		internal static SkeletonSession BeginCostume(Character character, SkeletonKind kind)
		{
			int viewID = ((MonoBehaviourPun)character).photonView.ViewID;
			KnownLivingViews.Remove(viewID);
			if (Sessions.TryGetValue(viewID, out var value))
			{
				value.DisposeEffects();
			}
			SkeletonSession skeletonSession = new SkeletonSession(character, kind, 0)
			{
				State = LifeState.Undead,
				IsCostume = true
			};
			Sessions[viewID] = skeletonSession;
			if (character.IsLocal)
			{
				NetworkSession.SetLocalCostumeState(costumed: true, kind);
			}
			return skeletonSession;
		}

		internal static void EndCostume(Character character)
		{
			if (TryGetViewId(character, out var viewId) && Sessions.TryGetValue(viewId, out var value) && value.IsCostume)
			{
				RemoveSession(viewId);
				if (character.IsLocal)
				{
					NetworkSession.SetLocalCostumeState(costumed: false, SkeletonKind.None);
				}
				DeathSnapshotTracker.Clear(character);
			}
		}

		private static void RestoreCostumeSession(Character character)
		{
			if (!TryGetViewId(character, out var viewId) || (IsUndead(character) && !IsCostume(character)))
			{
				return;
			}
			bool flag = NetworkSession.PropertyIsTrue(((MonoBehaviourPun)character).photonView.Owner, "SSR.Costume");
			SkeletonKind skeletonKind = (flag ? NetworkSession.GetSkeletonKind(((MonoBehaviourPun)character).photonView.Owner) : SkeletonKind.None);
			if (Sessions.TryGetValue(viewId, out var value))
			{
				if (value.IsCostume)
				{
					if (!flag)
					{
						RemoveSession(viewId);
						RestoreNormalLook(character);
					}
					else if (value.Kind != skeletonKind)
					{
						RemoveSession(viewId);
						ApplyCostumeVisuals(character, viewId, skeletonKind);
					}
				}
			}
			else if (flag && skeletonKind != SkeletonKind.None)
			{
				ApplyCostumeVisuals(character, viewId, skeletonKind);
			}
		}

		private static void ApplyCostumeVisuals(Character character, int viewId, SkeletonKind kind)
		{
			KnownLivingViews.Remove(viewId);
			SkeletonSession skeletonSession = new SkeletonSession(character, kind, 0)
			{
				State = LifeState.Undead,
				IsCostume = true
			};
			Sessions[viewId] = skeletonSession;
			CharacterRefs refs = character.refs;
			if (refs != null)
			{
				CharacterCustomization customization = refs.customization;
				if (customization != null)
				{
					CustomizationRefs refs2 = customization.refs;
					if (refs2 != null)
					{
						refs2.SetSkeleton(true, character.IsLocal);
					}
				}
			}
			SkeletonEffectLifecycle.Activate(skeletonSession);
			GameAssetCatalog.PlayReanimationFeedback(character);
		}

		private static void RestoreNormalLook(Character character)
		{
			if (character == null)
			{
				return;
			}
			CharacterRefs refs = character.refs;
			if (refs == null)
			{
				return;
			}
			CharacterCustomization customization = refs.customization;
			if (customization != null)
			{
				CustomizationRefs refs2 = customization.refs;
				if (refs2 != null)
				{
					refs2.SetSkeleton(false, character.IsLocal);
				}
			}
		}

		internal static void RefreshCostumeSessions()
		{
			List<Character> allCharacters = Character.AllCharacters;
			for (int i = 0; i < allCharacters.Count; i++)
			{
				Character val = allCharacters[i];
				if (!((Object)(object)val != (Object)null) || !((Object)(object)((MonoBehaviourPun)val).photonView != (Object)null))
				{
					continue;
				}
				try
				{
					RestoreCostumeSession(val);
				}
				catch (Exception arg)
				{
					ManualLogSource logSource = Plugin.LogSource;
					if (logSource != null)
					{
						logSource.LogWarning((object)$"Costume sync failed safely for {val.characterName}.\n{arg}");
					}
				}
			}
		}

		internal static bool IsCostume(Character character)
		{
			if (TryGetSession(character, out var session))
			{
				return session.IsCostume;
			}
			return false;
		}

		internal static SkeletonSession BeginDeath(Character character, SkeletonKind kind, int deathSequence)
		{
			int viewID = ((MonoBehaviourPun)character).photonView.ViewID;
			KnownLivingViews.Remove(viewID);
			DeathsInProgress.Add(viewID);
			if (Sessions.TryGetValue(viewID, out var value))
			{
				value.DisposeEffects();
			}
			SkeletonSession skeletonSession = new SkeletonSession(character, kind, deathSequence);
			Sessions[viewID] = skeletonSession;
			if (character.IsLocal)
			{
				NetworkSession.SetLocalSkeletonState(undead: true, kind, deathSequence);
			}
			return skeletonSession;
		}

		internal static SkeletonSession ChangeUndeadForm(Character character, SkeletonKind kind, int requestedSequence)
		{
			if (!TryGetViewId(character, out var viewId) || kind == SkeletonKind.None || !IsUndead(character) || IsCostume(character))
			{
				return null;
			}
			int num = requestedSequence;
			if (Sessions.TryGetValue(viewId, out var value))
			{
				num = value.DeathSequence;
				value.DisposeEffects();
			}
			else if (num <= 0)
			{
				num = NetworkSession.GetDeathSequence(((MonoBehaviourPun)character).photonView.Owner);
			}
			KnownLivingViews.Remove(viewId);
			SkeletonSession skeletonSession = new SkeletonSession(character, kind, num)
			{
				State = LifeState.Undead,
				IsCostume = false
			};
			Sessions[viewId] = skeletonSession;
			if (character.IsLocal)
			{
				NetworkSession.SetLocalSkeletonState(undead: true, kind, num);
			}
			return skeletonSession;
		}

		internal static void MarkArcadeReanimation(Character character)
		{
			if (TryGetViewId(character, out var viewId))
			{
				ArcadeReanimationsInProgress.Add(viewId);
			}
		}

		internal static bool IsArcadeReanimationInProgress(Character character)
		{
			if (TryGetViewId(character, out var viewId))
			{
				return ArcadeReanimationsInProgress.Contains(viewId);
			}
			return false;
		}

		internal static SkeletonSession FinishArcadeDeathAsCostume(Character character, SkeletonKind kind)
		{
			int viewID = ((MonoBehaviourPun)character).photonView.ViewID;
			if (Sessions.TryGetValue(viewID, out var value))
			{
				value.DisposeEffects();
			}
			SkeletonSession skeletonSession = new SkeletonSession(character, kind, 0)
			{
				State = LifeState.Undead,
				IsCostume = true
			};
			Sessions[viewID] = skeletonSession;
			KnownLivingViews.Remove(viewID);
			CharacterRefs refs = character.refs;
			if (refs != null)
			{
				CharacterCustomization customization = refs.customization;
				if (customization != null)
				{
					CustomizationRefs refs2 = customization.refs;
					if (refs2 != null)
					{
						refs2.SetSkeleton(true, character.IsLocal);
					}
				}
			}
			if (character.IsLocal)
			{
				NetworkSession.SetLocalArcadeSkeletonState(kind);
			}
			DeathSnapshotTracker.Clear(character);
			return skeletonSession;
		}

		internal static void BeginDeath(Character character)
		{
			DeathSnapshot orCreate = DeathSnapshotTracker.GetOrCreate(character, forcedSpore: false);
			SkeletonKind kind = DeathCauseResolver.Resolve(orCreate, character);
			BeginDeath(character, kind, orCreate?.Sequence ?? 0);
		}

		internal static bool TryGetSession(Character character, out SkeletonSession session)
		{
			session = null;
			if (TryGetViewId(character, out var viewId))
			{
				return Sessions.TryGetValue(viewId, out session);
			}
			return false;
		}

		internal static SkeletonKind GetKind(Character character)
		{
			if (!TryGetSession(character, out var session))
			{
				return SkeletonKind.None;
			}
			return session.Kind;
		}

		internal static bool HasKind(Character character, SkeletonKind kind)
		{
			if (kind != SkeletonKind.None)
			{
				return GetKind(character) == kind;
			}
			return false;
		}

		internal static bool AnyKind(SkeletonKind kind)
		{
			foreach (SkeletonSession value in Sessions.Values)
			{
				if (value.Kind == kind)
				{
					return true;
				}
			}
			return false;
		}

		internal static void FinishDeath(Character character)
		{
			if (TryGetViewId(character, out var viewId))
			{
				DeathsInProgress.Remove(viewId);
				ArcadeReanimationsInProgress.Remove(viewId);
				if (Sessions.TryGetValue(viewId, out var value))
				{
					value.State = LifeState.Undead;
				}
			}
		}

		internal static void AbandonDeath(Character character)
		{
			if (TryGetViewId(character, out var viewId))
			{
				DeathsInProgress.Remove(viewId);
				ArcadeReanimationsInProgress.Remove(viewId);
				RevivesInProgress.Remove(viewId);
				KnownLivingViews.Remove(viewId);
				RemoveSession(viewId);
				if (character.IsLocal)
				{
					NetworkSession.SetLocalSkeletonState(undead: false, SkeletonKind.None, 0);
					NetworkSession.SetLocalCostumeState(costumed: false, SkeletonKind.None);
				}
			}
		}

		internal static void PrepareVanillaDeath(Character character)
		{
			if (TryGetViewId(character, out var viewId))
			{
				DeathsInProgress.Remove(viewId);
				ArcadeReanimationsInProgress.Remove(viewId);
				RevivesInProgress.Remove(viewId);
				KnownLivingViews.Remove(viewId);
				RemoveSession(viewId);
				DeathSnapshotTracker.Clear(character);
				if (character.IsLocal)
				{
					NetworkSession.SetLocalSkeletonState(undead: false, SkeletonKind.None, 0);
				}
			}
		}

		internal static void BeginRevive(Character character)
		{
			int viewID = ((MonoBehaviourPun)character).photonView.ViewID;
			RevivesInProgress.Add(viewID);
			KnownLivingViews.Add(viewID);
			if (Sessions.TryGetValue(viewID, out var value))
			{
				value.State = LifeState.Reviving;
				value.DisposeEffects();
			}
			WebVisualService.Detach(character);
			StoneVisualService.Detach(character);
		}

		internal static void FinishRevive(Character character)
		{
			if (TryGetViewId(character, out var viewId))
			{
				DeathCoordinator.MarkRevived(character);
				RevivesInProgress.Remove(viewId);
				RemoveSession(viewId);
				DeathSnapshotTracker.Clear(character);
				if (character.IsLocal)
				{
					NetworkSession.SetLocalSkeletonState(undead: false, SkeletonKind.None, 0);
				}
			}
		}

		internal static void RestoreAfterFailedRevive(Character character)
		{
			if (!TryGetViewId(character, out var viewId))
			{
				return;
			}
			RevivesInProgress.Remove(viewId);
			KnownLivingViews.Remove(viewId);
			if (Sessions.TryGetValue(viewId, out var value))
			{
				value.State = LifeState.Undead;
				value.ResetEffects();
				if (character.IsLocal)
				{
					NetworkSession.SetLocalSkeletonState(undead: true, value.Kind, value.DeathSequence);
				}
			}
		}

		internal static void RestoreCostumeAfterFailedRevive(Character character)
		{
			if (!TryGetViewId(character, out var viewId))
			{
				return;
			}
			RevivesInProgress.Remove(viewId);
			KnownLivingViews.Remove(viewId);
			SkeletonSession skeletonSession = null;
			if (Sessions.TryGetValue(viewId, out var value))
			{
				skeletonSession = value;
				skeletonSession.State = LifeState.Undead;
				skeletonSession.IsCostume = true;
				skeletonSession.ResetEffects();
			}
			else
			{
				SkeletonKind skeletonKind = NetworkSession.GetSkeletonKind(((MonoBehaviourPun)character).photonView.Owner);
				if (skeletonKind == SkeletonKind.None)
				{
					skeletonKind = SkeletonKind.Trauma;
				}
				skeletonSession = new SkeletonSession(character, skeletonKind, 0)
				{
					State = LifeState.Undead,
					IsCostume = true
				};
				Sessions[viewId] = skeletonSession;
			}
			if (character.IsLocal)
			{
				NetworkSession.SetLocalCostumeState(costumed: true, skeletonSession.Kind);
			}
			CharacterRefs refs = character.refs;
			if (refs == null)
			{
				return;
			}
			CharacterCustomization customization = refs.customization;
			if (customization != null)
			{
				CustomizationRefs refs2 = customization.refs;
				if (refs2 != null)
				{
					refs2.SetSkeleton(true, character.IsLocal);
				}
			}
		}

		internal static bool IsDeathInProgress(Character character)
		{
			if (TryGetViewId(character, out var viewId))
			{
				return DeathsInProgress.Contains(viewId);
			}
			return false;
		}

		internal static bool ShouldPreserveInventory(Character character)
		{
			if (!TryGetViewId(character, out var viewId))
			{
				return false;
			}
			if (!DeathsInProgress.Contains(viewId))
			{
				return RevivesInProgress.Contains(viewId);
			}
			return true;
		}

		internal static bool AnyUndeadCharacter()
		{
			foreach (SkeletonSession value in Sessions.Values)
			{
				if (!value.IsCostume)
				{
					return true;
				}
			}
			List<Character> allCharacters = Character.AllCharacters;
			for (int i = 0; i < allCharacters.Count; i++)
			{
				Character character = allCharacters[i];
				if (IsUndead(character) && !IsCostume(character))
				{
					return true;
				}
			}
			return false;
		}

		internal static void BeginRunEnding()
		{
			//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)
			if (_runEnding)
			{
				return;
			}
			_runEnding = true;
			Scene activeScene = SceneManager.GetActiveScene();
			_runEndingSceneHandle = SceneHandle.op_Implicit(((Scene)(ref activeScene)).handle);
			foreach (SkeletonSession value in Sessions.Values)
			{
				value.DisposeEffects();
			}
		}

		internal static void RefreshSceneState()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (_runEnding)
			{
				Scene activeScene = SceneManager.GetActiveScene();
				if (((Scene)(ref activeScene)).handle != SceneHandle.op_Implicit(_runEndingSceneHandle))
				{
					ResetForCompletedRun();
					_runEnding = false;
					_runEndingSceneHandle = -1;
				}
			}
		}

		internal static void PruneDestroyedSessions()
		{
			PruneBuffer.Clear();
			foreach (KeyValuePair<int, SkeletonSession> session in Sessions)
			{
				if ((Object)(object)session.Value.Character == (Object)null || (Object)(object)((MonoBehaviourPun)session.Value.Character).photonView == (Object)null)
				{
					PruneBuffer.Add(session.Key);
				}
			}
			for (int i = 0; i < PruneBuffer.Count; i++)
			{
				int num = PruneBuffer[i];
				RemoveSession(num);
				KnownLivingViews.Remove(num);
				DeathsInProgress.Remove(num);
				ArcadeReanimationsInProgress.Remove(num);
				RevivesInProgress.Remove(num);
			}
			PruneBuffer.Clear();
		}

		internal static void ResetForCompletedRun()
		{
			foreach (SkeletonSession value in Sessions.Values)
			{
				value.DisposeEffects();
			}
			Sessions.Clear();
			KnownLivingViews.Clear();
			DeathsInProgress.Clear();
			ArcadeReanimationsInProgress.Clear();
			RevivesInProgress.Clear();
			PruneBuffer.Clear();
			DeathSnapshotTracker.ClearAll();
			RuntimeScheduler.ResetSceneState();
			NetworkSession.SetLocalSkeletonState(undead: false, SkeletonKind.None, 0);
		}

		private static void RemoveSession(int viewId)
		{
			if (!Sessions.TryGetValue(viewId, out var value))
			{
				return;
			}
			Sessions.Remove(viewId);
			value.DisposeEffects();
			try
			{
				WebVisualService.Detach(value.Character);
			}
			catch (Exception arg)
			{
				ManualLogSource logSource = Plugin.LogSource;
				if (logSource != null)
				{
					logSource.LogWarning((object)$"Web cleanup failed safely for view {viewId}.\n{arg}");
				}
			}
			StoneVisualService.Detach(VisualInstanceKey.Live(viewId));
		}

		private static bool TryGetViewId(Character character, out int viewId)
		{
			viewId = 0;
			if ((Object)(object)character == (Object)null || character.isBot || (Object)(object)((MonoBehaviourPun)character).photonView == (Object)null)
			{
				return false;
			}
			viewId = ((MonoBehaviourPun)character).photonView.ViewID;
			return viewId != 0;
		}
	}
}
namespace SecondAscent.Skeletons
{
	internal sealed class CactusSkeleton : ISkeletonForm
	{
		internal static readonly PaletteInfo Palette = PaletteInfo.Flat(new Color(0.12f, 0.72f, 0.2f));

		public SkeletonKind Kind => SkeletonKind.Cactus;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)statusType == 9;
		}

		internal static void ApplyVisual(SkeletonSession session)
		{
			Palette.ApplyTo(session);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			CactusEffectService.Attach(session);
		}
	}
	internal sealed class ColdSkeleton : ISkeletonForm
	{
		internal static readonly PaletteInfo Palette = PaletteInfo.Two(new Color(0.16f, 0.58f, 1f), new Color(0.82f, 0.95f, 1f), 0.35f);

		public SkeletonKind Kind => SkeletonKind.Cold;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)statusType == 2;
		}

		internal static void ApplyVisual(SkeletonSession session)
		{
			Palette.ApplyTo(session);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			CharacterModifierService.ApplyRunDelta(session, 0.25f);
			CharacterModifierService.ApplyClimbDelta(session, -0.1f);
			RandomEffectRuntime.Attach(session, RandomEffectKind.ColdFlash, RandomEffectKind.ColdSlip);
		}
	}
	internal sealed class FlyTrapSkeleton : ISkeletonForm
	{
		internal static readonly PaletteInfo Palette = PaletteInfo.Two(new Color(0.025f, 0.46f, 0.075f), new Color(0.005f, 0.16f, 0.02f), 0.45f);

		public SkeletonKind Kind => SkeletonKind.FlyTrap;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)statusType == 14;
		}

		internal static void ApplyVisual(SkeletonSession session)
		{
			Palette.ApplyTo(session);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			Character character = session.Character;
			session.Effects.Add(delegate
			{
				FlyTrapFogSpeedService.RemoveForSource(character);
			});
			RandomEffectRuntime.Attach(session, RandomEffectKind.ExplosiveBomb);
		}
	}
	internal sealed class HeatSkeleton : ISkeletonForm
	{
		internal static readonly Color CharredBase = new Color(0.035f, 0.028f, 0.025f);

		internal static readonly Color CalmBlue = Color.HSVToRGB(0.62f, 0.95f, 1f, true);

		internal const float AccentStrength = 0.88f;

		internal static readonly PaletteInfo Palette = PaletteInfo.Two(CharredBase, CalmBlue, 0.88f);

		public SkeletonKind Kind => SkeletonKind.Heat;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)statusType == 8;
		}

		internal static void ApplyVisual(SkeletonSession session)
		{
			Palette.ApplyTo(session);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			HeatExplosionChargeVisual.Attach(session);
			PortableStoveService.Attach(session);
			ContactEffectService.AttachHeat(session);
			RandomEffectRuntime.Attach(session, RandomEffectKind.Dynamite);
		}
	}
	internal sealed class HungerSkeleton : ISkeletonForm
	{
		internal static readonly PaletteInfo Palette = PaletteInfo.Two(new Color(1f, 0.78f, 0.04f), new Color(1f, 0.96f, 0.78f), 0.3f);

		public SkeletonKind Kind => SkeletonKind.Hunger;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)statusType == 1;
		}

		internal static void ApplyVisual(SkeletonSession session)
		{
			Palette.ApplyTo(session);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			CharacterModifierService.ApplyMovementDelta(session, 0.5f);
		}
	}
	internal sealed class PoisonSkeleton : ISkeletonForm
	{
		internal static readonly PaletteInfo Palette = PaletteInfo.Flat(new Color(0.58f, 0.08f, 0.78f));

		public SkeletonKind Kind => SkeletonKind.Poison;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)statusType == 3;
		}

		internal static void ApplyVisual(SkeletonSession session)
		{
			Palette.ApplyTo(session);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			RandomEffectRuntime.Attach(session, RandomEffectKind.PoisonBomb);
			Character character = session.Character;
			session.Effects.Add(delegate
			{
				EnergyBoostService.RemoveOwned(character);
			});
			session.Effects.Add(delegate
			{
				PoisonDrainService.RemoveForSource(character);
			});
		}
	}
	internal static class SkeletonFormFactory
	{
		private static readonly Dictionary<SkeletonKind, ISkeletonForm> Forms = new Dictionary<SkeletonKind, ISkeletonForm>
		{
			[SkeletonKind.Trauma] = new TraumaSkeleton(),
			[SkeletonKind.Heat] = new HeatSkeleton(),
			[SkeletonKind.Poison] = new PoisonSkeleton(),
			[SkeletonKind.Cold] = new ColdSkeleton(),
			[SkeletonKind.Sleep] = new SleepSkeleton(),
			[SkeletonKind.Spore] = new SporeSkeleton(),
			[SkeletonKind.Hunger] = new HungerSkeleton(),
			[SkeletonKind.Cactus] = new CactusSkeleton(),
			[SkeletonKind.Stone] = new StoneSkeleton(),
			[SkeletonKind.FlyTrap] = new FlyTrapSkeleton(),
			[SkeletonKind.Web] = new WebSkeleton()
		};

		internal static ISkeletonForm Get(SkeletonKind kind)
		{
			if (!Forms.TryGetValue(kind, out var value))
			{
				return Forms[SkeletonKind.Trauma];
			}
			return value;
		}

		internal static bool IsSelfImmuneTo(SkeletonKind kind, STATUSTYPE statusType)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (kind != SkeletonKind.None && Forms.TryGetValue(kind, out var value))
			{
				return value.IsSelfImmuneTo(statusType);
			}
			return false;
		}
	}
	internal sealed class SleepSkeleton : ISkeletonForm
	{
		internal static readonly PaletteInfo Palette = PaletteInfo.Two(new Color(1f, 0.24f, 0.62f), new Color(1f, 0.88f, 0.94f), 0.3f);

		public SkeletonKind Kind => SkeletonKind.Sleep;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)statusType == 6;
		}

		internal static void ApplyVisual(SkeletonSession session)
		{
			Palette.ApplyTo(session);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			CharacterModifierService.ApplyMovementDelta(session, -0.2f);
			RandomEffectRuntime.Attach(session, RandomEffectKind.SleepDoze);
			SleepGloomVisionService.Attach(session);
		}
	}
	internal sealed class SporeSkeleton : ISkeletonForm
	{
		public SkeletonKind Kind => SkeletonKind.Spore;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)statusType == 10;
		}

		internal static void ApplyVisual(SkeletonSession session, bool forceShow = false)
		{
			ZombieVisualService.Attach(session, forceShow);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			SporeHungerService.Attach(session);
			RandomEffectRuntime.Attach(session, RandomEffectKind.SporeBomb, RandomEffectKind.ShelfFungus);
			CharacterModifierService.RefreshWeightOnCleanup(session);
		}
	}
	internal sealed class StoneSkeleton : ISkeletonForm
	{
		internal static readonly PaletteInfo Palette = PaletteInfo.Flat(new Color(0.38f, 0.39f, 0.4f));

		public SkeletonKind Kind => SkeletonKind.Stone;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)statusType == 13;
		}

		internal static void ApplyVisual(SkeletonSession session, bool forceShow = false)
		{
			Palette.ApplyTo(session);
			StoneVisualService.Attach(session, forceShow);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			CharacterModifierService.ApplyMovementDelta(session, -1f / 3f);
			CharacterModifierService.ApplyMassMultiplier(session, 3f);
			CharacterModifierService.RefreshWeightOnCleanup(session);
			Character character = session.Character;
			if ((Object)(object)character != (Object)null && ((MonoBehaviourPun)character).photonView.IsMine)
			{
				using (StatusMutationScope.Allow(character))
				{
					character.refs.afflictions.SetStatus((STATUSTYPE)7, 0f, true);
				}
			}
			session.Effects.Add(delegate
			{
				StonePetrifyService.ReleaseForSource(character);
			});
		}
	}
	internal sealed class TraumaSkeleton : ISkeletonForm
	{
		public SkeletonKind Kind => SkeletonKind.Trauma;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)statusType == 0;
		}

		public void Activate(SkeletonSession session)
		{
			CharacterModifierService.ApplyRunDelta(session, -0.25f);
			CharacterModifierService.ApplyClimbDelta(session, 0.5f);
			CharacterModifierService.RefreshWeightOnCleanup(session);
			RandomEffectRuntime.Attach(session, RandomEffectKind.TraumaBone);
		}
	}
	internal sealed class WebSkeleton : ISkeletonForm
	{
		internal static readonly PaletteInfo Palette = PaletteInfo.Two(new Color(0.75f, 0.05f, 0.05f), new Color(0.05f, 0.15f, 0.65f), 0.5f);

		public SkeletonKind Kind => SkeletonKind.Web;

		public bool IsSelfImmuneTo(STATUSTYPE statusType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			return (int)statusType == 11;
		}

		internal static void ApplyVisual(SkeletonSession session, bool forceShow = false)
		{
			Palette.ApplyTo(session);
			WebVisualService.Attach(session, forceShow);
			SpiderVisualService.Attach(session, forceShow);
		}

		public void Activate(SkeletonSession session)
		{
			ApplyVisual(session);
			SpiderSenseSoundService.Play(session);
			WebContactService.Attach(session);
			WebGrappleService.Attach(session);
			CharacterModifierService.ApplyClimbDelta(session, 1f);
		}
	}
}
namespace SecondAscent.Runtime
{
	internal sealed class CheckboxHover : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
	{
		internal Image Image;

		internal Func<bool> Getter;

		internal Sprite OffSprite;

		internal Sprite OnSprite;

		internal Sprite OffHoverSprite;

		internal Sprite OnHoverSprite;

		private bool _hovered;

		internal void Refresh()
		{
			Apply(_hovered);
		}

		public void OnPointerEnter(PointerEventData eventData)
		{
			_hovered = true;
			Apply(hover: true);
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			_hovered = false;
			Apply(hover: false);
		}

		private void OnDisable()
		{
			_hovered = false;
		}

		private void Apply(bool hover)
		{
			if (!((Object)(object)Image == (Object)null) && Getter != null)
			{
				bool flag = Getter();
				Image.sprite = ((!hover) ? (flag ? OnSprite : OffSprite) : (flag ? OnHoverSprite : OffHoverSprite));
			}
		}
	}
	internal sealed class EffectHandle : IDisposable
	{
		private readonly List<Action> _cleanupActions = new List<Action>();

		private bool _disposed;

		internal void Add(Action cleanup)
		{
			if (cleanup != null)
			{
				if (_disposed)
				{
					cleanup();
				}
				else
				{
					_cleanupActions.Add(cleanup);
				}
			}
		}

		public void Dispose()
		{
			if (_disposed)
			{
				return;
			}
			_disposed = true;
			for (int num = _cleanupActions.Count - 1; num >= 0; num--)
			{
				try
				{
					_cleanupActions[num]();
				}
				catch (Exception arg)
				{
					ManualLogSource logSource = Plugin.LogSource;
					if (logSource != null)
					{
						logSource.LogError((object)$"Skeleton effect cleanup failed.\n{arg}");
					}
				}
			}
			_cleanupActions.Clear();
		}
	}
	internal sealed class FormVisualHandle : IDisposable
	{
		private readonly EffectHandle _effects;

		internal EffectHandle Effects => _effects;

		internal FormVisualHandle(EffectHandle effects)
		{
			_effects = effects;
		}

		public void Dispose()
		{
			_effects.Dispose();
		}
	}
	internal enum GoldenIdolChoiceAction : byte
	{
		ArcadeCostume = 1,
		StandardDeath,
		ChangeDeadSkeleton
	}
	internal enum GoldenIdolRejectReason : byte
	{
		InvalidRequest = 1,
		IncompatibleSession,
		UnavailableState,
		AlreadyActive
	}
	internal static class GoldenIdolSelectionService
	{
		private static readonly HashSet<int> ReservedItems = new HashSet<int>();

		internal static Action<int, SkeletonKind> LocalCommitted;

		internal static Action<int, GoldenIdolRejectReason> LocalRejected;

		internal static bool Request(Character character, int itemViewId, SkeletonKind kind)
		{
			if ((Object)(object)((character != null) ? ((MonoBehaviourPun)character).photonView : null) == (Object)null || !((MonoBehaviourPun)character).photonView.IsMine || !IsSelectableKind(kind) || !IsLocalIdolHeld(itemViewId))
			{
				return false;
			}
			SkeletonNetworkBridge.Attach(character);
			((MonoBehaviourPun)character).photonView.RPC("SSR_RequestGoldenIdolChoice", (RpcTarget)2, new object[2]
			{
				itemViewId,
				(int)kind
			});
			return true;
		}

		internal static bool IsLocalIdolHeld(int itemViewId)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			Character localCharacter = Character.localCharacter;
			PhotonView val = PhotonView.Find(itemViewId);
			Item val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Item>() : null);
			if ((Object)(object)localCharacter != (Object)null && (Object)(object)val2 != (Object)null && val2.itemID == 4243 && (int)val2.itemState == 1 && (Object)(object)val2.holderCharacter == (Object)(object)localCharacter)
			{
				return (Object)(object)localCharacter.data.currentItem == (Object)(object)val2;
			}
			return false;
		}

		internal static void ReceiveRequest(Character character, int itemViewId, SkeletonKind kind, PhotonMessageInfo info)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Invalid comparison between Unknown and I4
			if (!PhotonNetwork.IsMasterClient || (Object)(object)((character != null) ? ((MonoBehaviourPun)character).photonView : null) == (Object)null || info.Sender != ((MonoBehaviourPun)character).photonView.Owner || !IsSelectableKind(kind))
			{
				Reject(character, itemViewId, GoldenIdolRejectReason.InvalidRequest);
				return;
			}
			if (SkeletonRegistry.IsRunEnding || (Object)(object)character.data == (Object)null || character.data.dead)
			{
				Reject(character, itemViewId, GoldenIdolRejectReason.UnavailableState);
				return;
			}
			if (!NetworkSession.AllPlayersCompatible())
			{
				Reject(character, itemViewId, GoldenIdolRejectReason.IncompatibleSession);
				return;
			}
			PhotonView val = PhotonView.Find(itemViewId);
			Item val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Item>() : null);
			if ((Object)(object)val2 == (Object)null || val2.itemID != 4243 || (int)val2.itemState != 1 || (Object)(object)val2.holderCharacter != (Object)(object)character || (Object)(object)character.data.currentItem != (Object)(object)val2 || ReservedItems.Contains(itemViewId))
			{
				Reject(character, itemViewId, GoldenIdolRejectReason.InvalidRequest);
				return;
			}
			bool flag = SkeletonRegistry.IsCostume(character);
			bool flag2 = SkeletonRegistry.IsUndead(character) && !flag;
			if (SkeletonRegistry.GetKind(character) == kind)
			{
				Reject(character, itemViewId, GoldenIdolRejectReason.AlreadyActive);
				return;
			}
			GoldenIdolChoiceAction goldenIdolChoiceAction;
			if (HostSettings.Mode != GameMode.Arcade)
			{
				goldenIdolChoiceAction = ((!flag2) ? GoldenIdolChoiceAction.StandardDeath : GoldenIdolChoiceAction.ChangeDeadSkeleton);
			}
			else
			{
				if (flag2)
				{
					Reject(character, itemViewId, GoldenIdolRejectReason.UnavailableState);
					return;
				}
				goldenIdolChoiceAction = GoldenIdolChoiceAction.ArcadeCostume;
			}
			ReservedItems.Add(itemViewId);
			int num = ((goldenIdolChoiceAction == GoldenIdolChoiceAction.StandardDeath) ? Math.Max(1, NetworkSession.GetDeathSequence(((MonoBehaviourPun)character).photonView.Owner) + 1) : NetworkSession.GetDeathSequence(((MonoBehaviourPun)character).photonView.Owner));
			((MonoBehaviourPun)val2).photonView.RPC("Consume", (RpcTarget)5, new object[1] { ((MonoBehaviourPun)character).photonView.ViewID });
			((MonoBehaviourPun)character).photonView.RPC("SSR_CommitGoldenIdolChoice", (RpcTarget)5, new object[4]
			{
				itemViewId,
				(int)kind,
				(int)goldenIdolChoiceAction,
				num
			});
			if (goldenIdolChoiceAction == GoldenIdolChoiceAction.StandardDeath)
			{
				DeathCoordinator.ResolveChosenDeath(character, kind, num);
			}
		}

		internal static void Commit(Character character, int itemViewId, SkeletonKind kind, GoldenIdolChoiceAction action, int sequence, PhotonMessageInfo info)
		{
			//IL_0000: 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)
			if (info.Sender == null || !info.Sender.IsMasterClient || (Object)(object)((character != null) ? ((MonoBehaviourPun)character).photonView : null) == (Object)null || !IsSelectableKind(kind))
			{
				return;
			}
			switch (action)
			{
			default:
				return;
			case GoldenIdolChoiceAction.ArcadeCostume:
				SkeletonCostumeService.Apply(character, kind);
				break;
			case GoldenIdolChoiceAction.StandardDeath:
				if (SkeletonRegistry.IsCostume(character))
				{
					SkeletonCostumeService.Remove(character);
				}
				break;
			case GoldenIdolChoiceAction.ChangeDeadSkeleton:
				ApplyDeadSkeletonForm(character, kind, sequence);
				break;
			}
			if (character.IsLocal)
			{
				LocalCommitted?.Invoke(itemViewId, kind);
			}
		}

		internal static void ReceiveRejected(Character character, int itemViewId, GoldenIdolRejectReason reason, PhotonMessageInfo info)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (info.Sender != null && info.Sender.IsMasterClient && (Object)(object)character != (Object)null && character.IsLocal)
			{
				LocalRejected?.Invoke(itemViewId, reason);
			}
		}

		internal static void Reset()
		{
			ReservedItems.Clear();
		}

		private static void ApplyDeadSkeletonForm(Character character, SkeletonKind kind, int sequence)
		{
			if ((Object)(object)character == (Object)null || !SkeletonRegistry.IsUndead(character) || SkeletonRegistry.IsCostume(character))
			{
				return;
			}
			CharacterRefs refs = character.refs;
			if (refs != null)
			{
				CharacterCustomization customization = refs.customization;
				if (customization != null)
				{
					CustomizationRefs refs2 = customization.refs;
					if (refs2 != null)
					{
						refs2.SetSkeleton(true, character.IsLocal);
					}
				}
			}
			SkeletonSession skeletonSession = SkeletonRegistry.ChangeUndeadForm(character, kind, sequence);
			if (skeletonSession != null)
			{
				SkeletonEffectLifecycle.Activate(skeletonSession);
				GameAssetCatalog.PlayReanimationFeedback(character);
			}
		}

		private static void Reject(Character character, int itemViewId, GoldenIdolRejectReason reason)
		{
			if (!((Object)(object)((character != null) ? ((MonoBehaviourPun)character).photonView : null) == (Object)null))
			{
				((MonoBehaviourPun)character).photonView.RPC("SSR_RejectGoldenIdolChoice", ((MonoBehaviourPun)character).photonView.Owner, new object[2]
				{
					itemViewId,
					(int)reason
				});
			}
		}

		private static bool IsSelectableKind(SkeletonKind kind)
		{
			if (kind != SkeletonKind.None)
			{
				return Enum.IsDefined(typeof(SkeletonKind), kind);
			}
			return false;
		}
	}
	internal static class HostSettings
	{
		[Serializable]
		private sealed class Snapshot
		{
			public int mode;

			public bool playersCanSelect = true;

			public bool manualEffectHotkeys = true;

			public bool skeletonsCanDie;

			public bool skeletonsCanStarve;

			public bool heatCampfire = true;

			public bool heatBurnAura = true;

			public bool heatExplosion = true;

			public bool traumaBones = true;

			public bool coldExplosion = true;

			public bool coldSlip = true;

			public bool coldHooks = true;

			public bool hungerPreySniffer = true;

			public bool hungerAura = true;

			public bool poisonExplosion = true;

			public bool poisonDrain = true;

			public bool poisonBuff = true;

			public bool poisonAura = true;

			public bool sleepAura = true;

			public bool sleepFogDissolve = true;

			public bool sleepDoze = true;

			public bool sporeHungerEat = true;

			public bool sporeExplosion = true;

			public bool sporeShelfFungus = true;

			public bool cactusHealing = true;

			public bool cactusStick = true;

			public bool webGrapple = true;

			public bool webBite = true;

			public bool stonePetrify = true;

			public bool stoneForceImmunity = true;

			public bool flyTrapExplosion = true;

			public bool flyTrapTrampoline = true;
		}

		private const string RoomProperty = "SSR.HostSettings";

		private static bool _locked;

		private static GameMode _lockedMode;

		private static bool _lockedCanDie;

		private static bool _lockedCanStarve;

		private static object _clientSnapshotRoom;

		private static string _clientSnapshotJson;

		internal static ConfigEntry<GameMode> ModeEntry { get; private set; }

		internal static ConfigEntry<bool> PlayersCanSelectEntry { get; private set; }

		internal static ConfigEntry<bool> ManualEffectHotkeysEntry { get; private set; }

		internal static ConfigEntry<bool> SkeletonsCanDieEntry { get; private set; }

		internal static ConfigEntry<bool> SkeletonsCanStarveEntry { get; private set; }

		internal static ConfigEntry<bool> HeatCampfireEntry { get; private set; }

		internal static ConfigEntry<bool> HeatBurnAuraEntry { get; private set; }

		internal static ConfigEntry<bool> HeatExplosionEntry { get; private set; }

		internal static ConfigEntry<bool> TraumaBonesEntry { get; private set; }

		internal static ConfigEntry<bool> ColdExplosionEntry { get; private set; }

		internal static ConfigEntry<bool> ColdSlipEntry { get; private set; }

		internal static ConfigEntry<bool> ColdHooksEntry { get; private set; }

		internal static ConfigEntry<bool> HungerPreySnifferEntry { get; private set; }

		internal static ConfigEntry<bool> HungerAuraEntry { get; private set; }

		internal static ConfigEntry<bool> PoisonExplosionEntry { get; private set; }

		internal static ConfigEntry<bool> PoisonDrainEntry { get; private set; }

		internal static ConfigEntry<bool> PoisonBuffEntry { get; private set; }

		internal static ConfigEntry<bool> PoisonAuraEntry { get; private set; }

		internal static ConfigEntry<bool> SleepAuraEntry { get; private set; }

		internal static ConfigEntry<bool> SleepFogDissolveEntry { get; private set; }

		internal static ConfigEntry<bool> SleepDozeEntry { get; private set; }

		internal static ConfigEntry<bool> SporeHungerEatEntry { get; private set; }

		internal static ConfigEntry<bool> SporeExplosionEntry { get; private set; }

		internal static ConfigEntry<bool> SporeShelfFungusEntry { get; private set; }

		internal static ConfigEntry<bool> CactusHealingEntry { get; private set; }

		internal static ConfigEntry<bool> CactusStickEntry { get; private set; }

		internal static ConfigEntry<bool> WebGrappleEntry { get; private set; }

		internal static ConfigEntry<bool> WebBiteEntry { get; private set; }

		internal static ConfigEntry<bool> StonePetrifyEntry { get; private set; }

		internal static ConfigEntry<bool> StoneForceImmunityEntry { get; private set; }

		internal static ConfigEntry<bool> FlyTrapExplosionEntry { get; private set; }

		internal static ConfigEntry<bool> FlyTrapTrampolineEntry { get; private set; }

		internal static GameMode Mode { get; private set; } = GameMode.Standard;

		internal static bool PlayersCanSelect { get; private set; }

		internal static bool ManualEffectHotkeysEnabled { get; private set; }

		internal static bool SkeletonsCanDie { get; private set; }

		internal static bool SkeletonsCanStarve { get; private set; }

		internal static bool HeatCampfireEnabled { get; private set; } = true;

		internal static bool HeatBurnAuraEnabled { get; private set; } = true;

		internal static bool HeatExplosionEnabled { get; private set; } = true;

		internal static bool ColdExplosionEnabled { get; private set; } = true;

		internal static bool ColdSlipEnabled { get; private set; } = true;

		internal static bool ColdHooksEnabled { get; private set; } = true;

		internal static bool TraumaBonesEnabled { get; private set; } = true;

		internal static bool HungerPreySnifferEnabled { get; private set; } = true;

		internal static bool HungerAuraEnabled { get; private set; } = true;

		internal static bool PoisonExplosionEnabled { get; private set; } = true;

		internal static bool PoisonDrainEnabled { get; private set; } = true;

		internal static bool PoisonBuffEnabled { get; private set; } = true;

		internal static bool PoisonAuraEnabled { get; private set; } = true;

		internal static bool SleepAuraEnabled { get; private set; } = true;

		internal static bool SleepFogDissolveEnabled { get; private set; } = true;

		internal static bool SleepDozeEnabled { get; private set; } = true;

		internal static bool SporeHungerEatEnabled { get; private set; } = true;

		internal static bool SporeExplosionEnabled { get; private set; } = true;

		internal static bool SporeShelfFungusEnabled { get; private set; } = true;

		internal static bool CactusHealingEnabled { get; private set; } = true;

		internal static bool CactusStickEnabled { get; private set; } = true;

		internal static bool WebGrappleEnabled { get; private set; } = true;

		internal static bool WebBiteEnabled { get; private set; } = true;

		internal static bool StonePetrifyEnabled { get; private set; } = true;

		internal static bool StoneForceImmunityEnabled { get; private set; } = true;

		internal static bool FlyTrapExplosionEnabled { get; private set; } = true;

		internal static bool FlyTrapTrampolineEnabled { get; private set; } = true;

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

		internal static bool InAirport
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				Scene activeScene = SceneManager.GetActiveScene();
				return ((Scene)(ref activeScene)).name == "Airport";
			}
		}

		internal static bool RulesLocked => !InAirport;

		internal static bool CanSelectSkeleton
		{
			get
			{
				if (Mode == GameMode.Arcade)
				{
					return PlayersCanSelect;
				}
				return false;
			}
		}

		private static bool EffectiveArcadeToggle(GameMode mode, bool configured)
		{
			return mode == GameMode.Arcade && configured;
		}

		private static bool EffectiveCanDie(GameMode mode, bool configured)
		{
			return EffectiveArcadeToggle(mode, configured);
		}

		private static bool EffectiveCanStarve(GameMode mode, bool configured)
		{
			return EffectiveArcadeToggle(mode, configured);
		}

		private static void RefreshLock()
		{
			if (InAirport)
			{
				_locked = false;
			}
			else if (!_locked)
			{
				_locked = true;
				_lockedMode = ModeEntry?.Value ?? GameMode.Standard;
				_lockedCanDie = EffectiveCanDie(_lockedMode, SkeletonsCanDieEntry?.Value ?? false);
				_lockedCanStarve = EffectiveCanStarve(_lockedMode, SkeletonsCanStarveEntry?.Value ?? false);
				ManualLogSource logSource = Plugin.LogSource;
				if (logSource != null)
				{
					logSource.LogInfo((object)($"Host settings locked for this expedition: mode={_lockedMode}, " + $"playersCanSelect={EffectiveArcadeToggle(_lockedMode, PlayersCanSelectEntry?.Value ?? true)}, " + $"manualEffects={EffectiveArcadeToggle(_lockedMode, ManualEffectHotkeysEntry?.Value ?? true)}, " + $"canDie={_lockedCanDie}, " + $"canStarve={_lockedCanStarve}."));
				}
			}
		}

		internal static void Bind(ConfigFile config)
		{
			ModeEntry = config.Bind<GameMode>("Arcade", "GameMode", GameMode.Standard, "Standard: skeletons only ever come from a real death, as always. Arcade: also adds free choice of a skeleton form independent of death. Host-only - every player uses whatever the host has set here, regardless of their own copy of this file.");
			PlayersCanSelectEntry = config.Bind<bool>("Arcade", "PlayersCanSelect", true, "On (Arcade only): every player may pick and swap their own skeleton form from the quick-selection menu. Off: that menu is read-only for everyone, including the host; statues still allow selection. Always forced off in Standard. Host-only and editable during the expedition.");
			ManualEffectHotkeysEntry = config.Bind<bool>("Arcade", "ManualEffectHotkeys", true, "On (Arcade only): players may manually trigger the active skeleton's random effects with their personal hotkeys. Off: manual hotkeys do nothing, while the ordinary automatic random timers continue unchanged. Host-only and editable during the expedition. Always forced off in Standard.");
			SkeletonsCanDieEntry = config.Bind<bool>("Arcade", "SkeletonsCanDie", false, "Off (default): support skeletons are invincible, as always. On: a skeleton can take lethal damage a second time and dies for real (becomes a spectator ghost, the same as a normal death) - each form is passively immune to its own themed hazard first (Heat to fire, Cold to freezing, etc). Host-only.");
			SkeletonsCanStarveEntry = config.Bind<bool>("Arcade", "SkeletonsCanStarve", false, "Off (default): skeletons never accumulate Hunger, independent of SkeletonsCanDie. On: Hunger accumulates normally like a living player - the Hunger form itself stays immune regardless (its own theme), and whether this can actually kill a skeleton is still governed separately by SkeletonsCanDie. Host-only.");
			HeatCampfireEntry = config.Bind<bool>("Heat", "CampfireEffect", true, "Heat's own flaming head works as a real campfire for other scouts (warmth, cooking). Host-only.");
			HeatBurnAuraEntry = config.Bind<bool>("Heat", "BurnOnContact", true, "Heat burns scouts it physically touches. Host-only.");
			HeatExplosionEntry = config.Bind<bool>("Heat", "Explosion", true, "Heat's random Dynamite explosion event. Host-only.");
			TraumaBonesEntry = config.Bind<bool>("Trauma", "Bones", true, "Trauma sheds a climbable bone every couple of minutes. Host-only.");
			ColdExplosionEntry = config.Bind<bool>("Cold", "Explosion", true, "Cold's random Flash Plant burst event. Host-only.");
			ColdSlipEntry = config.Bind<bool>("Cold", "Slip", true, "Cold's random banana-style slip while sprinting. Host-only.");
			ColdHooksEntry = config.Bind<bool>("Cold", "Hooks", true, "Cold drops climbable ice hooks while climbing. Host-only.");
			HungerPreySnifferEntry = config.Bind<bool>("Hunger", "PreySniffer", true, "Hunger can hold RMB to highlight nearby food. Host-only.");
			HungerAuraEntry = config.Bind<bool>("Hunger", "GazeAura", true, "Hunger applies Hunger to anyone looking at it up close. Host-only.");
			PoisonExplosionEntry = config.Bind<bool>("Poison", "Explosion", true, "Poison's Spore Bomb burst - both the random timer event and the drain battery's own discharge. Host-only.");
			PoisonDrainEntry = config.Bind<bool>("Poison", "Drain", true, "Poison's battery ability (hold RMB+LMB on an ally to drain their Poison status). Host-only.");
			PoisonBuffEntry = config.Bind<bool>("Poison", "Buff", true, "Poison gets a refreshed Energy Drink buff when it receives poison. Host-only.");
			PoisonAuraEntry = config.Bind<bool>("Poison", "ProximityAura", true, "Poison applies Poison to anyone standing right next to it. Host-only.");
			SleepAuraEntry = config.Bind<bool>("Sleep", "GazeAura", true, "Sleep applies Drowsy to anyone looking at it up close. Host-only.");
			SleepFogDissolveEntry = config.Bind<bool>("Sleep", "FogDissolve", true, "Sleep carries a lit-candle-style zone that dissolves fog/Gloom nearby. Host-only.");
			SleepDozeEntry = config.Bind<bool>("Sleep", "Doze", true, "Sleep's random chance to doze off for a second. Host-only.");
			SporeHungerEatEntry = config.Bind<bool>("Spore", "EatPlayer", true, "Spore's Hunger is only cleared by eating a living scout, and can do so on contact. Host-only.");
			SporeExplosionEntry = config.Bind<bool>("Spore", "Explosion", true, "Spore's random Spore Bomb cloud event. Host-only.");
			SporeShelfFungusEntry = config.Bind<bool>("Spore", "ShelfFungus", true, "Spore's random Shelf Fungus platform event. Host-only.");
			CactusHealingEntry = config.Bind<bool>("Cactus", "Healing", true, "Cactus gradually treats negative statuses on a scout it's stuck to. Host-only.");
			CactusStickEntry = config.Bind<bool>("Cactus", "StickAndThorns", true, "Cactus sticks to and adds a thorn on touching a scout. Host-only.");
			WebGrappleEntry = config.Bind<bool>("Web", "Grapple", true, "Web's hand-origin grapple shot. Host-only.");
			WebBiteEntry = config.Bind<bool>("Web", "Bite", true, "Web has a chance to inflict Web status on contact. Host-only.");
			StonePetrifyEntry = config.Bind<bool>("Stone", "Petrify", true, "Stone can hold RMB while climbing to freeze solid on the wall. Host-only.");
			StoneForceImmunityEntry = config.Bind<bool>("Stone", "ForceImmunity", true, "Stone cannot be knocked back, blown, or made to fall. Host-only.");
			FlyTrapExplosionEntry = config.Bind<bool>("FlyTrap", "Explosion", true, "Fly Trap's random Explosive Spore Bomb event. Host-only.");
			FlyTrapTrampolineEntry = config.Bind<bool>("FlyTrap", "Trampoline", true, "Fly Trap gives a threefold pal-jump boost to whoever jumps off it. Host-only.");
			ApplyLocal();
		}

		private static void ApplyLocal()
		{
			Mode = (_locked ? _lockedMode : (ModeEntry?.Value ?? GameMode.Standard));
			PlayersCanSelect = EffectiveArcadeToggle(Mode, PlayersCanSelectEntry?.Value ?? true);
			ManualEffectHotkeysEnabled = EffectiveArcadeToggle(Mode, ManualEffectHotkeysEntry?.Value ?? true);
			SkeletonsCanDie = (_locked ? _lockedCanDie : EffectiveCanDie(Mode, SkeletonsCanDieEntry?.Value ?? false));
			SkeletonsCanStarve = (_locked ? _lockedCanStarve : EffectiveCanStarve(Mode, SkeletonsCanStarveEntry?.Value ?? false));
			HeatCampfireEnabled = HeatCampfireEntry?.Value ?? true;
			HeatBurnAuraEnabled = HeatBurnAuraEntry?.Value ?? true;
			HeatExplosionEnabled = HeatExplosionEntry?.Value ?? true;
			TraumaBonesEnabled = TraumaBonesEntry?.Value ?? true;
			ColdExplosionEnabled = ColdExplosionEntry?.Value ?? true;
			ColdSlipEnabled = ColdSlipEntry?.Value ?? true;
			ColdHooksEnabled = ColdHooksEntry?.Value ?? true;
			HungerPreySnifferEnabled = HungerPreySnifferEntry?.Value ?? true;
			HungerAuraEnabled = HungerAuraEntry?.Value ?? true;
			PoisonExplosionEnabled = PoisonExplosionEntry?.Value ?? true;
			PoisonDrainEnabled = PoisonDrainEntry?.Value ?? true;
			PoisonBuffEnabled = PoisonBuffEntry?.Value ?? true;
			PoisonAuraEnabled = PoisonAuraEntry?.Value ?? true;
			SleepAuraEnabled = SleepAuraEntry?.Value ?? true;
			SleepFogDissolveEnabled = SleepFogDissolveEntry?.Value ?? true;
			SleepDozeEnabled = SleepDozeEntry?.Value ?? true;
			SporeHungerEatEnabled = SporeHungerEatEntry?.Value ?? true;
			SporeExplosionEnabled = SporeExplosionEntry?.Value ?? true;
			SporeShelfFungusEnabled = SporeShelfFungusEntry?.Value ?? true;
			CactusHealingEnabled = CactusHealingEntry?.Value ?? true;
			CactusStickEnabled = CactusStickEntry?.Value ?? true;
			WebGrappleEnabled = WebGrappleEntry?.Value ?? true;
			WebBiteEnabled = WebBiteEntry?.Value ?? true;
			StonePetrifyEnabled = StonePetrifyEntry?.Value ?? true;
			StoneForceImmunityEnabled = StoneForceImmunityEntry?.Value ?? true;
			FlyTrapExplosionEnabled = FlyTrapExplosionEntry?.Value ?? true;
			FlyTrapTrampolineEnabled = FlyTrapTrampolineEntry?.Value ?? true;
		}

		private static void ApplyAwaitingHostSnapshot()
		{
			Mode = GameMode.Standard;
			PlayersCanSelect = false;
			ManualEffectHotkeysEnabled = false;
			SkeletonsCanDie = false;
			SkeletonsCanStarve = false;
			HeatCampfireEnabled = false;
			HeatBurnAuraEnabled = false;
			HeatExplosionEnabled = false;
			TraumaBonesEnabled = false;
			ColdExplosionEnabled = false;
			ColdSlipEnabled = false;
			ColdHooksEnabled = false;
			HungerPreySnifferEnabled = false;
			HungerAuraEnabled = false;
			PoisonExplosionEnabled = false;
			PoisonDrainEnabled = false;
			PoisonBuffEnabled = false;
			PoisonAuraEnabled = false;
			SleepAuraEnabled = false;
			SleepFogDissolveEnabled = false;
			SleepDozeEnabled = false;
			SporeHungerEatEnabled = false;
			SporeExplosionEnabled = false;
			SporeShelfFungusEnabled = false;
			CactusHealingEnabled = false;
			CactusStickEnabled = false;
			WebGrappleEnabled = false;
			WebBiteEnabled = false;
			StonePetrifyEnabled = false;
			StoneForceImmunityEnabled = false;
			FlyTrapExplosionEnabled = false;
			FlyTrapTrampolineEnabled = false;
		}

		internal static void ApplyModeDefaults(GameMode mode)
		{
			bool value = mode == GameMode.Arcade;
			if (SkeletonsCanDieEntry != null)
			{
				SkeletonsCanDieEntry.Value = value;
			}
			if (SkeletonsCanStarveEntry != null)
			{
				SkeletonsCanStarveEntry.Value = value;
			}
			ApplyLocal();
		}

		internal static void RestoreDefaults()
		{
			ConfigEntry<bool>[] array = new ConfigEntry<bool>[27]
			{
				TraumaBonesEntry, HeatCampfireEntry, HeatBurnAuraEntry, HeatExplosionEntry, ColdExplosionEntry, ColdSlipEntry, ColdHooksEntry, HungerPreySnifferEntry, HungerAuraEntry, PoisonExplosionEntry,
				PoisonDrainEntry, PoisonBuffEntry, PoisonAuraEntry, SleepAuraEntry, SleepFogDissolveEntry, SleepDozeEntry, SporeHungerEatEntry, SporeExplosionEntry, SporeShelfFungusEntry, CactusHealingEntry,
				CactusStickEntry, WebGrappleEntry, WebBiteEntry, StonePetrifyEntry, StoneForceImmunityEntry, FlyTrapExplosionEntry, FlyTrapTrampolineEntry
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] != null)
				{
					array[i].Value = true;
				}
			}
			if (PlayersCanSelectEntry != null)
			{
				PlayersCanSelectEntry.Value = true;
			}
			if (ManualEffectHotkeysEntry != null)
			{
				ManualEffectHotkeysEntry.Value = true;
			}
			ApplyModeDefaults(ModeEntry?.Value ?? GameMode.Standard);
		}

		internal static void RestoreRuntimeDefaults()
		{
			ConfigEntry<bool>[] array = new ConfigEntry<bool>[27]
			{
				TraumaBonesEntry, HeatCampfireEntry, HeatBurnAuraEntry, HeatExplosionEntry, ColdExplosionEntry, ColdSlipEntry, ColdHooksEntry, HungerPreySnifferEntry, HungerAuraEntry, PoisonExplosionEntry,
				PoisonDrainEntry, PoisonBuffEntry, PoisonAuraEntry, SleepAuraEntry, SleepFogDissolveEntry, SleepDozeEntry, SporeHungerEatEntry, SporeExplosionEntry, SporeShelfFungusEntry, CactusHealingEntry,
				CactusStickEntry, WebGrappleEntry, WebBiteEntry, StonePetrifyEntry, StoneForceImmunityEntry, FlyTrapExplosionEntry, FlyTrapTrampolineEntry
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] != null)
				{
					array[i].Value = true;
				}
			}
			if (PlayersCanSelectEntry != null)
			{
				PlayersCanSelectEntry.Value = true;
			}
			if (ManualEffectHotkeysEntry != null)
			{
				ManualEffectHotkeysEntry.Value = true;
			}
			ApplyLocal();
		}

		internal static void Sync()
		{
			if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)
			{
				RefreshLock();
			}
			if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient)
			{
				Publish();
			}
			Apply();
		}

		internal static void GuardGuestAuthority()
		{
			if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null && !PhotonNetwork.IsMasterClient)
			{
				Apply();
			}
		}

		private static void Publish()
		{
			//IL_0360: Unknown result type (might be due to invalid IL or missing references)
			//IL_036c: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Expected O, but got Unknown
			GameMode mode = (_locked ? _lockedMode : (ModeEntry?.Value ?? GameMode.Standard));
			string text = JsonUtility.ToJson((object)new Snapshot
			{
				mode = (int)mode,
				playersCanSelect = EffectiveArcadeToggle(mode, PlayersCanSelectEntry?.Value ?? true),
				manualEffectHotkeys = EffectiveArcadeToggle(mode, ManualEffectHotkeysEntry?.Value ?? true),
				skeletonsCanDie = (_locked ? _lockedCanDie : EffectiveCanDie(mode, SkeletonsCanDieEntry?.Value ?? false)),
				skeletonsCanStarve = (_locked ? _lockedCanStarve : EffectiveCanStarve(mode, SkeletonsCanStarveEntry?.Value ?? false)),
				heatCampfire = (HeatCampfireEntry?.Value ?? true),
				heatBurnAura = (HeatBurnAuraEntry?.Value ?? true),
				heatExplosion = (HeatExplosionEntry?.Value ?? true),
				traumaBones = (TraumaBonesEntry?.Value ?? true),
				coldExplosion = (ColdExplosionEntry?.Value ?? true),
				coldSlip = (ColdSlipEntry?.Value ?? true),
				coldHooks = (ColdHooksEntry?.Value ?? true),
				hungerPreySniffer = (HungerPreySnifferEntry?.Value ?? true),
				hungerAura = (HungerAuraEntry?.Value ?? true),
				poisonExplosion = (PoisonExplosionEntry?.Value ?? true),
				poisonDrain = (PoisonDrainEntry?.Value ?? true),
				poisonBuff = (PoisonBuffEntry?.Value ?? true),
				poisonAura = (PoisonAuraEntry?.Value ?? true),
				sleepAura = (SleepAuraEntry?.Value ?? true),
				sleepFogDissolve = (SleepFogDissolveEntry?.Value ?? true),
				sleepDoze = (SleepDozeEntry?.Value ?? true),
				sporeHungerEat = (SporeHungerEatEntry?.Value ?? true),
				sporeExplosion = (SporeExplosionEntry?.Value ?? true),
				sporeShelfFungus = (SporeShelfFungusEntry?.Value ?? true),
				cactusHealing = (CactusHealingEntry?.Value ?? true),
				cactusStick = (CactusStickEntry?.Value ?? true),
				webGrapple = (WebGrappleEntry?.Value ?? true),
				webBite = (WebBiteEntry?.Value ?? true),
				stonePetrify = (StonePetrifyEntry?.Value ?? true),
				stoneForceImmunity = (StoneForceImmunityEntry?.Value ?? true),
				flyTrapExplosion = (FlyTrapExplosionEntry?.Value ?? true),
				flyTrapTrampoline = (FlyTrapTrampolineEntry?.Value ?? true)
			});
			if (!((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"SSR.HostSettings", out object value) || !(value is string text2) || !(text2 == text))
			{
				PhotonNetwork.CurrentRoom.SetCustomProperties(new Hashtable { [(object)"SSR.HostSettings"] = text }, (Hashtable)null, (WebFlags)null);
			}
		}

		private static void Apply()
		{
			if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null || PhotonNetwork.IsMasterClient)
			{
				_clientSnapshotRoom = null;
				_clientSnapshotJson = null;
				ApplyLocal();
				return;
			}
			object value;
			string text = ((((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"SSR.HostSettings", out value) && value is string text2) ? text2 : null);
			if (_clientSnapshotRoom == PhotonNetwork.CurrentRoom && _clientSnapshotJson == text)
			{
				return;
			}
			_clientSnapshotRoom = PhotonNetwork.CurrentRoom;
			_clientSnapshotJson = text;
			if (text == null)
			{
				ApplyAwaitingHostSnapshot();
				return;
			}
			Snapshot snapshot;
			try
			{
				snapshot = JsonUtility.FromJson<Snapshot>(text);
			}
			catch (Exception arg)
			{
				ManualLogSource logSource = Plugin.LogSource;
				if (logSource != null)
				{
					logSource.LogWarning((object)$"Host settings: could not parse the room property; shared gameplay controls stay disabled until a valid host snapshot arrives.\n{arg}");
				}
				ApplyAwaitingHostSnapshot();
				return;
			}
			if (snapshot == null)
			{
				ApplyAwaitingHostSnapshot();
				return;
			}
			Mode = ((snapshot.mode == 1) ? GameMode.Arcade : GameMode.Standard);
			SkeletonsCanDie = EffectiveCanDie(Mode, snapshot.skeletonsCanDie);
			SkeletonsCanStarve = EffectiveCanStarve(Mode, snapshot.skeletonsCanStarve);
			PlayersCanSelect = EffectiveArcadeToggle(Mode, snapshot.playersCanSelect);
			ManualEffectHotkeysEnabled = EffectiveArcadeToggle(Mode, snapshot.manualEffectHotkeys);
			HeatCampfireEnabled = snapshot.heatCampfire;
			HeatBurnAuraEnabled = snapshot.heatBurnAura;
			HeatExplosionEnabled = snapshot.heatExplosion;
			TraumaBonesEnabled = snapshot.traumaBones;
			ColdExplosionEnabled = snapshot.coldExplosion;
			ColdSlipEnabled = snapshot.coldSlip;
			ColdHooksEnabled = snapshot.coldHooks;
			HungerPreySnifferEnabled = snapshot.hungerPreySniffer;
			HungerAuraEnabled = snapshot.hungerAura;
			PoisonExplosionEnabled = snapshot.poisonExplosion;
			PoisonDrainEnabled = snapshot.poisonDrain;
			PoisonBuffEnabled = snapshot.poisonBuff;
			PoisonAuraEnabled = snapshot.poisonAura;
			SleepAuraEnabled = snapshot.sleepAura;
			SleepFogDissolveEnabled = snapshot.sleepFogDissolve;
			SleepDozeEnabled = snapshot.sleepDoze;
			SporeHungerEatEnabled = snapshot.sporeHungerEat;
			SporeExplosionEnabled = snapshot.sporeExplosion;
			SporeShelfFungusEnabled = snapshot.sporeShelfFungus;
			CactusHealingEnabled = snapshot.cactusHealing;
			CactusStickEnabled = snapshot.cactusStick;
			WebGrappleEnabled = snapshot.webGrapple;
			WebBiteEnabled = snapshot.webBite;
			StonePetrifyEnabled = snapshot.stonePetrify;
			StoneForceImmunityEnabled = snapshot.stoneForceImmunity;
			FlyTrapExplosionEnabled = snapshot.flyTrapExplosion;
			FlyTrapTrampolineEnabled = snapshot.flyTrapTrampoline;
		}
	}
	internal sealed class HoverTint : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler
	{
		internal Image Target;

		internal Sprite NormalSprite;

		internal Sprite HoverSprite;

		private bool _hovered;

		private void OnEnable()
		{
			_hovered = false;
			Apply();
		}

		public void OnPointerEnter(PointerEventData eventData)
		{
			_hovered = true;
			Apply();
		}

		public void OnPointerExit(PointerEventData eventData)
		{
			_hovered = false;
			Apply();
		}

		private void OnDisable()
		{
			_hovered = false;
			Apply();
		}

		private void Apply()
		{
			if (!((Object)(object)Target == (Object)null))
			{
				Sprite val = (_hovered ? HoverSprite : NormalSprite);
				if ((Object)(object)val != (Object)null)
				{
					Target.sprite = val;
				}
			}
		}
	}
	internal static class LocalKeybinds
	{
		internal enum MenuAction
		{
			Settings,
			Selection,
			BecomeGhost
		}

		private static readonly Dictionary<RandomEffectKind, ConfigEntry<KeyCode>> EffectEntries = new Dictionary<RandomEffectKind, ConfigEntry<KeyCode>>();

		private static ConfigEntry<KeyCode> _settingsMenuEntry;

		private static ConfigEntry<KeyCode> _selectionMenuEntry;

		private static ConfigEntry<KeyCode> _becomeGhostEntry;

		internal static int Revision { get; private set; }

		internal static KeyCode SettingsMenuKey => (KeyCode)(((??)_settingsMenuEntry?.Value) ?? 288);

		internal static KeyCode SelectionMenuKey => (KeyCode)(((??)_selectionMenuEntry?.Value) ?? 110);

		internal static KeyCode BecomeGhostKey => (KeyCode)(((??)_becomeGhostEntry?.Value) ?? 112);

		internal static void Bin