diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs index 376fa6a0d..12fa56352 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs @@ -71,9 +71,22 @@ public abstract partial class BaseAI if (TryMove(d)) { - Mobile.CurrentSpeed = Mobile.Hits < Mobile.HitsMax * 0.3 - ? BadlyHurtMoveDelay(Mobile) - : Mobile.Warmode || Mobile.Combatant != null ? Mobile.ActiveSpeed : Mobile.PassiveSpeed; + if (Core.AOS && IsFollowingMaster()) + { + Mobile.CurrentSpeed = 0.1; + } + else if (Mobile.Hits < Mobile.HitsMax * 0.3) + { + Mobile.CurrentSpeed = BadlyHurtMoveDelay(Mobile); + } + else if (Mobile.Warmode || Mobile.Combatant != null) + { + Mobile.CurrentSpeed = Mobile.ActiveSpeed; + } + else + { + Mobile.CurrentSpeed = Mobile.PassiveSpeed; + } return MoveResult.Success; } diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs index 5a6915d0a..3c08d944b 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs @@ -17,37 +17,23 @@ using System; namespace Server.Mobiles; -internal sealed class AITimer : Timer +public sealed class AITimer : Timer { private readonly BaseAI _owner; private int _detectHiddenMinDelay; private int _detectHiddenMaxDelay; public AITimer(BaseAI owner) : base(TimeSpan.FromMilliseconds(Utility.Random(3000)), - TimeSpan.FromMilliseconds(GetBaseInterval(owner))) + TimeSpan.FromSeconds(owner.Mobile.CurrentSpeed)) { _owner = owner; _owner._nextDetectHidden = Core.TickCount; } - private static double GetBaseInterval(BaseAI owner) + public void Activate() { - double interval; - - if (owner.IsFollowingMaster()) - { - interval = owner.Mobile.CurrentSpeed * (Core.AOS ? 100 : 400); - } - else if (owner.Mobile.CurrentSpeed <= 0.4) - { - interval = owner.Mobile.CurrentSpeed * 1000; - } - else - { - interval = owner.Mobile.CurrentSpeed * 3000; - } - - return Math.Max(interval, Core.AOS ? 100 : 200); + Interval = TimeSpan.FromSeconds(_owner.Mobile.CurrentSpeed); + Start(); } protected override void OnTick() @@ -58,8 +44,6 @@ internal sealed class AITimer : Timer return; } - Interval = TimeSpan.FromMilliseconds(GetBaseInterval(_owner)); - _owner.Mobile.OnThink(); if (ShouldStop()) @@ -68,11 +52,11 @@ internal sealed class AITimer : Timer return; } + Interval = TimeSpan.FromSeconds(_owner.Mobile.CurrentSpeed); HandleBardEffects(); if (_owner.Mobile.Controlled ? !_owner.Obey() : !_owner.Think()) { - Stop(); return; } @@ -86,14 +70,14 @@ internal sealed class AITimer : Timer return true; } - if (_owner.Mobile.Map != null && _owner.Mobile.Map != Map.Internal && - (!_owner.Mobile.PlayerRangeSensitive || _owner.Mobile.Map.GetSector(_owner.Mobile.Location).Active)) + if (_owner.Mobile.Map == null || _owner.Mobile.Map == Map.Internal || _owner.Mobile.PlayerRangeSensitive && + !_owner.Mobile.Controlled && !_owner.Mobile.Map.GetSector(_owner.Mobile.Location).Active) { - return false; + _owner.Deactivate(); + return true; } - _owner.Deactivate(); - return true; + return false; } private void HandleBardEffects() diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs index d849354d9..57b6f6a25 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs @@ -28,10 +28,11 @@ public abstract partial class BaseAI { private ActionType _action; public long _nextDetectHidden; - public PathFollower Path { get; protected set; } - public readonly Timer _timer; public DateTime _lastOrder = DateTime.MinValue; public Mobile _commandIssuer; + + public PathFollower Path { get; protected set; } + public AITimer AITimer { get; } public long NextMove { get; set; } public BaseCreature Mobile { get; } @@ -43,11 +44,11 @@ public abstract partial class BaseAI public BaseAI(BaseCreature m) { Mobile = m; - _timer = new AITimer(this); + AITimer = new AITimer(this); if (!m.PlayerRangeSensitive || !World.Loading && m.Map != null && m.Map != Map.Internal && m.Map.GetSector(m.Location).Active) { - _timer.Start(); + AITimer.Start(); } if (Action != ActionType.Wander) @@ -360,7 +361,7 @@ public abstract partial class BaseAI else if (Mobile.IsAnimatedDead) { FollowMaster(); - + if (CheckMove() && CanMoveNow(out _) && !Mobile.CheckIdle()) { WalkRandomInHome(3, 2, 1); @@ -437,7 +438,7 @@ public abstract partial class BaseAI return true; } - public bool IsValidCombatant(Mobile combatant) => IsValidFocusMob(combatant) + public bool IsValidCombatant(Mobile combatant) => IsValidFocusMob(combatant) && (Mobile.InLOS(combatant) || IsHostile(combatant)); public bool IsValidFocusMob(Mobile focusMob) => @@ -594,7 +595,7 @@ public abstract partial class BaseAI Action = ActionType.Wander; return false; } - + Action = ActionType.Flee; return true; } @@ -618,14 +619,14 @@ public abstract partial class BaseAI { return false; } - + var hitPercent = (double)Mobile.Hits / Mobile.HitsMax; - + if (hitPercent > FleeHealthThreshold) { return false; } - + return Utility.RandomDouble() < FleeChance; } @@ -637,12 +638,12 @@ public abstract partial class BaseAI { return false; } - + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { return Utility.RandomDouble() < BackoffChance; } - + return false; } @@ -660,7 +661,7 @@ public abstract partial class BaseAI else { DebugSay("Focus target missing. Wandering..."); - + Action = ActionType.Wander; } @@ -882,8 +883,8 @@ public abstract partial class BaseAI return !valid && (acqType != FightMode.Evil || (bc?.GetMaster()?.Karma ?? m.Karma) >= 0); } - private bool IsHostile(Mobile from) => Mobile.Combatant == from || from.Combatant == Mobile - || IsAggressor(from) || IsAggressed(from); + private bool IsHostile(Mobile from) => + Mobile.Combatant == from || from.Combatant == Mobile || IsAggressor(from) || IsAggressed(from); private bool IsAggressor(Mobile from) { @@ -956,14 +957,9 @@ public abstract partial class BaseAI public virtual void Deactivate() { - if (!Mobile.PlayerRangeSensitive) - { - return; - } - if (Mobile.Map == Map.Internal || !Mobile.Controlled && !Mobile.Map.GetSector(Mobile.Location).Active) { - _timer.Stop(); + AITimer.Stop(); } if (ShouldReturnToHome(Mobile.Spawner)) @@ -990,19 +986,19 @@ public abstract partial class BaseAI Mobile.MoveToWorld(loc, spawner.Map); } - _timer.Start(); + Activate(); } public virtual void Activate() { - if (!_timer.Running) + if (!AITimer.Running) { - _timer.Start(); + AITimer.Activate(); } } public virtual void OnCurrentSpeedChanged() { - _timer.Interval = TimeSpan.FromMilliseconds(Mobile.CurrentSpeed * 1000); + AITimer.Interval = TimeSpan.FromSeconds(Mobile.CurrentSpeed); } } diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs index 214506b90..584fe486b 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs @@ -26,6 +26,8 @@ public abstract partial class BaseAI return; } + Activate(); + switch (Mobile.ControlOrder) { case OrderType.None: diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs index 61787dbf6..96844bf6f 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs @@ -337,7 +337,7 @@ public abstract partial class BaseAI { if (Mobile.IsDeadPet) { - return false; + return true; } if (IsInvalidControlTarget(Mobile.ControlTarget)) @@ -537,7 +537,7 @@ public abstract partial class BaseAI // 1043248: The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ // 1043249: The pet will not accept you as a master because it does not trust you.~3_BLANK~ Mobile.ControlOrder = OrderType.None; - return false; + return true; } if (!Mobile.CanBeControlledBy(from)) @@ -546,7 +546,7 @@ public abstract partial class BaseAI // 1043250: The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ // 1043251: The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ Mobile.ControlOrder = OrderType.None; - return false; + return true; } if (Mobile.Combatant != null || Mobile.Aggressors.Count > 0 || @@ -555,7 +555,7 @@ public abstract partial class BaseAI from.SendMessage("You can not transfer a pet while in combat."); to.SendMessage("You can not transfer a pet while in combat."); Mobile.ControlOrder = OrderType.None; - return false; + return true; } var fromState = from.NetState; @@ -564,7 +564,7 @@ public abstract partial class BaseAI if (fromState == null || toState == null) { Mobile.ControlOrder = OrderType.None; - return false; + return true; } if (from.HasTrade || to.HasTrade) @@ -574,7 +574,7 @@ public abstract partial class BaseAI to.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending Mobile.ControlOrder = OrderType.None; - return false; + return true; } var container = fromState.AddTrade(toState); diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index dd6aafebb..be1733290 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -2245,7 +2245,7 @@ namespace Server.Mobiles public void ChangeAIType(AIType newAI) { - AIObject?._timer.Stop(); + AIObject?.AITimer.Stop(); if (ForcedAI != null) { @@ -2368,7 +2368,7 @@ namespace Server.Mobiles { if (AIObject != null) { - AIObject._timer?.Stop(); + AIObject.AITimer?.Stop(); AIObject = null; } @@ -3863,6 +3863,8 @@ namespace Server.Mobiles OnAfterResurrect(); + AIObject?.Activate(); + var owner = ControlMaster; if (owner?.Deleted == false && owner.Map == Map && owner.InRange(this, 12) && CanSee(owner) && InLOS(owner)) @@ -3914,7 +3916,7 @@ namespace Server.Mobiles { SetLocation(Home, true); - if (!Map.GetSector(X, Y).Active) + if (PlayerRangeSensitive && !Map.GetSector(X, Y).Active) { AIObject?.Deactivate(); }