Decompiled source of ZenBreeding v0.4.1

plugins/ZenBreeding.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
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.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using Zen;
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.4.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.4.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]
	public 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 static readonly Sprite FertileIcon = AssetIO.LoadSpriteFromResource((BaseUnityPlugin)(object)ZenMod<Plugin>.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<GameObject>((IEnumerable<GameObject>)ZNetScene.instance.m_prefabs, (Action<GameObject>)RemoveProcreate);
			}
		}

		private static void RemoveProcreate(GameObject prefab)
		{
			Procreation val = default(Procreation);
			if (!Configs.BreedingAllowed.Value.Contains(((Object)prefab).name, true) && prefab.TryGetComponent<Procreation>(ref val))
			{
				Object.Destroy((Object)(object)val);
				Logging<Plugin>.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<CodeInstruction> EggGrow_GrowUpdate_Transpiler(IEnumerable<CodeInstruction> 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<Character, EggGrow, bool> 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<CodeInstruction> GrowUp_GrowUpdate_Transpiler(IEnumerable<CodeInstruction> 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<Character, Character, bool> 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")]
		private static IEnumerable<CodeInstruction> Procreation_Procreate_Transpiler(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Character), "SetLevel", (Type[])null, (Type[])null);
			Action<Character, int> action = ProcreateSetLevel_Intercept;
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ItemDrop), "SetQuality", (Type[])null, (Type[])null);
			Action<ItemDrop, int> action2 = ProcreateSetQuality_Intercept;
			return Transpilers.MethodReplacer(Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)action.Method), (MethodBase)methodInfo2, (MethodBase)action2.Method);
		}

		private static void ProcreateSetLevel_Intercept(Character baby, int level)
		{
			baby.SetLevel(level);
			ProcreateBaby(baby);
		}

		private static void ProcreateSetQuality_Intercept(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)
		{
			if (!CanBreed(egg))
			{
				egg.m_itemData.m_worldLevel = 100;
			}
			egg.m_itemData.m_customData["Zen_LaidEgg"] = true.ToString();
		}

		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<Plugin>.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")]
		private static void Tameable_GetHoverText(Tameable __instance, ref string __result)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			Procreation val = default(Procreation);
			if (__instance.IsTamed() && !__instance.m_nview.IsSterile() && ((Component)__instance).TryGetComponent<Procreation>(ref val) && ((Behaviour)val).enabled)
			{
				HudExt.UpdateHoverIcons(Hud.instance, (Sprite[])(object)new Sprite[1] { FertileIcon });
				if (val.IsPregnant())
				{
					__result += StringExt.Localize($"\n<color={UIColor.MajorInfo}>$info_pregnant</color>");
				}
				else
				{
					__result += StringExt.Localize($"\n<color={UIColor.MinorInfo}>$info_fertile</color>");
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ItemDrop), "GetHoverText")]
		private static void ItemDrop_GetHoverText(ItemDrop __instance, ref string __result)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (__instance.m_itemData.IsLaidEgg() && ((Character)Player.m_localPlayer).InGodMode())
			{
				__result += StringExt.Localize($"\n<color={UIColor.MinorInfo}>$info_sterile</color>");
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(int) })]
		private static void ItemDrop_ItemData_GetTooltip(ItemData __instance, ref string __result)
		{
			if (__instance.IsLaidEgg())
			{
				__result = Regex.Replace(__result, "\\$item_newgameplusitem.*\n", "$info_sterile</color>\n");
			}
		}
	}
	internal static class Configs
	{
		public static readonly ConfigEntry<Breeding.SterilizeType> Sterilization;

		public static readonly ConfigEntry<StringList> BreedingAllowed;

		static Configs()
		{
			Sterilization = Config.Define<Breeding.SterilizeType>(true, "Breeding", "Sterilize", Breeding.SterilizeType.BabiesOnly, "Prevent exponential reproduction of tamed creatures.\r\n- None: No protection, Vanilla behavior\r\n- All: Remove all reproduction from adults and offspring. No reproduction allowed.\r\n- BabiesOnly: Babies are sterilized. However, wild creatures which are tamed will still be able to produce offspring.\r\nNOTE: Eggs that are discovered or purchased will produce a fertile Hen. That hen can produce eggs.\r\nHowever, any laid eggs will produce a sterile Hen that can not produce eggs.\r\nTIP: When you are in admin/god mode you can glance at the fertility of eggs on the ground.");
			BreedingAllowed = Config.Define<StringList>(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.4.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal class Plugin : ZenMod<Plugin>
	{
		public const string PluginName = "ZenBreeding";

		public const string PluginVersion = "0.4.1";

		public const string PluginGUID = "ZenDragon.ZenBreeding";

		protected override void Setup()
		{
			base.ConfigSync += Breeding.InitPrefabs;
		}

		protected override void TitleScene(bool isFirstBoot)
		{
		}

		protected override void WorldStart()
		{
		}

		protected override void Shutdown()
		{
		}
	}
}