From 6a77a4afd6d3805487c6db89ec4a102d83408a20 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 27 Aug 2019 16:12:42 -0400 Subject: [PATCH] Revert Core/Movement: Smooth movement Port From (https://github.com/TrinityCore/TrinityCore/commit/2caec4f4d20b4c0f91abbcc60b756e00838c7bdd) --- Source/Framework/Constants/SmartAIConst.cs | 2 +- Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs | 367 +++++++++++------- Source/Game/AI/SmartScripts/SmartAI.cs | 287 +++++++------- Source/Game/AI/SmartScripts/SmartAIManager.cs | 25 +- Source/Game/AI/SmartScripts/SmartScript.cs | 52 +++ .../Movement/Generators/WaypointMovement.cs | 201 +++------- Source/Game/Movement/MotionMaster.cs | 5 - Source/Game/Movement/WaypointManager.cs | 42 +- 8 files changed, 488 insertions(+), 493 deletions(-) diff --git a/Source/Framework/Constants/SmartAIConst.cs b/Source/Framework/Constants/SmartAIConst.cs index 774d39c1c..a74696c4f 100644 --- a/Source/Framework/Constants/SmartAIConst.cs +++ b/Source/Framework/Constants/SmartAIConst.cs @@ -317,7 +317,7 @@ namespace Framework.Constants RemovePower = 110, // PowerType, newPower GameEventStop = 111, // GameEventId GameEventStart = 112, // GameEventId - // Not used + StartClosestWaypoint = 113, // wp1, wp2, wp3, wp4, wp5, wp6, wp7 MoveOffset = 114, RandomSound = 115, // SoundId1, SoundId2, SoundId3, SoundId4, SoundId5, onlySelf SetCorpseDelay = 116, // timer diff --git a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs index 745375e8e..7737fbddb 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs @@ -31,11 +31,10 @@ namespace Game.AI public npc_escortAI(Creature creature) : base(creature) { m_uiPlayerGUID = ObjectGuid.Empty; - m_uiWPWaitTimer = 1000; - m_uiPlayerCheckTimer = 0; + m_uiWPWaitTimer = 2500; + m_uiPlayerCheckTimer = 1000; m_uiEscortState = eEscortState.None; MaxPlayerDistance = 50; - LastWP = 0; m_pQuestForEscort = null; m_bIsActiveAttacker = true; m_bIsRunning = false; @@ -45,8 +44,21 @@ namespace Game.AI DespawnAtFar = true; ScriptWP = false; HasImmuneToNPCFlags = false; - m_bStarted = false; - m_bEnded = false; + } + + 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 @@ -89,15 +101,38 @@ namespace Game.AI public override void MoveInLineOfSight(Unit who) { - if (me.GetVictim()) - return; - if (me.HasReactState(ReactStates.Aggressive) && !me.HasUnitState(UnitState.Stunned) && who.isTargetableForAttack() && who.isInAccessiblePlaceFor(me)) + { if (HasEscortState(eEscortState.Escorting) && AssistPlayerInCombatAgainst(who)) return; - if (me.CanStartAttack(who, false)) - AttackStart(who); + if (!me.CanFly() && me.GetDistanceZ(who) > SharedConst.CreatureAttackRangeZ) + return; + + if (me.IsHostileTo(who)) + { + float fAttackRadius = me.GetAttackDistance(who); + if (me.IsWithinDistInMap(who, fAttackRadius) && me.IsWithinLOSInMap(who)) + { + if (!me.GetVictim()) + { + // Clear distracted state on combat + if (me.HasUnitState(UnitState.Distracted)) + { + me.ClearUnitState(UnitState.Distracted); + me.GetMotionMaster().Clear(); + } + + AttackStart(who); + } + else if (me.GetMap().IsDungeon()) + { + who.SetInCombatWith(me); + me.AddThreat(who, 0.0f); + } + } + } + } } public override void JustDied(Unit killer) @@ -126,13 +161,13 @@ namespace Game.AI public override void JustRespawned() { - RemoveEscortState(eEscortState.Escorting | eEscortState.Returning | eEscortState.Paused); + m_uiEscortState = eEscortState.None; if (!IsCombatMovementAllowed()) SetCombatMovement(true); //add a small delay before going to first waypoint, normal in near all cases - m_uiWPWaitTimer = 1000; + m_uiWPWaitTimer = 2500; if (me.GetFaction() != me.GetCreatureTemplate().Faction) me.RestoreFaction(); @@ -142,7 +177,6 @@ namespace Game.AI void ReturnToLastPoint() { - me.SetWalk(false); me.GetMotionMaster().MovePoint(0xFFFFFF, me.GetHomePosition()); } @@ -193,75 +227,73 @@ namespace Game.AI public override void UpdateAI(uint diff) { + //Waypoint Updating if (HasEscortState(eEscortState.Escorting) && !me.GetVictim() && m_uiWPWaitTimer != 0 && !HasEscortState(eEscortState.Returning)) { if (m_uiWPWaitTimer <= diff) { - if (!HasEscortState(eEscortState.Paused)) + //End of the line + if (WaypointList[CurrentWPIndex] == null) { m_uiWPWaitTimer = 0; - if (m_bEnded) + if (DespawnAtEnd) { - me.StopMoving(); - me.GetMotionMaster().Clear(false); - me.GetMotionMaster().MoveIdle(); + Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints"); - m_bEnded = false; - - if (DespawnAtEnd) + if (m_bCanReturnToStart) { - Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints"); + float fRetX, fRetY, fRetZ; + me.GetRespawnPosition(out fRetX, out fRetY, out fRetZ); - if (m_bCanReturnToStart) - { - float fRetX, fRetY, fRetZ; - me.GetRespawnPosition(out fRetX, out fRetY, out fRetZ); + me.GetMotionMaster().MovePoint(EscortPointIds.Home, fRetX, fRetY, fRetZ); - me.GetMotionMaster().MovePoint(EscortPointIds.Home, fRetX, fRetY, fRetZ); + m_uiWPWaitTimer = 0; - Log.outDebug(LogFilter.Scripts, $"EscortAI are returning home to spawn location: {EscortPointIds.Home}, {fRetX}, {fRetY}, {fRetZ}"); - } - else if (m_bCanInstantRespawn) - { - me.setDeathState(DeathState.JustDied); - me.Respawn(); - } - else - me.DespawnOrUnsummon(); + Log.outDebug(LogFilter.Scripts, $"EscortAI are returning home to spawn location: {EscortPointIds.Home}, {fRetX}, {fRetY}, {fRetZ}"); + return; + } + + if (m_bCanInstantRespawn) + { + me.setDeathState(DeathState.JustDied); + me.Respawn(); } else - Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints with Despawn off"); + me.DespawnOrUnsummon(); - RemoveEscortState(eEscortState.Escorting); return; } - - if (!m_bStarted) - { - m_bStarted = true; - me.GetMotionMaster().MovePath(_path, false); - } else { - WaypointMovementGenerator move = (WaypointMovementGenerator)me.GetMotionMaster().top(); - if (move != null) - WaypointStart(move.GetCurrentNode()); + Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints with Despawn off"); + return; } + + } + + if (!HasEscortState(eEscortState.Paused)) + { + var currentWp = WaypointList[CurrentWPIndex]; + me.GetMotionMaster().MovePoint(currentWp.Id, currentWp.X, currentWp.Y, currentWp.Z); + Log.outDebug(LogFilter.Scripts, $"EscortAI start waypoint {currentWp.Id} ({currentWp.X}, {currentWp.Y}, {currentWp.Z})."); + + WaypointStart(currentWp.Id); + + m_uiWPWaitTimer = 0; } } - else - m_uiWPWaitTimer -= diff; } //Check if player or any member of his group is within range if (HasEscortState(eEscortState.Escorting) && !m_uiPlayerGUID.IsEmpty() && !me.GetVictim() && !HasEscortState(eEscortState.Returning)) { - m_uiPlayerCheckTimer += diff; - if (m_uiPlayerCheckTimer > 1000) + if (m_uiPlayerCheckTimer <= diff) { if (DespawnAtFar && !IsPlayerOrGroupInRange()) { + Log.outDebug(LogFilter.Scripts, "EscortAI failed because player/group was to far away or not found"); + if (m_bCanInstantRespawn) { me.setDeathState(DeathState.JustDied); @@ -273,8 +305,10 @@ namespace Game.AI return; } - m_uiPlayerCheckTimer = 0; + m_uiPlayerCheckTimer = 1000; } + else + m_uiPlayerCheckTimer -= diff; } UpdateEscortAI(diff); @@ -290,84 +324,53 @@ namespace Game.AI public override void MovementInform(MovementGeneratorType moveType, uint pointId) { - // no action allowed if there is no escort - if (!HasEscortState(eEscortState.Escorting)) + if (moveType != MovementGeneratorType.Point || !HasEscortState(eEscortState.Escorting)) return; - if (moveType == MovementGeneratorType.Point) + //Combat start position reached, continue waypoint movement + if (pointId == EscortPointIds.LastPoint) { + Log.outDebug(LogFilter.Scripts, "EscortAI has returned to original position before combat"); + + me.SetWalk(!m_bIsRunning); + RemoveEscortState(eEscortState.Returning); + if (m_uiWPWaitTimer == 0) m_uiWPWaitTimer = 1; - - //Combat start position reached, continue waypoint movement - if (pointId == EscortPointIds.LastPoint) - { - Log.outDebug(LogFilter.Scripts, "EscortAI has returned to original position before combat"); - - me.SetWalk(!m_bIsRunning); - RemoveEscortState(eEscortState.Returning); - } - else if (pointId == EscortPointIds.Home) - { - Log.outDebug(LogFilter.Scripts, "EscortAI has returned to original home location and will continue from beginning of waypoint list."); - - m_bStarted = false; - } } - else if (moveType == MovementGeneratorType.Waypoint) + else if (pointId == EscortPointIds.Home) { - //Call WP function - WaypointReached(pointId); + Log.outDebug(LogFilter.Scripts, "EscortAI has returned to original home location and will continue from beginning of waypoint list."); - //End of the line - if (LastWP != 0 && LastWP == pointId) + CurrentWPIndex = 0; + m_uiWPWaitTimer = 1; + } + else + { + var currentWp = WaypointList[CurrentWPIndex]; + //Make sure that we are still on the right waypoint + if (currentWp.Id != pointId) { - LastWP = 0; - - m_bStarted = false; - m_bEnded = true; - - m_uiWPWaitTimer = 50; - + Log.outError(LogFilter.Misc, $"TSCR ERROR: EscortAI reached waypoint out of order {pointId}, expected {currentWp.Id}, creature entry {me.GetEntry()}"); return; } - Log.outDebug(LogFilter.Scripts, $"EscortAI Waypoint {pointId} reached"); + Log.outDebug(LogFilter.Scripts, $"EscortAI Waypoint {currentWp.Id} reached"); - WaypointMovementGenerator move = (WaypointMovementGenerator)me.GetMotionMaster().top(); - if (move != null) - m_uiWPWaitTimer = (uint)move.GetTrackerTimer().GetExpiry(); + //Call WP function + WaypointReached(currentWp.Id); - //Call WP start function - if (m_uiWPWaitTimer == 0 && !HasEscortState(eEscortState.Paused) && move != null) - WaypointStart(move.GetCurrentNode()); + m_uiWPWaitTimer = currentWp.WaitTimeMs + 1; - if (m_bIsRunning) - me.SetWalk(false); - else - me.SetWalk(true); + ++CurrentWPIndex; } } public void AddWaypoint(uint id, float x, float y, float z, uint waitTime = 0) { - GridDefines.NormalizeMapCoord(ref x); - GridDefines.NormalizeMapCoord(ref y); + Escort_Waypoint t = new Escort_Waypoint(id, x, y, z, waitTime); - WaypointNode wp = new WaypointNode(); - wp.id = id; - wp.x = x; - wp.y = y; - wp.z = z; - wp.orientation = 0.0f; - wp.moveType = m_bIsRunning ? WaypointMoveType.Run : WaypointMoveType.Walk; - wp.delay = waitTime; - wp.eventId = 0; - wp.eventChance = 100; - - _path.nodes.Add(wp); - - LastWP = id; + WaypointList.Add(t); ScriptWP = true; } @@ -378,29 +381,10 @@ namespace Game.AI if (movePoints.Empty()) return; - LastWP = movePoints.Last().uiPointId; - foreach (var point in movePoints) { - float x = point.fX; - float y = point.fY; - float z = point.fZ; - - GridDefines.NormalizeMapCoord(ref x); - GridDefines.NormalizeMapCoord(ref y); - - WaypointNode wp = new WaypointNode(); - wp.id = point.uiPointId; - wp.x = x; - wp.y = y; - wp.z = z; - wp.orientation = 0.0f; - wp.moveType = m_bIsRunning ? WaypointMoveType.Run : WaypointMoveType.Walk; - wp.delay = point.uiWaitTime; - wp.eventId = 0; - wp.eventChance = 100; - - _path.nodes.Add(wp); + Escort_Waypoint wayPoint = new Escort_Waypoint(point.uiPointId, point.fX, point.fY, point.fZ, point.uiWaitTime); + WaypointList.Add(wayPoint); } } @@ -439,6 +423,20 @@ namespace Game.AI return; } + if (!ScriptWP && resetWaypoints) + { + if (!WaypointList.Empty()) + WaypointList.Clear(); + FillPointMovementListForCreature(); + } + + + if (WaypointList.Empty()) + { + Log.outError(LogFilter.Scripts, $"EscortAI (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: {(quest != null ? quest.Id : 0)})."); + return; + } + //set variables m_bIsActiveAttacker = isActiveAttacker; m_bIsRunning = run; @@ -449,16 +447,12 @@ namespace Game.AI m_bCanInstantRespawn = instantRespawn; m_bCanReturnToStart = canLoopPath; - if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does - FillPointMovementListForCreature(); - if (m_bCanReturnToStart && m_bCanInstantRespawn) Log.outDebug(LogFilter.Scripts, "EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn."); if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint) { - me.StopMoving(); - me.GetMotionMaster().Clear(false); + me.GetMotionMaster().MovementExpired(); me.GetMotionMaster().MoveIdle(); Log.outDebug(LogFilter.Scripts, "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle."); } @@ -474,14 +468,14 @@ namespace Game.AI Log.outDebug(LogFilter.Scripts, $"EscortAI started. ActiveAttacker = {m_bIsActiveAttacker}, Run = {m_bIsRunning}, PlayerGUID = {m_uiPlayerGUID.ToString()}"); + CurrentWPIndex = 0; + //Set initial speed if (m_bIsRunning) me.SetWalk(false); else me.SetWalk(true); - m_bStarted = false; - AddEscortState(eEscortState.Escorting); } @@ -491,17 +485,75 @@ namespace Game.AI return; if (on) - { AddEscortState(eEscortState.Paused); - me.StopMoving(); - } else - { RemoveEscortState(eEscortState.Paused); - WaypointMovementGenerator move = (WaypointMovementGenerator)me.GetMotionMaster().top(); - if (move != null) - move.GetTrackerTimer().Reset(1); + } + + bool SetNextWaypoint(uint pointId, float x, float y, float z, float orientation) + { + me.UpdatePosition(x, y, z, orientation); + return SetNextWaypoint(pointId, false, true); + } + + bool SetNextWaypoint(uint pointId, bool setPosition, bool resetWaypointsOnFail) + { + if (!WaypointList.Empty()) + WaypointList.Clear(); + + FillPointMovementListForCreature(); + + if (WaypointList.Empty()) + return false; + + int size = WaypointList.Count; + Escort_Waypoint waypoint = new Escort_Waypoint(0, 0, 0, 0, 0); + do + { + waypoint = WaypointList.First(); + WaypointList.RemoveAt(0); + if (waypoint.Id == pointId) + { + if (setPosition) + me.UpdatePosition(waypoint.X, waypoint.Y, waypoint.Z, me.GetOrientation()); + + CurrentWPIndex = 0; + return true; + } } + while (!WaypointList.Empty()); + + // we failed. + // we reset the waypoints in the start; if we pulled any, reset it again + if (resetWaypointsOnFail && size != WaypointList.Count) + { + if (!WaypointList.Empty()) + WaypointList.Clear(); + + FillPointMovementListForCreature(); + } + + return false; + } + + bool GetWaypointPosition(uint pointId, ref float x, ref float y, ref float z) + { + var waypoints = Global.ScriptMgr.GetPointMoveList(me.GetEntry()); + if (waypoints == null) + return false; + + foreach (var pointMove in waypoints) + { + if (pointMove.uiPointId == pointId) + { + x = pointMove.fX; + y = pointMove.fY; + z = pointMove.fZ; + return true; + } + } + + return false; } public virtual void WaypointReached(uint pointId) { } @@ -518,7 +570,6 @@ namespace Game.AI public bool GetAttack() { return m_bIsActiveAttacker; }//used in EnterEvadeMode override public void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; } public ObjectGuid GetEventStarterGUID() { return m_uiPlayerGUID; } - public void SetWaitTimer(uint Timer) { m_uiWPWaitTimer = Timer; } public Player GetPlayerForEscort() { return Global.ObjAccessor.GetPlayer(me, m_uiPlayerGUID); } @@ -530,12 +581,12 @@ namespace Game.AI uint m_uiPlayerCheckTimer; eEscortState m_uiEscortState; float MaxPlayerDistance; - uint LastWP; - - WaypointPath _path = new WaypointPath(); Quest m_pQuestForEscort; //generally passed in Start() when regular escort script. + List WaypointList = new List(); + int CurrentWPIndex; + bool m_bIsActiveAttacker; //obsolete, determined by faction. bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK) bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used) @@ -544,8 +595,6 @@ namespace Game.AI bool DespawnAtFar; bool ScriptWP; bool HasImmuneToNPCFlags; - bool m_bStarted; - bool m_bEnded; } public enum eEscortState @@ -556,6 +605,24 @@ namespace Game.AI Paused = 0x004 //will not proceed with waypoints before state is removed } + class Escort_Waypoint + { + public Escort_Waypoint(uint id, float x, float y, float z, uint w) + { + Id = id; + X = x; + Y = y; + Z = z; + WaitTimeMs = w; + } + + public uint Id; + public float X; + public float Y; + public float Z; + public uint WaitTimeMs; + } + struct EscortPointIds { public const uint LastPoint = 0xFFFFFF; diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index 51b491f24..4278e2c22 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -42,19 +42,19 @@ namespace Game.AI mCurrentWPID = 0;//first wp id is 1 !! mWPReached = false; mWPPauseTimer = 0; - mOOCReached = false; - mEscortNPCFlags = 0; - mCanRepeatPath = false; // Spawn in run mode + me.SetWalk(false); mRun = true; - m_Ended = false; + + mLastOOCPos = me.GetPosition(); mCanAutoAttack = true; mCanCombatMove = true; mForcedPaused = false; + mLastWPIDReached = 0; mEscortQuestID = 0; mDespawnTime = 0; @@ -102,6 +102,25 @@ namespace Game.AI GetScript().OnReset(); } + WayPoint GetNextWayPoint() + { + if (mWayPoints.Empty()) + return null; + + mCurrentWPID++; + var wayPoint = mWayPoints.Find(p => p.Id == mCurrentWPID); + if (wayPoint != null) + { + mLastWP = wayPoint; + if (mLastWP.Id != mCurrentWPID) + { + Log.outError(LogFilter.Misc, "SmartAI.GetNextWayPoint: Got not expected waypoint id {mLastWP.id}, expected {mCurrentWPID}"); + } + return wayPoint; + } + return null; + } + public void StartPath(bool run = false, uint path = 0, bool repeat = false, Unit invoker = null) { if (me.IsInCombat())// no wp movement in combat @@ -113,31 +132,25 @@ namespace Game.AI if (HasEscortState(SmartEscortState.Escorting)) StopPath(); - SetRun(run); - if (path != 0) if (!LoadPath(path)) return; - if (_path.nodes.Empty()) + if (mWayPoints.Empty()) return; - mCurrentWPID = 1; - m_Ended = false; - - // Do not use AddEscortState, removing everything from previous cycle - mEscortState = SmartEscortState.Escorting; + AddEscortState(SmartEscortState.Escorting); mCanRepeatPath = repeat; - if (invoker && invoker.GetTypeId() == TypeId.Player) + SetRun(run); + + WayPoint wp = GetNextWayPoint(); + if (wp != null) { - mEscortNPCFlags = me.m_unitData.NpcFlags[0]; - me.SetNpcFlags(NPCFlags.None); + mLastOOCPos = me.GetPosition(); + me.GetMotionMaster().MovePoint(wp.Id, wp.X, wp.Y, wp.Z); + GetScript().ProcessEventsFor(SmartEvents.WaypointStart, null, wp.Id, GetScript().GetPathId()); } - - GetScript().ProcessEventsFor(SmartEvents.WaypointStart, null, mCurrentWPID, GetScript().GetPathId()); - - me.GetMotionMaster().MovePath(_path, mCanRepeatPath); } bool LoadPath(uint entry) @@ -145,36 +158,13 @@ namespace Game.AI if (HasEscortState(SmartEscortState.Escorting)) return false; - var path = Global.SmartAIMgr.GetPath(entry); - if (path.Empty()) + mWayPoints = Global.SmartAIMgr.GetPath(entry); + if (mWayPoints == null) { GetScript().SetPathId(0); return false; } - foreach (WayPoint waypoint in path) - { - float x = waypoint.x; - float y = waypoint.y; - float z = waypoint.z; - - GridDefines.NormalizeMapCoord(ref x); - GridDefines.NormalizeMapCoord(ref y); - - WaypointNode wp = new WaypointNode(); - wp.id = waypoint.id; - wp.x = x; - wp.y = y; - wp.z = z; - wp.orientation = 0.0f; - wp.moveType = mRun ? WaypointMoveType.Run : WaypointMoveType.Walk; - wp.delay = 0; - wp.eventId = 0; - wp.eventChance = 100; - - _path.nodes.Add(wp); - } - GetScript().SetPathId(entry); return true; } @@ -186,19 +176,21 @@ namespace Game.AI if (HasEscortState(SmartEscortState.Paused)) { - Log.outError(LogFilter.Server, $"SmartAI.PausePath: Creature entry {me.GetEntry()} wanted to pause waypoint (current waypoint: {mCurrentWPID}) movement while already paused, ignoring."); + Log.outError(LogFilter.Server, $"SmartAI.PausePath: Creature entry {me.GetEntry()} wanted to pause waypoint movement while already paused, ignoring."); return; } + mForcedPaused = forced; + mLastOOCPos = me.GetPosition(); AddEscortState(SmartEscortState.Paused); mWPPauseTimer = delay; - if (forced && !mWPReached) + if (forced) { - mForcedPaused = forced; SetRun(mRun); - me.StopMoving(); + me.StopMoving();//force stop + me.GetMotionMaster().MoveIdle();//force stop } - GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, mCurrentWPID, GetScript().GetPathId()); + GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, mLastWP.Id, GetScript().GetPathId()); } public void StopPath(uint DespawnTime = 0, uint quest = 0, bool fail = false) @@ -209,27 +201,33 @@ namespace Game.AI if (quest != 0) mEscortQuestID = quest; - if (mDespawnState != 2) - SetDespawnTime(DespawnTime); + SetDespawnTime(DespawnTime); + //mDespawnTime = DespawnTime; - me.StopMoving(); - me.GetMotionMaster().MovementExpired(false); + mLastOOCPos = me.GetPosition(); + me.StopMoving();//force stop me.GetMotionMaster().MoveIdle(); - GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, mCurrentWPID, GetScript().GetPathId()); + GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, mLastWP.Id, GetScript().GetPathId()); EndPath(fail); } public void EndPath(bool fail = false) { - RemoveEscortState(SmartEscortState.Escorting | SmartEscortState.Paused | SmartEscortState.Returning); - _path.nodes.Clear(); - mWPPauseTimer = 0; + GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, mLastWP.Id, GetScript().GetPathId()); - if (mEscortNPCFlags != 0) + RemoveEscortState(SmartEscortState.Escorting | SmartEscortState.Paused | SmartEscortState.Returning); + mWayPoints = null; + mCurrentWPID = 0; + mWPPauseTimer = 0; + mLastWP = null; + + if (mCanRepeatPath) { - me.SetNpcFlags((NPCFlags)mEscortNPCFlags); - mEscortNPCFlags = 0; + if (IsAIControlled()) + StartPath(mRun, GetScript().GetPathId(), true); } + else + GetScript().SetPathId(0); List targets = GetScript().GetTargetList(SharedConst.SmartEscortTargets); if (targets != null && mEscortQuestID != 0) @@ -275,36 +273,16 @@ namespace Game.AI } } - // End Path events should be only processed if it was SUCCESSFUL stop or stop called by SMART_ACTION_WAYPOINT_STOP - if (fail) - return; - - GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, mCurrentWPID, GetScript().GetPathId()); - - if (mCanRepeatPath) - { - if (IsAIControlled()) - StartPath(mRun, GetScript().GetPathId(), mCanRepeatPath); - } - else - GetScript().SetPathId(0); - if (mDespawnState == 1) StartDespawn(); } public void ResumePath() { - GetScript().ProcessEventsFor(SmartEvents.WaypointResumed, null, mCurrentWPID, GetScript().GetPathId()); - RemoveEscortState(SmartEscortState.Paused); - mForcedPaused = false; - mWPReached = false; - mWPPauseTimer = 0; SetRun(mRun); - WaypointMovementGenerator move = (WaypointMovementGenerator)me.GetMotionMaster().top(); - if (move != null) - move.GetTrackerTimer().Reset(1); + if (mLastWP != null) + me.GetMotionMaster().MovePoint(mLastWP.Id, mLastWP.X, mLastWP.Y, mLastWP.Z); } void ReturnToLastOOCPos() @@ -312,10 +290,8 @@ namespace Game.AI if (!IsAIControlled()) return; - me.SetWalk(false); - float x, y, z, o; - me.GetHomePosition(out x, out y, out z, out o); - me.GetMotionMaster().MovePoint(EventId.SmartEscortLastOCCPoint, x, y, z); + SetRun(mRun); + me.GetMotionMaster().MovePoint(EventId.SmartEscortLastOCCPoint, mLastOOCPos); } void UpdatePath(uint diff) @@ -327,12 +303,7 @@ namespace Game.AI { if (!IsEscortInvokerInRange()) { - StopPath(0, mEscortQuestID, true); - - // allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying - GetScript().ProcessEventsFor(SmartEvents.Death, me); - me.DespawnOrUnsummon(1); - return; + StopPath(mDespawnTime, mEscortQuestID, true); } mEscortInvokerCheckTimer = 1000; } @@ -342,29 +313,56 @@ namespace Game.AI // handle pause if (HasEscortState(SmartEscortState.Paused)) { - if (mWPPauseTimer <= diff) + if (mWPPauseTimer < diff) { - if (!me.IsInCombat() && !HasEscortState(SmartEscortState.Returning) && (mWPReached || mForcedPaused)) - ResumePath(); + if (!me.IsInCombat() && !HasEscortState(SmartEscortState.Returning) && (mWPReached || mLastWPIDReached == EventId.SmartEscortLastOCCPoint || mForcedPaused)) + { + GetScript().ProcessEventsFor(SmartEvents.WaypointResumed, null, mLastWP.Id, GetScript().GetPathId()); + RemoveEscortState(SmartEscortState.Paused); + if (mForcedPaused)// if paused between 2 wps resend movement + { + ResumePath(); + mWPReached = false; + mForcedPaused = false; + } + if (mLastWPIDReached == EventId.SmartEscortLastOCCPoint) + mWPReached = true; + } + mWPPauseTimer = 0; } else mWPPauseTimer -= diff; } - else if (m_Ended) // end path - { - m_Ended = false; - StopPath(); - return; - } if (HasEscortState(SmartEscortState.Returning)) { - if (mOOCReached)//reached OOC WP + if (mWPReached)//reached OOC WP { - mOOCReached = false; RemoveEscortState(SmartEscortState.Returning); if (!HasEscortState(SmartEscortState.Paused)) ResumePath(); + mWPReached = false; + } + } + + if ((!me.HasReactState(ReactStates.Passive) && me.IsInCombat()) || HasEscortState(SmartEscortState.Paused | SmartEscortState.Returning)) + return; + // handle next wp + if (mWPReached)//reached WP + { + mWPReached = false; + if (mCurrentWPID == GetWPCount()) + { + EndPath(); + } + else + { + WayPoint wp = GetNextWayPoint(); + if (wp != null) + { + SetRun(mRun); + me.GetMotionMaster().MovePoint(wp.Id, wp.X, wp.Y, wp.Z); + } } } } @@ -426,44 +424,23 @@ namespace Game.AI void MovepointReached(uint id) { - // override the id, path can be resumed any time and counter will reset - // mCurrentWPID holds proper id - - // both point movement and escort generator can enter this function - if (id == EventId.SmartEscortLastOCCPoint) - { - mOOCReached = true; - return; - } - - mCurrentWPID = id + 1; // in SmartAI increase by 1 + if (id != EventId.SmartEscortLastOCCPoint && mLastWPIDReached != id) + GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, id); + mLastWPIDReached = id; mWPReached = true; - GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, mCurrentWPID, GetScript().GetPathId()); - - if (HasEscortState(SmartEscortState.Paused)) - me.StopMoving(); - else if (HasEscortState(SmartEscortState.Escorting) && me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint) - { - mWPReached = false; - if (mCurrentWPID == _path.nodes.Count) - m_Ended = true; - else - SetRun(mRun); - } } public override void MovementInform(MovementGeneratorType MovementType, uint Data) { - if (MovementType == MovementGeneratorType.Point && Data == EventId.SmartEscortLastOCCPoint) + if ((MovementType == MovementGeneratorType.Point && Data == EventId.SmartEscortLastOCCPoint) || MovementType == MovementGeneratorType.Follow) me.ClearUnitState(UnitState.Evade); GetScript().ProcessEventsFor(SmartEvents.Movementinform, null, (uint)MovementType, Data); - if (!HasEscortState(SmartEscortState.Escorting)) + if (MovementType != MovementGeneratorType.Point || !HasEscortState(SmartEscortState.Escorting)) return; - if (MovementType == MovementGeneratorType.Waypoint || (MovementType == MovementGeneratorType.Point && Data == EventId.SmartEscortLastOCCPoint)) - MovepointReached(Data); + MovepointReached(Data); } void RemoveAuras() @@ -509,7 +486,6 @@ namespace Game.AI me.GetMotionMaster().MoveFollow(target, mFollowDist, mFollowAngle); // evade is not cleared in MoveFollow, so we can't keep it me.ClearUnitState(UnitState.Evade); - GetScript().OnReset(); } else if (owner) { @@ -520,8 +496,8 @@ namespace Game.AI me.GetMotionMaster().MoveTargetedHome(); } - if (!me.HasUnitState(UnitState.Evade)) - GetScript().OnReset(); + if (!HasEscortState(SmartEscortState.Escorting)) //dont mess up escort movement after combat + SetRun(mRun); } public override void MoveInLineOfSight(Unit who) @@ -625,13 +601,25 @@ namespace Game.AI me.InterruptNonMeleeSpells(false); // must be before ProcessEvents GetScript().ProcessEventsFor(SmartEvents.Aggro, victim); + + if (!IsAIControlled()) + return; + + mLastOOCPos = me.GetPosition(); + SetRun(mRun); + if (me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active) == MovementGeneratorType.Point) + me.GetMotionMaster().MovementExpired(); } public override void JustDied(Unit killer) { GetScript().ProcessEventsFor(SmartEvents.Death, killer); if (HasEscortState(SmartEscortState.Escorting)) + { EndPath(true); + me.StopMoving();//force stop + me.GetMotionMaster().MoveIdle(); + } } public override void KilledUnit(Unit victim) @@ -646,26 +634,10 @@ namespace Game.AI public override void AttackStart(Unit who) { - // dont allow charmed npcs to act on their own - if (me.HasUnitFlag(UnitFlags.PlayerControlled)) - { - if (who && mCanAutoAttack) - me.Attack(who, true); - return; - } - if (who && me.Attack(who, me.IsWithinMeleeRange(who))) { if (mCanCombatMove) - { - SetRun(mRun); - - MovementGeneratorType type = me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active); - if (type == MovementGeneratorType.Waypoint || type == MovementGeneratorType.Point) - me.StopMoving(); - me.GetMotionMaster().MoveChase(who); - } } } @@ -852,9 +824,12 @@ namespace Game.AI } else { + if (me.HasUnitState(UnitState.ConfusedMove | UnitState.FleeingMove)) + return; + + me.GetMotionMaster().MovementExpired(); + me.GetMotionMaster().Clear(true); me.StopMoving(); - if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Chase) - me.GetMotionMaster().Clear(false); me.GetMotionMaster().MoveIdle(); } } @@ -951,6 +926,8 @@ namespace Game.AI public void StartDespawn() { mDespawnState = 2; } + uint GetWPCount() { return (uint)mWayPoints?.Count; } + bool mIsCharmed; uint mFollowCreditType; uint mFollowArrivedTimer; @@ -961,13 +938,14 @@ namespace Game.AI float mFollowAngle; SmartScript mScript = new SmartScript(); + List mWayPoints; SmartEscortState mEscortState; uint mCurrentWPID; + uint mLastWPIDReached; bool mWPReached; - bool mOOCReached; - bool m_Ended; uint mWPPauseTimer; - uint mEscortNPCFlags; + WayPoint mLastWP; + Position mLastOOCPos;//set on enter combat bool mCanRepeatPath; bool mRun; bool mEvadeDisabled; @@ -976,7 +954,6 @@ namespace Game.AI bool mForcedPaused; uint mInvincibilityHpLevel; - WaypointPath _path = new WaypointPath(); uint mDespawnTime; uint mRespawnTime; uint mDespawnState; diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index 6d5e528b6..70766e634 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -248,13 +248,7 @@ namespace Game.AI last_id++; - WayPoint point = new WayPoint(); - point.id = id; - point.x = x; - point.y = y; - point.z = z; - - waypoint_map.Add(entry, point); + waypoint_map.Add(entry, new WayPoint(id, x, y, z)); last_entry = entry; total++; @@ -1163,6 +1157,7 @@ namespace Game.AI break; } + case SmartActions.StartClosestWaypoint: case SmartActions.Follow: case SmartActions.SetOrientation: case SmartActions.StoreTargetList: @@ -1544,10 +1539,18 @@ namespace Game.AI public class WayPoint { - public uint id; - public float x; - public float y; - public float z; + public WayPoint(uint id, float x, float y, float z) + { + Id = id; + X = x; + Y = y; + Z = z; + } + + public uint Id; + public float X; + public float Y; + public float Z; } public class SmartScriptHolder diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index bb3eaac28..bf48c4d68 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -2293,6 +2293,58 @@ namespace Game.AI Global.GameEventMgr.StartEvent(eventId, true); break; } + case SmartActions.StartClosestWaypoint: + { + uint[] waypoints = new uint[SharedConst.SmartActionParamCount]; + waypoints[0] = e.Action.closestWaypointFromList.wp1; + waypoints[1] = e.Action.closestWaypointFromList.wp2; + waypoints[2] = e.Action.closestWaypointFromList.wp3; + waypoints[3] = e.Action.closestWaypointFromList.wp4; + waypoints[4] = e.Action.closestWaypointFromList.wp5; + waypoints[5] = e.Action.closestWaypointFromList.wp6; + float distanceToClosest = float.MaxValue; + WayPoint closestWp = null; + + var targets = GetTargets(e, unit); + if (targets != null) + { + foreach (var obj in targets) + { + Creature target = obj.ToCreature(); + if (target != null) + { + if (IsSmart(target)) + { + for (byte i = 0; i < SharedConst.SmartActionParamCount; i++) + { + if (waypoints[i] == 0) + continue; + + var path = Global.SmartAIMgr.GetPath(waypoints[i]); + if (path == null || path.Empty()) + continue; + + WayPoint wp = path[0]; + if (wp != null) + { + float distToThisPath = target.GetDistance(wp.X, wp.Y, wp.Z); + + if (distToThisPath < distanceToClosest) + { + distanceToClosest = distToThisPath; + closestWp = wp; + } + } + } + + if (closestWp != null) + ((SmartAI)target.GetAI()).StartPath(false, closestWp.Id, true); + } + } + } + } + break; + } case SmartActions.RandomSound: { uint[] sounds = new uint[SharedConst.SmartActionParamCount - 1]; diff --git a/Source/Game/Movement/Generators/WaypointMovement.cs b/Source/Game/Movement/Generators/WaypointMovement.cs index ffab45379..561f2b4d3 100644 --- a/Source/Game/Movement/Generators/WaypointMovement.cs +++ b/Source/Game/Movement/Generators/WaypointMovement.cs @@ -37,23 +37,12 @@ namespace Game.Movement isArrivalDone = false; pathId = pathid; repeating = _repeating; - loadedFromDB = true; - } - - public WaypointMovementGenerator(WaypointPath _path, bool _repeating = true) - { - nextMoveTime = new TimeTrackerSmall(0); - isArrivalDone = false; - pathId = 0; - repeating = _repeating; - loadedFromDB = false; - path = _path; } public override void DoReset(Creature creature) { - if (!Stopped()) - StartMoveNow(creature); + creature.AddUnitState(UnitState.Roaming | UnitState.RoamingMove); + StartMoveNow(creature); } public override void DoFinalize(Creature creature) @@ -65,13 +54,11 @@ namespace Game.Movement public override void DoInitialize(Creature creature) { LoadPath(creature); + creature.AddUnitState(UnitState.Roaming | UnitState.RoamingMove); } public override bool DoUpdate(Creature creature, uint time_diff) { - if (!creature || !creature.IsAlive()) - return false; - // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff if (creature.HasUnitState(UnitState.NotMove)) @@ -81,13 +68,13 @@ namespace Game.Movement } // prevent a crash at empty waypoint path. - if (path == null || path.nodes.Empty()) + if (path == null || path.Empty()) return false; if (Stopped()) { if (CanMove((int)time_diff)) - return StartMoveNow(creature); + return StartMove(creature); } else { @@ -96,41 +83,11 @@ namespace Game.Movement creature.SetHomePosition(creature.GetPosition()); if (creature.IsStopped()) - Stop(loadedFromDB ? WorldConfig.GetIntValue(WorldCfg.CreatureStopForPlayer) : 2 * Time.Hour * Time.InMilliseconds); + Stop(WorldConfig.GetIntValue(WorldCfg.CreatureStopForPlayer)); else if (creature.moveSpline.Finalized()) { OnArrived(creature); - - isArrivalDone = true; - - if (!Stopped()) - { - if (creature.IsStopped()) - Stop(loadedFromDB ? WorldConfig.GetIntValue(WorldCfg.CreatureStopForPlayer) : 2 * Time.Hour * Time.InMilliseconds); - else - return StartMove(creature); - } - } - else - { - // speed changed during path execution, calculate remaining path and launch it once more - if (recalculateSpeed) - { - recalculateSpeed = false; - - if (!Stopped()) - return StartMove(creature); - } - else - { - uint pointId = (uint)creature.moveSpline.currentPathIdx(); - if (pointId > currentNode) - { - OnArrived(creature); - currentNode = pointId; - FormationMove(creature); - } - } + return StartMove(creature); } } @@ -143,6 +100,20 @@ namespace Game.Movement creature.GetAI().MovementInform(MovementGeneratorType.Waypoint, currentNode); } + public override bool GetResetPosition(Unit u, out float x, out float y, out float z) + { + x = y = z = 0; + // prevent a crash at empty waypoint path. + if (path == null || path.Empty()) + return false; + + var node = path.LookupByIndex((int)currentNode); + x = node.x; + y = node.y; + z = node.z; + return true; + } + void Stop(int time) { nextMoveTime.Reset(time); @@ -159,18 +130,15 @@ namespace Game.Movement return nextMoveTime.Passed(); } - bool StartMoveNow(Creature creature) + void StartMoveNow(Creature creature) { nextMoveTime.Reset(0); - return StartMove(creature); + StartMove(creature); } bool StartMove(Creature creature) { - if (!creature || !creature.IsAlive()) - return false; - - if (path == null || path.nodes.Empty()) + if (path == null || path.Empty()) return false; if (Stopped()) @@ -180,9 +148,9 @@ namespace Game.Movement if (isArrivalDone) { - if ((currentNode == path.nodes.Count - 1) && !repeating) // If that's our last waypoint + if ((currentNode == path.Count - 1) && !repeating) // If that's our last waypoint { - WaypointNode waypoint = path.nodes.LookupByIndex((int)currentNode); + WaypointData waypoint = path.LookupByIndex((int)currentNode); float x = waypoint.x; float y = waypoint.y; @@ -206,43 +174,21 @@ namespace Game.Movement // else if (vehicle) - this should never happen, vehicle offsets are const } + creature.GetMotionMaster().Initialize(); return false; } - currentNode = (uint)((currentNode + 1) % path.nodes.Count); + currentNode = (uint)((currentNode + 1) % path.Count); } - float finalOrient = 0.0f; - WaypointMoveType finalMove = WaypointMoveType.Walk; - - List pathing = new List(); - - pathing.Add(new Vector3(creature.GetPositionX(), creature.GetPositionY(), creature.GetPositionZ())); - for (int i = (int)currentNode; i < path.nodes.Count; ++i) - { - WaypointNode waypoint = path.nodes.LookupByIndex(i); - - pathing.Add(new Vector3(waypoint.x, waypoint.y, waypoint.z)); - - finalOrient = waypoint.orientation; - finalMove = waypoint.moveType; - - if (waypoint.delay != 0) - break; - } - - // if we have only 1 point, only current position, we shall return - if (pathing.Count < 2) - return false; + var node = path.LookupByIndex((int)currentNode); isArrivalDone = false; - recalculateSpeed = false; creature.AddUnitState(UnitState.RoamingMove); - MoveSplineInit init = new MoveSplineInit(creature); - var node = path.nodes.LookupByIndex((int)currentNode); Position formationDest = new Position(node.x, node.y, node.z, 0.0f); + MoveSplineInit init = new MoveSplineInit(creature); //! If creature is on transport, we assume waypoints set in DB are already transport offsets if (transportPath) @@ -253,8 +199,15 @@ namespace Game.Movement trans.CalculatePassengerPosition(ref formationDest.posX, ref formationDest.posY, ref formationDest.posZ, ref formationDest.Orientation); } - init.MovebyPath(pathing.ToArray(), (int)currentNode); - switch (finalMove) + //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call + //! but formationDest contains global coordinates + init.MoveTo(node.x, node.y, node.z); + + //! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table + if (node.orientation != 0 && node.delay != 0) + init.SetFacing(node.orientation); + + switch (node.moveType) { case WaypointMoveType.Land: init.SetAnimation(AnimType.ToGround); @@ -270,27 +223,24 @@ namespace Game.Movement break; } - if (finalOrient != 0.0f) - init.SetFacing(finalOrient); - init.Launch(); //Call for creature group update if (creature.GetFormation() != null && creature.GetFormation().getLeader() == creature) + { + creature.SetWalk(node.moveType != WaypointMoveType.Run); creature.GetFormation().LeaderMoveTo(formationDest.posX, formationDest.posY, formationDest.posZ); + } return true; } void LoadPath(Creature creature) { - if (loadedFromDB) - { - if (pathId == 0) - pathId = creature.GetWaypointPath(); + if (pathId == 0) + pathId = creature.GetWaypointPath(); - path = Global.WaypointMgr.GetPath(pathId); - } + path = Global.WaypointMgr.GetPath(pathId); if (path == null) { @@ -299,58 +249,37 @@ namespace Game.Movement return; } - if (!Stopped()) - StartMoveNow(creature); + StartMoveNow(creature); } void OnArrived(Creature creature) { - if (path == null || path.nodes.Empty()) + if (path == null || path.Empty()) return; - WaypointNode waypoint = path.nodes.LookupByIndex((int)currentNode); - if (waypoint.delay != 0) - { - creature.ClearUnitState(UnitState.RoamingMove); - Stop((int)waypoint.delay); - } + if (isArrivalDone) + return; - if (waypoint.eventId != 0 && RandomHelper.URand(0, 99) < waypoint.eventChance) + isArrivalDone = true; + + var current = path.LookupByIndex((int)currentNode); + + if (current.eventId != 0 && RandomHelper.URand(0, 99) < current.eventChance) { - Log.outDebug(LogFilter.Unit, "Creature movement start script {0} at point {1} for {2}.", waypoint.eventId, currentNode, creature.GetGUID()); + Log.outDebug(LogFilter.Unit, "Creature movement start script {0} at point {1} for {2}.", current.eventId, currentNode, creature.GetGUID()); creature.ClearUnitState(UnitState.RoamingMove); - creature.GetMap().ScriptsStart(ScriptsType.Waypoint, waypoint.eventId, creature, null); + creature.GetMap().ScriptsStart(ScriptsType.Waypoint, current.eventId, creature, null); } // Inform script MovementInform(creature); creature.UpdateWaypointID(currentNode); - creature.SetWalk(waypoint.moveType != WaypointMoveType.Run); - } - - void FormationMove(Creature creature) - { - if (path == null || path.nodes.Empty()) - return; - - bool transportPath = creature.GetTransport() != null; - - WaypointNode waypoint = path.nodes.LookupByIndex((int)currentNode); - - Position formationDest = new Position(waypoint.x, waypoint.y, waypoint.z, 0.0f); - - //! If creature is on transport, we assume waypoints set in DB are already transport offsets - if (transportPath) + if (current.delay != 0) { - ITransport trans = creature.GetDirectTransport(); - if (trans != null) - trans.CalculatePassengerPosition(ref formationDest.posX, ref formationDest.posY, ref formationDest.posZ, ref formationDest.Orientation); + creature.ClearUnitState(UnitState.RoamingMove); + Stop((int)current.delay); } - - // Call for creature group update - if (creature.GetFormation() != null && creature.GetFormation().getLeader() == creature) - creature.GetFormation().LeaderMoveTo(formationDest.posX, formationDest.posY, formationDest.posZ); } public override MovementGeneratorType GetMovementGeneratorType() @@ -358,20 +287,14 @@ namespace Game.Movement return MovementGeneratorType.Waypoint; } - public TimeTrackerSmall GetTrackerTimer() { return nextMoveTime; } - - public void UnitSpeedChanged() { recalculateSpeed = true; } - public uint GetCurrentNode() { return currentNode; } TimeTrackerSmall nextMoveTime; - bool recalculateSpeed; bool isArrivalDone; uint pathId; bool repeating; - bool loadedFromDB; - WaypointPath path; + List path; uint currentNode; } @@ -458,12 +381,12 @@ namespace Game.Movement MoveSplineInit init = new MoveSplineInit(owner); uint end = GetPathAtMapEnd(); init.args.path = new Vector3[end]; - for (int i = i_currentNode; i != end; ++i) + for (int i = (int)GetCurrentNode(); i != end; ++i) { Vector3 vertice = new Vector3(i_path[i].Loc.X, i_path[i].Loc.Y, i_path[i].Loc.Z); init.args.path[i] = vertice; } - init.SetFirstPointId(i_currentNode); + init.SetFirstPointId((int)GetCurrentNode()); init.SetFly(); init.SetSmooth(); init.SetUncompressed(); diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index 08f172cba..65448f66e 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -598,11 +598,6 @@ namespace Game.Movement StartMovement(new WaypointMovementGenerator(path_id, repeatable), MovementSlot.Idle); } - public void MovePath(WaypointPath path, bool repeatable) - { - StartMovement(new WaypointMovementGenerator(path, repeatable), MovementSlot.Idle); - } - void MoveRotate(uint time, RotateDirection direction) { if (time == 0) diff --git a/Source/Game/Movement/WaypointManager.cs b/Source/Game/Movement/WaypointManager.cs index c3509382a..dd5182386 100644 --- a/Source/Game/Movement/WaypointManager.cs +++ b/Source/Game/Movement/WaypointManager.cs @@ -25,7 +25,7 @@ namespace Game { WaypointManager() { - _waypointStore = new Dictionary(); + _waypointStore = new MultiMap(); } public void Load() @@ -55,7 +55,7 @@ namespace Game GridDefines.NormalizeMapCoord(ref x); GridDefines.NormalizeMapCoord(ref y); - WaypointNode wp = new WaypointNode(); + WaypointData wp = new WaypointData(); wp.id = result.Read(1); wp.x = x; wp.y = y; @@ -73,10 +73,7 @@ namespace Game wp.eventId = result.Read(8); wp.eventChance = result.Read(9); - if (!_waypointStore.ContainsKey(pathId)) - _waypointStore[pathId] = new WaypointPath(); - - _waypointStore[pathId].nodes.Add(wp); + _waypointStore.Add(pathId, wp); ++count; } while (result.NextRow()); @@ -104,7 +101,7 @@ namespace Game GridDefines.NormalizeMapCoord(ref x); GridDefines.NormalizeMapCoord(ref y); - WaypointNode wp = new WaypointNode(); + WaypointData wp = new WaypointData(); wp.id = result.Read(0); wp.x = x; wp.y = y; @@ -122,27 +119,24 @@ namespace Game wp.eventId = result.Read(7); wp.eventChance = result.Read(8); - if (!_waypointStore.ContainsKey(id)) - _waypointStore[id] = new WaypointPath(); - - _waypointStore[id].nodes.Add(wp); + _waypointStore.Add(id, wp); } while (result.NextRow()); } - public WaypointPath GetPath(uint id) + public List GetPath(uint id) { return _waypointStore.LookupByKey(id); } - Dictionary _waypointStore; + MultiMap _waypointStore; } - public class WaypointNode + public class WaypointData { - public WaypointNode() { moveType = WaypointMoveType.Run; } - public WaypointNode(uint _id, float _x, float _y, float _z, float _orientation = 0.0f, uint _delay = 0) + public WaypointData() { moveType = WaypointMoveType.Run; } + public WaypointData(uint _id, float _x, float _y, float _z, float _orientation = 0.0f, uint _delay = 0) { id = _id; x = _x; @@ -163,22 +157,6 @@ namespace Game public byte eventChance; } - public class WaypointPath - { - public WaypointPath() - { - nodes = new List(); - } - public WaypointPath(uint _id, List _nodes) - { - id = _id; - nodes = _nodes; - } - - public List nodes; - public uint id; - } - public enum WaypointMoveType { Walk,