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 POV LMTY BeastAI Adapter v0.3.3
plugins/POVLMTYBeastAIAdapter/POVLMTYBeastAIAdapter.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("POVLMTYBeastAIAdapter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("POVLMTYBeastAIAdapter")] [assembly: AssemblyTitle("POVLMTYBeastAIAdapter")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace POVLMTYBeastAIAdapter { [BepInPlugin("com.rehyun.povlmtybeastaiadapter", "POV LMTY BeastAI Adapter", "0.3.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.rehyun.povlmtybeastaiadapter"; public const string PluginName = "POV LMTY BeastAI Adapter"; public const string PluginVersion = "0.3.3"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("com.rehyun.povlmtybeastaiadapter"); MethodBase methodBase = AdapterPatch.TargetMethod(); if (methodBase == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not find LetMeTameYou.TameManager.AddTame. Adapter was not installed."); return; } _harmony.Patch(methodBase, new HarmonyMethod(typeof(AdapterPatch), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"POV LMTY BeastAI Adapter 0.3.3 loaded. Generic compatibility + adaptive taming fallback enabled; no prefab allowlist."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class AdapterPatch { private static readonly BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; internal static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("LetMeTameYou.TameManager"); if (type == null) { return null; } MethodInfo[] methods = type.GetMethods(AnyStatic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "AddTame")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(GameObject)) { return methodInfo; } } } return null; } public static bool Prefix(object[] __args) { try { if (__args == null || __args.Length < 2) { return true; } object obj = __args[0]; GameObject val = (GameObject)((obj is GameObject) ? obj : null); object obj2 = __args[1]; if ((Object)(object)val == (Object)null || obj2 == null) { return true; } string text = CleanPrefabName(((Object)val).name); Plugin.Log.LogInfo((object)("[LMTY Trace] BEGIN prefab=" + text + "; AI=" + GetBaseAIType(val) + "; " + $"MonsterAI={(Object)(object)val.GetComponent<MonsterAI>() != (Object)null}; AnimalAI={(Object)(object)val.GetComponent<AnimalAI>() != (Object)null}; " + $"Character={(Object)(object)val.GetComponent<Character>() != (Object)null}; Humanoid={(Object)(object)val.GetComponent<Humanoid>() != (Object)null}; " + $"Tameable={(Object)(object)val.GetComponent<Tameable>() != (Object)null}; Procreation={(Object)(object)val.GetComponent<Procreation>() != (Object)null}")); MonsterAI component = val.GetComponent<MonsterAI>(); Character component2 = val.GetComponent<Character>(); Humanoid component3 = val.GetComponent<Humanoid>(); AnimalAI component4 = val.GetComponent<AnimalAI>(); Tameable component5 = val.GetComponent<Tameable>(); Procreation component6 = val.GetComponent<Procreation>(); string baseAIType = GetBaseAIType(val); if (!((Object)(object)component != (Object)null) || !((Object)(object)component2 != (Object)null) || !((Object)(object)component3 == (Object)null) || !((Object)(object)component4 == (Object)null)) { return true; } if (!IsHighConfidencePovAI(component, out var evidence)) { Plugin.Log.LogWarning((object)("[Generic Compatibility] Suspicious LMTY target detected but auto-repair was skipped: prefab=" + text + "; AI=" + baseAIType + "; reason=unknown/non-POV AI. This prevents the adapter from modifying unrelated custom-AI mods.")); return true; } Plugin.Log.LogWarning((object)("[Generic Compatibility] AUTO-REPAIR triggered: prefab=" + text + "; AI=" + baseAIType + "; shape=MonsterAI+Character/no-Humanoid/no-AnimalAI; POV=" + evidence)); if (HasSpecialEggConfig(obj2, out var description)) { Plugin.Log.LogWarning((object)("[Generic Compatibility] prefab=" + text + " has special egg config (" + description + "). v0.3.x will not auto-repair this special reproduction path; original LMTY will handle it.")); return true; } if (GetBool(obj2, "removeTameable", fallback: false)) { return true; } ConfigureCharacter(val, component2, obj2); component5 = ConfigureTameable(val, component5, obj2); ConfigureFood(component, obj2); if (GetBool(obj2, "procreation", fallback: true)) { component6 = ConfigureProcreation(val, component6, obj2); ConfigureOffspring(val, component6, obj2); } else if ((Object)(object)component6 != (Object)null) { Object.DestroyImmediate((Object)(object)component6); Plugin.Log.LogInfo((object)"[BeastAI Adapter] procreation=false; removed Procreation."); } Procreation component7 = val.GetComponent<Procreation>(); Plugin.Log.LogInfo((object)("[Generic Compatibility] SUCCESS: LMTY creature settings applied without replacing POV " + baseAIType + ". " + $"Tameable={(Object)(object)val.GetComponent<Tameable>() != (Object)null}; " + $"Procreation={(Object)(object)component7 != (Object)null}; " + "offspring=" + (((Object)(object)component7 != (Object)null && (Object)(object)component7.m_offspring != (Object)null) ? ((Object)component7.m_offspring).name : "<none>"))); return false; } catch (Exception ex) { Plugin.Log.LogError((object)("[BeastAI Adapter] Adapter failed. Falling back to original LMTY AddTame.\n" + ex)); return true; } } private static void ConfigureCharacter(GameObject go, Character character, object tb) { string text = GetString(tb, "specialGroup", ""); bool flag = GetBool(tb, "hostileToSelf", fallback: false); if (!string.IsNullOrEmpty(text)) { character.m_group = text; } else if (!flag) { character.m_group = CleanPrefabName(((Object)go).name); } else { character.m_group = ""; } } private static Tameable ConfigureTameable(GameObject go, Tameable tameable, object tb) { if ((Object)(object)tameable == (Object)null) { tameable = go.AddComponent<Tameable>(); Plugin.Log.LogInfo((object)"[BeastAI Adapter] Added missing Tameable."); } tameable.m_commandable = GetBool(tb, "commandable", fallback: true); tameable.m_tamingTime = GetFloat(tb, "tamingTime", 1800f); tameable.m_fedDuration = GetFloat(tb, "fedDuration", 600f); return tameable; } private static void ConfigureFood(MonsterAI ai, object tb) { ai.m_consumeRange = GetFloat(tb, "consumeRange", 2f); ai.m_consumeSearchInterval = GetFloat(tb, "consumeSearchInterval", 10f); ai.m_consumeSearchRange = GetFloat(tb, "consumeSearchRange", 10f); ai.m_consumeItems = new List<ItemDrop>(); string text = GetString(tb, "consumeItems", ""); if (string.IsNullOrWhiteSpace(text)) { Plugin.Log.LogWarning((object)"[BeastAI Adapter] consumeItems is empty."); return; } if ((Object)(object)ObjectDB.instance == (Object)null) { Plugin.Log.LogWarning((object)"[BeastAI Adapter] ObjectDB.instance is null while applying food config."); return; } char[] separator = new char[3] { ',', ':', ';' }; string[] array = text.Split(separator, StringSplitOptions.RemoveEmptyEntries); List<string> list = new List<string>(); List<string> list2 = new List<string>(); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text2 = array2[i].Trim(); if (text2.Length != 0) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text2); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val != (Object)null) { ai.m_consumeItems.Add(val); list.Add(((Object)itemPrefab).name); } else { list2.Add(text2); } } } Plugin.Log.LogInfo((object)("[BeastAI Adapter] Food config: raw=\"" + text + "\"; resolved=[" + string.Join(", ", list.ToArray()) + "]; missing=[" + string.Join(", ", list2.ToArray()) + "]; count=" + ai.m_consumeItems.Count + "; consumeRange=" + ai.m_consumeRange + "; searchRange=" + ai.m_consumeSearchRange + "; searchInterval=" + ai.m_consumeSearchInterval)); EnsureConsumeBridge(((Component)ai).gameObject); } private static void EnsureConsumeBridge(GameObject go) { if (!((Object)(object)go == (Object)null) && (Object)(object)go.GetComponent<BeastAIConsumeBridge>() == (Object)null) { go.AddComponent<BeastAIConsumeBridge>(); Plugin.Log.LogInfo((object)("[BeastAI Adapter] Added BeastAIConsumeBridge to " + CleanPrefabName(((Object)go).name))); } } private static Procreation ConfigureProcreation(GameObject go, Procreation proc, object tb) { bool flag = false; if ((Object)(object)proc == (Object)null) { proc = go.AddComponent<Procreation>(); flag = true; } proc.m_maxCreatures = GetInt(tb, "maxCreatures", 5); float num = GetFloat(tb, "pregnancyChance", 0.66f); if (num > 1f) { num *= 0.01f; } if (num > 1f) { num = 0.66f; } proc.m_pregnancyChance = num; proc.m_pregnancyDuration = GetFloat(tb, "pregnancyDuration", 60f); if (proc.m_updateInterval < 20f) { proc.m_updateInterval = 20f; } float num2 = GetFloat(tb, "size", 1f); proc.m_partnerCheckRange = 4f * num2; proc.m_totalCheckRange = 10f * num2; ClearProcreationCaches(proc); Plugin.Log.LogInfo((object)($"[BeastAI Adapter] Procreation configured: added={flag}; max={proc.m_maxCreatures}; " + $"chance={proc.m_pregnancyChance}; pregnancy={proc.m_pregnancyDuration}s; " + $"partnerRange={proc.m_partnerCheckRange}; totalRange={proc.m_totalCheckRange}")); return proc; } private static void ConfigureOffspring(GameObject go, Procreation proc, object tb) { bool flag = GetBool(tb, "offspringOverwrite", fallback: false); float num = GetFloat(tb, "growTime", 1800f); if ((Object)(object)proc.m_offspring != (Object)null && !flag) { ApplyGrowTime(proc.m_offspring, num); Plugin.Log.LogInfo((object)("[Generic Compatibility] Keeping existing offspring: " + ((Object)proc.m_offspring).name)); return; } object member = GetMember(tb, "offspringPrefab"); GameObject val = (GameObject)((member is GameObject) ? member : null); string text = (((Object)(object)val != (Object)null) ? CleanPrefabName(((Object)val).name) : ((member != null) ? member.ToString().Trim() : "")); if (!string.IsNullOrEmpty(text)) { if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(text); } if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("Configured offspringPrefab could not be resolved from ZNetScene: " + text); } proc.m_offspring = val; ApplyGrowTime(val, num); ClearProcreationCaches(proc); Plugin.Log.LogInfo((object)("[Generic Compatibility] Explicit LMTY offspringPrefab assigned: " + ((Object)val).name)); return; } Type type = AccessTools.TypeByName("LetMeTameYou.TameManager"); MethodInfo obj = ((type != null) ? AccessTools.Method(type, "SpawnMini", new Type[2] { typeof(GameObject), typeof(float) }, (Type[])null) : null); if (obj == null) { throw new MissingMethodException("LetMeTameYou.TameManager.SpawnMini(GameObject,float) not found."); } object? obj2 = obj.Invoke(null, new object[2] { go, num }); GameObject val2 = (GameObject)((obj2 is GameObject) ? obj2 : null); if ((Object)(object)val2 == (Object)null) { throw new InvalidOperationException("LMTY SpawnMini returned null."); } proc.m_offspring = val2; ClearProcreationCaches(proc); Plugin.Log.LogInfo((object)("[Generic Compatibility] LMTY offspring assigned: " + ((Object)val2).name)); } private static void ApplyGrowTime(GameObject offspring, float growTime) { if ((Object)(object)offspring == (Object)null) { return; } Growup component = offspring.GetComponent<Growup>(); if ((Object)(object)component != (Object)null) { component.m_growTime = growTime; return; } EggGrow component2 = offspring.GetComponent<EggGrow>(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.m_grownPrefab != (Object)null) { Growup component3 = component2.m_grownPrefab.GetComponent<Growup>(); if ((Object)(object)component3 != (Object)null) { component3.m_growTime = growTime; } } } private static void ClearProcreationCaches(Procreation proc) { AccessTools.Field(typeof(Procreation), "m_offspringPrefab")?.SetValue(proc, null); AccessTools.Field(typeof(Procreation), "m_myPrefab")?.SetValue(proc, null); } private static bool HasSpecialEggConfig(object tb, out string description) { description = ""; object member = GetMember(tb, "egg"); if (member == null) { return false; } Type type = member.GetType(); string text; if (member is string) { text = ((string)member).Trim(); } else { if (!type.IsEnum) { return false; } text = member.ToString().Trim(); } if (string.IsNullOrEmpty(text) || string.Equals(text, "None", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "False", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "0", StringComparison.OrdinalIgnoreCase)) { return false; } description = text; return true; } private static bool IsHighConfidencePovAI(MonsterAI ai, out string evidence) { evidence = "<none>"; if ((Object)(object)ai == (Object)null) { return false; } Type type = ((object)ai).GetType(); Type type2 = AccessTools.TypeByName("PassiveTree.BeastAI"); if (type2 != null && (type == type2 || type2.IsAssignableFrom(type))) { evidence = "PassiveTree.BeastAI type match"; return true; } if ((type.FullName ?? type.Name ?? "").StartsWith("PassiveTree.", StringComparison.Ordinal)) { evidence = "PassiveTree namespace"; return true; } if (((type.Assembly != null) ? (type.Assembly.GetName().Name ?? "") : "").IndexOf("PathOfValheiman", StringComparison.OrdinalIgnoreCase) >= 0) { evidence = "PathOfValheiman assembly"; return true; } return false; } private static string GetBaseAIType(GameObject go) { BaseAI[] components = go.GetComponents<BaseAI>(); if (components == null || components.Length == 0) { return "<none>"; } List<string> list = new List<string>(); BaseAI[] array = components; foreach (BaseAI val in array) { if ((Object)(object)val != (Object)null) { list.Add(((object)val).GetType().FullName ?? ((object)val).GetType().Name); } } return string.Join(",", list); } private static string CleanPrefabName(string name) { if (string.IsNullOrEmpty(name)) { return ""; } if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length); } private static object GetMember(object obj, string name) { if (obj == null) { return null; } Type type = obj.GetType(); PropertyInfo property = type.GetProperty(name, AnyInstance); if (property != null) { return property.GetValue(obj, null); } FieldInfo field = type.GetField(name, AnyInstance); if (field != null) { return field.GetValue(obj); } return null; } private static bool GetBool(object obj, string name, bool fallback) { object member = GetMember(obj, name); try { return (member == null) ? fallback : Convert.ToBoolean(member); } catch { return fallback; } } private static int GetInt(object obj, string name, int fallback) { object member = GetMember(obj, name); try { return (member == null) ? fallback : Convert.ToInt32(member); } catch { return fallback; } } private static float GetFloat(object obj, string name, float fallback) { object member = GetMember(obj, name); try { return (member == null) ? fallback : Convert.ToSingle(member); } catch { return fallback; } } private static string GetString(object obj, string name, string fallback) { return GetMember(obj, name)?.ToString() ?? fallback; } } internal sealed class BeastAIConsumeBridge : MonoBehaviour { private MonsterAI _ai; private Tameable _tameable; private ZNetView _nview; private float _timer; private float _nextDiagnostic; private float _nextProgressDiagnostic; private float _progressTickTimer; private float _searchTimer; private ItemDrop _targetFood; private float _lastObservedTimeLeft = -1f; private float _nativeObservationElapsed; private bool _nativeProgressConfirmed; private bool _fallbackActivated; private bool _observationLogged; private const float NativeObservationWindow = 6f; private static readonly MethodInfo MoveToMethod = AccessTools.Method(typeof(BaseAI), "MoveTo", new Type[4] { typeof(float), typeof(Vector3), typeof(float), typeof(bool) }, (Type[])null); private static readonly MethodInfo DecreaseRemainingTimeMethod = AccessTools.Method(typeof(Tameable), "DecreaseRemainingTime", new Type[1] { typeof(float) }, (Type[])null); private static readonly MethodInfo GetRemainingTimeMethod = AccessTools.Method(typeof(Tameable), "GetRemainingTime", Type.EmptyTypes, (Type[])null); private static readonly MethodInfo TameMethod = AccessTools.Method(typeof(Tameable), "Tame", Type.EmptyTypes, (Type[])null); private void Awake() { _ai = ((Component)this).GetComponent<MonsterAI>(); _tameable = ((Component)this).GetComponent<Tameable>(); _nview = ((Component)this).GetComponent<ZNetView>(); if ((Object)(object)_ai != (Object)null && (Object)(object)_tameable != (Object)null) { Plugin.Log.LogInfo((object)("[BeastAI Feed] Runtime bridge awake: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; AI=" + ((object)_ai).GetType().FullName + "; foods=" + ((_ai.m_consumeItems != null) ? _ai.m_consumeItems.Count : 0))); } } private void Update() { //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_ai == (Object)null || (Object)(object)_tameable == (Object)null) { return; } _timer -= Time.deltaTime; if (_timer > 0f) { return; } _timer = 0.35f; if (_ai.m_consumeItems == null || _ai.m_consumeItems.Count == 0 || ((Object)(object)_nview != (Object)null && _nview.IsValid() && !_nview.IsOwner())) { return; } bool flag; try { flag = _tameable.IsHungry(); } catch { return; } if (!flag) { _targetFood = null; _searchTimer = 0f; RunTamingFallbackTick(); LogTamingProgress(); return; } _progressTickTimer = 0f; _lastObservedTimeLeft = -1f; _nativeObservationElapsed = 0f; _nativeProgressConfirmed = false; _fallbackActivated = false; _observationLogged = false; if (!IsValidTarget(_targetFood)) { _targetFood = null; _searchTimer -= 0.35f; if (_searchTimer <= 0f) { _searchTimer = Mathf.Max(1f, _ai.m_consumeSearchInterval); _targetFood = FindNearestAllowedFood(out var _); } } if ((Object)(object)_targetFood == (Object)null) { return; } float num = Vector3.Distance(((Component)this).transform.position, ((Component)_targetFood).transform.position); float num2 = Mathf.Max(_ai.m_consumeRange, _ai.m_consumeSearchRange); if (num > num2) { _targetFood = null; return; } float num3 = Mathf.Max(0.5f, _ai.m_consumeRange); if (num > num3) { TryMoveTo(((Component)_targetFood).transform.position, num3 * 0.8f); if (Time.time >= _nextDiagnostic) { _nextDiagnostic = Time.time + 3f; Plugin.Log.LogInfo((object)("[BeastAI Feed] Food detected: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; food=" + CleanName(((Object)((Component)_targetFood).gameObject).name) + "; distance=" + num.ToString("0.00") + "; eatRange=" + num3.ToString("0.00") + "; movingTowardFood=True")); } } else { ItemDrop targetFood = _targetFood; _targetFood = null; Consume(targetFood, num); } } private bool IsValidTarget(ItemDrop drop) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)drop == (Object)null || !((Component)drop).gameObject.activeInHierarchy || drop.m_itemData == null || drop.m_itemData.m_shared == null) { return false; } if (!IsAllowed(drop)) { return false; } float num = Mathf.Max(_ai.m_consumeRange, _ai.m_consumeSearchRange); return Vector3.Distance(((Component)this).transform.position, ((Component)drop).transform.position) <= num; } private void RunTamingFallbackTick() { if (_tameable.IsTamed()) { return; } _progressTickTimer += 0.35f; if (_progressTickTimer < 3f) { return; } float progressTickTimer = _progressTickTimer; _progressTickTimer = 0f; if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner()) { return; } bool flag = false; try { flag = ((BaseAI)_ai).IsAlerted(); } catch { flag = false; } ZDO zDO = _nview.GetZDO(); if (zDO == null) { return; } float num = zDO.GetFloat("TameTimeLeft", -1f); if (num < 0f) { long num2 = zDO.GetLong("TameLastFeeding", 0L); if (num2 <= 0) { if (Time.time >= _nextDiagnostic) { _nextDiagnostic = Time.time + 3f; Plugin.Log.LogWarning((object)("[Adaptive Taming] Fed-state has no TameTimeLeft and no TameLastFeeding; countdown not initialized: prefab=" + CleanName(((Object)((Component)this).gameObject).name))); } return; } num = Mathf.Max(0f, _tameable.m_tamingTime); zDO.Set("TameTimeLeft", num); _lastObservedTimeLeft = num; _nativeObservationElapsed = 0f; _nativeProgressConfirmed = false; _fallbackActivated = false; _observationLogged = false; Plugin.Log.LogWarning((object)("[Adaptive Taming] Fed creature had no TameTimeLeft; initialized countdown: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; TameTimeLeft=" + num.ToString("0.0") + "; TameLastFeeding=" + num2)); } if (flag) { if (Time.time >= _nextDiagnostic) { _nextDiagnostic = Time.time + 3f; Plugin.Log.LogInfo((object)("[BeastAI Feed] Taming paused: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; reason=alerted; TameTimeLeft=" + num.ToString("0.0"))); } _lastObservedTimeLeft = num; return; } if (_lastObservedTimeLeft >= 0f && num < _lastObservedTimeLeft - 0.01f) { if (!_nativeProgressConfirmed) { Plugin.Log.LogInfo((object)("[Adaptive Taming] Native progress detected; fallback remains inactive: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; previous=" + _lastObservedTimeLeft.ToString("0.0") + "; current=" + num.ToString("0.0"))); } _nativeProgressConfirmed = true; _fallbackActivated = false; _nativeObservationElapsed = 0f; _lastObservedTimeLeft = num; return; } if (_nativeProgressConfirmed) { _lastObservedTimeLeft = num; return; } _nativeObservationElapsed += progressTickTimer; if (!_fallbackActivated && _nativeObservationElapsed < 6f) { if (!_observationLogged) { _observationLogged = true; Plugin.Log.LogInfo((object)("[Adaptive Taming] Observing native timer before fallback: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; window=" + 6f.ToString("0") + "s; TameTimeLeft=" + num.ToString("0.0"))); } _lastObservedTimeLeft = num; return; } if (!_fallbackActivated) { _fallbackActivated = true; Plugin.Log.LogWarning((object)("[Adaptive Taming] Native timer did not advance; fallback activated: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; observed=" + _nativeObservationElapsed.ToString("0.0") + "s; TameTimeLeft=" + num.ToString("0.0"))); } try { if (DecreaseRemainingTimeMethod != null) { DecreaseRemainingTimeMethod.Invoke(_tameable, new object[1] { progressTickTimer }); } else { zDO.Set("TameTimeLeft", Mathf.Max(0f, num - progressTickTimer)); } float num3 = (_lastObservedTimeLeft = ((GetRemainingTimeMethod != null) ? ((float)GetRemainingTimeMethod.Invoke(_tameable, null)) : zDO.GetFloat("TameTimeLeft", -1f))); Plugin.Log.LogInfo((object)("[BeastAI Feed] Fallback taming tick: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; before=" + num.ToString("0.0") + "; after=" + num3.ToString("0.0") + "; alerted=False")); if (num3 <= 0f && !_tameable.IsTamed()) { if (TameMethod != null) { TameMethod.Invoke(_tameable, null); } Plugin.Log.LogInfo((object)("[BeastAI Feed] Taming completed by fallback: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; tamed=" + _tameable.IsTamed())); } } catch (Exception ex) { Plugin.Log.LogError((object)("[BeastAI Feed] Fallback taming tick failed on " + CleanName(((Object)((Component)this).gameObject).name) + ": " + ex)); } } private void LogTamingProgress() { if (_tameable.IsTamed() || Time.time < _nextProgressDiagnostic) { return; } _nextProgressDiagnostic = Time.time + 15f; if (!((Object)(object)_nview == (Object)null) && _nview.IsValid()) { ZDO zDO = _nview.GetZDO(); if (zDO != null) { float num = zDO.GetFloat("TameTimeLeft", -1f); float tamingTime = _tameable.m_tamingTime; float num2 = ((num >= 0f && tamingTime > 0f) ? (Mathf.Clamp01(1f - num / tamingTime) * 100f) : (-1f)); Plugin.Log.LogInfo((object)("[BeastAI Feed] Taming progress: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; hungry=False; TameTimeLeft=" + num.ToString("0.0") + "; tamingTime=" + tamingTime.ToString("0.0") + "; approxPercent=" + ((num2 >= 0f) ? num2.ToString("0.0") : "unknown") + "; tamed=" + _tameable.IsTamed())); } } } private ItemDrop FindNearestAllowedFood(out float nearestDistance) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) nearestDistance = float.MaxValue; ItemDrop result = null; float num = Mathf.Max(_ai.m_consumeRange, _ai.m_consumeSearchRange); Vector3 position = ((Component)this).transform.position; ItemDrop[] array = Object.FindObjectsByType<ItemDrop>((FindObjectsSortMode)0); foreach (ItemDrop val in array) { if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && val.m_itemData != null && val.m_itemData.m_shared != null && IsAllowed(val)) { float num2 = Vector3.Distance(position, ((Component)val).transform.position); if (!(num2 > num) && !(num2 >= nearestDistance)) { result = val; nearestDistance = num2; } } } return result; } private bool IsAllowed(ItemDrop drop) { string name = drop.m_itemData.m_shared.m_name; string text = CleanName(((Object)((Component)drop).gameObject).name); foreach (ItemDrop consumeItem in _ai.m_consumeItems) { if (!((Object)(object)consumeItem == (Object)null) && consumeItem.m_itemData != null && consumeItem.m_itemData.m_shared != null) { if (consumeItem.m_itemData.m_shared.m_name == name) { return true; } if (CleanName(((Object)((Component)consumeItem).gameObject).name) == text) { return true; } } } return false; } private void Consume(ItemDrop drop, float distance) { try { if ((Object)(object)drop == (Object)null || !drop.RemoveOne()) { return; } if (_ai.m_onConsumedItem != null) { _ai.m_onConsumedItem(null); } if ((Object)(object)_nview != (Object)null && _nview.IsValid()) { ZDO zDO = _nview.GetZDO(); if (zDO != null) { long num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTime().Ticks : DateTime.UtcNow.Ticks); zDO.Set("TameLastFeeding", num); float num2 = zDO.GetFloat("TameTimeLeft", -1f); bool flag = false; if (num2 < 0f) { num2 = _tameable.m_tamingTime; zDO.Set("TameTimeLeft", num2); flag = true; } _lastObservedTimeLeft = num2; _nativeObservationElapsed = 0f; _nativeProgressConfirmed = false; _fallbackActivated = false; _observationLogged = false; Plugin.Log.LogInfo((object)("[BeastAI Feed] Feeding state updated: TameLastFeeding=" + num + "; TameTimeLeft=" + num2.ToString("0.0") + "; initialized=" + flag + "; tamingTime=" + _tameable.m_tamingTime.ToString("0.0"))); } } Plugin.Log.LogInfo((object)("[BeastAI Feed] CONSUMED: prefab=" + CleanName(((Object)((Component)this).gameObject).name) + "; food=" + CleanName(((Object)((Component)drop).gameObject).name) + "; distance=" + distance.ToString("0.00") + "; tamed=" + _tameable.IsTamed())); } catch (Exception ex) { Plugin.Log.LogError((object)("[BeastAI Feed] Consume failed on " + CleanName(((Object)((Component)this).gameObject).name) + ": " + ex)); } } private void TryMoveTo(Vector3 point, float stopDistance) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (MoveToMethod == null) { return; } try { MoveToMethod.Invoke(_ai, new object[4] { 0.35f, point, stopDistance, false }); } catch { } } private static string CleanName(string name) { if (string.IsNullOrEmpty(name)) { return ""; } int num = name.IndexOf('('); int num2 = name.IndexOf(' '); int num3 = -1; if (num >= 0) { num3 = num; } if (num2 >= 0 && (num3 < 0 || num2 < num3)) { num3 = num2; } if (num3 < 0) { return name; } return name.Substring(0, num3); } } }