Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of InstantTame v1.2.0
InstantTame.dll
Decompiled 2 days agousing 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; } } } }