Core/Movement: MotionMaster reimplementation

Port From (https://github.com/TrinityCore/TrinityCore/commit/426f9f2f92b26fbb68e7cda9290ccbd586c6af4e)
This commit is contained in:
hondacrx
2021-09-29 18:24:37 -04:00
parent cf13f32062
commit a968772bad
47 changed files with 1954 additions and 1227 deletions
@@ -22,7 +22,7 @@ using System;
namespace Game.Movement
{
class ChaseMovementGenerator : AbstractFollower, IMovementGenerator
class ChaseMovementGenerator : MovementGenerator
{
static uint RANGE_CHECK_INTERVAL = 100; // time (ms) until we attempt to recalculate
@@ -30,32 +30,49 @@ namespace Game.Movement
ChaseAngle? _angle;
PathGenerator _path;
Position _lastTargetPosition;
Position _lastTargetPosition = new();
uint _rangeCheckTimer = RANGE_CHECK_INTERVAL;
bool _movingTowards = true;
bool _mutualChase = true;
public ChaseMovementGenerator(Unit target, ChaseRange? range, ChaseAngle? angle) : base(target)
AbstractFollower _abstractFollower;
public ChaseMovementGenerator(Unit target, ChaseRange? range, ChaseAngle? angle)
{
_abstractFollower = new AbstractFollower(target);
_range = range;
_angle = angle;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Chase;
}
public void Initialize(Unit owner)
public override void Initialize(Unit owner)
{
owner.AddUnitState(UnitState.Chase);
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
owner.SetWalk(false);
_path = null;
_lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f);
}
public bool Update(Unit owner, uint diff)
public override void Reset(Unit owner)
{
RemoveFlag(MovementGeneratorFlags.Deactivated);
Initialize(owner);
}
public override bool Update(Unit owner, uint diff)
{
// owner might be dead or gone (can we even get nullptr here?)
if (!owner || !owner.IsAlive())
return false;
// our target might have gone away
Unit target = GetTarget();
Unit target = _abstractFollower.GetTarget();
if (!target)
return false;
@@ -154,6 +171,7 @@ namespace Game.Movement
{
if (cOwner)
cOwner.SetCannotReachTarget(true);
owner.StopMoving();
return true;
}
@@ -178,26 +196,34 @@ namespace Game.Movement
return true;
}
public void Finalize(Unit owner)
public override void Deactivate(Unit owner)
{
owner.ClearUnitState(UnitState.Chase | UnitState.ChaseMove);
Creature cOwner = owner.ToCreature();
if (cOwner != null)
cOwner.SetCannotReachTarget(false);
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.ChaseMove);
}
public void Reset(Unit owner) { Initialize(owner); }
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
{
owner.ClearUnitState(UnitState.ChaseMove);
Creature cOwner = owner.ToCreature();
if (cOwner != null)
cOwner.SetCannotReachTarget(false);
}
}
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Chase; }
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Chase; }
public void UnitSpeedChanged() { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
public override void UnitSpeedChanged() { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
static bool IsMutualChase(Unit owner, Unit target)
{
if (target.GetMotionMaster().GetCurrentMovementGeneratorType() != MovementGeneratorType.Chase)
return false;
return ((ChaseMovementGenerator)target.GetMotionMaster().Top()).GetTarget() == owner;
return ((ChaseMovementGenerator)target.GetMotionMaster().GetCurrentMovementGenerator())._abstractFollower.GetTarget() == owner;
}
static bool PositionOkay(Unit owner, Unit target, float? minDistance, float? maxDistance, ChaseAngle? angle)
@@ -212,13 +238,12 @@ namespace Game.Movement
static void DoMovementInform(Unit owner, Unit target)
{
Creature cOwner = owner.ToCreature();
if (cOwner != null)
{
CreatureAI ai = cOwner.GetAI();
if (ai != null)
ai.MovementInform(MovementGeneratorType.Chase, (uint)target.GetGUID().GetCounter());
}
if (!owner.IsCreature())
return;
Creature creatureOwner = owner.ToCreature();
if (creatureOwner.IsAIEnabled && creatureOwner.GetAI() != null)
creatureOwner.GetAI().MovementInform(MovementGeneratorType.Chase, (uint)target.GetGUID().GetCounter());
}
}
}
@@ -26,14 +26,23 @@ namespace Game.Movement
public ConfusedGenerator()
{
_timer = new TimeTracker();
_reference = new();
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Highest;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Confused;
}
public override void DoInitialize(T owner)
{
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
if (!owner || !owner.IsAlive())
return;
owner.AddUnitState(UnitState.Confused);
// TODO: UNIT_FIELD_FLAGS should not be handled by generators
owner.AddUnitFlag(UnitFlags.Confused);
owner.StopMoving();
@@ -44,6 +53,7 @@ namespace Game.Movement
public override void DoReset(T owner)
{
RemoveFlag(MovementGeneratorFlags.Transitory | MovementGeneratorFlags.Deactivated);
DoInitialize(owner);
}
@@ -54,20 +64,19 @@ namespace Game.Movement
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
_path = null;
return true;
}
else
_interrupt = false;
RemoveFlag(MovementGeneratorFlags.Interrupted);
// waiting for next move
_timer.Update(diff);
if (!_interrupt && _timer.Passed() && owner.MoveSpline.Finalized())
if ((HasFlag(MovementGeneratorFlags.SpeedUpdatePending) && !owner.MoveSpline.Finalized()) || (_timer.Passed() && owner.MoveSpline.Finalized()))
{
// start moving
owner.AddUnitState(UnitState.ConfusedMove);
RemoveFlag(MovementGeneratorFlags.Transitory);
Position destination = new(_reference);
float distance = (float)(4.0f * RandomHelper.FRand(0.0f, 1.0f) - 2.0f);
@@ -87,6 +96,8 @@ namespace Game.Movement
return true;
}
owner.AddUnitState(UnitState.ConfusedMove);
MoveSplineInit init = new(owner);
init.MovebyPath(_path.GetPath());
init.SetWalk(true);
@@ -97,20 +108,31 @@ namespace Game.Movement
return true;
}
public override void DoFinalize(T owner)
public override void DoDeactivate(T owner)
{
if (owner.IsTypeId(TypeId.Player))
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.ConfusedMove);
}
public override void DoFinalize(T owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
{
owner.RemoveUnitFlag(UnitFlags.Confused);
owner.ClearUnitState(UnitState.Confused);
owner.StopMoving();
}
else if (owner.IsTypeId(TypeId.Unit))
{
owner.RemoveUnitFlag(UnitFlags.Confused);
owner.ClearUnitState(UnitState.Confused | UnitState.ConfusedMove);
if (owner.GetVictim())
owner.SetTarget(owner.GetVictim().GetGUID());
if (owner.IsPlayer())
{
owner.RemoveUnitFlag(UnitFlags.Confused);
owner.StopMoving();
}
else
{
owner.RemoveUnitFlag(UnitFlags.Confused);
owner.ClearUnitState(UnitState.ConfusedMove);
if (owner.GetVictim())
owner.SetTarget(owner.GetVictim().GetGUID());
}
}
}
@@ -119,9 +141,10 @@ namespace Game.Movement
return MovementGeneratorType.Confused;
}
public override void UnitSpeedChanged() { AddFlag(MovementGeneratorFlags.SpeedUpdatePending); }
PathGenerator _path;
TimeTracker _timer;
Position _reference;
bool _interrupt;
}
}
@@ -30,21 +30,30 @@ namespace Game.Movement
{
_fleeTargetGUID = fright;
_timer = new TimeTracker();
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Highest;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Fleeing;
}
public override void DoInitialize(T owner)
{
if (owner == null)
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
if (owner == null || !owner.IsAlive())
return;
// TODO: UNIT_FIELD_FLAGS should not be handled by generators
owner.AddUnitFlag(UnitFlags.Fleeing);
owner.AddUnitState(UnitState.Fleeing);
SetTargetLocation(owner);
_path = null;
SetTargetLocation(owner);
}
public override void DoReset(T owner)
{
RemoveFlag(MovementGeneratorFlags.Transitory | MovementGeneratorFlags.Deactivated);
DoInitialize(owner);
}
@@ -55,54 +64,66 @@ namespace Game.Movement
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
_path = null;
return true;
}
else
_interrupt = false;
RemoveFlag(MovementGeneratorFlags.Interrupted);
_timer.Update(diff);
if (!_interrupt && _timer.Passed() && owner.MoveSpline.Finalized())
if ((HasFlag(MovementGeneratorFlags.SpeedUpdatePending) && !owner.MoveSpline.Finalized()) || (_timer.Passed() && owner.MoveSpline.Finalized()))
{
RemoveFlag(MovementGeneratorFlags.Transitory);
SetTargetLocation(owner);
}
return true;
}
public override void DoFinalize(T owner)
public override void DoDeactivate(T owner)
{
if (owner.IsTypeId(TypeId.Player))
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.FleeingMove);
}
public override void DoFinalize(T owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
{
owner.RemoveUnitFlag(UnitFlags.Fleeing);
owner.ClearUnitState(UnitState.Fleeing);
owner.StopMoving();
}
else
{
owner.RemoveUnitFlag(UnitFlags.Fleeing);
owner.ClearUnitState(UnitState.Fleeing | UnitState.FleeingMove);
if (owner.GetVictim() != null)
owner.SetTarget(owner.GetVictim().GetGUID());
if (owner.IsPlayer())
{
owner.RemoveUnitFlag(UnitFlags.Fleeing);
owner.ClearUnitState(UnitState.FleeingMove);
owner.StopMoving();
}
else
{
owner.RemoveUnitFlag(UnitFlags.Fleeing);
owner.ClearUnitState(UnitState.FleeingMove);
if (owner.GetVictim() != null)
owner.SetTarget(owner.GetVictim().GetGUID());
}
}
}
void SetTargetLocation(T owner)
{
if (owner == null)
if (owner == null || !owner.IsAlive())
return;
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
_path = null;
return;
}
owner.AddUnitState(UnitState.FleeingMove);
Position destination = new Position(owner.GetPosition());
Position destination = new (owner.GetPosition());
GetPoint(owner, destination);
// Add LOS check for target point
@@ -125,6 +146,8 @@ namespace Game.Movement
return;
}
owner.AddUnitState(UnitState.FleeingMove);
MoveSplineInit init = new(owner);
init.MovebyPath(_path.GetPath());
init.SetWalk(false);
@@ -175,10 +198,14 @@ namespace Game.Movement
return MovementGeneratorType.Fleeing;
}
public override void UnitSpeedChanged()
{
AddFlag(MovementGeneratorFlags.SpeedUpdatePending);
}
PathGenerator _path;
ObjectGuid _fleeTargetGUID;
TimeTracker _timer;
bool _interrupt;
}
public class TimedFleeingGenerator : FleeingGenerator<Creature>
@@ -188,10 +215,25 @@ namespace Game.Movement
_totalFleeTime = new TimeTracker(time);
}
public override void Finalize(Unit owner)
public override bool Update(Unit owner, uint diff)
{
if (owner == null || !owner.IsAlive())
return false;
_totalFleeTime.Update(diff);
if (_totalFleeTime.Passed())
return false;
return DoUpdate(owner.ToCreature(), diff);
}
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (!active)
return;
owner.RemoveUnitFlag(UnitFlags.Fleeing);
owner.ClearUnitState(UnitState.Fleeing | UnitState.FleeingMove);
Unit victim = owner.GetVictim();
if (victim != null)
{
@@ -203,26 +245,6 @@ namespace Game.Movement
}
}
public override bool Update(Unit owner, uint diff)
{
if (!owner.IsAlive())
return false;
if (owner.HasUnitState(UnitState.Root | UnitState.Stunned))
{
owner.ClearUnitState(UnitState.FleeingMove);
return true;
}
_totalFleeTime.Update(diff);
if (_totalFleeTime.Passed())
return false;
// This calls grant-parent Update method hiden by FleeingMovementGenerator.Update(Creature &, uint32) version
// This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
return base.Update(owner, diff);
}
public override MovementGeneratorType GetMovementGeneratorType()
{
return MovementGeneratorType.TimedFleeing;
@@ -28,6 +28,116 @@ namespace Game.Movement
{
public class FlightPathMovementGenerator : MovementGeneratorMedium<Player>
{
public FlightPathMovementGenerator()
{
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Highest;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.InFlight;
}
public override void DoInitialize(Player owner)
{
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
DoReset(owner);
InitEndGridInfo();
}
public override void DoReset(Player owner)
{
RemoveFlag(MovementGeneratorFlags.Deactivated);
owner.CombatStopWithPets();
owner.AddUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
MoveSplineInit init = new(owner);
uint end = GetPathAtMapEnd();
init.args.path = new Vector3[end];
for (int i = (int)GetCurrentNode(); i != end; ++i)
{
Vector3 vertice = new(_path[i].Loc.X, _path[i].Loc.Y, _path[i].Loc.Z);
init.args.path[i] = vertice;
}
init.SetFirstPointId((int)GetCurrentNode());
init.SetFly();
init.SetSmooth();
init.SetUncompressed();
init.SetWalk(true);
init.SetVelocity(30.0f);
init.Launch();
}
public override bool DoUpdate(Player owner, uint diff)
{
if (owner == null)
return false;
uint pointId = (uint)(owner.MoveSpline.CurrentPathIdx() < 0 ? 0 : owner.MoveSpline.CurrentPathIdx());
if (pointId > _currentNode)
{
bool departureEvent = true;
do
{
DoEventIfAny(owner, _path[_currentNode], departureEvent);
while (!_pointsForPathSwitch.Empty() && _pointsForPathSwitch[0].PathIndex <= _currentNode)
{
_pointsForPathSwitch.RemoveAt(0);
owner.m_taxi.NextTaxiDestination();
if (!_pointsForPathSwitch.Empty())
{
owner.UpdateCriteria(CriteriaType.MoneySpentOnTaxis, (uint)_pointsForPathSwitch[0].Cost);
owner.ModifyMoney(-_pointsForPathSwitch[0].Cost);
}
}
if (pointId == _currentNode)
break;
if (_currentNode == _preloadTargetNode)
PreloadEndGrid();
_currentNode += (departureEvent ? 1 : 0);
departureEvent = !departureEvent;
}
while (true);
}
if (_currentNode >= (_path.Count - 1))
{
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
return true;
}
public override void DoDeactivate(Player owner)
{
AddFlag(MovementGeneratorFlags.Deactivated);
}
public override void DoFinalize(Player owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (!active)
return;
owner.Dismount();
owner.RemoveUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
if (owner.m_taxi.Empty())
{
// update z position to ground and orientation for landing point
// this prevent cheating with landing point at lags
// when client side flight end early in comparison server side
owner.StopMoving();
owner.SetFallInformation(0, owner.GetPositionZ());
}
owner.RemovePlayerFlag(PlayerFlags.TaxiBenchmark);
}
uint GetPathAtMapEnd()
{
if (_currentNode >= _path.Count)
@@ -92,90 +202,6 @@ namespace Game.Movement
}
}
public override void DoInitialize(Player owner)
{
Reset(owner);
InitEndGridInfo();
}
public override void DoFinalize(Player owner)
{
// remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
owner.ClearUnitState(UnitState.InFlight);
owner.Dismount();
owner.RemoveUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
if (owner.m_taxi.Empty())
{
// update z position to ground and orientation for landing point
// this prevent cheating with landing point at lags
// when client side flight end early in comparison server side
owner.StopMoving();
owner.SetFallInformation(0, owner.GetPositionZ());
}
owner.RemovePlayerFlag(PlayerFlags.TaxiBenchmark);
owner.RestoreDisplayId();
}
public override void DoReset(Player owner)
{
owner.AddUnitState(UnitState.InFlight);
owner.CombatStopWithPets();
owner.AddUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
MoveSplineInit init = new(owner);
uint end = GetPathAtMapEnd();
init.args.path = new Vector3[end];
for (int i = (int)GetCurrentNode(); i != end; ++i)
{
Vector3 vertice = new(_path[i].Loc.X, _path[i].Loc.Y, _path[i].Loc.Z);
init.args.path[i] = vertice;
}
init.SetFirstPointId((int)GetCurrentNode());
init.SetFly();
init.SetSmooth();
init.SetUncompressed();
init.SetWalk(true);
init.SetVelocity(30.0f);
init.Launch();
}
public override bool DoUpdate(Player player, uint time_diff)
{
uint pointId = (uint)player.MoveSpline.CurrentPathIdx();
if (pointId > _currentNode)
{
bool departureEvent = true;
do
{
DoEventIfAny(player, _path[_currentNode], departureEvent);
while (!_pointsForPathSwitch.Empty() && _pointsForPathSwitch[0].PathIndex <= _currentNode)
{
_pointsForPathSwitch.RemoveAt(0);
player.m_taxi.NextTaxiDestination();
if (!_pointsForPathSwitch.Empty())
{
player.UpdateCriteria(CriteriaType.MoneySpentOnTaxis, (uint)_pointsForPathSwitch[0].Cost);
player.ModifyMoney(-_pointsForPathSwitch[0].Cost);
}
}
if (pointId == _currentNode)
break;
if (_currentNode == _preloadTargetNode)
PreloadEndGrid();
_currentNode += (departureEvent ? 1 : 0);
departureEvent = !departureEvent;
}
while (true);
}
return _currentNode < (_path.Count - 1);
}
public void SetCurrentNodeAfterTeleport()
{
if (_path.Empty() || _currentNode >= _path.Count)
@@ -193,25 +219,16 @@ namespace Game.Movement
}
void DoEventIfAny(Player player, TaxiPathNodeRecord node, bool departure)
void DoEventIfAny(Player owner, TaxiPathNodeRecord node, bool departure)
{
uint eventid = departure ? node.DepartureEventID : node.ArrivalEventID;
if (eventid != 0)
{
Log.outDebug(LogFilter.Scripts, "Taxi {0} event {1} of node {2} of path {3} for player {4}", departure ? "departure" : "arrival", eventid, node.NodeIndex, node.PathID, player.GetName());
player.GetMap().ScriptsStart(ScriptsType.Event, eventid, player, player);
Log.outDebug(LogFilter.MapsScript, $"FlightPathMovementGenerator::DoEventIfAny: taxi {(departure ? "departure" : "arrival")} event {eventid} of node {node.NodeIndex} of path {node.PathID} for player {owner.GetName()}");
owner.GetMap().ScriptsStart(ScriptsType.Event, eventid, owner, owner);
}
}
bool GetResetPos(Player player, out float x, out float y, out float z)
{
TaxiPathNodeRecord node = _path[_currentNode];
x = node.Loc.X;
y = node.Loc.Y;
z = node.Loc.Z;
return true;
}
void InitEndGridInfo()
{
int nodeCount = _path.Count; //! Number of nodes in path.
@@ -223,24 +240,37 @@ namespace Game.Movement
void PreloadEndGrid()
{
// used to preload the final grid where the flightmaster is
// Used to preload the final grid where the flightmaster is
Map endMap = Global.MapMgr.FindBaseNonInstanceMap(_endMapId);
// Load the grid
if (endMap != null)
{
Log.outInfo(LogFilter.Server, "Preloading grid ({0}, {1}) for map {2} at node index {3}/{4}", _endGridX, _endGridY, _endMapId, _preloadTargetNode, _path.Count - 1);
Log.outDebug(LogFilter.Server, "FlightPathMovementGenerator::PreloadEndGrid: Preloading grid ({0}, {1}) for map {2} at node index {3}/{4}", _endGridX, _endGridY, _endMapId, _preloadTargetNode, _path.Count - 1);
endMap.LoadGrid(_endGridX, _endGridY);
}
else
Log.outInfo(LogFilter.Server, "Unable to determine map to preload flightmaster grid");
Log.outDebug(LogFilter.Server, "FlightPathMovementGenerator::PreloadEndGrid: Unable to determine map to preload flightmaster grid");
}
public override bool GetResetPosition(Unit u, out float x, out float y, out float z)
{
var node = _path[_currentNode];
x = node.Loc.X;
y = node.Loc.Y;
z = node.Loc.Z;
return true;
}
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Flight; }
public List<TaxiPathNodeRecord> GetPath() { return _path; }
bool HasArrived() { return (_currentNode >= _path.Count); }
bool HasArrived() { return _currentNode >= _path.Count; }
public void SkipCurrentNode() { ++_currentNode; }
@@ -22,7 +22,7 @@ using System;
namespace Game.Movement
{
public class FollowMovementGenerator : AbstractFollower, IMovementGenerator
public class FollowMovementGenerator : MovementGenerator
{
static uint CHECK_INTERVAL = 500;
static float FOLLOW_RANGE_TOLERANCE = 1.0f;
@@ -34,27 +34,46 @@ namespace Game.Movement
PathGenerator _path;
Position _lastTargetPosition;
public FollowMovementGenerator(Unit target, float range, ChaseAngle angle) : base(target)
AbstractFollower _abstractFollower;
public FollowMovementGenerator(Unit target, float range, ChaseAngle angle)
{
_abstractFollower = new AbstractFollower(target);
_range = range;
_angle = angle;
_lastTargetPosition = new();
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Follow;
}
public void Initialize(Unit owner)
public override void Initialize(Unit owner)
{
owner.AddUnitState(UnitState.Follow);
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
owner.StopMoving();
UpdatePetSpeed(owner);
_path = null;
_lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f);
}
public bool Update(Unit owner, uint diff)
public override void Reset(Unit owner)
{
RemoveFlag(MovementGeneratorFlags.Deactivated);
Initialize(owner);
}
public override bool Update(Unit owner, uint diff)
{
// owner might be dead or gone
if (!owner.IsAlive())
if (owner == null || !owner.IsAlive())
return false;
// our target might have gone away
Unit target = GetTarget();
Unit target = _abstractFollower.GetTarget();
if (target == null)
return false;
@@ -144,10 +163,20 @@ namespace Game.Movement
return true;
}
public void Finalize(Unit owner)
public override void Deactivate(Unit owner)
{
owner.ClearUnitState(UnitState.Follow | UnitState.FollowMove);
UpdatePetSpeed(owner);
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.FollowMove);
}
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
{
owner.ClearUnitState(UnitState.FollowMove);
UpdatePetSpeed(owner);
}
}
void UpdatePetSpeed(Unit owner)
@@ -155,7 +184,7 @@ namespace Game.Movement
Pet oPet = owner.ToPet();
if (oPet != null)
{
if (!GetTarget() || GetTarget().GetGUID() == owner.GetOwnerGUID())
if (!_abstractFollower.GetTarget() || _abstractFollower.GetTarget().GetGUID() == owner.GetOwnerGUID())
{
oPet.UpdateSpeed(UnitMoveType.Run);
oPet.UpdateSpeed(UnitMoveType.Walk);
@@ -164,11 +193,16 @@ namespace Game.Movement
}
}
public void Reset(Unit owner) { }
public Unit GetTarget()
{
return _abstractFollower.GetTarget();
}
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Follow; }
public void UnitSpeedChanged() { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Follow; }
public override void UnitSpeedChanged() { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
static bool PositionOkay(Unit owner, Unit target, float range, ChaseAngle? angle = null)
{
@@ -183,9 +217,9 @@ namespace Game.Movement
if (!owner.IsCreature())
return;
UnitAI ai = owner.GetAI();
if (ai != null)
((CreatureAI)ai).MovementInform(MovementGeneratorType.Follow, (uint)target.GetGUID().GetCounter());
Creature creatureOwner = owner.ToCreature();
if (creatureOwner.IsAIEnabled && creatureOwner.GetAI() != null)
creatureOwner.GetAI().MovementInform(MovementGeneratorType.Follow, (uint)target.GetGUID().GetCounter());
}
}
}
@@ -29,15 +29,21 @@ namespace Game.Movement
_moveType = moveType;
_run = run;
_orientation = orientation;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Roaming;
}
public override void DoInitialize(Creature owner)
{
owner.AddUnitState(UnitState.Roaming);
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
return;
}
@@ -73,6 +79,8 @@ namespace Game.Movement
public override void DoReset(Creature owner)
{
RemoveFlag(MovementGeneratorFlags.Transitory | MovementGeneratorFlags.Deactivated);
owner.StopMoving();
DoInitialize(owner);
}
@@ -84,15 +92,14 @@ namespace Game.Movement
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
return true;
}
if ((_interrupt && owner.MoveSpline.Finalized()) || (_recalculateSpeed && !owner.MoveSpline.Finalized()))
if ((HasFlag(MovementGeneratorFlags.Interrupted) && owner.MoveSpline.Finalized()) || (HasFlag(MovementGeneratorFlags.SpeedUpdatePending) && !owner.MoveSpline.Finalized()))
{
_recalculateSpeed = false;
_interrupt = false;
RemoveFlag(MovementGeneratorFlags.Interrupted | MovementGeneratorFlags.SpeedUpdatePending);
owner.AddUnitState(UnitState.RoamingMove);
@@ -122,20 +129,34 @@ namespace Game.Movement
init.Launch();
}
return !owner.MoveSpline.Finalized();
if (owner.MoveSpline.Finalized())
{
RemoveFlag(MovementGeneratorFlags.Transitory);
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
return true;
}
public override void DoFinalize(Creature owner)
public override void DoDeactivate(Creature owner)
{
owner.ClearUnitState(UnitState.Roaming | UnitState.RoamingMove);
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.RoamingMove);
}
if (owner.MoveSpline.Finalized())
public override void DoFinalize(Creature owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
owner.ClearUnitState(UnitState.RoamingMove);
if (movementInform && HasFlag(MovementGeneratorFlags.InformEnabled))
MovementInform(owner);
}
public void UnitSpeedChanged()
public override void UnitSpeedChanged()
{
_recalculateSpeed = true;
AddFlag(MovementGeneratorFlags.SpeedUpdatePending);
}
public override MovementGeneratorType GetMovementGeneratorType()
@@ -154,7 +175,5 @@ namespace Game.Movement
WaypointMoveType _moveType;
bool _run;
bool _orientation;
bool _recalculateSpeed;
bool _interrupt;
}
}
@@ -21,7 +21,7 @@ using System;
namespace Game.Movement
{
class GenericMovementGenerator : IMovementGenerator
class GenericMovementGenerator : MovementGenerator
{
MoveSplineInit _splineInit;
MovementGeneratorType _type;
@@ -39,25 +39,59 @@ namespace Game.Movement
_duration = new();
_arrivalSpellId = arrivalSpellId;
_arrivalSpellTargetGuid = arrivalSpellTargetGuid;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Roaming;
}
public void Initialize(Unit owner)
public override void Initialize(Unit owner)
{
if (HasFlag(MovementGeneratorFlags.Deactivated)) // Resume spline is not supported
{
RemoveFlag(MovementGeneratorFlags.Deactivated);
AddFlag(MovementGeneratorFlags.Finalized);
return;
}
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
_duration.Reset(_splineInit.Launch());
}
public bool Update(Unit owner, uint diff)
public override void Reset(Unit owner)
{
_duration.Update((int)diff);
if (_duration.Passed())
return false;
return !owner.MoveSpline.Finalized();
Initialize(owner);
}
public void Finalize(Unit owner)
public override bool Update(Unit owner, uint diff)
{
MovementInform(owner);
if (!owner || HasFlag(MovementGeneratorFlags.Finalized))
return false;
_duration.Update((int)diff);
if (_duration.Passed() || owner.MoveSpline.Finalized())
{
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
return true;
}
public override void Deactivate(Unit owner)
{
AddFlag(MovementGeneratorFlags.Deactivated);
}
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (movementInform && HasFlag(MovementGeneratorFlags.InformEnabled))
MovementInform(owner);
}
void MovementInform(Unit owner)
@@ -66,13 +100,10 @@ namespace Game.Movement
owner.CastSpell(Global.ObjAccessor.GetUnit(owner, _arrivalSpellTargetGuid), _arrivalSpellId, true);
Creature creature = owner.ToCreature();
if (creature != null)
if (creature.GetAI() != null)
creature.GetAI().MovementInform(_type, _pointId);
if (creature != null && creature.GetAI() != null)
creature.GetAI().MovementInform(_type, _pointId);
}
public void Reset(Unit owner) { }
public MovementGeneratorType GetMovementGeneratorType() { return _type; }
public override MovementGeneratorType GetMovementGeneratorType() { return _type; }
}
}
+54 -25
View File
@@ -23,67 +23,96 @@ namespace Game.AI
{
public class HomeMovementGenerator<T> : MovementGeneratorMedium<T> where T : Creature
{
public HomeMovementGenerator()
{
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Roaming;
}
public override void DoInitialize(T owner)
{
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
SetTargetLocation(owner);
}
public override void DoReset(T owner)
{
RemoveFlag(MovementGeneratorFlags.Deactivated);
DoInitialize(owner);
}
public override bool DoUpdate(T owner, uint diff)
{
_arrived = _skipToHome || owner.MoveSpline.Finalized();
return !_arrived;
if (HasFlag(MovementGeneratorFlags.Interrupted) || owner.MoveSpline.Finalized())
{
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
return true;
}
public override void DoFinalize(T owner)
public override void DoDeactivate(T owner)
{
if (_arrived)
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.RoamingMove);
}
public override void DoFinalize(T owner, bool active, bool movementInform)
{
if (!owner.IsCreature())
return;
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
owner.ClearUnitState(UnitState.RoamingMove | UnitState.Evade);
if (movementInform && HasFlag(MovementGeneratorFlags.InformEnabled))
{
owner.ClearUnitState(UnitState.Evade);
owner.SetWalk(true);
owner.SetSpawnHealth();
owner.LoadCreaturesAddon();
owner.GetAI().JustReachedHome();
owner.SetSpawnHealth();
}
}
void SetTargetLocation(T owner)
{
// if we are ROOT/STUNNED/DISTRACTED even after aura clear, finalize on next update - otherwise we would get stuck in evade
if (owner.HasUnitState(UnitState.Root | UnitState.Stunned | UnitState.Distracted))
{ // if we are ROOT/STUNNED/DISTRACTED even after aura clear, finalize on next update - otherwise we would get stuck in evade
_skipToHome = true;
{
AddFlag(MovementGeneratorFlags.Interrupted);
return;
}
owner.ClearUnitState(UnitState.AllErasable & ~UnitState.Evade);
owner.AddUnitState(UnitState.RoamingMove);
Position destination = owner.GetHomePosition();
MoveSplineInit init = new(owner);
float x, y, z, o;
// at apply we can select more nice return points base at current movegen
if (owner.GetMotionMaster().Empty() || !owner.GetMotionMaster().Top().GetResetPosition(owner, out x, out y, out z))
{
owner.GetHomePosition(out x, out y, out z, out o);
init.SetFacing(o);
}
owner.UpdateAllowedPositionZ(x, y, ref z);
init.MoveTo(x, y, z);
/*
* TODO: maybe this never worked, who knows, top is always this generator, so this code calls GetResetPosition on itself
*
* if (owner->GetMotionMaster()->empty() || !owner->GetMotionMaster()->top()->GetResetPosition(owner, x, y, z))
* {
* owner->GetHomePosition(x, y, z, o);
* init.SetFacing(o);
* }
*/
owner.UpdateAllowedPositionZ(destination.posX, destination.posY, ref destination.posZ);
init.MoveTo(destination);
init.SetFacing(destination.GetOrientation());
init.SetWalk(false);
init.Launch();
_skipToHome = false;
_arrived = false;
owner.ClearUnitState(UnitState.AllErasable & ~UnitState.Evade);
}
public override MovementGeneratorType GetMovementGeneratorType()
{
return MovementGeneratorType.Home;
}
bool _arrived;
bool _skipToHome;
}
}
+125 -48
View File
@@ -17,147 +17,224 @@
using Framework.Constants;
using Game.Entities;
using System;
namespace Game.Movement
{
public class IdleMovementGenerator : IMovementGenerator
public class IdleMovementGenerator : MovementGenerator
{
public void Initialize(Unit owner)
public IdleMovementGenerator()
{
Reset(owner);
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.Initialized;
BaseUnitState = 0;
}
public void Reset(Unit owner)
public override void Initialize(Unit owner)
{
if (!owner.IsStopped())
owner.StopMoving();
owner.StopMoving();
}
public bool Update(Unit owner, uint diff)
public override void Reset(Unit owner)
{
owner.StopMoving();
}
public override bool Update(Unit owner, uint diff)
{
return true;
}
public void Finalize(Unit owner)
public override void Deactivate(Unit owner) { }
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
}
public MovementGeneratorType GetMovementGeneratorType()
public override MovementGeneratorType GetMovementGeneratorType()
{
return MovementGeneratorType.Idle;
}
public bool IsActive { get; set; }
}
public class RotateMovementGenerator : IMovementGenerator
public class RotateMovementGenerator : MovementGenerator
{
public RotateMovementGenerator(uint time, RotateDirection direction)
public RotateMovementGenerator(uint id, uint time, RotateDirection direction)
{
_id = id;
_duration = time;
_maxDuration = time;
_direction = direction;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Rotating;
}
public void Initialize(Unit owner)
public override void Initialize(Unit owner)
{
if (!owner.IsStopped())
owner.StopMoving();
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
owner.StopMoving();
if (owner.GetVictim())
owner.SetInFront(owner.GetVictim());
owner.AddUnitState(UnitState.Rotating);
owner.AttackStop();
/*
* TODO: This code should be handled somewhere else, like MovementInform
*
* if (owner->GetVictim())
* owner->SetInFront(owner->GetVictim());
*
* owner->AttackStop();
*/
}
public void Reset(Unit owner) { }
public bool Update(Unit owner, uint diff)
public override void Reset(Unit owner)
{
RemoveFlag(MovementGeneratorFlags.Deactivated);
Initialize(owner);
}
public override bool Update(Unit owner, uint diff)
{
if (owner == null)
return false;
float angle = owner.GetOrientation();
angle += diff * MathFunctions.TwoPi / _maxDuration * (_direction == RotateDirection.Left ? 1.0f : -1.0f);
angle = MathFunctions.wrap(angle, 0.0f, MathFunctions.TwoPi);
angle = Math.Clamp(angle, 0.0f, MathF.PI * 2);
owner.SetOrientation(angle); // UpdateSplinePosition does not set orientation with UNIT_STATE_ROTATING
owner.SetFacingTo(angle); // Send spline movement to clients
MoveSplineInit init = new(owner);
init.MoveTo(owner, false);
if (!owner.GetTransGUID().IsEmpty())
init.DisableTransportPathTransformations();
init.SetFacing(angle);
init.Launch();
if (_duration > diff)
_duration -= diff;
else
{
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
return true;
}
public void Finalize(Unit owner)
public override void Deactivate(Unit owner)
{
owner.ClearUnitState(UnitState.Rotating);
if (owner.IsTypeId(TypeId.Unit))
owner.ToCreature().GetAI().MovementInform(MovementGeneratorType.Rotate, 0);
AddFlag(MovementGeneratorFlags.Deactivated);
}
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Rotate; }
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (movementInform && owner.IsCreature())
owner.ToCreature().GetAI().MovementInform(MovementGeneratorType.Rotate, _id);
}
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Rotate; }
uint _id;
uint _duration;
uint _maxDuration;
RotateDirection _direction;
}
public class DistractMovementGenerator : IMovementGenerator
public class DistractMovementGenerator : MovementGenerator
{
public DistractMovementGenerator(uint timer)
public DistractMovementGenerator(uint timer, float orientation)
{
_timer = timer;
_orientation = orientation;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Highest;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Distracted;
}
public void Initialize(Unit owner)
public override void Initialize(Unit owner)
{
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
// Distracted creatures stand up if not standing
if (!owner.IsStandState())
owner.SetStandState(UnitStandStateType.Stand);
owner.AddUnitState(UnitState.Distracted);
MoveSplineInit init = new(owner);
init.MoveTo(owner, false);
if (!owner.GetTransGUID().IsEmpty())
init.DisableTransportPathTransformations();
init.SetFacing(_orientation);
init.Launch();
}
public void Reset(Unit owner) { }
public bool Update(Unit owner, uint diff)
public override void Reset(Unit owner)
{
if (diff > _timer)
RemoveFlag(MovementGeneratorFlags.Deactivated);
Initialize(owner);
}
public override bool Update(Unit owner, uint diff)
{
if (owner == null)
return false;
if (diff > _timer)
{
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
_timer -= diff;
return true;
}
public virtual void Finalize(Unit owner)
public override void Deactivate(Unit owner)
{
owner.ClearUnitState(UnitState.Distracted);
AddFlag(MovementGeneratorFlags.Deactivated);
}
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
// TODO: This code should be handled somewhere else
// If this is a creature, then return orientation to original position (for idle movement creatures)
if (owner.IsTypeId(TypeId.Unit))
if (movementInform && HasFlag(MovementGeneratorFlags.InformEnabled) && owner.IsCreature())
{
float angle = owner.ToCreature().GetHomePosition().GetOrientation();
owner.SetFacingTo(angle);
}
}
public virtual MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Distract; }
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Distract; }
uint _timer;
float _orientation;
}
public class AssistanceDistractMovementGenerator : DistractMovementGenerator
{
public AssistanceDistractMovementGenerator(uint timer) : base(timer) { }
public AssistanceDistractMovementGenerator(uint timer, float orientation) : base(timer, orientation)
{
Priority = MovementGeneratorPriority.Normal;
}
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.AssistanceDistract; }
public override void Finalize(Unit owner)
public override void Finalize(Unit owner, bool active, bool movementInform)
{
owner.ClearUnitState(UnitState.Distracted);
owner.ToCreature().SetReactState(ReactStates.Aggressive);
}
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.AssistanceDistract; }
}
}
@@ -22,62 +22,86 @@ using Framework.GameMath;
namespace Game.Movement
{
public interface IMovementGenerator
public abstract class MovementGenerator
{
public void Finalize(Unit owner);
public MovementGeneratorMode Mode;
public MovementGeneratorPriority Priority;
public MovementGeneratorFlags Flags;
public UnitState BaseUnitState;
public void Initialize(Unit owner);
// on top first update
public virtual void Initialize(Unit owner) { }
public void Reset(Unit owner);
// on top reassign
public virtual void Reset(Unit owner) { }
public bool Update(Unit owner, uint time_diff);
// on top on MotionMaster::Update
public abstract bool Update(Unit owner, uint diff);
public MovementGeneratorType GetMovementGeneratorType();
// on current top if another movement replaces
public virtual void Deactivate(Unit owner) { }
public void UnitSpeedChanged() { }
// on movement delete
public virtual void Finalize(Unit owner, bool active, bool movementInform) { }
public void Pause(uint timer = 0) { }
public abstract MovementGeneratorType GetMovementGeneratorType();
public void Resume(uint overrideTimer = 0) { }
public virtual void UnitSpeedChanged() { }
// timer in ms
public virtual void Pause(uint timer = 0) { }
// timer in ms
public virtual void Resume(uint overrideTimer = 0) { }
// used by Evade code for select point to evade with expected restart default movement
public bool GetResetPosition(Unit u, out float x, out float y, out float z)
public virtual bool GetResetPosition(Unit u, out float x, out float y, out float z)
{
x = y = z = 0.0f;
return false;
}
public void AddFlag(MovementGeneratorFlags flag) { Flags |= flag; }
public bool HasFlag(MovementGeneratorFlags flag) { return (Flags & flag) != 0; }
public void RemoveFlag(MovementGeneratorFlags flag) { Flags &= ~flag; }
}
public abstract class MovementGeneratorMedium<T> : IMovementGenerator where T : Unit
public abstract class MovementGeneratorMedium<T> : MovementGenerator where T : Unit
{
public virtual void Initialize(Unit owner)
public override void Initialize(Unit owner)
{
DoInitialize((T)owner);
IsActive = true;
}
public virtual void Finalize(Unit owner)
{
DoFinalize((T)owner);
}
public virtual void Reset(Unit owner)
public override void Reset(Unit owner)
{
DoReset((T)owner);
}
public virtual bool Update(Unit owner, uint diff)
public override bool Update(Unit owner, uint diff)
{
return DoUpdate((T)owner, diff);
}
public override void Deactivate(Unit owner)
{
DoDeactivate((T)owner);
}
public override void Finalize(Unit owner, bool active, bool movementInform)
{
DoFinalize((T)owner, active, movementInform);
}
public bool IsActive { get; set; }
public abstract void DoInitialize(T owner);
public abstract void DoFinalize(T owner);
public abstract void DoFinalize(T owner, bool active, bool movementInform);
public abstract void DoReset(T owner);
public abstract bool DoUpdate(T owner, uint diff);
public abstract void DoDeactivate(T owner);
public virtual MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Max; }
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Max; }
}
}
@@ -22,31 +22,36 @@ namespace Game.Movement
{
public class PointMovementGenerator<T> : MovementGeneratorMedium<T> where T : Unit
{
public PointMovementGenerator(uint id, float x, float y, float z, bool generatePath, float speed = 0.0f, Unit faceTarget = null, SpellEffectExtraData spellEffectExtraData = null, float? finalOrient = null)
public PointMovementGenerator(uint id, float x, float y, float z, bool generatePath, float speed = 0.0f, float? finalOrient = null, Unit faceTarget = null, SpellEffectExtraData spellEffectExtraData = null)
{
_movementId = id;
_destination = new Position(x, y, z);
_speed = speed;
_generatePath = generatePath;
_finalOrient = finalOrient;
_faceTarget = faceTarget;
_spellEffectExtra = spellEffectExtraData;
_generatePath = generatePath;
_recalculateSpeed = false;
_finalOrient = finalOrient;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Roaming;
}
public override void DoInitialize(T owner)
{
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
if (_movementId == EventId.ChargePrepath)
{
owner.AddUnitState(UnitState.Roaming | UnitState.RoamingMove);
owner.AddUnitState(UnitState.RoamingMove);
return;
}
owner.AddUnitState(UnitState.Roaming);
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
return;
}
@@ -77,6 +82,8 @@ namespace Game.Movement
public override void DoReset(T owner)
{
RemoveFlag(MovementGeneratorFlags.Transitory | MovementGeneratorFlags.Deactivated);
owner.StopMoving();
DoInitialize(owner);
}
@@ -87,19 +94,25 @@ namespace Game.Movement
return false;
if (_movementId == EventId.ChargePrepath)
return !owner.MoveSpline.Finalized();
{
if (owner.MoveSpline.Finalized())
{
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
return true;
}
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
return true;
}
if ((_interrupt && owner.MoveSpline.Finalized()) || (_recalculateSpeed && !owner.MoveSpline.Finalized()))
if ((HasFlag(MovementGeneratorFlags.Interrupted) && owner.MoveSpline.Finalized()) || (HasFlag(MovementGeneratorFlags.SpeedUpdatePending) && !owner.MoveSpline.Finalized()))
{
_recalculateSpeed = false;
_interrupt = false;
RemoveFlag(MovementGeneratorFlags.Interrupted | MovementGeneratorFlags.SpeedUpdatePending);
owner.AddUnitState(UnitState.RoamingMove);
@@ -115,14 +128,28 @@ namespace Game.Movement
creature.SignalFormationMovement(_destination, _movementId);
}
return !owner.MoveSpline.Finalized();
if (owner.MoveSpline.Finalized())
{
RemoveFlag(MovementGeneratorFlags.Transitory);
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
return true;
}
public override void DoFinalize(T owner)
public override void DoDeactivate(T owner)
{
owner.ClearUnitState(UnitState.Roaming | UnitState.RoamingMove);
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.RoamingMove);
}
if (owner.MoveSpline.Finalized())
public override void DoFinalize(T owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
owner.ClearUnitState(UnitState.RoamingMove);
if (movementInform && HasFlag(MovementGeneratorFlags.InformEnabled))
MovementInform(owner);
}
@@ -135,11 +162,13 @@ namespace Game.Movement
}
}
public void UnitSpeedChanged()
public override void UnitSpeedChanged()
{
_recalculateSpeed = true;
AddFlag(MovementGeneratorFlags.SpeedUpdatePending);
}
public uint GetId() { return _movementId; }
public override MovementGeneratorType GetMovementGeneratorType()
{
return MovementGeneratorType.Point;
@@ -148,29 +177,33 @@ namespace Game.Movement
uint _movementId;
Position _destination;
float _speed;
Unit _faceTarget;
SpellEffectExtraData _spellEffectExtra;
bool _generatePath;
bool _recalculateSpeed;
bool _interrupt;
//! if set then unit will turn to specified _orient in provided _pos
float? _finalOrient;
Unit _faceTarget;
SpellEffectExtraData _spellEffectExtra;
}
public class AssistanceMovementGenerator : PointMovementGenerator<Creature>
{
public AssistanceMovementGenerator(float x, float y, float z) : base(0, x, y, z, true) { }
public AssistanceMovementGenerator(uint id, float x, float y, float z) : base(id, x, y, z, true) { }
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
owner.ClearUnitState(UnitState.RoamingMove);
if (movementInform && HasFlag(MovementGeneratorFlags.InformEnabled))
{
Creature ownerCreature = owner.ToCreature();
ownerCreature.SetNoCallAssistance(false);
ownerCreature.CallAssistance();
if (ownerCreature.IsAlive())
ownerCreature.GetMotionMaster().MoveSeekAssistanceDistract(WorldConfig.GetUIntValue(WorldCfg.CreatureFamilyAssistanceDelay));
}
}
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Assistance; }
public override void Finalize(Unit owner)
{
owner.ClearUnitState(UnitState.Roaming);
owner.StopMoving();
owner.ToCreature().SetNoCallAssistance(false);
owner.ToCreature().CallAssistance();
if (owner.IsAlive())
owner.GetMotionMaster().MoveSeekAssistanceDistract(WorldConfig.GetUIntValue(WorldCfg.CreatureFamilyAssistanceDelay));
}
}
}
@@ -24,18 +24,26 @@ namespace Game.Movement
{
public class RandomMovementGenerator : MovementGeneratorMedium<Creature>
{
public RandomMovementGenerator(float spawn_dist = 0.0f)
public RandomMovementGenerator(float spawnDist = 0.0f)
{
_timer = new TimeTracker();
_wanderDistance = spawn_dist;
_reference = new();
_wanderDistance = spawnDist;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Roaming;
}
public override void DoInitialize(Creature owner)
{
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
if (owner == null || !owner.IsAlive())
return;
owner.AddUnitState(UnitState.Roaming);
_reference = owner.GetPosition();
owner.StopMoving();
@@ -48,36 +56,52 @@ namespace Game.Movement
public override void DoReset(Creature owner)
{
RemoveFlag(MovementGeneratorFlags.Transitory | MovementGeneratorFlags.Deactivated);
DoInitialize(owner);
}
public override bool DoUpdate(Creature owner, uint diff)
{
if (!owner || !owner.IsAlive())
return false;
return true;
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
_path = null;
return true;
}
else
_interrupt = false;
RemoveFlag(MovementGeneratorFlags.Interrupted);
_timer.Update(diff);
if (!_interrupt && _timer.Passed() && owner.MoveSpline.Finalized())
if ((HasFlag(MovementGeneratorFlags.SpeedUpdatePending) && !owner.MoveSpline.Finalized()) || (_timer.Passed() && owner.MoveSpline.Finalized()))
{
RemoveFlag(MovementGeneratorFlags.Transitory);
SetRandomLocation(owner);
}
return true;
}
public override void DoFinalize(Creature owner)
public override void DoDeactivate(Creature owner)
{
owner.ClearUnitState(UnitState.Roaming);
owner.StopMoving();
owner.SetWalk(false);
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.RoamingMove);
}
public override void DoFinalize(Creature owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
{
owner.ClearUnitState(UnitState.RoamingMove);
owner.StopMoving();
// TODO: Research if this modification is needed, which most likely isnt
owner.SetWalk(false);
}
}
void SetRandomLocation(Creature owner)
@@ -87,14 +111,12 @@ namespace Game.Movement
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
_interrupt = true;
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
_path = null;
return;
}
owner.AddUnitState(UnitState.RoamingMove);
Position position = new(_reference);
float distance = RandomHelper.FRand(0.0f, 1.0f) * _wanderDistance;
float angle = RandomHelper.FRand(0.0f, 1.0f) * MathF.PI * 2.0f;
@@ -115,6 +137,8 @@ namespace Game.Movement
return;
}
owner.AddUnitState(UnitState.RoamingMove);
MoveSplineInit init = new(owner);
init.MovebyPath(_path.GetPath());
init.SetWalk(true);
@@ -125,6 +149,8 @@ namespace Game.Movement
owner.SignalFormationMovement(position);
}
public override void UnitSpeedChanged() { AddFlag(MovementGeneratorFlags.SpeedUpdatePending); }
public override MovementGeneratorType GetMovementGeneratorType()
{
return MovementGeneratorType.Random;
@@ -134,6 +160,5 @@ namespace Game.Movement
TimeTracker _timer;
Position _reference;
float _wanderDistance;
bool _interrupt;
}
}
@@ -23,7 +23,7 @@ using System.Collections.Generic;
namespace Game.Movement
{
public class SplineChainMovementGenerator : IMovementGenerator
public class SplineChainMovementGenerator : MovementGenerator
{
public SplineChainMovementGenerator(uint id, List<SplineChainLink> chain, bool walk = false)
{
@@ -31,117 +31,110 @@ namespace Game.Movement
_chain = chain;
_chainSize = (byte)chain.Count;
_walk = walk;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Roaming;
}
public SplineChainMovementGenerator(SplineChainResumeInfo info)
{
_id = info.PointID;
_chain = info.Chain;
_chainSize = (byte)info.Chain.Count;
_walk = info.IsWalkMode;
finished = info.SplineIndex >= info.Chain.Count;
_nextIndex = info.SplineIndex;
_nextFirstWP = info.PointIndex;
_msToNext = info.TimeToNext;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
if (info.SplineIndex >= info.Chain.Count)
AddFlag(MovementGeneratorFlags.Finalized);
BaseUnitState = UnitState.Roaming;
}
uint SendPathSpline(Unit me, Span<Vector3> wp)
public override void Initialize(Unit owner)
{
int numWp = wp.Length;
Cypher.Assert(numWp > 1, "Every path must have source & destination");
MoveSplineInit init = new(me);
if (numWp > 2)
init.MovebyPath(wp.ToArray());
else
init.MoveTo(wp[1], false, true);
init.SetWalk(_walk);
return (uint)init.Launch();
}
RemoveFlag(MovementGeneratorFlags.InitializationPending);
AddFlag(MovementGeneratorFlags.Initialized);
void SendSplineFor(Unit me, int index, ref uint toNext)
{
Cypher.Assert(index < _chainSize);
Log.outDebug(LogFilter.Movement, "{0}: Sending spline for {1}.", me.GetGUID().ToString(), index);
SplineChainLink thisLink = _chain[index];
uint actualDuration = SendPathSpline(me, new Span<Vector3>(thisLink.Points.ToArray()));
if (actualDuration != thisLink.ExpectedDuration)
if (_chainSize == 0)
{
Log.outDebug(LogFilter.Movement, "{0}: Sent spline for {1}, duration is {2} ms. Expected was {3} ms (delta {4} ms). Adjusting.", me.GetGUID().ToString(), index, actualDuration, thisLink.ExpectedDuration, actualDuration - thisLink.ExpectedDuration);
toNext = (uint)(actualDuration / thisLink.ExpectedDuration * toNext);
}
else
{
Log.outDebug(LogFilter.Movement, "{0}: Sent spline for {1}, duration is {2} ms.", me.GetGUID().ToString(), index, actualDuration);
}
}
public void Initialize(Unit me)
{
if (_chainSize != 0)
{
if (_nextFirstWP != 0) // this is a resumed movegen that has to start with a partial spline
{
if (finished)
return;
SplineChainLink thisLink = _chain[_nextIndex];
if (_nextFirstWP >= thisLink.Points.Count)
{
Log.outError(LogFilter.Movement, "{0}: Attempted to resume spline chain from invalid resume state ({1}, {2}).", me.GetGUID().ToString(), _nextIndex, _nextFirstWP);
_nextFirstWP = (byte)(thisLink.Points.Count - 1);
}
Span<Vector3> span = thisLink.Points.ToArray();
SendPathSpline(me, span[(_nextFirstWP - 1)..]);
Log.outDebug(LogFilter.Movement, "{0}: Resumed spline chain generator from resume state.", me.GetGUID().ToString());
++_nextIndex;
if (_nextIndex >= _chainSize)
_msToNext = 0;
else if (_msToNext == 0)
_msToNext = 1;
_nextFirstWP = 0;
}
else
{
_msToNext = Math.Max(_chain[_nextIndex].TimeToNext, 1u);
SendSplineFor(me, _nextIndex, ref _msToNext);
++_nextIndex;
if (_nextIndex >= _chainSize)
_msToNext = 0;
}
}
else
{
Log.outError(LogFilter.Movement, "SplineChainMovementGenerator.Initialize - empty spline chain passed for {0}.", me.GetGUID().ToString());
}
}
public void Finalize(Unit me)
{
if (!finished)
Log.outError(LogFilter.Movement, $"SplineChainMovementGenerator::Initialize: couldn't initialize generator, referenced spline is empty! ({owner.GetGUID()})");
return;
}
Creature cMe = me.ToCreature();
if (cMe && cMe.IsAIEnabled)
cMe.GetAI().MovementInform(MovementGeneratorType.SplineChain, _id);
if (_nextFirstWP != 0) // this is a resumed movegen that has to start with a partial spline
{
if (HasFlag(MovementGeneratorFlags.Finalized))
return;
SplineChainLink thisLink = _chain[_nextIndex];
if (_nextFirstWP >= thisLink.Points.Count)
{
Log.outError(LogFilter.Movement, $"SplineChainMovementGenerator::Initialize: attempted to resume spline chain from invalid resume state, _nextFirstWP >= path size (_nextIndex: {_nextIndex}, _nextFirstWP: {_nextFirstWP}). ({owner.GetGUID()})");
_nextFirstWP = (byte)(thisLink.Points.Count - 1);
}
owner.AddUnitState(UnitState.RoamingMove);
Span<Vector3> partial = thisLink.Points.ToArray();
SendPathSpline(owner, partial[(_nextFirstWP - 1)..]);
Log.outDebug(LogFilter.Movement, $"SplineChainMovementGenerator::Initialize: resumed spline chain generator from resume state. ({owner.GetGUID()})");
++_nextIndex;
if (_nextIndex >= _chainSize)
_msToNext = 0;
else if (_msToNext == 0)
_msToNext = 1;
_nextFirstWP = 0;
}
else
{
_msToNext = Math.Max(_chain[_nextIndex].TimeToNext, 1u);
SendSplineFor(owner, _nextIndex, ref _msToNext);
++_nextIndex;
if (_nextIndex >= _chainSize)
_msToNext = 0;
}
}
public bool Update(Unit me, uint diff)
public override void Reset(Unit owner)
{
if (finished)
RemoveFlag(MovementGeneratorFlags.Deactivated);
owner.StopMoving();
Initialize(owner);
}
public override bool Update(Unit owner, uint diff)
{
if (owner == null || HasFlag(MovementGeneratorFlags.Finalized))
return false;
// _msToNext being zero here means we're on the final spline
if (_msToNext == 0)
{
finished = me.MoveSpline.Finalized();
return !finished;
if (owner.MoveSpline.Finalized())
{
AddFlag(MovementGeneratorFlags.InformEnabled);
return false;
}
return true;
}
if (_msToNext <= diff)
{
// Send next spline
Log.outDebug(LogFilter.Movement, "{0}: Should send spline {1} ({2} ms late).", me.GetGUID().ToString(), _nextIndex, diff - _msToNext);
Log.outDebug(LogFilter.Movement, $"SplineChainMovementGenerator::Update: sending spline on index {_nextIndex} ({diff - _msToNext} ms late). ({owner.GetGUID()})");
_msToNext = Math.Max(_chain[_nextIndex].TimeToNext, 1u);
SendSplineFor(me, _nextIndex, ref _msToNext);
SendSplineFor(owner, _nextIndex, ref _msToNext);
++_nextIndex;
if (_nextIndex >= _chainSize)
{
@@ -152,32 +145,87 @@ namespace Game.Movement
}
else
_msToNext -= diff;
return true;
}
SplineChainResumeInfo GetResumeInfo(Unit me)
public override void Deactivate(Unit owner)
{
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.RoamingMove);
}
public override void Finalize(Unit owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
owner.ClearUnitState(UnitState.RoamingMove);
if (movementInform && HasFlag(MovementGeneratorFlags.InformEnabled))
{
Creature ownerCreature = owner.ToCreature();
if (ownerCreature != null && ownerCreature.IsAIEnabled)
ownerCreature.GetAI().MovementInform(MovementGeneratorType.SplineChain, _id);
}
}
uint SendPathSpline(Unit owner, Span<Vector3> path)
{
int nodeCount = path.Length;
Cypher.Assert(nodeCount > 1, $"SplineChainMovementGenerator::SendPathSpline: Every path must have source & destination (size > 1)! ({owner.GetGUID()})");
MoveSplineInit init = new(owner);
if (nodeCount > 2)
init.MovebyPath(path.ToArray());
else
init.MoveTo(path[1], false, true);
init.SetWalk(_walk);
return (uint)init.Launch();
}
void SendSplineFor(Unit owner, int index, ref uint duration)
{
Cypher.Assert(index < _chainSize, $"SplineChainMovementGenerator::SendSplineFor: referenced index ({index}) higher than path size ({_chainSize})!");
Log.outDebug(LogFilter.Movement, $"SplineChainMovementGenerator::SendSplineFor: sending spline on index: {index}. ({owner.GetGUID()})");
SplineChainLink thisLink = _chain[index];
uint actualDuration = SendPathSpline(owner, new Span<Vector3>(thisLink.Points.ToArray()));
if (actualDuration != thisLink.ExpectedDuration)
{
Log.outDebug(LogFilter.Movement, $"SplineChainMovementGenerator::SendSplineFor: sent spline on index: {index}, duration: {actualDuration} ms. Expected duration: {thisLink.ExpectedDuration} ms (delta {actualDuration - thisLink.ExpectedDuration} ms). Adjusting. ({owner.GetGUID()})");
duration = (uint)((double)actualDuration / (double)thisLink.ExpectedDuration * duration);
}
else
{
Log.outDebug(LogFilter.Movement, $"SplineChainMovementGenerator::SendSplineFor: sent spline on index {index}, duration: {actualDuration} ms. ({owner.GetGUID()})");
}
}
SplineChainResumeInfo GetResumeInfo(Unit owner)
{
if (_nextIndex == 0)
return new SplineChainResumeInfo(_id, _chain, _walk, 0, 0, _msToNext);
if (me.MoveSpline.Finalized())
if (owner.MoveSpline.Finalized())
{
if (_nextIndex < _chainSize)
return new SplineChainResumeInfo(_id, _chain, _walk, _nextIndex, 0, 1u);
else
return new SplineChainResumeInfo();
}
return new SplineChainResumeInfo(_id, _chain, _walk, (byte)(_nextIndex - 1), (byte)(me.MoveSpline.CurrentSplineIdx()), _msToNext);
return new SplineChainResumeInfo(_id, _chain, _walk, (byte)(_nextIndex - 1), (byte)owner.MoveSpline.CurrentSplineIdx(), _msToNext);
}
public void Reset(Unit owner) { }
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.SplineChain; }
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.SplineChain; }
public uint GetId() { return _id; }
uint _id;
uint _id;
List<SplineChainLink> _chain = new();
byte _chainSize;
bool _walk;
bool finished;
byte _nextIndex;
byte _nextFirstWP; // only used for resuming
uint _msToNext;
@@ -30,60 +30,211 @@ namespace Game.Movement
_pathId = pathId;
_repeating = repeating;
_loadedFromDB = true;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Roaming;
}
public WaypointMovementGenerator(WaypointPath path, bool repeating = true)
{
_nextMoveTime = new TimeTrackerSmall(0);
_repeating = repeating;
_repeating = repeating;
_path = path;
Mode = MovementGeneratorMode.Default;
Priority = MovementGeneratorPriority.Normal;
Flags = MovementGeneratorFlags.InitializationPending;
BaseUnitState = UnitState.Roaming;
}
public override void DoInitialize(Creature creature)
public override void Pause(uint timer = 0)
{
_done = false;
if (timer != 0)
{
AddFlag(MovementGeneratorFlags.TimedPaused);
_nextMoveTime.Reset((int)timer);
RemoveFlag(MovementGeneratorFlags.Paused);
}
else
{
AddFlag(MovementGeneratorFlags.Paused);
_nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
RemoveFlag(MovementGeneratorFlags.TimedPaused);
}
}
public override void Resume(uint overrideTimer = 0)
{
if (overrideTimer != 0)
_nextMoveTime.Reset((int)overrideTimer);
if (_nextMoveTime.Passed())
_nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
RemoveFlag(MovementGeneratorFlags.Paused);
}
public override bool GetResetPosition(Unit owner, out float x, out float y, out float z)
{
x = y = z = 0;
// prevent a crash at empty waypoint path.
if (_path == null || _path.nodes.Empty())
return false;
Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator::GetResetPosition: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})");
WaypointNode waypoint = _path.nodes.ElementAt(_currentNode);
x = waypoint.x;
y = waypoint.y;
z = waypoint.z;
return true;
}
public override void DoInitialize(Creature owner)
{
RemoveFlag(MovementGeneratorFlags.InitializationPending);
if (_loadedFromDB)
{
if (_pathId == 0)
_pathId = creature.GetWaypointPath();
_pathId = owner.GetWaypointPath();
_path = Global.WaypointMgr.GetPath(_pathId);
}
if (_path == null)
{
// No path id found for entry
Log.outError(LogFilter.Sql, $"WaypointMovementGenerator.DoInitialize: creature {creature.GetName()} ({creature.GetGUID()} DB GUID: {creature.GetSpawnId()}) doesn't have waypoint path id: {_pathId}");
Log.outError(LogFilter.Sql, $"WaypointMovementGenerator::DoInitialize: couldn't load path for creature ({owner.GetGUID()}) (_pathId: {_pathId})");
return;
}
owner.StopMoving();
_nextMoveTime.Reset(1000);
// inform AI
if (creature.IsAIEnabled)
creature.GetAI().WaypointPathStarted(_path.id);
if (owner.IsAIEnabled)
owner.GetAI().WaypointPathStarted(_path.id);
}
public override void DoFinalize(Creature creature)
public override void DoReset(Creature owner)
{
creature.ClearUnitState(UnitState.Roaming | UnitState.RoamingMove);
creature.SetWalk(false);
RemoveFlag(MovementGeneratorFlags.Transitory | MovementGeneratorFlags.Deactivated);
owner.StopMoving();
if (!HasFlag(MovementGeneratorFlags.Finalized) && _nextMoveTime.Passed())
_nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached
}
public override void DoReset(Creature creature)
public override bool DoUpdate(Creature owner, uint diff)
{
if (!_done && _nextMoveTime.Passed() && CanMove(creature))
StartMove(creature);
else if (_done)
if (!owner || !owner.IsAlive())
return true;
if (HasFlag(MovementGeneratorFlags.Finalized | MovementGeneratorFlags.Paused) || _path == null || _path.nodes.Empty())
return true;
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
{
// mimic IdleMovementGenerator
if (!creature.IsStopped())
creature.StopMoving();
AddFlag(MovementGeneratorFlags.Interrupted);
owner.StopMoving();
return true;
}
if (HasFlag(MovementGeneratorFlags.Interrupted))
{
/*
* relaunch only if
* - has a tiner? -> was it interrupted while not waiting aka moving? need to check both:
* -> has a timer - is it because its waiting to start next node?
* -> has a timer - is it because something set it while moving (like timed pause)?
*
* - doesnt have a timer? -> is movement valid?
*
* TODO: ((_nextMoveTime.Passed() && VALID_MOVEMENT) || (!_nextMoveTime.Passed() && !HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)))
*/
if (HasFlag(MovementGeneratorFlags.Initialized) && (_nextMoveTime.Passed() || !HasFlag(MovementGeneratorFlags.InformEnabled)))
{
StartMove(owner, true);
return true;
}
RemoveFlag(MovementGeneratorFlags.Interrupted);
}
// if it's moving
if (!owner.MoveSpline.Finalized())
{
// set home position at place (every MotionMaster::UpdateMotion)
if (owner.GetTransGUID().IsEmpty())
owner.SetHomePosition(owner.GetPosition());
// relaunch movement if its speed has changed
if (HasFlag(MovementGeneratorFlags.SpeedUpdatePending))
StartMove(owner, true);
}
else if (!_nextMoveTime.Passed()) // it's not moving, is there a timer?
{
if (UpdateTimer(diff))
{
if (!HasFlag(MovementGeneratorFlags.Initialized)) // initial movement call
{
StartMove(owner);
return true;
}
else if (!HasFlag(MovementGeneratorFlags.InformEnabled)) // timer set before node was reached, resume now
{
StartMove(owner, true);
return true;
}
}
else
return true; // keep waiting
}
else // not moving, no timer
{
if (HasFlag(MovementGeneratorFlags.Initialized) && !HasFlag(MovementGeneratorFlags.InformEnabled))
{
OnArrived(owner); // hooks and wait timer reset (if necessary)
AddFlag(MovementGeneratorFlags.InformEnabled); // signals to future StartMove that it reached a node
}
if (_nextMoveTime.Passed()) // OnArrived might have set a timer
StartMove(owner); // check path status, get next point and move if necessary & can
}
return true;
}
public override void DoDeactivate(Creature owner)
{
AddFlag(MovementGeneratorFlags.Deactivated);
owner.ClearUnitState(UnitState.RoamingMove);
}
public override void DoFinalize(Creature owner, bool active, bool movementInform)
{
AddFlag(MovementGeneratorFlags.Finalized);
if (active)
{
owner.ClearUnitState(UnitState.RoamingMove);
// TODO: Research if this modification is needed, which most likely isnt
owner.SetWalk(false);
}
}
void OnArrived(Creature creature)
void MovementInform(Creature owner)
{
if (owner.IsAIEnabled)
owner.GetAI().MovementInform(MovementGeneratorType.Waypoint, (uint)_currentNode);
}
void OnArrived(Creature owner)
{
if (_path == null || _path.nodes.Empty())
return;
@@ -92,103 +243,106 @@ namespace Game.Movement
WaypointNode waypoint = _path.nodes.ElementAt((int)_currentNode);
if (waypoint.delay != 0)
{
creature.ClearUnitState(UnitState.RoamingMove);
owner.ClearUnitState(UnitState.RoamingMove);
_nextMoveTime.Reset((int)waypoint.delay);
}
if (waypoint.eventId != 0 && RandomHelper.URand(0, 99) < waypoint.eventChance)
{
Log.outDebug(LogFilter.MapsScript, $"Creature movement start script {waypoint.eventId} at point {_currentNode} for {creature.GetGUID()}.");
creature.ClearUnitState(UnitState.RoamingMove);
creature.GetMap().ScriptsStart(ScriptsType.Waypoint, waypoint.eventId, creature, null);
Log.outDebug(LogFilter.MapsScript, $"Creature movement start script {waypoint.eventId} at point {_currentNode} for {owner.GetGUID()}.");
owner.ClearUnitState(UnitState.RoamingMove);
owner.GetMap().ScriptsStart(ScriptsType.Waypoint, waypoint.eventId, owner, null);
}
// inform AI
if (creature.IsAIEnabled)
if (owner.IsAIEnabled)
{
creature.GetAI().MovementInform(MovementGeneratorType.Waypoint, (uint)_currentNode);
creature.GetAI().WaypointReached(waypoint.id, _path.id);
owner.GetAI().MovementInform(MovementGeneratorType.Waypoint, (uint)_currentNode);
owner.GetAI().WaypointReached(waypoint.id, _path.id);
}
creature.UpdateCurrentWaypointInfo(waypoint.id, _path.id);
owner.UpdateCurrentWaypointInfo(waypoint.id, _path.id);
}
void StartMove(Creature creature, bool relaunch = false)
void StartMove(Creature owner, bool relaunch = false)
{
// sanity checks
if (creature == null || !creature.IsAlive() || _done || _path == null || _path.nodes.Empty() || (relaunch && _isArrivalDone))
if (owner == null || !owner.IsAlive() || HasFlag(MovementGeneratorFlags.Finalized) || _path == null || _path.nodes.Empty() || (relaunch && (HasFlag(MovementGeneratorFlags.InformEnabled) || !HasFlag(MovementGeneratorFlags.Initialized))))
return;
if (!relaunch) // on relaunch, can avoid this since its only called on valid movement
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting() || (owner.IsFormationLeader() && !owner.IsFormationLeaderMoveAllowed())) // if cannot move OR cannot move because of formation
{
if (!CanMove(creature) || (creature.IsFormationLeader() && !creature.IsFormationLeaderMoveAllowed())) // if cannot move OR cannot move because of formation
{
_nextMoveTime.Reset(1000); // delay 1s
return;
}
_nextMoveTime.Reset(1000); // delay 1s
return;
}
bool transportPath = creature.GetTransport() != null;
bool transportPath = !owner.GetTransGUID().IsEmpty();
if (_isArrivalDone)
if (HasFlag(MovementGeneratorFlags.InformEnabled) && HasFlag(MovementGeneratorFlags.Initialized))
{
Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})");
WaypointNode lastWaypoint = _path.nodes.ElementAt(_currentNode);
if ((_currentNode == _path.nodes.Count - 1) && !_repeating) // If that's our last waypoint
if (ComputeNextNode())
{
float x = lastWaypoint.x;
float y = lastWaypoint.y;
float z = lastWaypoint.z;
float o = creature.GetOrientation();
Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})");
// inform AI
if (owner.IsAIEnabled)
owner.GetAI().WaypointStarted(_path.nodes[_currentNode].id, _path.id);
}
else
{
WaypointNode currentWaypoint = _path.nodes[_currentNode];
float x = currentWaypoint.x;
float y = currentWaypoint.y;
float z = currentWaypoint.z;
float o = owner.GetOrientation();
if (!transportPath)
creature.SetHomePosition(x, y, z, o);
owner.SetHomePosition(x, y, z, o);
else
{
Transport trans = creature.GetTransport();
Transport trans = owner.GetTransport();
if (trans)
{
o -= trans.GetOrientation();
creature.SetTransportHomePosition(x, y, z, o);
owner.SetTransportHomePosition(x, y, z, o);
trans.CalculatePassengerPosition(ref x, ref y, ref z, ref o);
creature.SetHomePosition(x, y, z, o);
owner.SetHomePosition(x, y, z, o);
}
else
transportPath = false;
// else if (vehicle) - this should never happen, vehicle offsets are const
}
_done = true;
creature.UpdateCurrentWaypointInfo(0, 0);
AddFlag(MovementGeneratorFlags.Finalized);
owner.UpdateCurrentWaypointInfo(0, 0);
// inform AI
if (creature.IsAIEnabled)
creature.GetAI().WaypointPathEnded(lastWaypoint.id, _path.id);
if (owner.IsAIEnabled)
owner.GetAI().WaypointPathEnded(currentWaypoint.id, _path.id);
return;
}
_currentNode = (_currentNode + 1) % _path.nodes.Count;
}
else if (!HasFlag(MovementGeneratorFlags.Initialized))
{
AddFlag(MovementGeneratorFlags.Initialized);
// inform AI
if (creature.IsAIEnabled)
creature.GetAI().WaypointStarted(lastWaypoint.id, _path.id);
if (owner.IsAIEnabled)
owner.GetAI().WaypointStarted(_path.nodes[_currentNode].id, _path.id);
}
Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})");
WaypointNode waypoint = _path.nodes[_currentNode];
Position formationDest = new(waypoint.x, waypoint.y, waypoint.z, (waypoint.orientation != 0 && waypoint.delay != 0) ? waypoint.orientation : 0.0f);
_isArrivalDone = false;
_recalculateSpeed = false;
RemoveFlag(MovementGeneratorFlags.Transitory | MovementGeneratorFlags.InformEnabled | MovementGeneratorFlags.TimedPaused);
creature.AddUnitState(UnitState.RoamingMove);
owner.AddUnitState(UnitState.RoamingMove);
MoveSplineInit init = new(creature);
MoveSplineInit init = new(owner);
//! If creature is on transport, we assume waypoints set in DB are already transport offsets
if (transportPath)
{
init.DisableTransportPathTransformations();
ITransport trans = creature.GetDirectTransport();
ITransport trans = owner.GetDirectTransport();
if (trans != null)
{
float orientation = formationDest.GetOrientation();
@@ -224,113 +378,37 @@ namespace Game.Movement
init.Launch();
// inform formation
creature.SignalFormationMovement(formationDest, waypoint.id, waypoint.moveType, (waypoint.orientation != 0 && waypoint.delay != 0));
owner.SignalFormationMovement(formationDest, waypoint.id, waypoint.moveType, (waypoint.orientation != 0 && waypoint.delay != 0));
}
public override bool DoUpdate(Creature creature, uint diff)
bool ComputeNextNode()
{
if (!creature || !creature.IsAlive())
return true;
if (_done || _path == null || _path.nodes.Empty())
return true;
if (_stalled || creature.HasUnitState(UnitState.NotMove) || creature.IsMovementPreventedByCasting())
{
creature.StopMoving();
return true;
}
// if it's moving
if (!creature.MoveSpline.Finalized())
{
// set home position at place (every MotionMaster::UpdateMotion)
if (creature.GetTransGUID().IsEmpty())
creature.SetHomePosition(creature.GetPosition());
// relaunch movement if its speed has changed
if (_recalculateSpeed)
StartMove(creature, true);
}
else
{
// check if there is a wait time for the next movement
if (!_nextMoveTime.Passed())
{
// update timer since it's not moving
_nextMoveTime.Update((int)diff);
if (_nextMoveTime.Passed())
{
_nextMoveTime.Reset(0);
StartMove(creature); // check path status, get next point and move if necessary & can
}
}
else // if it's not moving and there is no timer, assume node is reached
{
OnArrived(creature); // hooks and wait timer reset (if necessary)
_isArrivalDone = true; // signals to future StartMove that it reached a node
if (_nextMoveTime.Passed())
StartMove(creature); // check path status, get next point and move if necessary & can
}
}
return true;
}
void MovementInform(Creature creature)
{
if (creature.IsAIEnabled)
creature.GetAI().MovementInform(MovementGeneratorType.Waypoint, (uint)_currentNode);
}
public bool GetResetPosition(Unit u, out float x, out float y, out float z)
{
x = y = z = 0;
// prevent a crash at empty waypoint path.
// prevent a crash at empty waypoint path.
if (_path == null || _path.nodes.Empty())
if ((_currentNode == _path.nodes.Count - 1) && !_repeating)
return false;
Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.GetResetPos: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})");
WaypointNode waypoint = _path.nodes.ElementAt(_currentNode);
x = waypoint.x;
y = waypoint.y;
z = waypoint.z;
_currentNode = (_currentNode + 1) % _path.nodes.Count;
return true;
}
public void Pause(uint timer = 0)
bool UpdateTimer(uint diff)
{
_stalled = timer == 0;
_nextMoveTime.Reset(timer != 0 ? (int)timer : 1);
}
public void Resume(uint overrideTimer = 0)
{
_stalled = false;
if (overrideTimer != 0)
_nextMoveTime.Reset((int)overrideTimer);
}
static bool CanMove(Creature creature)
{
return !creature.HasUnitState(UnitState.NotMove) && !creature.IsMovementPreventedByCasting();
_nextMoveTime.Update((int)diff);
if (_nextMoveTime.Passed())
{
_nextMoveTime.Reset(0);
return true;
}
return false;
}
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Waypoint; }
public void UnitSpeedChanged() { _recalculateSpeed = true; }
public override void UnitSpeedChanged() { AddFlag(MovementGeneratorFlags.SpeedUpdatePending); }
TimeTrackerSmall _nextMoveTime;
bool _recalculateSpeed;
bool _isArrivalDone;
uint _pathId;
bool _repeating;
bool _loadedFromDB;
bool _stalled;
bool _done;
WaypointPath _path;
int _currentNode;