aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Movement
diff options
context:
space:
mode:
authorRat <none@none>2010-06-05 23:40:08 +0200
committerRat <none@none>2010-06-05 23:40:08 +0200
commit75b80d9f5b02a643c983b2fb1ededed79fd5d133 (patch)
treeebd1c2cc12a2715909dd04c1ed147a260c6ceb14 /src/server/game/Movement
parent6a9357b13d7ea6bd7d77dbfc6587af9028caa401 (diff)
rearranged core files
--HG-- branch : trunk
Diffstat (limited to 'src/server/game/Movement')
-rw-r--r--src/server/game/Movement/DestinationHolder.cpp22
-rw-r--r--src/server/game/Movement/DestinationHolder.h67
-rw-r--r--src/server/game/Movement/DestinationHolderImp.h217
-rw-r--r--src/server/game/Movement/FollowerRefManager.h34
-rw-r--r--src/server/game/Movement/FollowerReference.cpp39
-rw-r--r--src/server/game/Movement/FollowerReference.h37
-rw-r--r--src/server/game/Movement/MotionMaster.cpp591
-rw-r--r--src/server/game/Movement/MotionMaster.h189
-rw-r--r--src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp181
-rw-r--r--src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h59
-rw-r--r--src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp444
-rw-r--r--src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h83
-rw-r--r--src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp92
-rw-r--r--src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h60
-rw-r--r--src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp114
-rw-r--r--src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h82
-rw-r--r--src/server/game/Movement/MovementGenerators/MovementGenerator.cpp27
-rw-r--r--src/server/game/Movement/MovementGenerators/MovementGenerator.h102
-rw-r--r--src/server/game/Movement/MovementGenerators/MovementGeneratorImpl.h34
-rw-r--r--src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp106
-rw-r--r--src/server/game/Movement/MovementGenerators/PointMovementGenerator.h68
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp205
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h56
-rw-r--r--src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp280
-rw-r--r--src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h76
-rw-r--r--src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp682
-rw-r--r--src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h129
-rw-r--r--src/server/game/Movement/MovementHandler.cpp746
-rw-r--r--src/server/game/Movement/Path.h88
-rw-r--r--src/server/game/Movement/TaxiHandler.cpp287
-rw-r--r--src/server/game/Movement/Transports.cpp589
-rw-r--r--src/server/game/Movement/Transports.h108
-rw-r--r--src/server/game/Movement/Traveller.h155
-rw-r--r--src/server/game/Movement/WaypointManager.cpp152
-rw-r--r--src/server/game/Movement/WaypointManager.h68
35 files changed, 6269 insertions, 0 deletions
diff --git a/src/server/game/Movement/DestinationHolder.cpp b/src/server/game/Movement/DestinationHolder.cpp
new file mode 100644
index 00000000000..905e9933b37
--- /dev/null
+++ b/src/server/game/Movement/DestinationHolder.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "DestinationHolder.h"
+
diff --git a/src/server/game/Movement/DestinationHolder.h b/src/server/game/Movement/DestinationHolder.h
new file mode 100644
index 00000000000..e09a153615c
--- /dev/null
+++ b/src/server/game/Movement/DestinationHolder.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_DESTINATION_HOLDER_H
+#define TRINITY_DESTINATION_HOLDER_H
+
+#include "Platform/Define.h"
+#include "Timer.h"
+
+class WorldObject;
+class Map;
+
+#define TRAVELLER_UPDATE_INTERVAL 300
+
+template<typename TRAVELLER>
+class DestinationHolder
+{
+ TimeTrackerSmall i_tracker;
+ uint32 i_totalTravelTime;
+ uint32 i_timeElapsed;
+ bool i_destSet;
+ float i_fromX, i_fromY, i_fromZ;
+ float i_destX, i_destY, i_destZ;
+
+ public:
+ DestinationHolder() : i_tracker(TRAVELLER_UPDATE_INTERVAL), i_totalTravelTime(0), i_timeElapsed(0),
+ i_destSet(false), i_fromX(0), i_fromY(0), i_fromZ(0), i_destX(0), i_destY(0), i_destZ(0) {}
+
+ uint32 SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove = true);
+ void GetDestination(float &x, float &y, float &z) const { x = i_destX; y = i_destY; z = i_destZ; }
+ bool UpdateExpired(void) const { return i_tracker.Passed(); }
+ void ResetUpdate(uint32 t = TRAVELLER_UPDATE_INTERVAL) { i_tracker.Reset(t); }
+ uint32 GetTotalTravelTime(void) const { return i_totalTravelTime; }
+ void IncreaseTravelTime(uint32 increment) { i_totalTravelTime += increment; }
+ void ResetTravelTime() { i_totalTravelTime = 0; }
+ bool HasDestination(void) const { return i_destSet; }
+ float GetDestinationDiff(float x, float y, float z) const;
+ bool HasArrived(void) const { return (i_totalTravelTime == 0 || i_timeElapsed >= i_totalTravelTime); }
+ bool UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool micro_movement=false);
+ uint32 StartTravel(TRAVELLER &traveller, bool sendMove = true);
+ void GetLocationNow(const Map * map, float &x, float &y, float &z, bool is3D = false) const;
+ void GetLocationNowNoMicroMovement(float &x, float &y, float &z) const; // For use without micro movement
+ float GetDistance3dFromDestSq(const WorldObject &obj) const;
+
+ private:
+ void _findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y);
+
+};
+#endif
+
diff --git a/src/server/game/Movement/DestinationHolderImp.h b/src/server/game/Movement/DestinationHolderImp.h
new file mode 100644
index 00000000000..95ff90a6a2a
--- /dev/null
+++ b/src/server/game/Movement/DestinationHolderImp.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_DESTINATIONHOLDERIMP_H
+#define TRINITY_DESTINATIONHOLDERIMP_H
+
+#include "MapManager.h"
+#include "DestinationHolder.h"
+
+#include <cmath>
+
+template<typename TRAVELLER>
+void
+DestinationHolder<TRAVELLER>::_findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y)
+{
+ /* given the point (x1, y1) and (x2, y2).. need to find the point (x,y) on the same line
+ * such that the distance from (x, y) to (x2, y2) is offset.
+ * Let the distance of p1 to p2 = d.. then the ratio of offset/d = (x2-x)/(x2-x1)
+ * hence x = x2 - (offset/d)*(x2-x1)
+ * like wise offset/d = (y2-y)/(y2-y1);
+ */
+ if (offset == 0)
+ {
+ x = x2;
+ y = y2;
+ }
+ else
+ {
+ double x_diff = double(x2 - x1);
+ double y_diff = double(y2 - y1);
+ double distance_d = (double)((x_diff*x_diff) + (y_diff * y_diff));
+ if (distance_d == 0)
+ {
+ x = x2;
+ y = y2;
+ }
+ else
+ {
+ distance_d = ::sqrt(distance_d); // starting distance
+ double distance_ratio = (double)(distance_d - offset)/(double)distance_d;
+ // line above has revised formula which is more correct, I think
+ x = (float)(x1 + (distance_ratio*x_diff));
+ y = (float)(y1 + (distance_ratio*y_diff));
+ }
+ }
+}
+
+template<typename TRAVELLER>
+uint32
+DestinationHolder<TRAVELLER>::SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove)
+{
+ i_destSet = true;
+ i_destX = dest_x;
+ i_destY = dest_y;
+ i_destZ = dest_z;
+
+ return StartTravel(traveller, sendMove);
+}
+
+template<typename TRAVELLER>
+uint32
+DestinationHolder<TRAVELLER>::StartTravel(TRAVELLER &traveller, bool sendMove)
+{
+ if (!i_destSet) return 0;
+
+ i_fromX = traveller.GetPositionX();
+ i_fromY = traveller.GetPositionY();
+ i_fromZ = traveller.GetPositionZ();
+
+ i_totalTravelTime = traveller.GetTotalTrevelTimeTo(i_destX,i_destY,i_destZ);
+ i_timeElapsed = 0;
+ if (sendMove)
+ traveller.MoveTo(i_destX, i_destY, i_destZ, i_totalTravelTime);
+ return i_totalTravelTime;
+}
+
+template<typename TRAVELLER>
+bool
+DestinationHolder<TRAVELLER>::UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool micro_movement)
+{
+ i_timeElapsed += diff;
+
+ // Update every TRAVELLER_UPDATE_INTERVAL
+ i_tracker.Update(diff);
+ if (!i_tracker.Passed())
+ return false;
+ else
+ ResetUpdate();
+
+ if (!i_destSet) return true;
+
+ float x, y, z;
+ if (!micro_movement)
+ GetLocationNowNoMicroMovement(x, y, z);
+ else
+ {
+ if (!traveller.GetTraveller().hasUnitState(UNIT_STAT_MOVING | UNIT_STAT_IN_FLIGHT))
+ return true;
+
+ if (traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT))
+ GetLocationNow(traveller.GetTraveller().GetBaseMap() ,x, y, z, true); // Should reposition Object with right Coord, so I can bypass some Grid Relocation
+ else
+ GetLocationNow(traveller.GetTraveller().GetBaseMap(), x, y, z, false);
+
+ // Change movement computation to micro movement based on last tick coords, this makes system work
+ // even on multiple floors zones without hugh vmaps usage ;)
+
+ // Take care of underrun of uint32
+ if (i_totalTravelTime >= i_timeElapsed)
+ i_totalTravelTime -= i_timeElapsed; // Consider only the remaining part
+ else
+ i_totalTravelTime = 0;
+
+ i_timeElapsed = 0;
+ i_fromX = x; // and change origine
+ i_fromY = y; // then I take into account only micro movement
+ i_fromZ = z;
+ }
+
+ if (traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y || traveller.GetTraveller().GetPositionZ() != z)
+ {
+ float ori = traveller.GetTraveller().GetAngle(x, y);
+ traveller.Relocation(x, y, z, ori);
+ }
+
+ return true;
+}
+
+template<typename TRAVELLER>
+void
+DestinationHolder<TRAVELLER>::GetLocationNow(const Map * map, float &x, float &y, float &z, bool is3D) const
+{
+ if (HasArrived())
+ {
+ x = i_destX;
+ y = i_destY;
+ z = i_destZ;
+ }
+ else if (HasDestination())
+ {
+ double percent_passed = (double)i_timeElapsed / (double)i_totalTravelTime;
+ const float distanceX = ((i_destX - i_fromX) * percent_passed);
+ const float distanceY = ((i_destY - i_fromY) * percent_passed);
+ const float distanceZ = ((i_destZ - i_fromZ) * percent_passed);
+ x = i_fromX + distanceX;
+ y = i_fromY + distanceY;
+ float z2 = i_fromZ + distanceZ;
+ // All that is not finished but previous code neither... Traveller need be able to swim.
+ if (is3D)
+ z = z2;
+ else
+ {
+ //That part is good for mob Walking on the floor. But the floor is not always what we thought.
+ z = map->GetHeight(x,y,i_fromZ,false); // Disable cave check
+ const float groundDist = sqrt(distanceX*distanceX + distanceY*distanceY);
+ const float zDist = fabs(i_fromZ - z) + 0.000001f;
+ const float slope = groundDist / zDist;
+ if (slope < 1.0f) // This prevents the ground returned by GetHeight to be used when in cave
+ z = z2; // a climb or jump of more than 45 is denied
+ }
+ }
+}
+
+template<typename TRAVELLER>
+float
+DestinationHolder<TRAVELLER>::GetDistance3dFromDestSq(const WorldObject &obj) const
+{
+ float x,y,z;
+ obj.GetPosition(x,y,z);
+ return (i_destX-x)*(i_destX-x)+(i_destY-y)*(i_destY-y)+(i_destZ-z)*(i_destZ-z);
+}
+
+template<typename TRAVELLER>
+float
+DestinationHolder<TRAVELLER>::GetDestinationDiff(float x, float y, float z) const
+{
+ return sqrt(((x-i_destX)*(x-i_destX)) + ((y-i_destY)*(y-i_destY)) + ((z-i_destZ)*(z-i_destZ)));
+}
+
+template<typename TRAVELLER>
+void
+DestinationHolder<TRAVELLER>::GetLocationNowNoMicroMovement(float &x, float &y, float &z) const
+{
+ if (HasArrived())
+ {
+ x = i_destX;
+ y = i_destY;
+ z = i_destZ;
+ }
+ else
+ {
+ double percent_passed = (double)i_timeElapsed / (double)i_totalTravelTime;
+ x = i_fromX + ((i_destX - i_fromX) * percent_passed);
+ y = i_fromY + ((i_destY - i_fromY) * percent_passed);
+ z = i_fromZ + ((i_destZ - i_fromZ) * percent_passed);
+ }
+}
+
+#endif
+
diff --git a/src/server/game/Movement/FollowerRefManager.h b/src/server/game/Movement/FollowerRefManager.h
new file mode 100644
index 00000000000..c2068b36f81
--- /dev/null
+++ b/src/server/game/Movement/FollowerRefManager.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FOLLOWERREFMANAGER
+#define _FOLLOWERREFMANAGER
+
+#include "Utilities/LinkedReference/RefManager.h"
+
+class Unit;
+class TargetedMovementGeneratorBase;
+
+class FollowerRefManager : public RefManager<Unit, TargetedMovementGeneratorBase>
+{
+
+};
+#endif
+
diff --git a/src/server/game/Movement/FollowerReference.cpp b/src/server/game/Movement/FollowerReference.cpp
new file mode 100644
index 00000000000..90d1c1d2e82
--- /dev/null
+++ b/src/server/game/Movement/FollowerReference.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Unit.h"
+#include "TargetedMovementGenerator.h"
+#include "FollowerReference.h"
+
+void FollowerReference::targetObjectBuildLink()
+{
+ getTarget()->addFollower(this);
+}
+
+void FollowerReference::targetObjectDestroyLink()
+{
+ getTarget()->removeFollower(this);
+}
+
+void FollowerReference::sourceObjectDestroyLink()
+{
+ getSource()->stopFollowing();
+}
+
diff --git a/src/server/game/Movement/FollowerReference.h b/src/server/game/Movement/FollowerReference.h
new file mode 100644
index 00000000000..e468f79f017
--- /dev/null
+++ b/src/server/game/Movement/FollowerReference.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FOLLOWERREFERENCE_H
+#define _FOLLOWERREFERENCE_H
+
+#include "Utilities/LinkedReference/Reference.h"
+
+class TargetedMovementGeneratorBase;
+class Unit;
+
+class FollowerReference : public Reference<Unit, TargetedMovementGeneratorBase>
+{
+ protected:
+ void targetObjectBuildLink();
+ void targetObjectDestroyLink();
+ void sourceObjectDestroyLink();
+};
+#endif
+
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
new file mode 100644
index 00000000000..5c20494bfcc
--- /dev/null
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MotionMaster.h"
+#include "CreatureAISelector.h"
+#include "Creature.h"
+#include "Traveller.h"
+
+#include "ConfusedMovementGenerator.h"
+#include "FleeingMovementGenerator.h"
+#include "HomeMovementGenerator.h"
+#include "IdleMovementGenerator.h"
+#include "PointMovementGenerator.h"
+#include "TargetedMovementGenerator.h"
+#include "WaypointMovementGenerator.h"
+#include "RandomMovementGenerator.h"
+
+#include <cassert>
+
+inline bool isStatic(MovementGenerator *mv)
+{
+ return (mv == &si_idleMovement);
+}
+
+void
+MotionMaster::Initialize()
+{
+ // clear ALL movement generators (including default)
+ while (!empty())
+ {
+ MovementGenerator *curr = top();
+ pop();
+ if (curr) DirectDelete(curr);
+ }
+
+ InitDefault();
+}
+
+// set new default movement generator
+void MotionMaster::InitDefault()
+{
+ if (i_owner->GetTypeId() == TYPEID_UNIT)
+ {
+ MovementGenerator* movement = FactorySelector::selectMovementGenerator(i_owner->ToCreature());
+ Mutate(movement == NULL ? &si_idleMovement : movement, MOTION_SLOT_IDLE);
+ }
+ else
+ {
+ Mutate(&si_idleMovement, MOTION_SLOT_IDLE);
+ }
+}
+
+MotionMaster::~MotionMaster()
+{
+ // clear ALL movement generators (including default)
+ while (!empty())
+ {
+ MovementGenerator *curr = top();
+ pop();
+ if (curr) DirectDelete(curr);
+ }
+}
+
+void
+MotionMaster::UpdateMotion(uint32 diff)
+{
+ if (i_owner->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED)) // what about UNIT_STAT_DISTRACTED? Why is this not included?
+ return;
+ assert(!empty());
+ m_cleanFlag |= MMCF_UPDATE;
+ if (!top()->Update(*i_owner, diff))
+ {
+ m_cleanFlag &= ~MMCF_UPDATE;
+ MovementExpired();
+ }
+ else
+ m_cleanFlag &= ~MMCF_UPDATE;
+
+ if (m_expList)
+ {
+ for (size_t i = 0; i < m_expList->size(); ++i)
+ {
+ MovementGenerator* mg = (*m_expList)[i];
+ DirectDelete(mg);
+ }
+
+ delete m_expList;
+ m_expList = NULL;
+
+ if (empty())
+ Initialize();
+ else if (needInitTop())
+ InitTop();
+ else if (m_cleanFlag & MMCF_RESET)
+ top()->Reset(*i_owner);
+
+ m_cleanFlag &= ~MMCF_RESET;
+ }
+}
+
+void
+MotionMaster::DirectClean(bool reset)
+{
+ while (size() > 1)
+ {
+ MovementGenerator *curr = top();
+ pop();
+ if (curr) DirectDelete(curr);
+ }
+
+ if (needInitTop())
+ InitTop();
+ else if (reset)
+ top()->Reset(*i_owner);
+}
+
+void
+MotionMaster::DelayedClean()
+{
+ while (size() > 1)
+ {
+ MovementGenerator *curr = top();
+ pop();
+ if (curr)
+ DelayedDelete(curr);
+ }
+}
+
+void
+MotionMaster::DirectExpire(bool reset)
+{
+ if (size() > 1)
+ {
+ MovementGenerator *curr = top();
+ pop();
+ DirectDelete(curr);
+ }
+
+ while (!top())
+ --i_top;
+
+ if (empty())
+ Initialize();
+ else if (needInitTop())
+ InitTop();
+ else if (reset)
+ top()->Reset(*i_owner);
+}
+
+void
+MotionMaster::DelayedExpire()
+{
+ if (size() > 1)
+ {
+ MovementGenerator *curr = top();
+ pop();
+ DelayedDelete(curr);
+ }
+
+ while (!top())
+ --i_top;
+}
+
+void MotionMaster::MoveIdle(MovementSlot slot)
+{
+ //if (empty() || !isStatic(top()))
+ // push(&si_idleMovement);
+ if (!isStatic(Impl[slot]))
+ Mutate(&si_idleMovement, slot);
+}
+
+void
+MotionMaster::MoveRandom(float spawndist)
+{
+ if (i_owner->GetTypeId() == TYPEID_UNIT)
+ {
+ DEBUG_LOG("Creature (GUID: %u) start moving random", i_owner->GetGUIDLow());
+ Mutate(new RandomMovementGenerator<Creature>(spawndist), MOTION_SLOT_IDLE);
+ }
+}
+
+void
+MotionMaster::MoveTargetedHome()
+{
+ //if (i_owner->hasUnitState(UNIT_STAT_FLEEING))
+ // return;
+
+ Clear(false);
+
+ if (i_owner->GetTypeId() == TYPEID_UNIT)
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted home", i_owner->GetEntry(), i_owner->GetGUIDLow());
+ Mutate(new HomeMovementGenerator<Creature>(), MOTION_SLOT_ACTIVE);
+ }
+ else
+ {
+ sLog.outError("Player (GUID: %u) attempt targeted home", i_owner->GetGUIDLow());
+ }
+}
+
+void
+MotionMaster::MoveConfused()
+{
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) move confused", i_owner->GetGUIDLow());
+ Mutate(new ConfusedMovementGenerator<Player>(), MOTION_SLOT_CONTROLLED);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) move confused",
+ i_owner->GetEntry(), i_owner->GetGUIDLow());
+ Mutate(new ConfusedMovementGenerator<Creature>(), MOTION_SLOT_CONTROLLED);
+ }
+}
+
+void
+MotionMaster::MoveChase(Unit* target, float dist, float angle)
+{
+ // ignore movement request if target not exist
+ if (!target || target == i_owner || i_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
+ return;
+
+ i_owner->clearUnitState(UNIT_STAT_FOLLOW);
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) chase to %s (GUID: %u)",
+ i_owner->GetGUIDLow(),
+ target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
+ Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle), MOTION_SLOT_ACTIVE);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) chase to %s (GUID: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
+ Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle), MOTION_SLOT_ACTIVE);
+ }
+}
+
+void
+MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot)
+{
+ // ignore movement request if target not exist
+ if (!target || target == i_owner || i_owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
+ return;
+
+ i_owner->addUnitState(UNIT_STAT_FOLLOW);
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) follow to %s (GUID: %u)", i_owner->GetGUIDLow(),
+ target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
+ Mutate(new TargetedMovementGenerator<Player>(*target,dist,angle), slot);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) follow to %s (GUID: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
+ target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
+ Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle), slot);
+ }
+}
+
+void
+MotionMaster::MovePoint(uint32 id, float x, float y, float z)
+{
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", i_owner->GetGUIDLow(), id, x, y, z);
+ Mutate(new PointMovementGenerator<Player>(id,x,y,z), MOTION_SLOT_ACTIVE);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), id, x, y, z);
+ Mutate(new PointMovementGenerator<Creature>(id,x,y,z), MOTION_SLOT_ACTIVE);
+ }
+}
+
+void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ)
+{
+ //this function may make players fall below map
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ float x, y, z;
+ float dist = speedXY * speedZ * 0.1f;
+ i_owner->GetNearPoint(i_owner, x, y, z, i_owner->GetObjectSize(), dist, i_owner->GetAngle(srcX, srcY) + M_PI);
+ MoveJump(x, y, z, speedXY, speedZ);
+}
+
+void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ)
+{
+ //this function may make players fall below map
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ float x, y, z;
+ float dist = speedXY * speedZ * 0.1f;
+ i_owner->GetClosePoint(x, y, z, i_owner->GetObjectSize(), dist, angle);
+ MoveJump(x, y, z, speedXY, speedZ);
+}
+
+void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float speedZ)
+{
+ uint32 moveFlag = MOVEFLAG_JUMP | MOVEFLAG_WALK;
+ uint32 time = speedZ * 100;
+
+ i_owner->addUnitState(UNIT_STAT_CHARGING | UNIT_STAT_JUMPING);
+ i_owner->m_TempSpeed = speedXY;
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) jump to point (X: %f Y: %f Z: %f)", i_owner->GetGUIDLow(), x, y, z);
+ Mutate(new PointMovementGenerator<Player>(0,x,y,z), MOTION_SLOT_CONTROLLED);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) jump to point (X: %f Y: %f Z: %f)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), x, y, z);
+ Mutate(new PointMovementGenerator<Creature>(0,x,y,z), MOTION_SLOT_CONTROLLED);
+ }
+
+ i_owner->SendMonsterMove(x, y, z, moveFlag, time, speedZ);
+}
+
+void
+MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
+{
+ if (Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
+ return;
+
+ i_owner->addUnitState(UNIT_STAT_CHARGING);
+ i_owner->m_TempSpeed = speed;
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", i_owner->GetGUIDLow(), x, y, z);
+ Mutate(new PointMovementGenerator<Player>(id,x,y,z), MOTION_SLOT_CONTROLLED);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) charge point (X: %f Y: %f Z: %f)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), x, y, z);
+ Mutate(new PointMovementGenerator<Creature>(id,x,y,z), MOTION_SLOT_CONTROLLED);
+ }
+}
+
+void MotionMaster::MoveFall(float z, uint32 id)
+{
+ i_owner->SetFlying(false);
+ i_owner->SendMovementFlagUpdate();
+ //AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
+ MoveCharge(i_owner->GetPositionX(), i_owner->GetPositionY(), z, SPEED_CHARGE, id);
+}
+
+void
+MotionMaster::MoveSeekAssistance(float x, float y, float z)
+{
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ sLog.outError("Player (GUID: %u) attempt to seek assistance",i_owner->GetGUIDLow());
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) seek assistance (X: %f Y: %f Z: %f)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), x, y, z);
+ i_owner->AttackStop();
+ i_owner->ToCreature()->SetReactState(REACT_PASSIVE);
+ Mutate(new AssistanceMovementGenerator(x,y,z), MOTION_SLOT_ACTIVE);
+ }
+}
+
+void
+MotionMaster::MoveSeekAssistanceDistract(uint32 time)
+{
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ sLog.outError("Player (GUID: %u) attempt to call distract after assistance",i_owner->GetGUIDLow());
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) is distracted after assistance call (Time: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), time);
+ Mutate(new AssistanceDistractMovementGenerator(time), MOTION_SLOT_ACTIVE);
+ }
+}
+
+void
+MotionMaster::MoveFleeing(Unit* enemy, uint32 time)
+{
+ if (!enemy)
+ return;
+
+ if (i_owner->HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
+ return;
+
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) flee from %s (GUID: %u)", i_owner->GetGUIDLow(),
+ enemy->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
+ enemy->GetTypeId() == TYPEID_PLAYER ? enemy->GetGUIDLow() : enemy->ToCreature()->GetDBTableGUIDLow());
+ Mutate(new FleeingMovementGenerator<Player>(enemy->GetGUID()), MOTION_SLOT_CONTROLLED);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) flee from %s (GUID: %u)%s",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(),
+ enemy->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
+ enemy->GetTypeId() == TYPEID_PLAYER ? enemy->GetGUIDLow() : enemy->ToCreature()->GetDBTableGUIDLow(),
+ time ? " for a limited time" : "");
+ if (time)
+ Mutate(new TimedFleeingMovementGenerator(enemy->GetGUID(), time), MOTION_SLOT_CONTROLLED);
+ else
+ Mutate(new FleeingMovementGenerator<Creature>(enemy->GetGUID()), MOTION_SLOT_CONTROLLED);
+ }
+}
+
+void
+MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode)
+{
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) taxi to (Path %u node %u)", i_owner->GetGUIDLow(), path, pathnode);
+ FlightPathMovementGenerator* mgen = new FlightPathMovementGenerator(path,pathnode);
+ Mutate(mgen, MOTION_SLOT_CONTROLLED);
+ }
+ else
+ {
+ sLog.outError("Creature (Entry: %u GUID: %u) attempt taxi to (Path %u node %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), path, pathnode);
+ }
+}
+
+void
+MotionMaster::MoveDistract(uint32 timer)
+{
+ if (Impl[MOTION_SLOT_CONTROLLED])
+ return;
+
+ if (i_owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ DEBUG_LOG("Player (GUID: %u) distracted (timer: %u)", i_owner->GetGUIDLow(), timer);
+ }
+ else
+ {
+ DEBUG_LOG("Creature (Entry: %u GUID: %u) (timer: %u)",
+ i_owner->GetEntry(), i_owner->GetGUIDLow(), timer);
+ }
+
+ DistractMovementGenerator* mgen = new DistractMovementGenerator(timer);
+ Mutate(mgen, MOTION_SLOT_CONTROLLED);
+}
+
+void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
+{
+ if (MovementGenerator *curr = Impl[slot])
+ {
+ Impl[slot] = NULL; // in case a new one is generated in this slot during directdelete
+ if (i_top == slot && (m_cleanFlag & MMCF_UPDATE))
+ DelayedDelete(curr);
+ else
+ DirectDelete(curr);
+ }
+ else if (i_top < slot)
+ {
+ i_top = slot;
+ }
+
+ if (i_top > slot)
+ needInit[slot] = true;
+ else
+ {
+ m->Initialize(*i_owner);
+ needInit[slot] = false;
+ }
+ Impl[slot] = m;
+}
+
+void MotionMaster::MovePath(uint32 path_id, bool repeatable)
+{
+ if (!path_id)
+ return;
+ //We set waypoint movement as new default movement generator
+ // clear ALL movement generators (including default)
+ /*while (!empty())
+ {
+ MovementGenerator *curr = top();
+ curr->Finalize(*i_owner);
+ pop();
+ if (!isStatic(curr))
+ delete curr;
+ }*/
+
+ //i_owner->GetTypeId() == TYPEID_PLAYER ?
+ //Mutate(new WaypointMovementGenerator<Player>(path_id, repeatable)):
+ Mutate(new WaypointMovementGenerator<Creature>(path_id, repeatable), MOTION_SLOT_IDLE);
+
+ DEBUG_LOG("%s (GUID: %u) start moving over path(Id:%u, repeatable: %s)",
+ i_owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature",
+ i_owner->GetGUIDLow(), path_id, repeatable ? "YES" : "NO");
+}
+
+void MotionMaster::MoveRotate(uint32 time, RotateDirection direction)
+{
+ if (!time)
+ return;
+
+ Mutate(new RotateMovementGenerator(time, direction), MOTION_SLOT_ACTIVE);
+}
+
+void MotionMaster::propagateSpeedChange()
+{
+ /*Impl::container_type::iterator it = Impl::c.begin();
+ for (; it != end(); ++it)
+ {
+ (*it)->unitSpeedChanged();
+ }*/
+ for (int i = 0; i <= i_top; ++i)
+ {
+ if (Impl[i])
+ Impl[i]->unitSpeedChanged();
+ }
+}
+
+MovementGeneratorType MotionMaster::GetCurrentMovementGeneratorType() const
+{
+ if (empty())
+ return IDLE_MOTION_TYPE;
+
+ return top()->GetMovementGeneratorType();
+}
+
+MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
+{
+ if (!Impl[slot])
+ return NULL_MOTION_TYPE;
+ else
+ return Impl[slot]->GetMovementGeneratorType();
+}
+
+void MotionMaster::InitTop()
+{
+ top()->Initialize(*i_owner);
+ needInit[i_top] = false;
+}
+
+void MotionMaster::DirectDelete(_Ty curr)
+{
+ if (isStatic(curr))
+ return;
+ curr->Finalize(*i_owner);
+ delete curr;
+}
+
+void MotionMaster::DelayedDelete(_Ty curr)
+{
+ sLog.outCrash("Unit (Entry %u) is trying to delete its updating MG (Type %u)!", i_owner->GetEntry(), curr->GetMovementGeneratorType());
+ if (isStatic(curr))
+ return;
+ if (!m_expList)
+ m_expList = new ExpireList();
+ m_expList->push_back(curr);
+}
+
+bool MotionMaster::GetDestination(float &x, float &y, float &z)
+{
+ if (empty())
+ return false;
+
+ return top()->GetDestination(x,y,z);
+}
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
new file mode 100644
index 00000000000..c4082ba086a
--- /dev/null
+++ b/src/server/game/Movement/MotionMaster.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_MOTIONMASTER_H
+#define TRINITY_MOTIONMASTER_H
+
+#include "Common.h"
+#include <vector>
+#include "SharedDefines.h"
+#include "Object.h"
+
+class MovementGenerator;
+class Unit;
+
+// Creature Entry ID used for waypoints show, visible only for GMs
+#define VISUAL_WAYPOINT 1
+
+// values 0 ... MAX_DB_MOTION_TYPE-1 used in DB
+enum MovementGeneratorType
+{
+ IDLE_MOTION_TYPE = 0, // IdleMovementGenerator.h
+ RANDOM_MOTION_TYPE = 1, // RandomMovementGenerator.h
+ WAYPOINT_MOTION_TYPE = 2, // WaypointMovementGenerator.h
+ MAX_DB_MOTION_TYPE = 3, // *** this and below motion types can't be set in DB.
+ ANIMAL_RANDOM_MOTION_TYPE = MAX_DB_MOTION_TYPE, // AnimalRandomMovementGenerator.h
+ CONFUSED_MOTION_TYPE = 4, // ConfusedMovementGenerator.h
+ TARGETED_MOTION_TYPE = 5, // TargetedMovementGenerator.h
+ HOME_MOTION_TYPE = 6, // HomeMovementGenerator.h
+ FLIGHT_MOTION_TYPE = 7, // WaypointMovementGenerator.h
+ POINT_MOTION_TYPE = 8, // PointMovementGenerator.h
+ FLEEING_MOTION_TYPE = 9, // FleeingMovementGenerator.h
+ DISTRACT_MOTION_TYPE = 10, // IdleMovementGenerator.h
+ ASSISTANCE_MOTION_TYPE= 11, // PointMovementGenerator.h (first part of flee for assistance)
+ ASSISTANCE_DISTRACT_MOTION_TYPE = 12, // IdleMovementGenerator.h (second part of flee for assistance)
+ TIMED_FLEEING_MOTION_TYPE = 13, // FleeingMovementGenerator.h (alt.second part of flee for assistance)
+ ROTATE_MOTION_TYPE = 14,
+ NULL_MOTION_TYPE = 15,
+};
+
+enum MovementSlot
+{
+ MOTION_SLOT_IDLE,
+ MOTION_SLOT_ACTIVE,
+ MOTION_SLOT_CONTROLLED,
+ MAX_MOTION_SLOT,
+};
+
+enum MMCleanFlag
+{
+ MMCF_NONE = 0,
+ MMCF_UPDATE = 1, // Clear or Expire called from update
+ MMCF_RESET = 2 // Flag if need top()->Reset()
+};
+
+enum RotateDirection
+{
+ ROTATE_DIRECTION_LEFT,
+ ROTATE_DIRECTION_RIGHT,
+};
+
+// assume it is 25 yard per 0.6 second
+#define SPEED_CHARGE 42.0f
+
+class MotionMaster //: private std::stack<MovementGenerator *>
+{
+ private:
+ //typedef std::stack<MovementGenerator *> Impl;
+ typedef MovementGenerator* _Ty;
+ _Ty Impl[MAX_MOTION_SLOT];
+ bool needInit[MAX_MOTION_SLOT];
+ typedef std::vector<_Ty> ExpireList;
+ int i_top;
+
+ bool empty() const { return (i_top < 0); }
+ void pop() { Impl[i_top] = NULL; --i_top; }
+ void push(_Ty _Val) { ++i_top; Impl[i_top] = _Val; }
+
+ bool needInitTop() const { return needInit[i_top]; }
+ void InitTop();
+ public:
+
+ explicit MotionMaster(Unit *unit) : i_owner(unit), m_expList(NULL), m_cleanFlag(MMCF_NONE), i_top(-1)
+ {
+ for (uint8 i = 0; i < MAX_MOTION_SLOT; ++i)
+ {
+ Impl[i] = NULL;
+ needInit[i] = true;
+ }
+ }
+ ~MotionMaster();
+
+ void Initialize();
+ void InitDefault();
+
+ int size() const { return i_top + 1; }
+ _Ty top() const { return Impl[i_top]; }
+ _Ty GetMotionSlot(int slot) const { return Impl[slot]; }
+
+ void DirectDelete(_Ty curr);
+ void DelayedDelete(_Ty curr);
+
+ void UpdateMotion(uint32 diff);
+ void Clear(bool reset = true)
+ {
+ if (m_cleanFlag & MMCF_UPDATE)
+ {
+ if (reset)
+ m_cleanFlag |= MMCF_RESET;
+ else
+ m_cleanFlag &= ~MMCF_RESET;
+ DelayedClean();
+ }
+ else
+ DirectClean(reset);
+ }
+ void MovementExpired(bool reset = true)
+ {
+ if (m_cleanFlag & MMCF_UPDATE)
+ {
+ if (reset)
+ m_cleanFlag |= MMCF_RESET;
+ else
+ m_cleanFlag &= ~MMCF_RESET;
+ DelayedExpire();
+ }
+ else
+ DirectExpire(reset);
+ }
+
+ void MoveIdle(MovementSlot slot = MOTION_SLOT_ACTIVE);
+ void MoveTargetedHome();
+ void MoveRandom(float spawndist = 0.0f);
+ void MoveFollow(Unit* target, float dist, float angle, MovementSlot slot = MOTION_SLOT_ACTIVE);
+ void MoveChase(Unit* target, float dist = 0.0f, float angle = 0.0f);
+ void MoveConfused();
+ void MoveFleeing(Unit* enemy, uint32 time = 0);
+ void MovePoint(uint32 id, const Position &pos)
+ { MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ); }
+ void MovePoint(uint32 id, float x,float y,float z);
+ void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
+ void MoveFall(float z, uint32 id = 0);
+ void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ);
+ void MoveJumpTo(float angle, float speedXY, float speedZ);
+ void MoveJump(float x, float y, float z, float speedXY, float speedZ);
+ void MoveSeekAssistance(float x,float y,float z);
+ void MoveSeekAssistanceDistract(uint32 timer);
+ void MoveTaxiFlight(uint32 path, uint32 pathnode);
+ void MoveDistract(uint32 time);
+ void MovePath(uint32 path_id, bool repeatable);
+ void MoveRotate(uint32 time, RotateDirection direction);
+
+ MovementGeneratorType GetCurrentMovementGeneratorType() const;
+ MovementGeneratorType GetMotionSlotType(int slot) const;
+
+ void propagateSpeedChange();
+
+ bool GetDestination(float &x, float &y, float &z);
+ private:
+ void Mutate(MovementGenerator *m, MovementSlot slot); // use Move* functions instead
+
+ void DirectClean(bool reset);
+ void DelayedClean();
+
+ void DirectExpire(bool reset);
+ void DelayedExpire();
+
+ Unit *i_owner;
+ ExpireList *m_expList;
+ uint8 m_cleanFlag;
+};
+#endif
+
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
new file mode 100644
index 00000000000..43c6052d2d3
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Creature.h"
+#include "MapManager.h"
+#include "Opcodes.h"
+#include "ConfusedMovementGenerator.h"
+#include "DestinationHolderImp.h"
+#include "VMapFactory.h"
+
+#ifdef MAP_BASED_RAND_GEN
+#define rand_norm() unit.rand_norm()
+#define urand(a,b) unit.urand(a,b)
+#endif
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Initialize(T &unit)
+{
+ const float wander_distance = 11;
+ float x,y,z;
+ x = unit.GetPositionX();
+ y = unit.GetPositionY();
+ z = unit.GetPositionZ();
+
+ Map const* map = unit.GetBaseMap();
+
+ i_nextMove = 1;
+
+ bool is_water_ok, is_land_ok;
+ _InitSpecific(unit, is_water_ok, is_land_ok);
+
+ VMAP::IVMapManager *vMaps = VMAP::VMapFactory::createOrGetVMapManager();
+
+ for (uint8 idx = 0; idx <= MAX_CONF_WAYPOINTS; ++idx)
+ {
+ const bool isInLoS = vMaps->isInLineOfSight(unit.GetMapId(), x, y, z + 2.0f, i_waypoints[idx][0], i_waypoints[idx][1], z + 2.0f);
+ if (isInLoS)
+ {
+ const float wanderX = wander_distance*rand_norm() - wander_distance/2;
+ const float wanderY = wander_distance*rand_norm() - wander_distance/2;
+ i_waypoints[idx][0] = x + wanderX;
+ i_waypoints[idx][1] = y + wanderY;
+ }
+ else
+ {
+ i_waypoints[idx][0] = x;
+ i_waypoints[idx][1] = y;
+ }
+
+ // prevent invalid coordinates generation
+ Trinity::NormalizeMapCoord(i_waypoints[idx][0]);
+ Trinity::NormalizeMapCoord(i_waypoints[idx][1]);
+
+ bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z);
+ // if generated wrong path just ignore
+ if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
+ {
+ i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
+ i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
+ }
+
+ unit.UpdateGroundPositionZ(i_waypoints[idx][0],i_waypoints[idx][1],z);
+ i_waypoints[idx][2] = z;
+ }
+
+ unit.SetUInt64Value(UNIT_FIELD_TARGET, 0);
+ unit.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit.CastStop();
+ unit.StopMoving();
+ unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ unit.addUnitState(UNIT_STAT_CONFUSED);
+}
+
+template<>
+void
+ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
+{
+ is_water_ok = creature.canSwim();
+ is_land_ok = creature.canWalk();
+}
+
+template<>
+void
+ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
+{
+ is_water_ok = true;
+ is_land_ok = true;
+}
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Reset(T &unit)
+{
+ i_nextMove = 1;
+ i_nextMoveTime.Reset(0);
+ i_destinationHolder.ResetUpdate();
+ unit.StopMoving();
+}
+
+template<class T>
+bool
+ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
+{
+ if (!&unit)
+ return true;
+
+ if (unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
+ return true;
+
+ if (i_nextMoveTime.Passed())
+ {
+ // currently moving, update location
+ Traveller<T> traveller(unit);
+ if (i_destinationHolder.UpdateTraveller(traveller, diff))
+ {
+ if (i_destinationHolder.HasArrived())
+ {
+ // arrived, stop and wait a bit
+ unit.clearUnitState(UNIT_STAT_MOVE);
+
+ i_nextMove = urand(1,MAX_CONF_WAYPOINTS);
+ i_nextMoveTime.Reset(urand(0, 1500-1)); // TODO: check the minimum reset time, should be probably higher
+ }
+ }
+ }
+ else
+ {
+ // waiting for next move
+ i_nextMoveTime.Update(diff);
+ if (i_nextMoveTime.Passed())
+ {
+ // start moving
+ assert(i_nextMove <= MAX_CONF_WAYPOINTS);
+ const float x = i_waypoints[i_nextMove][0];
+ const float y = i_waypoints[i_nextMove][1];
+ const float z = i_waypoints[i_nextMove][2];
+ Traveller<T> traveller(unit);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+ }
+ }
+ return true;
+}
+
+template<class T>
+void
+ConfusedMovementGenerator<T>::Finalize(T &unit)
+{
+ unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit.clearUnitState(UNIT_STAT_CONFUSED);
+ if (unit.GetTypeId() == TYPEID_UNIT && unit.getVictim())
+ unit.SetUInt64Value(UNIT_FIELD_TARGET, unit.getVictim()->GetGUID());
+}
+
+template void ConfusedMovementGenerator<Player>::Initialize(Player &player);
+template void ConfusedMovementGenerator<Creature>::Initialize(Creature &creature);
+template void ConfusedMovementGenerator<Player>::Finalize(Player &player);
+template void ConfusedMovementGenerator<Creature>::Finalize(Creature &creature);
+template void ConfusedMovementGenerator<Player>::Reset(Player &player);
+template void ConfusedMovementGenerator<Creature>::Reset(Creature &creature);
+template bool ConfusedMovementGenerator<Player>::Update(Player &player, const uint32 &diff);
+template bool ConfusedMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff);
+
+
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
new file mode 100644
index 00000000000..e1a71151d37
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_CONFUSEDGENERATOR_H
+#define TRINITY_CONFUSEDGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+
+#define MAX_CONF_WAYPOINTS 24
+
+template<class T>
+class ConfusedMovementGenerator
+: public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
+{
+ public:
+ explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {}
+
+ void Initialize(T &);
+ void Finalize(T &);
+ void Reset(T &);
+ bool Update(T &, const uint32 &);
+
+ bool GetDestination(float &x, float &y, float &z) const
+ {
+ if (i_destinationHolder.HasArrived()) return false;
+ i_destinationHolder.GetDestination(x,y,z);
+ return true;
+ }
+
+ MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
+ private:
+ void _InitSpecific(T &, bool &, bool &);
+ TimeTracker i_nextMoveTime;
+ float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ uint32 i_nextMove;
+};
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
new file mode 100644
index 00000000000..490fe034fd7
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "MapManager.h"
+#include "FleeingMovementGenerator.h"
+#include "DestinationHolderImp.h"
+#include "ObjectAccessor.h"
+
+#define MIN_QUIET_DISTANCE 28.0f
+#define MAX_QUIET_DISTANCE 43.0f
+
+template<class T>
+void
+FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
+{
+ if (!&owner)
+ return;
+
+ if (owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED))
+ return;
+
+ if (!_setMoveData(owner))
+ return;
+
+ float x, y, z;
+ if (!_getPoint(owner, x, y, z))
+ return;
+
+ owner.addUnitState(UNIT_STAT_FLEEING | UNIT_STAT_ROAMING);
+ Traveller<T> traveller(owner);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+}
+
+template<>
+bool FleeingMovementGenerator<Creature>::GetDestination(float &x, float &y, float &z) const
+{
+ if (i_destinationHolder.HasArrived())
+ return false;
+
+ i_destinationHolder.GetDestination(x, y, z);
+ return true;
+}
+
+template<>
+bool FleeingMovementGenerator<Player>::GetDestination(float & /*x*/, float & /*y*/, float & /*z*/) const
+{
+ return false;
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
+{
+ if (!&owner)
+ return false;
+
+ x = owner.GetPositionX();
+ y = owner.GetPositionY();
+ z = owner.GetPositionZ();
+
+ float temp_x, temp_y, angle = 0;
+ const Map * _map = owner.GetBaseMap();
+ //primitive path-finding
+ for (uint8 i = 0; i < 18; ++i)
+ {
+ if (i_only_forward && i > 2)
+ break;
+
+ float distance = 5.0f;
+
+ switch(i)
+ {
+ case 0:
+ angle = i_cur_angle;
+ break;
+ case 1:
+ angle = i_cur_angle;
+ distance /= 2;
+ break;
+ case 2:
+ angle = i_cur_angle;
+ distance /= 4;
+ break;
+ case 3:
+ angle = i_cur_angle + M_PI/4.0f;
+ break;
+ case 4:
+ angle = i_cur_angle - M_PI/4.0f;
+ break;
+ case 5:
+ angle = i_cur_angle + M_PI/4.0f;
+ distance /= 2;
+ break;
+ case 6:
+ angle = i_cur_angle - M_PI/4.0f;
+ distance /= 2;
+ break;
+ case 7:
+ angle = i_cur_angle + M_PI/2.0f;
+ break;
+ case 8:
+ angle = i_cur_angle - M_PI/2.0f;
+ break;
+ case 9:
+ angle = i_cur_angle + M_PI/2.0f;
+ distance /= 2;
+ break;
+ case 10:
+ angle = i_cur_angle - M_PI/2.0f;
+ distance /= 2;
+ break;
+ case 11:
+ angle = i_cur_angle + M_PI/4.0f;
+ distance /= 4;
+ break;
+ case 12:
+ angle = i_cur_angle - M_PI/4.0f;
+ distance /= 4;
+ break;
+ case 13:
+ angle = i_cur_angle + M_PI/2.0f;
+ distance /= 4;
+ break;
+ case 14:
+ angle = i_cur_angle - M_PI/2.0f;
+ distance /= 4;
+ break;
+ case 15:
+ angle = i_cur_angle + M_PI*3/4.0f;
+ distance /= 2;
+ break;
+ case 16:
+ angle = i_cur_angle - M_PI*3/4.0f;
+ distance /= 2;
+ break;
+ case 17:
+ angle = i_cur_angle + M_PI;
+ distance /= 2;
+ break;
+ }
+ temp_x = x + distance * cos(angle);
+ temp_y = y + distance * sin(angle);
+ Trinity::NormalizeMapCoord(temp_x);
+ Trinity::NormalizeMapCoord(temp_y);
+ if (owner.IsWithinLOS(temp_x,temp_y,z))
+ {
+ bool is_water_now = _map->IsInWater(x,y,z);
+
+ if (is_water_now && _map->IsInWater(temp_x,temp_y,z))
+ {
+ x = temp_x;
+ y = temp_y;
+ return true;
+ }
+ float new_z = _map->GetHeight(temp_x,temp_y,z,true);
+
+ if (new_z <= INVALID_HEIGHT)
+ continue;
+
+ bool is_water_next = _map->IsInWater(temp_x,temp_y,new_z);
+
+ if ((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
+ continue;
+
+ if (!(new_z - z) || distance / fabs(new_z - z) > 1.0f)
+ {
+ float new_z_left = _map->GetHeight(temp_x + 1.0f*cos(angle+M_PI/2),temp_y + 1.0f*sin(angle+M_PI/2),z,true);
+ float new_z_right = _map->GetHeight(temp_x + 1.0f*cos(angle-M_PI/2),temp_y + 1.0f*sin(angle-M_PI/2),z,true);
+ if (fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
+ {
+ x = temp_x;
+ y = temp_y;
+ z = new_z;
+ return true;
+ }
+ }
+ }
+ }
+ i_to_distance_from_caster = 0.0f;
+ i_nextCheckTime.Reset(urand(500,1000));
+ return false;
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::_setMoveData(T &owner)
+{
+ float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
+
+ if (i_to_distance_from_caster > 0.0f)
+ {
+ if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
+ // if we reach lower distance
+ (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
+ // if we can't be close
+ (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
+ // if we reach bigger distance
+ (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
+ (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE))
+ // if we leave 'quiet zone'
+ {
+ // we are very far or too close, stopping
+ i_to_distance_from_caster = 0.0f;
+ i_nextCheckTime.Reset(urand(500,1000));
+ return false;
+ }
+ else
+ {
+ // now we are running, continue
+ i_last_distance_from_caster = cur_dist_xyz;
+ return true;
+ }
+ }
+
+ float cur_dist;
+ float angle_to_caster;
+
+ Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID);
+
+ if (fright)
+ {
+ cur_dist = fright->GetDistance(&owner);
+ if (cur_dist < cur_dist_xyz)
+ {
+ i_caster_x = fright->GetPositionX();
+ i_caster_y = fright->GetPositionY();
+ i_caster_z = fright->GetPositionZ();
+ angle_to_caster = fright->GetAngle(&owner);
+ }
+ else
+ {
+ cur_dist = cur_dist_xyz;
+ angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI;
+ }
+ }
+ else
+ {
+ cur_dist = cur_dist_xyz;
+ angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + M_PI;
+ }
+
+ // if we too close may use 'path-finding' else just stop
+ i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
+
+ //get angle and 'distance from caster' to run
+ float angle;
+
+ if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
+ {
+ angle = rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * M_PI/3 + rand_norm()*M_PI*2/3;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE;
+ i_only_forward = true;
+ }
+ else if (cur_dist < MIN_QUIET_DISTANCE)
+ {
+ angle = M_PI/6 + rand_norm()*M_PI*2/3;
+ i_to_distance_from_caster = cur_dist*2/3 + rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
+ }
+ else if (cur_dist > MAX_QUIET_DISTANCE)
+ {
+ angle = rand_norm()*M_PI/3 + M_PI*2/3;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ }
+ else
+ {
+ angle = rand_norm()*M_PI;
+ i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ }
+
+ int8 sign = rand_norm() > 0.5f ? 1 : -1;
+ i_cur_angle = sign*angle + angle_to_caster;
+
+ // current distance
+ i_last_distance_from_caster = cur_dist;
+
+ return true;
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Initialize(T &owner)
+{
+ if (!&owner)
+ return;
+
+ _Init(owner);
+ owner.CastStop();
+ owner.addUnitState(UNIT_STAT_FLEEING | UNIT_STAT_ROAMING);
+ owner.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner.SetUInt64Value(UNIT_FIELD_TARGET, 0);
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ if (Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
+ {
+ i_caster_x = fright->GetPositionX();
+ i_caster_y = fright->GetPositionY();
+ i_caster_z = fright->GetPositionZ();
+ }
+ else
+ {
+ i_caster_x = owner.GetPositionX();
+ i_caster_y = owner.GetPositionY();
+ i_caster_z = owner.GetPositionZ();
+ }
+
+ i_only_forward = true;
+ i_cur_angle = 0.0f;
+ i_last_distance_from_caster = 0.0f;
+ i_to_distance_from_caster = 0.0f;
+ _setTargetLocation(owner);
+}
+
+template<>
+void
+FleeingMovementGenerator<Creature>::_Init(Creature &owner)
+{
+ if (!&owner)
+ return;
+
+ is_water_ok = owner.canSwim();
+ is_land_ok = owner.canWalk();
+}
+
+template<>
+void
+FleeingMovementGenerator<Player>::_Init(Player &)
+{
+ is_water_ok = true;
+ is_land_ok = true;
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Finalize(T &owner)
+{
+ owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner.clearUnitState(UNIT_STAT_FLEEING | UNIT_STAT_ROAMING);
+ if (owner.GetTypeId() == TYPEID_UNIT && owner.getVictim())
+ owner.SetUInt64Value(UNIT_FIELD_TARGET, owner.getVictim()->GetGUID());
+}
+
+template<class T>
+void
+FleeingMovementGenerator<T>::Reset(T &owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+bool
+FleeingMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
+{
+ if (!&owner || !owner.isAlive())
+ return false;
+ if (owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED))
+ return true;
+
+ Traveller<T> traveller(owner);
+
+ i_nextCheckTime.Update(time_diff);
+
+ if ((owner.IsStopped() && !i_destinationHolder.HasArrived()) || !i_destinationHolder.HasDestination())
+ {
+ _setTargetLocation(owner);
+ return true;
+ }
+
+ if (i_destinationHolder.UpdateTraveller(traveller, time_diff))
+ {
+ i_destinationHolder.ResetUpdate(50);
+ if (i_nextCheckTime.Passed() && i_destinationHolder.HasArrived())
+ {
+ _setTargetLocation(owner);
+ return true;
+ }
+ }
+ return true;
+}
+
+template void FleeingMovementGenerator<Player>::Initialize(Player &);
+template void FleeingMovementGenerator<Creature>::Initialize(Creature &);
+template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
+template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &);
+template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &);
+template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &);
+template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &);
+template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &);
+template void FleeingMovementGenerator<Player>::Finalize(Player &);
+template void FleeingMovementGenerator<Creature>::Finalize(Creature &);
+template void FleeingMovementGenerator<Player>::Reset(Player &);
+template void FleeingMovementGenerator<Creature>::Reset(Creature &);
+template bool FleeingMovementGenerator<Player>::Update(Player &, const uint32 &);
+template bool FleeingMovementGenerator<Creature>::Update(Creature &, const uint32 &);
+
+void TimedFleeingMovementGenerator::Finalize(Unit &owner)
+{
+ owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner.clearUnitState(UNIT_STAT_FLEEING | UNIT_STAT_ROAMING);
+ if (Unit* victim = owner.getVictim())
+ {
+ if (owner.isAlive())
+ {
+ owner.AttackStop();
+ owner.ToCreature()->AI()->AttackStart(victim);
+ }
+ }
+}
+
+bool TimedFleeingMovementGenerator::Update(Unit & owner, const uint32 & time_diff)
+{
+ if (!owner.isAlive())
+ return false;
+
+ if (owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED))
+ return true;
+
+ i_totalFleeTime.Update(time_diff);
+ if (i_totalFleeTime.Passed())
+ return false;
+
+ // This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, const uint32 &) version
+ // This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
+ return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);
+}
+
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
new file mode 100644
index 00000000000..5dee973efa6
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
@@ -0,0 +1,83 @@
+/*
+* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+*
+* Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef TRINITY_FLEEINGMOVEMENTGENERATOR_H
+#define TRINITY_FLEEINGMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+
+template<class T>
+class FleeingMovementGenerator
+: public MovementGeneratorMedium< T, FleeingMovementGenerator<T> >
+{
+ public:
+ FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
+
+ void Initialize(T &);
+ void Finalize(T &);
+ void Reset(T &);
+ bool Update(T &, const uint32 &);
+ bool GetDestination(float &x, float &y, float &z) const;
+
+ MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
+
+ private:
+ void _setTargetLocation(T &owner);
+ bool _getPoint(T &owner, float &x, float &y, float &z);
+ bool _setMoveData(T &owner);
+ void _Init(T &);
+
+ bool is_water_ok :1;
+ bool is_land_ok :1;
+ bool i_only_forward:1;
+
+ float i_caster_x;
+ float i_caster_y;
+ float i_caster_z;
+ float i_last_distance_from_caster;
+ float i_to_distance_from_caster;
+ float i_cur_angle;
+ uint64 i_frightGUID;
+ TimeTracker i_nextCheckTime;
+
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+};
+
+class TimedFleeingMovementGenerator
+: public FleeingMovementGenerator<Creature>
+{
+ public:
+ TimedFleeingMovementGenerator(uint64 fright, uint32 time) :
+ FleeingMovementGenerator<Creature>(fright),
+ i_totalFleeTime(time) {}
+
+ MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; }
+ bool Update(Unit &, const uint32 &);
+ void Finalize(Unit &);
+
+ private:
+ TimeTracker i_totalFleeTime;
+};
+
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
new file mode 100644
index 00000000000..54fbc34cf32
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "HomeMovementGenerator.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "Traveller.h"
+#include "DestinationHolderImp.h"
+#include "WorldPacket.h"
+
+void
+HomeMovementGenerator<Creature>::Initialize(Creature & owner)
+{
+ float x, y, z;
+ owner.GetHomePosition(x, y, z, ori);
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ owner.addUnitState(UNIT_STAT_EVADE);
+ _setTargetLocation(owner);
+}
+
+void
+HomeMovementGenerator<Creature>::Reset(Creature &)
+{
+}
+
+void
+HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
+{
+ if (!&owner)
+ return;
+
+ if (owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
+ return;
+
+ float x, y, z;
+ owner.GetHomePosition(x, y, z, ori);
+
+ CreatureTraveller traveller(owner);
+
+ uint32 travel_time = i_destinationHolder.SetDestination(traveller, x, y, z);
+ modifyTravelTime(travel_time);
+ owner.clearUnitState(UNIT_STAT_ALL_STATE);
+}
+
+bool
+HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff)
+{
+ CreatureTraveller traveller(owner);
+ i_destinationHolder.UpdateTraveller(traveller, time_diff);
+
+ if (time_diff > i_travel_timer)
+ {
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ // restore orientation of not moving creature at returning to home
+ if (owner.GetDefaultMovementType() == IDLE_MOTION_TYPE)
+ {
+ //sLog.outDebug("Entering HomeMovement::GetDestination(z,y,z)");
+ owner.SetOrientation(ori);
+ WorldPacket packet;
+ owner.BuildHeartBeatMsg(&packet);
+ owner.SendMessageToSet(&packet, false);
+ }
+
+ owner.clearUnitState(UNIT_STAT_EVADE);
+ owner.AI()->JustReachedHome();
+ return false;
+ }
+
+ i_travel_timer -= time_diff;
+
+ return true;
+}
+
+
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
new file mode 100644
index 00000000000..1b01f5357e7
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_HOMEMOVEMENTGENERATOR_H
+#define TRINITY_HOMEMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+
+class Creature;
+
+template < class T >
+class HomeMovementGenerator;
+
+template <>
+class HomeMovementGenerator<Creature>
+: public MovementGeneratorMedium< Creature, HomeMovementGenerator<Creature> >
+{
+ public:
+
+ HomeMovementGenerator() {}
+ ~HomeMovementGenerator() {}
+
+ void Initialize(Creature &);
+ void Finalize(Creature &) {}
+ void Reset(Creature &);
+ bool Update(Creature &, const uint32 &);
+ void modifyTravelTime(uint32 travel_time) { i_travel_timer = travel_time; }
+ MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; }
+
+ bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; }
+
+ private:
+ void _setTargetLocation(Creature &);
+ DestinationHolder< Traveller<Creature> > i_destinationHolder;
+
+ float ori;
+ uint32 i_travel_timer;
+};
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
new file mode 100644
index 00000000000..54d9ad16f7d
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "IdleMovementGenerator.h"
+#include "CreatureAI.h"
+#include "Creature.h"
+
+IdleMovementGenerator si_idleMovement;
+
+// StopMoving is needed to make unit stop if its last movement generator expires
+// But it should not be sent otherwise there are many redundent packets
+void IdleMovementGenerator::Initialize(Unit &owner)
+{
+ if (owner.hasUnitState(UNIT_STAT_MOVE))
+ owner.StopMoving();
+}
+
+void
+IdleMovementGenerator::Reset(Unit& owner)
+{
+ if (owner.hasUnitState(UNIT_STAT_MOVE))
+ owner.StopMoving();
+}
+
+void RotateMovementGenerator::Initialize(Unit& owner)
+{
+ if (owner.hasUnitState(UNIT_STAT_MOVE))
+ owner.StopMoving();
+
+ if (owner.getVictim())
+ owner.SetInFront(owner.getVictim());
+
+ owner.addUnitState(UNIT_STAT_ROTATING);
+
+ owner.AttackStop();
+}
+
+bool RotateMovementGenerator::Update(Unit& owner, const uint32& diff)
+{
+ float angle = owner.GetOrientation();
+ if (m_direction == ROTATE_DIRECTION_LEFT)
+ {
+ angle += (float)diff * M_PI * 2 / m_maxDuration;
+ while (angle >= M_PI * 2) angle -= M_PI * 2;
+ }
+ else
+ {
+ angle -= (float)diff * M_PI * 2 / m_maxDuration;
+ while (angle < 0) angle += M_PI * 2;
+ }
+ owner.SetOrientation(angle);
+ owner.SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
+
+ if (m_duration > diff)
+ m_duration -= diff;
+ else
+ return false;
+
+ return true;
+}
+
+void RotateMovementGenerator::Finalize(Unit &unit)
+{
+ unit.clearUnitState(UNIT_STAT_ROTATING);
+ if (unit.GetTypeId() == TYPEID_UNIT)
+ unit.ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
+}
+
+void
+DistractMovementGenerator::Initialize(Unit& owner)
+{
+ owner.addUnitState(UNIT_STAT_DISTRACTED);
+}
+
+void
+DistractMovementGenerator::Finalize(Unit& owner)
+{
+ owner.clearUnitState(UNIT_STAT_DISTRACTED);
+}
+
+bool
+DistractMovementGenerator::Update(Unit& /*owner*/, const uint32& time_diff)
+{
+ if (time_diff > m_timer)
+ return false;
+
+ m_timer -= time_diff;
+ return true;
+}
+
+void
+AssistanceDistractMovementGenerator::Finalize(Unit &unit)
+{
+ unit.clearUnitState(UNIT_STAT_DISTRACTED);
+ unit.ToCreature()->SetReactState(REACT_AGGRESSIVE);
+}
+
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
new file mode 100644
index 00000000000..57572220c2b
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_IDLEMOVEMENTGENERATOR_H
+#define TRINITY_IDLEMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+
+class IdleMovementGenerator : public MovementGenerator
+{
+ public:
+
+ void Initialize(Unit &);
+ void Finalize(Unit &) { }
+ void Reset(Unit &);
+ bool Update(Unit &, const uint32 &) { return true; }
+ MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; }
+};
+
+extern IdleMovementGenerator si_idleMovement;
+
+class RotateMovementGenerator : public MovementGenerator
+{
+ public:
+ explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) {}
+
+ void Initialize(Unit& owner);
+ void Finalize(Unit& owner);
+ void Reset(Unit& owner) { Initialize(owner); }
+ bool Update(Unit& owner, const uint32& time_diff);
+ MovementGeneratorType GetMovementGeneratorType() { return ROTATE_MOTION_TYPE; }
+
+ private:
+ uint32 m_duration, m_maxDuration;
+ RotateDirection m_direction;
+};
+
+class DistractMovementGenerator : public MovementGenerator
+{
+ public:
+ explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {}
+
+ void Initialize(Unit& owner);
+ void Finalize(Unit& owner);
+ void Reset(Unit& owner) { Initialize(owner); }
+ bool Update(Unit& owner, const uint32& time_diff);
+ MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; }
+
+ private:
+ uint32 m_timer;
+};
+
+class AssistanceDistractMovementGenerator : public DistractMovementGenerator
+{
+ public:
+ AssistanceDistractMovementGenerator(uint32 timer) :
+ DistractMovementGenerator(timer) {}
+
+ MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_DISTRACT_MOTION_TYPE; }
+ void Finalize(Unit& unit);
+};
+
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/MovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/MovementGenerator.cpp
new file mode 100644
index 00000000000..2cccf8a76dc
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/MovementGenerator.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "MovementGenerator.h"
+
+MovementGenerator::~MovementGenerator()
+{
+}
+
+
diff --git a/src/server/game/Movement/MovementGenerators/MovementGenerator.h b/src/server/game/Movement/MovementGenerators/MovementGenerator.h
new file mode 100644
index 00000000000..ab7a52c483e
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/MovementGenerator.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_MOVEMENTGENERATOR_H
+#define TRINITY_MOVEMENTGENERATOR_H
+
+#include "Platform/Define.h"
+#include "Policies/Singleton.h"
+#include "Dynamic/ObjectRegistry.h"
+#include "Dynamic/FactoryHolder.h"
+#include "Common.h"
+#include "MotionMaster.h"
+
+class Unit;
+
+class MovementGenerator
+{
+ public:
+ virtual ~MovementGenerator();
+
+ virtual void Initialize(Unit &) = 0;
+ virtual void Finalize(Unit &) = 0;
+
+ virtual void Reset(Unit &) = 0;
+
+ virtual bool Update(Unit &, const uint32 &time_diff) = 0;
+
+ virtual MovementGeneratorType GetMovementGeneratorType() = 0;
+
+ virtual void unitSpeedChanged() { }
+
+ virtual bool GetDestination(float& /*x*/, float& /*y*/, float& /*z*/) const { return false; }
+};
+
+template<class T, class D>
+class MovementGeneratorMedium : public MovementGenerator
+{
+ public:
+ void Initialize(Unit &u)
+ {
+ //u->AssertIsType<T>();
+ (static_cast<D*>(this))->Initialize(*((T*)&u));
+ }
+ void Finalize(Unit &u)
+ {
+ //u->AssertIsType<T>();
+ (static_cast<D*>(this))->Finalize(*((T*)&u));
+ }
+ void Reset(Unit &u)
+ {
+ //u->AssertIsType<T>();
+ (static_cast<D*>(this))->Reset(*((T*)&u));
+ }
+ bool Update(Unit &u, const uint32 &time_diff)
+ {
+ //u->AssertIsType<T>();
+ return (static_cast<D*>(this))->Update(*((T*)&u), time_diff);
+ }
+ public:
+ // will not link if not overridden in the generators
+ void Initialize(T &u);
+ void Finalize(T &u);
+ void Reset(T &u);
+ bool Update(T &u, const uint32 &time_diff);
+};
+
+struct SelectableMovement : public FactoryHolder<MovementGenerator,MovementGeneratorType>
+{
+ SelectableMovement(MovementGeneratorType mgt) : FactoryHolder<MovementGenerator,MovementGeneratorType>(mgt) {}
+};
+
+template<class REAL_MOVEMENT>
+struct MovementGeneratorFactory : public SelectableMovement
+{
+ MovementGeneratorFactory(MovementGeneratorType mgt) : SelectableMovement(mgt) {}
+
+ MovementGenerator* Create(void *) const;
+};
+
+typedef FactoryHolder<MovementGenerator,MovementGeneratorType> MovementGeneratorCreator;
+typedef FactoryHolder<MovementGenerator,MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry;
+typedef FactoryHolder<MovementGenerator,MovementGeneratorType>::FactoryHolderRepository MovementGeneratorRepository;
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/MovementGeneratorImpl.h b/src/server/game/Movement/MovementGenerators/MovementGeneratorImpl.h
new file mode 100644
index 00000000000..d2778a5bff8
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/MovementGeneratorImpl.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_MOVEMENTGENERATOR_IMPL_H
+#define TRINITY_MOVEMENTGENERATOR_IMPL_H
+
+#include "MovementGenerator.h"
+
+template<class MOVEMENT_GEN>
+inline MovementGenerator*
+MovementGeneratorFactory<MOVEMENT_GEN>::Create(void * /*data*/) const
+{
+ return (new MOVEMENT_GEN());
+}
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
new file mode 100644
index 00000000000..63787edda1d
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PointMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "DestinationHolderImp.h"
+#include "World.h"
+
+//----- Point Movement Generator
+template<class T>
+void PointMovementGenerator<T>::Initialize(T &unit)
+{
+ unit.StopMoving();
+ Traveller<T> traveller(unit);
+ // knockback effect has UNIT_STAT_JUMPING set,so if here we disable sentmonstermove there will be creature position sync problem between client and server
+ i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z, true /* !unit.hasUnitState(UNIT_STAT_JUMPING)*/);
+}
+
+template<class T>
+bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff)
+{
+ if (!&unit)
+ return false;
+
+ if (unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED))
+ {
+ if (unit.hasUnitState(UNIT_STAT_CHARGING))
+ return false;
+ else
+ return true;
+ }
+
+ Traveller<T> traveller(unit);
+
+ i_destinationHolder.UpdateTraveller(traveller, diff);
+
+ if (i_destinationHolder.HasArrived())
+ {
+ unit.clearUnitState(UNIT_STAT_MOVE);
+ arrived = true;
+ return false;
+ }
+
+ return true;
+}
+
+template<class T>
+void PointMovementGenerator<T>:: Finalize(T &unit)
+{
+ if (unit.hasUnitState(UNIT_STAT_CHARGING))
+ unit.clearUnitState(UNIT_STAT_CHARGING | UNIT_STAT_JUMPING);
+ if (arrived) // without this crash!
+ MovementInform(unit);
+}
+
+template<class T>
+void PointMovementGenerator<T>::MovementInform(T & /*unit*/)
+{
+}
+
+template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
+{
+ if (id == EVENT_FALL_GROUND)
+ {
+ unit.setDeathState(JUST_DIED);
+ unit.SetFlying(true);
+ }
+ unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
+}
+
+template void PointMovementGenerator<Player>::Initialize(Player&);
+template bool PointMovementGenerator<Player>::Update(Player &, const uint32 &diff);
+template void PointMovementGenerator<Player>::MovementInform(Player&);
+template void PointMovementGenerator<Player>::Finalize(Player&);
+
+template void PointMovementGenerator<Creature>::Initialize(Creature&);
+template bool PointMovementGenerator<Creature>::Update(Creature&, const uint32 &diff);
+template void PointMovementGenerator<Creature>::Finalize(Creature&);
+
+void AssistanceMovementGenerator::Finalize(Unit &unit)
+{
+ unit.ToCreature()->SetNoCallAssistance(false);
+ unit.ToCreature()->CallAssistance();
+ if (unit.isAlive())
+ unit.GetMotionMaster()->MoveSeekAssistanceDistract(sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
+}
+
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
new file mode 100644
index 00000000000..239cc73352a
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_POINTMOVEMENTGENERATOR_H
+#define TRINITY_POINTMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+#include "FollowerReference.h"
+
+template<class T>
+class PointMovementGenerator
+: public MovementGeneratorMedium< T, PointMovementGenerator<T> >
+{
+ public:
+ PointMovementGenerator(uint32 _id, float _x, float _y, float _z) : id(_id),
+ i_x(_x), i_y(_y), i_z(_z), i_nextMoveTime(0), arrived(false) {}
+
+ void Initialize(T &);
+ void Finalize(T &unit);
+ void Reset(T &unit){unit.StopMoving();}
+ bool Update(T &, const uint32 &diff);
+
+ void MovementInform(T &);
+
+ MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
+
+ bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; }
+ private:
+ uint32 id;
+ float i_x,i_y,i_z;
+ TimeTracker i_nextMoveTime;
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ bool arrived;
+};
+
+class AssistanceMovementGenerator
+: public PointMovementGenerator<Creature>
+{
+ public:
+ AssistanceMovementGenerator(float _x, float _y, float _z) :
+ PointMovementGenerator<Creature>(0, _x, _y, _z) {}
+
+ MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
+ void Finalize(Unit &);
+};
+
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
new file mode 100644
index 00000000000..74a703fb788
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Creature.h"
+#include "MapManager.h"
+#include "RandomMovementGenerator.h"
+#include "Traveller.h"
+#include "ObjectAccessor.h"
+#include "DestinationHolderImp.h"
+#include "Map.h"
+#include "Util.h"
+#include "CreatureGroups.h"
+
+#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV"
+
+template<>
+bool
+RandomMovementGenerator<Creature>::GetDestination(float &x, float &y, float &z) const
+{
+ if (i_destinationHolder.HasArrived())
+ return false;
+
+ i_destinationHolder.GetDestination(x, y, z);
+ return true;
+}
+
+#ifdef MAP_BASED_RAND_GEN
+#define rand_norm() creature.rand_norm()
+#endif
+
+template<>
+void
+RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
+{
+ float X,Y,Z,z,nx,ny,nz,ori,dist;
+
+ creature.GetHomePosition(X, Y, Z, ori);
+
+ z = creature.GetPositionZ();
+ Map const* map = creature.GetBaseMap();
+
+ // For 2D/3D system selection
+ //bool is_land_ok = creature.canWalk();
+ //bool is_water_ok = creature.canSwim();
+ bool is_air_ok = creature.canFly();
+
+ for (uint32 i = 0; ; ++i)
+ {
+
+ const float angle = rand_norm()*(M_PI*2);
+ const float range = rand_norm()*wander_distance;
+ const float distanceX = range * cos(angle);
+ const float distanceY = range * sin(angle);
+
+ nx = X + distanceX;
+ ny = Y + distanceY;
+
+ // prevent invalid coordinates generation
+ Trinity::NormalizeMapCoord(nx);
+ Trinity::NormalizeMapCoord(ny);
+
+ dist = (nx - X)*(nx - X) + (ny - Y)*(ny - Y);
+
+ if (i == 5)
+ {
+ nz = Z;
+ break;
+ }
+
+ if (is_air_ok) // 3D system above ground and above water (flying mode)
+ {
+ const float distanceZ = rand_norm() * sqrtf(dist)/2; // Limit height change
+ nz = Z + distanceZ;
+ float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height.
+ float wz = map->GetWaterLevel(nx, ny);
+ if (tz >= nz || wz >= nz)
+ continue; // Problem here, we must fly above the ground and water, not under. Let's try on next tick
+ }
+ //else if (is_water_ok) // 3D system under water and above ground (swimming mode)
+ else // 2D only
+ {
+ dist = dist >= 100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE)
+
+ // The fastest way to get an accurate result 90% of the time.
+ // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
+ nz = map->GetHeight(nx,ny,Z+dist-2.0f,false); // Map check
+ if (fabs(nz-Z)>dist)
+ {
+ nz = map->GetHeight(nx,ny,Z-2.0f,true); // Vmap Horizontal or above
+ if (fabs(nz-Z)>dist)
+ {
+ nz = map->GetHeight(nx,ny,Z+dist-2.0f,true); // Vmap Higher
+ if (fabs(nz-Z)>dist)
+ continue; // let's forget this bad coords where a z cannot be find and retry at next tick
+ }
+ }
+ }
+
+ break;
+ }
+
+ Traveller<Creature> traveller(creature);
+ creature.SetOrientation(creature.GetAngle(nx,ny));
+ i_destinationHolder.SetDestination(traveller, nx, ny, nz);
+ creature.addUnitState(UNIT_STAT_ROAMING);
+ if (is_air_ok)
+ {
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ }
+ //else if (is_water_ok) // Swimming mode to be done with more than this check
+ else
+ {
+ i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime()));
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ }
+
+ //Call for creature group update
+ if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
+ {
+ creature.GetFormation()->LeaderMoveTo(nx, ny, nz);
+ }
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Initialize(Creature &creature)
+{
+ if (!creature.isAlive())
+ return;
+
+ if (!wander_distance)
+ wander_distance = creature.GetRespawnRadius();
+
+ if (irand(0,RUNNING_CHANCE_RANDOMMV) > 0)
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ _setRandomLocation(creature);
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Reset(Creature &creature)
+{
+ Initialize(creature);
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Finalize(Creature & /*creature*/){}
+
+template<>
+bool
+RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
+{
+ if (creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
+ {
+ i_nextMoveTime.Update(i_nextMoveTime.GetExpiry()); // Expire the timer
+ creature.clearUnitState(UNIT_STAT_ROAMING);
+ return true;
+ }
+
+ i_nextMoveTime.Update(diff);
+
+ if (i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.canFly())
+ creature.clearUnitState(UNIT_STAT_ROAMING | UNIT_STAT_MOVE);
+
+ if (!i_destinationHolder.HasArrived() && creature.IsStopped())
+ creature.addUnitState(UNIT_STAT_ROAMING);
+
+ CreatureTraveller traveller(creature);
+
+ if (i_destinationHolder.UpdateTraveller(traveller, diff, true))
+ {
+ if (i_nextMoveTime.Passed())
+ {
+ if (irand(0,RUNNING_CHANCE_RANDOMMV) > 0)
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ _setRandomLocation(creature);
+ }
+ else if (creature.isPet() && creature.GetOwner() && !creature.IsWithinDist(creature.GetOwner(),PET_FOLLOW_DIST+2.5f))
+ {
+ creature.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ _setRandomLocation(creature);
+ }
+ }
+ return true;
+}
+
+
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
new file mode 100644
index 00000000000..0e5eed60bad
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_RANDOMMOTIONGENERATOR_H
+#define TRINITY_RANDOMMOTIONGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+
+template<class T>
+class RandomMovementGenerator
+: public MovementGeneratorMedium< T, RandomMovementGenerator<T> >
+{
+ public:
+ // Wander dist is related on db spawn dist. So what if we wanna set eandom movement on summoned creature?!
+ RandomMovementGenerator(float spawn_dist = 0.0f) : i_nextMoveTime(0), wander_distance(spawn_dist) {}
+
+ void _setRandomLocation(T &);
+ void Initialize(T &);
+ void Finalize(T &);
+ void Reset(T &);
+ bool Update(T &, const uint32 &);
+ bool GetDestination(float &x, float &y, float &z) const;
+ void UpdateMapPosition(uint32 mapid, float &x ,float &y, float &z)
+ {
+ i_destinationHolder.GetLocationNow(mapid, x,y,z);
+ }
+ MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
+ private:
+ TimeTrackerSmall i_nextMoveTime;
+
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ float wander_distance;
+ uint32 i_nextMove;
+};
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
new file mode 100644
index 00000000000..a2ad478bfd4
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ByteBuffer.h"
+#include "TargetedMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "DestinationHolderImp.h"
+#include "World.h"
+
+#define SMALL_ALPHA 0.05f
+
+#include <cmath>
+/*
+struct StackCleaner
+{
+ Creature &i_creature;
+ StackCleaner(Creature &creature) : i_creature(creature) {}
+ void Done(void) { i_creature.StopMoving(); }
+ ~StackCleaner()
+ {
+ i_creature->Clear();
+ }
+};
+*/
+
+template<class T>
+TargetedMovementGenerator<T>::TargetedMovementGenerator(Unit &target, float offset, float angle)
+: TargetedMovementGeneratorBase(target)
+, i_offset(offset), i_angle(angle), i_recalculateTravel(false)
+{
+ target.GetPosition(i_targetX, i_targetY, i_targetZ);
+}
+
+template<class T>
+bool
+TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
+{
+ if (!i_target.isValid() || !i_target->IsInWorld())
+ return false;
+
+ if (owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
+ return false;
+
+ float x, y, z;
+ Traveller<T> traveller(owner);
+ if (i_destinationHolder.HasDestination())
+ {
+ if (i_destinationHolder.HasArrived())
+ {
+ // prevent redundant micro-movement
+ if (!i_offset)
+ {
+ if (i_target->IsWithinMeleeRange(&owner))
+ return false;
+ }
+ else if (!i_angle && !owner.hasUnitState(UNIT_STAT_FOLLOW))
+ {
+ if (i_target->IsWithinDistInMap(&owner, i_offset))
+ return false;
+ }
+ else
+ {
+ if (i_target->IsWithinDistInMap(&owner, i_offset + 1.0f))
+ return false;
+ }
+ }
+ else
+ {
+ bool stop = false;
+ if (!i_offset)
+ {
+ if (i_target->IsWithinMeleeRange(&owner, 0))
+ stop = true;
+ }
+ else if (!i_angle && !owner.hasUnitState(UNIT_STAT_FOLLOW))
+ {
+ if (i_target->IsWithinDist(&owner, i_offset * 0.8f))
+ stop = true;
+ }
+
+ if (stop)
+ {
+ owner.GetPosition(x, y, z);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+ i_destinationHolder.StartTravel(traveller, false);
+ owner.StopMoving();
+ return false;
+ }
+ }
+
+ if (i_target->GetExactDistSq(i_targetX, i_targetY, i_targetZ) < 0.01f)
+ return false;
+ }
+
+ if (!i_offset)
+ {
+ // to nearest random contact position
+ i_target->GetRandomContactPoint(&owner, x, y, z, 0, MELEE_RANGE - 0.5f);
+ }
+ else if (!i_angle && !owner.hasUnitState(UNIT_STAT_FOLLOW))
+ {
+ // caster chase
+ i_target->GetContactPoint(&owner, x, y, z, i_offset * urand(80, 95) * 0.01f);
+ }
+ else
+ {
+ // to at i_offset distance from target and i_angle from target facing
+ i_target->GetClosePoint(x,y,z,owner.GetObjectSize(),i_offset,i_angle);
+ }
+
+ /*
+ We MUST not check the distance difference and avoid setting the new location for smaller distances.
+ By that we risk having far too many GetContactPoint() calls freezing the whole system.
+ In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
+ some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
+ If the distance to the target it too large to ignore,
+ but the distance to the new contact point is short enough to be ignored,
+ we will calculate a new contact point each update loop, but will never move to it.
+ The system will freeze.
+ ralf
+
+ //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
+ float bothObjectSize = i_target->GetObjectSize() + owner.GetObjectSize() + CONTACT_DISTANCE;
+ if (i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize)
+ return;
+ */
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+ owner.addUnitState(UNIT_STAT_CHASE);
+ i_destinationHolder.StartTravel(traveller);
+ return true;
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Initialize(T &owner)
+{
+ if (owner.isInCombat())
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ _setTargetLocation(owner);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Finalize(T &owner)
+{
+ owner.clearUnitState(UNIT_STAT_CHASE);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Reset(T &owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+bool
+TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
+{
+ if (!i_target.isValid() || !i_target->IsInWorld())
+ return false;
+
+ if (!&owner || !owner.isAlive())
+ return true;
+
+ if (owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING | UNIT_STAT_DISTRACTED))
+ return true;
+
+ // prevent movement while casting spells with cast time or channel time
+ if (owner.hasUnitState(UNIT_STAT_CASTING))
+ {
+ if (!owner.IsStopped())
+ owner.StopMoving();
+ return true;
+ }
+
+ // prevent crash after creature killed pet
+ if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget())
+ return true;
+
+ Traveller<T> traveller(owner);
+
+ if (!i_destinationHolder.HasDestination())
+ _setTargetLocation(owner);
+ else if (owner.IsStopped() && !i_destinationHolder.HasArrived())
+ {
+ owner.addUnitState(UNIT_STAT_CHASE);
+ i_destinationHolder.StartTravel(traveller);
+ return true;
+ }
+
+ if (i_destinationHolder.UpdateTraveller(traveller, time_diff))
+ {
+ // put targeted movement generators on a higher priority
+ //if (owner.GetObjectSize())
+ //i_destinationHolder.ResetUpdate(50);
+
+ // target moved
+ if (i_targetX != i_target->GetPositionX() || i_targetY != i_target->GetPositionY()
+ || i_targetZ != i_target->GetPositionZ())
+ {
+ if (_setTargetLocation(owner) || !owner.hasUnitState(UNIT_STAT_FOLLOW))
+ owner.SetInFront(i_target.getTarget());
+ i_target->GetPosition(i_targetX, i_targetY, i_targetZ);
+ }
+
+ if ((owner.IsStopped() && !i_destinationHolder.HasArrived()) || i_recalculateTravel)
+ {
+ i_recalculateTravel = false;
+ //Angle update will take place into owner.StopMoving()
+ owner.SetInFront(i_target.getTarget());
+
+ owner.StopMoving();
+ if (owner.IsWithinMeleeRange(i_target.getTarget()) && !owner.hasUnitState(UNIT_STAT_FOLLOW))
+ owner.Attack(i_target.getTarget(),true);
+ }
+ }
+
+ // Implemented for PetAI to handle resetting flags when pet owner reached
+ if (i_destinationHolder.HasArrived())
+ MovementInform(owner);
+
+ return true;
+}
+
+template<class T>
+Unit*
+TargetedMovementGenerator<T>::GetTarget() const
+{
+ return i_target.getTarget();
+}
+
+template<class T>
+void TargetedMovementGenerator<T>::MovementInform(T & /*unit*/)
+{
+}
+
+template <> void TargetedMovementGenerator<Creature>::MovementInform(Creature &unit)
+{
+ // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
+ unit.AI()->MovementInform(TARGETED_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+}
+
+template void TargetedMovementGenerator<Player>::MovementInform(Player&); // Not implemented for players
+template TargetedMovementGenerator<Player>::TargetedMovementGenerator(Unit &target, float offset, float angle);
+template TargetedMovementGenerator<Creature>::TargetedMovementGenerator(Unit &target, float offset, float angle);
+template bool TargetedMovementGenerator<Player>::_setTargetLocation(Player &);
+template bool TargetedMovementGenerator<Creature>::_setTargetLocation(Creature &);
+template void TargetedMovementGenerator<Player>::Initialize(Player &);
+template void TargetedMovementGenerator<Creature>::Initialize(Creature &);
+template void TargetedMovementGenerator<Player>::Finalize(Player &);
+template void TargetedMovementGenerator<Creature>::Finalize(Creature &);
+template void TargetedMovementGenerator<Player>::Reset(Player &);
+template void TargetedMovementGenerator<Creature>::Reset(Creature &);
+template bool TargetedMovementGenerator<Player>::Update(Player &, const uint32 &);
+template bool TargetedMovementGenerator<Creature>::Update(Creature &, const uint32 &);
+template Unit* TargetedMovementGenerator<Player>::GetTarget() const;
+template Unit* TargetedMovementGenerator<Creature>::GetTarget() const;
+
+
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
new file mode 100644
index 00000000000..d4204d23efa
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_TARGETEDMOVEMENTGENERATOR_H
+#define TRINITY_TARGETEDMOVEMENTGENERATOR_H
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "Traveller.h"
+#include "FollowerReference.h"
+
+class TargetedMovementGeneratorBase
+{
+ public:
+ TargetedMovementGeneratorBase(Unit &target) { i_target.link(&target, this); }
+ void stopFollowing() { }
+ protected:
+ FollowerReference i_target;
+};
+
+template<class T>
+class TargetedMovementGenerator
+: public MovementGeneratorMedium< T, TargetedMovementGenerator<T> >, public TargetedMovementGeneratorBase
+{
+ public:
+ TargetedMovementGenerator(Unit &target, float offset = 0, float angle = 0);
+ ~TargetedMovementGenerator() {}
+
+ void Initialize(T &);
+ void Finalize(T &);
+ void Reset(T &);
+ bool Update(T &, const uint32 &);
+ MovementGeneratorType GetMovementGeneratorType() { return TARGETED_MOTION_TYPE; }
+
+ void MovementInform(T &);
+
+ Unit* GetTarget() const;
+
+ bool GetDestination(float &x, float &y, float &z) const
+ {
+ if (i_destinationHolder.HasArrived() || !i_destinationHolder.HasDestination()) return false;
+ i_destinationHolder.GetDestination(x,y,z);
+ return true;
+ }
+
+ void unitSpeedChanged() { i_recalculateTravel=true; }
+ private:
+
+ bool _setTargetLocation(T &);
+
+ float i_offset;
+ float i_angle;
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ bool i_recalculateTravel;
+ float i_targetX, i_targetY, i_targetZ;
+};
+#endif
+
+
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
new file mode 100644
index 00000000000..2a0c7c0253d
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -0,0 +1,682 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+//Basic headers
+#include "WaypointMovementGenerator.h"
+#include "DestinationHolderImp.h"
+//Extended headers
+#include "ObjectMgr.h"
+#include "World.h"
+#include "MapManager.h" // for flightmaster grid preloading
+//Creature-specific headers
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "CreatureGroups.h"
+//Player-specific
+#include "Player.h"
+
+template<class T>
+void
+WaypointMovementGenerator<T>::Initialize(T & /*u*/){}
+
+template<>
+void
+WaypointMovementGenerator<Creature>::Finalize(Creature & /*u*/){}
+
+template<>
+void
+WaypointMovementGenerator<Player>::Finalize(Player & /*u*/){}
+
+template<class T>
+void
+WaypointMovementGenerator<T>::MovementInform(T & /*unit*/){}
+
+template<>
+void WaypointMovementGenerator<Creature>::MovementInform(Creature &unit)
+{
+ unit.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
+}
+
+template<>
+bool WaypointMovementGenerator<Creature>::GetDestination(float &x, float &y, float &z) const
+{
+ if (i_destinationHolder.HasArrived())
+ return false;
+
+ i_destinationHolder.GetDestination(x, y, z);
+ return true;
+}
+
+template<>
+bool WaypointMovementGenerator<Player>::GetDestination(float & /*x*/, float & /*y*/, float & /*z*/) const
+{
+ return false;
+}
+
+template<>
+void WaypointMovementGenerator<Creature>::Reset(Creature & /*unit*/)
+{
+ StopedByPlayer = true;
+ i_nextMoveTime.Reset(0);
+}
+
+template<>
+void WaypointMovementGenerator<Player>::Reset(Player & /*unit*/){}
+
+template<>
+void WaypointMovementGenerator<Creature>::InitTraveller(Creature &unit, const WaypointData &node)
+{
+ node.run ? unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE):
+ unit.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ unit.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
+ unit.SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
+
+ // TODO: make this part of waypoint node, so that creature can walk when desired?
+ if (unit.canFly())
+ unit.SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0x02);
+
+ unit.addUnitState(UNIT_STAT_ROAMING);
+}
+
+template<>
+void
+WaypointMovementGenerator<Creature>::Initialize(Creature &u)
+{
+ u.StopMoving();
+ //i_currentNode = -1; // uint32, become 0 in the first update
+ //i_nextMoveTime.Reset(0);
+ StopedByPlayer = false;
+ if (!path_id)
+ path_id = u.GetWaypointPath();
+ waypoints = sWaypointMgr->GetPath(path_id);
+ i_currentNode = 0;
+ if (waypoints && waypoints->size())
+ {
+ node = waypoints->front();
+ Traveller<Creature> traveller(u);
+ InitTraveller(u, *node);
+ i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z);
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+
+ //Call for creature group update
+ if (u.GetFormation() && u.GetFormation()->getLeader() == &u)
+ u.GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
+ }
+ else
+ node = NULL;
+}
+
+template<>
+void WaypointMovementGenerator<Player>::InitTraveller(Player & /*unit*/, const WaypointData & /*node*/){}
+
+template<class T>
+bool
+WaypointMovementGenerator<T>::Update(T & /*unit*/, const uint32 & /*diff*/)
+{
+ return false;
+}
+
+template<>
+bool
+WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff)
+{
+ if (!&unit)
+ return true;
+
+ if (!path_id)
+ return false;
+
+ // Waypoint movement can be switched on/off
+ // This is quite handy for escort quests and other stuff
+ if (unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
+ return true;
+
+ // Clear the generator if the path doesn't exist
+ if (!waypoints || !waypoints->size())
+ return false;
+
+ Traveller<Creature> traveller(unit);
+
+ i_nextMoveTime.Update(diff);
+ i_destinationHolder.UpdateTraveller(traveller, diff, true);
+
+ if (i_nextMoveTime.GetExpiry() < TIMEDIFF_NEXT_WP)
+ {
+ if (unit.IsStopped())
+ {
+ if (StopedByPlayer)
+ {
+ assert(node);
+ InitTraveller(unit, *node);
+ i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z);
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ StopedByPlayer = false;
+ return true;
+ }
+
+ if (i_currentNode == waypoints->size() - 1) // If that's our last waypoint
+ {
+ if (repeating) // If the movement is repeating
+ i_currentNode = 0; // Start moving all over again
+ else
+ {
+ unit.SetHomePosition(node->x, node->y, node->z, unit.GetOrientation());
+ unit.GetMotionMaster()->Initialize();
+ return false; // Clear the waypoint movement
+ }
+ }
+ else
+ ++i_currentNode;
+
+ node = waypoints->at(i_currentNode);
+ InitTraveller(unit, *node);
+ i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z);
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+
+ //Call for creature group update
+ if (unit.GetFormation() && unit.GetFormation()->getLeader() == &unit)
+ unit.GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
+ }
+ else
+ {
+ //Determine waittime
+ if (node->delay)
+ i_nextMoveTime.Reset(node->delay);
+
+ //note: disable "start" for mtmap
+ if (node->event_id && urand(0,99) < node->event_chance)
+ unit.GetMap()->ScriptsStart(sWaypointScripts, node->event_id, &unit, NULL/*, false*/);
+
+ i_destinationHolder.ResetTravelTime();
+ MovementInform(unit);
+ unit.UpdateWaypointID(i_currentNode);
+ unit.clearUnitState(UNIT_STAT_ROAMING);
+ unit.Relocate(node->x, node->y, node->z);
+ }
+ }
+ else
+ {
+ if (unit.IsStopped() && !i_destinationHolder.HasArrived())
+ {
+ if (!StopedByPlayer)
+ {
+ i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER);
+ i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER);
+ StopedByPlayer = true;
+ }
+ }
+ }
+ return true;
+}
+
+template void WaypointMovementGenerator<Player>::Initialize(Player &);
+template bool WaypointMovementGenerator<Player>::Update(Player &, const uint32 &);
+template void WaypointMovementGenerator<Player>::MovementInform(Player &);
+
+//----------------------------------------------------//
+void FlightPathMovementGenerator::LoadPath(Player &)
+{
+ objmgr.GetTaxiPathNodes(i_pathId, i_path,i_mapIds);
+}
+
+uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const
+{
+ if (i_currentNode >= i_mapIds.size())
+ return i_mapIds.size();
+
+ uint32 curMapId = i_mapIds[i_currentNode];
+ for (uint32 i = i_currentNode; i < i_mapIds.size(); ++i)
+ {
+ if (i_mapIds[i] != curMapId)
+ return i;
+ }
+
+ return i_mapIds.size();
+}
+
+void FlightPathMovementGenerator::Initialize(Player &player)
+{
+ player.getHostileRefManager().setOnlineOfflineState(false);
+ player.addUnitState(UNIT_STAT_IN_FLIGHT);
+ player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ LoadPath(player);
+ Traveller<Player> traveller(player);
+ // do not send movement, it was sent already
+ i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
+
+ player.SendMonsterMoveByPath(GetPath(), GetCurrentNode(), GetPathAtMapEnd());
+
+ // Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will
+ // be reinitialized for each flightmaster at the end of each spline (or stop) in the flight.
+
+ uint32 nodeCount = i_mapIds.size(); // Get the number of nodes in the path. i_path and i_mapIds are the
+ // same size when loaded in ObjectMgr::GetTaxiPathNodes, called from LoadPath()
+
+ m_endMapId = i_mapIds[nodeCount -1]; // Get the map ID from the last node
+ m_preloadTargetNode = nodeCount / 2; // Split the number of nodes in half to preload the flightmaster half-way through the flight
+ m_endGridX = i_path[nodeCount -1].x; // Get the X position from the last node
+ m_endGridY = i_path[nodeCount -1].y; // Get tye Y position from the last node
+}
+
+void FlightPathMovementGenerator::Finalize(Player & player)
+{
+ player.clearUnitState(UNIT_STAT_IN_FLIGHT);
+
+ float x, y, z;
+ i_destinationHolder.GetLocationNow(player.GetBaseMap(), x, y, z);
+ player.SetPosition(x, y, z, player.GetOrientation());
+}
+
+bool FlightPathMovementGenerator::Update(Player &player, const uint32 &diff)
+{
+ if (MovementInProgress())
+ {
+ Traveller<Player> traveller(player);
+ if (i_destinationHolder.UpdateTraveller(traveller, diff))
+ {
+ i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE);
+ if (i_destinationHolder.HasArrived())
+ {
+ uint32 curMap = i_mapIds[i_currentNode];
+ ++i_currentNode;
+ if (MovementInProgress())
+ {
+ DEBUG_LOG("loading node %u for player %s", i_currentNode, player.GetName());
+ if (i_mapIds[i_currentNode] == curMap)
+ {
+ // do not send movement, it was sent already
+ i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
+ }
+
+ // check if it's time to preload the flightmaster grid at path end
+ if (i_currentNode == m_preloadTargetNode)
+ PreloadEndGrid();
+
+ return true;
+ }
+ //else HasArrived()
+ }
+ else
+ return true;
+ }
+ else
+ return true;
+ }
+
+ // we have arrived at the end of the path
+ return false;
+}
+
+void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
+{
+ if (i_mapIds.empty())
+ return;
+
+ uint32 map0 = i_mapIds[0];
+ for (size_t i = 1; i < i_mapIds.size(); ++i)
+ {
+ if (i_mapIds[i] != map0)
+ {
+ i_currentNode = i;
+ return;
+ }
+ }
+}
+
+void FlightPathMovementGenerator::PreloadEndGrid()
+{
+ // used to preload the final grid where the flightmaster is
+ Map *endMap = MapManager::Instance().FindMap(m_endMapId);
+
+ // Load the grid
+ if (endMap)
+ {
+ sLog.outDetail("Preloading flightmaster at grid (%f, %f) for map %u", m_endGridX, m_endGridY, m_endMapId);
+ endMap->LoadGrid(m_endGridX, m_endGridY);
+ }
+ else
+ sLog.outDetail("Unable to determine map to preload flightmaster grid");
+}
+
+//
+// Unique1's ASTAR Pathfinding Code... For future use & reference...
+//
+
+#ifdef __PATHFINDING__
+
+int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
+
+int ShortenASTARRoute(short int *pathlist, int number)
+{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
+ short int temppathlist[MAX_PATHLIST_NODES];
+ int count = 0;
+ // int count2 = 0;
+ int temp, temp2;
+ int link;
+ int upto = 0;
+
+ for (temp = number; temp >= 0; temp--)
+ {
+ qboolean shortened = qfalse;
+
+ for (temp2 = 0; temp2 < temp; temp2++)
+ {
+ for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
+ {
+ if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
+ continue;
+
+ //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
+ // continue;
+
+ //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
+ // continue;
+
+ if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
+ { // Found a shorter route...
+ //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
+ {
+ temppathlist[count] = pathlist[temp2];
+ temp = temp2;
+ ++count;
+ shortened = qtrue;
+ }
+ }
+ }
+ }
+
+ if (!shortened)
+ {
+ temppathlist[count] = pathlist[temp];
+ ++count;
+ }
+ }
+
+ upto = count;
+
+ for (temp = 0; temp < count; temp++)
+ {
+ pathlist[temp] = temppathlist[upto];
+ --upto;
+ }
+
+ G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
+ return count;
+}
+
+/*
+===========================================================================
+CreatePathAStar
+This function uses the A* pathfinding algorithm to determine the
+shortest path between any two nodes.
+It's fairly complex, so I'm not really going to explain it much.
+Look up A* and binary heaps for more info.
+pathlist stores the ideal path between the nodes, in reverse order,
+and the return value is the number of nodes in that path
+===========================================================================
+*/
+int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
+{
+ //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
+ //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
+ short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
+ float gcost[MAX_NODES];
+ int fcost[MAX_NODES];
+ char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
+ short int parent[MAX_NODES];
+
+ short int numOpen = 0;
+ short int atNode, temp, newnode=-1;
+ qboolean found = qfalse;
+ int count = -1;
+ float gc;
+ int i, u, v, m;
+ vec3_t vec;
+
+ //clear out all the arrays
+ memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
+ memset(fcost, 0, sizeof(int)*MAX_NODES);
+ memset(list, 0, sizeof(char)*MAX_NODES);
+ memset(parent, 0, sizeof(short int)*MAX_NODES);
+ memset(gcost, -1, sizeof(float)*MAX_NODES);
+
+ //make sure we have valid data before calculating everything
+ if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
+ return -1;
+
+ openlist[1] = from; //add the starting node to the open list
+ ++numOpen;
+ gcost[from] = 0; //its f and g costs are obviously 0
+ fcost[from] = 0;
+
+ while (1)
+ {
+ if (numOpen != 0) //if there are still items in the open list
+ {
+ //pop the top item off of the list
+ atNode = openlist[1];
+ list[atNode] = 2; //put the node on the closed list so we don't check it again
+ --numOpen;
+
+ openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
+ v = 1;
+
+ //this while loop reorders the list so that the new lowest fcost is at the top again
+ while (1)
+ {
+ u = v;
+ if ((2*u+1) < numOpen) //if both children exist
+ {
+ if (fcost[openlist[u]] >= fcost[openlist[2*u]])
+ v = 2*u;
+ if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
+ v = 2*u+1;
+ }
+ else
+ {
+ if ((2*u) < numOpen) //if only one child exists
+ {
+ if (fcost[openlist[u]] >= fcost[openlist[2*u]])
+ v = 2*u;
+ }
+ }
+
+ if (u != v) //if they're out of order, swap this item with its parent
+ {
+ temp = openlist[u];
+ openlist[u] = openlist[v];
+ openlist[v] = temp;
+ }
+ else
+ break;
+ }
+
+ for (i = 0; i < nodes[atNode].enodenum; ++i) //loop through all the links for this node
+ {
+ newnode = nodes[atNode].links[i].targetNode;
+
+ //if this path is blocked, skip it
+ if (nodes[atNode].links[i].flags & PATH_BLOCKED)
+ continue;
+ //if this path is blocked, skip it
+ if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
+ continue;
+ //skip any unreachable nodes
+ if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
+ continue;
+ if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
+ continue;
+
+ if (list[newnode] == 2) //if this node is on the closed list, skip it
+ continue;
+
+ if (list[newnode] != 1) //if this node is not already on the open list
+ {
+ openlist[++numOpen] = newnode; //add the new node to the open list
+ list[newnode] = 1;
+ parent[newnode] = atNode; //record the node's parent
+
+ if (newnode == to) //if we've found the goal, don't keep computing paths!
+ break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
+
+ //store it's f cost value
+ fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
+
+ //this loop re-orders the heap so that the lowest fcost is at the top
+ m = numOpen;
+ while (m != 1) //while this item isn't at the top of the heap already
+ {
+ //if it has a lower fcost than its parent
+ if (fcost[openlist[m]] <= fcost[openlist[m/2]])
+ {
+ temp = openlist[m/2];
+ openlist[m/2] = openlist[m];
+ openlist[m] = temp; //swap them
+ m /= 2;
+ }
+ else
+ break;
+ }
+ }
+ else //if this node is already on the open list
+ {
+ gc = gcost[atNode];
+ VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
+ gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
+
+ if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
+ {
+ parent[newnode] = atNode; //set the new parent for this node
+ gcost[newnode] = gc; //and the new g cost
+
+ for (i = 1; i < numOpen; ++i) //loop through all the items on the open list
+ {
+ if (openlist[i] == newnode) //find this node in the list
+ {
+ //calculate the new fcost and store it
+ fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
+
+ //reorder the list again, with the lowest fcost item on top
+ m = i;
+ while (m != 1)
+ {
+ //if the item has a lower fcost than it's parent
+ if (fcost[openlist[m]] < fcost[openlist[m/2]])
+ {
+ temp = openlist[m/2];
+ openlist[m/2] = openlist[m];
+ openlist[m] = temp; //swap them
+ m /= 2;
+ }
+ else
+ break;
+ }
+ break; //exit the 'for' loop because we already changed this node
+ } //if
+ } //for
+ } //if (gc < gcost[newnode])
+ } //if (list[newnode] != 1) --> else
+ } //for (loop through links)
+ } //if (numOpen != 0)
+ else
+ {
+ found = qfalse; //there is no path between these nodes
+ break;
+ }
+
+ if (list[to] == 1) //if the destination node is on the open list, we're done
+ {
+ found = qtrue;
+ break;
+ }
+ } //while (1)
+
+ if (found == qtrue) //if we found a path
+ {
+ //G_Printf("%s - path found!n", bot->client->pers.netname);
+ count = 0;
+
+ temp = to; //start at the end point
+ while (temp != from) //travel along the path (backwards) until we reach the starting point
+ {
+ pathlist[count++] = temp; //add the node to the pathlist and increment the count
+ temp = parent[temp]; //move to the parent of this node to continue the path
+ }
+
+ pathlist[count++] = from; //add the beginning node to the end of the pathlist
+
+ #ifdef __BOT_SHORTEN_ROUTING__
+ count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
+ #endif //__BOT_SHORTEN_ROUTING__
+ }
+ else
+ {
+ //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
+ count = CreateDumbRoute(from, to, pathlist);
+
+ if (count > 0)
+ {
+ #ifdef __BOT_SHORTEN_ROUTING__
+ count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
+ #endif //__BOT_SHORTEN_ROUTING__
+ return count;
+ }
+ }
+
+ return count; //return the number of nodes in the path, -1 if not found
+}
+
+/*
+===========================================================================
+GetFCost
+Utility function used by A* pathfinding to calculate the
+cost to move between nodes towards a goal. Using the A*
+algorithm F = G + H, G here is the distance along the node
+paths the bot must travel, and H is the straight-line distance
+to the goal node.
+Returned as an int because more precision is unnecessary and it
+will slightly speed up heap access
+===========================================================================
+*/
+int GetFCost(int to, int num, int parentNum, float *gcost)
+{
+ float gc = 0;
+ float hc = 0;
+ vec3_t v;
+
+ if (gcost[num] == -1)
+ {
+ if (parentNum != -1)
+ {
+ gc = gcost[parentNum];
+ VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
+ gc += VectorLength(v);
+ }
+ gcost[num] = gc;
+ }
+ else
+ gc = gcost[num];
+
+ VectorSubtract(nodes[to].origin, nodes[num].origin, v);
+ hc = VectorLength(v);
+
+ return (int)(gc + hc);
+}
+#endif //__PATHFINDING__
+
+
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
new file mode 100644
index 00000000000..4b74e80e168
--- /dev/null
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_WAYPOINTMOVEMENTGENERATOR_H
+#define TRINITY_WAYPOINTMOVEMENTGENERATOR_H
+
+/** @page PathMovementGenerator is used to generate movements
+ * of waypoints and flight paths. Each serves the purpose
+ * of generate activities so that it generates updated
+ * packets for the players.
+ */
+
+#include "MovementGenerator.h"
+#include "DestinationHolder.h"
+#include "WaypointManager.h"
+#include "Path.h"
+#include "Traveller.h"
+
+#include "Player.h"
+
+#include <vector>
+#include <set>
+
+#define FLIGHT_TRAVEL_UPDATE 100
+#define STOP_TIME_FOR_PLAYER 3 * MINUTE * IN_MILISECONDS // 3 Minutes
+#define TIMEDIFF_NEXT_WP 250
+
+template<class T, class P = Path>
+class PathMovementBase
+{
+ public:
+ PathMovementBase() : i_currentNode(0) {}
+ virtual ~PathMovementBase() {};
+
+ bool MovementInProgress(void) const { return i_currentNode < i_path.Size(); }
+
+ void LoadPath(T &);
+ void ReloadPath(T &);
+ uint32 GetCurrentNode() const { return i_currentNode; }
+
+ protected:
+ uint32 i_currentNode;
+ DestinationHolder< Traveller<T> > i_destinationHolder;
+ P i_path;
+};
+
+template<class T>
+
+class WaypointMovementGenerator
+ : public MovementGeneratorMedium< T, WaypointMovementGenerator<T> >, public PathMovementBase<T>
+{
+ public:
+ WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true) :
+ i_nextMoveTime(0), path_id(_path_id), repeating(_repeating), StopedByPlayer(false), node(NULL) {}
+
+ void Initialize(T &);
+ void Finalize(T &);
+ void MovementInform(T &);
+ void InitTraveller(T &, const WaypointData &);
+ void GeneratePathId(T &);
+ void Reset(T &unit);
+ bool Update(T &, const uint32 &);
+ bool GetDestination(float &x, float &y, float &z) const;
+ MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
+
+ private:
+ WaypointData *node;
+ uint32 path_id;
+ TimeTrackerSmall i_nextMoveTime;
+ WaypointPath *waypoints;
+ bool repeating, StopedByPlayer;
+};
+
+/** FlightPathMovementGenerator generates movement of the player for the paths
+ * and hence generates ground and activities for the player.
+ */
+class FlightPathMovementGenerator
+: public MovementGeneratorMedium< Player, FlightPathMovementGenerator >,
+public PathMovementBase<Player>
+{
+ uint32 i_pathId;
+ std::vector<uint32> i_mapIds;
+ public:
+ explicit FlightPathMovementGenerator(uint32 id, uint32 startNode = 0) : i_pathId(id) { i_currentNode = startNode; }
+ void Initialize(Player &);
+ void Finalize(Player &);
+ void Reset(Player &) {}
+ bool Update(Player &, const uint32 &);
+ MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; }
+
+ void LoadPath(Player &);
+ void ReloadPath(Player &) { /* don't reload flight path */ }
+
+ Path& GetPath() { return i_path; }
+ uint32 GetPathAtMapEnd() const;
+ bool HasArrived() const { return (i_currentNode >= i_path.Size()); }
+ void SetCurrentNodeAfterTeleport();
+ void SkipCurrentNode() { ++i_currentNode; }
+ bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; }
+
+ private:
+ // storage for preloading the flightmaster grid at end
+ // before reaching final waypoint
+ uint32 m_endMapId;
+ uint32 m_preloadTargetNode;
+ float m_endGridX;
+ float m_endGridY;
+ void PreloadEndGrid();
+};
+#endif
+
+
diff --git a/src/server/game/Movement/MovementHandler.cpp b/src/server/game/Movement/MovementHandler.cpp
new file mode 100644
index 00000000000..1148fe174fc
--- /dev/null
+++ b/src/server/game/Movement/MovementHandler.cpp
@@ -0,0 +1,746 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "Corpse.h"
+#include "Player.h"
+#include "Vehicle.h"
+#include "SpellAuras.h"
+#include "MapManager.h"
+#include "Transports.h"
+#include "BattleGround.h"
+#include "WaypointMovementGenerator.h"
+#include "InstanceSaveMgr.h"
+#include "ObjectMgr.h"
+
+void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket & /*recv_data*/)
+{
+ sLog.outDebug("WORLD: got MSG_MOVE_WORLDPORT_ACK.");
+ HandleMoveWorldportAckOpcode();
+}
+
+void WorldSession::HandleMoveWorldportAckOpcode()
+{
+ // ignore unexpected far teleports
+ if (!GetPlayer()->IsBeingTeleportedFar())
+ return;
+
+ // get the teleport destination
+ WorldLocation &loc = GetPlayer()->GetTeleportDest();
+
+ // possible errors in the coordinate validity check
+ if (!MapManager::IsValidMapCoord(loc))
+ {
+ LogoutPlayer(false);
+ return;
+ }
+
+ // get the destination map entry, not the current one, this will fix homebind and reset greeting
+ MapEntry const* mEntry = sMapStore.LookupEntry(loc.GetMapId());
+ InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(loc.GetMapId());
+
+ // reset instance validity, except if going to an instance inside an instance
+ if (GetPlayer()->m_InstanceValid == false && !mInstance)
+ GetPlayer()->m_InstanceValid = true;
+
+ GetPlayer()->SetSemaphoreTeleportFar(false);
+
+ Map * oldMap = GetPlayer()->GetMap();
+ assert(oldMap);
+ if (GetPlayer()->IsInWorld())
+ {
+ sLog.outCrash("Player is still in world when teleported from map %u! to new map %u", oldMap->GetId(), loc.GetMapId());
+ oldMap->Remove(GetPlayer(), false);
+ }
+
+ // relocate the player to the teleport destination
+ Map * newMap = MapManager::Instance().CreateMap(loc.GetMapId(), GetPlayer(), 0);
+ // the CanEnter checks are done in TeleporTo but conditions may change
+ // while the player is in transit, for example the map may get full
+ if (!newMap || !newMap->CanEnter(GetPlayer()))
+ {
+ sLog.outError("Map %d could not be created for player %d, porting player to homebind", loc.GetMapId(), GetPlayer()->GetGUIDLow());
+ GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
+ return;
+ }
+ else
+ GetPlayer()->Relocate(&loc);
+
+ GetPlayer()->ResetMap();
+ GetPlayer()->SetMap(newMap);
+
+ GetPlayer()->SendInitialPacketsBeforeAddToMap();
+ if (!GetPlayer()->GetMap()->Add(GetPlayer()))
+ {
+ sLog.outError("WORLD: failed to teleport player %s (%d) to map %d because of unknown reason!", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.GetMapId());
+ GetPlayer()->ResetMap();
+ GetPlayer()->SetMap(oldMap);
+ GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
+ return;
+ }
+
+ // battleground state prepare (in case join to BG), at relogin/tele player not invited
+ // only add to bg group and object, if the player was invited (else he entered through command)
+ if (_player->InBattleGround())
+ {
+ // cleanup setting if outdated
+ if (!mEntry->IsBattleGroundOrArena())
+ {
+ // We're not in BG
+ _player->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE);
+ // reset destination bg team
+ _player->SetBGTeam(0);
+ }
+ // join to bg case
+ else if (BattleGround *bg = _player->GetBattleGround())
+ {
+ if (_player->IsInvitedForBattleGroundInstance(_player->GetBattleGroundId()))
+ bg->AddPlayer(_player);
+ }
+ }
+
+ GetPlayer()->SendInitialPacketsAfterAddToMap();
+
+ // flight fast teleport case
+ if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
+ {
+ if (!_player->InBattleGround())
+ {
+ // short preparations to continue flight
+ FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
+ flight->Initialize(*GetPlayer());
+ return;
+ }
+
+ // battleground state prepare, stop flight
+ GetPlayer()->GetMotionMaster()->MovementExpired();
+ GetPlayer()->CleanupAfterTaxiFlight();
+ }
+
+ // resurrect character at enter into instance where his corpse exist after add to map
+ Corpse *corpse = GetPlayer()->GetCorpse();
+ if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId())
+ {
+ if (mEntry->IsDungeon())
+ {
+ GetPlayer()->ResurrectPlayer(0.5f,false);
+ GetPlayer()->SpawnCorpseBones();
+ }
+ }
+
+ bool allowMount = !mEntry->IsDungeon() || mEntry->IsBattleGroundOrArena();
+ if (mInstance)
+ {
+ Difficulty diff = GetPlayer()->GetDifficulty(mEntry->IsRaid());
+ if (MapDifficulty const* mapDiff = GetMapDifficultyData(mEntry->MapID,diff))
+ {
+ if (mapDiff->resetTime)
+ {
+ if (uint32 timeReset = sInstanceSaveManager.GetResetTimeFor(mEntry->MapID,diff))
+ {
+ uint32 timeleft = timeReset - time(NULL);
+ GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft);
+ }
+ }
+ }
+ allowMount = mInstance->allowMount;
+ }
+
+ // mount allow check
+ if (!allowMount)
+ _player->RemoveAurasByType(SPELL_AURA_MOUNTED);
+
+ // update zone immediately, otherwise leave channel will cause crash in mtmap
+ uint32 newzone, newarea;
+ GetPlayer()->GetZoneAndAreaId(newzone, newarea);
+ GetPlayer()->UpdateZone(newzone, newarea);
+
+ // honorless target
+ if (GetPlayer()->pvpInfo.inHostileArea)
+ GetPlayer()->CastSpell(GetPlayer(), 2479, true);
+
+ // resummon pet
+ GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
+
+ //lets process all delayed operations on successful teleport
+ GetPlayer()->ProcessDelayedOperations();
+}
+
+void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data)
+{
+ sLog.outDebug("MSG_MOVE_TELEPORT_ACK");
+ uint64 guid;
+
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ uint32 flags, time;
+ recv_data >> flags >> time;
+ DEBUG_LOG("Guid " UI64FMTD, guid);
+ DEBUG_LOG("Flags %u, time %u", flags, time/IN_MILISECONDS);
+
+ Unit *mover = _player->m_mover;
+ Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
+
+ if (!plMover || !plMover->IsBeingTeleportedNear())
+ return;
+
+ if (guid != plMover->GetGUID())
+ return;
+
+ plMover->SetSemaphoreTeleportNear(false);
+
+ uint32 old_zone = plMover->GetZoneId();
+
+ WorldLocation const& dest = plMover->GetTeleportDest();
+
+ plMover->SetPosition(dest,true);
+
+ uint32 newzone, newarea;
+ plMover->GetZoneAndAreaId(newzone, newarea);
+ plMover->UpdateZone(newzone, newarea);
+
+ // new zone
+ if (old_zone != newzone)
+ {
+ // honorless target
+ if (plMover->pvpInfo.inHostileArea)
+ plMover->CastSpell(plMover, 2479, true);
+ }
+
+ // resummon pet
+ GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
+
+ //lets process all delayed operations on successful teleport
+ GetPlayer()->ProcessDelayedOperations();
+}
+
+void WorldSession::HandleMovementOpcodes(WorldPacket & recv_data)
+{
+ uint16 opcode = recv_data.GetOpcode();
+ //sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
+ recv_data.hexlike();
+
+ Unit *mover = _player->m_mover;
+
+ assert(mover != NULL); // there must always be a mover
+
+ Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
+
+ // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck
+ if (plMover && plMover->IsBeingTeleported())
+ {
+ recv_data.rpos(recv_data.wpos()); // prevent warnings spam
+ return;
+ }
+
+ /* extract packet */
+ uint64 guid;
+
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ MovementInfo movementInfo;
+ movementInfo.guid = guid;
+ ReadMovementInfo(recv_data, &movementInfo);
+ /*----------------*/
+
+ /* if (recv_data.size() != recv_data.rpos())
+ {
+ sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is " SIZEFMTD " bytes larger than it should be. Kicked as cheater.", _player->GetName(), _player->GetGUIDLow(), _player->GetSession()->GetAccountId(), recv_data.GetOpcode(), recv_data.size() - recv_data.rpos());
+ KickPlayer();*/
+ recv_data.rpos(recv_data.wpos()); // prevent warnings spam
+ /* return;
+ }*/
+
+ if (!Trinity::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o))
+ {
+ recv_data.rpos(recv_data.wpos()); // prevent warnings spam
+ return;
+ }
+
+ /* handle special cases */
+ if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT)
+ {
+ // transports size limited
+ // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped)
+ if (movementInfo.t_x > 50 || movementInfo.t_y > 50 || movementInfo.t_z > 50)
+ {
+ recv_data.rpos(recv_data.wpos()); // prevent warnings spam
+ return;
+ }
+
+ if (!Trinity::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y + movementInfo.t_y,
+ movementInfo.z + movementInfo.t_z, movementInfo.o + movementInfo.t_o))
+ {
+ recv_data.rpos(recv_data.wpos()); // prevent warnings spam
+ return;
+ }
+
+ // if we boarded a transport, add us to it
+ if (plMover && !plMover->GetTransport())
+ {
+ // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list
+ for (MapManager::TransportSet::const_iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter)
+ {
+ if ((*iter)->GetGUID() == movementInfo.t_guid)
+ {
+ plMover->m_transport = (*iter);
+ (*iter)->AddPassenger(plMover);
+ break;
+ }
+ }
+ }
+
+ if (!mover->GetTransport() && !mover->GetVehicle())
+ {
+ GameObject *go = mover->GetMap()->GetGameObject(movementInfo.t_guid);
+ if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT)
+ movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT;
+ }
+ }
+ else if (plMover && plMover->GetTransport()) // if we were on a transport, leave
+ {
+ plMover->m_transport->RemovePassenger(plMover);
+ plMover->m_transport = NULL;
+ movementInfo.t_x = 0.0f;
+ movementInfo.t_y = 0.0f;
+ movementInfo.t_z = 0.0f;
+ movementInfo.t_o = 0.0f;
+ movementInfo.t_time = 0;
+ movementInfo.t_seat = -1;
+ }
+
+ // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
+ if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight())
+ plMover->HandleFall(movementInfo);
+
+ if (plMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plMover->IsInWater())
+ {
+ // now client not include swimming flag in case jumping under water
+ plMover->SetInWater(!plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z));
+ }
+
+ /*----------------------*/
+
+ /* process position-change */
+ WorldPacket data(opcode, recv_data.size());
+ movementInfo.time = getMSTime();
+ movementInfo.guid = mover->GetGUID();
+ WriteMovementInfo(&data, &movementInfo);
+ mover->SendMessageToSet(&data, _player);
+
+ mover->m_movementInfo = movementInfo;
+
+ // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle()
+ if (mover->GetVehicle())
+ {
+ mover->SetOrientation(movementInfo.o);
+ return;
+ }
+
+ mover->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o);
+
+ if (plMover) // nothing is charmed, or player charmed
+ {
+ plMover->UpdateFallInformationIfNeed(movementInfo, opcode);
+
+ if (movementInfo.z < -500.0f)
+ {
+ if (plMover->InBattleGround()
+ && plMover->GetBattleGround()
+ && plMover->GetBattleGround()->HandlePlayerUnderMap(_player))
+ {
+ // do nothing, the handle already did if returned true
+ }
+ else
+ {
+ // NOTE: this is actually called many times while falling
+ // even after the player has been teleported away
+ // TODO: discard movement packets after the player is rooted
+ if (plMover->isAlive())
+ {
+ plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth());
+ // pl can be alive if GM/etc
+ if (!plMover->isAlive())
+ {
+ // change the death state to CORPSE to prevent the death timer from
+ // starting in the next player update
+ plMover->KillPlayer();
+ plMover->BuildPlayerRepop();
+ }
+ }
+
+ // cancel the death timer here if started
+ plMover->RepopAtGraveyard();
+ }
+ }
+ }
+ /*else // creature charmed
+ {
+ if (mover->canFly())
+ {
+ bool flying = mover->IsFlying();
+ if (flying != ((mover->GetByteValue(UNIT_FIELD_BYTES_1, 3) & 0x02) ? true : false))
+ mover->SetFlying(flying);
+ }
+ }*/
+
+ //sLog.outString("Receive Movement Packet %s:", opcodeTable[recv_data.GetOpcode()]);
+ //mover->OutMovementInfo();
+}
+
+void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
+{
+ uint32 opcode = recv_data.GetOpcode();
+ sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
+
+ /* extract packet */
+ uint64 guid;
+ uint32 unk1;
+ float newspeed;
+
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ // now can skip not our packet
+ if (_player->GetGUID() != guid)
+ {
+ recv_data.rpos(recv_data.wpos()); // prevent warnings spam
+ return;
+ }
+
+ // continue parse packet
+
+ recv_data >> unk1; // counter or moveEvent
+
+ MovementInfo movementInfo;
+ movementInfo.guid = guid;
+ ReadMovementInfo(recv_data, &movementInfo);
+
+ recv_data >> newspeed;
+ /*----------------*/
+
+ // client ACK send one packet for mounted/run case and need skip all except last from its
+ // in other cases anti-cheat check can be fail in false case
+ UnitMoveType move_type;
+ UnitMoveType force_move_type;
+
+ static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" };
+
+ switch(opcode)
+ {
+ case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; force_move_type = MOVE_WALK; break;
+ case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; force_move_type = MOVE_RUN; break;
+ case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_RUN_BACK; force_move_type = MOVE_RUN_BACK; break;
+ case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; force_move_type = MOVE_SWIM; break;
+ case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIM_BACK; force_move_type = MOVE_SWIM_BACK; break;
+ case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN_RATE; force_move_type = MOVE_TURN_RATE; break;
+ case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT; force_move_type = MOVE_FLIGHT; break;
+ case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLIGHT_BACK; force_move_type = MOVE_FLIGHT_BACK; break;
+ case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: move_type = MOVE_PITCH_RATE; force_move_type = MOVE_PITCH_RATE; break;
+ default:
+ sLog.outError("WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode);
+ return;
+ }
+
+ // skip all forced speed changes except last and unexpected
+ // in run/mounted case used one ACK and it must be skipped.m_forced_speed_changes[MOVE_RUN} store both.
+ if (_player->m_forced_speed_changes[force_move_type] > 0)
+ {
+ --_player->m_forced_speed_changes[force_move_type];
+ if (_player->m_forced_speed_changes[force_move_type] > 0)
+ return;
+ }
+
+ if (!_player->GetTransport() && fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f)
+ {
+ if (_player->GetSpeed(move_type) > newspeed) // must be greater - just correct
+ {
+ sLog.outError("%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value",
+ move_type_name[move_type], _player->GetName(), _player->GetSpeed(move_type), newspeed);
+ _player->SetSpeed(move_type,_player->GetSpeedRate(move_type),true);
+ }
+ else // must be lesser - cheating
+ {
+ sLog.outBasic("Player %s from account id %u kicked for incorrect speed (must be %f instead %f)",
+ _player->GetName(),_player->GetSession()->GetAccountId(),_player->GetSpeed(move_type), newspeed);
+ _player->GetSession()->KickPlayer();
+ }
+ }
+}
+
+void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data)
+{
+ sLog.outDebug("WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
+
+ uint64 guid;
+ recv_data >> guid;
+
+ if (GetPlayer()->IsInWorld())
+ if (Unit *mover = ObjectAccessor::GetUnit(*GetPlayer(), guid))
+ {
+ GetPlayer()->SetMover(mover);
+ if (mover != GetPlayer() && mover->canFly())
+ {
+ WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12);
+ data.append(mover->GetPackGUID());
+ data << uint32(0);
+ SendPacket(&data);
+ }
+ }
+ else
+ {
+ sLog.outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " UI64FMTD " and should be " UI64FMTD, guid, _player->m_mover->GetGUID());
+ GetPlayer()->SetMover(GetPlayer());
+ }
+}
+
+void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data)
+{
+ sLog.outDebug("WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER");
+
+ uint64 old_mover_guid;
+ if (!recv_data.readPackGUID(old_mover_guid))
+ return;
+
+ /*if (_player->m_mover->GetGUID() == old_mover_guid)
+ {
+ sLog.outError("HandleMoveNotActiveMover: incorrect mover guid: mover is " I64FMT " and should be " I64FMT " instead of " I64FMT, _player->m_mover->GetGUID(), _player->GetGUID(), old_mover_guid);
+ recv_data.rpos(recv_data.wpos()); // prevent warnings spam
+ return;
+ }*/
+
+ MovementInfo mi;
+ mi.guid = old_mover_guid;
+ ReadMovementInfo(recv_data, &mi);
+
+ //ReadMovementInfo(recv_data, &_player->m_mover->m_movementInfo);
+ _player->m_movementInfo = mi;
+}
+
+void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data)
+{
+ sLog.outDebug("WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE");
+ recv_data.hexlike();
+
+ uint64 vehicleGUID = _player->GetCharmGUID();
+
+ if (!vehicleGUID) // something wrong here...
+ {
+ recv_data.rpos(recv_data.wpos()); // prevent warnings spam
+ return;
+ }
+
+ uint64 guid;
+
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ MovementInfo mi;
+ mi.guid = guid;
+ ReadMovementInfo(recv_data, &mi);
+
+ _player->m_movementInfo = mi;
+
+ /*
+ ReadMovementInfo(recv_data, &_player->m_mover->m_movementInfo);*/
+ _player->ExitVehicle();
+}
+
+void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data)
+{
+ sLog.outDebug("WORLD: Recvd CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE");
+ recv_data.hexlike();
+
+ Unit* vehicle_base = GetPlayer()->GetVehicleBase();
+ if (!vehicle_base)
+ return;
+
+ switch (recv_data.GetOpcode())
+ {
+ case CMSG_REQUEST_VEHICLE_PREV_SEAT:
+ GetPlayer()->ChangeSeat(-1, false);
+ break;
+ case CMSG_REQUEST_VEHICLE_NEXT_SEAT:
+ GetPlayer()->ChangeSeat(-1, true);
+ break;
+ case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE:
+ {
+ uint64 guid; // current vehicle guid
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ ReadMovementInfo(recv_data, &vehicle_base->m_movementInfo);
+
+ uint64 accessory; // accessory guid
+ if (!recv_data.readPackGUID(accessory))
+ return;
+
+ int8 seatId;
+ recv_data >> seatId;
+
+ if (vehicle_base->GetGUID() != guid)
+ return;
+
+ if (!accessory)
+ GetPlayer()->ChangeSeat(-1, seatId > 0); // prev/next
+ else if (Unit *vehUnit = Unit::GetUnit(*GetPlayer(), accessory))
+ {
+ if (Vehicle *vehicle = vehUnit->GetVehicleKit())
+ if (vehicle->HasEmptySeat(seatId))
+ GetPlayer()->EnterVehicle(vehicle, seatId);
+ }
+ }
+ break;
+ case CMSG_REQUEST_VEHICLE_SWITCH_SEAT:
+ {
+ uint64 guid; // current vehicle guid
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ int8 seatId;
+ recv_data >> seatId;
+
+ if (vehicle_base->GetGUID() == guid)
+ GetPlayer()->ChangeSeat(seatId);
+ else if (Unit *vehUnit = Unit::GetUnit(*GetPlayer(), guid))
+ if (Vehicle *vehicle = vehUnit->GetVehicleKit())
+ if (vehicle->HasEmptySeat(seatId))
+ GetPlayer()->EnterVehicle(vehicle, seatId);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void WorldSession::HandleEnterPlayerVehicle(WorldPacket &data)
+{
+ // Read guid
+ uint64 guid;
+ data >> guid;
+
+ if (Player* pl=ObjectAccessor::FindPlayer(guid))
+ {
+ if (!pl->GetVehicleKit())
+ return;
+ if (!pl->IsInRaidWith(_player))
+ return;
+ if (!pl->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
+ return;
+ _player->EnterVehicle(pl);
+ }
+}
+
+void WorldSession::HandleEjectPasenger(WorldPacket &data)
+{
+ if (data.GetOpcode() == CMSG_EJECT_PASSENGER)
+ {
+ if (Vehicle* Vv= _player->GetVehicleKit())
+ {
+ uint64 guid;
+ data >> guid;
+ if (Player* Pl=ObjectAccessor::FindPlayer(guid))
+ Pl->ExitVehicle();
+ }
+ }
+}
+
+void WorldSession::HandleRequestVehicleExit(WorldPacket &recv_data)
+{
+ sLog.outDebug("WORLD: Recvd CMSG_REQUEST_VEHICLE_EXIT");
+ recv_data.hexlike();
+ GetPlayer()->ExitVehicle();
+}
+
+void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/)
+{
+ //sLog.outDebug("WORLD: Recvd CMSG_MOUNTSPECIAL_ANIM");
+
+ WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8);
+ data << uint64(GetPlayer()->GetGUID());
+
+ GetPlayer()->SendMessageToSet(&data, false);
+}
+
+void WorldSession::HandleMoveKnockBackAck(WorldPacket & recv_data)
+{
+ sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK");
+
+ uint64 guid; // guid - unused
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ recv_data.read_skip<uint32>(); // unk
+
+ MovementInfo movementInfo;
+ ReadMovementInfo(recv_data, &movementInfo);
+}
+
+void WorldSession::HandleMoveHoverAck(WorldPacket& recv_data)
+{
+ sLog.outDebug("CMSG_MOVE_HOVER_ACK");
+
+ uint64 guid; // guid - unused
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ recv_data.read_skip<uint32>(); // unk
+
+ MovementInfo movementInfo;
+ ReadMovementInfo(recv_data, &movementInfo);
+
+ recv_data.read_skip<uint32>(); // unk2
+}
+
+void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recv_data)
+{
+ sLog.outDebug("CMSG_MOVE_WATER_WALK_ACK");
+
+ uint64 guid; // guid - unused
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ recv_data.read_skip<uint32>(); // unk
+
+ MovementInfo movementInfo;
+ ReadMovementInfo(recv_data, &movementInfo);
+
+ recv_data.read_skip<uint32>(); // unk2
+}
+
+void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data)
+{
+ if (!_player->isAlive() || _player->isInCombat())
+ return;
+
+ uint64 summoner_guid;
+ bool agree;
+ recv_data >> summoner_guid;
+ recv_data >> agree;
+
+ _player->SummonIfPossible(agree);
+}
+
diff --git a/src/server/game/Movement/Path.h b/src/server/game/Movement/Path.h
new file mode 100644
index 00000000000..9ec079f3c9c
--- /dev/null
+++ b/src/server/game/Movement/Path.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITYCORE_PATH_H
+#define TRINITYCORE_PATH_H
+
+#include "Common.h"
+#include <vector>
+
+class Path
+{
+ public:
+ struct PathNode
+ {
+ float x,y,z;
+ };
+
+ void SetLength(const unsigned int sz)
+ {
+ i_nodes.resize(sz);
+ }
+
+ unsigned int Size() const { return i_nodes.size(); }
+ bool Empty() const { return i_nodes.empty(); }
+ void Resize(unsigned int sz) { i_nodes.resize(sz); }
+ void Clear(void) { i_nodes.clear(); }
+ PathNode const* GetNodes(uint32 start = 0) const { return &i_nodes[start]; }
+ float GetTotalLength() const { return GetTotalLength(0,Size()); }
+ float GetTotalLength(uint32 start, uint32 end) const
+ {
+ float len = 0, xd, yd, zd;
+ for (unsigned int idx=start+1; idx < end; ++idx)
+ {
+ xd = i_nodes[ idx ].x - i_nodes[ idx-1 ].x;
+ yd = i_nodes[ idx ].y - i_nodes[ idx-1 ].y;
+ zd = i_nodes[ idx ].z - i_nodes[ idx-1 ].z;
+ len += sqrtf(xd*xd + yd*yd + zd*zd);
+ }
+ return len;
+ }
+
+ float GetPassedLength(uint32 curnode, float x, float y, float z)
+ {
+ float len = 0, xd, yd, zd;
+ for (unsigned int idx=1; idx < curnode; ++idx)
+ {
+ xd = i_nodes[ idx ].x - i_nodes[ idx-1 ].x;
+ yd = i_nodes[ idx ].y - i_nodes[ idx-1 ].y;
+ zd = i_nodes[ idx ].z - i_nodes[ idx-1 ].z;
+ len += sqrtf(xd*xd + yd*yd + zd*zd);
+ }
+
+ if (curnode > 0)
+ {
+ xd = x - i_nodes[curnode-1].x;
+ yd = y - i_nodes[curnode-1].y;
+ zd = z - i_nodes[curnode-1].z;
+ len += sqrtf(xd*xd + yd*yd + zd*zd);
+ }
+
+ return len;
+ }
+
+ PathNode& operator[](const unsigned int idx) { return i_nodes[idx]; }
+ const PathNode& operator()(const unsigned int idx) const { return i_nodes[idx]; }
+
+ protected:
+ std::vector<PathNode> i_nodes;
+};
+#endif
+
diff --git a/src/server/game/Movement/TaxiHandler.cpp b/src/server/game/Movement/TaxiHandler.cpp
new file mode 100644
index 00000000000..b0660527f71
--- /dev/null
+++ b/src/server/game/Movement/TaxiHandler.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "UpdateMask.h"
+#include "Path.h"
+#include "WaypointMovementGenerator.h"
+#include "DestinationHolderImp.h"
+
+void WorldSession::HandleTaxiNodeStatusQueryOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("WORLD: Received CMSG_TAXINODE_STATUS_QUERY");
+
+ uint64 guid;
+
+ recv_data >> guid;
+ SendTaxiStatus(guid);
+}
+
+void WorldSession::SendTaxiStatus(uint64 guid)
+{
+ // cheating checks
+ Creature *unit = GetPlayer()->GetMap()->GetCreature(guid);
+ if (!unit)
+ {
+ sLog.outDebug("WorldSession::SendTaxiStatus - Unit (GUID: %u) not found.", uint32(GUID_LOPART(guid)));
+ return;
+ }
+
+ uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer()->GetTeam());
+
+ // not found nearest
+ if (curloc == 0)
+ return;
+
+ sLog.outDebug("WORLD: current location %u ",curloc);
+
+ WorldPacket data(SMSG_TAXINODE_STATUS, 9);
+ data << guid;
+ data << uint8(GetPlayer()->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0);
+ SendPacket(&data);
+ sLog.outDebug("WORLD: Sent SMSG_TAXINODE_STATUS");
+}
+
+void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket & recv_data)
+{
+ sLog.outDebug("WORLD: Received CMSG_TAXIQUERYAVAILABLENODES");
+
+ uint64 guid;
+ recv_data >> guid;
+
+ // cheating checks
+ Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
+ if (!unit)
+ {
+ sLog.outDebug("WORLD: HandleTaxiQueryAvailableNodes - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
+ return;
+ }
+
+ // remove fake death
+ if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
+ // unknown taxi node case
+ if (SendLearnNewTaxiNode(unit))
+ return;
+
+ // known taxi node case
+ SendTaxiMenu(unit);
+}
+
+void WorldSession::SendTaxiMenu(Creature* unit)
+{
+ // find current node
+ uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer()->GetTeam());
+
+ if (curloc == 0)
+ return;
+
+ bool lastTaxiCheaterState = GetPlayer()->isTaxiCheater();
+ if (unit->GetEntry() == 29480) GetPlayer()->SetTaxiCheater(true); // Grimwing in Ebon Hold, special case. NOTE: Not perfect, Zul'Aman should not be included according to WoWhead, and I think taxicheat includes it.
+
+ sLog.outDebug("WORLD: CMSG_TAXINODE_STATUS_QUERY %u ",curloc);
+
+ WorldPacket data(SMSG_SHOWTAXINODES, (4+8+4+8*4));
+ data << uint32(1);
+ data << uint64(unit->GetGUID());
+ data << uint32(curloc);
+ GetPlayer()->m_taxi.AppendTaximaskTo(data,GetPlayer()->isTaxiCheater());
+ SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent SMSG_SHOWTAXINODES");
+
+ GetPlayer()->SetTaxiCheater(lastTaxiCheaterState);
+}
+
+void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathNode)
+{
+ // remove fake death
+ if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
+ while (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
+ GetPlayer()->GetMotionMaster()->MovementExpired(false);
+
+ if (mountDisplayId)
+ GetPlayer()->Mount(mountDisplayId);
+
+ GetPlayer()->GetMotionMaster()->MoveTaxiFlight(path,pathNode);
+}
+
+bool WorldSession::SendLearnNewTaxiNode(Creature* unit)
+{
+ // find current node
+ uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer()->GetTeam());
+
+ if (curloc == 0)
+ return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result.
+
+ if (GetPlayer()->m_taxi.SetTaximaskNode(curloc))
+ {
+ WorldPacket msg(SMSG_NEW_TAXI_PATH, 0);
+ SendPacket(&msg);
+
+ WorldPacket update(SMSG_TAXINODE_STATUS, 9);
+ update << uint64(unit->GetGUID());
+ update << uint8(1);
+ SendPacket(&update);
+
+ return true;
+ }
+ else
+ return false;
+}
+
+void WorldSession::HandleActivateTaxiExpressOpcode (WorldPacket & recv_data)
+{
+ sLog.outDebug("WORLD: Received CMSG_ACTIVATETAXIEXPRESS");
+
+ uint64 guid;
+ uint32 node_count;
+
+ recv_data >> guid >> node_count;
+
+ Creature *npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
+ if (!npc)
+ {
+ sLog.outDebug("WORLD: HandleActivateTaxiExpressOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
+ return;
+ }
+ std::vector<uint32> nodes;
+
+ for (uint32 i = 0; i < node_count; ++i)
+ {
+ uint32 node;
+ recv_data >> node;
+ nodes.push_back(node);
+ }
+
+ if (nodes.empty())
+ return;
+
+ sLog.outDebug("WORLD: Received CMSG_ACTIVATETAXIEXPRESS from %d to %d" ,nodes.front(),nodes.back());
+
+ GetPlayer()->ActivateTaxiPathTo(nodes, npc);
+}
+
+void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data)
+{
+ sLog.outDebug("WORLD: Received CMSG_MOVE_SPLINE_DONE");
+
+ uint64 guid; // used only for proper packet read
+ if (!recv_data.readPackGUID(guid))
+ return;
+
+ MovementInfo movementInfo; // used only for proper packet read
+ ReadMovementInfo(recv_data, &movementInfo);
+
+ recv_data.read_skip<uint32>(); // unk
+
+ // in taxi flight packet received in 2 case:
+ // 1) end taxi path in far (multi-node) flight
+ // 2) switch from one map to other in case multim-map taxi path
+ // we need process only (1)
+
+ uint32 curDest = GetPlayer()->m_taxi.GetTaxiDestination();
+ if (!curDest)
+ return;
+
+ TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest);
+
+ // far teleport case
+ if (curDestNode && curDestNode->map_id != GetPlayer()->GetMapId())
+ {
+ if (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
+ {
+ // short preparations to continue flight
+ FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
+
+ flight->SetCurrentNodeAfterTeleport();
+ Path::PathNode const& node = flight->GetPath()[flight->GetCurrentNode()];
+ flight->SkipCurrentNode();
+
+ GetPlayer()->TeleportTo(curDestNode->map_id,node.x,node.y,node.z,GetPlayer()->GetOrientation());
+ }
+ return;
+ }
+
+ uint32 destinationnode = GetPlayer()->m_taxi.NextTaxiDestination();
+ if (destinationnode > 0) // if more destinations to go
+ {
+ // current source node for next destination
+ uint32 sourcenode = GetPlayer()->m_taxi.GetTaxiSource();
+
+ // Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path)
+ if (GetPlayer()->isTaxiCheater())
+ {
+ if (GetPlayer()->m_taxi.SetTaximaskNode(sourcenode))
+ {
+ WorldPacket data(SMSG_NEW_TAXI_PATH, 0);
+ _player->GetSession()->SendPacket(&data);
+ }
+ }
+
+ sLog.outDebug("WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode);
+
+ uint32 mountDisplayId = objmgr.GetTaxiMountDisplayId(sourcenode, GetPlayer()->GetTeam());
+
+ uint32 path, cost;
+ objmgr.GetTaxiPath(sourcenode, destinationnode, path, cost);
+
+ if (path && mountDisplayId)
+ SendDoFlight(mountDisplayId, path, 1); // skip start fly node
+ else
+ GetPlayer()->m_taxi.ClearTaxiDestinations(); // clear problematic path and next
+ return;
+ }
+
+ GetPlayer()->CleanupAfterTaxiFlight();
+ GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ());
+ if (GetPlayer()->pvpInfo.inHostileArea)
+ GetPlayer()->CastSpell(GetPlayer(), 2479, true);
+}
+
+void WorldSession::HandleActivateTaxiOpcode(WorldPacket & recv_data)
+{
+ sLog.outDebug("WORLD: Received CMSG_ACTIVATETAXI");
+
+ uint64 guid;
+ std::vector<uint32> nodes;
+ nodes.resize(2);
+
+ recv_data >> guid >> nodes[0] >> nodes[1];
+ sLog.outDebug("WORLD: Received CMSG_ACTIVATETAXI from %d to %d" ,nodes[0],nodes[1]);
+ Creature *npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
+ if (!npc)
+ {
+ sLog.outDebug("WORLD: HandleActivateTaxiOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)));
+ return;
+ }
+
+ GetPlayer()->ActivateTaxiPathTo(nodes, npc);
+}
diff --git a/src/server/game/Movement/Transports.cpp b/src/server/game/Movement/Transports.cpp
new file mode 100644
index 00000000000..444e115f8b9
--- /dev/null
+++ b/src/server/game/Movement/Transports.cpp
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+
+#include "Transports.h"
+#include "MapManager.h"
+#include "ObjectMgr.h"
+#include "Path.h"
+
+#include "WorldPacket.h"
+#include "DBCStores.h"
+#include "ProgressBar.h"
+
+#include "World.h"
+
+void MapManager::LoadTransports()
+{
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, name, period FROM transports");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u transports", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ bar.step();
+
+ Transport *t = new Transport;
+
+ Field *fields = result->Fetch();
+
+ uint32 entry = fields[0].GetUInt32();
+ std::string name = fields[1].GetCppString();
+ t->m_period = fields[2].GetUInt32();
+
+ const GameObjectInfo *goinfo = objmgr.GetGameObjectInfo(entry);
+
+ if (!goinfo)
+ {
+ sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry, name.c_str());
+ delete t;
+ continue;
+ }
+
+ if (goinfo->type != GAMEOBJECT_TYPE_MO_TRANSPORT)
+ {
+ sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template type wrong", entry, name.c_str());
+ delete t;
+ continue;
+ }
+
+ // sLog.outString("Loading transport %d between %s, %s", entry, name.c_str(), goinfo->name);
+
+ std::set<uint32> mapsUsed;
+
+ if (!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed))
+ // skip transports with empty waypoints list
+ {
+ sLog.outErrorDb("Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.",goinfo->moTransport.taxiPathId);
+ delete t;
+ continue;
+ }
+
+ float x, y, z, o;
+ uint32 mapid;
+ x = t->m_WayPoints[0].x; y = t->m_WayPoints[0].y; z = t->m_WayPoints[0].z; mapid = t->m_WayPoints[0].mapid; o = 1;
+
+ // creates the Gameobject
+ if (!t->Create(entry, mapid, x, y, z, o, 100, 0))
+ {
+ delete t;
+ continue;
+ }
+
+ m_Transports.insert(t);
+
+ for (std::set<uint32>::const_iterator i = mapsUsed.begin(); i != mapsUsed.end(); ++i)
+ m_TransportsByMap[*i].insert(t);
+
+ //If we someday decide to use the grid to track transports, here:
+ t->SetMap(MapManager::Instance().CreateMap(mapid, t, 0));
+
+ //t->GetMap()->Add<GameObject>((GameObject *)t);
+ ++count;
+ } while (result->NextRow());
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u transports", count);
+
+ // check transport data DB integrity
+ result = WorldDatabase.Query("SELECT gameobject.guid,gameobject.id,transports.name FROM gameobject,transports WHERE gameobject.id = transports.entry");
+ if (result) // wrong data found
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 guid = fields[0].GetUInt32();
+ uint32 entry = fields[1].GetUInt32();
+ std::string name = fields[2].GetCppString();
+ sLog.outErrorDb("Transport %u '%s' have record (GUID: %u) in `gameobject`. Transports DON'T must have any records in `gameobject` or its behavior will be unpredictable/bugged.",entry,name.c_str(),guid);
+ }
+ while (result->NextRow());
+ }
+}
+
+Transport::Transport() : GameObject()
+{
+ m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_ROTATION);
+}
+
+bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags)
+{
+ Relocate(x,y,z,ang);
+ // instance id and phaseMask isn't set to values different from std.
+
+ if (!IsPositionValid())
+ {
+ sLog.outError("Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",
+ guidlow,x,y);
+ return false;
+ }
+
+ Object::_Create(guidlow, 0, HIGHGUID_MO_TRANSPORT);
+
+ GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(guidlow);
+
+ if (!goinfo)
+ {
+ sLog.outErrorDb("Transport not created: entry in `gameobject_template` not found, guidlow: %u map: %u (X: %f Y: %f Z: %f) ang: %f",guidlow, mapid, x, y, z, ang);
+ return false;
+ }
+
+ m_goInfo = goinfo;
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
+
+ SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
+ //SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
+ SetUInt32Value(GAMEOBJECT_FLAGS, MAKE_PAIR32(0x28, 0x64));
+ SetUInt32Value(GAMEOBJECT_LEVEL, m_period);
+ SetEntry(goinfo->id);
+
+ SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
+
+ SetGoState(GO_STATE_READY);
+ SetGoType(GameobjectTypes(goinfo->type));
+
+ SetGoAnimProgress(animprogress);
+ if (dynflags)
+ SetUInt32Value(GAMEOBJECT_DYNAMIC, MAKE_PAIR32(0, dynflags));
+
+ SetName(goinfo->name);
+
+ return true;
+}
+
+struct keyFrame
+{
+ keyFrame(float _x, float _y, float _z, uint32 _mapid, int _actionflag, int _delay)
+ {
+ x = _x; y = _y; z = _z; mapid = _mapid; actionflag = _actionflag; delay = _delay; distFromPrev = -1; distSinceStop = -1; distUntilStop = -1;
+ tFrom = 0; tTo = 0;
+ }
+
+ float x;
+ float y;
+ float z;
+ uint32 mapid;
+ int actionflag;
+ int delay;
+ float distSinceStop;
+ float distUntilStop;
+ float distFromPrev;
+ float tFrom, tTo;
+};
+
+bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
+{
+ TransportPath path;
+ objmgr.GetTransportPathNodes(pathid, path);
+
+ if (path.Empty())
+ return false;
+
+ std::vector<keyFrame> keyFrames;
+ int mapChange = 0;
+ mapids.clear();
+ for (size_t i = 1; i < path.Size() - 1; ++i)
+ {
+ if (mapChange == 0)
+ {
+ if ((path[i].mapid == path[i+1].mapid))
+ {
+ keyFrame k(path[i].x, path[i].y, path[i].z, path[i].mapid, path[i].actionFlag, path[i].delay);
+ keyFrames.push_back(k);
+ mapids.insert(k.mapid);
+ }
+ else
+ {
+ mapChange = 1;
+ }
+ }
+ else
+ {
+ --mapChange;
+ }
+ }
+
+ int lastStop = -1;
+ int firstStop = -1;
+
+ // first cell is arrived at by teleportation :S
+ keyFrames[0].distFromPrev = 0;
+ if (keyFrames[0].actionflag == 2)
+ {
+ lastStop = 0;
+ }
+
+ // find the rest of the distances between key points
+ for (size_t i = 1; i < keyFrames.size(); ++i)
+ {
+ if ((keyFrames[i].actionflag == 1) || (keyFrames[i].mapid != keyFrames[i-1].mapid))
+ {
+ keyFrames[i].distFromPrev = 0;
+ }
+ else
+ {
+ keyFrames[i].distFromPrev =
+ sqrt(pow(keyFrames[i].x - keyFrames[i - 1].x, 2) +
+ pow(keyFrames[i].y - keyFrames[i - 1].y, 2) +
+ pow(keyFrames[i].z - keyFrames[i - 1].z, 2));
+ }
+ if (keyFrames[i].actionflag == 2)
+ {
+ // remember first stop frame
+ if (firstStop == -1)
+ firstStop = i;
+ lastStop = i;
+ }
+ }
+
+ float tmpDist = 0;
+ for (size_t i = 0; i < keyFrames.size(); ++i)
+ {
+ int j = (i + lastStop) % keyFrames.size();
+ if (keyFrames[j].actionflag == 2)
+ tmpDist = 0;
+ else
+ tmpDist += keyFrames[j].distFromPrev;
+ keyFrames[j].distSinceStop = tmpDist;
+ }
+
+ for (int i = int(keyFrames.size()) - 1; i >= 0; i--)
+ {
+ int j = (i + (firstStop+1)) % keyFrames.size();
+ tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev;
+ keyFrames[j].distUntilStop = tmpDist;
+ if (keyFrames[j].actionflag == 2)
+ tmpDist = 0;
+ }
+
+ for (size_t i = 0; i < keyFrames.size(); ++i)
+ {
+ if (keyFrames[i].distSinceStop < (30 * 30 * 0.5f))
+ keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop);
+ else
+ keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5f)) / 30) + 30;
+
+ if (keyFrames[i].distUntilStop < (30 * 30 * 0.5f))
+ keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop);
+ else
+ keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5f)) / 30) + 30;
+
+ keyFrames[i].tFrom *= 1000;
+ keyFrames[i].tTo *= 1000;
+ }
+
+ // for (int i = 0; i < keyFrames.size(); ++i) {
+ // sLog.outString("%f, %f, %f, %f, %f, %f, %f", keyFrames[i].x, keyFrames[i].y, keyFrames[i].distUntilStop, keyFrames[i].distSinceStop, keyFrames[i].distFromPrev, keyFrames[i].tFrom, keyFrames[i].tTo);
+ // }
+
+ // Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals
+ // speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2
+ int t = 0;
+ bool teleport = false;
+ if (keyFrames[keyFrames.size() - 1].mapid != keyFrames[0].mapid)
+ teleport = true;
+
+ WayPoint pos(keyFrames[0].mapid, keyFrames[0].x, keyFrames[0].y, keyFrames[0].z, teleport, 0);
+ m_WayPoints[0] = pos;
+ t += keyFrames[0].delay * 1000;
+
+ uint32 cM = keyFrames[0].mapid;
+ for (size_t i = 0; i < keyFrames.size() - 1; ++i)
+ {
+ float d = 0;
+ float tFrom = keyFrames[i].tFrom;
+ float tTo = keyFrames[i].tTo;
+
+ // keep the generation of all these points; we use only a few now, but may need the others later
+ if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)))
+ {
+ while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))
+ {
+ tFrom += 100;
+ tTo -= 100;
+
+ if (d > 0)
+ {
+ float newX, newY, newZ;
+ newX = keyFrames[i].x + (keyFrames[i + 1].x - keyFrames[i].x) * d / keyFrames[i + 1].distFromPrev;
+ newY = keyFrames[i].y + (keyFrames[i + 1].y - keyFrames[i].y) * d / keyFrames[i + 1].distFromPrev;
+ newZ = keyFrames[i].z + (keyFrames[i + 1].z - keyFrames[i].z) * d / keyFrames[i + 1].distFromPrev;
+
+ bool teleport = false;
+ if (keyFrames[i].mapid != cM)
+ {
+ teleport = true;
+ cM = keyFrames[i].mapid;
+ }
+
+ // sLog.outString("T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ);
+ WayPoint pos(keyFrames[i].mapid, newX, newY, newZ, teleport, i);
+ if (teleport)
+ m_WayPoints[t] = pos;
+ }
+
+ if (tFrom < tTo) // caught in tFrom dock's "gravitational pull"
+ {
+ if (tFrom <= 30000)
+ {
+ d = 0.5f * (tFrom / 1000) * (tFrom / 1000);
+ }
+ else
+ {
+ d = 0.5f * 30 * 30 + 30 * ((tFrom - 30000) / 1000);
+ }
+ d = d - keyFrames[i].distSinceStop;
+ }
+ else
+ {
+ if (tTo <= 30000)
+ {
+ d = 0.5f * (tTo / 1000) * (tTo / 1000);
+ }
+ else
+ {
+ d = 0.5f * 30 * 30 + 30 * ((tTo - 30000) / 1000);
+ }
+ d = keyFrames[i].distUntilStop - d;
+ }
+ t += 100;
+ }
+ t -= 100;
+ }
+
+ if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo)
+ t += 100 - ((long)keyFrames[i + 1].tTo % 100);
+ else
+ t += (long)keyFrames[i + 1].tTo % 100;
+
+ bool teleport = false;
+ if ((keyFrames[i + 1].actionflag == 1) || (keyFrames[i + 1].mapid != keyFrames[i].mapid))
+ {
+ teleport = true;
+ cM = keyFrames[i + 1].mapid;
+ }
+
+ WayPoint pos(keyFrames[i + 1].mapid, keyFrames[i + 1].x, keyFrames[i + 1].y, keyFrames[i + 1].z, teleport, i);
+
+ // sLog.outString("T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport);
+/*
+ if (keyFrames[i+1].delay > 5)
+ pos.delayed = true;
+*/
+ //if (teleport)
+ m_WayPoints[t] = pos;
+
+ t += keyFrames[i + 1].delay * 1000;
+ // sLog.outString("------");
+ }
+
+ uint32 timer = t;
+
+ // sLog.outDetail(" Generated %lu waypoints, total time %u.", (unsigned long)m_WayPoints.size(), timer);
+
+ m_curr = m_WayPoints.begin();
+ m_curr = GetNextWayPoint();
+ m_next = GetNextWayPoint();
+ m_pathTime = timer;
+
+ m_nextNodeTime = m_curr->first;
+
+ return true;
+}
+
+Transport::WayPointMap::const_iterator Transport::GetNextWayPoint()
+{
+ WayPointMap::const_iterator iter = m_curr;
+ ++iter;
+ if (iter == m_WayPoints.end())
+ iter = m_WayPoints.begin();
+ return iter;
+}
+
+void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z)
+{
+ Map const* oldMap = GetMap();
+ Relocate(x, y, z);
+
+ for (PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();)
+ {
+ Player *plr = *itr;
+ ++itr;
+
+ if (plr->isDead() && !plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
+ {
+ plr->ResurrectPlayer(1.0);
+ }
+ plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT);
+
+ //WorldPacket data(SMSG_811, 4);
+ //data << uint32(0);
+ //plr->GetSession()->SendPacket(&data);
+ }
+
+ //we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference...
+ //player far teleport would try to create same instance, but we need it NOW for transport...
+ //correct me if I'm wrong O.o
+ //yes, you're right
+
+ ResetMap();
+ Map * newMap = MapManager::Instance().CreateMap(newMapid, this, 0);
+ SetMap(newMap);
+ assert (GetMap());
+
+ if (oldMap != newMap)
+ {
+ UpdateForMap(oldMap);
+ UpdateForMap(newMap);
+ }
+}
+
+bool Transport::AddPassenger(Player* passenger)
+{
+ if (m_passengers.insert(passenger).second)
+ sLog.outDetail("Player %s boarded transport %s.", passenger->GetName(), GetName());
+ return true;
+}
+
+bool Transport::RemovePassenger(Player* passenger)
+{
+ if (m_passengers.erase(passenger))
+ sLog.outDetail("Player %s removed from transport %s.", passenger->GetName(), GetName());
+ return true;
+}
+
+void Transport::CheckForEvent(uint32 entry, uint32 wp_id)
+{
+ uint32 key = entry*100+wp_id;
+ if (objmgr.TransportEventMap.find(key) != objmgr.TransportEventMap.end())
+ GetMap()->ScriptsStart(sEventScripts, objmgr.TransportEventMap[key], this, NULL);
+}
+
+void Transport::Update(uint32 /*p_time*/)
+{
+ if (m_WayPoints.size() <= 1)
+ return;
+
+ m_timer = getMSTime() % m_period;
+ while (((m_timer - m_curr->first) % m_pathTime) > ((m_next->first - m_curr->first) % m_pathTime))
+ {
+ m_curr = GetNextWayPoint();
+ m_next = GetNextWayPoint();
+
+ // first check help in case client-server transport coordinates de-synchronization
+ if (m_curr->second.mapid != GetMapId() || m_curr->second.teleport)
+ {
+ TeleportTransport(m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z);
+ }
+ else
+ {
+ Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z);
+ }
+/*
+ if (m_curr->second.delayed)
+ {
+ switch (GetEntry())
+ {
+ case 176495:
+ case 164871:
+ case 175080:
+ SendPlaySound(11804, false); break; // ZeppelinDocked
+ case 20808:
+ case 181646:
+ case 176231:
+ case 176244:
+ case 176310:
+ case 177233:
+ SendPlaySound(5495, false);break; // BoatDockingWarning
+ default:
+ SendPlaySound(5154, false); break; // ShipDocked
+ }
+ }
+*/
+ /*
+ for (PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();)
+ {
+ PlayerSet::const_iterator it2 = itr;
+ ++itr;
+ //(*it2)->SetPosition(m_curr->second.x + (*it2)->GetTransOffsetX(), m_curr->second.y + (*it2)->GetTransOffsetY(), m_curr->second.z + (*it2)->GetTransOffsetZ(), (*it2)->GetTransOffsetO());
+ }
+ */
+
+ m_nextNodeTime = m_curr->first;
+
+ if (m_curr == m_WayPoints.begin() && (sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES) == 0)
+ sLog.outDetail(" ************ BEGIN ************** %s", this->m_name.c_str());
+
+ if ((sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES) == 0)
+ sLog.outDetail("%s moved to %d %f %f %f %d", this->m_name.c_str(), m_curr->second.id, m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid);
+
+ //Transport Event System
+ CheckForEvent(this->GetEntry(), m_curr->second.id);
+ }
+}
+
+void Transport::UpdateForMap(Map const* targetMap)
+{
+ Map::PlayerList const& pl = targetMap->GetPlayers();
+ if (pl.isEmpty())
+ return;
+
+ if (GetMapId() == targetMap->GetId())
+ {
+ for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
+ {
+ if (this != itr->getSource()->GetTransport())
+ {
+ UpdateData transData;
+ BuildCreateUpdateBlockForPlayer(&transData, itr->getSource());
+ WorldPacket packet;
+ transData.BuildPacket(&packet);
+ itr->getSource()->SendDirectMessage(&packet);
+ }
+ }
+ }
+ else
+ {
+ UpdateData transData;
+ BuildOutOfRangeUpdateBlock(&transData);
+ WorldPacket out_packet;
+ transData.BuildPacket(&out_packet);
+
+ for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
+ if (this != itr->getSource()->GetTransport())
+ itr->getSource()->SendDirectMessage(&out_packet);
+ }
+}
+
diff --git a/src/server/game/Movement/Transports.h b/src/server/game/Movement/Transports.h
new file mode 100644
index 00000000000..25b9ade1461
--- /dev/null
+++ b/src/server/game/Movement/Transports.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRANSPORTS_H
+#define TRANSPORTS_H
+
+#include "GameObject.h"
+
+#include <map>
+#include <set>
+#include <string>
+
+class TransportPath
+{
+ public:
+ struct PathNode
+ {
+ uint32 mapid;
+ float x,y,z;
+ uint32 actionFlag;
+ uint32 delay;
+ };
+
+ void SetLength(const unsigned int sz)
+ {
+ i_nodes.resize(sz);
+ }
+
+ unsigned int Size(void) const { return i_nodes.size(); }
+ bool Empty(void) const { return i_nodes.empty(); }
+ void Resize(unsigned int sz) { i_nodes.resize(sz); }
+ void Clear(void) { i_nodes.clear(); }
+ PathNode* GetNodes(void) { return static_cast<PathNode *>(&i_nodes[0]); }
+
+ PathNode& operator[](const unsigned int idx) { return i_nodes[idx]; }
+ const PathNode& operator()(const unsigned int idx) const { return i_nodes[idx]; }
+
+ protected:
+ std::vector<PathNode> i_nodes;
+};
+
+class Transport : public GameObject
+{
+ public:
+ explicit Transport();
+
+ bool Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags);
+ bool GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids);
+ void Update(uint32 p_time);
+ bool AddPassenger(Player* passenger);
+ bool RemovePassenger(Player* passenger);
+ void CheckForEvent(uint32 entry, uint32 wp_id);
+
+ typedef std::set<Player*> PlayerSet;
+ PlayerSet const& GetPassengers() const { return m_passengers; }
+
+ private:
+ struct WayPoint
+ {
+ WayPoint() : mapid(0), x(0), y(0), z(0), teleport(false), id(0) {}
+ WayPoint(uint32 _mapid, float _x, float _y, float _z, bool _teleport, uint32 _id) :
+ mapid(_mapid), x(_x), y(_y), z(_z), teleport(_teleport), id(_id) {}
+ uint32 mapid;
+ float x;
+ float y;
+ float z;
+ bool teleport;
+ uint32 id;
+ };
+
+ typedef std::map<uint32, WayPoint> WayPointMap;
+
+ WayPointMap::const_iterator m_curr;
+ WayPointMap::const_iterator m_next;
+ uint32 m_pathTime;
+ uint32 m_timer;
+
+ PlayerSet m_passengers;
+
+ public:
+ WayPointMap m_WayPoints;
+ uint32 m_nextNodeTime;
+ uint32 m_period;
+
+ private:
+ void TeleportTransport(uint32 newMapid, float x, float y, float z);
+ void UpdateForMap(Map const* map);
+ WayPointMap::const_iterator GetNextWayPoint();
+};
+#endif
+
diff --git a/src/server/game/Movement/Traveller.h b/src/server/game/Movement/Traveller.h
new file mode 100644
index 00000000000..c581f7017b5
--- /dev/null
+++ b/src/server/game/Movement/Traveller.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_TRAVELLER_H
+#define TRINITY_TRAVELLER_H
+
+#include "Creature.h"
+#include "Player.h"
+#include <cassert>
+
+/** Traveller is a wrapper for units (creatures or players) that
+ * travel from point A to point B using the destination holder.
+ */
+#define PLAYER_FLIGHT_SPEED 32.0f
+
+template<class T>
+struct Traveller
+{
+ T &i_traveller;
+ Traveller(T &t) : i_traveller(t) {}
+ Traveller(const Traveller &obj) : i_traveller(obj) {}
+ Traveller& operator=(const Traveller &obj)
+ {
+ this->~Traveller();
+ new (this) Traveller(obj);
+ return *this;
+ }
+
+ operator T&(void) { return i_traveller; }
+ operator const T&(void) { return i_traveller; }
+ float GetPositionX() const { return i_traveller.GetPositionX(); }
+ float GetPositionY() const { return i_traveller.GetPositionY(); }
+ float GetPositionZ() const { return i_traveller.GetPositionZ(); }
+ T& GetTraveller(void) { return i_traveller; }
+
+ float Speed(void) { assert(false); return 0.0f; }
+ float GetMoveDestinationTo(float x, float y, float z);
+ uint32 GetTotalTrevelTimeTo(float x, float y, float z);
+
+ void Relocation(float x, float y, float z, float orientation) {}
+ void Relocation(float x, float y, float z) { Relocation(x, y, z, i_traveller.GetOrientation()); }
+ void MoveTo(float x, float y, float z, uint32 t) {}
+};
+
+template<class T>
+inline uint32 Traveller<T>::GetTotalTrevelTimeTo(float x, float y, float z)
+{
+ float dist = GetMoveDestinationTo(x,y,z);
+ float speed = Speed();;
+ if (speed <= 0.0f)
+ return 0xfffffffe; // almost infinity-unit should stop
+ else
+ speed *= 0.001f; // speed is in seconds so convert from second to millisecond
+ return static_cast<uint32>(dist/speed);
+}
+
+// specialization for creatures
+template<>
+inline float Traveller<Creature>::Speed()
+{
+ if (i_traveller.hasUnitState(UNIT_STAT_CHARGING))
+ return i_traveller.m_TempSpeed;
+ else if (i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
+ return i_traveller.GetSpeed(MOVE_WALK);
+ else if (i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_FLYING))
+ return i_traveller.GetSpeed(MOVE_FLIGHT);
+ else
+ return i_traveller.GetSpeed(MOVE_RUN);
+}
+
+template<>
+inline void Traveller<Creature>::Relocation(float x, float y, float z, float orientation)
+{
+ i_traveller.SetPosition(x, y, z, orientation);
+}
+
+template<>
+inline float Traveller<Creature>::GetMoveDestinationTo(float x, float y, float z)
+{
+ float dx = x - GetPositionX();
+ float dy = y - GetPositionY();
+ float dz = z - GetPositionZ();
+
+ //if (i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_FLYING))
+ return sqrt((dx*dx) + (dy*dy) + (dz*dz));
+ //else //Walking on the ground
+ // return sqrt((dx*dx) + (dy*dy));
+}
+
+template<>
+inline void Traveller<Creature>::MoveTo(float x, float y, float z, uint32 t)
+{
+ //i_traveller.AI_SendMoveToPacket(x, y, z, t, i_traveller.GetUnitMovementFlags(), 0);
+ i_traveller.SendMonsterMove(x, y, z, t);
+}
+
+// specialization for players
+template<>
+inline float Traveller<Player>::Speed()
+{
+ if (i_traveller.hasUnitState(UNIT_STAT_CHARGING))
+ return i_traveller.m_TempSpeed;
+ else if (i_traveller.isInFlight())
+ return PLAYER_FLIGHT_SPEED;
+ else
+ return i_traveller.GetSpeed(i_traveller.m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN);
+}
+
+template<>
+inline float Traveller<Player>::GetMoveDestinationTo(float x, float y, float z)
+{
+ float dx = x - GetPositionX();
+ float dy = y - GetPositionY();
+ float dz = z - GetPositionZ();
+
+ //if (i_traveller.isInFlight())
+ return sqrt((dx*dx) + (dy*dy) + (dz*dz));
+ //else //Walking on the ground
+ // return sqrt((dx*dx) + (dy*dy));
+}
+
+template<>
+inline void Traveller<Player>::Relocation(float x, float y, float z, float orientation)
+{
+ i_traveller.SetPosition(x, y, z, orientation);
+}
+
+template<>
+inline void Traveller<Player>::MoveTo(float x, float y, float z, uint32 t)
+{
+ //Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags
+ i_traveller.SendMonsterMove(x, y, z, t);
+}
+
+typedef Traveller<Creature> CreatureTraveller;
+typedef Traveller<Player> PlayerTraveller;
+#endif
+
diff --git a/src/server/game/Movement/WaypointManager.cpp b/src/server/game/Movement/WaypointManager.cpp
new file mode 100644
index 00000000000..0cbdd0bf975
--- /dev/null
+++ b/src/server/game/Movement/WaypointManager.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Database/DatabaseEnv.h"
+#include "GridDefines.h"
+#include "WaypointManager.h"
+#include "ProgressBar.h"
+#include "MapManager.h"
+
+void WaypointStore::Free()
+{
+ for (UNORDERED_MAP<uint32, WaypointPath*>::const_iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr)
+ {
+ for (WaypointPath::const_iterator it = itr->second->begin(); it != itr->second->end(); ++it)
+ delete *it;
+ itr->second->clear();
+ delete itr->second;
+ }
+ waypoint_map.clear();
+}
+
+void WaypointStore::Load()
+{
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT COUNT(id) FROM waypoint_data");
+ if (!result)
+ {
+ sLog.outError("an error occured while loading the table `waypoint_data` (maybe it doesn't exist ?)");
+ exit(1); // Stop server at loading non exited table or not accessable table
+ }
+
+ records = (*result)[0].GetUInt32();
+
+ result = WorldDatabase.Query("SELECT id,point,position_x,position_y,position_z,move_flag,delay,action,action_chance FROM waypoint_data ORDER BY id, point");
+ if (!result)
+ {
+ sLog.outErrorDb("The table `waypoint_data` is empty or corrupted");
+ return;
+ }
+
+ WaypointPath* path_data = NULL;
+ uint32 total_records = result->GetRowCount();
+
+ barGoLink bar(total_records);
+ uint32 count = 0;
+ Field *fields;
+ uint32 last_id = 0;
+
+ do
+ {
+ fields = result->Fetch();
+ uint32 id = fields[0].GetUInt32();
+ bar.step();
+ count++;
+ WaypointData *wp = new WaypointData;
+
+ if (last_id != id)
+ path_data = new WaypointPath;
+
+ float x,y,z;
+ x = fields[2].GetFloat();
+ y = fields[3].GetFloat();
+ z = fields[4].GetFloat();
+
+ Trinity::NormalizeMapCoord(x);
+ Trinity::NormalizeMapCoord(y);
+
+ wp->id = fields[1].GetUInt32();
+ wp->x = x;
+ wp->y = y;
+ wp->z = z;
+ wp->run = fields[5].GetBool();
+ wp->delay = fields[6].GetUInt32();
+ wp->event_id = fields[7].GetUInt32();
+ wp->event_chance = fields[8].GetUInt8();
+
+ path_data->push_back(wp);
+
+ if (id != last_id)
+ waypoint_map[id] = path_data;
+
+ last_id = id;
+
+ } while (result->NextRow()) ;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u waypoints", count);
+}
+
+void WaypointStore::UpdatePath(uint32 id)
+{
+ if (waypoint_map.find(id)!= waypoint_map.end())
+ waypoint_map[id]->clear();
+
+ QueryResult_AutoPtr result;
+
+ result = WorldDatabase.PQuery("SELECT point,position_x,position_y,position_z,move_flag,delay,action,action_chance FROM waypoint_data WHERE id = %u ORDER BY point", id);
+
+ if (!result)
+ return;
+
+ WaypointPath* path_data;
+ path_data = new WaypointPath;
+ Field *fields;
+
+ do
+ {
+ fields = result->Fetch();
+
+ WaypointData *wp = new WaypointData;
+
+ float x,y,z;
+ x = fields[1].GetFloat();
+ y = fields[2].GetFloat();
+ z = fields[3].GetFloat();
+
+ Trinity::NormalizeMapCoord(x);
+ Trinity::NormalizeMapCoord(y);
+
+ wp->id = fields[0].GetUInt32();
+ wp->x = x;
+ wp->y = y;
+ wp->z = z;
+ wp->run = fields[4].GetBool();
+ wp->delay = fields[5].GetUInt32();
+ wp->event_id = fields[6].GetUInt32();
+ wp->event_chance = fields[7].GetUInt8();
+
+ path_data->push_back(wp);
+
+ }
+ while (result->NextRow());
+
+ waypoint_map[id] = path_data;
+}
+
diff --git a/src/server/game/Movement/WaypointManager.h b/src/server/game/Movement/WaypointManager.h
new file mode 100644
index 00000000000..7efca558146
--- /dev/null
+++ b/src/server/game/Movement/WaypointManager.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TRINITY_WAYPOINTMANAGER_H
+#define TRINITY_WAYPOINTMANAGER_H
+
+#include <ace/Singleton.h>
+#include <ace/Null_Mutex.h>
+#include <vector>
+
+struct WaypointData
+{
+ uint32 id;
+ float x,y,z;
+ bool run;
+ uint32 delay;
+ uint32 event_id;
+ uint8 event_chance;
+};
+
+typedef std::vector<WaypointData*> WaypointPath;
+
+class WaypointStore
+{
+ private :
+ uint32 records;
+ UNORDERED_MAP<uint32, WaypointPath*> waypoint_map;
+
+ public:
+ // Null Mutex is OK because WaypointMgr is initialized in the World thread before World is initialized
+ static WaypointStore* instance() { return ACE_Singleton<WaypointStore, ACE_Null_Mutex>::instance(); }
+
+ ~WaypointStore() { Free(); }
+ void UpdatePath(uint32 id);
+ void Load();
+ void Free();
+
+ WaypointPath* GetPath(uint32 id)
+ {
+ if (waypoint_map.find(id) != waypoint_map.end())
+ return waypoint_map[id];
+ else return 0;
+ }
+
+ inline uint32 GetRecordsCount() { return records; }
+};
+
+#define sWaypointMgr WaypointStore::instance()
+
+#endif
+