fix(AI): Fixes direction issue with NPCs (#371)

- [X] Fixes a bug with RunUO where direction is set multiple times and the run flag is not set properly.

TODO: Fix other direction/movement issues and duplicate packets.

Bumps release version
This commit is contained in:
Kamron Batman 2020-12-29 22:32:10 -08:00 • committed by GitHub
parent 7ef1d41c6f
commit 67c23bf9ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 207 additions and 297 deletions

View file

@ -480,8 +480,6 @@ namespace Server
while (!Closing)
{
// m_Signal.WaitOne();
Mobile.ProcessDeltaQueue();
Item.ProcessDeltaQueue();

View file

@ -294,7 +294,8 @@ namespace Server
Locked
}
[CustomEnum(new[] { "North", "Right", "East", "Down", "South", "Left", "West", "Up" }), Flags]
[Flags]
[CustomEnum(new[] { "North", "Right", "East", "Down", "South", "Left", "West", "Up" })]
public enum Direction : byte
{
North = 0x0,
@ -1348,9 +1349,7 @@ namespace Server
if (m_Direction != value)
{
m_Direction = value;
Delta(MobileDelta.Direction);
// ProcessDelta();
}
}
}
@ -9144,7 +9143,7 @@ namespace Server
public void BoltEffect(int hue) => Effects.SendBoltEffect(this, true, hue);
public Direction GetDirectionTo(int x, int y)
public Direction GetDirectionTo(int x, int y, bool run = false)
{
var dx = m_Location.m_X - x;
var dy = m_Location.m_Y - y;
@ -9165,30 +9164,25 @@ namespace Server
{
ret = rx > 0 ? Direction.Left : Direction.Right;
}
else if (rx >= 0 && ry >= 0)
{
ret = Direction.West;
}
else if (rx >= 0 && ry < 0)
{
ret = Direction.South;
}
else if (rx < 0 && ry < 0)
{
ret = Direction.East;
}
else
{
ret = Direction.North;
ret = rx switch
{
>= 0 when ry >= 0 => Direction.West,
>= 0 => Direction.South,
< 0 when ry < 0 => Direction.East,
_ => Direction.North
};
}
return ret;
return ret | (run ? Direction.Running : 0);
}
public Direction GetDirectionTo(Point2D p) => GetDirectionTo(p.m_X, p.m_Y);
public Direction GetDirectionTo(Point3D p) => GetDirectionTo(p.m_X, p.m_Y);
public Direction GetDirectionTo(Point2D p, bool run = false) => GetDirectionTo(p.m_X, p.m_Y, run);
public Direction GetDirectionTo(Point3D p, bool run = false) => GetDirectionTo(p.m_X, p.m_Y, run);
public Direction GetDirectionTo(IPoint2D p) => p == null ? Direction.North : GetDirectionTo(p.X, p.Y);
public Direction GetDirectionTo(IPoint2D p, bool run = false) =>
p == null ? Direction.North | (run ? Direction.Running : 0) : GetDirectionTo(p.X, p.Y, run);
public void PublicOverheadMessage(MessageType type, int hue, bool ascii, string text, bool noLineOfSight = true)
{

View file

@ -29,25 +29,10 @@ namespace Server
Enabled = ServerConfiguration.GetOrUpdateSetting("pathfinding.enable", true);
}
public MoveResult Move(Direction d)
{
if (Mover == null)
{
return m_From.Move(d) ? MoveResult.Success : MoveResult.Blocked;
}
public MoveResult Move(Direction d) =>
Mover?.Invoke(d) ?? (m_From.Move(d) ? MoveResult.Success : MoveResult.Blocked);
return Mover(d);
}
public Point3D GetGoalLocation()
{
if (Goal is Item item)
{
return item.GetWorldLocation();
}
return new Point3D(Goal);
}
public Point3D GetGoalLocation() => (Goal as Item)?.GetWorldLocation() ?? new Point3D(Goal);
public void Advance(ref Point3D p, int index)
{
@ -100,7 +85,7 @@ namespace Server
return true;
}
public bool Check(Point3D loc, Point3D goal, int range) =>
public static bool Check(Point3D loc, Point3D goal, int range) =>
Utility.InRange(loc, goal, range) && (range > 1 || Math.Abs(loc.Z - goal.Z) < 16);
public bool Follow(bool run, int range)
@ -117,28 +102,15 @@ namespace Server
if (!(Enabled && m_Path.Success))
{
d = m_From.GetDirectionTo(goal);
if (run)
{
d |= Direction.Running;
}
d = m_From.GetDirectionTo(goal, run);
m_From.SetDirection(d);
Move(d);
return Check(m_From.Location, goal, range);
}
d = m_From.GetDirectionTo(m_Next);
if (run)
{
d |= Direction.Running;
}
d = m_From.GetDirectionTo(m_Next, run);
m_From.SetDirection(d);
var res = Move(d);
if (res == MoveResult.Blocked)
@ -153,31 +125,17 @@ namespace Server
if (!m_Path.Success)
{
d = m_From.GetDirectionTo(goal);
if (run)
{
d |= Direction.Running;
}
d = m_From.GetDirectionTo(goal, run);
m_From.SetDirection(d);
Move(d);
return Check(m_From.Location, goal, range);
}
d = m_From.GetDirectionTo(m_Next);
if (run)
{
d |= Direction.Running;
}
d = m_From.GetDirectionTo(m_Next, run);
m_From.SetDirection(d);
res = Move(d);
if (res == MoveResult.Blocked)
if (Move(d) == MoveResult.Blocked)
{
return false;
}

