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;
}
}
}