Decompiled source of TameAll v1.2.0

plugins/TameAll.dll

Decompiled a week ago
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
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.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("TameAll")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0")]
[assembly: AssemblyProduct("TameAll")]
[assembly: AssemblyTitle("TameAll")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.0.0")]
[module: UnverifiableCode]
[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 Oddhatter.TameAll
{
	[BepInPlugin("com.oddhatter.tameall", "TameAll", "1.2.0")]
	[BepInProcess("valheim.exe")]
	public sealed class TameAllPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.oddhatter.tameall";

		public const string Name = "TameAll";

		public const string Version = "1.2.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyCode> TameKey;

		internal static ConfigEntry<KeyCode> StayAllKey;

		internal static ConfigEntry<float> MaxDistance;

		internal static ConfigEntry<float> MaxHealthPercent;

		internal static ConfigEntry<bool> AllowBosses;

		internal static ConfigEntry<bool> FollowAfterTaming;

		private const float ConfigReloadDelay = 0.5f;

		private Harmony _harmony;

		private FileSystemWatcher _configWatcher;

		private volatile bool _configFileChanged;

		private float _configReloadAt = -1f;

		private bool _configLockedLogged;

		private void Awake()
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. When false the key does nothing (creatures you already tamed stay tame).");
			TameKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "TameKey", (KeyCode)106, "Look at a creature and press this key to tame it instantly. Press it on a monster you tamed to make it wild again.");
			StayAllKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "StayAllKey", (KeyCode)104, "Press this key to make every creature that is following you stay where it is (they stay tame). Same as pressing Use on each one; press Use on one to make it follow again.");
			MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxDistance", 20f, new ConfigDescription("How far away the creature can be, in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			MaxHealthPercent = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxHealthPercent", 100f, new ConfigDescription("The creature's health must be at or below this percent of its maximum. 100 = any health; 25 = you have to wear it down to a quarter first.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			AllowBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowBosses", false, "true: bosses can be tamed too. A tamed boss never dies, so you won't get its trophy or the world progress it unlocks.");
			FollowAfterTaming = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FollowAfterTaming", true, "true: a creature starts following you as soon as it's tamed. Press Use on it to toggle follow / stay.");
			_harmony = new Harmony("com.oddhatter.tameall");
			_harmony.PatchAll(typeof(TameAllPlugin).Assembly);
			foreach (MethodBase patchedMethod in _harmony.GetPatchedMethods())
			{
				Log.LogInfo((object)("Patched " + patchedMethod.DeclaringType?.Name + "." + patchedMethod.Name));
			}
			WatchConfigFile();
			Log.LogInfo((object)"TameAll 1.2.0 loaded.");
		}

		private void Update()
		{
			Taming.UpdateKey();
			if (_configFileChanged)
			{
				_configFileChanged = false;
				_configReloadAt = Time.realtimeSinceStartup + 0.5f;
			}
			if (_configReloadAt >= 0f && Time.realtimeSinceStartup >= _configReloadAt)
			{
				_configReloadAt = -1f;
				ReloadConfigFromDisk();
			}
		}

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

		private void WatchConfigFile()
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			_configWatcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath))
			{
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
				IncludeSubdirectories = false
			};
			_configWatcher.Changed += OnConfigFileEvent;
			_configWatcher.Created += OnConfigFileEvent;
			_configWatcher.Renamed += OnConfigFileEvent;
			_configWatcher.EnableRaisingEvents = true;
		}

		private void OnConfigFileEvent(object sender, FileSystemEventArgs e)
		{
			_configFileChanged = true;
		}

		private void ReloadConfigFromDisk()
		{
			if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
			{
				return;
			}
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				_configLockedLogged = false;
				Log.LogInfo((object)"Configuration reloaded from disk.");
			}
			catch (IOException ex)
			{
				if (!_configLockedLogged)
				{
					_configLockedLogged = true;
					Log.LogWarning((object)("Config file busy, will retry: " + ex.Message));
				}
				_configReloadAt = Time.realtimeSinceStartup + 0.5f;
			}
			catch (Exception ex2)
			{
				Log.LogError((object)("Failed to reload configuration: " + ex2.Message));
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}
	}
	internal static class Taming
	{
		internal static void UpdateKey()
		{
			//IL_0029: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !TameAllPlugin.Enabled.Value || !((Character)localPlayer).TakeInput())
			{
				return;
			}
			if (ZInput.GetKeyDown(TameAllPlugin.StayAllKey.Value, false))
			{
				StayAll(localPlayer);
			}
			else if (ZInput.GetKeyDown(TameAllPlugin.TameKey.Value, false))
			{
				Character hoverCreature = localPlayer.GetHoverCreature();
				string text = CheckTarget(localPlayer, hoverCreature);
				if (text != null)
				{
					((Character)localPlayer).Message((MessageType)1, text, 0, (Sprite)null, false);
				}
				else if (hoverCreature.IsTamed())
				{
					Untame(localPlayer, hoverCreature);
				}
				else
				{
					Tame(localPlayer, hoverCreature);
				}
			}
		}

		private static void StayAll(Player player)
		{
			string playerName = player.GetPlayerName();
			int num = 0;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!allCharacter.IsTamed())
				{
					continue;
				}
				BaseAI baseAI = allCharacter.GetBaseAI();
				MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null);
				if (val != null)
				{
					Tameable component = ((Component)allCharacter).GetComponent<Tameable>();
					ZDO zDO = ((BaseAI)val).m_nview.GetZDO();
					if (!((Object)(object)component == (Object)null) && zDO != null && ((Object)(object)val.GetFollowTarget() == (Object)(object)((Component)player).gameObject || zDO.GetString(ZDOVars.s_follow, "") == playerName))
					{
						component.Command((Humanoid)(object)player, false);
						num++;
					}
				}
			}
			((Character)player).Message((MessageType)2, num switch
			{
				1 => "1 creature stays", 
				0 => "Nothing is following you", 
				_ => $"{num} creatures stay", 
			}, 0, (Sprite)null, false);
		}

		private static string CheckTarget(Player player, Character target)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)target == (Object)null || target.IsPlayer())
			{
				return "Look at a creature to tame it";
			}
			if (!(target.GetBaseAI() is MonsterAI))
			{
				return target.m_name + " can't be tamed";
			}
			if (target.IsTamed() && !IsTameableAddedByMod(((Component)target).gameObject))
			{
				return target.m_name + " is already tame";
			}
			if (target.IsBoss() && !target.IsTamed() && !TameAllPlugin.AllowBosses.Value)
			{
				return "Bosses can't be tamed (AllowBosses is off)";
			}
			if (Vector3.Distance(((Component)player).transform.position, ((Component)target).transform.position) > TameAllPlugin.MaxDistance.Value)
			{
				return target.m_name + " is too far away";
			}
			if (target.IsTamed())
			{
				return null;
			}
			if (((target.GetMaxHealth() > 0f) ? (target.GetHealth() / target.GetMaxHealth() * 100f) : 0f) > TameAllPlugin.MaxHealthPercent.Value + 0.01f)
			{
				return $"{target.m_name} is too healthy to tame (needs {TameAllPlugin.MaxHealthPercent.Value:0}% health or less)";
			}
			return null;
		}

		private static void Tame(Player player, Character target)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)target).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				component.ClaimOwnership();
				MonsterAI val = (MonsterAI)target.GetBaseAI();
				Tameable val2 = EnsureTameable(val);
				val.MakeTame();
				val.SetDespawnInDay(false);
				val.SetEventCreature(false);
				((BaseAI)val).SetHuntPlayer(false);
				val2.m_tamedEffect.Create(((Component)target).transform.position, ((Component)target).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
				if (TameAllPlugin.FollowAfterTaming.Value && (Object)(object)val.GetFollowTarget() == (Object)null)
				{
					val2.Command((Humanoid)(object)player, false);
				}
				((Character)player).Message((MessageType)2, target.m_name + " $hud_tamedone", 0, (Sprite)null, false);
				TameAllPlugin.Log.LogInfo((object)("Tamed " + Utils.GetPrefabName(((Component)target).gameObject) + "."));
			}
		}

		private static void Untame(Player player, Character target)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			ZNetView component = ((Component)target).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				component.ClaimOwnership();
				MonsterAI val = (MonsterAI)target.GetBaseAI();
				val.SetFollowTarget((GameObject)null);
				((BaseAI)val).SetPatrolPoint();
				component.GetZDO().Set(ZDOVars.s_follow, "");
				target.SetTamed(false);
				RemoveTameable(val);
				((Character)player).Message((MessageType)2, target.m_name + " is wild again", 0, (Sprite)null, false);
				TameAllPlugin.Log.LogInfo((object)("Untamed " + Utils.GetPrefabName(((Component)target).gameObject) + "."));
			}
		}

		internal static bool IsTameableAddedByMod(GameObject creature)
		{
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(Utils.GetPrefabName(creature)) : null);
			if ((Object)(object)val != (Object)null)
			{
				return (Object)(object)val.GetComponent<Tameable>() == (Object)null;
			}
			return false;
		}

		private static void RemoveTameable(MonsterAI ai)
		{
			Tameable component = ((Component)ai).GetComponent<Tameable>();
			if (!((Object)(object)component == (Object)null))
			{
				((BaseAI)ai).m_nview.Unregister("Command");
				((BaseAI)ai).m_nview.Unregister("SetName");
				((BaseAI)ai).m_nview.Unregister("RPC_UnSummon");
				Character character = ((BaseAI)ai).m_character;
				character.m_onDeath = (Action)Delegate.Remove(character.m_onDeath, new Action(component.OnDeath));
				ai.m_onConsumedItem = (Action<ItemDrop>)Delegate.Remove(ai.m_onConsumedItem, new Action<ItemDrop>(component.OnConsumedItem));
				((BaseAI)ai).m_tamable = null;
				Object.Destroy((Object)(object)component);
			}
		}

		internal static Tameable EnsureTameable(MonsterAI ai)
		{
			Tameable component = ((Component)ai).GetComponent<Tameable>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			component = ((Component)ai).gameObject.AddComponent<Tameable>();
			component.m_commandable = true;
			component.m_fedDuration = float.MaxValue;
			((BaseAI)ai).m_tamable = component;
			return component;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "Awake")]
	internal static class MonsterAI_Awake_Patch
	{
		private static void Postfix(MonsterAI __instance)
		{
			ZDO val = (((Object)(object)((BaseAI)__instance).m_nview != (Object)null) ? ((BaseAI)__instance).m_nview.GetZDO() : null);
			if (val != null && val.GetBool(ZDOVars.s_tamed, false) && (Object)(object)((Component)__instance).GetComponent<Tameable>() == (Object)null)
			{
				Taming.EnsureTameable(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Tameable), "TamingUpdate")]
	internal static class Tameable_TamingUpdate_Patch
	{
		private static bool Prefix(Tameable __instance)
		{
			return !Taming.IsTameableAddedByMod(((Component)__instance).gameObject);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}