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";
}
}