using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using PEAKLib.Core; using Photon.Pun; using Photon.Realtime; using PhotonCustomPropsUtils; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Parachuted")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Parachuted")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("223e22f1-d786-4698-9c76-78d5633cbf2b")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace Parachuted { [BepInPlugin("tony4twentys.Parachuted", "Parachuted", "1.0.0")] [BepInProcess("PEAK.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(CharacterSpawner), "SpawnMyPlayerCharacter")] private static class Patch_SpawnMyPlayerCharacter { private static void Prefix(SpawnFlavor spawnFlavor, Transform spawnOverride, ref Vector3 spawnOffset) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (IsModActive && IsShorePassedOutSpawn(spawnFlavor, spawnOverride)) { Vector3 shoreSpawnOffset = GetShoreSpawnOffset(); spawnOffset += shoreSpawnOffset; HideCrashedPlaneWreckage(); Debug.Log((object)$"[Parachuted] Shore spawn offset {shoreSpawnOffset} (up {DropHeight:0.#}m, ocean {OceanOffset:0.#}m)."); } } private static void Postfix(SpawnFlavor spawnFlavor, Transform spawnOverride, Character __result) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__result == (Object)null) && IsModActive && IsShorePassedOutSpawn(spawnFlavor, spawnOverride)) { ArmFreeParachute(__result); } } } [HarmonyPatch(typeof(Character), "FixedUpdate")] private static class Patch_CharacterFixedUpdate { private static void Prefix(Character __instance) { RestoreFreeParachuteFlag(__instance); } } [HarmonyPatch(typeof(Character), "Update")] private static class Patch_CharacterUpdate { private static void Postfix(Character __instance) { RestoreFreeParachuteFlag(__instance); } } [HarmonyPatch(typeof(CharacterBalloons), "RPC_SetParachute")] private static class Patch_RPC_SetParachute { private static void Postfix(CharacterBalloons __instance, bool open) { if (open && !((Object)(object)__instance == (Object)null)) { Character val = ((Component)__instance).GetComponentInParent(); if ((Object)(object)val == (Object)null) { val = Character.localCharacter; } ConsumeFreeParachute(val); } } } [HarmonyPatch(typeof(Campfire), "Awake")] private static class Patch_CampfireAwake { private static void Prefix(Campfire __instance) { if (IsModActive && IsShoreWreckCampfire(__instance)) { _shoreWreckFires.Add(__instance); __instance.forceLit = false; __instance.nameOverride = ""; } } private static void Postfix(Campfire __instance) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 if (!((Object)(object)__instance == (Object)null) && _shoreWreckFires.Contains(__instance)) { __instance.forceLit = false; if ((int)__instance.state != 2) { __instance.state = (FireState)0; } __instance.disableWhenLit = null; ApplyUnlitVisuals(__instance); _preparedCampfires.Add(__instance); } } } [HarmonyPatch(typeof(Campfire), "Light_Rpc")] private static class Patch_CampfireLightRpc { private static Exception Finalizer(Campfire __instance, Exception __exception) { if ((Object)(object)__instance != (Object)null && (_preparedCampfires.Contains(__instance) || _shoreWreckFires.Contains(__instance))) { ShowLitVisuals(__instance); } return __exception; } } public const string PluginGuid = "tony4twentys.Parachuted"; public const string PluginName = "Parachuted"; public const string PluginVersion = "1.0.0"; public const string PhotonCustomPropsPluginGuid = "com.snosz.photoncustompropsutils"; private const ushort ParachuteItemId = 176; private const string CrashedPlaneName = "crashed plane"; private static readonly HashSet _needsParachute = new HashSet(); private static readonly HashSet _lateOrReconnect = new HashSet(); private static readonly HashSet _disconnectedUserIds = new HashSet(); private static ConfigEntry _dropHeight; private static ConfigEntry _oceanOffset; private static ConfigEntry _giveParachute; private static ConfigEntry _hideCrashedPlane; private Harmony _harmony; private Coroutine _hideWreckageRoutine; private static bool _wreckageHidden; private static readonly HashSet _freeParachuteActors = new HashSet(); private static readonly HashSet _freeParachuteCharacters = new HashSet(); private static readonly HashSet _preparedCampfires = new HashSet(); private static readonly HashSet _shoreWreckFires = new HashSet(); internal static bool SyncedGiveParachute = true; internal static float SyncedDropHeight = 100f; internal static float SyncedOceanOffset = 80f; internal static bool SyncedHideCrashedPlane = true; private bool _applyingRemoteConfig; internal static Plugin Instance { get; private set; } public ManualLogSource Logger => ((BaseUnityPlugin)this).Logger; public static bool IsModActive => ParachutedModAuthorizationService.IsGameplayEnabled; private static float DropHeight => Mathf.Max(0f, SyncedDropHeight); private static float OceanOffset => SyncedOceanOffset; private void Awake() { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown Instance = this; _giveParachute = ((BaseUnityPlugin)this).Config.Bind("General", "GiveParachute", true, "On a Level_* shore sky-drop, open a parachute without putting item 176 in inventory (won't clash with other starter-item mods). Host-synced."); _dropHeight = ((BaseUnityPlugin)this).Config.Bind("General", "DropHeight", 100f, "Meters to raise the shore spawn into the sky. Host-synced."); _oceanOffset = ((BaseUnityPlugin)this).Config.Bind("General", "OceanOffset", 80f, "Meters from the shore spawn out to sea (opposite the spawn facing / inland climb) so you parachute toward the campfire. Negative flips inland. Host-synced."); _hideCrashedPlane = ((BaseUnityPlugin)this).Config.Bind("General", "HideCrashedPlane", true, "Hide the crashed plane fuselage/wings (keeps luggage and item spawners). Scene meshes are not networked — every client with this mod hides their own copy. Host-synced."); _giveParachute.SettingChanged += OnGameplaySettingChanged; _dropHeight.SettingChanged += OnGameplaySettingChanged; _oceanOffset.SettingChanged += OnGameplaySettingChanged; _hideCrashedPlane.SettingChanged += OnGameplaySettingChanged; ApplySnapshot(BuildSnapshot(), fromRemote: false); ParachutedModAuthorizationService.Initialize(); SceneManager.sceneLoaded += OnSceneLoaded; SubscribeEvents(); _harmony = new Harmony("tony4twentys.Parachuted"); _harmony.PatchAll(typeof(Plugin).Assembly); Logger.LogInfo((object)"Parachuted v1.0.0 loaded (host handshake + config sync)."); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; UnsubscribeEvents(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } internal void OnGameplayEnabled() { if (IsGameplayLevel()) { ScheduleHideWreckage(); } } private void OnGameplaySettingChanged(object sender, EventArgs e) { if (!_applyingRemoteConfig && (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)) { ParachutedModAuthorizationService.OnHostConfigChangedLocally(); } } internal ParachutedConfigSnapshot BuildSnapshot() { return new ParachutedConfigSnapshot { GiveParachute = (_giveParachute == null || _giveParachute.Value), DropHeight = ((_dropHeight != null) ? Mathf.Max(0f, _dropHeight.Value) : 100f), OceanOffset = ((_oceanOffset != null) ? _oceanOffset.Value : 80f), HideCrashedPlane = (_hideCrashedPlane == null || _hideCrashedPlane.Value) }; } internal void ApplySnapshot(ParachutedConfigSnapshot snapshot, bool fromRemote) { if (snapshot != null) { _applyingRemoteConfig = true; try { SyncedGiveParachute = snapshot.GiveParachute; SyncedDropHeight = Mathf.Max(0f, snapshot.DropHeight); SyncedOceanOffset = snapshot.OceanOffset; SyncedHideCrashedPlane = snapshot.HideCrashedPlane; } finally { _applyingRemoteConfig = false; } if (fromRemote) { Logger.LogInfo((object)$"[Parachuted] Applied host config (chute={SyncedGiveParachute}, drop={SyncedDropHeight:0.#}, ocean={SyncedOceanOffset:0.#}, hidePlane={SyncedHideCrashedPlane})."); } if (IsModActive && IsGameplayLevel()) { ScheduleHideWreckage(); } } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ResetSession(); SubscribeEvents(); ScheduleHideWreckage(); } private static void ResetSession() { _needsParachute.Clear(); _lateOrReconnect.Clear(); _disconnectedUserIds.Clear(); _wreckageHidden = false; _preparedCampfires.Clear(); _shoreWreckFires.Clear(); _freeParachuteActors.Clear(); _freeParachuteCharacters.Clear(); } private void SubscribeEvents() { UnsubscribeEvents(); PlayerHandler.CharacterRegistered = (Action)Delegate.Combine(PlayerHandler.CharacterRegistered, new Action(OnCharacterRegistered)); GlobalEvents.OnPlayerConnected = (Action)Delegate.Combine(GlobalEvents.OnPlayerConnected, new Action(OnPlayerConnected)); GlobalEvents.OnPlayerDisconnected = (Action)Delegate.Combine(GlobalEvents.OnPlayerDisconnected, new Action(OnPlayerDisconnected)); } private void UnsubscribeEvents() { PlayerHandler.CharacterRegistered = (Action)Delegate.Remove(PlayerHandler.CharacterRegistered, new Action(OnCharacterRegistered)); GlobalEvents.OnPlayerConnected = (Action)Delegate.Remove(GlobalEvents.OnPlayerConnected, new Action(OnPlayerConnected)); GlobalEvents.OnPlayerDisconnected = (Action)Delegate.Remove(GlobalEvents.OnPlayerDisconnected, new Action(OnPlayerDisconnected)); } private static void OnPlayerConnected(Player player) { if (player != null && PhotonNetwork.IsMasterClient && IsGameplayLevel() && (Object)(object)Character.localCharacter != (Object)null) { _lateOrReconnect.Add(player.ActorNumber); } } private static void OnPlayerDisconnected(Player player) { if (player != null) { if (!string.IsNullOrEmpty(player.UserId)) { _disconnectedUserIds.Add(player.UserId); } _needsParachute.Remove(player.ActorNumber); _lateOrReconnect.Remove(player.ActorNumber); } } private static void OnCharacterRegistered(Character character) { try { if (PhotonNetwork.IsMasterClient && IsGameplayLevel() && IsModActive && TryGetActorNumber(character, out var actorNumber)) { Player owner = ((MonoBehaviourPun)character).photonView.Owner; if (owner != null && !string.IsNullOrEmpty(owner.UserId) && _disconnectedUserIds.Remove(owner.UserId)) { _lateOrReconnect.Add(actorNumber); } if (!_lateOrReconnect.Contains(actorNumber)) { _needsParachute.Add(actorNumber); } } } catch (Exception arg) { Debug.LogError((object)$"[Parachuted] OnCharacterRegistered failed: {arg}"); } } private static void ArmFreeParachute(Character character) { if ((Object)(object)character == (Object)null || !IsModActive || !SyncedGiveParachute) { return; } _freeParachuteCharacters.Add(character); if (TryGetActorNumber(character, out var actorNumber)) { _freeParachuteActors.Add(actorNumber); } try { if ((Object)(object)character.data != (Object)null) { character.data.hasParachute = true; character.data.currentParachuteItem = null; character.data.currentParachuteSlot = null; } } catch (Exception arg) { Debug.LogError((object)$"[Parachuted] ArmFreeParachute failed: {arg}"); } Debug.Log((object)"[Parachuted] Armed a free parachute (no inventory item)."); } private static bool WantsFreeParachute(Character character) { if ((Object)(object)character == (Object)null || !IsModActive || !SyncedGiveParachute) { return false; } if (_freeParachuteCharacters.Contains(character)) { return true; } int actorNumber; return TryGetActorNumber(character, out actorNumber) && _freeParachuteActors.Contains(actorNumber); } private static void RestoreFreeParachuteFlag(Character character) { if (!((Object)(object)character?.data == (Object)null) && WantsFreeParachute(character)) { character.data.hasParachute = true; } } private static void ConsumeFreeParachute(Character character) { if (!((Object)(object)character == (Object)null)) { _freeParachuteCharacters.Remove(character); if (TryGetActorNumber(character, out var actorNumber)) { _freeParachuteActors.Remove(actorNumber); } } } private static bool IsShorePassedOutSpawn(SpawnFlavor spawnFlavor, Transform spawnOverride) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 if ((int)spawnFlavor != 1) { return false; } if ((Object)(object)spawnOverride != (Object)null) { return false; } return IsGameplayLevel(); } private static Vector3 GetShoreSpawnOffset() { //IL_0001: 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_0028: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_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_0047: 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) Vector3 val = Vector3.up * DropHeight; float oceanOffset = OceanOffset; if (Mathf.Abs(oceanOffset) < 0.01f) { return val; } if (TryGetOceanBackDirection(out var direction)) { val += direction * oceanOffset; } return val; } private static bool TryGetOceanBackDirection(out Vector3 direction) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: 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_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) direction = Vector3.zero; SpawnPoint localSpawnPoint = SpawnPoint.LocalSpawnPoint; if ((Object)(object)localSpawnPoint != (Object)null) { direction = -((Component)localSpawnPoint).transform.forward; direction.y = 0f; if (((Vector3)(ref direction)).sqrMagnitude > 0.01f) { ((Vector3)(ref direction)).Normalize(); return true; } } if (TryFindPlaneHalves(out var front, out var back, out var plane)) { if ((Object)(object)plane != (Object)null) { direction = -plane.forward; direction.y = 0f; if (((Vector3)(ref direction)).sqrMagnitude > 0.01f) { ((Vector3)(ref direction)).Normalize(); return true; } } if ((Object)(object)front != (Object)null && (Object)(object)back != (Object)null) { Vector3 val = back.position - front.position; val.y = 0f; Vector3 val2 = (((Object)(object)localSpawnPoint != (Object)null) ? ((Component)localSpawnPoint).transform.forward : Vector3.forward); val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { val2 = Vector3.forward; } ((Vector3)(ref val2)).Normalize(); Vector3 val3 = Vector3.Cross(Vector3.up, (((Vector3)(ref val)).sqrMagnitude > 0.01f) ? ((Vector3)(ref val)).normalized : Vector3.right); val3.y = 0f; if (((Vector3)(ref val3)).sqrMagnitude > 0.01f) { ((Vector3)(ref val3)).Normalize(); direction = ((Vector3.Dot(val3, -val2) >= 0f) ? val3 : (-val3)); return true; } } } return false; } private static bool TryFindPlaneHalves(out Transform front, out Transform back, out Transform plane) { front = null; back = null; plane = null; GameObject val = GameObject.Find("crashed plane"); if ((Object)(object)val == (Object)null) { Transform[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (Transform val2 in array) { if ((Object)(object)val2 != (Object)null && ((Object)val2).name.Equals("crashed plane", StringComparison.OrdinalIgnoreCase)) { val = ((Component)val2).gameObject; break; } } } if ((Object)(object)val == (Object)null) { return false; } plane = val.transform; Transform[] componentsInChildren = val.GetComponentsInChildren(true); foreach (Transform val3 in componentsInChildren) { if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3 == (Object)(object)plane)) { if (IsFrontHalfName(((Object)val3).name)) { front = val3; } else if (IsBackHalfName(((Object)val3).name)) { back = val3; } } } return (Object)(object)front != (Object)null || (Object)(object)back != (Object)null || (Object)(object)plane != (Object)null; } private static bool IsFrontHalfName(string name) { return name != null && name.IndexOf("front half", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool IsBackHalfName(string name) { return name != null && name.IndexOf("back half", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool IsWreckageName(string name) { if (string.IsNullOrEmpty(name)) { return false; } if (IsFrontHalfName(name) || IsBackHalfName(name)) { return true; } return name.IndexOf("Wings", StringComparison.OrdinalIgnoreCase) >= 0; } private void ScheduleHideWreckage() { if (_hideWreckageRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_hideWreckageRoutine); } if (IsModActive && IsGameplayLevel()) { _hideWreckageRoutine = ((MonoBehaviour)this).StartCoroutine(HideWreckageRoutine()); } } private static IEnumerator HideWreckageRoutine() { yield return null; HideCrashedPlaneWreckage(); yield return (object)new WaitForSeconds(1f); HideCrashedPlaneWreckage(); yield return (object)new WaitForSeconds(3f); HideCrashedPlaneWreckage(); } private static void HideCrashedPlaneWreckage() { if (!IsModActive || !IsGameplayLevel() || !TryFindPlaneHalves(out var front, out var back, out var plane)) { return; } if (SyncedHideCrashedPlane && !_wreckageHidden) { int num = 0; try { num += HideIfWreckage(front); num += HideIfWreckage(back); if ((Object)(object)plane != (Object)null) { Transform[] array = (Transform[])(object)new Transform[plane.childCount]; for (int i = 0; i < plane.childCount; i++) { array[i] = plane.GetChild(i); } foreach (Transform val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)front) && !((Object)(object)val == (Object)(object)back)) { num += HideIfWreckage(val); } } } } catch (Exception arg) { Debug.LogError((object)$"[Parachuted] Hide wreckage failed: {arg}"); } if (num > 0) { _wreckageHidden = true; Debug.Log((object)$"[Parachuted] Hid {num} crashed-plane wreckage object(s). Campfires reparented; luggage/spawners left in place."); } } if (_preparedCampfires.Count == 0) { MakeCrashedPlaneCampfiresUnlit(plane); } } private static int HideIfWreckage(Transform t) { if ((Object)(object)t == (Object)null || !IsWreckageName(((Object)t).name)) { return 0; } if (!((Component)t).gameObject.activeSelf) { return 0; } RescueCampfiresFromWreckage(t); if ((Object)(object)((Component)t).GetComponent() != (Object)null || (Object)(object)((Component)t).GetComponentInChildren(true) != (Object)null) { HideWreckageVisualsKeepingCampfires(t); return 1; } ((Component)t).gameObject.SetActive(false); return 1; } private static void RescueCampfiresFromWreckage(Transform wreckage) { if ((Object)(object)wreckage == (Object)null) { return; } Transform val = (((Object)(object)wreckage.parent != (Object)null) ? wreckage.parent : wreckage); Campfire[] componentsInChildren = ((Component)wreckage).GetComponentsInChildren(true); int num = 0; foreach (Campfire val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null) { continue; } Transform campfireVisualRoot = GetCampfireVisualRoot(val2); if (!((Object)(object)campfireVisualRoot == (Object)(object)wreckage) && campfireVisualRoot.IsChildOf(wreckage)) { campfireVisualRoot.SetParent(val, true); if (!((Component)campfireVisualRoot).gameObject.activeSelf) { ((Component)campfireVisualRoot).gameObject.SetActive(true); } num++; } } if (num > 0) { Debug.Log((object)$"[Parachuted] Reparented {num} campfire(s) off '{((Object)wreckage).name}' so the fire stays in the world."); } } private static void HideWreckageVisualsKeepingCampfires(Transform wreckage) { Renderer[] componentsInChildren = ((Component)wreckage).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent() != (Object)null)) { val.enabled = false; } } Collider[] componentsInChildren2 = ((Component)wreckage).GetComponentsInChildren(true); foreach (Collider val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).GetComponentInParent() != (Object)null)) { val2.enabled = false; } } } private static void MakeCrashedPlaneCampfiresUnlit(Transform plane) { if (!((Object)(object)plane == (Object)null)) { Campfire[] componentsInChildren = ((Component)plane).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { MakeCampfireUnlitAndLightable(componentsInChildren[i]); } } } private static Transform GetCampfireVisualRoot(Campfire fire) { Transform val = ((Component)fire).transform; Transform val2 = null; while ((Object)(object)val != (Object)null) { if (((Object)val).name.IndexOf("Campfire_Flame", StringComparison.OrdinalIgnoreCase) >= 0) { val2 = val; } val = val.parent; } return ((Object)(object)val2 != (Object)null) ? val2 : ((Component)fire).transform; } private static bool IsShoreWreckCampfire(Campfire fire) { if ((Object)(object)fire == (Object)null) { return false; } if (!string.IsNullOrEmpty(fire.nameOverride) && fire.nameOverride.Equals("FLAME", StringComparison.OrdinalIgnoreCase)) { return true; } Transform val = ((Component)fire).transform; while ((Object)(object)val != (Object)null) { if (((Object)val).name.IndexOf("Campfire_Flame", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } val = val.parent; } return false; } private static void MakeCampfireUnlitAndLightable(Campfire fire) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_00af: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)fire == (Object)null || _preparedCampfires.Contains(fire)) { return; } try { if ((int)fire.state != 2) { fire.forceLit = false; fire.state = (FireState)0; fire.beenBurningFor = 0f; fire.nameOverride = ""; fire.disableWhenLit = null; ApplyUnlitVisuals(fire); EnsureCampfireCollider(fire, GetCampfireVisualRoot(fire)); SnapCampfireToGround(GetCampfireVisualRoot(fire)); _preparedCampfires.Add(fire); Transform campfireVisualRoot = GetCampfireVisualRoot(fire); Debug.Log((object)$"[Parachuted] '{((Object)campfireVisualRoot).name}' is unlit and lightable (state={fire.state}, forceLit={fire.forceLit}, enableWhenLit={NameOf((Object)(object)fire.enableWhenLit)}, logRoot={NameOf((Object)(object)fire.logRoot)})."); } } catch (Exception arg) { Debug.LogError((object)$"[Parachuted] MakeCampfireUnlitAndLightable failed: {arg}"); } } private static string NameOf(Object obj) { return (obj != (Object)null) ? obj.name : "null"; } private static void ApplyUnlitVisuals(Campfire fire) { Transform campfireVisualRoot = GetCampfireVisualRoot(fire); if ((Object)(object)campfireVisualRoot != (Object)null && !((Component)campfireVisualRoot).gameObject.activeSelf) { ((Component)campfireVisualRoot).gameObject.SetActive(true); } BindLitReferences(fire, campfireVisualRoot); SetChildActive(campfireVisualRoot, "Base", active: false); SetChildActive(campfireVisualRoot, "Collider", active: true); SetChildActive(campfireVisualRoot, "Campfire", active: true); EnableLogs(fire); if ((Object)(object)fire.enableWhenLit != (Object)null) { ArmEnableWhenLit(fire.enableWhenLit); } SetChildActive(campfireVisualRoot, "VFX_Smoke_Lit", active: true); SetChildActive(campfireVisualRoot, "VFX_Smoke_Off", active: true); StopParticles(fire.smokeParticlesLit); StopParticles(fire.smokeParticlesOff); } private static void BindLitReferences(Campfire fire, Transform root) { if ((Object)(object)fire.enableWhenLit == (Object)null) { Transform val = FindNamedChild(root, "EnableWhenLit"); if ((Object)(object)val != (Object)null) { fire.enableWhenLit = ((Component)val).gameObject; } } if ((Object)(object)fire.smokeParticlesLit == (Object)null) { Transform val2 = FindNamedChild(root, "VFX_Smoke_Lit"); if ((Object)(object)val2 != (Object)null) { fire.smokeParticlesLit = ((Component)val2).GetComponent(); if ((Object)(object)fire.smokeParticlesLit == (Object)null) { fire.smokeParticlesLit = ((Component)val2).GetComponentInChildren(true); } } } if ((Object)(object)fire.fireParticles == (Object)null && (Object)(object)fire.enableWhenLit != (Object)null) { fire.fireParticles = fire.enableWhenLit.GetComponentInChildren(true); } } private static void EnableLogs(Campfire fire) { Transform campfireVisualRoot = GetCampfireVisualRoot(fire); SetChildActive(campfireVisualRoot, "Logs", active: true); EnableVisuals(FindNamedChild(campfireVisualRoot, "Logs")); if ((Object)(object)fire.logRoot == (Object)null) { return; } Transform val = fire.logRoot; while ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(true); if ((Object)(object)val == (Object)(object)campfireVisualRoot) { break; } val = val.parent; } EnableVisuals(fire.logRoot); } private static void ArmEnableWhenLit(GameObject go) { if ((Object)(object)go == (Object)null) { return; } go.SetActive(false); Transform[] componentsInChildren = go.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (!((Object)(object)componentsInChildren[i] == (Object)null) && !((Object)(object)componentsInChildren[i] == (Object)(object)go.transform)) { ((Component)componentsInChildren[i]).gameObject.SetActive(true); } } } private static void ShowLitVisuals(Campfire fire) { if (!((Object)(object)fire == (Object)null)) { Transform campfireVisualRoot = GetCampfireVisualRoot(fire); BindLitReferences(fire, campfireVisualRoot); SetChildActive(campfireVisualRoot, "Base", active: false); EnableLogs(fire); if ((Object)(object)fire.enableWhenLit != (Object)null) { EnableVisuals(fire.enableWhenLit.transform); } SetChildActive(campfireVisualRoot, "EnableWhenLit", active: true); SetChildActive(campfireVisualRoot, "VFX_Smoke_Lit", active: true); if ((Object)(object)fire.smokeParticlesLit != (Object)null) { ((Component)fire.smokeParticlesLit).gameObject.SetActive(true); fire.smokeParticlesLit.Play(true); } if ((Object)(object)fire.fireParticles != (Object)null) { ((Component)fire.fireParticles).gameObject.SetActive(true); fire.fireParticles.Play(true); } } } private static void EnsureCampfireCollider(Campfire fire, Transform root) { Transform val = FindNamedChild(root, "Collider"); if (!((Object)(object)val == (Object)null)) { ((Component)val).gameObject.SetActive(true); Collider component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { component.enabled = true; } if (!((Object)(object)((Component)val).GetComponentInParent() == (Object)(object)fire)) { val.SetParent(((Component)fire).transform, true); } } } private static void SnapCampfireToGround(Transform root) { //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_0023: 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_002d: 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_002f: 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_0055: 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_00e8: 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_009f: 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) if ((Object)(object)root == (Object)null) { return; } Vector3 val = root.position + Vector3.up * 8f; RaycastHit[] array = Physics.RaycastAll(val, Vector3.down, 40f); float num = float.NegativeInfinity; bool flag = false; for (int i = 0; i < array.Length; i++) { RaycastHit val2 = array[i]; if (!((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) && !((Component)((RaycastHit)(ref val2)).collider).transform.IsChildOf(root) && !((Object)(object)((Component)((RaycastHit)(ref val2)).collider).transform == (Object)(object)root) && ((RaycastHit)(ref val2)).point.y > num) { num = ((RaycastHit)(ref val2)).point.y; flag = true; } } if (flag) { Vector3 position = root.position; position.y = num; root.position = position; } } private static void EnableVisuals(Transform root) { if ((Object)(object)root == (Object)null) { return; } ((Component)root).gameObject.SetActive(true); Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Object)(object)componentsInChildren[i] != (Object)null) { ((Component)componentsInChildren[i]).gameObject.SetActive(true); } } Renderer[] componentsInChildren2 = ((Component)root).GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren2.Length; j++) { if ((Object)(object)componentsInChildren2[j] != (Object)null) { componentsInChildren2[j].enabled = true; } } } private static void StopParticles(ParticleSystem ps) { if (!((Object)(object)ps == (Object)null)) { ps.Stop(true, (ParticleSystemStopBehavior)0); } } private static Transform FindNamedChild(Transform root, string childName) { if ((Object)(object)root == (Object)null) { return null; } Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)val).name.Equals(childName, StringComparison.OrdinalIgnoreCase)) { return val; } } return null; } private static void SetChildActive(Transform root, string childName, bool active) { Transform val = FindNamedChild(root, childName); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(active); } } private static bool HasParachute(Character character) { if ((Object)(object)character == (Object)null) { return false; } try { if ((Object)(object)character.data != (Object)null && character.data.hasParachute) { return true; } CharacterData data = character.data; if ((Object)(object)((data != null) ? data.currentItem : null) != (Object)null && character.data.currentItem.itemID == 176) { return true; } Player player = character.player; if ((Object)(object)player == (Object)null) { return false; } if (SlotHasParachute(player.tempFullSlot)) { return true; } ItemSlot[] itemSlots = player.itemSlots; if (itemSlots == null) { return false; } for (int i = 0; i < itemSlots.Length; i++) { if (SlotHasParachute(itemSlots[i])) { return true; } } } catch { return false; } return false; } private static bool SlotHasParachute(ItemSlot slot) { return slot != null && !slot.IsEmpty() && (Object)(object)slot.prefab != (Object)null && slot.prefab.itemID == 176; } private static bool TryGetActorNumber(Character character, out int actorNumber) { actorNumber = 0; if ((Object)(object)character == (Object)null || character.isBot) { return false; } PhotonView photonView = ((MonoBehaviourPun)character).photonView; if (((photonView != null) ? photonView.Owner : null) == null) { return false; } actorNumber = photonView.Owner.ActorNumber; return true; } private static bool IsGameplayLevel() { //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) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).name != null && ((Scene)(ref activeScene)).name.IndexOf("Level_", StringComparison.OrdinalIgnoreCase) >= 0; } } internal static class ParachutedModAuthorizationService { public const string RoomConfigPropertyKey = "parachutedConfigV1"; private static PhotonScopedManager _photonManager; private static bool _photonManagerReady; private static bool _hostHasParachutedMod; private static string _pendingHostConfigPayload; public static bool IsGameplayEnabled { get; private set; } public static void Initialize() { Networking.AddHostHasPluginListeners("tony4twentys.Parachuted", (Action)OnHostHasParachutedMod, (Action)OnHostMissingParachutedMod); if (PhotonCustomPropsUtilsPlugin.IsReady) { SetupPhotonManager(); } else { PhotonCustomPropsUtilsPlugin.OnReady += SetupPhotonManager; } if (!PhotonNetwork.InRoom) { SetSoloAuthorized(); } } public static void SetSoloAuthorized() { _hostHasParachutedMod = true; IsGameplayEnabled = true; ParachutedConfigSyncService.ApplyLocalAuthoritativeConfig(); } private static void SetupPhotonManager() { if (_photonManagerReady) { return; } _photonManagerReady = true; _photonManager = PhotonCustomPropsUtilsPlugin.GetManager("tony4twentys.Parachuted"); _photonManager.RegisterOnJoinedRoom((Action)delegate { if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && _hostHasParachutedMod) { ParachutedConfigSyncService.ApplyLocalAuthoritativeConfig(); PublishHostConfigIfMaster(); } }); _photonManager.RegisterRoomProperty("parachutedConfigV1", (RoomEventType)2, (Action)delegate(string value) { if (!PhotonNetwork.IsMasterClient && !string.IsNullOrEmpty(value)) { if (!_hostHasParachutedMod) { _pendingHostConfigPayload = value; } else { ParachutedConfigSyncService.ApplyRemoteSnapshot(value); } } }); } private static void OnHostHasParachutedMod() { if (PhotonNetwork.InRoom) { _hostHasParachutedMod = true; IsGameplayEnabled = true; Plugin instance = Plugin.Instance; if (instance != null) { instance.Logger.LogInfo((object)"[Parachuted] Host handshake OK — gameplay enabled."); } if (PhotonNetwork.IsMasterClient) { ParachutedConfigSyncService.ApplyLocalAuthoritativeConfig(); PublishHostConfigIfMaster(); } else if (!string.IsNullOrEmpty(_pendingHostConfigPayload)) { ParachutedConfigSyncService.ApplyRemoteSnapshot(_pendingHostConfigPayload); _pendingHostConfigPayload = null; } Plugin.Instance?.OnGameplayEnabled(); } } private static void OnHostMissingParachutedMod() { if (PhotonNetwork.InRoom) { _hostHasParachutedMod = false; IsGameplayEnabled = false; _pendingHostConfigPayload = null; ParachutedConfigSyncService.ClearRemoteHostConfig(); ParachutedConfigSyncService.ApplyLocalAuthoritativeConfig(); Plugin instance = Plugin.Instance; if (instance != null) { instance.Logger.LogWarning((object)"[Parachuted] Host missing mod — gameplay disabled (anti-cheat)."); } } } public static void OnHostConfigChangedLocally() { if (!PhotonNetwork.InRoom) { ParachutedConfigSyncService.ApplyLocalAuthoritativeConfig(); } else if (IsGameplayEnabled && PhotonNetwork.IsMasterClient) { ParachutedConfigSyncService.ApplyLocalAuthoritativeConfig(); PublishHostConfigIfMaster(); } } private static void PublishHostConfigIfMaster() { if (_photonManager != null && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && !((Object)(object)Plugin.Instance == (Object)null)) { string text = ParachutedConfigSyncService.Serialize(Plugin.Instance.BuildSnapshot()); _photonManager.SetRoomProperty("parachutedConfigV1", (object)text); Plugin.Instance.Logger.LogInfo((object)"[Parachuted] Host published gameplay config."); } } } internal sealed class ParachutedConfigSnapshot { public bool GiveParachute = true; public float DropHeight = 100f; public float OceanOffset = 80f; public bool HideCrashedPlane = true; } internal static class ParachutedConfigSyncService { public const int SchemaVersion = 1; public const char PairSeparator = ';'; public const char KeyValueSeparator = '='; public static bool HasRemoteHostConfig { get; private set; } public static void ClearRemoteHostConfig() { HasRemoteHostConfig = false; } public static void ApplyLocalAuthoritativeConfig() { if (!((Object)(object)Plugin.Instance == (Object)null)) { ClearRemoteHostConfig(); Plugin.Instance.ApplySnapshot(Plugin.Instance.BuildSnapshot(), fromRemote: false); } } public static void ApplyRemoteSnapshot(string payload) { if (PhotonNetwork.IsMasterClient || string.IsNullOrEmpty(payload)) { return; } if (!TryDeserialize(payload, out var snapshot)) { Plugin instance = Plugin.Instance; if (instance != null) { instance.Logger.LogError((object)"[Parachuted] Failed to apply host config snapshot."); } } else { HasRemoteHostConfig = true; Plugin.Instance?.ApplySnapshot(snapshot, fromRemote: true); } } public static string Serialize(ParachutedConfigSnapshot snapshot) { if (snapshot == null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(96); AppendPair(stringBuilder, "v", 1.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "chute", snapshot.GiveParachute ? "1" : "0"); AppendPair(stringBuilder, "drop", snapshot.DropHeight.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "ocean", snapshot.OceanOffset.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "hidePlane", snapshot.HideCrashedPlane ? "1" : "0"); return stringBuilder.ToString(); } public static bool TryDeserialize(string payload, out ParachutedConfigSnapshot snapshot) { snapshot = new ParachutedConfigSnapshot(); if (string.IsNullOrEmpty(payload)) { return false; } string[] array = payload.Split(new char[1] { ';' }); int result = 0; bool flag = false; foreach (string text in array) { if (string.IsNullOrEmpty(text)) { continue; } int num = text.IndexOf('='); if (num > 0) { string text2 = text.Substring(0, num); string text3 = text.Substring(num + 1); switch (text2) { case "v": flag = int.TryParse(text3, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); break; case "chute": snapshot.GiveParachute = text3 == "1"; break; case "drop": float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.DropHeight); break; case "ocean": float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.OceanOffset); break; case "hidePlane": snapshot.HideCrashedPlane = text3 == "1"; break; } } } return flag && result >= 1 && result <= 1; } private static void AppendPair(StringBuilder builder, string key, string value) { if (builder.Length > 0) { builder.Append(';'); } builder.Append(key); builder.Append('='); builder.Append(value ?? string.Empty); } } }