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(); Invoke(_stick, victim, victimPart, ((Component)bodypartRig).transform.position, worldAnchor, (object)(STATUSTYPE)9, 0f); ConfigurableJoint[] components2 = ((Component)bodypartRig).GetComponents(); 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 action) { action(chest); } } internal static void AwardResourcefulness() { AchievementManager val = Object.FindFirstObjectByType(); 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)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)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)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)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 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("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(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(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() != (Object)null)) { return (Object)(object)val.GetComponent() != (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 Sessions = new Dictionary(); private static readonly HashSet KnownLivingViews = new HashSet(); private static readonly HashSet DeathsInProgress = new HashSet(); private static readonly HashSet ArcadeReanimationsInProgress = new HashSet(); private static readonly HashSet RevivesInProgress = new HashSet(); private static readonly List PruneBuffer = new List(); private static bool _runEnding; private static int _runEndingSceneHandle = -1; internal static bool IsRunEnding => _runEnding; internal static int ActiveCount => Sessions.Count; internal static IEnumerable 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 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 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 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 Forms = new Dictionary { [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 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 _cleanupActions = new List(); 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 ReservedItems = new HashSet(); internal static Action LocalCommitted; internal static Action 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() : 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() : 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 ModeEntry { get; private set; } internal static ConfigEntry PlayersCanSelectEntry { get; private set; } internal static ConfigEntry ManualEffectHotkeysEntry { get; private set; } internal static ConfigEntry SkeletonsCanDieEntry { get; private set; } internal static ConfigEntry SkeletonsCanStarveEntry { get; private set; } internal static ConfigEntry HeatCampfireEntry { get; private set; } internal static ConfigEntry HeatBurnAuraEntry { get; private set; } internal static ConfigEntry HeatExplosionEntry { get; private set; } internal static ConfigEntry TraumaBonesEntry { get; private set; } internal static ConfigEntry ColdExplosionEntry { get; private set; } internal static ConfigEntry ColdSlipEntry { get; private set; } internal static ConfigEntry ColdHooksEntry { get; private set; } internal static ConfigEntry HungerPreySnifferEntry { get; private set; } internal static ConfigEntry HungerAuraEntry { get; private set; } internal static ConfigEntry PoisonExplosionEntry { get; private set; } internal static ConfigEntry PoisonDrainEntry { get; private set; } internal static ConfigEntry PoisonBuffEntry { get; private set; } internal static ConfigEntry PoisonAuraEntry { get; private set; } internal static ConfigEntry SleepAuraEntry { get; private set; } internal static ConfigEntry SleepFogDissolveEntry { get; private set; } internal static ConfigEntry SleepDozeEntry { get; private set; } internal static ConfigEntry SporeHungerEatEntry { get; private set; } internal static ConfigEntry SporeExplosionEntry { get; private set; } internal static ConfigEntry SporeShelfFungusEntry { get; private set; } internal static ConfigEntry CactusHealingEntry { get; private set; } internal static ConfigEntry CactusStickEntry { get; private set; } internal static ConfigEntry WebGrappleEntry { get; private set; } internal static ConfigEntry WebBiteEntry { get; private set; } internal static ConfigEntry StonePetrifyEntry { get; private set; } internal static ConfigEntry StoneForceImmunityEntry { get; private set; } internal static ConfigEntry FlyTrapExplosionEntry { get; private set; } internal static ConfigEntry 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("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("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("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("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("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("Heat", "CampfireEffect", true, "Heat's own flaming head works as a real campfire for other scouts (warmth, cooking). Host-only."); HeatBurnAuraEntry = config.Bind("Heat", "BurnOnContact", true, "Heat burns scouts it physically touches. Host-only."); HeatExplosionEntry = config.Bind("Heat", "Explosion", true, "Heat's random Dynamite explosion event. Host-only."); TraumaBonesEntry = config.Bind("Trauma", "Bones", true, "Trauma sheds a climbable bone every couple of minutes. Host-only."); ColdExplosionEntry = config.Bind("Cold", "Explosion", true, "Cold's random Flash Plant burst event. Host-only."); ColdSlipEntry = config.Bind("Cold", "Slip", true, "Cold's random banana-style slip while sprinting. Host-only."); ColdHooksEntry = config.Bind("Cold", "Hooks", true, "Cold drops climbable ice hooks while climbing. Host-only."); HungerPreySnifferEntry = config.Bind("Hunger", "PreySniffer", true, "Hunger can hold RMB to highlight nearby food. Host-only."); HungerAuraEntry = config.Bind("Hunger", "GazeAura", true, "Hunger applies Hunger to anyone looking at it up close. Host-only."); PoisonExplosionEntry = config.Bind("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("Poison", "Drain", true, "Poison's battery ability (hold RMB+LMB on an ally to drain their Poison status). Host-only."); PoisonBuffEntry = config.Bind("Poison", "Buff", true, "Poison gets a refreshed Energy Drink buff when it receives poison. Host-only."); PoisonAuraEntry = config.Bind("Poison", "ProximityAura", true, "Poison applies Poison to anyone standing right next to it. Host-only."); SleepAuraEntry = config.Bind("Sleep", "GazeAura", true, "Sleep applies Drowsy to anyone looking at it up close. Host-only."); SleepFogDissolveEntry = config.Bind("Sleep", "FogDissolve", true, "Sleep carries a lit-candle-style zone that dissolves fog/Gloom nearby. Host-only."); SleepDozeEntry = config.Bind("Sleep", "Doze", true, "Sleep's random chance to doze off for a second. Host-only."); SporeHungerEatEntry = config.Bind("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("Spore", "Explosion", true, "Spore's random Spore Bomb cloud event. Host-only."); SporeShelfFungusEntry = config.Bind("Spore", "ShelfFungus", true, "Spore's random Shelf Fungus platform event. Host-only."); CactusHealingEntry = config.Bind("Cactus", "Healing", true, "Cactus gradually treats negative statuses on a scout it's stuck to. Host-only."); CactusStickEntry = config.Bind("Cactus", "StickAndThorns", true, "Cactus sticks to and adds a thorn on touching a scout. Host-only."); WebGrappleEntry = config.Bind("Web", "Grapple", true, "Web's hand-origin grapple shot. Host-only."); WebBiteEntry = config.Bind("Web", "Bite", true, "Web has a chance to inflict Web status on contact. Host-only."); StonePetrifyEntry = config.Bind("Stone", "Petrify", true, "Stone can hold RMB while climbing to freeze solid on the wall. Host-only."); StoneForceImmunityEntry = config.Bind("Stone", "ForceImmunity", true, "Stone cannot be knocked back, blown, or made to fall. Host-only."); FlyTrapExplosionEntry = config.Bind("FlyTrap", "Explosion", true, "Fly Trap's random Explosive Spore Bomb event. Host-only."); FlyTrapTrampolineEntry = config.Bind("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[] array = new ConfigEntry[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[] array = new ConfigEntry[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)((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)((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(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> EffectEntries = new Dictionary>(); private static ConfigEntry _settingsMenuEntry; private static ConfigEntry _selectionMenuEntry; private static ConfigEntry _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 Bind(ConfigFile config) { _settingsMenuEntry = config.Bind("Controls", "SettingsMenu", (KeyCode)288, "Local key used to open the Second Ascent settings window."); _selectionMenuEntry = config.Bind("Controls", "SkeletonSelectionMenu", (KeyCode)110, "Local key used to open the skeleton form-selection window."); _becomeGhostEntry = config.Bind("Controls", "BecomeGhost", (KeyCode)112, "Local key to hold for five seconds as a real Standard-mode skeleton to become a vanilla spectator ghost."); BindEffect(config, RandomEffectKind.TraumaBone, (KeyCode)122); BindEffect(config, RandomEffectKind.Dynamite, (KeyCode)122); BindEffect(config, RandomEffectKind.PoisonBomb, (KeyCode)122); BindEffect(config, RandomEffectKind.ColdFlash, (KeyCode)122); BindEffect(config, RandomEffectKind.ColdSlip, (KeyCode)120); BindEffect(config, RandomEffectKind.SleepDoze, (KeyCode)122); BindEffect(config, RandomEffectKind.SporeBomb, (KeyCode)122); BindEffect(config, RandomEffectKind.ShelfFungus, (KeyCode)120); BindEffect(config, RandomEffectKind.ExplosiveBomb, (KeyCode)122); } internal static KeyCode GetEffectKey(RandomEffectKind effect) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!EffectEntries.TryGetValue(effect, out var value)) { return (KeyCode)0; } return value.Value; } internal static bool TrySetMenuKey(MenuAction action, KeyCode key, out string error) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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) if (!IsAssignableKeyboardKey(key)) { error = Strings.Get("controls.invalid_key"); return false; } ConfigEntry val = EntryFor(action); if (val == null) { error = Strings.Get("controls.not_ready"); return false; } MenuAction[] array = new MenuAction[3] { MenuAction.Settings, MenuAction.Selection, MenuAction.BecomeGhost }; for (int i = 0; i < array.Length; i++) { if (array[i] != action && GetMenuKey(array[i]) == key) { error = Strings.Get("controls.conflict_menu"); return false; } } foreach (ConfigEntry value in EffectEntries.Values) { if (value.Value == key) { error = Strings.Get("controls.conflict_effect"); return false; } } val.Value = key; Revision++; error = null; return true; } internal static bool TrySetEffectKey(RandomEffectKind effect, KeyCode key, out string error) { //IL_001d: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_009a: Unknown result type (might be due to invalid IL or missing references) if (!EffectEntries.TryGetValue(effect, out var value)) { error = Strings.Get("controls.not_ready"); return false; } if (!IsAssignableKeyboardKey(key)) { error = Strings.Get("controls.invalid_key"); return false; } if (SettingsMenuKey == key || SelectionMenuKey == key || BecomeGhostKey == key) { error = Strings.Get("controls.conflict_menu"); return false; } SkeletonKind skeletonKind = FormForEffect(effect); foreach (KeyValuePair> effectEntry in EffectEntries) { if (effectEntry.Key != effect && FormForEffect(effectEntry.Key) == skeletonKind && effectEntry.Value.Value == key) { error = Strings.Get("controls.conflict_same_form"); return false; } } value.Value = key; Revision++; error = null; return true; } internal unsafe static string DisplayName(KeyCode key) { //IL_0000: 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_0065: Expected I4, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected I4, but got Unknown switch (key - 39) { default: switch (key - 91) { case 0: return "["; case 2: return "]"; case 1: return "\\"; } break; case 9: return "0"; case 10: return "1"; case 11: return "2"; case 12: return "3"; case 13: return "4"; case 14: return "5"; case 15: return "6"; case 16: return "7"; case 17: return "8"; case 18: return "9"; case 20: return ";"; case 0: return "'"; case 5: return ","; case 7: return "."; case 8: return "/"; case 6: return "-"; case 22: return "="; case 1: case 2: case 3: case 4: case 19: case 21: break; } return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString().ToUpperInvariant(); } internal static KeyCode GetMenuKey(MenuAction action) { //IL_0014: 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_0020: Unknown result type (might be due to invalid IL or missing references) return (KeyCode)(action switch { MenuAction.Settings => SettingsMenuKey, MenuAction.Selection => SelectionMenuKey, MenuAction.BecomeGhost => BecomeGhostKey, _ => 0, }); } internal static bool TryGetPressedKeyboardKey(out KeyCode key) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected I4, but got Unknown Array values = Enum.GetValues(typeof(KeyCode)); for (int i = 0; i < values.Length; i++) { KeyCode val = (KeyCode)values.GetValue(i); if (IsAssignableKeyboardKey(val) && Input.GetKeyDown(val)) { key = (KeyCode)(int)val; return true; } } key = (KeyCode)0; return false; } internal static SkeletonKind FormForEffect(RandomEffectKind effect) { switch (effect) { case RandomEffectKind.TraumaBone: return SkeletonKind.Trauma; case RandomEffectKind.Dynamite: return SkeletonKind.Heat; case RandomEffectKind.PoisonBomb: return SkeletonKind.Poison; case RandomEffectKind.ColdFlash: case RandomEffectKind.ColdSlip: return SkeletonKind.Cold; case RandomEffectKind.SleepDoze: return SkeletonKind.Sleep; case RandomEffectKind.SporeBomb: case RandomEffectKind.ShelfFungus: return SkeletonKind.Spore; case RandomEffectKind.ExplosiveBomb: return SkeletonKind.FlyTrap; default: return SkeletonKind.None; } } private static void BindEffect(ConfigFile config, RandomEffectKind effect, KeyCode defaultKey) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) EffectEntries[effect] = config.Bind("Controls.Effects", effect.ToString(), defaultKey, $"Local key used to trigger {effect} while its matching skeleton form is active."); } private static ConfigEntry EntryFor(MenuAction action) { return (ConfigEntry)(action switch { MenuAction.Settings => _settingsMenuEntry, MenuAction.Selection => _selectionMenuEntry, MenuAction.BecomeGhost => _becomeGhostEntry, _ => null, }); } private static bool IsAssignableKeyboardKey(KeyCode key) { //IL_0000: 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_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 if ((int)key != 0 && (int)key != 27) { return (int)key < 323; } return false; } } internal static class PreviewAnchorResolver { internal struct Anchor { internal Vector3 Position; internal Quaternion Rotation; internal string Source; internal float SlopeDegrees; } private const float CampfireRadius = 5.5f; private const float ProbeUp = 4f; private const float ProbeDown = 10f; private const float MaxSlopeDegrees = 25f; private const bool AlignToSlope = true; private const int CandidateDirections = 12; private static readonly bool AirportAnchorSet = true; private static readonly Vector3 AirportPosition = new Vector3(-18.101f, 2.568f, 74.475f); private static readonly float AirportYaw = 60.24f; internal static bool TryResolve(out Anchor anchor) { anchor = default(Anchor); if (!HostSettings.InAirport) { return TryResolveCampfire(out anchor); } return TryResolveAirport(out anchor); } private static bool TryResolveAirport(out Anchor anchor) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) anchor = default(Anchor); if (AirportAnchorSet) { Vector3 point; Vector3 normal; float slope; bool flag = TryGround(AirportPosition, out point, out normal, out slope); anchor.Position = (Vector3)(flag ? new Vector3(AirportPosition.x, point.y, AirportPosition.z) : AirportPosition); anchor.Rotation = Quaternion.Euler(0f, AirportYaw, 0f); anchor.Source = (flag ? "AirportAnchor" : "AirportAnchor (no floor under it)"); anchor.SlopeDegrees = slope; return true; } Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return false; } Vector3 val = Vector3.ProjectOnPlane(((Component)localCharacter).transform.forward, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = localCharacter.Center + val * 5.5f; if (!TryGround(val2, out var point2, out var normal2, out var slope2)) { point2 = val2; normal2 = Vector3.up; slope2 = 0f; } anchor.Position = point2; anchor.Rotation = BuildRotation(-val, normal2); anchor.Source = "AirportFallback(player-relative)"; anchor.SlopeDegrees = slope2; return true; } private static bool TryResolveCampfire(out Anchor anchor) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) anchor = default(Anchor); Campfire val = ResolveCampfire(); if ((Object)(object)val == (Object)null) { return false; } Vector3 position = ((Component)val).transform.position; Vector3 val2 = Vector3.ProjectOnPlane(((Component)val).transform.forward, Vector3.up); if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { val2 = Vector3.forward; } ((Vector3)(ref val2)).Normalize(); Anchor anchor2 = default(Anchor); bool flag = false; for (int i = 0; i < 12; i++) { float num = 360f * (float)i / 12f; Vector3 val3 = Quaternion.AngleAxis(num, Vector3.up) * val2; if (TryGround(position + val3 * 5.5f, out var point, out var normal, out var slope)) { Anchor anchor3 = new Anchor { Position = point, Rotation = BuildRotation(-val3, normal), Source = $"{CampfireLabel(val)} dir={num:0}°", SlopeDegrees = slope }; if (!flag) { anchor2 = anchor3; flag = true; } if (!(slope > 25f) && CameraHasRoom(anchor3)) { anchor = anchor3; return true; } } } if (flag) { anchor2.Source += " (no clear line)"; anchor = anchor2; return true; } anchor.Position = position; anchor.Rotation = BuildRotation(-val2, Vector3.up); anchor.Source = CampfireLabel(val) + " (no ground found)"; return true; } private static Campfire ResolveCampfire() { if (!MapHandler.ExistsAndInitialized) { return null; } try { return MapHandler.PreviousCampfire ?? MapHandler.CurrentCampfire; } catch (Exception arg) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)$"SSR preview: could not resolve a campfire anchor.\n{arg}"); } return null; } } private static string CampfireLabel(Campfire fire) { if (!string.IsNullOrEmpty(fire.nameOverride)) { return fire.nameOverride; } return ((Object)((Component)fire).gameObject).name; } private static bool TryGround(Vector3 wanted, out Vector3 point, out Vector3 normal, out float slope) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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) //IL_000d: 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) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) point = wanted; normal = Vector3.up; slope = 0f; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(wanted + Vector3.up * 4f, Vector3.down, ref val, 14f, -5, (QueryTriggerInteraction)1)) { return false; } point = ((RaycastHit)(ref val)).point; normal = ((RaycastHit)(ref val)).normal; slope = Vector3.Angle(((RaycastHit)(ref val)).normal, Vector3.up); return true; } private static bool CameraHasRoom(Anchor candidate) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Vector3 val = candidate.Rotation * Vector3.up; Vector3 val2 = candidate.Position + val * 1f; Vector3 val3 = candidate.Position + candidate.Rotation * PreviewWorldRig.CameraOffset; return !Physics.Linecast(val2, val3, -5, (QueryTriggerInteraction)1); } private static Quaternion BuildRotation(Vector3 forward, Vector3 normal) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) Vector3 val = normal; Vector3 val2 = Vector3.ProjectOnPlane(forward, val); if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { val2 = Vector3.ProjectOnPlane(forward, Vector3.up); val = Vector3.up; } return Quaternion.LookRotation(((Vector3)(ref val2)).normalized, val); } } internal static class PreviewDecorFactory { private sealed class PendingActivation { internal GameObject Root; internal int CreatedFrame; internal bool Activate; } private static readonly Type[] AllowedComponents = new Type[7] { typeof(Transform), typeof(MeshFilter), typeof(MeshRenderer), typeof(SkinnedMeshRenderer), typeof(ParticleSystem), typeof(ParticleSystemRenderer), typeof(Light) }; private static GameObject _stagingRoot; private static readonly List Pending = new List(); internal static GameObject Clone(GameObject source, Transform parent, bool worldPositionStays, bool activateWhenReady) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown if ((Object)(object)source == (Object)null) { return null; } EnsureStaging(); GameObject val = (GameObject)Object.Instantiate((Object)(object)source, _stagingRoot.transform, false); Sanitize(val); val.transform.SetParent(parent, worldPositionStays); if ((Object)(object)parent != (Object)null) { ApplyLayerRecursive(val.transform, ((Component)parent).gameObject.layer); } Pending.Add(new PendingActivation { Root = val, CreatedFrame = Time.frameCount, Activate = activateWhenReady }); return val; } internal static void SetLayerRecursive(GameObject root, int layer) { if ((Object)(object)root != (Object)null) { ApplyLayerRecursive(root.transform, layer); } } internal static void Tick() { for (int num = Pending.Count - 1; num >= 0; num--) { PendingActivation pendingActivation = Pending[num]; if ((Object)(object)pendingActivation.Root == (Object)null) { Pending.RemoveAt(num); } else if (Time.frameCount > pendingActivation.CreatedFrame) { Pending.RemoveAt(num); if (pendingActivation.Activate) { pendingActivation.Root.SetActive(true); } } } } internal static void Reset() { Pending.Clear(); if ((Object)(object)_stagingRoot != (Object)null) { Object.Destroy((Object)(object)_stagingRoot); _stagingRoot = null; } } private static void EnsureStaging() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown if (!((Object)(object)_stagingRoot != (Object)null)) { _stagingRoot = new GameObject("SSR_PreviewStaging"); Object.DontDestroyOnLoad((Object)(object)_stagingRoot); _stagingRoot.SetActive(false); } } private static void Sanitize(GameObject root) { Component[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !IsAllowed(((object)val).GetType())) { Object.Destroy((Object)(object)val); } } } private static bool IsAllowed(Type type) { for (int i = 0; i < AllowedComponents.Length; i++) { if (AllowedComponents[i].IsAssignableFrom(type)) { return true; } } return false; } private static void ApplyLayerRecursive(Transform root, int layer) { ((Component)root).gameObject.layer = layer; for (int i = 0; i < root.childCount; i++) { ApplyLayerRecursive(root.GetChild(i), layer); } } } internal static class PreviewFormVisuals { internal static void Apply(PreviewMannequin mannequin, SkeletonKind kind) { IVisualContext visualContext = mannequin.VisualContext; switch (kind) { case SkeletonKind.Heat: HeatSkeleton.Palette.ApplyTo(visualContext); AttachFire(visualContext); break; case SkeletonKind.Poison: PoisonSkeleton.Palette.ApplyTo(visualContext); break; case SkeletonKind.Cold: ColdSkeleton.Palette.ApplyTo(visualContext); break; case SkeletonKind.Sleep: SleepSkeleton.Palette.ApplyTo(visualContext); break; case SkeletonKind.Hunger: HungerSkeleton.Palette.ApplyTo(visualContext); break; case SkeletonKind.Cactus: CactusSkeleton.Palette.ApplyTo(visualContext); AttachThorns(mannequin, visualContext); break; case SkeletonKind.Stone: StoneSkeleton.Palette.ApplyTo(visualContext); StoneVisualService.Attach(visualContext, forceShow: true); break; case SkeletonKind.FlyTrap: FlyTrapSkeleton.Palette.ApplyTo(visualContext); break; case SkeletonKind.Web: WebSkeleton.Palette.ApplyTo(visualContext); WebVisualService.Attach(visualContext, forceShow: true); SpiderVisualService.Attach(visualContext, forceShow: true); break; case SkeletonKind.Spore: ZombieVisualService.Attach(visualContext, forceShow: true); break; case SkeletonKind.Trauma: break; } } private static void AttachFire(IVisualContext context) { //IL_0076: 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_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) Transform bodypart = context.GetBodypart((BodypartType)4); if ((Object)(object)bodypart == (Object)null) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)"Heat preview flame unavailable: the mannequin has no head transform."); } return; } GameObject fire = GameAssetCatalog.CreatePortableStoveFireVfx(bodypart); if ((Object)(object)fire == (Object)null) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogWarning((object)"Heat preview flame clone could not be created."); } return; } ((Object)fire).name = "SSR_PreviewHeatFire"; fire.transform.localPosition = PortableStoveService.FireLocalOffset; fire.transform.localRotation = Quaternion.identity; PortableStoveService.ApplyFireScale(fire.transform); Renderer[] componentsInChildren = fire.GetComponentsInChildren(true); ParticleSystem[] componentsInChildren2 = fire.GetComponentsInChildren(true); ManualLogSource logSource3 = Plugin.LogSource; if (logSource3 != null) { logSource3.LogInfo((object)($"Heat preview flame attached through original direct path: particles={componentsInChildren2.Length}, " + $"renderers={componentsInChildren.Length}, enabledRenderers={CountEnabled(componentsInChildren)}, " + $"playing={CountPlaying(componentsInChildren2)}, active={fire.activeInHierarchy}, " + $"localPosition={fire.transform.localPosition:0.###}, fittedScale={fire.transform.localScale:0.###}.")); } context.Effects.Add(delegate { if ((Object)(object)fire != (Object)null) { Object.Destroy((Object)(object)fire); } }); } private static int CountEnabled(Renderer[] renderers) { int num = 0; for (int i = 0; i < renderers.Length; i++) { if ((Object)(object)renderers[i] != (Object)null && renderers[i].enabled) { num++; } } return num; } private static int CountPlaying(ParticleSystem[] particles) { int num = 0; for (int i = 0; i < particles.Length; i++) { if ((Object)(object)particles[i] != (Object)null && particles[i].isPlaying) { num++; } } return num; } private static void AttachThorns(PreviewMannequin mannequin, IVisualContext context) { List list = Character.localCharacter?.refs?.afflictions?.physicalThorns; if (list == null) { return; } int num = 0; for (int i = 0; i < list.Count; i++) { if (num >= 12) { break; } ThornOnMe val = list[i]; if ((Object)(object)val == (Object)null || val.type != 0 || (Object)(object)((Component)val).gameObject == (Object)null) { continue; } Transform val2 = ResolveThornParent(((Component)val).transform.parent, mannequin, context); if ((Object)(object)val2 == (Object)null) { continue; } GameObject visual = context.InstantiateDecor(((Component)val).gameObject, val2, worldPositionStays: false); if ((Object)(object)visual == (Object)null) { continue; } ((Object)visual).name = "SSR_PreviewCactusThorn"; context.ActivateDecor(visual); num++; context.Effects.Add(delegate { if ((Object)(object)visual != (Object)null) { Object.Destroy((Object)(object)visual); } }); } } private static Transform ResolveThornParent(Transform liveParent, PreviewMannequin mannequin, IVisualContext context) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) Transform val = liveParent; while ((Object)(object)val != (Object)null) { if (Enum.TryParse(((Object)val).name, out BodypartType result)) { Transform bone = mannequin.GetBone(result); if ((Object)(object)bone != (Object)null) { return bone; } } val = val.parent; } return context.AttachRoot; } } internal sealed class PreviewMannequin : IDisposable { private enum RendererKind { Human, Sash, Medal, Accessory, ThirdEye, Eye, Skeleton } private sealed class RendererRecord { internal Renderer Renderer; internal RendererKind Kind; internal bool InAllRenderers; internal Material[] BaseMaterials; internal MaterialPropertyBlock BaseBlock; internal MaterialPropertyBlock[] BaseIndexBlocks; internal bool BaseEnabled; internal bool BaseGoActive; internal Mesh BaseMesh; internal Mesh BaseFilterMesh; internal float[] BaseBlendWeights; } private const string PoseState = "A_Scout_Emote_CrossedArms"; private static readonly int EmoteHash = Animator.StringToHash("Emote"); private static readonly int IsGroundedHash = Animator.StringToHash("Is Grounded"); private readonly GameObject _root; private readonly Animator _animator; private readonly Dictionary _paths; private readonly Dictionary _bones; private readonly List _records; private readonly Renderer _skeletonRenderer; private readonly List _wrapSources; private readonly List _ownedMaterials; private readonly Renderer[] _stoneRenderers; private readonly PreviewVisualContext _context; private readonly EffectHandle _baseEffects = new EffectHandle(); private EffectHandle _formEffects; private FormVisualHandle _currentForm; private bool _disposed; private static bool _buildLogged; internal GameObject Root => _root; internal IVisualContext VisualContext => _context; internal Animator Animator => _animator; internal Renderer SkeletonRenderer => _skeletonRenderer; internal Renderer[] StoneRenderers => _stoneRenderers; internal IReadOnlyList WebWraps => _wrapSources; internal EffectHandle CurrentEffects => _formEffects ?? _baseEffects; internal Renderer[] EyeRenderers { get { int num = 0; for (int i = 0; i < _records.Count; i++) { if (_records[i].Kind == RendererKind.Eye && (Object)(object)_records[i].Renderer != (Object)null) { num++; } } Renderer[] array = (Renderer[])(object)new Renderer[num]; int num2 = 0; for (int j = 0; j < _records.Count; j++) { if (_records[j].Kind == RendererKind.Eye && (Object)(object)_records[j].Renderer != (Object)null) { array[num2++] = _records[j].Renderer; } } return array; } } internal IReadOnlyList FrameRenderers { get { List list = new List(_records.Count); for (int i = 0; i < _records.Count; i++) { if ((Object)(object)_records[i].Renderer != (Object)null) { list.Add(_records[i].Renderer); } } return list; } } private PreviewMannequin(GameObject root, Animator animator, Dictionary paths, Dictionary bones, List records, Renderer skeletonRenderer, List wrapSources, List ownedMaterials, Renderer[] stoneRenderers) { _root = root; _animator = animator; _paths = paths; _bones = bones; _records = records; _skeletonRenderer = skeletonRenderer; _wrapSources = wrapSources; _ownedMaterials = ownedMaterials; _stoneRenderers = stoneRenderers; _context = new PreviewVisualContext(this); } internal Transform GetBone(BodypartType type) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!_bones.TryGetValue(type, out var value)) { return null; } return value; } internal void HideWrapSources() { for (int i = 0; i < _wrapSources.Count; i++) { if ((Object)(object)_wrapSources[i] != (Object)null) { ((Component)_wrapSources[i]).gameObject.SetActive(false); } } } internal void SetLayer(int layer) { if ((Object)(object)_root != (Object)null) { ApplyLayer(_root.transform, layer); } } internal void Pose() { if (!((Object)(object)_animator == (Object)null)) { _animator.applyRootMotion = false; _animator.SetBool(IsGroundedHash, true); _animator.SetBool(EmoteHash, true); _animator.Play("A_Scout_Emote_CrossedArms", 0, 0f); _animator.Update(0.1f); _animator.Update(0.1f); _animator.Update(0.1f); _animator.Update(0.3f); _animator.Update(0.3f); _animator.speed = 0f; } } internal void ApplyForm(SkeletonKind kind) { if (_disposed) { return; } if (_currentForm != null) { _currentForm.Dispose(); _currentForm = null; } ResetToCosmeticBaseline(); SetBaseMode(kind != SkeletonKind.Spore); FormVisualHandle formVisualHandle = new FormVisualHandle(_formEffects = new EffectHandle()); try { PreviewFormVisuals.Apply(this, kind); } catch (Exception arg) { formVisualHandle.Dispose(); _formEffects = null; ResetToCosmeticBaseline(); SetBaseMode(kind != SkeletonKind.Spore); ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)$"SSR preview form application failed for {kind}.\n{arg}"); } return; } _currentForm = formVisualHandle; } internal void ResetToCosmeticBaseline() { for (int i = 0; i < _records.Count; i++) { ResetRecord(_records[i]); } if ((Object)(object)_skeletonRenderer != (Object)null) { ((Component)_skeletonRenderer).gameObject.SetActive(false); } } internal void SetBaseMode(bool skeleton) { for (int i = 0; i < _records.Count; i++) { RendererRecord rendererRecord = _records[i]; Renderer renderer = rendererRecord.Renderer; if ((Object)(object)renderer == (Object)null || rendererRecord.Kind == RendererKind.Skeleton) { continue; } if (skeleton) { if (rendererRecord.Kind == RendererKind.Sash || rendererRecord.Kind == RendererKind.Medal) { renderer.enabled = true; continue; } if (rendererRecord.Kind == RendererKind.Accessory || rendererRecord.Kind == RendererKind.ThirdEye) { renderer.enabled = false; continue; } if (rendererRecord.InAllRenderers) { renderer.enabled = false; continue; } renderer.enabled = rendererRecord.BaseEnabled; ((Component)renderer).gameObject.SetActive(rendererRecord.BaseGoActive); } else { renderer.enabled = rendererRecord.BaseEnabled; ((Component)renderer).gameObject.SetActive(rendererRecord.BaseGoActive); } } if ((Object)(object)_skeletonRenderer != (Object)null) { ((Component)_skeletonRenderer).gameObject.SetActive(skeleton); _skeletonRenderer.enabled = skeleton; } } public void Dispose() { if (_disposed) { return; } _disposed = true; _currentForm?.Dispose(); _currentForm = null; _baseEffects.Dispose(); for (int i = 0; i < _ownedMaterials.Count; i++) { if ((Object)(object)_ownedMaterials[i] != (Object)null) { Object.Destroy((Object)(object)_ownedMaterials[i]); } } _ownedMaterials.Clear(); if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } } internal static PreviewMannequin Build(Character local) { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)local == (Object)null) { return null; } GameObject val = Resources.Load("Character"); Transform val2 = (((Object)(object)val != (Object)null) ? val.transform : ((Component)local).transform); RigCreator val3 = (((Object)(object)val != (Object)null) ? val.GetComponentInChildren(true) : local.refs?.rigCreator); Animator val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent() : null); CustomizationRefs val5 = ((!((Object)(object)val != (Object)null)) ? local.refs?.customization?.refs : val.GetComponentInChildren(true)?.refs); CustomizationRefs val6 = local.refs?.customization?.refs; if ((Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)"SSR preview: the Character rig source could not be resolved; preview unavailable."); } return null; } GameObject val7 = new GameObject("SSR_PreviewMannequin"); val7.SetActive(false); Dictionary dictionary = new Dictionary(); for (int i = 0; i < val2.childCount; i++) { Transform child = val2.GetChild(i); CopyTransforms(child, val7.transform, dictionary, ((Object)child).name); } string text = PathOf(((Component)val3).transform, val2); if (string.IsNullOrEmpty(text) || !dictionary.TryGetValue(text, out var value)) { Object.Destroy((Object)(object)val7); ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogWarning((object)"SSR preview: the rig hierarchy could not be located in the character source."); } return null; } Animator val8 = ((Component)value).gameObject.AddComponent(); val8.runtimeAnimatorController = val4.runtimeAnimatorController; val8.avatar = val4.avatar; val8.applyRootMotion = val4.applyRootMotion; val8.updateMode = val4.updateMode; val8.cullingMode = (AnimatorCullingMode)0; List ownedMaterials = new List(); List list = new List(); Renderer val9 = CreateSkeletonRenderer(val5, val6, val2, ((Component)local).transform, dictionary, ownedMaterials); if ((Object)(object)val9 != (Object)null) { list.Add(new RendererRecord { Renderer = val9, Kind = RendererKind.Skeleton }); } BuildHumanRenderers(val6, ((Component)local).transform, dictionary, ownedMaterials, list); Dictionary dictionary2 = new Dictionary(); foreach (KeyValuePair item in dictionary) { if ((Object)(object)item.Value != (Object)null && Enum.TryParse(((Object)item.Value).name, out BodypartType result) && !dictionary2.ContainsKey(result)) { dictionary2[result] = item.Value; } } List list2 = BuildWrapSources(local, ((Component)local).transform, dictionary, val7.transform); for (int j = 0; j < list.Count; j++) { CaptureBaseline(list[j]); } LogBuildDiagnostics(val, val8, dictionary2.Count, list, val9, list2.Count); PreviewMannequin previewMannequin = new PreviewMannequin(val7, val8, dictionary, dictionary2, list, val9, list2, ownedMaterials, val6?.stoneRenderers); val7.SetActive(true); previewMannequin.Pose(); return previewMannequin; } private static void BuildHumanRenderers(CustomizationRefs liveRefs, Transform liveRoot, Dictionary paths, List ownedMaterials, List records) { //IL_0254: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)liveRefs == (Object)null || (Object)(object)liveRoot == (Object)null) { return; } HashSet hashSet = new HashSet(); if (liveRefs.AllRenderers != null) { for (int i = 0; i < liveRefs.AllRenderers.Length; i++) { if ((Object)(object)liveRefs.AllRenderers[i] != (Object)null) { hashSet.Add(liveRefs.AllRenderers[i]); } } } List> candidates = new List>(); Add((Renderer)(object)liveRefs.mainRenderer, RendererKind.Human); Add(liveRefs.shorts, RendererKind.Human); Add(liveRefs.skirt, RendererKind.Human); Add(liveRefs.sashRenderer, RendererKind.Sash); Add(liveRefs.medalRenderer, RendererKind.Medal); Add(liveRefs.accessoryRenderer, RendererKind.Accessory); Add(liveRefs.mouthRenderer, RendererKind.Human); if ((Object)(object)liveRefs.thirdEye != (Object)null) { Add(liveRefs.thirdEye.GetComponent(), RendererKind.ThirdEye); } if (liveRefs.playerHats != null) { for (int j = 0; j < liveRefs.playerHats.Length; j++) { Add(liveRefs.playerHats[j], RendererKind.Human); } } if (liveRefs.EyeRenderers != null) { for (int k = 0; k < liveRefs.EyeRenderers.Length; k++) { Add(liveRefs.EyeRenderers[k], RendererKind.Eye); } } if (liveRefs.PlayerRenderers != null) { for (int l = 0; l < liveRefs.PlayerRenderers.Length; l++) { Add(liveRefs.PlayerRenderers[l], RendererKind.Human); } } for (int m = 0; m < candidates.Count; m++) { Renderer value = candidates[m].Value; RendererKind key = candidates[m].Key; if ((Object)(object)value == (Object)null || (Object)(object)((Component)value).transform == (Object)null) { continue; } string text = PathOf(((Component)value).transform, liveRoot); Transform val = EnsureLivePath(text, liveRoot, paths); if ((Object)(object)val == (Object)null) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("SSR preview: could not map cosmetic renderer path '" + text + "'.")); } continue; } SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((value is SkinnedMeshRenderer) ? value : null); Renderer val4; if (val2 != null) { SkinnedMeshRenderer val3 = ((Component)val).GetComponent(); if ((Object)(object)val3 == (Object)null) { val3 = ((Component)val).gameObject.AddComponent(); } val3.sharedMesh = val2.sharedMesh; ((Renderer)val3).localBounds = ((Renderer)val2).localBounds; val3.updateWhenOffscreen = true; if (!BindBones(val3, val2.bones, val2.rootBone, liveRoot, paths)) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogWarning((object)("SSR preview: bones could not be mapped for '" + text + "'.")); } continue; } val4 = (Renderer)(object)val3; } else { if (!(value is MeshRenderer)) { continue; } MeshFilter val5 = ((Component)val).GetComponent(); if ((Object)(object)val5 == (Object)null) { val5 = ((Component)val).gameObject.AddComponent(); } MeshFilter component = ((Component)value).GetComponent(); val5.sharedMesh = (((Object)(object)component != (Object)null) ? component.sharedMesh : null); MeshRenderer val6 = ((Component)val).GetComponent(); if ((Object)(object)val6 == (Object)null) { val6 = ((Component)val).gameObject.AddComponent(); } val4 = (Renderer)(object)val6; } bool flag = hashSet.Contains(value); CopyMaterialsAndState(value, val4, ownedMaterials, flag); records.Add(new RendererRecord { Renderer = val4, Kind = key, InAllRenderers = flag }); } void Add(Renderer renderer, RendererKind kind) { if (!((Object)(object)renderer == (Object)null)) { for (int n = 0; n < candidates.Count; n++) { if ((Object)(object)candidates[n].Value == (Object)(object)renderer) { return; } } candidates.Add(new KeyValuePair(kind, renderer)); } } } private static Renderer CreateSkeletonRenderer(CustomizationRefs prefabRefs, CustomizationRefs liveRefs, Transform prefabRoot, Transform liveRoot, Dictionary paths, List ownedMaterials) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) Renderer obj = prefabRefs?.skeletonRenderer; SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((obj is SkinnedMeshRenderer) ? obj : null); Renderer obj2 = liveRefs?.skeletonRenderer; SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((obj2 is SkinnedMeshRenderer) ? obj2 : null); SkinnedMeshRenderer val3 = (((Object)(object)val2 != (Object)null && val2.bones != null && val2.bones.Length != 0) ? val2 : val); Transform val4 = (((Object)(object)val3 == (Object)(object)val2) ? liveRoot : prefabRoot); Mesh val5 = prefabRefs?.skeletonThirdPerson ?? liveRefs?.skeletonThirdPerson; Material val6 = prefabRefs?.skeletonThirdPersonMat ?? liveRefs?.skeletonThirdPersonMat; if ((Object)(object)val6 == (Object)null && (Object)(object)val3 != (Object)null) { val6 = ((Renderer)val3).sharedMaterial; } if ((Object)(object)val5 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val6 == (Object)null) { return null; } string text = PathOf(((Component)val3).transform, val4); if (string.IsNullOrEmpty(text) || !paths.TryGetValue(text, out var value)) { return null; } SkinnedMeshRenderer val7 = ((Component)value).gameObject.AddComponent(); val7.sharedMesh = val5; Material val8 = new Material(val6) { name = "SSR_PreviewSkeletonMaterial" }; ownedMaterials.Add(val8); ((Renderer)val7).sharedMaterials = (Material[])(object)new Material[1] { val8 }; if (!BindBones(val7, val3.bones, val3.rootBone, val4, paths)) { Object.Destroy((Object)(object)val7); return null; } ((Renderer)val7).localBounds = ((Renderer)val3).localBounds; val7.updateWhenOffscreen = true; ((Component)val7).gameObject.SetActive(false); return (Renderer)(object)val7; } private static void LogBuildDiagnostics(GameObject prefab, Animator animator, int boneCount, List records, Renderer skeletonRenderer, int wrapSourceCount) { if (_buildLogged) { return; } _buildLogged = true; int num = 0; int num2 = 0; for (int i = 0; i < records.Count; i++) { if (records[i].Kind == RendererKind.Eye) { num2++; } else if (records[i].Kind != RendererKind.Skeleton) { num++; } } SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((skeletonRenderer is SkinnedMeshRenderer) ? skeletonRenderer : null); string text = "none"; if ((Object)(object)val != (Object)null && (Object)(object)val.sharedMesh != (Object)null) { text = $"mesh={((Object)val.sharedMesh).name} subMeshes={val.sharedMesh.subMeshCount} " + "material=" + (((Object)(object)((Renderer)val).sharedMaterial != (Object)null) ? ((Object)((Renderer)val).sharedMaterial).name : "null") + " " + $"bones={((val.bones != null) ? val.bones.Length : 0)} " + "rootBone=" + (((Object)(object)val.rootBone != (Object)null) ? ((Object)val.rootBone).name : "null") + " " + $"active={((Component)val).gameObject.activeSelf} enabled={((Renderer)val).enabled}"; } ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)("SSR preview mannequin built: prefab=" + (((Object)(object)prefab != (Object)null) ? "Character" : "live rig fallback") + " controller=" + (((Object)(object)animator != (Object)null && (Object)(object)animator.runtimeAnimatorController != (Object)null) ? ((Object)animator.runtimeAnimatorController).name : "null") + " " + $"bones={boneCount} human={num} eyes={num2} wraps={wrapSourceCount} " + "skeleton[" + text + "]")); } } private static List BuildWrapSources(Character local, Transform liveRoot, Dictionary paths, Transform mannequinRoot) { List list = new List(); List list2 = local?.refs?.afflictions?.webWraps; if (list2 == null) { return list; } HashSet hashSet = new HashSet(); for (int i = 0; i < list2.Count; i++) { if ((Object)(object)list2[i] != (Object)null && (Object)(object)((Component)list2[i]).transform.parent != (Object)null) { hashSet.Add(((Component)list2[i]).transform.parent); } } foreach (Transform item in hashSet) { Transform parent = mannequinRoot; if ((Object)(object)item.parent != (Object)null) { string text = PathOf(item.parent, liveRoot); if (!string.IsNullOrEmpty(text) && paths.TryGetValue(text, out var value)) { parent = value; } } GameObject val = PreviewDecorFactory.Clone(((Component)item).gameObject, parent, worldPositionStays: false, activateWhenReady: false); if (!((Object)(object)val == (Object)null)) { MeshRenderer[] componentsInChildren = val.GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren.Length; j++) { list.Add(componentsInChildren[j]); } } } return list; } private static void CopyMaterialsAndState(Renderer source, Renderer target, List ownedMaterials, bool forceEnabled = false) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0028: 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_0047: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) Material[] sharedMaterials = source.sharedMaterials; Material[] array = (Material[])(object)new Material[sharedMaterials.Length]; for (int i = 0; i < sharedMaterials.Length; i++) { if ((Object)(object)sharedMaterials[i] != (Object)null) { array[i] = new Material(sharedMaterials[i]) { name = ((Object)sharedMaterials[i]).name + " (SSR Preview)" }; ownedMaterials.Add(array[i]); } } target.sharedMaterials = array; MaterialPropertyBlock val = new MaterialPropertyBlock(); source.GetPropertyBlock(val); target.SetPropertyBlock(val); for (int j = 0; j < array.Length; j++) { MaterialPropertyBlock val2 = new MaterialPropertyBlock(); source.GetPropertyBlock(val2, j); target.SetPropertyBlock(val2, j); } target.enabled = forceEnabled || source.enabled; ((Component)target).gameObject.SetActive(((Component)source).gameObject.activeSelf); target.shadowCastingMode = source.shadowCastingMode; target.receiveShadows = source.receiveShadows; target.lightProbeUsage = source.lightProbeUsage; SkinnedMeshRenderer val3 = (SkinnedMeshRenderer)(object)((target is SkinnedMeshRenderer) ? target : null); if (val3 == null) { return; } SkinnedMeshRenderer val4 = (SkinnedMeshRenderer)(object)((source is SkinnedMeshRenderer) ? source : null); if (val4 != null && (Object)(object)val3.sharedMesh != (Object)null) { int blendShapeCount = val3.sharedMesh.blendShapeCount; for (int k = 0; k < blendShapeCount; k++) { val3.SetBlendShapeWeight(k, val4.GetBlendShapeWeight(k)); } } } private static void CaptureBaseline(RendererRecord record) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown Renderer renderer = record.Renderer; if ((Object)(object)renderer == (Object)null) { return; } record.BaseMaterials = renderer.sharedMaterials; record.BaseBlock = new MaterialPropertyBlock(); renderer.GetPropertyBlock(record.BaseBlock); record.BaseIndexBlocks = (MaterialPropertyBlock[])(object)new MaterialPropertyBlock[record.BaseMaterials.Length]; for (int i = 0; i < record.BaseMaterials.Length; i++) { MaterialPropertyBlock val = new MaterialPropertyBlock(); renderer.GetPropertyBlock(val, i); record.BaseIndexBlocks[i] = (val.isEmpty ? null : val); } record.BaseEnabled = renderer.enabled; record.BaseGoActive = ((Component)renderer).gameObject.activeSelf; SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null); if (val2 != null) { record.BaseMesh = val2.sharedMesh; if ((Object)(object)val2.sharedMesh != (Object)null) { int blendShapeCount = val2.sharedMesh.blendShapeCount; record.BaseBlendWeights = new float[blendShapeCount]; for (int j = 0; j < blendShapeCount; j++) { record.BaseBlendWeights[j] = val2.GetBlendShapeWeight(j); } } } MeshFilter component = ((Component)renderer).GetComponent(); record.BaseFilterMesh = (((Object)(object)component != (Object)null) ? component.sharedMesh : null); } private static void ResetRecord(RendererRecord record) { Renderer renderer = record.Renderer; if ((Object)(object)renderer == (Object)null) { return; } Material[] sharedMaterials = renderer.sharedMaterials; renderer.sharedMaterials = record.BaseMaterials; if (record.BaseBlock != null) { renderer.SetPropertyBlock(record.BaseBlock); } if (record.BaseIndexBlocks != null) { for (int i = 0; i < record.BaseIndexBlocks.Length; i++) { renderer.SetPropertyBlock(record.BaseIndexBlocks[i], i); } } if (sharedMaterials != null) { for (int j = 0; j < sharedMaterials.Length; j++) { if ((Object)(object)sharedMaterials[j] == (Object)null) { continue; } bool flag = false; for (int k = 0; k < record.BaseMaterials.Length; k++) { if ((Object)(object)record.BaseMaterials[k] == (Object)(object)sharedMaterials[j]) { flag = true; break; } } if (!flag) { Object.Destroy((Object)(object)sharedMaterials[j]); } } } renderer.enabled = record.BaseEnabled; ((Component)renderer).gameObject.SetActive(record.BaseGoActive); SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null); if (val != null) { if ((Object)(object)record.BaseMesh != (Object)null) { val.sharedMesh = record.BaseMesh; } if (record.BaseBlendWeights != null && (Object)(object)val.sharedMesh != (Object)null) { int num = Mathf.Min(record.BaseBlendWeights.Length, val.sharedMesh.blendShapeCount); for (int l = 0; l < num; l++) { val.SetBlendShapeWeight(l, record.BaseBlendWeights[l]); } } } if ((Object)(object)record.BaseFilterMesh != (Object)null) { MeshFilter component = ((Component)renderer).GetComponent(); if ((Object)(object)component != (Object)null) { component.sharedMesh = record.BaseFilterMesh; } } } private static bool BindBones(SkinnedMeshRenderer target, Transform[] sourceBones, Transform sourceRootBone, Transform sourceRoot, Dictionary paths) { if (sourceBones == null || sourceBones.Length == 0) { return false; } Transform[] array = (Transform[])(object)new Transform[sourceBones.Length]; for (int i = 0; i < sourceBones.Length; i++) { Transform val = sourceBones[i]; if ((Object)(object)val == (Object)null || !TryMap(val, sourceRoot, paths, out var copy)) { return false; } array[i] = copy; } target.bones = array; if ((Object)(object)sourceRootBone != (Object)null && TryMap(sourceRootBone, sourceRoot, paths, out var copy2)) { target.rootBone = copy2; } return true; } private static bool TryMap(Transform source, Transform sourceRoot, Dictionary paths, out Transform copy) { copy = null; string text = PathOf(source, sourceRoot); if (!string.IsNullOrEmpty(text) && paths.TryGetValue(text, out copy)) { return true; } foreach (KeyValuePair path in paths) { if ((Object)(object)path.Value != (Object)null && ((Object)path.Value).name == ((Object)source).name) { copy = path.Value; return true; } } return false; } private static Transform EnsureLivePath(string path, Transform liveRoot, Dictionary paths) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(path)) { return null; } string[] array = path.Split('/'); Transform val = null; Transform val2 = liveRoot; for (int i = 0; i < array.Length; i++) { string text = string.Empty; for (int j = 0; j <= i; j++) { text = ((j == 0) ? array[j] : (text + "/" + array[j])); } Transform value; Transform val3 = (paths.TryGetValue(text, out value) ? value : null); if ((Object)(object)val3 == (Object)null) { GameObject val4 = new GameObject(array[i]); Transform val5 = (((Object)(object)val2 != (Object)null) ? val2.Find(array[i]) : null); if ((Object)(object)val5 != (Object)null) { val4.transform.localPosition = val5.localPosition; val4.transform.localRotation = val5.localRotation; val4.transform.localScale = val5.localScale; } val4.transform.SetParent(val, false); paths[text] = val4.transform; val3 = val4.transform; } val = val3; val2 = (((Object)(object)val2 != (Object)null) ? val2.Find(array[i]) : null); } return val; } private static string PathOf(Transform transform, Transform root) { if ((Object)(object)transform == (Object)null || (Object)(object)transform == (Object)(object)root) { return string.Empty; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null && (Object)(object)parent != (Object)(object)root) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } private static void CopyTransforms(Transform source, Transform parent, Dictionary paths, string path) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(((Object)source).name); val.transform.SetParent(parent, false); val.transform.localPosition = source.localPosition; val.transform.localRotation = source.localRotation; val.transform.localScale = source.localScale; paths[path] = val.transform; for (int i = 0; i < source.childCount; i++) { Transform child = source.GetChild(i); CopyTransforms(child, val.transform, paths, path + "/" + ((Object)child).name); } } private static void ApplyLayer(Transform transform, int layer) { ((Component)transform).gameObject.layer = layer; for (int i = 0; i < transform.childCount; i++) { ApplyLayer(transform.GetChild(i), layer); } } } internal sealed class PreviewVisualContext : IVisualContext { private readonly PreviewMannequin _mannequin; private bool _forcePresetEyes; public VisualInstanceKey Key => VisualInstanceKey.Preview; public bool IsOwnerView => false; public bool IsLocal => false; public EffectHandle Effects => _mannequin.CurrentEffects; public Renderer SkeletonRenderer => _mannequin.SkeletonRenderer; public Renderer[] StoneRenderers => _mannequin.StoneRenderers; public Renderer[] EyeRenderers => _mannequin.EyeRenderers; public IReadOnlyList WebWraps => _mannequin.WebWraps; public Transform AttachRoot { get { if (!((Object)(object)_mannequin.Root != (Object)null)) { return null; } return _mannequin.Root.transform; } } public bool ForcePresetEyes { get { return _forcePresetEyes; } set { _forcePresetEyes = value; } } internal PreviewVisualContext(PreviewMannequin mannequin) { _mannequin = mannequin; } public Transform GetBodypart(BodypartType type) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _mannequin.GetBone(type); } public void SetSkeletonActive(bool active, bool isLocal) { } public void ResetWebWraps() { _mannequin.HideWrapSources(); } public void OnZombieVisualCleanup() { } public GameObject InstantiateDecor(GameObject source, Transform parent, bool worldPositionStays) { return PreviewDecorFactory.Clone(source, parent, worldPositionStays, activateWhenReady: true); } public void ActivateDecor(GameObject clone) { } } internal sealed class PreviewWorldRig { private const int TextureWidth = 384; private const int TextureHeight = 768; private const float SelectDebounceSeconds = 0.2f; private const float CameraDistance = 3f; private const float CameraHeight = 1.2f; internal const float LookAtHeight = 1f; private const float CameraFov = 58f; internal static readonly Vector3 CameraOffset = new Vector3(0f, 1.2f, 3f); private GameObject _rigRoot; private GameObject _characterAnchor; private int _rigScene = -1; private Camera _camera; private RenderTexture _renderTexture; private PreviewMannequin _mannequin; private GameObject _cameraGo; private int _layer = -1; private bool _framed; private Renderer[] _mannequinRenderers = Array.Empty(); private bool _hooked; private bool _loggedHook; private SkeletonKind _builtKind; private SkeletonKind _pendingKind; private float _pendingSince; private bool _hasPending; internal Texture PreviewTexture => (Texture)(object)_renderTexture; internal void SetKind(SkeletonKind kind) { if (kind != SkeletonKind.None && kind != _pendingKind) { _pendingKind = kind; _pendingSince = Time.unscaledTime; _hasPending = true; } } internal void Tick() { PreviewDecorFactory.Tick(); if (_hasPending && _pendingKind != _builtKind && Time.unscaledTime - _pendingSince >= 0.2f) { _hasPending = false; BuildKind(_pendingKind); } } internal void Teardown() { _hasPending = false; _pendingKind = SkeletonKind.None; _builtKind = SkeletonKind.None; _framed = false; if (_hooked) { RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering; _hooked = false; } for (int i = 0; i < _mannequinRenderers.Length; i++) { if ((Object)(object)_mannequinRenderers[i] != (Object)null) { _mannequinRenderers[i].forceRenderingOff = false; } } _mannequinRenderers = Array.Empty(); _loggedHook = false; _mannequin?.Dispose(); _mannequin = null; if ((Object)(object)_camera != (Object)null) { _camera.targetTexture = null; Object.Destroy((Object)(object)((Component)_camera).gameObject); _camera = null; _cameraGo = null; } if ((Object)(object)_rigRoot != (Object)null) { Object.Destroy((Object)(object)_rigRoot); _rigRoot = null; _characterAnchor = null; } if ((Object)(object)_renderTexture != (Object)null) { _renderTexture.Release(); Object.Destroy((Object)(object)_renderTexture); _renderTexture = null; } PreviewDecorFactory.Reset(); _layer = -1; } private void BuildKind(SkeletonKind kind) { //IL_0014: 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) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_rigRoot != (Object)null) { SceneHandle val = SceneHandle.op_Implicit(_rigScene); Scene activeScene = SceneManager.GetActiveScene(); if (val != ((Scene)(ref activeScene)).handle) { Teardown(); } } if (!EnsureRig()) { return; } if (_mannequin == null) { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return; } _mannequin = PreviewMannequin.Build(localCharacter); if (_mannequin == null) { return; } _mannequin.Root.transform.SetParent(_characterAnchor.transform, false); } _mannequin.ApplyForm(kind); _mannequin.SetLayer(_layer); IsolateFromWorld(); _builtKind = kind; if (!_framed) { _framed = FreezeFraming(); } } catch (Exception arg) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)$"SSR preview render failed for {kind}.\n{arg}"); } _builtKind = SkeletonKind.None; } } private bool EnsureRig() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_rigRoot != (Object)null) { return true; } if (!PreviewAnchorResolver.TryResolve(out var anchor)) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)"SSR preview: no anchor available yet (no campfire and no local character); the render stays blank."); } return false; } _layer = ResolveMannequinLayer(); Scene activeScene = SceneManager.GetActiveScene(); _rigScene = SceneHandle.op_Implicit(((Scene)(ref activeScene)).handle); _rigRoot = new GameObject("SSR_PreviewRig"); Object.DontDestroyOnLoad((Object)(object)_rigRoot); _rigRoot.transform.SetPositionAndRotation(anchor.Position, anchor.Rotation); _characterAnchor = new GameObject("SSR_CharacterAnchor"); _characterAnchor.transform.SetParent(_rigRoot.transform, false); _renderTexture = new RenderTexture(384, 768, 16, (RenderTextureFormat)0); _renderTexture.Create(); GameObject val = (_cameraGo = new GameObject("SSR_PreviewCamera")); _camera = val.AddComponent(); Camera val2 = ResolveMainCamera(); int num = (((Object)(object)val2 != (Object)null) ? val2.cullingMask : (-1)); if ((Object)(object)val2 != (Object)null) { _camera.CopyFrom(val2); } val.transform.SetParent(_rigRoot.transform, false); val.transform.localPosition = CameraOffset; _camera.fieldOfView = 58f; _camera.nearClipPlane = 0.05f; _camera.targetTexture = _renderTexture; _camera.aspect = 0.5f; ((Behaviour)_camera).enabled = true; _camera.cullingMask = num; if (!_hooked) { RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering; _hooked = true; } _characterAnchor.transform.localRotation = Quaternion.identity; AimCamera(_characterAnchor.transform.position + _rigRoot.transform.up * 1f); ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogInfo((object)($"SSR preview rig: source='{anchor.Source}' anchor={anchor.Position:0.##} slope={anchor.SlopeDegrees:0.#}° " + $"rigFwd={_rigRoot.transform.forward:0.##} rigUp={_rigRoot.transform.up:0.##} " + $"camera={val.transform.position:0.##} distance={Vector3.Distance(val.transform.position, _characterAnchor.transform.position):0.##}m " + $"fov={58f:0.#} layer={_layer} ('{LayerMask.LayerToName(_layer)}') " + $"worldMask=0x{num:X8} previewMask=0x{_camera.cullingMask:X8} " + "mainCamera=" + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "none"))); } return true; } private void AimCamera(Vector3 worldTarget) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_cameraGo == (Object)null) && !((Object)(object)_rigRoot == (Object)null)) { _cameraGo.transform.localPosition = new Vector3(0f, 1.2f, 3f); Vector3 val = worldTarget - _cameraGo.transform.position; if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { _cameraGo.transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized, _rigRoot.transform.up); } } } private bool FreezeFraming() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) if (_mannequin == null || (Object)(object)_rigRoot == (Object)null || (Object)(object)_characterAnchor == (Object)null) { return false; } if (!TryBodyBounds(out var bounds)) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)"SSR preview: the mannequin reported no visible renderers; the camera keeps its default aim."); } return false; } Vector3 val = _rigRoot.transform.InverseTransformPoint(((Bounds)(ref bounds)).center); _characterAnchor.transform.localPosition = new Vector3(0f - val.x, 0f, 0f - val.z); TryBodyBounds(out bounds); AimCamera(((Bounds)(ref bounds)).center); ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogInfo((object)($"SSR preview framing: body={((Bounds)(ref bounds)).center:0.##} size={((Bounds)(ref bounds)).size:0.##} " + $"bodyInRig={_rigRoot.transform.InverseTransformPoint(((Bounds)(ref bounds)).center):0.##} " + $"mannequinRoot={_mannequin.Root.transform.position:0.##} " + $"anchor={_characterAnchor.transform.position:0.##} " + $"camera={_cameraGo.transform.position:0.##} camFwd={_cameraGo.transform.forward:0.##} " + $"distance={Vector3.Distance(_cameraGo.transform.position, ((Bounds)(ref bounds)).center):0.##}m " + $"aimError={Vector3.Angle(_cameraGo.transform.forward, ((Bounds)(ref bounds)).center - _cameraGo.transform.position):0.#}° " + $"layer={_layer} ('{LayerMask.LayerToName(_layer)}') renderers={_mannequinRenderers.Length} " + $"inFrustum={GeometryUtility.TestPlanesAABB(GeometryUtility.CalculateFrustumPlanes(_camera), bounds)}")); } return true; } private bool TryBodyBounds(out Bounds bounds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) bounds = default(Bounds); IReadOnlyList frameRenderers = _mannequin.FrameRenderers; bool flag = false; for (int i = 0; i < frameRenderers.Count; i++) { Renderer val = frameRenderers[i]; if (!((Object)(object)val == (Object)null) && !(val is ParticleSystemRenderer) && val.enabled && ((Component)val).gameObject.activeInHierarchy) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } return flag; } private void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera) { bool flag = (Object)(object)camera == (Object)(object)_camera; for (int i = 0; i < _mannequinRenderers.Length; i++) { Renderer val = _mannequinRenderers[i]; if ((Object)(object)val != (Object)null) { val.forceRenderingOff = !flag; } } if (!_loggedHook) { _loggedHook = true; ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)($"SSR preview: per-camera visibility hook is live ({_mannequinRenderers.Length} renderer(s), " + "first camera '" + ((Object)camera).name + "').")); } } } private static int ResolveMannequinLayer() { Character localCharacter = Character.localCharacter; if (!((Object)(object)localCharacter != (Object)null)) { return 0; } return ((Component)localCharacter).gameObject.layer; } private void IsolateFromWorld() { if ((Object)(object)_mannequin?.Root == (Object)null) { return; } _mannequinRenderers = _mannequin.Root.GetComponentsInChildren(true); for (int i = 0; i < _mannequinRenderers.Length; i++) { if ((Object)(object)_mannequinRenderers[i] != (Object)null) { _mannequinRenderers[i].shadowCastingMode = (ShadowCastingMode)0; _mannequinRenderers[i].forceRenderingOff = true; } } Light[] componentsInChildren = _mannequin.Root.GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren.Length; j++) { if ((Object)(object)componentsInChildren[j] != (Object)null) { ((Behaviour)componentsInChildren[j]).enabled = false; } } } private static Camera ResolveMainCamera() { Camera main = Camera.main; if ((Object)(object)main != (Object)null) { return main; } Camera[] allCameras = Camera.allCameras; for (int i = 0; i < allCameras.Length; i++) { if ((Object)(object)allCameras[i] != (Object)null && (Object)(object)allCameras[i].targetTexture == (Object)null && ((Behaviour)allCameras[i]).enabled) { return allCameras[i]; } } return null; } private static int FindPreviewLayer() { for (int num = 31; num >= 8; num--) { if (string.IsNullOrEmpty(LayerMask.LayerToName(num))) { return num; } } return 31; } } internal static class ReviveTransactionManager { private sealed class ReviveTarget { internal Character Character; internal Vector3 Position; internal bool ApplyStatus; internal int StatueSegment; } private sealed class Transaction { internal Object Source; internal Item SourceItem; internal RespawnChest Chest; internal Character Interactor; internal Character Requester; internal readonly List Targets = new List(); internal readonly HashSet Awaiting = new HashSet(); internal float Deadline; internal int Attempts; internal bool EmergencySent; internal int Sequence; } private static Transaction _active; private static readonly HashSet ReduceBypass = new HashSet(); private static readonly Dictionary LastEffigyCommitSequence = new Dictionary(); private static int _nextTransactionSequence; internal static bool StartChest(RespawnChest chest, Character interactor) { if (SkeletonRegistry.IsRunEnding || (Object)(object)chest == (Object)null || (Object)(object)interactor == (Object)null || !interactor.IsLocal || (Object)(object)((Component)chest).GetComponent() == (Object)null) { return false; } if (!PhotonNetwork.IsMasterClient) { SkeletonNetworkBridge.Attach(interactor); ((MonoBehaviourPun)interactor).photonView.RPC("SSR_RequestChestRevive", (RpcTarget)2, new object[1] { ((Component)chest).GetComponent().ViewID }); return true; } return StartChestAuthoritative(chest, interactor); } internal static void ReceiveChestRequest(Character requester, int chestViewId, PhotonMessageInfo info) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) PhotonView val = PhotonView.Find(chestViewId); RespawnChest val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent()); if (PhotonNetwork.IsMasterClient && !((Object)(object)((requester != null) ? ((MonoBehaviourPun)requester).photonView : null) == (Object)null) && info.Sender == ((MonoBehaviourPun)requester).photonView.Owner && !((Object)(object)val2 == (Object)null) && !val2.IsSpent && !(Vector3.Distance(requester.Center, ((Component)val2).transform.position) > 6f)) { StartChestAuthoritative(val2, requester); } } private static bool StartChestAuthoritative(RespawnChest chest, Character interactor) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected I4, but got Unknown if (!PhotonNetwork.IsMasterClient || SkeletonRegistry.IsRunEnding || _active != null || (Object)(object)chest == (Object)null || chest.IsSpent || (Object)(object)interactor == (Object)null) { return false; } Transaction transaction = new Transaction { Source = (Object)(object)chest, Chest = chest, Interactor = interactor, Requester = interactor, Sequence = ++_nextTransactionSequence }; int statueSegment = (int)chest.SegmentNumber; AddAllEligible(transaction, () => chest.RandomRevivePoint, applyStatus: true, statueSegment); if (transaction.Targets.Count == 0) { return false; } Begin(transaction); return true; } internal static bool StartEffigy(ScoutEffigy effigy) { //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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) Character localCharacter = Character.localCharacter; if (SkeletonRegistry.IsRunEnding || (Object)(object)effigy == (Object)null || (Object)(object)localCharacter == (Object)null || !localCharacter.IsLocal || !GameBindings.IsConstructing(effigy) || (Object)(object)((Constructable)effigy).currentPreview == (Object)null || (Object)(object)((Component)effigy).GetComponent() == (Object)null) { return false; } RaycastHit constructHit = GameBindings.GetConstructHit(effigy); Vector3 point = ((RaycastHit)(ref constructHit)).point; if (!PhotonNetwork.IsMasterClient) { SkeletonNetworkBridge.Attach(localCharacter); ((MonoBehaviourPun)localCharacter).photonView.RPC("SSR_RequestEffigyRevive", (RpcTarget)2, new object[2] { ((Component)effigy).GetComponent().ViewID, point }); return true; } return StartEffigyAuthoritative(effigy, localCharacter, point); } internal static void ReceiveEffigyRequest(Character requester, int effigyViewId, Vector3 constructionPoint, PhotonMessageInfo info) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) PhotonView val = PhotonView.Find(effigyViewId); ScoutEffigy val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent()); if (PhotonNetwork.IsMasterClient && !((Object)(object)((requester != null) ? ((MonoBehaviourPun)requester).photonView : null) == (Object)null) && info.Sender == ((MonoBehaviourPun)requester).photonView.Owner && !((Object)(object)val2 == (Object)null) && val.Owner == info.Sender && !(Vector3.Distance(requester.Center, constructionPoint) > 8f)) { StartEffigyAuthoritative(val2, requester, constructionPoint); } } private static bool StartEffigyAuthoritative(ScoutEffigy effigy, Character requester, Vector3 constructionPoint) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsMasterClient || SkeletonRegistry.IsRunEnding || _active != null || (Object)(object)effigy == (Object)null || (Object)(object)requester == (Object)null) { return false; } List list = new List(); List allCharacters = Character.AllCharacters; for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if (IsEligible(val)) { list.Add(val); } } if (list.Count == 0) { return false; } Character val2 = list[Random.Range(0, list.Count)]; Transaction transaction = new Transaction { Source = (Object)(object)effigy, SourceItem = ((Component)effigy).GetComponent(), Requester = requester, Sequence = ++_nextTransactionSequence }; if ((Object)(object)transaction.SourceItem == (Object)null) { return false; } transaction.Targets.Add(new ReviveTarget { Character = val2, Position = constructionPoint + Vector3.up, ApplyStatus = false, StatueSegment = -1 }); transaction.Awaiting.Add(((MonoBehaviourPun)val2).photonView.ViewID); Begin(transaction); return true; } internal static bool ShouldDeferReduceUses(Action_ReduceUses action) { if ((Object)(object)action == (Object)null || (Object)(object)((Component)action).GetComponent() == (Object)null) { return false; } if (ReduceBypass.Contains(((Object)action).GetInstanceID())) { return false; } if (!HasEligibleTarget()) { if ((Object)(object)_active?.SourceItem != (Object)null) { return (Object)(object)((Component)action).GetComponent() == (Object)(object)_active.SourceItem; } return false; } return true; } internal static bool HasEligibleTarget() { List allCharacters = Character.AllCharacters; for (int i = 0; i < allCharacters.Count; i++) { if (IsEligible(allCharacters[i])) { return true; } } return false; } internal static bool HasArcadeCostumeTarget() { List allCharacters = Character.AllCharacters; for (int i = 0; i < allCharacters.Count; i++) { if (IsArcadeCostumeTarget(allCharacters[i])) { return true; } } return false; } internal static bool IsArcadeCostumeTarget(Character character) { if (HostSettings.Mode == GameMode.Arcade && (Object)(object)character != (Object)null && !character.isBot && (Object)(object)((MonoBehaviourPun)character).photonView != (Object)null && (Object)(object)character.data != (Object)null && !character.data.dead) { if (!SkeletonRegistry.IsCostume(character)) { return NetworkSession.PropertyIsTrue(((MonoBehaviourPun)character).photonView.Owner, "SSR.Costume"); } return true; } return false; } internal static void ReceiveAck(int targetViewId) { if (_active == null || !_active.Awaiting.Contains(targetViewId)) { return; } for (int i = 0; i < _active.Targets.Count; i++) { Character character = _active.Targets[i].Character; if (!((Object)(object)((character != null) ? ((MonoBehaviourPun)character).photonView : null) == (Object)null) && ((MonoBehaviourPun)character).photonView.ViewID == targetViewId) { if (!character.data.dead && !character.data.fullyPassedOut && !SkeletonRegistry.IsUndead(character)) { _active.Awaiting.Remove(targetViewId); } break; } } } internal static void Tick(float now) { if (SkeletonRegistry.IsRunEnding) { _active = null; } else { if (_active == null) { return; } if (_active.Awaiting.Count == 0) { Transaction active = _active; _active = null; Commit(active); } else { if (now < _active.Deadline) { return; } if (_active.Attempts < 2) { _active.Attempts++; SendPending(_active, emergency: false); _active.Deadline = now + 1.5f; } else if (!_active.EmergencySent) { _active.EmergencySent = true; SendPending(_active, emergency: true); _active.Deadline = now + 2f; ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)"Vanilla revival acknowledgement timed out. Running the inventory-safe emergency revival path before spending the resource."); } } else { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)"Revival could not be confirmed. The statue/effigy was deliberately left unspent so players can retry."); } _active = null; } } } } internal static void ApplyEmergencyRevive(Character character, Vector3 position, bool applyStatus, int statueSegment) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)character != (Object)null && SkeletonRegistry.IsUndead(character) && !SkeletonRegistry.IsCostume(character); bool flag2 = IsArcadeCostumeTarget(character); if ((Object)(object)character == (Object)null || (!character.data.dead && !character.data.fullyPassedOut && !flag && !flag2)) { if ((Object)(object)character != (Object)null && ((MonoBehaviourPun)character).photonView.IsMine) { SendAck(character); } return; } bool flag3 = flag; bool flag4 = flag2; try { if (flag3 || flag4) { SkeletonRegistry.BeginRevive(character); if (flag4 && character.IsLocal) { NetworkSession.SetLocalCostumeState(costumed: false, SkeletonKind.None); } character.data.RPC_SyncSkeleton(false); } GameBindings.Revive(character, applyStatus); GameBindings.Warp(character, position, poof: true); if (statueSegment > -1) { character.data.lastRevivedSegment = (Segment)(byte)statueSegment; } if (flag3 || flag4) { SkeletonRegistry.FinishRevive(character); } if (flag4) { CharacterRefs refs = character.refs; if (refs != null) { CharacterCustomization customization = refs.customization; if (customization != null) { CustomizationRefs refs2 = customization.refs; if (refs2 != null) { refs2.SetSkeleton(false, character.IsLocal); } } } } character.data.RecalculateInvincibility(); if (((MonoBehaviourPun)character).photonView.IsMine) { SendAck(character); } } catch (Exception arg) { if (flag3 || flag4) { if (flag4) { SkeletonRegistry.RestoreCostumeAfterFailedRevive(character); } else { SkeletonRegistry.RestoreAfterFailedRevive(character); } if (flag4) { CharacterRefs refs3 = character.refs; if (refs3 != null) { CharacterCustomization customization2 = refs3.customization; if (customization2 != null) { CustomizationRefs refs4 = customization2.refs; if (refs4 != null) { refs4.SetSkeleton(true, character.IsLocal); } } } } else { character.data.RPC_SyncSkeleton(true); } SkeletonEffectLifecycle.Activate(character); } ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)$"Emergency revival failed safely for {character.characterName}.\n{arg}"); } } } internal static void SendAck(Character character) { if ((Object)(object)character == (Object)null || (Object)(object)((MonoBehaviourPun)character).photonView == (Object)null || !((MonoBehaviourPun)character).photonView.IsMine) { return; } SkeletonNetworkBridge.Attach(character); try { ((MonoBehaviourPun)character).photonView.RPC("SSR_ReviveAck", (RpcTarget)0, new object[1] { ((MonoBehaviourPun)character).photonView.ViewID }); } catch (Exception arg) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)$"Could not send revival acknowledgement for {character.characterName}; the safe retry path remains available.\n{arg}"); } } } internal static void Reset() { _active = null; ReduceBypass.Clear(); LastEffigyCommitSequence.Clear(); _nextTransactionSequence = 0; } internal static void ReceiveEffigyCommit(Character requester, int itemViewId, int sequence, PhotonMessageInfo info) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((requester != null) ? ((MonoBehaviourPun)requester).photonView : null) == (Object)null || !((MonoBehaviourPun)requester).photonView.IsMine || info.Sender == null || !info.Sender.IsMasterClient || sequence <= 0) { return; } PhotonView val = PhotonView.Find(itemViewId); Item val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent()); if ((Object)(object)val2 == (Object)null || val.Owner != PhotonNetwork.LocalPlayer || (LastEffigyCommitSequence.TryGetValue(itemViewId, out var value) && sequence <= value)) { return; } LastEffigyCommitSequence[itemViewId] = sequence; Action_ReduceUses[] components = ((Component)val2).GetComponents(); foreach (Action_ReduceUses val3 in components) { if (((ItemAction)val3).OnCastFinished) { int instanceID = ((Object)val3).GetInstanceID(); ReduceBypass.Add(instanceID); try { ((ItemActionBase)val3).RunAction(); } finally { ReduceBypass.Remove(instanceID); } } } if ((Object)(object)Singleton.Instance != (Object)null) { Singleton.Instance.AddToRunBasedInt((RUNBASEDVALUETYPE)2, 1); } } private static void Begin(Transaction transaction) { _active = transaction; transaction.Attempts = 1; transaction.Deadline = Time.unscaledTime + 1.5f; try { SendPending(transaction, emergency: false); } catch (Exception arg) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)$"Initial revival send failed; the transaction will retry without spending the resource.\n{arg}"); } } } private static void SendPending(Transaction transaction, bool emergency) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < transaction.Targets.Count; i++) { ReviveTarget reviveTarget = transaction.Targets[i]; if ((Object)(object)reviveTarget.Character == (Object)null || !transaction.Awaiting.Contains(((MonoBehaviourPun)reviveTarget.Character).photonView.ViewID)) { continue; } try { SkeletonNetworkBridge.Attach(reviveTarget.Character); ((MonoBehaviourPun)reviveTarget.Character).photonView.RPC(emergency ? "SSR_EmergencyRevive" : "RPCA_ReviveAtPosition", (RpcTarget)0, new object[3] { reviveTarget.Position, reviveTarget.ApplyStatus, reviveTarget.StatueSegment }); } catch (Exception arg) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)$"Revival send failed for {reviveTarget.Character.characterName}; it remains pending.\n{arg}"); } } } } private static void Commit(Transaction transaction) { if ((Object)(object)transaction.Chest != (Object)null) { PhotonView component = ((Component)transaction.Chest).GetComponent(); component.RPC("RemoveSkeletonRPC", (RpcTarget)3, Array.Empty()); component.RPC("OpenLuggageRPC", (RpcTarget)3, new object[1] { false }); GameBindings.NotifyReviveChestUsed(transaction.Chest); GlobalEvents.TriggerRespawnChestOpened(transaction.Chest, transaction.Interactor); ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)"Revival statue committed after all revival acknowledgements were received."); } } else { if ((Object)(object)transaction.SourceItem == (Object)null) { return; } Character requester = transaction.Requester; if ((Object)(object)((requester != null) ? ((MonoBehaviourPun)requester).photonView : null) == (Object)null || (Object)(object)((MonoBehaviourPun)transaction.SourceItem).photonView == (Object)null) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)"Scout Effigy revival succeeded, but its owner/item vanished before the confirmed charge could be spent."); } return; } ((MonoBehaviourPun)transaction.Requester).photonView.RPC("SSR_CommitEffigyUse", ((MonoBehaviourPun)transaction.Requester).photonView.Owner, new object[2] { ((MonoBehaviourPun)transaction.SourceItem).photonView.ViewID, transaction.Sequence }); ManualLogSource logSource3 = Plugin.LogSource; if (logSource3 != null) { logSource3.LogInfo((object)"Scout Effigy charge committed after revival acknowledgement."); } } } private static void AddAllEligible(Transaction transaction, Func position, bool applyStatus, int statueSegment) { //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) List allCharacters = Character.AllCharacters; for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if (IsEligible(val)) { transaction.Targets.Add(new ReviveTarget { Character = val, Position = position(), ApplyStatus = applyStatus, StatueSegment = statueSegment }); transaction.Awaiting.Add(((MonoBehaviourPun)val).photonView.ViewID); } } } private static bool IsEligible(Character character) { if ((Object)(object)character != (Object)null && !character.isBot && (Object)(object)((MonoBehaviourPun)character).photonView != (Object)null) { if (!character.data.dead && !character.data.fullyPassedOut && (!SkeletonRegistry.IsUndead(character) || SkeletonRegistry.IsCostume(character))) { return IsArcadeCostumeTarget(character); } return true; } return false; } } internal static class RuntimeScheduler { private static float _nextSlowTick; private static float _nextEffectTick; private static float _nextFastErrorLog; private static float _nextEffectErrorLog; private static readonly Dictionary SlowErrorGates = new Dictionary(); internal static void Tick() { float unscaledTime = Time.unscaledTime; try { HostSettings.GuardGuestAuthority(); } catch (Exception exception) { LogRateLimited(ref _nextFastErrorLog, unscaledTime, "host authority guard", exception); } VanillaGhostService.TickInput(unscaledTime); WebGrappleService.Tick(unscaledTime); PoisonDrainService.TickInput(); PoisonDrainService.Tick(); StonePetrifyService.TickInput(); SleepGloomVisionService.TickDiagnostic(); try { DeathCoordinator.Tick(unscaledTime); ReviveTransactionManager.Tick(unscaledTime); } catch (Exception exception2) { LogRateLimited(ref _nextFastErrorLog, unscaledTime, "death/revival scheduler", exception2); } if (unscaledTime >= _nextEffectTick) { _nextEffectTick = unscaledTime + 0.25f; try { RandomEffectRuntime.Tick(PhotonNetwork.Time); } catch (Exception exception3) { LogRateLimited(ref _nextEffectErrorLog, unscaledTime, "burst scheduler", exception3); } } if (!(unscaledTime < _nextSlowTick)) { _nextSlowTick = unscaledTime + 0.5f; RunSlowStep(unscaledTime, "scene refresh", SkeletonRegistry.RefreshSceneState); RunSlowStep(unscaledTime, "destroyed-session cleanup", SkeletonRegistry.PruneDestroyedSessions); RunSlowStep(unscaledTime, "compatibility publish", NetworkSession.PublishCompatibility); RunSlowStep(unscaledTime, "host settings sync", HostSettings.Sync); RunSlowStep(unscaledTime, "legacy invincibility cleanup", LegacyInvincibilityCleanup.Tick); RunSlowStep(unscaledTime, "costume session sync", SkeletonRegistry.RefreshCostumeSessions); RunSlowStep(unscaledTime, "effect restore", SkeletonEffectLifecycle.RestoreMissingEffects); RunSlowStep(unscaledTime, "aura scheduler", AuraRuntime.Tick); RunSlowStep(unscaledTime, "cactus healing", CactusEffectService.TickHealing); RunSlowStep(unscaledTime, "cold climbing hooks", ColdClimbingHookService.Tick); RunSlowStep(unscaledTime, "hook visuals", HookVisualService.Tick); RunSlowStep(unscaledTime, "campfire statues", CampfireStatueService.Tick); RunSlowStep(unscaledTime, "hunger prey sniffer", HungerPreySnifferService.Tick); RunSlowStep(unscaledTime, "fly trap fog speed", FlyTrapFogSpeedService.Tick); } } internal static void FixedTick() { WebGrappleService.TickPhysics(Time.unscaledTime); } internal static void LateTick() { StonePetrifyService.TickFreeze(); } internal static void ResetSceneState() { _nextSlowTick = 0f; _nextEffectTick = 0f; _nextFastErrorLog = 0f; _nextEffectErrorLog = 0f; SlowErrorGates.Clear(); AuraRuntime.Reset(); RandomEffectRuntime.Reset(); CactusEffectService.Reset(); WebContactService.Reset(); WebGrappleService.Reset(); SporeHungerService.Reset(); VanillaGhostService.Reset(); EnergyBoostService.Reset(); SpiderVisualService.Reset(); SpiderSenseSoundService.Reset(); FlyTrapTrampolineService.Reset(); ColdClimbingHookService.Reset(); HookVisualService.Reset(); CampfireStatueService.Reset(); PoisonDrainService.Reset(); HungerPreySnifferService.Reset(); FlyTrapFogSpeedService.Reset(); SleepGloomVisionService.Reset(); StonePetrifyService.Reset(); ZombieVisualService.Reset(); StoneVisualService.Reset(); GameAssetCatalog.ResetSceneCache(); GoldenIdolSelectionService.Reset(); DeathCoordinator.Reset(); ReviveTransactionManager.Reset(); } private static void RunSlowStep(float now, string subsystem, Action action) { try { action(); } catch (Exception exception) { if (!SlowErrorGates.TryGetValue(subsystem, out var value)) { value = 0f; } LogRateLimited(ref value, now, subsystem, exception); SlowErrorGates[subsystem] = value; } } internal static void Reset() { ResetSceneState(); } private static void LogRateLimited(ref float nextAllowed, float now, string subsystem, Exception exception) { if (!(now < nextAllowed)) { nextAllowed = now + 10f; ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogError((object)$"Second Ascent {subsystem} failed; retrying safely.\n{exception}"); } } } } internal static class SettingsWindowModel { internal sealed class Setting { internal string Id; internal SkeletonKind Form; internal string LabelKey; internal string DescriptionKey; internal Func Getter; internal Action Setter; internal bool IsRule; internal bool ArcadeOnly; internal RandomEffectKind ManualEffect; internal bool LockDuringRun; internal string Label => Strings.Get(LabelKey); internal string Description => Strings.Get(DescriptionKey); } internal static readonly SkeletonKind[] Forms; internal static readonly Setting[] Rules; internal static readonly Setting[] Abilities; internal static bool CanEditRules { get { if (HostSettings.CanEdit) { return !HostSettings.RulesLocked; } return false; } } internal static GameMode Mode => HostSettings.Mode; internal static bool CanEdit(Setting setting) { if (setting != null && HostSettings.CanEdit && (!setting.ArcadeOnly || HostSettings.Mode == GameMode.Arcade)) { if (setting.LockDuringRun) { return !HostSettings.RulesLocked; } return true; } return false; } internal static void SetMode(GameMode mode) { if (HostSettings.ModeEntry != null) { HostSettings.ModeEntry.Value = mode; } HostSettings.ApplyModeDefaults(mode); } internal static void RestoreDefaults() { HostSettings.RestoreDefaults(); } internal static void RestoreRuntimeDefaults() { HostSettings.RestoreRuntimeDefaults(); } internal static IEnumerable ForForm(SkeletonKind form) { for (int i = 0; i < Abilities.Length; i++) { if (Abilities[i].Form == form) { yield return Abilities[i]; } } } static SettingsWindowModel() { Forms = new SkeletonKind[11] { SkeletonKind.Trauma, SkeletonKind.Heat, SkeletonKind.Cold, SkeletonKind.Hunger, SkeletonKind.Poison, SkeletonKind.Sleep, SkeletonKind.Spore, SkeletonKind.Cactus, SkeletonKind.Web, SkeletonKind.Stone, SkeletonKind.FlyTrap }; Rules = new Setting[4] { new Setting { Id = "SkeletonsCanDie", Form = SkeletonKind.None, LabelKey = "setting.skeletons_can_die.name", DescriptionKey = "setting.skeletons_can_die.description", Getter = () => HostSettings.SkeletonsCanDie, Setter = delegate(bool value) { HostSettings.SkeletonsCanDieEntry.Value = value; }, IsRule = true, ArcadeOnly = true, LockDuringRun = true }, new Setting { Id = "SkeletonsCanStarve", Form = SkeletonKind.None, LabelKey = "setting.skeletons_can_starve.name", DescriptionKey = "setting.skeletons_can_starve.description", Getter = () => HostSettings.SkeletonsCanStarve, Setter = delegate(bool value) { HostSettings.SkeletonsCanStarveEntry.Value = value; }, IsRule = true, ArcadeOnly = true, LockDuringRun = true }, new Setting { Id = "PlayersCanSelect", Form = SkeletonKind.None, LabelKey = "setting.players_can_select.name", DescriptionKey = "setting.players_can_select.description", Getter = () => HostSettings.PlayersCanSelect, Setter = delegate(bool value) { HostSettings.PlayersCanSelectEntry.Value = value; }, IsRule = true, ArcadeOnly = true, LockDuringRun = false }, new Setting { Id = "ManualEffectHotkeys", Form = SkeletonKind.None, LabelKey = "setting.manual_effect_hotkeys.name", DescriptionKey = "setting.manual_effect_hotkeys.description", Getter = () => HostSettings.ManualEffectHotkeysEnabled, Setter = delegate(bool value) { HostSettings.ManualEffectHotkeysEntry.Value = value; }, IsRule = true, ArcadeOnly = true, LockDuringRun = false } }; Abilities = new Setting[27] { new Setting { Id = "TraumaBones", Form = SkeletonKind.Trauma, LabelKey = "setting.trauma_bones.name", DescriptionKey = "setting.trauma_bones.description", Getter = () => HostSettings.TraumaBonesEnabled, Setter = delegate(bool value) { HostSettings.TraumaBonesEntry.Value = value; }, ManualEffect = RandomEffectKind.TraumaBone }, new Setting { Id = "HeatCampfire", Form = SkeletonKind.Heat, LabelKey = "setting.heat_campfire.name", DescriptionKey = "setting.heat_campfire.description", Getter = () => HostSettings.HeatCampfireEnabled, Setter = delegate(bool value) { HostSettings.HeatCampfireEntry.Value = value; } }, new Setting { Id = "HeatBurnAura", Form = SkeletonKind.Heat, LabelKey = "setting.heat_burn.name", DescriptionKey = "setting.heat_burn.description", Getter = () => HostSettings.HeatBurnAuraEnabled, Setter = delegate(bool value) { HostSettings.HeatBurnAuraEntry.Value = value; } }, new Setting { Id = "HeatExplosion", Form = SkeletonKind.Heat, LabelKey = "setting.heat_explosion.name", DescriptionKey = "setting.heat_explosion.description", Getter = () => HostSettings.HeatExplosionEnabled, Setter = delegate(bool value) { HostSettings.HeatExplosionEntry.Value = value; }, ManualEffect = RandomEffectKind.Dynamite }, new Setting { Id = "ColdExplosion", Form = SkeletonKind.Cold, LabelKey = "setting.cold_explosion.name", DescriptionKey = "setting.cold_explosion.description", Getter = () => HostSettings.ColdExplosionEnabled, Setter = delegate(bool value) { HostSettings.ColdExplosionEntry.Value = value; }, ManualEffect = RandomEffectKind.ColdFlash }, new Setting { Id = "ColdSlip", Form = SkeletonKind.Cold, LabelKey = "setting.cold_slip.name", DescriptionKey = "setting.cold_slip.description", Getter = () => HostSettings.ColdSlipEnabled, Setter = delegate(bool value) { HostSettings.ColdSlipEntry.Value = value; }, ManualEffect = RandomEffectKind.ColdSlip }, new Setting { Id = "ColdHooks", Form = SkeletonKind.Cold, LabelKey = "setting.cold_hooks.name", DescriptionKey = "setting.cold_hooks.description", Getter = () => HostSettings.ColdHooksEnabled, Setter = delegate(bool value) { HostSettings.ColdHooksEntry.Value = value; } }, new Setting { Id = "HungerPreySniffer", Form = SkeletonKind.Hunger, LabelKey = "setting.hunger_sniffer.name", DescriptionKey = "setting.hunger_sniffer.description", Getter = () => HostSettings.HungerPreySnifferEnabled, Setter = delegate(bool value) { HostSettings.HungerPreySnifferEntry.Value = value; } }, new Setting { Id = "HungerAura", Form = SkeletonKind.Hunger, LabelKey = "setting.hunger_aura.name", DescriptionKey = "setting.hunger_aura.description", Getter = () => HostSettings.HungerAuraEnabled, Setter = delegate(bool value) { HostSettings.HungerAuraEntry.Value = value; } }, new Setting { Id = "PoisonExplosion", Form = SkeletonKind.Poison, LabelKey = "setting.poison_explosion.name", DescriptionKey = "setting.poison_explosion.description", Getter = () => HostSettings.PoisonExplosionEnabled, Setter = delegate(bool value) { HostSettings.PoisonExplosionEntry.Value = value; }, ManualEffect = RandomEffectKind.PoisonBomb }, new Setting { Id = "PoisonDrain", Form = SkeletonKind.Poison, LabelKey = "setting.poison_drain.name", DescriptionKey = "setting.poison_drain.description", Getter = () => HostSettings.PoisonDrainEnabled, Setter = delegate(bool value) { HostSettings.PoisonDrainEntry.Value = value; } }, new Setting { Id = "PoisonBuff", Form = SkeletonKind.Poison, LabelKey = "setting.poison_buff.name", DescriptionKey = "setting.poison_buff.description", Getter = () => HostSettings.PoisonBuffEnabled, Setter = delegate(bool value) { HostSettings.PoisonBuffEntry.Value = value; } }, new Setting { Id = "PoisonAura", Form = SkeletonKind.Poison, LabelKey = "setting.poison_aura.name", DescriptionKey = "setting.poison_aura.description", Getter = () => HostSettings.PoisonAuraEnabled, Setter = delegate(bool value) { HostSettings.PoisonAuraEntry.Value = value; } }, new Setting { Id = "SleepAura", Form = SkeletonKind.Sleep, LabelKey = "setting.sleep_aura.name", DescriptionKey = "setting.sleep_aura.description", Getter = () => HostSettings.SleepAuraEnabled, Setter = delegate(bool value) { HostSettings.SleepAuraEntry.Value = value; } }, new Setting { Id = "SleepFogDissolve", Form = SkeletonKind.Sleep, LabelKey = "setting.sleep_fog.name", DescriptionKey = "setting.sleep_fog.description", Getter = () => HostSettings.SleepFogDissolveEnabled, Setter = delegate(bool value) { HostSettings.SleepFogDissolveEntry.Value = value; } }, new Setting { Id = "SleepDoze", Form = SkeletonKind.Sleep, LabelKey = "setting.sleep_doze.name", DescriptionKey = "setting.sleep_doze.description", Getter = () => HostSettings.SleepDozeEnabled, Setter = delegate(bool value) { HostSettings.SleepDozeEntry.Value = value; }, ManualEffect = RandomEffectKind.SleepDoze }, new Setting { Id = "SporeHungerEat", Form = SkeletonKind.Spore, LabelKey = "setting.spore_eat.name", DescriptionKey = "setting.spore_eat.description", Getter = () => HostSettings.SporeHungerEatEnabled, Setter = delegate(bool value) { HostSettings.SporeHungerEatEntry.Value = value; } }, new Setting { Id = "SporeExplosion", Form = SkeletonKind.Spore, LabelKey = "setting.spore_explosion.name", DescriptionKey = "setting.spore_explosion.description", Getter = () => HostSettings.SporeExplosionEnabled, Setter = delegate(bool value) { HostSettings.SporeExplosionEntry.Value = value; }, ManualEffect = RandomEffectKind.SporeBomb }, new Setting { Id = "SporeShelfFungus", Form = SkeletonKind.Spore, LabelKey = "setting.spore_fungus.name", DescriptionKey = "setting.spore_fungus.description", Getter = () => HostSettings.SporeShelfFungusEnabled, Setter = delegate(bool value) { HostSettings.SporeShelfFungusEntry.Value = value; }, ManualEffect = RandomEffectKind.ShelfFungus }, new Setting { Id = "CactusHealing", Form = SkeletonKind.Cactus, LabelKey = "setting.cactus_healing.name", DescriptionKey = "setting.cactus_healing.description", Getter = () => HostSettings.CactusHealingEnabled, Setter = delegate(bool value) { HostSettings.CactusHealingEntry.Value = value; } }, new Setting { Id = "CactusStick", Form = SkeletonKind.Cactus, LabelKey = "setting.cactus_stick.name", DescriptionKey = "setting.cactus_stick.description", Getter = () => HostSettings.CactusStickEnabled, Setter = delegate(bool value) { HostSettings.CactusStickEntry.Value = value; } }, new Setting { Id = "WebGrapple", Form = SkeletonKind.Web, LabelKey = "setting.web_grapple.name", DescriptionKey = "setting.web_grapple.description", Getter = () => HostSettings.WebGrappleEnabled, Setter = delegate(bool value) { HostSettings.WebGrappleEntry.Value = value; } }, new Setting { Id = "WebBite", Form = SkeletonKind.Web, LabelKey = "setting.web_bite.name", DescriptionKey = "setting.web_bite.description", Getter = () => HostSettings.WebBiteEnabled, Setter = delegate(bool value) { HostSettings.WebBiteEntry.Value = value; } }, new Setting { Id = "StonePetrify", Form = SkeletonKind.Stone, LabelKey = "setting.stone_petrify.name", DescriptionKey = "setting.stone_petrify.description", Getter = () => HostSettings.StonePetrifyEnabled, Setter = delegate(bool value) { HostSettings.StonePetrifyEntry.Value = value; } }, new Setting { Id = "StoneForceImmunity", Form = SkeletonKind.Stone, LabelKey = "setting.stone_force.name", DescriptionKey = "setting.stone_force.description", Getter = () => HostSettings.StoneForceImmunityEnabled, Setter = delegate(bool value) { HostSettings.StoneForceImmunityEntry.Value = value; } }, new Setting { Id = "FlyTrapExplosion", Form = SkeletonKind.FlyTrap, LabelKey = "setting.flytrap_explosion.name", DescriptionKey = "setting.flytrap_explosion.description", Getter = () => HostSettings.FlyTrapExplosionEnabled, Setter = delegate(bool value) { HostSettings.FlyTrapExplosionEntry.Value = value; }, ManualEffect = RandomEffectKind.ExplosiveBomb }, new Setting { Id = "FlyTrapTrampoline", Form = SkeletonKind.FlyTrap, LabelKey = "setting.flytrap_trampoline.name", DescriptionKey = "setting.flytrap_trampoline.description", Getter = () => HostSettings.FlyTrapTrampolineEnabled, Setter = delegate(bool value) { HostSettings.FlyTrapTrampolineEntry.Value = value; } } }; } } internal static class SettingsWindowRouter { internal static bool IsOpen => SettingsWindowView.IsOpen; internal static bool IsCapturingKey => SettingsWindowView.IsCapturingKey; internal static void Toggle() { SettingsWindowView.Toggle(); } internal static bool HandleKeyCapture() { return SettingsWindowView.HandleKeyCapture(); } internal static void Tick() { SettingsWindowView.Tick(); } internal static void Close() { SettingsWindowView.Close(); } internal static void KeepCursorAvailable() { SettingsWindowView.KeepCursorAvailable(); } } internal static class SettingsWindowView { private sealed class CheckboxRowBinding { internal SettingsWindowModel.Setting Setting; internal Button Button; internal CheckboxHover Hover; internal HoverTint RowHover; internal TextMeshProUGUI Label; internal TextMeshProUGUI Description; internal void Refresh() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) bool flag = SettingsWindowModel.CanEdit(Setting); ((Selectable)Button).interactable = flag; if ((Object)(object)Hover != (Object)null) { ((Behaviour)Hover).enabled = flag; Hover.Refresh(); ((Graphic)Hover.Image).color = (flag ? Color.white : SsrTheme.Disabled); } if ((Object)(object)RowHover != (Object)null) { ((Behaviour)RowHover).enabled = flag; } if ((Object)(object)Label != (Object)null) { ((Graphic)Label).color = (flag ? SsrTheme.TextLabel : SsrTheme.TextDim); } if ((Object)(object)Description != (Object)null) { ((Graphic)Description).color = (flag ? SsrTheme.TextBody : SsrTheme.TextDim); } } } private sealed class ModeChipBinding { internal Image Image; internal TextMeshProUGUI Label; internal GameMode Mode; internal Sprite ActiveSprite; internal Sprite InactiveSprite; internal Sprite DisabledSprite; internal void Refresh() { //IL_0041: 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_0065: 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) bool flag = SettingsWindowModel.Mode == Mode; bool canEditRules = SettingsWindowModel.CanEditRules; Image.sprite = ((!canEditRules) ? DisabledSprite : (flag ? ActiveSprite : InactiveSprite)); ((Graphic)Image).color = Color.white; ((Graphic)Label).color = ((!canEditRules) ? SsrTheme.TextDim : (flag ? SsrTheme.TextWhite : SsrTheme.TextHeading)); Button component = ((Component)Image).GetComponent