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 QuickBreeding v1.5.1
QuickBreeding.dll
Decompiled 2 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("QuickBreeding")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.5.1.0")] [assembly: AssemblyInformationalVersion("1.5.1+56a986fff1755d858bb42055beff427198966309")] [assembly: AssemblyProduct("QuickBreeding")] [assembly: AssemblyTitle("QuickBreeding")] [assembly: AssemblyVersion("1.5.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace QuickBreeding { internal class AnimalOverrides { private readonly ConfigFile _file; private readonly Dictionary<string, ConfigEntry<int>> _maxCreatures = new Dictionary<string, ConfigEntry<int>>(); private readonly Dictionary<string, ConfigEntry<string>> _breedingFoods = new Dictionary<string, ConfigEntry<string>>(); private readonly Dictionary<string, ConfigEntry<bool>> _generateFeed = new Dictionary<string, ConfigEntry<bool>>(); public AnimalOverrides(ConfigFile file) { _file = file; } public bool IsBound(string prefabName) { if (prefabName != null) { return _maxCreatures.ContainsKey(prefabName); } return false; } public bool IsExcluded(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return true; } string text = QuickBreedingPlugin.Settings?.ExcludedAnimals?.Value; if (string.IsNullOrWhiteSpace(text)) { return false; } string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { if (string.Equals(array[i].Trim(), prefabName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public void Bind(string prefabName, string displayName, string defaultFood, bool offerGeneratedItem) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown if (!string.IsNullOrEmpty(prefabName) && !_maxCreatures.ContainsKey(prefabName)) { string text = (string.IsNullOrWhiteSpace(displayName) ? prefabName : displayName); string text2 = "5 - Animal: " + text; _maxCreatures[prefabName] = _file.Bind<int>(text2, "MaxCreatures", 0, new ConfigDescription("Population cap for this animal. 0 = use the global setting.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); _breedingFoods[prefabName] = _file.Bind<string>(text2, "BreedingFoods", defaultFood ?? string.Empty, new ConfigDescription("Foods that trigger breeding for this animal when hand-fed. Blank = this animal cannot be hand-fed into breeding at all.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); if (offerGeneratedItem) { _generateFeed[prefabName] = _file.Bind<bool>(text2, "GenerateFeedItem", true, "Whether to create this animal's feed item. Turn off if you would rather use something you already have, then set BreedingFoods above to that item."); } } } public int MaxCreatures(string prefabName) { if (prefabName == null || !_maxCreatures.TryGetValue(prefabName, out var value)) { return 0; } return value.Value; } public string BreedingFoods(string prefabName) { if (prefabName == null || !_breedingFoods.TryGetValue(prefabName, out var value)) { return string.Empty; } return value.Value; } public bool ShouldGenerateFeed(string prefabName) { if (prefabName != null && _generateFeed.TryGetValue(prefabName, out var value)) { return value.Value; } return false; } } internal static class BreedingItems { private const string Enricher = "MushroomYellow"; private const string PrepTable = "piece_preptable"; private const int Yield = 2; private const int StackSize = 10; private static readonly Color EnrichedTint = new Color(1f, 0.85f, 0.2f); private const float EnrichedTintStrength = 0.4f; private static readonly Dictionary<string, string> BaseFoodByAnimal = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Asksvin", "Vineberry" }, { "Hen", "Dandelion" }, { "Lox", "Cloudberry" }, { "Moose", "Lingonberry" }, { "Wolf", "RawMeat" } }; private static readonly Dictionary<string, string> PlainFoodByAnimal = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Boar", "MushroomYellow" } }; public static void Register() { PrefabManager.OnVanillaPrefabsAvailable += OnPrefabsAvailable; } private static void OnPrefabsAvailable() { PrefabManager.OnVanillaPrefabsAvailable -= OnPrefabsAvailable; BindKnownAnimals(); if ((Object)(object)PrefabManager.Instance.GetPrefab("piece_preptable") == (Object)null) { QuickBreedingPlugin.Log.LogError((object)"Crafting station 'piece_preptable' not found - feed items were not registered."); return; } HashSet<string> hashSet = new HashSet<string>(); foreach (KeyValuePair<string, string> item in BaseFoodByAnimal) { if (!QuickBreedingPlugin.Animals.IsExcluded(item.Key) && QuickBreedingPlugin.Animals.ShouldGenerateFeed(item.Key) && hashSet.Add(item.Value)) { AddEnriched(item.Value); } } DescribeEnricher(); } private static void BindKnownAnimals() { foreach (KeyValuePair<string, string> item in PlainFoodByAnimal) { if (!QuickBreedingPlugin.Animals.IsExcluded(item.Key)) { QuickBreedingPlugin.Animals.Bind(item.Key, AnimalDisplayName(item.Key), item.Value, offerGeneratedItem: false); } } foreach (KeyValuePair<string, string> item2 in BaseFoodByAnimal) { if (!QuickBreedingPlugin.Animals.IsExcluded(item2.Key)) { QuickBreedingPlugin.Animals.Bind(item2.Key, AnimalDisplayName(item2.Key), ItemName(item2.Value), offerGeneratedItem: true); } } } private static void AddEnriched(string baseFood) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown if ((Object)(object)PrefabManager.Instance.GetPrefab(baseFood) == (Object)null) { QuickBreedingPlugin.Log.LogWarning((object)("Base food '" + baseFood + "' not found - skipping its feed item.")); return; } ItemConfig val = new ItemConfig(); val.Name = "Enriched " + DisplayName(baseFood); val.Description = "Animal feed. Hand-feed a pair to encourage breeding."; val.Amount = 2; val.StackSize = 10; val.CraftingStation = "piece_preptable"; val.Requirements = (RequirementConfig[])(object)new RequirementConfig[2] { new RequirementConfig { Item = baseFood, Amount = 1 }, new RequirementConfig { Item = "MushroomYellow", Amount = 1 } }; ItemConfig val2 = val; Sprite val3 = IconFor(baseFood, EnrichedTint, 0.4f); if ((Object)(object)val3 != (Object)null) { val2.Icon = val3; } CustomItem val4 = new CustomItem(ItemName(baseFood), baseFood, val2); MakeAnimalFeed(val4); ItemManager.Instance.AddItem(val4); } private static void MakeAnimalFeed(CustomItem item) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) SharedData val = ((item == null) ? null : item.ItemDrop?.m_itemData?.m_shared); if (val != null) { val.m_itemType = (ItemType)1; val.m_food = 0f; val.m_foodStamina = 0f; val.m_foodEitr = 0f; val.m_foodBurnTime = 0f; val.m_foodRegen = 0f; val.m_consumeStatusEffect = null; } } private static void DescribeEnricher() { GameObject prefab = PrefabManager.Instance.GetPrefab("MushroomYellow"); SharedData val = ((prefab == null) ? null : prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared); if (val != null) { Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(val.m_description) : null) ?? string.Empty; if (text.IndexOf("hand-feed", StringComparison.OrdinalIgnoreCase) < 0) { val.m_description = text + "\n\nHand-feed to a pair of boars to encourage breeding."; } } } private static Sprite Tint(Sprite source, Color tint, float strength) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: 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_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null) { return null; } Texture2D texture = source.texture; Rect textureRect = source.textureRect; int num = Mathf.Max(1, (int)((Rect)(ref textureRect)).width); int num2 = Mathf.Max(1, (int)((Rect)(ref textureRect)).height); RenderTexture temporary = RenderTexture.GetTemporary(((Texture)texture).width, ((Texture)texture).height, 0, (RenderTextureFormat)0); RenderTexture active = RenderTexture.active; try { Graphics.Blit((Texture)(object)texture, temporary); RenderTexture.active = temporary; Texture2D val = new Texture2D(num, num2, (TextureFormat)5, false); val.ReadPixels(new Rect(((Rect)(ref textureRect)).x, ((Rect)(ref textureRect)).y, (float)num, (float)num2), 0, 0); Color[] pixels = val.GetPixels(); for (int i = 0; i < pixels.Length; i++) { Color val2 = pixels[i]; pixels[i] = new Color(Mathf.Lerp(val2.r, tint.r, strength), Mathf.Lerp(val2.g, tint.g, strength), Mathf.Lerp(val2.b, tint.b, strength), val2.a); } val.SetPixels(pixels); val.Apply(); return Sprite.Create(val, new Rect(0f, 0f, (float)num, (float)num2), new Vector2(0.5f, 0.5f), source.pixelsPerUnit); } catch (Exception ex) { QuickBreedingPlugin.Log.LogWarning((object)("Could not tint icon, using the original: " + ex.Message)); return null; } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } private static Sprite IconFor(string prefabName, Color tint, float strength) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName); Sprite[] array = ((prefab == null) ? null : prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_icons); if (array != null && array.Length != 0) { return Tint(array[0], tint, strength); } return null; } private static string DisplayName(string prefabName) { GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName); return Localize((prefab == null) ? null : prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name) ?? prefabName; } private static string AnimalDisplayName(string prefabName) { GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName); return Localize((prefab == null) ? null : prefab.GetComponent<Character>()?.m_name); } private static string Localize(string token) { if (string.IsNullOrEmpty(token)) { return null; } Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(token) : null); if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("$")) { return text; } return null; } private static string ItemName(string baseFood) { return "QB_Enriched" + baseFood; } } internal class Config { public ConfigEntry<bool> EnableMod { get; } public ConfigEntry<float> HandFeedCooldown { get; } public ConfigEntry<int> RequiredLovePoints { get; } public ConfigEntry<float> PregnancyChance { get; } public ConfigEntry<float> PregnancyDuration { get; } public ConfigEntry<float> CheckInterval { get; } public ConfigEntry<float> PartnerCheckRange { get; } public ConfigEntry<float> TotalCheckRange { get; } public ConfigEntry<int> MaxCreatures { get; } public ConfigEntry<bool> BreedingFoodOnly { get; } public ConfigEntry<string> ExcludedAnimals { get; } public ConfigEntry<bool> DebugLogging { get; } private static ConfigurationManagerAttributes Synced => new ConfigurationManagerAttributes { IsAdminOnly = true }; public Config(ConfigFile file) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Expected O, but got Unknown //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Expected O, but got Unknown //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Expected O, but got Unknown //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Expected O, but got Unknown EnableMod = file.Bind<bool>("1 - General", "EnableMod", true, new ConfigDescription("Master switch. Off = vanilla breeding.", (AcceptableValueBase)null, new object[1] { Synced })); HandFeedCooldown = file.Bind<float>("2 - Breeding", "HandFeedCooldown", 60f, new ConfigDescription("Seconds before an animal can be hand-fed into breeding again. 0 = no cooldown.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3600f), new object[1] { Synced })); RequiredLovePoints = file.Bind<int>("2 - Breeding", "FeedsRequired", 1, new ConfigDescription("Hand-feeds needed before an animal is ready to breed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), new object[1] { Synced })); PregnancyDuration = file.Bind<float>("2 - Breeding", "PregnancyDuration", 2f, new ConfigDescription("Seconds between conceiving and the baby appearing. Affects ground feeding. 0 = leave vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), new object[1] { Synced })); CheckInterval = file.Bind<float>("2 - Breeding", "CheckInterval", 0f, new ConfigDescription("Seconds between breeding checks. Affects ground feeding. 0 = leave vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), new object[1] { Synced })); PregnancyChance = file.Bind<float>("2 - Breeding", "BreedChance", 0f, new ConfigDescription("Chance per check of progress toward conceiving. Affects ground feeding. 0 = leave vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { Synced })); PartnerCheckRange = file.Bind<float>("3 - Ranges", "PartnerCheckRange", 10f, new ConfigDescription("How far apart two animals can be and still pair up. 0 = leave vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 40f), new object[1] { Synced })); TotalCheckRange = file.Bind<float>("3 - Ranges", "TotalCheckRange", 0f, new ConfigDescription("Radius used to count nearby animals against MaxCreatures. 0 = leave vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), new object[1] { Synced })); MaxCreatures = file.Bind<int>("3 - Ranges", "MaxCreatures", 0, new ConfigDescription("Population cap before breeding stops. 0 = leave vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), new object[1] { Synced })); BreedingFoodOnly = file.Bind<bool>("4 - Breeding food", "BreedingFoodOnly", true, new ConfigDescription("On: only the items assigned to each animal work when hand-fed, even if the animal would not normally eat them. Off: any food the animal normally eats also works.", (AcceptableValueBase)null, new object[1] { Synced })); ExcludedAnimals = file.Bind<string>("1 - General", "ExcludedAnimals", "Placeable_HardRock,Wolf_spiritcaller", new ConfigDescription("Comma-separated creatures this mod should leave completely alone. Uses internal names, which the log lists when DebugLogging is on.", (AcceptableValueBase)null, new object[1] { Synced })); DebugLogging = file.Bind<bool>("9 - Debug", "DebugLogging", false, "Logs hand-feeding details, including each item's exact name."); } } [BepInPlugin("jelaw.quickbreeding", "Quick Breeding", "1.5.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class QuickBreedingPlugin : BaseUnityPlugin { public const string Guid = "jelaw.quickbreeding"; internal static ManualLogSource Log; internal static Config Settings; internal static QuickBreedingPlugin Instance; internal static AnimalOverrides Animals; internal static bool Active { get { if (Settings != null) { return Settings.EnableMod.Value; } return false; } } private void Awake() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Settings = new Config(((BaseUnityPlugin)this).Config); Animals = new AnimalOverrides(((BaseUnityPlugin)this).Config); BreedingItems.Register(); new Harmony("jelaw.quickbreeding").PatchAll(); } internal static void MakeHungryAfterBirth(Tameable a, Tameable b, float delay) { if ((Object)(object)Instance != (Object)null) { ((MonoBehaviour)Instance).StartCoroutine(HungerRoutine(a, b, delay)); } } private static IEnumerator HungerRoutine(Tameable a, Tameable b, float delay) { yield return (object)new WaitForSeconds(delay); TameableAccess.MakeHungry(a); TameableAccess.MakeHungry(b); } } internal static class Ownership { internal static bool Claim(ZNetView view) { if ((Object)(object)view == (Object)null || !view.IsValid()) { return false; } if (!view.IsOwner()) { view.ClaimOwnership(); } return view.IsOwner(); } } internal static class ProcreationAccess { private static readonly FieldInfo LoveKeyField = AccessTools.Field(typeof(Procreation), "s_lovePoints"); private static readonly FieldInfo NViewField = AccessTools.Field(typeof(Procreation), "m_nview"); private static readonly MethodInfo IsPregnantMethod = AccessTools.Method(typeof(Procreation), "IsPregnant", (Type[])null, (Type[])null); private static bool _loveKeyResolved; private static int _loveKey; private static readonly int LastBredKey = StringExtensionMethods.GetStableHashCode("jelaw_qb_lastBred"); private static readonly int PrimedKey = StringExtensionMethods.GetStableHashCode("jelaw_qb_primed"); private static int LoveKey { get { if (!_loveKeyResolved) { _loveKeyResolved = true; _loveKey = ((LoveKeyField?.GetValue(null) is int num) ? num : StringExtensionMethods.GetStableHashCode("lovePoints")); } return _loveKey; } } public static ZNetView View(Procreation p) { if (!((Object)(object)p == (Object)null)) { object? obj = NViewField?.GetValue(p); return (ZNetView)((obj is ZNetView) ? obj : null); } return null; } public static bool IsPregnant(Procreation p) { if ((Object)(object)p == (Object)null || IsPregnantMethod == null) { return false; } object obj = IsPregnantMethod.Invoke(p, null); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } public static int GetLove(Procreation p) { ZNetView obj = View(p); ZDO val = ((obj != null) ? obj.GetZDO() : null); if (val != null) { return val.GetInt(LoveKey, 0); } return 0; } public static void SetLove(Procreation p, int value) { ZNetView val = View(p); if (Ownership.Claim(val)) { val.GetZDO().Set(LoveKey, value, false); } } public static int GetPrimed(Procreation p) { ZNetView obj = View(p); ZDO val = ((obj != null) ? obj.GetZDO() : null); if (val != null) { return val.GetInt(PrimedKey, 0); } return 0; } public static void SetPrimed(Procreation p, int value) { ZNetView val = View(p); if (Ownership.Claim(val)) { val.GetZDO().Set(PrimedKey, value, false); } } public static double CooldownRemaining(Procreation p, float cooldown) { if (cooldown <= 0f) { return 0.0; } ZNetView obj = View(p); ZDO val = ((obj != null) ? obj.GetZDO() : null); if (val == null) { return 0.0; } long num = val.GetLong(LastBredKey, 0L); if (num == 0L) { return 0.0; } double totalSeconds = (ZNet.instance.GetTime() - new DateTime(num)).TotalSeconds; if (!(totalSeconds >= (double)cooldown)) { return (double)cooldown - totalSeconds; } return 0.0; } public static void StampBred(Procreation p) { ZNetView val = View(p); if (Ownership.Claim(val)) { val.GetZDO().Set(LastBredKey, ZNet.instance.GetTime().Ticks); } } public static bool MakePregnant(Procreation p) { ZNetView val = View(p); if (!Ownership.Claim(val)) { return false; } val.GetZDO().Set(ZDOVars.s_pregnant, ZNet.instance.GetTime().Ticks); return true; } } internal static class TameableAccess { private static readonly MethodInfo OnConsumedItemMethod = AccessTools.Method(typeof(Tameable), "OnConsumedItem", (Type[])null, (Type[])null); private static readonly MethodInfo IsHungryMethod = AccessTools.Method(typeof(Tameable), "IsHungry", (Type[])null, (Type[])null); private static readonly FieldInfo TameableNViewField = AccessTools.Field(typeof(Tameable), "m_nview"); public static bool IsHungry(Tameable t) { if ((Object)(object)t == (Object)null || IsHungryMethod == null) { return false; } object obj = IsHungryMethod.Invoke(t, null); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } public static void MakeHungry(Tameable t) { if (!((Object)(object)t == (Object)null)) { object? obj = TameableNViewField?.GetValue(t); ZNetView val = (ZNetView)((obj is ZNetView) ? obj : null); if (Ownership.Claim(val)) { val.GetZDO().Set(ZDOVars.s_tameLastFeeding, 0L); } } } public static bool Consume(Tameable tameable, ItemDrop item) { if ((Object)(object)tameable == (Object)null || (Object)(object)item == (Object)null || OnConsumedItemMethod == null) { return false; } ParameterInfo[] parameters = OnConsumedItemMethod.GetParameters(); if (parameters.Length != 1 || !parameters[0].ParameterType.IsAssignableFrom(typeof(ItemDrop))) { QuickBreedingPlugin.Log.LogWarning((object)"Tameable.OnConsumedItem has an unexpected signature - falling back to setting love directly."); return false; } OnConsumedItemMethod.Invoke(tameable, new object[1] { item }); return true; } } internal static class PartnerFinder { public static Procreation FindPartner(Procreation self, float range, Func<Procreation, bool> accept = null) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)self == (Object)null) { return null; } Character component = ((Component)self).GetComponent<Character>(); if ((Object)(object)component == (Object)null) { return null; } string prefabName = Utils.GetPrefabName(((Component)self).gameObject); float num = range * range; foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter == (Object)(object)component || !allCharacter.IsTamed() || Utils.GetPrefabName(((Component)allCharacter).gameObject) != prefabName) { continue; } Vector3 val = ((Component)allCharacter).transform.position - ((Component)component).transform.position; if (!(((Vector3)(ref val)).sqrMagnitude > num)) { Procreation component2 = ((Component)allCharacter).GetComponent<Procreation>(); if (!((Object)(object)component2 == (Object)null) && (accept == null || accept(component2))) { return component2; } } } return null; } public static int CountNearby(Procreation self, float range) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) Character val = (((Object)(object)self == (Object)null) ? null : ((Component)self).GetComponent<Character>()); if ((Object)(object)val == (Object)null) { return 0; } string prefabName = Utils.GetPrefabName(((Component)self).gameObject); float num = range * range; int num2 = 0; foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !(Utils.GetPrefabName(((Component)allCharacter).gameObject) != prefabName)) { Vector3 val2 = ((Component)allCharacter).transform.position - ((Component)val).transform.position; if (((Vector3)(ref val2)).sqrMagnitude <= num) { num2++; } } } return num2; } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetSceneAwakeBindPatch { private static bool _done; private static void Postfix(ZNetScene __instance) { if (_done || __instance?.m_prefabs == null || QuickBreedingPlugin.Animals == null) { return; } _done = true; int num = 0; foreach (GameObject prefab in __instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Character component = prefab.GetComponent<Character>(); if ((Object)(object)prefab.GetComponent<Procreation>() == (Object)null || (Object)(object)prefab.GetComponent<Tameable>() == (Object)null || (Object)(object)prefab.GetComponent<BaseAI>() == (Object)null || (Object)(object)component == (Object)null) { continue; } string prefabName = Utils.GetPrefabName(prefab); if (!QuickBreedingPlugin.Animals.IsExcluded(prefabName) && !QuickBreedingPlugin.Animals.IsBound(prefabName)) { QuickBreedingPlugin.Animals.Bind(prefabName, DisplayName(component), null, offerGeneratedItem: false); num++; if (QuickBreedingPlugin.Settings.DebugLogging.Value) { Procreation component2 = prefab.GetComponent<Procreation>(); QuickBreedingPlugin.Log.LogInfo((object)($"{prefabName}: maxCreatures={component2.m_maxCreatures}, totalCheckRange={component2.m_totalCheckRange}m, " + $"partnerCheckRange={component2.m_partnerCheckRange}m, requiredLove={component2.m_requiredLovePoints}, " + $"pregnancyDuration={component2.m_pregnancyDuration}s, updateInterval={component2.m_updateInterval}s")); QuickBreedingPlugin.Log.LogInfo((object)(prefabName + " eats: " + ConsumeList(prefab))); } } } if (QuickBreedingPlugin.Settings.DebugLogging.Value) { QuickBreedingPlugin.Log.LogInfo((object)$"Bound per-animal settings for {num} breedable creature(s)."); } } private static string ConsumeList(GameObject prefab) { List<ItemDrop> list = prefab.GetComponent<MonsterAI>()?.m_consumeItems; if (list == null || list.Count == 0) { return "(none)"; } List<string> list2 = new List<string>(); foreach (ItemDrop item in list) { if ((Object)(object)item != (Object)null) { list2.Add(((Object)item).name); } } if (list2.Count != 0) { return string.Join(", ", list2.ToArray()); } return "(none)"; } private static string DisplayName(Character character) { string name = character.m_name; if (string.IsNullOrEmpty(name)) { return null; } Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(name) : null); if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("$")) { return text; } return null; } } [HarmonyPatch(typeof(Procreation), "Awake")] internal static class ProcreationAwakePatch { private static void Prefix(Procreation __instance) { if (QuickBreedingPlugin.Active && !((Object)(object)__instance == (Object)null) && !QuickBreedingPlugin.Animals.IsExcluded(Utils.GetPrefabName(((Component)__instance).gameObject))) { Config settings = QuickBreedingPlugin.Settings; if (settings.CheckInterval.Value > 0f) { __instance.m_updateInterval = settings.CheckInterval.Value; } if (settings.PregnancyDuration.Value > 0f) { __instance.m_pregnancyDuration = settings.PregnancyDuration.Value; } float value = settings.PregnancyChance.Value; if (value > 0f) { __instance.m_pregnancyChance = ((value >= 1f) ? (-1f) : (1f - value)); } if (settings.PartnerCheckRange.Value > 0f) { __instance.m_partnerCheckRange = settings.PartnerCheckRange.Value; } if (settings.TotalCheckRange.Value > 0f) { __instance.m_totalCheckRange = settings.TotalCheckRange.Value; } int num = QuickBreedingPlugin.Animals.MaxCreatures(Utils.GetPrefabName(((Component)__instance).gameObject)); if (num > 0) { __instance.m_maxCreatures = num; } else if (settings.MaxCreatures.Value > 0) { __instance.m_maxCreatures = settings.MaxCreatures.Value; } } } } [HarmonyPatch(typeof(Tameable), "GetHoverText")] internal static class TameableHoverTextPatch { private static void Postfix(Tameable __instance, ref string __result) { if (!QuickBreedingPlugin.Active || string.IsNullOrEmpty(__result)) { return; } Procreation component = ((Component)__instance).GetComponent<Procreation>(); if (!((Object)(object)component == (Object)null)) { int num = Mathf.Max(1, QuickBreedingPlugin.Settings.RequiredLovePoints.Value); if (ProcreationAccess.GetPrimed(component) >= num) { __result += "\nReady to Breed"; } } } } [HarmonyPatch(typeof(Tameable), "Interact")] internal static class TameableInteractPatch { internal struct PartnerTally { public int InRange; public int Pregnant; public int Capped; public int CoolingDown; } private const float HeartSeconds = 5f; private const float HeartInterval = 0.8f; private static bool Prefix(Tameable __instance, Humanoid user, bool hold, bool alt, ref bool __result) { if (!QuickBreedingPlugin.Active) { return true; } if (hold || alt || (Object)(object)__instance == (Object)null) { return true; } Player val = (Player)(object)((user is Player) ? user : null); if ((Object)(object)val == (Object)null) { return true; } Character component = ((Component)__instance).GetComponent<Character>(); if ((Object)(object)component == (Object)null || !component.IsTamed()) { return true; } Procreation component2 = ((Component)__instance).GetComponent<Procreation>(); if ((Object)(object)component2 == (Object)null) { return true; } if (QuickBreedingPlugin.Animals.IsExcluded(Utils.GetPrefabName(((Component)__instance).gameObject))) { return true; } ItemData val2 = FindFood(val, __instance); if (val2 == null) { return true; } double num = ProcreationAccess.CooldownRemaining(component2, QuickBreedingPlugin.Settings.HandFeedCooldown.Value); if (num > 0.0) { ((Character)user).Message((MessageType)2, component.m_name + " is not ready to breed again (" + FormatSeconds(num) + ")", 0, (Sprite)null, false); __result = true; return false; } if (AtPopulationCap(component2, out var total, out var cap)) { ((Character)user).Message((MessageType)2, $"Max animals in area ({total}/{cap})", 0, (Sprite)null, false); __result = true; return false; } int num2 = Mathf.Max(1, QuickBreedingPlugin.Settings.RequiredLovePoints.Value); int primed = ProcreationAccess.GetPrimed(component2); bool flag = TameableAccess.IsHungry(__instance); if (primed >= num2 && !flag) { ((Character)user).Message((MessageType)2, component.m_name + " is already ready to breed", 0, (Sprite)null, false); __result = true; return false; } if (CollectEligiblePartners(__instance, component2, out var tally).Count == 0) { if (QuickBreedingPlugin.Settings.DebugLogging.Value) { QuickBreedingPlugin.Log.LogInfo((object)("refused " + Utils.GetPrefabName(((Component)__instance).gameObject) + ": no eligible partner " + $"within {component2.m_partnerCheckRange}m " + $"(in range {tally.InRange}, pregnant {tally.Pregnant}, " + $"capped {tally.Capped}, cooling down {tally.CoolingDown})")); } ((Character)user).Message((MessageType)2, (tally.CoolingDown > 0) ? ("No other " + component.m_name + " ready to breed yet") : ("No other " + component.m_name + " close enough to breed with"), 0, (Sprite)null, false); __result = true; return false; } ((Humanoid)val).GetInventory().RemoveItem(val2, 1); GameObject dropPrefab = val2.m_dropPrefab; TameableAccess.Consume(__instance, (dropPrefab != null) ? dropPrefab.GetComponent<ItemDrop>() : null); int num3 = Mathf.Min(primed + 1, num2); ProcreationAccess.SetPrimed(component2, num3); bool flag2 = num3 >= num2 && TryBreedNow(__instance, component2, num2); if (!flag2 && num3 >= num2) { ShowEligiblePartners(__instance, component2); } if (QuickBreedingPlugin.Settings.DebugLogging.Value) { AtPopulationCap(component2, out var total2, out var cap2); QuickBreedingPlugin.Log.LogInfo((object)($"fed {Utils.GetPrefabName(((Component)__instance).gameObject)}: primed {primed} -> {num3}" + $"/{num2}, conceived={flag2}, " + $"vanilla love={ProcreationAccess.GetLove(component2)}/{component2.m_requiredLovePoints}, " + $"hungry {flag} -> {TameableAccess.IsHungry(__instance)}, " + $"alerted={IsAlerted(__instance)}, pregnant={ProcreationAccess.IsPregnant(component2)}, " + $"nearby={total2}/{cap2} incl. young within {component2.m_totalCheckRange}m")); } ((Character)user).Message((MessageType)2, flag2 ? (component.m_name + " is expecting") : ((num3 >= num2) ? (component.m_name + " is ready to breed") : $"{component.m_name} fed ({num3}/{num2})"), 0, (Sprite)null, false); __result = true; return false; } private static bool IsAlerted(Tameable tameable) { BaseAI component = ((Component)tameable).GetComponent<BaseAI>(); if ((Object)(object)component != (Object)null) { return component.IsAlerted(); } return false; } private static string FormatSeconds(double seconds) { int num = Mathf.CeilToInt((float)seconds); if (num < 60) { return $"{num}s"; } return $"{num / 60}m {num % 60}s"; } private static bool TryBreedNow(Tameable tameable, Procreation procreation, int feedsNeeded) { if (ProcreationAccess.IsPregnant(procreation)) { return false; } if (IsAlerted(tameable)) { return false; } float cooldown = QuickBreedingPlugin.Settings.HandFeedCooldown.Value; Procreation val = PartnerFinder.FindPartner(procreation, procreation.m_partnerCheckRange, (Procreation candidate) => !ProcreationAccess.IsPregnant(candidate) && ProcreationAccess.GetPrimed(candidate) >= feedsNeeded && ProcreationAccess.CooldownRemaining(candidate, cooldown) <= 0.0); if ((Object)(object)val == (Object)null) { return false; } if (AtPopulationCap(procreation, out var _, out var _)) { return false; } if (!ProcreationAccess.MakePregnant(procreation)) { return false; } ProcreationAccess.SetPrimed(procreation, 0); ProcreationAccess.SetPrimed(val, 0); ProcreationAccess.StampBred(procreation); ProcreationAccess.StampBred(val); Tameable component = ((Component)val).GetComponent<Tameable>(); float delay = procreation.m_pregnancyDuration + 2f; QuickBreedingPlugin.MakeHungryAfterBirth(tameable, component, delay); return true; } private static List<Procreation> CollectEligiblePartners(Tameable fed, Procreation self) { PartnerTally tally; return CollectEligiblePartners(fed, self, out tally); } private static List<Procreation> CollectEligiblePartners(Tameable fed, Procreation self, out PartnerTally tally) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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) tally = default(PartnerTally); List<Procreation> list = new List<Procreation>(); Character component = ((Component)fed).GetComponent<Character>(); if ((Object)(object)component == (Object)null) { return list; } if (AtPopulationCap(self, out var total, out var cap)) { return list; } string prefabName = Utils.GetPrefabName(((Component)fed).gameObject); float num = self.m_partnerCheckRange * self.m_partnerCheckRange; foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter == (Object)(object)component || !allCharacter.IsTamed() || Utils.GetPrefabName(((Component)allCharacter).gameObject) != prefabName) { continue; } Vector3 val = ((Component)allCharacter).transform.position - ((Component)component).transform.position; if (((Vector3)(ref val)).sqrMagnitude > num) { continue; } Procreation component2 = ((Component)allCharacter).GetComponent<Procreation>(); if (!((Object)(object)component2 == (Object)null)) { tally.InRange++; if (ProcreationAccess.IsPregnant(component2)) { tally.Pregnant++; } else if (AtPopulationCap(component2, out cap, out total)) { tally.Capped++; } else if (ProcreationAccess.CooldownRemaining(component2, QuickBreedingPlugin.Settings.HandFeedCooldown.Value) > 0.0) { tally.CoolingDown++; } else { list.Add(component2); } } } return list; } private static void ShowEligiblePartners(Tameable fed, Procreation self) { List<Procreation> list = CollectEligiblePartners(fed, self); if (list.Count != 0) { list.Add(self); if ((Object)(object)QuickBreedingPlugin.Instance != (Object)null) { ((MonoBehaviour)QuickBreedingPlugin.Instance).StartCoroutine(PulseHearts(list)); } } } private static IEnumerator PulseHearts(List<Procreation> targets) { for (float elapsed = 0f; elapsed < 5f; elapsed += 0.8f) { foreach (Procreation target in targets) { if (!((Object)(object)target == (Object)null)) { Tameable component = ((Component)target).GetComponent<Tameable>(); if (!((Object)(object)component == (Object)null)) { component.m_sootheEffect.Create(((Component)target).transform.position, ((Component)target).transform.rotation, (Transform)null, 1f, -1, default(ZDOID)); } } } yield return (object)new WaitForSeconds(0.8f); } } private static bool AtPopulationCap(Procreation procreation, out int total, out int cap) { //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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) total = 0; cap = procreation.m_maxCreatures; ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(Utils.GetPrefabName(((Component)procreation).gameObject)) : null); if ((Object)(object)val == (Object)null) { return false; } Vector3 position = ((Component)procreation).transform.position; float totalCheckRange = procreation.m_totalCheckRange; int nrOfInstances = SpawnSystem.GetNrOfInstances(val, position, totalCheckRange, false, false); int num = (((Object)(object)procreation.m_offspring != (Object)null) ? SpawnSystem.GetNrOfInstances(procreation.m_offspring, position, totalCheckRange, false, false) : 0); total = nrOfInstances + num; return total >= cap; } private static ItemData FindFood(Player player, Tameable tameable) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return null; } bool value = QuickBreedingPlugin.Settings.DebugLogging.Value; if (QuickBreedingPlugin.Settings.BreedingFoodOnly.Value) { HashSet<string> hashSet = BreedingFoodNames(Utils.GetPrefabName(((Component)tameable).gameObject)); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null) { if (value) { QuickBreedingPlugin.Log.LogInfo((object)("hand-feed candidate: prefab='" + PrefabName(allItem) + "' token='" + allItem.m_shared?.m_name + "'")); } if (hashSet.Contains(Normalize(PrefabName(allItem))) || hashSet.Contains(Normalize(allItem.m_shared?.m_name))) { return allItem; } } } return null; } List<ItemDrop> list = ((Component)tameable).GetComponent<MonsterAI>()?.m_consumeItems; if (list == null) { return null; } foreach (ItemDrop item in list) { string text = item?.m_itemData?.m_shared?.m_name; if (string.IsNullOrEmpty(text)) { continue; } foreach (ItemData allItem2 in inventory.GetAllItems()) { if (allItem2?.m_shared?.m_name == text) { return allItem2; } } } return null; } private static string PrefabName(ItemData item) { if (!((Object)(object)item?.m_dropPrefab != (Object)null)) { return null; } return ((Object)item.m_dropPrefab).name; } private static string Normalize(string raw) { if (string.IsNullOrEmpty(raw)) { return string.Empty; } string text = raw.Trim().ToLowerInvariant(); if (text.StartsWith("$item_")) { text = text.Substring("$item_".Length); } return text.Replace("_", string.Empty); } private static HashSet<string> BreedingFoodNames(string prefabName) { string text = QuickBreedingPlugin.Animals.BreedingFoods(prefabName); HashSet<string> hashSet = new HashSet<string>(); if (text == null) { text = string.Empty; } string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = Normalize(array[i]); if (text2.Length > 0) { hashSet.Add(text2); } } return hashSet; } } }