Decompiled source of TameableDeer v1.0.8

BepInEx/plugins/nick008tameabledeer.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Nick008TameableDeer;

[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("nick008.tameabledeer", "nick008tameabledeer", "1.0.8")]
[BepInProcess("valheim.exe")]
public sealed class Nick008TameableDeerPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	private static class ZNetSceneAwakePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ZNetScene __instance)
		{
			try
			{
				GameObject fawn = RegisterFawnBeforeSceneAwake(__instance);
				ConfigureDeerCoreBeforeSceneAwake(__instance, fawn);
			}
			catch (Exception ex)
			{
				if (Log != null)
				{
					Log.LogError((object)("[nick008tameabledeer] Early Deer/fawn setup failed: " + ex));
				}
			}
		}
	}

	public const string PluginGuid = "nick008.tameabledeer";

	public const string PluginName = "nick008tameabledeer";

	public const string PluginVersion = "1.0.8";

	private const float TamingTime = 1500f;

	private const float FedDuration = 300f;

	private const float GrowTime = 2000f;

	private const int MaxNearbyDeer = 7;

	private const float PregnancyChance = 0.66f;

	private const float PregnancyDuration = 90f;

	internal static ManualLogSource Log;

	private int configuredSceneId = int.MinValue;

	private GameObject offspringRoot;

	internal static Nick008TameableDeerPlugin Instance;

	private static readonly string[] FoodPrefabNames = new string[8] { "Raspberry", "Blueberries", "Carrot", "Turnip", "Mushroom", "Cloudberry", "Onion", "apple" };

	private void Awake()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		new Harmony("nick008.tameabledeer").PatchAll();
		Log.LogInfo((object)"[nick008tameabledeer] 1.0.8 loaded. Waiting for Valheim creature database...");
		((MonoBehaviour)this).StartCoroutine(WatchForCreatureDatabase());
	}

	internal static GameObject RegisterFawnBeforeSceneAwake(ZNetScene scene)
	{
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Expected O, but got Unknown
		if ((Object)(object)scene == (Object)null)
		{
			return null;
		}
		for (int i = 0; i < scene.m_prefabs.Count; i++)
		{
			GameObject val = scene.m_prefabs[i];
			if ((Object)(object)val != (Object)null && ((Object)val).name == "nick008_DeerFawn")
			{
				return val;
			}
		}
		GameObject val2 = null;
		for (int j = 0; j < scene.m_prefabs.Count; j++)
		{
			GameObject val3 = scene.m_prefabs[j];
			if ((Object)(object)val3 != (Object)null && ((Object)val3).name == "Deer")
			{
				val2 = val3;
				break;
			}
		}
		if ((Object)(object)val2 == (Object)null)
		{
			return null;
		}
		Nick008TameableDeerPlugin instance = Instance;
		if ((Object)(object)instance == (Object)null)
		{
			return null;
		}
		if ((Object)(object)instance.offspringRoot == (Object)null)
		{
			instance.offspringRoot = new GameObject("nick008tameabledeer_offspring_root");
			instance.offspringRoot.transform.SetParent(((Component)instance).transform, false);
			instance.offspringRoot.SetActive(false);
		}
		GameObject val4 = Object.Instantiate<GameObject>(val2, instance.offspringRoot.transform);
		((Object)val4).name = "nick008_DeerFawn";
		val4.transform.localScale = val4.transform.localScale * 0.55f;
		CharacterDrop component = val4.GetComponent<CharacterDrop>();
		if ((Object)(object)component != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component);
		}
		Procreation component2 = val4.GetComponent<Procreation>();
		if ((Object)(object)component2 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component2);
		}
		Tameable component3 = val4.GetComponent<Tameable>();
		if ((Object)(object)component3 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component3);
		}
		Growup val5 = val4.GetComponent<Growup>();
		if ((Object)(object)val5 == (Object)null)
		{
			val5 = val4.AddComponent<Growup>();
		}
		val5.m_grownPrefab = val2;
		val5.m_growTime = 2000f;
		scene.m_prefabs.Add(val4);
		if (Log != null)
		{
			Log.LogInfo((object)"[nick008tameabledeer] Early-registered persistent fawn prefab before ZNetScene.Awake.");
		}
		return val4;
	}

	private static GameObject FindPrefabInSceneList(ZNetScene scene, string prefabName)
	{
		if ((Object)(object)scene == (Object)null || scene.m_prefabs == null)
		{
			return null;
		}
		for (int i = 0; i < scene.m_prefabs.Count; i++)
		{
			GameObject val = scene.m_prefabs[i];
			if ((Object)(object)val != (Object)null && ((Object)val).name == prefabName)
			{
				return val;
			}
		}
		return null;
	}

	internal static bool ConfigureDeerCoreBeforeSceneAwake(ZNetScene scene, GameObject fawn)
	{
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = FindPrefabInSceneList(scene, "Deer");
		GameObject val2 = FindPrefabInSceneList(scene, "Boar");
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)fawn == (Object)null)
		{
			return false;
		}
		Nick008DeerAI component = val.GetComponent<Nick008DeerAI>();
		Tameable component2 = val.GetComponent<Tameable>();
		Procreation component3 = val.GetComponent<Procreation>();
		if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && (Object)(object)component3 != (Object)null)
		{
			component3.m_offspring = fawn;
			return true;
		}
		Character component4 = val.GetComponent<Character>();
		AnimalAI component5 = val.GetComponent<AnimalAI>();
		MonsterAI component6 = val2.GetComponent<MonsterAI>();
		Tameable component7 = val2.GetComponent<Tameable>();
		Procreation component8 = val2.GetComponent<Procreation>();
		if ((Object)(object)component4 == (Object)null || (Object)(object)component5 == (Object)null || (Object)(object)component6 == (Object)null || (Object)(object)component7 == (Object)null || (Object)(object)component8 == (Object)null)
		{
			return false;
		}
		Humanoid val3 = val.GetComponent<Humanoid>();
		if ((Object)(object)val3 == (Object)null)
		{
			val3 = val.AddComponent<Humanoid>();
		}
		CopyPublicFields((Component)(object)val3, (Component)(object)component4);
		if ((Object)(object)((Character)val3).m_eye == (Object)null && val.transform.childCount > 0)
		{
			((Character)val3).m_eye = val.transform.GetChild(0);
		}
		Nick008DeerAI nick008DeerAI = val.GetComponent<Nick008DeerAI>();
		if ((Object)(object)nick008DeerAI == (Object)null)
		{
			nick008DeerAI = val.AddComponent<Nick008DeerAI>();
		}
		CopyPublicFields((Component)(object)nick008DeerAI, (Component)(object)component6);
		CopyPublicFields((Component)(object)nick008DeerAI, (Component)(object)component5);
		nick008DeerAI.SetCharacter((Character)(object)val3);
		if (((BaseAI)nick008DeerAI).m_randomMoveInterval > 30f)
		{
			((BaseAI)nick008DeerAI).m_randomMoveInterval = 30f;
		}
		Object.DestroyImmediate((Object)(object)component5);
		Object.DestroyImmediate((Object)(object)component4);
		((Character)val3).m_group = "Deer";
		Tameable val4 = val.GetComponent<Tameable>();
		if ((Object)(object)val4 == (Object)null)
		{
			val4 = val.AddComponent<Tameable>();
		}
		EffectList val5 = ((component7.m_sootheEffect != null) ? component7.m_sootheEffect : component7.m_tamedEffect);
		val4.m_petEffect = (EffectList)((val5 != null) ? ((object)CloneEffectList(val5)) : ((object)new EffectList()));
		val4.m_sootheEffect = CloneEffectList(component7.m_sootheEffect);
		val4.m_tamedEffect = CloneEffectList(component7.m_tamedEffect);
		val4.m_commandable = true;
		val4.m_startsTamed = false;
		val4.m_tamingTime = 1500f;
		val4.m_fedDuration = 300f;
		((MonsterAI)nick008DeerAI).m_consumeRange = 1.4f;
		((MonsterAI)nick008DeerAI).m_consumeSearchInterval = 10f;
		((MonsterAI)nick008DeerAI).m_consumeSearchRange = 15f;
		((MonsterAI)nick008DeerAI).m_enableHuntPlayer = false;
		((MonsterAI)nick008DeerAI).m_consumeItems = new List<ItemDrop>();
		Procreation val6 = val.GetComponent<Procreation>();
		if ((Object)(object)val6 == (Object)null)
		{
			val6 = val.AddComponent<Procreation>();
		}
		val6.m_maxCreatures = 7;
		val6.m_pregnancyChance = 0.66f;
		val6.m_pregnancyDuration = 90f;
		if (val6.m_updateInterval < 20f)
		{
			val6.m_updateInterval = 20f;
		}
		val6.m_partnerCheckRange = 4f;
		val6.m_totalCheckRange = 10f;
		val6.m_offspring = fawn;
		EffectList effectList = GetEffectList(component8, "m_loveEffects");
		EffectList effectList2 = GetEffectList(component8, "m_birthEffects");
		if (effectList != null)
		{
			SetEffectList(val6, "m_loveEffects", CloneEffectList(effectList));
		}
		if (effectList2 != null)
		{
			SetEffectList(val6, "m_birthEffects", CloneEffectList(effectList2));
		}
		if (Log != null)
		{
			Log.LogInfo((object)"[nick008tameabledeer] Early-configured Deer prefab before ZNetScene.Awake.");
		}
		return true;
	}

	private IEnumerator WatchForCreatureDatabase()
	{
		while (true)
		{
			try
			{
				ZNetScene instance = ZNetScene.instance;
				ObjectDB instance2 = ObjectDB.instance;
				if ((Object)(object)instance != (Object)null && (Object)(object)instance2 != (Object)null && (Object)(object)instance.GetPrefab("Deer") != (Object)null && (Object)(object)instance.GetPrefab("Boar") != (Object)null && (Object)(object)instance2.GetItemPrefab("Raspberry") != (Object)null)
				{
					int instanceID = ((Object)instance).GetInstanceID();
					if (instanceID != configuredSceneId && ConfigureDeer(instance, instance2))
					{
						configuredSceneId = instanceID;
					}
					if (configuredSceneId == instanceID)
					{
						ApplyDeerSounds(instance);
					}
					TameableNeckBoarCompat.ApplyIfNeeded(instance);
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[nick008tameabledeer] Setup watcher error: " + ex));
			}
			yield return (object)new WaitForSeconds(1f);
		}
	}

	private bool ConfigureDeer(ZNetScene scene, ObjectDB db)
	{
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		GameObject prefab = scene.GetPrefab("Deer");
		GameObject prefab2 = scene.GetPrefab("Boar");
		if ((Object)(object)prefab == (Object)null || (Object)(object)prefab2 == (Object)null)
		{
			return false;
		}
		if ((Object)(object)prefab.GetComponent<Nick008DeerAI>() != (Object)null && (Object)(object)prefab.GetComponent<Tameable>() != (Object)null && (Object)(object)prefab.GetComponent<Procreation>() != (Object)null)
		{
			Nick008DeerAI component = prefab.GetComponent<Nick008DeerAI>();
			int num = PopulateFoods(component, db);
			GameObject prefab3 = scene.GetPrefab("nick008_DeerFawn");
			if ((Object)(object)prefab3 != (Object)null)
			{
				prefab.GetComponent<Procreation>().m_offspring = prefab3;
			}
			ApplyDeerSounds(scene);
			Log.LogInfo((object)("[nick008tameabledeer] SUCCESS: early Deer setup completed. Foods found: " + num + "."));
			return true;
		}
		Character component2 = prefab.GetComponent<Character>();
		AnimalAI component3 = prefab.GetComponent<AnimalAI>();
		MonsterAI component4 = prefab2.GetComponent<MonsterAI>();
		Tameable component5 = prefab2.GetComponent<Tameable>();
		Procreation component6 = prefab2.GetComponent<Procreation>();
		if ((Object)(object)component2 == (Object)null || (Object)(object)component3 == (Object)null || (Object)(object)component4 == (Object)null || (Object)(object)component5 == (Object)null || (Object)(object)component6 == (Object)null)
		{
			Log.LogError((object)"[nick008tameabledeer] Vanilla Deer/Boar components were not found. Nothing was changed.");
			return false;
		}
		GameObject val = CreateFawnPrefab(scene, prefab);
		if ((Object)(object)val == (Object)null)
		{
			Log.LogError((object)"[nick008tameabledeer] Could not create/register deer offspring. Nothing was changed.");
			return false;
		}
		try
		{
			Humanoid val2 = prefab.GetComponent<Humanoid>();
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = prefab.AddComponent<Humanoid>();
			}
			CopyPublicFields((Component)(object)val2, (Component)(object)component2);
			if ((Object)(object)((Character)val2).m_eye == (Object)null && prefab.transform.childCount > 0)
			{
				((Character)val2).m_eye = prefab.transform.GetChild(0);
			}
			Nick008DeerAI nick008DeerAI = prefab.GetComponent<Nick008DeerAI>();
			if ((Object)(object)nick008DeerAI == (Object)null)
			{
				nick008DeerAI = prefab.AddComponent<Nick008DeerAI>();
			}
			CopyPublicFields((Component)(object)nick008DeerAI, (Component)(object)component4);
			CopyPublicFields((Component)(object)nick008DeerAI, (Component)(object)component3);
			nick008DeerAI.SetCharacter((Character)(object)val2);
			if (((BaseAI)nick008DeerAI).m_randomMoveInterval > 30f)
			{
				((BaseAI)nick008DeerAI).m_randomMoveInterval = 30f;
			}
			Object.DestroyImmediate((Object)(object)component3);
			Object.DestroyImmediate((Object)(object)component2);
			((Character)val2).m_group = "Deer";
			Tameable val3 = prefab.GetComponent<Tameable>();
			if ((Object)(object)val3 == (Object)null)
			{
				val3 = prefab.AddComponent<Tameable>();
			}
			EffectList val4 = ((component5.m_sootheEffect != null) ? component5.m_sootheEffect : component5.m_tamedEffect);
			val3.m_petEffect = (EffectList)((val4 != null) ? ((object)CloneEffectList(val4)) : ((object)new EffectList()));
			val3.m_sootheEffect = CloneEffectList(component5.m_sootheEffect);
			val3.m_tamedEffect = CloneEffectList(component5.m_tamedEffect);
			val3.m_commandable = true;
			val3.m_startsTamed = false;
			val3.m_tamingTime = 1500f;
			val3.m_fedDuration = 300f;
			((MonsterAI)nick008DeerAI).m_consumeRange = 1.4f;
			((MonsterAI)nick008DeerAI).m_consumeSearchInterval = 10f;
			((MonsterAI)nick008DeerAI).m_consumeSearchRange = 15f;
			((MonsterAI)nick008DeerAI).m_enableHuntPlayer = false;
			int num2 = PopulateFoods(nick008DeerAI, db);
			Procreation val5 = prefab.GetComponent<Procreation>();
			if ((Object)(object)val5 == (Object)null)
			{
				val5 = prefab.AddComponent<Procreation>();
			}
			val5.m_maxCreatures = 7;
			val5.m_pregnancyChance = 0.66f;
			val5.m_pregnancyDuration = 90f;
			if (val5.m_updateInterval < 20f)
			{
				val5.m_updateInterval = 20f;
			}
			val5.m_partnerCheckRange = 4f;
			val5.m_totalCheckRange = 10f;
			val5.m_offspring = val;
			EffectList effectList = GetEffectList(component6, "m_loveEffects");
			EffectList effectList2 = GetEffectList(component6, "m_birthEffects");
			if (effectList != null)
			{
				SetEffectList(val5, "m_loveEffects", CloneEffectList(effectList));
			}
			if (effectList2 != null)
			{
				SetEffectList(val5, "m_birthEffects", CloneEffectList(effectList2));
			}
			ApplyDeerSounds(scene);
			Log.LogInfo((object)"[nick008tameabledeer] SUCCESS: Deer is tameable and breedable.");
			Log.LogInfo((object)("[nick008tameabledeer] Foods found: " + num2 + ". Taming: 25 min. Fawn growth: 33m20s."));
			Log.LogInfo((object)"[nick008tameabledeer] Deer sounds: idle call on pet/mating, alerted call on birth.");
			Log.LogInfo((object)"[nick008tameabledeer] Only prefab 'Deer' was modified; Boar and Neck were not touched.");
			return true;
		}
		catch (Exception ex)
		{
			Log.LogError((object)("[nick008tameabledeer] Deer setup failed: " + ex));
			return false;
		}
	}

	private static int PopulateFoods(Nick008DeerAI deerAI, ObjectDB db)
	{
		if ((Object)(object)deerAI == (Object)null || (Object)(object)db == (Object)null)
		{
			return 0;
		}
		if (((MonsterAI)deerAI).m_consumeItems == null)
		{
			((MonsterAI)deerAI).m_consumeItems = new List<ItemDrop>();
		}
		else
		{
			((MonsterAI)deerAI).m_consumeItems.Clear();
		}
		int num = 0;
		for (int i = 0; i < FoodPrefabNames.Length; i++)
		{
			GameObject itemPrefab = db.GetItemPrefab(FoodPrefabNames[i]);
			if (!((Object)(object)itemPrefab == (Object)null))
			{
				ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
				if ((Object)(object)component != (Object)null && !((MonsterAI)deerAI).m_consumeItems.Contains(component))
				{
					((MonsterAI)deerAI).m_consumeItems.Add(component);
					num++;
				}
			}
		}
		return num;
	}

	private void ApplyDeerSounds(ZNetScene scene)
	{
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)scene == (Object)null)
		{
			return;
		}
		GameObject prefab = scene.GetPrefab("Deer");
		if ((Object)(object)prefab == (Object)null)
		{
			return;
		}
		GameObject prefab2 = scene.GetPrefab("sfx_deer_idle");
		GameObject prefab3 = scene.GetPrefab("sfx_deer_alerted");
		GameObject prefab4 = scene.GetPrefab("vfx_boar_love");
		GameObject prefab5 = scene.GetPrefab("vfx_boar_birth");
		Tameable component = prefab.GetComponent<Tameable>();
		if ((Object)(object)component != (Object)null && (Object)(object)prefab2 != (Object)null)
		{
			if (EffectListContains(component.m_petEffect, "btn_fx_neck_pet") || EffectListContains(component.m_petEffect, "fx_boar_pet"))
			{
				EffectList val = ((component.m_sootheEffect != null) ? component.m_sootheEffect : component.m_tamedEffect);
				component.m_petEffect = (EffectList)((val != null) ? ((object)CloneEffectList(val)) : ((object)new EffectList()));
			}
			EnsureEffectPrefab(component.m_petEffect, prefab2);
		}
		Procreation component2 = prefab.GetComponent<Procreation>();
		if ((Object)(object)component2 != (Object)null)
		{
			EffectList orCreateEffectList = GetOrCreateEffectList(component2, "m_loveEffects");
			EffectList orCreateEffectList2 = GetOrCreateEffectList(component2, "m_birthEffects");
			if ((Object)(object)prefab2 != (Object)null)
			{
				ReplaceNamedEffectPrefab(orCreateEffectList, "sfx_boar_love", prefab2);
				ReplaceNamedEffectPrefab(orCreateEffectList, "btn_sfx_neck_love", prefab2);
				EnsureEffectPrefab(orCreateEffectList, prefab2);
			}
			if ((Object)(object)prefab4 != (Object)null)
			{
				ReplaceNamedEffectPrefab(orCreateEffectList, "btn_vfx_hare_love", prefab4);
				EnsureEffectPrefab(orCreateEffectList, prefab4);
			}
			if ((Object)(object)prefab3 != (Object)null)
			{
				ReplaceNamedEffectPrefab(orCreateEffectList2, "sfx_boar_birth", prefab3);
				ReplaceNamedEffectPrefab(orCreateEffectList2, "btn_sfx_neck_birth", prefab3);
				EnsureEffectPrefab(orCreateEffectList2, prefab3);
			}
			if ((Object)(object)prefab5 != (Object)null)
			{
				ReplaceNamedEffectPrefab(orCreateEffectList2, "btn_vfx_hare_birth", prefab5);
				EnsureEffectPrefab(orCreateEffectList2, prefab5);
			}
		}
	}

	private static EffectList GetEffectList(object owner, string fieldName)
	{
		if (owner == null)
		{
			return null;
		}
		FieldInfo field = owner.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (!(field != null))
		{
			return null;
		}
		object? value = field.GetValue(owner);
		return (EffectList)((value is EffectList) ? value : null);
	}

	private static bool SetEffectList(object owner, string fieldName, EffectList value)
	{
		if (owner == null)
		{
			return false;
		}
		FieldInfo field = owner.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		field.SetValue(owner, value);
		return true;
	}

	private static EffectList GetOrCreateEffectList(object owner, string fieldName)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Expected O, but got Unknown
		EffectList effectList = GetEffectList(owner, fieldName);
		if (effectList != null)
		{
			return effectList;
		}
		effectList = new EffectList();
		SetEffectList(owner, fieldName, effectList);
		return effectList;
	}

	private static object CloneEffectData(object effectData)
	{
		if (effectData == null)
		{
			return null;
		}
		Type type = effectData.GetType();
		if (type.IsValueType)
		{
			return effectData;
		}
		MethodInfo method = typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
		if (!(method != null))
		{
			return effectData;
		}
		return method.Invoke(effectData, null);
	}

	private static EffectList CloneEffectList(EffectList source)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		if (source == null)
		{
			return null;
		}
		EffectList val = new EffectList();
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return val;
		}
		if (!(field.GetValue(source) is Array array))
		{
			return val;
		}
		Type elementType = array.GetType().GetElementType();
		if (elementType == null)
		{
			return val;
		}
		Array array2 = Array.CreateInstance(elementType, array.Length);
		for (int i = 0; i < array.Length; i++)
		{
			array2.SetValue(CloneEffectData(array.GetValue(i)), i);
		}
		field.SetValue(val, array2);
		return val;
	}

	private static bool EffectListContains(EffectList effectList, string wantedPrefabName)
	{
		if (effectList == null)
		{
			return false;
		}
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		if (!(field.GetValue(effectList) is Array array))
		{
			return false;
		}
		for (int i = 0; i < array.Length; i++)
		{
			object value = array.GetValue(i);
			if (value != null)
			{
				FieldInfo field2 = value.GetType().GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				GameObject val = (GameObject)((field2 != null) ? /*isinst with value type is only supported in some contexts*/: null);
				string text = (((Object)(object)val != (Object)null) ? ((Object)val).name : string.Empty);
				if (!string.IsNullOrEmpty(text) && text.IndexOf(wantedPrefabName, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
		}
		return false;
	}

	private static bool ReplaceNamedEffectPrefab(EffectList effectList, string oldPrefabName, GameObject replacementPrefab)
	{
		if (effectList == null || (Object)(object)replacementPrefab == (Object)null)
		{
			return false;
		}
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		if (!(field.GetValue(effectList) is Array array))
		{
			return false;
		}
		bool result = false;
		for (int i = 0; i < array.Length; i++)
		{
			object value = array.GetValue(i);
			if (value == null)
			{
				continue;
			}
			FieldInfo field2 = value.GetType().GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field2 == null)
			{
				continue;
			}
			object? value2 = field2.GetValue(value);
			GameObject val = (GameObject)((value2 is GameObject) ? value2 : null);
			string text = (((Object)(object)val != (Object)null) ? ((Object)val).name : string.Empty);
			if (!string.IsNullOrEmpty(text) && text.IndexOf(oldPrefabName, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				field2.SetValue(value, replacementPrefab);
				if (value.GetType().IsValueType)
				{
					array.SetValue(value, i);
				}
				result = true;
			}
		}
		return result;
	}

	private static bool EnsureEffectPrefab(EffectList effectList, GameObject prefab)
	{
		if (effectList == null || (Object)(object)prefab == (Object)null || EffectListContains(effectList, ((Object)prefab).name))
		{
			return false;
		}
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		Array array = field.GetValue(effectList) as Array;
		Type type = array?.GetType().GetElementType();
		if (type == null && field.FieldType.IsArray)
		{
			type = field.FieldType.GetElementType();
		}
		if (type == null)
		{
			return false;
		}
		int num = array?.Length ?? 0;
		Array array2 = Array.CreateInstance(type, num + 1);
		for (int i = 0; i < num; i++)
		{
			array2.SetValue(array.GetValue(i), i);
		}
		object obj = ((num > 0) ? CloneEffectData(array.GetValue(0)) : null);
		if (obj == null)
		{
			obj = Activator.CreateInstance(type);
		}
		FieldInfo field2 = type.GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field2 == null)
		{
			return false;
		}
		field2.SetValue(obj, prefab);
		FieldInfo field3 = type.GetField("m_enabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field3 != null && field3.FieldType == typeof(bool))
		{
			field3.SetValue(obj, true);
		}
		array2.SetValue(obj, num);
		field.SetValue(effectList, array2);
		return true;
	}

	private GameObject CreateFawnPrefab(ZNetScene scene, GameObject deer)
	{
		//IL_00aa: 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_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Expected O, but got Unknown
		int stableHashCode = StringExtensionMethods.GetStableHashCode("nick008_DeerFawn");
		GameObject prefab = scene.GetPrefab(stableHashCode);
		if ((Object)(object)prefab != (Object)null)
		{
			Growup component = prefab.GetComponent<Growup>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_growTime = 2000f;
				component.m_grownPrefab = deer;
			}
			return prefab;
		}
		if ((Object)(object)offspringRoot == (Object)null)
		{
			offspringRoot = new GameObject("nick008tameabledeer_offspring_root");
			offspringRoot.transform.SetParent(((Component)this).transform, false);
			offspringRoot.SetActive(false);
		}
		GameObject val = Object.Instantiate<GameObject>(deer, offspringRoot.transform);
		((Object)val).name = "nick008_DeerFawn";
		val.transform.localScale = val.transform.localScale * 0.55f;
		CharacterDrop component2 = val.GetComponent<CharacterDrop>();
		if ((Object)(object)component2 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component2);
		}
		Procreation component3 = val.GetComponent<Procreation>();
		if ((Object)(object)component3 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component3);
		}
		Tameable component4 = val.GetComponent<Tameable>();
		if ((Object)(object)component4 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component4);
		}
		Growup val2 = val.GetComponent<Growup>();
		if ((Object)(object)val2 == (Object)null)
		{
			val2 = val.AddComponent<Growup>();
		}
		val2.m_grownPrefab = deer;
		val2.m_growTime = 2000f;
		RegisterNamedPrefab(scene, stableHashCode, val);
		Log.LogInfo((object)"[nick008tameabledeer] Registered offspring prefab: nick008_DeerFawn");
		return val;
	}

	private static void RegisterNamedPrefab(ZNetScene scene, int hash, GameObject prefab)
	{
		FieldInfo field = typeof(ZNetScene).GetField("m_namedPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			throw new MissingFieldException("ZNetScene.m_namedPrefabs");
		}
		object value = field.GetValue(scene);
		if (value == null)
		{
			throw new InvalidOperationException("ZNetScene.m_namedPrefabs is null");
		}
		Type type = value.GetType();
		MethodInfo method = type.GetMethod("ContainsKey", new Type[1] { typeof(int) });
		MethodInfo method2 = type.GetMethod("Remove", new Type[1] { typeof(int) });
		MethodInfo method3 = type.GetMethod("Add", new Type[2]
		{
			typeof(int),
			typeof(GameObject)
		});
		if (method == null || method2 == null || method3 == null)
		{
			throw new MissingMethodException("Could not access ZNetScene prefab dictionary methods");
		}
		if ((bool)method.Invoke(value, new object[1] { hash }))
		{
			method2.Invoke(value, new object[1] { hash });
		}
		if (!scene.m_prefabs.Contains(prefab))
		{
			scene.m_prefabs.Add(prefab);
		}
		method3.Invoke(value, new object[2] { hash, prefab });
	}

	private static void CopyPublicFields(Component destination, Component source)
	{
		if ((Object)(object)destination == (Object)null || (Object)(object)source == (Object)null)
		{
			return;
		}
		BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public;
		FieldInfo[] fields = ((object)source).GetType().GetFields(bindingAttr);
		Type type = ((object)destination).GetType();
		foreach (FieldInfo fieldInfo in fields)
		{
			FieldInfo field = type.GetField(fieldInfo.Name, bindingAttr);
			if (!(field == null) && !field.IsInitOnly && (field.FieldType.IsAssignableFrom(fieldInfo.FieldType) || fieldInfo.FieldType.IsAssignableFrom(field.FieldType)))
			{
				try
				{
					field.SetValue(destination, fieldInfo.GetValue(source));
				}
				catch
				{
				}
			}
		}
	}
}
public sealed class Nick008DeerAI : MonsterAI
{
	public float m_timeToSafe = 4f;

