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 ReturnByDeath v0.2.0
ReturnByDeath.dll
Decompiled 2 days 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.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using REPOLib.Modules; using ReturnByDeath.Domain; using UnityEngine; using UnityEngine.Events; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ReturnByDeath")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.0.0")] [assembly: AssemblyInformationalVersion("0.2.0")] [assembly: AssemblyProduct("ReturnByDeath")] [assembly: AssemblyTitle("ReturnByDeath")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.2.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 ReturnByDeath { internal static class CerealVisual { private static Texture2D artwork; internal static void Apply(GameObject prefab) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown if ((Object)(object)artwork == (Object)null) { using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ReturnByDeath.Cereal.png")) { if (stream == null) { throw new FileNotFoundException("Embedded cereal artwork missing"); } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); artwork = new Texture2D(2, 2, (TextureFormat)4, true); if (!ImageConversion.LoadImage(artwork, memoryStream.ToArray())) { throw new IOException("Invalid cereal texture"); } } ((Object)artwork).name = "Return By Death Cereal Albedo"; ((Texture)artwork).wrapMode = (TextureWrapMode)1; ((Texture)artwork).filterMode = (FilterMode)1; Object.DontDestroyOnLoad((Object)(object)artwork); } Transform obj = prefab.transform.Find("Mesh"); if ((Object)(object)obj == (Object)null) { throw new MissingComponentException("Vanilla cereal mesh not found"); } MeshRenderer[] componentsInChildren = ((Component)obj).GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val in componentsInChildren) { Material[] sharedMaterials = ((Renderer)val).sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { sharedMaterials[j] = new Material(sharedMaterials[j]); if (sharedMaterials[j].HasProperty("_MainTex")) { sharedMaterials[j].SetTexture("_MainTex", (Texture)(object)artwork); } if (sharedMaterials[j].HasProperty("_BaseMap")) { sharedMaterials[j].SetTexture("_BaseMap", (Texture)(object)artwork); } if (sharedMaterials[j].HasProperty("_BaseColorMap")) { sharedMaterials[j].SetTexture("_BaseColorMap", (Texture)(object)artwork); } } ((Renderer)val).sharedMaterials = sharedMaterials; } } } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")] internal static class DeathPatch { private static void Postfix(PlayerAvatar __instance) { Plugin.Runtime?.ObserveDeath(__instance); } } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathDone")] internal static class HeadProtectionPatch { private static void Prefix(PlayerAvatar __instance) { Plugin.Runtime?.ProtectHead(__instance); } } [HarmonyPatch(typeof(PlayerAvatar), "ReviveRPC")] internal static class ReturnedPatch { private static void Postfix(PlayerAvatar __instance) { Plugin.Runtime?.AfterRevive(__instance); } } [HarmonyPatch(typeof(RunManager), "AllPlayersDeadSet")] internal static class HoldGameOverPatch { private static bool Prefix(bool _set) { if (_set && Plugin.Runtime != null) { return !Plugin.Runtime.HoldFailure(); } return true; } } [HarmonyPatch(typeof(RunManager), "ChangeLevel")] internal static class LevelPatch { private static bool Prefix(bool _levelFailed) { if (_levelFailed && Plugin.Runtime != null && Plugin.Runtime.HoldFailure()) { return false; } Plugin.Runtime?.LevelChanging(); return true; } } [HarmonyPatch(typeof(PlayerAvatar), "Start")] internal static class LevelReadyPatch { private static void Postfix() { Plugin.Runtime?.LevelReady(); } } [HarmonyPatch(typeof(StatsManager), "RunStartStats")] internal static class NewRunPatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { Plugin.Runtime?.NewRun(); } } [BepInPlugin("sqyan.ReturnByDeath", "Return By Death", "0.2.0")] [BepInDependency("REPOLib", "4.3.0")] public sealed class Plugin : BaseUnityPlugin { public const string UpgradeId = "ReturnByDeath"; internal static PlayerUpgrade Upgrade; internal static RevivalRuntime Runtime; internal static Plugin Instance; private Harmony harmony; private float nextError; private void Awake() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown Instance = this; Log.Source = ((BaseUnityPlugin)this).Logger; ReturnAudio.Initialize(((Component)this).gameObject); Runtime = new RevivalRuntime(); harmony = new Harmony("sqyan.ReturnByDeath"); harmony.PatchAll(typeof(Plugin).Assembly); Log.Info("Return By Death 0.2.0 loaded. Personal permanent upgrade; one return per level; tiers 1/50/100 HP; $25,000 regular minimum."); } private void Update() { try { RevivalShop.Tick(); Runtime.Tick(); } catch (Exception ex) { if (Time.unscaledTime >= nextError) { nextError = Time.unscaledTime + 5f; Log.Error(ex.ToString()); } } } private void OnDestroy() { Runtime?.Reset(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Instance = null; } } internal static class Log { internal static ManualLogSource Source; internal static void Info(string value) { ManualLogSource source = Source; if (source != null) { source.LogInfo((object)value); } } internal static void Warn(string value) { ManualLogSource source = Source; if (source != null) { source.LogWarning((object)value); } } internal static void Error(string value) { ManualLogSource source = Source; if (source != null) { source.LogError((object)value); } } } internal static class ReturnAudio { private static AudioSource source; internal static void Initialize(GameObject owner) { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ReturnByDeath.Return.wav"); if (stream == null) { throw new FileNotFoundException("Return sound missing"); } BinaryReader reader = new BinaryReader(stream); try { if (Four() != "RIFF") { throw new InvalidDataException("Expected RIFF"); } reader.ReadInt32(); if (Four() != "WAVE") { throw new InvalidDataException("Expected WAVE"); } int num = 1; int num2 = 44100; int num3 = 16; byte[] array = null; while (stream.Position + 8 <= stream.Length) { string text = Four(); int num4 = reader.ReadInt32(); long position = stream.Position + num4 + (num4 & 1); if (text == "fmt ") { if (reader.ReadInt16() != 1) { throw new InvalidDataException("PCM WAV required"); } num = reader.ReadInt16(); num2 = reader.ReadInt32(); reader.ReadInt32(); reader.ReadInt16(); num3 = reader.ReadInt16(); } else if (text == "data") { array = reader.ReadBytes(num4); } stream.Position = position; } if (array == null || num3 != 16) { throw new InvalidDataException("16-bit WAV data required"); } float[] array2 = new float[array.Length / 2]; for (int i = 0; i < array2.Length; i++) { array2[i] = (float)BitConverter.ToInt16(array, i * 2) / 32768f; } AudioClip val = AudioClip.Create("Return By Death", array2.Length / num, num, num2, false); val.SetData(array2, 0); source = owner.AddComponent<AudioSource>(); source.clip = val; source.spatialBlend = 0f; source.volume = 0.65f; source.playOnAwake = false; } finally { if (reader != null) { ((IDisposable)reader).Dispose(); } } string Four() { return new string(reader.ReadChars(4)); } } internal static void Play() { if ((Object)(object)source != (Object)null) { source.Stop(); source.Play(); } } } internal sealed class RevivalRuntime { private sealed class Pending { internal PlayerAvatar Player; internal int Level; internal float Due; internal float Deadline; } private const string UsageKey = "returnByDeathUsedOnLevel"; private readonly ReturnCharge charges = new ReturnCharge(); private readonly Dictionary<string, Pending> pending = new Dictionary<string, Pending>(); private readonly Dictionary<string, Vector3> armed = new Dictionary<string, Vector3>(); private readonly Dictionary<string, float> snapUntil = new Dictionary<string, float>(); private readonly List<string> keys = new List<string>(); private readonly NetworkedEvent action; private bool transitioning; private int CurrentLevel { get { if (!((Object)(object)StatsManager.instance != (Object)null) || !StatsManager.instance.runStats.TryGetValue("level", out var value)) { return -1; } return value; } } private bool IsHost { get { if ((Object)(object)GameManager.instance != (Object)null) { return SemiFunc.IsMasterClientOrSingleplayer(); } return false; } } internal RevivalRuntime() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown action = new NetworkedEvent("Return By Death v1", (Action<EventData>)Receive); } private void BindUsage() { StatsManager instance = StatsManager.instance; if (!((Object)(object)instance == (Object)null)) { if (instance.dictionaryOfDictionaries.TryGetValue("returnByDeathUsedOnLevel", out var value)) { charges.UsedOnLevel = value; } else { instance.dictionaryOfDictionaries["returnByDeathUsedOnLevel"] = charges.UsedOnLevel; } } } internal void NewRun() { Reset(); charges.UsedOnLevel = new Dictionary<string, int>(); if ((Object)(object)StatsManager.instance != (Object)null) { StatsManager.instance.dictionaryOfDictionaries["returnByDeathUsedOnLevel"] = charges.UsedOnLevel; } } internal void ObserveDeath(PlayerAvatar player) { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) if (!IsHost || transitioning || (Object)(object)player == (Object)null || !player.deadSet || !SemiFunc.RunIsLevel() || CurrentLevel < 0) { return; } TruckSafetySpawnPoint instance = TruckSafetySpawnPoint.instance; if (!((Object)(object)instance == (Object)null) && Plugin.Upgrade != null) { BindUsage(); if (charges.TryReserve(player.steamID, CurrentLevel, Plugin.Upgrade.GetLevel(player) > 0)) { pending[player.steamID] = new Pending { Player = player, Level = CurrentLevel, Due = Time.unscaledTime + 0.75f, Deadline = Time.unscaledTime + 4f }; Dispatch("arm", player, ((Component)instance).transform.position); Log.Info("Return armed for an upgraded player; one charge reserved for this level."); } } } internal bool HoldFailure() { if (!IsHost) { return false; } foreach (Pending value in pending.Values) { if ((Object)(object)value.Player != (Object)null && value.Player.deadSet && Time.unscaledTime < value.Deadline) { return true; } } return false; } internal void ProtectHead(PlayerAvatar player) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player != (Object)null && armed.TryGetValue(player.steamID, out var value)) { player.playerAvatarCollision.deathHeadPosition = value + Vector3.up * 0.25f; } } internal void Tick() { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameManager.instance == (Object)null) { return; } BindUsage(); keys.Clear(); keys.AddRange(snapUntil.Keys); foreach (string key in keys) { PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(key); Vector3 value; if ((Object)(object)val == (Object)null || Time.unscaledTime > snapUntil[key]) { snapUntil.Remove(key); armed.Remove(key); } else if (val.isLocal && !val.deadSet && armed.TryGetValue(key, out value)) { SnapLocal(val, value); } } if (!IsHost || transitioning) { return; } keys.Clear(); keys.AddRange(this.pending.Keys); foreach (string key2 in keys) { Pending pending = this.pending[key2]; PlayerAvatar player = pending.Player; if ((Object)(object)player == (Object)null || !player.deadSet || pending.Level != CurrentLevel || Time.unscaledTime >= pending.Deadline) { charges.Refund(key2, pending.Level); this.pending.Remove(key2); if ((Object)(object)player != (Object)null) { Dispatch("cancel", player, Vector3.zero); } } else if (!(Time.unscaledTime < pending.Due) && player.isDisabled && !((Object)(object)player.playerDeathHead == (Object)null) && !((Object)(object)TruckSafetySpawnPoint.instance == (Object)null)) { Vector3 position = ((Component)TruckSafetySpawnPoint.instance).transform.position; Dispatch("prepare", player, position); player.Revive(false); charges.Complete(key2); this.pending.Remove(key2); Log.Info("Return committed in the truck; tier-based health applied; charge spent for this level."); } } } private void Dispatch(string phase, PlayerAvatar player, Vector3 position) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //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_0053: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown Apply(phase, player, position); if (SemiFunc.IsMultiplayer()) { NetworkedEvent obj = action; Hashtable val = new Hashtable(); ((Dictionary<object, object>)val).Add((object)"phase", (object)phase); ((Dictionary<object, object>)val).Add((object)"view", (object)player.photonView.ViewID); ((Dictionary<object, object>)val).Add((object)"point", (object)position); obj.RaiseEvent((object)val, NetworkingEvents.RaiseOthers, SendOptions.SendReliable); } } private void Receive(EventData message) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if (PhotonNetwork.MasterClient == null || message.Sender != PhotonNetwork.MasterClient.ActorNumber) { return; } object customData = message.CustomData; Hashtable val = (Hashtable)((customData is Hashtable) ? customData : null); if (val != null && val[(object)"phase"] is string phase && val[(object)"view"] is int num && val[(object)"point"] is Vector3 point) { PhotonView obj = PhotonView.Find(num); PlayerAvatar val2 = ((obj != null) ? ((Component)obj).GetComponent<PlayerAvatar>() : null); if ((Object)(object)val2 != (Object)null) { Apply(phase, val2, point); } } } private void Apply(string phase, PlayerAvatar player, Vector3 point) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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) string steamID = player.steamID; if (phase == "cancel") { armed.Remove(steamID); snapUntil.Remove(steamID); return; } armed[steamID] = point; if (!(phase != "prepare") && !((Object)(object)player.playerDeathHead == (Object)null)) { PlayerDeathHead playerDeathHead = player.playerDeathHead; playerDeathHead.OverridePositionRotationReset(); ((Component)playerDeathHead).transform.position = point + Vector3.up * 0.25f; PhysGrabObject physGrabObject = playerDeathHead.physGrabObject; if (IsHost) { physGrabObject.Teleport(((Component)playerDeathHead).transform.position, Quaternion.identity); } physGrabObject.centerPoint = ((Component)playerDeathHead).transform.position; } } internal void AfterRevive(PlayerAvatar player) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || player.deadSet || !armed.TryGetValue(player.steamID, out var value)) { return; } if (player.isLocal) { SnapLocal(player, value); int num = PurchaseRules.RevivalHealth(Plugin.Upgrade.GetLevel(player), player.playerHealth.maxHealth); player.playerHealth.health = num; StatsManager.instance.SetPlayerHealth(player.steamID, num, false); if (SemiFunc.IsMultiplayer()) { ((Component)player.playerHealth).GetComponent<PhotonView>().RPC("UpdateHealthRPC", (RpcTarget)1, new object[4] { num, player.playerHealth.maxHealth, true, false }); } ReturnAudio.Play(); snapUntil[player.steamID] = Time.unscaledTime + 0.6f; } else { armed.Remove(player.steamID); } } private static void SnapLocal(PlayerAvatar player, Vector3 point) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) ((Component)player).transform.position = point; player.clientPosition = point; player.clientPositionCurrent = point; if ((Object)(object)player.playerTransform != (Object)null) { player.playerTransform.position = point; } if ((Object)(object)PlayerController.instance != (Object)null) { ((Component)PlayerController.instance).transform.position = point; PlayerController.instance.rb.linearVelocity = Vector3.zero; } player.FallDamageResetSet(1f); } internal void LevelChanging() { transitioning = true; Reset(); transitioning = true; } internal void Reset() { pending.Clear(); armed.Clear(); snapUntil.Clear(); charges.ClearPending(); transitioning = false; } internal void LevelReady() { transitioning = false; } } internal static class RevivalShop { internal static Item Item; private static GameObject storage; private static float nextRegistrationAttempt; internal static void Tick() { if ((Object)(object)Item != (Object)null) { BindSavedState(); } if ((Object)(object)Item == (Object)null && Time.unscaledTime >= nextRegistrationAttempt) { nextRegistrationAttempt = Time.unscaledTime + 5f; try { Register(); } catch (Exception ex) { Log.Error("Return By Death deferred registration failed: " + ex); } } } internal static void Register() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Invalid comparison between Unknown and I4 //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Expected O, but got Unknown if ((Object)(object)Item != (Object)null) { BindSavedState(); return; } Item val = null; GameObject val2 = null; StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.itemDictionary.Count == 0) { return; } Item[] array = instance.itemDictionary.Values.OrderBy<Item, string>((Item x) => ((Object)x).name, StringComparer.Ordinal).ToArray(); Item[] array2 = array; foreach (Item val3 in array2) { if ((int)val3.itemType == 3 && val3.prefab != null) { GameObject prefab = ((PrefabRef<GameObject>)(object)val3.prefab).Prefab; if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab.GetComponent<ItemUpgradePlayerGrabStrength>() == (Object)null)) { val = val3; val2 = prefab; break; } } } if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("No upgrade prefab with ItemUpgradePlayerGrabStrength found among " + array.Count() + " item assets"); } Log.Info("Return By Death template resolved by component: " + ((Object)val).name + " -> " + ((Object)val2).name); storage = new GameObject("ReturnByDeath Prefab Storage"); storage.SetActive(false); Object.DontDestroyOnLoad((Object)(object)storage); GameObject obj = Object.Instantiate<GameObject>(val2, storage.transform, false); ((Object)obj).name = "Item Upgrade Return By Death"; obj.SetActive(true); ItemAttributes component = obj.GetComponent<ItemAttributes>(); ItemUpgradePlayerGrabStrength component2 = obj.GetComponent<ItemUpgradePlayerGrabStrength>(); if ((Object)(object)component2 != (Object)null) { Object.DestroyImmediate((Object)(object)component2); } Item val4 = Object.Instantiate<Item>(val); ((Object)val4).name = "Item Upgrade Return By Death"; val4.itemName = "RETURN BY DEATH"; val4.itemNameLocalized = null; val4.description = "Return By Death: once per level, return to the truck after death. Three personal tiers: 1 / 50 / 100 HP."; val4.value = ScriptableObject.CreateInstance<Value>(); val4.value.valueMin = 6250f; val4.value.valueMax = 7500f; val4.maxAmountInShop = 1; val4.maxAmount = 6; val4.maxPurchase = false; component.item = val4; obj.AddComponent<RevivalConsume>(); obj.GetComponent<ItemUpgrade>().upgradeEvent = new UnityEvent(); CerealVisual.Apply(obj); Plugin.Upgrade = Upgrades.GetUpgrade("ReturnByDeath") ?? Upgrades.RegisterUpgrade("ReturnByDeath", val4, (Action<PlayerAvatar, int>)null, (Action<PlayerAvatar, int>)null); if (Plugin.Upgrade == null || Items.RegisterItem(component) == null) { throw new InvalidOperationException("REPOLib rejected Return By Death registration"); } Item = val4; BindSavedState(); Log.Info("Return By Death registered: personal cereal upgrade; one return per level; three health tiers; regular price floor $25,000."); } private static void BindSavedState() { StatsManager instance = StatsManager.instance; if (!((Object)(object)instance == (Object)null) && Plugin.Upgrade != null) { string key = "playerUpgradeReturnByDeath"; if (instance.dictionaryOfDictionaries.TryGetValue(key, out var value)) { Plugin.Upgrade.PlayerDictionary = value; } else { instance.dictionaryOfDictionaries[key] = Plugin.Upgrade.PlayerDictionary; } } } } public sealed class RevivalConsume : MonoBehaviour { private bool consumed; private void Awake() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown ItemUpgrade component = ((Component)this).GetComponent<ItemUpgrade>(); component.upgradeEvent = new UnityEvent(); component.upgradeEvent.AddListener(new UnityAction(Consume)); } public void Consume() { if (consumed) { return; } PlayerAvatar val = SemiFunc.PlayerAvatarGetFromPhotonID(((Component)this).GetComponent<ItemToggle>().playerTogglePhotonID); if (!((Object)(object)val == (Object)null) && val.isLocal && Plugin.Upgrade != null) { consumed = true; if (Plugin.Upgrade.GetLevel(val) < 3) { Plugin.Upgrade.AddLevel(val, 1); ReturnAudio.Play(); } } } } [HarmonyPatch(typeof(StatsManager), "RunStartStats")] internal static class RegisterRevivalPatch { [HarmonyPostfix] [HarmonyPriority(800)] private static void Postfix() { try { RevivalShop.Register(); } catch (Exception ex) { Log.Error("Return By Death registration failed: " + ex); } } } [HarmonyPatch(typeof(ShopManager), "GetAllItemsFromStatsManager")] internal static class RegisterRevivalBeforeShopPatch { [HarmonyPrefix] private static void Prefix() { try { RevivalShop.Register(); } catch (Exception ex) { Log.Error("Return By Death shop registration failed: " + ex); } } } [HarmonyPatch(typeof(ShopManager), "UpgradeValueGet")] internal static class RevivalPricePatch { [HarmonyPostfix] private static void Postfix(Item item, ref float __result) { if ((Object)(object)item == (Object)(object)RevivalShop.Item && (Object)(object)item != (Object)null) { __result = Mathf.Max(25f, __result); } } } [HarmonyPatch(typeof(ItemToggle), "ToggleItem")] internal static class PreventDuplicatePurchasePatch { private static bool Prefix(ItemToggle __instance, bool toggle, int player) { if (!toggle || (Object)(object)((Component)__instance).GetComponent<RevivalConsume>() == (Object)null) { return true; } PlayerAvatar val = SemiFunc.PlayerAvatarGetFromPhotonID(player); if ((Object)(object)val == (Object)null || Plugin.Upgrade == null) { return true; } if (Plugin.Upgrade.GetLevel(val) < 3) { return true; } Log.Info("Return By Death is already owned; cereal box was not consumed."); return false; } } } namespace ReturnByDeath.Domain { public static class PurchaseRules { public const int MaxLevel = 3; public static int RevivalHealth(int level, int maxHealth) { int val = ((level >= 3) ? 100 : ((level != 2) ? 1 : 50)); return Math.Max(1, Math.Min(val, maxHealth)); } } public sealed class ReturnCharge { public Dictionary<string, int> UsedOnLevel = new Dictionary<string, int>(); private readonly HashSet<string> reserved = new HashSet<string>(); public bool TryReserve(string id, int level, bool owned) { if (!owned || string.IsNullOrEmpty(id) || reserved.Contains(id) || (UsedOnLevel.TryGetValue(id, out var value) && value == level)) { return false; } reserved.Add(id); UsedOnLevel[id] = level; return true; } public void Complete(string id) { reserved.Remove(id); } public void Refund(string id, int level) { if (reserved.Remove(id) && UsedOnLevel.TryGetValue(id, out var value) && value == level) { UsedOnLevel.Remove(id); } } public void ClearPending() { reserved.Clear(); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }