Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TameableHares v1.0.4
plugins/nick008tameablehares.dll
Decompiled 2 days agousing 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; } }