using System; 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 GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("KeepItemsOnTeleport")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("KeepItemsOnTeleport")] [assembly: AssemblyTitle("KeepItemsOnTeleport")] [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.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; } } [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 KeepItemsOnTeleport { [HarmonyPatch(typeof(ShipTeleporter))] internal static class ShipTeleporterPatches { [HarmonyPatch("PressTeleportButtonClientRpc")] [HarmonyPostfix] private static void PressTeleportButtonClientRpcPostfix(ShipTeleporter __instance) { if (!((Object)(object)__instance == (Object)null) && !__instance.isInverseTeleporter) { PlayerControllerB val = StartOfRound.Instance?.mapScreen?.targetedPlayer; if (!((Object)(object)val == (Object)null)) { TeleportSession.Mark(val.playerClientId, isInverse: false); } } } [HarmonyPatch("TeleportPlayerOutWithInverseTeleporter")] [HarmonyPrefix] private static void InverseTeleportPrefix(int playerObj) { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && playerObj >= 0 && playerObj < instance.allPlayerScripts.Length) { PlayerControllerB val = instance.allPlayerScripts[playerObj]; if (!((Object)(object)val == (Object)null)) { TeleportSession.Mark(val.playerClientId, isInverse: true); } } } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class DropHeldItemsPatches { [HarmonyPatch("DropAllHeldItems")] [HarmonyPrefix] [HarmonyPriority(0)] private static bool DropAllHeldItemsPrefix(PlayerControllerB __instance, bool disconnecting) { return !ShouldKeepItems(__instance, disconnecting, "DropAllHeldItems"); } [HarmonyPatch("DropAllHeldItemsAndSync")] [HarmonyPrefix] [HarmonyPriority(0)] private static bool DropAllHeldItemsAndSyncPrefix(PlayerControllerB __instance) { return !ShouldKeepItems(__instance, disconnecting: false, "DropAllHeldItemsAndSync"); } [HarmonyPatch("DropAllHeldItemsAndSyncNonexact")] [HarmonyPrefix] [HarmonyPriority(0)] private static bool DropAllHeldItemsAndSyncNonexactPrefix(PlayerControllerB __instance) { return !ShouldKeepItems(__instance, disconnecting: false, "DropAllHeldItemsAndSyncNonexact"); } private static bool ShouldKeepItems(PlayerControllerB player, bool disconnecting, string source) { if (Plugin.Enabled == null || !Plugin.Enabled.Value) { return false; } if ((Object)(object)player == (Object)null) { return false; } if (disconnecting || player.isPlayerDead) { return false; } bool isInverse; bool flag = TeleportSession.TryGet(player.playerClientId, out isInverse); if (!flag && player.teleportedLastFrame) { isInverse = GuessInverseFromPlayer(player); flag = true; } if (!flag) { return false; } if (!(isInverse ? Plugin.KeepOnInverseTeleporter.Value : Plugin.KeepOnNormalTeleporter.Value)) { TeleportSession.Clear(player.playerClientId); return false; } TeleportSession.Clear(player.playerClientId); Plugin.V($"Kept items on teleport ({source}) player={player.playerClientId} inverse={isInverse}"); return true; } private static bool GuessInverseFromPlayer(PlayerControllerB player) { try { if (player.isInHangarShipRoom) { return true; } } catch { } return false; } } [BepInPlugin("com.benhough.lethal.KeepItemsOnTeleport", "KeepItemsOnTeleport", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string ModGuid = "com.benhough.lethal.KeepItemsOnTeleport"; public const string ModName = "KeepItemsOnTeleport"; public const string ModVersion = "1.0.0"; private readonly Harmony _harmony = new Harmony("com.benhough.lethal.KeepItemsOnTeleport"); internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static ConfigEntry Enabled { get; private set; } internal static ConfigEntry KeepOnNormalTeleporter { get; private set; } internal static ConfigEntry KeepOnInverseTeleporter { get; private set; } internal static ConfigEntry Verbose { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master toggle — keep inventory when ship-teleported."); KeepOnNormalTeleporter = ((BaseUnityPlugin)this).Config.Bind("General", "KeepOnNormalTeleporter", true, "Keep items when beamed back to the ship (normal teleporter)."); KeepOnInverseTeleporter = ((BaseUnityPlugin)this).Config.Bind("General", "KeepOnInverseTeleporter", true, "Keep items when beamed into the facility (inverse teleporter)."); Verbose = ((BaseUnityPlugin)this).Config.Bind("General", "VerboseLogging", false, "Log when a teleport drop is skipped."); try { _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)"KeepItemsOnTeleport v1.0.0 loaded."); } catch (Exception arg) { Log.LogError((object)$"Harmony patch failed: {arg}"); } } internal static void V(string msg) { if (Verbose != null && Verbose.Value) { Log.LogInfo((object)msg); } } } internal static class PluginInfo { public const string PLUGIN_GUID = "com.benhough.lethal.KeepItemsOnTeleport"; public const string PLUGIN_NAME = "KeepItemsOnTeleport"; public const string PLUGIN_VERSION = "1.0.0"; } internal static class TeleportSession { private static readonly Dictionary InverseByPlayer = new Dictionary(); internal static void Mark(ulong playerClientId, bool isInverse) { InverseByPlayer[playerClientId] = isInverse; Plugin.V($"Marked teleport player={playerClientId} inverse={isInverse}"); } internal static bool TryGet(ulong playerClientId, out bool isInverse) { return InverseByPlayer.TryGetValue(playerClientId, out isInverse); } internal static void Clear(ulong playerClientId) { if (InverseByPlayer.Remove(playerClientId)) { Plugin.V($"Cleared teleport mark player={playerClientId}"); } } } }