Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TameCraft v1.1.3
BepInEx\plugins\TameCraft\TameCraft.dll
Decompiled 2 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TameCraft.Common; using TameCraft.Configuration; using TameCraft.Core; using TameCraft.Subsystems.Affection; using TameCraft.Subsystems.Breeding; using TameCraft.Subsystems.Genetics; using TameCraft.Subsystems.Offspring; using TameCraft.Subsystems.Pregnancy; using TameCraft.Subsystems.Taming; 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("momos3939")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("A configurable Valheim 1.0 animal husbandry overhaul focused on making taming, affection, breeding, pregnancy, and offspring growth faster and more convenient while preserving vanilla behavior when disabled.")] [assembly: AssemblyFileVersion("1.1.3.0")] [assembly: AssemblyInformationalVersion("1.1.3")] [assembly: AssemblyProduct("TameCraft")] [assembly: AssemblyTitle("TameCraft")] [assembly: AssemblyVersion("1.1.3.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 TameCraft { [BepInPlugin("com.tamecraft.valheim", "TameCraft", "1.1.3")] public class TameCraftPlugin : BaseUnityPlugin { public const string PluginGUID = "com.tamecraft.valheim"; public const string PluginName = "TameCraft"; public const string PluginVersion = "1.1.3"; private Harmony? _harmony; public static TameCraftPlugin? Instance { get; private set; } private void Awake() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown Instance = this; ModConfig.Initialize(((BaseUnityPlugin)this).Config); SubsystemLogger.Initialize(((BaseUnityPlugin)this).Logger, () => ModConfig.DebugLogging?.Value ?? false); if (!ModConfig.Enabled.Value) { SubsystemLogger.LogInfo(Subsystem.Husbandry, "TameCraft v1.1.3 is disabled in configuration."); return; } _harmony = new Harmony("com.tamecraft.valheim"); _harmony.PatchAll(); SubsystemLogger.LogInfo(Subsystem.Husbandry, "TameCraft v1.1.3 initialized and patched successfully."); if (ModConfig.DebugLogging.Value) { SubsystemLogger.LogDebug(Subsystem.Husbandry, "Verbose debug logging is enabled."); } } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; SubsystemLogger.LogInfo(Subsystem.Husbandry, "TameCraft unloaded."); Instance = null; } } } namespace TameCraft.Subsystems.Taming { public static class TamingSubsystem { private static readonly HashSet<int> _configuredCreatureZdos = new HashSet<int>(); public static void ConfigureCreature(Tameable tameable, CreatureProfile profile) { if ((Object)(object)tameable == (Object)null || profile == null || !ModConfig.Enabled.Value || !ModConfig.Taming.Enabled.Value) { return; } string prefabName = profile.PrefabName; if (ModConfig.Taming.Advanced.TryGetTamingEnabledOverride(prefabName, out var value) && !value) { return; } ZNetView component = ((Component)tameable).GetComponent<ZNetView>(); int item = (((Object)(object)component != (Object)null && component.IsValid()) ? ((object)Unsafe.As<ZDOID, ZDOID>(ref component.GetZDO().m_uid)/*cast due to .constrained prefix*/).GetHashCode() : ((Object)tameable).GetInstanceID()); float orCreateTamingTime = VanillaBaselineCache.GetOrCreateTamingTime(prefabName, tameable.m_tamingTime); float num; if (ModConfig.Taming.Advanced.TryGetTameDurationOverride(prefabName, out var value2)) { num = value2; } else { float num2 = Mathf.Max(0.01f, ModConfig.Taming.TamingSpeedMultiplier.Value); num = orCreateTamingTime / num2; } tameable.m_tamingTime = num; if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner() && !tameable.IsTamed()) { ZDO zDO = component.GetZDO(); float num3 = zDO.GetFloat(ZDOVars.s_tameTimeLeft, -1f); if (num3 < 0f || num3 > num) { zDO.Set(ZDOVars.s_tameTimeLeft, num); } } float orCreateFedDuration = VanillaBaselineCache.GetOrCreateFedDuration(prefabName, tameable.m_fedDuration); float fedDuration; if (ModConfig.Taming.Advanced.TryGetFeedingIntervalOverride(prefabName, out var value3)) { fedDuration = value3; } else { float num4 = Mathf.Max(0.01f, ModConfig.Taming.FeedingIntervalMultiplier.Value); fedDuration = orCreateFedDuration / num4; } tameable.m_fedDuration = fedDuration; MonsterAI component2 = ((Component)tameable).GetComponent<MonsterAI>(); if ((Object)(object)component2 != (Object)null) { ConfigureAllowedFoods(component2, profile); } if (_configuredCreatureZdos.Add(item)) { bool value4; bool flag = (ModConfig.Taming.Advanced.TryGetAllowTamingWhileAlertedOverride(prefabName, out value4) ? value4 : ModConfig.Taming.AllowTamingWhileAlerted.Value); bool flag2 = ModConfig.Taming.Advanced.IsHungerIgnored(prefabName); SubsystemLogger.LogDebug(Subsystem.Taming, $"Configured {profile.DisplayName}: duration={tameable.m_tamingTime:F1}s, consumptionInterval={tameable.m_fedDuration:F1}s, alertTaming={flag}, ignoreHunger={flag2}"); } } public static void ConfigureAllowedFoods(MonsterAI monsterAI, CreatureProfile profile) { if ((Object)(object)ObjectDB.instance == (Object)null || (Object)(object)monsterAI == (Object)null) { return; } List<ItemDrop> orCreateNativeConsumeItems = VanillaBaselineCache.GetOrCreateNativeConsumeItems(profile.PrefabName, monsterAI.m_consumeItems); if (ModConfig.Taming.Advanced.TryGetAllowedFoodsOverride(profile.PrefabName, out List<string> foods) && foods != null && foods.Count > 0) { List<ItemDrop> list = new List<ItemDrop>(); foreach (string item in foods) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Trim()); if ((Object)(object)itemPrefab != (Object)null) { ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { list.Add(component); } } } if (list.Count > 0) { monsterAI.m_consumeItems = list; } } else if (orCreateNativeConsumeItems != null && orCreateNativeConsumeItems.Count > 0) { monsterAI.m_consumeItems = new List<ItemDrop>(orCreateNativeConsumeItems); } } public static void OnFeedAccepted(Tameable tameable, CreatureProfile profile, ItemDrop? item) { if (!((Object)(object)tameable == (Object)null) && profile != null && ModConfig.Enabled.Value && ModConfig.Taming.Enabled.Value && PersistenceSystem.TryGetState(((Component)tameable).GetComponent<Character>(), out CreatureState state) && state != null && state.IsOwner) { int value = ModConfig.Affection.AffectionPerFeed.Value; if (value > 0) { AffectionSubsystem.RecordFeeding(state, profile, item, value); } } } } } namespace TameCraft.Subsystems.Pregnancy { public static class PregnancySubsystem { public static float GetRemainingGestationSeconds(CreatureState? state) { if (state == null || !state.IsPregnant || state.PregnancyStart <= 0) { return 0f; } if ((Object)(object)ZNet.instance == (Object)null) { return 0f; } double num = ZNet.instance.GetTimeSeconds() - (double)state.PregnancyStart; double num2 = (double)state.PregnancyDuration - num; if (!(num2 > 0.0)) { return 0f; } return (float)num2; } public static void OnConceptionDetected(Procreation procreation, CreatureProfile profile) { if (!((Object)(object)procreation == (Object)null) && profile != null && PersistenceSystem.TryGetState(((Component)procreation).GetComponent<Character>(), out CreatureState state) && state != null && state.IsOwner) { string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; long pregnancyStart = (long)(((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : 0.0); float pregnancyDuration = procreation.m_pregnancyDuration; string text2 = ResolveMatingPartnerId(procreation, profile); state.IsPregnant = true; state.PregnancyStart = pregnancyStart; state.PregnancyDuration = pregnancyDuration; state.ParentId = text2; string text3 = (string.IsNullOrEmpty(text2) ? "<none/ambiguous>" : text2); SubsystemLogger.LogInfo(Subsystem.Pregnancy, $"{profile.DisplayName} <{text}> conception detected. Sire={text3} Duration={pregnancyDuration:0.#}s"); } } public static void OnBirthDetected(Procreation procreation, CreatureProfile profile) { if (!((Object)(object)procreation == (Object)null) && profile != null && PersistenceSystem.TryGetState(((Component)procreation).GetComponent<Character>(), out CreatureState state) && state != null && state.IsOwner) { string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; state.IsPregnant = false; state.PregnancyStart = 0L; state.PregnancyDuration = 0f; SubsystemLogger.LogInfo(Subsystem.Pregnancy, profile.DisplayName + " <" + text + "> birth detected. Native pregnancy completed."); } } public static void OnMotherDeath(Character character) { if (!((Object)(object)character == (Object)null) && CreatureRegistry.TryGetProfile(((Component)character).gameObject, out CreatureProfile profile) && profile != null && PersistenceSystem.TryGetState(character, out CreatureState state) && state != null && state.IsOwner && state.IsPregnant) { string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; state.IsPregnant = false; state.PregnancyStart = 0L; state.PregnancyDuration = 0f; state.ParentId = string.Empty; SubsystemLogger.LogInfo(Subsystem.Pregnancy, "Mother " + profile.DisplayName + " <" + text + "> died; pregnancy metadata cleared."); } } public static void SynchronizeState(Procreation procreation, CreatureProfile profile) { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)procreation == (Object)null || profile == null || !PersistenceSystem.TryGetState(((Component)procreation).GetComponent<Character>(), out CreatureState state) || state == null || !state.IsOwner) { return; } ZNetView component = ((Component)procreation).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return; } long num = component.GetZDO().GetLong(ZDOVars.s_pregnant, 0L); bool flag = num > 0; if (flag && !state.IsPregnant) { long num2 = (long)((double)new DateTime(num).Ticks / 10000000.0); if (num2 <= 0 && (Object)(object)ZNet.instance != (Object)null) { num2 = (long)ZNet.instance.GetTimeSeconds(); } state.IsPregnant = true; state.PregnancyStart = num2; state.PregnancyDuration = procreation.m_pregnancyDuration; SubsystemLogger.LogDebug(Subsystem.Pregnancy, $"{profile.DisplayName} <{state.ZDO?.m_uid}> state restored from native ZDO."); } else if (!flag && state.IsPregnant) { state.IsPregnant = false; state.PregnancyStart = 0L; state.PregnancyDuration = 0f; SubsystemLogger.LogDebug(Subsystem.Pregnancy, $"{profile.DisplayName} <{state.ZDO?.m_uid}> state cleared (native is not pregnant)."); } } public static void LogGestationProgress(Procreation procreation, CreatureProfile profile) { if (!((Object)(object)procreation == (Object)null) && profile != null && PersistenceSystem.TryGetState(((Component)procreation).GetComponent<Character>(), out CreatureState state) && state != null) { float remainingGestationSeconds = GetRemainingGestationSeconds(state); string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; string text2 = (string.IsNullOrEmpty(state.ParentId) ? "<none/ambiguous>" : state.ParentId); SubsystemLogger.LogDebug(Subsystem.Pregnancy, $"{profile.DisplayName} <{text}> gestation: {remainingGestationSeconds:0.0}s / {state.PregnancyDuration:0.0}s remaining (Sire: {text2})"); } } public static string ResolveMatingPartnerId(Procreation procreation, CreatureProfile profile) { //IL_0018: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)procreation == (Object)null || profile == null) { return string.Empty; } Vector3 position = ((Component)procreation).transform.position; float partnerCheckRange = procreation.m_partnerCheckRange; List<Character> list = new List<Character>(); List<BaseAI> baseAIInstances = BaseAI.BaseAIInstances; if (baseAIInstances != null) { for (int i = 0; i < baseAIInstances.Count; i++) { BaseAI val = baseAIInstances[i]; if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)(object)((Component)procreation).gameObject || !((Object)((Component)val).gameObject).name.StartsWith(profile.PrefabName, StringComparison.OrdinalIgnoreCase) || Vector3.Distance(position, ((Component)val).transform.position) > partnerCheckRange) { continue; } Procreation component = ((Component)val).GetComponent<Procreation>(); if (!((Object)(object)component != (Object)null) || component.ReadyForProcreation()) { Character component2 = ((Component)val).GetComponent<Character>(); if ((Object)(object)component2 != (Object)null && !component2.IsDead()) { list.Add(component2); } } } } if (list.Count == 1) { ZNetView component3 = ((Component)list[0]).GetComponent<ZNetView>(); if ((Object)(object)component3 != (Object)null && component3.IsValid()) { return ((object)Unsafe.As<ZDOID, ZDOID>(ref component3.GetZDO()?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? string.Empty; } } if (list.Count > 1) { SubsystemLogger.LogDebug(Subsystem.Pregnancy, $"Ambiguous sire detection for {profile.DisplayName}: {list.Count} eligible partners within {partnerCheckRange}m. Leaving ParentId empty to prevent incorrect lineage."); } return string.Empty; } } } namespace TameCraft.Subsystems.Offspring { public class JuvenileSnapshot { public string ZdoId { get; set; } = string.Empty; public int Generation { get; set; } public int Affection { get; set; } public string MotherId { get; set; } = string.Empty; public string ParentId { get; set; } = string.Empty; public int GeneticStarLevel { get; set; } public int GeneticGeneration { get; set; } public string GeneticTraits { get; set; } = string.Empty; public CreatureProfile? Profile { get; set; } } public static class OffspringContext { [ThreadStatic] public static Character? ActiveMother; [ThreadStatic] public static Character? CapturedNewborn; [ThreadStatic] public static CreatureState? ActiveMaturingJuvenile; [ThreadStatic] public static JuvenileSnapshot? ActiveJuvenileSnapshot; [ThreadStatic] public static Character? CapturedAdult; public static bool IsInBirthOrMaturityTransition { get { if (!((Object)(object)ActiveMother != (Object)null) && ActiveMaturingJuvenile == null) { return ActiveJuvenileSnapshot != null; } return true; } } } public static class OffspringSubsystem { public static void ConfigureGrowup(Growup growup) { if ((Object)(object)growup == (Object)null || !ModConfig.Enabled.Value || !ModConfig.Offspring.Enabled.Value) { return; } string cleanPrefabName = VanillaBaselineCache.GetCleanPrefabName(((Component)growup).gameObject); if (!ModConfig.Offspring.Advanced.TryGetOffspringEnabledOverride(cleanPrefabName, out var value) || value) { float orCreateGrowTime = VanillaBaselineCache.GetOrCreateGrowTime(cleanPrefabName, growup.m_growTime); float num; if (ModConfig.Offspring.Advanced.TryGetGrowTimeOverride(cleanPrefabName, out var value2)) { num = value2; } else { float num2 = Mathf.Max(0.01f, ModConfig.Offspring.GrowthSpeedMultiplier.Value); num = orCreateGrowTime / num2; } growup.m_growTime = num; SubsystemLogger.LogDebug(Subsystem.Offspring, $"[Offspring] Growth configured for {cleanPrefabName}: growTime={num:F1}s (Vanilla: {orCreateGrowTime:F1}s)"); } } public static float GetRemainingGrowthSeconds(Growup growup) { if ((Object)(object)growup == (Object)null) { return 0f; } BaseAI component = ((Component)growup).GetComponent<BaseAI>(); if ((Object)(object)component == (Object)null) { return 0f; } TimeSpan timeSinceSpawned = component.GetTimeSinceSpawned(); return Mathf.Max(0f, growup.m_growTime - (float)timeSinceSpawned.TotalSeconds); } public static void OnOffspringBorn(Character offspringChar, Character motherChar) { if ((Object)(object)offspringChar == (Object)null || (Object)(object)motherChar == (Object)null) { return; } PersistenceSystem.TryGetState(motherChar, out CreatureState state); if (CreatureRegistry.TryGetProfile(offspringChar, out CreatureProfile profile) && profile != null && profile.Enabled) { CreatureState creatureState = PersistenceSystem.OnCreatureLoaded(offspringChar, profile); if (creatureState != null && creatureState.IsValid) { string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref state?.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? string.Empty; string text2 = state?.ParentId ?? string.Empty; int num = state?.Generation ?? 0; int num2 = num + 1; float value; float num3 = (ModConfig.Offspring.Advanced.TryGetInheritAffectionPercentageOverride(profile.PrefabName, out value) ? value : ModConfig.Offspring.InheritAffectionPercentage.Value); int value2 = ModConfig.Affection.MaxAffection.Value; int num4 = state?.Affection ?? 0; int num5 = Mathf.Clamp(Mathf.RoundToInt((float)num4 * num3), 0, value2); creatureState.StateVersion = 1; creatureState.MotherId = text; creatureState.ParentId = text2; creatureState.Generation = num2; creatureState.Affection = num5; creatureState.IsPregnant = false; creatureState.PregnancyStart = 0L; creatureState.PregnancyDuration = 0f; GeneticsSubsystem.ApplyGeneticsToNewborn(offspringChar, motherChar); string text3 = ((object)Unsafe.As<ZDOID, ZDOID>(ref creatureState.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; SubsystemLogger.LogInfo(Subsystem.Offspring, "[Offspring] Birth detected: " + profile.DisplayName + " offspring <" + text3 + "> born from mother <" + text + ">."); SubsystemLogger.LogInfo(Subsystem.Offspring, string.Format("[Offspring] Generation assigned: {0} (Mother: {1}), Sire: <{2}>.", num2, num, string.IsNullOrEmpty(text2) ? "<none/ambiguous>" : text2)); SubsystemLogger.LogInfo(Subsystem.Offspring, $"[Offspring] Affection inherited: {num5}/{value2} (Mother: {num4}, {num3:P0})."); } } } public static void OnAdulthoodReached(JuvenileSnapshot juvenileSnapshot, Character adultChar) { if (juvenileSnapshot == null || (Object)(object)adultChar == (Object)null) { return; } CreatureProfile profile = juvenileSnapshot.Profile; if (profile == null) { CreatureRegistry.TryGetProfile(adultChar, out profile); } if (profile != null && profile.Enabled) { CreatureState creatureState = PersistenceSystem.OnCreatureLoaded(adultChar, profile); if (creatureState != null && creatureState.IsValid) { creatureState.StateVersion = 1; creatureState.Generation = juvenileSnapshot.Generation; creatureState.Affection = juvenileSnapshot.Affection; creatureState.MotherId = juvenileSnapshot.MotherId; creatureState.ParentId = juvenileSnapshot.ParentId; GeneticsSubsystem.TransferGeneticsToAdult(juvenileSnapshot, adultChar); creatureState.IsPregnant = false; creatureState.PregnancyStart = 0L; creatureState.PregnancyDuration = 0f; string zdoId = juvenileSnapshot.ZdoId; string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref creatureState.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; SubsystemLogger.LogInfo(Subsystem.Offspring, "[Offspring] Maturity detected: " + profile.DisplayName + " juvenile <" + zdoId + "> matured into adult <" + text + ">."); SubsystemLogger.LogInfo(Subsystem.Offspring, $"[Offspring] Metadata transferred: Generation: {creatureState.Generation}, Affection: {creatureState.Affection}, Mother: <{creatureState.MotherId}>, Sire: <{creatureState.ParentId}>."); } } } public static void OnAdulthoodReached(CreatureState juvenileState, Character adultChar) { if (juvenileState != null && !((Object)(object)adultChar == (Object)null)) { OnAdulthoodReached(new JuvenileSnapshot { ZdoId = (((object)Unsafe.As<ZDOID, ZDOID>(ref juvenileState.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"), Generation = juvenileState.Generation, Affection = juvenileState.Affection, MotherId = juvenileState.MotherId, ParentId = juvenileState.ParentId, GeneticStarLevel = juvenileState.GeneticStarLevel, GeneticGeneration = juvenileState.GeneticGeneration, GeneticTraits = juvenileState.GeneticTraits, Profile = juvenileState.Profile }, adultChar); } } public static Character? ResolveFallbackNewborn(Procreation procreation, CreatureProfile profile) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)procreation == (Object)null || profile == null) { return null; } Vector3 position = ((Component)procreation).transform.position; string offspringPrefabName = profile.OffspringPrefabName; if (string.IsNullOrEmpty(offspringPrefabName)) { return null; } float num = 2.5f; int num2 = 0; Character val = null; List<Character> allCharacters = Character.GetAllCharacters(); for (int i = 0; i < allCharacters.Count; i++) { Character val2 = allCharacters[i]; if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null) && val2.IsTamed() && string.Equals(Utils.GetPrefabName(((Component)val2).gameObject), offspringPrefabName, StringComparison.OrdinalIgnoreCase) && !(Vector3.Distance(position, ((Component)val2).transform.position) > num)) { BaseAI component = ((Component)val2).GetComponent<BaseAI>(); if ((Object)(object)component != (Object)null && component.GetTimeSinceSpawned().TotalSeconds < 3.0) { val = val2; num2++; } } } if (num2 == 1 && (Object)(object)val != (Object)null) { ZNetView component2 = ((Component)val).GetComponent<ZNetView>(); string text = ((component2 == null) ? null : ((object)Unsafe.As<ZDOID, ZDOID>(ref component2.GetZDO()?.m_uid)/*cast due to .constrained prefix*/).ToString()) ?? "unknown"; object[] obj = new object[4] { profile.DisplayName, null, null, null }; ZNetView component3 = ((Component)procreation).GetComponent<ZNetView>(); obj[1] = ((component3 == null) ? ((ZDOID?)null) : component3.GetZDO()?.m_uid); obj[2] = ((Object)val).name; obj[3] = text; SubsystemLogger.LogWarning(Subsystem.Offspring, string.Format("[Offspring] WARNING: fallback spatial offspring resolution used for mother {0} <{1}>. Found unique newborn {2} <{3}>.", obj)); return val; } if (num2 > 1) { SubsystemLogger.LogWarning(Subsystem.Offspring, $"[Offspring] WARNING: Rejected ambiguous spatial offspring resolution: {num2} matching newborns found within {num:F1}m of mother. Lineage not assigned to prevent corruption."); } return null; } public static Character? ResolveFallbackAdult(Growup growup, CreatureProfile profile) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)growup == (Object)null || profile == null) { return null; } Vector3 position = ((Component)growup).transform.position; string prefabName = profile.PrefabName; float num = 2f; int num2 = 0; Character val = null; List<Character> allCharacters = Character.GetAllCharacters(); for (int i = 0; i < allCharacters.Count; i++) { Character val2 = allCharacters[i]; if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((Component)val2).gameObject == (Object)null) && val2.IsTamed() && string.Equals(Utils.GetPrefabName(((Component)val2).gameObject), prefabName, StringComparison.OrdinalIgnoreCase) && !(Vector3.Distance(position, ((Component)val2).transform.position) > num)) { BaseAI component = ((Component)val2).GetComponent<BaseAI>(); if ((Object)(object)component != (Object)null && component.GetTimeSinceSpawned().TotalSeconds < 3.0) { val = val2; num2++; } } } if (num2 == 1 && (Object)(object)val != (Object)null) { ZNetView component2 = ((Component)val).GetComponent<ZNetView>(); string text = ((component2 == null) ? null : ((object)Unsafe.As<ZDOID, ZDOID>(ref component2.GetZDO()?.m_uid)/*cast due to .constrained prefix*/).ToString()) ?? "unknown"; SubsystemLogger.LogWarning(Subsystem.Offspring, "[Offspring] WARNING: fallback spatial adult resolution used for maturing juvenile. Found unique adult " + ((Object)val).name + " <" + text + ">."); return val; } if (num2 > 1) { SubsystemLogger.LogWarning(Subsystem.Offspring, $"[Offspring] WARNING: Rejected ambiguous spatial adult resolution: {num2} matching adults found within {num:F1}m of transformation point. Lineage not transferred to prevent corruption."); } return null; } } } namespace TameCraft.Subsystems.Genetics { public class GeneticsState { public string MotherZdoId { get; set; } = string.Empty; public int MotherStar { get; set; } public string SireZdoId { get; set; } = string.Empty; public int SireStar { get; set; } public StarInheritanceMode InheritanceMode { get; set; } public float MotherWeight { get; set; } public float SireWeight { get; set; } public float InheritanceRoll { get; set; } public string SelectedParent { get; set; } = string.Empty; public int InheritedStar { get; set; } public bool MutationEnabled { get; set; } public float MutationChance { get; set; } public float MutationRoll { get; set; } public bool MutationTriggered { get; set; } public int MutationDelta { get; set; } public int MutatedStar { get; set; } public int FinalGeneticStar { get; set; } public int FinalVisibleLevel { get; set; } public int Generation { get; set; } public string GeneticTraits { get; set; } = string.Empty; } public static class GeneticsSubsystem { private static readonly Random _random = new Random(); public static bool TryParseZDOID(string? str, out ZDOID zdoid) { //IL_0001: 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_0040: 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) zdoid = ZDOID.None; if (string.IsNullOrEmpty(str)) { return false; } string[] array = str.Split(':'); if (array.Length == 2 && long.TryParse(array[0], out var result) && uint.TryParse(array[1], out var result2)) { zdoid = new ZDOID(result, result2); return true; } return false; } public static void ResolveParentStars(CreatureState motherState, IGeneticsConfig config, out int motherStar, out int sireStar, out string sireId, out bool sireFallbackApplied) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (motherState == null) { throw new ArgumentNullException("motherState"); } if (config == null) { throw new ArgumentNullException("config"); } sireFallbackApplied = false; sireId = motherState.ParentId; if (!motherState.HasGeneticData) { InitializeLegacyCreature(motherState); } motherStar = motherState.GeneticStarLevel; bool flag = false; sireStar = 0; if (!string.IsNullOrEmpty(sireId) && TryParseZDOID(sireId, out var zdoid)) { ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(zdoid) : null); if (val != null) { int num = val.GetInt(ZDOKeys.GeneticStarLevelHash, -1); if (num >= 0) { sireStar = num; flag = true; } else { int num2 = val.GetInt(ZDOVars.s_level, 1); sireStar = Math.Max(0, num2 - 1); flag = true; } } } if (!flag) { sireFallbackApplied = true; GeneticsParentResolutionFallback parentResolutionFallback = config.ParentResolutionFallback; SubsystemLogger.LogWarning(Subsystem.Genetics, $"WARNING: Sire <{sireId}> could not be resolved; using configured fallback {parentResolutionFallback}."); switch (parentResolutionFallback) { case GeneticsParentResolutionFallback.FallbackToMother: sireStar = motherStar; break; case GeneticsParentResolutionFallback.FallbackToLowerParent: sireStar = Math.Min(motherStar, 0); break; case GeneticsParentResolutionFallback.AbortInheritance: sireStar = 0; break; default: sireStar = motherStar; break; } } } public static GeneticsState DetermineOffspringGenetics(CreatureState motherState, int motherStar, int sireStar, string sireId, bool sireFallbackApplied, IGeneticsConfig config) { GeneticsState geneticsState = new GeneticsState { MotherZdoId = (((object)Unsafe.As<ZDOID, ZDOID>(ref motherState.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"), MotherStar = motherStar, SireZdoId = (string.IsNullOrEmpty(sireId) ? "none" : sireId), SireStar = sireStar, InheritanceMode = config.StarInheritanceMode, Generation = motherState.GeneticGeneration + 1, GeneticTraits = string.Empty }; if (sireFallbackApplied && config.ParentResolutionFallback == GeneticsParentResolutionFallback.AbortInheritance) { geneticsState.SelectedParent = "AbortInheritance"; geneticsState.InheritedStar = 0; geneticsState.InheritanceRoll = 0f; geneticsState.FinalGeneticStar = 0; geneticsState.FinalVisibleLevel = 1; return geneticsState; } config.GetNormalizedWeights(out var normMotherWeight, out var normSireWeight); geneticsState.MotherWeight = normMotherWeight; geneticsState.SireWeight = normSireWeight; switch (config.StarInheritanceMode) { case StarInheritanceMode.Vanilla: geneticsState.InheritedStar = motherStar; geneticsState.SelectedParent = "Mother"; geneticsState.InheritanceRoll = 0f; break; case StarInheritanceMode.HigherParent: if (motherStar >= sireStar) { geneticsState.InheritedStar = motherStar; geneticsState.SelectedParent = "Mother"; } else { geneticsState.InheritedStar = sireStar; geneticsState.SelectedParent = "Sire"; } geneticsState.InheritanceRoll = 0f; break; case StarInheritanceMode.LowerParent: if (motherStar <= sireStar) { geneticsState.InheritedStar = motherStar; geneticsState.SelectedParent = "Mother"; } else { geneticsState.InheritedStar = sireStar; geneticsState.SelectedParent = "Sire"; } geneticsState.InheritanceRoll = 0f; break; default: { if (motherStar == sireStar) { geneticsState.InheritedStar = motherStar; geneticsState.SelectedParent = "BothEqual"; geneticsState.InheritanceRoll = 50f; break; } float num = (geneticsState.InheritanceRoll = (float)(_random.NextDouble() * 100.0)); if (num < normMotherWeight) { geneticsState.InheritedStar = motherStar; geneticsState.SelectedParent = "Mother"; } else { geneticsState.InheritedStar = sireStar; geneticsState.SelectedParent = "Sire"; } break; } } geneticsState.MutationEnabled = config.MutationEnabled; geneticsState.MutationChance = Mathf.Clamp01(config.MutationChance); geneticsState.MutatedStar = geneticsState.InheritedStar; if (geneticsState.MutationEnabled && geneticsState.MutationChance > 0f) { float num3 = (geneticsState.MutationRoll = (float)_random.NextDouble()); if (num3 <= geneticsState.MutationChance) { geneticsState.MutationTriggered = true; if (config.MutationDirection == MutationDirection.Bidirectional) { int num5 = (geneticsState.MutationDelta = ((_random.NextDouble() < 0.5) ? 1 : (-1))); geneticsState.MutatedStar = geneticsState.InheritedStar + num5; } else { geneticsState.MutationDelta = 1; geneticsState.MutatedStar = geneticsState.InheritedStar + 1; } } } int val = 2; int val2 = Math.Min(config.MutationMaxStar, val); val2 = Math.Max(0, val2); geneticsState.FinalGeneticStar = Math.Clamp(geneticsState.MutatedStar, 0, val2); geneticsState.FinalVisibleLevel = geneticsState.FinalGeneticStar + 1; return geneticsState; } public static void ApplyGeneticsToNewborn(Character newbornChar, Character motherChar) { if ((Object)(object)newbornChar == (Object)null || (Object)(object)motherChar == (Object)null) { return; } if ((!CreatureState.TryGet(newbornChar, out CreatureState state) || state == null) && CreatureRegistry.TryGetProfile(newbornChar, out CreatureProfile profile) && profile != null) { state = CreatureState.GetOrCreate(newbornChar, profile); } if (state == null || !state.IsValid || !state.IsOwner || state.HasGeneticData) { return; } if ((!CreatureState.TryGet(motherChar, out CreatureState state2) || state2 == null) && CreatureRegistry.TryGetProfile(motherChar, out CreatureProfile profile2) && profile2 != null) { state2 = CreatureState.GetOrCreate(motherChar, profile2); } if (state2 == null || !state2.IsValid) { return; } IGeneticsConfig genetics = ModConfig.Genetics; if (!genetics.Enabled) { int geneticStarLevel = Math.Max(0, newbornChar.GetLevel() - 1); state.GeneticStarLevel = geneticStarLevel; state.GeneticGeneration = state2.GeneticGeneration + 1; state.GeneticTraits = string.Empty; return; } ResolveParentStars(state2, genetics, out int motherStar, out int sireStar, out string sireId, out bool sireFallbackApplied); GeneticsState geneticsState = DetermineOffspringGenetics(state2, motherStar, sireStar, sireId, sireFallbackApplied, genetics); state.GeneticStarLevel = geneticsState.FinalGeneticStar; state.GeneticGeneration = geneticsState.Generation; state.GeneticTraits = geneticsState.GeneticTraits; newbornChar.SetLevel(geneticsState.FinalVisibleLevel); if (ModConfig.DebugLogging.Value) { SubsystemLogger.LogInfo(Subsystem.Genetics, $"Parents:\nMother=<{geneticsState.MotherZdoId}>\nMotherStar={geneticsState.MotherStar}\nSire=<{geneticsState.SireZdoId}>\nSireStar={geneticsState.SireStar}"); SubsystemLogger.LogInfo(Subsystem.Genetics, $"Inheritance:\nMode={geneticsState.InheritanceMode}\nMotherWeight={geneticsState.MotherWeight:0.##}\nSireWeight={geneticsState.SireWeight:0.##}\nRoll={geneticsState.InheritanceRoll:F2}\nSelected={geneticsState.SelectedParent}\nResultStar={geneticsState.InheritedStar}"); SubsystemLogger.LogInfo(Subsystem.Genetics, $"Mutation:\nEnabled={geneticsState.MutationEnabled.ToString().ToLowerInvariant()}\nResultStar={geneticsState.FinalGeneticStar}"); SubsystemLogger.LogInfo(Subsystem.Genetics, $"Final:\nGeneticStar={geneticsState.FinalGeneticStar}\nVisibleLevel={geneticsState.FinalVisibleLevel}"); } } public static void TransferGeneticsToAdult(JuvenileSnapshot snapshot, Character adultChar) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if (snapshot == null || (Object)(object)adultChar == (Object)null || !CreatureRegistry.TryGetProfile(adultChar, out CreatureProfile profile) || profile == null) { return; } CreatureState orCreate = CreatureState.GetOrCreate(adultChar, profile); if (orCreate.IsValid && orCreate.IsOwner) { orCreate.GeneticStarLevel = snapshot.GeneticStarLevel; orCreate.GeneticGeneration = snapshot.GeneticGeneration; orCreate.GeneticTraits = snapshot.GeneticTraits; adultChar.SetLevel(snapshot.GeneticStarLevel + 1); if (ModConfig.DebugLogging.Value) { SubsystemLogger.LogInfo(Subsystem.Genetics, $"Adulthood: Transferred {snapshot.GeneticStarLevel}★ (Gen {snapshot.GeneticGeneration}) to adult {profile.DisplayName} <{adultChar.GetZDOID()}>"); } } } public static void TransferGeneticsToAdult(CreatureState juvenileState, Character adultChar) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if (juvenileState == null || (Object)(object)adultChar == (Object)null || !CreatureRegistry.TryGetProfile(adultChar, out CreatureProfile profile) || profile == null) { return; } CreatureState orCreate = CreatureState.GetOrCreate(adultChar, profile); if (orCreate.IsValid && orCreate.IsOwner) { orCreate.GeneticStarLevel = juvenileState.GeneticStarLevel; orCreate.GeneticGeneration = juvenileState.GeneticGeneration; orCreate.GeneticTraits = juvenileState.GeneticTraits; adultChar.SetLevel(juvenileState.GeneticStarLevel + 1); if (ModConfig.DebugLogging.Value) { SubsystemLogger.LogInfo(Subsystem.Genetics, $"Adulthood: Transferred {juvenileState.GeneticStarLevel}★ (Gen {juvenileState.GeneticGeneration}) to adult {profile.DisplayName} <{adultChar.GetZDOID()}>"); } } } public static void InitializeLegacyCreature(CreatureState state) { if (state != null && state.IsValid && state.IsOwner) { int num = (state.GeneticStarLevel = Math.Max(0, state.Character.GetLevel() - 1)); state.GeneticGeneration = state.Generation; state.GeneticTraits = string.Empty; SubsystemLogger.LogInfo(Subsystem.Genetics, $"Legacy creature initialized from vanilla star level: {num}★"); } } } } namespace TameCraft.Subsystems.Breeding { public static class BreedingSubsystem { private static readonly HashSet<int> _configuredProcreationInstances = new HashSet<int>(); private static readonly Func<Procreation, bool>? s_isPregnant = AccessTools.MethodDelegate<Func<Procreation, bool>>(AccessTools.Method(typeof(Procreation), "IsPregnant", (Type[])null, (Type[])null), (object)null, true); public static void ConfigureCreature(Procreation procreation, CreatureProfile profile) { if ((Object)(object)procreation == (Object)null || profile == null || !ModConfig.Enabled.Value || !ModConfig.Breeding.Enabled.Value) { return; } string prefabName = profile.PrefabName; if (!ModConfig.Breeding.Advanced.TryGetBreedingEnabledOverride(prefabName, out var value) || value) { float orCreateCheckInterval = VanillaBaselineCache.GetOrCreateCheckInterval(prefabName, procreation.m_updateInterval); if (ModConfig.Breeding.Advanced.TryGetUpdateIntervalOverride(prefabName, out var value2)) { procreation.m_updateInterval = value2; } else { float num = Mathf.Max(0.01f, ModConfig.Breeding.BreedingCheckSpeedMultiplier.Value); procreation.m_updateInterval = orCreateCheckInterval / num; } float orCreatePregnancyDuration = VanillaBaselineCache.GetOrCreatePregnancyDuration(prefabName, procreation.m_pregnancyDuration); if (ModConfig.Breeding.Advanced.TryGetPregnancyDurationOverride(prefabName, out var value3)) { procreation.m_pregnancyDuration = value3; } else if (orCreatePregnancyDuration > 0f) { float num2 = Mathf.Max(0.01f, ModConfig.Breeding.PregnancySpeedMultiplier.Value); procreation.m_pregnancyDuration = orCreatePregnancyDuration / num2; } else { procreation.m_pregnancyDuration = 0f; } if (ModConfig.Breeding.Advanced.TryGetPregnancyChanceOverride(prefabName, out var value4)) { procreation.m_pregnancyChance = value4; } else { procreation.m_pregnancyChance = ModConfig.Breeding.PregnancyChance.Value; } if (ModConfig.Breeding.Advanced.TryGetRequiredLovePointsOverride(prefabName, out var value5)) { procreation.m_requiredLovePoints = value5; } else { procreation.m_requiredLovePoints = ModConfig.Breeding.RequiredLovePoints.Value; } float orCreatePartnerCheckRange = VanillaBaselineCache.GetOrCreatePartnerCheckRange(prefabName, procreation.m_partnerCheckRange); if (ModConfig.Breeding.Advanced.TryGetPartnerCheckRangeOverride(prefabName, out var value6)) { procreation.m_partnerCheckRange = value6; } else { float num3 = Mathf.Max(0.01f, ModConfig.Breeding.PartnerCheckRangeMultiplier.Value); procreation.m_partnerCheckRange = orCreatePartnerCheckRange * num3; } float orCreateEnclosureCheckRange = VanillaBaselineCache.GetOrCreateEnclosureCheckRange(prefabName, procreation.m_totalCheckRange); if (ModConfig.Breeding.Advanced.TryGetEnclosureCheckRangeOverride(prefabName, out var value7)) { procreation.m_totalCheckRange = value7; } else { float num4 = Mathf.Max(0.01f, ModConfig.Breeding.EnclosureCheckRangeMultiplier.Value); procreation.m_totalCheckRange = orCreateEnclosureCheckRange * num4; } int orCreateCreatureLimit = VanillaBaselineCache.GetOrCreateCreatureLimit(prefabName, procreation.m_maxCreatures); if (ModConfig.Breeding.Advanced.TryGetCreatureLimitOverride(prefabName, out var value8)) { procreation.m_maxCreatures = value8; } else { float num5 = Mathf.Max(0.01f, ModConfig.Breeding.CreatureLimitMultiplier.Value); procreation.m_maxCreatures = Mathf.Max(1, Mathf.RoundToInt((float)orCreateCreatureLimit * num5)); } int instanceID = ((Object)procreation).GetInstanceID(); if (_configuredProcreationInstances.Add(instanceID)) { SubsystemLogger.LogDebug(Subsystem.Breeding, $"Configured {profile.DisplayName} Procreation: interval={procreation.m_updateInterval:F1}s, chance={procreation.m_pregnancyChance * 100f:0}%, lovePointsReq={procreation.m_requiredLovePoints}, gestation={procreation.m_pregnancyDuration:F1}s, partnerRange={procreation.m_partnerCheckRange:F1}m, enclosureRange={procreation.m_totalCheckRange:F1}m, limit={procreation.m_maxCreatures}"); } } } public static bool IsPregnant(Procreation procreation) { if ((Object)(object)procreation == (Object)null) { return false; } if (s_isPregnant != null) { return s_isPregnant(procreation); } ZNetView component = ((Component)procreation).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { return component.GetZDO().GetLong(ZDOVars.s_pregnant, 0L) > 0; } return false; } public static bool CheckBreedingEligibility(Procreation procreation, CreatureProfile profile) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)procreation == (Object)null || profile == null) { return true; } if (!PersistenceSystem.TryGetState(((Component)procreation).GetComponent<Character>(), out CreatureState state) || state == null) { return true; } string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; if (ModConfig.Breeding.RequireAffectionForBreeding.Value) { bool num = AffectionSubsystem.IsAffectionSufficientForBreeding(state, profile); int affection = state.Affection; int value = ModConfig.Breeding.BreedingAffectionThreshold.Value; if (!num) { SubsystemLogger.LogDebug(Subsystem.Breeding, $"{profile.DisplayName} <{text}> breeding blocked: Affection={affection}/{value} (Insufficient Affection)"); return false; } } if (ModConfig.DebugLogging.Value) { Vector3 position = ((Component)procreation).transform.position; Tameable component = ((Component)procreation).GetComponent<Tameable>(); BaseAI component2 = ((Component)procreation).GetComponent<BaseAI>(); bool flag = (Object)(object)component != (Object)null && !component.IsHungry(); bool flag2 = (Object)(object)component2 != (Object)null && component2.IsAlerted(); GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(profile.PrefabName) : null); int num2 = (((Object)(object)val != (Object)null) ? SpawnSystem.GetNrOfInstances(val, position, procreation.m_partnerCheckRange, false, false) : 0); int num3 = (((Object)(object)val != (Object)null) ? SpawnSystem.GetNrOfInstances(val, position, procreation.m_totalCheckRange, false, false) : 0); int value2 = ModConfig.Breeding.BreedingAffectionThreshold.Value; SubsystemLogger.LogDebug(Subsystem.Breeding, $"{profile.DisplayName} <{text}> check: Affection={state.Affection}/{value2}, Partner={num2 >= 2}, Fed={flag}, Alerted={flag2}, Population={num3}/{procreation.m_maxCreatures}"); } return true; } public static void OnProcreationCompleted(Procreation procreation, CreatureProfile profile, int oldLovePoints, bool wasPregnant) { if ((Object)(object)procreation == (Object)null || profile == null) { return; } ZNetView component = ((Component)procreation).GetComponent<ZNetView>(); string text = ((component == null) ? null : ((object)Unsafe.As<ZDOID, ZDOID>(ref component.GetZDO()?.m_uid)/*cast due to .constrained prefix*/).ToString()) ?? "unknown"; if (IsPregnant(procreation) && !wasPregnant) { SubsystemLogger.LogInfo(Subsystem.Breeding, profile.DisplayName + " <" + text + "> conceived successfully! Native pregnancy initiated."); return; } int lovePoints = procreation.GetLovePoints(); if (lovePoints > oldLovePoints) { SubsystemLogger.LogInfo(Subsystem.Breeding, $"{profile.DisplayName} <{text}> mated with partner; love points {oldLovePoints} -> {lovePoints}/{procreation.m_requiredLovePoints}."); } } } } namespace TameCraft.Subsystems.Affection { public static class AffectionSubsystem { public static AffectionTier GetTier(CreatureState state, CreatureProfile profile) { if (state == null || profile == null) { return AffectionTier.Distant; } int affection = state.Affection; if (affection < ModConfig.Affection.NeutralThreshold.Value) { return AffectionTier.Distant; } if (affection < ModConfig.Affection.BondedThreshold.Value) { return AffectionTier.Neutral; } if (affection < ModConfig.Affection.DevotedThreshold.Value) { return AffectionTier.Bonded; } return AffectionTier.Devoted; } public static bool IsAffectionSufficient(CreatureState state, int requiredPoints) { if (state == null) { return false; } return state.Affection >= requiredPoints; } public static bool IsAffectionSufficientForBreeding(CreatureState state, CreatureProfile profile) { if (state == null || profile == null) { return false; } return state.Affection >= ModConfig.Breeding.BreedingAffectionThreshold.Value; } public static void OnPetInteraction(Tameable tameable, CreatureProfile profile, Humanoid user) { if ((Object)(object)tameable == (Object)null || profile == null || (Object)(object)user == (Object)null || !ModConfig.Enabled.Value || !ModConfig.Affection.Enabled.Value || !ModConfig.Affection.PettingAffectionEnabled.Value || !PersistenceSystem.TryGetState(((Component)tameable).GetComponent<Character>(), out CreatureState state) || state == null) { return; } ZNetView component = ((Component)tameable).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { string hoverName = ((Character)user).GetHoverName(); if (component.IsOwner()) { RecordPetting(state, profile, hoverName); return; } component.InvokeRPC("TameCraft_Pet", new object[1] { hoverName }); } } public static void RecordPetting(CreatureState state, CreatureProfile profile, string playerName) { if (state == null || profile == null || !state.IsOwner || !ModConfig.Enabled.Value || !ModConfig.Affection.Enabled.Value || !ModConfig.Affection.PettingAffectionEnabled.Value) { return; } long num = (long)(((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTimeSeconds() : 0.0); float value = ModConfig.Affection.PettingCooldownSeconds.Value; long lastPetTime = state.LastPetTime; if (lastPetTime > 0 && (float)(num - lastPetTime) < value) { return; } state.LastPetTime = num; int value2 = ModConfig.Affection.AffectionPerPet.Value; if (value2 > 0) { int value3 = ModConfig.Affection.MaxAffection.Value; int affection = state.Affection; AffectionTier tier = GetTier(state, profile); state.Affection = Mathf.Clamp(affection + value2, 0, value3); AffectionTier tier2 = GetTier(state, profile); string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; SubsystemLogger.LogDebug(Subsystem.Affection, $"{profile.DisplayName} <{text}> petted by {playerName}; affection {affection} -> {state.Affection} (Tier: {tier2})"); if (tier2 > tier) { SubsystemLogger.LogInfo(Subsystem.Affection, $"{profile.DisplayName} <{text}> reached {tier2} affection tier ({state.Affection} points)!"); } } } public static void RecordFeeding(CreatureState state, CreatureProfile profile, ItemDrop? item, int gain) { if (state != null && profile != null && state.IsOwner) { int value = ModConfig.Affection.MaxAffection.Value; int affection = state.Affection; AffectionTier tier = GetTier(state, profile); state.Affection = Mathf.Clamp(affection + gain, 0, value); AffectionTier tier2 = GetTier(state, profile); string text = item?.m_itemData?.m_shared?.m_name ?? ((item != null) ? ((Object)item).name : null) ?? "Food"; string text2 = ((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; SubsystemLogger.LogDebug(Subsystem.Affection, $"{profile.DisplayName} <{text2}> fed with {text}; affection {affection} -> {state.Affection} (Tier: {tier2})"); if (tier2 > tier) { SubsystemLogger.LogInfo(Subsystem.Affection, $"{profile.DisplayName} <{text2}> reached {tier2} affection tier ({state.Affection} points)!"); } } } } public enum AffectionTier { Distant, Neutral, Bonded, Devoted } } namespace TameCraft.Patches { [HarmonyPatch(typeof(Tameable), "Interact")] public static class TameableInteractPatch { [HarmonyPostfix] public static void Postfix(Tameable __instance, Humanoid user, bool hold, bool alt, bool __result) { if (__result && !((Object)(object)__instance == (Object)null) && !((Object)(object)user == (Object)null) && !hold && !alt && ModConfig.Enabled.Value && ModConfig.Affection.Enabled.Value && ModConfig.Affection.PettingAffectionEnabled.Value && __instance.IsTamed() && !__instance.HaveRider() && CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) && profile != null && profile.Enabled && (!((Object)(object)((Component)__instance).GetComponent<Growup>() != (Object)null) || (ModConfig.Offspring.Advanced.TryGetJuvenilePettingAllowedOverride(profile.PrefabName, out var value) ? value : ModConfig.Offspring.JuvenilePettingAllowed.Value))) { AffectionSubsystem.OnPetInteraction(__instance, profile, user); } } } [HarmonyPatch(typeof(Character), "Awake")] public static class CharacterLifecyclePatches { [HarmonyPostfix] public static void AwakePostfix(Character __instance) { if (ModConfig.Enabled.Value && !((Object)(object)__instance == (Object)null)) { CreatureProfile profile; if ((Object)(object)OffspringContext.ActiveMother != (Object)null && (Object)(object)OffspringContext.CapturedNewborn == (Object)null) { OffspringContext.CapturedNewborn = __instance; } else if (OffspringContext.ActiveMaturingJuvenile != null && (Object)(object)OffspringContext.CapturedAdult == (Object)null) { OffspringContext.CapturedAdult = __instance; } else if (CreatureRegistry.TryGetProfile(__instance, out profile) && profile != null && profile.Enabled) { PersistenceSystem.OnCreatureLoaded(__instance, profile); } } } } [HarmonyPatch(typeof(Character), "OnDeath")] public static class CharacterDeathPatch { [HarmonyPostfix] public static void Postfix(Character __instance) { if (!((Object)(object)__instance == (Object)null) && ModConfig.Enabled.Value) { PregnancySubsystem.OnMotherDeath(__instance); } } } [HarmonyPatch(typeof(Growup), "Start")] public static class GrowupStartPatch { [HarmonyPostfix] public static void Postfix(Growup __instance) { if (ModConfig.Enabled.Value && ModConfig.Offspring.Enabled.Value && !((Object)(object)__instance == (Object)null)) { OffspringSubsystem.ConfigureGrowup(__instance); } } } [HarmonyPatch(typeof(Growup), "GrowUpdate")] public static class GrowupGrowUpdatePatch { [HarmonyPrefix] public static bool Prefix(Growup __instance, out (bool willMature, JuvenileSnapshot? juvenileSnapshot) __state) { __state = (willMature: false, juvenileSnapshot: null); if ((Object)(object)__instance == (Object)null) { return true; } if (!ModConfig.Enabled.Value || !ModConfig.Offspring.Enabled.Value) { return true; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return true; } BaseAI component2 = ((Component)__instance).GetComponent<BaseAI>(); if ((Object)(object)component2 == (Object)null) { return true; } TimeSpan timeSinceSpawned = component2.GetTimeSinceSpawned(); if (timeSinceSpawned.TotalSeconds > (double)__instance.m_growTime) { PersistenceSystem.TryGetState(((Component)__instance).GetComponent<Character>(), out CreatureState state); JuvenileSnapshot juvenileSnapshot = null; if (state != null && state.IsValid) { juvenileSnapshot = new JuvenileSnapshot { ZdoId = (((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"), Generation = state.Generation, Affection = state.Affection, MotherId = state.MotherId, ParentId = state.ParentId, GeneticStarLevel = state.GeneticStarLevel, GeneticGeneration = state.GeneticGeneration, GeneticTraits = state.GeneticTraits, Profile = state.Profile }; } __state = (willMature: true, juvenileSnapshot: juvenileSnapshot); OffspringContext.ActiveMaturingJuvenile = state; OffspringContext.ActiveJuvenileSnapshot = juvenileSnapshot; OffspringContext.CapturedAdult = null; string arg = juvenileSnapshot?.ZdoId ?? ((object)Unsafe.As<ZDOID, ZDOID>(ref state?.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; SubsystemLogger.LogDebug(Subsystem.Offspring, $"[Offspring] Juvenile <{arg}> ready to mature ({timeSinceSpawned.TotalSeconds:F1}s > {__instance.m_growTime:F1}s). Arming adult capture..."); } else if (ModConfig.DebugLogging.Value) { float num = Mathf.Max(0f, __instance.m_growTime - (float)timeSinceSpawned.TotalSeconds); string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref component.GetZDO()?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; SubsystemLogger.LogDebug(Subsystem.Offspring, $"[Offspring] Growth update: {((Object)((Component)__instance).gameObject).name} <{text}>: {num:F1}s / {__instance.m_growTime:F1}s remaining."); } return true; } [HarmonyPostfix] public static void Postfix(Growup __instance, (bool willMature, JuvenileSnapshot? juvenileSnapshot) __state) { try { if (ModConfig.Enabled.Value && ModConfig.Offspring.Enabled.Value && __state.willMature && __state.juvenileSnapshot != null) { Character val = OffspringContext.CapturedAdult; if ((Object)(object)val == (Object)null && (Object)(object)__instance != (Object)null && __state.juvenileSnapshot.Profile != null) { val = OffspringSubsystem.ResolveFallbackAdult(__instance, __state.juvenileSnapshot.Profile); } if ((Object)(object)val != (Object)null) { OffspringSubsystem.OnAdulthoodReached(__state.juvenileSnapshot, val); } } } finally { OffspringContext.ActiveMaturingJuvenile = null; OffspringContext.ActiveJuvenileSnapshot = null; OffspringContext.CapturedAdult = null; } } } [HarmonyPatch(typeof(Procreation), "Awake")] public static class ProcreationAwakePatch { [HarmonyPrefix] public static void Prefix(Procreation __instance) { if (!((Object)(object)__instance == (Object)null) && ModConfig.Enabled.Value && ModConfig.Breeding.Enabled.Value && CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) && profile != null && profile.Enabled) { BreedingSubsystem.ConfigureCreature(__instance, profile); } } } [HarmonyPatch(typeof(Procreation), "Procreate")] public static class ProcreationProcreatePatch { [HarmonyPrefix] public static bool Prefix(Procreation __instance, out (int lovePoints, bool isPregnant) __state) { __state = (lovePoints: 0, isPregnant: false); if ((Object)(object)__instance == (Object)null) { return true; } __state = (lovePoints: __instance.GetLovePoints(), isPregnant: BreedingSubsystem.IsPregnant(__instance)); if (!ModConfig.Enabled.Value || !ModConfig.Breeding.Enabled.Value) { return true; } if (!CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) || profile == null || !profile.Enabled) { return true; } BreedingSubsystem.ConfigureCreature(__instance, profile); PregnancySubsystem.SynchronizeState(__instance, profile); if (BreedingSubsystem.IsPregnant(__instance)) { if (ModConfig.DebugLogging.Value) { PregnancySubsystem.LogGestationProgress(__instance, profile); } OffspringContext.ActiveMother = ((Component)__instance).GetComponent<Character>(); OffspringContext.CapturedNewborn = null; return true; } if (!BreedingSubsystem.CheckBreedingEligibility(__instance, profile)) { return false; } return true; } [HarmonyPostfix] public static void Postfix(Procreation __instance, (int lovePoints, bool isPregnant) __state) { try { if ((Object)(object)__instance == (Object)null || !ModConfig.Enabled.Value || !ModConfig.Breeding.Enabled.Value || !CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) || profile == null || !profile.Enabled) { return; } bool item = __state.isPregnant; bool flag = BreedingSubsystem.IsPregnant(__instance); if (item && !flag) { PregnancySubsystem.OnBirthDetected(__instance, profile); Character val = OffspringContext.CapturedNewborn; if ((Object)(object)val == (Object)null) { val = OffspringSubsystem.ResolveFallbackNewborn(__instance, profile); } if ((Object)(object)val != (Object)null) { Character component = ((Component)__instance).GetComponent<Character>(); OffspringSubsystem.OnOffspringBorn(val, component); } } else if (!item && flag) { PregnancySubsystem.OnConceptionDetected(__instance, profile); } else { BreedingSubsystem.OnProcreationCompleted(__instance, profile, __state.lovePoints, __state.isPregnant); } } finally { OffspringContext.ActiveMother = null; OffspringContext.CapturedNewborn = null; } } } [HarmonyPatch(typeof(Tameable), "Awake")] public static class TameableAwakePatch { [HarmonyPostfix] public static void Postfix(Tameable __instance) { if (!((Object)(object)__instance == (Object)null) && ModConfig.Enabled.Value && ModConfig.Taming.Enabled.Value && CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) && profile != null && profile.Enabled) { TamingSubsystem.ConfigureCreature(__instance, profile); } } } [HarmonyPatch(typeof(Tameable), "OnConsumedItem")] public static class TameableOnConsumedItemPatch { [HarmonyPostfix] public static void Postfix(Tameable __instance, ItemDrop item) { if (!((Object)(object)__instance == (Object)null) && ModConfig.Enabled.Value && ModConfig.Taming.Enabled.Value && CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) && profile != null && profile.Enabled) { TamingSubsystem.OnFeedAccepted(__instance, profile, item); } } } [HarmonyPatch(typeof(Tameable), "TamingUpdate")] public static class TameableTamingUpdatePatch { private static readonly Action<Tameable, float>? s_decreaseRemainingTime = AccessTools.MethodDelegate<Action<Tameable, float>>(AccessTools.Method(typeof(Tameable), "DecreaseRemainingTime", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<Tameable, float>? s_getRemainingTime = AccessTools.MethodDelegate<Func<Tameable, float>>(AccessTools.Method(typeof(Tameable), "GetRemainingTime", (Type[])null, (Type[])null), (object)null, true); private static readonly Action<Tameable>? s_tame = AccessTools.MethodDelegate<Action<Tameable>>(AccessTools.Method(typeof(Tameable), "Tame", (Type[])null, (Type[])null), (object)null, true); [HarmonyPrefix] public static bool Prefix(Tameable __instance) { //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || !ModConfig.Taming.Enabled.Value) { return true; } if (!CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) || profile == null || !profile.Enabled) { return true; } TamingSubsystem.ConfigureCreature(__instance, profile); bool value; bool flag = (ModConfig.Taming.Advanced.TryGetAllowTamingWhileAlertedOverride(profile.PrefabName, out value) ? value : ModConfig.Taming.AllowTamingWhileAlerted.Value); bool flag2 = ModConfig.Taming.Advanced.IsHungerIgnored(profile.PrefabName); if (!flag && !flag2) { return true; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return false; } if (__instance.IsTamed()) { return false; } if (__instance.IsHungry() && !flag2) { return false; } MonsterAI component2 = ((Component)__instance).GetComponent<MonsterAI>(); if (!flag && (Object)(object)component2 != (Object)null && ((BaseAI)component2).IsAlerted()) { return false; } if (component2 != null) { component2.SetDespawnInDay(false); } if (component2 != null) { component2.SetEventCreature(false); } if (s_decreaseRemainingTime != null) { s_decreaseRemainingTime(__instance, 3f); } if (((s_getRemainingTime != null) ? s_getRemainingTime(__instance) : 0f) <= 0f) { if (s_tame != null) { s_tame(__instance); } SubsystemLogger.LogDebug(Subsystem.Taming, profile.DisplayName + " completed taming!"); } else { EffectList sootheEffect = __instance.m_sootheEffect; if (sootheEffect != null) { sootheEffect.Create(((Component)__instance).transform.position, ((Component)__instance).transform.rotation, (Transform)null, 1f, -1, default(ZDOID)); } } return false; } } [HarmonyPatch(typeof(MonsterAI), "UpdateAI")] public static class MonsterAIUpdateAIPatch { private static readonly Func<MonsterAI, Humanoid, float, bool>? s_updateConsumeItem = AccessTools.MethodDelegate<Func<MonsterAI, Humanoid, float, bool>>(AccessTools.Method(typeof(MonsterAI), "UpdateConsumeItem", (Type[])null, (Type[])null), (object)null, true); [HarmonyPostfix] public static void Postfix(MonsterAI __instance, float dt) { if ((Object)(object)__instance == (Object)null || !ModConfig.Enabled.Value || !ModConfig.Taming.Enabled.Value || !CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) || profile == null || !profile.Enabled || !(ModConfig.Taming.Advanced.TryGetAllowTamingWhileAlertedOverride(profile.PrefabName, out var value) ? value : ModConfig.Taming.AllowTamingWhileAlerted.Value) || !((BaseAI)__instance).IsAlerted()) { return; } Tameable component = ((Component)__instance).GetComponent<Tameable>(); if ((Object)(object)component != (Object)null && component.IsHungry()) { Humanoid component2 = ((Component)__instance).GetComponent<Humanoid>(); if ((Object)(object)component2 != (Object)null) { s_updateConsumeItem?.Invoke(__instance, component2, dt); } } } } [HarmonyPatch(typeof(Tameable), "Tame")] public static class TameableTamePatch { [HarmonyPostfix] public static void Postfix(Tameable __instance) { if (!((Object)(object)__instance == (Object)null) && CreatureRegistry.TryGetProfile(((Component)__instance).gameObject, out CreatureProfile profile) && profile != null && profile.Enabled) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); string text = ((component == null) ? null : ((object)Unsafe.As<ZDOID, ZDOID>(ref component.GetZDO()?.m_uid)/*cast due to .constrained prefix*/).ToString()) ?? "unknown"; SubsystemLogger.LogInfo(Subsystem.Taming, "Creature " + profile.DisplayName + " <" + text + "> became tamed."); } } } } namespace TameCraft.Core { public class CreatureProfile { public string PrefabName { get; } public string? OffspringPrefabName { get; } public string DisplayName { get; } public bool Enabled { get; set; } public bool IsMammalianProcreation { get; } public bool IsEggLayer { get; } public CreatureProfile(string prefabName, string displayName, bool enabled = true, bool isMammalianProcreation = true, bool isEggLayer = false, string? offspringPrefabName = null) { PrefabName = prefabName ?? throw new ArgumentNullException("prefabName"); DisplayName = displayName ?? throw new ArgumentNullException("displayName"); Enabled = enabled; IsMammalianProcreation = isMammalianProcreation; IsEggLayer = isEggLayer; OffspringPrefabName = offspringPrefabName; } public override string ToString() { return $"CreatureProfile[{PrefabName} ({DisplayName}), Enabled={Enabled}, Mammal={IsMammalianProcreation}, Egg={IsEggLayer}]"; } } public static class CreatureRegistry { private static readonly Dictionary<string, CreatureProfile> _profilesByName; private static readonly Dictionary<string, CreatureProfile> _profilesByOffspringName; static CreatureRegistry() { _profilesByName = new Dictionary<string, CreatureProfile>(StringComparer.OrdinalIgnoreCase); _profilesByOffspringName = new Dictionary<string, CreatureProfile>(StringComparer.OrdinalIgnoreCase); InitializeDefaultProfiles(); } private static void InitializeDefaultProfiles() { RegisterProfile(new CreatureProfile("Boar", "Boar", enabled: true, isMammalianProcreation: true, isEggLayer: false, "Boar_piggy")); RegisterProfile(new CreatureProfile("Wolf", "Wolf", enabled: true, isMammalianProcreation: true, isEggLayer: false, "Wolf_cub")); RegisterProfile(new CreatureProfile("Lox", "Lox", enabled: true, isMammalianProcreation: true, isEggLayer: false, "Lox_calf")); RegisterProfile(new CreatureProfile("Hen", "Hen", enabled: true, isMammalianProcreation: false, isEggLayer: true, "Chicken")); RegisterProfile(new CreatureProfile("Asksvin", "Asksvin", enabled: true, isMammalianProcreation: false, isEggLayer: true, "Asksvin_hatchling")); } public static void RegisterProfile(CreatureProfile profile) { if (profile == null) { throw new ArgumentNullException("profile"); } lock (_profilesByName) { _profilesByName[profile.PrefabName] = profile; if (!string.IsNullOrEmpty(profile.OffspringPrefabName)) { _profilesByOffspringName[profile.OffspringPrefabName] = profile; } } } public static bool TryGetProfileByOffspring(string? offspringPrefab, out CreatureProfile? profile) { profile = null; if (string.IsNullOrEmpty(offspringPrefab)) { return false; } lock (_profilesByName) { return _profilesByOffspringName.TryGetValue(offspringPrefab, out profile); } } public static bool TryGetProfile(string? prefabName, out CreatureProfile? profile) { profile = null; if (string.IsNullOrEmpty(prefabName)) { return false; } lock (_profilesByName) { if (_profilesByName.TryGetValue(prefabName, out profile)) { return true; } if (_profilesByOffspringName.TryGetValue(prefabName, out profile)) { return true; } profile = new CreatureProfile(prefabName, prefabName); _profilesByName[prefabName] = profile; return true; } } public static bool TryGetProfile(GameObject? gameObject, out CreatureProfile? profile) { profile = null; if ((Object)(object)gameObject == (Object)null) { return false; } return TryGetProfile(Utils.GetPrefabName(gameObject), out profile); } public static bool TryGetProfile(Character? character, out CreatureProfile? profile) { profile = null; if ((Object)(object)character == (Object)null) { return false; } return TryGetProfile(((Component)character).gameObject, out profile); } public static bool IsSupported(string? prefabName) { if (TryGetProfile(prefabName, out CreatureProfile profile) && profile != null) { return profile.Enabled; } return false; } public static bool IsSupported(GameObject? gameObject) { if (TryGetProfile(gameObject, out CreatureProfile profile) && profile != null) { return profile.Enabled; } return false; } public static bool IsSupported(Character? character) { if (TryGetProfile(character, out CreatureProfile profile) && profile != null) { return profile.Enabled; } return false; } } public class CreatureState { private static readonly ConditionalWeakTable<Character, CreatureState> _stateCache = new ConditionalWeakTable<Character, CreatureState>(); public Character Character { get; } public ZNetView? NetView { get; } public CreatureProfile Profile { get; } public ZDO? ZDO { get { if (!((Object)(object)NetView != (Object)null) || !NetView.IsValid()) { return null; } return NetView.GetZDO(); } } public bool IsValid { get { if ((Object)(object)NetView != (Object)null && NetView.IsValid()) { return ZDO != null; } return false; } } public bool IsOwner { get { if ((Object)(object)NetView != (Object)null && NetView.IsValid()) { return NetView.IsOwner(); } return false; } } public bool IsRpcRegistered { get; set; } public int StateVersion { get { ZDO? zDO = ZDO; if (zDO == null) { return 0; } return zDO.GetInt(ZDOKeys.StateVersionHash, 0); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.StateVersionHash, value, false); } } } } public int Affection { get { ZDO? zDO = ZDO; if (zDO == null) { return 0; } return zDO.GetInt(ZDOKeys.AffectionHash, 0); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.AffectionHash, value, false); } } } } public bool IsPregnant { get { ZDO? zDO = ZDO; if (zDO == null) { return false; } return zDO.GetBool(ZDOKeys.IsPregnantHash, false); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.IsPregnantHash, value); } } } } public long PregnancyStart { get { ZDO? zDO = ZDO; if (zDO == null) { return 0L; } return zDO.GetLong(ZDOKeys.PregnancyStartHash, 0L); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.PregnancyStartHash, value); } } } } public float PregnancyDuration { get { ZDO? zDO = ZDO; if (zDO == null) { return 0f; } return zDO.GetFloat(ZDOKeys.PregnancyDurationHash, 0f); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.PregnancyDurationHash, value); } } } } public string ParentId { get { ZDO? zDO = ZDO; return ((zDO != null) ? zDO.GetString(ZDOKeys.ParentIdHash, string.Empty) : null) ?? string.Empty; } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.ParentIdHash, value ?? string.Empty); } } } } public string MotherId { get { ZDO? zDO = ZDO; return ((zDO != null) ? zDO.GetString(ZDOKeys.MotherIdHash, string.Empty) : null) ?? string.Empty; } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.MotherIdHash, value ?? string.Empty); } } } } public int Generation { get { ZDO? zDO = ZDO; if (zDO == null) { return 0; } return zDO.GetInt(ZDOKeys.GenerationHash, 0); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.GenerationHash, value, false); } } } } public long LastPetTime { get { ZDO? zDO = ZDO; if (zDO == null) { return 0L; } return zDO.GetLong(ZDOKeys.LastPetTimeHash, 0L); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.LastPetTimeHash, value); } } } } public bool HasGeneticData { get { if (ZDO != null) { return ZDO.GetInt(ZDOKeys.GeneticStarLevelHash, -1) >= 0; } return false; } } public int GeneticStarLevel { get { ZDO? zDO = ZDO; int num = ((zDO != null) ? zDO.GetInt(ZDOKeys.GeneticStarLevelHash, -1) : (-1)); if (num >= 0) { return num; } return Math.Max(0, Character.GetLevel() - 1); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.GeneticStarLevelHash, value, false); } } } } public int GeneticGeneration { get { ZDO? zDO = ZDO; if (zDO == null) { return Generation; } return zDO.GetInt(ZDOKeys.GeneticGenerationHash, Generation); } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.GeneticGenerationHash, value, false); } } } } public string GeneticTraits { get { ZDO? zDO = ZDO; return ((zDO != null) ? zDO.GetString(ZDOKeys.GeneticTraitsHash, string.Empty) : null) ?? string.Empty; } set { if (IsOwner) { ZDO? zDO = ZDO; if (zDO != null) { zDO.Set(ZDOKeys.GeneticTraitsHash, value ?? string.Empty); } } } } public CreatureState(Character character, ZNetView? netView, CreatureProfile profile) { Character = character ?? throw new ArgumentNullException("character"); NetView = netView; Profile = profile ?? throw new ArgumentNullException("profile"); } public static bool TryGet(Character? character, out CreatureState? state) { state = null; if ((Object)(object)character == (Object)null) { return false; } return _stateCache.TryGetValue(character, out state); } public static CreatureState GetOrCreate(Character character, CreatureProfile profile) { if ((Object)(object)character == (Object)null) { throw new ArgumentNullException("character"); } if (_stateCache.TryGetValue(character, out CreatureState value)) { return value; } ZNetView component = ((Component)character).GetComponent<ZNetView>(); CreatureState creatureState = new CreatureState(character, component, profile); _stateCache.Add(character, creatureState); return creatureState; } } public static class PersistenceSystem { public const int CurrentStateVersion = 1; public static CreatureState? OnCreatureLoaded(Character character, CreatureProfile profile) { if ((Object)(object)character == (Object)null || profile == null) { return null; } CreatureState state = CreatureState.GetOrCreate(character, profile); if (!state.IsValid) { return state; } if (!state.IsRpcRegistered && (Object)(object)state.NetView != (Object)null && state.NetView.IsValid()) { try { state.NetView.Register<string>("TameCraft_Pet", (Action<long, string>)delegate(long sender, string playerName) { if (state.IsOwner) { AffectionSubsystem.RecordPetting(state, profile, playerName); } }); } catch (ArgumentException) { } state.IsRpcRegistered = true; } string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref state.ZDO?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"; if (state.StateVersion == 0) { if (state.IsOwner) { state.StateVersion = 1; state.Affection = 0; state.IsPregnant = false; state.PregnancyStart = 0L; state.PregnancyDuration = 0f; state.ParentId = string.Empty; state.Generation = 0; state.LastPetTime = 0L; SubsystemLogger.LogDebug(Subsystem.Persistence, $"Initialized {profile.DisplayName} <{text}> with state version {1}"); } } else { SubsystemLogger.LogDebug(Subsystem.Persistence, $"Loaded {profile.DisplayName} <{text}>: affection={state.Affection} lastPet={state.LastPetTime} pregnancy={state.IsPregnant} generation={state.Generation}"); } if (state.IsOwner && !state.HasGeneticData && !OffspringContext.IsInBirthOrMaturityTransition) { GeneticsSubsystem.InitializeLegacyCreature(state); } return state; } public static bool TryGetState(Character? character, out CreatureState? state) { state = null; if ((Object)(object)character == (Object)null) { return false; } if (CreatureState.TryGet(character, out state)) { return true; } if (CreatureRegistry.TryGetProfile(character, out CreatureProfile profile) && profile != null && profile.Enabled) { state = CreatureState.GetOrCreate(character, profile); return true; } return false; } } public static class VanillaBaselineCache { private static readonly object _lock = new object(); private static readonly Dictionary<string, float> _tamingTimes = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> _fedDurations = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> _checkIntervals = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> _pregnancyDurations = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> _growTimes = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> _partnerCheckRanges = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, float> _enclosureCheckRanges = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, int> _creatureLimits = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, List<ItemDrop>> _nativeConsumeItems = new Dictionary<string, List<ItemDrop>>(StringComparer.OrdinalIgnoreCase); public static string GetCleanPrefabName(GameObject? obj) { if ((Object)(object)obj == (Object)null) { return string.Empty; } return Utils.GetPrefabName(obj); } public static float GetOrCreateTamingTime(string prefabName, float originalValue) { if (string.IsNullOrEmpty(prefabName)) { if (!(originalValue > 0f)) { return 1800f; } return originalValue; } lock (_lock) { if (!_tamingTimes.TryGetValue(prefabName, out var value)) { value = ((originalValue > 0f) ? originalValue : 1800f); _tamingTimes[prefabName] = value; } return value; } } public static float GetOrCreateFedDuration(string prefabName, float originalValue) { if (string.IsNullOrEmpty(prefabName)) { if (!(originalValue > 0f)) { return 120f; } return originalValue; } lock (_lock) { if (!_fedDurations.TryGetValue(prefabName, out var value)) { value = ((originalValue > 0f) ? originalValue : 120f); _fedDurations[prefabName] = value; } return value; } } public static float GetOrCreateCheckInterval(string prefabName, float originalValue) { if (string.IsNullOrEmpty(prefabName)) { if (!(originalValue > 0f)) { return 30f; } return originalValue; } lock (_lock) { if (!_checkIntervals.TryGetValue(prefabName, out var value)) { value = ((originalValue > 0f) ? originalValue : 30f); _checkIntervals[prefabName] = value; } return value; } } public static float GetOrCreatePregnancyDuration(string prefabName, float originalValue) { if (string.IsNullOrEmpty(prefabName)) { return Mathf.Max(0f, originalValue); } lock (_lock) { if (!_pregnancyDurations.TryGetValue(prefabName, out var value)) { value = Mathf.Max(0f, originalValue); _pregnancyDurations[prefabName] = value; } return value; } } public static float GetOrCreateGrowTime(string prefabName, float originalValue) { if (string.IsNullOrEmpty(prefabName)) { if (!(originalValue > 0f)) { return 3000f; } return originalValue; } lock (_lock) { if (!_growTimes.TryGetValue(prefabName, out var value)) { value = ((originalValue > 0f) ? originalValue : 3000f); _growTimes[prefabName] = value; } return value; } } public static float GetOrCreatePartnerCheckRange(string prefabName, float originalValue) { if (string.IsNullOrEmpty(prefabName)) { if (!(originalValue > 0f)) { return 3f; } return originalValue; } lock (_lock) { if (!_partnerCheckRanges.TryGetValue(prefabName, out var value)) { value = ((originalValue > 0f) ? originalValue : 3f); _partnerCheckRanges[prefabName] = value; } return value; } } public static float GetOrCreateEnclosureCheckRange(string prefabName, float originalValue) { if (string.IsNullOrEmpty(prefabName)) { if (!(originalValue > 0f)) { return 10f; } return originalValue; } lock (_lock) { if (!_enclosureCheckRanges.TryGetValue(prefabName, out var value)) { value = ((originalValue > 0f) ? originalValue : 10f); _enclosureCheckRanges[prefabName] = value; } return value; } } public static int GetOrCreateCreatureLimit(string prefabName, int originalValue) { if (string.IsNullOrEmpty(prefabName)) { if (originalValue <= 0) { return 5; } return originalValue; } lock (_lock) { if (!_creatureLimits.TryGetValue(prefabName, out var value)) { value = ((originalValue > 0) ? originalValue : 5); _creatureLimits[prefabName] = value; } return value; } } public static List<ItemDrop> GetOrCreateNativeConsumeItems(string prefabName, List<ItemDrop>? originalItems) { if (string.IsNullOrEmpty(prefabName)) { return originalItems ?? new List<ItemDrop>(); } lock (_lock) { if (!_nativeConsumeItems.TryGetValue(prefabName, out List<ItemDrop> value)) { value = ((originalItems != null) ? new List<ItemDrop>(originalItems) : new List<ItemDrop>()); _nativeConsumeItems[prefabName] = value; } return value; } } } } namespace TameCraft.Configuration { public class GlobalAffectionConfig { private const string Section = "Affection"; public ConfigEntry<bool> Enabled { get; } public ConfigEntry<bool> PettingAffectionEnabled { get; } public ConfigEntry<float> PettingCooldownSeconds { get; } public ConfigEntry<int> AffectionPerFeed { get; } public ConfigEntry<int> AffectionPerPet { get; } public ConfigEntry<int> MaxAffection { get; } public ConfigEntry<int> NeutralThreshold { get; } public ConfigEntry<int> BondedThreshold { get; } public ConfigEntry<int> DevotedThreshold { get; } public ConfigEntry<float> AffectionDecayIntervalSeconds { get; } public GlobalAffectionConfig(ConfigFile config) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown if (config == null) { throw new ArgumentNullException("config"); } Enabled = config.Bind<bool>("Affection", "Enabled", true, "Master switch for the Affection subsystem. If false, affection mechanics remain inactive."); PettingAffectionEnabled = config.Bind<bool>("Affection", "PettingAffectionEnabled", true, "Enables persistent affection gain when petting a tamed creature."); PettingCooldownSeconds = config.Bind<float>("Affection", "PettingCooldownSeconds", 15f, new ConfigDescription("Cooldown in seconds between petting interactions for a creature. (Default: 15s).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 300f), Array.Empty<object>())); AffectionPerFeed = config.Bind<int>("Affection", "AffectionPerFeed", 2, new ConfigDescription("Affection points awarded each time a creature consumes food. (Default: 2).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>())); AffectionPerPet = config.Bind<int>("Affection", "AffectionPerPet", 2, new ConfigDescription("Affection points awarded each time a creature is petted. (Default: 2).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>())); MaxAffection = config.Bind<int>("Affection", "MaxAffection", 20, new ConfigDescription("Maximum affection score a creature can accumulate. (Default: 20).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); NeutralThreshold = config.Bind<int>("Affection", "NeutralThreshold", 1, "Affection score required to reach Neutral tier."); BondedThreshold = config.Bind<int>("Affection", "BondedThreshold", 5, "Affection score required to reach Bonded tier."); DevotedThreshold = config.Bind<int>("Affection", "DevotedThreshold", 10, "Affection score required to reach Devoted tier."); AffectionDecayIntervalSeconds = config.Bind<float>("Affection", "AffectionDecayIntervalSeconds", 0f, "World seconds of neglect after which affection decreases by 1 point. Set to 0 to disable affection decay entirely (stress-free default)."); } } public class GlobalBreedingConfig { private const string Section = "Breeding"; public ConfigEntry<bool> Enabled { get; } public ConfigEntry<float> BreedingCheckSpeedMultiplier { get; } public ConfigEntry<float> PregnancySpeedMultiplier { get; } public ConfigEntry<float> PregnancyChance { get; } public ConfigEntry<int> RequiredLovePoints { get; } public ConfigEntry<bool> RequireAffectionForBreeding { get; } public ConfigEntry<int> BreedingAffectionThreshold { get; } public ConfigEntry<float> PartnerCheckRangeMultiplier { get; } public ConfigEntry<float> EnclosureCheckRangeMultiplier { get; } public ConfigEntry<float> CreatureLimitMultiplier { get; } public BreedingAdvancedConfig Advanced { get; } public GlobalBreedingConfig(ConfigFile config) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Expected O, but got Unknown //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Expected O, but got Unknown //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Expected O, but got Unknown if (config == null) { throw new ArgumentNullException("config"); } Enabled = config.Bind<bool>("Breeding", "Enabled", true, "Master switch for TameCraft breeding subsystem. If false, vanilla Procreation runs completely unaltered."); BreedingCheckSpeedMultiplier = config.Bind<float>("Breeding", "BreedingCheckSpeedMultiplier", 2f, new ConfigDescription("Speed multiplier for how frequently creatures check for partners and attempt to breed. 1.0 = vanilla (~30s check interval). 2.0 = twice as frequent (~15s check interval).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), Array.Empty<object>())); PregnancySpeedMultiplier = config.Bind<float>("Breeding", "PregnancySpeedMultiplier", 2f, new ConfigDescription("Speed multiplier for pregnancy/gestation duration relative to each creature's native Valheim pregnancy duration. 1.0 = native Valheim value (Boar/Wolf 60s, Lox 120s; egg-layers preserved at 0s). 2.0 = approximately twice as fast (Boar/Wolf ~30s, Lox ~60s).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), Array.Empty<object>())); PregnancyChance = config.Bind<float>("Breeding", "PregnancyChance", 0.8f, new ConfigDescription("Probability (0.0 to 1.0) of a breeding interaction progressing when conditions are satisfied. (Vanilla: 0.33, TameCraft default: 0.80).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); RequiredLovePoints = config.Bind<int>("Breeding", "RequiredLovePoints", 2, new ConfigDescription("Number of successful love-point interactions required to trigger pregnancy. (Vanilla: Boar/Wolf/Hen/Asksvin 3, Lox 4; TameCraft default: 2).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); RequireAffectionForBreeding = config.Bind<bool>("Breeding", "RequireAffectionForBreeding", true, "Whether creatures require reaching the breeding affection threshold before they can procreate."); BreedingAffectionThreshold = config.Bind<int>("Breeding", "BreedingAffectionThreshold", 5, new ConfigDescription("Minimum affection score required for creatures to mate when RequireAffectionForBreeding is enabled. (Default: 5).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); PartnerCheckRangeMultiplier = config.Bind<float>("Breeding", "PartnerCheckRangeMultiplier", 1f, new ConfigDescription("Multiplier for partner mating search radius relative to creature native baseline (Native: Boar 3m, Wolf 3m, Hen 4m, Asksvin 4m, Lox 8m). 1.0 = native baseline.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), Array.Empty<object>())); EnclosureCheckRangeMultiplier = config.Bind<float>("Breeding", "EnclosureCheckRangeMultiplier", 1f, new ConfigDescription("Multiplier for enclosure population check radius relative to creature native baseline (Native: Boar/Wolf/Hen/Asksvin 10m, Lox 20m). 1.0 = native baseline.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), Array.Empty<object>())); CreatureLimitMultiplier = config.Bind<float>("Breeding", "CreatureLimitMultiplier", 1f, new ConfigDescription("Multiplier for maximum creatures of this species allowed in enclosure check range before breeding stops (Native: Boar 5, Wolf 4, Lox 4, Hen 10, Asksvin 10). 1.0 = native baseline.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f), Array.Empty<object>())); Advanced = new BreedingAdvancedConfig(config); } } public class BreedingAdvancedConfig { private const string Section = "Breeding.Advanced"; public ConfigEntry<string> PartnerCheckRangeOverrides { get; } public ConfigEntry<string> EnclosureCheckRangeOverrides { get; } public ConfigEntry<string> CreatureLimitOverrides { get; } public ConfigEntry<string> BreedingEnabledOverrides { get; } public ConfigEntry<string> UpdateIntervalOverrides { get; } public ConfigEntry<string> PregnancyDurationOverrides { get; } public ConfigEntry<string> PregnancyChanceOverrides { get; } public ConfigEntry<string> RequiredLovePointsOverrides { get; } public BreedingAdvancedConfig(ConfigFile config) { if (config == null) { throw new ArgumentNullException("config"); } PartnerCheckRangeOverrides = config.Bind<string>("Breeding.Advanced", "PartnerCheckRangeOverrides", string.Empty, "Per-creature mating partner search radius overrides in meters (e.g. 'Boar:6,Wolf:6,Lox:10,Asksvin:6').\nConfigures the maximum distance checked for a partner (native Procreation.m_partnerCheckRange; Vanilla: Boar/Wolf 3m, Hen/Asksvin 4m, Lox 8m).\nSupported canonical creature keys: Boar, Wolf, Lox, Asksvin, Hen (use 'Hen', not 'Chicken').\nCompatible modded creatures with Procreation are supported using their exact registered prefab name.\nFormat: 'CreatureA:Distance,CreatureB:Distance'. Overrides global PartnerCheckRangeMultiplier when set."); EnclosureCheckRangeOverrides = config.Bind<string>("Breeding.Advanced", "EnclosureCheckRangeOverrides", string.Empty, "Per-creature enclosure check radius overrides in meters (e.g. 'Boar:15,Lox:25,Wolf:15,Asksvin:15').\nConfigures the radius checked for local population density (native Procreation.m_totalCheckRange; Vanilla: Boar/Wolf/Hen/Asksvin 10m, Lox 20m).\nSupported canonical creature keys: Boar, Wolf, Lox, Asksvin, Hen (use 'Hen', not 'Chicken').\nCompatible modded creatures with Procreation are supported using their exact registered prefab name.\nFormat: 'CreatureA:Distance,CreatureB:Distance'. Overrides global EnclosureCheckRangeMultiplier when set."); CreatureLimitOverrides = config.Bind<string>("Breeding.Advanced", "CreatureLimitOverrides", string.Empty, "Per-creature maximum population limit within the enclosure radius (e.g. 'Boar:20,Lox:6,Wolf:20,Asksvin:8').\nIf the number of creatures of this species within the enclosure radius reaches this limit, breeding pauses.\nSupported canonical creature keys: Boar, Wolf, Lox, Asksvin, Hen (use 'Hen', not 'Chicken').\nCompatible modded creatures with Procreation are supported using their exact registered prefab name.\nFormat: 'CreatureA:Count,CreatureB:Count'. Overrides global CreatureLimitMultiplier when set."); BreedingEnabledOverrides = config.Bind<string>("Breeding.Advanced", "BreedingEnabledOverrides", string.Empty, "Per-creature breeding enabled overrides (e.g. 'Hen:false,Boar:true').\nSet to false to disable TameCraft breeding modifications for a specific creature and leave it 100% vanilla.\nSupported canonical creature keys: Boar, Wolf, Lox, Asksvin, Hen (use 'Hen', not 'Chicken').\nCompatible modded creatures with Procreation are supported using their exact registered prefab name.\nFormat: 'CreatureA:false,CreatureB:true'."); UpdateIntervalOverrides = config.Bind<string>("Breeding.Advanced", "UpdateIntervalOverrides", string.Empty, "Per-creature breeding evaluation frequency in seconds (e.g. 'Boar:15,Wolf:15,Lox:15').\nConfigures seconds between partner and condition checks (native Procreation.m_updateInterval; Vanilla: 30s).\nSupported canonical creature keys: Boar, Wolf, Lox, Asksvin, Hen (use 'Hen', not 'Chicken').\nCompatible modded creatures with Procreation are supported using their exact registered prefab name.\nFormat: 'CreatureA:Seconds,CreatureB:Seconds'. Overrides global BreedingCheckSpeedMultiplier when set."); PregnancyDurationOverrides = config.Bind<string>("Breeding.Advanced", "PregnancyDurationOverrides", string.Empty, "Per-creature gestation duration overrides in seconds from conception to birth (e.g. 'Boar:30,Wolf:30,Lox:60').\nConfigures gestation duration (native Procreation.m_pregnancyDuration; Vanilla: Boar 60s, Wolf 60s, Lox 120s).\nApplies to mammalian live-gestation species with native Procreation. Preserved at 0 for egg-layers (Hen, Asksvin).\nSupported canonical creature keys: Boar, Wolf, Lox (and compatible modded live-breeders).\nCompatible modded creatures are supported using their exact registered prefab name.\nFormat: 'CreatureA:Seconds,CreatureB:Seconds'. Overrides global PregnancySpeedMultiplier when set."); PregnancyChanceOverrides = config.Bind<string>("Breeding.Advanced", "PregnancyChanceOverrides", string.Empty, "Per-creature conception probability overrides [0.0 to 1.0] when mating conditions are satisfied (e.g. 'Boar:0.8,Wolf:0.8,Lox:0.5').\nConfigures the probability of progressing towards pregnancy each check (native Procreation.m_pregnancyChance).\nSupported canonical creature keys: Boar, Wolf, Lox, Asksvin, Hen (use 'Hen', not 'Chicken').\nCompatible modded creatures with Procreation are supported using their exact registered prefab name.\nFormat: 'CreatureA:Chance,CreatureB:Chance'. Overrides global PregnancyChance when set."); RequiredLovePointsOverrides = config.Bind<string>("Breeding.Advanced", "Re