diff --git a/Source/Framework/Constants/SmartAIConst.cs b/Source/Framework/Constants/SmartAIConst.cs index d39190ef2..ac6b8b9ba 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 - StartClosestWaypoint = 113, // wp1, wp2, wp3, wp4, wp5, wp6, wp7 + // Not used MoveOffset = 114, RandomSound = 115, // SoundId1, SoundId2, SoundId3, SoundId4, SoundId5, onlySelf SetCorpseDelay = 116, // timer diff --git a/Source/Game/AI/AISelector.cs b/Source/Game/AI/AISelector.cs index 24a8fdfae..443f24d86 100644 --- a/Source/Game/AI/AISelector.cs +++ b/Source/Game/AI/AISelector.cs @@ -102,9 +102,9 @@ namespace Game.AI switch (creature.m_defaultMovementType) { case MovementGeneratorType.Random: - return new RandomMovementGenerator(); + return new RandomMovementGenerator(); case MovementGeneratorType.Waypoint: - return new WaypointMovementGenerator(); + return new WaypointMovementGenerator(); } return null; } diff --git a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs index 1b2feba14..30b8161d1 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs @@ -21,6 +21,8 @@ using Game.Groups; using System; using System.Collections.Generic; using System.Linq; +using Game.Movement; +using Game.Maps; namespace Game.AI { @@ -29,10 +31,11 @@ namespace Game.AI public npc_escortAI(Creature creature) : base(creature) { m_uiPlayerGUID = ObjectGuid.Empty; - m_uiWPWaitTimer = 2500; - m_uiPlayerCheckTimer = 1000; + m_uiWPWaitTimer = 1000; + m_uiPlayerCheckTimer = 0; m_uiEscortState = eEscortState.None; MaxPlayerDistance = 50; + LastWP = 0; m_pQuestForEscort = null; m_bIsActiveAttacker = true; m_bIsRunning = false; @@ -42,21 +45,8 @@ namespace Game.AI DespawnAtFar = true; ScriptWP = false; HasImmuneToNPCFlags = false; - } - - public override void AttackStart(Unit who) - { - if (!who) - return; - - if (me.Attack(who, true)) - { - if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Point) - me.GetMotionMaster().MovementExpired(); - - if (IsCombatMovementAllowed()) - me.GetMotionMaster().MoveChase(who); - } + m_bStarted = false; + m_bEnded = false; } //see followerAI @@ -99,38 +89,15 @@ 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.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); - } - } - } - } + if (me.CanStartAttack(who, false)) + AttackStart(who); } public override void JustDied(Unit killer) @@ -158,13 +125,13 @@ namespace Game.AI public override void JustRespawned() { - m_uiEscortState = eEscortState.None; + RemoveEscortState(eEscortState.Escorting | eEscortState.Returning | eEscortState.Paused); if (!IsCombatMovementAllowed()) SetCombatMovement(true); //add a small delay before going to first waypoint, normal in near all cases - m_uiWPWaitTimer = 2500; + m_uiWPWaitTimer = 1000; if (me.getFaction() != me.GetCreatureTemplate().Faction) me.RestoreFaction(); @@ -174,9 +141,8 @@ namespace Game.AI void ReturnToLastPoint() { - float x, y, z, o; - me.GetHomePosition(out x, out y, out z, out o); - me.GetMotionMaster().MovePoint(0xFFFFFF, x, y, z); + me.SetWalk(false); + me.GetMotionMaster().MovePoint(0xFFFFFF, me.GetHomePosition()); } public override void EnterEvadeMode(EvadeReason why = EvadeReason.Other) @@ -226,57 +192,62 @@ 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) { - //End of the line - if (WPIndex == WaypointList.Count) + if (!HasEscortState(eEscortState.Paused)) { - if (DespawnAtEnd) + m_uiWPWaitTimer = 0; + + if (m_bEnded) { - Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints"); + me.StopMoving(); + me.GetMotionMaster().Clear(false); + me.GetMotionMaster().MoveIdle(); - if (m_bCanReturnToStart) + m_bEnded = false; + + if (DespawnAtEnd) { - float fRetX, fRetY, fRetZ; - me.GetRespawnPosition(out fRetX, out fRetY, out fRetZ); + Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints"); - me.GetMotionMaster().MovePoint(0xFFFFFE, fRetX, fRetY, fRetZ); + if (m_bCanReturnToStart) + { + float fRetX, fRetY, fRetZ; + me.GetRespawnPosition(out fRetX, out fRetY, out fRetZ); - m_uiWPWaitTimer = 0; + me.GetMotionMaster().MovePoint(EscortPointIds.Home, fRetX, fRetY, fRetZ); - Log.outDebug(LogFilter.Scripts, "EscortAI are returning home to spawn location: {0}, {1}, {2}, {3}", 0xFFFFFE, fRetX, fRetY, fRetZ); - return; - } - - if (m_bCanInstantRespawn) - { - me.setDeathState(DeathState.JustDied); - me.Respawn(); + 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(); } else - me.DespawnOrUnsummon(); + Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints with Despawn off"); + RemoveEscortState(eEscortState.Escorting); return; } + + if (!m_bStarted) + { + m_bStarted = true; + me.GetMotionMaster().MovePath(_path, false); + } else { - Log.outDebug(LogFilter.Scripts, "EscortAI reached end of waypoints with Despawn off"); - return; + WaypointMovementGenerator move = (WaypointMovementGenerator)me.GetMotionMaster().top(); + if (move != null) + WaypointStart(move.GetCurrentNode()); } } - - if (!HasEscortState(eEscortState.Paused)) - { - me.GetMotionMaster().MovePoint(GetCurrentWaypoint().id, GetCurrentWaypoint().x, GetCurrentWaypoint().y, GetCurrentWaypoint().z); - Log.outDebug(LogFilter.Scripts, "EscortAI start waypoint {0} ({1}, {2}, {3}).", GetCurrentWaypoint().id, GetCurrentWaypoint().x, GetCurrentWaypoint().y, GetCurrentWaypoint().z); - - WaypointStart(GetCurrentWaypoint().id); - - m_uiWPWaitTimer = 0; - } } else m_uiWPWaitTimer -= diff; @@ -285,12 +256,11 @@ namespace Game.AI //Check if player or any member of his group is within range if (HasEscortState(eEscortState.Escorting) && !m_uiPlayerGUID.IsEmpty() && !me.GetVictim() && !HasEscortState(eEscortState.Returning)) { - if (m_uiPlayerCheckTimer <= diff) + m_uiPlayerCheckTimer += diff; + if (m_uiPlayerCheckTimer > 1000) { 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); @@ -302,10 +272,8 @@ namespace Game.AI return; } - m_uiPlayerCheckTimer = 1000; + m_uiPlayerCheckTimer = 0; } - else - m_uiPlayerCheckTimer -= diff; } UpdateEscortAI(diff); @@ -321,52 +289,85 @@ namespace Game.AI public override void MovementInform(MovementGeneratorType moveType, uint pointId) { - if (moveType != MovementGeneratorType.Point || !HasEscortState(eEscortState.Escorting)) + // no action allowed if there is no escort + if (!HasEscortState(eEscortState.Escorting)) return; - //Combat start position reached, continue waypoint movement - if (pointId == 0xFFFFFF) + if (moveType == MovementGeneratorType.Point) { - 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; - } - else if (pointId == 0xFFFFFE) - { - Log.outDebug(LogFilter.Scripts, "EscortAI has returned to original home location and will continue from beginning of waypoint list."); - WPIndex = 0; - m_uiWPWaitTimer = 1; - } - else - { - //Make sure that we are still on the right waypoint - if (GetCurrentWaypoint().id != pointId) + //Combat start position reached, continue waypoint movement + if (pointId == EscortPointIds.LastPoint) { - Log.outDebug(LogFilter.Server, "TSCR ERROR: EscortAI reached waypoint out of order {0}, expected {1}, creature entry {2}", pointId, GetCurrentWaypoint().id, me.GetEntry()); + 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) + { + //Call WP function + WaypointReached(pointId); + + //End of the line + if (LastWP != 0 && LastWP == pointId) + { + LastWP = 0; + + m_bStarted = false; + m_bEnded = true; + + m_uiWPWaitTimer = 50; + return; } - Log.outDebug(LogFilter.Scripts, "EscortAI Waypoint {0} reached", GetCurrentWaypoint().id); + Log.outDebug(LogFilter.Scripts, $"EscortAI Waypoint {pointId} reached"); - //Call WP function - WaypointReached(GetCurrentWaypoint().id); + WaypointMovementGenerator move = (WaypointMovementGenerator)me.GetMotionMaster().top(); + if (move != null) + m_uiWPWaitTimer = (uint)move.GetTrackerTimer().GetExpiry(); - m_uiWPWaitTimer = GetCurrentWaypoint().WaitTimeMs + 1; + //Call WP start function + if (m_uiWPWaitTimer == 0 && !HasEscortState(eEscortState.Paused) && move != null) + WaypointStart(move.GetCurrentNode()); - ++WPIndex; + if (m_bIsRunning) + me.SetWalk(false); + else + me.SetWalk(true); } } public void AddWaypoint(uint id, float x, float y, float z, uint waitTime = 0) { - Escort_Waypoint t = new Escort_Waypoint(id, x, y, z, waitTime); + GridDefines.NormalizeMapCoord(ref x); + GridDefines.NormalizeMapCoord(ref y); + + 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; } @@ -376,10 +377,29 @@ namespace Game.AI if (movePoints.Empty()) return; - foreach (var pointMove in movePoints) + LastWP = movePoints.Last().uiPointId; + + foreach (var point in movePoints) { - Escort_Waypoint point = new Escort_Waypoint(pointMove.uiPointId, pointMove.fX, pointMove.fY, pointMove.fZ, pointMove.uiWaitTime); - WaypointList.Add(point); + 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); } } @@ -418,20 +438,6 @@ namespace Game.AI return; } - if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does - { - if (!WaypointList.Empty()) - WaypointList.Clear(); - FillPointMovementListForCreature(); - } - - if (WaypointList.Empty()) - { - Log.outError(LogFilter.Scripts, "EscortAI (script: {0}, creature entry: {1}) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: {2}).", - me.GetScriptName(), me.GetEntry(), quest != null ? quest.Id : 0); - return; - } - //set variables m_bIsActiveAttacker = isActiveAttacker; m_bIsRunning = run; @@ -442,12 +448,16 @@ 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.GetMotionMaster().MovementExpired(); + me.StopMoving(); + me.GetMotionMaster().Clear(false); me.GetMotionMaster().MoveIdle(); Log.outDebug(LogFilter.Scripts, "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle."); } @@ -460,9 +470,7 @@ namespace Game.AI me.RemoveFlag(UnitFields.Flags, UnitFlags.ImmuneToNpc); } - Log.outDebug(LogFilter.Scripts, "EscortAI started with {0} waypoints. ActiveAttacker = {1}, Run = {2}, PlayerGUID = {3}", WaypointList.Count, m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID); - - WPIndex = 0; + Log.outDebug(LogFilter.Scripts, $"EscortAI started. ActiveAttacker = {m_bIsActiveAttacker}, Run = {m_bIsRunning}, PlayerGUID = {m_uiPlayerGUID.ToString()}"); //Set initial speed if (m_bIsRunning) @@ -470,6 +478,8 @@ namespace Game.AI else me.SetWalk(true); + m_bStarted = false; + AddEscortState(eEscortState.Escorting); } @@ -479,75 +489,17 @@ namespace Game.AI return; if (on) + { AddEscortState(eEscortState.Paused); + me.StopMoving(); + } else + { RemoveEscortState(eEscortState.Paused); - } - - 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.FirstOrDefault(); - WaypointList.RemoveAt(0); - if (waypoint.id == pointId) - { - if (setPosition) - me.UpdatePosition(waypoint.x, waypoint.y, waypoint.z, me.GetOrientation()); - - WPIndex = 0; - return true; - } + WaypointMovementGenerator move = (WaypointMovementGenerator)me.GetMotionMaster().top(); + if (move != null) + move.GetTrackerTimer().Reset(1); } - 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; - } - - public bool GetWaypointPosition(uint pointId, ref float x, ref float y, ref float z) - { - var waypoints = Global.ScriptMgr.GetPointMoveList(me.GetEntry()); - if (waypoints.Empty()) - return false; - - foreach (var point in waypoints) - { - if (point.uiPointId == pointId) - { - x = point.fX; - y = point.fY; - z = point.fZ; - return true; - } - } - - return false; } public virtual void WaypointReached(uint pointId) { } @@ -564,6 +516,7 @@ 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); } @@ -575,16 +528,12 @@ namespace Game.AI uint m_uiPlayerCheckTimer; eEscortState m_uiEscortState; float MaxPlayerDistance; + uint LastWP; + + WaypointPath _path; Quest m_pQuestForEscort; //generally passed in Start() when regular escort script. - List WaypointList = new List(); - Escort_Waypoint GetCurrentWaypoint() - { - return WaypointList[WPIndex]; - } - int WPIndex; - 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) @@ -593,24 +542,8 @@ namespace Game.AI bool DespawnAtFar; bool ScriptWP; bool HasImmuneToNPCFlags; - } - - public 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; + bool m_bStarted; + bool m_bEnded; } public enum eEscortState @@ -620,4 +553,10 @@ namespace Game.AI Returning = 0x002, //escort is returning after being in combat Paused = 0x004 //will not proceed with waypoints before state is removed } + + struct EscortPointIds + { + public const uint LastPoint = 0xFFFFFF; + public const uint Home = 0xFFFFFE; + } } diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index 9fecad382..b1f1d0460 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -18,6 +18,8 @@ using Framework.Constants; using Game.Entities; using Game.Groups; +using Game.Maps; +using Game.Movement; using Game.Scripting; using Game.Spells; using System; @@ -33,29 +35,26 @@ namespace Game.AI public SmartAI(Creature creature) : base(creature) { + mIsCharmed = false; // copy script to local (protection for table reload) - mWayPoints = null; mEscortState = SmartEscortState.None; mCurrentWPID = 0;//first wp id is 1 !! mWPReached = false; mWPPauseTimer = 0; - mLastWP = null; + mOOCReached = false; + mEscortNPCFlags = 0; mCanRepeatPath = false; - // spawn in run mode - creature.SetWalk(false); - mRun = false; - - mLastOOCPos = creature.GetPosition(); + // Spawn in run mode + mRun = true; + m_Ended = false; mCanAutoAttack = true; mCanCombatMove = true; mForcedPaused = false; - mLastWPIDReached = 0; - mEscortQuestID = 0; mDespawnTime = 0; @@ -103,25 +102,6 @@ namespace Game.AI GetScript().OnReset(); } - SmartPath GetNextWayPoint() - { - if (mWayPoints == null || mWayPoints.Empty()) - return null; - - mCurrentWPID++; - var path = mWayPoints.LookupByKey(mCurrentWPID); - if (path != null) - { - mLastWP = path; - if (mLastWP.id != mCurrentWPID) - { - Log.outError(LogFilter.Server, "SmartAI.GetNextWayPoint: Got not expected waypoint id {0}, expected {1}", mLastWP.id, mCurrentWPID); - } - return path; - } - 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 @@ -129,28 +109,35 @@ namespace Game.AI Log.outError(LogFilter.Server, "SmartAI.StartPath: Creature entry {0} wanted to start waypoint movement while in combat, ignoring.", me.GetEntry()); return; } + if (HasEscortState(SmartEscortState.Escorting)) StopPath(); + SetRun(run); + if (path != 0) if (!LoadPath(path)) return; - if (mWayPoints == null || mWayPoints.Empty()) + if (_path.nodes.Empty()) return; - AddEscortState(SmartEscortState.Escorting); + mCurrentWPID = 1; + m_Ended = false; + + // Do not use AddEscortState, removing everything from previous cycle + mEscortState = SmartEscortState.Escorting; mCanRepeatPath = repeat; - SetRun(run); - - SmartPath wp = GetNextWayPoint(); - if (wp != null) + if (invoker && invoker.GetTypeId() == TypeId.Player) { - mLastOOCPos = me.GetPosition(); - me.GetMotionMaster().MovePoint(wp.id, wp.x, wp.y, wp.z); - GetScript().ProcessEventsFor(SmartEvents.WaypointStart, null, wp.id, GetScript().GetPathId()); + mEscortNPCFlags = me.GetUInt32Value(UnitFields.NpcFlags); + me.SetFlag(UnitFields.NpcFlags, 0); } + + GetScript().ProcessEventsFor(SmartEvents.WaypointStart, null, mCurrentWPID, GetScript().GetPathId()); + + me.GetMotionMaster().MovePath(_path, mCanRepeatPath); } bool LoadPath(uint entry) @@ -158,12 +145,36 @@ namespace Game.AI if (HasEscortState(SmartEscortState.Escorting)) return false; - mWayPoints = Global.SmartAIMgr.GetPath(entry); - if (mWayPoints == null) + var path = Global.SmartAIMgr.GetPath(entry); + if (path.Empty()) { 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; } @@ -172,22 +183,22 @@ namespace Game.AI { if (!HasEscortState(SmartEscortState.Escorting)) return; + if (HasEscortState(SmartEscortState.Paused)) { - Log.outError(LogFilter.Server, "SmartAI.PausePath: Creature entry {0} wanted to pause waypoint movement while already paused, ignoring.", me.GetEntry()); + Log.outError(LogFilter.Server, $"SmartAI.PausePath: Creature entry {me.GetEntry()} wanted to pause waypoint (current waypoint: {mCurrentWPID}) movement while already paused, ignoring."); return; } - mForcedPaused = forced; - mLastOOCPos = me.GetPosition(); + AddEscortState(SmartEscortState.Paused); mWPPauseTimer = delay; - if (forced) + if (forced && !mWPReached) { + mForcedPaused = forced; SetRun(mRun); - me.StopMoving();//force stop - me.GetMotionMaster().MoveIdle();//force stop + me.StopMoving(); } - GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, mLastWP.id, GetScript().GetPathId()); + GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, mCurrentWPID, GetScript().GetPathId()); } public void StopPath(uint DespawnTime = 0, uint quest = 0, bool fail = false) @@ -197,33 +208,28 @@ namespace Game.AI if (quest != 0) mEscortQuestID = quest; - SetDespawnTime(DespawnTime); - mDespawnTime = DespawnTime; - mLastOOCPos = me.GetPosition(); - me.StopMoving();//force stop + if (mDespawnState != 2) + SetDespawnTime(DespawnTime); + + me.StopMoving(); + me.GetMotionMaster().MovementExpired(false); me.GetMotionMaster().MoveIdle(); - GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, mLastWP.id, GetScript().GetPathId()); + GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, mCurrentWPID, GetScript().GetPathId()); EndPath(fail); } public void EndPath(bool fail = false) { - GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, mLastWP.id, GetScript().GetPathId()); - RemoveEscortState(SmartEscortState.Escorting | SmartEscortState.Paused | SmartEscortState.Returning); - mWayPoints = null; - mCurrentWPID = 0; + _path.nodes.Clear(); mWPPauseTimer = 0; - mLastWP = null; - if (mCanRepeatPath) + if (mEscortNPCFlags != 0) { - if (IsAIControlled()) - StartPath(mRun, GetScript().GetPathId(), true); + me.SetFlag(UnitFields.NpcFlags, mEscortNPCFlags); + mEscortNPCFlags = 0; } - else - GetScript().SetPathId(0); List targets = GetScript().GetTargetList(SharedConst.SmartEscortTargets); if (targets != null && mEscortQuestID != 0) @@ -266,15 +272,37 @@ 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); - if (mLastWP != null) - me.GetMotionMaster().MovePoint(mLastWP.id, mLastWP.x, mLastWP.y, mLastWP.z); + + WaypointMovementGenerator move = (WaypointMovementGenerator)me.GetMotionMaster().top(); + if (move != null) + move.GetTrackerTimer().Reset(1); } void ReturnToLastOOCPos() @@ -282,77 +310,59 @@ namespace Game.AI if (!IsAIControlled()) return; - SetRun(mRun); - me.GetMotionMaster().MovePoint(EventId.SmartEscortLastOCCPoint, mLastOOCPos); + 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); } void UpdatePath(uint diff) { if (!HasEscortState(SmartEscortState.Escorting)) return; + if (mEscortInvokerCheckTimer < diff) { if (!IsEscortInvokerInRange()) { - StopPath(mDespawnTime, mEscortQuestID, true); + 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; } mEscortInvokerCheckTimer = 1000; } - else mEscortInvokerCheckTimer -= diff; + else + mEscortInvokerCheckTimer -= diff; + // handle pause if (HasEscortState(SmartEscortState.Paused)) { - if (mWPPauseTimer < diff) + if (mWPPauseTimer <= diff) { - 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; + if (!me.IsInCombat() && !HasEscortState(SmartEscortState.Returning) && (mWPReached || mForcedPaused)) + ResumePath(); } else - { mWPPauseTimer -= diff; - } } + else if (m_Ended) // end path + { + m_Ended = false; + StopPath(); + return; + } + if (HasEscortState(SmartEscortState.Returning)) { if (mWPReached)//reached OOC WP { + mOOCReached = false; RemoveEscortState(SmartEscortState.Returning); if (!HasEscortState(SmartEscortState.Paused)) ResumePath(); - mWPReached = false; - } - } - if (me.IsInCombat() || HasEscortState(SmartEscortState.Paused | SmartEscortState.Returning)) - return; - // handle next wp - if (mWPReached)//reached WP - { - mWPReached = false; - - if (mCurrentWPID == GetWPCount()) - { - EndPath(); - } - else - { - SmartPath wp = GetNextWayPoint(); - if (wp != null) - { - SetRun(mRun); - me.GetMotionMaster().MovePoint(wp.id, wp.x, wp.y, wp.z); - } } } } @@ -415,22 +425,44 @@ namespace Game.AI void MovepointReached(uint id) { - if (id != EventId.SmartEscortLastOCCPoint && mLastWPIDReached != id) - GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, 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 - 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) || MovementType == MovementGeneratorType.Follow) + if (MovementType == MovementGeneratorType.Point && Data == EventId.SmartEscortLastOCCPoint) me.ClearUnitState(UnitState.Evade); GetScript().ProcessEventsFor(SmartEvents.Movementinform, null, (uint)MovementType, Data); - if (MovementType != MovementGeneratorType.Point || !HasEscortState(SmartEscortState.Escorting)) + if (!HasEscortState(SmartEscortState.Escorting)) return; - MovepointReached(Data); + + if (MovementType == MovementGeneratorType.Waypoint || (MovementType == MovementGeneratorType.Point && Data == EventId.SmartEscortLastOCCPoint)) + MovepointReached(Data); } void RemoveAuras() @@ -460,28 +492,35 @@ namespace Game.AI GetScript().ProcessEventsFor(SmartEvents.Evade);//must be after aura clear so we can cast spells from db SetRun(mRun); - Unit target = !mFollowGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, mFollowGuid) : null; - Unit owner = me.GetCharmerOrOwner(); + if (HasEscortState(SmartEscortState.Escorting)) { AddEscortState(SmartEscortState.Returning); ReturnToLastOOCPos(); } - else if (target) - { - me.GetMotionMaster().MoveFollow(target, mFollowDist, mFollowAngle); - // evade is not cleared in MoveFollow, so we can't keep it - me.ClearUnitState(UnitState.Evade); - } - else if (owner) - { - me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, SharedConst.PetFollowAngle); - me.ClearUnitState(UnitState.Evade); - } else - me.GetMotionMaster().MoveTargetedHome(); + { + Unit target = !mFollowGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, mFollowGuid) : null; + Unit owner = me.GetCharmerOrOwner(); - Reset(); + if (target) + { + 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) + { + me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, SharedConst.PetFollowAngle); + me.ClearUnitState(UnitState.Evade); + } + else + me.GetMotionMaster().MoveTargetedHome(); + } + + if (!me.HasUnitState(UnitState.Evade)) + GetScript().OnReset(); } public override void MoveInLineOfSight(Unit who) @@ -585,25 +624,13 @@ 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) @@ -618,16 +645,26 @@ namespace Game.AI public override void AttackStart(Unit who) { - if (who != null && me.Attack(who, true)) + // dont allow charmed npcs to act on their own + if (me.HasFlag(UnitFields.Flags, UnitFlags.PlayerControlled)) { - SetRun(mRun); - if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Point) - me.GetMotionMaster().MovementExpired(); + if (who && mCanAutoAttack) + me.Attack(who, true); + return; + } + if (who && me.Attack(who, me.IsWithinMeleeRange(who))) + { if (mCanCombatMove) - me.GetMotionMaster().MoveChase(who); + { + SetRun(mRun); - mLastOOCPos = me.GetPosition(); + MovementGeneratorType type = me.GetMotionMaster().GetMotionSlotType(MovementSlot.Active); + if (type == MovementGeneratorType.Waypoint || type == MovementGeneratorType.Point) + me.StopMoving(); + + me.GetMotionMaster().MoveChase(who); + } } } @@ -814,12 +851,9 @@ 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(); } } @@ -916,8 +950,6 @@ namespace Game.AI public void StartDespawn() { mDespawnState = 2; } - int GetWPCount() { return mWayPoints != null ? mWayPoints.Count : 0; } - bool mIsCharmed; uint mFollowCreditType; uint mFollowArrivedTimer; @@ -928,15 +960,13 @@ namespace Game.AI float mFollowAngle; SmartScript mScript = new SmartScript(); - Dictionary mWayPoints; SmartEscortState mEscortState; uint mCurrentWPID; - uint mLastWPIDReached; bool mWPReached; + bool mOOCReached; + bool m_Ended; uint mWPPauseTimer; - SmartPath mLastWP; - Position mLastOOCPos;//set on enter combat - + uint mEscortNPCFlags; bool mCanRepeatPath; bool mRun; bool mEvadeDisabled; @@ -945,6 +975,7 @@ namespace Game.AI bool mForcedPaused; uint mInvincibilityHpLevel; + WaypointPath _path; uint mDespawnTime; uint mRespawnTime; uint mDespawnState; diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index 4d9688549..61b6201b9 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -239,7 +239,6 @@ namespace Game.AI if (last_entry != entry) { - waypoint_map[entry] = new Dictionary(); last_id = 1; count++; } @@ -248,7 +247,14 @@ namespace Game.AI Log.outError(LogFilter.Sql, "SmartWaypointMgr.LoadFromDB: Path entry {0}, unexpected point id {1}, expected {2}.", entry, id, last_id); last_id++; - waypoint_map[entry][id] = new SmartPath(id, x, y, z); + + WayPoint point = new WayPoint(); + point.id = id; + point.x = x; + point.y = y; + point.z = z; + + waypoint_map.Add(entry, point); last_entry = entry; total++; @@ -993,7 +999,8 @@ namespace Game.AI break; case SmartActions.WpStart: { - if (Global.SmartAIMgr.GetPath(e.Action.wpStart.pathID) == null) + List path = Global.SmartAIMgr.GetPath(e.Action.wpStart.pathID); + if (path.Empty()) { Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Creature {0} Event {1} Action {2} uses non-existent WaypointPath id {3}, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.Action.wpStart.pathID); return false; @@ -1404,7 +1411,7 @@ namespace Game.AI return temp; } - public Dictionary GetPath(uint id) + public List GetPath(uint id) { return waypoint_map.LookupByKey(id); } @@ -1428,7 +1435,7 @@ namespace Game.AI } MultiMap[] mEventMap = new MultiMap[(int)SmartScriptType.Max]; - Dictionary> waypoint_map = new Dictionary>(); + MultiMap waypoint_map = new MultiMap(); Dictionary SmartAITypeMask = new Dictionary { @@ -1534,16 +1541,8 @@ namespace Game.AI }; } - public class SmartPath + public class WayPoint { - public SmartPath(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; diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 85e982ffd..99d025df5 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -2228,58 +2228,6 @@ 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; - SmartPath closestWp = null; - - var targets = GetTargets(e, unit); - - foreach (var obj in targets) - { - Creature target = obj.ToCreature(); - if (target) - { - 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.Empty()) - continue; - - SmartPath 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/RandomMovement.cs b/Source/Game/Movement/Generators/RandomMovement.cs index 8ca764d46..f737c97ec 100644 --- a/Source/Game/Movement/Generators/RandomMovement.cs +++ b/Source/Game/Movement/Generators/RandomMovement.cs @@ -22,7 +22,7 @@ using System; namespace Game.Movement { - public class RandomMovementGenerator : MovementGeneratorMedium where T : Creature + public class RandomMovementGenerator : MovementGeneratorMedium { public RandomMovementGenerator(float spawn_dist = 0.0f) { @@ -38,7 +38,7 @@ namespace Game.Movement return MovementGeneratorType.Random; } - public override void DoInitialize(T creature) + public override void DoInitialize(Creature creature) { if (!creature.IsAlive()) return; @@ -53,17 +53,17 @@ namespace Game.Movement _setRandomLocation(creature); } - public override void DoFinalize(T creature) + public override void DoFinalize(Creature creature) { creature.ClearUnitState(UnitState.Roaming | UnitState.RoamingMove); creature.SetWalk(false); } - public override void DoReset(T creature) + public override void DoReset(Creature creature) { DoInitialize(creature); } - public override bool DoUpdate(T creature, uint diff) + public override bool DoUpdate(Creature creature, uint diff) { if (!creature || !creature.IsAlive()) return false; @@ -84,7 +84,7 @@ namespace Game.Movement return true; } - public void _setRandomLocation(T creature) + public void _setRandomLocation(Creature creature) { if (creature.IsMovementPreventedByCasting()) { diff --git a/Source/Game/Movement/Generators/WaypointMovement.cs b/Source/Game/Movement/Generators/WaypointMovement.cs index f530174ff..66791ffd7 100644 --- a/Source/Game/Movement/Generators/WaypointMovement.cs +++ b/Source/Game/Movement/Generators/WaypointMovement.cs @@ -26,10 +26,11 @@ using System.Linq; namespace Game.Movement { - public class WaypointMovementGenerator : MovementGeneratorMedium where T : Creature + public class WaypointMovementGenerator : MovementGeneratorMedium { const int FLIGHT_TRAVEL_UPDATE = 100; const int TIMEDIFF_NEXT_WP = 250; + public WaypointMovementGenerator(uint pathid = 0, bool _repeating = true) { nextMoveTime = new TimeTrackerSmall(0); @@ -37,52 +38,100 @@ namespace Game.Movement pathId = pathid; repeating = _repeating; } - public override void DoReset(T owner) + + public WaypointMovementGenerator(WaypointPath _path, bool _repeating = true) { - owner.AddUnitState(UnitState.Roaming | UnitState.RoamingMove); - StartMoveNow(owner); + nextMoveTime = new TimeTrackerSmall(0); + isArrivalDone = false; + pathId = 0; + repeating = _repeating; + loadedFromDB = false; + path = _path; } - public override void DoFinalize(T owner) + public override void DoReset(Creature creature) { - owner.ClearUnitState(UnitState.Roaming | UnitState.RoamingMove); - owner.SetWalk(false); + if (!Stopped()) + StartMoveNow(creature); } - public override void DoInitialize(T owner) + + public override void DoFinalize(Creature creature) { - LoadPath(owner); - owner.AddUnitState(UnitState.Roaming | UnitState.RoamingMove); + creature.ClearUnitState(UnitState.Roaming | UnitState.RoamingMove); + creature.SetWalk(false); } - public override bool DoUpdate(T owner, uint time_diff) + + public override void DoInitialize(Creature creature) { + LoadPath(creature); + } + + 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 (owner.HasUnitState(UnitState.NotMove)) + if (creature.HasUnitState(UnitState.NotMove)) { - owner.ClearUnitState(UnitState.RoamingMove); + creature.ClearUnitState(UnitState.RoamingMove); return true; } + // prevent a crash at empty waypoint path. - if (path == null || path.Empty()) + if (path == null || path.nodes.Empty()) return false; if (Stopped()) { if (CanMove((int)time_diff)) - return StartMove(owner); + return StartMoveNow(creature); } else { - if (owner.IsStopped()) - Stop(WorldConfig.GetIntValue(WorldCfg.CreatureStopForPlayer)); - else if (owner.moveSpline.Finalized()) + if (creature.IsStopped()) + Stop(loadedFromDB ? WorldConfig.GetIntValue(WorldCfg.CreatureStopForPlayer) : 2 * Time.Hour * Time.InMilliseconds); + else if (creature.moveSpline.Finalized()) { - OnArrived(owner); - return StartMove(owner); + 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 true; } + void MovementInform(Creature creature) { if (creature.IsAIEnabled) @@ -93,38 +142,44 @@ namespace Game.Movement { nextMoveTime.Reset(time); } + bool Stopped() { return !nextMoveTime.Passed(); } + bool CanMove(int diff) { nextMoveTime.Update(diff); return nextMoveTime.Passed(); } - void StartMoveNow(Creature creature) + + bool StartMoveNow(Creature creature) { nextMoveTime.Reset(0); - StartMove(creature); + return StartMove(creature); } + bool StartMove(Creature creature) { - if (path == null || path.Empty()) + if (!creature || !creature.IsAlive()) return false; - if (Stopped()) - return true; + if (path == null || path.nodes.Empty()) + return false; bool transportPath = creature.GetTransport() != null; if (isArrivalDone) { - if ((currentNode == path.Count - 1) && !repeating) // If that's our last waypoint + if ((currentNode == path.nodes.Count - 1) && !repeating) // If that's our last waypoint { - float x = path[(int)currentNode].x; - float y = path[(int)currentNode].y; - float z = path[(int)currentNode].z; - float o = path[(int)currentNode].orientation; + WaypointNode waypoint = path.nodes.LookupByIndex((int)currentNode); + + float x = waypoint.x; + float y = waypoint.y; + float z = waypoint.z; + float o = creature.GetOrientation(); if (!transportPath) creature.SetHomePosition(x, y, z, o); @@ -143,21 +198,43 @@ namespace Game.Movement // else if (vehicle) - this should never happen, vehicle offsets are const } - creature.GetMotionMaster().Initialize(); return false; } - currentNode = (uint)((currentNode + 1) % path.Count); + currentNode = (uint)((currentNode + 1) % path.nodes.Count); } - WaypointData node = path.LookupByIndex((int)currentNode); + 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; isArrivalDone = false; + recalculateSpeed = false; creature.AddUnitState(UnitState.RoamingMove); MoveSplineInit init = new MoveSplineInit(creature); - Position formationDest = new Position(node.x, node.y, node.z); + var node = path.nodes.LookupByIndex((int)currentNode); + Position formationDest = new Position(node.x, node.y, node.z, 0.0f); //! If creature is on transport, we assume waypoints set in DB are already transport offsets if (transportPath) @@ -168,13 +245,8 @@ namespace Game.Movement trans.CalculatePassengerPosition(ref formationDest.posX, ref formationDest.posY, ref formationDest.posZ, ref formationDest.Orientation); } - init.MoveTo(formationDest.posX, formationDest.posY, formationDest.posZ); - - //! 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(formationDest.Orientation); - - switch (node.movetype) + init.MovebyPath(pathing.ToArray(), (int)currentNode); + switch (finalMove) { case WaypointMoveType.Land: init.SetAnimation(AnimType.ToGround); @@ -190,23 +262,26 @@ 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 (pathId == 0) - pathId = creature.GetWaypointPath(); + if (loadedFromDB) + { + if (pathId == 0) + pathId = creature.GetWaypointPath(); - path = Global.WaypointMgr.GetPath(pathId); + path = Global.WaypointMgr.GetPath(pathId); + } if (path == null) { @@ -215,35 +290,54 @@ namespace Game.Movement return; } - StartMoveNow(creature); + if (!Stopped()) + StartMoveNow(creature); } void OnArrived(Creature creature) { - if (path == null || path.Empty()) + if (path == null || path.nodes.Empty()) return; - if (isArrivalDone) - return; - - creature.ClearUnitState(UnitState.RoamingMove); - isArrivalDone = true; - - var wpData = path.LookupByIndex((int)currentNode); - if (wpData.event_id != 0 && RandomHelper.IRand(0, 99) < wpData.event_chance) + WaypointNode waypoint = path.nodes.LookupByIndex((int)currentNode); + if (waypoint.delay != 0) { - Log.outDebug(LogFilter.Unit, "Creature movement start script {0} at point {1} for {2}.", wpData.event_id, currentNode, creature.GetGUID()); - creature.GetMap().ScriptsStart(ScriptsType.Waypoint, wpData.event_id, creature, null); + creature.ClearUnitState(UnitState.RoamingMove); + Stop((int)waypoint.delay); + } + + if (waypoint.eventId != 0 && RandomHelper.URand(0, 99) < waypoint.eventChance) + { + Log.outDebug(LogFilter.Unit, "Creature movement start script {0} at point {1} for {2}.", waypoint.eventId, currentNode, creature.GetGUID()); + creature.ClearUnitState(UnitState.RoamingMove); + creature.GetMap().ScriptsStart(ScriptsType.Waypoint, waypoint.eventId, creature, null); } // Inform script MovementInform(creature); creature.UpdateWaypointID(currentNode); - if (wpData.delay != 0) + creature.SetWalk(waypoint.moveType != WaypointMoveType.Run); + } + + void FormationMove(Creature creature) + { + 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) { - creature.ClearUnitState(UnitState.RoamingMove); - Stop((int)wpData.delay); + ITransport trans = creature.GetDirectTransport(); + if (trans != null) + trans.CalculatePassengerPosition(ref formationDest.posX, ref formationDest.posY, ref formationDest.posZ, ref formationDest.Orientation); } + + // 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() @@ -251,11 +345,20 @@ 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; - List path; + bool loadedFromDB; + WaypointPath path; uint currentNode; } diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index a0714e8ef..536b50d08 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -35,22 +35,17 @@ namespace Game.Movement public MotionMaster(Unit me) { _owner = me; - _expList = null; + _expireList = null; _top = -1; _cleanFlag = MMCleanFlag.None; for (byte i = 0; i < (int)MovementSlot.Max; ++i) { - Impl[i] = null; - _needInit[i] = true; + _slot[i] = null; + _initialize[i] = true; } } - bool isStatic(IMovementGenerator mv) - { - return (mv == staticIdleMovement); - } - public void Initialize() { while (!empty()) @@ -94,19 +89,19 @@ namespace Game.Movement else _cleanFlag &= ~MMCleanFlag.Update; - if (_expList != null) + if (_expireList != null) { - for (int i = 0; i < _expList.Count; ++i) + for (int i = 0; i < _expireList.Count; ++i) { - IMovementGenerator mg = _expList[i]; + IMovementGenerator mg = _expireList[i]; DirectDelete(mg); } - _expList = null; + _expireList = null; if (empty()) Initialize(); - else if (needInitTop()) + else if (NeedInitTop()) InitTop(); else if (Convert.ToBoolean(_cleanFlag & MMCleanFlag.Reset)) top().Reset(_owner); @@ -117,81 +112,86 @@ namespace Game.Movement _owner.UpdateUnderwaterState(_owner.GetMap(), _owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ()); } - void DirectClean(bool reset) + public void Clear(bool reset = true) { - while (size() > 1) + if (Convert.ToBoolean(_cleanFlag & MMCleanFlag.Update)) { - IMovementGenerator curr = top(); - pop(); - if (curr != null) - DirectDelete(curr); + if (reset) + _cleanFlag |= MMCleanFlag.Reset; + else + _cleanFlag &= ~MMCleanFlag.Reset; + DelayedClean(); } + else + DirectClean(reset); + } + public void MovementExpired(bool reset = true) + { + if (Convert.ToBoolean(_cleanFlag & MMCleanFlag.Update)) + { + if (reset) + _cleanFlag |= MMCleanFlag.Reset; + else + _cleanFlag &= ~MMCleanFlag.Reset; + DelayedExpire(); + } + else + DirectExpire(reset); + } + + public MovementGeneratorType GetCurrentMovementGeneratorType() + { if (empty()) - return; + return MovementGeneratorType.Idle; - if (needInitTop()) - InitTop(); - else if (reset) - top().Reset(_owner); + return top().GetMovementGeneratorType(); } - void DelayedClean() + public MovementGeneratorType GetMotionSlotType(MovementSlot slot) { - while (size() > 1) + if (_slot[(int)slot] == null) + return MovementGeneratorType.Null; + else + return _slot[(int)slot].GetMovementGeneratorType(); + } + + public IMovementGenerator GetMotionSlot(int slot) + { + Contract.Assert(slot >= 0); + return _slot[slot]; + } + + public void propagateSpeedChange() + { + for (int i = 0; i <= _top; ++i) { - IMovementGenerator curr = top(); - pop(); - if (curr != null) - DelayedDelete(curr); + if (_slot[i] != null) + _slot[i].unitSpeedChanged(); } } - void DirectExpire(bool reset) + public bool GetDestination(out float x, out float y, out float z) { - if (size() > 1) - { - IMovementGenerator curr = top(); - pop(); - DirectDelete(curr); - } + x = 0f; + y = 0f; + z = 0f; + if (_owner.moveSpline.Finalized()) + return false; - while (!empty() && top() == null)//not sure this will work - --_top; - - if (empty()) - Initialize(); - else if (needInitTop()) - InitTop(); - else if (reset) - top().Reset(_owner); - } - - void DelayedExpire() - { - if (size() > 1) - { - IMovementGenerator curr = top(); - pop(); - DelayedDelete(curr); - } - - while (!empty() && top() == null) - --_top; + Vector3 dest = _owner.moveSpline.FinalDestination(); + x = dest.X; + y = dest.Y; + z = dest.Z; + return true; } public void MoveIdle() { - if (empty() || !isStatic(top())) + if (empty() || !IsStatic(top())) StartMovement(staticIdleMovement, MovementSlot.Idle); } - public void MoveRandom(float spawndist = 0.0f) - { - if (_owner.IsTypeId(TypeId.Unit)) - StartMovement(new RandomMovementGenerator(spawndist), MovementSlot.Idle); - } - public void MoveTargetedHome() { Clear(false); @@ -208,12 +208,22 @@ namespace Game.Movement } } - public void MoveConfused() + public void MoveRandom(float spawndist = 0.0f) { + if (_owner.IsTypeId(TypeId.Unit)) + StartMovement(new RandomMovementGenerator(spawndist), MovementSlot.Idle); + } + + public void MoveFollow(Unit target, float dist = 0.0f, float angle = 0.0f, MovementSlot slot = MovementSlot.Idle) + { + // ignore movement request if target not exist + if (!target || target == _owner) + return; + if (_owner.IsTypeId(TypeId.Player)) - StartMovement(new ConfusedGenerator(), MovementSlot.Controlled); + StartMovement(new FollowMovementGenerator(target, dist, angle), slot); else - StartMovement(new ConfusedGenerator(), MovementSlot.Controlled); + StartMovement(new FollowMovementGenerator(target, dist, angle), slot); } public void MoveChase(Unit target, float dist = 0.0f, float angle = 0.0f) @@ -227,15 +237,33 @@ namespace Game.Movement StartMovement(new ChaseMovementGenerator(target, dist, angle), MovementSlot.Active); } - public void MoveFollow(Unit target, float dist = 0.0f, float angle = 0.0f, MovementSlot slot = MovementSlot.Idle) + public void MoveConfused() { - if (!target || target == _owner) + if (_owner.IsTypeId(TypeId.Player)) + StartMovement(new ConfusedGenerator(), MovementSlot.Controlled); + else + StartMovement(new ConfusedGenerator(), MovementSlot.Controlled); + } + + public void MoveFleeing(Unit enemy, uint time) + { + if (!enemy) return; if (_owner.IsTypeId(TypeId.Player)) - StartMovement(new FollowMovementGenerator(target, dist, angle), slot); + StartMovement(new FleeingGenerator(enemy.GetGUID()), MovementSlot.Controlled); else - StartMovement(new FollowMovementGenerator(target, dist, angle), slot); + { + if (time != 0) + StartMovement(new TimedFleeingGenerator(enemy.GetGUID(), time), MovementSlot.Controlled); + else + StartMovement(new FleeingGenerator(enemy.GetGUID()), MovementSlot.Controlled); + } + } + + public void MovePoint(uint id, Position pos, bool generatePath = true) + { + MovePoint(id, pos.posX, pos.posY, pos.posZ, generatePath); } public void MovePoint(ulong id, float x, float y, float z, bool generatePath = true) @@ -246,9 +274,25 @@ namespace Game.Movement StartMovement(new PointMovementGenerator(id, x, y, z, generatePath), MovementSlot.Active); } - public void MovePoint(uint id, Position pos, bool generatePath = true) + void MoveCloserAndStop(uint id, Unit target, float distance) { - MovePoint(id, pos.posX, pos.posY, pos.posZ, generatePath); + float distanceToTravel = _owner.GetExactDist2d(target) - distance; + if (distanceToTravel > 0.0f) + { + float angle = _owner.GetAngle(target); + float destx = _owner.GetPositionX() + distanceToTravel * (float)Math.Cos(angle); + float desty = _owner.GetPositionY() + distanceToTravel * (float)Math.Sin(angle); + MovePoint(id, destx, desty, target.GetPositionZ()); + } + else + { + // we are already close enough. We just need to turn toward the target without changing position. + MoveSplineInit init = new MoveSplineInit(_owner); + init.MoveTo(_owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZMinusOffset()); + init.SetFacing(target); + init.Launch(); + StartMovement(new EffectMovementGenerator(id), MovementSlot.Active); + } } public void MoveLand(uint id, Position pos) @@ -275,6 +319,34 @@ namespace Game.Movement StartMovement(new EffectMovementGenerator(id), MovementSlot.Active); } + public void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint id = EventId.Charge, bool generatePath = false, Unit target = null, SpellEffectExtraData spellEffectExtraData = null) + { + if (_slot[(int)MovementSlot.Controlled] != null && _slot[(int)MovementSlot.Controlled].GetMovementGeneratorType() != MovementGeneratorType.Distract) + return; + + if (_owner.IsTypeId(TypeId.Player)) + StartMovement(new PointMovementGenerator(id, x, y, z, generatePath, speed, target, spellEffectExtraData), MovementSlot.Controlled); + else + StartMovement(new PointMovementGenerator(id, x, y, z, generatePath, speed, target, spellEffectExtraData), MovementSlot.Controlled); + } + + public void MoveCharge(PathGenerator path, float speed = SPEED_CHARGE, Unit target = null, SpellEffectExtraData spellEffectExtraData = null) + { + Vector3 dest = path.GetActualEndPosition(); + + MoveCharge(dest.X, dest.Y, dest.Z, SPEED_CHARGE, EventId.ChargePrepath); + + // Charge movement is not started when using EVENT_CHARGE_PREPATH + MoveSplineInit init = new MoveSplineInit(_owner); + init.MovebyPath(path.GetPath()); + init.SetVelocity(speed); + if (target != null) + init.SetFacing(target); + if (spellEffectExtraData != null) + init.SetSpellEffectExtraData(spellEffectExtraData); + init.Launch(); + } + public void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ, SpellEffectExtraData spellEffectExtraData = null) { //this function may make players fall below map @@ -464,34 +536,6 @@ namespace Game.Movement StartMovement(new EffectMovementGenerator(id), MovementSlot.Controlled); } - public void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint id = EventId.Charge, bool generatePath = false, Unit target = null, SpellEffectExtraData spellEffectExtraData = null) - { - if (Impl[(int)MovementSlot.Controlled] != null && Impl[(int)MovementSlot.Controlled].GetMovementGeneratorType() != MovementGeneratorType.Distract) - return; - - if (_owner.IsTypeId(TypeId.Player)) - StartMovement(new PointMovementGenerator(id, x, y, z, generatePath, speed, target, spellEffectExtraData), MovementSlot.Controlled); - else - StartMovement(new PointMovementGenerator(id, x, y, z, generatePath, speed, target, spellEffectExtraData), MovementSlot.Controlled); - } - - public void MoveCharge(PathGenerator path, float speed = SPEED_CHARGE, Unit target = null, SpellEffectExtraData spellEffectExtraData = null) - { - Vector3 dest = path.GetActualEndPosition(); - - MoveCharge(dest.X, dest.Y, dest.Z, SPEED_CHARGE, EventId.ChargePrepath); - - // Charge movement is not started when using EVENT_CHARGE_PREPATH - MoveSplineInit init = new MoveSplineInit(_owner); - init.MovebyPath(path.GetPath()); - init.SetVelocity(speed); - if (target != null) - init.SetFacing(target); - if (spellEffectExtraData != null) - init.SetSpellEffectExtraData(spellEffectExtraData); - init.Launch(); - } - public void MoveSeekAssistance(float x, float y, float z) { if (_owner.IsTypeId(TypeId.Player)) @@ -513,22 +557,6 @@ namespace Game.Movement StartMovement(new AssistanceDistractMovementGenerator(time), MovementSlot.Active); } - public void MoveFleeing(Unit enemy, uint time) - { - if (!enemy) - return; - - if (_owner.IsTypeId(TypeId.Player)) - StartMovement(new FleeingGenerator(enemy.GetGUID()), MovementSlot.Controlled); - else - { - if (time != 0) - StartMovement(new TimedFleeingGenerator(enemy.GetGUID(), time), MovementSlot.Controlled); - else - StartMovement(new FleeingGenerator(enemy.GetGUID()), MovementSlot.Controlled); - } - } - public void MoveTaxiFlight(uint path, uint pathnode) { if (_owner.IsTypeId(TypeId.Player)) @@ -555,45 +583,23 @@ namespace Game.Movement public void MoveDistract(uint timer) { - if (Impl[(int)MovementSlot.Controlled] != null) + if (_slot[(int)MovementSlot.Controlled] != null) return; StartMovement(new DistractMovementGenerator(timer), MovementSlot.Controlled); } - void StartMovement(IMovementGenerator m, MovementSlot slot) - { - int _slot = (int)slot; - IMovementGenerator curr = Impl[_slot]; - if (curr != null) - { - Impl[_slot] = null; // in case a new one is generated in this slot during directdelete - if (_top == _slot && Convert.ToBoolean(_cleanFlag & MMCleanFlag.Update)) - DelayedDelete(curr); - else - DirectDelete(curr); - } - else if (_top < _slot) - { - _top = _slot; - } - - Impl[_slot] = m; - if (_top > _slot) - _needInit[_slot] = true; - else - { - _needInit[_slot] = false; - m.Initialize(_owner); - } - } - public void MovePath(uint path_id, bool repeatable) { if (path_id == 0) return; - StartMovement(new WaypointMovementGenerator(path_id, repeatable), MovementSlot.Idle); + 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) @@ -604,90 +610,133 @@ namespace Game.Movement StartMovement(new RotateMovementGenerator(time, direction), MovementSlot.Active); } - public void propagateSpeedChange() - { - for (int i = 0; i <= _top; ++i) - { - if (Impl[i] != null) - Impl[i].unitSpeedChanged(); - } - } - - public MovementGeneratorType GetCurrentMovementGeneratorType() + void pop() { if (empty()) - return MovementGeneratorType.Idle; + return; - return top().GetMovementGeneratorType(); + _slot[_top] = null; + while (!empty() && top() == null) + --_top; } - public MovementGeneratorType GetMotionSlotType(MovementSlot slot) + bool NeedInitTop() { - if (Impl[(int)slot] == null) - return MovementGeneratorType.Null; - else - return Impl[(int)slot].GetMovementGeneratorType(); + if (empty()) + return false; + + return _initialize[_top]; } void InitTop() { top().Initialize(_owner); - _needInit[_top] = false; + _initialize[_top] = false; + } + + void StartMovement(IMovementGenerator m, MovementSlot slot) + { + IMovementGenerator curr = _slot[(int)slot]; + if (curr != null) + { + _slot[(int)slot] = null; // in case a new one is generated in this slot during directdelete + if (_top == (int)slot && Convert.ToBoolean(_cleanFlag & MMCleanFlag.Update)) + DelayedDelete(curr); + else + DirectDelete(curr); + } + else if (_top < (int)slot) + { + _top = (int)slot; + } + + _slot[(int)slot] = m; + if (_top > (int)slot) + _initialize[(int)slot] = true; + else + { + _initialize[(int)slot] = false; + m.Initialize(_owner); + } + } + + void DirectClean(bool reset) + { + while (size() > 1) + { + IMovementGenerator curr = top(); + pop(); + if (curr != null) + DirectDelete(curr); + } + + if (empty()) + return; + + if (NeedInitTop()) + InitTop(); + else if (reset) + top().Reset(_owner); + } + + void DelayedClean() + { + while (size() > 1) + { + IMovementGenerator curr = top(); + pop(); + if (curr != null) + DelayedDelete(curr); + } + } + + void DirectExpire(bool reset) + { + if (size() > 1) + { + IMovementGenerator curr = top(); + pop(); + DirectDelete(curr); + } + + while (!empty() && top() == null)//not sure this will work + --_top; + + if (empty()) + Initialize(); + else if (NeedInitTop()) + InitTop(); + else if (reset) + top().Reset(_owner); + } + + void DelayedExpire() + { + if (size() > 1) + { + IMovementGenerator curr = top(); + pop(); + DelayedDelete(curr); + } + + while (!empty() && top() == null) + --_top; } void DirectDelete(IMovementGenerator curr) { - if (isStatic(curr)) + if (IsStatic(curr)) return; curr.Finalize(_owner); } void DelayedDelete(IMovementGenerator curr) { - if (isStatic(curr)) + if (IsStatic(curr)) return; - if (_expList == null) - _expList = new List(); - _expList.Add(curr); - } - - public bool GetDestination(out float x, out float y, out float z) - { - x = 0f; - y = 0f; - z = 0f; - if (_owner.moveSpline.Finalized()) - return false; - - Vector3 dest = _owner.moveSpline.FinalDestination(); - x = dest.X; - y = dest.Y; - z = dest.Z; - return true; - } - - void pop() - { - if (empty()) - return; - - Impl[_top] = null; - while (!empty() && top() == null) - --_top; - } - - void push(IMovementGenerator _Val) - { - ++_top; - Impl[_top] = _Val; - } - - bool needInitTop() - { - if (empty()) - return false; - - return _needInit[_top]; + if (_expireList == null) + _expireList = new List(); + _expireList.Add(curr); } public bool empty() { return (_top < 0); } @@ -697,41 +746,7 @@ namespace Game.Movement public IMovementGenerator top() { Contract.Assert(!empty()); - return Impl[_top]; - } - - public IMovementGenerator GetMotionSlot(int slot) - { - Contract.Assert(slot >= 0); - return Impl[slot]; - } - - public void Clear(bool reset = true) - { - if (Convert.ToBoolean(_cleanFlag & MMCleanFlag.Update)) - { - if (reset) - _cleanFlag |= MMCleanFlag.Reset; - else - _cleanFlag &= ~MMCleanFlag.Reset; - DelayedClean(); - } - else - DirectClean(reset); - } - - public void MovementExpired(bool reset = true) - { - if (Convert.ToBoolean(_cleanFlag & MMCleanFlag.Update)) - { - if (reset) - _cleanFlag |= MMCleanFlag.Reset; - else - _cleanFlag &= ~MMCleanFlag.Reset; - DelayedExpire(); - } - else - DirectExpire(reset); + return _slot[_top]; } public static uint SplineId @@ -739,14 +754,19 @@ namespace Game.Movement get { return splineId++; } } + bool IsStatic(IMovementGenerator movement) + { + return (movement == staticIdleMovement); + } + static uint splineId = 0; public Unit _owner { get; private set; } - protected IMovementGenerator[] Impl = new IMovementGenerator[(int)MovementSlot.Max]; + protected IMovementGenerator[] _slot = new IMovementGenerator[(int)MovementSlot.Max]; MMCleanFlag _cleanFlag; - bool[] _needInit = new bool[(int)MovementSlot.Max]; + bool[] _initialize = new bool[(int)MovementSlot.Max]; int _top; - List _expList = new List(); + List _expireList = new List(); } public class JumpArrivalCastArgs diff --git a/Source/Game/Movement/WaypointManager.cs b/Source/Game/Movement/WaypointManager.cs index fe2eb7620..165c34c42 100644 --- a/Source/Game/Movement/WaypointManager.cs +++ b/Source/Game/Movement/WaypointManager.cs @@ -25,7 +25,7 @@ namespace Game { WaypointManager() { - _waypointStore = new MultiMap(); + _waypointStore = new Dictionary(); } public void Load() @@ -45,8 +45,6 @@ namespace Game do { - WaypointData wp = new WaypointData(); - uint pathId = result.Read(0); float x = result.Read(2); @@ -57,23 +55,28 @@ namespace Game GridDefines.NormalizeMapCoord(ref x); GridDefines.NormalizeMapCoord(ref y); + WaypointNode wp = new WaypointNode(); wp.id = result.Read(1); wp.x = x; wp.y = y; wp.z = z; wp.orientation = o; - wp.movetype = (WaypointMoveType)result.Read(6); - if (wp.movetype >= WaypointMoveType.Max) + wp.moveType = (WaypointMoveType)result.Read(6); + + if (wp.moveType >= WaypointMoveType.Max) { Log.outError(LogFilter.Sql, "Waypoint {0} in waypoint_data has invalid move_type, ignoring", wp.id); continue; } wp.delay = result.Read(7); - wp.event_id = result.Read(8); - wp.event_chance = result.Read(9); + wp.eventId = result.Read(8); + wp.eventChance = result.Read(9); - _waypointStore.Add(pathId, wp); + if (!_waypointStore.ContainsKey(pathId)) + _waypointStore[pathId] = new WaypointPath(); + + _waypointStore[pathId].nodes.Add(wp); ++count; } while (result.NextRow()); @@ -82,8 +85,7 @@ namespace Game public void ReloadPath(uint id) { - if (_waypointStore.ContainsKey(id)) - _waypointStore.Remove(id); + _waypointStore.Remove(id); PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.SEL_WAYPOINT_DATA_BY_ID); stmt.AddValue(0, id); @@ -94,8 +96,6 @@ namespace Game do { - WaypointData wp = new WaypointData(); - float x = result.Read(1); float y = result.Read(2); float z = result.Read(3); @@ -104,45 +104,79 @@ namespace Game GridDefines.NormalizeMapCoord(ref x); GridDefines.NormalizeMapCoord(ref y); + WaypointNode wp = new WaypointNode(); wp.id = result.Read(0); wp.x = x; wp.y = y; wp.z = z; wp.orientation = o; - wp.movetype = (WaypointMoveType)result.Read(5); + wp.moveType = (WaypointMoveType)result.Read(5); - if (wp.movetype >= WaypointMoveType.Max) + if (wp.moveType >= WaypointMoveType.Max) { Log.outError(LogFilter.Sql, "Waypoint {0} in waypoint_data has invalid move_type, ignoring", wp.id); continue; } wp.delay = result.Read(6); - wp.event_id = result.Read(7); - wp.event_chance = result.Read(8); + wp.eventId = result.Read(7); + wp.eventChance = result.Read(8); - _waypointStore.Add(id, wp); + if (!_waypointStore.ContainsKey(id)) + _waypointStore[id] = new WaypointPath(); + + _waypointStore[id].nodes.Add(wp); } while (result.NextRow()); } - public List GetPath(uint id) + public WaypointPath GetPath(uint id) { return _waypointStore.LookupByKey(id); } - MultiMap _waypointStore; + Dictionary _waypointStore; } - public struct WaypointData + public class WaypointNode { + public WaypointNode() { moveType = WaypointMoveType.Run; } + public WaypointNode(uint _id, float _x, float _y, float _z, float _orientation = 0.0f, uint _delay = 0) + { + id = _id; + x = _x; + y = _y; + z = _z; + orientation = _orientation; + delay = _delay; + eventId = 0; + moveType = WaypointMoveType.Walk; + eventChance = 100; + } + public uint id; public float x, y, z, orientation; public uint delay; - public uint event_id; - public WaypointMoveType movetype; - public byte event_chance; + public uint eventId; + public WaypointMoveType moveType; + 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