diff options
| author | Rat <none@none> | 2010-06-05 23:40:08 +0200 |
|---|---|---|
| committer | Rat <none@none> | 2010-06-05 23:40:08 +0200 |
| commit | 75b80d9f5b02a643c983b2fb1ededed79fd5d133 (patch) | |
| tree | ebd1c2cc12a2715909dd04c1ed147a260c6ceb14 /src/server/game/Movement | |
| parent | 6a9357b13d7ea6bd7d77dbfc6587af9028caa401 (diff) | |
rearranged core files
--HG--
branch : trunk
Diffstat (limited to 'src/server/game/Movement')
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 + |
