Decompiled source of InstantTame v1.2.0

InstantTame.dll

Decompiled 2 days ago
using System;
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 BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("InstantTame")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP")]
[assembly: AssemblyProduct("InstantTame")]
[assembly: AssemblyCopyright("Copyright © HP 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d2d6a754-2f4d-48db-b3c7-4e28f9a4bcac")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[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 YouDied.InstantTame
{
	[BepInPlugin("YouDied.InstantTame", "InstantTame", "1.2.0")]
	public class InstantTamePlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "YouDied.InstantTame";

		public const string PluginName = "InstantTame";

		public const string PluginVersion = "1.2.0";

		public static ConfigEntry<KeyboardShortcut> TameKey;

		public static ConfigEntry<KeyboardShortcut> ForceTameKey;

		public static ConfigEntry<int> TameRadius;

		public static ConfigEntry<bool> TameAllInRadius;

		public static ConfigEntry<bool> ForceTameCommandable;

		public static ConfigEntry<bool> TamedSpawnersMakeTameChildren;

		public static ConfigEntry<string> SpawnerNames;

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			TameKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "TameKey", new KeyboardShortcut((KeyCode)57, Array.Empty<KeyCode>()), "Hotkey to instantly tame wild (normally tameable) creatures in range.");
			ForceTameKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ForceTameKey", new KeyboardShortcut((KeyCode)48, Array.Empty<KeyCode>()), "Hotkey to force-tame creatures that are not normally tameable (Draugr, Skeletons, Greydwarves, etc). Pressing it again on an already force-tamed creature untames it (toggle). Normal tameables (TameKey) are never affected by this.");
			TameRadius = ((BaseUnityPlugin)this).Config.Bind<int>("General", "TameRadius", 10, new ConfigDescription("Radius (meters) to search for creatures.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			TameAllInRadius = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "TameAllInRadius", false, "If true, tames every eligible creature in radius. If false, tames only the nearest one.");
			ForceTameCommandable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceTameCommandable", true, "If true, force-tamed creatures get a Tameable component added so they can be given follow/stay commands. If false, they just stop being hostile.");
			TamedSpawnersMakeTameChildren = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawners", "TamedSpawnersMakeTameChildren", true, "If true, creatures freshly spawned near a tamed spawner creature (e.g. the ticks a tamed Gjall spawns) are tamed automatically instead of spawning hostile.");
			SpawnerNames = ((BaseUnityPlugin)this).Config.Bind<string>("Spawners", "SpawnerNames", "$enemy_gjall", "Comma-separated Character m_name values of tamed creatures whose newly spawned children should be tamed. Default is the Gjall. Add more names separated by commas (e.g. \"$enemy_gjall,$enemy_seeker_brood\").");
			try
			{
				Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "YouDied.InstantTame");
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Harmony patches applied successfully.");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Harmony patching FAILED: {arg}");
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} v{1} loaded. Build: {2:yyyyMMdd-HHmm}", "InstantTame", "1.2.0", DateTime.Now));
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	public static class Player_Update_Patch
	{
		private static readonly MethodInfo TameableTameMethod = AccessTools.Method(typeof(Tameable), "Tame", (Type[])null, (Type[])null);

		private static readonly HashSet<ZDOID> ForceTamedIDs = new HashSet<ZDOID>();

		private static float _lastDiagLog = -999f;

		private const float InheritScanInterval = 0.5f;

		private const float InheritFreshSeconds = 6f;

		private const float InheritSpawnerRadius = 30f;

		private static float _lastInheritScan = -999f;

		private static string _spawnerNamesRaw;

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

		private static bool _inheritErrorLogged;

		private static void Postfix(Player __instance)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			bool flag = (Object)(object)__instance == (Object)(object)Player.m_localPlayer;
			bool flag2 = (Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus();
			bool flag3 = Console.IsVisible();
			bool flag4 = InventoryGui.IsVisible();
			KeyboardShortcut value;
			if (Time.time - _lastDiagLog > 3f && flag)
			{
				_lastDiagLog = Time.time;
				ManualLogSource log = InstantTamePlugin.Log;
				string text = $"[Diag] localPlayer={flag} chatFocus={flag2} consoleVisible={flag3} ";
				object arg = flag4;
				value = InstantTamePlugin.TameKey.Value;
				string text2 = $"inventoryVisible={arg} tameKeyDown={((KeyboardShortcut)(ref value)).IsDown()} ";
				value = InstantTamePlugin.ForceTameKey.Value;
				log.LogInfo((object)(text + text2 + $"forceTameKeyDown={((KeyboardShortcut)(ref value)).IsDown()}"));
			}
			if (!flag)
			{
				return;
			}
			InheritTameFromSpawners();
			if (!flag2 && !flag3 && !flag4)
			{
				value = InstantTamePlugin.TameKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					InstantTamePlugin.Log.LogInfo((object)"[Diag] TameKey IsDown() fired - calling TameNearby.");
					TameNearby(__instance);
				}
				value = InstantTamePlugin.ForceTameKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					InstantTamePlugin.Log.LogInfo((object)"[Diag] ForceTameKey IsDown() fired - calling ToggleForceTameNearby.");
					ToggleForceTameNearby(__instance);
				}
			}
		}

		private static void InheritTameFromSpawners()
		{
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			if (!InstantTamePlugin.TamedSpawnersMakeTameChildren.Value || Time.time - _lastInheritScan < 0.5f)
			{
				return;
			}
			_lastInheritScan = Time.time;
			try
			{
				string text = InstantTamePlugin.SpawnerNames.Value ?? "";
				if (text != _spawnerNamesRaw)
				{
					_spawnerNamesRaw = text;
					_spawnerNames.Clear();
					string[] array = text.Split(new char[1] { ',' });
					foreach (string text2 in array)
					{
						string text3 = text2.Trim();
						if (text3.Length > 0)
						{
							_spawnerNames.Add(text3);
						}
					}
				}
				if (_spawnerNames.Count == 0)
				{
					return;
				}
				List<Character> allCharacters = Character.GetAllCharacters();
				if (allCharacters == null || allCharacters.Count == 0)
				{
					return;
				}
				List<Character> list = new List<Character>();
				foreach (Character item in allCharacters)
				{
					if (!((Object)(object)item == (Object)null) && !item.IsDead() && item.IsTamed() && _spawnerNames.Contains(item.m_name))
					{
						list.Add(item);
					}
				}
				if (list.Count == 0)
				{
					return;
				}
				List<Character> list2 = new List<Character>();
				foreach (Character item2 in allCharacters)
				{
					if ((Object)(object)item2 == (Object)null || item2.IsPlayer() || item2.IsDead() || item2.IsTamed() || (Object)(object)((Component)item2).GetComponent<Tameable>() != (Object)null)
					{
						continue;
					}
					BaseAI component = ((Component)item2).GetComponent<BaseAI>();
					if ((Object)(object)component == (Object)null || component.GetTimeSinceSpawned().TotalSeconds > 6.0)
					{
						continue;
					}
					foreach (Character item3 in list)
					{
						if (Vector3.Distance(((Component)item3).transform.position, ((Component)item2).transform.position) <= 30f)
						{
							list2.Add(item2);
							break;
						}
					}
				}
				foreach (Character item4 in list2)
				{
					if (ForceTame(item4))
					{
						ForceTamedIDs.Add(item4.GetZDOID());
						InstantTamePlugin.Log.LogInfo((object)("Tamed freshly spawned " + item4.m_name + " next to a tamed spawner."));
					}
				}
			}
			catch (Exception arg)
			{
				if (!_inheritErrorLogged)
				{
					_inheritErrorLogged = true;
					InstantTamePlugin.Log.LogWarning((object)$"InheritTameFromSpawners failed (logged once): {arg}");
				}
			}
		}

		private static void TameNearby(Player player)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			float num = InstantTamePlugin.TameRadius.Value;
			Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, num);
			HashSet<Character> hashSet = new HashSet<Character>();
			List<Character> list = new List<Character>();
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				Character componentInParent = ((Component)val).GetComponentInParent<Character>();
				if (!((Object)(object)componentInParent == (Object)null) && hashSet.Add(componentInParent) && !componentInParent.IsPlayer() && !componentInParent.IsDead() && !componentInParent.IsTamed() && !((Object)(object)((Component)componentInParent).GetComponent<Tameable>() == (Object)null))
				{
					list.Add(componentInParent);
				}
			}
			if (list.Count == 0)
			{
				((Character)player).Message((MessageType)2, "No tameable creatures in range.", 0, (Sprite)null, false);
				return;
			}
			list = list.OrderBy((Character c) => Vector3.Distance(((Component)player).transform.position, ((Component)c).transform.position)).ToList();
			List<Character> source = (InstantTamePlugin.TameAllInRadius.Value ? list : new List<Character> { list[0] });
			int num2 = source.Count(NormalTame);
			((Character)player).Message((MessageType)2, num2 switch
			{
				1 => "Creature tamed!", 
				0 => "Tame attempt failed (see log).", 
				_ => $"Tamed {num2} creature(s).", 
			}, 0, (Sprite)null, false);
		}

		private static void ToggleForceTameNearby(Player player)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			float num = InstantTamePlugin.TameRadius.Value;
			Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, num);
			HashSet<Character> hashSet = new HashSet<Character>();
			List<Character> list = new List<Character>();
			List<Character> list2 = new List<Character>();
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				Character componentInParent = ((Component)val).GetComponentInParent<Character>();
				if (!((Object)(object)componentInParent == (Object)null) && hashSet.Add(componentInParent) && !componentInParent.IsPlayer() && !componentInParent.IsDead() && (!((Object)(object)((Component)componentInParent).GetComponent<Tameable>() != (Object)null) || ForceTamedIDs.Contains(componentInParent.GetZDOID())))
				{
					if (componentInParent.IsTamed() && ForceTamedIDs.Contains(componentInParent.GetZDOID()))
					{
						list2.Add(componentInParent);
					}
					else if (!componentInParent.IsTamed())
					{
						list.Add(componentInParent);
					}
				}
			}
			if (list.Count > 0)
			{
				RunForceTame(player, list);
			}
			else if (list2.Count > 0)
			{
				RunForceUntame(player, list2);
			}
			else
			{
				((Character)player).Message((MessageType)2, "No force-tameable creatures in range.", 0, (Sprite)null, false);
			}
		}

		private static void RunForceTame(Player player, List<Character> candidates)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			candidates = candidates.OrderBy((Character c) => Vector3.Distance(((Component)player).transform.position, ((Component)c).transform.position)).ToList();
			List<Character> list = (InstantTamePlugin.TameAllInRadius.Value ? candidates : new List<Character> { candidates[0] });
			int num = 0;
			foreach (Character item in list)
			{
				if (ForceTame(item))
				{
					ForceTamedIDs.Add(item.GetZDOID());
					num++;
				}
			}
			((Character)player).Message((MessageType)2, (num == 0) ? "Tame attempt failed (see log)." : $"Force-tamed {num} creature(s).", 0, (Sprite)null, false);
		}

		private static void RunForceUntame(Player player, List<Character> candidates)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			candidates = candidates.OrderBy((Character c) => Vector3.Distance(((Component)player).transform.position, ((Component)c).transform.position)).ToList();
			List<Character> list = (InstantTamePlugin.TameAllInRadius.Value ? candidates : new List<Character> { candidates[0] });
			int num = 0;
			foreach (Character item in list)
			{
				if (ForceUntame(item))
				{
					ForceTamedIDs.Remove(item.GetZDOID());
					num++;
				}
			}
			((Character)player).Message((MessageType)2, (num == 0) ? "Untame attempt failed (see log)." : $"Untamed {num} creature(s).", 0, (Sprite)null, false);
		}

		private static bool NormalTame(Character c)
		{
			Tameable component = ((Component)c).GetComponent<Tameable>();
			if ((Object)(object)component == (Object)null || TameableTameMethod == null)
			{
				return false;
			}
			try
			{
				TameableTameMethod.Invoke(component, null);
				return true;
			}
			catch (Exception ex)
			{
				InstantTamePlugin.Log.LogWarning((object)("NormalTame failed on " + c.m_name + ": " + ex.Message));
				return false;
			}
		}

		private static bool ForceTame(Character c)
		{
			MonsterAI component = ((Component)c).GetComponent<MonsterAI>();
			if ((Object)(object)component != (Object)null)
			{
				component.MakeTame();
				if (InstantTamePlugin.ForceTameCommandable.Value)
				{
					Tameable val = ((Component)c).gameObject.AddComponent<Tameable>();
					val.m_commandable = true;
				}
				return true;
			}
			c.SetTamed(true);
			return true;
		}

		private static bool ForceUntame(Character c)
		{
			try
			{
				c.SetTamed(false);
				MonsterAI component = ((Component)c).GetComponent<MonsterAI>();
				Tameable component2 = ((Component)c).GetComponent<Tameable>();
				if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null)
				{
					component.SetFollowTarget((GameObject)null);
					Object.Destroy((Object)(object)component2);
				}
				return true;
			}
			catch (Exception ex)
			{
				InstantTamePlugin.Log.LogWarning((object)("ForceUntame failed on " + c.m_name + ": " + ex.Message));
				return false;
			}
		}
	}
}