From 75f326bfdd8ccf30ee5c07f0b3510f2c69edcd1b Mon Sep 17 00:00:00 2001 From: Robert Dickey Date: Fri, 11 Sep 2026 19:26:47 -0500 Subject: [PATCH] fix: handle null maps when deactivating creature AI (#2629) An uncontrolled creature with `Map == null` throws in `BaseAI.Deactivate()` when the condition reaches `Map.GetSector()`. A controlled creature with a null map avoids the exception but leaves its AI timer running. Treat a null map like `Map.Internal` in the existing stop condition. This stops the timer without dereferencing the missing map and leaves the existing valid-map condition and return-home scheduling unchanged. Addresses only the null-map `Deactivate()` item in #2627; the other audit items remain separate. No era-specific rules are changed. --- .../Tests/Mobiles/AI/AIDeactivationTests.cs | 33 +++++++++++++++++++ .../UOContent/Mobiles/AI/BaseAI/BaseAI.cs | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 Projects/UOContent.Tests/Tests/Mobiles/AI/AIDeactivationTests.cs diff --git a/Projects/UOContent.Tests/Tests/Mobiles/AI/AIDeactivationTests.cs b/Projects/UOContent.Tests/Tests/Mobiles/AI/AIDeactivationTests.cs new file mode 100644 index 000000000..fc3f82aa8 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Mobiles/AI/AIDeactivationTests.cs @@ -0,0 +1,33 @@ +using Server; +using Xunit; + +namespace UOContent.Tests.Mobiles.AI; + +[Collection("Sequential UOContent Tests")] +public class AIDeactivationTests +{ + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void Deactivate_WithoutWorldMap_StopsTimer(bool internalMap, bool controlled) + { + var creature = new PetTestStub(); + try + { + creature.Controlled = controlled; + creature.Map = internalMap ? Map.Internal : null; + creature.AIObject.Activate(); + Assert.True(creature.AIObject.AITimer.Running); + + creature.AIObject.Deactivate(); + + Assert.False(creature.AIObject.AITimer.Running); + } + finally + { + creature.Delete(); + } + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs index e7b16be9c..e762c5c61 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs @@ -1102,7 +1102,7 @@ public abstract partial class BaseAI public virtual void Deactivate() { - if (Mobile.Map == Map.Internal || !Mobile.Controlled && !Mobile.Map.GetSector(Mobile.Location).Active) + if (Mobile.Map == null || Mobile.Map == Map.Internal || !Mobile.Controlled && !Mobile.Map.GetSector(Mobile.Location).Active) { AITimer.Stop(); }