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 RainproofBuildings v1.0.0
BepInEx/plugins/NegroniForge-RainproofBuildings/RainWearProtection.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("RainWearProtection.Tests")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("RainWearProtection")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Protect your buildings from rain and water wear.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Rainproof Buildings")] [assembly: AssemblyTitle("RainWearProtection")] [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 NegroniForge.RainWearProtection { [BepInPlugin("negroniforge.valheim.rainwearprotection", "Rainproof Buildings", "1.0.0")] [BepInIncompatibility("uk.co.oliapps.valheim.noraindamage")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "negroniforge.valheim.rainwearprotection"; public const string Version = "1.0.0"; internal static volatile WearPolicy Policy = new WearPolicy(enabled: true, DamageFilter.ALL, "", 0f, 5f); private ConfigEntry<bool> enabledSetting; private ConfigEntry<DamageFilter> filter; private ConfigEntry<string> list; private ConfigEntry<float> maximum; private ConfigEntry<float> rate; private Harmony harmony; private void Awake() { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown enabledSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable protection. False uses vanilla rain and water wear."); filter = ((BaseUnityPlugin)this).Config.Bind<DamageFilter>("Damage Filter", "Filter Type", DamageFilter.ALL, "ALL, INCLUSIVE or EXCLUSIVE. Unselected pieces use vanilla wear."); list = ((BaseUnityPlugin)this).Config.Bind<string>("Damage Filter", "Filter List", "", "Comma-separated, case-sensitive substrings of GameObject names. Whitespace and empty entries are ignored."); maximum = ((BaseUnityPlugin)this).Config.Bind<float>("Rain Damage", "Max Damage", 0f, "Maximum fraction of maximum HP lost to rain/water (0..1). 0 fully protects; 1 allows destruction."); rate = ((BaseUnityPlugin)this).Config.Bind<float>("Rain Damage", "Damage Per Minute", 5f, "Percentage points of maximum HP per vanilla rain tick (>60 seconds). Negative or non-finite values become zero."); RefreshSettings(null, null); ((BaseUnityPlugin)this).Config.SettingChanged += RefreshSettings; harmony = new Harmony("negroniforge.valheim.rainwearprotection"); try { harmony.Patch((MethodBase)AccessTools.Method(typeof(WearNTear), "UpdateWear", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(RainTranspiler), "Transpile", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Rain/water wear patch applied (two validated edits). Other UpdateWear logic remains original."); } catch (Exception ex) { harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Logger.LogError((object)("Rain/water protection NOT active: UpdateWear was not recognized or patch installation failed. Original wear remains. " + ex.GetBaseException().Message)); } } private void RefreshSettings(object sender, SettingChangedEventArgs args) { Policy = new WearPolicy(enabledSetting.Value, filter.Value, list.Value, maximum.Value, rate.Value); } private void OnDestroy() { ((BaseUnityPlugin)this).Config.SettingChanged -= RefreshSettings; Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private static bool Selected(WearPolicy policy, WearNTear piece) { if (policy.Enabled) { if (policy.Filter != DamageFilter.ALL) { return policy.Selects(((Object)((Component)piece).gameObject).name); } return true; } return false; } public static bool ShouldRunRain(WearNTear piece) { WearPolicy policy = Policy; if (!Selected(policy, piece)) { return piece.GetHealthPercentage() > 0.5f; } if (policy.MaxDamage > 0f) { return policy.Rate > 0f; } return false; } public static float RainContribution(WearNTear piece, ZNetView view) { WearPolicy policy = Policy; if (!Selected(policy, piece)) { return 5f; } if (policy.MaxDamage <= 0f || policy.Rate <= 0f) { return 0f; } ZDO zDO = view.GetZDO(); if (zDO != null) { return policy.Contribution(zDO.GetFloat(ZDOVars.s_health, piece.m_health), piece.m_health); } return 0f; } } internal static class RainTranspiler { private static FieldInfo Field(string name) { return AccessTools.Field(typeof(WearNTear), name); } private static MethodInfo Method(string name) { return AccessTools.Method(typeof(WearNTear), name, (Type[])null, (Type[])null); } public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions) { //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected O, but got Unknown //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Expected O, but got Unknown List<CodeInstruction> source = instructions.ToList(); List<int> list = (from i in Enumerable.Range(0, Math.Max(0, source.Count - 38)) where Matches(source, i) select i).ToList(); if (list.Count != 1) { throw new InvalidOperationException("Expected exactly one intact rain region; found " + list.Count + "."); } int num = list[0]; List<CodeInstruction> list2 = ((IEnumerable<CodeInstruction>)source).Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction c) => new CodeInstruction(c))).ToList(); list2[num + 6].operand = AccessTools.Method(typeof(Plugin), "ShouldRunRain", (Type[])null, (Type[])null); list2[num + 7].opcode = OpCodes.Nop; list2[num + 7].operand = null; list2[num + 8].opcode = OpCodes.Brfalse; list2[num + 30].opcode = OpCodes.Ldarg_0; list2[num + 30].operand = null; list2.InsertRange(num + 31, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[3] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldfld, (object)Field("m_nview")), new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Plugin), "RainContribution", (Type[])null, (Type[])null)) }); return list2; } private static bool Matches(List<CodeInstruction> code, int s) { string[] array = new string[39] { "ldarg.0", "ldfld", "brfalse", "ldloc.0", "brtrue", "ldarg.0", "call", "ldc.r4", "ble.un", "ldarg.0", "call", "brfalse", "ldarg.0", "ldfld", "ldc.r4", "bne.un", "ldarg.0", "ldarg.1", "stfld", "br", "ldarg.1", "ldarg.0", "ldfld", "sub", "ldc.r4", "ble.un", "ldarg.0", "ldarg.1", "stfld", "ldloc.1", "ldc.r4", "add", "stloc.1", "br", "ldarg.0", "ldc.r4", "stfld", "ldarg.0", "ldfld" }; for (int i = 0; i < array.Length; i++) { string text = code[s + i].opcode.Name; if (text.EndsWith(".s")) { text = text.Substring(0, text.Length - 2); } if (text != array[i] || code[s + i].blocks.Count != 0) { return false; } } if (!object.Equals(code[s + 1].operand, Field("m_noRoofWear")) || !object.Equals(code[s + 6].operand, Method("GetHealthPercentage")) || !object.Equals(code[s + 10].operand, Method("IsWet")) || !object.Equals(code[s + 38].operand, Field("m_noSupportWear"))) { return false; } int[] array2 = new int[5] { 13, 18, 22, 28, 36 }; foreach (int num in array2) { if (!object.Equals(code[s + num].operand, Field("m_rainTimer"))) { return false; } } (int, float)[] array3 = new(int, float)[5] { (7, 0.5f), (14, 0f), (24, 60f), (30, 5f), (35, 0f) }; for (int j = 0; j < array3.Length; j++) { (int, float) tuple = array3[j]; if (!object.Equals(code[s + tuple.Item1].operand, tuple.Item2)) { return false; } } (int, int)[] array4 = new(int, int)[8] { (2, 37), (4, 37), (8, 37), (11, 34), (15, 20), (19, 37), (25, 37), (33, 37) }; for (int j = 0; j < array4.Length; j++) { (int, int) tuple2 = array4[j]; if (!(code[s + tuple2.Item1].operand is Label item) || !code[s + tuple2.Item2].labels.Contains(item)) { return false; } } HashSet<Label> hashSet = new HashSet<Label>(code.Skip(s + 1).Take(36).SelectMany((CodeInstruction c) => c.labels)); for (int num2 = 0; num2 < code.Count; num2++) { if (num2 < s || num2 > s + 36) { if (code[num2].operand is Label item2 && hashSet.Contains(item2)) { return false; } if (code[num2].operand is Label[] source && source.Any(hashSet.Contains)) { return false; } } } for (int num3 = 1; num3 <= 36; num3++) { if (num3 != 20 && num3 != 34 && code[s + num3].labels.Count != 0) { return false; } } return true; } } public enum DamageFilter { ALL, INCLUSIVE, EXCLUSIVE } internal sealed class WearPolicy { internal readonly bool Enabled; internal readonly DamageFilter Filter; internal readonly float MaxDamage; internal readonly float Rate; private readonly string[] tokens; internal WearPolicy(bool enabled, DamageFilter filter, string list, float maxDamage, float rate) { Enabled = enabled; Filter = filter; MaxDamage = (Finite(maxDamage) ? Math.Max(0f, Math.Min(1f, maxDamage)) : 0f); Rate = (Finite(rate) ? Math.Max(0f, rate) : 0f); tokens = (from s in (list ?? "").Split(new char[1] { ',' }) select s.Trim() into s where s.Length > 0 select s).Distinct().ToArray(); } internal bool Selects(string name) { if (!Enabled) { return false; } if (Filter == DamageFilter.ALL) { return true; } bool flag = false; for (int i = 0; i < tokens.Length; i++) { if (name.IndexOf(tokens[i], StringComparison.Ordinal) >= 0) { flag = true; break; } } if (Filter != DamageFilter.INCLUSIVE) { if (Filter == DamageFilter.EXCLUSIVE) { return !flag; } return false; } return flag; } internal float Contribution(float currentHealth, float maxHealth) { if (MaxDamage <= 0f || Rate <= 0f || !Finite(currentHealth) || !Finite(maxHealth) || maxHealth <= 0f) { return 0f; } double val = ((double)currentHealth - (double)maxHealth * (1.0 - (double)MaxDamage)) / (double)maxHealth * 100.0; return (float)Math.Max(0.0, Math.Min(Rate, val)); } private static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } }