diff options
Diffstat (limited to 'src')
64 files changed, 551 insertions, 191 deletions
diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp index 2fbdfe05b52..0b525fc1ab6 100644 --- a/src/common/Collision/DynamicTree.cpp +++ b/src/common/Collision/DynamicTree.cpp @@ -21,6 +21,7 @@ #include "GameObjectModel.h" #include "Log.h" #include "MapTree.h" +#include "ModelIgnoreFlags.h" #include "ModelInstance.h" #include "RegularGrid.h" #include "Timer.h" @@ -142,7 +143,7 @@ struct DynamicTreeIntersectionCallback bool operator()(G3D::Ray const& r, GameObjectModel const& obj, float& distance) { - _didHit = obj.intersectRay(r, distance, true, _phaseShift); + _didHit = obj.intersectRay(r, distance, true, _phaseShift, VMAP::ModelIgnoreFlags::Nothing); return _didHit; } diff --git a/src/common/Collision/Management/IVMapManager.h b/src/common/Collision/Management/IVMapManager.h index cec5657f5ed..d8073e86b78 100644 --- a/src/common/Collision/Management/IVMapManager.h +++ b/src/common/Collision/Management/IVMapManager.h @@ -21,6 +21,7 @@ #include <string> #include "Define.h" +#include "ModelIgnoreFlags.h" //=========================================================== @@ -38,6 +39,13 @@ namespace VMAP VMAP_LOAD_RESULT_IGNORED }; + enum class LoadResult : uint8 + { + Success, + FileNotFound, + VersionMismatch + }; + #define VMAP_INVALID_HEIGHT -100000.0f // for check #define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case @@ -55,12 +63,12 @@ namespace VMAP virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; - virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; + virtual LoadResult existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; virtual void unloadMap(unsigned int pMapId, int x, int y) = 0; virtual void unloadMap(unsigned int pMapId) = 0; - virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0; + virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) = 0; virtual float getHeight(unsigned int pMapId, float x, float y, float z, float maxSearchDist) = 0; /** test if we hit an object. return true if we hit one. rx, ry, rz will hold the hit position or the dest position, if no intersection was found diff --git a/src/common/Collision/Management/VMapManager2.cpp b/src/common/Collision/Management/VMapManager2.cpp index 163769f33a9..0ad872fe6c1 100644 --- a/src/common/Collision/Management/VMapManager2.cpp +++ b/src/common/Collision/Management/VMapManager2.cpp @@ -191,7 +191,7 @@ namespace VMAP } } - bool VMapManager2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2) + bool VMapManager2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) { if (!isLineOfSightCalcEnabled() || IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS)) return true; @@ -202,7 +202,7 @@ namespace VMAP Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1); Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2); if (pos1 != pos2) - return instanceTree->second->isInLineOfSight(pos1, pos2); + return instanceTree->second->isInLineOfSight(pos1, pos2, ignoreFlags); } return true; @@ -306,7 +306,7 @@ namespace VMAP return false; } - WorldModel* VMapManager2::acquireModelInstance(const std::string& basepath, const std::string& filename) + WorldModel* VMapManager2::acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags/* Only used when creating the model */) { //! Critical section, thread safe access to iLoadedModelFiles std::lock_guard<std::mutex> lock(LoadedModelFilesLock); @@ -322,6 +322,9 @@ namespace VMAP return NULL; } TC_LOG_DEBUG("maps", "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str()); + + worldmodel->Flags = flags; + model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first; model->second.setModel(worldmodel); } @@ -348,7 +351,7 @@ namespace VMAP } } - bool VMapManager2::existsMap(const char* basePath, unsigned int mapId, int x, int y) + LoadResult VMapManager2::existsMap(const char* basePath, unsigned int mapId, int x, int y) { return StaticMapTree::CanLoadMap(std::string(basePath), mapId, x, y, this); } diff --git a/src/common/Collision/Management/VMapManager2.h b/src/common/Collision/Management/VMapManager2.h index 5bb556ccf64..c48a8e985a6 100644 --- a/src/common/Collision/Management/VMapManager2.h +++ b/src/common/Collision/Management/VMapManager2.h @@ -111,7 +111,7 @@ namespace VMAP void unloadMap(unsigned int mapId) override; void unloadSingleMap(uint32 mapId); - bool isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2) override ; + bool isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) override ; /** fill the hit pos and return true, if an object was hit */ @@ -123,7 +123,7 @@ namespace VMAP bool getAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const override; bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const override; - WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename); + WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags = 0); void releaseModelInstance(const std::string& filename); // what's the use of this? o.O @@ -131,7 +131,7 @@ namespace VMAP { return getMapFileName(mapId); } - virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y) override; + virtual LoadResult existsMap(const char* basePath, unsigned int mapId, int x, int y) override; void getInstanceMapTree(InstanceTreeMap &instanceMapTree); diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp index 3ba9c860d6f..9b735061c29 100644 --- a/src/common/Collision/Maps/MapTree.cpp +++ b/src/common/Collision/Maps/MapTree.cpp @@ -33,14 +33,13 @@ using G3D::Vector3; namespace VMAP { - class MapRayCallback { public: - MapRayCallback(ModelInstance* val): prims(val), hit(false) { } + MapRayCallback(ModelInstance* val, ModelIgnoreFlags ignoreFlags): prims(val), hit(false), flags(ignoreFlags) { } bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit=true) { - bool result = prims[entry].intersectRay(ray, distance, pStopAtFirstHit); + bool result = prims[entry].intersectRay(ray, distance, pStopAtFirstHit, flags); if (result) hit = true; return result; @@ -49,6 +48,7 @@ namespace VMAP protected: ModelInstance* prims; bool hit; + ModelIgnoreFlags flags; }; class AreaInfoCallback @@ -142,10 +142,10 @@ namespace VMAP Else, pMaxDist is not modified and returns false; */ - bool StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool pStopAtFirstHit) const + bool StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const { float distance = pMaxDist; - MapRayCallback intersectionCallBack(iTreeValues); + MapRayCallback intersectionCallBack(iTreeValues, ignoreFlags); iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit); if (intersectionCallBack.didHit()) pMaxDist = distance; @@ -153,7 +153,7 @@ namespace VMAP } //========================================================= - bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) const + bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2, ModelIgnoreFlags ignoreFlag) const { float maxDist = (pos2 - pos1).magnitude(); // return false if distance is over max float, in case of cheater teleporting to the end of the universe @@ -167,7 +167,7 @@ namespace VMAP return true; // direction with length of 1 G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist); - if (getIntersectionTime(ray, maxDist, true)) + if (getIntersectionTime(ray, maxDist, true, ignoreFlag)) return false; return true; @@ -193,7 +193,7 @@ namespace VMAP Vector3 dir = (pPos2 - pPos1)/maxDist; // direction with length of 1 G3D::Ray ray(pPos1, dir); float dist = maxDist; - if (getIntersectionTime(ray, dist, false)) + if (getIntersectionTime(ray, dist, false, ModelIgnoreFlags::Nothing)) { pResultHitPos = pPos1 + dir * dist; if (pModifyDist < 0) @@ -229,7 +229,7 @@ namespace VMAP Vector3 dir = Vector3(0, 0, -1); G3D::Ray ray(pPos, dir); // direction with length of 1 float maxDist = maxSearchDist; - if (getIntersectionTime(ray, maxDist, false)) + if (getIntersectionTime(ray, maxDist, false, ModelIgnoreFlags::Nothing)) { height = pPos.z - maxDist; } @@ -255,35 +255,43 @@ namespace VMAP } //========================================================= - - bool StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2* vm) + LoadResult StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2* vm) { std::string basePath = vmapPath; if (basePath.length() > 0 && basePath[basePath.length()-1] != '/' && basePath[basePath.length()-1] != '\\') basePath.push_back('/'); std::string fullname = basePath + VMapManager2::getMapFileName(mapID); - bool success = true; + + LoadResult result = LoadResult::Success; + FILE* rf = fopen(fullname.c_str(), "rb"); if (!rf) - return false; + return LoadResult::FileNotFound; char chunk[8]; if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) { fclose(rf); - return false; + return LoadResult::VersionMismatch; } FILE* tf = OpenMapTileFile(basePath, mapID, tileX, tileY, vm).File; if (!tf) - success = false; + return LoadResult::FileNotFound; else { - if (!readChunk(tf, chunk, VMAP_MAGIC, 8)) - success = false; - fclose(tf); + std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY); + FILE* tf = fopen(tilefile.c_str(), "rb"); + if (!tf) + result = LoadResult::FileNotFound; + else + { + if (!readChunk(tf, chunk, VMAP_MAGIC, 8)) + result = LoadResult::VersionMismatch; + fclose(tf); + } } fclose(rf); - return success; + return result; } //========================================================= @@ -371,7 +379,7 @@ namespace VMAP if (result) { // acquire model instance - WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name); + WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags); if (!model) TC_LOG_ERROR("misc", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY); diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h index 122f5dc63eb..df1ce431109 100644 --- a/src/common/Collision/Maps/MapTree.h +++ b/src/common/Collision/Maps/MapTree.h @@ -28,6 +28,8 @@ namespace VMAP class ModelInstance; class GroupModel; class VMapManager2; + enum class LoadResult : uint8; + enum class ModelIgnoreFlags : uint32; struct TC_COMMON_API LocationInfo { @@ -65,18 +67,18 @@ namespace VMAP private: static TileFileOpenResult OpenMapTileFile(std::string const& basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2* vm); - bool getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool pStopAtFirstHit) const; + bool getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const; //bool containsLoadedMapTile(unsigned int pTileIdent) const { return(iLoadedMapTiles.containsKey(pTileIdent)); } public: static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY); static uint32 packTileID(uint32 tileX, uint32 tileY) { return tileX<<16 | tileY; } static void unpackTileID(uint32 ID, uint32 &tileX, uint32 &tileY) { tileX = ID >> 16; tileY = ID & 0xFF; } - static bool CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2* vm); + static LoadResult CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY, VMapManager2* vm); StaticMapTree(uint32 mapID, const std::string &basePath); ~StaticMapTree(); - bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2) const; + bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2, ModelIgnoreFlags ignoreFlags) const; bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const; float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const; bool getAreaInfo(G3D::Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const; diff --git a/src/common/Collision/Models/GameObjectModel.cpp b/src/common/Collision/Models/GameObjectModel.cpp index 07c1ffe548c..8e4ba0a9679 100644 --- a/src/common/Collision/Models/GameObjectModel.cpp +++ b/src/common/Collision/Models/GameObjectModel.cpp @@ -161,7 +161,7 @@ GameObjectModel* GameObjectModel::Create(std::unique_ptr<GameObjectModelOwnerBas return mdl; } -bool GameObjectModel::intersectRay(G3D::Ray const& ray, float& maxDist, bool stopAtFirstHit, PhaseShift const& phaseShift) const +bool GameObjectModel::intersectRay(G3D::Ray const& ray, float& maxDist, bool stopAtFirstHit, PhaseShift const& phaseShift, VMAP::ModelIgnoreFlags ignoreFlags) const { if (!isCollisionEnabled() || !owner->IsSpawned()) return false; @@ -177,7 +177,7 @@ bool GameObjectModel::intersectRay(G3D::Ray const& ray, float& maxDist, bool sto Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale; Ray modRay(p, iInvRot * ray.direction()); float distance = maxDist * iInvScale; - bool hit = iModel->IntersectRay(modRay, distance, stopAtFirstHit); + bool hit = iModel->IntersectRay(modRay, distance, stopAtFirstHit, ignoreFlags); if (hit) { distance *= iScale; diff --git a/src/common/Collision/Models/GameObjectModel.h b/src/common/Collision/Models/GameObjectModel.h index 287f93f87ca..0ea8bbbbcfb 100644 --- a/src/common/Collision/Models/GameObjectModel.h +++ b/src/common/Collision/Models/GameObjectModel.h @@ -31,6 +31,7 @@ namespace VMAP { class WorldModel; struct AreaInfo; + enum class ModelIgnoreFlags : uint32; } class GameObject; @@ -69,7 +70,7 @@ public: bool isCollisionEnabled() const { return _collisionEnabled; } bool isMapObject() const { return isWmo; } - bool intersectRay(G3D::Ray const& ray, float& maxDist, bool stopAtFirstHit, PhaseShift const& phaseShift) const; + bool intersectRay(G3D::Ray const& ray, float& maxDist, bool stopAtFirstHit, PhaseShift const& phaseShift, VMAP::ModelIgnoreFlags ignoreFlags) const; void intersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, PhaseShift const& phaseShift) const; static GameObjectModel* Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath); diff --git a/src/common/Collision/Models/ModelIgnoreFlags.h b/src/common/Collision/Models/ModelIgnoreFlags.h new file mode 100644 index 00000000000..beb9f965523 --- /dev/null +++ b/src/common/Collision/Models/ModelIgnoreFlags.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ModelIgnoreFlags_h__ +#define ModelIgnoreFlags_h__ + +#include "Define.h" + +namespace VMAP +{ +enum class ModelIgnoreFlags : uint32 +{ + Nothing = 0x00, + M2 = 0x01 +}; + +inline ModelIgnoreFlags operator&(ModelIgnoreFlags left, ModelIgnoreFlags right) { return ModelIgnoreFlags(uint32(left) & uint32(right)); } +} + +#endif // ModelIgnoreFlags_h__ diff --git a/src/common/Collision/Models/ModelInstance.cpp b/src/common/Collision/Models/ModelInstance.cpp index a3d90ef7dec..a2c62bc565d 100644 --- a/src/common/Collision/Models/ModelInstance.cpp +++ b/src/common/Collision/Models/ModelInstance.cpp @@ -31,7 +31,7 @@ namespace VMAP iInvScale = 1.f/iScale; } - bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const + bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const { if (!iModel) { @@ -55,7 +55,7 @@ namespace VMAP Vector3 p = iInvRot * (pRay.origin() - iPos) * iInvScale; Ray modRay(p, iInvRot * pRay.direction()); float distance = pMaxDist * iInvScale; - bool hit = iModel->IntersectRay(modRay, distance, pStopAtFirstHit); + bool hit = iModel->IntersectRay(modRay, distance, pStopAtFirstHit, ignoreFlags); if (hit) { distance *= iScale; diff --git a/src/common/Collision/Models/ModelInstance.h b/src/common/Collision/Models/ModelInstance.h index 04fe5b06c7b..0bdcbc29393 100644 --- a/src/common/Collision/Models/ModelInstance.h +++ b/src/common/Collision/Models/ModelInstance.h @@ -31,6 +31,7 @@ namespace VMAP class WorldModel; struct AreaInfo; struct LocationInfo; + enum class ModelIgnoreFlags : uint32; enum ModelFlags { @@ -66,7 +67,7 @@ namespace VMAP ModelInstance(): iInvScale(0.0f), iModel(nullptr) { } ModelInstance(const ModelSpawn &spawn, WorldModel* model); void setUnloaded() { iModel = nullptr; } - bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const; + bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, ModelIgnoreFlags ignoreFlags) const; void intersectPoint(const G3D::Vector3& p, AreaInfo &info) const; bool GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const; bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const; diff --git a/src/common/Collision/Models/WorldModel.cpp b/src/common/Collision/Models/WorldModel.cpp index beab8f80a1c..ad416943861 100644 --- a/src/common/Collision/Models/WorldModel.cpp +++ b/src/common/Collision/Models/WorldModel.cpp @@ -19,6 +19,8 @@ #include "WorldModel.h" #include "VMapDefinitions.h" #include "MapTree.h" +#include "ModelInstance.h" +#include "ModelIgnoreFlags.h" using G3D::Vector3; using G3D::Ray; @@ -472,8 +474,16 @@ namespace VMAP bool hit; }; - bool WorldModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const + bool WorldModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const { + // If the caller asked us to ignore certain objects we should check flags + if ((ignoreFlags & ModelIgnoreFlags::M2) != ModelIgnoreFlags::Nothing) + { + // M2 models are not taken into account for LoS calculation if caller requested their ignoring. + if (Flags & MOD_M2) + return false; + } + // small M2 workaround, maybe better make separate class with virtual intersection funcs // in any case, there's no need to use a bound tree if we only have one submodel if (groupModels.size() == 1) diff --git a/src/common/Collision/Models/WorldModel.h b/src/common/Collision/Models/WorldModel.h index 8951b3fdf89..64b15c8cba4 100644 --- a/src/common/Collision/Models/WorldModel.h +++ b/src/common/Collision/Models/WorldModel.h @@ -32,6 +32,7 @@ namespace VMAP class TreeNode; struct AreaInfo; struct LocationInfo; + enum class ModelIgnoreFlags : uint32; class TC_COMMON_API MeshTriangle { @@ -111,12 +112,13 @@ namespace VMAP //! pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry! void setGroupModels(std::vector<GroupModel> &models); void setRootWmoID(uint32 id) { RootWMOID = id; } - bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const; + bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit, ModelIgnoreFlags ignoreFlags) const; bool IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const; bool GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const; bool writeFile(const std::string &filename); bool readFile(const std::string &filename); void getGroupModels(std::vector<GroupModel>& outGroupModels); + uint32 Flags; protected: uint32 RootWMOID; std::vector<GroupModel> groupModels; diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index 2ce427ef991..ceba32fbf9f 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -333,9 +333,9 @@ class HookList final typedef typename ContainerType::const_iterator const_iterator; typedef typename ContainerType::iterator iterator; - HookList<T>& operator+=(T t) + HookList<T>& operator+=(T&& t) { - _container.push_back(t); + _container.push_back(std::move(t)); return *this; } diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp index f52f0c2772f..6ddfabc3c60 100644 --- a/src/server/bnetserver/Server/Session.cpp +++ b/src/server/bnetserver/Server/Session.cpp @@ -245,11 +245,17 @@ uint32 Battlenet::Session::HandleLogon(authentication::v1::LogonRequest const* l uint32 Battlenet::Session::HandleVerifyWebCredentials(authentication::v1::VerifyWebCredentialsRequest const* verifyWebCredentialsRequest, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation) { - return VerifyWebCredentials(verifyWebCredentialsRequest->web_credentials(), continuation); + if (verifyWebCredentialsRequest->has_web_credentials()) + return VerifyWebCredentials(verifyWebCredentialsRequest->web_credentials(), continuation); + + return ERROR_DENIED; } uint32 Battlenet::Session::VerifyWebCredentials(std::string const& webCredentials, std::function<void(ServiceBase*, uint32, ::google::protobuf::Message const*)>& continuation) { + if (webCredentials.empty()) + return ERROR_DENIED; + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_INFO); stmt->setString(0, webCredentials); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 62c64fd6d38..b822e4ecab3 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -196,6 +196,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_GIFT, "DELETE FROM character_gifts WHERE item_guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM, "SELECT entry, flags FROM character_gifts WHERE item_guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_ACCOUNT_BY_NAME, "SELECT account FROM characters WHERE name = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_UPD_ACCOUNT_BY_GUID, "UPDATE characters SET account = ? WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES, "DELETE FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, "INSERT INTO account_instance_times (accountId, instanceId, releaseTime) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_MATCH_MAKER_RATING, "SELECT matchMakerRating FROM character_arena_stats WHERE guid = ? AND slot = ?", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index c4c081ab73a..2c31b819593 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -165,6 +165,7 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_GIFT, CHAR_SEL_CHARACTER_GIFT_BY_ITEM, CHAR_SEL_ACCOUNT_BY_NAME, + CHAR_UPD_ACCOUNT_BY_GUID, CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES, CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, CHAR_SEL_MATCH_MAKER_RATING, diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index f30b2369e91..cab622be0cb 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -50,6 +50,11 @@ bool PetAI::_needToStop() if (me->IsCharmed() && me->GetVictim() == me->GetCharmer()) return true; + // dont allow pets to follow targets far away from owner + if (Unit* owner = me->GetCharmerOrOwner()) + if (owner->GetExactDist(me) >= (owner->GetVisibilityRange()-10.0f)) + return true; + return !me->IsValidAttackTarget(me->GetVictim()); } diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index 82cb9b893f7..6b3750325ca 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -280,6 +280,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/) me->ResetPlayerDamageReq(); me->SetLastDamagedTime(0); me->SetCannotReachTarget(false); + me->DoNotReacquireTarget(); if (me->IsInEvadeMode()) return false; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 17851adc4de..a19d01fceb0 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -1707,7 +1707,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, dest, e.action.MoveToPos.disablePathfinding == 0); } else - me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), e.action.MoveToPos.disablePathfinding == 0); + { + float x, y, z; + target->GetPosition(x, y, z); + if (e.action.MoveToPos.ContactDistance > 0) + target->GetContactPoint(me, x, y, z, e.action.MoveToPos.ContactDistance); + me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, x, y, z, e.action.MoveToPos.disablePathfinding == 0); + } break; } case SMART_ACTION_RESPAWN_TARGET: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 7b9e1ff5f3d..ac765bfa47a 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -513,7 +513,7 @@ enum SMART_ACTION SMART_ACTION_SET_ORIENTATION = 66, // SMART_ACTION_CREATE_TIMED_EVENT = 67, // id, InitialMin, InitialMax, RepeatMin(only if it repeats), RepeatMax(only if it repeats), chance SMART_ACTION_PLAYMOVIE = 68, // entry - SMART_ACTION_MOVE_TO_POS = 69, // PointId, transport, disablePathfinding + SMART_ACTION_MOVE_TO_POS = 69, // PointId, transport, disablePathfinding, ContactDistance SMART_ACTION_RESPAWN_TARGET = 70, // SMART_ACTION_EQUIP = 71, // entry, slotmask slot1, slot2, slot3 , only slots with mask set will be sent to client, bits are 1, 2, 4, leaving mask 0 is defaulted to mask 7 (send all), slots1-3 are only used if no entry is set SMART_ACTION_CLOSE_GOSSIP = 72, // none @@ -997,6 +997,7 @@ struct SmartAction uint32 pointId; uint32 transport; uint32 disablePathfinding; + uint32 ContactDistance; } MoveToPos; struct diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 0c1cb84d270..018f4e0c73f 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -603,7 +603,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE = 695, RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL = 696, RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS = 697, - // 698 - reuse + RBAC_PERM_COMMAND_CHARACTER_CHANGEACCOUNT = 698, RBAC_PERM_COMMAND_RELOAD_SPELL_PROC = 699, RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPTS = 700, RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION = 701, diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index d11e6f79b19..9531bdff1ed 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -1115,8 +1115,8 @@ void AchievementGlobalMgr::LoadRewards() _achievementRewards.clear(); // need for reload case - // 0 1 2 3 4 5 6 7 - QueryResult result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text, mailTemplate FROM achievement_reward"); + // 0 1 2 3 4 5 6 7 + QueryResult result = WorldDatabase.Query("SELECT ID, TitleA, TitleH, ItemID, Sender, Subject, Body, MailTemplateID FROM achievement_reward"); if (!result) { @@ -1124,16 +1124,14 @@ void AchievementGlobalMgr::LoadRewards() return; } - uint32 count = 0; - do { Field* fields = result->Fetch(); - uint32 entry = fields[0].GetUInt32(); - AchievementEntry const* achievement = sAchievementStore.LookupEntry(entry); + uint32 id = fields[0].GetUInt32(); + AchievementEntry const* achievement = sAchievementStore.LookupEntry(id); if (!achievement) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` contains a wrong achievement entry (Entry: %u), ignored.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` contains a wrong achievement ID (%u), ignored.", id); continue; } @@ -1149,19 +1147,19 @@ void AchievementGlobalMgr::LoadRewards() // must be title or mail at least if (!reward.TitleId[0] && !reward.TitleId[1] && !reward.SenderCreatureId) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not contain title or item reward data. Ignored.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) does not contain title or item reward data. Ignored.", id); continue; } if (achievement->Faction == ACHIEVEMENT_FACTION_ANY && (!reward.TitleId[0] ^ !reward.TitleId[1])) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains the title (A: %u H: %u) for only one team.", entry, reward.TitleId[0], reward.TitleId[1]); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) contains the title (A: %u H: %u) for only one team.", id, reward.TitleId[0], reward.TitleId[1]); if (reward.TitleId[0]) { CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.TitleId[0]); if (!titleEntry) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_A`, set to 0", entry, reward.TitleId[0]); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_A`, set to 0", id, reward.TitleId[0]); reward.TitleId[0] = 0; } } @@ -1171,7 +1169,7 @@ void AchievementGlobalMgr::LoadRewards() CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.TitleId[1]); if (!titleEntry) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_H`, set to 0", entry, reward.TitleId[1]); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_H`, set to 0", id, reward.TitleId[1]); reward.TitleId[1] = 0; } } @@ -1181,51 +1179,49 @@ void AchievementGlobalMgr::LoadRewards() { if (!sObjectMgr->GetCreatureTemplate(reward.SenderCreatureId)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid creature entry %u as sender, mail reward skipped.", entry, reward.SenderCreatureId); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) contains an invalid creature ID %u as sender, mail reward skipped.", id, reward.SenderCreatureId); reward.SenderCreatureId = 0; } } else { if (reward.ItemId) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains an item reward. Item will not be rewarded.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) does not have sender data, but contains an item reward. Item will not be rewarded.", id); if (!reward.Subject.empty()) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains a mail subject.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) does not have sender data, but contains a mail subject.", id); if (!reward.Body.empty()) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains mail text.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) does not have sender data, but contains mail text.", id); if (reward.MailTemplateId) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but has a MailTemplateId.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) does not have sender data, but has a MailTemplate.", id); } if (reward.MailTemplateId) { if (!sMailTemplateStore.LookupEntry(reward.MailTemplateId)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using an invalid MailTemplateId (%u).", entry, reward.MailTemplateId); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) is using an invalid MailTemplate (%u).", id, reward.MailTemplateId); reward.MailTemplateId = 0; } else if (!reward.Subject.empty() || !reward.Body.empty()) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using MailTemplateId (%u) and mail subject/text.", entry, reward.MailTemplateId); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) is using MailTemplate (%u) and mail subject/text.", id, reward.MailTemplateId); } if (reward.ItemId) { if (!sObjectMgr->GetItemTemplate(reward.ItemId)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid item id %u, reward mail will not contain the rewarded item.", entry, reward.ItemId); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (ID: %u) contains an invalid item id %u, reward mail will not contain the rewarded item.", id, reward.ItemId); reward.ItemId = 0; } } - _achievementRewards[entry] = reward; - ++count; - } - while (result->NextRow()); + _achievementRewards[id] = reward; + } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u achievement rewards in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u achievement rewards in %u ms.", uint32(_achievementRewards.size()), GetMSTimeDiffToNow(oldMSTime)); } void AchievementGlobalMgr::LoadRewardLocales() @@ -1234,13 +1230,12 @@ void AchievementGlobalMgr::LoadRewardLocales() _achievementRewardLocales.clear(); // need for reload case - QueryResult result = WorldDatabase.Query("SELECT entry, subject_loc1, text_loc1, subject_loc2, text_loc2, subject_loc3, text_loc3, subject_loc4, text_loc4, " - "subject_loc5, text_loc5, subject_loc6, text_loc6, subject_loc7, text_loc7, subject_loc8, text_loc8" - " FROM locales_achievement_reward"); + // 0 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT ID, Locale, Subject, Body FROM achievement_reward_locale"); if (!result) { - TC_LOG_INFO("server.loading", ">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 achievement reward locale strings. DB table `achievement_reward_locale` is empty."); return; } @@ -1248,24 +1243,23 @@ void AchievementGlobalMgr::LoadRewardLocales() { Field* fields = result->Fetch(); - uint32 entry = fields[0].GetUInt32(); + uint32 id = fields[0].GetUInt32(); + std::string localeName = fields[1].GetString(); - if (_achievementRewards.find(entry) == _achievementRewards.end()) + if (_achievementRewards.find(id) == _achievementRewards.end()) { - TC_LOG_ERROR("sql.sql", "Table `locales_achievement_reward` (Entry: %u) contains locale strings for a non-existing achievement reward.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward_locale` (ID: %u) contains locale strings for a non-existing achievement reward.", id); continue; } - AchievementRewardLocale& data = _achievementRewardLocales[entry]; + AchievementRewardLocale& data = _achievementRewardLocales[id]; + LocaleConstant locale = GetLocaleByName(localeName); + if (locale == LOCALE_enUS) + continue; - for (uint8 i = OLD_TOTAL_LOCALES - 1; i > 0; --i) - { - LocaleConstant locale = (LocaleConstant) i; - ObjectMgr::AddLocaleString(fields[1 + 2 * (i - 1)].GetString(), locale, data.Subject); - ObjectMgr::AddLocaleString(fields[1 + 2 * (i - 1) + 1].GetString(), locale, data.Body); - } - } - while (result->NextRow()); + ObjectMgr::AddLocaleString(fields[2].GetString(), locale, data.Subject); + ObjectMgr::AddLocaleString(fields[3].GetString(), locale, data.Body); + } while (result->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u achievement reward locale strings in %u ms.", uint32(_achievementRewardLocales.size()), GetMSTimeDiffToNow(oldMSTime)); } diff --git a/src/server/game/Combat/HostileRefManager.cpp b/src/server/game/Combat/HostileRefManager.cpp index 98883f3ac70..bcea34d7e83 100644 --- a/src/server/game/Combat/HostileRefManager.cpp +++ b/src/server/game/Combat/HostileRefManager.cpp @@ -142,6 +142,26 @@ void HostileRefManager::deleteReferencesForFaction(uint32 faction) } //================================================= +// delete all references out of specified range + +void HostileRefManager::deleteReferencesOutOfRange(float range) +{ + HostileReference* ref = getFirst(); + range = range*range; + while (ref) + { + HostileReference* nextRef = ref->next(); + Unit* owner = ref->GetSource()->GetOwner(); + if (!owner->isActiveObject() && owner->GetExactDist2dSq(GetOwner()) > range) + { + ref->removeReference(); + delete ref; + } + ref = nextRef; + } +} + +//================================================= // delete one reference, defined by Unit void HostileRefManager::deleteReference(Unit* creature) diff --git a/src/server/game/Combat/HostileRefManager.h b/src/server/game/Combat/HostileRefManager.h index fc26d950e67..585d96b7443 100644 --- a/src/server/game/Combat/HostileRefManager.h +++ b/src/server/game/Combat/HostileRefManager.h @@ -53,6 +53,9 @@ class TC_GAME_API HostileRefManager : public RefManager<Unit, ThreatManager> // Remove specific faction references void deleteReferencesForFaction(uint32 faction); + // for combat bugs + void deleteReferencesOutOfRange(float range); + HostileReference* getFirst() { return ((HostileReference*) RefManager<Unit, ThreatManager>::getFirst()); } void updateThreatTables(); diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index d72ffa343ca..2655e0e0a12 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -1111,7 +1111,7 @@ enum VehicleSeatFlags { VEHICLE_SEAT_FLAG_HAS_LOWER_ANIM_FOR_ENTER = 0x00000001, VEHICLE_SEAT_FLAG_HAS_LOWER_ANIM_FOR_RIDE = 0x00000002, - VEHICLE_SEAT_FLAG_UNK3 = 0x00000004, + VEHICLE_SEAT_FLAG_DISABLE_GRAVITY = 0x00000004, // Passenger will not be affected by gravity VEHICLE_SEAT_FLAG_SHOULD_USE_VEH_SEAT_EXIT_ANIM_ON_VOLUNTARY_EXIT = 0x00000008, VEHICLE_SEAT_FLAG_UNK5 = 0x00000010, VEHICLE_SEAT_FLAG_UNK6 = 0x00000020, diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index f521cdfd3b0..fde7d49a943 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -286,6 +286,10 @@ void Creature::RemoveCorpse(bool setSpawnTime) if (setSpawnTime) m_respawnTime = time(NULL) + respawnDelay; + // if corpse was removed during falling, the falling will continue and override relocation to respawn position + if (IsFalling()) + StopMoving(); + float x, y, z, o; GetRespawnPosition(x, y, z, &o); SetHomePosition(x, y, z, o); @@ -2812,6 +2816,10 @@ void Creature::UpdateMovementFlags() if (m_playerMovingMe) return; + // Creatures with CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE should control MovementFlags in your own scripts + if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE) + return; + // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc) float ground = GetMap()->GetHeight(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZMinusOffset()); @@ -2842,8 +2850,8 @@ void Creature::SetObjectScale(float scale) if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(GetDisplayId())) { - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, minfo->bounding_radius * scale); - SetFloatValue(UNIT_FIELD_COMBATREACH, minfo->combat_reach * scale); + SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (IsPet() ? 1.0f : minfo->bounding_radius) * scale); + SetFloatValue(UNIT_FIELD_COMBATREACH, (IsPet() ? DEFAULT_COMBAT_REACH : minfo->combat_reach) * scale); } } @@ -2853,8 +2861,8 @@ void Creature::SetDisplayId(uint32 modelId, float displayScale /*= 1.f*/) if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId)) { - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, minfo->bounding_radius * GetObjectScale()); - SetFloatValue(UNIT_FIELD_COMBATREACH, minfo->combat_reach * GetObjectScale()); + SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (IsPet() ? 1.0f : minfo->bounding_radius) * GetObjectScale()); + SetFloatValue(UNIT_FIELD_COMBATREACH, (IsPet() ? DEFAULT_COMBAT_REACH : minfo->combat_reach) * GetObjectScale()); } } diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 813aaa27e27..02fdcfbff16 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -264,6 +264,7 @@ enum CreatureFlagsExtra CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me + CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE = 0x00000200, // creature won't update movement flags CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging) CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes @@ -279,7 +280,7 @@ enum CreatureFlagsExtra #define CREATURE_FLAG_EXTRA_DB_ALLOWED (CREATURE_FLAG_EXTRA_INSTANCE_BIND | CREATURE_FLAG_EXTRA_CIVILIAN | \ CREATURE_FLAG_EXTRA_NO_PARRY | CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN | CREATURE_FLAG_EXTRA_NO_BLOCK | \ CREATURE_FLAG_EXTRA_NO_CRUSH | CREATURE_FLAG_EXTRA_NO_XP_AT_KILL | CREATURE_FLAG_EXTRA_TRIGGER | \ - CREATURE_FLAG_EXTRA_NO_TAUNT | CREATURE_FLAG_EXTRA_WORLDEVENT | CREATURE_FLAG_EXTRA_NO_CRIT | \ + CREATURE_FLAG_EXTRA_NO_TAUNT | CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE | CREATURE_FLAG_EXTRA_WORLDEVENT | CREATURE_FLAG_EXTRA_NO_CRIT | \ CREATURE_FLAG_EXTRA_NO_SKILLGAIN | CREATURE_FLAG_EXTRA_TAUNT_DIMINISH | CREATURE_FLAG_EXTRA_ALL_DIMINISH | \ CREATURE_FLAG_EXTRA_GUARD | CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING | CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ | CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK) diff --git a/src/server/game/Entities/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp index 89b553d0792..9f5b461ef8d 100644 --- a/src/server/game/Entities/Creature/GossipDef.cpp +++ b/src/server/game/Entities/Creature/GossipDef.cpp @@ -443,7 +443,7 @@ void PlayerMenu::SendQuestGiverQuestDetails(Quest const* quest, ObjectGuid npcGU packet.PortraitTurnIn = quest->GetQuestTurnInPortrait(); packet.AutoLaunched = autoLaunched; packet.DisplayPopup = displayPopup; - packet.QuestFlags[0] = quest->GetFlags(); + packet.QuestFlags[0] = quest->GetFlags() & (sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT) ? ~QUEST_FLAGS_AUTO_ACCEPT : ~0); packet.QuestFlags[1] = quest->GetFlagsEx(); packet.SuggestedPartyMembers = quest->GetSuggestedPlayers(); diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h index 2bf4a423590..a699be4bcf7 100644 --- a/src/server/game/Entities/GameObject/GameObjectData.h +++ b/src/server/game/Entities/GameObject/GameObjectData.h @@ -19,6 +19,7 @@ #define GameObjectData_h__ #include "Common.h" +#include "DBCEnums.h" #include "SharedDefines.h" #include <string> #include <vector> diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 9d8a17ecbd7..2921ddf673f 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1693,7 +1693,7 @@ bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool return distsq < maxdist * maxdist; } -bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const +bool WorldObject::IsWithinLOSInMap(const WorldObject* obj, VMAP::ModelIgnoreFlags ignoreFlags) const { if (!IsInMap(obj)) return false; @@ -1704,7 +1704,7 @@ bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const else obj->GetHitSpherePointFor(GetPosition(), x, y, z); - return IsWithinLOS(x, y, z); + return IsWithinLOS(x, y, z, ignoreFlags); } float WorldObject::GetDistance(const WorldObject* obj) const @@ -1781,7 +1781,7 @@ bool WorldObject::IsWithinDistInMap(WorldObject const* obj, float dist2compare, return obj && IsInMap(obj) && IsInPhase(obj) && _IsWithinDist(obj, dist2compare, is3D); } -bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const +bool WorldObject::IsWithinLOS(float ox, float oy, float oz, VMAP::ModelIgnoreFlags ignoreFlags) const { /*float x, y, z; GetPosition(x, y, z); @@ -1795,7 +1795,7 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const else GetHitSpherePointFor({ ox, oy, oz }, x, y, z); - return GetMap()->isInLineOfSight(GetPhaseShift(), x, y, z + 2.0f, ox, oy, oz + 2.0f); + return GetMap()->isInLineOfSight(GetPhaseShift(), x, y, z + 2.0f, ox, oy, oz + 2.0f, ignoreFlags); } return true; @@ -2122,6 +2122,14 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo corpseVisibility = true; } } + + if (Unit const* target = obj->ToUnit()) + { + // Don't allow to detect vehicle accessories if you can't see vehicle + if (Unit const* vehicle = target->GetVehicleBase()) + if (!thisPlayer->HaveAtClient(vehicle)) + return false; + } } WorldObject const* viewpoint = this; @@ -2251,7 +2259,7 @@ bool WorldObject::CanDetectStealthOf(WorldObject const* obj, bool checkAlert) co if (!HasInArc(float(M_PI), obj)) return false; - GameObject const* go = ToGameObject(); + GameObject const* go = obj->ToGameObject(); for (uint32 i = 0; i < TOTAL_STEALTH_TYPES; ++i) { if (!(obj->m_stealth.GetFlags() & (1 << i))) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 79420076f5b..17979a441a8 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -22,6 +22,7 @@ #include "Common.h" #include "GridReference.h" #include "GridRefManager.h" +#include "ModelIgnoreFlags.h" #include "MovementInfo.h" #include "ObjectDefines.h" #include "ObjectGuid.h" @@ -476,8 +477,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation // use only if you will sure about placing both object at same map bool IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D = true) const; bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true) const; - bool IsWithinLOS(float x, float y, float z) const; - bool IsWithinLOSInMap(WorldObject const* obj) const; + bool IsWithinLOS(float x, float y, float z, VMAP::ModelIgnoreFlags ignoreFlags = VMAP::ModelIgnoreFlags::Nothing) const; + bool IsWithinLOSInMap(WorldObject const* obj, VMAP::ModelIgnoreFlags ignoreFlags = VMAP::ModelIgnoreFlags::Nothing) const; Position GetHitSpherePointFor(Position const& dest) const; void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const; bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index d8ab46e6b07..f16f234ef2a 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -853,8 +853,10 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) } // Resistance - for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(cinfo->resistance[i])); + // Hunters pet should not inherit resistances from creature_template, they have separate auras for that + if (!IsHunterPet()) + for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) + SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(cinfo->resistance[i])); // Health, Mana or Power, Armor PetLevelInfo const* pInfo = sObjectMgr->GetPetLevelInfo(creature_ID, petlevel); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index ce02cdad3ce..f32bfb5d3e2 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -210,6 +210,7 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this) m_MirrorTimerFlags = UNDERWATER_NONE; m_MirrorTimerFlagsLast = UNDERWATER_NONE; m_isInWater = false; + m_hostileReferenceCheckTimer = 0; m_drunkTimer = 0; m_deathTimer = 0; m_deathExpireTime = 0; @@ -1348,6 +1349,18 @@ void Player::Update(uint32 p_time) //if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID()))) RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true); + if (IsAlive()) + { + if (m_hostileReferenceCheckTimer <= p_time) + { + m_hostileReferenceCheckTimer = 15 * IN_MILLISECONDS; + if (!GetMap()->IsDungeon()) + getHostileRefManager().deleteReferencesOutOfRange(GetVisibilityRange()); + } + else + m_hostileReferenceCheckTimer -= p_time; + } + //we should execute delayed teleports only for alive(!) players //because we don't want player's ghost teleported from graveyard if (IsHasDelayedTeleport() && IsAlive()) @@ -2268,6 +2281,7 @@ bool Player::IsGroupVisibleFor(Player const* p) const default: return IsInSameGroupWith(p); case 1: return IsInSameRaidWith(p); case 2: return GetTeam() == p->GetTeam(); + case 3: return false; } } @@ -5877,7 +5891,6 @@ bool Player::UpdatePosition(float x, float y, float z, float orientation, bool t // mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE); //if (movementInfo.flags & MOVEMENTFLAG_TURNING) // mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); - //AURA_INTERRUPT_FLAG_JUMP not sure // group update if (GetGroup()) @@ -22108,21 +22121,6 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc return false; } - // check node starting pos data set case if provided - if (node->Pos.X != 0.0f || node->Pos.Y != 0.0f || node->Pos.Z != 0.0f) - { - if (node->ContinentID != GetMapId() || !IsInDist(node->Pos.X, node->Pos.Y, node->Pos.Z, 2 * INTERACTION_DISTANCE)) - { - GetSession()->SendActivateTaxiReply(ERR_TAXITOOFARAWAY); - return false; - } - } - // node must have pos if taxi master case (npc != NULL) - else if (npc) - { - GetSession()->SendActivateTaxiReply(ERR_TAXIUNSPECIFIEDSERVERERROR); - return false; - } // Prepare to flight start now // stop combat at start taxi flight if any @@ -25752,6 +25750,13 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot, AELootResult* aeResult/* return; } + // dont allow protected item to be looted by someone else + if (!item->rollWinnerGUID.IsEmpty() && item->rollWinnerGUID != GetGUID()) + { + SendLootRelease(GetLootGUID()); + return; + } + ItemPosCountVec dest; InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count); if (msg == EQUIP_ERR_OK) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index d53dbf33db0..391fbb34be3 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2587,6 +2587,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool m_SeasonalQuestChanged; time_t m_lastDailyQuestTime; + uint32 m_hostileReferenceCheckTimer; uint32 m_drunkTimer; uint32 m_weaponChangeTimer; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 87d8bad3de6..8261a97c32a 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -7190,11 +7190,13 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) { + if (!(itr->first & spellInfo->GetSchoolMask())) + continue; + SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second); - if ((itr->first & spellInfo->GetSchoolMask()) - && !(immuneSpellInfo && immuneSpellInfo->IsPositive() && spellInfo->IsPositive() && IsFriendlyTo(caster)) - && !spellInfo->CanPierceImmuneAura(immuneSpellInfo)) - return true; + if (!(immuneSpellInfo && immuneSpellInfo->IsPositive() && spellInfo->IsPositive() && caster && IsFriendlyTo(caster))) + if (!spellInfo->CanPierceImmuneAura(immuneSpellInfo)) + return true; } return false; @@ -7523,6 +7525,11 @@ void Unit::Mount(uint32 mount, uint32 VehicleId, uint32 creatureEntry) player->UnsummonPetTemporaryIfAny(); } + // if we have charmed npc, stun him also (everywhere) + if (Unit* charm = player->GetCharm()) + if (charm->GetTypeId() == TYPEID_UNIT) + charm->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); + player->SendMovementSetCollisionHeight(player->GetCollisionHeight(true)); } @@ -7565,6 +7572,11 @@ void Unit::Dismount() } else player->ResummonPetTemporaryUnSummonedIfAny(); + + // if we have charmed npc, remove stun also + if (Unit* charm = player->GetCharm()) + if (charm->GetTypeId() == TYPEID_UNIT && charm->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED) && !charm->HasUnitState(UNIT_STATE_STUNNED)) + charm->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); } } @@ -7775,6 +7787,8 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy) (*itr)->SetInCombatState(PvP, enemy); (*itr)->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); } + + ProcSkillsAndAuras(enemy, PROC_FLAG_ENTER_COMBAT, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); } void Unit::ClearInCombat() @@ -12881,7 +12895,8 @@ bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool tel return false; } - bool const turn = (GetOrientation() != orientation); + // Check if angular distance changed + bool const turn = G3D::fuzzyGt(M_PI - fabs(fabs(GetOrientation() - orientation) - M_PI), 0.0f); // G3D::fuzzyEq won't help here, in some cases magnitudes differ by a little more than G3D::eps, but should be considered equal bool const relocated = (teleport || diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 4b66e148fe5..e8366a48c79 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -208,6 +208,11 @@ void Vehicle::ApplyAllImmunities() // why we need to apply this? we can simple add immunities to slow mechanic in DB _me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_DECREASE_SPEED, true); break; + case 335: // Salvaged Chopper + case 336: // Salvaged Siege Engine + case 338: // Salvaged Demolisher + _me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, false); // Battering Ram + break; default: break; } @@ -480,6 +485,10 @@ Vehicle* Vehicle::RemovePassenger(Unit* unit) if (seat->second.SeatInfo->CanEnterOrExit() && ++UsableSeatNum) _me->SetFlag64(UNIT_NPC_FLAGS, (_me->GetTypeId() == TYPEID_PLAYER ? UNIT_NPC_FLAG_PLAYER_VEHICLE : UNIT_NPC_FLAG_SPELLCLICK)); + // Enable gravity for passenger when he did not have it active before entering the vehicle + if (seat->second.SeatInfo->Flags & VEHICLE_SEAT_FLAG_DISABLE_GRAVITY && !seat->second.Passenger.IsGravityDisabled) + unit->SetDisableGravity(false); + // Remove UNIT_FLAG_NOT_SELECTABLE if passenger did not have it before entering vehicle if (seat->second.SeatInfo->Flags & VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE && !seat->second.Passenger.IsUnselectable) unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); @@ -760,6 +769,7 @@ bool VehicleJoinEvent::Execute(uint64, uint32) Passenger->SetVehicle(Target); Seat->second.Passenger.Guid = Passenger->GetGUID(); Seat->second.Passenger.IsUnselectable = Passenger->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Seat->second.Passenger.IsGravityDisabled = Passenger->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); if (Seat->second.SeatInfo->CanEnterOrExit()) { ASSERT(Target->UsableSeatNum); @@ -792,6 +802,9 @@ bool VehicleJoinEvent::Execute(uint64, uint32) player->UnsummonPetTemporaryIfAny(); } + if (veSeat->Flags & VEHICLE_SEAT_FLAG_DISABLE_GRAVITY) + Passenger->SetDisableGravity(true); + if (Seat->second.SeatInfo->Flags & VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE) Passenger->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h index 95c244423c5..a4bdf647e27 100644 --- a/src/server/game/Entities/Vehicle/VehicleDefines.h +++ b/src/server/game/Entities/Vehicle/VehicleDefines.h @@ -80,11 +80,13 @@ struct PassengerInfo { ObjectGuid Guid; bool IsUnselectable; + bool IsGravityDisabled; void Reset() { Guid.Clear(); IsUnselectable = false; + IsGravityDisabled = false; } }; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index a9bbf7bbade..d0b70bf3d8a 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1986,8 +1986,8 @@ void ObjectMgr::LoadCreatures() int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord; int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord; - bool exists = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.mapid, gx, gy); - if (!exists) + VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.mapid, gx, gy); + if (result != VMAP::LoadResult::Success) TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: " UI64FMTD " Entry: %u MapID: %u) spawned on a possible invalid position (X: %f Y: %f Z: %f)", guid, data.id, data.mapid, data.posX, data.posY, data.posZ); } @@ -2345,8 +2345,8 @@ void ObjectMgr::LoadGameobjects() int gx = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord; int gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord; - bool exists = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.mapid, gx, gy); - if (!exists) + VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), data.mapid, gx, gy); + if (result != VMAP::LoadResult::Success) TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: " UI64FMTD " Entry: %u MapID: %u) spawned on a possible invalid position (X: %f Y: %f Z: %f)", guid, data.id, data.mapid, data.posX, data.posY, data.posZ); } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 184bd97e17d..7321244578c 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1283,7 +1283,8 @@ void Group::CountTheRoll(Rolls::iterator rollI) else { item->is_blocked = false; - player->SendEquipError(msg, NULL, NULL, roll->itemid); + item->rollWinnerGUID = player->GetGUID(); + player->SendEquipError(msg, nullptr, nullptr, roll->itemid); } } } @@ -1335,7 +1336,8 @@ void Group::CountTheRoll(Rolls::iterator rollI) else { item->is_blocked = false; - player->SendEquipError(msg, NULL, NULL, roll->itemid); + item->rollWinnerGUID = player->GetGUID(); + player->SendEquipError(msg, nullptr, nullptr, roll->itemid); } } else if (rollvote == DISENCHANT) @@ -1360,7 +1362,7 @@ void Group::CountTheRoll(Rolls::iterator rollI) for (uint32 i = 0; i < max_slot; ++i) { LootItem* lootItem = loot.LootItemInSlot(i, player); - player->SendEquipError(msg, NULL, NULL, lootItem->itemid); + player->SendEquipError(msg, nullptr, nullptr, lootItem->itemid); player->SendItemRetrievalMail(lootItem->itemid, lootItem->count); } } diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index cfafcb5427b..fb354886846 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -360,7 +360,7 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg, case CHAT_MSG_RAID_WARNING: { Group* group = GetPlayer()->GetGroup(); - if (!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())) || group->isBGGroup()) + if (!group || !(group->isRaidGroup() || sWorld->getBoolConfig(CONFIG_CHAT_PARTY_RAID_WARNINGS)) || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())) || group->isBGGroup()) return; sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group); diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 99828a84c46..ece5d077eb2 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -33,6 +33,7 @@ #include "Transport.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" +#include "SpellMgr.h" #define MOVEMENT_PACKET_TIME_DELAY 0 @@ -439,6 +440,12 @@ void WorldSession::HandleMovementOpcode(OpcodeClient opcode, MovementInfo& movem } else plrMover->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); + + if (opcode == CMSG_MOVE_JUMP) + { + plrMover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_JUMP, 605); // Mind Control + plrMover->ProcSkillsAndAuras(nullptr, PROC_FLAG_JUMP, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); + } } } diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 3b1ff9b7d36..4d91ee2d11b 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -592,13 +592,11 @@ void WorldSession::HandlePetAbandon(WorldPackets::Pet::PetAbandon& packet) if (!_player->IsInWorld()) return; + // pet/charmed Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, packet.Pet); - if (pet) + if (pet && pet->ToPet() && pet->ToPet()->getPetType() == HUNTER_PET) { - if (pet->IsPet()) - _player->RemovePet(pet->ToPet(), PET_SAVE_AS_DELETED); - else if (pet->GetGUID() == _player->GetCharmGUID()) - _player->StopCastingCharm(); + _player->RemovePet((Pet*)pet, PET_SAVE_AS_DELETED); } } diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index dbefda3cd11..4ec66720641 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -172,6 +172,7 @@ void WorldSession::HandleActivateTaxiOpcode(WorldPackets::Taxi::ActivateTaxi& ac if (!unit) { TC_LOG_DEBUG("network", "WORLD: HandleActivateTaxiOpcode - %s not found or you can't interact with it.", activateTaxi.Vendor.ToString().c_str()); + SendActivateTaxiReply(ERR_TAXITOOFARAWAY); return; } diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index 621f2a18e28..2443ba4c3d9 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -41,6 +41,7 @@ #include "WorldSession.h" #include <sstream> #include <cstdarg> +#include "SpellMgr.h" BossBoundaryData::~BossBoundaryData() { @@ -338,6 +339,13 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state) uint32 resInterval = GetCombatResurrectionChargeInterval(); InitializeCombatResurrections(1, resInterval); SendEncounterStart(1, 9, resInterval, resInterval); + + Map::PlayerList const &playerList = instance->GetPlayers(); + if (!playerList.isEmpty()) + for (Map::PlayerList::const_iterator i = playerList.begin(); i != playerList.end(); ++i) + if (Player* player = i->GetSource()) + if (player->IsAlive()) + player->ProcSkillsAndAuras(nullptr, PROC_FLAG_ENCOUNTER_START, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); break; } case FAIL: diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp index 681f441ea84..e9a4e0c4757 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -55,6 +55,7 @@ LootItem::LootItem(LootStoreItem const& li) is_blocked = 0; is_underthreshold = 0; is_counted = 0; + rollWinnerGUID = ObjectGuid::Empty; canSave = true; } @@ -518,6 +519,13 @@ void Loot::BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player* v // => item is lootable slot_type = LOOT_SLOT_TYPE_ALLOW_LOOT; } + else if (!items[i].rollWinnerGUID.IsEmpty()) + { + if (items[i].rollWinnerGUID == viewer->GetGUID()) + slot_type = LOOT_SLOT_TYPE_OWNER; + else + continue; + } else // item shall not be displayed. continue; diff --git a/src/server/game/Loot/Loot.h b/src/server/game/Loot/Loot.h index a2c30e42a0c..f9da5537806 100644 --- a/src/server/game/Loot/Loot.h +++ b/src/server/game/Loot/Loot.h @@ -140,6 +140,7 @@ struct TC_GAME_API LootItem uint8 context; ConditionContainer conditions; // additional loot condition GuidSet allowedGUIDs; + ObjectGuid rollWinnerGUID; // Stores the guid of person who won loot, if his bags are full only he can see the item in loot list! uint8 count : 8; bool is_looted : 1; bool is_blocked : 1; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index e840569672e..a0e7043532d 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -125,13 +125,20 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy) { if (vmgr->isMapLoadingEnabled()) { - bool exists = vmgr->existsMap((sWorld->GetDataPath()+ "vmaps").c_str(), mapid, gx, gy); - if (!exists) + VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), mapid, gx, gy); + std::string name = vmgr->getDirFileName(mapid, gx, gy); + switch (result) { - std::string name = vmgr->getDirFileName(mapid, gx, gy); - TC_LOG_ERROR("maps", "VMap file '%s' does not exist", (sWorld->GetDataPath()+"vmaps/"+name).c_str()); - TC_LOG_ERROR("maps", "Please place VMAP-files (*.vmtree and *.vmtile) in the vmap-directory (%s), or correct the DataDir setting in your worldserver.conf file.", (sWorld->GetDataPath()+"vmaps/").c_str()); - return false; + case VMAP::LoadResult::Success: + break; + case VMAP::LoadResult::FileNotFound: + TC_LOG_ERROR("maps", "VMap file '%s' does not exist", (sWorld->GetDataPath() + "vmaps/" + name).c_str()); + TC_LOG_ERROR("maps", "Please place VMAP files (*.vmtree and *.vmtile) in the vmap directory (%s), or correct the DataDir setting in your worldserver.conf file.", (sWorld->GetDataPath() + "vmaps/").c_str()); + return false; + case VMAP::LoadResult::VersionMismatch: + TC_LOG_ERROR("maps", "VMap file '%s' couldn't be loaded", (sWorld->GetDataPath() + "vmaps/" + name).c_str()); + TC_LOG_ERROR("maps", "This is because the version of the VMap file and the version of this module are different, please re-extract the maps with the tools compiled with this module."); + return false; } } } @@ -2878,9 +2885,9 @@ float Map::GetWaterLevel(PhaseShift const& phaseShift, float x, float y) const return 0; } -bool Map::isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2) const +bool Map::isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, VMAP::ModelIgnoreFlags ignoreFlags) const { - return VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(PhasingHandler::GetTerrainMapId(phaseShift, this, x1, y1), x1, y1, z1, x2, y2, z2) + return VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(PhasingHandler::GetTerrainMapId(phaseShift, this, x1, y1), x1, y1, z1, x2, y2, z2, ignoreFlags) && _dynamicTree.isInLineOfSight({ x1, y1, z1 }, { x2, y2, z2 }, phaseShift); } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 7d453f0406a..5498b097b54 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -67,6 +67,7 @@ enum WeatherState : uint32; namespace Trinity { struct ObjectUpdater; } namespace G3D { class Plane; } +namespace VMAP { enum class ModelIgnoreFlags : uint32; } struct ScriptAction { @@ -493,7 +494,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType> float GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground = nullptr, bool swim = false) const; float GetHeight(PhaseShift const& phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; - bool isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2) const; + bool isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, VMAP::ModelIgnoreFlags ignoreFlags) const; void Balance() { _dynamicTree.balance(); } void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); } void InsertGameObjectModel(const GameObjectModel& model) { _dynamicTree.insert(model); } diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 039c3b81604..820edc8adb9 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -927,7 +927,8 @@ enum TrinityStrings LANG_ACCOUNT_BNET_NOT_LINKED = 1189, LANG_DISALLOW_TICKETS_CONFIG = 1190, LANG_BAN_EXISTS = 1191, - // Room for more level 3 1192-1198 not used + LANG_CHANGEACCOUNT_SUCCESS = 1192, + // Room for more level 3 1193-1198 not used // Debug commands LANG_DEBUG_AREATRIGGER_LEFT = 1999, diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp index c4cd065ccbf..f3e3f7db303 100644 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp @@ -54,7 +54,7 @@ void FleeingMovementGenerator<T>::_setTargetLocation(T* owner) Position mypos = owner->GetPosition(); bool isInLOS = VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight( PhasingHandler::GetTerrainMapId(owner->GetPhaseShift(), owner->GetMap(), mypos.m_positionX, mypos.m_positionY), - mypos.m_positionX, mypos.m_positionY, mypos.m_positionZ + 2.0f, x, y, z + 2.0f); + mypos.m_positionX, mypos.m_positionY, mypos.m_positionZ + 2.0f, x, y, z + 2.0f, VMAP::ModelIgnoreFlags::Nothing); if (!isInLOS) { i_nextCheckTime.Reset(200); diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 51e54b3ba26..c9f75345754 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -120,6 +120,9 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) if (!i_path || i_path->nodes.empty()) return false; + if (Stopped()) + return true; + bool transportPath = creature->GetTransport() != nullptr; if (IsArrivalDone) diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 2367e26218c..c7e87633db0 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -402,7 +402,8 @@ class CreatureGameObjectAreaTriggerScriptRegistrySwapHooks ASSERT(!creature->IsCharmed(), "There is a disabled AI which is still loaded."); - creature->AI()->EnterEvadeMode(); + if (creature->IsAlive()) + creature->AI()->EnterEvadeMode(); } static void UnloadDestroyScript(Creature* creature) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 09d994e985d..d9b428c0598 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1936,7 +1936,7 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar if (Unit* unit = (*itr)->ToUnit()) { uint32 deficit = unit->GetMaxHealth() - unit->GetHealth(); - if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit)) + if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2)) { foundItr = itr; maxHPDeficit = deficit; @@ -1951,10 +1951,10 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar { if (foundItr == tempTargets.end()) { - if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr)) + if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } - else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr)) + else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } } @@ -2020,7 +2020,6 @@ void Spell::prepareDataForTriggerSystem() // For other spells trigger procflags are set in Spell::DoAllEffectOnTarget // Because spell positivity is dependant on target } - m_hitMask = PROC_HIT_NONE; // Hunter trap spells - activation proc for Lock and Load, Entrapment and Misdirection if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && @@ -2842,6 +2841,9 @@ bool Spell::UpdateChanneledTargetList() range = m_spellInfo->GetMaxRange(m_spellInfo->IsPositive()); if (Player* modOwner = m_caster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this); + + // add little tolerance level + range += std::min(MAX_SPELL_RANGE_TOLERANCE, range*0.1f); // 10% but no more than MAX_SPELL_RANGE_TOLERANCE } for (std::vector<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) @@ -3380,8 +3382,14 @@ void Spell::handle_immediate() // process immediate effects (items, ground, etc.) also initialize some variables _handle_immediate_phase(); - for (std::vector<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - DoAllEffectOnTarget(&(*ihit)); + // consider spell hit for some spells without target, so they may proc on finish phase correctly + if (m_UniqueTargetInfo.empty()) + m_hitMask = PROC_HIT_NORMAL; + else + { + for (std::vector<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + DoAllEffectOnTarget(&(*ihit)); + } for (std::vector<GOTargetInfo>::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit) DoAllEffectOnTarget(&(*ihit)); @@ -4999,7 +5007,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (DynamicObject* dynObj = m_caster->GetDynObject(m_triggeredByAuraSpell->Id)) losTarget = dynObj; - if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget, VMAP::ModelIgnoreFlags::M2)) return SPELL_FAILED_LINE_OF_SIGHT; } } @@ -5011,7 +5019,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint float x, y, z; m_targets.GetDstPos()->GetPosition(x, y, z); - if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS) && !m_caster->IsWithinLOS(x, y, z)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_DISABLE_LOS) && !m_caster->IsWithinLOS(x, y, z, VMAP::ModelIgnoreFlags::M2)) return SPELL_FAILED_LINE_OF_SIGHT; } @@ -5294,6 +5302,10 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!target) return SPELL_FAILED_DONT_REPORT; + // first we must check to see if the target is in LoS. A path can usually be built but LoS matters for charge spells + if (!target->IsWithinLOSInMap(m_caster)) //Do full LoS/Path check. Don't exclude m2 + return SPELL_FAILED_LINE_OF_SIGHT; + float objSize = target->GetObjectSize(); float range = m_spellInfo->GetMaxRange(true, m_caster, this) * 1.5f + objSize; // can't be overly strict @@ -6045,6 +6057,10 @@ SpellCastResult Spell::CheckRange(bool strict) const float minRange, maxRange; std::tie(minRange, maxRange) = GetMinMaxRange(strict); + // dont check max_range to strictly after cast + if (m_spellInfo->RangeEntry && m_spellInfo->RangeEntry->Flags != SPELL_RANGE_MELEE && !strict) + maxRange += std::min(MAX_SPELL_RANGE_TOLERANCE, maxRange*0.1f); // 10% but no more than MAX_SPELL_RANGE_TOLERANCE + // get square values for sqr distance checks minRange *= minRange; maxRange *= maxRange; @@ -6845,7 +6861,7 @@ bool Spell::CheckEffectTarget(Unit const* target, SpellEffectInfo const* effect, /// @todo shit below shouldn't be here, but it's temporary //Check targets for LOS visibility if (losPosition) - return target->IsWithinLOS(losPosition->GetPositionX(), losPosition->GetPositionY(), losPosition->GetPositionZ()); + return target->IsWithinLOS(losPosition->GetPositionX(), losPosition->GetPositionY(), losPosition->GetPositionZ(), VMAP::ModelIgnoreFlags::M2); else { // Get GO cast coordinates if original caster -> GO @@ -6854,7 +6870,7 @@ bool Spell::CheckEffectTarget(Unit const* target, SpellEffectInfo const* effect, caster = m_caster->GetMap()->GetGameObject(m_originalCasterGUID); if (!caster) caster = m_caster; - if (target != m_caster && !target->IsWithinLOSInMap(caster)) + if (target != m_caster && !target->IsWithinLOSInMap(caster, VMAP::ModelIgnoreFlags::M2)) return false; } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index cd4f3f51a08..c0a9dff527b 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -65,6 +65,7 @@ enum TriggerCastFlags : uint32; enum WeaponAttackType : uint8; #define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILLISECONDS) +#define MAX_SPELL_RANGE_TOLERANCE 3.0f enum SpellCastFlags { diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 0e04e33937d..65aef5dca41 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -225,7 +225,7 @@ enum SpellAuraInterruptFlags : uint32 AURA_INTERRUPT_FLAG_CAST = 0x00000004, // 2 cast any spells AURA_INTERRUPT_FLAG_MOVE = 0x00000008, // 3 removed by any movement AURA_INTERRUPT_FLAG_TURNING = 0x00000010, // 4 removed by any turning - AURA_INTERRUPT_FLAG_JUMP = 0x00000020, // 5 removed by entering combat + AURA_INTERRUPT_FLAG_JUMP = 0x00000020, // 5 removed by jumping AURA_INTERRUPT_FLAG_NOT_MOUNTED = 0x00000040, // 6 removed by dismounting AURA_INTERRUPT_FLAG_NOT_ABOVEWATER = 0x00000080, // 7 removed by entering water AURA_INTERRUPT_FLAG_NOT_UNDERWATER = 0x00000100, // 8 removed by leaving water diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 483f695283f..2bb498cf9af 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -1457,6 +1457,15 @@ void SpellMgr::LoadSpellProcs() isTriggerAura[SPELL_AURA_MOD_SPELL_DAMAGE_FROM_CASTER] = true; isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true; isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true; + isTriggerAura[SPELL_AURA_MOD_INVISIBILITY] = true; + isTriggerAura[SPELL_AURA_FORCE_REACTION] = true; + isTriggerAura[SPELL_AURA_MOD_TAUNT] = true; + isTriggerAura[SPELL_AURA_MOD_DETAUNT] = true; + isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_DONE] = true; + isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER_PCT] = true; + isTriggerAura[SPELL_AURA_MOD_HIT_CHANCE] = true; + isTriggerAura[SPELL_AURA_MOD_WEAPON_CRIT_PERCENT] = true; + isTriggerAura[SPELL_AURA_MOD_BLOCK_PERCENT] = true; isTriggerAura[SPELL_AURA_MOD_ROOT_2] = true; isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true; @@ -1466,6 +1475,7 @@ void SpellMgr::LoadSpellProcs() isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true; isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true; isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true; + isAlwaysTriggeredAura[SPELL_AURA_MOD_INVISIBILITY] = true; isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true; isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true; isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true; @@ -1478,6 +1488,7 @@ void SpellMgr::LoadSpellProcs() spellTypeMask[SPELL_AURA_MOD_ROOT_2] = PROC_SPELL_TYPE_DAMAGE; spellTypeMask[SPELL_AURA_MOD_STUN] = PROC_SPELL_TYPE_DAMAGE; spellTypeMask[SPELL_AURA_TRANSFORM] = PROC_SPELL_TYPE_DAMAGE; + spellTypeMask[SPELL_AURA_MOD_INVISIBILITY] = PROC_SPELL_TYPE_DAMAGE; // This generates default procs to retain compatibility with previous proc system TC_LOG_INFO("server.loading", "Generating spell proc data from SpellMap..."); @@ -1561,14 +1572,30 @@ void SpellMgr::LoadSpellProcs() procEntry.SpellPhaseMask = PROC_SPELL_PHASE_HIT; procEntry.HitMask = PROC_HIT_NONE; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent - // Reflect auras should only proc off reflects for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE)) { - if (effect && (effect->IsAura(SPELL_AURA_REFLECT_SPELLS) || effect->IsAura(SPELL_AURA_REFLECT_SPELLS_SCHOOL))) + if (!effect || !effect->IsAura()) + continue; + + switch (effect->ApplyAuraName) { - procEntry.HitMask = PROC_HIT_REFLECT; - break; + // Reflect auras should only proc off reflects + case SPELL_AURA_REFLECT_SPELLS: + case SPELL_AURA_REFLECT_SPELLS_SCHOOL: + procEntry.HitMask = PROC_HIT_REFLECT; + break; + // Only drop charge on crit + case SPELL_AURA_MOD_WEAPON_CRIT_PERCENT: + procEntry.HitMask = PROC_HIT_CRITICAL; + break; + // Only drop charge on block + case SPELL_AURA_MOD_BLOCK_PERCENT: + procEntry.HitMask = PROC_HIT_BLOCK; + break; + default: + continue; } + break; } procEntry.AttributesMask = 0; @@ -2850,7 +2877,9 @@ void SpellMgr::LoadSpellInfoCorrections() ApplySpellFix({ 50661, // Weakened Resolve - 68979 // Unleashed Souls + 68979, // Unleashed Souls + 48714, // Compelled + 7853, // The Art of Being a Water Terror: Force Cast on Player }, [](SpellInfo* spellInfo) { spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(13); // 50000yd diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index a6cf260746d..cde18f19ffe 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1484,6 +1484,9 @@ void World::LoadConfigSettings(bool reload) // prevent character rename on character customization m_bool_configs[CONFIG_PREVENT_RENAME_CUSTOMIZATION] = sConfigMgr->GetBoolDefault("PreventRenameCharacterOnCustomization", false); + // Allow 5-man parties to use raid warnings + m_bool_configs[CONFIG_CHAT_PARTY_RAID_WARNINGS] = sConfigMgr->GetBoolDefault("PartyRaidWarnings", false); + // Check Invalid Position m_bool_configs[CONFIG_CREATURE_CHECK_INVALID_POSITION] = sConfigMgr->GetBoolDefault("Creature.CheckInvalidPosition", false); m_bool_configs[CONFIG_GAME_OBJECT_CHECK_INVALID_POSITION] = sConfigMgr->GetBoolDefault("GameObject.CheckInvalidPosition", false); @@ -3612,6 +3615,15 @@ void World::UpdateCharacterInfoLevel(ObjectGuid const& guid, uint8 level) itr->second.Level = level; } +void World::UpdateCharacterInfoAccount(ObjectGuid const& guid, uint32 accountId) +{ + auto itr = _characterInfoStore.find(guid); + if (itr == _characterInfoStore.end()) + return; + + itr->second.AccountId = accountId; +} + void World::UpdateCharacterInfoDeleted(ObjectGuid const& guid, bool deleted, std::string const* name /*= nullptr*/) { CharacterInfoContainer::iterator itr = _characterInfoStore.find(guid); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 972da5bfa89..1d0d01f4b1f 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -122,6 +122,7 @@ enum WorldBoolConfigs CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY, CONFIG_WEATHER, CONFIG_QUEST_IGNORE_RAID, + CONFIG_CHAT_PARTY_RAID_WARNINGS, CONFIG_DETECT_POS_COLLISION, CONFIG_RESTRICTED_LFG_CHANNEL, CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, @@ -803,6 +804,7 @@ class TC_GAME_API World bool HasCharacterInfo(ObjectGuid const& guid) { return _characterInfoStore.find(guid) != _characterInfoStore.end(); } void UpdateCharacterInfo(ObjectGuid const& guid, std::string const& name, uint8 gender = GENDER_NONE, uint8 race = RACE_NONE); void UpdateCharacterInfoLevel(ObjectGuid const& guid, uint8 level); + void UpdateCharacterInfoAccount(ObjectGuid const& guid, uint32 accountId); void UpdateCharacterInfoDeleted(ObjectGuid const& guid, bool deleted, std::string const* name = nullptr); uint32 GetCleaningFlags() const { return m_CleaningFlags; } diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index 101d522e85b..e2a29b62bea 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -61,6 +61,7 @@ public: { "customize", rbac::RBAC_PERM_COMMAND_CHARACTER_CUSTOMIZE, true, &HandleCharacterCustomizeCommand, "", }, { "changefaction", rbac::RBAC_PERM_COMMAND_CHARACTER_CHANGEFACTION, true, &HandleCharacterChangeFactionCommand, "", }, { "changerace", rbac::RBAC_PERM_COMMAND_CHARACTER_CHANGERACE, true, &HandleCharacterChangeRaceCommand, "", }, + { "changeaccount", rbac::RBAC_PERM_COMMAND_CHARACTER_CHANGEACCOUNT, true, &HandleCharacterChangeAccountCommand, "", }, { "deleted", rbac::RBAC_PERM_COMMAND_CHARACTER_DELETED, true, NULL, "", characterDeletedCommandTable }, { "erase", rbac::RBAC_PERM_COMMAND_CHARACTER_ERASE, true, &HandleCharacterEraseCommand, "", }, { "level", rbac::RBAC_PERM_COMMAND_CHARACTER_LEVEL, true, &HandleCharacterLevelCommand, "", }, @@ -551,6 +552,86 @@ public: return true; } + static bool HandleCharacterChangeAccountCommand(ChatHandler* handler, char const* args) + { + char* playerNameStr; + char* accountNameStr; + handler->extractOptFirstArg(const_cast<char*>(args), &playerNameStr, &accountNameStr); + if (!accountNameStr) + return false; + + ObjectGuid targetGuid; + std::string targetName; + if (!handler->extractPlayerTarget(playerNameStr, nullptr, &targetGuid, &targetName)) + return false; + + CharacterInfo const* characterInfo = sWorld->GetCharacterInfo(targetGuid); + if (!characterInfo) + { + handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); + handler->SetSentErrorMessage(true); + return false; + } + + uint32 oldAccountId = characterInfo->AccountId; + uint32 newAccountId = oldAccountId; + + std::string accountName(accountNameStr); + if (!Utf8ToUpperOnlyLatin(accountName)) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME); + stmt->setString(0, accountName); + if (PreparedQueryResult result = LoginDatabase.Query(stmt)) + newAccountId = (*result)[0].GetUInt32(); + else + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + // nothing to do :) + if (newAccountId == oldAccountId) + return true; + + if (uint32 charCount = AccountMgr::GetCharactersCount(newAccountId)) + { + if (charCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) + { + handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, accountName.c_str(), newAccountId); + handler->SetSentErrorMessage(true); + return false; + } + } + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_BY_GUID); + stmt->setUInt32(0, newAccountId); + stmt->setUInt32(1, targetGuid.GetCounter()); + CharacterDatabase.DirectExecute(stmt); + + sWorld->UpdateRealmCharCount(oldAccountId); + sWorld->UpdateRealmCharCount(newAccountId); + + sWorld->UpdateCharacterInfoAccount(targetGuid, newAccountId); + + handler->PSendSysMessage(LANG_CHANGEACCOUNT_SUCCESS, targetName.c_str(), accountName.c_str()); + + std::string logString = Trinity::StringFormat("changed ownership of player %s (%s) from account %u to account %u", targetName.c_str(), targetGuid.ToString().c_str(), oldAccountId, newAccountId); + if (WorldSession* session = handler->GetSession()) + { + if (Player* player = session->GetPlayer()) + sLog->outCommand(session->GetAccountId(), "GM %s (Account: %u) %s", player->GetName().c_str(), session->GetAccountId(), logString.c_str()); + } + else + sLog->outCommand(0, "%s %s", handler->GetTrinityString(LANG_CONSOLE), logString.c_str()); + return true; + } + static bool HandleCharacterReputationCommand(ChatHandler* handler, char const* args) { Player* target; diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index c72893c77a9..06ebeb5eb71 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -51,7 +51,7 @@ struct EnumName #define CREATE_NAMED_ENUM(VALUE) { VALUE, STRINGIZE(VALUE) } #define NPC_FLAG_COUNT 24 -#define FLAGS_EXTRA_COUNT 19 +#define FLAGS_EXTRA_COUNT 20 EnumName<NPCFlags, uint32> const npcFlagTexts[NPC_FLAG_COUNT] = { @@ -193,6 +193,7 @@ EnumName<CreatureFlagsExtra> const flagsExtra[FLAGS_EXTRA_COUNT] = CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_XP_AT_KILL), CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_TRIGGER), CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_TAUNT), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE), CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_WORLDEVENT), CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_GUARD), CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_CRIT), diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_saviana_ragefire.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_saviana_ragefire.cpp index f43052ebb0a..79a0f293fb2 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_saviana_ragefire.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_saviana_ragefire.cpp @@ -20,6 +20,7 @@ #include "MotionMaster.h" #include "ruby_sanctum.h" #include "ScriptedCreature.h" +#include "SpellMgr.h" #include "SpellScript.h" enum Texts @@ -76,14 +77,14 @@ class boss_saviana_ragefire : public CreatureScript struct boss_saviana_ragefireAI : public BossAI { - boss_saviana_ragefireAI(Creature* creature) : BossAI(creature, DATA_SAVIANA_RAGEFIRE) - { - } + boss_saviana_ragefireAI(Creature* creature) : BossAI(creature, DATA_SAVIANA_RAGEFIRE) { } void Reset() override { _Reset(); me->SetReactState(REACT_AGGRESSIVE); + me->SetCanFly(false); + me->SetDisableGravity(false); } void EnterCombat(Unit* /*who*/) override @@ -91,15 +92,15 @@ class boss_saviana_ragefire : public CreatureScript _EnterCombat(); Talk(SAY_AGGRO); events.Reset(); - events.ScheduleEvent(EVENT_ENRAGE, 20000, EVENT_GROUP_LAND_PHASE); - events.ScheduleEvent(EVENT_FLAME_BREATH, 14000, EVENT_GROUP_LAND_PHASE); - events.ScheduleEvent(EVENT_FLIGHT, 60000); + events.ScheduleEvent(EVENT_ENRAGE, Seconds(20), EVENT_GROUP_LAND_PHASE); + events.ScheduleEvent(EVENT_FLAME_BREATH, Seconds(14), EVENT_GROUP_LAND_PHASE); + events.ScheduleEvent(EVENT_FLIGHT, Seconds(60), EVENT_GROUP_LAND_PHASE); } void JustDied(Unit* /*killer*/) override { _JustDied(); - me->PlayDirectSound(SOUND_ID_DEATH); + DoPlaySoundToSet(me, SOUND_ID_DEATH); } void MovementInform(uint32 type, uint32 point) override @@ -110,32 +111,32 @@ class boss_saviana_ragefire : public CreatureScript switch (point) { case POINT_FLIGHT: - events.ScheduleEvent(EVENT_CONFLAGRATION, 1000); + events.ScheduleEvent(EVENT_CONFLAGRATION, Seconds(1)); Talk(SAY_CONFLAGRATION); break; case POINT_LAND: - events.ScheduleEvent(EVENT_LAND_GROUND, 1); + events.ScheduleEvent(EVENT_LAND_GROUND, Milliseconds(1)); break; case POINT_LAND_GROUND: me->SetCanFly(false); me->SetDisableGravity(false); me->RemoveByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); me->SetReactState(REACT_AGGRESSIVE); - DoStartMovement(me->GetVictim()); + events.ScheduleEvent(EVENT_ENRAGE, Seconds(1), EVENT_GROUP_LAND_PHASE); + events.ScheduleEvent(EVENT_FLAME_BREATH, Seconds(2), Seconds(4), EVENT_GROUP_LAND_PHASE); + events.ScheduleEvent(EVENT_FLIGHT, Seconds(50), EVENT_GROUP_LAND_PHASE); break; case POINT_TAKEOFF: - events.ScheduleEvent(EVENT_AIR_MOVEMENT, 1); + events.ScheduleEvent(EVENT_AIR_MOVEMENT, Milliseconds(1)); break; default: break; } } - void JustReachedHome() override + void EnterEvadeMode(EvadeReason /*why*/) override { - _JustReachedHome(); - me->SetCanFly(false); - me->SetDisableGravity(false); + _DespawnAtEvade(); } void KilledUnit(Unit* victim) override @@ -169,21 +170,20 @@ class boss_saviana_ragefire : public CreatureScript pos.Relocate(me); pos.m_positionZ += 10.0f; me->GetMotionMaster()->MoveTakeoff(POINT_TAKEOFF, pos); - events.ScheduleEvent(EVENT_FLIGHT, 50000); - events.DelayEvents(12500, EVENT_GROUP_LAND_PHASE); + events.CancelEventGroup(EVENT_GROUP_LAND_PHASE); break; } case EVENT_CONFLAGRATION: - DoCast(me, SPELL_CONFLAGRATION, true); + DoCastSelf(SPELL_CONFLAGRATION, true); break; case EVENT_ENRAGE: - DoCast(me, SPELL_ENRAGE); + DoCastSelf(SPELL_ENRAGE); Talk(EMOTE_ENRAGED); - events.ScheduleEvent(EVENT_ENRAGE, urand(15000, 20000), EVENT_GROUP_LAND_PHASE); + events.Repeat(Seconds(24)); break; case EVENT_FLAME_BREATH: DoCastVictim(SPELL_FLAME_BREATH); - events.ScheduleEvent(EVENT_FLAME_BREATH, urand(20000, 30000), EVENT_GROUP_LAND_PHASE); + events.Repeat(Seconds(20), Seconds(30)); break; case EVENT_AIR_MOVEMENT: me->GetMotionMaster()->MovePoint(POINT_FLIGHT, SavianaRagefireFlyOutPos); @@ -220,6 +220,7 @@ class ConflagrationTargetSelector } }; +// 74452 - Conflagration class spell_saviana_conflagration_init : public SpellScriptLoader { public: @@ -229,6 +230,14 @@ class spell_saviana_conflagration_init : public SpellScriptLoader { PrepareSpellScript(spell_saviana_conflagration_init_SpellScript); + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_FLAME_BEACON) + || !sSpellMgr->GetSpellInfo(SPELL_CONFLAGRATION_2)) + return false; + return true; + } + void FilterTargets(std::list<WorldObject*>& targets) { targets.remove_if(ConflagrationTargetSelector()); @@ -257,6 +266,7 @@ class spell_saviana_conflagration_init : public SpellScriptLoader } }; +// 74455 - Conflagration class spell_saviana_conflagration_throwback : public SpellScriptLoader { public: diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp index a9c638c010d..f003260b8d1 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp @@ -252,10 +252,6 @@ class boss_blood_council_controller : public CreatureScript void EnterEvadeMode(EvadeReason /*why*/) override { - // Avoid do set boss state to fail with hotswap - if (instance->GetBossState(DATA_BLOOD_PRINCE_COUNCIL) == DONE) - return; - for (uint32 bossData : PrincesData) if (Creature* prince = ObjectAccessor::GetCreature(*me, instance->GetGuidData(bossData))) instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, prince); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index a99a85394c6..8c9fc12ea18 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1836,6 +1836,14 @@ PreserveCustomChannels = 1 PreserveCustomChannelDuration = 14 # +# PartyRaidWarnings +# Description: Allow any user to use raid warnings when in a 5-man party. +# Default: 0 - (Disabled, Blizzlike) +# 1 - (Enabled) + +PartyRaidWarnings = 0 + +# ################################################################################################### ################################################################################################### @@ -1996,6 +2004,7 @@ Support.SuggestionsEnabled = 0 # Default: 1 - (Raid) # 0 - (Party) # 2 - (Faction) +# 3 - (None) Visibility.GroupMode = 1 |