diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index e9649225cd..0801edecfd 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -879,10 +879,10 @@ void MotionMaster::MoveDistract(uint32 timer) void MotionMaster::Mutate(MovementGenerator* m, MovementSlot slot) { + bool const delayed = (_cleanFlag & MMCF_UPDATE); + while (MovementGenerator* curr = Impl[slot]) { - bool delayed = (_top == slot && (_cleanFlag & MMCF_UPDATE)); - // clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete Impl[slot] = nullptr; while (!empty() && !top()) diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 03fb5b5e8f..272a1f823f 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -157,27 +157,31 @@ void WaypointMovementGenerator::ProcessWaypointArrival(Creature* creat creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.EventId, creature, nullptr); } - creature->UpdateWaypointID(waypoint.Id); - creature->UpdateCurrentWaypointInfo(waypoint.Id, i_path->Id); + // scripts can invalidate current path, store what we need + uint32 const waypointId = waypoint.Id; + uint32 const pathId = i_path->Id; + + creature->UpdateWaypointID(waypointId); + creature->UpdateCurrentWaypointInfo(waypointId, pathId); // Inform AI if (CreatureAI* AI = creature->AI()) { - AI->MovementInform(WAYPOINT_MOTION_TYPE, waypoint.Id); - AI->WaypointReached(waypoint.Id, i_path->Id); + AI->MovementInform(WAYPOINT_MOTION_TYPE, waypointId); + AI->WaypointReached(waypointId, pathId); } if (Unit* owner = creature->GetCharmerOrOwner()) { if (UnitAI* AI = owner->GetAI()) - AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypoint.Id); + AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypointId); } else { if (TempSummon* tempSummon = creature->ToTempSummon()) if (Unit* owner2 = tempSummon->GetSummonerUnit()) if (UnitAI* AI = owner2->GetAI()) - AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypoint.Id); + AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypointId); } // Path end notifications fire after WaypointReached so that m_path_id @@ -187,11 +191,11 @@ void WaypointMovementGenerator::ProcessWaypointArrival(Creature* creat creature->UpdateCurrentWaypointInfo(0, 0); if (CreatureAI* AI = creature->AI()) - AI->PathEndReached(i_path->Id); + AI->PathEndReached(pathId); // Re-fetch AI — PathEndReached may have despawned the creature or swapped its AI if (CreatureAI* AI = creature->AI()) - AI->WaypointPathEnded(waypoint.Id, i_path->Id); + AI->WaypointPathEnded(waypointId, pathId); } // All hooks called and infos updated. Time to increment the waypoint node id @@ -385,8 +389,15 @@ bool WaypointMovementGenerator::DoUpdate(Creature* creature, uint32 di WaypointNode const& passedWp = i_path->Nodes.at(i_currentNode); UpdateHomePosition(creature, passedWp); - creature->UpdateWaypointID(passedWp.Id); - creature->UpdateCurrentWaypointInfo(passedWp.Id, i_path->Id); + + // Save data before AI callbacks — they can invalidate the reference + uint32 const wpId = passedWp.Id; + uint32 const wpPathId = i_path->Id; + uint32 const wpDelay = passedWp.Delay; + std::optional const wpOrientation = passedWp.Orientation; + + creature->UpdateWaypointID(wpId); + creature->UpdateCurrentWaypointInfo(wpId, wpPathId); if (passedWp.EventId && urand(0, 99) < passedWp.EventChance) { @@ -396,23 +407,24 @@ bool WaypointMovementGenerator::DoUpdate(Creature* creature, uint32 di if (CreatureAI* AI = creature->AI()) { - AI->MovementInform(WAYPOINT_MOTION_TYPE, passedWp.Id); - AI->WaypointReached(passedWp.Id, i_path->Id); + AI->MovementInform(WAYPOINT_MOTION_TYPE, wpId); + AI->WaypointReached(wpId, wpPathId); } // Advance node - i_currentNode = (i_currentNode + 1) % i_path->Nodes.size(); + if (i_path && !i_path->Nodes.empty()) + i_currentNode = (i_currentNode + 1) % i_path->Nodes.size(); // If this waypoint has a delay, stop the spline and pause - if (passedWp.Delay > 0) + if (wpDelay > 0) { creature->StopMoving(); creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - _waypointDelay = passedWp.Delay; + _waypointDelay = wpDelay; _waypointReached = true; _smoothSplineLaunched = false; - if (passedWp.Orientation.has_value()) - creature->SetFacingTo(*passedWp.Orientation); + if (wpOrientation.has_value()) + creature->SetFacingTo(*wpOrientation); return true; } @@ -423,15 +435,17 @@ bool WaypointMovementGenerator::DoUpdate(Creature* creature, uint32 di if (!_repeating) { // Path ended + uint32 const endWpId = i_path->Nodes.at(i_currentNode).Id; + uint32 const endPathId = i_path->Id; _done = true; _smoothSplineLaunched = false; creature->UpdateCurrentWaypointInfo(0, 0); if (CreatureAI* AI = creature->AI()) - AI->PathEndReached(i_path->Id); + AI->PathEndReached(endPathId); // Re-fetch AI — PathEndReached may have despawned the creature or swapped its AI if (CreatureAI* AI = creature->AI()) - AI->WaypointPathEnded(i_path->Nodes.at(i_currentNode).Id, i_path->Id); + AI->WaypointPathEnded(endWpId, endPathId); } else {