Pets slowed dramatically after #2591 (issue #2593): the per-step budget grew from half a think interval to the full RunUO-parity move table, and a guarding pet resolves passive (OnCombatantChange clears Warmode whenever the combatant clears), putting Guard/Come at ~1.05s/step for Medium-bucket pets. Order changes and speed-ups also waited out the previously scheduled AITimer wake, because the timer wheel reads Interval only after the next fire. - CurrentMoveSpeed: a controlled pet executing a master's movement order (Come/Follow/Guard, no combatant) paces steps on the think clock — the wild-creature move table no longer slows obedience. Combat chases and herding keep their own pacing. - AITimer: track the pending wake and reschedule (Stop, Delay = remaining, Start) when a speed-up or fresh order moves the earliest deadline up; changes inside a tick still flow through ScheduleNext. New Prod() wakes the AI immediately on player commands, including from a stopped timer (stable claims no longer wait out the random construction stagger). - DoOrderGuard: guard-following routes through WalkMobileRange so it registers a move intent (between-think move wakes) and paths around obstacles instead of bare greedy stepping quantized to the think grid. Closes #2593 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
48 lines
1.6 KiB
C#
48 lines
1.6 KiB
C#
using System.Collections.Generic;
|
|
using Server;
|
|
using Server.Mobiles;
|
|
using Xunit;
|
|
|
|
namespace UOContent.Tests.Mobiles.AI;
|
|
|
|
// Guard-order following drives real movement primitives (and may pathfind), so it shares
|
|
// the pathfinding sequential collection like ApproachTargetTests.
|
|
[Collection("Sequential Pathfinding Tests")]
|
|
public class GuardFollowTests
|
|
{
|
|
[Fact]
|
|
public void GuardFollow_StepsTowardMaster_AndRegistersMoveIntent()
|
|
{
|
|
var map = Map.Maps[1];
|
|
Assert.NotNull(map);
|
|
map.GetAverageZ(1500, 1600, out _, out var z, out _);
|
|
|
|
var master = new PlayerMobile(World.NewMobile);
|
|
master.DefaultMobileInit();
|
|
master.MoveToWorld(new Point3D(1494, 1600, (sbyte)z), map);
|
|
|
|
var pet = new PetTestStub();
|
|
pet.MoveToWorld(new Point3D(1500, 1600, (sbyte)z), map); // 6 tiles east, open terrain
|
|
pet.SetControlMaster(master);
|
|
|
|
var ai = pet.AIObject;
|
|
ai.AITimer?.Stop(); // drive manually
|
|
pet.ControlOrder = OrderType.Guard;
|
|
ai.AITimer?.Stop(); // the order change may restart the timer
|
|
|
|
var start = pet.Location;
|
|
ai.NextMove = 0;
|
|
ai.Obey();
|
|
|
|
var moved = pet.Location != start;
|
|
var hasIntent = ai.TryGetMoveWake(out _);
|
|
|
|
pet.Delete();
|
|
master.Delete();
|
|
|
|
Assert.True(moved, "a guarding pet beyond guard range must step toward its master");
|
|
// Between-think move wakes require a registered move intent; bare greedy stepping
|
|
// quantizes guard-following to the think grid (issue #2593).
|
|
Assert.True(hasIntent, "guard-following must register a move intent");
|
|
}
|
|
}
|