Decompiled source of SecondAscent v1.1.0
SecondAscent.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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