Decompiled source of BabyTames v1.0.1

BepInEx/plugins/BabyTames.dll

Decompiled 5 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BabyTames")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("BabyTames")]
[assembly: AssemblyTitle("BabyTames")]
[assembly: AssemblyVersion("1.0.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 BabyTames
{
	internal static class Babies
	{
		private static readonly int KeepBabyKey = StringExtensionMethods.GetStableHashCode("BabyTames_KeepBaby");

		private static readonly int FrozenAgeKey = StringExtensionMethods.GetStableHashCode("BabyTames_FrozenAge");

		private static readonly FieldRef<Character, ZNetView> CharacterView = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview");

		private static readonly FieldRef<Player, Character> HoveringCreature = AccessTools.FieldRefAccess<Player, Character>("m_hoveringCreature");

		private static readonly Func<Player, bool> TakeInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<BaseAI, GameObject, float> Follow = AccessTools.MethodDelegate<Action<BaseAI, GameObject, float>>(AccessTools.Method(typeof(BaseAI), "Follow", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Regex GrowthLine = new Regex("^(Grows up in .*|Growing up)$", RegexOptions.Multiline);

		private const string StayingBaby = "Staying a baby";

		private static string pendingName;

		private static string pendingNameAuthor;

		private static string pendingFollow;

		internal static ZNetView ViewOf(Character character)
		{
			return CharacterView.Invoke(character);
		}

		internal static bool IsBaby(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			ZNetView val = CharacterView.Invoke(character);
			if ((Object)(object)val != (Object)null && val.IsValid())
			{
				return Plugin.BabyHashes.Contains(val.GetZDO().GetPrefab());
			}
			return false;
		}

		internal static bool IsTameBaby(Character character)
		{
			if (IsBaby(character))
			{
				return character.IsTamed();
			}
			return false;
		}

		internal static bool IsKept(ZDO zdo)
		{
			return zdo.GetBool(KeepBabyKey, false);
		}

		internal static Character Hovered(Player player)
		{
			return HoveringCreature.Invoke(player);
		}

		internal static void SetUpPrefabs(ZNetScene scene)
		{
			foreach (GameObject prefab in scene.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null || !Plugin.BabyHashes.Contains(StringExtensionMethods.GetStableHashCode(((Object)prefab).name)))
				{
					continue;
				}
				if ((Object)(object)prefab.GetComponent<Character>() == (Object)null || (Object)(object)prefab.GetComponent<BaseAI>() == (Object)null)
				{
					Plugin.Log.LogWarning((object)(((Object)prefab).name + " is not a creature with an AI, skipping"));
					continue;
				}
				Tameable val = prefab.GetComponent<Tameable>();
				if ((Object)(object)val == (Object)null)
				{
					val = prefab.AddComponent<Tameable>();
					CopyFromGrown(prefab, val);
				}
				if (Plugin.CommandableBabies.Value)
				{
					val.m_commandable = true;
				}
			}
		}

		private static void CopyFromGrown(GameObject baby, Tameable tameable)
		{
			Growup component = baby.GetComponent<Growup>();
			Tameable val = (((Object)(object)component != (Object)null && (Object)(object)component.m_grownPrefab != (Object)null) ? component.m_grownPrefab.GetComponent<Tameable>() : null);
			if (!((Object)(object)val == (Object)null))
			{
				tameable.m_fedDuration = val.m_fedDuration;
				tameable.m_tamingTime = val.m_tamingTime;
				tameable.m_petEffect = val.m_petEffect;
				tameable.m_sootheEffect = val.m_sootheEffect;
				tameable.m_tameText = val.m_tameText;
				tameable.m_nameBeforeText = val.m_nameBeforeText;
			}
		}

		internal static void Tick()
		{
			//IL_0021: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !TakeInput(localPlayer))
			{
				return;
			}
			KeyboardShortcut value = Plugin.KeepBabyKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				Character val = Hovered(localPlayer);
				if (IsTameBaby(val) && !((Object)(object)((Component)val).GetComponent<Growup>() == (Object)null))
				{
					Toggle(localPlayer, val);
				}
			}
		}

		private static void Toggle(Player player, Character baby)
		{
			ZNetView obj = CharacterView.Invoke(baby);
			obj.ClaimOwnership();
			ZDO zDO = obj.GetZDO();
			bool flag = !IsKept(zDO);
			if (flag)
			{
				Growup component = ((Component)baby).GetComponent<Growup>();
				BaseAI baseAI = baby.GetBaseAI();
				float num = (((Object)(object)baseAI != (Object)null) ? ((float)baseAI.GetTimeSinceSpawned().TotalSeconds) : 0f);
				num = Mathf.Clamp(num, 0f, Mathf.Max(0f, component.m_growTime - 30f));
				zDO.Set(FrozenAgeKey, num);
				zDO.Set(KeepBabyKey, true);
				PinAge(zDO);
			}
			else
			{
				PinAge(zDO);
				zDO.Set(KeepBabyKey, false);
			}
			((Character)player).Message((MessageType)2, baby.GetHoverName() + (flag ? " will stay a baby" : " will grow up"), 0, (Sprite)null, false);
		}

		private static void PinAge(ZDO zdo)
		{
			long ticks = TimeSpan.FromSeconds(zdo.GetFloat(FrozenAgeKey, 0f)).Ticks;
			zdo.Set(ZDOVars.s_spawnTime, ZNet.instance.GetTime().Ticks - ticks);
		}

		internal static bool BeforeGrowUpdate(Growup growup)
		{
			pendingName = null;
			pendingNameAuthor = null;
			pendingFollow = null;
			Character component = ((Component)growup).GetComponent<Character>();
			if (!IsTameBaby(component))
			{
				return true;
			}
			ZNetView val = CharacterView.Invoke(component);
			if (!val.IsOwner())
			{
				return true;
			}
			ZDO zDO = val.GetZDO();
			if (IsKept(zDO))
			{
				PinAge(zDO);
				return false;
			}
			if (Plugin.CarryOverOnGrowUp.Value)
			{
				pendingName = zDO.GetString(ZDOVars.s_tamedName, "");
				pendingNameAuthor = zDO.GetString(ZDOVars.s_tamedNameAuthor, "");
				pendingFollow = zDO.GetString(ZDOVars.s_follow, "");
			}
			return true;
		}

		internal static void AfterGrowUpdate()
		{
			pendingName = null;
			pendingNameAuthor = null;
			pendingFollow = null;
		}

		internal static void OnTameableAwake(Tameable tameable)
		{
			if (pendingName == null)
			{
				return;
			}
			ZNetView component = ((Component)tameable).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner())
			{
				ZDO zDO = component.GetZDO();
				if (pendingName.Length > 0)
				{
					zDO.Set(ZDOVars.s_tamedName, pendingName);
					zDO.Set(ZDOVars.s_tamedNameAuthor, pendingNameAuthor);
				}
				if (pendingFollow.Length > 0)
				{
					zDO.Set(ZDOVars.s_follow, pendingFollow);
				}
				pendingName = null;
			}
		}

		internal static string AddHoverLines(Character character, string hoverText)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (hoverText.Length == 0 || !IsTameBaby(character) || (Object)(object)((Component)character).GetComponent<Growup>() == (Object)null)
			{
				return hoverText;
			}
			bool flag = IsKept(CharacterView.Invoke(character).GetZDO());
			if (flag)
			{
				if (GrowthLine.IsMatch(hoverText))
				{
					hoverText = GrowthLine.Replace(hoverText, "Staying a baby");
				}
				else
				{
					int num = hoverText.IndexOf('\n');
					hoverText = ((num >= 0) ? (hoverText.Substring(0, num) + "\nStaying a baby" + hoverText.Substring(num)) : (hoverText + "\nStaying a baby"));
				}
			}
			return hoverText + "\n[<color=yellow><b>" + KeyText(Plugin.KeepBabyKey.Value) + "</b></color>] Keep Baby: " + (flag ? "On" : "Off");
		}

		internal static bool SwallowsInteract(Tameable tameable)
		{
			//IL_0005: 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)
			KeyboardShortcut value = Plugin.KeepBabyKey.Value;
			if (((KeyboardShortcut)(ref value)).IsPressed())
			{
				return IsTameBaby(((Component)tameable).GetComponent<Character>());
			}
			return false;
		}

		internal static bool IsHungryOverride(Tameable tameable)
		{
			if (!IsTameBaby(((Component)tameable).GetComponent<Character>()))
			{
				return false;
			}
			MonsterAI component = ((Component)tameable).GetComponent<MonsterAI>();
			if (!((Object)(object)component == (Object)null) && component.m_consumeItems != null)
			{
				return component.m_consumeItems.Count == 0;
			}
			return true;
		}

		internal static bool HandleCommand(Tameable tameable, ZDOID characterID, bool message)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((Component)tameable).GetComponent<MonsterAI>() != (Object)null)
			{
				return true;
			}
			Character component = ((Component)tameable).GetComponent<Character>();
			if (!IsTameBaby(component))
			{
				return true;
			}
			GameObject val = ZNetScene.instance.FindInstance(characterID);
			Player val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Player>() : null);
			ZNetView val3 = CharacterView.Invoke(component);
			if ((Object)(object)val2 == (Object)null || !val3.IsOwner())
			{
				return false;
			}
			ZDO zDO = val3.GetZDO();
			bool flag = zDO.GetString(ZDOVars.s_follow, "").Length == 0;
			zDO.Set(ZDOVars.s_follow, flag ? val2.GetPlayerName() : "");
			if (message)
			{
				((Character)val2).Message((MessageType)2, tameable.GetHoverName() + (flag ? " $hud_tamefollow" : " $hud_tamestay"), 0, (Sprite)null, false);
			}
			return false;
		}

		internal static void AfterAnimalUpdate(AnimalAI ai, float dt, bool updated)
		{
			if (!updated)
			{
				return;
			}
			Character component = ((Component)ai).GetComponent<Character>();
			if (!IsTameBaby(component))
			{
				return;
			}
			string text = CharacterView.Invoke(component).GetZDO().GetString(ZDOVars.s_follow, "");
			if (text.Length == 0)
			{
				return;
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (allPlayer.GetPlayerName() == text)
				{
					Follow((BaseAI)(object)ai, ((Component)allPlayer).gameObject, dt);
					break;
				}
			}
		}

		internal static bool BlocksDamage(Character character, HitData hit)
		{
			if (!Plugin.InvincibleBabies.Value || !IsTameBaby(character))
			{
				return false;
			}
			if (Plugin.ButcherKnifeKills.Value && hit != null)
			{
				Character attacker = hit.GetAttacker();
				Humanoid val = (Humanoid)(object)((attacker is Humanoid) ? attacker : null);
				if (val != null && ((Character)val).IsPlayer())
				{
					ItemData currentWeapon = val.GetCurrentWeapon();
					if (currentWeapon != null && currentWeapon.m_shared.m_tamedOnly)
					{
						return false;
					}
				}
			}
			return true;
		}

		private static string KeyText(KeyboardShortcut shortcut)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			string text = "";
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				text = text + KeyName(modifier) + " + ";
			}
			return text + KeyName(((KeyboardShortcut)(ref shortcut)).MainKey);
		}

		private unsafe static string KeyName(KeyCode key)
		{
			//IL_0000: 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_0024: Expected I4, but got Unknown
			switch (key - 303)
			{
			case 4:
			case 5:
				return "Alt";
			case 0:
			case 1:
				return "Shift";
			case 2:
			case 3:
				return "Ctrl";
			default:
				return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
			}
		}
	}
	internal static class BetterTamesCompat
	{
		internal const string BetterTamesGuid = "xtav1m.BetterTames";

		internal static void Install(Harmony harmony)
		{
			if (Chainloader.PluginInfos.ContainsKey("xtav1m.BetterTames"))
			{
				Type type = AccessTools.TypeByName("BetterTames.Scripts.Protection.Protection");
				if (type == null)
				{
					Plugin.Log.LogWarning((object)"Better Tames found, but not its Protection class; its Alt + E toggle may also fire on babies");
					return;
				}
				Patch(harmony, type, "Tick", "Tick_Prefix");
				Patch(harmony, type, "AddPrompt", "AddPrompt_Prefix");
			}
		}

		private static void Patch(Harmony harmony, Type type, string method, string prefix)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)("Better Tames method " + method + " not found, skipping"));
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(BetterTamesCompat), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static bool Tick_Prefix()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				return !Babies.IsTameBaby(Babies.Hovered(localPlayer));
			}
			return true;
		}

		private static bool AddPrompt_Prefix(Character character, string hoverText, ref string __result)
		{
			if (!Babies.IsTameBaby(character))
			{
				return true;
			}
			__result = hoverText;
			return false;
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static void ZNetScene_Awake_Postfix(ZNetScene __instance)
		{
			Babies.SetUpPrefabs(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Growup), "GrowUpdate")]
		private static bool Growup_GrowUpdate_Prefix(Growup __instance)
		{
			return Babies.BeforeGrowUpdate(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Growup), "GrowUpdate")]
		private static void Growup_GrowUpdate_Postfix()
		{
			Babies.AfterGrowUpdate();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Tameable), "Awake")]
		private static void Tameable_Awake_Postfix(Tameable __instance)
		{
			Babies.OnTameableAwake(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Tameable), "Interact")]
		private static bool Tameable_Interact_Prefix(Tameable __instance, ref bool __result)
		{
			if (!Babies.SwallowsInteract(__instance))
			{
				return true;
			}
			__result = true;
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Tameable), "RPC_Command")]
		private static bool Tameable_RPC_Command_Prefix(Tameable __instance, ZDOID characterID, bool message)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Babies.HandleCommand(__instance, characterID, message);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(AnimalAI), "UpdateAI")]
		private static void AnimalAI_UpdateAI_Postfix(AnimalAI __instance, float dt, bool __result)
		{
			Babies.AfterAnimalUpdate(__instance, dt, __result);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Tameable), "IsHungry")]
		private static void Tameable_IsHungry_Postfix(Tameable __instance, ref bool __result)
		{
			if (__result && Babies.IsHungryOverride(__instance))
			{
				__result = false;
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyPatch(typeof(Character), "GetHoverText")]
		private static void Character_GetHoverText_Postfix(Character __instance, ref string __result)
		{
			__result = Babies.AddHoverLines(__instance, __result);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static bool Character_RPC_Damage_Prefix(Character __instance, HitData hit)
		{
			return !Babies.BlocksDamage(__instance, hit);
		}
	}
	[BepInPlugin("taylor.babytames", "Baby Tames", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "taylor.babytames";

		public const string Name = "Baby Tames";

		public const string Version = "1.0.1";

		private const string GeneralSection = "1 - General";

		private const string GrowthSection = "2 - Growth";

		private const string ProtectionSection = "3 - Protection";

		internal const string DefaultBabies = "Boar_piggy, Wolf_cub, Chicken, Lox_Calf, Asksvin_hatchling, Moose_calf";

		internal static ManualLogSource Log;

		internal static ConfigEntry<string> BabyPrefabs;

		internal static ConfigEntry<bool> CommandableBabies;

		internal static ConfigEntry<KeyboardShortcut> KeepBabyKey;

		internal static ConfigEntry<bool> CarryOverOnGrowUp;

		internal static ConfigEntry<bool> InvincibleBabies;

		internal static ConfigEntry<bool> ButcherKnifeKills;

		internal static readonly HashSet<int> BabyHashes = new HashSet<int>();

		private Harmony harmony;

		private void Awake()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BabyPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("1 - General", "Baby Prefabs", "Boar_piggy, Wolf_cub, Chicken, Lox_Calf, Asksvin_hatchling, Moose_calf", "Comma-separated prefab names (case sensitive) of the baby creatures this mod applies to. Changes to this list take effect after a restart.");
			CommandableBabies = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Babies Follow", true, "Pressing E on a tamed baby makes it follow you or stay, like a grown tame. Better Tames' Command Any Tame setting can also turn this on.");
			KeepBabyKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("2 - Growth", "Keep Baby Key", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Switch the tame baby you are looking at between growing up and staying a baby.");
			CarryOverOnGrowUp = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Growth", "Keep Name And Follow On Grow Up", true, "A baby that grows up keeps the name you gave it, and keeps following you if it was.");
			InvincibleBabies = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Protection", "Invincible Babies", true, "Tamed babies take no damage.");
			ButcherKnifeKills = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Protection", "Butcher Knife Still Kills", true, "A butcher knife can still hurt an invincible baby.");
			string[] array = BabyPrefabs.Value.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					BabyHashes.Add(StringExtensionMethods.GetStableHashCode(text));
				}
			}
			harmony = new Harmony("taylor.babytames");
			harmony.PatchAll();
			BetterTamesCompat.Install(harmony);
		}

		private void Update()
		{
			Babies.Tick();
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
}