Decompiled source of DeepNorthHotSprings v1.0.1

DeepNorthStructures.dll

Decompiled 6 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("igentuman")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Enables Valheim's vanilla Deep North Hot Springs and adds their restorative pool effects.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+219718f651790ec01c9ed8fa9a9580ec0e93939c")]
[assembly: AssemblyProduct("DeepNorthStructures")]
[assembly: AssemblyTitle("DeepNorthStructures")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 DeepNorthStructures
{
	internal sealed class HotSpringArea : MonoBehaviour
	{
		private static readonly HashSet<HotSpringArea> Instances = new HashSet<HotSpringArea>();

		private WaterVolume[] _waterVolumes = Array.Empty<WaterVolume>();

		private float _radius = 15f;

		internal void Initialize(float locationRadius)
		{
			_radius = Mathf.Clamp(locationRadius, 6f, 30f);
			_waterVolumes = ((Component)this).GetComponentsInChildren<WaterVolume>(true);
		}

		private void OnEnable()
		{
			Instances.Add(this);
		}

		private void OnDisable()
		{
			Instances.Remove(this);
		}

		internal static bool Contains(Player player)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!((Character)player).InWater())
			{
				return false;
			}
			Vector3 position = ((Component)player).transform.position;
			foreach (HotSpringArea instance in Instances)
			{
				if ((Object)(object)instance != (Object)null && instance.Contains(position))
				{
					return true;
				}
			}
			return false;
		}

		private bool Contains(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_0011: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = position - ((Component)this).transform.position;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude > _radius * _radius)
			{
				return false;
			}
			if (_waterVolumes.Length == 0)
			{
				return true;
			}
			WaterVolume[] waterVolumes = _waterVolumes;
			foreach (WaterVolume val2 in waterVolumes)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Collider component = ((Component)val2).GetComponent<Collider>();
				if (!((Object)(object)component == (Object)null))
				{
					Bounds bounds = component.bounds;
					((Bounds)(ref bounds)).Expand(new Vector3(1f, 2f, 1f));
					if (((Bounds)(ref bounds)).Contains(position))
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	[BepInPlugin("com.igentuman.deepnorthstructures", "Deep North Hot Springs", "1.0.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ZoneSystem), "SetupLocations")]
		private static class SetupLocationsPatch
		{
			private static void Prefix()
			{
				int num = 0;
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				foreach (LocationList allLocationList in LocationList.GetAllLocationLists())
				{
					foreach (ZoneLocation location in allLocationList.m_locations)
					{
						if (!location.m_prefab.IsValid)
						{
							continue;
						}
						string name = location.m_prefab.Name;
						if (name == "TheDarkestHole")
						{
							location.m_enable = false;
						}
						else if (IsHotSpring(name))
						{
							hashSet.Add(name);
							if (!location.m_enable)
							{
								location.m_enable = true;
								num++;
							}
						}
					}
				}
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)$"Hot Spring location setup: found {hashSet.Count}/3 prefabs; enabled {num}");
				foreach (string hotSpringPrefab in HotSpringPrefabs)
				{
					if (!hashSet.Contains(hotSpringPrefab))
					{
						((BaseUnityPlugin)Instance).Logger.LogWarning((object)(hotSpringPrefab + " was not found. The current Valheim build may have renamed or removed it."));
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZoneSystem), "Load")]
		private static class LoadLocationsPatch
		{
			private static void Postfix(ZoneSystem __instance)
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && __instance.LocationsGenerated)
				{
					((MonoBehaviour)__instance).StartCoroutine(GenerateMissingHotSprings(__instance));
				}
			}
		}

		[HarmonyPatch(typeof(ZoneSystem), "SpawnLocation")]
		private static class SpawnLocationPatch
		{
			private static void Postfix(ZoneLocation location, GameObject? __result)
			{
				if (!((Object)(object)__result == (Object)null) && location.m_prefab.IsValid && IsHotSpring(location.m_prefab.Name))
				{
					(__result.GetComponent<HotSpringArea>() ?? __result.AddComponent<HotSpringArea>()).Initialize(Mathf.Max(location.m_exteriorRadius, location.m_interiorRadius));
				}
			}
		}

		[HarmonyPatch(typeof(Player), "UpdateEnvStatusEffects")]
		private static class PlayerEnvironmentPatch
		{
			private static float _timeInHotSpring;

			private static float _healthTick;

			private static float _normalRestingDelay;

			private static bool _wasInHotSpring;

			private static void Postfix(Player __instance, float dt)
			{
				if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
				{
					return;
				}
				if (!HotSpringArea.Contains(__instance))
				{
					if (_wasInHotSpring)
					{
						StatusEffect statusEffect = ((Character)__instance).GetSEMan().GetStatusEffect(SEMan.s_statusEffectResting);
						SE_Cozy val = (SE_Cozy)(object)((statusEffect is SE_Cozy) ? statusEffect : null);
						if (val != null)
						{
							val.m_delay = _normalRestingDelay;
						}
					}
					_timeInHotSpring = 0f;
					_healthTick = 0f;
					_wasInHotSpring = false;
					return;
				}
				SEMan sEMan = ((Character)__instance).GetSEMan();
				sEMan.AddStatusEffect(SEMan.s_statusEffectResting, false, 0, 0f, (short)(-1));
				StatusEffect statusEffect2 = sEMan.GetStatusEffect(SEMan.s_statusEffectResting);
				SE_Cozy val2 = (SE_Cozy)(object)((statusEffect2 is SE_Cozy) ? statusEffect2 : null);
				if (val2 != null)
				{
					if (!_wasInHotSpring)
					{
						_normalRestingDelay = val2.m_delay;
						((StatusEffect)val2).ResetTime();
					}
					val2.m_delay = 60f;
				}
				_wasInHotSpring = true;
				_timeInHotSpring += dt;
				_healthTick += dt;
				if (_healthTick >= 1f)
				{
					((Character)__instance).Heal(_healthTick * 2f, false);
					_healthTick = 0f;
				}
				if (!(_timeInHotSpring < 60f))
				{
					StatusEffect statusEffect3 = sEMan.GetStatusEffect(SEMan.s_statusEffectRested);
					if ((Object)(object)statusEffect3 == (Object)null)
					{
						sEMan.AddStatusEffect(SEMan.s_statusEffectRested, true, 0, 0f, (short)(-1));
						statusEffect3 = sEMan.GetStatusEffect(SEMan.s_statusEffectRested);
					}
					else
					{
						statusEffect3.ResetTime();
					}
					SE_Rested val3 = (SE_Rested)(object)((statusEffect3 is SE_Rested) ? statusEffect3 : null);
					if (val3 != null)
					{
						int comfortLevel = __instance.GetComfortLevel();
						((StatusEffect)val3).m_ttl = val3.m_baseTTL + (float)(comfortLevel - 1) * val3.m_TTLPerComfortLevel;
					}
				}
			}
		}

		[HarmonyPatch(typeof(SEMan), "RemoveStatusEffect", new Type[]
		{
			typeof(int),
			typeof(bool)
		})]
		private static class KeepRestingWhileInHotSpringPatch
		{
			private static bool Prefix(SEMan __instance, int nameHash)
			{
				Player localPlayer = Player.m_localPlayer;
				if (nameHash == SEMan.s_statusEffectResting && !((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).GetSEMan() == __instance)
				{
					return !HotSpringArea.Contains(localPlayer);
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Player), "GetComfortLevel")]
		private static class ComfortLevelPatch
		{
			private static void Postfix(Player __instance, ref int __result)
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && HotSpringArea.Contains(__instance))
				{
					__result += 5;
				}
			}
		}

		public const string PluginGuid = "com.igentuman.deepnorthstructures";

		public const string PluginName = "Deep North Hot Springs";

		public const string PluginVersion = "1.0.1";

		internal const int HotSpringComfortLevel = 5;

		internal const float RestedDelay = 60f;

		internal const float HealthRegenerationPerSecond = 2f;

		private static readonly HashSet<string> HotSpringPrefabs = new HashSet<string>(StringComparer.Ordinal) { "HotSpring1", "HotSpring2", "HotSpring3" };

		private Harmony? _harmony;

		internal static Plugin Instance { get; private set; } = null;

		private void Awake()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			Instance = this;
			_harmony = new Harmony("com.igentuman.deepnorthstructures");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Deep North Hot Springs 1.0.1 loaded");
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static bool IsHotSpring(string prefabName)
		{
			return HotSpringPrefabs.Contains(prefabName);
		}

		private static IEnumerator GenerateMissingHotSprings(ZoneSystem zoneSystem)
		{
			yield return null;
			MethodInfo generateLocation = AccessTools.Method(typeof(ZoneSystem), "GenerateLocationsTimeSliced", new Type[3]
			{
				typeof(ZoneLocation),
				typeof(Stopwatch),
				typeof(ZPackage)
			}, (Type[])null);
			if (generateLocation == null)
			{
				((BaseUnityPlugin)Instance).Logger.LogError((object)"Could not find Valheim's location generator; Hot Springs were not added.");
				yield break;
			}
			Dictionary<string, ZoneLocation> locations = new Dictionary<string, ZoneLocation>(StringComparer.Ordinal);
			foreach (LocationList allLocationList in LocationList.GetAllLocationLists())
			{
				foreach (ZoneLocation location2 in allLocationList.m_locations)
				{
					if (location2.m_prefab.IsValid && IsHotSpring(location2.m_prefab.Name))
					{
						locations[location2.m_prefab.Name] = location2;
					}
				}
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			foreach (string prefabName in HotSpringPrefabs)
			{
				if (!locations.TryGetValue(prefabName, out ZoneLocation location))
				{
					continue;
				}
				int before = CountLocations(zoneSystem, prefabName);
				if (before < location.m_quantity)
				{
					ZPackage val = new ZPackage();
					if (!(generateLocation.Invoke(zoneSystem, new object[3] { location, stopwatch, val }) is IEnumerator enumerator4))
					{
						((BaseUnityPlugin)Instance).Logger.LogError((object)("Could not start generation for " + prefabName + "."));
						continue;
					}
					yield return enumerator4;
					int num = CountLocations(zoneSystem, prefabName);
					((BaseUnityPlugin)Instance).Logger.LogInfo((object)$"Automatically generated {num - before} missing {prefabName} locations ({num}/{location.m_quantity}).");
					location = null;
				}
			}
		}

		private static int CountLocations(ZoneSystem zoneSystem, string prefabName)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (LocationInstance value in zoneSystem.m_locationInstances.Values)
			{
				if (value.m_location.m_prefabName == prefabName || (value.m_location.m_prefab.IsValid && value.m_location.m_prefab.Name == prefabName))
				{
					num++;
				}
			}
			return num;
		}
	}
}