	private float targetUpdateTimer;

	private float timeSinceSensedTarget;

	private Character targetCreature;

	private static MethodInfo updateConsumeItemMethod;

	private static MethodInfo updateRegenerationMethod;

	private static bool reflectionResolved = false;

	private static bool reflectionWarningShown = false;

	public void SetCharacter(Character character)
	{
		((BaseAI)this).m_character = character;
	}

	private static MethodInfo FindInstanceMethod(Type type, string name, int parameterCount)
	{
		Type type2 = type;
		while (type2 != null)
		{
			MethodInfo[] methods = type2.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			for (int i = 0; i < methods.Length; i++)
			{
				if (methods[i].Name == name && methods[i].GetParameters().Length == parameterCount)
				{
					return methods[i];
				}
			}
			type2 = type2.BaseType;
		}
		return null;
	}

	private static void ResolveReflection()
	{
		if (reflectionResolved)
		{
			return;
		}
		reflectionResolved = true;
		updateConsumeItemMethod = FindInstanceMethod(typeof(MonsterAI), "UpdateConsumeItem", 2);
		updateRegenerationMethod = FindInstanceMethod(typeof(BaseAI), "UpdateRegeneration", 1);
		if ((updateConsumeItemMethod == null || updateRegenerationMethod == null) && !reflectionWarningShown)
		{
			reflectionWarningShown = true;
			if (Nick008TameableDeerPlugin.Log != null)
			{
				Nick008TameableDeerPlugin.Log.LogWarning((object)"[nick008tameabledeer] Some private AI helpers were not found. Taming may still work, but please send the BepInEx log if deer do not eat.");
			}
		}
	}

