using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using Zen; using Zen.Interop; using Zen.Lib; using Zen.Lib.Config; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ZenBreeding")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ZenBreeding")] [assembly: AssemblyCopyright("Copyright \ufffd 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.6.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.6.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 ZenBreeding { [HarmonyPatch] internal static class Breeding { internal enum SterilizeType { None, All, BabiesOnly } private static readonly int ZDOVarSterile = StringExtensionMethods.GetStableHashCode("ZenBreeding_Sterile"); private const string LaidEgg = "Zen_LaidEgg"; private const int LaidEggStackID = 100; private const string SterileText = "$info_sterile"; private const string FertileText = "$info_fertile"; private const string PregnantText = "$info_pregnant"; private static readonly Sprite FertileIcon = AssetIO.LoadSpriteFromResource((BaseUnityPlugin)(object)ZenMod.Instance, "heart.png"); private static bool IsSterile(this ZNetView? nview) { if (Object.op_Implicit((Object)(object)nview) && nview.IsValid()) { return nview.GetZDO().GetBool(ZDOVarSterile, false); } return false; } private static bool IsLaid(this EggGrow egg) { return egg.m_item.m_itemData.IsLaidEgg(); } private static bool IsLaidEgg(this ItemData egg) { return egg.m_customData.ContainsKey("Zen_LaidEgg"); } internal static void InitPrefabs() { if (Configs.Sterilization.Value == SterilizeType.All) { CollectionExtensions.Do((IEnumerable)ZNetScene.instance.m_prefabs, (Action)RemoveProcreate); } } private static void RemoveProcreate(GameObject prefab) { Procreation val = default(Procreation); if (!Configs.BreedingAllowed.Value.Contains(((Object)prefab).name, true) && prefab.TryGetComponent(ref val)) { Object.Destroy((Object)(object)val); Logging.Message((object)("Sterilized prefab: " + ((Object)prefab).name), 0); } } private static bool CanBreed(Character c) { return CanBreed(GameObjectExt.GetPrefabName(((Component)c).gameObject)); } private static bool CanBreed(EggGrow egg) { return CanBreed(GameObjectExt.GetPrefabName(((Component)egg).gameObject)); } private static bool CanBreed(ItemDrop item) { return CanBreed(ItemDataExt.GetPrefabName(item)); } private static bool CanBreed(string prefabName) { if (Configs.Sterilization.Value != 0) { return Configs.BreedingAllowed.Value.Contains(prefabName, true); } return true; } [HarmonyPostfix] [HarmonyPatch(typeof(Procreation), "ReadyForProcreation")] private static void Procreation_ReadyForProcreation(Procreation __instance, ref bool __result) { if (__instance.m_nview.IsValid()) { __result = __result && !__instance.m_nview.IsSterile(); } } [HarmonyPrefix] [HarmonyPatch(typeof(Procreation), "MakePregnant")] private static void Procreation_MakePregnant(Procreation __instance, ref bool __runOriginal) { if (__instance.m_nview.IsValid()) { __runOriginal = !__instance.m_nview.IsSterile(); } } [HarmonyTranspiler] [HarmonyPatch(typeof(EggGrow), "GrowUpdate")] private static IEnumerable EggGrow_GrowUpdate_Transpiler(IEnumerable codes) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown Func func = EggGrow_Intercept; CodeMatch[] array = (CodeMatch[])(object)new CodeMatch[3] { CodeMatch.op_Implicit(OpCodes.Ldloc_1), new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Object), "op_Implicit", (Type[])null, (Type[])null), (string)null), CodeMatch.op_Implicit(OpCodes.Brfalse) }; return new CodeMatcher(codes, (ILGenerator)null).MatchStartForward(array).ThrowIfInvalid("Unable to match IL").Advance(1) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldarg_0, (object)null) }) .Set(OpCodes.Call, (object)func.Method) .InstructionEnumeration(); } private static bool EggGrow_Intercept(Character? hatchling, EggGrow egg) { if (!Object.op_Implicit((Object)(object)hatchling)) { return false; } if (CanBreed(egg)) { return true; } if (egg.IsLaid()) { Sterilize(hatchling); } return true; } [HarmonyTranspiler] [HarmonyPatch(typeof(Growup), "GrowUpdate")] private static IEnumerable GrowUp_GrowUpdate_Transpiler(IEnumerable codes) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) Func func = GrowUp_Intercept; CodeMatch[] array = (CodeMatch[])(object)new CodeMatch[6] { CodeMatch.op_Implicit(OpCodes.Ldloc_1), new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Object), "op_Implicit", (Type[])null, (Type[])null), (string)null), CodeMatch.op_Implicit(OpCodes.Brfalse), CodeMatch.op_Implicit(OpCodes.Ldloc_2), new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Object), "op_Implicit", (Type[])null, (Type[])null), (string)null), CodeMatch.op_Implicit(OpCodes.Brfalse) }; return new CodeMatcher(codes, (ILGenerator)null).MatchStartForward(array).ThrowIfInvalid("Unable to match IL").Advance(1) .SetAndAdvance(OpCodes.Nop, (object)null) .SetAndAdvance(OpCodes.Nop, (object)null) .Advance(1) .Set(OpCodes.Call, (object)func.Method) .InstructionEnumeration(); } private static bool GrowUp_Intercept(Character young, Character mature) { if (!Object.op_Implicit((Object)(object)young) || !Object.op_Implicit((Object)(object)mature)) { return false; } if (CanBreed(mature)) { return true; } if (young.m_nview.IsSterile()) { Sterilize(mature); } return true; } [HarmonyTranspiler] [HarmonyPatch(typeof(Procreation), "Procreate")] [HarmonyPriority(0)] private static IEnumerable Procreation_Procreate_Transpiler(IEnumerable codes) { MethodInfo methodInfo = AccessTools.Method(typeof(Character), "SetLevel", (Type[])null, (Type[])null); Action action = SetLevelIntercept; MethodInfo methodInfo2 = AccessTools.Method(typeof(ItemDrop), "SetQuality", (Type[])null, (Type[])null); Action action2 = SetQualityIntercept; return Transpilers.MethodReplacer(Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)action.Method), (MethodBase)methodInfo2, (MethodBase)action2.Method); static void SetLevelIntercept(Character baby, int level) { baby.SetLevel(level); ProcreateBaby(baby); } static void SetQualityIntercept(ItemDrop item, int quality) { item.SetQuality(quality); ProcreateEgg(item); } } private static void ProcreateBaby(Character baby) { if (!CanBreed(baby)) { Sterilize(baby); } } private static void ProcreateEgg(ItemDrop egg) { SetEggState(egg.m_itemData, isLaid: true, !CanBreed(egg)); } internal static void SetEggState(ItemData egg, bool isLaid, bool separateStacks) { egg.m_worldLevel = (separateStacks ? 100 : Game.m_worldLevel); if (isLaid) { egg.m_customData["Zen_LaidEgg"] = true.ToString(); ItemDataExt.SetTooltipExtra(egg, "$info_sterile"); } else { egg.m_customData.Remove("Zen_LaidEgg"); ItemDataExt.SetTooltipExtra(egg, (string)null); } } private static void Sterilize(Character offspring) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Logging.Info((object)$"Sterilize {((Object)offspring).name} {MathExt.XZY(((Component)offspring).transform.position)}", 0); if (offspring.m_nview.IsOwner()) { offspring.m_nview.GetZDO().Set(ZDOVarSterile, true); } } [HarmonyPostfix] [HarmonyPatch(typeof(Tameable), "GetHoverText")] [HarmonyPriority(100)] private static void Tameable_GetHoverText(Tameable __instance, ref string __result) { Procreation val = default(Procreation); if (__instance.IsTamed() && !__instance.m_nview.IsSterile() && ((Component)__instance).TryGetComponent(ref val) && ((Behaviour)val).enabled) { HoverItem.UpdateIcons((Sprite[])(object)new Sprite[1] { FertileIcon }); if (val.IsPregnant()) { __result += StringExt.Localize(string.Format("\n{1}", UIColor.MajorInfo, "$info_pregnant")); } else { __result += StringExt.Localize(string.Format("\n{1}", UIColor.MinorInfo, "$info_fertile")); } } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemDrop), "GetHoverText")] [HarmonyPriority(100)] private static void ItemDrop_GetHoverText(ItemDrop __instance, ref string __result) { if (__instance.m_itemData.IsLaidEgg()) { __result += StringExt.Localize(string.Format("\n\n{1}", UIColor.MajorInfo, "$info_sterile")); } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(int) })] private static void ItemDrop_ItemData_GetTooltip(ItemData __instance, ref string __result) { if (__instance.IsLaidEgg()) { string description = __instance.m_shared.m_description; __result = Regex.Replace(__result, "\\$item_newgameplusitem.*\n", string.Empty); __result = __result.Replace(description, string.Format("{0}\n\n{2}", description, UIColor.Orange, "$info_sterile")); } } } internal static class BreedingCommands { private const string CmdSterilize = "sterilize"; private const string CmdFertilize = "fertilize"; private static List Inventory { get { Player localPlayer = Player.m_localPlayer; return ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory().GetAllItems() : null) ?? new List(); } } private static string Syntax(string command) { return command + " "; } public static void Init() { ZenMod.Terminal.CreateCommand("sterilize", "Sterilize egg in your inventory", true, (Action)ProcessEgg); ZenMod.Terminal.CreateCommand("fertilize", "Fertilize egg in you inventory", true, (Action)ProcessEgg); } private static ItemData[] FindItems(string name) { string name2 = name; return Inventory.Where((ItemData item) => ItemDataExt.GetPrefabName(item).ToLower() == name2.ToLower()).ToArray(); } private static void ProcessEgg(params string[] args) { if (args.Length < 2) { Console.instance.Print(Syntax(args[0])); return; } string text = args[0].ToLower().Replace("zen_".ToLower(), string.Empty); string text2 = args[1]; ItemData[] array = FindItems(text2); if (array.Length == 0) { Console.instance.Print("No item found: " + text2); return; } bool flag; if (!(text == "sterilize")) { if (!(text == "fertilize")) { throw new Exception("Unknown command: " + text); } flag = false; } else { flag = true; } bool flag2 = flag; int num = 0; ItemData[] array2 = array; foreach (ItemData val in array2) { Breeding.SetEggState(val, flag2, flag2); num += val.m_stack; } Console.instance.Print($"{StringExt.ToProperCase(text, true)} {num} {text2}"); } } internal static class Configs { public static readonly ConfigEntry Sterilization; public static readonly ConfigEntry BreedingAllowed; static Configs() { Sterilization = Config.Define(true, "Breeding", "Sterilize", Breeding.SterilizeType.BabiesOnly, "Prevent exponential reproduction of tamed creatures.\r\n- None: Vanilla behavior, no protection.\r\n- All: Sterilize all, prevent all reproduction.\r\n- BabiesOnly: Babies are sterilized. Tamed creatures can produce offspring.\r\nEggs that are discovered or purchased will produce a fertile Hen that can lay eggs.\r\nHowever, any laid eggs will produce a sterile Hen that can not produce eggs.\r\n[When you are in admin/god mode you can glance at the fertility of eggs on the ground.]"); BreedingAllowed = Config.Define(true, "Breeding", "Allowed Creatures", StringList.Empty, "Comma separated list of prefabs that are allowed to reproduce when sterilization is enabled.\r\nFor example: If you add \"ChickenEgg\" to this list then chicken eggs will grow into fertile chickens that can lay eggs.\r\n[restart required for changes to take effect]"); } } [BepInPlugin("ZenDragon.ZenBreeding", "ZenBreeding", "0.6.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class Plugin : ZenMod { public const string PluginName = "ZenBreeding"; public const string PluginVersion = "0.6.1"; public const string PluginGUID = "ZenDragon.ZenBreeding"; protected override void Setup() { base.ConfigSync += Breeding.InitPrefabs; BreedingCommands.Init(); } protected override void TitleScene(bool isFirstBoot) { } protected override void WorldStart() { } protected override void Shutdown() { } protected override HelpInfo? GetHelpInfo() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(HelpInfo.Format(Configs.Sterilization, true, true, false)); return new HelpInfo("Breeding", stringBuilder.ToString()); } } }