Decompiled source of CapeKeepsDry v1.1.2

BepInEx/plugins/CapeKeepsDryPlugin.dll

Decompiled 13 hours ago
using 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);
		}
	}
}