using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; 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: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PlaneCrash")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+066f553d411e5cf6ee8adc4e6da956bea5fcd584")] [assembly: AssemblyProduct("PlaneCrash")] [assembly: AssemblyTitle("PlaneCrash")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 PlaneCrash { [BepInPlugin("com.jill920.planecrash", "Plane Crash", "1.0.0")] public class Plugin : BaseUnityPlugin { internal sealed class PlaneCrashListener : MonoBehaviourPunCallbacks { private bool _isInAirport = true; private bool _crashWindowActive = false; private float _crashTimer = 0f; private HashSet _damagedPlayers = new HashSet(); private void Start() { SceneManager.sceneLoaded += OnSceneLoaded; PlayerHandler.CharacterRegistered = (Action)Delegate.Combine(PlayerHandler.CharacterRegistered, new Action(OnCharacterRegistered)); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; PlayerHandler.CharacterRegistered = (Action)Delegate.Remove(PlayerHandler.CharacterRegistered, new Action(OnCharacterRegistered)); } private void Update() { if (_crashWindowActive) { _crashTimer -= Time.deltaTime; if (_crashTimer <= 0f) { _crashWindowActive = false; LogInfo("[PlaneCrash] Crash window closed. No more players will receive crash damage."); } } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { string name = ((Scene)(ref scene)).name; LogInfo("[PlaneCrash] Scene loaded: " + name); if (name == "Airport") { _isInAirport = true; _crashWindowActive = false; _damagedPlayers.Clear(); LogInfo("[PlaneCrash] Reset state at Airport"); } else if (name.StartsWith("Level") && _isInAirport) { _isInAirport = false; _crashWindowActive = true; _crashTimer = CrashWindowDuration.Value; _damagedPlayers.Clear(); if (PhotonNetwork.IsMasterClient) { LogInfo($"[PlaneCrash] Plane crash! Crash window open for {_crashTimer:F0}s (host only)"); ApplyDamageToExistingPlayers(); } else { LogInfo("[PlaneCrash] Plane crash detected but not host - waiting for damage from host"); } } else if (name.StartsWith("Biome_1") && !_isInAirport) { LogInfo("[PlaneCrash] Loaded into level from non-airport - no crash damage"); } } private void ApplyDamageToExistingPlayers() { if (!PhotonNetwork.IsMasterClient || !_crashWindowActive) { return; } foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter.data == (Object)null || allCharacter.isBot) { continue; } PhotonView photonView = ((MonoBehaviourPun)allCharacter).photonView; Player val = ((photonView != null) ? photonView.Owner : null); if (val == null) { continue; } int viewID = ((MonoBehaviourPun)allCharacter).photonView.ViewID; if (!_damagedPlayers.Contains(viewID)) { if (allCharacter.refs == null || (Object)(object)allCharacter.refs.afflictions == (Object)null) { LogInfo("[PlaneCrash] Afflictions not ready for " + val.NickName + ", will apply on registration"); continue; } float num = GenerateDamage(); ApplyInjury(allCharacter, num); _damagedPlayers.Add(viewID); LogInfo($"[PlaneCrash] Applied crash damage to existing player: {val.NickName} ({num:F2} injury)"); } } } private void OnCharacterRegistered(Character character) { if ((Object)(object)character == (Object)null || (Object)(object)((MonoBehaviourPun)character).photonView == (Object)null || character.isBot) { return; } if (!_crashWindowActive) { LogInfo("[PlaneCrash] Character registered after crash window: " + character.characterName + " - no damage"); } else { if (!PhotonNetwork.IsMasterClient) { return; } Player owner = ((MonoBehaviourPun)character).photonView.Owner; if (owner != null) { int viewID = ((MonoBehaviourPun)character).photonView.ViewID; if (_damagedPlayers.Contains(viewID)) { LogInfo("[PlaneCrash] Player already damaged: " + owner.NickName); } else { ((MonoBehaviour)this).StartCoroutine(ApplyDamageWhenReady(character, owner, viewID)); } } } } private IEnumerator ApplyDamageWhenReady(Character character, Player owner, int viewID) { float timeout = 5f; while (timeout > 0f && ((Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.afflictions == (Object)null)) { yield return (object)new WaitForSeconds(0.1f); timeout -= 0.1f; } if ((Object)(object)character == (Object)null) { LogWarning("[PlaneCrash] Character became null while waiting for afflictions"); yield break; } if (character.refs == null || (Object)(object)character.refs.afflictions == (Object)null) { LogWarning("[PlaneCrash] Afflictions never initialized for " + owner.NickName + ", cannot apply damage"); yield break; } if (!_crashWindowActive) { LogInfo("[PlaneCrash] Crash window closed while waiting for " + owner.NickName + " - no damage"); yield break; } float damage = GenerateDamage(); ApplyInjury(character, damage); _damagedPlayers.Add(viewID); LogInfo($"[PlaneCrash] Applied crash damage to {owner.NickName} on join ({damage:F2} injury, {_crashTimer:F1}s remaining in window)"); } private float GenerateDamage() { float value = MaxDamage.Value; Random random = new Random(DateTime.Now.Millisecond + (int)(Time.time * 1000f) % 1000 + Random.Range(0, 9999)); double num = random.NextDouble(); float num2 = ((num < 0.15) ? (0.05f + (float)random.NextDouble() * 0.15f) : ((num < 0.45) ? (0.2f + (float)random.NextDouble() * 0.3f) : ((num < 0.75) ? (0.5f + (float)random.NextDouble() * 0.3f) : ((!(num < 0.92)) ? (0.95f + (float)random.NextDouble() * 0.05f) : (0.8f + (float)random.NextDouble() * 0.2f))))); if (num2 >= 0.99f) { num2 = 0.95f + (float)random.NextDouble() * 0.04f; } return value * num2; } private void ApplyInjury(Character character, float damageAmount) { if ((Object)(object)character == (Object)null || (Object)(object)character.data == (Object)null) { return; } try { CharacterAfflictions val = character.refs?.afflictions; if ((Object)(object)val == (Object)null) { LogWarning("[PlaneCrash] No afflictions found for " + character.characterName); return; } float currentStatus = val.GetCurrentStatus((STATUSTYPE)0); float num = Mathf.Min(currentStatus + damageAmount, 1f); float num2 = num - currentStatus; if (Mathf.Abs(num2) < 0.001f) { LogInfo($"[PlaneCrash] {character.characterName} already at {currentStatus:F2} injury, no change needed"); return; } float[] array = new float[CharacterAfflictions.NumStatusTypes]; array[0] = num2; ((MonoBehaviourPun)character).photonView.RPC("RPC_ApplyStatusesFromFloatArray", (RpcTarget)0, new object[1] { array }); LogInfo($"[PlaneCrash] Applied {damageAmount:F2} injury to {character.characterName} (total: {num:F2})"); } catch (Exception ex) { LogError("[PlaneCrash] Failed to apply injury to " + character.characterName + ": " + ex.Message); } } public override void OnPlayerEnteredRoom(Player newPlayer) { if (_crashWindowActive && PhotonNetwork.IsMasterClient) { LogInfo("[PlaneCrash] Player entered room during crash window: " + newPlayer.NickName + " - will get damage when character registers"); } } private static string GetStablePlayerKey(Player player) { if (player == null) { return string.Empty; } if (!string.IsNullOrEmpty(player.UserId)) { return "USERID:" + player.UserId; } if (!string.IsNullOrEmpty(player.NickName)) { return "NICK:" + player.NickName; } return "ACTOR:" + player.ActorNumber; } } internal static ManualLogSource Log; internal static ConfigEntry MaxDamage; internal static ConfigEntry CrashWindowDuration; private PlaneCrashListener _listener; private void Awake() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; MaxDamage = ((BaseUnityPlugin)this).Config.Bind("General", "MaxDamage", 0.7f, new ConfigDescription("Maximum injury damage a player can receive from the crash (0-1)", (AcceptableValueBase)(object)new AcceptableValueRange(0.01f, 1f), Array.Empty())); CrashWindowDuration = ((BaseUnityPlugin)this).Config.Bind("General", "CrashWindowDuration", 180f, new ConfigDescription("How long the crash window stays open for late joiners (seconds)", (AcceptableValueBase)(object)new AcceptableValueRange(30f, 300f), Array.Empty())); _listener = ((Component)this).gameObject.AddComponent(); LogInfo("========================================"); LogInfo("[PlaneCrash] LOADED v1.0.0"); LogInfo($" MaxDamage: {MaxDamage.Value:F2}"); LogInfo($" CrashWindowDuration: {CrashWindowDuration.Value:F0}s"); LogInfo("========================================"); } private void OnDestroy() { if ((Object)(object)_listener != (Object)null) { Object.Destroy((Object)(object)_listener); } } internal static void LogInfo(string msg) { Log.LogInfo((object)msg); } internal static void LogWarning(string msg) { Log.LogWarning((object)msg); } internal static void LogError(string msg) { Log.LogError((object)msg); } } }