Decompiled source of Dvala v1.0.0

plugins/Dvala/Dvala.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Ezomic.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Thijssen Software")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")]
[assembly: AssemblyDescription("A dungeon left alone long enough fills back up.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ace478022558d798e713eeb53a1a25018d215fcb")]
[assembly: AssemblyProduct("Dvala")]
[assembly: AssemblyTitle("Dvala")]
[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 Dvala
{
	internal static class Dungeons
	{
		private static readonly int StampKey = StringExtensionMethods.GetStableHashCode("dvala_day");

		private static readonly List<DungeonGenerator> Managed = new List<DungeonGenerator>();

		private static float _managedAt = -999f;

		internal static IEnumerable<DungeonGenerator> Live()
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				yield break;
			}
			DungeonGenerator[] array = Object.FindObjectsOfType<DungeonGenerator>();
			foreach (DungeonGenerator val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					ZNetView component = ((Component)val).GetComponent<ZNetView>();
					if (!((Object)(object)component == (Object)null) && component.IsValid())
					{
						yield return val;
					}
				}
			}
		}

		internal static bool Wanted(DungeonGenerator generator)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			return (generator.m_themes & DvalaConfig.Themes()) > 0;
		}

		internal static int Stamped(DungeonGenerator generator)
		{
			ZNetView component = ((Component)generator).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return -1;
			}
			return component.GetZDO().GetInt(StampKey, -1);
		}

		internal static void Stamp(DungeonGenerator generator, int day)
		{
			ZNetView component = ((Component)generator).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				component.ClaimOwnership();
				component.GetZDO().Set(StampKey, day, false);
			}
		}

		internal static bool Inside(Vector3 point)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (Time.realtimeSinceStartup - _managedAt > 2f)
			{
				_managedAt = Time.realtimeSinceStartup;
				Managed.Clear();
				foreach (DungeonGenerator item in Live())
				{
					if (Wanted(item))
					{
						Managed.Add(item);
					}
				}
			}
			if (Managed.Count == 0)
			{
				return false;
			}
			for (int i = 0; i < Managed.Count; i++)
			{
				DungeonGenerator val = Managed[i];
				if (!((Object)(object)val == (Object)null) && Inside(val, point))
				{
					return true;
				}
			}
			return false;
		}

		internal unsafe static string Describe(DungeonGenerator generator)
		{
			//IL_0030: 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)
			ZNetView component = ((Component)generator).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return ((Object)generator).name;
			}
			string name = ((Object)generator).name;
			ZDOID uid = component.GetZDO().m_uid;
			return name + " " + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString();
		}

		internal static int Today()
		{
			EnvMan instance = EnvMan.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				return instance.GetDay();
			}
			return -1;
		}

		internal static bool Occupied(DungeonGenerator generator)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null) && Inside(generator, ((Component)allPlayer).transform.position))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool Inside(DungeonGenerator generator, Vector3 point)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(0f, DvalaConfig.RoomPadding.Value);
			Room[] componentsInChildren = ((Component)generator).GetComponentsInChildren<Room>();
			foreach (Room val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Vector3 val2 = Quaternion.Inverse(((Component)val).transform.rotation) * (point - ((Component)val).transform.position);
					Vector3 val3 = Vector3Int.op_Implicit(val.m_size) * 0.5f;
					if (Mathf.Abs(val2.x) <= val3.x + num && Mathf.Abs(val2.y) <= val3.y + num && Mathf.Abs(val2.z) <= val3.z + num)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	internal static class DvalaConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> Days;

		internal static ConfigEntry<bool> SkipOccupied;

		internal static ConfigEntry<float> RoomPadding;

		internal static ConfigEntry<float> CheckSeconds;

		internal static ConfigEntry<bool> Crypts;

		internal static ConfigEntry<bool> Caves;

		internal static ConfigEntry<bool> Mines;

		internal static ConfigEntry<bool> Ashlands;

		internal static ConfigEntry<bool> HildirRooms;

		internal static ConfigEntry<bool> Camps;

		internal static ConfigEntry<bool> KeepVeins;

		internal static ConfigEntry<bool> Verbose;

		internal static void Bind(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("Dvala", "Enabled", true, "Off leaves the plugin loaded and changing nothing.");
			Days = cfg.Bind<int>("Dvala", "Days", 30, "In-game days a dungeon must be left alone before it fills back up. A day is 20 minutes of a running world, so 30 is about 10 hours of play - and on a dedicated server the clock keeps turning while nobody is on. The count starts when Dvala first sees the dungeon, never at day zero, so installing this on an old world does not restock everything in it at once.");
			SkipOccupied = cfg.Bind<bool>("Dvala", "SkipOccupied", true, "Never restock a dungeon with a player inside it. Off means a dungeon can refill around you mid-run, which re-arms the spawners you just cleared and puts fresh loot in chests you already emptied. It also makes the day it happens unpredictable, since it turns on where people happen to be.");
			RoomPadding = cfg.Bind<float>("Dvala", "RoomPadding", 1.5f, "Metres of slack when asking whether a point is inside a dungeon room. Only raise it if a player standing in a doorway is treated as outside; a large value starts catching the ground above a shallow crypt.");
			CheckSeconds = cfg.Bind<float>("Dvala", "CheckSeconds", 30f, "How often to look, in real seconds. This is not how often anything resets - that is Days. Lower costs nothing measurable and only changes how soon after midnight a due dungeon notices.");
			Crypts = cfg.Bind<bool>("Contents", "Crypts", true, "Burial chambers, forest crypts and sunken crypts.");
			Caves = cfg.Bind<bool>("Contents", "Caves", true, "Troll caves and the frost caves in the mountains.");
			Mines = cfg.Bind<bool>("Contents", "Mines", true, "Mistlands infested mines. The Queen's own room is never included.");
			Ashlands = cfg.Bind<bool>("Contents", "Ashlands", true, "Ashlands ruins and fortresses.");
			HildirRooms = cfg.Bind<bool>("Contents", "HildirRooms", true, "Hildir's three: the Sealed Tower, the Howling Cavern and the Smouldering Tomb. On, so their mini-bosses return on the timer like anything else. If you use Lur, its horn still works and still wakes one immediately - what this takes away is the horn being the only way. Turn it off to keep that.");
			Camps = cfg.Bind<bool>("Contents", "Camps", false, "Fuling camps, Meadows villages and farms. These are built by the dungeon generator but they sit on the surface where people build, so restocking them means re-arming spawners next door to somebody's house.");
			KeepVeins = cfg.Bind<bool>("Contents", "KeepVeins", true, "Stop a fully mined vein from deleting itself, so it can be restocked later. Without this, a vein you strip bare is gone for good and only the chests, spawners and pickables in that room come back. What is left behind is an invisible husk you can walk through, holding nothing, until the timer fills it back in - and it costs the world exactly what a half-mined vein already costs it, one saved object. Interiors only: veins on the surface are never held back, whatever else is switched on here.");
			Verbose = cfg.Bind<bool>("Dvala", "Verbose", false, "Write what was found and what was changed to BepInEx/LogOutput.log. Off unless something looks wrong; it is one line per dungeon and one per item.");
		}

		internal static Theme Themes()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			Theme val = (Theme)0;
			if (Crypts.Value)
			{
				val = (Theme)(val | 0xB);
			}
			if (Caves.Value)
			{
				val = (Theme)(val | 4);
			}
			if (Mines.Value)
			{
				val = (Theme)(val | 0x80);
			}
			if (Ashlands.Value)
			{
				val = (Theme)(val | 0x3000);
			}
			if (HildirRooms.Value)
			{
				val = (Theme)(val | 0xE00);
			}
			if (Camps.Value)
			{
				val = (Theme)(val | 0x70);
			}
			return val;
		}
	}
	internal static class DvalaPatches
	{
		[HarmonyPatch(typeof(MineRock5), "AllDestroyed")]
		[HarmonyPostfix]
		private static void KeepMinedVeins(MineRock5 __instance, ref bool __result)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (__result && DvalaConfig.Enabled.Value && DvalaConfig.KeepVeins.Value && !((Object)(object)__instance == (Object)null) && Dungeons.Inside(((Component)__instance).transform.position))
			{
				__result = false;
			}
		}
	}
	[BepInPlugin("ezomic.valheim.dvala", "Dvala", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class DvalaPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ezomic.valheim.dvala";

		public const string PluginName = "Dvala";

		public const string PluginVersion = "1.0.0";

		public const string PluginAuthor = "Robbin Thijssen";

		private const string CoreGuid = "ezomic.valheim.core";

		internal static ManualLogSource Log;

		internal static bool CorePresent;

		private Harmony _harmony;

		private float _since;

		private bool _faulted;

		private void Awake()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			DvalaConfig.Bind(((BaseUnityPlugin)this).Config);
			TryRegisterWithCore();
			_harmony = new Harmony("ezomic.valheim.dvala");
			_harmony.PatchAll(typeof(DvalaPatches));
			Log.LogInfo((object)"Dvala 1.0.0 by Robbin Thijssen - ready.");
		}

		private void TryRegisterWithCore()
		{
			CorePresent = Chainloader.PluginInfos.ContainsKey("ezomic.valheim.core");
			if (!CorePresent)
			{
				Log.LogInfo((object)"Core not installed - running standalone, without the version gate.");
			}
			else
			{
				RegisterWithCore();
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void RegisterWithCore()
		{
			Suite.Register("ezomic.valheim.dvala", "Dvala", "1.0.0", ((BaseUnityPlugin)this).Config, (Requirement)0, (Assembly)null);
			Suite.Sync((ConfigEntryBase)(object)DvalaConfig.Enabled);
		}

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

		private void Update()
		{
			if (!DvalaConfig.Enabled.Value)
			{
				return;
			}
			_since += Time.deltaTime;
			if (_since < Mathf.Max(1f, DvalaConfig.CheckSeconds.Value))
			{
				return;
			}
			_since = 0f;
			try
			{
				Sweep();
			}
			catch (Exception ex)
			{
				if (!_faulted)
				{
					_faulted = true;
					Log.LogError((object)("Dvala's sweep threw and will keep being attempted, but this is reported once: " + ex));
				}
			}
		}

		private void Sweep()
		{
			if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)EnvMan.instance == (Object)null)
			{
				return;
			}
			int num = Dungeons.Today();
			if (num < 0)
			{
				return;
			}
			int num2 = Mathf.Max(1, DvalaConfig.Days.Value);
			foreach (DungeonGenerator item in Dungeons.Live())
			{
				if (!Dungeons.Wanted(item))
				{
					continue;
				}
				int num3 = Dungeons.Stamped(item);
				if (num3 < 0)
				{
					Dungeons.Stamp(item, num);
				}
				else if (num - num3 >= num2 && (!DvalaConfig.SkipOccupied.Value || !Dungeons.Occupied(item)))
				{
					Restore.Counts counts = Restore.Dungeon(item);
					Dungeons.Stamp(item, num);
					int num4 = Dungeons.Stamped(item);
					if (DvalaConfig.Verbose.Value || counts.Total > 0)
					{
						ManualLogSource log = Log;
						string[] obj = new string[7]
						{
							"Restocked ",
							Dungeons.Describe(item),
							" after ",
							(num - num3).ToString(),
							" days: ",
							null,
							null
						};
						Restore.Counts counts2 = counts;
						obj[5] = counts2.ToString();
						obj[6] = ".";
						log.LogInfo((object)string.Concat(obj));
					}
					if (num4 != num)
					{
						Log.LogWarning((object)("Stamp did not stick on " + Dungeons.Describe(item) + ": wrote " + num + ", reads back " + num4 + ". It will restock again on the next sweep."));
					}
				}
			}
		}
	}
	internal static class Restore
	{
		internal struct Counts
		{
			internal int Pickables;

			internal int Chests;

			internal int Veins;

			internal int Spawners;

			internal int PickablesSeen;

			internal int ChestsSeen;

			internal int VeinsSeen;

			internal int SpawnersSeen;

			internal int Total => Pickables + Chests + Veins + Spawners;

			public override string ToString()
			{
				return "pickables " + Pickables + "/" + PickablesSeen + ", chests " + Chests + "/" + ChestsSeen + ", veins " + Veins + "/" + VeinsSeen + ", spawners " + Spawners + "/" + SpawnersSeen;
			}
		}

		internal static Counts Dungeon(DungeonGenerator generator)
		{
			Counts counts = default(Counts);
			Pickables(generator, ref counts);
			Chests(generator, ref counts);
			Veins(generator, ref counts);
			Spawners(generator, ref counts);
			return counts;
		}

		private static void Pickables(DungeonGenerator generator, ref Counts counts)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			Pickable[] array = Object.FindObjectsOfType<Pickable>();
			foreach (Pickable val in array)
			{
				if ((Object)(object)val == (Object)null || !Dungeons.Inside(generator, ((Component)val).transform.position))
				{
					continue;
				}
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					counts.PickablesSeen++;
					if (component.GetZDO().GetBool(ZDOVars.s_picked, false))
					{
						component.ClaimOwnership();
						component.InvokeRPC(ZNetView.Everybody, "RPC_SetPicked", new object[1] { false });
						counts.Pickables++;
					}
				}
			}
		}

		private static void Chests(DungeonGenerator generator, ref Counts counts)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Container[] array = Object.FindObjectsOfType<Container>();
			Piece val2 = default(Piece);
			foreach (Container val in array)
			{
				if ((Object)(object)val == (Object)null || !Dungeons.Inside(generator, ((Component)val).transform.position) || (((Component)val).TryGetComponent<Piece>(ref val2) && val2.GetCreator() != 0L) || val.m_defaultItems == null || val.m_defaultItems.m_drops == null || val.m_defaultItems.m_drops.Count == 0)
				{
					continue;
				}
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid())
				{
					continue;
				}
				Inventory inventory = val.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				counts.ChestsSeen++;
				if (inventory.NrOfItems() > 0)
				{
					continue;
				}
				component.ClaimOwnership();
				foreach (ItemData dropListItem in val.m_defaultItems.GetDropListItems())
				{
					inventory.AddItem(dropListItem);
				}
				counts.Chests++;
			}
		}

		private static void Veins(DungeonGenerator generator, ref Counts counts)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			MineRock5[] array = Object.FindObjectsOfType<MineRock5>();
			foreach (MineRock5 val in array)
			{
				if ((Object)(object)val == (Object)null || !Dungeons.Inside(generator, ((Component)val).transform.position))
				{
					continue;
				}
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid())
				{
					continue;
				}
				counts.VeinsSeen++;
				if (component.GetZDO().GetString(ZDOVars.s_health, "").Length == 0)
				{
					continue;
				}
				int num = ((Component)val).GetComponentsInChildren<Collider>(true).Length;
				if (num != 0)
				{
					float num2 = val.m_health + (float)Game.m_worldLevel * val.m_health * Game.instance.m_worldLevelMineHPMultiplier;
					ZPackage val2 = new ZPackage();
					val2.Write(num);
					for (int j = 0; j < num; j++)
					{
						val2.Write(num2);
					}
					string text = Convert.ToBase64String(val2.GetArray());
					if (!(component.GetZDO().GetString(ZDOVars.s_health, "") == text))
					{
						component.ClaimOwnership();
						component.GetZDO().Set(ZDOVars.s_health, text);
						counts.Veins++;
					}
				}
			}
		}

		private static void Spawners(DungeonGenerator generator, ref Counts counts)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Invalid comparison between Unknown and I4
			//IL_0068: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			CreatureSpawner[] array = Object.FindObjectsOfType<CreatureSpawner>();
			foreach (CreatureSpawner val in array)
			{
				if ((Object)(object)val == (Object)null || !Dungeons.Inside(generator, ((Component)val).transform.position))
				{
					continue;
				}
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid())
				{
					continue;
				}
				counts.SpawnersSeen++;
				ZDO zDO = component.GetZDO();
				if ((int)zDO.GetConnectionType() == 3)
				{
					ZDOID connectionZDOID = zDO.GetConnectionZDOID((ConnectionType)3);
					if (((ZDOID)(ref connectionZDOID)).IsNone() || ZDOMan.instance.GetZDO(connectionZDOID) == null)
					{
						component.ClaimOwnership();
						zDO.SetConnection((ConnectionType)0, ZDOID.None);
						counts.Spawners++;
					}
				}
			}
		}
	}
}