From 0e77439e208d5d9624b408251c63ff8c32acfa3a Mon Sep 17 00:00:00 2001 From: hondacrx Date: Thu, 4 Feb 2021 10:32:48 -0500 Subject: [PATCH] Core/Misc: waypoint movement Port From (https://github.com/TrinityCore/TrinityCore/commit/bf12bae46746149d870cd3e52ccd610f0a975047) --- Source/Game/AI/CoreAI/UnitAI.cs | 4 + Source/Game/AI/SmartScripts/SmartAI.cs | 113 +++++++++++++----- Source/Game/AI/SmartScripts/SmartScript.cs | 2 +- Source/Game/Chat/Commands/WPCommands.cs | 2 +- .../Game/Entities/Creature/Creature.Fields.cs | 6 +- Source/Game/Entities/Creature/Creature.cs | 14 ++- .../Game/Entities/Creature/CreatureGroups.cs | 2 +- Source/Game/Entities/Unit/Unit.Movement.cs | 5 +- .../Movement/Generators/WaypointMovement.cs | 27 +++-- Source/Game/Movement/MotionMaster.cs | 10 +- 10 files changed, 130 insertions(+), 55 deletions(-) diff --git a/Source/Game/AI/CoreAI/UnitAI.cs b/Source/Game/AI/CoreAI/UnitAI.cs index 5ff38646f..662f053dd 100644 --- a/Source/Game/AI/CoreAI/UnitAI.cs +++ b/Source/Game/AI/CoreAI/UnitAI.cs @@ -531,10 +531,14 @@ namespace Game.AI // Called when the dialog status between a player and the creature is requested. public virtual QuestGiverStatus GetDialogStatus(Player player) { return QuestGiverStatus.ScriptedDefault; } + public virtual void WaypointPathStarted(uint nodeId, uint pathId) { } + public virtual void WaypointStarted(uint nodeId, uint pathId) { } public virtual void WaypointReached(uint nodeId, uint pathId) { } + public virtual void WaypointPathEnded(uint nodeId, uint pathId) { } + public AISpellInfoType GetAISpellInfo(uint spellId, Difficulty difficulty) { return AISpellInfo.LookupByKey((spellId, difficulty)); diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index b26ffcdaf..51819dd3e 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -133,6 +133,17 @@ namespace Game.AI public void PausePath(uint delay, bool forced) { + if (!HasEscortState(SmartEscortState.Escorting)) + { + me.PauseMovement(delay, MovementSlot.Idle, forced); + if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint) + { + var waypointInfo = me.GetCurrentWaypointInfo(); + GetScript().ProcessEventsFor(SmartEvents.WaypointPaused, null, waypointInfo.nodeId, waypointInfo.pathId); + } + return; + } + if (HasEscortState(SmartEscortState.Paused)) { Log.outError(LogFilter.Server, $"SmartAI.PausePath: Creature entry {me.GetEntry()} wanted to pause waypoint movement while already paused, ignoring."); @@ -157,6 +168,30 @@ namespace Game.AI public void StopPath(uint despawnTime = 0, uint quest = 0, bool fail = false) { + if (!HasEscortState(SmartEscortState.Escorting)) + { + (uint nodeId, uint pathId) waypointInfo = new (); + if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Waypoint) + waypointInfo = me.GetCurrentWaypointInfo(); + + if (mDespawnState != 2) + SetDespawnTime(despawnTime); + + me.GetMotionMaster().MoveIdle(); + + if (waypointInfo.Item1 != 0) + GetScript().ProcessEventsFor(SmartEvents.WaypointStopped, null, waypointInfo.Item1, waypointInfo.Item2); + + if (!fail) + { + if (waypointInfo.Item1 != 0) + GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, waypointInfo.Item1, waypointInfo.Item2); + if (mDespawnState == 1) + StartDespawn(); + } + return; + } + if (quest != 0) mEscortQuestID = quest; @@ -269,6 +304,9 @@ namespace Game.AI void UpdatePath(uint diff) { + if (!HasEscortState(SmartEscortState.Escorting)) + return; + if (_escortInvokerCheckTimer < diff) { if (!IsEscortInvokerInRange()) @@ -391,6 +429,15 @@ namespace Game.AI return true; } + public override void WaypointPathStarted(uint nodeId, uint pathId) + { + if (!HasEscortState(SmartEscortState.Escorting)) + { + GetScript().ProcessEventsFor(SmartEvents.WaypointStart, null, nodeId, pathId); + return; + } + } + public override void WaypointStarted(uint nodeId, uint pathId) { @@ -398,6 +445,12 @@ namespace Game.AI public override void WaypointReached(uint nodeId, uint pathId) { + if (!HasEscortState(SmartEscortState.Escorting)) + { + GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, nodeId, pathId); + return; + } + _currentWaypointNode = nodeId; GetScript().ProcessEventsFor(SmartEvents.WaypointReached, null, _currentWaypointNode, pathId); @@ -417,6 +470,15 @@ namespace Game.AI } } + public override void WaypointPathEnded(uint nodeId, uint pathId) + { + if (!HasEscortState(SmartEscortState.Escorting)) + { + GetScript().ProcessEventsFor(SmartEvents.WaypointEnded, null, nodeId, pathId); + return; + } + } + public override void MovementInform(MovementGeneratorType movementType, uint id) { if (movementType == MovementGeneratorType.Point && id == EventId.SmartEscortLastOCCPoint) @@ -450,10 +512,17 @@ namespace Game.AI me.AddUnitState(UnitState.Evade); - GetScript().ProcessEventsFor(SmartEvents.Evade);//must be after aura clear so we can cast spells from db + GetScript().ProcessEventsFor(SmartEvents.Evade); // must be after _EnterEvadeMode (spells, auras, ...) SetRun(mRun); - if (HasEscortState(SmartEscortState.Escorting)) + + Unit owner = me.GetCharmerOrOwner(); + if (owner != null) + { + me.GetMotionMaster().MoveFollow(owner, SharedConst.PetFollowDist, SharedConst.PetFollowAngle); + me.ClearUnitState(UnitState.Evade); + } + else if (HasEscortState(SmartEscortState.Escorting)) { AddEscortState(SmartEscortState.Returning); ReturnToLastOOCPos(); @@ -461,19 +530,12 @@ namespace Game.AI else { Unit target = !mFollowGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, mFollowGuid) : null; - Unit owner = me.GetCharmerOrOwner(); - 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(); } @@ -552,12 +614,15 @@ namespace Game.AI mRespawnTime = 0; mDespawnState = 0; _escortState = SmartEscortState.None; + me.SetVisible(true); + if (me.GetFaction() != me.GetCreatureTemplate().Faction) me.RestoreFaction(); - mJustReset = true; - JustReachedHome(); + + GetScript().OnReset(); GetScript().ProcessEventsFor(SmartEvents.Respawn); + mFollowGuid.Clear();//do not reset follower on Reset(), we need it after combat evade mFollowDist = 0; mFollowAngle = 0; @@ -570,23 +635,18 @@ namespace Game.AI public override void JustReachedHome() { GetScript().OnReset(); - if (!mJustReset) + GetScript().ProcessEventsFor(SmartEvents.ReachedHome); + + CreatureGroup formation = me.GetFormation(); + if (formation == null || formation.GetLeader() == me || !formation.IsFormed()) { - GetScript().ProcessEventsFor(SmartEvents.ReachedHome); - - CreatureGroup formation = me.GetFormation(); - if (formation == null || formation.GetLeader() == me || !formation.IsFormed()) - { - if (me.GetMotionMaster().GetCurrentMovementGeneratorType() == MovementGeneratorType.Idle && me.GetWaypointPath() != 0) - me.GetMotionMaster().MovePath(me.GetWaypointPath(), true); - else - me.ResumeMovement(); - } - else if (formation.IsFormed()) - me.GetMotionMaster().MoveIdle(); // wait the order of leader + if (me.GetMotionMaster().GetMotionSlotType(MovementSlot.Idle) != MovementGeneratorType.Waypoint && me.GetWaypointPath() != 0) + me.GetMotionMaster().MovePath(me.GetWaypointPath(), true); + else + me.ResumeMovement(); } - - mJustReset = false; + else if (formation.IsFormed()) + me.GetMotionMaster().MoveIdle(); // wait the order of leader } public override void EnterCombat(Unit victim) @@ -993,7 +1053,6 @@ namespace Game.AI uint mDespawnTime; uint mRespawnTime; uint mDespawnState; - bool mJustReset; // Vehicle conditions bool mHasConditions; diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 36f1783c3..6c15912ad 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -3189,7 +3189,7 @@ namespace Game.AI case SmartEvents.WaypointStopped: case SmartEvents.WaypointEnded: { - if (me == null || (e.Event.waypoint.pointID != 0 && var0 != e.Event.waypoint.pointID) || (e.Event.waypoint.pathID != 0 && GetPathId() != e.Event.waypoint.pathID)) + if (me == null || (e.Event.waypoint.pointID != 0 && var0 != e.Event.waypoint.pointID) || (e.Event.waypoint.pathID != 0 && var1 != e.Event.waypoint.pathID)) return; ProcessAction(e, unit); break; diff --git a/Source/Game/Chat/Commands/WPCommands.cs b/Source/Game/Chat/Commands/WPCommands.cs index 182b71f88..cee99691b 100644 --- a/Source/Game/Chat/Commands/WPCommands.cs +++ b/Source/Game/Chat/Commands/WPCommands.cs @@ -998,7 +998,7 @@ namespace Game.Chat.Commands stmt.AddValue(0, guidLow); DB.World.Execute(stmt); - target.UpdateWaypointID(0); + target.UpdateCurrentWaypointInfo(0, 0); stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_MOVEMENT_TYPE); stmt.AddValue(0, (byte)MovementGeneratorType.Idle); diff --git a/Source/Game/Entities/Creature/Creature.Fields.cs b/Source/Game/Entities/Creature/Creature.Fields.cs index b0b0af4ae..9e7e37ecf 100644 --- a/Source/Game/Entities/Creature/Creature.Fields.cs +++ b/Source/Game/Entities/Creature/Creature.Fields.cs @@ -67,9 +67,9 @@ namespace Game.Entities LootModes m_LootMode; // Bitmask (default: LOOT_MODE_DEFAULT) that determines what loot will be lootable - //WaypointMovementGenerator vars - uint m_waypointID; - uint m_path_id; + // Waypoint path + uint _waypointPathId; + (uint nodeId, uint pathId) _currentWaypointNodeInfo; //Formation var CreatureGroup m_formation; diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index f0a6f6f82..53f5151fc 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -53,6 +53,8 @@ namespace Game.Entities ResetLootMode(); // restore default loot mode m_homePosition = new WorldLocation(); + + _currentWaypointNodeInfo = new(); } public override void Dispose() @@ -2334,7 +2336,7 @@ namespace Game.Entities //Load Path if (cainfo.path_id != 0) - m_path_id = cainfo.path_id; + _waypointPathId = cainfo.path_id; if (cainfo.auras != null) { @@ -3200,12 +3202,12 @@ namespace Game.Entities public void GetTransportHomePosition(out float x, out float y, out float z, out float ori) { m_transportHomePosition.GetPosition(out x, out y, out z, out ori); } public Position GetTransportHomePosition() { return m_transportHomePosition; } - public uint GetWaypointPath() { return m_path_id; } - public void LoadPath(uint pathid) { m_path_id = pathid; } - - public uint GetCurrentWaypointID() { return m_waypointID; } - public void UpdateWaypointID(uint wpID) { m_waypointID = wpID; } + public uint GetWaypointPath() { return _waypointPathId; } + public void LoadPath(uint pathid) { _waypointPathId = pathid; } + public (uint nodeId, uint pathId) GetCurrentWaypointInfo() { return _currentWaypointNodeInfo; } + public void UpdateCurrentWaypointInfo(uint nodeId, uint pathId) { _currentWaypointNodeInfo = (nodeId, pathId); } + public CreatureGroup GetFormation() { return m_formation; } public void SetFormation(CreatureGroup formation) { m_formation = formation; } diff --git a/Source/Game/Entities/Creature/CreatureGroups.cs b/Source/Game/Entities/Creature/CreatureGroups.cs index ddee4e0f5..4b9ec8279 100644 --- a/Source/Game/Entities/Creature/CreatureGroups.cs +++ b/Source/Game/Entities/Creature/CreatureGroups.cs @@ -235,7 +235,7 @@ namespace Game.Entities continue; if (pair.Value.point_1 != 0) - if (m_leader.GetCurrentWaypointID() == pair.Value.point_1 - 1 || m_leader.GetCurrentWaypointID() == pair.Value.point_2 - 1) + if (m_leader.GetCurrentWaypointInfo().nodeId == pair.Value.point_1 - 1 || m_leader.GetCurrentWaypointInfo().nodeId == pair.Value.point_2 - 1) pair.Value.follow_angle = (float)Math.PI * 2 - pair.Value.follow_angle; float angle = pair.Value.follow_angle; diff --git a/Source/Game/Entities/Unit/Unit.Movement.cs b/Source/Game/Entities/Unit/Unit.Movement.cs index aa7c3aaeb..8189c374d 100644 --- a/Source/Game/Entities/Unit/Unit.Movement.cs +++ b/Source/Game/Entities/Unit/Unit.Movement.cs @@ -159,7 +159,7 @@ namespace Game.Entities init.Stop(); } - public void PauseMovement(uint timer = 0, MovementSlot slot = 0) + public void PauseMovement(uint timer = 0, MovementSlot slot = 0, bool forced = true) { if (slot >= MovementSlot.Max) return; @@ -168,7 +168,8 @@ namespace Game.Entities if (movementGenerator != null) movementGenerator.Pause(timer); - StopMoving(); + if (forced) + StopMoving(); } public void ResumeMovement(uint timer = 0, MovementSlot slot = 0) diff --git a/Source/Game/Movement/Generators/WaypointMovement.cs b/Source/Game/Movement/Generators/WaypointMovement.cs index 9ab055496..0438a085f 100644 --- a/Source/Game/Movement/Generators/WaypointMovement.cs +++ b/Source/Game/Movement/Generators/WaypointMovement.cs @@ -61,6 +61,10 @@ namespace Game.Movement } _nextMoveTime.Reset(1000); + + // inform AI + if (creature.GetAI() != null) + creature.GetAI().WaypointPathStarted(1, _path.id); } public override void DoInitialize(Creature creature) @@ -92,6 +96,7 @@ namespace Game.Movement if (_path == null || _path.nodes.Empty()) return; + Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.OnArrived: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); WaypointNode waypoint = _path.nodes.ElementAt((int)_currentNode); if (waypoint.delay != 0) { @@ -110,12 +115,10 @@ namespace Game.Movement if (creature.IsAIEnabled) { creature.GetAI().MovementInform(MovementGeneratorType.Waypoint, (uint)_currentNode); - - Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.OnArrived: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); - creature.GetAI().WaypointReached(_path.nodes[_currentNode].id, _path.id); + creature.GetAI().WaypointReached(waypoint.id, _path.id); } - creature.UpdateWaypointID((uint)_currentNode); + creature.UpdateCurrentWaypointInfo(waypoint.id, _path.id); } bool StartMove(Creature creature) @@ -137,10 +140,10 @@ namespace Game.Movement if (_isArrivalDone) { + Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); + WaypointNode lastWaypoint = _path.nodes.ElementAt(_currentNode); if ((_currentNode == _path.nodes.Count - 1) && !_repeating) // If that's our last waypoint { - WaypointNode lastWaypoint = _path.nodes.ElementAt(_currentNode); - float x = lastWaypoint.x; float y = lastWaypoint.y; float z = lastWaypoint.z; @@ -163,6 +166,11 @@ namespace Game.Movement // else if (vehicle) - this should never happen, vehicle offsets are const } _done = true; + creature.UpdateCurrentWaypointInfo(0, 0); + + // inform AI + if (creature.IsAIEnabled) + creature.GetAI().WaypointPathEnded(lastWaypoint.id, _path.id); return true; } @@ -170,12 +178,10 @@ namespace Game.Movement // inform AI if (creature.IsAIEnabled) - { - Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); - creature.GetAI().WaypointStarted(_path.nodes[(int)_currentNode].id, _path.id); - } + creature.GetAI().WaypointStarted(lastWaypoint.id, _path.id); } + Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.StartMove: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); WaypointNode waypoint = _path.nodes.ElementAt(_currentNode); Position formationDest = new Position(waypoint.x, waypoint.y, waypoint.z, (waypoint.orientation != 0 && waypoint.delay != 0) ? waypoint.orientation : 0.0f); @@ -288,6 +294,7 @@ namespace Game.Movement if (_path == null || _path.nodes.Empty()) return false; + Cypher.Assert(_currentNode < _path.nodes.Count, $"WaypointMovementGenerator.GetResetPos: tried to reference a node id ({_currentNode}) which is not included in path ({_path.id})"); WaypointNode waypoint = _path.nodes.ElementAt(_currentNode); x = waypoint.x; diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index 72d3a3f5e..05e48b8a3 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -137,15 +137,17 @@ namespace Game.Movement public MovementGeneratorType GetMotionSlotType(MovementSlot slot) { - if (_slot[(int)slot] == null) + if (Empty() || slot >= MovementSlot.Max || _slot[(int)slot] == null) return MovementGeneratorType.Max; - else - return _slot[(int)slot].GetMovementGeneratorType(); + + return _slot[(int)slot].GetMovementGeneratorType(); } public IMovementGenerator GetMotionSlot(MovementSlot slot) { - Cypher.Assert((int)slot >= 0); + if (Empty() || slot >= MovementSlot.Max || _slot[(int)slot] == null) + return null; + return _slot[(int)slot]; }