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 CapeKeepsDry v1.1.2
BepInEx/plugins/CapeKeepsDryPlugin.dll
Decompiled 13 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; 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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("CapeKeepsDryPlugin")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+397ae2e8603f1cdc48b04aaa59f62696520473aa")] [assembly: AssemblyProduct("CapeKeepsDryPlugin")] [assembly: AssemblyTitle("CapeKeepsDryPlugin")] [assembly: AssemblyVersion("1.0.0.0")] [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 CapeKeepsDryPlugin { [BepInPlugin("com.capekeepsdry.mod", "Cape Keeps Dry", "1.1.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class CapeKeepsDryPlugin : BaseUnityPlugin { internal static ManualLogSource Log; public const string PluginGuid = "com.capekeepsdry.mod"; public const string PluginName = "Cape Keeps Dry"; public const string PluginVersion = "1.1.2"; internal const string RainCoverEffectName = "CapeKeepsDry_RainCover"; internal static ConfigEntry<float> RainCoverSeconds; internal static ConfigEntry<float> RoofCoverThreshold; internal static ConfigEntry<bool> RequireShoulderPiece; internal static ConfigEntry<bool> EnableGradualRainCoverTimerRecovery; internal static StatusEffect RainCoverEffect; internal static int RainCoverHash; private Harmony _harmony = null; private FileSystemWatcher _configWatcher; private int _configReloadPending; private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown RainCoverSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RainCoverDuration", 120f, new ConfigDescription("Seconds a shoulder piece protects a player from becoming Wet while exposed to rain.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3600f), Array.Empty<object>())); RoofCoverThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RoofCoverThreshold", 0.5f, new ConfigDescription("Minimum roof/cover percentage (0-1) treated as a player being under roof.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); RequireShoulderPiece = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RequireShoulderPiece", true, "When enabled, Rain Cover requires an equipped item in the shoulder slot (normally a cape)."); EnableGradualRainCoverTimerRecovery = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableGradualRainCoverTimerRecovery", true, "When enabled, Rain Cover timer recovers gradually when a player is under roof. When disbled, resets Rain Cover instantly."); CreateRainCoverStatusEffect(); _harmony = new Harmony("com.capekeepsdry.mod"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Cape Keeps Dry 1.1.2 loaded."); StartConfigWatcher(); Log = ((BaseUnityPlugin)this).Logger; } private void OnDestroy() { if (_configWatcher != null) { _configWatcher.EnableRaisingEvents = false; _configWatcher.Dispose(); _configWatcher = null; } Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void CreateRainCoverStatusEffect() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown StatusEffect val = ScriptableObject.CreateInstance<StatusEffect>(); ((Object)val).name = "CapeKeepsDry_RainCover"; val.m_name = "Rain Cover"; val.m_tooltip = "Your shoulder piece is keeping the rain off, for now."; val.m_icon = CreateIcon(); val.m_startMessageType = (MessageType)1; val.m_ttl = Mathf.Max(1f, RainCoverSeconds.Value); RainCoverEffect = val; RainCoverHash = val.NameHash(); ItemManager.Instance.AddStatusEffect(new CustomStatusEffect(val, false)); } private static Sprite CreateIcon() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false) { name = "CapeKeepsDry_RainCoverIcon", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; Color val2 = default(Color); ((Color)(ref val2))..ctor(0f, 0f, 0f, 0f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.92f, 0.96f, 1f, 1f); Color val4 = default(Color); ((Color)(ref val4))..ctor(0.35f, 0.65f, 0.95f, 1f); Color[] array = (Color[])(object)new Color[1024]; for (int i = 0; i < array.Length; i++) { array[i] = val2; } for (int j = 15; j <= 23; j++) { for (int k = 5; k <= 26; k++) { float num = ((float)k - 15.5f) / 11f; float num2 = ((float)j - 15f) / 9f; if (num * num + num2 * num2 <= 1f && j >= 16) { array[j * 32 + k] = val4; } } } for (int l = 7; l <= 17; l++) { array[l * 32 + 16] = val3; } for (int m = 16; m <= 20; m++) { array[224 + m] = val3; } array[276] = val3; array[308] = val3; (int, int)[] array2 = new(int, int)[4] { (8, 10), (24, 11), (5, 6), (27, 7) }; for (int n = 0; n < array2.Length; n++) { (int, int) tuple = array2[n]; array[tuple.Item2 * 32 + tuple.Item1] = val3; if (tuple.Item2 + 1 < 32) { array[(tuple.Item2 + 1) * 32 + tuple.Item1] = val3; } } val.SetPixels(array); val.Apply(); return Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 100f); } private void StartConfigWatcher() { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; string directoryName = Path.GetDirectoryName(configFilePath); string fileName = Path.GetFileName(configFilePath); _configWatcher = new FileSystemWatcher(directoryName, fileName) { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite) }; _configWatcher.Changed += OnConfigFileChanged; _configWatcher.Created += OnConfigFileChanged; _configWatcher.Renamed += OnConfigFileRenamed; _configWatcher.EnableRaisingEvents = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Watching config file: " + configFilePath)); } private void OnConfigFileChanged(object sender, FileSystemEventArgs e) { Interlocked.Exchange(ref _configReloadPending, 1); } private void OnConfigFileRenamed(object sender, RenamedEventArgs e) { Interlocked.Exchange(ref _configReloadPending, 1); } private void Update() { if (Interlocked.Exchange(ref _configReloadPending, 0) == 0) { return; } try { ((BaseUnityPlugin)this).Config.Reload(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Config file reloaded."); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to reload config: {arg}"); } } } internal static class RainCoverController { private sealed class State { public float Remaining; public bool Initialized; public bool ExhaustedThisExposure; } private static readonly Dictionary<Player, State> States = new Dictionary<Player, State>(); private static readonly FieldInfo UnderRoofField = AccessTools.Field(typeof(Player), "m_underRoof"); private static readonly FieldInfo CoverPercentageField = AccessTools.Field(typeof(Player), "m_coverPercentage"); internal static void BeforeEnvironmentUpdate(Player player, float dt) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null) { return; } State state = GetState(player); float num = Mathf.Max(1f, CapeKeepsDryPlugin.RainCoverSeconds.Value); if (!state.Initialized) { state.Remaining = num; state.Initialized = true; } bool flag = EnvMan.IsWet(); bool flag2 = IsSheltered(player); bool flag3 = sEMan.HaveStatusEffect(SEMan.s_statusEffectWet); bool flag4 = !CapeKeepsDryPlugin.RequireShoulderPiece.Value || HasShoulderItemEquipped(player); bool flag5 = sEMan.HaveStatusEffect(SEMan.s_statusEffectCampFire); if (((Character)player).IsSwimming()) { RemoveRainCover(player); } else if (flag2) { if (CapeKeepsDryPlugin.EnableGradualRainCoverTimerRecovery.Value && state.Remaining < num) { float num2 = Mathf.Max(0f, dt); if (flag5) { num2 *= 10f; } state.Remaining = Mathf.Max(0f, state.Remaining + num2); if (state.Remaining < num) { UpdateRainCoverTTL(player, num2, dt); } } else { state.Remaining = num; state.ExhaustedThisExposure = false; RemoveRainCover(player); } } else if (!flag) { RemoveRainCover(player); } else if (flag3 || !flag4) { RemoveRainCover(player); } else if (state.ExhaustedThisExposure || state.Remaining <= 0f) { state.Remaining = 0f; state.ExhaustedThisExposure = true; RemoveRainCover(player); } else { EnsureRainCover(player); state.Remaining = Mathf.Max(0f, state.Remaining - Mathf.Max(0f, dt)); if (state.Remaining <= 0f) { state.ExhaustedThisExposure = true; RemoveRainCover(player); } } } internal static void AfterEnvironmentUpdate(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null && EnvMan.IsWet() && !IsSheltered(player) && !sEMan.HaveStatusEffect(SEMan.s_statusEffectWet)) { State state = GetState(player); if ((CapeKeepsDryPlugin.RequireShoulderPiece.Value && !HasShoulderItemEquipped(player)) || state.ExhaustedThisExposure || !(state.Remaining > 0f) || !sEMan.HaveStatusEffect(CapeKeepsDryPlugin.RainCoverHash)) { sEMan.AddStatusEffect(SEMan.s_statusEffectWet, true, 0, 0f, (short)(-1)); } } } internal static bool ShouldBlockWet(SEMan seman) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || seman == null || ((Character)localPlayer).GetSEMan() != seman) { return false; } if (((Character)localPlayer).IsSwimming()) { return false; } if (!EnvMan.IsWet() || IsSheltered(localPlayer)) { return false; } if (CapeKeepsDryPlugin.RequireShoulderPiece.Value && !HasShoulderItemEquipped(localPlayer)) { return false; } State state = GetState(localPlayer); return !state.ExhaustedThisExposure && state.Remaining > 0f && seman.HaveStatusEffect(CapeKeepsDryPlugin.RainCoverHash); } internal static void ClearState(Player player) { if ((Object)(object)player != (Object)null) { States.Remove(player); } } private static State GetState(Player player) { if (!States.TryGetValue(player, out var value)) { value = new State(); States[player] = value; } return value; } private static bool IsSheltered(Player player) { bool flag = UnderRoofField != null && (bool)UnderRoofField.GetValue(player); float num = ((CoverPercentageField != null) ? ((float)CoverPercentageField.GetValue(player)) : 0f); return flag && num >= CapeKeepsDryPlugin.RoofCoverThreshold.Value; } private static void EnsureRainCover(Player player) { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null && !sEMan.HaveStatusEffect(CapeKeepsDryPlugin.RainCoverHash)) { CapeKeepsDryPlugin.RainCoverEffect.m_ttl = Mathf.Max(1f, CapeKeepsDryPlugin.RainCoverSeconds.Value); sEMan.AddStatusEffect(CapeKeepsDryPlugin.RainCoverEffect, false, 0, 0f, (short)(-1)); } } private static void RemoveRainCover(Player player) { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null && sEMan.HaveStatusEffect(CapeKeepsDryPlugin.RainCoverHash)) { sEMan.RemoveStatusEffect(CapeKeepsDryPlugin.RainCoverHash, true); } ClearState(player); } private static void UpdateRainCoverTTL(Player player, float delta, float dt) { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { StatusEffect statusEffect = sEMan.GetStatusEffect(CapeKeepsDryPlugin.RainCoverHash); if ((Object)(object)statusEffect != (Object)null) { statusEffect.m_ttl += delta + dt; } } } private static bool HasShoulderItemEquipped(Player player) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Invalid comparison between Unknown and I4 if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem?.m_shared == null || (int)allItem.m_shared.m_itemType != 17 || !((Humanoid)player).IsItemEquiped(allItem)) { continue; } return true; } return false; } } [HarmonyPatch(typeof(Player), "UpdateEnvStatusEffects")] internal static class PlayerUpdateEnvStatusEffectsPatch { private static void Prefix(Player __instance, float dt) { RainCoverController.BeforeEnvironmentUpdate(__instance, dt); } private static void Postfix(Player __instance) { RainCoverController.AfterEnvironmentUpdate(__instance); } } [HarmonyPatch(typeof(SEMan), "Internal_AddStatusEffect")] internal static class SEManInternalAddStatusEffectPatch { private static bool Prefix(SEMan __instance, int nameHash, ref StatusEffect __result) { if (nameHash != SEMan.s_statusEffectWet) { return true; } if (!RainCoverController.ShouldBlockWet(__instance)) { return true; } __result = null; return false; } } [HarmonyPatch(typeof(Player), "OnDestroy")] internal static class PlayerDestroyPatch { private static void Prefix(Player __instance) { RainCoverController.ClearState(__instance); } } }