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 FehuMounts v420.100.3
plugins/FehuMounts/FehuMounts.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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 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("FehuMounts")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("420.100.3.0")] [assembly: AssemblyInformationalVersion("420.100.3-rc.1")] [assembly: AssemblyProduct("FehuMounts")] [assembly: AssemblyTitle("FehuMounts")] [assembly: AssemblyVersion("420.100.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 FehuMounts { internal static class DeerRules { public static float SpeedMultiplier(int stars) { if (stars < 5) { return stars switch { 3 => 1.15f, 4 => 1.25f, _ => 1f, }; } return 1.35f; } public static int OffspringStars(int first, int second, double roll, int maximum) { int num = Math.Min(first, second); int num2 = Math.Max(first, second); return Math.Max(0, Math.Min(maximum, (roll < 0.7) ? num : ((roll < 0.95) ? num2 : (num2 + 1)))); } public static bool MayUse(long owner, long player) { if (player != 0L) { if (owner != 0L) { return owner == player; } return true; } return false; } public static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } public static float PregnancyRemaining(float remaining, float seconds) { if (!(remaining <= 0f)) { return Math.Max(0.001f, remaining - Math.Max(0f, seconds)); } return 0f; } public static bool CanGiveBirth(int population, int maximum) { return population < Math.Max(2, maximum); } public static string CleanName(string value) { StringBuilder stringBuilder = new StringBuilder(); string text = value ?? ""; foreach (char c in text) { if (!char.IsControl(c) && c != '<' && c != '>') { stringBuilder.Append(c); } if (stringBuilder.Length == 20) { break; } } return stringBuilder.ToString().Trim(); } public static float Stamina(float current, float maximum, float seconds, bool sprinting, int stars) { float num = ((stars >= 5) ? 10.5f : ((stars == 4) ? 12.75f : 15f)); return Math.Max(0f, Math.Min(maximum, current + (sprinting ? (0f - num) : 10f) * Math.Max(0f, seconds))); } } internal static class DeerSettings { private static ConfigFile _config = null; private static readonly Dictionary<ConfigEntryBase, object> LocalValues = new Dictionary<ConfigEntryBase, object>(); internal static bool ClientReady; internal static ConfigEntry<string> Foods = null; internal static ConfigEntry<float> GrowthSeconds = null; internal static ConfigEntry<bool> ProtectFawns = null; internal static ConfigEntry<float> WalkSpeed = null; internal static ConfigEntry<float> RunSpeed = null; internal static bool InputAllowed { get { if (!Menu.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && !InventoryGui.IsVisible() && !StoreGui.IsVisible()) { if (!((Object)(object)Chat.instance == (Object)null)) { return !Chat.instance.HasFocus(); } return true; } return false; } } internal static string[] FoodNames => (from x in Foods.Value.Split(',') select x.Trim() into x where x.Length > 0 select x).Distinct().ToArray(); private static ConfigEntryBase[] Shared => (ConfigEntryBase[])(object)new ConfigEntryBase[26] { (ConfigEntryBase)Plugin.EnableDeerTaming, (ConfigEntryBase)Plugin.EnableDeerBreeding, (ConfigEntryBase)Plugin.EnableDeerRiding, (ConfigEntryBase)Plugin.MinStarsToRide, (ConfigEntryBase)Plugin.MaxDeerStars, (ConfigEntryBase)Plugin.MateRange, (ConfigEntryBase)Plugin.PregnancyDuration, (ConfigEntryBase)Plugin.MaxNearbyDeer, (ConfigEntryBase)Plugin.NearbyDeerRange, (ConfigEntryBase)Plugin.BreedingCooldown, (ConfigEntryBase)Plugin.DeerFoodScanRange, (ConfigEntryBase)Plugin.TamingFoodProgressPerEat, (ConfigEntryBase)Plugin.UseBoarLikeTaming, (ConfigEntryBase)Plugin.DeerTamingDuration, (ConfigEntryBase)Plugin.DeerTamingTickSeconds, (ConfigEntryBase)Plugin.InstantTamingPerFood, (ConfigEntryBase)Plugin.FollowStartDistance, (ConfigEntryBase)Plugin.FollowStopDistance, (ConfigEntryBase)Plugin.FollowTeleportDistance, (ConfigEntryBase)Plugin.OnlyOwnerCommands, (ConfigEntryBase)Plugin.OnlyOwnerCanRide, (ConfigEntryBase)Foods, (ConfigEntryBase)GrowthSeconds, (ConfigEntryBase)ProtectFawns, (ConfigEntryBase)WalkSpeed, (ConfigEntryBase)RunSpeed }; internal static void Bind(ConfigFile config) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown _config = config; Foods = config.Bind<string>("Taming", "DeerFoods", "Raspberry,Blueberries,Carrot,Turnip,Mushroom,Cloudberry,Onion", "Exact food prefab IDs, comma separated. Server controls gameplay."); GrowthSeconds = config.Bind<float>("Breeding", "FawnGrowthSeconds", 2000f, new ConfigDescription("Loaded, server-simulated seconds needed for a fawn to grow up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 86400f), Array.Empty<object>())); ProtectFawns = config.Bind<bool>("Breeding", "ProtectFawns", true, "Protect fawns from the butcher knife. Does not protect against enemies."); WalkSpeed = config.Bind<float>("Riding", "WalkSpeed", 5f, new ConfigDescription("Mounted walk/jog speed before star and loyalty bonuses.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 12f), Array.Empty<object>())); RunSpeed = config.Bind<float>("Riding", "RunSpeed", 10f, new ConfigDescription("Mounted gallop speed before star and loyalty bonuses.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>())); ConfigEntry<bool> val = config.Bind<bool>("Migration", "LegacySettingsMigrated", false, "Internal: legacy settings have been considered once."); if (!val.Value) { ConfigEntry<int> val2 = config.Bind<int>("General", "MinStarsToRide", 2, "Legacy key. Use Riding/MinStarsToRide."); if (Plugin.MinStarsToRide.Value == 2 && val2.Value != 2) { Plugin.MinStarsToRide.Value = val2.Value; } val.Value = true; } } internal static void Publish() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write("420.100.3"); ConfigEntryBase[] shared = Shared; foreach (ConfigEntryBase val2 in shared) { val.Write(Convert.ToString(val2.BoxedValue, CultureInfo.InvariantCulture)); } ZRoutedRpc.instance.InvokeRoutedRPC(0L, "FehuMounts.SettingsV110", new object[1] { val }); } internal static void Receive(long sender, ZPackage packet) { if (DeerNetwork.IsServer || (Object)(object)ZNet.instance == (Object)null || sender != ZNet.instance.GetServerPeer()?.m_uid || packet.Size() > 8192) { return; } try { if (packet.ReadString() != "420.100.3") { ClientReady = false; Plugin.Log.LogWarning((object)"[FehuMounts] Server/client version mismatch. Install the same FehuMounts build."); return; } ConfigEntryBase[] shared = Shared; object[] array = new object[shared.Length]; for (int i = 0; i < shared.Length; i++) { array[i] = Convert.ChangeType(packet.ReadString(), shared[i].SettingType, CultureInfo.InvariantCulture); if (array[i] is float value && !DeerRules.IsFinite(value)) { return; } } bool saveOnConfigSet = _config.SaveOnConfigSet; _config.SaveOnConfigSet = false; try { for (int j = 0; j < shared.Length; j++) { if (!LocalValues.ContainsKey(shared[j])) { LocalValues.Add(shared[j], shared[j].BoxedValue); } shared[j].BoxedValue = array[j]; } ClientReady = true; } finally { _config.SaveOnConfigSet = saveOnConfigSet; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FehuMounts] Settings rejected: " + ex.Message)); } } internal static void RestoreLocal() { bool saveOnConfigSet = _config.SaveOnConfigSet; _config.SaveOnConfigSet = false; try { foreach (KeyValuePair<ConfigEntryBase, object> localValue in LocalValues) { localValue.Key.BoxedValue = localValue.Value; } } finally { _config.SaveOnConfigSet = saveOnConfigSet; LocalValues.Clear(); ClientReady = false; } } } internal sealed class DeerNetwork : MonoBehaviour { private const string Rpc = "FehuMounts.RequestV110"; private ZRoutedRpc? _registered; private float _nextSettings; private readonly Dictionary<long, float> _lastRequest = new Dictionary<long, float>(); internal static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } private void Update() { if (ZRoutedRpc.instance != null) { if (_registered != ZRoutedRpc.instance) { DeerSettings.RestoreLocal(); ZRoutedRpc.instance.Register<ZPackage>("FehuMounts.RequestV110", (Action<long, ZPackage>)Receive); ZRoutedRpc.instance.Register<ZPackage>("FehuMounts.SettingsV110", (Action<long, ZPackage>)DeerSettings.Receive); _registered = ZRoutedRpc.instance; _lastRequest.Clear(); _nextSettings = 0f; } if (IsServer && Time.time >= _nextSettings) { _nextSettings = Time.time + 3f; DeerSettings.Publish(); } } } internal static void Request(ZNetView? view, string action, string text = "", Vector3 direction = default(Vector3), bool run = false) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)view == (Object)null) && view.IsValid() && ZRoutedRpc.instance != null && !((Object)(object)Player.m_localPlayer == (Object)null) && (IsServer || DeerSettings.ClientReady)) { ZPackage val = new ZPackage(); val.Write(view.GetZDO().m_uid); val.Write(action); val.Write(text); val.Write(direction); val.Write(run); long num = (IsServer ? ZNet.GetUID() : (ZNet.instance.GetServerPeer()?.m_uid ?? 0)); if (num != 0L) { ZRoutedRpc.instance.InvokeRoutedRPC(num, "FehuMounts.RequestV110", new object[1] { val }); } } } private void OnDestroy() { DeerSettings.RestoreLocal(); } private void Receive(long sender, ZPackage packet) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) if (!IsServer || packet.Size() > 512) { return; } Player val; if (sender == ZNet.GetUID()) { val = Player.m_localPlayer; } else { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer == null || !peer.IsReady() || ((ZDOID)(ref peer.m_characterID)).IsNone()) { return; } GameObject val2 = ZNetScene.instance.FindInstance(peer.m_characterID); val = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<Player>() : null); } if ((Object)(object)val == (Object)null || ((Character)val).IsDead()) { return; } float time = Time.time; if (_lastRequest.TryGetValue(sender, out var value) && time - value < 0.04f) { return; } _lastRequest[sender] = time; if (_lastRequest.Count > 512) { _lastRequest.Clear(); } try { ZDOID val3 = packet.ReadZDOID(); string action = packet.ReadString(); string text = packet.ReadString(); Vector3 direction = packet.ReadVector3(); bool run = packet.ReadBool(); GameObject val4 = ZNetScene.instance.FindInstance(val3); if (!((Object)(object)val4 == (Object)null)) { Character component = val4.GetComponent<Character>(); DeerLifecycle component2 = val4.GetComponent<DeerLifecycle>(); ZNetView component3 = val4.GetComponent<ZNetView>(); if (Plugin.IsDeer(component) && !((Object)(object)component2 == (Object)null) && !((Object)(object)component3 == (Object)null) && component3.IsValid() && component3.IsOwner() && !(Vector3.Distance(((Component)val).transform.position, val4.transform.position) > 5f)) { component2.Handle(val, action, text, direction, run); } } } catch (Exception ex) { if (Plugin.DebugMode.Value) { Plugin.Log.LogWarning((object)("[FehuMounts] Rejected malformed request: " + ex.Message)); } } } } internal sealed class DeerLifecycle : MonoBehaviour { internal static readonly HashSet<ZDOID> ServerObjects = new HashSet<ZDOID>(); private ZDOID _registeredId; private bool _registeredObject; private static readonly MethodInfo? MoveToMethod = typeof(BaseAI).GetMethod("MoveTo", BindingFlags.Instance | BindingFlags.NonPublic); internal const string RiderKey = "FehuMounts_RiderID"; internal const string StaminaKey = "FehuMounts_MountStamina"; internal const string NameKey = "FehuMounts.Name"; private const string FawnKey = "FehuMounts.Fawn"; private const string AgeKey = "FehuMounts.Age"; private const string PregnancyKey = "FehuMounts.PregnancyRemaining"; private const string ChildStarsKey = "FehuMounts.ChildStars"; private ZNetView _view; private Character _deer; private Tameable _tameable; private AnimalAI _ai; private Vector3 _rideDirection; private bool _running; private float _lastInput; private float _lastPet = -10f; private float _elapsed; private float _nextPublish; private float _stamina = -1f; private long _previousRider; private bool _sessionInitialized; private float _baseSpeed; private float _baseWalkSpeed; private float _baseRunSpeed; internal static bool IsFawn(ZNetView? view) { if ((Object)(object)view != (Object)null && view.IsValid()) { return view.GetZDO().GetBool("FehuMounts.Fawn", false); } return false; } internal static bool HasRider(Character deer) { ZNetView component = ((Component)deer).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { return component.GetZDO().GetLong("FehuMounts_RiderID", 0L) != 0; } return false; } internal static float LoyaltyBonus(Character deer) { ZNetView component = ((Component)deer).GetComponent<ZNetView>(); int num = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO().GetInt("FehuMounts_Loyalty", 0) : 0); if (num < 90) { if (num < 60) { if (num < 30) { return 1f; } return 1.05f; } return 1.1f; } return 1.15f; } internal static void WalkTo(AnimalAI ai, Vector3 position, bool run) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (MoveToMethod == null) { ((BaseAI)ai).StopMoving(); return; } MoveToMethod.Invoke(ai, new object[4] { 0.25f, position, 1.5f, run }); } private void Awake() { _view = ((Component)this).GetComponent<ZNetView>(); _deer = ((Component)this).GetComponent<Character>(); _tameable = ((Component)this).GetComponent<Tameable>(); _ai = ((Component)this).GetComponent<AnimalAI>(); if ((Object)(object)_deer != (Object)null) { _baseSpeed = _deer.m_speed; _baseWalkSpeed = _deer.m_walkSpeed; _baseRunSpeed = _deer.m_runSpeed; } } private void Update() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) if (!DeerNetwork.IsServer || (Object)(object)_view == (Object)null || !_view.IsValid() || (Object)(object)_deer == (Object)null || (Object)(object)_ai == (Object)null) { return; } if (_tameable == null) { _tameable = ((Component)this).GetComponent<Tameable>(); } if ((Object)(object)_tameable == (Object)null) { return; } if (!_registeredObject) { _registeredId = _view.GetZDO().m_uid; ServerObjects.Add(_registeredId); _registeredObject = true; } if (!_view.IsOwner()) { _view.ClaimOwnership(); return; } ZDO zDO = _view.GetZDO(); if (!_sessionInitialized) { _sessionInitialized = true; zDO.Set("FehuMounts_RiderID", 0L); } if (Plugin.IsFehuTamed(_view, _tameable) && !_deer.IsTamed()) { _deer.SetTamed(true); } long num = zDO.GetLong("FehuMounts_RiderID", 0L); if (num != _previousRider) { _rideDirection = Vector3.zero; _lastInput = Time.time; _previousRider = num; } if (num != 0L) { Player player = Player.GetPlayer(num); if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || Time.time - _lastInput > 1.5f || !Plugin.EnableDeerRiding.Value || IsFawn(_view)) { Dismount(); } else { if (_stamina < 0f) { _stamina = zDO.GetFloat("FehuMounts_MountStamina", (float)MaxStamina(_deer)); } bool flag = _running && _stamina > 0f && ((Vector3)(ref _rideDirection)).sqrMagnitude > 0.01f; _deer.m_speed = DeerSettings.WalkSpeed.Value; _deer.m_walkSpeed = DeerSettings.WalkSpeed.Value; _deer.m_runSpeed = DeerSettings.RunSpeed.Value; if (((Vector3)(ref _rideDirection)).sqrMagnitude > 0.01f) { ((BaseAI)_ai).MoveTowards(_rideDirection, flag); } else { ((BaseAI)_ai).StopMoving(); } _deer.SetRun(flag); _stamina = DeerRules.Stamina(_stamina, MaxStamina(_deer), Time.deltaTime, flag, _deer.GetLevel() - 1); } } else { if (_stamina < 0f) { _stamina = zDO.GetFloat("FehuMounts_MountStamina", (float)MaxStamina(_deer)); } _stamina = DeerRules.Stamina(_stamina, MaxStamina(_deer), Time.deltaTime, sprinting: false, _deer.GetLevel() - 1); } if (Time.time >= _nextPublish) { _nextPublish = Time.time + 0.25f; zDO.Set("FehuMounts_MountStamina", _stamina); } _elapsed += Time.deltaTime; if (_elapsed < 1f) { return; } float elapsed = _elapsed; _elapsed = 0f; if (IsFawn(_view)) { float num2 = zDO.GetFloat("FehuMounts.Age", 0f) + elapsed; zDO.Set("FehuMounts.Age", num2); if (num2 >= DeerSettings.GrowthSeconds.Value) { zDO.Set("FehuMounts.Fawn", false); _tameable.m_tamedEffect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, 0, default(ZDOID)); } } float num3 = zDO.GetFloat("FehuMounts.PregnancyRemaining", 0f); if (num3 > 0f && Plugin.EnableDeerBreeding.Value) { zDO.Set("FehuMounts.PregnancyRemaining", DeerRules.PregnancyRemaining(num3, elapsed)); } } private void OnDisable() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (_registeredObject) { ServerObjects.Remove(_registeredId); } _registeredObject = false; } internal void Handle(Player player, string action, string text, Vector3 direction, bool run) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) ZDO zDO = _view.GetZDO(); long playerID = player.GetPlayerID(); long ownerId = Plugin.GetOwnerId(_view); if (!Plugin.IsFehuTamed(_view, _tameable)) { return; } long num = zDO.GetLong("FehuMounts_RiderID", 0L); if (action == "input") { if (num == playerID && DeerRules.IsFinite(direction.x) && DeerRules.IsFinite(direction.y) && DeerRules.IsFinite(direction.z)) { direction.y = 0f; _rideDirection = Vector3.ClampMagnitude(direction, 1f); _running = run; _lastInput = Time.time; } } else if (action == "dismount") { if (num == playerID) { Dismount(); } } else { if ((action != "pet" && action != "rename" && action != "follow" && action != "ride") || ((!(action == "ride") || Plugin.OnlyOwnerCanRide.Value) && (!(action == "follow") || Plugin.OnlyOwnerCommands.Value) && !DeerRules.MayUse(ownerId, playerID))) { return; } if (ownerId == 0L) { zDO.Set(Plugin.OwnerHash, playerID); zDO.Set(Plugin.OwnerNameHash, player.GetPlayerName()); } switch (action) { case "pet": if (!(Time.time - _lastPet < 5f)) { _lastPet = Time.time; ((Component)this).GetComponent<DeerLoyaltyController>()?.AddLoyalty(2); _tameable.m_petEffect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, 0, default(ZDOID)); } return; case "rename": { string text2 = DeerRules.CleanName(text); zDO.Set("FehuMounts.Name", text2); zDO.Set(ZDOVars.s_tamedName, text2); return; } case "follow": if (num == 0L) { bool flag = !zDO.GetBool(Plugin.FollowHash, false); zDO.Set(Plugin.FollowHash, flag); zDO.Set(Plugin.StayPositionHash, ((Component)this).transform.position); return; } break; } if (action == "ride" && num == 0L && !IsFawn(_view) && Plugin.EnableDeerRiding.Value && _deer.GetLevel() - 1 >= Plugin.MinStarsToRide.Value) { zDO.Set("FehuMounts_RiderID", playerID); _rideDirection = Vector3.zero; _lastInput = Time.time; } } } private void Dismount() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) ZDO zDO = _view.GetZDO(); zDO.Set("FehuMounts_RiderID", 0L); zDO.Set(Plugin.FollowHash, false); zDO.Set(Plugin.StayPositionHash, ((Component)this).transform.position); _rideDirection = Vector3.zero; ((BaseAI)_ai).StopMoving(); _deer.m_speed = _baseSpeed; _deer.m_walkSpeed = _baseWalkSpeed; _deer.m_runSpeed = _baseRunSpeed; } internal static int MaxStamina(Character deer) { int num = deer.GetLevel() - 1; if (num < 5) { return num switch { 3 => 125, 4 => 140, _ => 100, }; } return 160; } internal void UpdateBreeding() { //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.EnableDeerBreeding.Value || (Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner() || (Object)(object)_tameable == (Object)null || IsFawn(_view)) { return; } ZDO zDO = _view.GetZDO(); float num = zDO.GetFloat("FehuMounts.PregnancyRemaining", 0f); if (num > 0f) { if (num <= 0.001f && DeerRules.CanGiveBirth(CountPopulation(includePregnancy: false), Plugin.MaxNearbyDeer.Value)) { GiveBirth(); } } else { if (!Plugin.EnableDeerBreeding.Value || !Plugin.IsFehuTamed(_view, _tameable) || _tameable.IsHungry() || Plugin.GetOwnerId(_view) == 0L || CoolingDown(_view) || CountPopulation(includePregnancy: true) >= Mathf.Max(2, Plugin.MaxNearbyDeer.Value)) { return; } DeerLifecycle[] array = Object.FindObjectsByType<DeerLifecycle>((FindObjectsSortMode)0); foreach (DeerLifecycle deerLifecycle in array) { if (!((Object)(object)deerLifecycle == (Object)(object)this) && !((Object)(object)deerLifecycle._view == (Object)null) && deerLifecycle._view.IsValid() && deerLifecycle._view.IsOwner() && !IsFawn(deerLifecycle._view) && !((Object)(object)deerLifecycle._tameable == (Object)null) && Plugin.IsFehuTamed(deerLifecycle._view, deerLifecycle._tameable) && !deerLifecycle._tameable.IsHungry() && Plugin.GetOwnerId(deerLifecycle._view) == Plugin.GetOwnerId(_view) && !CoolingDown(deerLifecycle._view) && !(deerLifecycle._view.GetZDO().GetFloat("FehuMounts.PregnancyRemaining", 0f) > 0f) && !(Vector3.Distance(((Component)this).transform.position, ((Component)deerLifecycle).transform.position) > Mathf.Max(1f, Plugin.MateRange.Value))) { long num2 = Now() + (long)(Mathf.Max(1f, Plugin.BreedingCooldown.Value) * 10000000f); zDO.Set(Plugin.BreedingCooldownHash, num2); deerLifecycle._view.GetZDO().Set(Plugin.BreedingCooldownHash, num2); zDO.Set("FehuMounts.PregnancyRemaining", Mathf.Max(1f, Plugin.PregnancyDuration.Value)); int num3 = DeerRules.OffspringStars(_deer.GetLevel() - 1, deerLifecycle._deer.GetLevel() - 1, Random.value, Mathf.Clamp(Plugin.MaxDeerStars.Value, 0, 5)); zDO.Set("FehuMounts.ChildStars", num3); _tameable.m_sootheEffect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, 0, default(ZDOID)); break; } } } } private static long Now() { return ZNet.instance.GetTime().Ticks; } private static bool CoolingDown(ZNetView view) { return view.GetZDO().GetLong(Plugin.BreedingCooldownHash, 0L) > Now(); } private int CountPopulation(bool includePregnancy) { //IL_0035: 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) int num = 0; DeerLifecycle[] array = Object.FindObjectsByType<DeerLifecycle>((FindObjectsSortMode)0); foreach (DeerLifecycle deerLifecycle in array) { if (!((Object)(object)deerLifecycle._view == (Object)null) && deerLifecycle._view.IsValid() && !(Vector3.Distance(((Component)this).transform.position, ((Component)deerLifecycle).transform.position) > Mathf.Max(1f, Plugin.NearbyDeerRange.Value))) { num++; if (includePregnancy && deerLifecycle._view.GetZDO().GetFloat("FehuMounts.PregnancyRemaining", 0f) > 0f) { num++; } } } return num; } private unsafe void GiveBirth() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = ZNetScene.instance.GetPrefab("Deer"); if ((Object)(object)prefab == (Object)null) { return; } GameObject val = Object.Instantiate<GameObject>(prefab, ((Component)this).transform.position, ((Component)this).transform.rotation); ZNetView component = val.GetComponent<ZNetView>(); Character component2 = val.GetComponent<Character>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || (Object)(object)component2 == (Object)null) { if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner()) { ZNetScene.instance.Destroy(val); } return; } ZDO zDO = component.GetZDO(); zDO.Set("FehuMounts.Fawn", true); zDO.Set("FehuMounts.Age", 0f); component2.SetLevel(_view.GetZDO().GetInt("FehuMounts.ChildStars", 0) + 1); Plugin.CompleteTaming(val, Plugin.GetOwnerId(_view), _view.GetZDO().GetString(Plugin.OwnerNameHash, "")); zDO.Set(ZDOVars.s_tameLastFeeding, Now()); _view.GetZDO().Set("FehuMounts.PregnancyRemaining", 0f); ManualLogSource log = Plugin.Log; ZDOID uid = zDO.m_uid; log.LogInfo((object)("[FehuMounts] Fawn born: " + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString())); } internal static string Describe(Character deer, ZNetView view) { ZDO zDO = view.GetZDO(); string text = DeerRules.CleanName(zDO.GetString("FehuMounts.Name", "")); bool flag = IsFawn(view); if (text.Length == 0) { text = (flag ? "Fawn" : "Deer"); } int num = Math.Max(0, deer.GetLevel() - 1); string text2 = text + " — " + num + "★"; string text3 = zDO.GetString(Plugin.OwnerNameHash, ""); if (text3.Length > 0) { text2 = text2 + "\nOwner: " + DeerRules.CleanName(text3); } if (flag) { text2 = text2 + "\nGrowing: " + Mathf.FloorToInt(100f * Mathf.Clamp01(zDO.GetFloat("FehuMounts.Age", 0f) / DeerSettings.GrowthSeconds.Value)) + "%"; } text2 = text2 + "\nLoyalty: " + zDO.GetInt("FehuMounts_Loyalty", 0); Tameable component = ((Component)deer).GetComponent<Tameable>(); text2 += (((Object)(object)component != (Object)null && component.IsHungry()) ? " · Hungry" : " · Fed"); float num2 = zDO.GetFloat("FehuMounts.PregnancyRemaining", 0f); if (num2 > 0f) { text2 = text2 + "\nExpecting fawn: " + Mathf.CeilToInt(num2) + "s"; } text2 = text2 + "\n[E] " + (zDO.GetBool(Plugin.FollowHash, false) ? "Stay" : "Follow") + " · [Q] Pet · [R] Rename"; if (!flag && Plugin.EnableDeerRiding.Value && num >= Plugin.MinStarsToRide.Value) { text2 += "\n[Shift + E] Ride Deer"; } if (Plugin.GetOwnerId(view) == 0L) { text2 += "\nFirst owner interaction claims this deer"; } return text2; } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class FawnProtectionPatch { private static bool Prefix(Character __instance, HitData hit) { if (!Plugin.IsDeer(__instance) || !DeerSettings.ProtectFawns.Value || !DeerLifecycle.IsFawn(((Component)__instance).GetComponent<ZNetView>())) { return true; } Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); ItemData val2 = (((Object)(object)val != (Object)null) ? ((Humanoid)val).GetCurrentWeapon() : null); if (!((Object)(object)val2?.m_dropPrefab == (Object)null)) { return !((Object)val2.m_dropPrefab).name.Equals("KnifeButcher", StringComparison.Ordinal); } return true; } } [HarmonyPatch(typeof(Character), "RPC_SetTamed")] internal static class DeerNativeTamingAuthorityPatch { private static bool Prefix(Character __instance, long sender) { if (!((Object)(object)((Component)__instance).GetComponent<DeerLifecycle>() == (Object)null) && DeerNetwork.IsServer) { return sender == ZNet.GetUID(); } return true; } } [HarmonyPatch(typeof(ZDO), "SetOwner")] internal static class DeerSimulationOwnerPatch { private static void Prefix(ZDO __instance, ref long __0) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (DeerNetwork.IsServer && DeerLifecycle.ServerObjects.Contains(__instance.m_uid)) { __0 = ZNet.GetUID(); } } } [BepInPlugin("wmymysl.fehumounts", "FehuMounts", "420.100.3")] [BepInIncompatibility("nick008.tameabledeer")] [BepInIncompatibility("qua8ion.valheim.rideabletames")] [BepInIncompatibility("adrirf7.TameableAnimals")] [BepInIncompatibility("Tie.ServerTaming")] [BepInIncompatibility("meldurson.LetMeTameYou")] public sealed class Plugin : BaseUnityPlugin { public const string ModGuid = "wmymysl.fehumounts"; public const string ModName = "FehuMounts"; public const string ModVersion = "420.100.3"; internal static ConfigEntry<bool> EnableDeerTaming = null; internal static ConfigEntry<bool> EnableDeerBreeding = null; internal static ConfigEntry<bool> EnableDeerRiding = null; internal static ConfigEntry<bool> EnableWhistle = null; internal static ConfigEntry<int> MinStarsToRide = null; internal static ConfigEntry<int> MaxDeerStars = null; internal static ConfigEntry<float> MateRange = null; internal static ConfigEntry<float> PregnancyDuration = null; internal static ConfigEntry<int> MaxNearbyDeer = null; internal static ConfigEntry<float> NearbyDeerRange = null; internal static ConfigEntry<float> BreedingCooldown = null; internal static ConfigEntry<float> WhistleRange = null; internal static ConfigEntry<bool> OnlyOwnerCanSummon = null; internal static ConfigEntry<bool> OnlyOwnerCommands = null; internal static ConfigEntry<float> DeerFoodScanRange = null; internal static ConfigEntry<bool> ManualTamingFallback = null; internal static ConfigEntry<float> TamingFoodProgressPerEat = null; internal static ConfigEntry<bool> UseBoarLikeTaming = null; internal static ConfigEntry<float> DeerTamingDuration = null; internal static ConfigEntry<float> DeerTamingTickSeconds = null; internal static ConfigEntry<bool> InstantTamingPerFood = null; internal static ConfigEntry<float> FollowStartDistance = null; internal static ConfigEntry<float> FollowStopDistance = null; internal static ConfigEntry<float> FollowTeleportDistance = null; internal static ConfigEntry<bool> OnlyOwnerCanRide = null; internal static ConfigEntry<bool> DebugRawFood = null; internal static ConfigEntry<bool> DebugMode = null; internal static ConfigEntry<bool> DebugFood = null; internal static ConfigEntry<bool> DebugBreeding = null; internal static ConfigEntry<bool> DebugRiding = null; internal static ManualLogSource Log = null; internal const string TameProgressKey = "FehuMounts_TameProgress"; internal static readonly int TameProgressLogHash = StableHash("FehuMounts.TameProgressLog"); internal static readonly int OwnerHash = StableHash("FehuMounts.Owner"); internal static readonly int OwnerNameHash = StableHash("FehuMounts.OwnerName"); internal static readonly int IsTamedHash = StableHash("FehuMounts.IsTamed"); internal static readonly int CommandStateHash = StableHash("FehuMounts.CommandState"); internal static readonly int StayPositionHash = StableHash("FehuMounts.StayPosition"); internal static readonly int BreedingCooldownHash = StableHash("FehuMounts.BreedingCooldown"); internal static readonly int FollowHash = StableHash("FehuMounts.Follow"); private static readonly Harmony Harmony = new Harmony("wmymysl.fehumounts"); private void Awake() { Log = ((BaseUnityPlugin)this).Logger; EnableDeerTaming = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDeerTaming", true, "Enable vanilla taming behavior for Deer."); EnableDeerBreeding = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDeerBreeding", true, "Enable tamed Deer breeding."); EnableDeerRiding = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableDeerRiding", true, "Enable riding for tamed Deer with enough stars."); EnableWhistle = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableWhistle", false, "Reserved for the future Fehu Whistle phase. No whistle is registered in this build."); MinStarsToRide = ((BaseUnityPlugin)this).Config.Bind<int>("Riding", "MinStarsToRide", 2, "Minimum Deer stars required to ride."); MaxDeerStars = ((BaseUnityPlugin)this).Config.Bind<int>("Breeding", "MaxDeerStars", 5, "Maximum Deer stars produced by breeding."); MateRange = ((BaseUnityPlugin)this).Config.Bind<float>("Breeding", "MateRange", 8f, "Maximum distance between two deer that can breed."); PregnancyDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Breeding", "PregnancyDuration", 120f, "Seconds before a fed breeding pair can create offspring."); MaxNearbyDeer = ((BaseUnityPlugin)this).Config.Bind<int>("Breeding", "MaxNearbyDeer", 6, "Maximum deer allowed within NearbyDeerRange before breeding stops."); NearbyDeerRange = ((BaseUnityPlugin)this).Config.Bind<float>("Breeding", "NearbyDeerRange", 20f, "Range used for breeding crowd checks."); BreedingCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Breeding", "BreedingCooldown", 300f, "Minimum seconds before the same deer can mate again."); WhistleRange = ((BaseUnityPlugin)this).Config.Bind<float>("Whistle", "WhistleRange", 80f, "Reserved for the future Fehu Whistle phase."); OnlyOwnerCanSummon = ((BaseUnityPlugin)this).Config.Bind<bool>("Whistle", "OnlyOwnerCanSummon", true, "Reserved for the future Fehu Whistle phase."); OnlyOwnerCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("Commands", "OnlyOwnerCommands", true, "Restrict custom Deer follow/stay commands to the feeding owner."); DeerFoodScanRange = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "DeerFoodScanRange", 8f, "Range in meters used by DeerFoodConsumer to scan dropped food."); ManualTamingFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Taming", "ManualTamingFallback", true, "Use ZDO-based taming progress when vanilla OnConsumedItem cannot run without MonsterAI."); TamingFoodProgressPerEat = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "TamingFoodProgressPerEat", 20f, "Percentage points of permanent taming progress granted by every eaten food item."); UseBoarLikeTaming = ((BaseUnityPlugin)this).Config.Bind<bool>("Taming", "UseBoarLikeTaming", true, "Increase deer tameness slowly while it is fed and calm."); DeerTamingDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "DeerTamingDuration", 1800f, "Seconds required to tame a continuously fed, calm deer."); DeerTamingTickSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Taming", "DeerTamingTickSeconds", 2f, "Server-authoritative interval for deer taming progress."); InstantTamingPerFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Taming", "InstantTamingPerFood", false, "Legacy behavior. Keep false for boar-like taming."); FollowStartDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Commands", "FollowStartDistance", 8f, "Distance at which a following deer starts moving to its owner."); FollowStopDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Commands", "FollowStopDistance", 3f, "Distance at which a following deer stops near its owner."); FollowTeleportDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Commands", "FollowTeleportDistance", 80f, "Legacy key: maximum follow distance. Deer stop beyond this distance; no teleportation."); OnlyOwnerCanRide = ((BaseUnityPlugin)this).Config.Bind<bool>("Riding", "OnlyOwnerCanRide", true, "Restrict riding to the deer owner."); DebugRawFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugRawFood", false, "Write raw ItemDrop name fields for every scanned food item."); DebugMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugMode", false, "Write general FehuMounts diagnostics to the BepInEx log."); DebugFood = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugFood", false, "Write food scans and per-feed diagnostics."); DebugBreeding = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugBreeding", false, "Write breeding checks and diagnostics."); DebugRiding = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugRiding", false, "Write mount eligibility and riding diagnostics."); DeerSettings.Bind(((BaseUnityPlugin)this).Config); ((Component)this).gameObject.AddComponent<DeerNetwork>(); Harmony.PatchAll(); Log.LogInfo((object)"FehuMounts 420.100.3 RC loaded. Fehu Whistle remains disabled."); } private void OnDestroy() { Harmony.UnpatchSelf(); } internal static bool IsDeer(Character? character) { if ((Object)(object)character == (Object)null) { return false; } return Utils.GetPrefabName(((Component)character).gameObject).Equals("Deer", StringComparison.Ordinal); } internal static void PatchDeerPrefab(ZNetScene scene) { GameObject val = FindPrefab(scene, "Deer"); if ((Object)(object)val == (Object)null) { WriteDebug("[FehuMounts] Deer prefab FAIL"); return; } WriteDebug("[FehuMounts] Deer prefab found"); EnsureDeerTameable(val, isRuntimeInstance: false); } internal static void EnsureDeerTameable(GameObject deer, bool isRuntimeInstance) { AnimalAI component = deer.GetComponent<AnimalAI>(); MonsterAI component2 = deer.GetComponent<MonsterAI>(); BaseAI component3 = deer.GetComponent<BaseAI>(); Character component4 = deer.GetComponent<Character>(); Tameable component5 = deer.GetComponent<Tameable>(); Component component6 = deer.GetComponent("Creature"); WriteComponentReport(component, component2, component3, component6, component4, component5); if ((Object)(object)component2 != (Object)null || (Object)(object)deer.GetComponent<Sadle>() != (Object)null) { Log.LogWarning((object)"[FehuMounts] Conflicting deer AI/saddle detected. Disable other deer taming/riding mods."); } else if (!((Object)(object)component == (Object)null) && !((Object)(object)component4 == (Object)null)) { Tameable val; if ((Object)(object)component5 == (Object)null) { val = deer.AddComponent<Tameable>(); WriteDebug("[FehuMounts] Tameable added"); } else { val = component5; WriteDebug("[FehuMounts] Tameable existing"); } AssignTameableToAnimalAi(component, val); ConfigureLikeBoar(val); List<ItemDrop> list = BuildFoodList(); if ((Object)(object)deer.GetComponent<DeerLifecycle>() == (Object)null) { deer.AddComponent<DeerLifecycle>(); } (deer.GetComponent<DeerFoodConsumer>() ?? deer.AddComponent<DeerFoodConsumer>()).Configure(val, list); if (deer.GetComponent<FehuMountRideController>() == null) { deer.AddComponent<FehuMountRideController>(); } if (deer.GetComponent<DeerCommandController>() == null) { deer.AddComponent<DeerCommandController>(); } if (deer.GetComponent<DeerLoyaltyController>() == null) { deer.AddComponent<DeerLoyaltyController>(); } if ((Object)(object)deer.GetComponent<DeerBreedingController>() == (Object)null) { deer.AddComponent<DeerBreedingController>(); } if ((Object)(object)deer.GetComponent<DeerVisualController>() == (Object)null) { deer.AddComponent<DeerVisualController>(); } ((Behaviour)val).enabled = true; WriteDebug("[FehuMounts] Deer patch SUCCESS"); if (isRuntimeInstance) { WriteDebug($"[FehuMounts] Runtime Deer patched instance={((Object)deer).name} hasTameable={(Object)(object)val != (Object)null} hasMonsterAI=false consumeItems={list.Count}"); WriteDebug("[FehuMounts] Deer final hover should be Tameable"); WriteDebug($"[FehuMounts] Deer has Tameable enabled={((Behaviour)val).enabled}"); WriteDebug($"[FehuMounts] Deer consumeItems count={list.Count}"); } } } private static void ConfigureLikeBoar(Tameable tameable) { GameObject? obj = FindPrefab(ZNetScene.instance, "Boar"); Tameable val = ((obj != null) ? obj.GetComponent<Tameable>() : null); if ((Object)(object)val != (Object)null) { tameable.m_fedDuration = val.m_fedDuration; tameable.m_tamingTime = val.m_tamingTime; tameable.m_tamingSpeedMultiplierRange = val.m_tamingSpeedMultiplierRange; tameable.m_tamingBoostMultiplier = val.m_tamingBoostMultiplier; tameable.m_tamedEffect = val.m_tamedEffect; tameable.m_sootheEffect = val.m_sootheEffect; tameable.m_petEffect = val.m_petEffect; } else { tameable.m_fedDuration = 600f; tameable.m_tamingTime = 1800f; } tameable.m_startsTamed = false; tameable.m_tamingTime = Mathf.Max(1f, DeerTamingDuration.Value); tameable.m_commandable = true; tameable.m_unsummonDistance = 0f; tameable.m_saddle = null; tameable.m_saddleItem = null; tameable.m_dropSaddleOnDeath = false; } private static void AssignTameableToAnimalAi(AnimalAI? animalAi, Tameable? tameable) { if (!((Object)(object)animalAi == (Object)null) && !((Object)(object)tameable == (Object)null)) { typeof(BaseAI).GetField("m_tamable", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(animalAi, tameable); } } private static List<ItemDrop> BuildFoodList() { List<ItemDrop> list = new List<ItemDrop>(); string[] foodNames = DeerSettings.FoodNames; foreach (string prefabName in foodNames) { AddFood(list, prefabName); } WriteDebug($"[FehuMounts] Deer Tameable consumeItems count={list.Count}"); WriteDebug("[FehuMounts] Foods assigned: Raspberry, Blueberries, Carrot, Turnip"); return list; } private static void WriteComponentReport(AnimalAI? animalAi, MonsterAI? monsterAi, BaseAI? baseAi, Component? creature, Character? character, Tameable? tameable) { WriteDebug("[FehuMounts] Deer components:"); WriteDebug("- AnimalAI " + (((Object)(object)animalAi != (Object)null) ? "FOUND" : "MISSING")); WriteDebug("- MonsterAI " + (((Object)(object)monsterAi != (Object)null) ? "FOUND" : "MISSING")); WriteDebug("- BaseAI " + (((Object)(object)baseAi != (Object)null) ? "FOUND" : "MISSING")); WriteDebug("- Creature " + (((Object)(object)creature != (Object)null) ? "FOUND" : "MISSING")); WriteDebug("- Character " + (((Object)(object)character != (Object)null) ? "FOUND" : "MISSING")); WriteDebug("- Tameable " + (((Object)(object)tameable != (Object)null) ? "FOUND" : "MISSING")); if ((Object)(object)animalAi != (Object)null) { WriteDebug("[FehuMounts] AnimalAI found"); } } private static void AddFood(List<ItemDrop> foods, string prefabName) { ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(prefabName) : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); if ((Object)(object)val2 == (Object)null) { WriteDebug("[FehuMounts] Added food " + prefabName + " FAIL"); return; } foods.Add(val2); string[] obj = new string[6] { "[FehuMounts] Added food ", prefabName, " OK objectdb_prefab=", ((Object)val).name, " drop_prefab=", null }; GameObject dropPrefab = val2.m_itemData.m_dropPrefab; obj[5] = ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? "null"; WriteDebug(string.Concat(obj)); } private static GameObject? FindPrefab(ZNetScene? scene, string prefabName) { if ((Object)(object)scene == (Object)null) { return null; } foreach (GameObject prefab in scene.m_prefabs) { if ((Object)(object)prefab != (Object)null && ((Object)prefab).name.Equals(prefabName, StringComparison.Ordinal)) { return prefab; } } return null; } private static void WriteDebug(string message) { if (DebugMode != null && DebugMode.Value) { Log.LogInfo((object)message); } } internal static void WriteFoodDebug(string message) { if (DebugFood.Value) { Log.LogInfo((object)message); } } internal static void WriteBreedingDebug(string message) { if (DebugBreeding.Value) { Log.LogInfo((object)message); } } internal static void WriteRidingDebug(string message) { if (DebugRiding.Value) { Log.LogInfo((object)message); } } internal static int StableHash(string text) { int num = 5381; foreach (char c in text) { num = ((num << 5) + num) ^ c; } return num; } internal static long GetOwnerId(ZNetView view) { if (!view.IsValid()) { return 0L; } return view.GetZDO().GetLong(OwnerHash, 0L); } internal static bool IsFehuTamed(ZNetView? view, Tameable? tameable) { if ((Object)(object)view != (Object)null && view.IsValid()) { if (!view.GetZDO().GetBool(IsTamedHash, false) && !view.GetZDO().GetBool(ZDOVars.s_tamed, false)) { if ((Object)(object)tameable != (Object)null) { return tameable.IsTamed(); } return false; } return true; } return false; } internal static void CompleteTaming(GameObject deer, long ownerId = 0L, string ownerName = "") { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) ZNetView component = deer.GetComponent<ZNetView>(); Tameable component2 = deer.GetComponent<Tameable>(); Character component3 = deer.GetComponent<Character>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null) && !((Object)(object)component3 == (Object)null) && component.IsValid() && component.IsOwner() && DeerNetwork.IsServer) { ZDO zDO = component.GetZDO(); if (ownerId == 0L) { ownerId = GetOwnerId(component); } if (ownerId != 0L) { zDO.Set(OwnerHash, ownerId); } if (!string.IsNullOrEmpty(ownerName)) { zDO.Set(OwnerNameHash, ownerName); } component3.SetTamed(true); zDO.Set("FehuMounts_TameProgress", 100f); zDO.Set(IsTamedHash, true); zDO.Set(ZDOVars.s_tamed, true); zDO.Set(ZDOVars.s_tameTimeLeft, 0f); zDO.Set(StayPositionHash, deer.transform.position); zDO.Set(CommandStateHash, "Stay"); zDO.Set(FollowHash, false); ((Behaviour)(deer.GetComponent<DeerCommandController>() ?? deer.AddComponent<DeerCommandController>())).enabled = true; int num = Mathf.Max(0, component3.GetLevel() - 1); if (EnableDeerRiding.Value && num >= MinStarsToRide.Value) { ((Behaviour)(deer.GetComponent<FehuMountRideController>() ?? deer.AddComponent<FehuMountRideController>())).enabled = true; } component2.m_tamedEffect.Create(deer.transform.position, deer.transform.rotation, deer.transform, 1f, 0, default(ZDOID)); Log.LogInfo((object)$"[FehuMounts] COMPLETE TAMING deer={zDO.m_uid} owner={ownerId} stars={num} vanillaTamed={component2.IsTamed()}"); } } } internal sealed class DeerFoodConsumer : MonoBehaviour { private const float ScanInterval = 2f; private readonly List<ItemDrop> _allowedFoods = new List<ItemDrop>(); private Tameable? _tameable; private ZNetView? _znetView; private float _nextScan; private float _nextTamingTick; public void Configure(Tameable tameable, List<ItemDrop> allowedFoods) { _tameable = tameable; _allowedFoods.Clear(); _allowedFoods.AddRange(allowedFoods); } private void Awake() { _tameable = ((Component)this).GetComponent<Tameable>(); _znetView = ((Component)this).GetComponent<ZNetView>(); } private void Update() { UpdateTamingProgress(); if (Time.time < _nextScan) { return; } _nextScan = Time.time + 2f; if (_tameable == null) { _tameable = ((Component)this).GetComponent<Tameable>(); } if (_znetView == null) { _znetView = ((Component)this).GetComponent<ZNetView>(); } bool flag = DeerNetwork.IsServer && (Object)(object)_tameable != (Object)null && (Object)(object)_znetView != (Object)null && _znetView.IsValid() && _znetView.IsOwner(); bool flag2 = (Object)(object)_tameable != (Object)null && _tameable.IsTamed(); bool flag3 = (Object)(object)_tameable != (Object)null && _tameable.IsHungry(); Plugin.WriteFoodDebug($"[FehuMounts] DeerFood scan owner={flag} tamed={flag2} hungry={flag3}"); if (!flag || (Object)(object)_tameable == (Object)null || (Object)(object)_znetView == (Object)null || (!flag2 && !Plugin.EnableDeerTaming.Value) || !flag3) { return; } ItemDrop val = FindNearbyAllowedFood(); if ((Object)(object)val == (Object)null) { return; } ZNetView component = ((Component)val).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return; } component.ClaimOwnership(); if (!component.IsOwner()) { return; } long num = component.GetZDO().GetLong(ZDOVars.s_creator, 0L); if (val.RemoveOne()) { if (Plugin.GetOwnerId(_znetView) == 0L && num != 0L && (Object)(object)Player.GetPlayer(num) != (Object)null) { _znetView.GetZDO().Set(Plugin.OwnerHash, num); _znetView.GetZDO().Set(Plugin.OwnerNameHash, Player.GetPlayer(num).GetPlayerName()); } if (ApplyManualFeedFallback()) { PlayHeartEffect(_tameable.m_sootheEffect); } } } private ItemDrop? FindNearbyAllowedFood() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) Collider[] array = Physics.OverlapSphere(((Component)this).transform.position, Plugin.DeerFoodScanRange.Value); ItemDrop result = null; float num = Plugin.DeerFoodScanRange.Value; HashSet<ItemDrop> hashSet = new HashSet<ItemDrop>(); Collider[] array2 = array; foreach (Collider val in array2) { ItemDrop val2 = ((Component)val).GetComponent<ItemDrop>() ?? ((Component)val).GetComponentInParent<ItemDrop>(); if (!((Object)(object)val2 == (Object)null) && hashSet.Add(val2)) { float num2 = Vector3.Distance(((Component)this).transform.position, ((Component)val2).transform.position); string match; bool flag = IsAllowed(val2, out match); string text = val2.m_itemData.m_shared.m_name ?? string.Empty; string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text); if (Plugin.DebugRawFood.Value) { Plugin.Log.LogInfo((object)("[FehuMounts] RAW item.name=" + ((Object)val2).name)); Plugin.Log.LogInfo((object)("[FehuMounts] RAW gameObject.name=" + ((Object)((Component)val2).gameObject).name)); Plugin.Log.LogInfo((object)("[FehuMounts] RAW shared_name=" + text)); Plugin.Log.LogInfo((object)("[FehuMounts] RAW localized_name=" + text2)); } Plugin.WriteFoodDebug($"[FehuMounts] Nearby item={Utils.GetPrefabName(((Component)val2).gameObject)} stack={val2.m_itemData.m_stack} allowed={flag} distance={num2:F1}"); Plugin.WriteFoodDebug("[FehuMounts] Food match=" + match); if (flag && num2 <= num) { result = val2; num = num2; } } } return result; } private bool IsAllowed(ItemDrop itemDrop, out string match) { string prefabName = Utils.GetPrefabName(((Component)itemDrop).gameObject); string[] foodNames = DeerSettings.FoodNames; foreach (string text in foodNames) { if (prefabName.Equals(text, StringComparison.Ordinal)) { match = "prefab-id:" + text; return true; } } match = "none"; return false; } private bool ApplyManualFeedFallback() { if ((Object)(object)_tameable == (Object)null || (Object)(object)_znetView == (Object)null || !_znetView.IsValid()) { return false; } try { ZDO zDO = _znetView.GetZDO(); zDO.Set(ZDOVars.s_tameLastFeeding, ((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTime().Ticks : DateTime.UtcNow.Ticks); float num = zDO.GetFloat("FehuMounts_TameProgress", 0f); if (Plugin.InstantTamingPerFood.Value) { num = Mathf.Min(100f, num + Mathf.Max(0f, Plugin.TamingFoodProgressPerEat.Value)); SetTamingProgress(zDO, num); } Plugin.WriteFoodDebug($"[FehuMounts] Deer ate food progress={num:F1}%"); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[FehuMounts] Deer manual feed fallback FAIL: " + ex.Message)); return false; } } private void UpdateTamingProgress() { //IL_014a: Unknown result type (might be due to invalid IL or missing references) if (Time.time < _nextTamingTick) { return; } _nextTamingTick = Time.time + Mathf.Max(0.25f, Plugin.DeerTamingTickSeconds.Value); if (_tameable == null) { _tameable = ((Component)this).GetComponent<Tameable>(); } if (_znetView == null) { _znetView = ((Component)this).GetComponent<ZNetView>(); } if ((Object)(object)_tameable == (Object)null || (Object)(object)_znetView == (Object)null || !_znetView.IsValid() || !_znetView.IsOwner() || !DeerNetwork.IsServer) { return; } ZDO zDO = _znetView.GetZDO(); if (DeerLifecycle.IsFawn(_znetView)) { return; } if (_tameable.IsTamed()) { if (!zDO.GetBool(Plugin.IsTamedHash, false)) { Plugin.CompleteTaming(((Component)this).gameObject, 0L); Plugin.Log.LogInfo((object)"[FehuMounts] Deer detected vanilla tame state / tameall"); } return; } float num = zDO.GetFloat("FehuMounts_TameProgress", 0f); if (num >= 100f) { Plugin.CompleteTaming(((Component)this).gameObject, 0L); } else { if (!Plugin.EnableDeerTaming.Value || !Plugin.UseBoarLikeTaming.Value || _tameable.IsHungry()) { return; } BaseAI component = ((Component)this).GetComponent<BaseAI>(); Player closestPlayer = Player.GetClosestPlayer(((Component)this).transform.position, 10f); if ((!((Object)(object)component != (Object)null) || !component.IsAlerted()) && !((Object)(object)closestPlayer != (Object)null)) { float num2 = 100f * Mathf.Max(0.25f, Plugin.DeerTamingTickSeconds.Value) / Mathf.Max(1f, Plugin.DeerTamingDuration.Value); num = Mathf.Min(100f, num + num2); SetTamingProgress(zDO, num); Plugin.WriteFoodDebug($"[FehuMounts] Deer taming progress {num:F1}%"); if (num >= 100f) { Plugin.CompleteTaming(((Component)this).gameObject, 0L); } } } } private void SetTamingProgress(ZDO zdo, float progress) { float num = Mathf.Clamp(progress, 0f, 100f); zdo.Set("FehuMounts_TameProgress", num); zdo.Set(ZDOVars.s_tameTimeLeft, Mathf.Max(0f, _tameable.m_tamingTime * (1f - num / 100f) - 0.01f)); } private void PlayHeartEffect(EffectList effect) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) try { GameObject[] array = effect.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, 0, default(ZDOID)); Plugin.WriteFoodDebug($"[FehuMounts] Heart effect played={array != null && array.Length != 0}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FehuMounts] Heart effect played=false error=" + ex.Message)); } } } internal sealed class DeerBreedingController : MonoBehaviour { private float _nextCheck; private void Update() { if (DeerNetwork.IsServer && !(Time.time < _nextCheck)) { _nextCheck = Time.time + 2f; ((Component)this).GetComponent<DeerLifecycle>()?.UpdateBreeding(); } } } internal sealed class DeerVisualController : MonoBehaviour { private Character? _character; private Vector3 _baseScale; private bool _initialized; private void Awake() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) _character = ((Component)this).GetComponent<Character>(); _baseScale = ((Component)this).transform.localScale; _initialized = true; } private void Start() { ApplyScale(); } private void OnEnable() { if (_initialized) { ApplyScale(); } } private void Update() { ApplyScale(); } private void ApplyScale() { //IL_00e0: 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 (_character == null) { _character = ((Component)this).GetComponent<Character>(); } if (!((Object)(object)_character == (Object)null)) { float num = Mathf.Clamp(_character.GetLevel() - 1, 0, Plugin.MaxDeerStars.Value) switch { 5 => 1.4f, 4 => 1.3f, 3 => 1.2f, 2 => 1.1f, 1 => 1.05f, _ => 1f, }; ZNetView component = ((Component)this).GetComponent<ZNetView>(); if (DeerLifecycle.IsFawn(component)) { float num2 = Mathf.Clamp01(component.GetZDO().GetFloat("FehuMounts.Age", 0f) / DeerSettings.GrowthSeconds.Value); num *= Mathf.Lerp(0.45f, 0.95f, num2); } ((Component)this).transform.localScale = _baseScale * num; } } } internal sealed class FehuMountRideController : MonoBehaviour { private Player? _localRider; private Transform? _seat; private float _nextInput; private bool _wasUse; private float _dismountUntil; public bool TryStartRide(Player rider) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) DeerNetwork.Request(((Component)this).GetComponent<ZNetView>(), "ride"); return true; } private void Update() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)this).GetComponent<ZNetView>(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)component == (Object)null || !component.IsValid() || (Object)(object)localPlayer == (Object)null) { return; } if (component.GetZDO().GetLong("FehuMounts_RiderID", 0L) != localPlayer.GetPlayerID()) { _dismountUntil = 0f; Detach(); return; } if (Time.time < _dismountUntil) { if (Time.time >= _nextInput) { _nextInput = Time.time + 0.2f; DeerNetwork.Request(component, "dismount"); } return; } if ((Object)(object)_localRider == (Object)null) { if (_seat == null) { _seat = CreateSeat(); } if (((Character)localPlayer).IsAttached()) { DeerNetwork.Request(component, "dismount"); return; } ((Character)localPlayer).AttachStart(_seat, ((Component)this).gameObject, true, false, false, "attach_asksvin", new Vector3(1.5f, 0.1f, 0f), (Transform)null); if (!((Character)localPlayer).IsAttached()) { return; } _localRider = localPlayer; _wasUse = ZInput.GetButton("Use"); _nextInput = Time.time + 0.15f; } if (!((Character)localPlayer).IsAttached() || ((Character)localPlayer).IsDead()) { RequestDismount(component); } else { if (Time.time < _nextInput) { return; } _nextInput = Time.time + 0.1f; if (!DeerSettings.InputAllowed) { DeerNetwork.Request(component, "input"); return; } bool button = ZInput.GetButton("Use"); if ((button && !_wasUse) || ZInput.GetButtonDown("Jump")) { RequestDismount(component); _wasUse = button; return; } _wasUse = button; Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(ZInput.GetButton("Right") ? 1f : (ZInput.GetButton("Left") ? (-1f) : 0f), 0f, ZInput.GetButton("Forward") ? 1f : (ZInput.GetButton("Backward") ? (-1f) : 0f)); Vector3 direction = ((Component)localPlayer).transform.TransformDirection(((Vector3)(ref val)).normalized); DeerNetwork.Request(component, "input", "", direction, ZInput.GetButton("Run")); } } private Transform CreateSeat() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("FehuMountSeat"); val.transform.SetParent(((Component)this).transform, false); val.transform.localPosition = new Vector3(0f, 1.05f, -0.15f); return val.transform; } private void RequestDismount(ZNetView view) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) _dismountUntil = Time.time + 2f; DeerNetwork.Request(view, "dismount"); Detach(); } private void Detach() { if ((Object)(object)_localRider != (Object)null && ((Character)_localRider).IsAttached()) { ((Character)_localRider).AttachStop(); } _localRider = null; } private void OnDisable() { Detach(); } private void OnGUI() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_localRider == (Object)null)) { ZNetView component = ((Component)this).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { float num = component.GetZDO().GetFloat("FehuMounts_MountStamina", 0f); GUI.Box(new Rect((float)Screen.width / 2f - 150f, (float)Screen.height - 100f, 300f, 52f), "Deer stamina: " + Mathf.CeilToInt(num) + " / " + DeerLifecycle.MaxStamina(((Component)this).GetComponent<Character>()) + "\nE / Jump: dismount"); } } } internal static void ApplyBonuses(Sadle sadle, Character deer) { } internal static float SpeedMultiplier(Character deer) { return DeerRules.SpeedMultiplier(deer.GetLevel() - 1); } } internal sealed class DeerLoyaltyController : MonoBehaviour { private const string LoyaltyKey = "FehuMounts_Loyalty"; private Tameable? _tameable; private ZNetView? _view; private Character? _character; private float _nextTick; private float _baseWalkSpeed; private float _baseRunSpeed; private void Awake() { _tameable = ((Component)this).GetComponent<Tameable>(); _view = ((Component)this).GetComponent<ZNetView>(); _character = ((Component)this).GetComponent<Character>(); if ((Object)(object)_character != (Object)null) { _baseWalkSpeed = _character.m_walkSpeed; _baseRunSpeed = _character.m_runSpeed; } } private void Update() { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if (Time.time < _nextTick) { return; } _nextTick = Time.time + 60f; if (!((Object)(object)_tameable == (Object)null) && !((Object)(object)_view == (Object)null) && !((Object)(object)_character == (Object)null) && _view.IsValid() && _view.IsOwner() && DeerNetwork.IsServer && Plugin.IsFehuTamed(_view, _tameable)) { long ownerId = Plugin.GetOwnerId(_view); Player val = ((ownerId == 0L) ? null : Player.GetPlayer(ownerId)); if ((Object)(object)val != (Object)null && Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position) <= 10f) { AddLoyalty(1); } int num = _view.GetZDO().GetInt("FehuMounts_Loyalty", 0); float num2 = ((num >= 90) ? 1.15f : ((num >= 60) ? 1.1f : ((num >= 30) ? 1.05f : 1f))); _character.m_walkSpeed = _baseWalkSpeed * num2; _character.m_runSpeed = _baseRunSpeed * num2; } } public void AddLoyalty(int amount) { if (!((Object)(object)_view == (Object)null) && _view.IsValid() && _view.IsOwner() && DeerNetwork.IsServer) { ZDO zDO = _view.GetZDO(); zDO.Set("FehuMounts_Loyalty", Mathf.Clamp(zDO.GetInt("FehuMounts_Loyalty", 0) + amount, 0, 100)); } } } internal sealed class DeerCommandController : MonoBehaviour { private Tameable? _tameable; private AnimalAI? _animalAi; private ZNetView? _znetView; private float _nextUpdate; private void Awake() { _tameable = ((Component)this).GetComponent<Tameable>(); _animalAi = ((Component)this).GetComponent<AnimalAI>(); _znetView = ((Component)this).GetComponent<ZNetView>(); } private void Update() { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) HandlePetInput(); HandleRenameInput(); if (Time.time < _nextUpdate) { return; } _nextUpdate = Time.time + 0.25f; if (_tameable == null) { _tameable = ((Component)this).GetComponent<Tameable>(); } if (_animalAi == null) { _animalAi = ((Component)this).GetComponent<AnimalAI>(); } if (_znetView == null) { _znetView = ((Component)this).GetComponent<ZNetView>(); } if (!DeerNetwork.IsServer || (Object)(object)_tameable == (Object)null || (Object)(object)_animalAi == (Object)null || (Object)(object)_znetView == (Object)null || !_znetView.IsValid() || !_znetView.IsOwner() || !Plugin.IsFehuTamed(_znetView, _tameable) || _znetView.GetZDO().GetLong("FehuMounts_RiderID", 0L) != 0L) { return; } ZDO zDO = _znetView.GetZDO(); if (!zDO.GetBool(Plugin.FollowHash, false)) { ((BaseAI)_animalAi).StopMoving(); Vector3 vec = zDO.GetVec3(Plugin.StayPositionHash, ((Component)this).transform.position); float num = Vector3.Distance(((Component)this).transform.position, vec); if (num > 6f) { _ = vec - ((Component)this).transform.position; DeerLifecycle.WalkTo(_animalAi, vec, run: false); Plugin.WriteRidingDebug($"[FehuMounts] STAY HOLD distanceFromStay={num:F1}"); } return; } long ownerId = Plugin.GetOwnerId(_znetView); Player val = ((ownerId != 0L) ? Player.GetPlayer(ownerId) : null); if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - ((Component)this).transform.position; val2.y = 0f; float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude > Plugin.FollowTeleportDistance.Value) { ((BaseAI)_animalAi).StopMoving(); } else if (magnitude <= Plugin.FollowStopDistance.Value) { ((BaseAI)_animalAi).StopMoving(); } else if (magnitude < Plugin.FollowStartDistance.Value) { ((BaseAI)_animalAi).StopMoving(); } else { DeerLifecycle.WalkTo(_animalAi, ((Component)val).transform.position, magnitude > Plugin.FollowStartDistance.Value * 1.5f); } } } private void HandlePetInput() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) if (DeerSettings.InputAllowed && Input.GetKeyDown((KeyCode)113) && !((Object)(object)_tameable == (Object)null) && !((Object)(object)_znetView == (Object)null) && Plugin.IsFehuTamed(_znetView, _tameable)) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)localPlayer.GetHoverCreature() != (Object)(object)((Component)this).GetComponent<Character>())) { DeerNetwork.Request(_znetView, "pet"); } } } private void HandleRenameInput() { if (!DeerSettings.InputAllowed || !Input.GetKeyDown((KeyCode)114) || (Object)(object)_tameable == (Object)null || (Object)(object)_znetView == (Object)null || !Plugin.IsFehuTamed(_znetView, _tameable)) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)localPlayer.GetHoverCreature() != (Object)(object)((Component)this).GetComponent<Character>() || (Object)(object)TextInput.instance == (Object)null) { return; } long ownerId = Plugin.GetOwnerId(_znetView); if (Plugin.OnlyOwnerCommands.Value && ownerId != 0L && ownerId != localPlayer.GetPlayerID()) { ((Character)localPlayer).Message((MessageType)2, "This deer belongs to another owner.", 0, (Sprite)null, false); return; } try { Plugin.Log.LogInfo((object)"[FehuMounts] R rename key detected"); TextInput.instance.RequestText((TextReceiver)(object)new DeerRenameReceiver(_znetView), "Rename Deer", 20); Plugin.Log.LogInfo((object)"[FehuMounts] Rename dialog opened"); } catch (Exception ex) { Plugin.Log.LogError((object)("[FehuMounts] Rename failed: " + ex.Message)); } } public static bool ToggleFollow(Tameable tameable, Player player, bool showMessage) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)tameable).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !Plugin.IsFehuTamed(component, tameable)) { return false; } DeerNetwork.Request(component, "follow"); return true; } } internal sealed class DeerRenameReceiver : TextReceiver { private readonly ZNetView _view; public DeerRenameReceiver(ZNetView view) { _view = view; } public string GetText() { if (!_view.IsValid()) { return ""; } return _view.GetZDO().GetString("FehuMounts.Name", ""); } public void SetText(string text) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) DeerNetwork.Request(_view, "rename", text); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetSceneAwakePatch { private static void Postfix(ZNetScene __instance) { Plugin.PatchDeerPrefab(__instance); } } [HarmonyPatch(typeof(Character), "Awake")] internal static class CharacterAwakePatch { private static void Postfix(Character __instance) { if (Plugin.IsDeer(__instance)) { Plugin.EnsureDeerTameable(((Component)__instance).gameObject, isRuntimeInstance: true); } } } [HarmonyPatch(typeof(Tameable), "Update")] internal static class DeerTameableUpdatePatch { private static bool Prefix(Tameable __instance) { return (Object)(object)((Component)__instance).GetComponent<DeerLifecycle>() == (Object)null; } } [HarmonyPatch(typeof(AnimalAI), "UpdateAI")] internal static class FehuTamedDeerCalmPatch { private static bool Prefix(AnimalAI __instance) { Tameable component = ((Component)__instance).GetComponent<Tameable>(); ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)((Component)__instance).GetComponent<DeerLifecycle>() == (Object)null || !Plugin.IsFehuTamed(component2, component)) { return true; } return false; } } [HarmonyPatch(typeof(Tameable), "GetHoverText")] internal static class DeerTameableHoverPatch { private static bool Prefix(Tameable __instance, ref string __result) { Character component = ((Component)__instance).GetComponent<Character>(); ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>(); if (!Plugin.IsDeer(component) || (Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid()) { return true; } float num = component2.GetZDO().GetFloat("FehuMounts_TameProgress", 0f); if (!Plugin.IsFehuTamed(component2, __instance)) { __result = $"Deer (Tameness {Mathf.FloorToInt(num)}%, Acclimatizing)"; return false; } __result = DeerLifecycle.Describe(component, component2); return false; } } [HarmonyPatch(typeof(Tameable), "Command")] internal static class DeerTameableCommandPatch { private static bool Prefix(Tameable __instance, Humanoid user, bool message) { if (!Plugin.IsDeer(((Component)__instance).GetComponent<Character>())) { return true; } Player val = (Player)(object)((user is Player) ? user : null); if (val != null) { DeerCommandController.ToggleFollow(__instance, val, message); } return false; } } [HarmonyPatch(typeof(Tameable), "Interact")] internal static class DeerTameableInteractPatch { private static bool Prefix(Tameable __instance, object[] __args, ref bool __result) { Character component = ((Component)__instance).GetComponent<Character>(); ZNetView component2 = ((Component)__instance).GetComponent<ZNetView>(); if (!Plugin.IsDeer(component) || (Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !Plugin.IsFehuTamed(component2, __instance)) { return true; } Player val = null; foreach (object obj in __args) { Player val2 = (Player)((obj is Player) ? obj : null); if (val2 != null) { val = val2; break; } } if ((Object)(object)val == (Object)null) { return true; } if (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) { __result = false; return false; } if ((Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303)) && component.GetLevel() - 1 >= Plugin.MinStarsToRide.Value) { FehuMountRideController fehuMountRideController = ((Component)__instance).GetComponent<FehuMountRideController>() ?? ((Component)__instance).gameObject.AddComponent<FehuMountRideController>(); __result = fehuMountRideController.TryStartRide(val); return false; } __result = DeerCommandController.ToggleFollow(__instance, val, showMessage: true); return false; } } [HarmonyPatch(typeof(Character), "GetJogSpeedFactor")] internal static class DeerJogSpeedPatch { private static void Postfix(Character __instance, ref float __result) { if (Plugin.IsDeer(__instance) && DeerLifecycle.HasRider(__instance)) { __result *= FehuMountRideController.SpeedMultiplier(__instance); __result *= DeerLifecycle.LoyaltyBonus(__instance); } } } [HarmonyPatch(typeof(Character), "GetRunSpeedFactor")] internal static class DeerRunSpeedPatch { private static void Postfix(Character __instance, ref float __result) { if (Plugin.IsDeer(__instance) && DeerLifecycle.HasRider(__instance)) { __result *= FehuMountRideController.SpeedMultiplier(__instance); __result *= DeerLifecycle.LoyaltyBonus(__instance); } } } }