Decompiled source of NagMessages v1.0.1

NagMessages.dll

Decompiled 6 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("NagMessages")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Riintouge")]
[assembly: AssemblyProduct("NagMessages")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("7f021c97-1954-4a61-83ad-7bb4be2e18ef")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.1.0")]
namespace NagMessages;

[BepInPlugin("com.riintouge.nagmessages", "Nag Messages", "1.0.1")]
[BepInProcess("valheim.exe")]
public class NagMessages : BaseUnityPlugin
{
	private class NagArgs
	{
		public double delay;

		public bool force;

		public Coroutine self;
	}

	[HarmonyPatch(typeof(MessageHud))]
	private class MessageHudPatch
	{
		internal const double MessageTTL = 4.0;

		private static string CurrentMessage = null;

		private static double MinimumTimeOfNextMessage = 0.0;

		private static readonly LinkedList<string> PendingMessages = new LinkedList<string>();

		[HarmonyPatch("ShowMessage")]
		[HarmonyPrefix]
		private static bool ShowMessagePrefix(ref MessageType type, ref string text)
		{
			if ((int)type != 2)
			{
				return true;
			}
			if (Utility.IsNullOrWhiteSpace(text))
			{
				if (PendingMessages.Count > 0)
				{
					text = PendingMessages.First();
					PendingMessages.RemoveFirst();
				}
				CurrentMessage = text;
				return true;
			}
			double timeAsDouble = Time.timeAsDouble;
			if (!IsEnabled.Value || !QueueCenterMessages.Value || timeAsDouble >= MinimumTimeOfNextMessage)
			{
				MinimumTimeOfNextMessage = timeAsDouble + 4.0;
				CurrentMessage = text;
				return true;
			}
			if (text.CompareTo(CurrentMessage) == 0)
			{
				return false;
			}
			if (!PendingMessages.Contains(text))
			{
				PendingMessages.AddLast(text);
				((MonoBehaviour)Instance).StartCoroutine("ShowNextMessage", (object)(float)(MinimumTimeOfNextMessage - timeAsDouble));
				MinimumTimeOfNextMessage += 4.0;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Game))]
	private class GamePatch
	{
		private static bool FirstSpawnPending;

		[HarmonyPatch("FixedUpdate")]
		[HarmonyPrefix]
		private static void FixedUpdatePrefix(ref bool ___m_firstSpawn)
		{
			if (___m_firstSpawn)
			{
				FirstSpawnPending = true;
			}
		}

		[HarmonyPatch("UpdateRespawn")]
		[HarmonyPostfix]
		private static void UpdateRespawnPostfix()
		{
			if (FirstSpawnPending && Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				FirstSpawnPending = false;
				Instance.NagAboutPower(8.0, force: true);
				Instance.NagAboutHunger(12.0, force: true);
			}
		}
	}

	[HarmonyPatch(typeof(Player))]
	private class PlayerPatch
	{
		[HarmonyPatch("ActivateGuardianPower")]
		[HarmonyPrefix]
		private static void ActivateGuardianPowerPrefix(ref Player __instance, out float __state)
		{
			__state = __instance.m_guardianPowerCooldown;
		}

		[HarmonyPatch("ActivateGuardianPower")]
		[HarmonyPostfix]
		private static void ActivateGuardianPowerPostfix(ref Player __instance, ref StatusEffect ___m_guardianSE, ref float __state)
		{
			if (__instance.m_guardianPowerCooldown != __state && Object.op_Implicit((Object)(object)___m_guardianSE))
			{
				StatusEffect statusEffect = ((Character)__instance).GetSEMan().GetStatusEffect(___m_guardianSE.NameHash());
				if (Object.op_Implicit((Object)(object)statusEffect))
				{
					Instance.NagAboutPower(statusEffect.GetRemaningTime(), force: true);
				}
			}
		}

		[HarmonyPatch("SetGuardianPower")]
		[HarmonyPostfix]
		private static void SetGuardianPowerPostfix()
		{
			Instance.NagAboutPower();
		}

		[HarmonyPatch("SetMaxEitr")]
		[HarmonyPrefix]
		private static void SetMaxEitrPrefix(ref Player __instance, float eitr)
		{
			float maxEitr = ((Character)__instance).GetMaxEitr();
			if (IsEnabled.Value && eitr < maxEitr && eitr <= (float)EitrThreshold.Value && maxEitr > (float)EitrThreshold.Value)
			{
				((Character)__instance).Message((MessageType)2, "Your maximum eitr is getting too low!", 0, (Sprite)null);
			}
		}

		[HarmonyPatch("SetMaxHealth")]
		[HarmonyPrefix]
		private static void SetMaxHealthPrefix(ref Player __instance, float health)
		{
			float maxHealth = ((Character)__instance).GetMaxHealth();
			if (IsEnabled.Value && health < maxHealth && health <= (float)HealthThreshold.Value && maxHealth > (float)HealthThreshold.Value)
			{
				((Character)__instance).Message((MessageType)2, "Your maximum health is getting too low!", 0, (Sprite)null);
			}
		}

		[HarmonyPatch("SetMaxStamina")]
		[HarmonyPrefix]
		private static void SetMaxStaminaPrefix(ref Player __instance, float stamina)
		{
			float maxStamina = ((Character)__instance).GetMaxStamina();
			if (IsEnabled.Value && stamina < maxStamina && stamina <= (float)StaminaThreshold.Value && maxStamina > (float)StaminaThreshold.Value)
			{
				((Character)__instance).Message((MessageType)2, "Your maximum stamina is getting too low!", 0, (Sprite)null);
			}
		}

		[HarmonyPatch("UpdateFood")]
		[HarmonyPrefix]
		private static void UpdateFoodPrefix(ref List<Food> ___m_foods, out int __state)
		{
			__state = ___m_foods.Count;
		}

		[HarmonyPatch("UpdateFood")]
		[HarmonyPostfix]
		private static void UpdateFoodPostfix(ref List<Food> ___m_foods, ref int __state)
		{
			if (___m_foods.Count == 0 && __state > 0)
			{
				Instance.NagAboutHunger();
			}
		}
	}

	private readonly int BonemassPowerNameHash = "GP_Bonemass".GetHashCode();

	private readonly int EikthyrPowerNameHash = "GP_Eikthyr".GetHashCode();

	private readonly int ModerPowerNameHash = "GP_Moder".GetHashCode();

	private readonly int TheElderPowerNameHash = "GP_TheElder".GetHashCode();

	private readonly int TheQueenPowerNameHash = "GP_Queen".GetHashCode();

	private readonly int YagluthPowerNameHash = "GP_Yagluth".GetHashCode();

	private static List<Coroutine> PowerNagCoroutines = new List<Coroutine>();

	internal static double MinTimeOfNextPowerNag = 0.0;

	private static List<Coroutine> HungerNagCoroutines = new List<Coroutine>();

	internal static double MinTimeOfNextHungerNag = 0.0;

	public static NagMessages Instance = null;

	public static ConfigEntry<bool> IsEnabled;

	public static ConfigEntry<bool> LoadOnStart;

	public static ConfigEntry<bool> QueueCenterMessages;

	public static ConfigEntry<bool> AllowBonemass;

	public static ConfigEntry<bool> AllowEikthyr;

	public static ConfigEntry<bool> AllowModer;

	public static ConfigEntry<bool> AllowTheElder;

	public static ConfigEntry<bool> AllowTheQueen;

	public static ConfigEntry<bool> AllowYagluth;

	public static ConfigEntry<int> PowerNagFrequency;

	private static int LastPowerNagFrequency;

	public static ConfigEntry<int> EitrThreshold;

	public static ConfigEntry<int> HealthThreshold;

	public static ConfigEntry<int> HungerNagFrequency;

	private static int LastHungerNagFrequency;

	public static ConfigEntry<int> StaminaThreshold;

	private readonly Harmony Harmony = new Harmony("com.riintouge.nagmessages");

	public void NagAboutPower()
	{
		NagAboutPower((float)PowerNagFrequency.Value * 60f, force: false);
	}

	public void NagAboutPower(double delay, bool force)
	{
		NagArgs nagArgs = new NagArgs
		{
			delay = delay,
			force = force
		};
		Coroutine item = (nagArgs.self = ((MonoBehaviour)Instance).StartCoroutine(CoNagAboutPower(nagArgs)));
		PowerNagCoroutines.Add(item);
	}

	private IEnumerator CoNagAboutPower(NagArgs args)
	{
		yield return (object)new WaitForSecondsRealtime(1f);
		if (args.delay > 1.0)
		{
			yield return (object)new WaitForSecondsRealtime((float)args.delay);
		}
		Instance.NagAboutPower(args);
	}

	private void NagAboutPower(NagArgs args)
	{
		Player localPlayer = Player.m_localPlayer;
		if (!IsEnabled.Value || !Object.op_Implicit((Object)(object)localPlayer))
		{
			return;
		}
		double timeAsDouble = Time.timeAsDouble;
		if (!args.force && timeAsDouble < MinTimeOfNextPowerNag)
		{
			((MonoBehaviour)Instance).StopCoroutine(args.self);
			PowerNagCoroutines.Remove(args.self);
			if (PowerNagCoroutines.Count == 0)
			{
				NagAboutPower(MinTimeOfNextPowerNag - timeAsDouble, force: false);
			}
			return;
		}
		int hashCode = localPlayer.GetGuardianPowerName().GetHashCode();
		if ((AllowBonemass.Value || hashCode != BonemassPowerNameHash) && (AllowEikthyr.Value || hashCode != EikthyrPowerNameHash) && (AllowModer.Value || hashCode != ModerPowerNameHash) && (AllowTheElder.Value || hashCode != TheElderPowerNameHash) && (AllowTheQueen.Value || hashCode != TheQueenPowerNameHash) && (AllowYagluth.Value || hashCode != YagluthPowerNameHash))
		{
			return;
		}
		((Character)localPlayer).Message((MessageType)2, "Change your forsaken power!", 0, (Sprite)null);
		MinTimeOfNextPowerNag = timeAsDouble + (double)((float)PowerNagFrequency.Value * 60f);
		foreach (Coroutine powerNagCoroutine in PowerNagCoroutines)
		{
			((MonoBehaviour)Instance).StopCoroutine(powerNagCoroutine);
		}
		PowerNagCoroutines.Clear();
		NagAboutPower();
	}

	public void NagAboutHunger()
	{
		NagAboutHunger((float)HungerNagFrequency.Value * 60f, force: false);
	}

	public void NagAboutHunger(double delay, bool force)
	{
		NagArgs nagArgs = new NagArgs
		{
			delay = delay,
			force = force
		};
		Coroutine item = (nagArgs.self = ((MonoBehaviour)Instance).StartCoroutine(CoNagAboutHunger(nagArgs)));
		HungerNagCoroutines.Add(item);
	}

	private IEnumerator CoNagAboutHunger(NagArgs args)
	{
		yield return (object)new WaitForSecondsRealtime(1f);
		if (args.delay > 1.0)
		{
			yield return (object)new WaitForSecondsRealtime((float)args.delay);
		}
		Instance.NagAboutHunger(args);
	}

	private void NagAboutHunger(NagArgs args)
	{
		Player localPlayer = Player.m_localPlayer;
		if (!IsEnabled.Value || !Object.op_Implicit((Object)(object)localPlayer))
		{
			return;
		}
		double timeAsDouble = Time.timeAsDouble;
		if (!args.force && timeAsDouble < MinTimeOfNextHungerNag)
		{
			((MonoBehaviour)Instance).StopCoroutine(args.self);
			HungerNagCoroutines.Remove(args.self);
			if (HungerNagCoroutines.Count == 0)
			{
				NagAboutHunger(MinTimeOfNextHungerNag - timeAsDouble, force: false);
			}
		}
		else
		{
			if (localPlayer.GetFoods().Count != 0)
			{
				return;
			}
			((Character)localPlayer).Message((MessageType)2, "Your stomach is growling", 0, (Sprite)null);
			MinTimeOfNextHungerNag = timeAsDouble + (double)((float)HungerNagFrequency.Value * 60f);
			foreach (Coroutine hungerNagCoroutine in HungerNagCoroutines)
			{
				((MonoBehaviour)Instance).StopCoroutine(hungerNagCoroutine);
			}
			HungerNagCoroutines.Clear();
			NagAboutHunger();
		}
	}

	public IEnumerator ShowNextMessage(float seconds)
	{
		yield return (object)new WaitForSecondsRealtime(seconds);
		if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)MessageHud.instance))
		{
			MessageHud.instance.ShowMessage((MessageType)2, (string)null, 0, (Sprite)null);
		}
	}

	private void Awake()
	{
		IsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Core", "Enable", true, "Whether this plugin has any effect when loaded.");
		LoadOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Core", "LoadOnStart", true, "Whether this plugin loads on game start.");
		QueueCenterMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "QueueCenterMessages", true, "Whether front-and-center messages will be queued for display. Provided for compatibility.");
		AllowBonemass = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Forsaken Powers", "AllowBonemass", true, "If false, periodically nag the player to switch powers.");
		AllowEikthyr = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Forsaken Powers", "AllowEikthyr", true, "If false, periodically nag the player to switch powers.");
		AllowModer = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Forsaken Powers", "AllowModer", true, "If false, periodically nag the player to switch powers.");
		AllowTheElder = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Forsaken Powers", "AllowTheElder", true, "If false, periodically nag the player to switch powers.");
		AllowTheQueen = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Forsaken Powers", "AllowTheQueen", true, "If false, periodically nag the player to switch powers.");
		AllowYagluth = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Forsaken Powers", "AllowYagluth", true, "If false, periodically nag the player to switch powers.");
		PowerNagFrequency = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Forsaken Powers", "PowerNagFrequency", 3, "Minimum time in minutes between forsaken power nag messages.");
		EitrThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Food Effects", "EitrThreshold", 35, "Warn the player when their maximum eitr drops to or below this amount.");
		HealthThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Food Effects", "HealthThreshold", 0, "Warn the player when their maximum health drops to or below this amount.");
		HungerNagFrequency = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Food Effects", "HungerNagFrequency", 3, "Minimum time in minutes between hunger nag messages.");
		StaminaThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Food Effects", "StaminaThreshold", 0, "Warn the player when their maximum stamina drops to or below this amount.");
		if (LoadOnStart.Value)
		{
			Instance = this;
			Harmony.PatchAll();
			LastHungerNagFrequency = HungerNagFrequency.Value;
			((BaseUnityPlugin)this).Config.SettingChanged += Config_SettingChanged;
		}
	}

	private void Config_SettingChanged(object sender, SettingChangedEventArgs e)
	{
		if (e.ChangedSetting == AllowBonemass || e.ChangedSetting == AllowEikthyr || e.ChangedSetting == AllowModer || e.ChangedSetting == AllowTheElder || e.ChangedSetting == AllowTheQueen || e.ChangedSetting == AllowYagluth)
		{
			if (!(bool)e.ChangedSetting.BoxedValue)
			{
				NagAboutPower();
			}
		}
		else if (e.ChangedSetting == PowerNagFrequency)
		{
			int num = PowerNagFrequency.Value - LastPowerNagFrequency;
			double num2 = (double)num * 60.0;
			if (num > 0)
			{
				MinTimeOfNextPowerNag += num2;
			}
			else if (num < 0)
			{
				double timeAsDouble = Time.timeAsDouble;
				num2 = Math.Abs(num2);
				if (MinTimeOfNextPowerNag - num2 < timeAsDouble)
				{
					MinTimeOfNextPowerNag = timeAsDouble + (double)PowerNagFrequency.Value * 60.0;
				}
				else
				{
					MinTimeOfNextPowerNag -= num2;
				}
				NagAboutPower();
			}
			LastPowerNagFrequency = PowerNagFrequency.Value;
		}
		else
		{
			if (e.ChangedSetting != HungerNagFrequency)
			{
				return;
			}
			int num3 = HungerNagFrequency.Value - LastHungerNagFrequency;
			double num4 = (double)num3 * 60.0;
			if (num3 > 0)
			{
				MinTimeOfNextHungerNag += num4;
			}
			else if (num3 < 0)
			{
				double timeAsDouble2 = Time.timeAsDouble;
				num4 = Math.Abs(num4);
				if (MinTimeOfNextHungerNag - num4 < timeAsDouble2)
				{
					MinTimeOfNextHungerNag = timeAsDouble2 + (double)HungerNagFrequency.Value * 60.0;
				}
				else
				{
					MinTimeOfNextHungerNag -= num4;
				}
				NagAboutHunger();
			}
			LastHungerNagFrequency = HungerNagFrequency.Value;
		}
	}
}