Decompiled source of TameableHares v1.0.4

plugins/nick008tameablehares.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

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

[BepInPlugin("nick008.tameablehares", "nick008tameablehares", "1.0.4")]
[BepInProcess("valheim.exe")]
public sealed class Nick008TameableHaresPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	private static class ZNetSceneAwakePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ZNetScene __instance)
		{
			try
			{
				GameObject kit = RegisterKitBeforeSceneAwake(__instance);
				ConfigureHareCoreBeforeSceneAwake(__instance, kit);
			}
			catch (Exception ex)
			{
				if (Log != null)
				{
					Log.LogError((object)("[nick008tameablehares] Early Hare/kit setup failed: " + ex));
				}
			}
		}
	}

	[HarmonyPatch(typeof(Tameable), "GetHoverText")]
	private static class HareTamingPercentPatch
	{
		private static readonly MethodInfo GetTamenessMethod = AccessTools.Method(typeof(Tameable), "GetTameness", (Type[])null, (Type[])null);

		[HarmonyPostfix]
		private static void Postfix(Tameable __instance, ref string __result)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).gameObject == (Object)null || !((Object)((Component)__instance).gameObject).name.StartsWith("Hare", StringComparison.Ordinal) || string.IsNullOrEmpty(__result) || __result.IndexOf('%') >= 0 || GetTamenessMethod == null)
				{
					return;
				}
				object obj = GetTamenessMethod.Invoke(__instance, null);
				if (obj is float)
				{
					float num = Mathf.Clamp01((float)obj);
					if (!(num <= 0.0001f) && !(num >= 0.9999f))
					{
						int num2 = Mathf.Clamp(Mathf.FloorToInt(num * 100f), 1, 99);
						object obj2 = __result;
						__result = string.Concat(obj2, " (", num2, "%)");
					}
				}
			}
			catch (Exception ex)
			{
				if (Log != null)
				{
					Log.LogWarning((object)("[nick008tameablehares] Could not append Hare taming percentage: " + ex.Message));
				}
			}
		}
	}

	public const string PluginGuid = "nick008.tameablehares";

	public const string PluginName = "nick008tameablehares";

	public const string PluginVersion = "1.0.4";

	private const float TamingTime = 1500f;

	private const float FedDuration = 300f;

	private const float GrowTime = 2000f;

	private const int MaxNearbyHare = 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 Nick008TameableHaresPlugin Instance;

	private static readonly string[] FoodPrefabNames = new string[4] { "Carrot", "Turnip", "MushroomJotunPuffs", "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.tameablehares").PatchAll();
		Log.LogInfo((object)"[nick008tameablehares] 1.0.4 loaded. Waiting for Valheim creature database...");
		((MonoBehaviour)this).StartCoroutine(WatchForCreatureDatabase());
	}

	internal static GameObject RegisterKitBeforeSceneAwake(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_HareKit")
			{
				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 == "Hare")
			{
				val2 = val3;
				break;
			}
		}
		if ((Object)(object)val2 == (Object)null)
		{
			return null;
		}
		Nick008TameableHaresPlugin instance = Instance;
		if ((Object)(object)instance == (Object)null)
		{
			return null;
		}
		if ((Object)(object)instance.offspringRoot == (Object)null)
		{
			instance.offspringRoot = new GameObject("nick008tameablehares_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_HareKit";
		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)"[nick008tameablehares] Early-registered persistent kit 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 ConfigureHareCoreBeforeSceneAwake(ZNetScene scene, GameObject kit)
	{
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = FindPrefabInSceneList(scene, "Hare");
		GameObject val2 = FindPrefabInSceneList(scene, "Boar");
		if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)kit == (Object)null)
		{
			return false;
		}
		Nick008HareAI component = val.GetComponent<Nick008HareAI>();
		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 = kit;
			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);
		}
		Nick008HareAI nick008HareAI = val.GetComponent<Nick008HareAI>();
		if ((Object)(object)nick008HareAI == (Object)null)
		{
			nick008HareAI = val.AddComponent<Nick008HareAI>();
		}
		CopyPublicFields((Component)(object)nick008HareAI, (Component)(object)component6);
		CopyPublicFields((Component)(object)nick008HareAI, (Component)(object)component5);
		nick008HareAI.SetCharacter((Character)(object)val3);
		if (((BaseAI)nick008HareAI).m_randomMoveInterval > 30f)
		{
			((BaseAI)nick008HareAI).m_randomMoveInterval = 30f;
		}
		Object.DestroyImmediate((Object)(object)component5);
		Object.DestroyImmediate((Object)(object)component4);
		((Character)val3).m_group = "Hare";
		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)nick008HareAI).m_consumeRange = 1.4f;
		((MonsterAI)nick008HareAI).m_consumeSearchInterval = 10f;
		((MonsterAI)nick008HareAI).m_consumeSearchRange = 15f;
		((MonsterAI)nick008HareAI).m_enableHuntPlayer = false;
		((MonsterAI)nick008HareAI).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 = kit;
		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)"[nick008tameablehares] Early-configured Hare 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("Hare") != (Object)null && (Object)(object)instance.GetPrefab("Boar") != (Object)null && (Object)(object)instance2.GetItemPrefab("Carrot") != (Object)null)
				{
					int instanceID = ((Object)instance).GetInstanceID();
					if (instanceID != configuredSceneId && ConfigureHare(instance, instance2))
					{
						configuredSceneId = instanceID;
					}
					if (configuredSceneId == instanceID)
					{
						ApplyHareSounds(instance);
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[nick008tameablehares] Setup watcher error: " + ex));
			}
			yield return (object)new WaitForSeconds(1f);
		}
	}

	private bool ConfigureHare(ZNetScene scene, ObjectDB db)
	{
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		GameObject prefab = scene.GetPrefab("Hare");
		GameObject prefab2 = scene.GetPrefab("Boar");
		if ((Object)(object)prefab == (Object)null || (Object)(object)prefab2 == (Object)null)
		{
			return false;
		}
		if ((Object)(object)prefab.GetComponent<Nick008HareAI>() != (Object)null && (Object)(object)prefab.GetComponent<Tameable>() != (Object)null && (Object)(object)prefab.GetComponent<Procreation>() != (Object)null)
		{
			Nick008HareAI component = prefab.GetComponent<Nick008HareAI>();
			int num = PopulateFoods(component, db);
			GameObject prefab3 = scene.GetPrefab("nick008_HareKit");
			if ((Object)(object)prefab3 != (Object)null)
			{
				prefab.GetComponent<Procreation>().m_offspring = prefab3;
			}
			ApplyHareSounds(scene);
			Log.LogInfo((object)("[nick008tameablehares] SUCCESS: early Hare 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)"[nick008tameablehares] Vanilla Hare/Boar components were not found. Nothing was changed.");
			return false;
		}
		GameObject val = CreateKitPrefab(scene, prefab);
		if ((Object)(object)val == (Object)null)
		{
			Log.LogError((object)"[nick008tameablehares] Could not create/register hare 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);
			}
			Nick008HareAI nick008HareAI = prefab.GetComponent<Nick008HareAI>();
			if ((Object)(object)nick008HareAI == (Object)null)
			{
				nick008HareAI = prefab.AddComponent<Nick008HareAI>();
			}
			CopyPublicFields((Component)(object)nick008HareAI, (Component)(object)component4);
			CopyPublicFields((Component)(object)nick008HareAI, (Component)(object)component3);
			nick008HareAI.SetCharacter((Character)(object)val2);
			if (((BaseAI)nick008HareAI).m_randomMoveInterval > 30f)
			{
				((BaseAI)nick008HareAI).m_randomMoveInterval = 30f;
			}
			Object.DestroyImmediate((Object)(object)component3);
			Object.DestroyImmediate((Object)(object)component2);
			((Character)val2).m_group = "Hare";
			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)nick008HareAI).m_consumeRange = 1.4f;
			((MonsterAI)nick008HareAI).m_consumeSearchInterval = 10f;
			((MonsterAI)nick008HareAI).m_consumeSearchRange = 15f;
			((MonsterAI)nick008HareAI).m_enableHuntPlayer = false;
			int num2 = PopulateFoods(nick008HareAI, 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));
			}
			ApplyHareSounds(scene);
			Log.LogInfo((object)"[nick008tameablehares] SUCCESS: Hare is tameable and breedable.");
			Log.LogInfo((object)("[nick008tameablehares] Foods found: " + num2 + ". TEST timings: taming 1 min, kit growth 1 min."));
			Log.LogInfo((object)"[nick008tameablehares] Hare sounds: vanilla idle call on pet/mating, alerted call on birth.");
			Log.LogInfo((object)"[nick008tameablehares] Only prefab 'Hare' is modified; Boar, Deer and Neck are not touched.");
			return true;
		}
		catch (Exception ex)
		{
			Log.LogError((object)("[nick008tameablehares] Hare setup failed: " + ex));
			return false;
		}
	}

	private static int PopulateFoods(Nick008HareAI hareAI, ObjectDB db)
	{
		if ((Object)(object)hareAI == (Object)null || (Object)(object)db == (Object)null)
		{
			return 0;
		}
		if (((MonsterAI)hareAI).m_consumeItems == null)
		{
			((MonsterAI)hareAI).m_consumeItems = new List<ItemDrop>();
		}
		else
		{
			((MonsterAI)hareAI).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)hareAI).m_consumeItems.Contains(component))
				{
					((MonsterAI)hareAI).m_consumeItems.Add(component);
					num++;
				}
			}
		}
		return num;
	}

	private void ApplyHareSounds(ZNetScene scene)
	{
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)scene == (Object)null)
		{
			return;
		}
		GameObject prefab = scene.GetPrefab("Hare");
		if ((Object)(object)prefab == (Object)null)
		{
			return;
		}
		GameObject prefab2 = scene.GetPrefab("sfx_hare_idle");
		GameObject val = scene.GetPrefab("sfx_hare_alerted") ?? prefab2;
		GameObject prefab3 = scene.GetPrefab("vfx_boar_love");
		GameObject prefab4 = 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 val2 = ((component.m_sootheEffect != null) ? component.m_sootheEffect : component.m_tamedEffect);
				component.m_petEffect = (EffectList)((val2 != null) ? ((object)CloneEffectList(val2)) : ((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)prefab3 != (Object)null)
			{
				EnsureEffectPrefab(orCreateEffectList, prefab3);
			}
			if ((Object)(object)val != (Object)null)
			{
				ReplaceNamedEffectPrefab(orCreateEffectList2, "sfx_boar_birth", val);
				ReplaceNamedEffectPrefab(orCreateEffectList2, "btn_sfx_neck_birth", val);
				EnsureEffectPrefab(orCreateEffectList2, val);
			}
			if ((Object)(object)prefab4 != (Object)null)
			{
				EnsureEffectPrefab(orCreateEffectList2, prefab4);
			}
		}
	}

	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 CreateKitPrefab(ZNetScene scene, GameObject hare)
	{
		//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_HareKit");
		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 = hare;
			}
			return prefab;
		}
		if ((Object)(object)offspringRoot == (Object)null)
		{
			offspringRoot = new GameObject("nick008tameablehares_offspring_root");
			offspringRoot.transform.SetParent(((Component)this).transform, false);
			offspringRoot.SetActive(false);
		}
		GameObject val = Object.Instantiate<GameObject>(hare, offspringRoot.transform);
		((Object)val).name = "nick008_HareKit";
		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 = hare;
		val2.m_growTime = 2000f;
		RegisterNamedPrefab(scene, stableHashCode, val);
		Log.LogInfo((object)"[nick008tameablehares] Registered offspring prefab: nick008_HareKit");
		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 Nick008HareAI : 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 (Nick008TameableHaresPlugin.Log != null)
			{
				Nick008TameableHaresPlugin.Log.LogWarning((object)"[nick008tameablehares] Some private AI helpers were not found. Taming may still work, but please send the BepInEx log if hare 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;
				Nick008TameableHaresPlugin.Log.LogWarning((object)("[nick008tameablehares] 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;
	}
}
[HarmonyPatch]
internal static class HareKitButcherProtectionPatch
{
	private const string KitPrefabName = "nick008_HareKit";

	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 (!IsKit(__instance) || !IsButcherKnifeHit(__0))
			{
				return true;
			}
			if (!loggedBlockedHit && Nick008TameableHaresPlugin.Log != null)
			{
				loggedBlockedHit = true;
				Nick008TameableHaresPlugin.Log.LogInfo((object)"[nick008tameablehares] Protected hare kit from accidental butcher-knife hit.");
			}
			return false;
		}
		catch (Exception ex)
		{
			if (Nick008TameableHaresPlugin.Log != null)
			{
				Nick008TameableHaresPlugin.Log.LogWarning((object)("[nick008tameablehares] Hare-kit butcher protection check failed: " + ex.Message));
			}
			return true;
		}
	}

	private static bool IsKit(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_HareKit", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("nick008_HareKit(", StringComparison.OrdinalIgnoreCase))
		{
			return text.StartsWith("nick008_HareKit ", StringComparison.OrdinalIgnoreCase);
		}
		return true;
	}

	private static bool IsButcherKnifeHit(object hit)
	{
		if (hit == null)
		{
			return false;
		}
		object obj = InvokeNoArgMethod(hit, "GetAttacker");
		if (obj == null || !IsTypeOrBaseNamed(obj.GetType(), "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;
	}
}