	private bool TryUpdateConsumeItem(Humanoid humanoid, float dt)
	{
		ResolveReflection();
		if (updateConsumeItemMethod == null || (Object)(object)humanoid == (Object)null)
		{
			return false;
		}
		try
		{
			object obj = updateConsumeItemMethod.Invoke(this, new object[2] { humanoid, dt });
			return obj is bool && (bool)obj;
		}
		catch (Exception ex)
		{
			if (!reflectionWarningShown)
			{
				reflectionWarningShown = true;
				Nick008TameableDeerPlugin.Log.LogWarning((object)("[nick008tameabledeer] UpdateConsumeItem reflection failed: " + ex.Message));
			}
			return false;
		}
	}

	private void TryUpdateRegeneration(float dt)
	{
		ResolveReflection();
		if (updateRegenerationMethod == null)
		{
			return;
		}
		try
		{
			updateRegenerationMethod.Invoke(this, new object[1] { dt });
		}
		catch
		{
		}
	}

	public override bool UpdateAI(float dt)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		if (((BaseAI)this).m_afraidOfFire && ((BaseAI)this).AvoidFire(dt, (Character)null, true))
		{
			return true;
		}
		targetUpdateTimer -= dt;
		if (targetUpdateTimer <= 0f)
		{
			targetUpdateTimer = (Character.IsCharacterInRange(((Component)this).transform.position, 32f) ? 2f : 10f);
			Character val = ((BaseAI)this).FindEnemy();
			if ((Object)(object)val != (Object)null)
			{
				targetCreature = val;
			}
		}
		if ((Object)(object)((BaseAI)this).m_tamable != (Object)null && (Object)(object)((BaseAI)this).m_tamable.m_saddle != (Object)null && ((BaseAI)this).m_tamable.m_saddle.UpdateRiding(dt))
		{
			return true;
		}
		if ((Object)(object)targetCreature != (Object)null && targetCreature.IsDead())
		{
			targetCreature = null;
		}
		if ((Object)(object)targetCreature != (Object)null)
		{
			bool flag = ((BaseAI)this).CanSenseTarget(targetCreature);
			((BaseAI)this).SetTargetInfo(targetCreature.GetZDOID());
			if (flag)
			{
				timeSinceSensedTarget = 0f;
				((BaseAI)this).SetAlerted(true);
			}
			else
			{
				timeSinceSensedTarget += dt;
			}
		}
		else
		{
			((BaseAI)this).SetTargetInfo(ZDOID.None);
			timeSinceSensedTarget += dt;
		}
		if (((BaseAI)this).IsAlerted() && timeSinceSensedTarget > m_timeToSafe)
		{
			targetCreature = null;
			((BaseAI)this).SetAlerted(false);
			timeSinceSensedTarget = 0f;
		}
		Character character = ((BaseAI)this).m_character;
		Humanoid humanoid = (Humanoid)(object)((character is Humanoid) ? character : null);
		if (!((BaseAI)this).IsAlerted() && TryUpdateConsumeItem(humanoid, dt))
		{
			return true;
		}
		if ((Object)(object)targetCreature != (Object)null)
		{
			((BaseAI)this).Flee(dt, ((Component)targetCreature).transform.position);
			targetCreature.OnTargeted(false, false);
			return true;
		}
		GameObject followTarget = ((MonsterAI)this).GetFollowTarget();
		if ((Object)(object)followTarget != (Object)null)
		{
			((BaseAI)this).Follow(followTarget, dt);
		}
		else
		{
			((BaseAI)this).IdleMovement(dt);
		}
		((BaseAI)this).ChargeStop();
		TryUpdateRegeneration(dt);
		return true;
	}
}
internal static class TameableNeckBoarCompat
{
	internal static EffectList VanillaBoarPet;

