Decompiled source of AbsentBreed v1.0.0

AbsentBreed.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")]
[assembly: AssemblyCompany("AbsentBreed")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim Plugin")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+41963fcec691b0b4b9bb842977e7b5196bd07614")]
[assembly: AssemblyProduct("AbsentBreed")]
[assembly: AssemblyTitle("AbsentBreed")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace AbsentBreed
{
	public class BreedCatchupPatch
	{
		[HarmonyPatch(typeof(MonsterAI))]
		public class MonsterAIUpdate_Patch
		{
			private static MethodBase TargetMethod()
			{
				return typeof(MonsterAI).GetMethod("UpdateConsumeItem", BindingFlags.Instance | BindingFlags.NonPublic);
			}

			private static void Prefix(MonsterAI __instance, ref float ___m_consumeSearchInterval, ref float ___m_consumeSearchRange)
			{
				BreedCatchupData tameableData = TamedTimingData.GetTameableData(__instance);
				if (tameableData != null && tameableData.State == CatchupState.CatchingUp)
				{
					tameableData.ConsumeSearchInterval = ___m_consumeSearchInterval;
					tameableData.ConsumeSearchRange = ___m_consumeSearchRange;
					___m_consumeSearchInterval = 0f;
					___m_consumeSearchRange *= Constants.CONSUMABLE_SEARCH_RADIUS_MULTIPLIER;
				}
			}

			private static void Postfix(MonsterAI __instance, ref float ___m_consumeSearchInterval, ref float ___m_consumeSearchRange, ref ItemDrop ___m_consumeTarget, ref Action<ItemDrop> ___m_onConsumedItem)
			{
				BreedCatchupData tameableData = TamedTimingData.GetTameableData(__instance);
				if (tameableData != null && tameableData.State == CatchupState.CatchingUp && (Object)(object)___m_consumeTarget != (Object)null)
				{
					Plugin.Log("MonsterAI insta-consumed food as part of speed-breeding");
					___m_consumeTarget.RemoveOne();
					if (___m_onConsumedItem != null)
					{
						___m_onConsumedItem(___m_consumeTarget);
					}
					___m_consumeTarget = null;
					___m_consumeSearchInterval = tameableData.ConsumeSearchInterval;
					___m_consumeSearchRange = tameableData.ConsumeSearchRange;
				}
			}
		}

		[HarmonyPatch(typeof(Character))]
		public class CharacterUpdateWalking_Patch
		{
		}

		[HarmonyPatch(typeof(Tameable))]
		public class TameableTamingUpdate_Patch
		{
			private static MethodBase TargetMethod()
			{
				return typeof(Tameable).GetMethod("TamingUpdate", BindingFlags.Instance | BindingFlags.NonPublic);
			}

			private static void Postfix(ZNetView ___m_nview, Character ___m_character)
			{
				if (___m_character.IsTamed())
				{
					if (___m_nview.IsValid() && ___m_nview.IsOwner())
					{
						TamedTimingData.SetLastCheckedOnTime(___m_nview.GetZDO());
					}
					else
					{
						Plugin.Log("Tamed animal, but m_nview was not valid!");
					}
				}
			}
		}

		[HarmonyPatch(typeof(Tameable))]
		public class TameableAwake_Patch
		{
			private static MethodBase TargetMethod()
			{
				return typeof(Tameable).GetMethod("Awake", BindingFlags.Instance | BindingFlags.NonPublic);
			}

			private static void Postfix(Tameable __instance, Character ___m_character, MonsterAI ___m_monsterAI, ZNetView ___m_nview)
			{
				if (!___m_character.IsTamed())
				{
					return;
				}
				if (___m_nview.IsValid() && ___m_nview.IsOwner())
				{
					if (Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<Procreation>()))
					{
						TamedTimingData.RegisterTameable(__instance, ___m_character, ___m_monsterAI, ___m_nview.GetZDO());
					}
				}
				else
				{
					Plugin.Log("Would have registered a tamed animal, but we are not the owner");
				}
			}
		}

		[HarmonyPatch(typeof(Procreation))]
		public class ProcreationAwake_Patch
		{
			private static MethodBase TargetMethod()
			{
				return typeof(Procreation).GetMethod("Awake", BindingFlags.Instance | BindingFlags.NonPublic);
			}

			private static void Postfix(Procreation __instance, Tameable ___m_tameable, Character ___m_character)
			{
				if (___m_character.IsTamed())
				{
					TamedTimingData.RegisterProcreation(__instance, ___m_tameable);
				}
			}
		}

		[HarmonyPatch(typeof(Procreation))]
		public class ProcreationProcreate_Patch
		{
			private static MethodBase TargetMethod()
			{
				return typeof(Procreation).GetMethod("Procreate", BindingFlags.Instance | BindingFlags.NonPublic);
			}

			private static void Postfix(Procreation __instance, ref float ___m_totalCheckRange, ref float ___m_partnerCheckRange, ref float ___m_pregnancyDuration, ref Tameable ___m_tameable, ref float ___m_updateInterval)
			{
				BreedCatchupData tameableData = TamedTimingData.GetTameableData(___m_tameable);
				if (tameableData != null && tameableData.State == CatchupState.NormalBehavior && tameableData.RemainingCatchupTicks > 0)
				{
					Plugin.Log("Tamed animal " + ((object)(ZDOID)(ref tameableData.ID)).ToString() + " is speed-breeding for " + new TimeSpan(tameableData.RemainingCatchupTicks).TotalSeconds + " secs");
					tameableData.State = CatchupState.CatchingUp;
					tameableData.SetSpeedyProcreateValues(ref ___m_totalCheckRange, ref ___m_partnerCheckRange, ref ___m_pregnancyDuration, ref ___m_tameable, ___m_updateInterval);
					((MonoBehaviour)tameableData.ProcreationInstance).CancelInvoke("Procreate");
					((MonoBehaviour)tameableData.ProcreationInstance).InvokeRepeating("Procreate", 1f, 1f);
				}
				if (tameableData != null && tameableData.State == CatchupState.CatchingUp)
				{
					if (tameableData.RemainingCatchupTicks > 0)
					{
						tameableData.ConsumeCatchupTime(tameableData.ProcreateUpdateTicks);
						return;
					}
					Plugin.Log("Tamed animal " + ((object)(ZDOID)(ref tameableData.ID)).ToString() + " finished speed-breeding");
					tameableData.State = CatchupState.NormalBehavior;
					tameableData.ResetProcreateValues(ref ___m_totalCheckRange, ref ___m_partnerCheckRange, ref ___m_pregnancyDuration, ref ___m_tameable);
					((MonoBehaviour)__instance).CancelInvoke("Procreate");
					((MonoBehaviour)__instance).InvokeRepeating("Procreate", tameableData.BaseProcreateUpdateSeconds, tameableData.BaseProcreateUpdateSeconds);
					TamedTimingData.UnregisterTameable(___m_tameable);
				}
			}
		}

		[HarmonyPatch(typeof(Growup))]
		public class GrowupGrowUpdate_Patch
		{
			private static MethodBase TargetMethod()
			{
				return typeof(Growup).GetMethod("Start", BindingFlags.Instance | BindingFlags.NonPublic);
			}

			private static void Postfix(Growup __instance)
			{
				if (TamedTimingData.TryGetRemainingCatchupTime(out var time))
				{
					float num = __instance.m_growTime - (float)time;
					float num2 = Mathf.Clamp(num, 0f, __instance.m_growTime);
					Plugin.Log($"Offspring growtime (base:{__instance.m_growTime}, new:{num}, clamped:{num2})");
					__instance.m_growTime = num2;
				}
			}
		}

		[HarmonyPatch(typeof(EggGrow))]
		public class EggGrowGrowUpdate_Patch
		{
			private static MethodBase TargetMethod()
			{
				return typeof(EggGrow).GetMethod("Start", BindingFlags.Instance | BindingFlags.NonPublic);
			}

			private static void Postfix(EggGrow __instance)
			{
				if (TamedTimingData.TryGetRemainingCatchupTime(out var time))
				{
					float num = __instance.m_growTime - (float)time;
					float num2 = Mathf.Clamp(num, 0f, __instance.m_growTime);
					Plugin.Log($"Egg growtime (base:{__instance.m_growTime}, new:{num}, clamped:{num2})");
					__instance.m_growTime = num2;
				}
			}
		}
	}
	public static class Constants
	{
		public static float CONSUMABLE_SEARCH_RADIUS_MULTIPLIER = 3f;

		public const float SPEEDY_PROCREATE_UPDATE_INTERVAL = 1f;

		public const float BREED_PARTNER_RANGE_MULTIPLIER = 10f;

		public const float CROWDED_RANGE_MULTIPLIER = 0.5f;
	}
	[BepInPlugin("AbsentBreed", "AbsentBreed", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony harmony = new Harmony("untamedprogress.ValheimMod");

		public static Plugin Instance;

		public static Queue<string> _printQueue = new Queue<string>();

		private void Awake()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin AbsentBreed is loaded!");
			harmony.PatchAll();
			Instance = this;
			Flush();
		}

		public static void Log(string message)
		{
			_printQueue.Enqueue(message);
			if ((Object)(object)Instance != (Object)null)
			{
				Instance.Flush();
			}
		}

		public void Flush()
		{
			while (_printQueue.Count > 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)_printQueue.Dequeue());
			}
		}
	}
	public static class TameableExtensions
	{
		private static Dictionary<Tameable, ZDOID> _tameableIDMap = new Dictionary<Tameable, ZDOID>();

		public static ZDOID GetZDOID(this Tameable tameable)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return tameable.GetZDO().m_uid;
		}

		public static ZDO GetZDO(this Tameable tameable)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			FieldInfo[] fields = ((object)tameable).GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
			FieldInfo[] array = fields;
			foreach (FieldInfo fieldInfo in array)
			{
				Plugin.Log("field: " + fieldInfo.Name);
			}
			FieldInfo fieldInfo2 = fields.FirstOrDefault((FieldInfo f) => string.Equals(f.Name, "m_nview", StringComparison.OrdinalIgnoreCase));
			if (fieldInfo2 != null)
			{
				Plugin.Log("Field info non-null");
				return ((ZNetView)fieldInfo2.GetValue(tameable)).GetZDO();
			}
			throw new InvalidOperationException("Failed to access m_nview using reflection.");
		}
	}
	public enum CatchupState
	{
		CatchingUp,
		NormalBehavior
	}
	public struct ProcreateStats
	{
		public float ProcreateUpdateBaseInterval;

		public float FullPenCheckRange;

		public float BreedPartnerCheckRange;

		public float PregnancyDuration;

		public float FedDuration;
	}
	public class BreedCatchupData
	{
		public Procreation ProcreationInstance;

		public Tameable TameableInstance;

		public Character CharacterInstance;

		public ZDOID ID;

		public float ConsumeSearchInterval;

		public float ConsumeSearchRange;

		public long RemainingCatchupTicks;

		public ProcreateStats NormalProcreationValues { get; set; }

		public long ProcreateUpdateTicks => TimeSpan.FromSeconds(NormalProcreationValues.ProcreateUpdateBaseInterval).Ticks;

		public float BaseProcreateUpdateSeconds => (float)new TimeSpan(ProcreateUpdateTicks).TotalSeconds;

		public CatchupState State { get; set; }

		public void ConsumeCatchupTime(long consumedTicks)
		{
			RemainingCatchupTicks -= consumedTicks;
		}

		public void SetSpeedyProcreateValues(ref float totalCheckRange, ref float partnerCheckRange, ref float pregnancyDuration, ref Tameable tameable, float baseUpdateInterval)
		{
			NormalProcreationValues = new ProcreateStats
			{
				FullPenCheckRange = totalCheckRange,
				BreedPartnerCheckRange = partnerCheckRange,
				PregnancyDuration = pregnancyDuration,
				FedDuration = tameable.m_fedDuration,
				ProcreateUpdateBaseInterval = baseUpdateInterval
			};
			float num = baseUpdateInterval / 1f;
			totalCheckRange *= 0.5f;
			partnerCheckRange *= 10f;
			pregnancyDuration /= num;
			Tameable obj = tameable;
			obj.m_fedDuration /= num;
		}

		public void ResetProcreateValues(ref float totalCheckRange, ref float partnerCheckRange, ref float pregnancyDuration, ref Tameable tameable)
		{
			totalCheckRange = NormalProcreationValues.FullPenCheckRange;
			partnerCheckRange = NormalProcreationValues.BreedPartnerCheckRange;
			pregnancyDuration = NormalProcreationValues.PregnancyDuration;
			tameable.m_fedDuration = NormalProcreationValues.FedDuration;
		}
	}
	public class TamedTimingData
	{
		public const string TIMESTAMP_KEY = "breedmod_last_check";

		private static Dictionary<Tameable, BreedCatchupData> TameableDataMap = new Dictionary<Tameable, BreedCatchupData>();

		private static Dictionary<Tameable, Procreation> TameableProcreationMap = new Dictionary<Tameable, Procreation>();

		private static Dictionary<Tameable, MonsterAI> TameableMonsterMap = new Dictionary<Tameable, MonsterAI>();

		private static Dictionary<MonsterAI, Tameable> MonsterTameableMap = new Dictionary<MonsterAI, Tameable>();

		public static BreedCatchupData GetTameableData(Tameable tameable)
		{
			if (TameableDataMap.TryGetValue(tameable, out var value))
			{
				return value;
			}
			return null;
		}

		public static BreedCatchupData GetTameableData(MonsterAI monsterAi)
		{
			if (MonsterTameableMap.TryGetValue(monsterAi, out var value))
			{
				return GetTameableData(value);
			}
			return null;
		}

		public static void RegisterTameable(Tameable tameable, Character character, MonsterAI monsterAI, ZDO zdo)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			long remainingCatchupTicks = ZNet.instance.GetTime().Ticks - GetLastCheckedOnTime(zdo);
			TameableDataMap[tameable] = new BreedCatchupData
			{
				ID = zdo.m_uid,
				TameableInstance = tameable,
				RemainingCatchupTicks = remainingCatchupTicks,
				State = CatchupState.NormalBehavior,
				CharacterInstance = character
			};
			TameableMonsterMap[tameable] = monsterAI;
			MonsterTameableMap[monsterAI] = tameable;
			AfterRegistration(tameable);
		}

		public static void RegisterProcreation(Procreation procreation, Tameable tameable)
		{
			TameableProcreationMap[tameable] = procreation;
			AfterRegistration(tameable);
		}

		public static void AfterRegistration(Tameable tameable)
		{
			if (TameableProcreationMap.TryGetValue(tameable, out var value) && TameableDataMap.TryGetValue(tameable, out var value2))
			{
				Plugin.Log("After successful tameable registration; invoking procreation once");
				value2.ProcreationInstance = value;
				((MonoBehaviour)value2.ProcreationInstance).Invoke("Procreate", 1f);
			}
		}

		public static void UnregisterTameable(Tameable tameable)
		{
			MonsterTameableMap.Remove(TameableMonsterMap[tameable]);
			TameableDataMap.Remove(tameable);
			TameableMonsterMap.Remove(tameable);
		}

		private static long GetLastCheckedOnTime(ZDO zdo)
		{
			long ticks = ZNet.instance.GetTime().Ticks;
			return zdo.GetLong("breedmod_last_check", ticks);
		}

		public static void SetLastCheckedOnTime(ZDO zdo)
		{
			long ticks = ZNet.instance.GetTime().Ticks;
			zdo.Set("breedmod_last_check", ticks);
		}

		public static bool TryGetRemainingCatchupTime(out double time)
		{
			if (TameableDataMap?.Values == null || !TameableDataMap.Values.Any())
			{
				time = 0.0;
				return false;
			}
			BreedCatchupData breedCatchupData = TameableDataMap.Values.OrderByDescending((BreedCatchupData d) => d?.RemainingCatchupTicks ?? 0).FirstOrDefault();
			if (breedCatchupData == null)
			{
				time = 0.0;
				return false;
			}
			time = new TimeSpan(breedCatchupData.RemainingCatchupTicks).TotalSeconds;
			return true;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "AbsentBreed";

		public const string PLUGIN_NAME = "AbsentBreed";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}