Decompiled source of OdinHorse Tame Fix v1.0.2

OdinHorseTameFix.dll

Decompiled 9 hours ago
using System;
using System.Collections;
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 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("OdinHorseTameFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("OdinHorseTameFix")]
[assembly: AssemblyTitle("OdinHorseTameFix")]
[assembly: AssemblyVersion("1.0.2.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 ValheimLiteni.OdinHorseTameFix
{
	[BepInPlugin("ValheimLiteni.OdinHorseTameFix", "OdinHorse Tame Fix", "1.0.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private class FleeState
		{
			public Character character;

			public float lowHealth;

			public bool hurtUnreachable;

			public bool notAlerted;

			public bool afraidOfFire;

			public bool avoidFire;

			public bool calm;
		}

		[HarmonyPatch(typeof(Tameable), "Awake")]
		private static class TameableAwake
		{
			[HarmonyPriority(0)]
			private static void Postfix(Tameable __instance)
			{
				try
				{
					string key = ((Object)((Component)__instance).gameObject).name.Replace("(Clone)", "").Trim();
					if (!Configs().TryGetValue(key, out var value))
					{
						return;
					}
					Register(__instance);
					if (TryValue(value, "TamingTime", out var value2))
					{
						__instance.m_tamingTime = Convert.ToSingle(value2);
					}
					if (TryValue(value, "FedDuration", out var value3))
					{
						__instance.m_fedDuration = Convert.ToSingle(value3);
					}
					object value4;
					bool flag = (__instance.m_startsTamed = TryValue(value, "SpawnsTamed", out value4) && value4.ToString() == "On");
					ZNetView val = tameNview.Invoke(__instance);
					if (!((Object)(object)val == (Object)null) && val.IsValid())
					{
						ZDO zDO = val.GetZDO();
						float num = default(float);
						if (val.IsOwner() && zDO.GetFloat(ZDOVars.s_tameTimeLeft, ref num) && num > __instance.m_tamingTime)
						{
							zDO.Set(ZDOVars.s_tameTimeLeft, __instance.m_tamingTime);
						}
						Character val2 = tameCharacter.Invoke(__instance);
						if (flag && (Object)(object)val2 != (Object)null && !val2.IsTamed())
						{
							val2.SetTamed(true);
						}
					}
				}
				catch (Exception ex)
				{
					Log.LogError((object)("Tameable.Awake: " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
		private static class MonsterAIUpdateAI
		{
			[HarmonyPriority(800)]
			private static void Prefix(MonsterAI __instance)
			{
				if (horses.TryGetValue(__instance, out var value))
				{
					bool flag = neverFlee.Value && (Object)(object)value.character != (Object)null && value.character.IsTamed();
					if (flag != value.calm)
					{
						value.calm = flag;
						__instance.m_fleeIfLowHealth = (flag ? 0f : value.lowHealth);
						__instance.m_fleeIfHurtWhenTargetCantBeReached = !flag && value.hurtUnreachable;
						__instance.m_fleeIfNotAlerted = !flag && value.notAlerted;
						((BaseAI)__instance).m_afraidOfFire = !flag && value.afraidOfFire;
						((BaseAI)__instance).m_avoidFire = !flag && value.avoidFire;
					}
				}
			}
		}

		[HarmonyPatch]
		private static class BetterArcherySpine
		{
			private static FieldInfo drawing;

			private static FieldInfo timer;

			private static MethodBase TargetMethod()
			{
				Type type = AccessTools.TypeByName("BetterArchery.Player_LateUpdate_Patch");
				Type type2 = AccessTools.TypeByName("BetterArchery.PlayerBody");
				drawing = ((type2 == null) ? null : AccessTools.Field(type2, "onBowDrawing"));
				timer = ((type == null) ? null : AccessTools.Field(type, "spineTimer"));
				if (!(type == null))
				{
					return AccessTools.Method(type, "Postfix", (Type[])null, (Type[])null);
				}
				return null;
			}

			private static bool Prepare(MethodBase original)
			{
				if (original != null)
				{
					return true;
				}
				if (TargetMethod() == null)
				{
					return false;
				}
				if (drawing == null || timer == null)
				{
					Log.LogWarning((object)"BetterArchery changed - its spine turn is left alone");
					return false;
				}
				return true;
			}

			private static void Prefix(Player __0, out bool __state)
			{
				__state = false;
				if (archeryNoSpineTurn.Value && !((Object)(object)__0 == (Object)null) && !((Object)(object)__0 != (Object)(object)Player.m_localPlayer) && ((Character)__0).IsRiding())
				{
					__state = (bool)drawing.GetValue(null);
					drawing.SetValue(null, false);
					timer.SetValue(null, 0f);
				}
			}

			private static void Postfix(bool __state)
			{
				if (__state)
				{
					drawing.SetValue(null, true);
				}
			}
		}

		public const string Guid = "ValheimLiteni.OdinHorseTameFix";

		public const string Version = "1.0.2";

		public const string OdinHorseGuid = "Raelaziel.OdinHorse";

		public const string BetterArcheryGuid = "ishid4.mods.betterarchery";

		internal static ManualLogSource Log;

		private static ConfigEntry<bool> neverFlee;

		private static ConfigEntry<bool> archeryNoSpineTurn;

		private static Dictionary<string, object> configs;

		private static FieldInfo customGet;

		private static readonly FieldRef<Tameable, ZNetView> tameNview = AccessTools.FieldRefAccess<Tameable, ZNetView>("m_nview");

		private static readonly FieldRef<Tameable, Character> tameCharacter = AccessTools.FieldRefAccess<Tameable, Character>("m_character");

		private static readonly ConditionalWeakTable<MonsterAI, FleeState> horses = new ConditionalWeakTable<MonsterAI, FleeState>();

		private void Awake()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			neverFlee = ((BaseUnityPlugin)this).Config.Bind<bool>("Horse", "Tamed Horses Never Flee", true, "If on, tamed OdinHorse horses stand their ground: they don't run away when hurt, low on health, unable to reach the enemy, or near fire. Wild horses are not changed. The horse's AI runs in the game of the player next to it, so that player's setting counts.");
			archeryNoSpineTurn = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "BetterArchery No Spine Turn While Riding", true, "If on and BetterArchery is installed, its upper-body turn while drawing a bow is skipped on horseback, so it no longer fights OdinHorse's mounted aiming (the rider flickered left and right). Only affects your own character.");
			new Harmony("ValheimLiteni.OdinHorseTameFix").PatchAll(typeof(Plugin).Assembly);
		}

		private static void Register(Tameable tameable)
		{
			MonsterAI component = ((Component)tameable).GetComponent<MonsterAI>();
			if (!((Object)(object)component == (Object)null) && !horses.TryGetValue(component, out var _))
			{
				horses.Add(component, new FleeState
				{
					character = (tameCharacter.Invoke(tameable) ?? ((Component)tameable).GetComponent<Character>()),
					lowHealth = component.m_fleeIfLowHealth,
					hurtUnreachable = component.m_fleeIfHurtWhenTargetCantBeReached,
					notAlerted = component.m_fleeIfNotAlerted,
					afraidOfFire = ((BaseAI)component).m_afraidOfFire,
					avoidFire = ((BaseAI)component).m_avoidFire
				});
			}
		}

		private static Dictionary<string, object> Configs()
		{
			if (configs != null)
			{
				return configs;
			}
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			try
			{
				if (!Chainloader.PluginInfos.TryGetValue("Raelaziel.OdinHorse", out var value) || (Object)(object)value.Instance == (Object)null)
				{
					return configs = dictionary;
				}
				Type type = ((object)value.Instance).GetType().Assembly.GetType("CreatureManager.Creature");
				if (type == null)
				{
					Log.LogWarning((object)"OdinHorse has no CreatureManager.Creature - nothing to fix");
					return configs = dictionary;
				}
				IEnumerable enumerable = AccessTools.Field(type, "registeredCreatures")?.GetValue(null) as IEnumerable;
				IDictionary dictionary2 = AccessTools.Field(type, "creatureConfigs")?.GetValue(null) as IDictionary;
				FieldInfo fieldInfo = AccessTools.Field(type, "Prefab");
				if (enumerable == null || dictionary2 == null || fieldInfo == null)
				{
					Log.LogWarning((object)"OdinHorse's CreatureManager changed - nothing to fix");
					return configs = dictionary;
				}
				foreach (object item in enumerable)
				{
					object? value2 = fieldInfo.GetValue(item);
					GameObject val = (GameObject)((value2 is GameObject) ? value2 : null);
					if ((Object)(object)val != (Object)null && dictionary2.Contains(item))
					{
						dictionary[((Object)val).name] = dictionary2[item];
					}
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"reading OdinHorse's creatures: {arg}");
			}
			if (dictionary.Count > 0)
			{
				configs = dictionary;
				Log.LogInfo((object)("taming settings applied to: " + string.Join(", ", configs.Keys)));
			}
			return dictionary;
		}

		private static bool TryValue(object creatureConfig, string name, out object value)
		{
			value = null;
			object obj = AccessTools.Field(creatureConfig.GetType(), name)?.GetValue(creatureConfig);
			if (obj == null)
			{
				return false;
			}
			if (customGet == null || customGet.DeclaringType != obj.GetType())
			{
				customGet = AccessTools.Field(obj.GetType(), "get");
			}
			if (!(customGet?.GetValue(obj) is Delegate obj2))
			{
				return false;
			}
			value = obj2.DynamicInvoke();
			return value != null;
		}
	}
}