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 LowHealthChallenge v1.0.1
LowHealthChallenge.dll
Decompiled 3 months agousing 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 HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; 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("zabu")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("zabumod")] [assembly: AssemblyTitle("zabumod")] [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 REPOJP.LowHealthChallengeMod { [BepInPlugin("REPOJP.LowHealthChallenge", "LowHealthChallenge", "1.0.0")] public sealed class LowHealthChallengePlugin : BaseUnityPlugin { [HarmonyPatch(typeof(PlayerAvatar), "ChatMessageSendRPC")] private static class PlayerAvatar_ChatMessageSendRPC_Patch { private static void Postfix(PlayerAvatar __instance, string _message, PhotonMessageInfo _info) { if (Object.op_Implicit((Object)(object)__instance) && __instance.isLocal) { ExecuteCommand(_message); } } } [HarmonyPatch(typeof(PlayerAvatar), "AddToStatsManagerRPC")] private static class PlayerAvatar_AddToStatsManagerRPC_Patch { private static void Postfix(PlayerAvatar __instance) { if (Object.op_Implicit((Object)(object)Instance) && Instance.enableMod.Value && OverrideActive && IsHost() && Object.op_Implicit((Object)(object)__instance)) { ApplyFixedHealthToSinglePlayer(__instance); } } } [HarmonyPatch(typeof(ShopManager), "GetAllItemsFromStatsManager")] private static class ShopManager_GetAllItemsFromStatsManager_Patch { private static void Postfix(ShopManager __instance) { if (Object.op_Implicit((Object)(object)Instance) && Instance.enableMod.Value && Instance.blockHealthUpgradeInShop.Value && IsHost() && Object.op_Implicit((Object)(object)__instance)) { __instance.potentialItemUpgrades.RemoveAll(IsHealthUpgradeItem); } } } [HarmonyPatch(typeof(StatsManager), "Start")] private static class StatsManager_Start_Patch { private static void Postfix() { if (Object.op_Implicit((Object)(object)Instance) && Instance.enableMod.Value) { Instance.NormalizeConfigAndApply("StatsManagerStart"); } } } [HarmonyPatch(typeof(RunManager), "ChangeLevel")] private static class RunManager_ChangeLevel_Patch { private static void Postfix() { if (Object.op_Implicit((Object)(object)Instance) && Instance.enableMod.Value && IsHost()) { ((MonoBehaviour)Instance).StartCoroutine(Instance.DelayedReapplyAfterLevelChange()); } } } [CompilerGenerated] private sealed class <DelayedReapplyAfterLevelChange>d__45 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public LowHealthChallengePlugin <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedReapplyAfterLevelChange>d__45(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; case 1: <>1__state = -1; if (!<>4__this.enableMod.Value) { return false; } if (!OverrideActive) { return false; } if (!IsHost()) { return false; } <>4__this.RefreshHealthUpgradeItemsDisabledState(); ApplyFixedHealthToAllPlayers(); if (<>4__this.destroyExistingHealthUpgradeItems.Value) { <>4__this.DestroySpawnedHealthUpgradeItems(); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <DestroyHealthUpgradeLoop>d__35 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public LowHealthChallengePlugin <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DestroyHealthUpgradeLoop>d__35(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; if (<>4__this.enableMod.Value && IsHost() && <>4__this.destroyExistingHealthUpgradeItems.Value) { <>4__this.DestroySpawnedHealthUpgradeItems(); } break; } <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <EnforceLoop>d__34 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public LowHealthChallengePlugin <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnforceLoop>d__34(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; if (<>4__this.enableMod.Value && OverrideActive && IsHost()) { <>4__this.RefreshHealthUpgradeItemsDisabledState(); if (<>4__this.reapplyHealthOverrideIfChanged.Value) { ApplyFixedHealthToAllPlayers(); } } break; } <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string PluginGuid = "REPOJP.LowHealthChallenge"; public const string PluginName = "LowHealthChallenge"; public const string PluginVersion = "1.0.0"; internal static LowHealthChallengePlugin Instance; internal static ManualLogSource Log; private Harmony harmony; private ConfigEntry<bool> enableMod; private ConfigEntry<int> targetHealth; private ConfigEntry<bool> blockHealthUpgradeInShop; private ConfigEntry<bool> blockHealthUpgradeInWorld; private ConfigEntry<bool> destroyExistingHealthUpgradeItems; private ConfigEntry<bool> reapplyHealthOverrideIfChanged; private ConfigEntry<bool> logRoundedResult; private bool suppressTargetHealthEvent; private Coroutine enforceCoroutine; private Coroutine destroyHealthUpgradeCoroutine; internal static readonly int[] AllowedHealthValues = new int[6] { 0, 20, 40, 60, 80, 100 }; internal static bool OverrideActive; internal static int RoundedHealthValue = 40; internal static int MappedHealthUpgradeValue = -3; private void Awake() { //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; enableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableMod", true, "Enable or disable this mod"); targetHealth = ((BaseUnityPlugin)this).Config.Bind<int>("Health", "TargetHealth", 40, "Target health input 0-100. This value is always rewritten to the rounded value"); blockHealthUpgradeInShop = ((BaseUnityPlugin)this).Config.Bind<bool>("Health Upgrade Blocking", "BlockHealthUpgradeInShop", true, "Remove health upgrade from shop candidates"); blockHealthUpgradeInWorld = ((BaseUnityPlugin)this).Config.Bind<bool>("Health Upgrade Blocking", "BlockHealthUpgradeInWorld", true, "Disable health upgrade items in item dictionary"); destroyExistingHealthUpgradeItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Health Upgrade Blocking", "DestroyExistingHealthUpgradeItems", true, "Destroy spawned health upgrade items"); reapplyHealthOverrideIfChanged = ((BaseUnityPlugin)this).Config.Bind<bool>("Health Upgrade Blocking", "ReapplyHealthOverrideIfChanged", false, "Reapply fixed health if changed"); logRoundedResult = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogRoundedResult", true, "Log rounded result"); targetHealth.SettingChanged += OnTargetHealthSettingChanged; harmony = new Harmony("REPOJP.LowHealthChallenge"); harmony.PatchAll(); NormalizeConfigAndApply("Awake"); enforceCoroutine = ((MonoBehaviour)this).StartCoroutine(EnforceLoop()); destroyHealthUpgradeCoroutine = ((MonoBehaviour)this).StartCoroutine(DestroyHealthUpgradeLoop()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LowHealthChallenge 1.0.0 loaded"); } private void OnDestroy() { targetHealth.SettingChanged -= OnTargetHealthSettingChanged; if (harmony != null) { harmony.UnpatchSelf(); } } private void OnTargetHealthSettingChanged(object sender, EventArgs e) { if (!suppressTargetHealthEvent) { NormalizeConfigAndApply("ConfigChanged"); } } internal static bool IsHost() { return SemiFunc.IsMasterClientOrSingleplayer(); } internal static bool TryGetSteamId(PlayerAvatar avatar, out string steamId) { steamId = null; if (!Object.op_Implicit((Object)(object)avatar)) { return false; } if (!string.IsNullOrWhiteSpace(avatar.steamID)) { steamId = avatar.steamID; return true; } try { steamId = SemiFunc.PlayerGetSteamID(avatar); } catch { steamId = null; } return !string.IsNullOrWhiteSpace(steamId); } internal static int NormalizeHealthValue(int input) { int num = Mathf.Clamp(input, 0, 100); int num2 = AllowedHealthValues[0]; int num3 = Math.Abs(num - num2); for (int i = 1; i < AllowedHealthValues.Length; i++) { int num4 = AllowedHealthValues[i]; int num5 = Math.Abs(num - num4); if (num5 < num3) { num2 = num4; num3 = num5; } else if (num5 == num3 && num4 > num2) { num2 = num4; num3 = num5; } } return num2; } internal static int MapRoundedHealthToUpgradeValue(int roundedHealth) { return (roundedHealth - 100) / 20; } private void NormalizeConfigAndApply(string source) { if (!enableMod.Value) { OverrideActive = false; return; } int value = targetHealth.Value; int num = NormalizeHealthValue(value); int num2 = MapRoundedHealthToUpgradeValue(num); RoundedHealthValue = num; MappedHealthUpgradeValue = num2; OverrideActive = true; WriteRoundedHealthToConfigIfNeeded(num); if (logRoundedResult.Value) { Log.LogInfo((object)$"[{source}] Requested health: {value}"); Log.LogInfo((object)$"[{source}] Rounded health: {num}"); Log.LogInfo((object)$"[{source}] Mapped health upgrade: {num2}"); } RefreshHealthUpgradeItemsDisabledState(); if (IsHost()) { ApplyFixedHealthToAllPlayers(); } } private void WriteRoundedHealthToConfigIfNeeded(int rounded) { if (targetHealth.Value == rounded) { return; } suppressTargetHealthEvent = true; try { targetHealth.Value = rounded; ((BaseUnityPlugin)this).Config.Save(); } finally { suppressTargetHealthEvent = false; } } internal static void SetTargetHealthFromCommand(int requestedValue) { if (Object.op_Implicit((Object)(object)Instance)) { int num = NormalizeHealthValue(requestedValue); Instance.suppressTargetHealthEvent = true; try { Instance.targetHealth.Value = num; ((BaseUnityPlugin)Instance).Config.Save(); } finally { Instance.suppressTargetHealthEvent = false; } RoundedHealthValue = num; MappedHealthUpgradeValue = MapRoundedHealthToUpgradeValue(num); OverrideActive = true; if (Instance.logRoundedResult.Value) { Log.LogInfo((object)$"[Command] Requested health: {requestedValue}"); Log.LogInfo((object)$"[Command] Rounded health: {num}"); Log.LogInfo((object)$"[Command] Mapped health upgrade: {MappedHealthUpgradeValue}"); } Instance.RefreshHealthUpgradeItemsDisabledState(); if (IsHost()) { ApplyFixedHealthToAllPlayers(); } } } private static void ApplyFixedHealthToAllPlayers() { if (!OverrideActive || !IsHost() || !Object.op_Implicit((Object)(object)GameDirector.instance) || GameDirector.instance.PlayerList == null || !Object.op_Implicit((Object)(object)StatsManager.instance)) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { ApplyFixedHealthToSinglePlayer(player); } } private static void ApplyFixedHealthToSinglePlayer(PlayerAvatar avatar) { if (!OverrideActive || !Object.op_Implicit((Object)(object)avatar) || !Object.op_Implicit((Object)(object)StatsManager.instance) || !TryGetSteamId(avatar, out string steamId)) { return; } string text = avatar.playerName; if (string.IsNullOrWhiteSpace(text)) { try { text = SemiFunc.PlayerGetName(avatar); } catch { text = steamId; } } StatsManager.instance.PlayerAdd(steamId, text); SetPlayerUpgradeHealth(steamId, MappedHealthUpgradeValue); int playerHealth = StatsManager.instance.GetPlayerHealth(steamId); int num = ((playerHealth > RoundedHealthValue) ? RoundedHealthValue : playerHealth); num = Mathf.Clamp(num, 0, RoundedHealthValue); StatsManager.instance.SetPlayerHealth(steamId, num, true); ApplyImmediateHealthToAvatar(avatar, num, RoundedHealthValue); } private static void SetPlayerUpgradeHealth(string steamId, int value) { if (!StatsManager.instance.playerUpgradeHealth.ContainsKey(steamId)) { StatsManager.instance.playerUpgradeHealth.Add(steamId, value); } else { StatsManager.instance.playerUpgradeHealth[steamId] = value; } if (Object.op_Implicit((Object)(object)PunManager.instance)) { PunManager.instance.UpdateStat("playerUpgradeHealth", steamId, value); } } private static void ApplyImmediateHealthToAvatar(PlayerAvatar avatar, int currentHealth, int maxHealth) { if (Object.op_Implicit((Object)(object)avatar) && Object.op_Implicit((Object)(object)avatar.playerHealth)) { avatar.playerHealth.maxHealth = maxHealth; avatar.playerHealth.health = Mathf.Clamp(currentHealth, 0, maxHealth); if (Object.op_Implicit((Object)(object)avatar.playerHealth.photonView)) { avatar.playerHealth.photonView.RPC("UpdateHealthRPC", (RpcTarget)1, new object[3] { avatar.playerHealth.health, avatar.playerHealth.maxHealth, false }); } } } [IteratorStateMachine(typeof(<EnforceLoop>d__34))] private IEnumerator EnforceLoop() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <EnforceLoop>d__34(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<DestroyHealthUpgradeLoop>d__35))] private IEnumerator DestroyHealthUpgradeLoop() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DestroyHealthUpgradeLoop>d__35(0) { <>4__this = this }; } private void RefreshHealthUpgradeItemsDisabledState() { if (!enableMod.Value || !IsHost() || !Object.op_Implicit((Object)(object)StatsManager.instance)) { return; } foreach (Item value in StatsManager.instance.itemDictionary.Values) { if (Object.op_Implicit((Object)(object)value) && IsHealthUpgradeItem(value) && blockHealthUpgradeInWorld.Value) { value.disabled = true; } } } private static bool IsHealthUpgradeItem(Item item) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 if (!Object.op_Implicit((Object)(object)item)) { return false; } if ((int)item.itemType != 3) { return false; } if (item.prefab == null || !item.prefab.IsValid()) { return false; } GameObject val = null; try { val = item.prefab.Prefab; } catch { val = null; } if (!Object.op_Implicit((Object)(object)val)) { return false; } return (Object)(object)val.GetComponentInChildren<ItemUpgradePlayerHealth>(true) != (Object)null; } private void DestroySpawnedHealthUpgradeItems() { if (!destroyExistingHealthUpgradeItems.Value) { return; } ItemUpgradePlayerHealth[] array = Object.FindObjectsOfType<ItemUpgradePlayerHealth>(true); ItemUpgradePlayerHealth[] array2 = array; foreach (ItemUpgradePlayerHealth val in array2) { if (!Object.op_Implicit((Object)(object)val)) { continue; } GameObject gameObject = ((Component)val).gameObject; PhotonView componentInParent = gameObject.GetComponentInParent<PhotonView>(); if (Object.op_Implicit((Object)(object)componentInParent) && Object.op_Implicit((Object)(object)((Component)componentInParent).gameObject)) { gameObject = ((Component)componentInParent).gameObject; } try { if (Object.op_Implicit((Object)(object)componentInParent) && PhotonNetwork.InRoom) { PhotonNetwork.Destroy(gameObject); } else { Object.Destroy((Object)(object)gameObject); } } catch { try { Object.Destroy((Object)(object)gameObject); } catch { } } } } internal static void ExecuteCommand(string rawMessage) { if (!Object.op_Implicit((Object)(object)Instance) || !Instance.enableMod.Value || !IsHost() || string.IsNullOrWhiteSpace(rawMessage)) { return; } string text = rawMessage.Trim(); if (string.Equals(text, "/hpnow", StringComparison.OrdinalIgnoreCase)) { Log.LogInfo((object)$"Current TargetHealth(Config): {Instance.targetHealth.Value}"); Log.LogInfo((object)$"Current RoundedHealthValue: {RoundedHealthValue}"); Log.LogInfo((object)$"Current MappedHealthUpgradeValue: {MappedHealthUpgradeValue}"); } else if (string.Equals(text, "/hpreset", StringComparison.OrdinalIgnoreCase)) { SetTargetHealthFromCommand(100); } else if (text.StartsWith("/hpset ", StringComparison.OrdinalIgnoreCase)) { string text2 = text.Substring(7).Trim(); if (!int.TryParse(text2, out var result)) { Log.LogInfo((object)("Invalid /hpset value: " + text2)); } else { SetTargetHealthFromCommand(result); } } } [IteratorStateMachine(typeof(<DelayedReapplyAfterLevelChange>d__45))] private IEnumerator DelayedReapplyAfterLevelChange() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedReapplyAfterLevelChange>d__45(0) { <>4__this = this }; } } }