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 FeedLikeGrandma v1.0.0
FeedLikeGrandma.dll
Decompiled 8 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using LocalizationManager; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("FeedLikeGrandma")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("FeedLikeGrandma")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 LocalizationManager { [PublicAPI] public class Localizer { private static readonly Dictionary<string, Dictionary<string, Func<string>>> PlaceholderProcessors; private static readonly Dictionary<string, Dictionary<string, string>> loadedTexts; private static readonly ConditionalWeakTable<Localization, string> localizationLanguage; private static readonly List<WeakReference<Localization>> localizationObjects; private static BaseUnityPlugin? _plugin; private static readonly List<string> fileExtensions; private static BaseUnityPlugin plugin { get { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown if (_plugin == null) { IEnumerable<TypeInfo> source; try { source = Assembly.GetExecutingAssembly().DefinedTypes.ToList(); } catch (ReflectionTypeLoadException ex) { source = from t in ex.Types where t != null select t.GetTypeInfo(); } _plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t))); } return _plugin; } } public static event Action? OnLocalizationComplete; private static void UpdatePlaceholderText(Localization localization, string key) { localizationLanguage.TryGetValue(localization, out string value); string text = loadedTexts[value][key]; if (PlaceholderProcessors.TryGetValue(key, out Dictionary<string, Func<string>> value2)) { text = value2.Aggregate(text, (string current, KeyValuePair<string, Func<string>> kv) => current.Replace("{" + kv.Key + "}", kv.Value())); } localization.AddWord(key, text); } public static void AddPlaceholder<T>(string key, string placeholder, ConfigEntry<T> config, Func<T, string>? convertConfigValue = null) where T : notnull { if (convertConfigValue == null) { convertConfigValue = (T val) => val.ToString(); } if (!PlaceholderProcessors.ContainsKey(key)) { PlaceholderProcessors[key] = new Dictionary<string, Func<string>>(); } config.SettingChanged += delegate { UpdatePlaceholder(); }; if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage())) { UpdatePlaceholder(); } void UpdatePlaceholder() { PlaceholderProcessors[key][placeholder] = () => convertConfigValue(config.Value); UpdatePlaceholderText(Localization.instance, key); } } public static void AddText(string key, string text) { List<WeakReference<Localization>> list = new List<WeakReference<Localization>>(); foreach (WeakReference<Localization> localizationObject in localizationObjects) { if (localizationObject.TryGetTarget(out var target)) { Dictionary<string, string> dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)]; if (!target.m_translations.ContainsKey(key)) { dictionary[key] = text; target.AddWord(key, text); } } else { list.Add(localizationObject); } } foreach (WeakReference<Localization> item in list) { localizationObjects.Remove(item); } } public static void Load() { _ = plugin; } public static void LoadLocalizationLater(Localization __instance) { LoadLocalization(Localization.instance, __instance.GetSelectedLanguage()); } public static void SafeCallLocalizeComplete() { Localizer.OnLocalizationComplete?.Invoke(); } private static void LoadLocalization(Localization __instance, string language) { if (!localizationLanguage.Remove(__instance)) { localizationObjects.Add(new WeakReference<Localization>(__instance)); } localizationLanguage.Add(__instance, language); Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories) where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0 select f) { string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' }); if (array.Length >= 2) { string text = array[1]; if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } byte[] array2 = LoadTranslationFromAssembly("English"); if (array2 == null) { throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml."); } Dictionary<string, string> dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(Encoding.UTF8.GetString(array2)); if (dictionary2 == null) { throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty."); } string text2 = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text2 = File.ReadAllText(value); } else { byte[] array3 = LoadTranslationFromAssembly(language); if (array3 != null) { text2 = Encoding.UTF8.GetString(array3); } } } if (text2 == null && dictionary.TryGetValue("English", out var value2)) { text2 = File.ReadAllText(value2); } if (text2 != null) { foreach (KeyValuePair<string, string> item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(text2) ?? new Dictionary<string, string>()) { dictionary2[item2.Key] = item2.Value; } } loadedTexts[language] = dictionary2; foreach (KeyValuePair<string, string> item3 in dictionary2) { UpdatePlaceholderText(__instance, item3.Key); } } static Localizer() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown PlaceholderProcessors = new Dictionary<string, Dictionary<string, Func<string>>>(); loadedTexts = new Dictionary<string, Dictionary<string, string>>(); localizationLanguage = new ConditionalWeakTable<Localization, string>(); localizationObjects = new List<WeakReference<Localization>>(); fileExtensions = new List<string>(2) { ".json", ".yml" }; Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager"); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static byte[]? LoadTranslationFromAssembly(string language) { foreach (string fileExtension in fileExtensions) { byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension); if (array != null) { return array; } } return null; } public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null) { using MemoryStream memoryStream = new MemoryStream(); if ((object)containingAssembly == null) { containingAssembly = Assembly.GetCallingAssembly(); } string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal)); if (text != null) { containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream); } return (memoryStream.Length == 0L) ? null : memoryStream.ToArray(); } } public static class LocalizationManagerVersion { public const string Version = "1.4.1"; } } namespace System.Runtime.CompilerServices { internal static class IsExternalInit { } } namespace FeedLikeGrandma { internal static class FeedConsoleCommands { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__WriteReference; public static ConsoleOptionsFetcher <1>__GetEmptyOptions; } private static readonly List<string> EmptyOptions = new List<string>(); private static bool Registered; internal static void Register() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if (!Registered) { Registered = true; object obj = <>O.<0>__WriteReference; if (obj == null) { ConsoleEvent val = WriteReference; <>O.<0>__WriteReference = val; obj = (object)val; } object obj2 = <>O.<1>__GetEmptyOptions; if (obj2 == null) { ConsoleOptionsFetcher val2 = GetEmptyOptions; <>O.<1>__GetEmptyOptions = val2; obj2 = (object)val2; } new ConsoleCommand("flg:reference", "Write generated FeedLikeGrandma reference YAML. Usage: flg:reference", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false); } } private static List<string> GetEmptyOptions() { return EmptyOptions; } private static void WriteReference(ConsoleEventArgs args) { if (FeedLikeGrandmaPlugin.TryWriteReferenceConfigurationFile(out string path, out string error)) { Terminal context = args.Context; if (context != null) { context.AddString("Wrote FeedLikeGrandma reference to " + path); } } else { Terminal context2 = args.Context; if (context2 != null) { context2.AddString(error); } } } } internal static class FeedAutoFeedSystem { private const float ContainerCleanupInterval = 5f; private static readonly float[] DropPointAngles = new float[8] { 0f, 45f, -45f, 90f, -90f, 135f, -135f, 180f }; private static readonly List<Container> RegisteredFeedContainers = new List<Container>(); private static float _nextContainerCleanupTime; internal static void Shutdown() { RegisteredFeedContainers.Clear(); _nextContainerCleanupTime = 0f; } internal static void RegisterContainer(Container? container) { if (IsContainerAlive(container) && FeedBarrelSystem.IsFeedBarrel(container)) { FeedBarrelSystem.ApplyConfiguredContainerSize(container); if (!RegisteredFeedContainers.Contains(container)) { RegisteredFeedContainers.Add(container); } } } internal static void DeregisterContainer(Container? container) { if (!((Object)(object)container == (Object)null)) { RegisteredFeedContainers.Remove(container); } } internal static void RefreshRegisteredContainerSizes() { CleanupContainers(); foreach (Container registeredFeedContainer in RegisteredFeedContainers) { FeedBarrelSystem.ApplyConfiguredContainerSize(registeredFeedContainer); } } internal static bool TryFindClosestConsumableFromFeedBarrel(MonsterAI monsterAI, float maxRange, out ItemDrop? consumable) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) consumable = null; if (!FeedBarrelSystem.IsEnabled) { return false; } if ((Object)(object)monsterAI == (Object)null || maxRange <= 0f) { return false; } if (monsterAI.m_consumeItems == null || monsterAI.m_consumeItems.Count == 0) { return false; } CleanupContainersThrottled(); if (RegisteredFeedContainers.Count == 0) { return false; } GameObject gameObject = ((Component)monsterAI).gameObject; if (!FeedSystem.HasRuleFor(gameObject)) { return false; } ZNetView component = gameObject.GetComponent<ZNetView>(); Tameable component2 = gameObject.GetComponent<Tameable>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || (Object)(object)component2 == (Object)null || !component2.IsHungry() || ((BaseAI)monsterAI).IsAlerted()) { return false; } Vector3 position = ((Component)monsterAI).transform.position; float sqrRange = maxRange * maxRange; Container val = null; ItemData val2 = null; Vector3 dropPosition = Vector3.zero; float num = float.PositiveInfinity; foreach (Container registeredFeedContainer in RegisteredFeedContainers) { if (IsEligibleContainer(registeredFeedContainer, position, sqrRange)) { Vector3 val3 = GetContainerCenter(registeredFeedContainer) - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (!(sqrMagnitude >= num) && TrySelectFood(monsterAI, registeredFeedContainer, out ItemData item) && item != null && TryGetReachableDropPoint(monsterAI, registeredFeedContainer, position, maxRange, out var dropPosition2)) { val = registeredFeedContainer; val2 = item; dropPosition = dropPosition2; num = sqrMagnitude; } } } if ((Object)(object)val != (Object)null && val2 != null && TryRemoveAndDrop(val, val2, dropPosition, out consumable) && (Object)(object)consumable != (Object)null) { return true; } return false; } private static bool TrySelectFood(MonsterAI monsterAI, Container container, out ItemData? item) { item = null; Inventory inventory = container.GetInventory(); if (inventory == null) { return false; } if (!FeedSystem.TrySelectAutoFeedItem(monsterAI, inventory.GetAllItems(), out item) || item == null) { return false; } return EnsureDropPrefab(item); } private static bool TryRemoveAndDrop(Container container, ItemData item, Vector3 dropPosition, out ItemDrop? droppedItem) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) droppedItem = null; Inventory inventory = container.GetInventory(); ZNetView nview = container.m_nview; if (inventory == null || (Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } if (!nview.IsOwner()) { nview.ClaimOwnership(); } if (!nview.IsOwner()) { return false; } ItemData val = item.Clone(); val.m_dropPrefab = item.m_dropPrefab; if (!inventory.RemoveOneItem(item)) { return false; } container.Save(); droppedItem = ItemDrop.DropItem(val, 1, dropPosition, ((Component)container).transform.rotation); return (Object)(object)droppedItem != (Object)null; } private static bool TryGetReachableDropPoint(MonsterAI monsterAI, Container container, Vector3 animalPosition, float maxRange, out Vector3 dropPosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) Vector3 containerCenter = GetContainerCenter(container); Vector3 val = (((Object)(object)container.m_piece != (Object)null) ? ((StaticTarget)container.m_piece).FindClosestPoint(animalPosition) : containerCenter); Vector3 val2 = HorizontalNormalized(animalPosition - val); if (((Vector3)(ref val2)).sqrMagnitude <= 0f) { val2 = -((Component)container).transform.forward; } float num = Mathf.Max(1.25f, monsterAI.m_consumeRange * 0.75f); if (TryUseDropPoint(monsterAI, animalPosition, val + val2 * num, maxRange, out dropPosition)) { return true; } float num2 = Mathf.Max(1.5f, monsterAI.m_consumeRange + 0.5f); float[] dropPointAngles = DropPointAngles; foreach (float num3 in dropPointAngles) { Vector3 val3 = Quaternion.Euler(0f, num3, 0f) * val2; if (TryUseDropPoint(monsterAI, animalPosition, containerCenter + val3 * num2, maxRange, out dropPosition)) { return true; } } dropPosition = Vector3.zero; return false; } private static bool TryUseDropPoint(MonsterAI monsterAI, Vector3 animalPosition, Vector3 point, float maxRange, out Vector3 dropPosition) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) point = SnapToGround(point); Vector3 val = point - animalPosition; if (((Vector3)(ref val)).sqrMagnitude > maxRange * maxRange || !((BaseAI)monsterAI).HavePath(point)) { dropPosition = Vector3.zero; return false; } dropPosition = point + Vector3.up * 0.25f; return true; } private static Vector3 GetContainerCenter(Container container) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)container.m_piece != (Object)null) { return ((StaticTarget)container.m_piece).GetCenter(); } return ((Component)container).transform.position; } private static Vector3 HorizontalNormalized(Vector3 vector) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) vector.y = 0f; if (!(((Vector3)(ref vector)).sqrMagnitude > 0.0001f)) { return Vector3.zero; } return ((Vector3)(ref vector)).normalized; } private static Vector3 SnapToGround(Vector3 point) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) float y = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(point, ref y)) { point.y = y; } return point; } private static bool IsEligibleContainer(Container container, Vector3 animalPosition, float sqrRange) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!IsContainerObjectValid(container)) { return false; } Inventory inventory = container.GetInventory(); if (inventory == null || inventory.NrOfItems() <= 0 || container.IsInUse()) { return false; } if (!FeedBarrelSystem.IsEnabled || !FeedBarrelSystem.IsFeedBarrel(container)) { return false; } Vector3 val = GetContainerCenter(container) - animalPosition; return ((Vector3)(ref val)).sqrMagnitude <= sqrRange; } private static bool IsContainerAlive(Container? container) { return (Object)(object)container != (Object)null; } private static bool IsContainerObjectValid(Container? container) { if (!IsContainerAlive(container)) { return false; } ZNetView nview = container.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || container.GetInventory() == null) { return false; } return nview.GetZDO().GetLong(ZDOVars.s_creator, 0L) != 0; } private static bool EnsureDropPrefab(ItemData item) { if ((Object)(object)item.m_dropPrefab != (Object)null) { return true; } GameObject dropPrefab = default(GameObject); if (ObjectDB.instance == null || !ObjectDB.instance.TryGetItemPrefab(item.m_shared, ref dropPrefab)) { return false; } item.m_dropPrefab = dropPrefab; return true; } private static void CleanupContainersThrottled() { if (RegisteredFeedContainers.Count != 0 && !(Time.time < _nextContainerCleanupTime)) { _nextContainerCleanupTime = Time.time + 5f; CleanupContainers(); } } private static void CleanupContainers() { RegisteredFeedContainers.RemoveAll((Container container) => !IsContainerAlive(container) || !FeedBarrelSystem.IsFeedBarrel(container)); } } internal static class FeedBarrelSystem { private sealed class RendererMaterialSnapshot { public Renderer Renderer { get; } public Material[] Materials { get; } public RendererMaterialSnapshot(Renderer renderer, Material[] materials) { Renderer = renderer; Materials = materials; } } internal const string PrefabName = "FLG_FeedBarrel"; private const string SourcePrefabName = "piece_chest_barrel"; private const string EctoplasmMaterialName = "ectoplasm_mat"; private const int DefaultColumns = 6; private const int DefaultRows = 2; private const int MinColumns = 6; private const int MaxColumns = 8; private const int MinRows = 2; private const int MaxRows = 100; private const int IconSize = 128; private const int IconLayer = 30; private static GameObject? _prefab; private static GameObject? _prefabContainer; private static readonly Dictionary<string, Material> MaterialLookupCache = new Dictionary<string, Material>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> MaterialLookupMissCache = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static List<RendererMaterialSnapshot>? _originalMaterials; private static Sprite? _originalIcon; private static Sprite? _ectoplasmIcon; private static bool _materialLookupCacheBuilt; internal static bool IsEnabled => !string.IsNullOrWhiteSpace(FeedLikeGrandmaPlugin.FeedBarrelRecipe.Value); internal static void OnGameDataChanged() { MaterialLookupCache.Clear(); MaterialLookupMissCache.Clear(); _materialLookupCacheBuilt = false; } internal static void RegisterPrefabsAndPieces(ObjectDB? objectDb = null) { GameObject val = EnsurePrefab(); if (val != null) { ConfigurePrefab(val, objectDb ?? ObjectDB.instance); if (ZNetScene.instance != null) { RegisterWithZNetScene(ZNetScene.instance, val); } RegisterWithHammer(objectDb ?? ObjectDB.instance, val); FeedAutoFeedSystem.RefreshRegisteredContainerSizes(); } } internal static bool IsFeedBarrel(Container? container) { if ((Object)(object)container == (Object)null) { return false; } return IsFeedBarrel(((Component)container).gameObject); } internal static bool IsFeedBarrel(GameObject? gameObject) { if ((Object)(object)gameObject == (Object)null) { return false; } return string.Equals(Utils.GetPrefabName(gameObject), "FLG_FeedBarrel", StringComparison.Ordinal); } private static GameObject? EnsurePrefab() { if (_prefab != null) { return _prefab; } GameObject val = FindScenePrefab("FLG_FeedBarrel"); if (val != null) { _prefab = val; return _prefab; } GameObject val2 = FindScenePrefab("piece_chest_barrel"); if (val2 == null) { return null; } GameObject val3 = Object.Instantiate<GameObject>(val2, EnsurePrefabContainer().transform); ((Object)val3).name = "FLG_FeedBarrel"; if (val3.GetComponent<Piece>() == null || val3.GetComponent<Container>() == null || val3.GetComponent<ZNetView>() == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)"Could not create FLG_FeedBarrel: piece_chest_barrel did not have the expected Piece, Container, and ZNetView components."); Object.Destroy((Object)(object)val3); return null; } _prefab = val3; return _prefab; } private static void ConfigurePrefab(GameObject prefab, ObjectDB? objectDb) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) Piece component = prefab.GetComponent<Piece>(); if (component != null) { bool isEnabled = IsEnabled; component.m_name = "$flg_feed_barrel"; component.m_description = "$flg_feed_barrel_description"; component.m_enabled = isEnabled; component.m_category = (PieceCategory)0; StoreOriginalIcon(component); StoreOriginalMaterials(prefab); ApplyConfiguredMaterial(prefab, component); if (objectDb != null) { component.m_resources = (isEnabled ? BuildRequirements(objectDb, FeedLikeGrandmaPlugin.FeedBarrelRecipe.Value).ToArray() : Array.Empty<Requirement>()); } } Container component2 = prefab.GetComponent<Container>(); if (component2 != null) { component2.m_name = "$flg_feed_barrel"; ApplyConfiguredContainerSize(component2); } } internal static void ApplyConfiguredContainerSize(Container? container) { if (container != null && IsFeedBarrel(container)) { GetConfiguredContainerSize(out var columns, out var rows); container.m_width = columns; container.m_height = rows; Inventory inventory = container.GetInventory(); if (inventory != null) { inventory.m_width = columns; inventory.m_height = rows; } } } private static void ApplyConfiguredMaterial(GameObject prefab, Piece piece) { RestoreOriginalMaterials(); piece.m_icon = _originalIcon; if (FeedLikeGrandmaPlugin.FeedBarrelMaterialStyle.Value != FeedLikeGrandmaPlugin.FeedBarrelMaterial.Ectoplasm) { return; } Material val = ResolveMaterial("ectoplasm_mat"); if (val == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)"FLG_FeedBarrel material 'ectoplasm_mat' was not found. Using default barrel material."); return; } ApplyMaterialOverride(prefab, val); if (_ectoplasmIcon == null) { _ectoplasmIcon = RenderPieceIcon(prefab); } if (_ectoplasmIcon != null) { piece.m_icon = _ectoplasmIcon; } } private static void GetConfiguredContainerSize(out int columns, out int rows) { columns = ((FeedLikeGrandmaPlugin.FeedBarrelContainerColumns != null) ? FeedLikeGrandmaPlugin.FeedBarrelContainerColumns.Value : 6); rows = ((FeedLikeGrandmaPlugin.FeedBarrelContainerRows != null) ? FeedLikeGrandmaPlugin.FeedBarrelContainerRows.Value : 2); columns = Mathf.Clamp(columns, 6, 8); rows = Mathf.Clamp(rows, 2, 100); } private static void RegisterWithZNetScene(ZNetScene zNetScene, GameObject prefab) { int stableHashCode = StringExtensionMethods.GetStableHashCode("FLG_FeedBarrel"); if ((Object)(object)prefab.GetComponent<ZNetView>() != (Object)null) { if (zNetScene.m_prefabs.All((GameObject existing) => (Object)(object)existing == (Object)null || ((Object)existing).name != "FLG_FeedBarrel")) { zNetScene.m_prefabs.Add(prefab); } } else if (zNetScene.m_nonNetViewPrefabs.All((GameObject existing) => (Object)(object)existing == (Object)null || ((Object)existing).name != "FLG_FeedBarrel")) { zNetScene.m_nonNetViewPrefabs.Add(prefab); } if (!zNetScene.m_namedPrefabs.ContainsKey(stableHashCode)) { zNetScene.m_namedPrefabs.Add(stableHashCode, prefab); } else if ((Object)(object)zNetScene.m_namedPrefabs[stableHashCode] == (Object)null || ((Object)zNetScene.m_namedPrefabs[stableHashCode]).name == "FLG_FeedBarrel") { zNetScene.m_namedPrefabs[stableHashCode] = prefab; } } private static void RegisterWithHammer(ObjectDB? objectDb, GameObject prefab) { if (objectDb == null) { return; } GameObject itemPrefab = objectDb.GetItemPrefab("Hammer"); PieceTable val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>())?.m_itemData.m_shared.m_buildPieces; if (val != null) { val.m_pieces.RemoveAll((GameObject existing) => (Object)(object)existing == (Object)null || ((Object)existing).name == "FLG_FeedBarrel"); if (!IsEnabled) { RefreshLocalBuildTable(val); return; } val.m_pieces.Add(prefab); RefreshLocalBuildTable(val); } } private static void RefreshLocalBuildTable(PieceTable pieceTable) { if (Player.m_localPlayer != null && Player.m_localPlayer.m_buildPieces != null && (Object)(object)Player.m_localPlayer.m_buildPieces == (Object)(object)pieceTable) { ((Humanoid)Player.m_localPlayer).SetPlaceMode(pieceTable); } } private static IEnumerable<Requirement> BuildRequirements(ObjectDB objectDb, string recipeText) { if (string.IsNullOrWhiteSpace(recipeText)) { yield break; } string[] array = recipeText.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Trim().Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length != 0) { string text = array2[0].Trim(); int result = 1; if (array2.Length > 1) { int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out result); result = Mathf.Max(1, result); } GameObject val = FindItemPrefab(objectDb, text); ItemDrop val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<ItemDrop>()); if (val2 == null) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("FLG_FeedBarrel build ingredient '" + text + "' was not found.")); continue; } yield return new Requirement { m_resItem = val2, m_amount = result, m_amountPerLevel = 0, m_recover = true }; } } } private static GameObject? FindItemPrefab(ObjectDB objectDb, string itemName) { GameObject itemPrefab = objectDb.GetItemPrefab(itemName); if (itemPrefab != null) { return itemPrefab; } int stableHashCode = StringExtensionMethods.GetStableHashCode(itemName); if (!objectDb.m_itemByHash.TryGetValue(stableHashCode, out var value)) { return null; } return value; } private static GameObject? FindScenePrefab(string prefabName) { if (ZNetScene.instance == null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if (prefab != null) { return prefab; } int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName); if (!ZNetScene.instance.m_namedPrefabs.TryGetValue(stableHashCode, out var value)) { return null; } return value; } private static void StoreOriginalIcon(Piece piece) { if (_originalIcon == null) { _originalIcon = piece.m_icon; } } private static void StoreOriginalMaterials(GameObject prefab) { if (_originalMaterials == null) { _originalMaterials = (from renderer in GetVisualRenderers(prefab) select new RendererMaterialSnapshot(renderer, renderer.sharedMaterials.ToArray())).ToList(); } } private static void RestoreOriginalMaterials() { if (_originalMaterials == null) { return; } foreach (RendererMaterialSnapshot originalMaterial in _originalMaterials) { if (originalMaterial.Renderer != null) { originalMaterial.Renderer.sharedMaterials = originalMaterial.Materials.ToArray(); } } } private static void ApplyMaterialOverride(GameObject prefab, Material material) { foreach (Renderer visualRenderer in GetVisualRenderers(prefab)) { Material[] sharedMaterials = visualRenderer.sharedMaterials; if (sharedMaterials.Length != 0) { Material[] array = (Material[])(object)new Material[sharedMaterials.Length]; for (int i = 0; i < array.Length; i++) { array[i] = material; } visualRenderer.sharedMaterials = array; } } } private static Material? ResolveMaterial(string materialName) { string text = NormalizeMaterialName(materialName); if (text.Length == 0 || MaterialLookupMissCache.Contains(text)) { return null; } EnsureMaterialLookupCache(force: false); if (TryResolveCachedMaterial(materialName, text, out Material material)) { return material; } EnsureMaterialLookupCache(force: true); if (TryResolveCachedMaterial(materialName, text, out material)) { return material; } MaterialLookupMissCache.Add(text); return null; } private static bool TryResolveCachedMaterial(string materialName, string normalizedName, out Material? material) { if (MaterialLookupCache.TryGetValue(materialName, out Material value) && value != null) { material = value; return true; } if (MaterialLookupCache.TryGetValue(normalizedName, out Material value2) && value2 != null) { material = value2; return true; } material = null; return false; } private static void EnsureMaterialLookupCache(bool force) { if (_materialLookupCacheBuilt && !force) { return; } MaterialLookupCache.Clear(); MaterialLookupMissCache.Clear(); Material[] array = Resources.FindObjectsOfTypeAll<Material>(); foreach (Material val in array) { if (val != null && !string.IsNullOrWhiteSpace(((Object)val).name)) { if (!MaterialLookupCache.ContainsKey(((Object)val).name)) { MaterialLookupCache[((Object)val).name] = val; } string text = NormalizeMaterialName(((Object)val).name); if (text.Length > 0 && !MaterialLookupCache.ContainsKey(text)) { MaterialLookupCache[text] = val; } } } _materialLookupCacheBuilt = true; } private static string NormalizeMaterialName(string name) { return (name ?? string.Empty).Replace(" (Instance)", string.Empty).Trim(); } private static Sprite? RenderPieceIcon(GameObject sourcePrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Expected O, but got Unknown //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) if ((int)SystemInfo.graphicsDeviceType == 4) { return null; } GameObject val = null; Camera val2 = null; Light val3 = null; RenderTexture val4 = null; RenderTexture active = RenderTexture.active; try { val = SpawnIconRenderClone(sourcePrefab, out List<Renderer> renderers); if (val == null || renderers.Count == 0) { return null; } Bounds bounds = renderers[0].bounds; foreach (Renderer item in renderers.Skip(1)) { ((Bounds)(ref bounds)).Encapsulate(item.bounds); } Vector3 size = ((Bounds)(ref bounds)).size; Transform transform = val.transform; transform.position -= ((Bounds)(ref bounds)).center; val2 = new GameObject("FeedLikeGrandma Feed Barrel Icon Camera", new Type[1] { typeof(Camera) }).GetComponent<Camera>(); val2.backgroundColor = Color.clear; val2.clearFlags = (CameraClearFlags)2; val2.fieldOfView = 0.5f; val2.farClipPlane = 10000000f; val2.cullingMask = 1073741824; ((Component)val2).transform.rotation = Quaternion.Euler(0f, 180f, 0f); val3 = new GameObject("FeedLikeGrandma Feed Barrel Icon Light", new Type[1] { typeof(Light) }).GetComponent<Light>(); ((Component)val3).transform.position = Vector3.zero; ((Component)val3).transform.rotation = Quaternion.Euler(5f, 180f, 5f); val3.type = (LightType)1; val3.cullingMask = 1073741824; val3.intensity = 1.3f; float num = (Mathf.Max(size.x, size.y) + 0.1f) / Mathf.Tan(val2.fieldOfView * ((float)Math.PI / 180f)); if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f) { num = Mathf.Max(size.x, Mathf.Max(size.y, size.z)) + 1f; } ((Component)val2).transform.position = new Vector3(0f, 0f, num); val4 = (RenderTexture.active = (val2.targetTexture = RenderTexture.GetTemporary(128, 128, 24, (RenderTextureFormat)0))); GL.Clear(true, true, Color.clear); val2.Render(); Texture2D val6 = new Texture2D(128, 128, (TextureFormat)4, false) { name = "FLG_FeedBarrel_Icon" }; Rect val7 = default(Rect); ((Rect)(ref val7))..ctor(0f, 0f, 128f, 128f); val6.ReadPixels(val7, 0, 0); val6.Apply(); Sprite obj = Sprite.Create(val6, val7, new Vector2(0.5f, 0.5f), 100f); ((Object)obj).name = ((Object)val6).name; return obj; } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogWarning((object)("FLG_FeedBarrel icon render failed: " + ex.Message)); return null; } finally { RenderTexture.active = active; if (val2 != null) { val2.targetTexture = null; DestroyTemporaryObject(((Component)val2).gameObject); } if (val3 != null) { DestroyTemporaryObject(((Component)val3).gameObject); } if ((Object)(object)val4 != (Object)null) { RenderTexture.ReleaseTemporary(val4); } if (val != null) { DestroyTemporaryObject(val); } } } private static GameObject? SpawnIconRenderClone(GameObject sourcePrefab, out List<Renderer> renderers) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) renderers = new List<Renderer>(); GameObject val = null; try { val = new GameObject("FLG_FeedBarrel_IconRoot"); val.SetActive(false); GameObject val2 = Object.Instantiate<GameObject>(sourcePrefab, val.transform, false); ((Object)val2).name = "FLG_FeedBarrel_IconRender"; StripIconRenderClone(val2); val2.transform.SetParent((Transform)null, false); Object.DestroyImmediate((Object)(object)val); val = null; val2.transform.position = Vector3.zero; val2.transform.rotation = Quaternion.Euler(23f, 51f, 25.8f); SetLayerRecursive(val2, 30); val2.SetActive(true); renderers = (from renderer in GetVisualRenderers(val2) where ((Component)renderer).gameObject.activeInHierarchy select renderer).ToList(); if (renderers.Count == 0) { DestroyTemporaryObject(val2); return null; } HashSet<Renderer> hashSet = new HashSet<Renderer>(renderers); Renderer[] componentsInChildren = val2.GetComponentsInChildren<Renderer>(true); foreach (Renderer val3 in componentsInChildren) { if (val3 != null && !hashSet.Contains(val3)) { val3.enabled = false; } } return val2; } catch { if (val != null) { Object.DestroyImmediate((Object)(object)val); } throw; } } private static void StripIconRenderClone(GameObject renderObject) { Transform[] componentsInChildren = renderObject.GetComponentsInChildren<Transform>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Component[] components = ((Component)componentsInChildren[i]).GetComponents<Component>(); for (int num = components.Length - 1; num >= 0; num--) { Component val = components[num]; if (!((Object)(object)val == (Object)null) && !(val is Transform) && !(val is MeshFilter) && !(val is MeshRenderer) && !(val is SkinnedMeshRenderer)) { try { Object.DestroyImmediate((Object)(object)val); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not strip icon render component '" + ((object)val).GetType().Name + "' from '" + ((Object)renderObject).name + "': " + ex.Message)); } } } } } private static List<Renderer> GetVisualRenderers(GameObject prefab) { return (from renderer in prefab.GetComponentsInChildren<Renderer>(true) where (Object)(object)renderer != (Object)null && renderer.enabled && !((object)renderer).GetType().Name.Equals("ParticleSystemRenderer", StringComparison.Ordinal) select renderer).ToList(); } private static void SetLayerRecursive(GameObject gameObject, int layer) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) gameObject.layer = layer; foreach (Transform item in gameObject.transform) { SetLayerRecursive(((Component)item).gameObject, layer); } } private static void DestroyTemporaryObject(GameObject? gameObject) { if (gameObject == null) { return; } try { gameObject.SetActive(false); Object.DestroyImmediate((Object)(object)gameObject); } catch (Exception ex) { FeedLikeGrandmaPlugin.Log.LogDebug((object)("Could not immediately destroy icon render object '" + ((Object)gameObject).name + "': " + ex.Message)); Object.Destroy((Object)(object)gameObject); } } private static GameObject EnsurePrefabContainer() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if (_prefabContainer != null) { return _prefabContainer; } _prefabContainer = new GameObject("FeedLikeGrandma Feed Barrel Prefabs"); _prefabContainer.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_prefabContainer); return _prefabContainer; } } internal static class FeedBetterRidingCompatibility { private const string BetterRidingPluginTypeName = "BetterRiding.BetterRidingPlugin"; private const string MountStaminaSystemTypeName = "BetterRiding.Behaviour.MountStaminaSystem"; private const string MountAttackControllerTypeName = "BetterRiding.Behaviour.MountAttackController"; private const string BetterRidingBlockFlyingDismountPatchTypeName = "BetterRiding.Behaviour.BlockFlyingDismountPatch"; private const string DragonRidersBlockFlyingDismountPatchTypeName = "DragonRiders.Patches.MountController.BlockFlyingDismountPatch"; private const float AttackRecordCleanupInterval = 60f; private const float AttackRecordTtl = 1800f; private static readonly Dictionary<int, float> LastAttackTimes = new Dictionary<int, float>(); private static readonly Dictionary<string, ConfigEntry<bool>?> BetterRidingBoolConfigEntries = new Dictionary<string, ConfigEntry<bool>>(StringComparer.Ordinal); private static readonly List<int> ExpiredAttackKeys = new List<int>(); private static float _nextAttackRecordCleanupTime; private static bool _betterRidingPluginTypeSearched; private static Type? _betterRidingPluginType; private static bool _mountStaminaSystemTypeSearched; private static Type? _mountStaminaSystemType; private static bool _mountStaminaSaddleFieldSearched; private static FieldInfo? _mountStaminaSaddleField; private static int _mountAttackSafetySaddleId; private static int _mountAttackSafetyCharacterId; private static int _mountAttackSafetyFrame; private static bool _mountAttackSafetyAllowed = true; private static Type? BetterRidingPluginType { get { if (!_betterRidingPluginTypeSearched) { _betterRidingPluginType = FindType("BetterRiding.BetterRidingPlugin"); _betterRidingPluginTypeSearched = true; } return _betterRidingPluginType; } } private static Type? MountStaminaSystemType { get { if (!_mountStaminaSystemTypeSearched) { _mountStaminaSystemType = FindType("BetterRiding.Behaviour.MountStaminaSystem"); _mountStaminaSystemTypeSearched = true; } return _mountStaminaSystemType; } } internal static bool HasBetterRiding => BetterRidingPluginType != null; internal static bool HasBetterRidingMountStaminaSystem => MountStaminaSystemType != null; internal static void Shutdown() { LastAttackTimes.Clear(); BetterRidingBoolConfigEntries.Clear(); ExpiredAttackKeys.Clear(); _nextAttackRecordCleanupTime = 0f; _mountAttackSafetySaddleId = 0; _mountAttackSafetyCharacterId = 0; _mountAttackSafetyFrame = 0; _mountAttackSafetyAllowed = true; _betterRidingPluginTypeSearched = false; _betterRidingPluginType = null; _mountStaminaSystemTypeSearched = false; _mountStaminaSystemType = null; _mountStaminaSaddleFieldSearched = false; _mountStaminaSaddleField = null; } private static Type? FindType(string typeName) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType(typeName, throwOnError: false); if (type != null) { return type; } } return null; } internal static MethodBase? GetMountStaminaSystemMethod(string methodName) { Type mountStaminaSystemType = MountStaminaSystemType; if (!(mountStaminaSystemType == null)) { return AccessTools.Method(mountStaminaSystemType, methodName, (Type[])null, (Type[])null); } return null; } internal static MethodBase? GetMountAttackUpdate() { Type type = FindType("BetterRiding.Behaviour.MountAttackController"); if (!(type == null)) { return AccessTools.Method(type, "Update", (Type[])null, (Type[])null); } return null; } internal static MethodBase? GetBetterRidingBlockFlyingDismountPrefix() { Type type = FindType("BetterRiding.Behaviour.BlockFlyingDismountPatch"); if (!(type == null)) { return AccessTools.Method(type, "Prefix", (Type[])null, (Type[])null); } return null; } internal static MethodBase? GetDragonRidersBlockFlyingDismountPrefix() { Type type = FindType("DragonRiders.Patches.MountController.BlockFlyingDismountPatch"); if (!(type == null)) { return AccessTools.Method(type, "Prefix", (Type[])null, (Type[])null); } return null; } internal static bool HandleMountedAttack(Humanoid humanoid, ref bool result) { if (!TryFindAttackSaddle(humanoid, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } float time = Time.time; CleanupAttackRecords(time); int mountKey = GetMountKey(saddle); if (mountKey != 0 && LastAttackTimes.TryGetValue(mountKey, out var value) && time - value < mountStamina.AttackCooldown) { result = false; return false; } if (!CanUseAttackStamina(saddle, mountStamina)) { result = false; return false; } UseAttackStamina(saddle, mountStamina); if (mountKey != 0) { LastAttackTimes[mountKey] = time; } result = true; return false; } private static void CleanupAttackRecords(float now) { if (LastAttackTimes.Count == 0 || now < _nextAttackRecordCleanupTime) { return; } _nextAttackRecordCleanupTime = now + 60f; ExpiredAttackKeys.Clear(); foreach (KeyValuePair<int, float> lastAttackTime in LastAttackTimes) { if (now - lastAttackTime.Value > 1800f) { ExpiredAttackKeys.Add(lastAttackTime.Key); } } foreach (int expiredAttackKey in ExpiredAttackKeys) { LastAttackTimes.Remove(expiredAttackKey); } ExpiredAttackKeys.Clear(); } internal static void SuppressPlayerAttackControlsWhileRiding(Player player, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold) { if (player != null && ((Character)player).IsRiding()) { attack = false; attackHold = false; secondaryAttack = false; secondaryAttackHold = false; ResetPlayerDrawAttack(player); } } internal static bool AllowPlayerAttackInput(Player player) { if (player == null || !((Character)player).IsRiding()) { return true; } ResetPlayerDrawAttack(player); return false; } internal static bool CanAttack(object mountStaminaSystem, ref bool result) { if (!TryGetSystemSaddle(mountStaminaSystem, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } result = CanUseAttackStamina(saddle, mountStamina); return false; } internal static bool CanJump(object mountStaminaSystem, ref bool result) { if (!TryGetSystemSaddle(mountStaminaSystem, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } result = CanUseJumpStamina(saddle, mountStamina); return false; } internal static bool OnAttackStart(object mountStaminaSystem) { if (!TryGetSystemSaddle(mountStaminaSystem, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } UseAttackStamina(saddle, mountStamina); return false; } internal static bool OnJumpStart(object mountStaminaSystem) { if (!TryGetSystemSaddle(mountStaminaSystem, out Sadle saddle) || !TryGetMountStamina(saddle, out ResolvedMountStaminaValues mountStamina) || mountStamina == null) { return true; } UseJumpStamina(saddle, mountStamina); return false; } internal static bool AllowMountAttackUpdate() { Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { return true; } IDoodadController doodadController = localPlayer.GetDoodadController(); Sadle val = (Sadle)(object)((doodadController is Sadle) ? doodadController : null); if (val == null) { return true; } int instanceID = ((Object)val).GetInstanceID(); Character character = val.GetCharacter(); if (character == null) { return true; } int instanceID2 = ((Object)character).GetInstanceID(); if (instanceID == _mountAttackSafetySaddleId && instanceID2 == _mountAttackSafetyCharacterId && Time.frameCount - _mountAttackSafetyFrame <= (_mountAttackSafetyAllowed ? 30 : 5)) { return _mountAttackSafetyAllowed; } bool num = IsMountAttackStateSafe(character); _mountAttackSafetySaddleId = instanceID; _mountAttackSafetyCharacterId = instanceID2; _mountAttackSafetyFrame = Time.frameCount; _mountAttackSafetyAllowed = num; return num; } internal static bool AllowBlockFlyingDismount(Player player, ref bool result) { if (!ShouldAllowWaterDismount(player)) { return true; } result = true; return false; } private static bool IsMountAttackStateSafe(Character character) { Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null); if (val == null) { return true; } if (character.m_baseAI != null && val.m_defaultItems != null) { return val.m_defaultItems.Length >= 2; } return false; } private static bool ShouldAllowWaterDismount(Player player) { if (player == null || !((Character)player).IsRiding()) { return false; } IDoodadController doodadController = player.m_doodadController; IDoodadController obj = ((doodadController is Sadle) ? doodadController : null); Character character = ((obj != null) ? ((Sadle)obj).GetCharacter() : null); if (obj != null) { if (!IsInWater((Character?)(object)player)) { return IsInWater(character); } return true; } return false; } private static bool IsInWater(Character? character) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (character == null) { return false; } if (!character.InWater()) { if (character.m_waterLevel > character.m_tarLevel) { return character.m_waterLevel - ((Component)character).transform.position.y > 0.05f; } return false; } return true; } private static void ResetPlayerDrawAttack(Player player) { if (!(((Humanoid)player).m_attackDrawTime <= 0f)) { string text = ((Humanoid)player).GetCurrentWeapon()?.m_shared.m_attack.m_drawAnimationState; if (!string.IsNullOrEmpty(text)) { ((Character)player).m_zanim.SetBool(text, false); ((Character)player).m_zanim.SetFloat("drawpercent", 0f); } ((Humanoid)player).m_attackDrawTime = 0f; } } private static bool CanUseAttackStamina(Sadle? saddle, ResolvedMountStaminaValues mountStamina) { if (GetBetterRidingBoolConfig("EnableAttackStaminaDrain", fallback: true)) { return CanUseStamina(saddle, mountStamina.AttackStaminaCost); } return true; } private static bool CanUseJumpStamina(Sadle? saddle, ResolvedMountStaminaValues mountStamina) { if (GetBetterRidingBoolConfig("EnableJumpStaminaDrain", fallback: true)) { return CanUseStamina(saddle, mountStamina.JumpStaminaCost); } return true; } private static void UseAttackStamina(Sadle? saddle, ResolvedMountStaminaValues mountStamina) { if (GetBetterRidingBoolConfig("EnableAttackStaminaDrain", fallback: true)) { UseStaminaDirect(saddle, mountStamina.AttackStaminaCost); } } private static void UseJumpStamina(Sadle? saddle, ResolvedMountStaminaValues mountStamina) { if (GetBetterRidingBoolConfig("EnableJumpStaminaDrain", fallback: true)) { UseStaminaDirect(saddle, mountStamina.JumpStaminaCost); } } private static bool CanUseStamina(Sadle? saddle, float amount) { if (saddle != null && saddle.m_nview != null && saddle.m_nview.IsValid()) { return saddle.GetStamina() >= amount; } return false; } private static void UseStaminaDirect(Sadle? saddle, float amount) { if (!(amount <= 0f) && saddle != null && saddle.m_nview != null && saddle.m_nview.IsValid() && saddle.m_nview.IsOwner()) { float stamina = saddle.GetStamina(); if (!(stamina < amount)) { saddle.SetStamina(Mathf.Max(0f, stamina - amount)); } } } private static bool TryGetSystemSaddle(object mountStaminaSystem, out Sadle? saddle) { saddle = null; if (mountStaminaSystem == null) { return false; } FieldInfo mountStaminaSaddleField = GetMountStaminaSaddleField(); if (mountStaminaSaddleField == null) { return false; } object? value = mountStaminaSaddleField.GetValue(mountStaminaSystem); saddle = (Sadle?)((value is Sadle) ? value : null); return saddle != null; } private static bool TryFindAttackSaddle(Humanoid humanoid, out Sadle? saddle) { saddle = null; if (humanoid == null) { return false; } Player val = (Player)(object)((humanoid is Player) ? humanoid : null); if (val != null && ((Character)val).IsRiding()) { IDoodadController doodadController = val.m_doodadController; saddle = (Sadle?)(object)((doodadController is Sadle) ? doodadController : null); return saddle != null; } Player localPlayer = Player.m_localPlayer; if (localPlayer == null || !((Character)localPlayer).IsRiding()) { return false; } IDoodadController doodadController2 = localPlayer.m_doodadController; saddle = (Sadle?)(object)((doodadController2 is Sadle) ? doodadController2 : null); Sadle? obj = saddle; Character val2 = ((obj != null) ? obj.GetCharacter() : null); if (val2 == null || (object)val2 != humanoid) { saddle = null; return false; } return true; } private static bool TryGetMountStamina(Sadle? saddle, out ResolvedMountStaminaValues? mountStamina) { mountStamina = null; if (saddle == null) { return false; } Character character = saddle.GetCharacter(); if (character != null) { return FeedSystem.TryGetMountStamina(character, out mountStamina); } return false; } private static int GetMountKey(Sadle? saddle) { if (saddle == null) { return 0; } Character character = saddle.GetCharacter(); if (character != null) { return ((Object)((Component)character).gameObject).GetInstanceID(); } return 0; } private static bool GetBetterRidingBoolConfig(string fieldName, bool fallback) { if (!BetterRidingBoolConfigEntries.TryGetValue(fieldName, out ConfigEntry<bool> value)) { Type betterRidingPluginType = BetterRidingPluginType; value = ((betterRidingPluginType == null) ? null : AccessTools.Field(betterRidingPluginType, fieldName)?.GetValue(null)) as ConfigEntry<bool>; BetterRidingBoolConfigEntries[fieldName] = value; } return value?.Value ?? fallback; } private static FieldInfo? GetMountStaminaSaddleField() { if (!_mountStaminaSaddleFieldSearched) { Type mountStaminaSystemType = MountStaminaSystemType; _mountStaminaSaddleField = ((mountStaminaSystemType == null) ? null : AccessTools.Field(mountStaminaSystemType, "m_saddle")); _mountStaminaSaddleFieldSearched = true; } return _mountStaminaSaddleField; } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class BetterRidingSuppressPlayerAttackHoldPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } [HarmonyPriority(0)] [HarmonyAfter(new string[] { "Yggdrah.BetterRiding" })] private static void Prefix(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold) { FeedBetterRidingCompatibility.SuppressPlayerAttackControlsWhileRiding(__instance, ref attack, ref attackHold, ref secondaryAttack, ref secondaryAttackHold); } } [HarmonyPatch(typeof(Player), "PlayerAttackInput")] internal static class BetterRidingSuppressPlayerAttackInputPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.HasBetterRiding; } private static bool Prefix(Player __instance) { return FeedBetterRidingCompatibility.AllowPlayerAttackInput(__instance); } } [HarmonyPatch] internal static class BetterRidingHandleMountedAttackPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("HandleMountedAttack") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("HandleMountedAttack"); } private static bool Prefix(Humanoid instance, ref bool __result) { return FeedBetterRidingCompatibility.HandleMountedAttack(instance, ref __result); } } [HarmonyPatch] internal static class BetterRidingCanAttackPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("CanAttack") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("CanAttack"); } private static bool Prefix(object __instance, ref bool __result) { return FeedBetterRidingCompatibility.CanAttack(__instance, ref __result); } } [HarmonyPatch] internal static class BetterRidingCanJumpPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("CanJump") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("CanJump"); } private static bool Prefix(object __instance, ref bool __result) { return FeedBetterRidingCompatibility.CanJump(__instance, ref __result); } } [HarmonyPatch] internal static class BetterRidingOnAttackStartPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("OnAttackStart") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("OnAttackStart"); } private static bool Prefix(object __instance) { return FeedBetterRidingCompatibility.OnAttackStart(__instance); } } [HarmonyPatch] internal static class BetterRidingOnJumpStartPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("OnJumpStart") != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountStaminaSystemMethod("OnJumpStart"); } private static bool Prefix(object __instance) { return FeedBetterRidingCompatibility.OnJumpStart(__instance); } } [HarmonyPatch] internal static class BetterRidingMountAttackSafetyPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetMountAttackUpdate() != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetMountAttackUpdate(); } private static bool Prefix() { return FeedBetterRidingCompatibility.AllowMountAttackUpdate(); } } [HarmonyPatch] internal static class BetterRidingBlockFlyingDismountWaterPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetBetterRidingBlockFlyingDismountPrefix() != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetBetterRidingBlockFlyingDismountPrefix(); } [HarmonyPriority(800)] private static bool Prefix(Player __0, ref bool __result) { return FeedBetterRidingCompatibility.AllowBlockFlyingDismount(__0, ref __result); } } [HarmonyPatch] internal static class DragonRidersBlockFlyingDismountWaterPatch { private static bool Prepare() { return FeedBetterRidingCompatibility.GetDragonRidersBlockFlyingDismountPrefix() != null; } private static MethodBase? TargetMethod() { return FeedBetterRidingCompatibility.GetDragonRidersBlockFlyingDismountPrefix(); } [HarmonyPriority(800)] private static bool Prefix(Player __0, ref bool __result) { return FeedBetterRidingCompatibility.AllowBlockFlyingDismount(__0, ref __result); } } internal sealed class RawFeedConfig { public Dictionary<string, RawFeedingRule> Rules { get; } = new Dictionary<string, RawFeedingRule>(StringComparer.OrdinalIgnoreCase); public Dictionary<string, float> FoodNumbers { get; } = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); public RawFeedValues Defaults { get; set; } = new RawFeedValues(); } internal sealed class RawFoodEntry { public string PrefabName { get; init; } = string.Empty; public int? RequiredQuality { get; init; } public float? Value { get; init; } public bool DefaultToOne { get; init; } } internal sealed class RawFeedValues { public float LevelUpConstant { get; init; } = 1f; public float TamingConstant { get; init; } = 1f; public float GrowthConstant { get; init; } = 1f; public float HealConstant { get; init; } = 1f; public float FedConstant { get; init; } = 60f; } internal sealed class RawTameValues { public float FedDuration { get; init; } public float TamingTime { get; init; } public bool StartsTamed { get; init; } public bool Commandable { get; init; } } internal sealed class RawProcreationValues { public int MaxCreatures { get; init; } public float TotalCheckRange { get; init; } } internal sealed class RawGrowValues { public float GrowTime { get; init; } } internal sealed class RawEggGrowValues { public float GrowTime { get; init; } } internal sealed class RawMountStaminaValues { public float AttackStaminaCost { get; init; } public float JumpStaminaCost { get; init; } public float AttackCooldown { get; init; } } internal sealed class RawFeedingRule { public string PrefabName { get; init; } = string.Empty; public RawFeedValues? Feed { get; set; } public RawTameValues? Tame { get; set; } public RawProcreationValues? Procreation { get; set; } public RawGrowValues? Grow { get; set; } public RawEggGrowValues? EggGrow { get; set; } public RawMountStaminaValues? MountStamina { get; set; } public bool HasRestrictedBiomeOverride { get; set; } public List<string> RestrictedBiomes { get; } = new List<string>(); public List<RawFoodEntry> Foods { get; } = new List<RawFoodEntry>(); public List<string> ConsumeItems { get; } = new List<string>(); public Dictionary<int, List<RawFoodEntry>> LevelUpItems { get; } = new Dictionary<int, List<RawFoodEntry>>(); } internal sealed class ResolvedFoodEntry { public string PrefabName { get; init; } = string.Empty; public int? RequiredQuality { get; init; } public float Value { get; init; } public Sprite Icon { get; init; } } internal sealed class ResolvedFeedingRule { public GameObject Prefab { get; init; } public float LevelUpConstant { get; init; } public float TamingConstant { get; init; } public float GrowthConstant { get; init; } public float HealConstant { get; init; } public float FedConstant { get; init; } public List<ResolvedFoodEntry> Foods { get; init; } = new List<ResolvedFoodEntry>(); public Dictionary<int, List<ResolvedFoodEntry>> LevelUpItems { get; init; } = new Dictionary<int, List<ResolvedFoodEntry>>(); public ResolvedMountStaminaValues? MountStamina { get; init; } public bool HasRestrictedBiomeOverride { get; init; } public IReadOnlyList<string> RestrictedBiomes { get; init; } = Array.Empty<string>(); } internal sealed class ResolvedMountStaminaValues { public float AttackStaminaCost { get; init; } public float JumpStaminaCost { get; init; } public float AttackCooldown { get; init; } } internal static class FeedingYamlParser { public static RawFeedConfig Parse(string yamlText, List<string> warnings) { RawFeedConfig rawFeedConfig = new RawFeedConfig(); if (string.IsNullOrWhiteSpace(yamlText)) { return rawFeedConfig; } YamlStream yamlStream = new YamlStream(); yamlStream.Load(new StringReader(yamlText)); if (yamlStream.Documents.Count == 0 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { warnings.Add("YAML root must be a mapping of defaults, foodNumbers, and prefab names."); return rawFeedConfig; } foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children) { string text = ReadScalar(child.Key).Trim(); if (text.Length == 0) { warnings.Add(Location(child.Key) + ": top-level key cannot be empty."); continue; } if (text.Equals("defaults", StringComparison.OrdinalIgnoreCase)) { ParseDefaults(rawFeedConfig, child.Value, warnings); continue; } if (text.Equals("foodNumbers", StringComparison.OrdinalIgnoreCase)) { ParseFoodNumbers(rawFeedConfig.FoodNumbers, child.Value, "foodNumbers", warnings); continue; } RawFeedingRule value = ParseRule(text, child.Value, warnings); if (rawFeedConfig.Rules.ContainsKey(text)) { warnings.Add(Location(child.Key) + ": prefab '" + text + "' was defined more than once. The last definition won."); } rawFeedConfig.Rules[text] = value; } return rawFeedConfig; } private static void ParseDefaults(RawFeedConfig config, YamlNode node, List<string> warnings) { if (!(node is YamlMappingNode yamlMappingNode)) { warnings.Add(Location(node) + ": defaults must be a mapping."); return; } foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children) { string text = ReadScalar(child.Key).Trim(); if (text.Equals("feed", StringComparison.OrdinalIgnoreCase)) { config.Defaults = ParseFeedValues(child.Value, "defaults.feed", warnings); } else { warnings.Add(Location(child.Key) + ": unknown defaults field '" + text + "'."); } } } private static RawFeedingRule ParseRule(string prefabName, YamlNode node, List<string> warnings) { RawFeedingRule rawFeedingRule = new RawFeedingRule { PrefabName = prefabName }; if (!(node is YamlMappingNode yamlMappingNode)) { warnings.Add(Location(node) + ": prefab '" + prefabName + "' must contain a mapping."); return rawFeedingRule; } foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children) { string text = ReadScalar(child.Key).Trim(); switch (NormalizeField(text)) { case "feed": rawFeedingRule.Feed = ParseFeedValues(child.Value, prefabName + ".feed", warnings); break; case "consumeitems": ParseConsumeItems(rawFeedingRule, child.Value, warnings); break; case "levelupitems": ParseLevelUpItems(rawFeedingRule, child.Value, prefabName + ".levelUpItems", warnings); break; case "tame": rawFeedingRule.Tame = ParseTameValues(child.Value, prefabName + ".tame", warnings); break; case "procreation": rawFeedingRule.Procreation = ParseProcreationValues(child.Value, prefabName + ".procreation", warnings); break; case "grow": rawFeedingRule.Grow = ParseGrowValues(child.Value, prefabName + ".grow", warnings); break; case "egggrow": rawFeedingRule.EggGrow = ParseEggGrowValues(child.Value, prefabName + ".eggGrow", warnings); break; case "mountstamina": rawFeedingRule.MountStamina = ParseMountStaminaValues(child.Value, prefabName + ".mountStamina", warnings); break; case "restrictedbiome": case "restrictedbiomes": rawFeedingRule.HasRestrictedBiomeOverride = true; rawFeedingRule.RestrictedBiomes.Clear(); rawFeedingRule.RestrictedBiomes.AddRange((from value in ParseStringList(child.Value) select Unquote(value).Trim() into value where value.Length > 0 select value).Distinct<string>(StringComparer.OrdinalIgnoreCase)); break; default: warnings.Add(Location(child.Key) + ": unknown field '" + text + "' for prefab '" + prefabName + "'."); break; } } return rawFeedingRule; } private static void ParseConsumeItems(RawFeedingRule rule, YamlNode node, List<string> warnings) { if (node is YamlSequenceNode yamlSequenceNode) { { foreach (YamlNode child in yamlSequenceNode.Children) { AddConsumeItemEntry(rule, child, warnings); } return; } } foreach (string item in ParseStringList(node)) { AddConsumeItemEntry(rule, item, node, warnings); } } private static void AddConsumeItemEntry(RawFeedingRule rule, YamlNode node, List<string> warnings) { if (node is YamlMappingNode yamlMappingNode && yamlMappingNode.Children.Count == 1) { KeyValuePair<YamlNode, YamlNode> keyValuePair = yamlMappingNode.Children.First(); AddFoodEntry(rule, ReadScalar(keyValuePair.Key).Trim(), ReadScalar(keyValuePair.Value).Trim(), defaultToOne: true, node, warnings); AddConsumeItem(rule, ReadScalar(keyValuePair.Key).Trim(), node, warnings); return; } if (node is YamlSequenceNode yamlSequenceNode) { List<string> list = yamlSequenceNode.Children.Select((YamlNode child) => ReadScalar(child).Trim()).ToList(); if (list.Count >= 2) { AddFoodEntry(rule, list[0], list[1], defaultToOne: true, node, warnings); AddConsumeItem(rule, list[0], node, warnings); return; } } AddConsumeItemEntry(rule, ReadScalar(node).Trim(), node, warnings); } private static void AddConsumeItemEntry(RawFeedingRule rule, string entryText, YamlNode node, List<string> warnings) { string valueText = null; string foodName; if (TrySplitCommaValue(entryText, out string key, out string value)) { foodName = Unquote(key).Trim(); valueText = value; } else { foodName = Unquote(entryText).Trim(); } AddFoodEntry(rule, foodName, valueText, defaultToOne: true, node, warnings); AddConsumeItem(rule, foodName, node, warnings); } private static void AddFoodEntry(RawFeedingRule rule, string foodName, string? valueText, bool defaultToOne, YamlNode node, List<string> warnings) { AddFoodEntry(rule.Foods, rule.PrefabName, foodName, valueText, defaultToOne, node, warnings); } private static void AddFoodEntry(List<RawFoodEntry> foods, string ruleName, string foodName, string? valueText, bool defaultToOne, YamlNode node, List<string> warnings) { string prefabName; int? requiredQuality; if (string.IsNullOrWhiteSpace(foodName)) { warnings.Add(Location(node) + ": food item name cannot be empty."); } else if (TryParseFoodToken(foodName, node, warnings, out prefabName, out requiredQuality)) { float? value = null; if (!string.IsNullOrWhiteSpace(valueText)) { value = Math.Max(0f, ParseFloat(valueText, ruleName + "." + FormatFoodKey(prefabName, requiredQuality), node, warnings)); } int num = foods.FindIndex((RawFoodEntry entry) => entry.PrefabName.Equals(prefabName, StringComparison.OrdinalIgnoreCase) && entry.RequiredQuality == requiredQuality); if (num >= 0) { warnings.Add(Location(node) + ": food '" + FormatFoodKey(prefabName, requiredQuality) + "' was defined more than once for '" + ruleName + "'. The last value won."); foods.RemoveAt(num); } foods.Add(new RawFoodEntry { PrefabName = prefabName, RequiredQuality = requiredQuality, Value = value, DefaultToOne = defaultToOne }); } } private static void AddConsumeItem(RawFeedingRule rule, string foodName, YamlNode node, List<string> warnings) { if (!string.IsNullOrWhiteSpace(foodName) && TryParseFoodToken(foodName, node, warnings, out string prefabName, out int? _) && rule.ConsumeItems.FindIndex((string entry) => entry.Equals(prefabName, StringComparison.OrdinalIgnoreCase)) < 0) { rule.ConsumeItems.Add(prefabName); } } private static void ParseLevelUpItems(RawFeedingRule rule, YamlNode node, string context, List<string> warnings) { if (!(node is YamlMappingNode yamlMappingNode)) { warnings.Add(Location(node) + ": " + context + " must be a mapping of current Character level to consumeItems."); return; } foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children) { int num = Math.Max(1, ParseInt(child.Key, context + ".level", warnings)); List<RawFoodEntry> list = new List<RawFoodEntry>(); if (child.Value is YamlSequenceNode yamlSequenceNode) { foreach (YamlNode child2 in yamlSequenceNode.Children) { AddLevelUpConsumeItemEntry(list, $"{rule.PrefabName}.levelUpItems.{num}", child2, warnings); } } else { foreach (string item in ParseStringList(child.Value)) { AddLevelUpConsumeItemEntry(list, $"{rule.PrefabName}.levelUpItems.{num}", item, child.Value, warnings); } } if (rule.LevelUpItems.ContainsKey(num)) { warnings.Add($"{Location(child.Key)}: levelUpItems for Character level {num} was defined more than once for prefab '{rule.PrefabName}'. The last definition won."); } rule.LevelUpItems[num] = list; } } private static void AddLevelUpConsumeItemEntry(List<RawFoodEntry> foods, string context, YamlNode node, List<string> warnings) { if (node is YamlMappingNode yamlMappingNode && yamlMappingNode.Children.Count == 1) { KeyValuePair<YamlNode, YamlNode> keyValuePair = yamlMappingNode.Children.First(); AddFoodEntry(foods, context, ReadScalar(keyValuePair.Key).Trim(), ReadScalar(keyValuePair.Value).Trim(), defaultToOne: true, node, warnings); return; } if (node is YamlSequenceNode yamlSequenceNode) { List<string> list = yamlSequenceNode.Children.Select((YamlNode child) => ReadScalar(child).Trim()).ToList(); if (list.Count >= 2) { AddFoodEntry(foods, context, list[0], list[1], defaultToOne: true, node, warnings); return; } } AddLevelUpConsumeItemEntry(foods, context, ReadScalar(node).Trim(), node, warnings); } private static void AddLevelUpConsumeItemEntry(List<RawFoodEntry> foods, string context, string entryText, YamlNode node, List<string> warnings) { string valueText = null; string foodName; if (TrySplitCommaValue(entryText, out string key, out string value)) { foodName = Unquote(key).Trim(); valueText = value; } else { foodName = Unquote(entryText).Trim(); } AddFoodEntry(foods, context, foodName, valueText, defaultToOne: true, node, warnings); } private static void ParseFoodNumbers(Dictionary<string, float> foodNumbers, YamlNode node, string context, List<string> warnings) { if (node is YamlSequenceNode yamlSequenceNode) { { foreach (YamlNode child in yamlSequenceNode.Children) { AddFoodNumber(foodNumbers, child, context, warnings); } return; } } warnings.Add(Location(node) + ": " + context + " must be a sequence. Use entries like '- Raspberry' or '- Raspberry, 3'."); } private static void AddFoodNumber(Dictionary<string, float> foodNumbers, YamlNode node, string context, List<string> warnings) { string text = ReadScalar(node).Trim(); string key; string value; bool flag = TrySplitCommaValue(text, out key, out value); if (TryParseFoodToken(Unquote(flag ? key : text).Trim(), node, warnings, out string prefabName, out int? requiredQuality)) { string text2 = FormatFoodKey(prefabName, requiredQuality); foodNumbers[text2] = (flag ? Math.Max(0f, ParseFloat(value, context + "." + text2, node, warnings)) : 1f); } } internal static string FormatFoodKey(string prefabName, int? requiredQuality) { if (!requiredQuality.HasValue) { return prefabName; } return prefabName + "@" + requiredQuality.Value.ToString(CultureInfo.InvariantCulture); } private static bool TryParseFoodToken(string foodName, YamlNode node, List<string> warnings, out string prefabName, out int? requiredQuality) { prefabName = Unquote(foodName).Trim(); requiredQuality = null; if (prefabName.Length == 0) { warnings.Add(Location(node) + ": food item name cannot be empty."); return false; } int num = prefabName.LastIndexOf('@'); if (num < 0) { return true; } string text = prefabName.Substring(0, num).Trim(); string s = prefabName.Substring(num + 1).Trim(); if (text.Length == 0 || !int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0) { warnings.Add(Location(node) + ": food item '" + foodName + "' must use ItemName@quality with a positive integer quality."); prefabName = string.Empty; return false; } prefabName = text; requiredQuality = result; return true; } private static RawFeedValues ParseFeedValues(YamlNode node, string field, List<string> warnings) { List<string> list = ReadTupleParts(node); if (list.Count != 5) { warnings.Add(Location(node) + ": field '" + field + "' must contain 5 values: levelupconstant, tamingconstant, growthconstant, healconstant, fedconstant."); return new RawFeedValues(); } return new RawFeedValues { LevelUpConstant = Math.Max(0f, ParseFloat(list[0], field + ".levelupconstant", node, warnings)), TamingConstant = Math.Max(0f, ParseFloat(list[1], field + ".tamingconstant", node, warnings)), GrowthConstant = Math.Max(0f, ParseFloat(list[2], field + ".growthconstant", node, warnings)), HealConstant = Math.Max(0f, ParseFloat(list[3], field + ".healconstant", node, warnings)), FedConstant = Math.Max(0f, ParseFloat(list[4], field + ".fedconstant", node, warnings)) }; } private static RawTameValues? ParseTameValues(YamlNode node, string field, List<string> warnings) { List<string> list = ReadTupleParts(node); if (list.Count != 4) { warnings.Add(Location(node) + ": field '" + field + "' must contain 4 values: fedDuration, tamingTime, startsTamed, commandable."); return null; } return new RawTameValues { FedDuration = Math.Max(0f, ParseFloat(list[0], field + ".fedDuration", node, warnings)), TamingTime = Math.Max(0f, ParseFloat(list[1], field + ".tamingTime", node, warnings)), StartsTamed = ParseBool(list[2], field + ".startsTamed", node, warnings), Commandable = ParseBool(list[3], field + ".commandable", node, warnings) }; } private static RawProcreationValues? ParseProcreationValues(YamlNode node, string field, List<string> warnings) { List<string> list = ReadTupleParts(node); if (list.Count != 2) { warnings.Add(Location(node) + ": field '" + field + "' must contain 2 values: maxCreatures, totalCheckRange."); return null; } return new RawProcreationValues { MaxCreatures = Math.Max(0, ParseInt(list[0], field + ".maxCreatures", node, warnings)), TotalCheckRange = Math.Max(0f, ParseFloat(list[1], field + ".totalCheckRange", node, warnings)) }; } private static RawGrowValues? ParseGrowValues(YamlNode node, string field, List<string> warnings) { List<string> list = ReadTupleParts(node); if (list.Count != 1) { warnings.Add(Location(node) + ": field '" + field + "' must contain 1 value: growTime."); return null; } return new RawGrowValues { GrowTime = Math.Max(0f, ParseFloat(list[0], field + ".growTime", node, warnings)) }; } private static RawEggGrowValues? ParseEggGrowValues(YamlNode node, string field, List<string> warnings) { List<string> list = ReadTupleParts(node); if (list.Count != 1) { warnings.Add(Location(node) + ": field '" + field + "' must contain 1 value: growTime."); return null; } return new RawEggGrowValues { GrowTime = Math.Max(0f, ParseFloat(list[0], field + ".growTime", node, warnings)) }; } private static RawMountStaminaValues? ParseMountStaminaValues(YamlNode node, string field, List<string> warnings) { List<string> list = ReadTupleParts(node); if (list.Count != 3) { warnings.Add(Location(node) + ": field '" + field + "' must contain 3 values: attackStaminaCost, jumpStaminaCost, attackCooldown."); return null; } return new RawMountStaminaValues { AttackStaminaCost = Math.Max(0f, ParseFloat(list[0], field + ".attackStaminaCost", node, warnings)), JumpStaminaCost = Math.Max(0f, ParseFloat(list[1], field + ".jumpStaminaCost", node, warnings)), AttackCooldown = Math.Max(0f, ParseFloat(list[2], field + ".attackCooldown", node, warnings)) }; } private static List<string> ParseStringList(YamlNode node) { if (node is YamlSequenceNode yamlSequenceNode) { return (from child in yamlSequenceNode.Children select ReadScalar(child).Trim() into value where value.Length > 0 select value).ToList(); } string text = ReadScalar(node).Trim(); if (text.Length == 0) { return new List<string>(); } if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal)) { return (from part in SplitCommaText(text.Substring(1, text.Length - 2)) select Unquote(part).Trim() into value where value.Length > 0 select value).ToList(); } return new List<string> { Unquote(text) }; } private static List<string> ReadTupleParts(YamlNode node) { if (node is YamlSequenceNode yamlSequenceNode) { return yamlSequenceNode.Children.Select((YamlNode child) => ReadScalar(child).Trim()).ToList(); } string text = ReadScalar(node).Trim(); if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal)) { text = text.Substring(1, text.Length - 2); } return (from part in SplitCommaText(text) select Unquote(part).Trim()).ToList(); } private static int ParseInt(YamlNode node, string field, List<string> warnings) { return ParseInt(ReadScalar(node), field, node, warnings); } private static int ParseInt(string valueText, string field, YamlNode node, List<string> warnings) { if (int.TryParse(Unquote(valueText).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } warnings.Add(Location(node) + ": field '" + field + "' must be an integer."); return 0; } private static float ParseFloat(YamlNode node, string field, List<string> warnings) { return ParseFloat(ReadScalar(node), field, node, warnings); } private static float ParseFloat(string valueText, string field, YamlNode node, List<string> warnings) { if (float.TryParse(Unquote(valueText).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } warnings.Add(Location(node) + ": field '" + field + "' must be a number."); return 0f; } private static bool ParseBool(string valueText, string field, YamlNode node, List<string> warnings) { string text = Unquote(valueText).Trim(); if (bool.TryParse(text, out var result)) { return result; } string text2 = text.ToLowerInvariant(); if (text2 != null) { switch (text2.Length) { case 1: { char c = text2[0]; if ((uint)c <= 49u) { if (c != '0') { if (c != '1') { break; } goto IL_00dd; } } else if (c != 'n') { if (c != 'y') { break; } goto IL_00dd; } goto IL_00df; } case 3: { char c = text2[0]; if (c != 'o') { if (c != 'y' || !(text2 == "yes")) { break; } goto IL_00dd; } if (!(text2 == "off")) { break; } goto IL_00df; } case 2: { char c = text2[0]; if (c != 'n') { if (c != 'o' || !(text2 == "on")) { break; } goto IL_00dd; } if (!(text2 == "no")) { break; } goto IL_00df; } IL_00df: return false; IL_00dd: return true; } } warnings.Add(Location(node) + ": field '" + field + "' must be true or false."); return false; } private static string ReadScalar(YamlNode node) { object obj; if (!(node is YamlScalarNode yamlScalarNode)) { obj = node.ToString(); if (obj == null) { return string.Empty; } } else { obj = yamlScalarNode.Value ?? string.Empty; } return (string)obj; } private static string NormalizeField(string field) { return field.Replace("_", string.Empty).Replace("-", string.Empty).Trim() .ToLowerInvariant(); } private static bool TrySplitCommaValue(string text, out string key, out string value) { List<string> list = SplitCommaText(text); if (list.Count < 2) { key = string.Empty; value = string.Empty; return false; } key = list[0].Trim(); value = list[1].Trim(); return key.Length > 0; } private static List<string> SplitCommaText(string text) { List<string> list = new List<string>(); bool flag = false; bool flag2 = false; int num = 0; for (int i = 0; i < text.Length; i++) { char c = text[i]; if (c == '\'' && !flag2) { flag = !flag; } else if (c == '"' && !flag) { flag2 = !flag2; } else if (!(c != ',' || flag || flag2)) { list.Add(text.Substring(num, i - num).Trim()); num = i + 1; } } list.Add(text.Substring(num).Trim()); return list; } private static string Unquote(string text) { text = text.Trim(); if (text.Length >= 2 && ((text[0] == '"' && text[text.Length - 1] == '"') || (text[0] == '\'' && text[text.Length - 1] == '\''))) { return text.Substring(1, text.Length - 2).Replace("''", "'"); } return text; } private static string Location(YamlNode node) { if (node.Start.Line <= 0) { return "YAML"; } return $"Line {node.Start.Line}"; } } internal static class FeedDragonFlightRestrictionSystem { private sealed class RestrictionState { public int MountId { get; init; } public Biome Biome { get; init; } public bool Restricted { get; init; } } private const string FlightControlsTypeName = "BetterRiding.Behaviour.FlightControls"; private const string FlightControlsPlayerSetControlsPatchTypeName = "BetterRiding.Behaviour.FlightControls+Ygg_PlayerSetControls"; private const string MountControllerTypeName = "BetterRiding.Behaviour.MountController"; private const string MountControllerPlayerUpdatePatchTypeName = "BetterRiding.Behaviour.MountController+Player_Update_Patch"; private const string ExpandWorldBiomeManagerTypeName = "ExpandWorldData.BiomeManager"; private const string BiomeRestrictedMessage = "$flg_dragon_flight_restricted"; private const string EncumberedRestrictedMessage = "$flg_dragon_flight_encumbered"; private static readonly Dictionary<int, RestrictionState> PlayerStates = new Dictionary<int, RestrictionState>(); private static readonly Dictionary<string, Biome> ResolvedBiomeNames = new Dictionary<string, Biome>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Type?> TypeCache = new Dictionary<string, Type>(StringComparer.Ordinal); private static readonly Dictionary<string, MethodInfo?> MethodCache = new Dictionary<string, MethodInfo>(StringComparer.Ordinal); private static readonly Dictionary<string, FieldInfo?> FieldCache = new Dictionary<string, FieldInfo>(StringComparer.Ordinal); private static readonly Dictionary<string, ConfigEntry<KeyCode>?> FlightKeyConfigCache = new Dictionary<string, ConfigEntry<KeyCode>>(StringComparer.Ordinal); private static readonly Dictionary<KeyCode, string> JoystickButtonNameCache = new Dictionary<KeyCode, string>(); private static float _lastMessageTime = -100f; private static bool _isDragonDelegateSearched; private static Func<Sadle, bool>? _isDragonDelegate; private static MethodInfo? _isDragonMethod; private static readonly object[] IsDragonArgs = new object[1]; private static string _cachedDefaultRestrictedBiomeText = string.Empty; private static IReadOnlyList<string> _cachedDefaultRestrictedBiomes = Array.Empty<string>(); internal static MethodBase? GetBetterRidingSetControlsPrefix() { return GetMethod("BetterRiding.Behaviour.FlightControls+Ygg_PlayerSetControls", "Prefix"); } internal static MethodBase? GetBetterRidingPlayerUpdatePostfix() { return GetMethod("BetterRiding.Behaviour.MountController+Player_Update_Patch", "Postfix"); } internal static void Shutdown() { PlayerStates.Clear(); ResolvedBiomeNames.Clear(); TypeCache.Clear(); MethodCache.Clear(); FieldCache.Clear(); FlightKeyConfigCache.Clear(); JoystickButtonNameCache.Clear(); _lastMessageTime = -100f; _isDragonDelegateSearched = false; _isDragonDelegate = null; _isDragonMethod = null; IsDragonArgs[0] = null; _cachedDefaultRestrictedBiomeText = string.Empty; _cachedDefaultRestrictedBiomes = Array.Empty<string>(); } internal static bool BlockFlightToggleIfRestricted(Player player, ref bool jump) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (jump || !IsFlightToggleKeyDown()) { retu