Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SelfReviveUpgrade v1.0.0
SelfReviveUpgrade.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; 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 ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using REPOLib.Modules; using REPOLib.Objects.Sdk; using UnityEngine; [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: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("McHorse")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+66113413f87d85f7a794401f134f1527319ecedb")] [assembly: AssemblyProduct("SelfReviveUpgrade")] [assembly: AssemblyTitle("SelfReviveUpgrade")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 SelfReviveUpgrade { internal static class SelfReviveRuntime { private struct CachedEquipEntry { internal int SpotIndex; internal int ItemPhotonViewId; internal CachedEquipEntry(int spotIndex, int itemPhotonViewId) { SpotIndex = spotIndex; ItemPhotonViewId = itemPhotonViewId; } } private static readonly Dictionary<string, float> PendingReviveBySteamId = new Dictionary<string, float>(); private static readonly Dictionary<string, float> PostReviveTruckSnapUntilBySteamId = new Dictionary<string, float>(); private static readonly HashSet<string> SuppressUnequipBySteamId = new HashSet<string>(); private static readonly Dictionary<string, List<CachedEquipEntry>> CachedEquipsBySteamId = new Dictionary<string, List<CachedEquipEntry>>(); private static readonly NetworkedEvent ReviveRequestEvent = new NetworkedEvent("SelfReviveUpgrade_ReviveRequest", (Action<EventData>)HandleReviveRequestEvent); internal static Item? RegisteredShopItem { get; set; } internal static bool ShouldSuppressUnequip(PlayerAvatar? avatar) { if ((Object)(object)avatar == (Object)null) { return false; } return SuppressUnequipBySteamId.Contains(avatar.steamID); } internal static void TryArmRevive(PlayerAvatar? avatar) { PlayerUpgrade val = default(PlayerUpgrade); if (!((Object)(object)avatar == (Object)null) && avatar.isLocal && avatar.deadSet && Upgrades.TryGetUpgrade("SelfRevive", ref val) && val.GetLevel(avatar) > 0) { string steamID = avatar.steamID; if (!PendingReviveBySteamId.ContainsKey(steamID)) { SuppressUnequipBySteamId.Add(steamID); PendingReviveBySteamId[steamID] = Time.time + Mathf.Max(0.1f, SelfReviveUpgrade.ReviveDelaySeconds.Value); LogDebug("Armed self revive for " + steamID + "."); } } } internal static void ClearReviveState(string steamId) { PendingReviveBySteamId.Remove(steamId); SuppressUnequipBySteamId.Remove(steamId); } internal static bool WasPendingSelfRevive(string steamId) { return PendingReviveBySteamId.ContainsKey(steamId); } internal static void CaptureEquippedItems(PlayerAvatar avatar) { if (!avatar.isLocal || (Object)(object)Inventory.instance == (Object)null) { return; } List<InventorySpot> list = TraverseInventorySpots(); if (list == null) { return; } List<CachedEquipEntry> list2 = new List<CachedEquipEntry>(); foreach (InventorySpot item in list) { if (!((Object)(object)item == (Object)null) && item.IsOccupied() && !((Object)(object)item.CurrentItem == (Object)null)) { PhotonView component = ((Component)item.CurrentItem).GetComponent<PhotonView>(); if (!((Object)(object)component == (Object)null)) { list2.Add(new CachedEquipEntry(item.inventorySpotIndex, component.ViewID)); } } } if (list2.Count > 0) { CachedEquipsBySteamId[avatar.steamID] = list2; } } internal static void Update() { ProcessPostReviveTruckSnap(); if (PendingReviveBySteamId.Count == 0) { return; } PlayerAvatar val = PlayerController.instance?.playerAvatarScript; if ((Object)(object)val == (Object)null) { return; } string steamID = val.steamID; if (PendingReviveBySteamId.TryGetValue(steamID, out var value) && !(Time.time < value)) { if (GameManager.Multiplayer() && !PhotonNetwork.IsMasterClient) { RequestMasterRevive(val.steamID); } else { CommitRevive(val.steamID); } } } private static void RequestMasterRevive(string steamId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0014: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) Hashtable val = new Hashtable(); ((Dictionary<object, object>)val).Add((object)"SteamId", (object)steamId); Hashtable val2 = val; ReviveRequestEvent.RaiseEvent((object)val2, NetworkingEvents.RaiseMasterClient, SendOptions.SendReliable); } private static void HandleReviveRequestEvent(EventData eventData) { if (PhotonNetwork.IsMasterClient) { object customData = eventData.CustomData; Hashtable val = (Hashtable)((customData is Hashtable) ? customData : null); if (val != null && ((Dictionary<object, object>)(object)val).ContainsKey((object)"SteamId")) { string steamId = (string)val[(object)"SteamId"]; CommitRevive(steamId); } } } private static void CommitRevive(string steamId) { PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(steamId); if ((Object)(object)val == (Object)null) { ClearReviveState(steamId); return; } PlayerUpgrade val2 = default(PlayerUpgrade); if (!Upgrades.TryGetUpgrade("SelfRevive", ref val2)) { ClearReviveState(steamId); return; } if (val2.GetLevel(steamId) <= 0) { ClearReviveState(steamId); return; } if (!val.deadSet) { ClearReviveState(steamId); return; } val2.RemoveLevel(steamId, 1); MoveDeathHeadToTruck(val); val.Revive(true); if (!val.isLocal) { ClearReviveState(steamId); } LogDebug("Committed self revive for " + steamId + "."); } private static void MoveDeathHeadToTruck(PlayerAvatar avatar) { //IL_0042: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) PlayerDeathHead playerDeathHead = avatar.playerDeathHead; TruckSafetySpawnPoint instance = TruckSafetySpawnPoint.instance; Transform val = ((instance != null) ? ((Component)instance).transform : null); if (!((Object)(object)playerDeathHead == (Object)null) && !((Object)(object)val == (Object)null)) { playerDeathHead.OverridePositionRotationReset(); ((Component)playerDeathHead).transform.SetPositionAndRotation(val.position, val.rotation); PhysGrabObject value = Traverse.Create((object)playerDeathHead).Field("physGrabObject").GetValue<PhysGrabObject>(); if (value != null) { value.Teleport(val.position, val.rotation); } } } internal static void ApplyConfiguredHealth(PlayerAvatar avatar) { int maxHealth = avatar.playerHealth.maxHealth; int num = Mathf.Clamp(Mathf.RoundToInt((float)maxHealth * (SelfReviveUpgrade.ReviveHealthPercent.Value / 100f)), 1, maxHealth); int num2 = Mathf.Max(0, num - avatar.playerHealth.health); if (num2 > 0) { avatar.playerHealth.HealOther(num2, true); } } internal static void ApplyReviveImmunity(PlayerAvatar avatar) { float num = Mathf.Max(0f, SelfReviveUpgrade.ReviveImmunitySeconds.Value); if (!(num <= 0f)) { avatar.playerHealth.InvincibleSet(num); avatar.FallDamageResetSet(num); } } internal static void SnapLocalAvatarToTruck(PlayerAvatar avatar) { //IL_002b: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) TruckSafetySpawnPoint instance = TruckSafetySpawnPoint.instance; Transform val = ((instance != null) ? ((Component)instance).transform : null); if ((Object)(object)val == (Object)null) { return; } ((Component)avatar).transform.SetPositionAndRotation(val.position, val.rotation); avatar.clientPositionCurrent = val.position; avatar.clientPosition = val.position; if ((Object)(object)avatar.playerTransform != (Object)null) { avatar.playerTransform.SetPositionAndRotation(val.position, val.rotation); } if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } PlayerTumble tumble = avatar.tumble; if (tumble != null) { PhysGrabObject physGrabObject = tumble.physGrabObject; if (physGrabObject != null) { physGrabObject.Teleport(val.position, val.rotation); } } } internal static void ArmPostReviveTruckSnap(PlayerAvatar avatar) { if (avatar.isLocal) { PostReviveTruckSnapUntilBySteamId[avatar.steamID] = Time.time + 0.5f; SnapLocalAvatarToTruck(avatar); } } private static void ProcessPostReviveTruckSnap() { PlayerAvatar val = PlayerController.instance?.playerAvatarScript; if ((Object)(object)val == (Object)null) { return; } string steamID = val.steamID; if (PostReviveTruckSnapUntilBySteamId.TryGetValue(steamID, out var value)) { if (Time.time > value || val.deadSet) { PostReviveTruckSnapUntilBySteamId.Remove(steamID); } else { SnapLocalAvatarToTruck(val); } } } internal static void RestoreEquippedItems(PlayerAvatar avatar) { if (!avatar.isLocal || !CachedEquipsBySteamId.TryGetValue(avatar.steamID, out List<CachedEquipEntry> value)) { return; } Inventory instance = Inventory.instance; if ((Object)(object)instance == (Object)null) { return; } int localPhysGrabberPhotonViewId = GetLocalPhysGrabberPhotonViewId(instance); foreach (CachedEquipEntry item in value) { InventorySpot spotByIndex = instance.GetSpotByIndex(item.SpotIndex); if (!((Object)(object)spotByIndex == (Object)null) && !spotByIndex.IsOccupied()) { PhotonView val = PhotonView.Find(item.ItemPhotonViewId); ItemEquippable val2 = ((val != null) ? ((Component)val).GetComponent<ItemEquippable>() : null); if (!((Object)(object)val2 == (Object)null)) { val2.RequestEquip(item.SpotIndex, localPhysGrabberPhotonViewId); } } } CachedEquipsBySteamId.Remove(avatar.steamID); } private static List<InventorySpot>? TraverseInventorySpots() { return Traverse.Create((object)Inventory.instance).Field("inventorySpots").GetValue<List<InventorySpot>>(); } private static int GetLocalPhysGrabberPhotonViewId(Inventory inventory) { PhysGrabber value = Traverse.Create((object)inventory).Field("physGrabber").GetValue<PhysGrabber>(); int? obj; if (value == null) { obj = null; } else { PhotonView photonView = value.photonView; obj = ((photonView != null) ? new int?(photonView.ViewID) : null); } int? num = obj; return num.GetValueOrDefault(-1); } private static void LogDebug(string message) { if (SelfReviveUpgrade.DebugLogging.Value) { SelfReviveUpgrade.Logger.LogInfo((object)message); } } } [BepInPlugin("McHorse.SelfReviveUpgrade", "SelfReviveUpgrade", "1.0")] public class SelfReviveUpgrade : BaseUnityPlugin { private static AssetBundle? _embeddedBundle; internal const string ReviveUpgradeId = "SelfRevive"; internal static SelfReviveUpgrade Instance { get; private set; } internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } internal static ConfigEntry<float> ReviveHealthPercent { get; private set; } internal static ConfigEntry<float> ReviveDelaySeconds { get; private set; } internal static ConfigEntry<float> ReviveImmunitySeconds { get; private set; } internal static ConfigEntry<bool> DebugLogging { get; private set; } private void Awake() { Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; BindConfig(); RegisterBundledItemAndUpgrade(); Patch(); Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); } internal void Patch() { //IL_001a: 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_0021: Expected O, but got Unknown //IL_0026: Expected O, but got Unknown if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { SelfReviveRuntime.Update(); } private void BindConfig() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown ReviveHealthPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Revive", "HealthPercent", 35f, new ConfigDescription("Health percent restored after self revive.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); ReviveDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Revive", "DelaySeconds", 1f, new ConfigDescription("Delay after death before self revive triggers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>())); ReviveImmunitySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Revive", "ImmunitySeconds", 3f, new ConfigDescription("Damage and fall immunity duration right after self revive.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableVerboseLogs", false, "Enable extra debug logs."); } private void RegisterBundledItemAndUpgrade() { byte[] array = ReadEmbeddedBundleBytes(); if (array == null || array.Length == 0) { Logger.LogError((object)"Embedded bundle 'selfreviveupgrade' not found or empty."); return; } _embeddedBundle = AssetBundle.LoadFromMemory(array); if ((Object)(object)_embeddedBundle == (Object)null) { Logger.LogError((object)"AssetBundle.LoadFromMemory failed."); return; } ItemContent val = _embeddedBundle.LoadAsset<ItemContent>("Item Content"); if ((Object)(object)val == (Object)null) { Logger.LogError((object)"Could not load ItemContent from bundle at path: Item Content"); return; } Items.RegisterItem(val); Item val2 = val.Prefab?.item; if ((Object)(object)val2 == (Object)null) { Logger.LogError((object)"ItemContent has no Item on prefab."); return; } val2.maxPurchase = false; val2.value.valueMin = 12500f; val2.value.valueMax = 17500f; SelfReviveRuntime.RegisteredShopItem = val2; if (Upgrades.RegisterUpgrade("SelfRevive", val2, (Action<PlayerAvatar, int>)null, (Action<PlayerAvatar, int>)null) == null) { Logger.LogError((object)"Failed to register upgrade (ID may already be registered)."); } else { Logger.LogInfo((object)("Registered item '" + val2.itemName + "' and upgrade 'SelfRevive'.")); } } private static byte[]? ReadEmbeddedBundleBytes() { Assembly assembly = typeof(SelfReviveUpgrade).Assembly; string text = assembly.GetManifestResourceNames().FirstOrDefault((string n) => n.EndsWith("selfreviveupgrade", StringComparison.OrdinalIgnoreCase)); if (text == null) { return null; } using Stream stream = assembly.GetManifestResourceStream(text); if (stream == null) { return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); return memoryStream.ToArray(); } } } namespace SelfReviveUpgrade.Patches { [HarmonyPatch] internal static class DeathPatches { [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathDone")] [HarmonyPrefix] private static void PlayerDeathDonePrefix(PlayerAvatar __instance) { SelfReviveRuntime.CaptureEquippedItems(__instance); } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathDone")] [HarmonyPostfix] private static void PlayerDeathDonePostfix(PlayerAvatar __instance) { SelfReviveRuntime.TryArmRevive(__instance); } [HarmonyPatch(typeof(Inventory), "ForceUnequip")] [HarmonyPrefix] private static bool ForceUnequipPrefix(Inventory __instance) { PlayerAvatar value = Traverse.Create((object)__instance).Field("playerAvatar").GetValue<PlayerAvatar>(); return !SelfReviveRuntime.ShouldSuppressUnequip(value); } [HarmonyPatch(typeof(PlayerAvatar), "ReviveRPC")] [HarmonyPostfix] private static void ReviveRpcPostfix(PlayerAvatar __instance) { if (__instance.isLocal && SelfReviveRuntime.WasPendingSelfRevive(__instance.steamID)) { SelfReviveRuntime.ArmPostReviveTruckSnap(__instance); SelfReviveRuntime.ApplyReviveImmunity(__instance); SelfReviveRuntime.ApplyConfiguredHealth(__instance); SelfReviveRuntime.RestoreEquippedItems(__instance); SelfReviveRuntime.ClearReviveState(__instance.steamID); } } } }