Decompiled source of AnimalFeedGuard v1.0.0

BepInEx/plugins/AnimalFeedGuard/AnimalFeedGuard.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("AnimalFeedGuard")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c5dddd1db62830a856b662e8e41fafb08d31c869")]
[assembly: AssemblyProduct("AnimalFeedGuard")]
[assembly: AssemblyTitle("AnimalFeedGuard")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 AnimalFeedGuard
{
	internal static class FeedRules
	{
		internal static bool WithinRadius(float squaredDistance, float radius)
		{
			if (radius > 0f && squaredDistance >= 0f)
			{
				return squaredDistance <= radius * radius;
			}
			return false;
		}

		internal static bool SameFood(string? item, string? animalFood)
		{
			if (!string.IsNullOrEmpty(item))
			{
				return string.Equals(item, animalFood, StringComparison.Ordinal);
			}
			return false;
		}
	}
	[BepInPlugin("com.ziluck.valheim.animalfeedguard", "Animal Feed Guard", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private readonly struct FeedingAnimal
		{
			internal readonly Vector3 Position;

			internal readonly List<ItemDrop> Foods;

			internal FeedingAnimal(Vector3 position, List<ItemDrop> foods)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Position = position;
				Foods = foods;
			}
		}

		[HarmonyPatch(typeof(Player), "AutoPickup")]
		private static class AutomaticPickupPatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				FieldInfo field = AccessTools.Field(typeof(ItemDrop), "m_autoPickup");
				MethodInfo filter = AccessTools.Method(typeof(Plugin), "CanAutoPickup", (Type[])null, (Type[])null);
				List<CodeInstruction> list = instructions.ToList();
				if (list.Count((CodeInstruction i) => CodeInstructionExtensions.LoadsField(i, field, false)) != 1)
				{
					throw new InvalidOperationException("Expected one ItemDrop.m_autoPickup check. Game version or another pickup mod changed this method.");
				}
				foreach (CodeInstruction item in list)
				{
					if (CodeInstructionExtensions.LoadsField(item, field, false))
					{
						item.opcode = OpCodes.Call;
						item.operand = filter;
					}
					yield return item;
				}
			}
		}

		public const string Guid = "com.ziluck.valheim.animalfeedguard";

		private static ConfigEntry<bool> protectionEnabled = null;

		private static ConfigEntry<float> radius = null;

		private Harmony? harmony;

		private static int cachedFrame = -1;

		private static readonly List<FeedingAnimal> animals = new List<FeedingAnimal>();

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			protectionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Leave edible animal feed on the ground near tamed animals during automatic pickup. Manual pickup is unaffected.");
			radius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Protection Radius", 5f, new ConfigDescription("Maximum distance in metres between the dropped item and a living tamed animal that can eat it. 0 disables protection. Includes animals that are already fed. Uses full 3D distance, without a line-of-sight requirement.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			try
			{
				harmony = new Harmony("com.ziluck.valheim.animalfeedguard");
				harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Animal Feed Guard ready. Protection radius: {radius.Value}m.");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Animal Feed Guard could not patch automatic pickup. Feed protection is NOT active: {arg}");
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
			}
		}

		private void OnDestroy()
		{
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			animals.Clear();
			cachedFrame = -1;
		}

		internal static bool CanAutoPickup(ItemDrop item)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)item) || !item.m_autoPickup)
			{
				return false;
			}
			if (!protectionEnabled.Value || radius.Value <= 0f)
			{
				return true;
			}
			string text = item.m_itemData?.m_shared?.m_name;
			if (string.IsNullOrEmpty(text))
			{
				return true;
			}
			RefreshAnimals();
			Vector3 position = ((Component)item).transform.position;
			foreach (FeedingAnimal animal in animals)
			{
				Vector3 val = position - animal.Position;
				if (!FeedRules.WithinRadius(((Vector3)(ref val)).sqrMagnitude, radius.Value))
				{
					continue;
				}
				foreach (ItemDrop food in animal.Foods)
				{
					if (Object.op_Implicit((Object)(object)food) && FeedRules.SameFood(text, food.m_itemData?.m_shared?.m_name))
					{
						return false;
					}
				}
			}
			return true;
		}

		private static void RefreshAnimals()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (cachedFrame == Time.frameCount)
			{
				return;
			}
			cachedFrame = Time.frameCount;
			animals.Clear();
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (Object.op_Implicit((Object)(object)allCharacter) && !allCharacter.IsDead() && allCharacter.IsTamed())
				{
					MonsterAI component = ((Component)allCharacter).GetComponent<MonsterAI>();
					if (Object.op_Implicit((Object)(object)component) && component.m_consumeItems != null && component.m_consumeItems.Count != 0)
					{
						animals.Add(new FeedingAnimal(((Component)allCharacter).transform.position, component.m_consumeItems));
					}
				}
			}
		}
	}
}