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<Character>();
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<int> _needsParachute = new HashSet<int>();
private static readonly HashSet<int> _lateOrReconnect = new HashSet<int>();
private static readonly HashSet<string> _disconnectedUserIds = new HashSet<string>();
private static ConfigEntry<float> _dropHeight;
private static ConfigEntry<float> _oceanOffset;
private static ConfigEntry<bool> _giveParachute;
private static ConfigEntry<bool> _hideCrashedPlane;
private Harmony _harmony;
private Coroutine _hideWreckageRoutine;
private static bool _wreckageHidden;
private static readonly HashSet<int> _freeParachuteActors = new HashSet<int>();
private static readonly HashSet<Character> _freeParachuteCharacters = new HashSet<Character>();
private static readonly HashSet<Campfire> _preparedCampfires = new HashSet<Campfire>();
private static readonly HashSet<Campfire> _shoreWreckFires = new HashSet<Campfire>();
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<bool>("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<float>("General", "DropHeight", 100f, "Meters to raise the shore spawn into the sky. Host-synced.");
_oceanOffset = ((BaseUnityPlugin)this).Config.Bind<float>("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<bool>("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<Character>)Delegate.Combine(PlayerHandler.CharacterRegistered, new Action<Character>(OnCharacterRegistered));
GlobalEvents.OnPlayerConnected = (Action<Player>)Delegate.Combine(GlobalEvents.OnPlayerConnected, new Action<Player>(OnPlayerConnected));
GlobalEvents.OnPlayerDisconnected = (Action<Player>)Delegate.Combine(GlobalEvents.OnPlayerDisconnected, new Action<Player>(OnPlayerDisconnected));
}
private void UnsubscribeEvents()
{
PlayerHandler.CharacterRegistered = (Action<Character>)Delegate.Remove(PlayerHandler.CharacterRegistered, new Action<Character>(OnCharacterRegistered));
GlobalEvents.OnPlayerConnected = (Action<Player>)Delegate.Remove(GlobalEvents.OnPlayerConnected, new Action<Player>(OnPlayerConnected));
GlobalEvents.OnPlayerDisconnected = (Action<Player>)Delegate.Remove(GlobalEvents.OnPlayerDisconnected, new Action<Player>(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<Transform>((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<Transform>(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<Campfire>() != (Object)null || (Object)(object)((Component)t).GetComponentInChildren<Campfire>(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<Campfire>(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<Renderer>(true);
foreach (Renderer val in componentsInChildren)
{
if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponentInParent<Campfire>() != (Object)null))
{
val.enabled = false;
}
}
Collider[] componentsInChildren2 = ((Component)wreckage).GetComponentsInChildren<Collider>(true);
foreach (Collider val2 in componentsInChildren2)
{
if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).GetComponentInParent<Campfire>() != (Object)null))
{
val2.enabled = false;
}
}
}
private static void MakeCrashedPlaneCampfiresUnlit(Transform plane)
{
if (!((Object)(object)plane == (Object)null))
{
Campfire[] componentsInChildren = ((Component)plane).GetComponentsInChildren<Campfire>(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<ParticleSystem>();
if ((Object)(object)fire.smokeParticlesLit == (Object)null)
{
fire.smokeParticlesLit = ((Component)val2).GetComponentInChildren<ParticleSystem>(true);
}
}
}
if ((Object)(object)fire.fireParticles == (Object)null && (Object)(object)fire.enableWhenLit != (Object)null)
{
fire.fireParticles = fire.enableWhenLit.GetComponentInChildren<ParticleSystem>(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<Transform>(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<Collider>();
if ((Object)(object)component != (Object)null)
{
component.enabled = true;
}
if (!((Object)(object)((Component)val).GetComponentInParent<Campfire>() == (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<Transform>(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<Renderer>(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<Transform>(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<Player>)delegate
{
if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && _hostHasParachutedMod)
{
ParachutedConfigSyncService.ApplyLocalAuthoritativeConfig();
PublishHostConfigIfMaster();
}
});
_photonManager.RegisterRoomProperty<string>("parachutedConfigV1", (RoomEventType)2, (Action<string>)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);
}
}
}