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
@@ -17,11 +17,23 @@
namespace Framework.Constants
{
public enum MovementGeneratorMode
{
Default = 0,
Override
}
public enum MovementGeneratorPriority
{
None = 0,
Normal,
Highest
}
public enum MovementSlot
{
Idle,
Default = 0,
Active,
Controlled,
Max
}
@@ -50,6 +62,41 @@ namespace Framework.Constants
Max
}
public enum MotionMasterFlags
{
None = 0x0,
Update = 0x1, // Update in progress
StaticInitializationPending = 0x2
}
public enum MotionMasterDelayedActionType
{
Clear = 0,
ClearSlot,
ClearMode,
ClearPriority,
Add,
Remove,
RemoveType,
Initialize
}
public enum MovementGeneratorFlags
{
None = 0x000,
InitializationPending = 0x001,
Initialized = 0x002,
SpeedUpdatePending = 0x004,
Interrupted = 0x008,
Paused = 0x010,
TimedPaused = 0x020,
Deactivated = 0x040,
InformEnabled = 0x080,
Finalized = 0x100,
Transitory = SpeedUpdatePending | Interrupted
}
public struct EventId
{
public const uint Charge = 1003;
@@ -63,6 +110,7 @@ namespace Framework.Constants
public const uint Face = 1006;
public const uint VehicleBoard = 1007;
public const uint VehicleExit = 1008;
public const uint AssistMove = 1009;
public const uint SmartRandomPoint = 0xFFFFFE;
public const uint SmartEscortLastOCCPoint = 0xFFFFFF;
+1 -1
View File
@@ -381,7 +381,7 @@ namespace Framework.Constants
LoadEquipment = 124, // id
TriggerRandomTimedEvent = 125, // id min range, id max range
RemoveAllGameobjects = 126,
StopMotion = 127, // stopMoving, movementExpired
RemoveMovement = 127, // movementType, forced
PlayAnimkit = 128,
ScenePlay = 129, // sceneId
SceneCancel = 130, // sceneId
+1 -1
View File
@@ -97,7 +97,7 @@ namespace Game.AI
return new NullCreatureAI(creature);
}
public static IMovementGenerator SelectMovementAI(Unit unit)
public static MovementGenerator SelectMovementGenerator(Unit unit)
{
MovementGeneratorType type = unit.GetDefaultMovementType();
Creature creature = unit.ToCreature();
+3 -5
View File
@@ -156,9 +156,7 @@ namespace Game.AI
me.SendAIReaction(AiReaction.Alert);
// Face the unit (stealthed player) and set distracted state for 5 seconds
me.GetMotionMaster().MoveDistract(5 * Time.InMilliseconds);
me.StopMoving();
me.SetFacingTo(me.GetAbsoluteAngle(who));
me.GetMotionMaster().MoveDistract(5 * Time.InMilliseconds, me.GetAbsoluteAngle(who));
}
// Called for reaction at stopping attack at no attackers or targets
@@ -174,8 +172,8 @@ namespace Game.AI
Unit owner = me.GetCharmerOrOwner();
if (owner != null)
{
me.GetMotionMaster().Clear(false);
me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, me.GetFollowAngle(), MovementSlot.Active);
me.GetMotionMaster().Clear();
me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, me.GetFollowAngle());
}
else
{
+3 -18
View File
@@ -44,21 +44,6 @@ namespace Game.AI
return Global.ObjAccessor.GetPlayer(me, _playerGUID);
}
public override void AttackStart(Unit target)
{
if (target == null)
return;
if (me.Attack(target, true))
{
if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Point)
me.GetMotionMaster().MovementExpired();
if (IsCombatMovementAllowed())
me.GetMotionMaster().MoveChase(target);
}
}
//see followerAI
bool AssistPlayerInCombatAgainst(Unit who)
{
@@ -236,7 +221,7 @@ namespace Game.AI
else if (_resume)
{
_resume = false;
IMovementGenerator movementGenerator = me.GetMotionMaster().GetMotionSlot(MovementSlot.Idle);
MovementGenerator movementGenerator = me.GetMotionMaster().GetCurrentMovementGenerator(MovementSlot.Default);
if (movementGenerator != null)
movementGenerator.Resume(0);
}
@@ -436,7 +421,7 @@ namespace Game.AI
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
me.GetMotionMaster().MoveIdle();
me.GetMotionMaster().Clear(MovementSlot.Active);
me.GetMotionMaster().Clear(MovementGeneratorPriority.Normal);
//disable npcflags
me.SetNpcFlags(NPCFlags.None);
@@ -464,7 +449,7 @@ namespace Game.AI
if (on)
{
AddEscortState(EscortState.Paused);
IMovementGenerator movementGenerator = me.GetMotionMaster().GetMotionSlot(MovementSlot.Idle);
MovementGenerator movementGenerator = me.GetMotionMaster().GetCurrentMovementGenerator(MovementSlot.Default);
if (movementGenerator != null)
movementGenerator.Pause(0);
}
+20 -9
View File
@@ -158,7 +158,7 @@ namespace Game.AI
{
if (!HasEscortState(SmartEscortState.Escorting))
{
me.PauseMovement(delay, MovementSlot.Idle, forced);
me.PauseMovement(delay, MovementSlot.Default, forced);
if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint)
{
var (nodeId, pathId) = me.GetCurrentWaypointInfo();
@@ -597,8 +597,11 @@ namespace Game.AI
CreatureGroup formation = me.GetFormation();
if (formation == null || formation.GetLeader() == me || !formation.IsFormed())
{
if (me.GetMotionMaster().GetMotionSlotType(MovementSlot.Idle) != MovementGeneratorType.Waypoint && me.GetWaypointPath() != 0)
me.GetMotionMaster().MovePath(me.GetWaypointPath(), true);
if (me.GetMotionMaster().GetCurrentMovementGeneratorType(MovementSlot.Default) != MovementGeneratorType.Waypoint)
{
if (me.GetWaypointPath() != 0)
me.GetMotionMaster().MovePath(me.GetWaypointPath(), true);
}
else
me.ResumeMovement();
}
@@ -644,7 +647,8 @@ namespace Game.AI
if (who != null && me.Attack(who, _canAutoAttack))
{
me.GetMotionMaster().Clear(MovementSlot.Active);
me.GetMotionMaster().Clear(MovementGeneratorPriority.Normal);
me.PauseMovement();
if (_canCombatMove)
{
@@ -826,13 +830,20 @@ namespace Game.AI
if (me.IsEngaged())
{
if (on && !me.HasReactState(ReactStates.Passive) && me.GetVictim() && me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Max)
if (on)
{
SetRun(_run);
me.GetMotionMaster().MoveChase(me.GetVictim());
if (!me.HasReactState(ReactStates.Passive) && me.GetVictim() && !me.GetMotionMaster().HasMovementGenerator(movement => movement.Mode == MovementGeneratorMode.Default && movement.Priority == MovementGeneratorPriority.Normal))
{
SetRun(_run);
me.GetMotionMaster().MoveChase(me.GetVictim());
}
}
else
{
var movement = me.GetMotionMaster().GetMovementGenerator(a => a.GetMovementGeneratorType() == MovementGeneratorType.Chase && a.Mode == MovementGeneratorMode.Default && a.Priority == MovementGeneratorPriority.Normal);
if (movement != null)
me.GetMotionMaster().Remove(movement);
}
else if (!on && me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Chase)
me.GetMotionMaster().Clear(MovementSlot.Active);
}
}
@@ -1511,7 +1511,7 @@ namespace Game.AI
case SmartActions.RemoveAllGameobjects:
case SmartActions.SpawnSpawngroup:
case SmartActions.DespawnSpawngroup:
case SmartActions.StopMotion:
case SmartActions.RemoveMovement:
break;
default:
Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Not handled action_type({0}), event_type({1}), Entry {2} SourceType {3} Event {4}, skipped.", e.GetActionType(), e.GetEventType(), e.EntryOrGuid, e.GetScriptType(), e.EventId);
@@ -2515,7 +2515,7 @@ namespace Game.AI
public RandomTimedEvent randomTimedEvent;
[FieldOffset(4)]
public StopMotion stopMotion;
public RemoveMovement removeMovement;
[FieldOffset(4)]
public RespawnData respawnData;
@@ -3022,10 +3022,10 @@ namespace Game.AI
public uint minId;
public uint maxId;
}
public struct StopMotion
public struct RemoveMovement
{
public uint stopMovement;
public uint movementExpired;
public uint movementType;
public uint forced;
}
public struct RespawnData
{
+4 -4
View File
@@ -2293,16 +2293,16 @@ namespace Game.AI
break;
}
case SmartActions.StopMotion:
case SmartActions.RemoveMovement:
{
foreach (var target in targets)
{
if (IsUnit(target))
{
if (e.Action.stopMotion.stopMovement != 0)
if (e.Action.removeMovement.movementType != 0 && e.Action.removeMovement.movementType < (uint)MovementGeneratorType.Max)
target.ToUnit().GetMotionMaster().Remove((MovementGeneratorType)e.Action.removeMovement.movementType);
if (e.Action.removeMovement.forced != 0)
target.ToUnit().StopMoving();
if (e.Action.stopMotion.movementExpired != 0)
target.ToUnit().GetMotionMaster().MovementExpired();
}
}
+24 -65
View File
@@ -97,13 +97,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
player.TeleportTo(mapId, x, y, z, o);
@@ -137,13 +133,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
player.TeleportTo(gy.Loc);
return true;
@@ -179,13 +171,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
Map map = Global.MapMgr.CreateBaseMap(mapId);
float z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
@@ -227,13 +215,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
player.TeleportTo(goData.spawnPoint);
return true;
@@ -287,13 +271,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
Map map = Global.MapMgr.CreateBaseMap(mapId);
z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
@@ -333,13 +313,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
player.TeleportTo(node.ContinentID, node.Pos.X, node.Pos.Y, node.Pos.Z, player.GetOrientation());
return true;
@@ -372,13 +348,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
player.TeleportTo(at.ContinentID, at.Pos.X, at.Pos.Y, at.Pos.Z, player.GetOrientation());
return true;
@@ -439,13 +411,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
float z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
@@ -501,13 +469,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
player.TeleportTo(mapId, x, y, z, ort);
return true;
@@ -548,13 +512,12 @@ namespace Game.Chat.Commands
}
Player player = handler.GetSession().GetPlayer();
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
ticket.TeleportTo(player);
return true;
@@ -592,13 +555,9 @@ namespace Game.Chat.Commands
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
player.TeleportTo(player.GetMapId(), x, y, z, o);
return true;
+2 -6
View File
@@ -109,13 +109,9 @@ namespace Game.Chat
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
// before GM
float x, y, z;
+41 -70
View File
@@ -354,13 +354,9 @@ namespace Game.Chat
// stop flight if need
if (_player.IsInFlight())
{
_player.GetMotionMaster().MovementExpired();
_player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
_player.FinishTaxiFlight();
else
_player.SaveRecallPosition();
_player.SaveRecallPosition(); // save only in non-flight case
// to point to see at target with same orientation
float x, y, z;
@@ -387,13 +383,9 @@ namespace Game.Chat
// stop flight if need
if (_player.IsInFlight())
{
_player.GetMotionMaster().MovementExpired();
_player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
_player.FinishTaxiFlight();
else
_player.SaveRecallPosition();
_player.SaveRecallPosition(); // save only in non-flight case
loc.SetOrientation(_player.GetOrientation());
_player.TeleportTo(loc);
@@ -1216,20 +1208,19 @@ namespace Game.Chat
handler.SendSysMessage(CypherStrings.MovegensList, (unit.IsTypeId(TypeId.Player) ? "Player" : "Creature"), unit.GetGUID().ToString());
MotionMaster motionMaster = unit.GetMotionMaster();
float x, y, z;
motionMaster.GetDestination(out x, out y, out z);
for (byte i = 0; i < (int)MovementSlot.Max; ++i)
if (unit.GetMotionMaster().Empty())
{
IMovementGenerator movementGenerator = motionMaster.GetMotionSlot(i);
if (movementGenerator == null)
{
handler.SendSysMessage("Empty");
continue;
}
handler.SendSysMessage("Empty");
return true;
}
switch (movementGenerator.GetMovementGeneratorType())
float x, y, z;
unit.GetMotionMaster().GetDestination(out x, out y, out z);
var list = unit.GetMotionMaster().GetMovementGeneratorsInformation();
foreach (MovementGeneratorInformation info in list)
{
switch (info.Type)
{
case MovementGeneratorType.Idle:
handler.SendSysMessage(CypherStrings.MovegensIdle);
@@ -1244,45 +1235,33 @@ namespace Game.Chat
handler.SendSysMessage(CypherStrings.MovegensConfused);
break;
case MovementGeneratorType.Chase:
{
Unit target = ((ChaseMovementGenerator)movementGenerator).GetTarget();
if (!target)
handler.SendSysMessage(CypherStrings.MovegensChaseNull);
else if (target.IsTypeId(TypeId.Player))
handler.SendSysMessage(CypherStrings.MovegensChasePlayer, target.GetName(), target.GetGUID().ToString());
else
handler.SendSysMessage(CypherStrings.MovegensChaseCreature, target.GetName(), target.GetGUID().ToString());
break;
}
if (info.TargetGUID.IsEmpty())
handler.SendSysMessage(CypherStrings.MovegensChaseNull);
else if (info.TargetGUID.IsPlayer())
handler.SendSysMessage(CypherStrings.MovegensChasePlayer, info.TargetName, info.TargetGUID.ToString());
else
handler.SendSysMessage(CypherStrings.MovegensChaseCreature, info.TargetName, info.TargetGUID.ToString());
break;
case MovementGeneratorType.Follow:
{
Unit target = ((FollowMovementGenerator)movementGenerator).GetTarget();
if (!target)
handler.SendSysMessage(CypherStrings.MovegensFollowNull);
else if (target.IsTypeId(TypeId.Player))
handler.SendSysMessage(CypherStrings.MovegensFollowPlayer, target.GetName(), target.GetGUID().ToString());
else
handler.SendSysMessage(CypherStrings.MovegensFollowCreature, target.GetName(), target.GetGUID().ToString());
break;
}
if (info.TargetGUID.IsEmpty())
handler.SendSysMessage(CypherStrings.MovegensFollowNull);
else if (info.TargetGUID.IsPlayer())
handler.SendSysMessage(CypherStrings.MovegensFollowPlayer, info.TargetName, info.TargetGUID.ToString());
else
handler.SendSysMessage(CypherStrings.MovegensFollowCreature, info.TargetName, info.TargetGUID.ToString());
break;
case MovementGeneratorType.Home:
{
if (unit.IsTypeId(TypeId.Unit))
handler.SendSysMessage(CypherStrings.MovegensHomeCreature, x, y, z);
else
handler.SendSysMessage(CypherStrings.MovegensHomePlayer);
break;
}
if (unit.IsTypeId(TypeId.Unit))
handler.SendSysMessage(CypherStrings.MovegensHomeCreature, x, y, z);
else
handler.SendSysMessage(CypherStrings.MovegensHomePlayer);
break;
case MovementGeneratorType.Flight:
handler.SendSysMessage(CypherStrings.MovegensFlight);
break;
case MovementGeneratorType.Point:
{
handler.SendSysMessage(CypherStrings.MovegensPoint, x, y, z);
break;
}
handler.SendSysMessage(CypherStrings.MovegensPoint, x, y, z);
break;
case MovementGeneratorType.Fleeing:
handler.SendSysMessage(CypherStrings.MovegensFear);
break;
@@ -1293,7 +1272,7 @@ namespace Game.Chat
handler.SendSysMessage(CypherStrings.MovegensEffect);
break;
default:
handler.SendSysMessage(CypherStrings.MovegensUnknown, movementGenerator.GetMovementGeneratorType());
handler.SendSysMessage(CypherStrings.MovegensUnknown, info.Type);
break;
}
}
@@ -1915,11 +1894,7 @@ namespace Game.Chat
}
// stop flight if need
if (target.IsInFlight())
{
target.GetMotionMaster().MovementExpired();
target.CleanupAfterTaxiFlight();
}
target.FinishTaxiFlight();
target.Recall();
return true;
@@ -2162,14 +2137,10 @@ namespace Game.Chat
target.SendSysMessage(CypherStrings.SummonedBy, handler.PlayerLink(_player.GetName()));
// stop flight if need
if (target.IsInFlight())
{
target.GetMotionMaster().MovementExpired();
target.CleanupAfterTaxiFlight();
}
// save only in non-flight case
if (_player.IsInFlight())
_player.FinishTaxiFlight();
else
target.SaveRecallPosition();
_player.SaveRecallPosition(); // save only in non-flight case
// before GM
float x, y, z;
+12 -12
View File
@@ -976,23 +976,23 @@ namespace Game.Chat
return false;
}
if (/*creature.GetMotionMaster().empty() ||*/
creature.GetMotionMaster().GetCurrentMovementGeneratorType() != MovementGeneratorType.Follow)
MovementGenerator movement = creature.GetMotionMaster().GetMovementGenerator(a =>
{
if (a.GetMovementGeneratorType() == MovementGeneratorType.Follow)
{
FollowMovementGenerator followMovement = a as FollowMovementGenerator;
return followMovement != null && followMovement.GetTarget() == player;
}
return false;
});
if (movement != null)
{
handler.SendSysMessage(CypherStrings.CreatureNotFollowYou, creature.GetName());
return false;
}
FollowMovementGenerator mgen = (FollowMovementGenerator)creature.GetMotionMaster().Top();
if (mgen.GetTarget() != player)
{
handler.SendSysMessage(CypherStrings.CreatureNotFollowYou, creature.GetName());
return false;
}
// reset movement
creature.GetMotionMaster().MovementExpired(true);
creature.GetMotionMaster().Remove(movement);
handler.SendSysMessage(CypherStrings.CreatureNotFollowYouNow, creature.GetName());
return true;
}
+11 -24
View File
@@ -35,40 +35,35 @@ namespace Game.Chat
if (args.Empty())
return false;
Player me = handler.GetPlayer();
Player player = handler.GetPlayer();
GameTele tele = handler.ExtractGameTeleFromLink(args);
if (tele == null)
{
handler.SendSysMessage(CypherStrings.CommandTeleNotfound);
return false;
}
if (me.IsInCombat() && !handler.GetSession().HasPermission(RBACPermissions.CommandTeleName))
if (player.IsInCombat() && !handler.GetSession().HasPermission(RBACPermissions.CommandTeleName))
{
handler.SendSysMessage(CypherStrings.YouInCombat);
return false;
}
var map = CliDB.MapStorage.LookupByKey(tele.mapId);
if (map == null || (map.IsBattlegroundOrArena() && (me.GetMapId() != tele.mapId || !me.IsGameMaster())))
if (map == null || (map.IsBattlegroundOrArena() && (player.GetMapId() != tele.mapId || !player.IsGameMaster())))
{
handler.SendSysMessage(CypherStrings.CannotTeleToBg);
return false;
}
// stop flight if need
if (me.IsInFlight())
{
me.GetMotionMaster().MovementExpired();
me.CleanupAfterTaxiFlight();
}
// save only in non-flight case
if (player.IsInFlight())
player.FinishTaxiFlight();
else
me.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
me.TeleportTo(tele.mapId, tele.posX, tele.posY, tele.posZ, tele.orientation);
player.TeleportTo(tele.mapId, tele.posX, tele.posY, tele.posZ, tele.orientation);
return true;
}
@@ -196,13 +191,9 @@ namespace Game.Chat
// stop flight if need
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
// save only in non-flight case
player.FinishTaxiFlight();
else
player.SaveRecallPosition();
player.SaveRecallPosition(); // save only in non-flight case
player.TeleportTo(tele.mapId, tele.posX, tele.posY, tele.posZ, tele.orientation);
}
@@ -270,13 +261,9 @@ namespace Game.Chat
// stop flight if need
if (target.IsInFlight())
{
target.GetMotionMaster().MovementExpired();
target.CleanupAfterTaxiFlight();
}
// save only in non-flight case
target.FinishTaxiFlight();
else
target.SaveRecallPosition();
target.SaveRecallPosition(); // save only in non-flight case
target.TeleportTo(tele.mapId, tele.posX, tele.posY, tele.posZ, tele.orientation);
}
+1 -5
View File
@@ -1264,11 +1264,7 @@ namespace Game.DungeonFinding
if (!player.GetMap().IsDungeon())
player.SetBattlegroundEntryPoint();
if (player.IsInFlight())
{
player.GetMotionMaster().MovementExpired();
player.CleanupAfterTaxiFlight();
}
player.FinishTaxiFlight();
if (!player.TeleportTo(mapid, x, y, z, orientation))
error = LfgTeleportResult.NoReturnLocation;
+4 -4
View File
@@ -110,7 +110,7 @@ namespace Game.Entities
public bool IsReturningHome()
{
if (GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Home)
if (GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Home)
return true;
return false;
@@ -551,8 +551,7 @@ namespace Game.Entities
{
Unit charmer = Global.ObjAccessor.GetUnit(this, LastCharmerGUID);
if (charmer)
if (CanStartAttack(charmer, true))
i_AI.AttackStart(charmer);
EngageWithTarget(charmer);
}
LastCharmerGUID.Clear();
@@ -1917,7 +1916,8 @@ namespace Game.Entities
SetNativeDisplayId(display.CreatureDisplayID, display.DisplayScale);
}
GetMotionMaster().InitDefault();
GetMotionMaster().InitializeDefault();
//Re-initialize reactstate that could be altered by movementgenerators
InitializeReactState();
@@ -266,7 +266,8 @@ namespace Game.Entities
creature.GetMotionMaster().Initialize();
else
creature.GetMotionMaster().MoveIdle();
Log.outDebug(LogFilter.Unit, "Set {0} movement for member GUID: {1}", dismiss ? "default" : "idle", creature.GetGUID().ToString());
Log.outDebug(LogFilter.Unit, $"CreatureGroup::FormationReset: Set {(dismiss ? "default" : "idle")} movement for member {creature.GetGUID()}");
}
}
@@ -314,20 +315,20 @@ namespace Game.Entities
if (!member.IsFlying())
member.UpdateGroundPositionZ(dx, dy, ref dz);
Position point = new(dx, dy, dz, destination.GetOrientation());
member.GetMotionMaster().MoveFormation(id, point, moveType, !member.IsWithinDist(_leader, dist + 5.0f), orientation);
member.SetHomePosition(dx, dy, dz, pathangle);
Position point = new(dx, dy, dz, destination.GetOrientation());
member.GetMotionMaster().MoveFormation(id, point, moveType, !member.IsWithinDist(_leader, dist + 5.0f), orientation);
}
}
public bool CanLeaderStartMoving()
{
foreach (var itr in _members)
foreach (var pair in _members)
{
if (itr.Key != _leader && itr.Key.IsAlive())
if (pair.Key != _leader && pair.Key.IsAlive())
{
if (itr.Key.IsEngaged() || itr.Key.IsReturningHome())
if (pair.Key.IsEngaged() || pair.Key.IsReturningHome())
return false;
}
}
+11 -11
View File
@@ -2120,11 +2120,7 @@ namespace Game.Entities
return;
// stop taxi flight at summon
if (IsInFlight())
{
GetMotionMaster().MovementExpired();
CleanupAfterTaxiFlight();
}
FinishTaxiFlight();
// drop flag at summon
// this code can be reached only when GM is summoning player who carries flag, because player should be immune to summoning spells when he carries flag
@@ -6880,9 +6876,18 @@ namespace Game.Entities
return ActivateTaxiPathTo(nodes, null, spellid);
}
public void FinishTaxiFlight()
{
if (!IsInFlight())
return;
GetMotionMaster().Remove(MovementGeneratorType.Flight);
m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
}
public void CleanupAfterTaxiFlight()
{
m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
Dismount();
RemoveUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
}
@@ -7206,13 +7211,8 @@ namespace Game.Entities
public void SetMover(Unit target)
{
m_unitMovedByMe.m_playerMovingMe = null;
if (m_unitMovedByMe.IsCreature())
m_unitMovedByMe.GetMotionMaster().Initialize();
m_unitMovedByMe = target;
m_unitMovedByMe.m_playerMovingMe = this;
if (m_unitMovedByMe.IsCreature())
m_unitMovedByMe.GetMotionMaster().Initialize();
MoveSetActiveMover packet = new();
packet.MoverGUID = target.GetGUID();
+9 -11
View File
@@ -172,11 +172,11 @@ namespace Game.Entities
if (MotionMaster.IsInvalidMovementSlot(slot))
return;
IMovementGenerator movementGenerator = GetMotionMaster().GetMotionSlot(slot);
MovementGenerator movementGenerator = GetMotionMaster().GetCurrentMovementGenerator(slot);
if (movementGenerator != null)
movementGenerator.Pause(timer);
if (forced)
if (forced && GetMotionMaster().GetCurrentSlot() == slot)
StopMoving();
}
@@ -185,7 +185,7 @@ namespace Game.Entities
if (MotionMaster.IsInvalidMovementSlot(slot))
return;
IMovementGenerator movementGenerator = GetMotionMaster().GetMotionSlot(slot);
MovementGenerator movementGenerator = GetMotionMaster().GetCurrentMovementGenerator(slot);
if (movementGenerator != null)
movementGenerator.Resume(timer);
}
@@ -208,7 +208,7 @@ namespace Game.Entities
init.DisableTransportPathTransformations(); // It makes no sense to target global orientation
init.SetFacing(ori);
//GetMotionMaster().LaunchMoveSpline(init, EventId.Face, MovementSlot.Controlled);
//GetMotionMaster().LaunchMoveSpline(init, EventId.Face, MovementGeneratorPriority.Highest);
init.Launch();
}
@@ -223,7 +223,7 @@ namespace Game.Entities
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZ(), false);
init.SetFacing(GetAbsoluteAngle(obj)); // when on transport, GetAbsoluteAngle will still return global coordinates (and angle) that needs transforming
//GetMotionMaster().LaunchMoveSpline(init, EventId.Face, MovementSlot.Controlled);
//GetMotionMaster().LaunchMoveSpline(init, EventId.Face, MovementGeneratorPriority.Highest);
init.Launch();
}
@@ -232,7 +232,7 @@ namespace Game.Entities
MoveSplineInit init = new(this);
init.MoveTo(x, y, z, generatePath, forceDestination);
init.SetVelocity(speed);
GetMotionMaster().LaunchMoveSpline(init, 0, MovementSlot.Active, MovementGeneratorType.Point);
GetMotionMaster().LaunchMoveSpline(init, 0, MovementGeneratorPriority.Normal, MovementGeneratorType.Point);
}
public void KnockbackFrom(float x, float y, float speedXY, float speedZ, SpellEffectExtraData spellEffectExtraData = null)
@@ -539,7 +539,7 @@ namespace Game.Entities
{
if (GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Follow)
{
Unit followed = ((AbstractFollower)GetMotionMaster().Top()).GetTarget();
Unit followed = (GetMotionMaster().GetCurrentMovementGenerator() as FollowMovementGenerator).GetTarget();
if (followed != null && followed.GetGUID() == GetOwnerGUID() && !followed.IsInCombat())
{
float ownerSpeed = followed.GetSpeedRate(mtype);
@@ -1254,8 +1254,7 @@ namespace Game.Entities
{
if (IsAlive())
{
if (GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Fleeing)
GetMotionMaster().MovementExpired();
GetMotionMaster().Remove(MovementGeneratorType.Fleeing);
if (GetVictim() != null)
SetTarget(GetVictim().GetGUID());
}
@@ -1280,8 +1279,7 @@ namespace Game.Entities
{
if (IsAlive())
{
if (GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Confused)
GetMotionMaster().MovementExpired();
GetMotionMaster().Remove(MovementGeneratorType.Confused);
if (GetVictim() != null)
SetTarget(GetVictim().GetGUID());
}
+3 -8
View File
@@ -353,11 +353,8 @@ namespace Game.Entities
if (IsTypeId(TypeId.Unit))
{
IMovementGenerator movementGenerator = GetMotionMaster().GetMotionSlot(MovementSlot.Idle);
if (movementGenerator != null)
movementGenerator.Pause(0);
GetMotionMaster().Clear(MovementSlot.Active);
PauseMovement(0, 0, false);
GetMotionMaster().Clear(MovementGeneratorPriority.Normal);
StopMoving();
@@ -476,7 +473,7 @@ namespace Game.Entities
RestoreFaction();
///@todo Handle SLOT_IDLE motion resume
GetMotionMaster().InitDefault();
GetMotionMaster().InitializeDefault();
Creature creature = ToCreature();
if (creature)
@@ -547,8 +544,6 @@ namespace Game.Entities
player.SetClientControl(this, true);
}
EngageWithTarget(charmer);
// a guardian should always have charminfo
if (playerCharmer && this != charmer.GetFirstControlled())
playerCharmer.SendRemoveControlBar();
+3 -3
View File
@@ -171,7 +171,7 @@ namespace Game.Entities
}
UpdateSplineMovement(diff);
GetMotionMaster().UpdateMotion(diff);
GetMotionMaster().Update(diff);
}
void _UpdateSpells(uint diff)
@@ -999,7 +999,7 @@ namespace Game.Entities
init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), height, false);
init.SetFacing(GetOrientation());
init.SetTransportExit();
GetMotionMaster().LaunchMoveSpline(init, EventId.VehicleExit, MovementSlot.Controlled);
GetMotionMaster().LaunchMoveSpline(init, EventId.VehicleExit, MovementGeneratorPriority.Highest);
if (player != null)
player.ResummonPetTemporaryUnSummonedIfAny();
@@ -1353,7 +1353,7 @@ namespace Game.Entities
// * Using 'call pet' on dead pets
// * Using 'call stabled pet'
// * Logging in with dead pets
GetMotionMaster().Clear(false);
GetMotionMaster().Clear();
GetMotionMaster().MoveIdle();
}
StopMoving();
+1 -1
View File
@@ -641,7 +641,7 @@ namespace Game.Entities
init.MoveTo(veSeat.AttachmentOffset.X, veSeat.AttachmentOffset.Y, veSeat.AttachmentOffset.Z, false, true);
init.SetFacing(0.0f);
init.SetTransportEnter();
Passenger.GetMotionMaster().LaunchMoveSpline(init, EventId.VehicleBoard, MovementSlot.Controlled);
Passenger.GetMotionMaster().LaunchMoveSpline(init, EventId.VehicleBoard, MovementGeneratorPriority.Highest);
Creature creature = Target.GetBase().ToCreature();
if (creature != null)
+1 -5
View File
@@ -345,11 +345,7 @@ namespace Game
GetPlayer().SpawnCorpseBones();
}
// stop taxi flight at port
if (GetPlayer().IsInFlight())
{
GetPlayer().GetMotionMaster().MovementExpired();
GetPlayer().CleanupAfterTaxiFlight();
}
GetPlayer().FinishTaxiFlight();
BattlefieldStatusActive battlefieldStatus;
Global.BattlegroundMgr.BuildBattlegroundStatusActive(out battlefieldStatus, bg, GetPlayer(), battlefieldPort.Ticket.Id, GetPlayer().GetBattlegroundQueueJoinTime(bgQueueTypeId), bg.GetArenaType());
+6 -8
View File
@@ -324,22 +324,21 @@ namespace Game
}
// flight fast teleport case
if (GetPlayer().GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Flight)
if (GetPlayer().IsInFlight())
{
if (!GetPlayer().InBattleground())
{
if (!seamlessTeleport)
{
// short preparations to continue flight
IMovementGenerator movementGenerator = GetPlayer().GetMotionMaster().Top();
MovementGenerator movementGenerator = GetPlayer().GetMotionMaster().GetCurrentMovementGenerator();
movementGenerator.Initialize(GetPlayer());
}
return;
}
// Battlegroundstate prepare, stop flight
GetPlayer().GetMotionMaster().MovementExpired();
GetPlayer().CleanupAfterTaxiFlight();
GetPlayer().FinishTaxiFlight();
}
// resurrect character at enter into instance where his corpse exist after add to map
@@ -719,13 +718,12 @@ namespace Game
TaxiNodesRecord curDestNode = CliDB.TaxiNodesStorage.LookupByKey(curDest);
// far teleport case
if (curDestNode != null && curDestNode.ContinentID != GetPlayer().GetMapId())
if (curDestNode != null && curDestNode.ContinentID != GetPlayer().GetMapId() && GetPlayer().GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Flight)
{
if (GetPlayer().GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Flight)
FlightPathMovementGenerator flight = GetPlayer().GetMotionMaster().GetCurrentMovementGenerator() as FlightPathMovementGenerator;
if (flight != null)
{
// short preparations to continue flight
FlightPathMovementGenerator flight = (FlightPathMovementGenerator)GetPlayer().GetMotionMaster().Top();
flight.SetCurrentNodeAfterTeleport();
TaxiPathNodeRecord node = flight.GetPath()[(int)flight.GetCurrentNode()];
flight.SkipCurrentNode();
+2 -2
View File
@@ -141,7 +141,7 @@ namespace Game
{
case CommandStates.Stay: //flat=1792 //STAY
pet.StopMoving();
pet.GetMotionMaster().Clear(false);
pet.GetMotionMaster().Clear();
pet.GetMotionMaster().MoveIdle();
charmInfo.SetCommandState(CommandStates.Stay);
@@ -249,7 +249,7 @@ namespace Game
break;
case CommandStates.MoveTo:
pet.StopMoving();
pet.GetMotionMaster().Clear(false);
pet.GetMotionMaster().Clear();
pet.GetMotionMaster().MovePoint(0, x, y, z);
charmInfo.SetCommandState(CommandStates.MoveTo);
+3 -3
View File
@@ -130,7 +130,7 @@ namespace Game
if (GetPlayer().HasUnitState(UnitState.Died))
GetPlayer().RemoveAurasByType(AuraType.FeignDeath);
GetPlayer().GetMotionMaster().Clear(MovementSlot.Controlled);
GetPlayer().GetMotionMaster().Remove(MovementGeneratorType.Flight);
if (mountDisplayId != 0)
GetPlayer().Mount(mountDisplayId);
@@ -235,11 +235,11 @@ namespace Game
[WorldPacketHandler(ClientOpcodes.TaxiRequestEarlyLanding, Processing = PacketProcessing.ThreadSafe)]
void HandleTaxiRequestEarlyLanding(TaxiRequestEarlyLanding taxiRequestEarlyLanding)
{
if (GetPlayer().GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Flight)
FlightPathMovementGenerator flight = GetPlayer().GetMotionMaster().GetCurrentMovementGenerator() as FlightPathMovementGenerator;
if (flight != null)
{
if (GetPlayer().m_taxi.RequestEarlyLanding())
{
FlightPathMovementGenerator flight = (FlightPathMovementGenerator)GetPlayer().GetMotionMaster().Top();
flight.LoadPath(GetPlayer(), flight.GetPath()[(int)flight.GetCurrentNode()].NodeIndex);
flight.Reset(GetPlayer());
}
-1
View File
@@ -4875,7 +4875,6 @@ namespace Game.Maps
if (!cSource.IsAlive())
return;
cSource.GetMotionMaster().MovementExpired();
cSource.GetMotionMaster().MoveIdle();
switch ((MovementGeneratorType)step.script.Movement.MovementType)
@@ -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;
File diff suppressed because it is too large Load Diff
+1 -10
View File
@@ -2439,8 +2439,6 @@ namespace Game.Spells
[AuraEffectHandler(AuraType.ModPossessPet)]
void HandleModPossessPet(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply)
{
// Used by spell "Eyes of the Beast"
if (!mode.HasAnyFlag(AuraEffectHandleModes.Real))
return;
@@ -2459,9 +2457,6 @@ namespace Game.Spells
if (caster.ToPlayer().GetPet() != pet)
return;
// Must clear current motion or pet leashes back to owner after a few yards
// when under spell 'Eyes of the Beast'
pet.GetMotionMaster().Clear();
pet.SetCharmedBy(caster, CharmType.Possess, aurApp);
}
else
@@ -2475,13 +2470,9 @@ namespace Game.Spells
// Reinitialize the pet bar or it will appear greyed out
caster.ToPlayer().PetSpellInitialize();
// Follow owner only if not fighting or owner didn't click "stay" at new location
// This may be confusing because pet bar shows "stay" when under the spell but it retains
// the "follow" flag. Player MUST click "stay" while under the spell.
// TODO: remove this
if (pet.GetVictim() == null && !pet.GetCharmInfo().HasCommandState(CommandStates.Stay))
{
pet.GetMotionMaster().MoveFollow(caster, SharedConst.PetFollowDist, pet.GetFollowAngle());
}
}
}
}
+1 -5
View File
@@ -1922,11 +1922,7 @@ namespace Game.Spells
if (unitTarget.HasUnitState(UnitState.Confused | UnitState.Stunned | UnitState.Fleeing))
return;
if (unitTarget.IsTypeId(TypeId.Unit))
unitTarget.GetMotionMaster().MoveDistract((uint)(damage * Time.InMilliseconds));
unitTarget.StopMoving();
unitTarget.SetFacingTo(unitTarget.GetAbsoluteAngle(destTarget));
unitTarget.GetMotionMaster().MoveDistract((uint)(damage * Time.InMilliseconds), unitTarget.GetAbsoluteAngle(destTarget));
}
[SpellEffectHandler(SpellEffectName.Pickpocket)]
+1 -1
View File
@@ -96,7 +96,7 @@ namespace Scripts.Pets
float x = me.GetPositionX() + 20 * (float)Math.Cos(me.GetOrientation());
float y = me.GetPositionY() + 20 * (float)Math.Sin(me.GetOrientation());
float z = me.GetPositionZ() + 40;
me.GetMotionMaster().Clear(false);
me.GetMotionMaster().Clear();
me.GetMotionMaster().MovePoint(0, x, y, z);
// Despawn as soon as possible
+2 -2
View File
@@ -189,8 +189,8 @@ namespace Scripts.Pets
me.CombatStop(true);
if (owner && !me.HasUnitState(UnitState.Follow))
{
me.GetMotionMaster().Clear(false);
me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, me.GetFollowAngle(), MovementSlot.Active);
me.GetMotionMaster().Clear();
me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, me.GetFollowAngle());
}
}
}
+2 -2
View File
@@ -186,13 +186,13 @@ namespace Scripts.World.EmeraldDragons
if (target)
{
_roamTimer = RandomHelper.URand(15000, 30000);
me.GetMotionMaster().Clear(false);
me.GetMotionMaster().Clear();
me.GetMotionMaster().MoveChase(target, 0.2f);
}
else
{
_roamTimer = 2500;
me.GetMotionMaster().Clear(false);
me.GetMotionMaster().Clear();
me.GetMotionMaster().MoveRandom(25.0f);
}
// Seeping fog movement is slow enough for a player to be able to walk backwards and still outpace it
+1 -1
View File
@@ -2323,7 +2323,7 @@ namespace Scripts.World.NpcSpecial
init.DisableTransportPathTransformations();
init.MoveTo(x, y, z, false);
init.SetFacing(o);
who.GetMotionMaster().LaunchMoveSpline(init, EventId.VehicleBoard, MovementSlot.Controlled);
who.GetMotionMaster().LaunchMoveSpline(init, EventId.VehicleBoard, MovementGeneratorPriority.Highest);
who.m_Events.AddEvent(new CastFoodSpell(who, SpellIds.ChairSpells[who.GetEntry()]), who.m_Events.CalculateTime(1000));
Creature creature = who.ToCreature();
if (creature)