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 TameableDeer v1.0.8
BepInEx/plugins/nick008tameabledeer.dll
Decompiled a day agousing 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; } }