View file

@ -57,11 +57,7 @@ namespace Server.Mobiles
return true;
}
if (WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
{
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
}
else
if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
{
if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1)
{

View file

@ -42,31 +42,32 @@ namespace Server.Mobiles
if (Core.TickCount - m_Mobile.LastMoveTime > 1000)
{
if (WalkMobileRange(m_Mobile.Combatant, 1, true, m_Mobile.RangeFight, m_Mobile.Weapon.MaxRange))
if (
m_Mobile.Combatant != null &&
!WalkMobileRange(
m_Mobile.Combatant,
1,
true,
m_Mobile.RangeFight,
m_Mobile.Weapon.MaxRange
)
)
{
// Be sure to face the combatant
m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant.Location);
}
else
{
if (m_Mobile.Combatant != null)
if (m_Mobile.Debug)
{
m_Mobile.DebugSay("I am still not in range of {0}", m_Mobile.Combatant.Name);
}
if ((int)m_Mobile.GetDistanceToSqrt(m_Mobile.Combatant) > m_Mobile.RangePerception + 1)
{
if (m_Mobile.Debug)
{
m_Mobile.DebugSay("I am still not in range of {0}", m_Mobile.Combatant.Name);
m_Mobile.DebugSay("I have lost {0}", m_Mobile.Combatant.Name);
}
if ((int)m_Mobile.GetDistanceToSqrt(m_Mobile.Combatant) > m_Mobile.RangePerception + 1)
{
if (m_Mobile.Debug)
{
m_Mobile.DebugSay("I have lost {0}", m_Mobile.Combatant.Name);
}
m_Mobile.Combatant = null;
Action = ActionType.Guard;
return true;
}
m_Mobile.Combatant = null;
Action = ActionType.Guard;
return true;
}
}
}

View file

@ -1203,7 +1203,7 @@ namespace Server.Mobiles
!m_Mobile.Combatant.IsDeadBondedPet)
{
m_Mobile.Warmode = true;
m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant);
// m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun);
}
else
{
@ -1323,7 +1323,7 @@ namespace Server.Mobiles
!m_Mobile.Combatant.IsDeadBondedPet)
{
m_Mobile.Warmode = true;
m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant);
// m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun);
}
else
{
@ -1644,8 +1644,6 @@ namespace Server.Mobiles
m_Mobile.DebugSay("My master told me to stay");
}
// m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.ControlMaster );
return true;
}
@ -1982,6 +1980,7 @@ namespace Server.Mobiles
}
// This makes them always move one step, never any direction changes
// TODO: This is firing off deltas which aren't needed. Look into replacing/removing this
m_Mobile.Direction = d;
var delay = (int)(TransformMoveDelay(m_Mobile.CurrentSpeed) * 1000);
@ -2005,145 +2004,145 @@ namespace Server.Mobiles
return v ? MoveResult.Success : MoveResult.Blocked;
}
if (!m_Mobile.Move(d))
if (m_Mobile.Move(d))
{
var wasPushing = m_Mobile.Pushing;
MoveImpl.IgnoreMovableImpassables = false;
return MoveResult.Success;
}
var blocked = true;
var wasPushing = m_Mobile.Pushing;
var canOpenDoors = m_Mobile.CanOpenDoors;
var canDestroyObstacles = m_Mobile.CanDestroyObstacles;
var blocked = true;
if (canOpenDoors || canDestroyObstacles)
var canOpenDoors = m_Mobile.CanOpenDoors;
var canDestroyObstacles = m_Mobile.CanDestroyObstacles;
if (canOpenDoors || canDestroyObstacles)
{
m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles.");
var map = m_Mobile.Map;
if (map != null)
{
m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles.");
int x = m_Mobile.X, y = m_Mobile.Y;
Movement.Movement.Offset(d, ref x, ref y);
var map = m_Mobile.Map;
var destroyables = 0;
if (map != null)
var eable = map.GetItemsInRange(new Point3D(x, y, m_Mobile.Location.Z), 1);
foreach (var item in eable)
{
int x = m_Mobile.X, y = m_Mobile.Y;
Movement.Movement.Offset(d, ref x, ref y);
var destroyables = 0;
var eable = map.GetItemsInRange(new Point3D(x, y, m_Mobile.Location.Z), 1);
foreach (var item in eable)
if (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > m_Mobile.Z &&
m_Mobile.Z + 16 > door.Z)
{
if (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > m_Mobile.Z &&
m_Mobile.Z + 16 > door.Z)
if (door.X != x || door.Y != y)
{
if (door.X != x || door.Y != y)
{
continue;
}
if (!door.Locked || !door.UseLocks())
{
m_Obstacles.Enqueue(door);
}
if (!canDestroyObstacles)
{
break;
}
continue;
}
else if (canDestroyObstacles && item.Movable && item.ItemData.Impassable &&
item.Z + item.ItemData.Height > m_Mobile.Z && m_Mobile.Z + 16 > item.Z)
{
if (!m_Mobile.InRange(item.GetWorldLocation(), 1))
{
continue;
}
m_Obstacles.Enqueue(item);
++destroyables;
if (!door.Locked || !door.UseLocks())
{
m_Obstacles.Enqueue(door);
}
if (!canDestroyObstacles)
{
break;
}
}
eable.Free();
if (destroyables > 0)
else if (canDestroyObstacles && item.Movable && item.ItemData.Impassable &&
item.Z + item.ItemData.Height > m_Mobile.Z && m_Mobile.Z + 16 > item.Z)
{
Effects.PlaySound(new Point3D(x, y, m_Mobile.Z), m_Mobile.Map, 0x3B3);
}
if (m_Obstacles.Count > 0)
{
blocked = false; // retry movement
}
while (m_Obstacles.Count > 0)
{
var item = m_Obstacles.Dequeue();
if (item is BaseDoor door)
if (!m_Mobile.InRange(item.GetWorldLocation(), 1))
{
m_Mobile.DebugSay(
"Little do they expect, I've learned how to open doors. Didn't they read the script??"
);
m_Mobile.DebugSay("*twist*");
continue;
}
door.Use(m_Mobile);
m_Obstacles.Enqueue(item);
++destroyables;
}
}
eable.Free();
if (destroyables > 0)
{
Effects.PlaySound(new Point3D(x, y, m_Mobile.Z), m_Mobile.Map, 0x3B3);
}
if (m_Obstacles.Count > 0)
{
blocked = false; // retry movement
}
while (m_Obstacles.Count > 0)
{
var item = m_Obstacles.Dequeue();
if (item is BaseDoor door)
{
m_Mobile.DebugSay(
"Little do they expect, I've learned how to open doors. Didn't they read the script??"
);
m_Mobile.DebugSay("*twist*");
door.Use(m_Mobile);
}
else
{
m_Mobile.DebugSay(
"Ugabooga. I'm so big and tough I can destroy it: {0}",
item.GetType().Name
);
if (item is Container cont)
{
for (var i = 0; i < cont.Items.Count; ++i)
{
var check = cont.Items[i];
if (check.Movable && check.ItemData.Impassable &&
cont.Z + check.ItemData.Height > m_Mobile.Z)
{
m_Obstacles.Enqueue(check);
}
}
cont.Destroy();
}
else
{
m_Mobile.DebugSay(
"Ugabooga. I'm so big and tough I can destroy it: {0}",
item.GetType().Name
);
if (item is Container cont)
{
for (var i = 0; i < cont.Items.Count; ++i)
{
var check = cont.Items[i];
if (check.Movable && check.ItemData.Impassable &&
cont.Z + check.ItemData.Height > m_Mobile.Z)
{
m_Obstacles.Enqueue(check);
}
}
cont.Destroy();
}
else
{
item.Delete();
}
item.Delete();
}
}
}
if (!blocked)
{
blocked = !m_Mobile.Move(d);
}
if (!blocked)
{
blocked = !m_Mobile.Move(d);
}
}
}
if (blocked)
if (blocked)
{
var offset = Utility.RandomDouble() >= 0.6 ? 1 : -1;
for (var i = 0; i < 2; ++i)
{
var offset = Utility.RandomDouble() >= 0.6 ? 1 : -1;
m_Mobile.TurnInternal(offset);
for (var i = 0; i < 2; ++i)
if (m_Mobile.Move(m_Mobile.Direction))
{
m_Mobile.TurnInternal(offset);
if (m_Mobile.Move(m_Mobile.Direction))
{
MoveImpl.IgnoreMovableImpassables = false;
return MoveResult.SuccessAutoTurn;
}
MoveImpl.IgnoreMovableImpassables = false;
return MoveResult.SuccessAutoTurn;
}
MoveImpl.IgnoreMovableImpassables = false;
return wasPushing ? MoveResult.BadState : MoveResult.Blocked;
}
MoveImpl.IgnoreMovableImpassables = false;
return MoveResult.Success;
return wasPushing ? MoveResult.BadState : MoveResult.Blocked;
}
MoveImpl.IgnoreMovableImpassables = false;
@ -2281,10 +2280,9 @@ namespace Server.Mobiles
return true;
}
}
else if (!DoMove(m_Mobile.GetDirectionTo(m), true))
else if (!DoMove(m_Mobile.GetDirectionTo(m, run), true))
{
m_Path = new PathFollower(m_Mobile, m);
m_Path.Mover = DoMoveImpl;
m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl };
if (m_Path.Follow(run, 1))
{
@ -2341,22 +2339,8 @@ namespace Server.Mobiles
}
else
{
Direction dirTo;
if (iCurrDist > iWantDistMax)
{
dirTo = m_Mobile.GetDirectionTo(m);
}
else
{
dirTo = m.GetDirectionTo(m_Mobile);
}
// Add the run flag
if (bRun)
{
dirTo = dirTo | Direction.Running;
}
var dirTo = iCurrDist > iWantDistMax ?
m_Mobile.GetDirectionTo(m, bRun) : m.GetDirectionTo(m_Mobile, bRun);
if (!DoMove(dirTo, true) && needCloser)
{
@ -2382,12 +2366,7 @@ namespace Server.Mobiles
// Get the current distance
var iNewDist = (int)m_Mobile.GetDistanceToSqrt(m);
if (iNewDist >= iWantDistMin && iNewDist <= iWantDistMax)
{
return true;
}
return false;
return iNewDist >= iWantDistMin && iNewDist <= iWantDistMax;
}
/*

View file

@ -37,30 +37,31 @@ namespace Server.Mobiles
return true;
}
if (WalkMobileRange(m_Mobile.Combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
if (
m_Mobile.Combatant != null &&
!WalkMobileRange(
m_Mobile.Combatant,
1,
true,
m_Mobile.RangeFight,
m_Mobile.RangeFight
)
)
{
// Be sure to face the combatant
m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant.Location);
}
else
{
if (m_Mobile.Combatant != null)
if (m_Mobile.Debug)
{
m_Mobile.DebugSay($"I am still not in range of {m_Mobile.Combatant.Name}");
}
if ((int)m_Mobile.GetDistanceToSqrt(m_Mobile.Combatant) > m_Mobile.RangePerception + 1)
{
if (m_Mobile.Debug)
{
m_Mobile.DebugSay($"I am still not in range of {m_Mobile.Combatant.Name}");
m_Mobile.DebugSay($"I have lost {m_Mobile.Combatant.Name}");
}
if ((int)m_Mobile.GetDistanceToSqrt(m_Mobile.Combatant) > m_Mobile.RangePerception + 1)
{
if (m_Mobile.Debug)
{
m_Mobile.DebugSay($"I have lost {m_Mobile.Combatant.Name}");
}
Action = ActionType.Guard;
return true;
}
Action = ActionType.Guard;
return true;
}
}

View file

@ -67,44 +67,33 @@ namespace Server.Mobiles
}
}
/*if (!m_Mobile.InLOS( combatant ))
if (!MoveTo(combatant, true, m_Mobile.RangeFight))
{
if (AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ))
{
m_Mobile.Combatant = combatant = m_Mobile.FocusMob;
m_Mobile.FocusMob = null;
}
}*/
if (MoveTo(combatant, true, m_Mobile.RangeFight))
{
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
}
else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
{
if (m_Mobile.Debug)
if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true))
{
m_Mobile.DebugSay("My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name);
if (m_Mobile.Debug)
{
m_Mobile.DebugSay("My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob?.Name ?? "null");
}
m_Mobile.Combatant = m_Mobile.FocusMob;
Action = ActionType.Combat;
return true;
}
m_Mobile.Combatant = m_Mobile.FocusMob;
Action = ActionType.Combat;
return true;
}
else if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1)
{
if (m_Mobile.Debug)
if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1)
{
m_Mobile.DebugSay("I cannot find {0}, so my guard is up", combatant.Name);
if (m_Mobile.Debug)
{
m_Mobile.DebugSay("I cannot find {0}, so my guard is up", combatant.Name);
}
Action = ActionType.Guard;
return true;
}
Action = ActionType.Guard;
return true;
}
else
{
if (m_Mobile.Debug)
{
m_Mobile.DebugSay("I should be closer to {0}", combatant.Name);
@ -117,7 +106,7 @@ namespace Server.Mobiles
{
// We are low on health, should we flee?
var flee = false;
bool flee;
if (m_Mobile.Hits < combatant.Hits)
{

View file

@ -37,11 +37,7 @@ namespace Server.Mobiles
return true;
}
if (WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
{
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
}
else
if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
{
if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1)
{

View file

@ -42,10 +42,12 @@ namespace Server.Mobiles
return true;
}
if (WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight))
{
m_Mobile.DebugSay("I should be closer to {0}", combatant.Name);
}
else
{
m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant);
if (m_toDisarm?.IsChildOf(m_Mobile.Backpack) != false)
{
m_toDisarm = combatant.FindItemOnLayer(Layer.OneHanded) ?? combatant.FindItemOnLayer(Layer.TwoHanded);
@ -111,17 +113,13 @@ namespace Server.Mobiles
}
}
}
else
{
m_Mobile.DebugSay("I should be closer to {0}", combatant.Name);
}
if (m_Mobile.Hits >= m_Mobile.HitsMax * 20 / 100 || !m_Mobile.CanFlee)
{
return true;
}
// We are low on health, should we flee?
// We are low on health, should we flee?
bool flee;
if (m_Mobile.Hits < combatant.Hits)

View file

@ -105,7 +105,7 @@ namespace Server.Mobiles
Warmode = false;
FocusMob = Combatant = null;
CurrentSpeed = .01;
CurrentSpeed = 0.01;
}
}