plugins/Taum/Taum.dll

Decompiled 21 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 Ezomic.Shared;
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("Lead a tamed boar or hen on a crafted halter.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+65d55ce3fad9f553ca8123356c93a24835772f42")]
[assembly: AssemblyProduct("Taum")]
[assembly: AssemblyTitle("Taum")]
[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 Ezomic.Shared
{
	public static class Prefabs
	{
		private sealed class Kept
		{
			internal string Name;

			internal Func<GameObject> Build;

			internal bool Item;

			internal string Tool;

			internal GameObject Prefab;

			internal int Failures;

			internal bool Abandoned;
		}

		private static ManualLogSource _log;

		private static GameObject _holder;

		private static readonly List<Kept> Standing = new List<Kept>();

		private const int MaxFailures = 5;

		private static readonly HashSet<string> Complained = new HashSet<string>();

		private static FieldRef<ZNetScene, Dictionary<int, GameObject>> _named;

		private static MethodInfo _updateRegisters;

		public static ManualLogSource Log
		{
			get
			{
				return _log ?? (_log = Logger.CreateLogSource("Prefabs"));
			}
			set
			{
				_log = value;
			}
		}

		public static Transform Holder
		{
			get
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Expected O, but got Unknown
				if ((Object)(object)_holder == (Object)null)
				{
					_holder = new GameObject(Log.SourceName + "Prefabs");
					_holder.SetActive(false);
					Object.DontDestroyOnLoad((Object)(object)_holder);
				}
				return _holder.transform;
			}
		}

		public static void Keep(string name, Func<GameObject> build, bool item = false, string buildTool = null)
		{
			if (string.IsNullOrEmpty(name))
			{
				throw new ArgumentNullException("name");
			}
			if (build == null)
			{
				throw new ArgumentNullException("build");
			}
			foreach (Kept item2 in Standing)
			{
				if (!(item2.Name != name))
				{
					item2.Build = build;
					item2.Item = item;
					item2.Tool = buildTool;
					item2.Failures = 0;
					item2.Abandoned = false;
					return;
				}
			}
			Standing.Add(new Kept
			{
				Name = name,
				Build = build,
				Item = item,
				Tool = buildTool
			});
			Log.LogInfo((object)("Keeping " + name + " registered."));
		}

		public static void Drop(string name)
		{
			Standing.RemoveAll((Kept k) => k.Name == name);
		}

		public static void Tick()
		{
			if (Standing.Count == 0)
			{
				return;
			}
			ZNetScene instance = ZNetScene.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				for (int i = 0; i < Standing.Count; i++)
				{
					Apply(Standing[i], instance);
				}
			}
		}

		private static void Apply(Kept kept, ZNetScene scene)
		{
			if (!kept.Abandoned && (!((Object)(object)kept.Prefab == (Object)null) || TryBuild(kept)))
			{
				if ((Object)(object)scene.GetPrefab(kept.Name) == (Object)null)
				{
					Register(kept.Prefab);
				}
				if (kept.Item)
				{
					RegisterItem(kept.Prefab);
				}
				if (!string.IsNullOrEmpty(kept.Tool))
				{
					AddToTool(kept.Prefab, kept.Tool);
				}
			}
		}

		private static bool TryBuild(Kept kept)
		{
			GameObject val = null;
			try
			{
				val = kept.Build();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Building " + kept.Name + " failed: " + ex.Message));
			}
			if ((Object)(object)val != (Object)null)
			{
				kept.Prefab = val;
				kept.Failures = 0;
				if (((Object)val).name != kept.Name)
				{
					((Object)val).name = kept.Name;
				}
				return true;
			}
			if (++kept.Failures < 5)
			{
				return false;
			}
			kept.Abandoned = true;
			Log.LogError((object)(kept.Name + " could not be built after " + 5 + " attempts and will not be retried. Anything already built with it in a world is untouched; it simply will not be placeable this session."));
			return false;
		}

		public static bool Known(string name)
		{
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				return (Object)(object)ZNetScene.instance.GetPrefab(name) != (Object)null;
			}
			return false;
		}

		public static GameObject Clone(GameObject source, string name)
		{
			if ((Object)(object)source == (Object)null)
			{
				return null;
			}
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			ZNetView.m_forceDisableInit = true;
			GameObject val;
			try
			{
				val = Object.Instantiate<GameObject>(source, Holder);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
			((Object)val).name = name;
			return val;
		}

		public static GameObject Donor(string commaSeparated, out string chosen)
		{
			chosen = null;
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			string[] array = (commaSeparated ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					GameObject prefab = ZNetScene.instance.GetPrefab(text);
					if (!((Object)(object)prefab == (Object)null))
					{
						chosen = text;
						return prefab;
					}
				}
			}
			return null;
		}

		public static bool Register(GameObject prefab)
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)prefab == (Object)null)
			{
				return false;
			}
			if ((Object)(object)instance.GetPrefab(((Object)prefab).name) != (Object)null)
			{
				return true;
			}
			if (!instance.m_prefabs.Contains(prefab))
			{
				instance.m_prefabs.Add(prefab);
			}
			try
			{
				NamedPrefabs(instance)[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = prefab;
			}
			catch (Exception ex)
			{
				ComplainOnce("scene:" + ((Object)prefab).name, "Could not register " + ((Object)prefab).name + " with ZNetScene: " + ex.Message);
				return false;
			}
			Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ZNetScene."));
			return true;
		}

		public static bool RegisterItem(GameObject prefab)
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)prefab == (Object)null)
			{
				return false;
			}
			if (instance.m_items == null || instance.m_items.Count == 0)
			{
				return false;
			}
			if ((Object)(object)instance.GetItemPrefab(((Object)prefab).name) != (Object)null)
			{
				return true;
			}
			if (!instance.m_items.Contains(prefab))
			{
				instance.m_items.Add(prefab);
			}
			try
			{
				UpdateRegisters(instance);
			}
			catch (Exception ex)
			{
				ComplainOnce("db:" + ((Object)prefab).name, "Could not refresh ObjectDB for " + ((Object)prefab).name + ": " + ex.Message);
				return false;
			}
			Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ObjectDB."));
			return true;
		}

		public static PieceTable ToolPieces(string toolPrefab)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(toolPrefab))
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(toolPrefab);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null)
			{
				return null;
			}
			PieceTable buildPieces = val.m_itemData.m_shared.m_buildPieces;
			if (!((Object)(object)buildPieces != (Object)null) || buildPieces.m_pieces == null)
			{
				return null;
			}
			return buildPieces;
		}

		public static bool InTool(GameObject prefab, string toolPrefab)
		{
			PieceTable val = ToolPieces(toolPrefab);
			if ((Object)(object)val != (Object)null && (Object)(object)prefab != (Object)null)
			{
				return val.m_pieces.Contains(prefab);
			}
			return false;
		}

		public static bool AddToTool(GameObject prefab, string toolPrefab)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			PieceTable val = ToolPieces(toolPrefab);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (val.m_pieces.Contains(prefab))
			{
				return true;
			}
			val.m_pieces.Add(prefab);
			Log.LogInfo((object)(((Object)prefab).name + " added to the " + toolPrefab + "."));
			return true;
		}

		private static void ComplainOnce(string key, string message)
		{
			if (Complained.Add(key))
			{
				Log.LogError((object)message);
			}
		}

		private static Dictionary<int, GameObject> NamedPrefabs(ZNetScene scene)
		{
			if (_named == null)
			{
				_named = AccessTools.FieldRefAccess<ZNetScene, Dictionary<int, GameObject>>("m_namedPrefabs");
			}
			return _named.Invoke(scene);
		}

		private static void UpdateRegisters(ObjectDB db)
		{
			if (_updateRegisters == null)
			{
				_updateRegisters = AccessTools.Method(typeof(ObjectDB), "UpdateRegisters", (Type[])null, (Type[])null);
			}
			if (_updateRegisters == null)
			{
				throw new MissingMethodException("ObjectDB.UpdateRegisters");
			}
			_updateRegisters.Invoke(db, null);
		}
	}
}
namespace Taum
{
	[HarmonyPatch]
	internal static class Follow
	{
		private static bool Wants(Tameable tameable)
		{
			if (!TaumConfig.Enabled.Value || (Object)(object)tameable == (Object)null)
			{
				return false;
			}
			if (!tameable.IsTamed())
			{
				return false;
			}
			return !tameable.m_commandable;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Tameable), "Interact")]
		private static bool AltToFollow(Tameable __instance, Humanoid user, bool hold, bool alt, ref bool __result)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (hold || alt || !Wants(__instance))
			{
				return true;
			}
			if (!Input.GetKey(TaumConfig.FollowKey.Value))
			{
				return true;
			}
			__instance.Command(user, true);
			__result = true;
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Tameable), "GetHoverText")]
		private static void Offer(Tameable __instance, ref string __result)
		{
			//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)
			if (Wants(__instance) && !string.IsNullOrEmpty(__result))
			{
				string text = ((object)TaumConfig.FollowKey.Value/*cast due to .constrained prefix*/).ToString().Replace("Left", "").Replace("Right", "");
				__result += Localization.instance.Localize("\n[<color=yellow><b>" + text + " + $KEY_Use</b></color>] Follow / stay");
			}
		}
	}
	internal static class TaumConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> Verbose;

		internal static ConfigEntry<KeyCode> FollowKey;

		internal static void Bind(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("Taum", "Enabled", true, "Off leaves the plugin loaded and changing nothing.");
			Verbose = cfg.Bind<bool>("Taum", "Verbose", false, "Write each halter put on, taken off and dropped to BepInEx/LogOutput.log.");
			FollowKey = cfg.Bind<KeyCode>("Taum", "FollowKey", (KeyCode)308, "Held with E on a tamed boar or hen to toggle follow/stay. Alt because vanilla already spends Shift+E on renaming and plain E on petting - all three gestures keep their own key.");
		}

		internal static Dictionary<string, string> Table(string raw, char separator = ',')
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrEmpty(raw))
			{
				return dictionary;
			}
			string[] array = raw.Split(new char[1] { separator }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				int num = text.IndexOf(':');
				if (num > 0 && num != text.Length - 1)
				{
					dictionary[text.Substring(0, num).Trim()] = text.Substring(num + 1).Trim();
				}
			}
			return dictionary;
		}
	}
	internal static class TaumPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "OnSpawned")]
		private static void OnSpawned(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && TaumConfig.Enabled.Value && TaumConfig.Verbose.Value)
			{
				TaumPlugin.Log.LogInfo((object)"Player spawned - patches are live.");
			}
		}
	}
	[BepInPlugin("ezomic.valheim.taum", "Taum", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class TaumPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ezomic.valheim.taum";

		public const string PluginName = "Taum";

		public const string PluginVersion = "1.0.0";

		public const string PluginAuthor = "Robbin Thijssen";

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

		internal static ManualLogSource Log;

		private static readonly HashSet<string> Said = new HashSet<string>();

		internal static bool CorePresent;

		private Harmony _harmony;

		internal static void LogOnce(string message)
		{
			if (Log != null && Said.Add(message))
			{
				Log.LogWarning((object)message);
			}
		}

		private void Awake()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			TaumConfig.Bind(((BaseUnityPlugin)this).Config);
			Prefabs.Log = ((BaseUnityPlugin)this).Logger;
			TryRegisterWithCore();
			_harmony = new Harmony("ezomic.valheim.taum");
			_harmony.PatchAll(typeof(TaumPatches));
			_harmony.PatchAll(typeof(Follow));
			Log.LogInfo((object)"Taum 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.taum", "Taum", "1.0.0", ((BaseUnityPlugin)this).Config, (Requirement)0, (Assembly)null);
			Suite.Sync((ConfigEntryBase)(object)TaumConfig.Enabled);
		}

		private void Update()
		{
			if (TaumConfig.Enabled.Value)
			{
				Prefabs.Tick();
			}
		}

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