From e434c94fd477121a64c5cb25e87e1255e38cf501 Mon Sep 17 00:00:00 2001 From: Vanquish-6 Date: Thu, 27 Mar 2025 18:05:03 -0700 Subject: [PATCH 1/6] Add monster navigation improvements and stuck detection --- .../WorldObjects/Monster_Awareness.cs | 8 +++ .../WorldObjects/Monster_Navigation.cs | 63 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/Source/ACE.Server/WorldObjects/Monster_Awareness.cs b/Source/ACE.Server/WorldObjects/Monster_Awareness.cs index e1bbb77d68..6fffbe92fe 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Awareness.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Awareness.cs @@ -59,6 +59,14 @@ public virtual void Sleep() PhysicsObj.CachedVelocity = Vector3.Zero; ClearRetaliateTargets(); + + // Heal to full health when returning home + if (Health.Current < Health.MaxValue) + { + Health.Current = Health.MaxValue; + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}).Sleep() - Healed to full health"); + } } public Tolerance Tolerance diff --git a/Source/ACE.Server/WorldObjects/Monster_Navigation.cs b/Source/ACE.Server/WorldObjects/Monster_Navigation.cs index b0e378998a..5e7e6643f8 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Navigation.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Navigation.cs @@ -64,6 +64,13 @@ partial class Creature public double NextMoveTime; public double NextCancelTime; + /// + /// Fields for enhanced stuck detection + /// + public Vector3 LastStuckCheckPosition; + public int StuckCounter = 0; + public double LastStuckCheckTime; + /// /// Starts the process of monster turning towards target /// @@ -248,8 +255,64 @@ public void Movement() return; } + // Standard stuck check from MoveToManager if (PhysicsObj.MovementManager.MoveToManager.FailProgressCount > 0 && Timers.RunningTime > NextCancelTime) + { + // Instead of just canceling, also increment stuck counter + StuckCounter++; CancelMoveTo(); + + // If stuck multiple times, force return home - about 15 seconds (5 detections with ~3 seconds each) + if (StuckCounter >= 5) + { + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}) - Stuck multiple times, returning home"); + + StuckCounter = 0; + MoveToHome(); + } + return; + } + + // Additional stuck detection - check if position hasn't changed much over time + if (Timers.RunningTime - LastStuckCheckTime >= 5.0) // Check every 5 seconds + { + var currentPos = Location.ToGlobal(); + + if (LastStuckCheckPosition != Vector3.Zero) + { + var distMoved = Vector3.Distance(currentPos, LastStuckCheckPosition); + + // If barely moved in the last 5 seconds but still trying to move + if (distMoved < 1.5f && IsMoving) + { + StuckCounter++; + + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}) - Barely moved ({distMoved:F2} units), StuckCounter={StuckCounter}"); + + // If stuck in place multiple times, force return home - about 15 seconds (3 detections of 5 seconds each) + if (StuckCounter >= 3) + { + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}) - Stuck in place for ~15 seconds, returning home"); + + StuckCounter = 0; + CancelMoveTo(); + MoveToHome(); + return; + } + } + else + { + // Reset counter if making good progress + StuckCounter = 0; + } + } + + LastStuckCheckPosition = currentPos; + LastStuckCheckTime = Timers.RunningTime; + } } public void UpdatePosition(bool netsend = true) From 9ba4f3b52a4709dee0fb14e8e59a31b9042e8e53 Mon Sep 17 00:00:00 2001 From: Vanquish-6 Date: Fri, 28 Mar 2025 09:51:07 -0700 Subject: [PATCH 2/6] Improve monster stuck detection, add fast healing when returning home, and make returning monsters respond to distress calls --- .../WorldObjects/Monster_Awareness.cs | 90 +++++++++++++++++-- 1 file changed, 84 insertions(+), 6 deletions(-) diff --git a/Source/ACE.Server/WorldObjects/Monster_Awareness.cs b/Source/ACE.Server/WorldObjects/Monster_Awareness.cs index 6fffbe92fe..947e4fc10b 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Awareness.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Awareness.cs @@ -60,12 +60,69 @@ public virtual void Sleep() ClearRetaliateTargets(); - // Heal to full health when returning home + // Start fast health regeneration when monster returns home if (Health.Current < Health.MaxValue) { - Health.Current = Health.MaxValue; + // Set a high regeneration rate (much higher than normal) + var originalHealthRate = GetProperty(PropertyFloat.HealthRate) ?? 0; + + // Ensure a minimum regen rate regardless of original value + // Using an extremely high value to ensure quick healing + var fastHealthRate = Math.Max(10.0f, originalHealthRate * 50.0f); + + SetProperty(PropertyFloat.HealthRate, fastHealthRate); + + // Create a heartbeat action to monitor health and reset regen rate and damage history when fully healed + var actionChain = new ActionChain(); + actionChain.AddDelaySeconds(0.5f); // Check more frequently + + actionChain.AddAction(this, () => + { + // Direct healing - add 15% of missing health per tick + if (Health.Current < Health.MaxValue) + { + var missingHealth = Health.MaxValue - Health.Current; + var healAmount = (uint)(missingHealth * 0.15f); + + // Ensure we heal at least 10% of max health per tick + healAmount = Math.Max(healAmount, (uint)(Health.MaxValue * 0.10f)); + + // Apply direct healing + UpdateVitalDelta(Health, (int)healAmount); + + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}).Sleep() - Direct healing for {healAmount} points"); + } + + // If monster is at full health, reset regeneration rate and damage history + if (Health.Current >= Health.MaxValue) + { + // Reset health rate to original value + if (originalHealthRate > 0) + SetProperty(PropertyFloat.HealthRate, originalHealthRate); + else + RemoveProperty(PropertyFloat.HealthRate); + + // Reset damage history + DamageHistory.Reset(); + + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}).Sleep() - Fully healed and damage history cleared"); + } + else + { + // Still healing, check again + var newChain = new ActionChain(); + newChain.AddDelaySeconds(0.5f); // Check more frequently + newChain.AddAction(this, () => Sleep()); + newChain.EnqueueChain(); + } + }); + + actionChain.EnqueueChain(); + if (DebugMove) - Console.WriteLine($"{Name} ({Guid}).Sleep() - Healed to full health"); + Console.WriteLine($"{Name} ({Guid}).Sleep() - Started fast health regeneration"); } } @@ -462,10 +519,22 @@ public void AlertFriendly() foreach (var obj in visibleObjs) { var nearbyCreature = obj.WeenieObj.WorldObject as Creature; - if (nearbyCreature == null || nearbyCreature.IsAwake || !nearbyCreature.Attackable && nearbyCreature.TargetingTactic == TargetingTactic.None) + + // Skip creatures that can't be alerted: + // - null creatures + // - creatures with tolerance exclusions + if (nearbyCreature == null || (nearbyCreature.Tolerance & AlertExclude) != 0) continue; - - if ((nearbyCreature.Tolerance & AlertExclude) != 0) + + // Original condition modified to allow monsters in Return state to hear distress calls + // Old condition: if (nearbyCreature == null || nearbyCreature.IsAwake || !nearbyCreature.Attackable && nearbyCreature.TargetingTactic == TargetingTactic.None) + // continue; + + // Modified checks: + // - skip already awake monsters UNLESS they're returning home + // - skip non-attackable monsters with no targeting tactic + if ((nearbyCreature.IsAwake && nearbyCreature.MonsterState != State.Return) || + (!nearbyCreature.Attackable && nearbyCreature.TargetingTactic == TargetingTactic.None)) continue; if (CreatureType != null && CreatureType == nearbyCreature.CreatureType || @@ -497,6 +566,15 @@ public void AlertFriendly() alerted = true; + // If monster was returning home, cancel that and respond to the distress call + if (nearbyCreature.MonsterState == State.Return) + { + if (nearbyCreature.DebugMove) + Console.WriteLine($"{nearbyCreature.Name} ({nearbyCreature.Guid}) - Interrupting return home to respond to distress call"); + + nearbyCreature.CancelMoveTo(); + } + nearbyCreature.AttackTarget = AttackTarget; nearbyCreature.WakeUp(false); } From 9317bd309f767a5eea8e04ba86ab25cd420e1f0e Mon Sep 17 00:00:00 2001 From: Vanquish-6 Date: Thu, 1 May 2025 08:28:17 -0700 Subject: [PATCH 3/6] Fixes, added logic for weenie properties with stuck and aiimobile set --- .../WorldObjects/Monster_Awareness.cs | 110 +++++++++++------- .../ACE.Server/WorldObjects/Monster_Combat.cs | 35 ++++++ .../WorldObjects/Monster_Navigation.cs | 8 ++ 3 files changed, 109 insertions(+), 44 deletions(-) diff --git a/Source/ACE.Server/WorldObjects/Monster_Awareness.cs b/Source/ACE.Server/WorldObjects/Monster_Awareness.cs index 947e4fc10b..2c9719ff0b 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Awareness.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Awareness.cs @@ -23,6 +23,9 @@ partial class Creature /// public bool IsAwake = false; + private float? _originalHealthRate = null; // Store original health rate before fast heal + private bool _isFastHealingActive = false; // Flag to control the fast healing action chain + /// /// Transitions a monster from idle to awake state /// @@ -63,12 +66,16 @@ public virtual void Sleep() // Start fast health regeneration when monster returns home if (Health.Current < Health.MaxValue) { + // Store the original rate before potentially modifying it + _originalHealthRate = (float?)GetProperty(PropertyFloat.HealthRate); + _isFastHealingActive = true; // Activate fast healing + // Set a high regeneration rate (much higher than normal) - var originalHealthRate = GetProperty(PropertyFloat.HealthRate) ?? 0; + var originalHealthRateValue = _originalHealthRate ?? 0; // Use 0 if null // Ensure a minimum regen rate regardless of original value // Using an extremely high value to ensure quick healing - var fastHealthRate = Math.Max(10.0f, originalHealthRate * 50.0f); + var fastHealthRate = Math.Max(10.0f, originalHealthRateValue * 50.0f); SetProperty(PropertyFloat.HealthRate, fastHealthRate); @@ -76,48 +83,7 @@ public virtual void Sleep() var actionChain = new ActionChain(); actionChain.AddDelaySeconds(0.5f); // Check more frequently - actionChain.AddAction(this, () => - { - // Direct healing - add 15% of missing health per tick - if (Health.Current < Health.MaxValue) - { - var missingHealth = Health.MaxValue - Health.Current; - var healAmount = (uint)(missingHealth * 0.15f); - - // Ensure we heal at least 10% of max health per tick - healAmount = Math.Max(healAmount, (uint)(Health.MaxValue * 0.10f)); - - // Apply direct healing - UpdateVitalDelta(Health, (int)healAmount); - - if (DebugMove) - Console.WriteLine($"{Name} ({Guid}).Sleep() - Direct healing for {healAmount} points"); - } - - // If monster is at full health, reset regeneration rate and damage history - if (Health.Current >= Health.MaxValue) - { - // Reset health rate to original value - if (originalHealthRate > 0) - SetProperty(PropertyFloat.HealthRate, originalHealthRate); - else - RemoveProperty(PropertyFloat.HealthRate); - - // Reset damage history - DamageHistory.Reset(); - - if (DebugMove) - Console.WriteLine($"{Name} ({Guid}).Sleep() - Fully healed and damage history cleared"); - } - else - { - // Still healing, check again - var newChain = new ActionChain(); - newChain.AddDelaySeconds(0.5f); // Check more frequently - newChain.AddAction(this, () => Sleep()); - newChain.EnqueueChain(); - } - }); + actionChain.AddAction(this, ProcessFastHealingTick); actionChain.EnqueueChain(); @@ -126,6 +92,62 @@ public virtual void Sleep() } } + /// + /// Handles a single tick of the fast healing process initiated by Sleep(). + /// + private void ProcessFastHealingTick() + { + // Exit if fast healing was cancelled externally (e.g., by taking damage) + if (!_isFastHealingActive) return; + + // Direct healing - add 15% of missing health per tick + if (Health.Current < Health.MaxValue) + { + var missingHealth = Health.MaxValue - Health.Current; + var healAmount = (uint)(missingHealth * 0.15f); + + // Ensure we heal at least 10% of max health per tick + healAmount = Math.Max(healAmount, (uint)(Health.MaxValue * 0.10f)); + + // Apply direct healing + UpdateVitalDelta(Health, (int)healAmount); + + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}).ProcessFastHealingTick() - Direct healing for {healAmount} points"); + } + + // If monster is at full health, stop the process + if (Health.Current >= Health.MaxValue) + { + _isFastHealingActive = false; // Deactivate flag + + // Reset health rate to original value using the stored value + if (_originalHealthRate.HasValue) + SetProperty(PropertyFloat.HealthRate, _originalHealthRate.Value); + else + RemoveProperty(PropertyFloat.HealthRate); + + _originalHealthRate = null; // Clear the stored value + + // Reset damage history + DamageHistory.Reset(); + + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}).ProcessFastHealingTick() - Fully healed, stopping fast heal."); + } + else + { + // Still healing, schedule the next tick if still active + if (_isFastHealingActive) + { + var nextTickChain = new ActionChain(); + nextTickChain.AddDelaySeconds(0.5f); // Check more frequently + nextTickChain.AddAction(this, ProcessFastHealingTick); // Schedule this function again + nextTickChain.EnqueueChain(); + } + } + } + public Tolerance Tolerance { get => (Tolerance)(GetProperty(PropertyInt.Tolerance) ?? 0); diff --git a/Source/ACE.Server/WorldObjects/Monster_Combat.cs b/Source/ACE.Server/WorldObjects/Monster_Combat.cs index a9ebe8c61d..88f0c0c4b8 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Combat.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Combat.cs @@ -388,6 +388,41 @@ public virtual uint TakeDamage(WorldObject source, DamageType damageType, float DamageHistory.OnHeal((uint)-damage); } + // Step 1: Stop fast healing if it's active when damage is taken + bool wasFastHealing = _isFastHealingActive; + if (_isFastHealingActive) + { + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}) - Interrupting fast healing due to taking damage."); + + _isFastHealingActive = false; + if (_originalHealthRate.HasValue) + { + SetProperty(PropertyFloat.HealthRate, _originalHealthRate.Value); + _originalHealthRate = null; + } + } + + // Step 2: React to the damage source if needed (wake up, target attacker, stop returning) + if (source is Creature attacker) + { + // React if not already awake/fighting OR if was returning home (even if fast healing wasn't active yet) + if (!IsAwake || MonsterState == State.Return) + { + if (DebugMove) + Console.WriteLine($"{Name} ({Guid}) - Reacting to damage from {attacker.Name} ({attacker.Guid}). Current State: {MonsterState}, IsAwake: {IsAwake}"); + + // Stop returning home if that's what we were doing + if (MonsterState == State.Return) + { + CancelMoveTo(); + } + + AttackTarget = attacker; + WakeUp(false); // Wake up, set state to Awake + } + } + if (Health.Current <= 0) { OnDeath(DamageHistory.LastDamager, damageType, crit); diff --git a/Source/ACE.Server/WorldObjects/Monster_Navigation.cs b/Source/ACE.Server/WorldObjects/Monster_Navigation.cs index 5e7e6643f8..44e1fc170e 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Navigation.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Navigation.cs @@ -245,6 +245,14 @@ public Vector3 GetDestination() /// public void Movement() { + // Skip stuck checks for intentionally immobile/stuck monsters + if ((GetProperty(PropertyBool.Stuck) ?? false) || (GetProperty(PropertyBool.AiImmobile) ?? false)) + { + // Optionally, ensure they aren't trying to move if immobile + if (IsMoving) CancelMoveTo(); + return; + } + //if (!IsRanged) UpdatePosition(); From 4e027d5e6b1fe843954cc7e746dbd32976e306bd Mon Sep 17 00:00:00 2001 From: Vanquish-6 Date: Thu, 1 May 2025 10:11:35 -0700 Subject: [PATCH 4/6] Refactor Monster Healing & Add Immobile Stuck Fix Refactors fast monster healing based on reviewer feedback, replacing the flag/ActionChain approach with integration into VitalHeartBeat via GetStanceMod when MonsterState is Idle. Ensures monsters still correctly interrupt healing (implicitly via state change) and retaliate if attacked during return or idle heal. Adds logic to Movement() to skip stuck detection for monsters flagged with Stuck or AiImmobile. Simplifies implementation, reduces state variables, and improves maintainability. --- .../WorldObjects/Creature_Vitals.cs | 12 ++- .../WorldObjects/Monster_Awareness.cs | 88 +------------------ .../ACE.Server/WorldObjects/Monster_Combat.cs | 15 ---- 3 files changed, 12 insertions(+), 103 deletions(-) diff --git a/Source/ACE.Server/WorldObjects/Creature_Vitals.cs b/Source/ACE.Server/WorldObjects/Creature_Vitals.cs index 4f33a9d6e3..6427b0976c 100644 --- a/Source/ACE.Server/WorldObjects/Creature_Vitals.cs +++ b/Source/ACE.Server/WorldObjects/Creature_Vitals.cs @@ -206,9 +206,17 @@ public float GetAttributeMod(CreatureVital vital) /// public float GetStanceMod(CreatureVital vital) { - // only applies to players - if ((this as Player) == null) return 1.0f; + // Apply monster fast vital regen if idle (typically after returning home) + if (!(this is Player)) + { + // Only boost Health/Stamina, not Mana (consistent with player logic) + if (vital.Vital != PropertyAttribute2nd.MaxMana && MonsterState == State.Idle) + return 50.0f; // Large multiplier for fast regen + + return 1.0f; // Default for monsters not idle + } + // Player-specific logic below // does not apply for mana? if (vital.Vital == PropertyAttribute2nd.MaxMana) return 1.0f; diff --git a/Source/ACE.Server/WorldObjects/Monster_Awareness.cs b/Source/ACE.Server/WorldObjects/Monster_Awareness.cs index 2c9719ff0b..30a399feac 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Awareness.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Awareness.cs @@ -23,9 +23,6 @@ partial class Creature /// public bool IsAwake = false; - private float? _originalHealthRate = null; // Store original health rate before fast heal - private bool _isFastHealingActive = false; // Flag to control the fast healing action chain - /// /// Transitions a monster from idle to awake state /// @@ -62,90 +59,9 @@ public virtual void Sleep() PhysicsObj.CachedVelocity = Vector3.Zero; ClearRetaliateTargets(); - - // Start fast health regeneration when monster returns home - if (Health.Current < Health.MaxValue) - { - // Store the original rate before potentially modifying it - _originalHealthRate = (float?)GetProperty(PropertyFloat.HealthRate); - _isFastHealingActive = true; // Activate fast healing - - // Set a high regeneration rate (much higher than normal) - var originalHealthRateValue = _originalHealthRate ?? 0; // Use 0 if null - - // Ensure a minimum regen rate regardless of original value - // Using an extremely high value to ensure quick healing - var fastHealthRate = Math.Max(10.0f, originalHealthRateValue * 50.0f); - - SetProperty(PropertyFloat.HealthRate, fastHealthRate); - - // Create a heartbeat action to monitor health and reset regen rate and damage history when fully healed - var actionChain = new ActionChain(); - actionChain.AddDelaySeconds(0.5f); // Check more frequently - - actionChain.AddAction(this, ProcessFastHealingTick); - - actionChain.EnqueueChain(); - - if (DebugMove) - Console.WriteLine($"{Name} ({Guid}).Sleep() - Started fast health regeneration"); - } - } - - /// - /// Handles a single tick of the fast healing process initiated by Sleep(). - /// - private void ProcessFastHealingTick() - { - // Exit if fast healing was cancelled externally (e.g., by taking damage) - if (!_isFastHealingActive) return; - // Direct healing - add 15% of missing health per tick - if (Health.Current < Health.MaxValue) - { - var missingHealth = Health.MaxValue - Health.Current; - var healAmount = (uint)(missingHealth * 0.15f); - - // Ensure we heal at least 10% of max health per tick - healAmount = Math.Max(healAmount, (uint)(Health.MaxValue * 0.10f)); - - // Apply direct healing - UpdateVitalDelta(Health, (int)healAmount); - - if (DebugMove) - Console.WriteLine($"{Name} ({Guid}).ProcessFastHealingTick() - Direct healing for {healAmount} points"); - } - - // If monster is at full health, stop the process - if (Health.Current >= Health.MaxValue) - { - _isFastHealingActive = false; // Deactivate flag - - // Reset health rate to original value using the stored value - if (_originalHealthRate.HasValue) - SetProperty(PropertyFloat.HealthRate, _originalHealthRate.Value); - else - RemoveProperty(PropertyFloat.HealthRate); - - _originalHealthRate = null; // Clear the stored value - - // Reset damage history - DamageHistory.Reset(); - - if (DebugMove) - Console.WriteLine($"{Name} ({Guid}).ProcessFastHealingTick() - Fully healed, stopping fast heal."); - } - else - { - // Still healing, schedule the next tick if still active - if (_isFastHealingActive) - { - var nextTickChain = new ActionChain(); - nextTickChain.AddDelaySeconds(0.5f); // Check more frequently - nextTickChain.AddAction(this, ProcessFastHealingTick); // Schedule this function again - nextTickChain.EnqueueChain(); - } - } + // Reset damage history when going to sleep/idle + DamageHistory.Reset(); } public Tolerance Tolerance diff --git a/Source/ACE.Server/WorldObjects/Monster_Combat.cs b/Source/ACE.Server/WorldObjects/Monster_Combat.cs index 88f0c0c4b8..ee9d7b66c9 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Combat.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Combat.cs @@ -388,21 +388,6 @@ public virtual uint TakeDamage(WorldObject source, DamageType damageType, float DamageHistory.OnHeal((uint)-damage); } - // Step 1: Stop fast healing if it's active when damage is taken - bool wasFastHealing = _isFastHealingActive; - if (_isFastHealingActive) - { - if (DebugMove) - Console.WriteLine($"{Name} ({Guid}) - Interrupting fast healing due to taking damage."); - - _isFastHealingActive = false; - if (_originalHealthRate.HasValue) - { - SetProperty(PropertyFloat.HealthRate, _originalHealthRate.Value); - _originalHealthRate = null; - } - } - // Step 2: React to the damage source if needed (wake up, target attacker, stop returning) if (source is Creature attacker) { From 58284a0dd0e483e443919147a75ed7795826dc30 Mon Sep 17 00:00:00 2001 From: Vanquish-6 Date: Thu, 1 May 2025 11:52:37 -0700 Subject: [PATCH 5/6] get rid of stuck/aiimobile check currently change vital regen higher --- Source/ACE.Server/WorldObjects/Creature_Vitals.cs | 2 +- Source/ACE.Server/WorldObjects/Monster_Navigation.cs | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Source/ACE.Server/WorldObjects/Creature_Vitals.cs b/Source/ACE.Server/WorldObjects/Creature_Vitals.cs index 6427b0976c..9277bd356a 100644 --- a/Source/ACE.Server/WorldObjects/Creature_Vitals.cs +++ b/Source/ACE.Server/WorldObjects/Creature_Vitals.cs @@ -211,7 +211,7 @@ public float GetStanceMod(CreatureVital vital) { // Only boost Health/Stamina, not Mana (consistent with player logic) if (vital.Vital != PropertyAttribute2nd.MaxMana && MonsterState == State.Idle) - return 50.0f; // Large multiplier for fast regen + return 250.0f; // Large multiplier for fast regen return 1.0f; // Default for monsters not idle } diff --git a/Source/ACE.Server/WorldObjects/Monster_Navigation.cs b/Source/ACE.Server/WorldObjects/Monster_Navigation.cs index 44e1fc170e..93d1324b98 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Navigation.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Navigation.cs @@ -245,13 +245,6 @@ public Vector3 GetDestination() /// public void Movement() { - // Skip stuck checks for intentionally immobile/stuck monsters - if ((GetProperty(PropertyBool.Stuck) ?? false) || (GetProperty(PropertyBool.AiImmobile) ?? false)) - { - // Optionally, ensure they aren't trying to move if immobile - if (IsMoving) CancelMoveTo(); - return; - } //if (!IsRanged) UpdatePosition(); From 96786e0cbcdd1e4afb4938e0c5672fe4b2373ae3 Mon Sep 17 00:00:00 2001 From: Vanquish-6 Date: Sat, 3 May 2025 10:59:32 -0700 Subject: [PATCH 6/6] bypass stuck logic if mob set with bool property 52 (aiImobile) --- Source/ACE.Server/WorldObjects/Monster_Navigation.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Source/ACE.Server/WorldObjects/Monster_Navigation.cs b/Source/ACE.Server/WorldObjects/Monster_Navigation.cs index 93d1324b98..b3f9050420 100644 --- a/Source/ACE.Server/WorldObjects/Monster_Navigation.cs +++ b/Source/ACE.Server/WorldObjects/Monster_Navigation.cs @@ -245,9 +245,7 @@ public Vector3 GetDestination() /// public void Movement() { - - //if (!IsRanged) - UpdatePosition(); + UpdatePosition(); if (MonsterState == State.Awake && GetDistanceToTarget() >= MaxChaseRange) { @@ -257,7 +255,7 @@ public void Movement() } // Standard stuck check from MoveToManager - if (PhysicsObj.MovementManager.MoveToManager.FailProgressCount > 0 && Timers.RunningTime > NextCancelTime) + if (!AiImmobile && PhysicsObj.MovementManager.MoveToManager.FailProgressCount > 0 && Timers.RunningTime > NextCancelTime) { // Instead of just canceling, also increment stuck counter StuckCounter++; @@ -276,7 +274,7 @@ public void Movement() } // Additional stuck detection - check if position hasn't changed much over time - if (Timers.RunningTime - LastStuckCheckTime >= 5.0) // Check every 5 seconds + if (!AiImmobile && Timers.RunningTime - LastStuckCheckTime >= 5.0) // Check every 5 seconds { var currentPos = Location.ToGlobal();