	internal static EffectList VanillaBoarLove;

	internal static EffectList VanillaBoarBirth;

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

	internal static void ApplyIfNeeded(ZNetScene scene)
	{
		if (!((Object)(object)scene == (Object)null) && IsTameableNeckLoaded())
		{
			FixBoar(scene);
		}
	}

	internal static bool IsTameableNeckLoaded()
	{
		foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
		{
			string text = pluginInfo.Key ?? string.Empty;
			string text2 = string.Empty;
			if (pluginInfo.Value != null && pluginInfo.Value.Metadata != null)
			{
				text2 = pluginInfo.Value.Metadata.Name ?? string.Empty;
			}
			if (text.IndexOf("TameableNeck", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("TameableNeck", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	internal static void FixBoar(ZNetScene scene)
	{
		GameObject prefab = scene.GetPrefab("Boar");
		if ((Object)(object)prefab == (Object)null)
		{
			return;
		}
		Tameable component = prefab.GetComponent<Tameable>();
		if ((Object)(object)component != (Object)null)
		{
			if (component.m_petEffect != null)
			{
				component.m_petEffect = CloneEffectList(component.m_petEffect);
			}
			GameObject prefab2 = scene.GetPrefab("fx_boar_pet");
			if (ReplaceNamedEffectPrefab(component.m_petEffect, "btn_fx_neck_pet", prefab2))
			{
				LogOnce("boar:petexplicit", "Restored vanilla fx_boar_pet on Boar.");
			}
			else if (VanillaBoarPet != null && EffectListContains(component.m_petEffect, "btn_fx_neck_pet"))
			{
				component.m_petEffect = CloneEffectList(VanillaBoarPet);
			}
		}
		Procreation component2 = prefab.GetComponent<Procreation>();
		if (!((Object)(object)component2 != (Object)null))
		{
			return;
		}
		EffectList val = CloneEffectList(GetEffectList(component2, "m_birthEffects"));
		EffectList val2 = CloneEffectList(GetEffectList(component2, "m_loveEffects"));
		if (val != null)
		{
			SetEffectList(component2, "m_birthEffects", val);
		}
		if (val2 != null)
		{
			SetEffectList(component2, "m_loveEffects", val2);
		}
		bool flag = false;
		flag |= ReplaceNamedEffectPrefab(component2, "m_birthEffects", "btn_sfx_neck_birth", scene.GetPrefab("sfx_boar_birth"));
		flag |= ReplaceNamedEffectPrefab(component2, "m_birthEffects", "btn_vfx_hare_birth", scene.GetPrefab("vfx_boar_birth"));
		bool flag2 = false;
		flag2 |= ReplaceNamedEffectPrefab(component2, "m_loveEffects", "btn_sfx_neck_love", scene.GetPrefab("sfx_boar_love"));
		flag2 |= ReplaceNamedEffectPrefab(component2, "m_loveEffects", "btn_vfx_hare_love", scene.GetPrefab("vfx_boar_love"));
		if (flag)
		{
			LogOnce("boar:birthexplicit", "Restored vanilla sfx_boar_birth / vfx_boar_birth on Boar.");
		}
		if (flag2)
		{
			LogOnce("boar:loveexplicit", "Restored vanilla sfx_boar_love / vfx_boar_love on Boar.");
		}
		EffectList effectList = GetEffectList(component2, "m_birthEffects");
		if (EffectListContains(effectList, "btn_sfx_neck_birth"))
		{
			if (VanillaBoarBirth != null)
			{
				SetEffectList(component2, "m_birthEffects", CloneEffectList(VanillaBoarBirth));
			}
			else
			{
				RemoveNamedEffectPrefab(component2, "m_birthEffects", "btn_sfx_neck_birth");
			}
		}
		EffectList effectList2 = GetEffectList(component2, "m_loveEffects");
		if (EffectListContains(effectList2, "btn_sfx_neck_love"))
		{
			if (VanillaBoarLove != null)
			{
				SetEffectList(component2, "m_loveEffects", CloneEffectList(VanillaBoarLove));
			}
			else
			{
				RemoveNamedEffectPrefab(component2, "m_loveEffects", "btn_sfx_neck_love");
			}
		}
	}

	internal static bool IsBoar(Component component)
	{
		if ((Object)(object)component == (Object)null || (Object)(object)component.gameObject == (Object)null)
		{
			return false;
		}
		string text = ((Object)component.gameObject).name ?? string.Empty;
		if (!text.Equals("Boar", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Boar(", StringComparison.OrdinalIgnoreCase))
		{
			return text.StartsWith("Boar ", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	internal static void CaptureBoarPet(Tameable tameable)
	{
		if (VanillaBoarPet == null && IsBoar((Component)(object)tameable) && tameable.m_petEffect != null && !EffectListContains(tameable.m_petEffect, "btn_fx_neck_pet"))
		{
			VanillaBoarPet = CloneEffectList(tameable.m_petEffect);
			if (Nick008TameableDeerPlugin.Log != null)
			{
				Nick008TameableDeerPlugin.Log.LogInfo((object)"[nick008tameabledeer/neckcompat] Captured vanilla Boar pet effect before mod patches.");
			}
		}
	}

	internal static void CaptureBoarBreeding(Procreation procreation)
	{
		if (!IsBoar((Component)(object)procreation))
		{
			return;
		}
		if (VanillaBoarBirth == null)
		{
			EffectList effectList = GetEffectList(procreation, "m_birthEffects");
			if (effectList != null && !EffectListContains(effectList, "btn_sfx_neck_birth"))
			{
				VanillaBoarBirth = CloneEffectList(effectList);
				if (Nick008TameableDeerPlugin.Log != null)
				{
					Nick008TameableDeerPlugin.Log.LogInfo((object)"[nick008tameabledeer/neckcompat] Captured vanilla Boar birth effect before mod patches.");
				}
			}
		}
		if (VanillaBoarLove != null)
		{
			return;
		}
		EffectList effectList2 = GetEffectList(procreation, "m_loveEffects");
		if (effectList2 != null && !EffectListContains(effectList2, "btn_sfx_neck_love"))
		{
			VanillaBoarLove = CloneEffectList(effectList2);
			if (Nick008TameableDeerPlugin.Log != null)
			{
				Nick008TameableDeerPlugin.Log.LogInfo((object)"[nick008tameabledeer/neckcompat] Captured vanilla Boar love/mating effect before mod patches.");
			}
		}
	}

	internal static void RestoreBoarPet(Tameable tameable)
	{
		if (VanillaBoarPet != null && IsBoar((Component)(object)tameable))
		{
			tameable.m_petEffect = CloneEffectList(VanillaBoarPet);
		}
	}

	internal static void RestoreBoarBreeding(Procreation procreation)
	{
		if (IsBoar((Component)(object)procreation))
		{
			if (VanillaBoarBirth != null)
			{
				SetEffectList(procreation, "m_birthEffects", CloneEffectList(VanillaBoarBirth));
			}
			if (VanillaBoarLove != null)
			{
				SetEffectList(procreation, "m_loveEffects", CloneEffectList(VanillaBoarLove));
			}
		}
	}

	private static void LogOnce(string key, string message)
	{
		if (!logged.Contains(key))
		{
			logged.Add(key);
			Nick008TameableDeerPlugin.Log.LogInfo((object)("[nick008tameabledeer/neckcompat] " + message));
		}
	}

	internal static EffectList GetEffectList(object owner, string fieldName)
	{
		if (owner == null)
		{
			return null;
		}
		FieldInfo field = owner.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (!(field != null))
		{
			return null;
		}
		object? value = field.GetValue(owner);
		return (EffectList)((value is EffectList) ? value : null);
	}

	internal static bool SetEffectList(object owner, string fieldName, EffectList value)
	{
		if (owner == null)
		{
			return false;
		}
		FieldInfo field = owner.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		field.SetValue(owner, value);
		return true;
	}

	private static object CloneEffectData(object effectData)
	{
		if (effectData == null)
		{
			return null;
		}
		Type type = effectData.GetType();
		if (type.IsValueType)
		{
			return effectData;
		}
		MethodInfo method = typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
		if (!(method != null))
		{
			return effectData;
		}
		return method.Invoke(effectData, null);
	}

	internal static EffectList CloneEffectList(EffectList source)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		if (source == null)
		{
			return null;
		}
		EffectList val = new EffectList();
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return val;
		}
		if (!(field.GetValue(source) is Array array))
		{
			return val;
		}
		Type elementType = array.GetType().GetElementType();
		if (elementType == null)
		{
			return val;
		}
		Array array2 = Array.CreateInstance(elementType, array.Length);
		for (int i = 0; i < array.Length; i++)
		{
			array2.SetValue(CloneEffectData(array.GetValue(i)), i);
		}
		field.SetValue(val, array2);
		return val;
	}

	internal static bool EffectListContains(EffectList effectList, string wantedPrefabName)
	{
		if (effectList == null)
		{
			return false;
		}
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		if (!(field.GetValue(effectList) is Array array))
		{
			return false;
		}
		for (int i = 0; i < array.Length; i++)
		{
			object value = array.GetValue(i);
			if (value != null)
			{
				FieldInfo field2 = value.GetType().GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				GameObject val = (GameObject)((field2 != null) ? /*isinst with value type is only supported in some contexts*/: null);
				string text = (((Object)(object)val != (Object)null) ? ((Object)val).name : string.Empty);
				if (!string.IsNullOrEmpty(text) && text.IndexOf(wantedPrefabName, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
		}
		return false;
	}

	private static bool ReplaceNamedEffectPrefab(object owner, string effectListFieldName, string oldPrefabName, GameObject replacementPrefab)
	{
		EffectList effectList = GetEffectList(owner, effectListFieldName);
		return ReplaceNamedEffectPrefab(effectList, oldPrefabName, replacementPrefab);
	}

	private static bool ReplaceNamedEffectPrefab(EffectList effectList, string oldPrefabName, GameObject replacementPrefab)
	{
		if (effectList == null || (Object)(object)replacementPrefab == (Object)null)
		{
			return false;
		}
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		if (!(field.GetValue(effectList) is Array array))
		{
			return false;
		}
		bool result = false;
		for (int i = 0; i < array.Length; i++)
		{
			object value = array.GetValue(i);
			if (value == null)
			{
				continue;
			}
			FieldInfo field2 = value.GetType().GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field2 == null)
			{
				continue;
			}
			object? value2 = field2.GetValue(value);
			GameObject val = (GameObject)((value2 is GameObject) ? value2 : null);
			string text = (((Object)(object)val != (Object)null) ? ((Object)val).name : string.Empty);
			if (!string.IsNullOrEmpty(text) && text.IndexOf(oldPrefabName, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				field2.SetValue(value, replacementPrefab);
				if (value.GetType().IsValueType)
				{
					array.SetValue(value, i);
				}
				result = true;
			}
		}
		return result;
	}

	private static bool EnsureEffectPrefab(EffectList effectList, GameObject prefab)
	{
		if (effectList == null || (Object)(object)prefab == (Object)null || EffectListContains(effectList, ((Object)prefab).name))
		{
			return false;
		}
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		Array array = field.GetValue(effectList) as Array;
		Type type = array?.GetType().GetElementType();
		if (type == null)
		{
			FieldInfo field2 = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field2 != null && field2.FieldType.IsArray)
			{
				type = field2.FieldType.GetElementType();
			}
		}
		if (type == null)
		{
			return false;
		}
		int num = array?.Length ?? 0;
		Array array2 = Array.CreateInstance(type, num + 1);
		for (int i = 0; i < num; i++)
		{
			array2.SetValue(array.GetValue(i), i);
		}
		object obj = null;
		if (num > 0)
		{
			obj = CloneEffectData(array.GetValue(0));
		}
		if (obj == null)
		{
			obj = Activator.CreateInstance(type);
		}
		FieldInfo field3 = type.GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field3 == null)
		{
			return false;
		}
		field3.SetValue(obj, prefab);
		FieldInfo field4 = type.GetField("m_enabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field4 != null && field4.FieldType == typeof(bool))
		{
			field4.SetValue(obj, true);
		}
		array2.SetValue(obj, num);
		field.SetValue(effectList, array2);
		return true;
	}

	private static bool RemoveNamedEffectPrefab(object owner, string effectListFieldName, string blockedPrefabName)
	{
		EffectList effectList = GetEffectList(owner, effectListFieldName);
		if (effectList == null)
		{
			return false;
		}
		FieldInfo field = typeof(EffectList).GetField("m_effectPrefabs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			return false;
		}
		if (!(field.GetValue(effectList) is Array { Length: not 0 } array))
		{
			return false;
		}
		List<object> list = new List<object>();
		bool flag = false;
		for (int i = 0; i < array.Length; i++)
		{
			object value = array.GetValue(i);
			if (value == null)
			{
				list.Add(value);
				continue;
			}
			FieldInfo field2 = value.GetType().GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			GameObject val = (GameObject)((field2 != null) ? /*isinst with value type is only supported in some contexts*/: null);
			string text = (((Object)(object)val != (Object)null) ? ((Object)val).name : string.Empty);
			if (!string.IsNullOrEmpty(text) && text.IndexOf(blockedPrefabName, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				flag = true;
			}
			else
			{
				list.Add(value);
			}
		}
		if (!flag)
		{
			return false;
		}
		Type elementType = array.GetType().GetElementType();
		if (elementType == null)
		{
			return false;
		}
		Array array2 = Array.CreateInstance(elementType, list.Count);
		for (int j = 0; j < list.Count; j++)
		{
			array2.SetValue(list[j], j);
		}
		field.SetValue(effectList, array2);
		return true;
	}
}
[HarmonyPatch(typeof(Tameable), "Awake")]
internal static class TameableDeerBoarTameableAwakePatch
{
	[HarmonyPriority(800)]
	[HarmonyPrefix]
	private static void Prefix(Tameable __instance)
	{
		if (TameableNeckBoarCompat.IsTameableNeckLoaded())
		{
			TameableNeckBoarCompat.CaptureBoarPet(__instance);
		}
	}

	[HarmonyPostfix]
	[HarmonyAfter(new string[] { "blacks7ar.TameableNeck" })]
	[HarmonyPriority(0)]
	private static void Postfix(Tameable __instance)
	{
		if (TameableNeckBoarCompat.IsTameableNeckLoaded())
		{
			TameableNeckBoarCompat.RestoreBoarPet(__instance);
		}
	}
}
[HarmonyPatch(typeof(Procreation), "Awake")]
internal static class TameableDeerBoarProcreationAwakePatch
{
	[HarmonyPriority(800)]
	[HarmonyPrefix]
	private static void Prefix(Procreation __instance)
	{
		if (TameableNeckBoarCompat.IsTameableNeckLoaded())
		{
			TameableNeckBoarCompat.CaptureBoarBreeding(__instance);
		}
	}

	[HarmonyPriority(0)]
	[HarmonyAfter(new string[] { "blacks7ar.TameableNeck" })]
	[HarmonyPostfix]
	private static void Postfix(Procreation __instance)
	{
		if (TameableNeckBoarCompat.IsTameableNeckLoaded())
		{
			TameableNeckBoarCompat.RestoreBoarBreeding(__instance);
		}
	}
}
[HarmonyPatch]
internal static class FawnButcherProtectionPatch
{
	private const string FawnPrefabName = "nick008_DeerFawn";

	private static bool loggedBlockedHit;

	private static MethodBase TargetMethod()
	{
		MethodInfo[] methods = typeof(Character).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo methodInfo in methods)
		{
			if (!(methodInfo.Name != "Damage"))
			{
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (parameters.Length == 1 && parameters[0].ParameterType.Name == "HitData")
				{
					return methodInfo;
				}
			}
		}
		return null;
	}

	[HarmonyPrefix]
	private static bool Prefix(Character __instance, object __0)
	{
		try
		{
			if (!IsFawn(__instance) || !IsButcherKnifeHit(__0))
			{
				return true;
			}
			if (!loggedBlockedHit && Nick008TameableDeerPlugin.Log != null)
			{
				loggedBlockedHit = true;
				Nick008TameableDeerPlugin.Log.LogInfo((object)"[nick008tameabledeer] Protected deer fawn from accidental butcher-knife hit.");
			}
			return false;
		}
		catch (Exception ex)
		{
			if (Nick008TameableDeerPlugin.Log != null)
			{
				Nick008TameableDeerPlugin.Log.LogWarning((object)("[nick008tameabledeer] Fawn butcher protection check failed: " + ex.Message));
			}
			return true;
		}
	}

	private static bool IsFawn(Character character)
	{
		if ((Object)(object)character == (Object)null || (Object)(object)((Component)character).gameObject == (Object)null)
		{
			return false;
		}
		string text = ((Object)((Component)character).gameObject).name ?? string.Empty;
		if (!text.Equals("nick008_DeerFawn", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("nick008_DeerFawn(", StringComparison.OrdinalIgnoreCase))
		{
			return text.StartsWith("nick008_DeerFawn ", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private static bool IsButcherKnifeHit(object hit)
	{
		if (hit == null)
		{
			return false;
		}
		object obj = InvokeNoArgMethod(hit, "GetAttacker");
		if (obj == null)
		{
			return false;
		}
		Type type = obj.GetType();
		if (!IsTypeOrBaseNamed(type, "Player"))
		{
			return false;
		}
		object obj2 = InvokeNoArgMethod(obj, "GetCurrentWeapon");
		if (obj2 == null)
		{
			return false;
		}
		object fieldValue = GetFieldValue(obj2, "m_dropPrefab");
		GameObject val = (GameObject)((fieldValue is GameObject) ? fieldValue : null);
		if ((Object)(object)val != (Object)null && LooksLikeButcherKnife(((Object)val).name))
		{
			return true;
		}
		object fieldValue2 = GetFieldValue(obj2, "m_shared");
		object fieldValue3 = GetFieldValue(fieldValue2, "m_name");
		if (fieldValue3 != null && LooksLikeButcherKnife(fieldValue3.ToString()))
		{
			return true;
		}
		return LooksLikeButcherKnife(obj2.ToString());
	}

	private static bool LooksLikeButcherKnife(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return false;
		}
		string text = value.ToLowerInvariant();
		if (!text.Contains("butcher") && !text.Contains("knifebutcher"))
		{
			return text.Contains("butcherknife");
		}
		return true;
	}

	private static bool IsTypeOrBaseNamed(Type type, string name)
	{
		Type type2 = type;
		while (type2 != null)
		{
			if (string.Equals(type2.Name, name, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			type2 = type2.BaseType;
		}
		return false;
	}

	private static object InvokeNoArgMethod(object instance, string methodName)
	{
		if (instance == null)
		{
			return null;
		}
		Type type = instance.GetType();
		while (type != null)
		{
			MethodInfo method = type.GetMethod(methodName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			if (method != null)
			{
				return method.Invoke(instance, null);
			}
			type = type.BaseType;
		}
		return null;
	}

	private static object GetFieldValue(object instance, string fieldName)
	{
		if (instance == null)
		{
			return null;
		}
		Type type = instance.GetType();
		while (type != null)
		{
			FieldInfo field = type.GetField(fieldName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(instance);
			}
			type = type.BaseType;
		}
		return null;
	}
}