Merge fix
This commit is contained in:
@@ -31,6 +31,8 @@ This is the minimum interface to the VMapMamager.
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
class StaticMapTree;
|
||||
|
||||
enum VMAP_LOAD_RESULT
|
||||
{
|
||||
VMAP_LOAD_RESULT_ERROR,
|
||||
@@ -88,20 +90,8 @@ namespace VMAP
|
||||
|
||||
virtual ~IVMapMgr() = default;
|
||||
|
||||
virtual int loadMap(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, 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
|
||||
return a position, that is pReduceDist closer to the origin
|
||||
*/
|
||||
virtual bool GetObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float pModifyDist) = 0;
|
||||
/**
|
||||
send debug commands
|
||||
*/
|
||||
@@ -123,12 +113,6 @@ namespace VMAP
|
||||
[[nodiscard]] bool isMapLoadingEnabled() const { return (iEnableLineOfSightCalc || iEnableHeightCalc ); }
|
||||
|
||||
[[nodiscard]] virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const = 0;
|
||||
|
||||
/**
|
||||
Query world model area info.
|
||||
\param z gets adjusted to the ground height for which this are info is valid
|
||||
*/
|
||||
virtual bool GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional<uint8> reqLiquidType, AreaAndLiquidData& data) const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "MMapFactory.h"
|
||||
#include <cstring>
|
||||
namespace MMAP
|
||||
{
|
||||
// ######################## MMapFactory ########################
|
||||
// our global singleton copy
|
||||
MMapMgr* g_MMapMgr = nullptr;
|
||||
bool MMapFactory::forbiddenMaps[1000] = {0};
|
||||
|
||||
MMapMgr* MMapFactory::createOrGetMMapMgr()
|
||||
{
|
||||
if (!g_MMapMgr)
|
||||
{
|
||||
g_MMapMgr = new MMapMgr();
|
||||
}
|
||||
|
||||
return g_MMapMgr;
|
||||
}
|
||||
|
||||
void MMapFactory::InitializeDisabledMaps()
|
||||
{
|
||||
memset(&forbiddenMaps, 0, sizeof(forbiddenMaps));
|
||||
int32 f[] = {616 /*EoE*/, 649 /*ToC25*/, 650 /*ToC5*/, -1};
|
||||
uint32 i = 0;
|
||||
while (f[i] >= 0)
|
||||
{
|
||||
forbiddenMaps[f[i++]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MMapFactory::clear()
|
||||
{
|
||||
if (g_MMapMgr)
|
||||
{
|
||||
delete g_MMapMgr;
|
||||
g_MMapMgr = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 _MMAP_FACTORY_H
|
||||
#define _MMAP_FACTORY_H
|
||||
|
||||
#include "MMapMgr.h"
|
||||
|
||||
namespace MMAP
|
||||
{
|
||||
enum MMAP_LOAD_RESULT
|
||||
{
|
||||
MMAP_LOAD_RESULT_ERROR,
|
||||
MMAP_LOAD_RESULT_OK,
|
||||
MMAP_LOAD_RESULT_IGNORED,
|
||||
};
|
||||
|
||||
// static class
|
||||
// holds all mmap global data
|
||||
// access point to MMapMgr singleton
|
||||
class MMapFactory
|
||||
{
|
||||
public:
|
||||
static MMapMgr* createOrGetMMapMgr();
|
||||
static void clear();
|
||||
static void InitializeDisabledMaps();
|
||||
static bool forbiddenMaps[1000];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -24,63 +24,8 @@
|
||||
namespace MMAP
|
||||
{
|
||||
// ######################## MMapMgr ########################
|
||||
MMapMgr::~MMapMgr()
|
||||
std::shared_ptr<dtNavMesh> MMapMgr::LoadNavMesh(uint32 mapId)
|
||||
{
|
||||
for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
|
||||
{
|
||||
delete i->second;
|
||||
}
|
||||
|
||||
// by now we should not have maps loaded
|
||||
// if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
|
||||
}
|
||||
|
||||
void MMapMgr::InitializeThreadUnsafe(const std::vector<uint32>& mapIds)
|
||||
{
|
||||
// the caller must pass the list of all mapIds that will be used in the VMapMgr2 lifetime
|
||||
for (const uint32& mapId : mapIds)
|
||||
{
|
||||
loadedMMaps.emplace(mapId, nullptr);
|
||||
}
|
||||
|
||||
thread_safe_environment = false;
|
||||
}
|
||||
|
||||
MMapDataSet::const_iterator MMapMgr::GetMMapData(uint32 mapId) const
|
||||
{
|
||||
// return the iterator if found or end() if not found/NULL
|
||||
MMapDataSet::const_iterator itr = loadedMMaps.find(mapId);
|
||||
if (itr != loadedMMaps.cend() && !itr->second)
|
||||
{
|
||||
itr = loadedMMaps.cend();
|
||||
}
|
||||
|
||||
return itr;
|
||||
}
|
||||
|
||||
bool MMapMgr::loadMapData(uint32 mapId)
|
||||
{
|
||||
// we already have this map loaded?
|
||||
MMapDataSet::iterator itr = loadedMMaps.find(mapId);
|
||||
if (itr != loadedMMaps.end())
|
||||
{
|
||||
if (itr->second)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (thread_safe_environment)
|
||||
{
|
||||
itr = loadedMMaps.insert(MMapDataSet::value_type(mapId, nullptr)).first;
|
||||
}
|
||||
else
|
||||
{
|
||||
ABORT("Invalid mapId {} passed to MMapMgr after startup in thread unsafe environment", mapId);
|
||||
}
|
||||
}
|
||||
|
||||
// load and init dtNavMesh - read parameters from file
|
||||
std::string fileName = Acore::StringFormat(MAP_FILE_NAME_FORMAT, sConfigMgr->GetOption<std::string>("DataDir", "."), mapId);
|
||||
|
||||
@@ -88,7 +33,7 @@ namespace MMAP
|
||||
if (!file)
|
||||
{
|
||||
LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '{}'", fileName);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dtNavMeshParams params;
|
||||
@@ -97,7 +42,7 @@ namespace MMAP
|
||||
if (count != 1)
|
||||
{
|
||||
LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '{}'", fileName);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dtNavMesh* mesh = dtAllocNavMesh();
|
||||
@@ -106,15 +51,13 @@ namespace MMAP
|
||||
{
|
||||
dtFreeNavMesh(mesh);
|
||||
LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {:03} from file {}", mapId, fileName);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG_DEBUG("maps", "MMAP:loadMapData: Loaded {:03}.mmap", mapId);
|
||||
|
||||
// store inside our map list
|
||||
MMapData* mmap_data = new MMapData(mesh);
|
||||
itr->second = mmap_data;
|
||||
return true;
|
||||
std::shared_ptr<dtNavMesh> navMesh = std::shared_ptr<dtNavMesh>(mesh, NavMeshDeleter());
|
||||
return navMesh;
|
||||
}
|
||||
|
||||
uint32 MMapMgr::packTileID(int32 x, int32 y)
|
||||
@@ -122,26 +65,8 @@ namespace MMAP
|
||||
return uint32(x << 16 | y);
|
||||
}
|
||||
|
||||
bool MMapMgr::loadMap(uint32 mapId, int32 x, int32 y)
|
||||
bool MMapMgr::LoadTile(dtNavMesh* navMesh, uint32 mapId, int32 x, int32 y)
|
||||
{
|
||||
// make sure the mmap is loaded and ready to load tiles
|
||||
if (!loadMapData(mapId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// get this mmap data
|
||||
MMapData* mmap = loadedMMaps[mapId];
|
||||
ASSERT(mmap->navMesh);
|
||||
|
||||
// check if we already have this tile loaded
|
||||
uint32 packedGridPos = packTileID(x, y);
|
||||
if (mmap->loadedTileRefs.find(packedGridPos) != mmap->loadedTileRefs.end())
|
||||
{
|
||||
LOG_ERROR("maps", "MMAP:loadMap: Asked to load already loaded navmesh tile. {:03}{:02}{:02}.mmtile", mapId, x, y);
|
||||
return false;
|
||||
}
|
||||
|
||||
// load this tile :: mmaps/MMMXXYY.mmtile
|
||||
std::string fileName = Acore::StringFormat(TILE_FILE_NAME_FORMAT, sConfigMgr->GetOption<std::string>("DataDir", "."), mapId, x, y);
|
||||
FILE* file = fopen(fileName.c_str(), "rb");
|
||||
@@ -184,10 +109,8 @@ namespace MMAP
|
||||
dtTileRef tileRef = 0;
|
||||
|
||||
// memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
|
||||
if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
|
||||
if (dtStatusSucceed(navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef)))
|
||||
{
|
||||
mmap->loadedTileRefs.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
|
||||
++loadedTiles;
|
||||
dtMeshHeader* header = (dtMeshHeader*)data;
|
||||
LOG_DEBUG("maps", "MMAP:loadMap: Loaded mmtile {:03}[{:02},{:02}] into {:03}[{:02},{:02}]", mapId, x, y, mapId, header->x, header->y);
|
||||
return true;
|
||||
@@ -198,149 +121,19 @@ namespace MMAP
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MMapMgr::unloadMap(uint32 mapId, int32 x, int32 y)
|
||||
ManagedNavMeshQuery MMapMgr::CreateNavMeshQuery(dtNavMesh* navMesh)
|
||||
{
|
||||
// check if we have this map loaded
|
||||
MMapDataSet::const_iterator itr = GetMMapData(mapId);
|
||||
if (itr == loadedMMaps.end())
|
||||
{
|
||||
// file may not exist, therefore not loaded
|
||||
LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. {:03}{:02}{:02}.mmtile", mapId, x, y);
|
||||
return false;
|
||||
}
|
||||
|
||||
MMapData* mmap = itr->second;
|
||||
|
||||
// check if we have this tile loaded
|
||||
uint32 packedGridPos = packTileID(x, y);
|
||||
if (mmap->loadedTileRefs.find(packedGridPos) == mmap->loadedTileRefs.end())
|
||||
{
|
||||
// file may not exist, therefore not loaded
|
||||
LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh tile. {:03}{:02}{:02}.mmtile", mapId, x, y);
|
||||
return false;
|
||||
}
|
||||
|
||||
dtTileRef tileRef = mmap->loadedTileRefs[packedGridPos];
|
||||
|
||||
// unload, and mark as non loaded
|
||||
if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, nullptr, nullptr)))
|
||||
{
|
||||
// this is technically a memory leak
|
||||
// if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
|
||||
// we cannot recover from this error - assert out
|
||||
LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:03}{:02}{:02}.mmtile from navmesh", mapId, x, y);
|
||||
ABORT();
|
||||
}
|
||||
|
||||
mmap->loadedTileRefs.erase(packedGridPos);
|
||||
--loadedTiles;
|
||||
LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:03}[{:02},{:02}] from {:03}", mapId, x, y, mapId);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MMapMgr::unloadMap(uint32 mapId)
|
||||
{
|
||||
MMapDataSet::iterator itr = loadedMMaps.find(mapId);
|
||||
if (itr == loadedMMaps.end() || !itr->second)
|
||||
{
|
||||
// file may not exist, therefore not loaded
|
||||
LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map {:03}", mapId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// unload all tiles from given map
|
||||
MMapData* mmap = itr->second;
|
||||
for (auto& i : mmap->loadedTileRefs)
|
||||
{
|
||||
uint32 x = (i.first >> 16);
|
||||
uint32 y = (i.first & 0x0000FFFF);
|
||||
|
||||
if (dtStatusFailed(mmap->navMesh->removeTile(i.second, nullptr, nullptr)))
|
||||
{
|
||||
LOG_ERROR("maps", "MMAP:unloadMap: Could not unload {:03}{:02}{:02}.mmtile from navmesh", mapId, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
--loadedTiles;
|
||||
LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded mmtile {:03}[{:02},{:02}] from {:03}", mapId, x, y, mapId);
|
||||
}
|
||||
}
|
||||
|
||||
delete mmap;
|
||||
itr->second = nullptr;
|
||||
LOG_DEBUG("maps", "MMAP:unloadMap: Unloaded {:03}.mmap", mapId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MMapMgr::unloadMapInstance(uint32 mapId, uint32 instanceId)
|
||||
{
|
||||
// check if we have this map loaded
|
||||
MMapDataSet::const_iterator itr = GetMMapData(mapId);
|
||||
if (itr == loadedMMaps.end())
|
||||
{
|
||||
// file may not exist, therefore not loaded
|
||||
LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map {:03}", mapId);
|
||||
return false;
|
||||
}
|
||||
|
||||
MMapData* mmap = itr->second;
|
||||
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
|
||||
{
|
||||
LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId {:03} instanceId {}", mapId, instanceId);
|
||||
return false;
|
||||
}
|
||||
|
||||
dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
|
||||
|
||||
dtFreeNavMeshQuery(query);
|
||||
mmap->navMeshQueries.erase(instanceId);
|
||||
LOG_DEBUG("maps", "MMAP:unloadMapInstance: Unloaded mapId {:03} instanceId {}", mapId, instanceId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
dtNavMesh const* MMapMgr::GetNavMesh(uint32 mapId)
|
||||
{
|
||||
MMapDataSet::const_iterator itr = GetMMapData(mapId);
|
||||
if (itr == loadedMMaps.end())
|
||||
// allocate mesh query
|
||||
dtNavMeshQuery* query = dtAllocNavMeshQuery();
|
||||
ASSERT(query);
|
||||
|
||||
if (dtStatusFailed(query->init(navMesh, 1024)))
|
||||
{
|
||||
dtFreeNavMeshQuery(query);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return itr->second->navMesh;
|
||||
}
|
||||
|
||||
dtNavMeshQuery const* MMapMgr::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
|
||||
{
|
||||
MMapDataSet::const_iterator itr = GetMMapData(mapId);
|
||||
if (itr == loadedMMaps.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MMapData* mmap = itr->second;
|
||||
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
|
||||
{
|
||||
// check again after acquiring mutex
|
||||
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
|
||||
{
|
||||
// allocate mesh query
|
||||
dtNavMeshQuery* query = dtAllocNavMeshQuery();
|
||||
ASSERT(query);
|
||||
|
||||
if (dtStatusFailed(query->init(mmap->navMesh, 1024)))
|
||||
{
|
||||
dtFreeNavMeshQuery(query);
|
||||
LOG_ERROR("maps", "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {:03} instanceId {}", mapId, instanceId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG_DEBUG("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {:03} instanceId {}", mapId, instanceId);
|
||||
mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
|
||||
}
|
||||
}
|
||||
|
||||
return mmap->navMeshQueries[instanceId];
|
||||
ManagedNavMeshQuery navMeshQuery = ManagedNavMeshQuery(query);
|
||||
return navMeshQuery;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
#include "DetourAlloc.h"
|
||||
#include "DetourExtended.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// memory management
|
||||
inline void* dtCustomAlloc(std::size_t size, dtAllocHint /*hint*/)
|
||||
@@ -39,67 +38,40 @@ inline void dtCustomFree(void* ptr)
|
||||
// move map related classes
|
||||
namespace MMAP
|
||||
{
|
||||
enum MMAP_LOAD_RESULT
|
||||
{
|
||||
MMAP_LOAD_RESULT_ERROR,
|
||||
MMAP_LOAD_RESULT_OK,
|
||||
MMAP_LOAD_RESULT_IGNORED,
|
||||
};
|
||||
|
||||
static char const* const MAP_FILE_NAME_FORMAT = "{}/mmaps/{:03}.mmap";
|
||||
static char const* const TILE_FILE_NAME_FORMAT = "{}/mmaps/{:03}{:02}{:02}.mmtile";
|
||||
|
||||
typedef std::unordered_map<uint32, dtTileRef> MMapTileSet;
|
||||
typedef std::unordered_map<uint32, dtNavMeshQuery*> NavMeshQuerySet;
|
||||
|
||||
// dummy struct to hold map's mmap data
|
||||
struct MMapData
|
||||
struct NavMeshDeleter
|
||||
{
|
||||
MMapData(dtNavMesh* mesh) : navMesh(mesh) { }
|
||||
|
||||
~MMapData()
|
||||
{
|
||||
for (auto& navMeshQuerie : navMeshQueries)
|
||||
{
|
||||
dtFreeNavMeshQuery(navMeshQuerie.second);
|
||||
}
|
||||
|
||||
if (navMesh)
|
||||
{
|
||||
dtFreeNavMesh(navMesh);
|
||||
}
|
||||
}
|
||||
|
||||
// we have to use single dtNavMeshQuery for every instance, since those are not thread safe
|
||||
NavMeshQuerySet navMeshQueries; // instanceId to query
|
||||
dtNavMesh* navMesh;
|
||||
MMapTileSet loadedTileRefs; // maps [map grid coords] to [dtTile]
|
||||
void operator()(dtNavMesh* navMesh) noexcept { dtFreeNavMesh(navMesh); }
|
||||
};
|
||||
|
||||
typedef std::unordered_map<uint32, MMapData*> MMapDataSet;
|
||||
struct NavMeshQueryDeleter
|
||||
{
|
||||
void operator()(dtNavMeshQuery* query) noexcept { dtFreeNavMeshQuery(query); }
|
||||
};
|
||||
|
||||
using ManagedNavMeshQuery = std::unique_ptr<dtNavMeshQuery, NavMeshQueryDeleter>;
|
||||
|
||||
// singleton class
|
||||
// holds all all access to mmap loading unloading and meshes
|
||||
class MMapMgr
|
||||
{
|
||||
public:
|
||||
MMapMgr() = default;
|
||||
~MMapMgr();
|
||||
MMapMgr() = default;
|
||||
~MMapMgr() = default;
|
||||
|
||||
void InitializeThreadUnsafe(const std::vector<uint32>& mapIds);
|
||||
bool loadMap(uint32 mapId, int32 x, int32 y);
|
||||
bool unloadMap(uint32 mapId, int32 x, int32 y);
|
||||
bool unloadMap(uint32 mapId);
|
||||
bool unloadMapInstance(uint32 mapId, uint32 instanceId);
|
||||
|
||||
// the returned [dtNavMeshQuery const*] is NOT threadsafe
|
||||
dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
|
||||
dtNavMesh const* GetNavMesh(uint32 mapId);
|
||||
|
||||
[[nodiscard]] uint32 getLoadedTilesCount() const { return loadedTiles; }
|
||||
[[nodiscard]] uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
|
||||
static std::shared_ptr<dtNavMesh> LoadNavMesh(uint32 mapId);
|
||||
static bool LoadTile(dtNavMesh* navMesh, uint32 mapId, int32 x, int32 y);
|
||||
static ManagedNavMeshQuery CreateNavMeshQuery(dtNavMesh* navMesh);
|
||||
|
||||
private:
|
||||
bool loadMapData(uint32 mapId);
|
||||
uint32 packTileID(int32 x, int32 y);
|
||||
[[nodiscard]] MMapDataSet::const_iterator GetMMapData(uint32 mapId) const;
|
||||
|
||||
MMapDataSet loadedMMaps;
|
||||
uint32 loadedTiles{0};
|
||||
bool thread_safe_environment{true};
|
||||
static uint32 packTileID(int32 x, int32 y);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,8 @@
|
||||
|
||||
#include "VMapMgr2.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "MapDefines.h"
|
||||
#include "MapTree.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "WorldModel.h"
|
||||
#include <G3D/Vector3.h>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
@@ -35,34 +32,13 @@ namespace VMAP
|
||||
{
|
||||
GetLiquidFlagsPtr = &GetLiquidFlagsDummy;
|
||||
IsVMAPDisabledForPtr = &IsVMAPDisabledForDummy;
|
||||
thread_safe_environment = true;
|
||||
}
|
||||
|
||||
VMapMgr2::~VMapMgr2()
|
||||
{
|
||||
for (InstanceTreeMap::iterator i = iInstanceMapTrees.begin(); i != iInstanceMapTrees.end(); ++i)
|
||||
{
|
||||
delete i->second;
|
||||
}
|
||||
|
||||
for (ModelFileMap::iterator i = iLoadedModelFiles.begin(); i != iLoadedModelFiles.end(); ++i)
|
||||
{
|
||||
delete i->second.getModel();
|
||||
}
|
||||
}
|
||||
|
||||
void VMapMgr2::InitializeThreadUnsafe(const std::vector<uint32>& mapIds)
|
||||
{
|
||||
// the caller must pass the list of all mapIds that will be used in the VMapMgr2 lifetime
|
||||
for (const uint32& mapId : mapIds)
|
||||
{
|
||||
iInstanceMapTrees.emplace(mapId, nullptr);
|
||||
}
|
||||
|
||||
thread_safe_environment = false;
|
||||
}
|
||||
|
||||
Vector3 VMapMgr2::convertPositionToInternalRep(float x, float y, float z) const
|
||||
Vector3 VMapMgr2::convertPositionToInternalRep(float x, float y, float z)
|
||||
{
|
||||
Vector3 pos;
|
||||
const float mid = 0.5f * MAX_NUMBER_OF_GRIDS * SIZE_OF_GRIDS;
|
||||
@@ -73,18 +49,6 @@ namespace VMAP
|
||||
return pos;
|
||||
}
|
||||
|
||||
InstanceTreeMap::const_iterator VMapMgr2::GetMapTree(uint32 mapId) const
|
||||
{
|
||||
// return the iterator if found or end() if not found/NULL
|
||||
InstanceTreeMap::const_iterator itr = iInstanceMapTrees.find(mapId);
|
||||
if (itr != iInstanceMapTrees.cend() && !itr->second)
|
||||
{
|
||||
itr = iInstanceMapTrees.cend();
|
||||
}
|
||||
|
||||
return itr;
|
||||
}
|
||||
|
||||
// move to MapTree too?
|
||||
std::string VMapMgr2::getMapFileName(unsigned int mapId)
|
||||
{
|
||||
@@ -95,245 +59,9 @@ namespace VMAP
|
||||
return fname.str();
|
||||
}
|
||||
|
||||
int VMapMgr2::loadMap(const char* basePath, unsigned int mapId, int x, int y)
|
||||
{
|
||||
int result = VMAP_LOAD_RESULT_IGNORED;
|
||||
if (isMapLoadingEnabled())
|
||||
{
|
||||
if (_loadMap(mapId, basePath, x, y))
|
||||
{
|
||||
result = VMAP_LOAD_RESULT_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = VMAP_LOAD_RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// load one tile (internal use only)
|
||||
bool VMapMgr2::_loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY)
|
||||
{
|
||||
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
|
||||
if (instanceTree == iInstanceMapTrees.end())
|
||||
{
|
||||
if (thread_safe_environment)
|
||||
{
|
||||
instanceTree = iInstanceMapTrees.insert(InstanceTreeMap::value_type(mapId, nullptr)).first;
|
||||
}
|
||||
else
|
||||
ABORT("Invalid mapId {} tile [{}, {}] passed to VMapMgr2 after startup in thread unsafe environment",
|
||||
mapId, tileX, tileY);
|
||||
}
|
||||
|
||||
if (!instanceTree->second)
|
||||
{
|
||||
std::string mapFileName = getMapFileName(mapId);
|
||||
StaticMapTree* newTree = new StaticMapTree(mapId, basePath);
|
||||
if (!newTree->InitMap(mapFileName, this))
|
||||
{
|
||||
delete newTree;
|
||||
return false;
|
||||
}
|
||||
instanceTree->second = newTree;
|
||||
}
|
||||
|
||||
return instanceTree->second->LoadMapTile(tileX, tileY, this);
|
||||
}
|
||||
|
||||
void VMapMgr2::unloadMap(unsigned int mapId)
|
||||
{
|
||||
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
|
||||
if (instanceTree != iInstanceMapTrees.end() && instanceTree->second)
|
||||
{
|
||||
instanceTree->second->UnloadMap(this);
|
||||
if (instanceTree->second->numLoadedTiles() == 0)
|
||||
{
|
||||
delete instanceTree->second;
|
||||
instanceTree->second = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VMapMgr2::unloadMap(unsigned int mapId, int x, int y)
|
||||
{
|
||||
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
|
||||
if (instanceTree != iInstanceMapTrees.end() && instanceTree->second)
|
||||
{
|
||||
instanceTree->second->UnloadMapTile(x, y, this);
|
||||
if (instanceTree->second->numLoadedTiles() == 0)
|
||||
{
|
||||
delete instanceTree->second;
|
||||
instanceTree->second = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool VMapMgr2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags)
|
||||
{
|
||||
#if defined(ENABLE_VMAP_CHECKS)
|
||||
if (!isLineOfSightCalcEnabled() || IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
|
||||
if (instanceTree != iInstanceMapTrees.end())
|
||||
{
|
||||
Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1);
|
||||
Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2);
|
||||
if (pos1 != pos2)
|
||||
{
|
||||
return instanceTree->second->isInLineOfSight(pos1, pos2, ignoreFlags);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
get the hit position and return true if we hit something
|
||||
otherwise the result pos will be the dest pos
|
||||
*/
|
||||
bool VMapMgr2::GetObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist)
|
||||
{
|
||||
#if defined(ENABLE_VMAP_CHECKS)
|
||||
if (isLineOfSightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LOS))
|
||||
#endif
|
||||
{
|
||||
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
|
||||
if (instanceTree != iInstanceMapTrees.end())
|
||||
{
|
||||
Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1);
|
||||
Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2);
|
||||
Vector3 resultPos;
|
||||
bool result = instanceTree->second->GetObjectHitPos(pos1, pos2, resultPos, modifyDist);
|
||||
resultPos = convertPositionToInternalRep(resultPos.x, resultPos.y, resultPos.z);
|
||||
rx = resultPos.x;
|
||||
ry = resultPos.y;
|
||||
rz = resultPos.z;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rx = x2;
|
||||
ry = y2;
|
||||
rz = z2;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
get height or INVALID_HEIGHT if no height available
|
||||
*/
|
||||
|
||||
float VMapMgr2::getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist)
|
||||
{
|
||||
#if defined(ENABLE_VMAP_CHECKS)
|
||||
if (isHeightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_HEIGHT))
|
||||
#endif
|
||||
{
|
||||
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
|
||||
if (instanceTree != iInstanceMapTrees.end())
|
||||
{
|
||||
Vector3 pos = convertPositionToInternalRep(x, y, z);
|
||||
float height = instanceTree->second->getHeight(pos, maxSearchDist);
|
||||
if (height >= G3D::finf())
|
||||
{
|
||||
return height = VMAP_INVALID_HEIGHT_VALUE; // No height
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
return VMAP_INVALID_HEIGHT_VALUE;
|
||||
}
|
||||
|
||||
bool VMapMgr2::GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional<uint8> reqLiquidType, AreaAndLiquidData& data) const
|
||||
{
|
||||
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
|
||||
if (instanceTree != iInstanceMapTrees.end())
|
||||
{
|
||||
LocationInfo info;
|
||||
Vector3 pos = convertPositionToInternalRep(x, y, z);
|
||||
if (instanceTree->second->GetLocationInfo(pos, info))
|
||||
{
|
||||
data.floorZ = info.ground_Z;
|
||||
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS))
|
||||
{
|
||||
uint32 liquidType = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc
|
||||
float liquidLevel;
|
||||
if (!reqLiquidType || (GetLiquidFlagsPtr(liquidType) & *reqLiquidType))
|
||||
if (info.hitInstance->GetLiquidLevel(pos, info, liquidLevel))
|
||||
data.liquidInfo.emplace(liquidType, liquidLevel);
|
||||
}
|
||||
|
||||
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG))
|
||||
data.areaInfo.emplace(info.hitModel->GetWmoID(), info.hitInstance->adtId, info.rootId, info.hitModel->GetMogpFlags(), info.hitInstance->ID);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldModel* VMapMgr2::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);
|
||||
|
||||
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
|
||||
if (model == iLoadedModelFiles.end())
|
||||
{
|
||||
WorldModel* worldmodel = new WorldModel();
|
||||
if (!worldmodel->readFile(basepath + filename + ".vmo"))
|
||||
{
|
||||
LOG_ERROR("maps", "VMapMgr2: could not load '{}{}.vmo'", basepath, filename);
|
||||
delete worldmodel;
|
||||
return nullptr;
|
||||
}
|
||||
LOG_DEBUG("maps", "VMapMgr2: loading file '{}{}'", basepath, filename);
|
||||
|
||||
worldmodel->Flags = flags;
|
||||
|
||||
model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first;
|
||||
model->second.setModel(worldmodel);
|
||||
}
|
||||
|
||||
return model->second.getModel();
|
||||
}
|
||||
|
||||
void VMapMgr2::releaseModelInstance(const std::string& filename)
|
||||
{
|
||||
//! Critical section, thread safe access to iLoadedModelFiles
|
||||
std::lock_guard<std::mutex> lock(LoadedModelFilesLock);
|
||||
|
||||
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
|
||||
if (model == iLoadedModelFiles.end())
|
||||
{
|
||||
LOG_ERROR("maps", "VMapMgr2: trying to unload non-loaded file '{}'", filename);
|
||||
return;
|
||||
}
|
||||
if (model->second.decRefCount() == 0)
|
||||
{
|
||||
LOG_DEBUG("maps", "VMapMgr2: unloading file '{}'", filename);
|
||||
delete model->second.getModel();
|
||||
iLoadedModelFiles.erase(model);
|
||||
}
|
||||
}
|
||||
|
||||
LoadResult VMapMgr2::existsMap(const char* basePath, unsigned int mapId, int x, int y)
|
||||
{
|
||||
return StaticMapTree::CanLoadMap(std::string(basePath), mapId, x, y);
|
||||
}
|
||||
|
||||
void VMapMgr2::GetInstanceMapTree(InstanceTreeMap& instanceMapTree)
|
||||
{
|
||||
instanceMapTree = iInstanceMapTrees;
|
||||
}
|
||||
|
||||
} // namespace VMAP
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
#define _VMAPMANAGER2_H
|
||||
|
||||
#include "IVMapMgr.h"
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
//===========================================================
|
||||
|
||||
@@ -46,24 +43,6 @@ namespace G3D
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
class StaticMapTree;
|
||||
class WorldModel;
|
||||
|
||||
class ManagedModel
|
||||
{
|
||||
public:
|
||||
ManagedModel() { }
|
||||
void setModel(WorldModel* model) { iModel = model; }
|
||||
WorldModel* getModel() { return iModel; }
|
||||
int decRefCount() { return --iRefCount; }
|
||||
protected:
|
||||
WorldModel* iModel{nullptr};
|
||||
int iRefCount{0};
|
||||
};
|
||||
|
||||
typedef std::unordered_map<uint32, StaticMapTree*> InstanceTreeMap;
|
||||
typedef std::unordered_map<std::string, ManagedModel> ModelFileMap;
|
||||
|
||||
enum DisableTypes
|
||||
{
|
||||
VMAP_DISABLE_AREAFLAG = 0x1,
|
||||
@@ -75,58 +54,25 @@ namespace VMAP
|
||||
class VMapMgr2 : public IVMapMgr
|
||||
{
|
||||
protected:
|
||||
// Tree to check collision
|
||||
ModelFileMap iLoadedModelFiles;
|
||||
InstanceTreeMap iInstanceMapTrees;
|
||||
bool thread_safe_environment;
|
||||
|
||||
// Mutex for iLoadedModelFiles
|
||||
std::mutex LoadedModelFilesLock;
|
||||
|
||||
bool _loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY);
|
||||
/* void _unloadMap(uint32 pMapId, uint32 x, uint32 y); */
|
||||
|
||||
static uint32 GetLiquidFlagsDummy(uint32) { return 0; }
|
||||
static bool IsVMAPDisabledForDummy(uint32 /*entry*/, uint8 /*flags*/) { return false; }
|
||||
|
||||
InstanceTreeMap::const_iterator GetMapTree(uint32 mapId) const;
|
||||
|
||||
public:
|
||||
// public for debug
|
||||
[[nodiscard]] G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const;
|
||||
static G3D::Vector3 convertPositionToInternalRep(float x, float y, float z);
|
||||
static std::string getMapFileName(unsigned int mapId);
|
||||
|
||||
VMapMgr2();
|
||||
~VMapMgr2() override;
|
||||
|
||||
void InitializeThreadUnsafe(const std::vector<uint32>& mapIds);
|
||||
|
||||
int loadMap(const char* pBasePath, unsigned int mapId, int x, int y) override;
|
||||
|
||||
void unloadMap(unsigned int mapId, int x, int y) override;
|
||||
void unloadMap(unsigned int mapId) 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
|
||||
*/
|
||||
bool GetObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) override;
|
||||
float getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist) override;
|
||||
|
||||
bool processCommand(char* /*command*/) override { return false; } // for debug and extensions
|
||||
|
||||
bool GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, Optional<uint8> reqLiquidType, AreaAndLiquidData& data) const override;
|
||||
|
||||
WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename, uint32 flags);
|
||||
void releaseModelInstance(const std::string& filename);
|
||||
|
||||
// what's the use of this? o.O
|
||||
[[nodiscard]] std::string getDirFileName(unsigned int mapId, int /*x*/, int /*y*/) const override
|
||||
{
|
||||
return getMapFileName(mapId);
|
||||
}
|
||||
LoadResult existsMap(const char* basePath, unsigned int mapId, int x, int y) override;
|
||||
void GetInstanceMapTree(InstanceTreeMap& instanceMapTree);
|
||||
|
||||
typedef uint32(*GetLiquidFlagsFn)(uint32 liquidType);
|
||||
GetLiquidFlagsFn GetLiquidFlagsPtr;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "Log.h"
|
||||
#include "WorldModelStore.h"
|
||||
|
||||
std::shared_ptr<VMAP::WorldModel> WorldModelStore::AcquireModelInstance(std::string const& basepath, std::string const& filename, uint32 flags/* Only used when creating the model */)
|
||||
{
|
||||
//! Critical section, thread safe access
|
||||
std::lock_guard<std::mutex> lock(_lock);
|
||||
|
||||
ModelFileMap::iterator model = _loadedModels.find(filename);
|
||||
if (model == _loadedModels.end())
|
||||
{
|
||||
std::shared_ptr<VMAP::WorldModel> worldmodel = std::make_shared<VMAP::WorldModel>();
|
||||
LOG_DEBUG("maps", "WorldModelStore: loading file '{}{}'", basepath, filename);
|
||||
if (!worldmodel->readFile(basepath + filename + ".vmo"))
|
||||
{
|
||||
LOG_ERROR("maps", "WorldModelStore: could not load '{}{}.vmo'", basepath, filename);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
worldmodel->Flags = flags;
|
||||
|
||||
model = _loadedModels.insert(std::pair<std::string, std::shared_ptr<VMAP::WorldModel>>(filename, worldmodel)).first;
|
||||
}
|
||||
|
||||
return model->second;
|
||||
}
|
||||
+21
-21
@@ -15,32 +15,32 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _IMMAPMANAGER_H
|
||||
#define _IMMAPMANAGER_H
|
||||
#ifndef _WORLDMODELSTORE_H
|
||||
#define _WORLDMODELSTORE_H
|
||||
|
||||
// Interface for IMMapManger
|
||||
namespace MMAP
|
||||
#include "WorldModel.h"
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
class WorldModelStore
|
||||
{
|
||||
enum MMAP_LOAD_RESULT
|
||||
public:
|
||||
static WorldModelStore* instance()
|
||||
{
|
||||
MMAP_LOAD_RESULT_ERROR,
|
||||
MMAP_LOAD_RESULT_OK,
|
||||
MMAP_LOAD_RESULT_IGNORED,
|
||||
};
|
||||
static WorldModelStore instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
class IMMapMgr
|
||||
{
|
||||
private:
|
||||
bool iEnablePathFinding;
|
||||
std::shared_ptr<VMAP::WorldModel> AcquireModelInstance(std::string const& basepath, std::string const& filename, uint32 flags);
|
||||
|
||||
public:
|
||||
IMMapMgr() : iEnablePathFinding(true) {}
|
||||
virtual ~IMMapMgr(void) {}
|
||||
private:
|
||||
typedef std::unordered_map<std::string, std::shared_ptr<VMAP::WorldModel>> ModelFileMap;
|
||||
ModelFileMap _loadedModels;
|
||||
|
||||
//Enabled/Disabled Pathfinding
|
||||
void setEnablePathFinding(bool value) { iEnablePathFinding = value; }
|
||||
bool isEnablePathFinding() const { return (iEnablePathFinding); }
|
||||
};
|
||||
}
|
||||
std::mutex _lock;
|
||||
};
|
||||
|
||||
#define sWorldModelStore WorldModelStore::instance()
|
||||
|
||||
#endif
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ModelInstance.h"
|
||||
#include "VMapDefinitions.h"
|
||||
#include "VMapMgr2.h"
|
||||
#include "WorldModelStore.h"
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
@@ -259,7 +260,7 @@ namespace VMAP
|
||||
|
||||
//=========================================================
|
||||
|
||||
bool StaticMapTree::InitMap(const std::string& fname, VMapMgr2* vm)
|
||||
bool StaticMapTree::InitMap(const std::string& fname)
|
||||
{
|
||||
//VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '{}'", fname);
|
||||
bool success = false;
|
||||
@@ -291,13 +292,12 @@ namespace VMAP
|
||||
#endif
|
||||
if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
|
||||
{
|
||||
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags);
|
||||
std::shared_ptr<WorldModel> model = sWorldModelStore->AcquireModelInstance(iBasePath, spawn.name, spawn.flags);
|
||||
//VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading {}", spawn.name);
|
||||
if (model)
|
||||
{
|
||||
// assume that global model always is the first and only tree value (could be improved...)
|
||||
iTreeValues[0] = ModelInstance(spawn, model);
|
||||
iLoadedSpawns[0] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -312,23 +312,14 @@ namespace VMAP
|
||||
|
||||
//=========================================================
|
||||
|
||||
void StaticMapTree::UnloadMap(VMapMgr2* vm)
|
||||
void StaticMapTree::UnloadMap()
|
||||
{
|
||||
for (loadedSpawnMap::iterator i = iLoadedSpawns.begin(); i != iLoadedSpawns.end(); ++i)
|
||||
{
|
||||
iTreeValues[i->first].setUnloaded();
|
||||
for (uint32 refCount = 0; refCount < i->second; ++refCount)
|
||||
{
|
||||
vm->releaseModelInstance(iTreeValues[i->first].name);
|
||||
}
|
||||
}
|
||||
iLoadedSpawns.clear();
|
||||
iLoadedTiles.clear();
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm)
|
||||
bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY)
|
||||
{
|
||||
if (!iIsTiled)
|
||||
{
|
||||
@@ -367,10 +358,11 @@ namespace VMAP
|
||||
if (result)
|
||||
{
|
||||
// acquire model instance
|
||||
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name, spawn.flags);
|
||||
std::shared_ptr<WorldModel> model = sWorldModelStore->AcquireModelInstance(iBasePath, spawn.name, spawn.flags);
|
||||
if (!model)
|
||||
{
|
||||
LOG_ERROR("maps", "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [{}, {}]", tileX, tileY);
|
||||
// why do we continue to try to load if the model was unsuccessful here?
|
||||
}
|
||||
|
||||
// update tree
|
||||
@@ -378,22 +370,22 @@ namespace VMAP
|
||||
|
||||
if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1)
|
||||
{
|
||||
if (!iLoadedSpawns.count(referencedVal))
|
||||
if (referencedVal >= iNTreeValues)
|
||||
{
|
||||
#if defined(VMAP_DEBUG)
|
||||
if (referencedVal > iNTreeValues)
|
||||
{
|
||||
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{})", referencedVal, iNTreeValues);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
iTreeValues[referencedVal] = ModelInstance(spawn, model);
|
||||
iLoadedSpawns[referencedVal] = 1;
|
||||
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : invalid tree element ({}/{})", referencedVal, iNTreeValues);
|
||||
continue;
|
||||
}
|
||||
|
||||
// This looks odd and is confusing, took some research to figure it out:
|
||||
// the first WorldModel will create a "groupmodel" of all other same-models in the tile
|
||||
// we don't actually care about anything else
|
||||
if (!iTreeValues[referencedVal].getWorldModel())
|
||||
{
|
||||
iTreeValues[referencedVal] = ModelInstance(spawn, model);
|
||||
}
|
||||
#if defined(VMAP_DEBUG)
|
||||
else
|
||||
{
|
||||
++iLoadedSpawns[referencedVal];
|
||||
#if defined(VMAP_DEBUG)
|
||||
if (iTreeValues[referencedVal].ID != spawn.ID)
|
||||
{
|
||||
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
|
||||
@@ -402,8 +394,8 @@ namespace VMAP
|
||||
{
|
||||
LOG_DEBUG("maps", "StaticMapTree::LoadMapTile() : name collision on GUID={}", spawn.ID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -427,7 +419,7 @@ namespace VMAP
|
||||
|
||||
//=========================================================
|
||||
|
||||
void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm)
|
||||
void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY)
|
||||
{
|
||||
uint32 tileID = packTileID(tileX, tileY);
|
||||
loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
|
||||
@@ -436,57 +428,6 @@ namespace VMAP
|
||||
LOG_ERROR("maps", "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:{} X:{} Y:{}", iMapID, tileX, tileY);
|
||||
return;
|
||||
}
|
||||
if (tile->second) // file associated with tile
|
||||
{
|
||||
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
|
||||
FILE* tf = fopen(tilefile.c_str(), "rb");
|
||||
if (tf)
|
||||
{
|
||||
bool result = true;
|
||||
char chunk[8];
|
||||
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
uint32 numSpawns;
|
||||
if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
for (uint32 i = 0; i < numSpawns && result; ++i)
|
||||
{
|
||||
// read model spawns
|
||||
ModelSpawn spawn;
|
||||
result = ModelSpawn::readFromFile(tf, spawn);
|
||||
if (result)
|
||||
{
|
||||
// release model instance
|
||||
vm->releaseModelInstance(spawn.name);
|
||||
|
||||
// update tree
|
||||
uint32 referencedNode;
|
||||
|
||||
if (fread(&referencedNode, sizeof(uint32), 1, tf) != 1)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!iLoadedSpawns.count(referencedNode))
|
||||
{
|
||||
LOG_ERROR("maps", "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '{}' (ID:{})", spawn.name, spawn.ID);
|
||||
}
|
||||
else if (--iLoadedSpawns[referencedNode] == 0)
|
||||
{
|
||||
iTreeValues[referencedNode].setUnloaded();
|
||||
iLoadedSpawns.erase(referencedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(tf);
|
||||
}
|
||||
}
|
||||
iLoadedTiles.erase(tile);
|
||||
|
||||
METRIC_EVENT("map_events", "UnloadMapTile",
|
||||
|
||||
@@ -60,8 +60,6 @@ namespace VMAP
|
||||
// some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
|
||||
// empty tiles have no tile file, hence map with bool instead of just a set (consistency check)
|
||||
loadedTileMap iLoadedTiles;
|
||||
// stores <tree_index, reference_count> to invalidate tree values, unload map, and to be able to report errors
|
||||
loadedSpawnMap iLoadedSpawns;
|
||||
std::string iBasePath;
|
||||
|
||||
private:
|
||||
@@ -81,10 +79,10 @@ namespace VMAP
|
||||
[[nodiscard]] float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const;
|
||||
bool GetLocationInfo(const G3D::Vector3& pos, LocationInfo& info) const;
|
||||
|
||||
bool InitMap(const std::string& fname, VMapMgr2* vm);
|
||||
void UnloadMap(VMapMgr2* vm);
|
||||
bool LoadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm);
|
||||
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapMgr2* vm);
|
||||
bool InitMap(const std::string& fname);
|
||||
void UnloadMap();
|
||||
bool LoadMapTile(uint32 tileX, uint32 tileY);
|
||||
void UnloadMapTile(uint32 tileX, uint32 tileY);
|
||||
[[nodiscard]] bool isTiled() const { return iIsTiled; }
|
||||
[[nodiscard]] uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
|
||||
void GetModelInstances(ModelInstance*& models, uint32& count);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "VMapFactory.h"
|
||||
#include "VMapMgr2.h"
|
||||
#include "WorldModel.h"
|
||||
#include "WorldModelStore.h"
|
||||
|
||||
using G3D::Vector3;
|
||||
using G3D::Ray;
|
||||
@@ -101,14 +102,6 @@ void LoadGameObjectModelList(std::string const& dataPath)
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
GameObjectModel::~GameObjectModel()
|
||||
{
|
||||
if (iModel)
|
||||
{
|
||||
VMAP::VMapFactory::createOrGetVMapMgr()->releaseModelInstance(name);
|
||||
}
|
||||
}
|
||||
|
||||
bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath)
|
||||
{
|
||||
ModelList::const_iterator it = model_list.find(modelOwner->GetDisplayId());
|
||||
@@ -125,7 +118,7 @@ bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> model
|
||||
return false;
|
||||
}
|
||||
|
||||
iModel = VMAP::VMapFactory::createOrGetVMapMgr()->acquireModelInstance(dataPath + "vmaps/", it->second.name,
|
||||
iModel = sWorldModelStore->AcquireModelInstance(dataPath + "vmaps/", it->second.name,
|
||||
it->second.isWmo ? VMAP::ModelFlags::MOD_WORLDSPAWN : VMAP::ModelFlags::MOD_M2);
|
||||
|
||||
if (!iModel)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <G3D/Matrix3.h>
|
||||
#include <G3D/Ray.h>
|
||||
#include <G3D/Vector3.h>
|
||||
#include <memory>
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
@@ -58,7 +59,7 @@ public:
|
||||
|
||||
[[nodiscard]] const G3D::AABox& GetBounds() const { return iBound; }
|
||||
|
||||
~GameObjectModel();
|
||||
~GameObjectModel() = default;
|
||||
|
||||
[[nodiscard]] const G3D::Vector3& GetPosition() const { return iPos; }
|
||||
|
||||
@@ -86,7 +87,7 @@ private:
|
||||
G3D::Vector3 iPos;
|
||||
float iInvScale{0};
|
||||
float iScale{0};
|
||||
VMAP::WorldModel* iModel{nullptr};
|
||||
std::shared_ptr<VMAP::WorldModel> iModel;
|
||||
std::unique_ptr<GameObjectModelOwnerBase> owner;
|
||||
bool isWmo{false};
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ using G3D::Ray;
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
ModelInstance::ModelInstance(const ModelSpawn& spawn, WorldModel* model): ModelSpawn(spawn), iModel(model)
|
||||
ModelInstance::ModelInstance(const ModelSpawn& spawn, std::shared_ptr<WorldModel> model): ModelSpawn(spawn), iModel(model)
|
||||
{
|
||||
iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi() * iRot.y / 180.f, G3D::pi() * iRot.x / 180.f, G3D::pi() * iRot.z / 180.f).inverse();
|
||||
iInvScale = 1.f / iScale;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <G3D/Matrix3.h>
|
||||
#include <G3D/Ray.h>
|
||||
#include <G3D/Vector3.h>
|
||||
#include <memory>
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
@@ -63,16 +64,15 @@ namespace VMAP
|
||||
{
|
||||
public:
|
||||
ModelInstance() { }
|
||||
ModelInstance(const ModelSpawn& spawn, WorldModel* model);
|
||||
void setUnloaded() { iModel = nullptr; }
|
||||
ModelInstance(const ModelSpawn& spawn, std::shared_ptr<WorldModel> model);
|
||||
bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit, ModelIgnoreFlags ignoreFlags) const;
|
||||
bool GetLocationInfo(const G3D::Vector3& p, LocationInfo& info) const;
|
||||
bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo& info, float& liqHeight) const;
|
||||
WorldModel* getWorldModel() { return iModel; }
|
||||
WorldModel* getWorldModel() { return iModel.get(); }
|
||||
protected:
|
||||
G3D::Matrix3 iInvRot;
|
||||
float iInvScale{0.0f};
|
||||
WorldModel* iModel{nullptr};
|
||||
std::shared_ptr<WorldModel> iModel;
|
||||
};
|
||||
} // namespace VMAP
|
||||
|
||||
|
||||
@@ -3629,6 +3629,15 @@ Wintergrasp.NoBattleTimer = 150
|
||||
|
||||
Wintergrasp.CrashRestartTimer = 10
|
||||
|
||||
#
|
||||
# Wintergrasp.SkipBattleSessionCount
|
||||
# Description: When the peace timer expires, if the number of active sessions
|
||||
# exceeds this value, a new war will not start.
|
||||
# Set to 0 to disable this check.
|
||||
# Default: 3500
|
||||
|
||||
Wintergrasp.SkipBattleSessionCount = 3500
|
||||
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
|
||||
@@ -119,6 +119,13 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST_LOCALIZED, "SELECT id, locale, text FROM autobroadcast_locale WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_INS_AUTOBROADCAST, "INSERT INTO autobroadcast (realmid, weight, text) VALUES (?, ?, ?)", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_DEL_AUTOBROADCAST, "DELETE FROM autobroadcast WHERE id = ? AND (realmid = ? OR realmid = -1)", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_INS_AUTOBROADCAST_LOCALE, "REPLACE INTO autobroadcast_locale (realmid, id, locale, text) VALUES (?, ?, ?, ?)", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_DEL_AUTOBROADCAST_LOCALE, "DELETE FROM autobroadcast_locale WHERE id = ? AND (realmid = ? OR realmid = -1)", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST_BY_ID, "SELECT 1 FROM autobroadcast WHERE id = ? AND (realmid = ? OR realmid = -1)", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST_LOCALE_BY_ID, "SELECT locale, text FROM autobroadcast_locale WHERE (realmid = ? OR realmid = -1) AND id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST_MAX_ID, "SELECT MAX(id) FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_MOTD, "SELECT text FROM motd WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_MOTD_LOCALE, "SELECT locale, text FROM motd_localized WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_REP_MOTD, "REPLACE INTO motd (realmid, text) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||
|
||||
@@ -101,6 +101,13 @@ enum LoginDatabaseStatements : uint32
|
||||
LOGIN_DEL_ACCOUNT,
|
||||
LOGIN_SEL_AUTOBROADCAST,
|
||||
LOGIN_SEL_AUTOBROADCAST_LOCALIZED,
|
||||
LOGIN_INS_AUTOBROADCAST,
|
||||
LOGIN_DEL_AUTOBROADCAST,
|
||||
LOGIN_INS_AUTOBROADCAST_LOCALE,
|
||||
LOGIN_DEL_AUTOBROADCAST_LOCALE,
|
||||
LOGIN_SEL_AUTOBROADCAST_BY_ID,
|
||||
LOGIN_SEL_AUTOBROADCAST_LOCALE_BY_ID,
|
||||
LOGIN_SEL_AUTOBROADCAST_MAX_ID,
|
||||
LOGIN_SEL_MOTD,
|
||||
LOGIN_SEL_MOTD_LOCALE,
|
||||
LOGIN_REP_MOTD,
|
||||
|
||||
@@ -52,7 +52,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID, "UPDATE waypoint_data SET wpguid = ? WHERE id = ? and point = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_ID, "SELECT MAX(id) FROM waypoint_data", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_POINT, "SELECT MAX(point) FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data WHERE id = ? ORDER BY point", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, velocity, delay, smoothTransition, move_type, action, action_chance FROM waypoint_data WHERE id = ? ORDER BY point", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID, "SELECT point, position_x, position_y, position_z FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, "SELECT position_x, position_y, position_z FROM waypoint_data WHERE point = 1 AND id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID, "SELECT position_x, position_y, position_z, orientation FROM waypoint_data WHERE id = ? ORDER BY point DESC LIMIT 1", CONNECTION_SYNCH);
|
||||
@@ -77,7 +77,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, CreatureImmunitiesId, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id1 = ? OR id2 = ? OR id3 = ?", CONNECTION_SYNCH);
|
||||
|
||||
@@ -20,7 +20,12 @@
|
||||
|
||||
PassiveAI::PassiveAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
|
||||
PossessedAI::PossessedAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
|
||||
NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
|
||||
NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c)
|
||||
{
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
// TODO: Remove once WorldObject casting is ported (triggers won't create combat refs from spell casts)
|
||||
me->SetIsCombatDisallowed(true);
|
||||
}
|
||||
|
||||
int32 NullCreatureAI::Permissible(Creature const* creature)
|
||||
{
|
||||
|
||||
@@ -198,8 +198,12 @@ void CreatureAI::OnOwnerCombatInteraction(Unit* target)
|
||||
if (!target || !me->IsAlive())
|
||||
return;
|
||||
|
||||
// Prevent guardian from disengaging from current target
|
||||
if (me->GetVictim() && me->GetVictim()->IsAlive())
|
||||
return;
|
||||
|
||||
if (!me->HasReactState(REACT_PASSIVE) && me->CanStartAttack(target, true))
|
||||
me->EngageWithTarget(target);
|
||||
AttackStart(target);
|
||||
}
|
||||
|
||||
// Distract creature, if player gets too close while stealthed/prowling
|
||||
@@ -211,6 +215,9 @@ void CreatureAI::TriggerAlert(Unit const* who) const
|
||||
// If this unit isn't an NPC, is already distracted, is in combat, is confused, stunned or fleeing, do nothing
|
||||
if (!me->IsCreature() || me->IsEngaged() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED))
|
||||
return;
|
||||
// If the creature is immune to players, it should not be alerted by them
|
||||
if (me->IsImmuneToPC())
|
||||
return;
|
||||
// Only alert for hostiles!
|
||||
if (me->IsCivilian() || me->HasReactState(REACT_PASSIVE) || !me->IsHostileTo(who) || !me->_IsTargetAcceptable(who))
|
||||
return;
|
||||
@@ -236,14 +243,19 @@ void CreatureAI::EnterEvadeMode(EvadeReason why)
|
||||
{
|
||||
if (Unit* owner = me->GetCharmerOrOwner())
|
||||
{
|
||||
me->GetMotionMaster()->Clear(false);
|
||||
me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE);
|
||||
// Owned creatures (pets/guardians) follow their owner — clear evade state
|
||||
// so they can re-enter combat immediately via CanBeginCombat
|
||||
me->ClearUnitState(UNIT_STATE_EVADE);
|
||||
if (!me->IsVehicle()) // vehicles should not follow their owner (passenger)
|
||||
{
|
||||
me->GetMotionMaster()->Clear(false);
|
||||
me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Required to prevent attacking creatures that are evading and cause them to reenter combat
|
||||
// Does not apply to MoveFollow
|
||||
me->AddUnitState(UNIT_STATE_EVADE);
|
||||
// Does not apply to MoveFollow — UNIT_STATE_EVADE is already set from _EnterEvadeMode
|
||||
me->GetMotionMaster()->MoveTargetedHome();
|
||||
}
|
||||
}
|
||||
@@ -378,8 +390,11 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set evade state early to prevent recursion when CombatStop triggers JustExitedCombat
|
||||
// This will be cleared in MoveTargetedHome for pets/guardians following their owner
|
||||
// Set evade state early to prevent recursion: CombatStop below purges combat
|
||||
// refs, which triggers JustExitedCombat -> EnterEvadeMode -> _EnterEvadeMode.
|
||||
// The IsInEvadeMode() check above will catch it.
|
||||
// EnterEvadeMode will clear this for owned creatures (pets/guardians) that
|
||||
// use MoveFollow instead of MoveTargetedHome.
|
||||
me->AddUnitState(UNIT_STATE_EVADE);
|
||||
|
||||
// don't remove vehicle auras, passengers aren't supposed to drop off the vehicle
|
||||
|
||||
@@ -186,6 +186,13 @@ public:
|
||||
// Called at MovePath End
|
||||
virtual void PathEndReached(uint32 /*pathId*/) {}
|
||||
|
||||
/// == Waypoints system =============================
|
||||
|
||||
virtual void WaypointPathStarted(uint32 /*pathId*/) { }
|
||||
virtual void WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { }
|
||||
virtual void WaypointReached(uint32 /*nodeId*/, uint32 /*pathId*/) { }
|
||||
virtual void WaypointPathEnded(uint32 /*nodeId*/, uint32 /*pathId*/) { }
|
||||
|
||||
void OnCharmed(bool apply) override;
|
||||
|
||||
// Called at reaching home after evade
|
||||
|
||||
@@ -472,7 +472,7 @@ void ScriptedAI::DoResetThreat(Unit* unit)
|
||||
|
||||
void ScriptedAI::DoResetThreatList()
|
||||
{
|
||||
if (!me->CanHaveThreatList() || me->GetThreatMgr().IsThreatListEmpty())
|
||||
if (!me->CanHaveThreatList() || me->GetThreatMgr().IsThreatListEmpty(true))
|
||||
{
|
||||
LOG_ERROR("entities.unit.ai", "DoResetThreatList called for creature that either cannot have threat list or has empty threat list (me entry = {})", me->GetEntry());
|
||||
return;
|
||||
|
||||
@@ -87,6 +87,7 @@ public:
|
||||
|
||||
void GenerateWaypointArray(Movement::PointsArray* points);
|
||||
|
||||
using CreatureAI::WaypointReached;
|
||||
virtual void WaypointReached(uint32 pointId) = 0;
|
||||
virtual void WaypointStart(uint32 /*pointId*/) {}
|
||||
|
||||
|
||||
@@ -109,27 +109,67 @@ void SmartAI::UpdateDespawn(const uint32 diff)
|
||||
mDespawnTime -= diff;
|
||||
}
|
||||
|
||||
WaypointData const* SmartAI::GetNextWayPoint()
|
||||
void SmartAI::UpdateFollow(const uint32 diff)
|
||||
{
|
||||
if (!mWayPoints || mWayPoints->empty())
|
||||
if (!mFollowGuid)
|
||||
return;
|
||||
|
||||
if (mFollowArrivedTimer < diff)
|
||||
{
|
||||
if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, mFollowArrivedAlive))
|
||||
StopFollow(true);
|
||||
else
|
||||
mFollowArrivedTimer = 1000;
|
||||
}
|
||||
else
|
||||
mFollowArrivedTimer -= diff;
|
||||
|
||||
if (mFollowGuid.IsPlayer())
|
||||
{
|
||||
if (_followCheckTimer < diff)
|
||||
{
|
||||
bool shouldDespawn = false;
|
||||
if (Player* player = ObjectAccessor::FindPlayer(mFollowGuid))
|
||||
{
|
||||
float checkDist = me->GetInstanceScript() ? SMART_ESCORT_MAX_PLAYER_DIST * 2 : SMART_ESCORT_MAX_PLAYER_DIST;
|
||||
if (!me->IsWithinDistInMap(player, checkDist))
|
||||
shouldDespawn = true;
|
||||
}
|
||||
else
|
||||
shouldDespawn = true;
|
||||
|
||||
if (shouldDespawn)
|
||||
me->DespawnOrUnsummon();
|
||||
|
||||
_followCheckTimer = 1000;
|
||||
}
|
||||
else
|
||||
_followCheckTimer -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
WaypointNode const* SmartAI::GetNextWayPoint()
|
||||
{
|
||||
if (!mWayPoints || mWayPoints->Nodes.empty())
|
||||
return nullptr;
|
||||
|
||||
mCurrentWPID++;
|
||||
auto itr = mWayPoints->find(mCurrentWPID);
|
||||
if (itr != mWayPoints->end())
|
||||
{
|
||||
mLastWP = &(*itr).second;
|
||||
if (mLastWP->id != mCurrentWPID)
|
||||
LOG_ERROR("scripts.ai.sai", "SmartAI::GetNextWayPoint: Got not expected waypoint id {}, expected {}", mLastWP->id, mCurrentWPID);
|
||||
// mCurrentWPID is 1-based for SmartAI escort paths
|
||||
if (mCurrentWPID > mWayPoints->Nodes.size())
|
||||
return nullptr;
|
||||
|
||||
return &(*itr).second;
|
||||
}
|
||||
return nullptr;
|
||||
// Nodes are 0-indexed, mCurrentWPID is 1-based
|
||||
WaypointNode const& node = mWayPoints->Nodes[mCurrentWPID - 1];
|
||||
mLastWP = &node;
|
||||
if (mLastWP->Id != mCurrentWPID)
|
||||
LOG_ERROR("scripts.ai.sai", "SmartAI::GetNextWayPoint: Got not expected waypoint id {}, expected {}", mLastWP->Id, mCurrentWPID);
|
||||
|
||||
return mLastWP;
|
||||
}
|
||||
|
||||
void SmartAI::GenerateWayPointArray(Movement::PointsArray* points)
|
||||
{
|
||||
if (!mWayPoints || mWayPoints->empty())
|
||||
if (!mWayPoints || mWayPoints->Nodes.empty())
|
||||
return;
|
||||
|
||||
// Flying unit, just fill array
|
||||
@@ -138,16 +178,12 @@ void SmartAI::GenerateWayPointArray(Movement::PointsArray* points)
|
||||
// xinef: first point in vector is unit real position
|
||||
points->clear();
|
||||
points->push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
|
||||
uint32 wpCounter = mCurrentWPID;
|
||||
auto itr = mWayPoints->find(wpCounter++);
|
||||
do
|
||||
// mCurrentWPID is 1-based
|
||||
for (uint32 i = mCurrentWPID > 0 ? mCurrentWPID - 1 : 0; i < mWayPoints->Nodes.size(); ++i)
|
||||
{
|
||||
WaypointData const& wp = (*itr).second;
|
||||
points->push_back(G3D::Vector3(wp.x, wp.y, wp.z));
|
||||
|
||||
itr = mWayPoints->find(wpCounter++);
|
||||
WaypointNode const& wp = mWayPoints->Nodes[i];
|
||||
points->push_back(G3D::Vector3(wp.X, wp.Y, wp.Z));
|
||||
}
|
||||
while (itr != mWayPoints->end());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -156,15 +192,15 @@ void SmartAI::GenerateWayPointArray(Movement::PointsArray* points)
|
||||
std::vector<G3D::Vector3> pVector;
|
||||
// xinef: first point in vector is unit real position
|
||||
pVector.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
|
||||
uint32 wpCounter = mCurrentWPID;
|
||||
|
||||
uint32 length = (mWayPoints->size() - mCurrentWPID) * size;
|
||||
uint32 startIdx = mCurrentWPID > 0 ? mCurrentWPID - 1 : 0;
|
||||
uint32 length = (uint32)((mWayPoints->Nodes.size() - startIdx) * size);
|
||||
|
||||
uint32 cnt = 0;
|
||||
for (auto itr = mWayPoints->find(wpCounter); itr != mWayPoints->end() && cnt++ <= length; ++itr)
|
||||
for (uint32 i = startIdx; i < mWayPoints->Nodes.size() && cnt++ <= length; ++i)
|
||||
{
|
||||
WaypointData const& wp = (*itr).second;
|
||||
pVector.push_back(G3D::Vector3(wp.x, wp.y, wp.z));
|
||||
WaypointNode const& wp = mWayPoints->Nodes[i];
|
||||
pVector.push_back(G3D::Vector3(wp.X, wp.Y, wp.Z));
|
||||
}
|
||||
|
||||
if (pVector.size() > 2) // more than source + dest
|
||||
@@ -204,10 +240,10 @@ void SmartAI::StartPath(ForcedMovement forcedMovement, uint32 path, bool repeat,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mWayPoints || mWayPoints->empty())
|
||||
if (!mWayPoints || mWayPoints->Nodes.empty())
|
||||
return;
|
||||
|
||||
if (WaypointData const* wp = GetNextWayPoint())
|
||||
if (WaypointNode const* wp = GetNextWayPoint())
|
||||
{
|
||||
AddEscortState(SMART_ESCORT_ESCORTING);
|
||||
mCanRepeatPath = repeat;
|
||||
@@ -223,7 +259,7 @@ void SmartAI::StartPath(ForcedMovement forcedMovement, uint32 path, bool repeat,
|
||||
GenerateWayPointArray(&pathPoints);
|
||||
|
||||
me->GetMotionMaster()->MoveSplinePath(&pathPoints, mForcedMovement);
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_START, nullptr, wp->id, GetScript()->GetPathId());
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_START, nullptr, wp->Id, GetScript()->GetPathId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,10 +318,11 @@ void SmartAI::PausePath(uint32 delay, bool forced)
|
||||
me->StopMoving();
|
||||
me->GetMotionMaster()->MoveIdle();//force stop
|
||||
|
||||
auto waypoint = mWayPoints->find(mCurrentWPID);
|
||||
if (waypoint->second.orientation.has_value())
|
||||
if (mCurrentWPID > 0 && mCurrentWPID <= mWayPoints->Nodes.size())
|
||||
{
|
||||
me->SetFacingTo(*waypoint->second.orientation);
|
||||
WaypointNode const& waypoint = mWayPoints->Nodes[mCurrentWPID - 1];
|
||||
if (waypoint.Orientation.has_value())
|
||||
me->SetFacingTo(*waypoint.Orientation);
|
||||
}
|
||||
}
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_ESCORT_PAUSED, nullptr, mCurrentWPID, GetScript()->GetPathId());
|
||||
@@ -536,23 +573,7 @@ void SmartAI::UpdateAI(uint32 diff)
|
||||
GetScript()->OnUpdate(diff);
|
||||
UpdatePath(diff);
|
||||
UpdateDespawn(diff);
|
||||
|
||||
//TODO move to void
|
||||
if (mFollowGuid)
|
||||
{
|
||||
if (mFollowArrivedTimer < diff)
|
||||
{
|
||||
if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, mFollowArrivedAlive))
|
||||
{
|
||||
StopFollow(true);
|
||||
return;
|
||||
}
|
||||
|
||||
mFollowArrivedTimer = 1000;
|
||||
}
|
||||
else
|
||||
mFollowArrivedTimer -= diff;
|
||||
}
|
||||
UpdateFollow(diff);
|
||||
|
||||
if (!IsAIControlled())
|
||||
{
|
||||
@@ -632,7 +653,7 @@ void SmartAI::MovepointReached(uint32 id)
|
||||
|
||||
if (mLastWP)
|
||||
{
|
||||
me->SetPosition(mLastWP->x, mLastWP->y, mLastWP->z, me->GetOrientation());
|
||||
me->SetPosition(mLastWP->X, mLastWP->Y, mLastWP->Z, me->GetOrientation());
|
||||
me->SetHomePosition(me->GetPosition());
|
||||
}
|
||||
|
||||
@@ -755,7 +776,7 @@ void SmartAI::MoveInLineOfSight(Unit* who)
|
||||
|
||||
bool SmartAI::CanAIAttack(Unit const* /*who*/) const
|
||||
{
|
||||
return !(me->GetReactState() == REACT_PASSIVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
|
||||
@@ -1255,6 +1276,32 @@ void SmartAI::PathEndReached(uint32 /*pathId*/)
|
||||
me->LoadPath(0);
|
||||
}
|
||||
|
||||
void SmartAI::WaypointPathStarted(uint32 /*pathId*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SmartAI::WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/)
|
||||
{
|
||||
}
|
||||
|
||||
void SmartAI::WaypointReached(uint32 nodeId, uint32 pathId)
|
||||
{
|
||||
if (!HasEscortState(SMART_ESCORT_ESCORTING))
|
||||
{
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, nodeId, pathId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SmartAI::WaypointPathEnded(uint32 nodeId, uint32 pathId)
|
||||
{
|
||||
if (!HasEscortState(SMART_ESCORT_ESCORTING))
|
||||
{
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, nodeId, pathId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void SmartAI::DistancingEnded()
|
||||
{
|
||||
SetCurrentRangeMode(true, _pendingDistancing);
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false);
|
||||
void EndPath(bool fail = false);
|
||||
void ResumePath();
|
||||
WaypointData const* GetNextWayPoint();
|
||||
WaypointNode const* GetNextWayPoint();
|
||||
void GenerateWayPointArray(Movement::PointsArray* points);
|
||||
bool HasEscortState(uint32 uiEscortState) { return (mEscortState & uiEscortState); }
|
||||
void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; }
|
||||
@@ -206,6 +206,11 @@ public:
|
||||
|
||||
void PathEndReached(uint32 pathId) override;
|
||||
|
||||
void WaypointPathStarted(uint32 pathId) override;
|
||||
void WaypointStarted(uint32 nodeId, uint32 pathId) override;
|
||||
void WaypointReached(uint32 nodeId, uint32 pathId) override;
|
||||
void WaypointPathEnded(uint32 nodeId, uint32 pathId) override;
|
||||
|
||||
bool CanRespawn() override { return mcanSpawn; };
|
||||
void SetCanRespawn(bool canSpawn) { mcanSpawn = canSpawn; }
|
||||
|
||||
@@ -222,6 +227,7 @@ private:
|
||||
bool mIsCharmed;
|
||||
uint32 mFollowCreditType;
|
||||
uint32 mFollowArrivedTimer;
|
||||
uint32 _followCheckTimer;
|
||||
uint32 mFollowCredit;
|
||||
uint32 mFollowArrivedEntry;
|
||||
bool mFollowArrivedAlive;
|
||||
@@ -238,9 +244,9 @@ private:
|
||||
bool mWPReached;
|
||||
bool mOOCReached;
|
||||
uint32 mWPPauseTimer;
|
||||
WaypointData const* mLastWP;
|
||||
WaypointNode const* mLastWP;
|
||||
uint32 mEscortNPCFlags;
|
||||
uint32 GetWPCount() { return mWayPoints ? mWayPoints->size() : 0; }
|
||||
uint32 GetWPCount() { return mWayPoints ? mWayPoints->Nodes.size() : 0; }
|
||||
bool mCanRepeatPath;
|
||||
bool mEvadeDisabled;
|
||||
bool mCanAutoAttack;
|
||||
@@ -253,6 +259,7 @@ private:
|
||||
uint32 mDespawnTime;
|
||||
uint32 mDespawnState;
|
||||
void UpdateDespawn(const uint32 diff);
|
||||
void UpdateFollow(const uint32 diff);
|
||||
uint32 mEscortInvokerCheckTimer;
|
||||
bool mJustReset;
|
||||
|
||||
|
||||
@@ -2533,14 +2533,12 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
break;
|
||||
}
|
||||
|
||||
if (!path || path->empty())
|
||||
if (!path || path->Nodes.empty())
|
||||
continue;
|
||||
|
||||
auto itrWp = path->find(1);
|
||||
if (itrWp != path->end())
|
||||
{
|
||||
WaypointData const& wpData = itrWp->second;
|
||||
float distToThisPath = creature->GetExactDistSq(wpData.x, wpData.y, wpData.z);
|
||||
WaypointNode const& wpData = path->Nodes[0];
|
||||
float distToThisPath = creature->GetExactDistSq(wpData.X, wpData.Y, wpData.Z);
|
||||
|
||||
if (distToThisPath < distanceToClosest)
|
||||
{
|
||||
@@ -2957,7 +2955,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (!IsPlayer(target))
|
||||
continue;
|
||||
|
||||
target->ToPlayer()->SendCinematicStart(e.action.cinematic.entry);
|
||||
target->ToPlayer()->GetCinematicMgr().StartCinematic(e.action.cinematic.entry);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -48,11 +48,6 @@ void SmartWaypointMgr::LoadFromDB()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
for (auto itr : waypoint_map)
|
||||
{
|
||||
delete itr.second;
|
||||
}
|
||||
|
||||
waypoint_map.clear();
|
||||
|
||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP);
|
||||
@@ -78,14 +73,15 @@ void SmartWaypointMgr::LoadFromDB()
|
||||
float x = fields[2].Get<float>();
|
||||
float y = fields[3].Get<float>();
|
||||
float z = fields[4].Get<float>();
|
||||
Optional<float> o;
|
||||
std::optional<float> o;
|
||||
if (!fields[5].IsNull())
|
||||
o = fields[5].Get<float>();
|
||||
uint32 delay = fields[6].Get<uint32>();
|
||||
|
||||
if (last_entry != entry)
|
||||
{
|
||||
waypoint_map[entry] = new WaypointPath();
|
||||
waypoint_map[entry] = WaypointPath();
|
||||
waypoint_map[entry].Id = entry;
|
||||
last_id = 1;
|
||||
count++;
|
||||
}
|
||||
@@ -94,15 +90,15 @@ void SmartWaypointMgr::LoadFromDB()
|
||||
LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry {}, unexpected point id {}, expected {}.", entry, id, last_id);
|
||||
|
||||
last_id++;
|
||||
WaypointData data;
|
||||
data.id = id;
|
||||
data.x = x;
|
||||
data.y = y;
|
||||
data.z = z;
|
||||
data.orientation = o;
|
||||
data.delay = delay;
|
||||
data.move_type = WAYPOINT_MOVE_TYPE_MAX;
|
||||
(*waypoint_map[entry]).emplace(id, data);
|
||||
WaypointNode node;
|
||||
node.Id = id;
|
||||
node.X = x;
|
||||
node.Y = y;
|
||||
node.Z = z;
|
||||
node.Orientation = o;
|
||||
node.Delay = delay;
|
||||
node.MoveType = WAYPOINT_MOVE_TYPE_MAX;
|
||||
waypoint_map[entry].Nodes.push_back(std::move(node));
|
||||
|
||||
last_entry = entry;
|
||||
total++;
|
||||
@@ -112,14 +108,6 @@ void SmartWaypointMgr::LoadFromDB()
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
SmartWaypointMgr::~SmartWaypointMgr()
|
||||
{
|
||||
for (auto itr : waypoint_map)
|
||||
{
|
||||
delete itr.second;
|
||||
}
|
||||
}
|
||||
|
||||
SmartAIMgr* SmartAIMgr::instance()
|
||||
{
|
||||
static SmartAIMgr instance;
|
||||
|
||||
@@ -2064,21 +2064,22 @@ class SmartWaypointMgr
|
||||
{
|
||||
SmartWaypointMgr() {}
|
||||
public:
|
||||
~SmartWaypointMgr();
|
||||
~SmartWaypointMgr() = default;
|
||||
|
||||
static SmartWaypointMgr* instance();
|
||||
|
||||
void LoadFromDB();
|
||||
|
||||
WaypointPath* GetPath(uint32 id)
|
||||
WaypointPath const* GetPath(uint32 id) const
|
||||
{
|
||||
if (waypoint_map.find(id) != waypoint_map.end())
|
||||
return waypoint_map[id];
|
||||
else return 0;
|
||||
auto itr = waypoint_map.find(id);
|
||||
if (itr != waypoint_map.end())
|
||||
return &itr->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<uint32, WaypointPath*> waypoint_map;
|
||||
std::unordered_map<uint32, WaypointPath> waypoint_map;
|
||||
};
|
||||
|
||||
// all events for a single entry
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Transport.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
|
||||
@@ -57,9 +58,6 @@ Battlefield::Battlefield() :
|
||||
NoWarBattleTime(0),
|
||||
RestartAfterCrash(0),
|
||||
TimeForAcceptInvite(20),
|
||||
KickDontAcceptTimer(1000),
|
||||
KickAfkPlayersTimer(1000),
|
||||
LastResurrectTimer(RESURRECTION_INTERVAL),
|
||||
StartGroupingTimer(0),
|
||||
StartGrouping(false)
|
||||
{
|
||||
@@ -132,6 +130,7 @@ void Battlefield::HandlePlayerLeaveZone(Player* player, uint32 /*zone*/)
|
||||
cp->HandlePlayerLeave(player);
|
||||
|
||||
InvitedPlayers[player->GetTeamId()].erase(player->GetGUID());
|
||||
PlayersInQueue[player->GetTeamId()].erase(player->GetGUID());
|
||||
PlayersWillBeKick[player->GetTeamId()].erase(player->GetGUID());
|
||||
Players[player->GetTeamId()].erase(player->GetGUID());
|
||||
SendRemoveWorldStates(player);
|
||||
@@ -147,7 +146,11 @@ bool Battlefield::Update(uint32 diff)
|
||||
{
|
||||
if (Timer <= diff)
|
||||
{
|
||||
if (!IsEnabled() || (!IsWarTime() && sWorldSessionMgr->GetActiveSessionCount() > 3500)) // if WG is disabled or there is more than 3500 connections, switch automatically
|
||||
uint32 sessionLimit = sWorld->getIntConfig(CONFIG_WINTERGRASP_SKIP_BATTLE_SESSION_COUNT);
|
||||
bool tooManySessions = sessionLimit && !IsWarTime()
|
||||
&& sWorldSessionMgr->GetActiveSessionCount() > sessionLimit;
|
||||
|
||||
if (!IsEnabled() || tooManySessions)
|
||||
{
|
||||
Active = true;
|
||||
EndBattle(false);
|
||||
@@ -174,52 +177,16 @@ bool Battlefield::Update(uint32 diff)
|
||||
SendUpdateWorldStates();
|
||||
}
|
||||
|
||||
_scheduler.Update(diff);
|
||||
|
||||
bool objectiveChanged = false;
|
||||
if (IsWarTime())
|
||||
{
|
||||
if (KickAfkPlayersTimer <= diff)
|
||||
{
|
||||
KickAfkPlayersTimer = 20000;
|
||||
KickAfkPlayers();
|
||||
}
|
||||
else
|
||||
KickAfkPlayersTimer -= diff;
|
||||
|
||||
// Kick players who chose not to accept invitation to the battle
|
||||
if (KickDontAcceptTimer <= diff)
|
||||
{
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team)
|
||||
for (PlayerTimerMap::value_type const& pair : InvitedPlayers[team])
|
||||
if (pair.second <= now)
|
||||
KickPlayerFromBattlefield(pair.first);
|
||||
|
||||
InvitePlayersInZoneToWar();
|
||||
for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team)
|
||||
for (PlayerTimerMap::value_type const& pair : PlayersWillBeKick[team])
|
||||
if (pair.second <= now)
|
||||
KickPlayerFromBattlefield(pair.first);
|
||||
|
||||
KickDontAcceptTimer = 5000;
|
||||
}
|
||||
else
|
||||
KickDontAcceptTimer -= diff;
|
||||
|
||||
for (BfCapturePoint* cp : CapturePoints)
|
||||
if (cp->Update(diff))
|
||||
objectiveChanged = true;
|
||||
}
|
||||
|
||||
if (LastResurrectTimer <= diff)
|
||||
{
|
||||
for (BfGraveyard* gy : GraveyardList)
|
||||
if (gy)
|
||||
gy->Resurrect();
|
||||
LastResurrectTimer = RESURRECTION_INTERVAL;
|
||||
}
|
||||
else
|
||||
LastResurrectTimer -= diff;
|
||||
|
||||
return objectiveChanged;
|
||||
}
|
||||
|
||||
@@ -324,7 +291,8 @@ void Battlefield::KickAfkPlayers()
|
||||
void Battlefield::KickPlayerFromBattlefield(ObjectGuid guid)
|
||||
{
|
||||
if (Player* player = ObjectAccessor::FindPlayer(guid))
|
||||
if (player->GetZoneId() == GetZoneId() && !player->IsGameMaster())
|
||||
if (player->GetZoneId() == GetZoneId() && !player->IsGameMaster()
|
||||
&& !PlayersInWar[player->GetTeamId()].count(guid))
|
||||
player->TeleportTo(KickPosition);
|
||||
}
|
||||
|
||||
@@ -342,6 +310,30 @@ void Battlefield::StartBattle()
|
||||
Timer = BattleTime;
|
||||
Active = true;
|
||||
|
||||
// Schedule war-only periodic timers
|
||||
_scheduler.Schedule(1s, BATTLEFIELD_TIMER_GROUP_WAR, [this](TaskContext context)
|
||||
{
|
||||
KickAfkPlayers();
|
||||
context.Repeat(20s);
|
||||
});
|
||||
|
||||
_scheduler.Schedule(1s, BATTLEFIELD_TIMER_GROUP_WAR, [this](TaskContext context)
|
||||
{
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team)
|
||||
for (PlayerTimerMap::value_type const& pair : InvitedPlayers[team])
|
||||
if (pair.second <= now)
|
||||
KickPlayerFromBattlefield(pair.first);
|
||||
|
||||
InvitePlayersInZoneToWar();
|
||||
for (uint8 team = 0; team < PVP_TEAMS_COUNT; ++team)
|
||||
for (PlayerTimerMap::value_type const& pair : PlayersWillBeKick[team])
|
||||
if (pair.second <= now)
|
||||
KickPlayerFromBattlefield(pair.first);
|
||||
|
||||
context.Repeat(5s);
|
||||
});
|
||||
|
||||
InvitePlayersInZoneToWar();
|
||||
InvitePlayersInQueueToWar();
|
||||
|
||||
@@ -359,6 +351,8 @@ void Battlefield::EndBattle(bool endByTimer)
|
||||
|
||||
Active = false;
|
||||
|
||||
_scheduler.CancelGroup(BATTLEFIELD_TIMER_GROUP_WAR);
|
||||
|
||||
StartGrouping = false;
|
||||
|
||||
if (!endByTimer)
|
||||
@@ -424,7 +418,7 @@ void Battlefield::PlayerAcceptInviteToWar(Player* player)
|
||||
{
|
||||
player->GetSession()->SendBfEntered(BattleId);
|
||||
PlayersInWar[player->GetTeamId()].insert(player->GetGUID());
|
||||
InvitedPlayers[player->GetTeamId(true)].erase(player->GetGUID());
|
||||
InvitedPlayers[player->GetTeamId()].erase(player->GetGUID());
|
||||
|
||||
if (player->isAFK())
|
||||
player->ToggleAFK();
|
||||
@@ -626,7 +620,9 @@ void Battlefield::RemovePlayerFromResurrectQueue(ObjectGuid playerGuid)
|
||||
void Battlefield::SendAreaSpiritHealerQueryOpcode(Player* player, ObjectGuid const& guid)
|
||||
{
|
||||
WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12);
|
||||
uint32 time = LastResurrectTimer; // resurrect every 30 seconds
|
||||
Milliseconds remaining = _scheduler.GetNextGroupOccurrence(BATTLEFIELD_TIMER_GROUP_RESURRECT);
|
||||
uint32 time = static_cast<uint32>(std::clamp(remaining,
|
||||
Milliseconds::zero(), Milliseconds(RESURRECTION_INTERVAL)).count());
|
||||
|
||||
data << guid << time;
|
||||
ASSERT(player);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "GameObject.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "ZoneScript.h"
|
||||
|
||||
enum BattlefieldTypes
|
||||
@@ -55,6 +56,13 @@ enum BattlefieldSounds
|
||||
|
||||
constexpr auto BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL = 1000;
|
||||
|
||||
enum BattlefieldTimerGroups
|
||||
{
|
||||
BATTLEFIELD_TIMER_GROUP_RESURRECT = 1,
|
||||
BATTLEFIELD_TIMER_GROUP_WAR = 2,
|
||||
BATTLEFIELD_TIMER_GROUP_SAVE = 3,
|
||||
};
|
||||
|
||||
const uint32 BattlefieldFactions[PVP_TEAMS_COUNT] =
|
||||
{
|
||||
1732, // Alliance
|
||||
@@ -401,18 +409,16 @@ protected:
|
||||
uint32 NoWarBattleTime; // Time between two battles
|
||||
uint32 RestartAfterCrash; // Delay to restart Wintergrasp if the server crashed during a running battle
|
||||
uint32 TimeForAcceptInvite;
|
||||
uint32 KickDontAcceptTimer;
|
||||
WorldLocation KickPosition; // Position where players are teleported if they switch to afk during the battle or if they don't accept invitation
|
||||
|
||||
uint32 KickAfkPlayersTimer; // Timer for check Afk in war
|
||||
|
||||
// Graveyard variables
|
||||
GraveyardVect GraveyardList; // Vector which contains the different GY of the battle
|
||||
uint32 LastResurrectTimer; // Timer for resurrect player every 30 sec
|
||||
|
||||
uint32 StartGroupingTimer; // Timer for invite players in area 15 minutes before start battle
|
||||
bool StartGrouping; // bool for knowing if all players in area have been invited
|
||||
|
||||
TaskScheduler _scheduler;
|
||||
|
||||
GuidUnorderedSet Groups[PVP_TEAMS_COUNT]; // Contains different raid groups
|
||||
|
||||
std::vector<uint64> Data64;
|
||||
|
||||
@@ -77,8 +77,6 @@ bool BattlefieldWG::SetupBattlefield()
|
||||
|
||||
Data32.resize(BATTLEFIELD_WG_DATA_MAX);
|
||||
|
||||
SaveTimer = 60000;
|
||||
|
||||
// Init Graveyards
|
||||
SetGraveyardNumber(BATTLEFIELD_WG_GRAVEYARD_MAX);
|
||||
|
||||
@@ -189,37 +187,31 @@ bool BattlefieldWG::SetupBattlefield()
|
||||
}
|
||||
|
||||
UpdateCounterVehicle(true);
|
||||
|
||||
// Schedule always-running periodic timers
|
||||
_scheduler.Schedule(Milliseconds(RESURRECTION_INTERVAL),
|
||||
BATTLEFIELD_TIMER_GROUP_RESURRECT, [this](TaskContext context)
|
||||
{
|
||||
for (BfGraveyard* gy : GraveyardList)
|
||||
if (gy)
|
||||
gy->Resurrect();
|
||||
context.Repeat();
|
||||
});
|
||||
|
||||
_scheduler.Schedule(60s, BATTLEFIELD_TIMER_GROUP_SAVE, [this](TaskContext context)
|
||||
{
|
||||
sWorldState->setWorldState(WORLD_STATE_BATTLEFIELD_WG_ACTIVE, Active);
|
||||
sWorldState->setWorldState(WORLD_STATE_BATTLEFIELD_WG_DEFENDER, DefenderTeam);
|
||||
sWorldState->setWorldState(ClockWorldState[0], Timer);
|
||||
context.Repeat();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattlefieldWG::Update(uint32 diff)
|
||||
{
|
||||
bool result = Battlefield::Update(diff);
|
||||
if (SaveTimer <= diff)
|
||||
{
|
||||
sWorldState->setWorldState(WORLD_STATE_BATTLEFIELD_WG_ACTIVE, Active);
|
||||
sWorldState->setWorldState(WORLD_STATE_BATTLEFIELD_WG_DEFENDER, DefenderTeam);
|
||||
sWorldState->setWorldState(ClockWorldState[0], Timer);
|
||||
SaveTimer = 60 * IN_MILLISECONDS;
|
||||
}
|
||||
else
|
||||
SaveTimer -= diff;
|
||||
|
||||
// Update Tenacity
|
||||
if (IsWarTime())
|
||||
{
|
||||
if (TenacityUpdateTimer <= diff)
|
||||
{
|
||||
TenacityUpdateTimer = 10000;
|
||||
if (!UpdateTenacityList.empty())
|
||||
UpdateTenacity();
|
||||
UpdateTenacityList.clear();
|
||||
}
|
||||
else
|
||||
TenacityUpdateTimer -= diff;
|
||||
}
|
||||
|
||||
return result;
|
||||
return Battlefield::Update(diff);
|
||||
}
|
||||
|
||||
void BattlefieldWG::OnBattleStart()
|
||||
@@ -293,7 +285,14 @@ void BattlefieldWG::OnBattleStart()
|
||||
|
||||
// Xinef: reset tenacity counter
|
||||
TenacityStack = 0;
|
||||
TenacityUpdateTimer = 20000;
|
||||
|
||||
_scheduler.Schedule(20s, BATTLEFIELD_TIMER_GROUP_WAR, [this](TaskContext context)
|
||||
{
|
||||
if (!UpdateTenacityList.empty())
|
||||
UpdateTenacity();
|
||||
UpdateTenacityList.clear();
|
||||
context.Repeat(10s);
|
||||
});
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE))
|
||||
ChatHandler(nullptr).SendWorldText(BATTLEFIELD_WG_WORLD_START_MESSAGE);
|
||||
|
||||
@@ -449,8 +449,6 @@ protected:
|
||||
GuidUnorderedSet UpdateTenacityList;
|
||||
|
||||
int32 TenacityStack;
|
||||
uint32 TenacityUpdateTimer;
|
||||
uint32 SaveTimer;
|
||||
|
||||
ObjectGuid TitansRelic;
|
||||
};
|
||||
|
||||
@@ -132,6 +132,8 @@ bool ArenaTeam::AddMember(ObjectGuid playerGuid)
|
||||
|
||||
if (sWorld->getIntConfig(CONFIG_ARENA_START_PERSONAL_RATING) > 0)
|
||||
personalRating = sWorld->getIntConfig(CONFIG_ARENA_START_PERSONAL_RATING);
|
||||
else if (sArenaSeasonMgr->GetCurrentSeason() < 6)
|
||||
personalRating = 1500;
|
||||
else if (GetRating() >= 1000)
|
||||
personalRating = 1000;
|
||||
|
||||
|
||||
@@ -427,6 +427,9 @@ bool CombatManager::UpdateOwnerCombatState() const
|
||||
_owner->AtExitCombat();
|
||||
if (!_owner->IsCreature())
|
||||
_owner->AtDisengage();
|
||||
|
||||
if (Player* player = _owner->ToPlayer())
|
||||
sScriptMgr->OnPlayerLeaveCombat(player);
|
||||
}
|
||||
|
||||
if (Unit* master = _owner->GetCharmerOrOwner())
|
||||
|
||||
@@ -540,6 +540,11 @@ void ThreatManager::TauntUpdate()
|
||||
|
||||
// taunt aura update also re-evaluates all suppressed states (retail behavior)
|
||||
EvaluateSuppressed(true);
|
||||
|
||||
// immediately reselect victim so taunt takes effect without waiting
|
||||
// for the next THREAT_UPDATE_INTERVAL (1 s) timer tick
|
||||
UpdateVictim();
|
||||
_updateTimer = THREAT_UPDATE_INTERVAL;
|
||||
}
|
||||
|
||||
void ThreatManager::SetTauntStateForTesting(
|
||||
|
||||
@@ -182,6 +182,8 @@ public:
|
||||
// Test-only: directly set taunt state on a threat ref
|
||||
// Uses uint32 to avoid incomplete-type issue with ThreatReference
|
||||
void SetTauntStateForTesting(Unit* target, uint32 state);
|
||||
// Immediately reselect victim (bypasses 1-second Update timer)
|
||||
void UpdateVictimForTesting() { UpdateVictim(); }
|
||||
|
||||
///== REDIRECT SYSTEM ==
|
||||
void RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, uint32 pct);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "DisableMgr.h"
|
||||
#include "GameEventMgr.h"
|
||||
#include "MMapFactory.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "OutdoorPvP.h"
|
||||
#include "Player.h"
|
||||
@@ -448,5 +447,17 @@ bool DisableMgr::IsPathfindingEnabled(Map const* map)
|
||||
if (!map)
|
||||
return false;
|
||||
|
||||
return !MMAP::MMapFactory::forbiddenMaps[map->GetId()] && (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena());
|
||||
// Still not an ideal solution but removes the extra awful forbiddenMaps hack
|
||||
// int32 f[] = {616 /*EoE*/, 649 /*ToC25*/, 650 /*ToC5*/, -1};
|
||||
switch (map->GetId())
|
||||
{
|
||||
case 616: // EoE
|
||||
case 649: // ToC25
|
||||
case 650: // ToC5
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena());
|
||||
}
|
||||
|
||||
@@ -388,6 +388,33 @@ void Creature::SearchFormation()
|
||||
}
|
||||
}
|
||||
|
||||
bool Creature::IsFormationLeader() const
|
||||
{
|
||||
if (!m_formation)
|
||||
return false;
|
||||
|
||||
return m_formation->GetLeader() == this;
|
||||
}
|
||||
|
||||
void Creature::SignalFormationMovement()
|
||||
{
|
||||
if (!m_formation)
|
||||
return;
|
||||
|
||||
if (!m_formation->GetLeader() || m_formation->GetLeader() != this)
|
||||
return;
|
||||
|
||||
m_formation->LeaderStartedMoving();
|
||||
}
|
||||
|
||||
bool Creature::IsFormationLeaderMoveAllowed() const
|
||||
{
|
||||
if (!m_formation)
|
||||
return true;
|
||||
|
||||
return m_formation->CanLeaderStartMoving();
|
||||
}
|
||||
|
||||
void Creature::RemoveCorpse(bool setSpawnTime, bool skipVisibility)
|
||||
{
|
||||
if (getDeathState() != DeathState::Corpse)
|
||||
@@ -643,7 +670,7 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
|
||||
SetControlled(true, UNIT_STATE_ROOT);
|
||||
UpdateMovementFlags();
|
||||
|
||||
LoadSpellTemplateImmunity();
|
||||
LoadTemplateImmunities(cInfo->CreatureImmunitiesId);
|
||||
|
||||
if (updateAI)
|
||||
{
|
||||
@@ -2132,39 +2159,55 @@ void Creature::InitializeReactState()
|
||||
SetReactState(REACT_AGGRESSIVE);
|
||||
}
|
||||
|
||||
bool Creature::HasMechanicTemplateImmunity(uint32 mask) const
|
||||
bool Creature::HasMechanicTemplateImmunity(uint64 mask) const
|
||||
{
|
||||
return !GetOwnerGUID().IsPlayer() && (GetCreatureTemplate()->MechanicImmuneMask & mask);
|
||||
if (GetOwnerGUID().IsPlayer())
|
||||
return false;
|
||||
|
||||
if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(_creatureImmunitiesId))
|
||||
return (immunities->Mechanic.to_ullong() & mask) != 0;
|
||||
|
||||
// no custom immunity entry => no mechanic immunity
|
||||
return false;
|
||||
}
|
||||
|
||||
void Creature::LoadSpellTemplateImmunity()
|
||||
void Creature::LoadTemplateImmunities(int32 creatureImmunitiesId)
|
||||
{
|
||||
// uint32 max used for "spell id", the immunity system will not perform SpellInfo checks against invalid spells
|
||||
// used so we know which immunities were loaded from template
|
||||
static uint32 const placeholderSpellId = std::numeric_limits<uint32>::max();
|
||||
static uint32 constexpr placeholderSpellId = std::numeric_limits<uint32>::max();
|
||||
|
||||
// unapply template immunities (in case we're updating entry)
|
||||
for (uint8 i = SPELL_SCHOOL_NORMAL; i <= SPELL_SCHOOL_ARCANE; ++i)
|
||||
auto applyCreatureImmunities = [this](CreatureImmunities const* immunities, bool apply)
|
||||
{
|
||||
ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, i, false);
|
||||
}
|
||||
if (!immunities)
|
||||
return;
|
||||
for (std::size_t i = 0; i < immunities->School.size(); ++i)
|
||||
if (immunities->School[i])
|
||||
ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, apply);
|
||||
for (std::size_t i = 0; i < immunities->DispelType.size(); ++i)
|
||||
if (immunities->DispelType[i])
|
||||
ApplySpellImmune(placeholderSpellId, IMMUNITY_DISPEL, i, apply);
|
||||
for (std::size_t i = 0; i < immunities->Mechanic.size(); ++i)
|
||||
if (immunities->Mechanic[i])
|
||||
ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, i, apply);
|
||||
for (SpellEffects effect : immunities->Effect)
|
||||
ApplySpellImmune(placeholderSpellId, IMMUNITY_EFFECT, effect, apply);
|
||||
for (AuraType aura : immunities->Aura)
|
||||
ApplySpellImmune(placeholderSpellId, IMMUNITY_STATE, aura, apply);
|
||||
};
|
||||
|
||||
// don't inherit immunities for hunter pets
|
||||
if (GetOwnerGUID().IsPlayer() && IsHunterPet())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// unapply old template if any
|
||||
if (CreatureImmunities const* oldImmunities = sSpellMgr->GetCreatureImmunities(_creatureImmunitiesId))
|
||||
applyCreatureImmunities(oldImmunities, false);
|
||||
|
||||
if (uint8 mask = GetCreatureTemplate()->SpellSchoolImmuneMask)
|
||||
// apply requested immunities
|
||||
if (CreatureImmunities const* newImmunities = sSpellMgr->GetCreatureImmunities(creatureImmunitiesId))
|
||||
{
|
||||
for (uint8 i = SPELL_SCHOOL_NORMAL; i <= SPELL_SCHOOL_ARCANE; ++i)
|
||||
{
|
||||
if (mask & (1 << i))
|
||||
{
|
||||
ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, true);
|
||||
}
|
||||
}
|
||||
_creatureImmunitiesId = creatureImmunitiesId;
|
||||
applyCreatureImmunities(newImmunities, true);
|
||||
}
|
||||
else
|
||||
_creatureImmunitiesId = 0;
|
||||
}
|
||||
|
||||
bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
@@ -2179,11 +2222,13 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
|
||||
// Xinef: this should exclude self casts...
|
||||
// Spells that don't have effectMechanics.
|
||||
if (spellInfo->Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(1 << (spellInfo->Mechanic - 1)))
|
||||
if (spellInfo->Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(UI64LIT(1) << spellInfo->Mechanic))
|
||||
return true;
|
||||
|
||||
// This check must be done instead of 'if (GetCreatureTemplate()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))' for not break
|
||||
// the check of mechanic immunity on DB (tested) because GetCreatureTemplate()->MechanicImmuneMask and m_spellImmune[IMMUNITY_MECHANIC] don't have same data.
|
||||
// The above helper uses the creature_immunities table rather than a
|
||||
// simple mask on creature_template. We can't rely on the old mask field
|
||||
// (which has been removed) because it no longer exists and did not always
|
||||
// match the runtime immunity set stored in m_spellImmune.
|
||||
bool immunedToAllEffects = true;
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
if (spellInfo->Effects[i].IsEffect() && !IsImmunedToSpellEffect(spellInfo, i))
|
||||
@@ -2197,16 +2242,16 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
return Unit::IsImmunedToSpell(spellInfo, spell);
|
||||
}
|
||||
|
||||
bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
|
||||
bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const
|
||||
{
|
||||
// Xinef: this should exclude self casts...
|
||||
if (spellInfo->Effects[index].Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(1 << (spellInfo->Effects[index].Mechanic - 1)))
|
||||
if (spellInfo->Effects[index].Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(UI64LIT(1) << spellInfo->Effects[index].Mechanic))
|
||||
return true;
|
||||
|
||||
if (GetCreatureTemplate()->type == CREATURE_TYPE_MECHANICAL && spellInfo->Effects[index].Effect == SPELL_EFFECT_HEAL)
|
||||
return true;
|
||||
|
||||
return Unit::IsImmunedToSpellEffect(spellInfo, index);
|
||||
return Unit::IsImmunedToSpellEffect(spellInfo, index, caster);
|
||||
}
|
||||
|
||||
SpellInfo const* Creature::reachWithSpellAttack(Unit* victim)
|
||||
|
||||
@@ -105,12 +105,11 @@ public:
|
||||
bool isCanInteractWithBattleMaster(Player* player, bool msg) const;
|
||||
bool CanResetTalents(Player* player) const;
|
||||
bool CanCreatureAttack(Unit const* victim, bool skipDistCheck = false) const;
|
||||
void LoadSpellTemplateImmunity();
|
||||
bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr) override;
|
||||
|
||||
[[nodiscard]] bool HasMechanicTemplateImmunity(uint32 mask) const;
|
||||
[[nodiscard]] bool HasMechanicTemplateImmunity(uint64 mask) const;
|
||||
// redefine Unit::IsImmunedToSpell
|
||||
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const override;
|
||||
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const override;
|
||||
// redefine Unit::IsImmunedToSpellEffect
|
||||
[[nodiscard]] bool isElite() const
|
||||
{
|
||||
@@ -186,6 +185,8 @@ public:
|
||||
void UpdateAttackPowerAndDamage(bool ranged = false) override;
|
||||
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override;
|
||||
|
||||
void LoadTemplateImmunities(int32 creatureImmunitiesId);
|
||||
|
||||
void LoadSparringPct();
|
||||
[[nodiscard]] float GetSparringPct() const { return _sparringPct; }
|
||||
|
||||
@@ -357,6 +358,14 @@ public:
|
||||
[[nodiscard]] uint32 GetCurrentWaypointID() const { return m_waypointID; }
|
||||
void UpdateWaypointID(uint32 wpID) { m_waypointID = wpID; }
|
||||
|
||||
// nodeId, pathId
|
||||
std::pair<uint32, uint32> GetCurrentWaypointInfo() const { return _currentWaypointNodeInfo; }
|
||||
void UpdateCurrentWaypointInfo(uint32 nodeId, uint32 pathId) { _currentWaypointNodeInfo = { nodeId, pathId }; }
|
||||
|
||||
bool IsFormationLeader() const;
|
||||
void SignalFormationMovement();
|
||||
bool IsFormationLeaderMoveAllowed() const;
|
||||
|
||||
void SearchFormation();
|
||||
[[nodiscard]] CreatureGroup const* GetFormation() const { return m_formation; }
|
||||
[[nodiscard]] CreatureGroup* GetFormation() { return m_formation; }
|
||||
@@ -486,6 +495,7 @@ protected:
|
||||
|
||||
SpellSchoolMask m_meleeDamageSchoolMask;
|
||||
uint32 m_originalEntry;
|
||||
int32 _creatureImmunitiesId;
|
||||
uint32 _gossipMenuId;
|
||||
|
||||
bool m_moveInLineOfSightDisabled;
|
||||
@@ -517,6 +527,7 @@ private:
|
||||
// WaypointMovementGenerator variable
|
||||
uint32 m_waypointID;
|
||||
uint32 m_path_id;
|
||||
std::pair<uint32, uint32> _currentWaypointNodeInfo{0, 0};
|
||||
|
||||
// Formation variable
|
||||
CreatureGroup* m_formation;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "DatabaseEnv.h"
|
||||
#include "ItemTemplate.h"
|
||||
#include "LootMgr.h"
|
||||
#include "SpawnData.h"
|
||||
#include "Unit.h"
|
||||
#include <list>
|
||||
|
||||
@@ -202,7 +203,6 @@ struct CreatureTemplate
|
||||
float speed_swim;
|
||||
float speed_flight;
|
||||
float detection_range; // Detection Range for Line of Sight aggro
|
||||
float scale;
|
||||
uint32 rank;
|
||||
uint32 dmgschool;
|
||||
float DamageModifier;
|
||||
@@ -237,8 +237,7 @@ struct CreatureTemplate
|
||||
bool RacialLeader;
|
||||
uint32 movementId;
|
||||
bool RegenHealth;
|
||||
uint32 MechanicImmuneMask;
|
||||
uint8 SpellSchoolImmuneMask;
|
||||
int32 CreatureImmunitiesId;
|
||||
uint32 flags_extra;
|
||||
uint32 ScriptID;
|
||||
WorldPacket queryData; // pussywizard
|
||||
@@ -367,33 +366,23 @@ typedef std::unordered_map<uint8, EquipmentInfo> EquipmentInfoContainerInternal;
|
||||
typedef std::unordered_map<uint32, EquipmentInfoContainerInternal> EquipmentInfoContainer;
|
||||
|
||||
// from `creature` table
|
||||
struct CreatureData
|
||||
struct CreatureData : public SpawnData
|
||||
{
|
||||
CreatureData() = default;
|
||||
ObjectGuid::LowType spawnId{ 0 }; // mod_playerbots
|
||||
CreatureData() : SpawnData(SPAWN_TYPE_CREATURE) {}
|
||||
uint32 id1{0}; // entry in creature_template
|
||||
uint32 id2{0}; // entry in creature_template
|
||||
uint32 id3{0}; // entry in creature_template
|
||||
uint16 mapid{0};
|
||||
uint32 phaseMask{0};
|
||||
uint32 displayid{0};
|
||||
int8 equipmentId{0};
|
||||
float posX{0.0f};
|
||||
float posY{0.0f};
|
||||
float posZ{0.0f};
|
||||
float orientation{0.0f};
|
||||
uint32 spawntimesecs{0};
|
||||
float wander_distance{0.0f};
|
||||
uint32 currentwaypoint{0};
|
||||
uint32 curhealth{0};
|
||||
uint32 curmana{0};
|
||||
uint8 movementType{0};
|
||||
uint8 spawnMask{0};
|
||||
uint32 npcflag{0};
|
||||
uint32 unit_flags{0}; // enum UnitFlags mask values
|
||||
uint32 dynamicflags{0};
|
||||
uint32 ScriptId;
|
||||
bool dbData{true};
|
||||
};
|
||||
|
||||
struct CreatureModelInfo
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Trainer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Trainer::CanTeachSpell(Player const* player, Spell const* trainerSpell)
|
||||
bool Trainer::CanTeachSpell(Player const* player, Spell const* trainerSpell) const
|
||||
{
|
||||
SpellState state = GetSpellState(player, trainerSpell);
|
||||
if (state != SpellState::Available)
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Trainer
|
||||
[[nodiscard]] Spell const* GetSpell(uint32 spellId) const;
|
||||
[[nodiscard]] std::vector<Spell> const& GetSpells() const { return _spells; }
|
||||
void SendSpells(Creature* npc, Player* player, LocaleConstant locale) const;
|
||||
bool CanTeachSpell(Player const* player, Spell const* trainerSpell);
|
||||
bool CanTeachSpell(Player const* player, Spell const* trainerSpell) const;
|
||||
void TeachSpell(Creature* npc, Player* player, uint32 spellId);
|
||||
|
||||
[[nodiscard]] Type GetTrainerType() const { return _type; }
|
||||
|
||||
@@ -1703,7 +1703,7 @@ void GameObject::Use(Unit* user)
|
||||
Player* player = user->ToPlayer();
|
||||
|
||||
if (info->camera.cinematicId)
|
||||
player->SendCinematicStart(info->camera.cinematicId);
|
||||
player->GetCinematicMgr().StartCinematic(info->camera.cinematicId);
|
||||
|
||||
if (info->camera.eventID)
|
||||
{
|
||||
@@ -2142,7 +2142,32 @@ void GameObject::SendCustomAnim(uint32 anim)
|
||||
SendMessageToSet(&data, true);
|
||||
}
|
||||
|
||||
bool GameObject::IsInRange(float x, float y, float z, float radius) const
|
||||
bool GameObject::IsInRange2d(float x, float y, float radius) const
|
||||
{
|
||||
GameObjectDisplayInfoEntry const* info = sGameObjectDisplayInfoStore.LookupEntry(m_goInfo->displayId);
|
||||
if (!info)
|
||||
return IsWithinDist2d(x, y, radius);
|
||||
|
||||
float sinA = std::sin(GetOrientation());
|
||||
float cosA = cos(GetOrientation());
|
||||
float dx = x - GetPositionX();
|
||||
float dy = y - GetPositionY();
|
||||
float dist = std::sqrt(dx * dx + dy * dy);
|
||||
//! Check if the distance between the 2 objects is 0, can happen if both objects are on the same position.
|
||||
//! The code below this check wont crash if dist is 0 because 0/0 in float operations is valid, and returns infinite
|
||||
if (G3D::fuzzyEq(dist, 0.0f))
|
||||
return true;
|
||||
|
||||
float scale = GetObjectScale();
|
||||
float sinB = dx / dist;
|
||||
float cosB = dy / dist;
|
||||
dx = dist * (cosA * cosB + sinA * sinB);
|
||||
dy = dist * (cosA * sinB - sinA * cosB);
|
||||
return dx < (info->maxX * scale) + radius && dx >(info->minX * scale) - radius
|
||||
&& dy < (info->maxY * scale) + radius && dy >(info->minY * scale) - radius;
|
||||
}
|
||||
|
||||
bool GameObject::IsInRange3d(float x, float y, float z, float radius) const
|
||||
{
|
||||
GameObjectDisplayInfoEntry const* info = sGameObjectDisplayInfoStore.LookupEntry(m_goInfo->displayId);
|
||||
if (!info)
|
||||
@@ -2217,6 +2242,11 @@ void GameObject::UpdatePackedRotation()
|
||||
m_packedRotation = z | (y << 21) | (x << 42);
|
||||
}
|
||||
|
||||
bool GameObject::IsWithinSightRange(Position const& pos, float dist) const
|
||||
{
|
||||
return IsInRange2d(pos.GetPositionX(), pos.GetPositionY(), dist);
|
||||
}
|
||||
|
||||
void GameObject::SetWorldRotation(G3D::Quat const& rot)
|
||||
{
|
||||
G3D::Quat rotation = rot;
|
||||
|
||||
@@ -284,7 +284,8 @@ public:
|
||||
|
||||
void CastSpell(Unit* target, uint32 spell);
|
||||
void SendCustomAnim(uint32 anim);
|
||||
[[nodiscard]] bool IsInRange(float x, float y, float z, float radius) const;
|
||||
bool IsInRange2d(float x, float y, float radius) const;
|
||||
bool IsInRange3d(float x, float y, float z, float radius) const;
|
||||
|
||||
void ModifyHealth(int32 change, Unit* attackerOrHealer = nullptr, uint32 spellId = 0);
|
||||
void SetDestructibleBuildingModifyState(bool allow) { m_allowModifyDestructibleBuilding = allow; }
|
||||
@@ -418,8 +419,11 @@ private:
|
||||
{
|
||||
//! Following check does check 3d distance
|
||||
dist2compare += obj->GetObjectSize();
|
||||
return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare);
|
||||
return IsInRange3d(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare);
|
||||
}
|
||||
|
||||
bool IsWithinSightRange(Position const& pos, float dist) const override;
|
||||
|
||||
GameObjectAI* m_AI;
|
||||
|
||||
bool m_saveStateOnDb = false;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define GAMEOBJECTDATA_H
|
||||
|
||||
#include "SharedDefines.h"
|
||||
#include "SpawnData.h"
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -710,24 +711,15 @@ enum GOState
|
||||
};
|
||||
|
||||
// from `gameobject`
|
||||
struct GameObjectData
|
||||
struct GameObjectData : public SpawnData
|
||||
{
|
||||
explicit GameObjectData() = default;
|
||||
uint32 id{ 0 }; // entry in gamobject_template
|
||||
uint16 mapid{ 0 };
|
||||
uint32 phaseMask{ 0 };
|
||||
float posX{ 0.0f };
|
||||
float posY{ 0.0f };
|
||||
float posZ{ 0.0f };
|
||||
float orientation{ 0.0f };
|
||||
GameObjectData() : SpawnData(SPAWN_TYPE_GAMEOBJECT) {}
|
||||
uint32 id{0}; // entry in gameobject_template
|
||||
G3D::Quat rotation;
|
||||
int32 spawntimesecs{ 0 };
|
||||
uint32 ScriptId;
|
||||
uint32 animprogress{ 0 };
|
||||
GOState go_state{ GO_STATE_ACTIVE };
|
||||
uint8 spawnMask{ 0 };
|
||||
uint8 artKit{ 0 };
|
||||
bool dbData{ true };
|
||||
int32 spawntimesecs{0};
|
||||
uint32 animprogress{0};
|
||||
GOState go_state{GO_STATE_ACTIVE};
|
||||
uint8 artKit{0};
|
||||
};
|
||||
|
||||
#endif // GameObjectData_h__
|
||||
|
||||
@@ -1362,6 +1362,12 @@ bool WorldObject::IsWithinDist2d(const Position* pos, float dist) const
|
||||
return IsInDist2d(pos, dist + GetObjectSize());
|
||||
}
|
||||
|
||||
// Visibility always uses 2d checks, factors in self-object size already. Gameobjects will override this for custom calc
|
||||
bool WorldObject::IsWithinSightRange(Position const& pos, float dist) const
|
||||
{
|
||||
return IsInDist2d(&pos, dist + GetObjectSize());
|
||||
}
|
||||
|
||||
// use only if you will sure about placing both object at same map
|
||||
bool WorldObject::IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D, bool incOwnRadius, bool incTargetRadius) const
|
||||
{
|
||||
@@ -1674,7 +1680,7 @@ float WorldObject::GetGridActivationRange() const
|
||||
{
|
||||
if (ToPlayer())
|
||||
{
|
||||
if (ToPlayer()->GetCinematicMgr()->IsOnCinematic())
|
||||
if (ToPlayer()->GetCinematicMgr().IsOnCinematic())
|
||||
{
|
||||
return DEFAULT_VISIBILITY_INSTANCE;
|
||||
}
|
||||
@@ -1725,7 +1731,7 @@ float WorldObject::GetSightRange(WorldObject const* target) const
|
||||
return VISIBILITY_DIST_WINTERGRASP;
|
||||
else if (target->IsVisibilityOverridden())
|
||||
return target->GetVisibilityOverrideDistance();
|
||||
else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic())
|
||||
else if (ToPlayer()->GetCinematicMgr().IsOnCinematic())
|
||||
return DEFAULT_VISIBILITY_INSTANCE;
|
||||
else
|
||||
return GetMap()->GetVisibilityRange();
|
||||
@@ -1794,10 +1800,10 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
|
||||
if (distanceCheck)
|
||||
{
|
||||
bool corpseCheck = false;
|
||||
WorldObject const* viewpoint = this;
|
||||
Position const* sightPosition = this;
|
||||
if (Player const* thisPlayer = ToPlayer())
|
||||
{
|
||||
viewpoint = thisPlayer->GetSeer();
|
||||
sightPosition = &thisPlayer->GetSightPosition();
|
||||
|
||||
if (Creature const* creature = obj->ToCreature())
|
||||
{
|
||||
@@ -1843,8 +1849,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
|
||||
return false;
|
||||
}
|
||||
|
||||
// Xinef: check reversely obj vs viewpoint, object could be a gameObject which overrides _IsWithinDist function to include gameobject size
|
||||
if (!corpseCheck && !viewpoint->IsWithinDist(obj, GetSightRange(obj), false))
|
||||
if (!corpseCheck && !obj->IsWithinSightRange(*sightPosition, GetSightRange(obj)))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -543,6 +543,7 @@ public:
|
||||
bool IsWithinDist3d(const Position* pos, float dist) const;
|
||||
[[nodiscard]] bool IsWithinDist2d(float x, float y, float dist) const;
|
||||
bool IsWithinDist2d(const Position* pos, float dist) const;
|
||||
virtual bool IsWithinSightRange(Position const& pos, float dist) const;
|
||||
// use only if you will sure about placing both object at same map
|
||||
bool IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D = true, bool incOwnRadius = true, bool incTargetRadius = true) const;
|
||||
bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true, bool incOwnRadius = true, bool incTargetRadius = true) const;
|
||||
|
||||
@@ -491,7 +491,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
|
||||
}
|
||||
|
||||
// must be after SetMinion (owner guid check)
|
||||
//LoadTemplateImmunities();
|
||||
LoadTemplateImmunities(0);
|
||||
//LoadMechanicTemplateImmunity();
|
||||
m_loading = false;
|
||||
});
|
||||
|
||||
@@ -17,92 +17,84 @@
|
||||
|
||||
#include "CinematicMgr.h"
|
||||
#include "M2Stores.h"
|
||||
#include "MotionMaster.h"
|
||||
#include "Player.h"
|
||||
|
||||
CinematicMgr::CinematicMgr(Player* playerref)
|
||||
CinematicMgr::CinematicMgr(Player& player) : _player(player)
|
||||
{
|
||||
player = playerref;
|
||||
m_cinematicDiff = 0;
|
||||
m_lastCinematicCheck = 0;
|
||||
m_activeCinematicCameraId = 0;
|
||||
m_cinematicLength = 0;
|
||||
m_cinematicCamera = nullptr;
|
||||
m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f);
|
||||
m_CinematicObject = nullptr;
|
||||
_cinematicDiff = 0;
|
||||
_activeCinematicCameraId = 0;
|
||||
_cinematicCamera = nullptr;
|
||||
_remoteSightPosition = Position(0.0f, 0.0f, 0.0f);
|
||||
_cinematicUpdateTimer.SetInterval(CINEMATIC_UPDATEDIFF);
|
||||
}
|
||||
|
||||
CinematicMgr::~CinematicMgr()
|
||||
void CinematicMgr::StartCinematic(uint32 const cinematicSequenceId)
|
||||
{
|
||||
if (m_cinematicCamera && m_activeCinematicCameraId)
|
||||
EndCinematic();
|
||||
_player.SendCinematicStart(cinematicSequenceId);
|
||||
if (CinematicSequencesEntry const* sequence = sCinematicSequencesStore.LookupEntry(cinematicSequenceId))
|
||||
SetActiveCinematicCamera(sequence->cinematicCamera);
|
||||
}
|
||||
|
||||
void CinematicMgr::BeginCinematic()
|
||||
void CinematicMgr::StartCinematicCamera()
|
||||
{
|
||||
// Sanity check for active camera set
|
||||
if (m_activeCinematicCameraId == 0)
|
||||
if (_activeCinematicCameraId == 0)
|
||||
return;
|
||||
|
||||
if (std::vector<FlyByCamera> const* flyByCameras = GetFlyByCameras(m_activeCinematicCameraId))
|
||||
if (std::vector<FlyByCamera> const* flyByCameras = GetFlyByCameras(_activeCinematicCameraId))
|
||||
{
|
||||
// Initialize diff, and set camera
|
||||
m_cinematicDiff = 0;
|
||||
m_cinematicCamera = flyByCameras;
|
||||
_cinematicDiff = 0;
|
||||
_cinematicUpdateTimer.Reset();
|
||||
_cinematicCamera = flyByCameras;
|
||||
|
||||
auto camitr = m_cinematicCamera->begin();
|
||||
if (camitr != m_cinematicCamera->end())
|
||||
auto camitr = _cinematicCamera->begin();
|
||||
if (camitr != _cinematicCamera->end())
|
||||
{
|
||||
Position const& pos = camitr->locations;
|
||||
if (!pos.IsPositionValid())
|
||||
return;
|
||||
|
||||
player->GetMap()->LoadGrid(pos.GetPositionX(), pos.GetPositionY());
|
||||
m_CinematicObject = player->SummonCreature(VISUAL_WAYPOINT, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 120000);
|
||||
if (m_CinematicObject)
|
||||
{
|
||||
m_CinematicObject->setActive(true);
|
||||
player->SetViewpoint(m_CinematicObject, true);
|
||||
}
|
||||
|
||||
// Get cinematic length
|
||||
m_cinematicLength = flyByCameras->back().timeStamp;
|
||||
_player.GetMap()->LoadGridsInRange(pos, MAX_VISIBILITY_DISTANCE);
|
||||
_remoteSightPosition.Relocate(pos);
|
||||
_player.UpdateVisibilityForPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CinematicMgr::EndCinematic()
|
||||
{
|
||||
if (m_activeCinematicCameraId == 0)
|
||||
if (_activeCinematicCameraId == 0)
|
||||
return;
|
||||
|
||||
m_cinematicDiff = 0;
|
||||
m_cinematicCamera = nullptr;
|
||||
m_activeCinematicCameraId = 0;
|
||||
if (m_CinematicObject)
|
||||
{
|
||||
if (WorldObject* vpObject = player->GetViewpoint())
|
||||
if (vpObject == m_CinematicObject)
|
||||
player->SetViewpoint(m_CinematicObject, false);
|
||||
|
||||
m_CinematicObject->AddObjectToRemoveList();
|
||||
}
|
||||
_cinematicDiff = 0;
|
||||
_cinematicUpdateTimer.Reset();
|
||||
_cinematicCamera = nullptr;
|
||||
_activeCinematicCameraId = 0;
|
||||
_player.UpdateVisibilityForPlayer();
|
||||
}
|
||||
|
||||
void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/)
|
||||
void CinematicMgr::UpdateCinematic(uint32 const diff)
|
||||
{
|
||||
if (m_activeCinematicCameraId == 0 || !m_cinematicCamera || m_cinematicCamera->size() == 0)
|
||||
if (_activeCinematicCameraId == 0 || !_cinematicCamera || _cinematicCamera->empty())
|
||||
return;
|
||||
|
||||
_cinematicDiff += diff;
|
||||
_cinematicUpdateTimer.Update(diff);
|
||||
if (!_cinematicUpdateTimer.Passed())
|
||||
return;
|
||||
|
||||
_cinematicUpdateTimer.Reset();
|
||||
|
||||
Position lastPosition;
|
||||
uint32 lastTimestamp = 0;
|
||||
Position nextPosition;
|
||||
uint32 nextTimestamp = 0;
|
||||
|
||||
// Obtain direction of travel
|
||||
for (FlyByCamera cam : *m_cinematicCamera)
|
||||
for (FlyByCamera const& cam : *_cinematicCamera)
|
||||
{
|
||||
if (cam.timeStamp > m_cinematicDiff)
|
||||
if (cam.timeStamp > _cinematicDiff)
|
||||
{
|
||||
nextPosition.Relocate(cam.locations);
|
||||
nextTimestamp = cam.timeStamp;
|
||||
@@ -113,26 +105,25 @@ void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/)
|
||||
}
|
||||
float angle = lastPosition.GetAbsoluteAngle(&nextPosition);
|
||||
angle -= lastPosition.GetOrientation();
|
||||
if (angle < 0)
|
||||
angle += 2 * float(M_PI);
|
||||
angle = Position::NormalizeOrientation(angle);
|
||||
|
||||
// Look for position around 2 second ahead of us.
|
||||
int32 workDiff = m_cinematicDiff;
|
||||
int32 workDiff = _cinematicDiff;
|
||||
|
||||
// Modify result based on camera direction (Humans for example, have the camera point behind)
|
||||
workDiff += static_cast<int32>(float(CINEMATIC_LOOKAHEAD) * cos(angle));
|
||||
|
||||
// Get an iterator to the last entry in the cameras, to make sure we don't go beyond the end
|
||||
auto endItr = m_cinematicCamera->rbegin();
|
||||
if (endItr != m_cinematicCamera->rend() && workDiff > static_cast<int32>(endItr->timeStamp))
|
||||
auto endItr = _cinematicCamera->rbegin();
|
||||
if (endItr != _cinematicCamera->rend() && workDiff > static_cast<int32>(endItr->timeStamp))
|
||||
workDiff = endItr->timeStamp;
|
||||
|
||||
// Never try to go back in time before the start of cinematic!
|
||||
if (workDiff < 0)
|
||||
workDiff = m_cinematicDiff;
|
||||
workDiff = _cinematicDiff;
|
||||
|
||||
// Obtain the previous and next waypoint based on timestamp
|
||||
for (FlyByCamera cam : *m_cinematicCamera)
|
||||
for (FlyByCamera const& cam : *_cinematicCamera)
|
||||
{
|
||||
if (static_cast<int32>(cam.timeStamp) >= workDiff)
|
||||
{
|
||||
@@ -157,12 +148,15 @@ void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/)
|
||||
Position interPosition(lastPosition.m_positionX + (xDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionY +
|
||||
(yDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionZ + (zDiff * (float(interDiff) / float(timeDiff))));
|
||||
|
||||
// Advance (at speed) to this position. The remote sight object is used
|
||||
// to send update information to player in cinematic
|
||||
if (m_CinematicObject && interPosition.IsPositionValid())
|
||||
m_CinematicObject->MonsterMoveWithSpeed(interPosition.m_positionX, interPosition.m_positionY, interPosition.m_positionZ, 500.0f);
|
||||
// Advance _remoteSightPosition to new position
|
||||
if (interPosition.IsPositionValid())
|
||||
{
|
||||
_player.GetMap()->LoadGridsInRange(interPosition, MAX_VISIBILITY_DISTANCE);
|
||||
_remoteSightPosition.Relocate(interPosition);
|
||||
_player.UpdateVisibilityForPlayer();
|
||||
}
|
||||
|
||||
// If we never received an end packet 10 seconds after the final timestamp then force an end
|
||||
if (m_cinematicDiff > m_cinematicLength + 10 * IN_MILLISECONDS)
|
||||
if (_cinematicDiff > _cinematicCamera->back().timeStamp + 10 * IN_MILLISECONDS)
|
||||
EndCinematic();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "Define.h"
|
||||
#include "Position.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "Timer.h"
|
||||
#include <vector>
|
||||
|
||||
constexpr auto CINEMATIC_UPDATEDIFF = 500;
|
||||
@@ -31,31 +31,27 @@ struct FlyByCamera;
|
||||
|
||||
class AC_GAME_API CinematicMgr
|
||||
{
|
||||
friend class Player;
|
||||
public:
|
||||
explicit CinematicMgr(Player* playerref);
|
||||
~CinematicMgr();
|
||||
explicit CinematicMgr(Player& player);
|
||||
~CinematicMgr() = default;
|
||||
|
||||
// Cinematic camera data and remote sight functions
|
||||
uint32 GetActiveCinematicCamera() const { return m_activeCinematicCameraId; }
|
||||
void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { m_activeCinematicCameraId = cinematicCameraId; }
|
||||
bool IsOnCinematic() const { return (m_cinematicCamera != nullptr); }
|
||||
void BeginCinematic();
|
||||
void StartCinematic(uint32 const cinematicSequenceId);
|
||||
uint32 GetActiveCinematicCamera() const { return _activeCinematicCameraId; }
|
||||
void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { _activeCinematicCameraId = cinematicCameraId; }
|
||||
bool IsOnCinematic() const { return (_cinematicCamera != nullptr); }
|
||||
void StartCinematicCamera();
|
||||
void EndCinematic();
|
||||
void UpdateCinematicLocation(uint32 diff);
|
||||
void UpdateCinematic(uint32 const diff);
|
||||
Position const& GetRemoteSightPosition() const { return _remoteSightPosition; }
|
||||
|
||||
private:
|
||||
// Remote location information
|
||||
Player* player;
|
||||
|
||||
protected:
|
||||
uint32 m_cinematicDiff;
|
||||
uint32 m_lastCinematicCheck;
|
||||
uint32 m_activeCinematicCameraId;
|
||||
uint32 m_cinematicLength;
|
||||
std::vector<FlyByCamera> const* m_cinematicCamera;
|
||||
Position m_remoteSightPosition;
|
||||
TempSummon* m_CinematicObject;
|
||||
Player& _player;
|
||||
uint32 _cinematicDiff;
|
||||
uint32 _activeCinematicCameraId;
|
||||
std::vector<FlyByCamera> const* _cinematicCamera;
|
||||
Position _remoteSightPosition;
|
||||
IntervalTimer _cinematicUpdateTimer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -154,7 +154,7 @@ static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4355)
|
||||
#endif
|
||||
Player::Player(WorldSession* session): Unit(), m_mover(this)
|
||||
Player::Player(WorldSession* session): Unit(), m_mover(this), _cinematicMgr(*this)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default:4355)
|
||||
@@ -395,8 +395,6 @@ Player::Player(WorldSession* session): Unit(), m_mover(this)
|
||||
|
||||
m_creationTime = 0s;
|
||||
|
||||
_cinematicMgr = new CinematicMgr(this);
|
||||
|
||||
m_achievementMgr = new AchievementMgr(this);
|
||||
m_reputationMgr = new ReputationMgr(this);
|
||||
|
||||
@@ -462,7 +460,6 @@ Player::~Player()
|
||||
delete m_runes;
|
||||
delete m_achievementMgr;
|
||||
delete m_reputationMgr;
|
||||
delete _cinematicMgr;
|
||||
|
||||
sWorldSessionMgr->DecreasePlayerCount();
|
||||
|
||||
@@ -772,7 +769,9 @@ bool Player::IsImmuneToEnvironmentalDamage()
|
||||
|
||||
uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
|
||||
{
|
||||
if (IsImmuneToEnvironmentalDamage())
|
||||
// DAMAGE_FALL_TO_VOID bypasses all immunities (e.g. Divine Shield) to prevent
|
||||
// players from being stuck infinitely falling below the map
|
||||
if (type != DAMAGE_FALL_TO_VOID && IsImmuneToEnvironmentalDamage())
|
||||
return 0;
|
||||
|
||||
// Absorb, resist some environmental damage type
|
||||
@@ -5697,10 +5696,6 @@ void Player::SendCinematicStart(uint32 CinematicSequenceId) const
|
||||
WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
|
||||
data << uint32(CinematicSequenceId);
|
||||
SendDirectMessage(&data);
|
||||
if (CinematicSequencesEntry const* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId))
|
||||
{
|
||||
_cinematicMgr->SetActiveCinematicCamera(sequence->cinematicCamera);
|
||||
}
|
||||
}
|
||||
|
||||
void Player::SendMovieStart(uint32 MovieId)
|
||||
@@ -10457,7 +10452,7 @@ void Player::SendTaxiNodeStatusMultiple()
|
||||
if (!creature->HasNpcFlag(UNIT_NPC_FLAG_FLIGHTMASTER))
|
||||
return;
|
||||
|
||||
uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetMapId(), GetTeamId());
|
||||
uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(*creature, GetTeamId(true));
|
||||
if (!nearestNode)
|
||||
return;
|
||||
|
||||
@@ -12515,21 +12510,52 @@ bool Player::HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item cons
|
||||
if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
|
||||
return true;
|
||||
|
||||
// Keep active non-passive auras (e.g. Bladestorm) when disarmed
|
||||
if (!spellInfo->IsPassive())
|
||||
{
|
||||
bool hasWeaponInSlot = false;
|
||||
for (uint8 i = EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i)
|
||||
if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
|
||||
{
|
||||
hasWeaponInSlot = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasWeaponInSlot)
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
if (spellInfo->Effects[i].IsAura())
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ITEM_CLASS_ARMOR:
|
||||
{
|
||||
// most used check: shield only
|
||||
if (spellInfo->EquippedItemSubClassMask & (1 << ITEM_SUBCLASS_ARMOR_SHIELD))
|
||||
{
|
||||
if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
|
||||
if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
|
||||
return true;
|
||||
|
||||
// Keep active non-passive auras (e.g. Shield Wall) when disarmed
|
||||
Item* offhand = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
|
||||
if (!spellInfo->IsPassive() && offhand && offhand != ignoreItem)
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
if (spellInfo->Effects[i].IsAura())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// tabard not have dependent spells
|
||||
for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_MAINHAND; ++i)
|
||||
if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
|
||||
return true;
|
||||
|
||||
// shields can be equipped to offhand slot
|
||||
if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
|
||||
if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
|
||||
return true;
|
||||
|
||||
// ranged slot can have some armor subclasses
|
||||
if (Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
||||
if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
|
||||
@@ -13210,6 +13236,14 @@ WorldObject* Player::GetViewpoint() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Position const& Player::GetSightPosition() const
|
||||
{
|
||||
if (_cinematicMgr.IsOnCinematic())
|
||||
return _cinematicMgr.GetRemoteSightPosition();
|
||||
|
||||
return *m_seer;
|
||||
}
|
||||
|
||||
bool Player::CanUseBattlegroundObject(GameObject* gameobject) const
|
||||
{
|
||||
// It is possible to call this method will a nullptr pointer, only skipping faction check.
|
||||
@@ -16279,7 +16313,10 @@ bool Player::IsWorldObjectOutOfSightRange(WorldObject const* target) const
|
||||
}
|
||||
|
||||
// Check if out of range
|
||||
return !m_seer->IsWithinDist(target, GetSightRange(target), false);
|
||||
if (!target->IsWithinSightRange(GetSightPosition(), GetSightRange(target)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Player::GetPlayerName()
|
||||
|
||||
@@ -1394,7 +1394,8 @@ public:
|
||||
[[nodiscard]] TradeData* GetTradeData() const { return m_trade; }
|
||||
void TradeCancel(bool sendback, TradeStatus status = TRADE_STATUS_TRADE_CANCELED);
|
||||
|
||||
CinematicMgr* GetCinematicMgr() const { return _cinematicMgr; }
|
||||
CinematicMgr& GetCinematicMgr() { return _cinematicMgr; }
|
||||
CinematicMgr const& GetCinematicMgr() const { return _cinematicMgr; }
|
||||
|
||||
void UpdateEnchantTime(uint32 time);
|
||||
void UpdateSoulboundTradeItems();
|
||||
@@ -2388,6 +2389,7 @@ public:
|
||||
WorldObject* GetSeer() const { return m_seer; }
|
||||
void SetViewpoint(WorldObject* target, bool apply);
|
||||
[[nodiscard]] WorldObject* GetViewpoint() const;
|
||||
Position const& GetSightPosition() const;
|
||||
void StopCastingCharm(Aura* except = nullptr);
|
||||
void StopCastingBindSight(Aura* except = nullptr);
|
||||
|
||||
@@ -2984,7 +2986,7 @@ private:
|
||||
Item* _StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update);
|
||||
Item* _LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint32 timeDiff, Field* fields);
|
||||
|
||||
CinematicMgr* _cinematicMgr;
|
||||
CinematicMgr _cinematicMgr;
|
||||
|
||||
typedef GuidSet RefundableItemsSet;
|
||||
RefundableItemsSet m_refundableItems;
|
||||
|
||||
@@ -68,14 +68,8 @@ void Player::Update(uint32 p_time)
|
||||
m_nextMailDelivereTime = time_t(0);
|
||||
}
|
||||
|
||||
// Update cinematic location, if 500ms have passed and we're doing a
|
||||
// cinematic now.
|
||||
_cinematicMgr->m_cinematicDiff += p_time;
|
||||
if (_cinematicMgr->m_cinematicCamera && _cinematicMgr->m_activeCinematicCameraId && GetMSTimeDiffToNow(_cinematicMgr->m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF)
|
||||
{
|
||||
_cinematicMgr->m_lastCinematicCheck = getMSTime();
|
||||
_cinematicMgr->UpdateCinematicLocation(p_time);
|
||||
}
|
||||
// Update cinematic camera (if needed)
|
||||
_cinematicMgr.UpdateCinematic(p_time);
|
||||
|
||||
// used to implement delayed far teleports
|
||||
SetMustDelayTeleport(true);
|
||||
@@ -1609,8 +1603,8 @@ void Player::UpdateVisibilityForPlayer(bool mapChange)
|
||||
m_seer = this;
|
||||
|
||||
Acore::VisibleNotifier notifier(*this, mapChange);
|
||||
Cell::VisitObjects(m_seer, notifier, GetSightRange());
|
||||
Cell::VisitFarVisibleObjects(m_seer, notifier, VISIBILITY_DISTANCE_GIGANTIC);
|
||||
Cell::VisitObjects(GetSightPosition().GetPositionX(), GetSightPosition().GetPositionY(), GetMap(), notifier, GetSightRange());
|
||||
Cell::VisitFarVisibleObjects(GetSightPosition().GetPositionX(), GetSightPosition().GetPositionY(), GetMap(), notifier, VISIBILITY_DISTANCE_GIGANTIC);
|
||||
notifier.SendToSelf();
|
||||
|
||||
if (mapChange)
|
||||
|
||||
@@ -179,7 +179,7 @@ void Totem::UnSummon(Milliseconds msTime)
|
||||
AddObjectToRemoveList();
|
||||
}
|
||||
|
||||
bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
|
||||
bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const
|
||||
{
|
||||
// xinef: immune to all positive spells, except of stoneclaw totem absorb, sentry totem bind sight and intervene
|
||||
// totems positive spells have unit_caster target
|
||||
@@ -209,5 +209,5 @@ bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) con
|
||||
break;
|
||||
}
|
||||
|
||||
return Creature::IsImmunedToSpellEffect(spellInfo, index);
|
||||
return Creature::IsImmunedToSpellEffect(spellInfo, index, caster);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
void UpdateAttackPowerAndDamage(bool /*ranged*/) override {}
|
||||
void UpdateDamagePhysical(WeaponAttackType /*attType*/) override {}
|
||||
|
||||
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const override;
|
||||
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const override;
|
||||
|
||||
protected:
|
||||
TotemType m_type;
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
#include "Vehicle.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
float baseMoveSpeed[MAX_MOVE_TYPE] =
|
||||
@@ -125,6 +126,28 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo, uint8 damageIndex)
|
||||
m_damageType(DIRECT_DAMAGE), m_attackType(dmgInfo.attackType), m_absorb(dmgInfo.damages[damageIndex].absorb), m_resist(dmgInfo.damages[damageIndex].resist), m_block(dmgInfo.blocked_amount),
|
||||
m_cleanDamage(dmgInfo.cleanDamage), m_hitMask(0)
|
||||
{
|
||||
switch (dmgInfo.TargetState)
|
||||
{
|
||||
case VICTIMSTATE_IS_IMMUNE:
|
||||
m_hitMask |= PROC_HIT_IMMUNE;
|
||||
break;
|
||||
case VICTIMSTATE_BLOCKS:
|
||||
m_hitMask |= PROC_HIT_FULL_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dmgInfo.HitInfo & (HITINFO_PARTIAL_ABSORB | HITINFO_FULL_ABSORB))
|
||||
m_hitMask |= PROC_HIT_ABSORB;
|
||||
|
||||
if (dmgInfo.HitInfo & HITINFO_FULL_RESIST)
|
||||
m_hitMask |= PROC_HIT_FULL_RESIST;
|
||||
|
||||
if (m_block)
|
||||
m_hitMask |= PROC_HIT_BLOCK;
|
||||
|
||||
bool const damageNullified = (dmgInfo.HitInfo & (HITINFO_FULL_ABSORB | HITINFO_FULL_RESIST)) != 0
|
||||
|| (m_hitMask & (PROC_HIT_IMMUNE | PROC_HIT_FULL_BLOCK)) != 0;
|
||||
|
||||
switch (dmgInfo.hitOutCome)
|
||||
{
|
||||
case MELEE_HIT_MISS:
|
||||
@@ -140,25 +163,19 @@ DamageInfo::DamageInfo(CalcDamageInfo const& dmgInfo, uint8 damageIndex)
|
||||
m_hitMask |= PROC_HIT_EVADE;
|
||||
break;
|
||||
case MELEE_HIT_BLOCK:
|
||||
m_hitMask |= PROC_HIT_BLOCK;
|
||||
[[fallthrough]];
|
||||
case MELEE_HIT_CRUSHING:
|
||||
case MELEE_HIT_GLANCING:
|
||||
case MELEE_HIT_NORMAL:
|
||||
m_hitMask |= PROC_HIT_NORMAL;
|
||||
if (!damageNullified)
|
||||
m_hitMask |= PROC_HIT_NORMAL;
|
||||
break;
|
||||
case MELEE_HIT_CRIT:
|
||||
m_hitMask |= PROC_HIT_CRITICAL;
|
||||
if (!damageNullified)
|
||||
m_hitMask |= PROC_HIT_CRITICAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (dmgInfo.damages[damageIndex].absorb)
|
||||
m_hitMask |= PROC_HIT_ABSORB;
|
||||
|
||||
if (dmgInfo.blocked_amount)
|
||||
m_hitMask |= (dmgInfo.damages[damageIndex].damage == dmgInfo.blocked_amount) ? PROC_HIT_FULL_BLOCK : PROC_HIT_BLOCK;
|
||||
}
|
||||
|
||||
DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask)
|
||||
@@ -372,9 +389,6 @@ Unit::Unit() : WorldObject(),
|
||||
m_transform = 0;
|
||||
m_canModifyStats = false;
|
||||
|
||||
for (uint8 i = 0; i < MAX_SPELL_IMMUNITY; ++i)
|
||||
m_spellImmune[i].clear();
|
||||
|
||||
for (uint8 i = 0; i < UNIT_MOD_END; ++i)
|
||||
{
|
||||
m_auraFlatModifiersGroup[i][BASE_VALUE] = 0.0f;
|
||||
@@ -891,6 +905,48 @@ bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster /*= nullptr*/)
|
||||
{
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
bool immuneToAllEffects = true;
|
||||
bool hasCheckedEffect = false;
|
||||
|
||||
for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (!(effectMask & (1u << i)))
|
||||
continue;
|
||||
|
||||
if (!spellInfo->Effects[i].IsEffect())
|
||||
continue;
|
||||
|
||||
hasCheckedEffect = true;
|
||||
|
||||
if (IsImmunedToSpellEffect(spellInfo, i, caster))
|
||||
{
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR4_NO_PARTIAL_IMMUNITY))
|
||||
return true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
immuneToAllEffects = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasCheckedEffect && immuneToAllEffects)
|
||||
return true;
|
||||
|
||||
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
{
|
||||
if (IsImmunedToSchool(spellInfo))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Unit::HasBreakableByDamageAuraType(AuraType type, uint32 excludeAura) const
|
||||
{
|
||||
AuraEffectList const& auras = GetAuraEffectsByType(type);
|
||||
@@ -2075,7 +2131,7 @@ void Unit::DealDamageShieldDamage(Unit* victim)
|
||||
}
|
||||
|
||||
// ...or immuned
|
||||
if (IsImmunedToDamageOrSchool(i_spellProto))
|
||||
if (IsImmunedToDamage(victim, i_spellProto))
|
||||
{
|
||||
victim->SendSpellDamageImmune(this, i_spellProto->Id);
|
||||
continue;
|
||||
@@ -3203,7 +3259,7 @@ void Unit::SendMeleeAttackStop(Unit* victim)
|
||||
bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType)
|
||||
{
|
||||
// These spells can't be blocked
|
||||
if (spellProto && spellProto->HasAttribute(SPELL_ATTR0_NO_ACTIVE_DEFENSE))
|
||||
if (spellProto && (spellProto->HasAttribute(SPELL_ATTR0_NO_ACTIVE_DEFENSE) || spellProto->HasAttribute(SPELL_ATTR3_ALWAYS_HIT)))
|
||||
return false;
|
||||
|
||||
if (victim->HasIgnoreHitDirectionAura() || victim->HasInArc(M_PI, this))
|
||||
@@ -3571,6 +3627,9 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo
|
||||
// Resist
|
||||
SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spell, bool CanReflect)
|
||||
{
|
||||
if (spell->HasAttribute(SPELL_ATTR3_ALWAYS_HIT))
|
||||
return SPELL_MISS_NONE;
|
||||
|
||||
// Check for immune
|
||||
if (victim->IsImmunedToSpell(spell))
|
||||
return SPELL_MISS_IMMUNE;
|
||||
@@ -3581,11 +3640,6 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spell, bool Ca
|
||||
&& (!IsHostileTo(victim))) // prevent from affecting enemy by "positive" spell
|
||||
return SPELL_MISS_NONE;
|
||||
|
||||
// Check for immune
|
||||
// xinef: check for school immunity only
|
||||
if (victim->IsImmunedToSchool(spell))
|
||||
return SPELL_MISS_IMMUNE;
|
||||
|
||||
if (this == victim)
|
||||
return SPELL_MISS_NONE;
|
||||
|
||||
@@ -3650,13 +3704,17 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, Spell const* spell, bool CanRef
|
||||
return SPELL_MISS_NONE;
|
||||
}
|
||||
|
||||
// Check for immune
|
||||
// xinef: check for school immunity only
|
||||
if (victim->IsImmunedToSchool(spell))
|
||||
// Check for immune to spell effects (includes school, mechanics, state, dispel immunities)
|
||||
if (victim->IsImmunedToSpell(spellInfo, MAX_EFFECT_MASK, this))
|
||||
{
|
||||
return SPELL_MISS_IMMUNE;
|
||||
}
|
||||
|
||||
// Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply
|
||||
// returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit
|
||||
if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(this, spellInfo))
|
||||
return SPELL_MISS_IMMUNE;
|
||||
|
||||
if (this == victim)
|
||||
{
|
||||
return SPELL_MISS_NONE;
|
||||
@@ -5447,7 +5505,7 @@ void Unit::RemoveMovementImpairingAuras(bool withRoot)
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode, uint32 except)
|
||||
void Unit::RemoveAurasWithMechanic(uint64 mechanic_mask, AuraRemoveMode removemode, uint32 except)
|
||||
{
|
||||
for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
|
||||
{
|
||||
@@ -5466,7 +5524,7 @@ void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemo
|
||||
|
||||
void Unit::RemoveAurasByShapeShift()
|
||||
{
|
||||
uint32 mechanic_mask = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT);
|
||||
uint64 mechanic_mask = (UI64LIT(1) << MECHANIC_SNARE) | (UI64LIT(1) << MECHANIC_ROOT);
|
||||
for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
|
||||
{
|
||||
Aura const* aura = iter->second->GetBase();
|
||||
@@ -6015,17 +6073,17 @@ bool Unit::HasNegativeAuraWithAttribute(uint32 flag, ObjectGuid guid)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Unit::HasAuraWithMechanic(uint32 mechanicMask) const
|
||||
bool Unit::HasAuraWithMechanic(uint64 mechanicMask) const
|
||||
{
|
||||
for (AuraApplicationMap::const_iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
|
||||
{
|
||||
SpellInfo const* spellInfo = iter->second->GetBase()->GetSpellInfo();
|
||||
if (spellInfo->Mechanic && (mechanicMask & (1 << spellInfo->Mechanic)))
|
||||
if (spellInfo->Mechanic && (mechanicMask & (UI64LIT(1) << spellInfo->Mechanic)))
|
||||
return true;
|
||||
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
if (iter->second->HasEffect(i) && spellInfo->Effects[i].Effect && spellInfo->Effects[i].Mechanic)
|
||||
if (mechanicMask & (1 << spellInfo->Effects[i].Mechanic))
|
||||
if (mechanicMask & (UI64LIT(1) << spellInfo->Effects[i].Mechanic))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7339,19 +7397,22 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
|
||||
// set position before any AI calls/assistance
|
||||
//if (IsCreature())
|
||||
// ToCreature()->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ());
|
||||
if (creature && !IsControlledByPlayer())
|
||||
if (creature)
|
||||
{
|
||||
EngageWithTarget(victim);
|
||||
|
||||
creature->SendAIReaction(AI_REACTION_HOSTILE);
|
||||
if (!IsControlledByPlayer())
|
||||
{
|
||||
creature->SendAIReaction(AI_REACTION_HOSTILE);
|
||||
|
||||
/// @todo: Implement aggro range, detection range and assistance range templates
|
||||
if (!(creature->HasFlagsExtra(CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE)))
|
||||
creature->CallAssistance();
|
||||
/// @todo: Implement aggro range, detection range and assistance range templates
|
||||
if (!(creature->HasFlagsExtra(CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE)))
|
||||
creature->CallAssistance();
|
||||
|
||||
creature->SetAssistanceTimer(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD));
|
||||
creature->SetAssistanceTimer(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD));
|
||||
|
||||
SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
|
||||
SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
// delay offhand weapon attack by 50% of the base attack time
|
||||
@@ -8836,7 +8897,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui
|
||||
});
|
||||
}
|
||||
|
||||
if (uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask())
|
||||
if (uint64 mechanicMask = spellProto->GetAllEffectsMechanicMask())
|
||||
{
|
||||
int32 modifierMax = 0;
|
||||
int32 modifierMin = 0;
|
||||
@@ -8851,7 +8912,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui
|
||||
if (!caster || caster->GetGUID() != (*i)->GetCasterGUID())
|
||||
continue;
|
||||
|
||||
if (mechanicMask & uint32(1 << (*i)->GetMiscValue()))
|
||||
if (mechanicMask & uint64(UI64LIT(1) << (*i)->GetMiscValue()))
|
||||
{
|
||||
if ((*i)->GetAmount() > 0)
|
||||
{
|
||||
@@ -9742,107 +9803,90 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask)
|
||||
return AdvertisedBenefit;
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const
|
||||
uint32 Unit::GetDamageImmunityMask() const
|
||||
{
|
||||
if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If m_immuneToDamage type contain magic, IMMUNE damage.
|
||||
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
|
||||
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
|
||||
if ((itr->type & meleeSchoolMask) == meleeSchoolMask)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
uint32 mask = 0;
|
||||
SpellImmuneContainer const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
|
||||
for (auto const& [immunitySchoolMask, _] : damageList)
|
||||
mask |= immunitySchoolMask;
|
||||
return mask;
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const
|
||||
bool Unit::IsImmunedToDamage(SpellSchoolMask schoolMask) const
|
||||
{
|
||||
if (!spellInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 schoolMask = spellInfo->GetSchoolMask();
|
||||
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If m_immuneToDamage type contain magic, IMMUNE damage.
|
||||
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
|
||||
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
|
||||
if ((itr->type & schoolMask) == schoolMask)
|
||||
// If m_immuneToSchool type contains all requested schools, IMMUNE damage.
|
||||
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
for (auto const& itr : schoolList)
|
||||
if (IsImmuneMaskFully(SpellSchoolMask(itr.first), schoolMask))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
// If m_immuneToDamage type contains all requested schools, IMMUNE damage.
|
||||
SpellImmuneContainer const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
|
||||
return std::ranges::any_of(damageList, [schoolMask](auto const& immune)
|
||||
{
|
||||
return Unit::IsImmuneMaskFully(SpellSchoolMask(immune.first), schoolMask);
|
||||
});
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToDamage(Spell const* spell) const
|
||||
bool Unit::IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const
|
||||
{
|
||||
SpellInfo const* spellInfo = spell->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
|
||||
{
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 schoolMask = spell->GetSpellSchoolMask();
|
||||
SpellSchoolMask schoolMask = SpellSchoolMask(spellInfo->GetSchoolMask());
|
||||
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If m_immuneToDamage type contain magic, IMMUNE damage.
|
||||
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
|
||||
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
|
||||
auto hasImmunity = [&](SpellImmuneContainer const& container)
|
||||
{
|
||||
if ((itr->type & schoolMask) == schoolMask)
|
||||
uint32 schoolImmunityMask = 0;
|
||||
for (auto const& [immunitySchoolMask, immunityAuraId] : container)
|
||||
{
|
||||
return true;
|
||||
SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId);
|
||||
if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this))
|
||||
continue;
|
||||
|
||||
if (immuneAuraInfo && spellInfo->CanPierceImmuneAura(immuneAuraInfo))
|
||||
continue;
|
||||
|
||||
schoolImmunityMask |= immunitySchoolMask;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToSchool(SpellSchoolMask meleeSchoolMask) const
|
||||
{
|
||||
if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// We need to be immune to all types
|
||||
return (schoolImmunityMask & schoolMask) == schoolMask;
|
||||
};
|
||||
|
||||
// If m_immuneToSchool type contain this school type, IMMUNE damage.
|
||||
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
||||
if ((itr->type & meleeSchoolMask) == meleeSchoolMask)
|
||||
return true;
|
||||
if (hasImmunity(m_spellImmune[IMMUNITY_SCHOOL]))
|
||||
return true;
|
||||
|
||||
// If m_immuneToDamage type contain magic, IMMUNE damage.
|
||||
if (hasImmunity(m_spellImmune[IMMUNITY_DAMAGE]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToSchool(SpellSchoolMask schoolMask) const
|
||||
{
|
||||
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||
return false;
|
||||
|
||||
// Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered by at least one immunity
|
||||
// (e.g., Anti-Magic Shell provides school immunity to all magic schools)
|
||||
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
return std::ranges::any_of(schoolList, [schoolMask](auto const& immune)
|
||||
{
|
||||
return (immune.first & schoolMask) == schoolMask;
|
||||
});
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToSchool(SpellInfo const* spellInfo) const
|
||||
{
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
|
||||
@@ -9856,10 +9900,10 @@ bool Unit::IsImmunedToSchool(SpellInfo const* spellInfo) const
|
||||
|
||||
if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471)
|
||||
{
|
||||
// If m_immuneToSchool type contain this school type, IMMUNE damage.
|
||||
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
||||
if ((itr->type & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->spellId)))
|
||||
// Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered and spell can't pierce
|
||||
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
||||
if ((itr->first & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->second)))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9882,11 +9926,11 @@ bool Unit::IsImmunedToSchool(Spell const* spell) const
|
||||
|
||||
if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471)
|
||||
{
|
||||
// If m_immuneToSchool type contain this school type, IMMUNE damage.
|
||||
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
||||
// Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered and spell can't pierce
|
||||
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
||||
{
|
||||
if ((itr->type & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->spellId)))
|
||||
if ((itr->first & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->second)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -9896,19 +9940,49 @@ bool Unit::IsImmunedToSchool(Spell const* spell) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToDamageOrSchool(SpellSchoolMask meleeSchoolMask) const
|
||||
bool Unit::IsImmunedToDamageOrSchool(SpellSchoolMask schoolMask) const
|
||||
{
|
||||
if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsImmunedToDamage(meleeSchoolMask) || IsImmunedToSchool(meleeSchoolMask);
|
||||
return IsImmunedToDamage(schoolMask) || IsImmunedToSchool(schoolMask);
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToDamageOrSchool(SpellInfo const* spellInfo) const
|
||||
bool Unit::IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spellInfo) const
|
||||
{
|
||||
return IsImmunedToDamage(spellInfo) || IsImmunedToSchool(spellInfo);
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
return false;
|
||||
|
||||
SpellSchoolMask schoolMask = SpellSchoolMask(spellInfo->GetSchoolMask());
|
||||
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||
return false;
|
||||
|
||||
auto hasImmunity = [&](SpellImmuneContainer const& container)
|
||||
{
|
||||
uint32 schoolImmunityMask = 0;
|
||||
for (auto const& [immunitySchoolMask, immunityAuraId] : container)
|
||||
{
|
||||
SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId);
|
||||
if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this))
|
||||
continue;
|
||||
|
||||
schoolImmunityMask |= immunitySchoolMask;
|
||||
}
|
||||
|
||||
// We need to be immune to all types
|
||||
return (schoolImmunityMask & schoolMask) == schoolMask;
|
||||
};
|
||||
|
||||
// If m_immuneToSchool type contain this school type, IMMUNE periodic tick.
|
||||
if (hasImmunity(m_spellImmune[IMMUNITY_SCHOOL]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
@@ -9917,39 +9991,35 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
return false;
|
||||
|
||||
// Single spell immunity.
|
||||
SpellImmuneList const& idList = m_spellImmune[IMMUNITY_ID];
|
||||
for (SpellImmuneList::const_iterator itr = idList.begin(); itr != idList.end(); ++itr)
|
||||
if (itr->type == spellInfo->Id)
|
||||
return true;
|
||||
SpellImmuneContainer const& idList = m_spellImmune[IMMUNITY_ID];
|
||||
if (idList.count(spellInfo->Id) > 0)
|
||||
return true;
|
||||
|
||||
// xinef: my special immunity, if spellid is not on this list it means npc is immune
|
||||
SpellImmuneList const& allowIdList = m_spellImmune[IMMUNITY_ALLOW_ID];
|
||||
SpellImmuneContainer const& allowIdList = m_spellImmune[IMMUNITY_ALLOW_ID];
|
||||
if (!allowIdList.empty())
|
||||
{
|
||||
for (SpellImmuneList::const_iterator itr = allowIdList.begin(); itr != allowIdList.end(); ++itr)
|
||||
if (itr->type == spellInfo->Id)
|
||||
return false;
|
||||
return true;
|
||||
if (allowIdList.count(spellInfo->Id) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
|
||||
return false;
|
||||
|
||||
if (spellInfo->Dispel)
|
||||
if (uint32 dispel = spellInfo->Dispel)
|
||||
{
|
||||
SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
|
||||
for (SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr)
|
||||
if (itr->type == spellInfo->Dispel)
|
||||
return true;
|
||||
SpellImmuneContainer const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
|
||||
if (dispelList.count(dispel) > 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Spells that don't have effectMechanics.
|
||||
if (spellInfo->Mechanic)
|
||||
if (uint32 mechanic = spellInfo->Mechanic)
|
||||
{
|
||||
SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
|
||||
for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
|
||||
if (itr->type == spellInfo->Mechanic)
|
||||
return true;
|
||||
SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
|
||||
if (mechanicList.count(mechanic) > 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool immuneToAllEffects = true;
|
||||
@@ -9974,7 +10044,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
if (immuneToAllEffects) //Return immune only if the target is immune to all spell effects.
|
||||
return true;
|
||||
|
||||
if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471)
|
||||
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
{
|
||||
SpellSchoolMask spellSchoolMask = spellInfo->GetSchoolMask();
|
||||
if (spell)
|
||||
@@ -9984,16 +10054,24 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
|
||||
if (spellSchoolMask != SPELL_SCHOOL_MASK_NONE)
|
||||
{
|
||||
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
||||
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
||||
{
|
||||
SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->spellId);
|
||||
if (((itr->type & spellSchoolMask) == spellSchoolMask)
|
||||
&& (!immuneSpellInfo || immuneSpellInfo->IsPositive()) && !spellInfo->IsPositive()
|
||||
&& !spellInfo->CanPierceImmuneAura(immuneSpellInfo))
|
||||
SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second);
|
||||
if (!(itr->first & spellSchoolMask))
|
||||
continue;
|
||||
|
||||
if (immuneSpellInfo && !immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS))
|
||||
{
|
||||
return true;
|
||||
Unit const* spellCaster = spell ? spell->GetCaster() : nullptr;
|
||||
if (spellCaster && spellCaster->IsFriendlyTo(this))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spellInfo->CanPierceImmuneAura(immuneSpellInfo))
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10001,7 +10079,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
|
||||
bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const
|
||||
{
|
||||
if (!spellInfo || !spellInfo->Effects[index].IsEffect())
|
||||
return false;
|
||||
@@ -10013,12 +10091,16 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
|
||||
return false;
|
||||
|
||||
// Special-case handling for Vezax's Aura of Despair.
|
||||
static constexpr uint32 SPELL_AURA_OF_DESPAIR_1 = 62692;
|
||||
static constexpr uint32 SPELL_AURA_OF_DESPAIR_2 = 64848;
|
||||
|
||||
//If m_immuneToEffect type contain this effect type, IMMUNE effect.
|
||||
uint32 effect = spellInfo->Effects[index].Effect;
|
||||
SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT];
|
||||
for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
|
||||
SpellImmuneContainer const& effectList = m_spellImmune[IMMUNITY_EFFECT];
|
||||
for (SpellImmuneContainer::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
|
||||
{
|
||||
if (itr->type == effect && (itr->spellId != 62692 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo))))
|
||||
if (itr->first == effect && (itr->second != SPELL_AURA_OF_DESPAIR_1 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo))))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -10026,41 +10108,32 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons
|
||||
|
||||
if (uint32 mechanic = spellInfo->Effects[index].Mechanic)
|
||||
{
|
||||
SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
|
||||
for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
|
||||
if (itr->type == mechanic)
|
||||
return true;
|
||||
auto const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
|
||||
if (mechanicList.count(mechanic) > 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (uint32 aura = spellInfo->Effects[index].ApplyAuraName)
|
||||
if (!spellInfo->HasAttribute(SPELL_ATTR3_ALWAYS_HIT))
|
||||
{
|
||||
SpellImmuneList const& list = m_spellImmune[IMMUNITY_STATE];
|
||||
for (SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr)
|
||||
if (uint32 aura = spellInfo->Effects[index].ApplyAuraName)
|
||||
{
|
||||
if (itr->type == aura && (itr->spellId != 64848 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo))))
|
||||
SpellImmuneContainer const& list = m_spellImmune[IMMUNITY_STATE];
|
||||
for (SpellImmuneContainer::const_iterator itr = list.begin(); itr != list.end(); ++itr)
|
||||
{
|
||||
if (!spellInfo->HasAttribute(SPELL_ATTR3_ALWAYS_HIT))
|
||||
{
|
||||
if (itr->blockType == SPELL_BLOCK_TYPE_ALL || spellInfo->IsPositive()) // xinef: added for pet scaling
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
{
|
||||
// Check for immune to application of harmful magical effects
|
||||
AuraEffectList const& immuneAuraApply = GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL);
|
||||
for (AuraEffectList::const_iterator iter = immuneAuraApply.begin(); iter != immuneAuraApply.end(); ++iter)
|
||||
{
|
||||
if (/*(spellInfo->Dispel == DISPEL_MAGIC || spellInfo->Dispel == DISPEL_CURSE || spellInfo->Dispel == DISPEL_DISEASE) &&*/ // Magic debuff, xinef: all kinds?
|
||||
((*iter)->GetMiscValue() & spellInfo->GetSchoolMask()) && // Check school
|
||||
!spellInfo->IsPositiveEffect(index) && // Harmful
|
||||
spellInfo->Effects[index].Effect != SPELL_EFFECT_PERSISTENT_AREA_AURA) // Not Persistent area auras
|
||||
{
|
||||
if (itr->first == aura && (itr->second != SPELL_AURA_OF_DESPAIR_2 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo))))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
{
|
||||
// Check for immune to application of harmful magical effects
|
||||
for (AuraEffect* immuneAuraApply : GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL))
|
||||
{
|
||||
if (!(immuneAuraApply->GetMiscValue() & spellInfo->GetSchoolMask())) // Check school
|
||||
continue;
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || (caster && !IsFriendlyTo(caster))) // Harmful
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10289,17 +10362,17 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT
|
||||
});
|
||||
|
||||
// Mod damage from spell mechanic
|
||||
uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask();
|
||||
uint64 mechanicMask = spellProto->GetAllEffectsMechanicMask();
|
||||
|
||||
// Shred, Maul - "Effects which increase Bleed damage also increase Shred damage"
|
||||
if (spellProto->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags[0] & 0x00008800)
|
||||
mechanicMask |= (1 << MECHANIC_BLEED);
|
||||
mechanicMask |= (UI64LIT(1) << MECHANIC_BLEED);
|
||||
|
||||
if (mechanicMask)
|
||||
{
|
||||
TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT, [mechanicMask](AuraEffect const* aurEff) -> bool
|
||||
{
|
||||
if (mechanicMask & uint32(1 << (aurEff->GetMiscValue())))
|
||||
if (mechanicMask & uint64(UI64LIT(1) << (aurEff->GetMiscValue())))
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
@@ -10367,51 +10440,17 @@ private:
|
||||
uint32 _type;
|
||||
};
|
||||
|
||||
void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType)
|
||||
void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType /*blockType*/)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
// xinef: immunities with spellId 0 are intended to be applied only once (script purposes mosty)
|
||||
if (spellId == 0 && std::find_if(m_spellImmune[op].begin(), m_spellImmune[op].end(), spellIdImmunityPredicate(type)) != m_spellImmune[op].end())
|
||||
return;
|
||||
|
||||
SpellImmune immune;
|
||||
immune.spellId = spellId;
|
||||
immune.type = type;
|
||||
immune.blockType = blockType;
|
||||
m_spellImmune[op].push_back(std::move(immune));
|
||||
}
|
||||
m_spellImmune[op].emplace(type, spellId);
|
||||
else
|
||||
{
|
||||
for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr)
|
||||
auto bounds = m_spellImmune[op].equal_range(type);
|
||||
for (auto itr = bounds.first; itr != bounds.second;)
|
||||
{
|
||||
if (itr->spellId == spellId && itr->type == type)
|
||||
{
|
||||
m_spellImmune[op].erase(itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::ApplySpellDispelImmunity(SpellInfo const* spellProto, DispelType type, bool apply)
|
||||
{
|
||||
ApplySpellImmune(spellProto->Id, IMMUNITY_DISPEL, type, apply);
|
||||
|
||||
if (apply && spellProto->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
|
||||
{
|
||||
// Create dispel mask by dispel type
|
||||
uint32 dispelMask = SpellInfo::GetDispelMask(type);
|
||||
// Dispel all existing auras vs current dispel type
|
||||
AuraApplicationMap& auras = GetAppliedAuras();
|
||||
for (AuraApplicationMap::iterator itr = auras.begin(); itr != auras.end();)
|
||||
{
|
||||
SpellInfo const* spell = itr->second->GetBase()->GetSpellInfo();
|
||||
if (spell->GetDispelMask() & dispelMask)
|
||||
{
|
||||
// Dispel aura
|
||||
RemoveAura(itr);
|
||||
}
|
||||
if (itr->second == spellId)
|
||||
itr = m_spellImmune[op].erase(itr);
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
@@ -11177,8 +11216,9 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
|
||||
{
|
||||
if (Creature* creature = ToCreature())
|
||||
{
|
||||
uint32 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask;
|
||||
if (immuneMask & (1 << (MECHANIC_SNARE - 1)) || immuneMask & (1 << (MECHANIC_DAZE - 1)))
|
||||
// use creature helper which now consults creature_immunities table
|
||||
if (creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_SNARE) ||
|
||||
creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_DAZE))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -11199,7 +11239,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
|
||||
if (creature
|
||||
&& !IsPet()
|
||||
&& !(IsControlledByPlayer() && IsVehicle())
|
||||
&& !(creature->HasMechanicTemplateImmunity(MECHANIC_SNARE))
|
||||
&& !(creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_SNARE))
|
||||
&& !(creature->IsDungeonBoss()))
|
||||
{
|
||||
// 1.6% for each % under 30.
|
||||
@@ -11395,6 +11435,8 @@ void Unit::AtTargetAttacked(Unit* target, bool canInitialAggro)
|
||||
target->EngageWithTarget(this);
|
||||
if (Unit* targetOwner = target->GetCharmerOrOwner())
|
||||
targetOwner->EngageWithTarget(this);
|
||||
if (Unit* myOwner = GetCharmerOrOwner())
|
||||
target->EngageWithTarget(myOwner);
|
||||
|
||||
// Patch 3.0.8: All player spells which cause a creature to become aggressive
|
||||
// to you will now also immediately cause the creature to be tapped.
|
||||
@@ -11422,12 +11464,8 @@ Unit* Creature::SelectVictim()
|
||||
|
||||
Unit* target = nullptr;
|
||||
|
||||
// Taunt handling is now done inside ThreatManager via TauntState
|
||||
if (CanHaveThreatList())
|
||||
{
|
||||
if (!m_threatManager.IsThreatListEmpty())
|
||||
target = m_threatManager.GetCurrentVictim();
|
||||
}
|
||||
target = m_threatManager.GetCurrentVictim();
|
||||
else if (!HasReactState(REACT_PASSIVE))
|
||||
{
|
||||
// we have player pet probably
|
||||
@@ -15087,7 +15125,7 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint8 effMask, Unit* target)
|
||||
{
|
||||
if (!(effMask & (1 << i)))
|
||||
continue;
|
||||
if (target->IsImmunedToSpellEffect(spellInfo, i))
|
||||
if (target->IsImmunedToSpellEffect(spellInfo, i, this))
|
||||
effMask &= ~(1 << i);
|
||||
}
|
||||
|
||||
@@ -15739,6 +15777,12 @@ void Unit::_ExitVehicle(Position const* exitPosition)
|
||||
pos.RelocateOffset({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO });
|
||||
else if (seatAddon->ExitParameter == VehicleExitParameters::VehicleExitParamDest)
|
||||
pos.Relocate({ seatAddon->ExitParameterX, seatAddon->ExitParameterY, seatAddon->ExitParameterZ, seatAddon->ExitParameterO });
|
||||
|
||||
bool isInLoS = GetMap()->isInLineOfSight(GetPositionX(), GetPositionY(), GetPositionZ(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing);
|
||||
float floorZ = GetMapHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
|
||||
|
||||
if (!isInLoS || pos.GetPositionZ() < floorZ)
|
||||
pos = vehicleBase->GetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16344,8 +16388,8 @@ void Unit::ExecuteDelayedUnitRelocationEvent()
|
||||
GetMap()->LoadGridsInRange(*player, MAX_VISIBILITY_DISTANCE);
|
||||
|
||||
Acore::PlayerRelocationNotifier notifier(*player);
|
||||
Cell::VisitObjects(viewPoint, notifier, player->GetSightRange());
|
||||
Cell::VisitFarVisibleObjects(viewPoint, notifier, VISIBILITY_DISTANCE_GIGANTIC);
|
||||
Cell::VisitObjects(player->GetSightPosition().GetPositionX(), player->GetSightPosition().GetPositionY(), player->GetMap(), notifier, player->GetSightRange());
|
||||
Cell::VisitFarVisibleObjects(player->GetSightPosition().GetPositionX(), player->GetSightPosition().GetPositionY(), player->GetMap(), notifier, VISIBILITY_DISTANCE_GIGANTIC);
|
||||
notifier.SendToSelf();
|
||||
|
||||
this->AddToNotify(NOTIFY_AI_RELOCATION);
|
||||
|
||||
@@ -1408,7 +1408,7 @@ public:
|
||||
void RemoveAurasWithInterruptFlags(uint32 flag, uint32 except = 0, bool isAutoshot = false);
|
||||
void RemoveAurasWithAttribute(uint32 flags);
|
||||
void RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID);
|
||||
void RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode = AURA_REMOVE_BY_DEFAULT, uint32 except = 0);
|
||||
void RemoveAurasWithMechanic(uint64 mechanic_mask, AuraRemoveMode removemode = AURA_REMOVE_BY_DEFAULT, uint32 except = 0);
|
||||
void RemoveMovementImpairingAuras(bool withRoot);
|
||||
void RemoveAurasByShapeShift();
|
||||
|
||||
@@ -1500,7 +1500,7 @@ public:
|
||||
bool HasNegativeAuraWithInterruptFlag(uint32 flag, ObjectGuid guid = ObjectGuid::Empty);
|
||||
[[nodiscard]] bool HasVisibleAuraType(AuraType auraType) const;
|
||||
bool HasNegativeAuraWithAttribute(uint32 flag, ObjectGuid guid = ObjectGuid::Empty);
|
||||
[[nodiscard]] bool HasAuraWithMechanic(uint32 mechanicMask) const;
|
||||
[[nodiscard]] bool HasAuraWithMechanic(uint64 mechanicMask) const;
|
||||
|
||||
[[nodiscard]] bool HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint32 familyFlags) const;
|
||||
|
||||
@@ -1636,17 +1636,22 @@ public:
|
||||
|
||||
// Spells immunities
|
||||
void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType = SPELL_BLOCK_TYPE_ALL);
|
||||
void ApplySpellDispelImmunity(SpellInfo const* spellProto, DispelType type, bool apply);
|
||||
virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr);
|
||||
[[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const;
|
||||
[[nodiscard]] bool IsImmunedToDamage(SpellInfo const* spellInfo) const;
|
||||
[[nodiscard]] bool IsImmunedToDamage(Spell const* spell) const;
|
||||
[[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask meleeSchoolMask) const;
|
||||
bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster = nullptr);
|
||||
[[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask schoolMask) const;
|
||||
[[nodiscard]] bool IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const;
|
||||
[[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask schoolMask) const;
|
||||
|
||||
static bool IsImmuneMaskFully(SpellSchoolMask immuneMask, SpellSchoolMask schoolMask) { return (immuneMask & schoolMask) == schoolMask; }
|
||||
|
||||
[[nodiscard]] uint32 GetSchoolImmunityMask() const;
|
||||
[[nodiscard]] uint32 GetDamageImmunityMask() const;
|
||||
|
||||
[[nodiscard]] bool IsImmunedToSchool(SpellInfo const* spellInfo) const;
|
||||
[[nodiscard]] bool IsImmunedToSchool(Spell const* spell) const;
|
||||
[[nodiscard]] bool IsImmunedToDamageOrSchool(SpellSchoolMask meleeSchoolMask) const;
|
||||
bool IsImmunedToDamageOrSchool(SpellInfo const* spellInfo) const;
|
||||
virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const;
|
||||
[[nodiscard]] bool IsImmunedToDamageOrSchool(SpellSchoolMask schoolMask) const;
|
||||
[[nodiscard]] bool IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spellInfo) const;
|
||||
virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const;
|
||||
|
||||
// Critic chances
|
||||
bool isBlockCritical();
|
||||
@@ -2082,7 +2087,8 @@ public:
|
||||
|
||||
float m_modAttackSpeedPct[3];
|
||||
|
||||
SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
|
||||
typedef std::unordered_multimap<uint32 /*type*/, uint32 /*spellId*/> SpellImmuneContainer;
|
||||
SpellImmuneContainer m_spellImmune[MAX_SPELL_IMMUNITY];
|
||||
uint32 m_lastSanctuaryTime;
|
||||
|
||||
// pet auras
|
||||
|
||||
@@ -310,6 +310,9 @@ ObjectMgr::ObjectMgr():
|
||||
{
|
||||
_playerClassInfo[i] = nullptr;
|
||||
}
|
||||
|
||||
// Initialize default spawn group
|
||||
_spawnGroupDataStore[0] = {0, "Default Group", 0, SpawnGroupFlags(SPAWNGROUP_FLAG_SYSTEM)};
|
||||
}
|
||||
|
||||
ObjectMgr::~ObjectMgr()
|
||||
@@ -522,18 +525,18 @@ void ObjectMgr::LoadCreatureTemplates()
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, "
|
||||
// 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
||||
"gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, "
|
||||
// 23 24 25 26 27 28 29 30 31 32
|
||||
// 9 10 11 12 13 14 15 16 17 18 19 20 21
|
||||
"gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, `rank`, dmgschool, "
|
||||
// 22 23 24 25 26 27 28 29 30 31
|
||||
"DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, "
|
||||
// 33 34 35 36 37
|
||||
// 32 33 34 35 36
|
||||
"type, type_flags, lootid, pickpocketloot, skinloot, "
|
||||
// 38 39 40 41 42 43 44 45 46 47
|
||||
// 37 38 39 40 41 42 43 44 45 46
|
||||
"PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, "
|
||||
// 48 49 50 51 52 53 54 55 56 57 58 59
|
||||
"ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, "
|
||||
// 60 61 62
|
||||
"spell_school_immune_mask, flags_extra, ScriptName "
|
||||
// 47 48 49 50 51 52 53 54 55 56 57
|
||||
"ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, "
|
||||
// 58 59 60
|
||||
"CreatureImmunitiesId, flags_extra, ScriptName "
|
||||
"FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId ORDER BY entry DESC;");
|
||||
|
||||
if (!result)
|
||||
@@ -619,24 +622,23 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook)
|
||||
creatureTemplate.speed_swim = fields[17].Get<float>();
|
||||
creatureTemplate.speed_flight = fields[18].Get<float>();
|
||||
creatureTemplate.detection_range = fields[19].Get<float>();
|
||||
creatureTemplate.scale = fields[20].Get<float>();
|
||||
creatureTemplate.rank = uint32(fields[21].Get<uint8>());
|
||||
creatureTemplate.dmgschool = uint32(fields[22].Get<int8>());
|
||||
creatureTemplate.DamageModifier = fields[23].Get<float>();
|
||||
creatureTemplate.BaseAttackTime = fields[24].Get<uint32>();
|
||||
creatureTemplate.RangeAttackTime = fields[25].Get<uint32>();
|
||||
creatureTemplate.BaseVariance = fields[26].Get<float>();
|
||||
creatureTemplate.RangeVariance = fields[27].Get<float>();
|
||||
creatureTemplate.unit_class = uint32(fields[28].Get<uint8>());
|
||||
creatureTemplate.unit_flags = fields[29].Get<uint32>();
|
||||
creatureTemplate.unit_flags2 = fields[30].Get<uint32>();
|
||||
creatureTemplate.dynamicflags = fields[31].Get<uint32>();
|
||||
creatureTemplate.family = uint32(fields[32].Get<uint8>());
|
||||
creatureTemplate.type = uint32(fields[33].Get<uint8>());
|
||||
creatureTemplate.type_flags = fields[34].Get<uint32>();
|
||||
creatureTemplate.lootid = fields[35].Get<uint32>();
|
||||
creatureTemplate.pickpocketLootId = fields[36].Get<uint32>();
|
||||
creatureTemplate.SkinLootId = fields[37].Get<uint32>();
|
||||
creatureTemplate.rank = uint32(fields[20].Get<uint8>());
|
||||
creatureTemplate.dmgschool = uint32(fields[21].Get<int8>());
|
||||
creatureTemplate.DamageModifier = fields[22].Get<float>();
|
||||
creatureTemplate.BaseAttackTime = fields[23].Get<uint32>();
|
||||
creatureTemplate.RangeAttackTime = fields[24].Get<uint32>();
|
||||
creatureTemplate.BaseVariance = fields[25].Get<float>();
|
||||
creatureTemplate.RangeVariance = fields[26].Get<float>();
|
||||
creatureTemplate.unit_class = uint32(fields[27].Get<uint8>());
|
||||
creatureTemplate.unit_flags = fields[28].Get<uint32>();
|
||||
creatureTemplate.unit_flags2 = fields[29].Get<uint32>();
|
||||
creatureTemplate.dynamicflags = fields[30].Get<uint32>();
|
||||
creatureTemplate.family = uint32(fields[31].Get<uint8>());
|
||||
creatureTemplate.type = uint32(fields[32].Get<uint8>());
|
||||
creatureTemplate.type_flags = fields[33].Get<uint32>();
|
||||
creatureTemplate.lootid = fields[34].Get<uint32>();
|
||||
creatureTemplate.pickpocketLootId = fields[35].Get<uint32>();
|
||||
creatureTemplate.SkinLootId = fields[36].Get<uint32>();
|
||||
|
||||
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
|
||||
{
|
||||
@@ -648,49 +650,56 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook)
|
||||
creatureTemplate.spells[i] = 0;
|
||||
}
|
||||
|
||||
creatureTemplate.PetSpellDataId = fields[38].Get<uint32>();
|
||||
creatureTemplate.VehicleId = fields[39].Get<uint32>();
|
||||
creatureTemplate.mingold = fields[40].Get<uint32>();
|
||||
creatureTemplate.maxgold = fields[41].Get<uint32>();
|
||||
creatureTemplate.AIName = fields[42].Get<std::string>();
|
||||
creatureTemplate.MovementType = uint32(fields[43].Get<uint8>());
|
||||
if (!fields[44].IsNull())
|
||||
creatureTemplate.PetSpellDataId = fields[37].Get<uint32>();
|
||||
creatureTemplate.VehicleId = fields[38].Get<uint32>();
|
||||
creatureTemplate.mingold = fields[39].Get<uint32>();
|
||||
creatureTemplate.maxgold = fields[40].Get<uint32>();
|
||||
creatureTemplate.AIName = fields[41].Get<std::string>();
|
||||
creatureTemplate.MovementType = uint32(fields[42].Get<uint8>());
|
||||
if (!fields[43].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[44].Get<uint8>());
|
||||
creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[43].Get<uint8>());
|
||||
}
|
||||
|
||||
creatureTemplate.Movement.Swim = fields[45].Get<bool>();
|
||||
if (!fields[46].IsNull())
|
||||
creatureTemplate.Movement.Swim = fields[44].Get<bool>();
|
||||
if (!fields[45].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[46].Get<uint8>());
|
||||
creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[45].Get<uint8>());
|
||||
}
|
||||
|
||||
creatureTemplate.Movement.Rooted = fields[47].Get<bool>();
|
||||
creatureTemplate.Movement.Rooted = fields[46].Get<bool>();
|
||||
if (!fields[47].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[47].Get<uint8>());
|
||||
}
|
||||
if (!fields[48].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[48].Get<uint8>());
|
||||
creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[48].Get<uint8>());
|
||||
}
|
||||
if (!fields[49].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[49].Get<uint8>());
|
||||
}
|
||||
if (!fields[50].IsNull())
|
||||
{
|
||||
creatureTemplate.Movement.InteractionPauseTimer = fields[50].Get<uint32>();
|
||||
creatureTemplate.Movement.InteractionPauseTimer = fields[49].Get<uint32>();
|
||||
}
|
||||
|
||||
creatureTemplate.HoverHeight = fields[51].Get<float>();
|
||||
creatureTemplate.ModHealth = fields[52].Get<float>();
|
||||
creatureTemplate.ModMana = fields[53].Get<float>();
|
||||
creatureTemplate.ModArmor = fields[54].Get<float>();
|
||||
creatureTemplate.ModExperience = fields[55].Get<float>();
|
||||
creatureTemplate.RacialLeader = fields[56].Get<bool>();
|
||||
creatureTemplate.movementId = fields[57].Get<uint32>();
|
||||
creatureTemplate.RegenHealth = fields[58].Get<bool>();
|
||||
creatureTemplate.MechanicImmuneMask = fields[59].Get<uint32>();
|
||||
creatureTemplate.SpellSchoolImmuneMask = fields[60].Get<uint8>();
|
||||
creatureTemplate.flags_extra = fields[61].Get<uint32>();
|
||||
creatureTemplate.ScriptID = GetScriptId(fields[62].Get<std::string>());
|
||||
creatureTemplate.HoverHeight = fields[50].Get<float>();
|
||||
creatureTemplate.ModHealth = fields[51].Get<float>();
|
||||
creatureTemplate.ModMana = fields[52].Get<float>();
|
||||
creatureTemplate.ModArmor = fields[53].Get<float>();
|
||||
creatureTemplate.ModExperience = fields[54].Get<float>();
|
||||
creatureTemplate.RacialLeader = fields[55].Get<bool>();
|
||||
creatureTemplate.movementId = fields[56].Get<uint32>();
|
||||
creatureTemplate.RegenHealth = fields[57].Get<bool>();
|
||||
creatureTemplate.CreatureImmunitiesId = fields[58].Get<int32>();
|
||||
creatureTemplate.flags_extra = fields[59].Get<uint32>();
|
||||
creatureTemplate.ScriptID = GetScriptId(fields[60].Get<std::string>());
|
||||
|
||||
// Warn about deprecated immunity flags that should be moved to `creature_immunities` table
|
||||
if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT)
|
||||
LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit NO_TAUNT (0x100) set. This will be migrated to the `creature_immunities` table in a future update.", entry);
|
||||
if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_AVOID_AOE)
|
||||
LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit AVOID_AOE (0x400000) set. This will be migrated to the `creature_immunities` table in a future update.", entry);
|
||||
if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
|
||||
LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit IMMUNITY_KNOCKBACK (0x40000000) set. This will be migrated to the `creature_immunities` table in a future update.", entry);
|
||||
|
||||
// useful if the creature template load is being triggered from outside this class
|
||||
if (triggerHook)
|
||||
@@ -2394,6 +2403,7 @@ void ObjectMgr::LoadCreatures()
|
||||
data.unit_flags = fields[21].Get<uint32>();
|
||||
data.dynamicflags = fields[22].Get<uint32>();
|
||||
data.ScriptId = GetScriptId(fields[23].Get<std::string>());
|
||||
data.spawnGroupId = 0;
|
||||
|
||||
if (!data.ScriptId)
|
||||
data.ScriptId = cInfo->ScriptID;
|
||||
@@ -2572,6 +2582,7 @@ CreatureData const* ObjectMgr::LoadCreatureDataFromDB(ObjectGuid::LowType spawnI
|
||||
creatureData.unit_flags = fields[19].Get<uint32>();
|
||||
creatureData.dynamicflags = fields[20].Get<uint32>();
|
||||
creatureData.ScriptId = GetScriptId(fields[21].Get<std::string>());
|
||||
creatureData.spawnGroupId = 0;
|
||||
|
||||
if (!creatureData.ScriptId)
|
||||
creatureData.ScriptId = cInfo->ScriptID;
|
||||
@@ -2754,6 +2765,7 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, fl
|
||||
data.phaseMask = PHASEMASK_NORMAL;
|
||||
data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
|
||||
data.dbData = false;
|
||||
data.spawnGroupId = 0;
|
||||
|
||||
AddGameobjectToGrid(spawnId, &data);
|
||||
|
||||
@@ -2909,6 +2921,7 @@ void ObjectMgr::LoadGameobjects()
|
||||
data.rotation.w = fields[10].Get<float>();
|
||||
data.spawntimesecs = fields[11].Get<int32>();
|
||||
data.ScriptId = GetScriptId(fields[18].Get<std::string>());
|
||||
data.spawnGroupId = 0;
|
||||
if (!data.ScriptId)
|
||||
data.ScriptId = gInfo->ScriptId;
|
||||
|
||||
@@ -7160,6 +7173,11 @@ void ObjectMgr::LoadAreaTriggerScripts()
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
uint32 ObjectMgr::GetNearestTaxiNode(WorldLocation const& loc, uint32 teamId)
|
||||
{
|
||||
return GetNearestTaxiNode(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetMapId(), teamId);
|
||||
}
|
||||
|
||||
uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 teamId)
|
||||
{
|
||||
bool found = false;
|
||||
@@ -8672,6 +8690,19 @@ void ObjectMgr::DeleteGOData(ObjectGuid::LowType guid)
|
||||
_gameObjectDataStore.erase(guid);
|
||||
}
|
||||
|
||||
SpawnData const* ObjectMgr::GetSpawnData(SpawnObjectType type, ObjectGuid::LowType spawnId) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SPAWN_TYPE_CREATURE:
|
||||
return GetCreatureData(spawnId);
|
||||
case SPAWN_TYPE_GAMEOBJECT:
|
||||
return GetGameObjectData(spawnId);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, std::string const& table, bool starter, bool go)
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
@@ -9719,6 +9750,7 @@ void ObjectMgr::LoadTrainers()
|
||||
|
||||
// For reload case
|
||||
_trainers.clear();
|
||||
_classTrainers.clear();
|
||||
|
||||
std::unordered_map<int32, std::vector<Trainer::Spell>> spellsByTrainer;
|
||||
if (QueryResult trainerSpellsResult = WorldDatabase.Query("SELECT TrainerId, SpellId, MoneyCost, ReqSkillLine, ReqSkillRank, ReqAbility1, ReqAbility2, ReqAbility3, ReqLevel FROM trainer_spell"))
|
||||
@@ -9795,7 +9827,18 @@ void ObjectMgr::LoadTrainers()
|
||||
spellsByTrainer.erase(spellsItr);
|
||||
}
|
||||
|
||||
_trainers.emplace(std::piecewise_construct, std::forward_as_tuple(trainerId), std::forward_as_tuple(trainerId, trainerType, requirement, std::move(greeting), std::move(spells)));
|
||||
auto [it, isNew] = _trainers.emplace(std::piecewise_construct, std::forward_as_tuple(trainerId), std::forward_as_tuple(trainerId, trainerType, requirement, std::move(greeting), std::move(spells)));
|
||||
ASSERT(isNew);
|
||||
if (trainerType == Trainer::Type::Class)
|
||||
{
|
||||
if (!requirement || requirement >= MAX_CLASSES)
|
||||
LOG_ERROR("sql.sql", "Table `trainer` has invalid class requirement for trainer {}, ignoring");
|
||||
else
|
||||
{
|
||||
uint8 classId = static_cast<uint8>(requirement);
|
||||
_classTrainers[classId].push_back(&it->second);
|
||||
}
|
||||
}
|
||||
} while (trainersResult->NextRow());
|
||||
}
|
||||
|
||||
|
||||
@@ -506,6 +506,7 @@ struct AcoreString
|
||||
typedef std::map<ObjectGuid, ObjectGuid> LinkedRespawnContainer;
|
||||
typedef std::unordered_map<ObjectGuid::LowType, CreatureData> CreatureDataContainer;
|
||||
typedef std::unordered_map<ObjectGuid::LowType, GameObjectData> GameObjectDataContainer;
|
||||
typedef std::unordered_map<uint32, SpawnGroupTemplateData> SpawnGroupDataContainer;
|
||||
typedef std::map<TempSummonGroupKey, std::vector<TempSummonData> > TempSummonDataContainer;
|
||||
typedef std::map<TempSummonGroupKey, std::vector<GameObjectSummonData> > GameObjectSummonDataContainer;
|
||||
typedef std::unordered_map<uint32, CreatureLocale> CreatureLocaleContainer;
|
||||
@@ -811,6 +812,7 @@ public:
|
||||
void GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const;
|
||||
|
||||
uint32 GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 teamId);
|
||||
uint32 GetNearestTaxiNode(WorldLocation const& loc, uint32 teamId);
|
||||
void GetTaxiPath(uint32 source, uint32 destination, uint32& path, uint32& cost);
|
||||
uint32 GetTaxiMountDisplayId(uint32 id, TeamId teamId, bool allowed_alt_team = false);
|
||||
|
||||
@@ -1269,6 +1271,14 @@ public:
|
||||
if (itr == _gameObjectDataStore.end()) return nullptr;
|
||||
return &itr->second;
|
||||
}
|
||||
[[nodiscard]] SpawnData const* GetSpawnData(SpawnObjectType type, ObjectGuid::LowType spawnId) const;
|
||||
|
||||
[[nodiscard]] SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const
|
||||
{
|
||||
auto itr = _spawnGroupDataStore.find(groupId);
|
||||
return itr != _spawnGroupDataStore.end() ? &itr->second : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] CreatureLocale const* GetCreatureLocale(uint32 entry) const
|
||||
{
|
||||
CreatureLocaleContainer::const_iterator itr = _creatureLocaleStore.find(entry);
|
||||
@@ -1419,6 +1429,7 @@ public:
|
||||
bool DeleteGameTele(std::string_view name);
|
||||
|
||||
Trainer::Trainer* GetTrainer(uint32 creatureId);
|
||||
std::vector<Trainer::Trainer const*> const& GetClassTrainers(uint8 classId) const { return _classTrainers.at(classId); }
|
||||
|
||||
[[nodiscard]] VendorItemData const* GetNpcVendorItemList(uint32 entry) const
|
||||
{
|
||||
@@ -1642,6 +1653,7 @@ private:
|
||||
LinkedRespawnContainer _linkedRespawnStore;
|
||||
CreatureLocaleContainer _creatureLocaleStore;
|
||||
GameObjectDataContainer _gameObjectDataStore;
|
||||
SpawnGroupDataContainer _spawnGroupDataStore;
|
||||
GameObjectLocaleContainer _gameObjectLocaleStore;
|
||||
GameObjectTemplateContainer _gameObjectTemplateStore;
|
||||
GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore;
|
||||
@@ -1667,6 +1679,7 @@ private:
|
||||
|
||||
CacheVendorItemContainer _cacheVendorItemStore;
|
||||
std::unordered_map<uint32, Trainer::Trainer> _trainers;
|
||||
std::unordered_map<uint8, std::vector<Trainer::Trainer const*>> _classTrainers;
|
||||
std::unordered_map<uint32, uint32> _creatureDefaultTrainers;
|
||||
|
||||
std::set<uint32> _difficultyEntries[MAX_DIFFICULTY - 1]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate
|
||||
|
||||
@@ -107,6 +107,7 @@ struct Cell
|
||||
template<class T> static void VisitObjects(float x, float y, Map* map, T& visitor, float radius);
|
||||
|
||||
template<class T> static void VisitFarVisibleObjects(WorldObject const* obj, T& visitor, float radius);
|
||||
template<class T> static void VisitFarVisibleObjects(float x, float y, Map* map, T& visitor, float radius);
|
||||
|
||||
private:
|
||||
template<class T, class CONTAINER> void VisitCircle(TypeContainerVisitor<T, CONTAINER>&, Map&, CellCoord const&, CellCoord const&) const;
|
||||
|
||||
@@ -164,11 +164,7 @@ inline void Cell::VisitCircle(TypeContainerVisitor<T, CONTAINER>& visitor, Map&
|
||||
template<class T>
|
||||
inline void Cell::VisitObjects(WorldObject const* center_obj, T& visitor, float radius)
|
||||
{
|
||||
CellCoord p(Acore::ComputeCellCoord(center_obj->GetPositionX(), center_obj->GetPositionY()));
|
||||
Cell cell(p);
|
||||
|
||||
TypeContainerVisitor<T, GridTypeMapContainer> gnotifier(visitor);
|
||||
cell.Visit(p, gnotifier, *center_obj->GetMap(), *center_obj, radius);
|
||||
VisitObjects(center_obj->GetPositionX(), center_obj->GetPositionY(), center_obj->GetMap(), visitor, radius);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -184,11 +180,17 @@ inline void Cell::VisitObjects(float x, float y, Map* map, T& visitor, float rad
|
||||
template<class T>
|
||||
inline void Cell::VisitFarVisibleObjects(WorldObject const* center_obj, T& visitor, float radius)
|
||||
{
|
||||
CellCoord p(Acore::ComputeCellCoord(center_obj->GetPositionX(), center_obj->GetPositionY()));
|
||||
VisitFarVisibleObjects(center_obj->GetPositionX(), center_obj->GetPositionY(), center_obj->GetMap(), visitor, radius);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void Cell::VisitFarVisibleObjects(float x, float y, Map* map, T& visitor, float radius)
|
||||
{
|
||||
CellCoord p(Acore::ComputeCellCoord(x, y));
|
||||
Cell cell(p);
|
||||
|
||||
TypeContainerVisitor<T, FarVisibleGridContainer> gnotifier(visitor);
|
||||
cell.Visit(p, gnotifier, *center_obj->GetMap(), *center_obj, radius);
|
||||
cell.Visit(p, gnotifier, *map, x, y, radius);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "DisableMgr.h"
|
||||
#include "GridTerrainLoader.h"
|
||||
#include "MMapFactory.h"
|
||||
#include "IVMapMgr.h"
|
||||
#include "Map.h"
|
||||
#include "MMapMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "VMapFactory.h"
|
||||
@@ -9,6 +9,7 @@
|
||||
void GridTerrainLoader::LoadTerrain()
|
||||
{
|
||||
LoadMap();
|
||||
|
||||
if (_map->GetInstanceId() == 0)
|
||||
{
|
||||
LoadVMap();
|
||||
@@ -18,13 +19,10 @@ void GridTerrainLoader::LoadTerrain()
|
||||
|
||||
void GridTerrainLoader::LoadMap()
|
||||
{
|
||||
// Instances will point to the parent maps terrain data
|
||||
// Instances will point to the parent maps terrain data, no need to load anything.
|
||||
if (_map->GetInstanceId() != 0)
|
||||
{
|
||||
// load grid map for base map
|
||||
Map* parentMap = const_cast<Map*>(_map->GetParent());
|
||||
|
||||
// GetGridTerrainData will create the parent map grid
|
||||
_grid.SetTerrainData(parentMap->GetGridTerrainDataSharedPtr(GridCoord(_grid.GetX(), _grid.GetY())));
|
||||
return;
|
||||
}
|
||||
@@ -51,7 +49,7 @@ void GridTerrainLoader::LoadMap()
|
||||
|
||||
void GridTerrainLoader::LoadVMap()
|
||||
{
|
||||
int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapMgr()->loadMap((sWorld->GetDataPath() + "vmaps").c_str(), _map->GetId(), _grid.GetX(), _grid.GetY());
|
||||
int const vmapLoadResult = _map->GetMapCollisionData().LoadVMapTile(_grid.GetX(), _grid.GetY());
|
||||
switch (vmapLoadResult)
|
||||
{
|
||||
case VMAP::VMAP_LOAD_RESULT_OK:
|
||||
@@ -66,15 +64,14 @@ void GridTerrainLoader::LoadVMap()
|
||||
LOG_DEBUG("maps", "Ignored VMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})",
|
||||
_map->GetMapName(), _map->GetId(), _grid.GetX(), _grid.GetY(), _grid.GetX(), _grid.GetY());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GridTerrainLoader::LoadMMap()
|
||||
{
|
||||
if (!DisableMgr::IsPathfindingEnabled(_map))
|
||||
return;
|
||||
|
||||
int mmapLoadResult = MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(_map->GetId(), _grid.GetX(), _grid.GetY());
|
||||
int const mmapLoadResult = _map->GetMapCollisionData().LoadMMapTile(_grid.GetX(), _grid.GetY());
|
||||
switch (mmapLoadResult)
|
||||
{
|
||||
case MMAP::MMAP_LOAD_RESULT_OK:
|
||||
@@ -89,6 +86,8 @@ void GridTerrainLoader::LoadMMap()
|
||||
LOG_DEBUG("maps", "Ignored MMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})",
|
||||
_map->GetMapName(), _map->GetId(), _grid.GetX(), _grid.GetY(), _grid.GetX(), _grid.GetY());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,13 +144,3 @@ bool GridTerrainLoader::ExistVMap(uint32 mapid, int gx, int gy)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GridTerrainUnloader::UnloadTerrain()
|
||||
{
|
||||
// Only parent maps manage terrain data
|
||||
if (_map->GetInstanceId() != 0)
|
||||
return;
|
||||
|
||||
VMAP::VMapFactory::createOrGetVMapMgr()->unloadMap(_map->GetId(), _grid.GetX(), _grid.GetY());
|
||||
MMAP::MMapFactory::createOrGetMMapMgr()->unloadMap(_map->GetId(), _grid.GetX(), _grid.GetY());
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "GridDefines.h"
|
||||
|
||||
class Map;
|
||||
|
||||
class GridTerrainLoader
|
||||
{
|
||||
public:
|
||||
@@ -40,17 +42,4 @@ private:
|
||||
Map* _map;
|
||||
};
|
||||
|
||||
class GridTerrainUnloader
|
||||
{
|
||||
public:
|
||||
GridTerrainUnloader(MapGridType& grid, Map* map)
|
||||
: _grid(grid), _map(map) { }
|
||||
|
||||
void UnloadTerrain();
|
||||
|
||||
private:
|
||||
MapGridType& _grid;
|
||||
Map* _map;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,9 +8,19 @@ void MapGridManager::CreateGrid(uint16 const x, uint16 const y)
|
||||
if (IsGridCreated(x, y))
|
||||
return;
|
||||
|
||||
// If we are an instance, ensure parent map has already created the grid before proceeding.
|
||||
// Note: grid loading is locked and is safe across multiple map threads in the
|
||||
// event of multiple child maps attempting to create the same parent map grid
|
||||
if (_map->GetInstanceId() != 0)
|
||||
{
|
||||
Map* parentMap = const_cast<Map*>(_map->GetParent());
|
||||
parentMap->EnsureGridCreated(GridCoord(x, y));
|
||||
}
|
||||
|
||||
std::unique_ptr<MapGridType> grid = std::make_unique<MapGridType>(x, y);
|
||||
grid->link(_map);
|
||||
|
||||
// Terrain is loading during create (should/can we move this to LoadGrid?)
|
||||
GridTerrainLoader loader(*grid, _map);
|
||||
loader.LoadTerrain();
|
||||
|
||||
@@ -19,6 +29,7 @@ void MapGridManager::CreateGrid(uint16 const x, uint16 const y)
|
||||
++_createdGridsCount;
|
||||
}
|
||||
|
||||
// Loads objects, terrain already loaded in CreateGrid
|
||||
bool MapGridManager::LoadGrid(uint16 const x, uint16 const y)
|
||||
{
|
||||
MapGridType* grid = GetGrid(x, y);
|
||||
@@ -55,9 +66,6 @@ void MapGridManager::UnloadGrid(uint16 const x, uint16 const y)
|
||||
grid->VisitAllCells(visitor);
|
||||
}
|
||||
|
||||
GridTerrainUnloader terrainUnloader(*grid, _map);
|
||||
terrainUnloader.UnloadTerrain();
|
||||
|
||||
_mapGrid[x][y] = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ void MessageDistDeliverer::Visit(VisiblePlayersMap const& m)
|
||||
for (auto const& kvPair : m)
|
||||
{
|
||||
Player const* target = kvPair.second;
|
||||
if (i_distSq != 0.0f && target->m_seer->GetExactDist2dSq(i_source) > i_distSq)
|
||||
if (i_distSq != 0.0f && target->GetSightPosition().GetExactDist2dSq(i_source) > i_distSq)
|
||||
continue;
|
||||
|
||||
// @todo: Might not need this check anymore
|
||||
|
||||
@@ -1384,7 +1384,7 @@ namespace Acore
|
||||
AnyPlayerExactPositionInGameObjectRangeCheck(GameObject const* go, float range) : _go(go), _range(range) {}
|
||||
bool operator()(Player* u)
|
||||
{
|
||||
if (!_go->IsInRange(u->GetPositionX(), u->GetPositionY(), u->GetPositionZ(), _range))
|
||||
if (!_go->IsInRange3d(u->GetPositionX(), u->GetPositionY(), u->GetPositionZ(), _range))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -1604,7 +1604,7 @@ namespace Acore
|
||||
bool operator() (GameObject* go)
|
||||
{
|
||||
if (!entry || (go->GetGOInfo() && go->GetGOInfo()->entry == entry))
|
||||
return go->IsInRange(x, y, z, range);
|
||||
return go->IsInRange3d(x, y, z, range);
|
||||
else return false;
|
||||
}
|
||||
private:
|
||||
|
||||
@@ -868,9 +868,9 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
|
||||
if (ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass()))
|
||||
{
|
||||
if (cEntry->CinematicSequence)
|
||||
pCurrChar->SendCinematicStart(cEntry->CinematicSequence);
|
||||
pCurrChar->GetCinematicMgr().StartCinematic(cEntry->CinematicSequence);
|
||||
else if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()))
|
||||
pCurrChar->SendCinematicStart(rEntry->CinematicSequence);
|
||||
pCurrChar->GetCinematicMgr().StartCinematic(rEntry->CinematicSequence);
|
||||
|
||||
// send new char string if not empty
|
||||
std::string_view newCharString = sWorld->getStringConfig(CONFIG_NEW_CHAR_STRING);
|
||||
|
||||
@@ -874,7 +874,11 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry)
|
||||
// Stop the npc if moving
|
||||
if (uint32 pause = vendor->GetMovementTemplate().GetInteractionPauseTimer())
|
||||
vendor->PauseMovement(pause);
|
||||
vendor->SetHomePosition(vendor->GetPosition());
|
||||
|
||||
// Update home position for patrolling NPCs only (prevents drift for stationary NPCs)
|
||||
if (vendor->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE ||
|
||||
vendor->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
|
||||
vendor->SetHomePosition(vendor->GetPosition());
|
||||
|
||||
SetCurrentVendor(vendorEntry);
|
||||
|
||||
|
||||
@@ -732,7 +732,8 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
|
||||
if (player->isDebugAreaTriggers)
|
||||
ChatHandler(this).PSendSysMessage(LANG_DEBUG_AREATRIGGER_REACHED, triggerId);
|
||||
|
||||
if (sScriptMgr->OnAreaTrigger(player, atEntry))
|
||||
// Skip areatrigger scripts for GMs unless debug areatriggers is enabled
|
||||
if ((!player->IsGameMaster() || player->isDebugAreaTriggers) && sScriptMgr->OnAreaTrigger(player, atEntry))
|
||||
return;
|
||||
|
||||
if (player->IsAlive())
|
||||
@@ -947,14 +948,14 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data)
|
||||
|
||||
void WorldSession::HandleCompleteCinematic(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
// If player has sight bound to visual waypoint NPC we should remove it
|
||||
GetPlayer()->GetCinematicMgr()->EndCinematic();
|
||||
// End the current cinematic and restore the normal player view
|
||||
GetPlayer()->GetCinematicMgr().EndCinematic();
|
||||
}
|
||||
|
||||
void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recv_data*/)
|
||||
{
|
||||
// Sent by client when cinematic actually begun. So we begin the server side process
|
||||
GetPlayer()->GetCinematicMgr()->BeginCinematic();
|
||||
GetPlayer()->GetCinematicMgr().StartCinematicCamera();
|
||||
}
|
||||
|
||||
void WorldSession::HandleSetActionBarToggles(WorldPacket& recv_data)
|
||||
|
||||
@@ -162,7 +162,11 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData)
|
||||
// Stop the npc if moving
|
||||
if (uint32 pause = unit->GetMovementTemplate().GetInteractionPauseTimer())
|
||||
unit->PauseMovement(pause);
|
||||
unit->SetHomePosition(unit->GetPosition());
|
||||
|
||||
// Update home position for patrolling NPCs only (prevents drift for stationary NPCs)
|
||||
if (unit->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE ||
|
||||
unit->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
|
||||
unit->SetHomePosition(unit->GetPosition());
|
||||
|
||||
// If spiritguide, no need for gossip menu, just put player into resurrect queue
|
||||
if (unit->IsSpiritGuide())
|
||||
|
||||
@@ -96,7 +96,11 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData)
|
||||
// Stop the npc if moving
|
||||
if (uint32 pause = creature->GetMovementTemplate().GetInteractionPauseTimer())
|
||||
creature->PauseMovement(pause);
|
||||
creature->SetHomePosition(creature->GetPosition());
|
||||
|
||||
// Update home position for patrolling NPCs only (prevents drift for stationary NPCs)
|
||||
if (creature->GetDefaultMovementType() == WAYPOINT_MOTION_TYPE ||
|
||||
creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
|
||||
creature->SetHomePosition(creature->GetPosition());
|
||||
|
||||
if (sScriptMgr->OnGossipHello(_player, creature))
|
||||
return;
|
||||
|
||||
@@ -43,7 +43,7 @@ void WorldSession::SendTaxiStatus(ObjectGuid guid)
|
||||
}
|
||||
|
||||
// find taxi node
|
||||
uint32 nearest = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), player->GetTeamId());
|
||||
uint32 nearest = sObjectMgr->GetNearestTaxiNode(*unit, player->GetTeamId(true));
|
||||
if (!nearest)
|
||||
{
|
||||
return;
|
||||
@@ -84,7 +84,7 @@ void WorldSession::HandleTaxiQueryAvailableNodes(WorldPacket& recvData)
|
||||
void WorldSession::SendTaxiMenu(Creature* unit)
|
||||
{
|
||||
// find current node
|
||||
uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeamId());
|
||||
uint32 curloc = sObjectMgr->GetNearestTaxiNode(*unit, GetPlayer()->GetTeamId(true));
|
||||
|
||||
if (curloc == 0)
|
||||
return;
|
||||
@@ -127,7 +127,7 @@ void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathN
|
||||
bool WorldSession::SendLearnNewTaxiNode(Creature* unit)
|
||||
{
|
||||
// find current node
|
||||
uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeamId());
|
||||
uint32 curloc = sObjectMgr->GetNearestTaxiNode(*unit, GetPlayer()->GetTeamId(true));
|
||||
|
||||
if (curloc == 0)
|
||||
return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result.
|
||||
|
||||
@@ -134,6 +134,11 @@ void InstanceScript::HandleGameObject(ObjectGuid GUID, bool open, GameObject* go
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceScript::HandleGameObject(uint32 type, bool open)
|
||||
{
|
||||
HandleGameObject(ObjectGuid::Empty, open, GetGameObject(type));
|
||||
}
|
||||
|
||||
bool InstanceScript::IsEncounterInProgress() const
|
||||
{
|
||||
for (std::vector<BossInfo>::const_iterator itr = bosses.begin(); itr != bosses.end(); ++itr)
|
||||
|
||||
@@ -193,11 +193,21 @@ public:
|
||||
//Called when a player enters/leaves water bodies.
|
||||
virtual void OnPlayerInWaterStateUpdate(Player* /*player*/, bool /*inWater*/) {}
|
||||
|
||||
//Handle open / close objects
|
||||
//use HandleGameObject(ObjectGuid::Empty, boolen, GO); in OnObjectCreate in instance scripts
|
||||
//use HandleGameObject(GUID, boolen, nullptr); in any other script
|
||||
/**
|
||||
* @brief Open or close a GameObject by GUID.
|
||||
* @param guid The GUID of the GameObject. Pass ObjectGuid::Empty when providing the go pointer directly.
|
||||
* @param open true to open (GO_STATE_ACTIVE), false to close (GO_STATE_READY).
|
||||
* @param go Optional pointer to the GameObject. If nullptr, the object is looked up by GUID.
|
||||
*/
|
||||
void HandleGameObject(ObjectGuid guid, bool open, GameObject* go = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Open or close a GameObject registered via LoadObjectData.
|
||||
* @param type The ObjectData type constant (e.g. GO_FRONT_DOOR) used in the gameObjectData array.
|
||||
* @param open true to open (GO_STATE_ACTIVE), false to close (GO_STATE_READY).
|
||||
*/
|
||||
void HandleGameObject(uint32 type, bool open);
|
||||
|
||||
//change active state of doors or buttons
|
||||
void DoUseDoorOrButton(ObjectGuid guid, uint32 withRestoreTime = 0, bool useAlternativeState = false);
|
||||
|
||||
|
||||
@@ -418,83 +418,34 @@ bool LootItem::AllowedForPlayer(Player const* player, ObjectGuid source) const
|
||||
{
|
||||
ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid);
|
||||
if (!pProto)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_LOOT, itemid, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isMasterLooter = player->GetGroup() && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID();
|
||||
bool itemVisibleForMasterLooter = !needs_quest && (!follow_loot_rules || !is_underthreshold);
|
||||
|
||||
// DB conditions check
|
||||
if (!sConditionMgr->IsObjectMeetToConditions(const_cast<Player*>(player), conditions))
|
||||
{
|
||||
// Master Looter can see conditioned recipes
|
||||
if (isMasterLooter && itemVisibleForMasterLooter)
|
||||
{
|
||||
if (pProto->HasFlag(ITEM_FLAG_HIDE_UNUSABLE_RECIPE) || (pProto->Class == ITEM_CLASS_RECIPE && pProto->Bonding == BIND_WHEN_PICKED_UP && pProto->Spells[1].SpellId != 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// not show loot for not own team
|
||||
if (pProto->HasFlag2(ITEM_FLAG2_FACTION_HORDE) && player->GetTeamId(true) != TEAM_HORDE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pProto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeamId(true) != TEAM_ALLIANCE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Master looter can see all items even if the character can't loot them
|
||||
if (isMasterLooter && itemVisibleForMasterLooter)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't allow loot for players without profession or those who already know the recipe
|
||||
// profession / recipe checks
|
||||
if (pProto->HasFlag(ITEM_FLAG_HIDE_UNUSABLE_RECIPE) && (!player->HasSkill(pProto->RequiredSkill) || player->HasSpell(pProto->Spells[1].SpellId)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow to loot soulbound recipes that the player has already learned
|
||||
if (pProto->Class == ITEM_CLASS_RECIPE && pProto->Bonding == BIND_WHEN_PICKED_UP && pProto->Spells[1].SpellId != 0 && player->HasSpell(pProto->Spells[1].SpellId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check quest requirements
|
||||
if (!pProto->HasFlagCu(ITEM_FLAGS_CU_IGNORE_QUEST_STATUS))
|
||||
{
|
||||
// Don't drop quest items if the player is missing the relevant quest
|
||||
if (needs_quest && !player->HasQuestForItem(itemid))
|
||||
return false;
|
||||
|
||||
// for items that start quests
|
||||
if (pProto->StartQuest)
|
||||
{
|
||||
// Don't drop the item if the player has already finished the quest OR player already has the item in their inventory, and that item is unique OR the player has not finished a prerequisite quest
|
||||
uint32 prevQuestId = sObjectMgr->GetQuestTemplate(pProto->StartQuest) ? sObjectMgr->GetQuestTemplate(pProto->StartQuest)->GetPrevQuestId() : 0;
|
||||
if (player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE || (player->HasItemCount(itemid, pProto->MaxCount) && pProto->MaxCount) || (prevQuestId && !player->GetQuestRewardStatus(prevQuestId)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (needs_quest && !pProto->HasFlagCu(ITEM_FLAGS_CU_IGNORE_QUEST_STATUS) && !player->HasQuestForItem(itemid))
|
||||
return false;
|
||||
|
||||
if (!sScriptMgr->OnAllowedForPlayerLootCheck(player, source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -691,7 +642,7 @@ QuestItemList* Loot::FillQuestLoot(Player* player)
|
||||
|
||||
QuestItemList* ql = new QuestItemList();
|
||||
|
||||
Player* lootOwner = (roundRobinPlayer) ? ObjectAccessor::FindPlayer(roundRobinPlayer) : player;
|
||||
bool isMasterLooter = player->GetGroup() && player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID();
|
||||
|
||||
for (uint8 i = 0; i < quest_items.size(); ++i)
|
||||
{
|
||||
@@ -699,32 +650,33 @@ QuestItemList* Loot::FillQuestLoot(Player* player)
|
||||
|
||||
sScriptMgr->OnPlayerBeforeFillQuestLootItem(player, item);
|
||||
|
||||
// Quest item is not free for all and is already assigned to another player
|
||||
// or player doesn't need it
|
||||
if (item.is_blocked || !item.AllowedForPlayer(player, sourceWorldObjectGUID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
bool allowed = item.AllowedForPlayer(player, sourceWorldObjectGUID);
|
||||
|
||||
// Player is not the loot owner, and loot owner still needs this quest item
|
||||
if (!item.freeforall &&
|
||||
lootOwner && lootOwner != player &&
|
||||
item.AllowedForPlayer(lootOwner, sourceWorldObjectGUID))
|
||||
{
|
||||
if (!allowed && !isMasterLooter)
|
||||
continue;
|
||||
}
|
||||
|
||||
ql->push_back(QuestItem(i));
|
||||
++unlootedCount;
|
||||
|
||||
if (!item.freeforall)
|
||||
// Only add "allowed looter" if you are actually allowed to loot.
|
||||
if (allowed)
|
||||
{
|
||||
item.is_blocked = true;
|
||||
item.AddAllowedLooter(player);
|
||||
|
||||
if (item.freeforall)
|
||||
{
|
||||
++unlootedCount;
|
||||
}
|
||||
else if (!item.is_counted)
|
||||
{
|
||||
++unlootedCount;
|
||||
item.is_counted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ql->empty())
|
||||
{
|
||||
delete ql;
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "MapInstanced.h"
|
||||
#include "Metric.h"
|
||||
#include "MiscPackets.h"
|
||||
#include "MMapFactory.h"
|
||||
#include "Object.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
@@ -62,13 +61,12 @@ Map::~Map()
|
||||
|
||||
if (!m_scriptSchedule.empty())
|
||||
sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
|
||||
|
||||
MMAP::MMapFactory::createOrGetMMapMgr()->unloadMapInstance(GetId(), i_InstanceId);
|
||||
}
|
||||
|
||||
Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) :
|
||||
_mapGridManager(this), i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId),
|
||||
m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), _instanceResetPeriod(0),
|
||||
_mapGridManager(this), i_mapEntry(sMapStore.LookupEntry(id)), _mapCollisionData(*this, _parent),
|
||||
i_spawnMode(SpawnMode), i_InstanceId(InstanceId), m_unloadTimer(0),
|
||||
m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), _instanceResetPeriod(0),
|
||||
_transportsUpdateIter(_transports.end()), i_scriptLock(false), _defaultLight(GetDefaultMapLight(id))
|
||||
{
|
||||
m_parentMap = (_parent ? _parent : this);
|
||||
@@ -432,7 +430,7 @@ void Map::UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone)
|
||||
void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
|
||||
{
|
||||
if (t_diff)
|
||||
_dynamicTree.update(t_diff);
|
||||
_mapCollisionData.GetDynamicTree().update(t_diff);
|
||||
|
||||
// Update world sessions and players
|
||||
for (m_mapRefIter = m_mapRefMgr.begin(); m_mapRefIter != m_mapRefMgr.end(); ++m_mapRefIter)
|
||||
@@ -1037,9 +1035,10 @@ void Map::UnloadAll()
|
||||
|
||||
for (GridRefMgr<MapGridType>::iterator i = GridRefMgr<MapGridType>::begin(); i != GridRefMgr<MapGridType>::end();)
|
||||
{
|
||||
MapGridType& grid(*i->GetSource());
|
||||
MapGridType* grid = i->GetSource();
|
||||
++i;
|
||||
UnloadGrid(grid); // deletes the grid and removes it from the GridRefMgr
|
||||
if (grid)
|
||||
UnloadGrid(*grid);
|
||||
}
|
||||
|
||||
// pussywizard: crashfix, some npc can be left on transport (not a default passenger)
|
||||
@@ -1073,9 +1072,9 @@ void Map::UnloadAll()
|
||||
|
||||
std::shared_ptr<GridTerrainData> Map::GetGridTerrainDataSharedPtr(GridCoord const& gridCoord)
|
||||
{
|
||||
// ensure GridMap is created
|
||||
EnsureGridCreated(gridCoord);
|
||||
return _mapGridManager.GetGrid(gridCoord.x_coord, gridCoord.y_coord)->GetTerrainDataSharedPtr();
|
||||
MapGridType* grid = _mapGridManager.GetGrid(gridCoord.x_coord, gridCoord.y_coord);
|
||||
ASSERT(grid); // Grid should always exist during this call
|
||||
return grid->GetTerrainDataSharedPtr();
|
||||
}
|
||||
|
||||
GridTerrainData* Map::GetGridTerrainData(GridCoord const& gridCoord)
|
||||
@@ -1152,10 +1151,7 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float
|
||||
|
||||
float vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
|
||||
if (checkVMap)
|
||||
{
|
||||
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
|
||||
vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); // look from a bit higher pos to find the floor
|
||||
}
|
||||
vmapHeight = _mapCollisionData.GetStaticTree().getHeight(x, y, z, maxSearchDist); // look from a bit higher pos to find the floor
|
||||
|
||||
// mapHeight set for any above raw ground Z or <= INVALID_HEIGHT
|
||||
// vmapheight set for any under Z value or <= INVALID_HEIGHT
|
||||
@@ -1203,12 +1199,11 @@ static inline bool IsInWMOInterior(uint32 mogpFlags)
|
||||
bool Map::GetAreaInfo(uint32 phaseMask, float x, float y, float z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
|
||||
{
|
||||
float check_z = z;
|
||||
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
|
||||
VMAP::AreaAndLiquidData vdata;
|
||||
VMAP::AreaAndLiquidData ddata;
|
||||
|
||||
bool hasVmapAreaInfo = vmgr->GetAreaAndLiquidData(GetId(), x, y, z, {}, vdata) && vdata.areaInfo.has_value();
|
||||
bool hasDynamicAreaInfo = _dynamicTree.GetAreaAndLiquidData(x, y, z, phaseMask, {}, ddata) && ddata.areaInfo.has_value();
|
||||
bool hasVmapAreaInfo = _mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, {}, vdata) && vdata.areaInfo.has_value();
|
||||
bool hasDynamicAreaInfo = _mapCollisionData.GetDynamicTree().GetAreaAndLiquidData(x, y, z, phaseMask, {}, ddata) && ddata.areaInfo.has_value();
|
||||
auto useVmap = [&] { check_z = vdata.floorZ; groupId = vdata.areaInfo->groupId; adtId = vdata.areaInfo->adtId; rootId = vdata.areaInfo->rootId; flags = vdata.areaInfo->mogpFlags; };
|
||||
auto useDyn = [&] { check_z = ddata.floorZ; groupId = ddata.areaInfo->groupId; adtId = ddata.areaInfo->adtId; rootId = ddata.areaInfo->rootId; flags = ddata.areaInfo->mogpFlags; };
|
||||
if (hasVmapAreaInfo)
|
||||
@@ -1299,10 +1294,9 @@ LiquidData const Map::GetLiquidData(uint32 phaseMask, float x, float y, float z,
|
||||
LiquidData liquidData;
|
||||
liquidData.Status = LIQUID_MAP_NO_WATER;
|
||||
|
||||
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
|
||||
VMAP::AreaAndLiquidData vmapData;
|
||||
bool useGridLiquid = true;
|
||||
if (vmgr->GetAreaAndLiquidData(GetId(), x, y, z, ReqLiquidType, vmapData) && vmapData.liquidInfo)
|
||||
if (_mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, ReqLiquidType, vmapData) && vmapData.liquidInfo)
|
||||
{
|
||||
useGridLiquid = !vmapData.areaInfo || !IsInWMOInterior(vmapData.areaInfo->mogpFlags);
|
||||
LOG_DEBUG("maps", "GetLiquidStatus(): vmap liquid level: {} ground: {} type: {}", vmapData.liquidInfo->level, vmapData.floorZ, vmapData.liquidInfo->type);
|
||||
@@ -1382,11 +1376,10 @@ void Map::GetFullTerrainStatusForPosition(uint32 /*phaseMask*/, float x, float y
|
||||
{
|
||||
GridTerrainData* gmap = GetGridTerrainData(x, y);
|
||||
|
||||
VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr();
|
||||
VMAP::AreaAndLiquidData vmapData;
|
||||
// VMAP::AreaAndLiquidData dynData;
|
||||
VMAP::AreaAndLiquidData* wmoData = nullptr;
|
||||
vmgr->GetAreaAndLiquidData(GetId(), x, y, z, reqLiquidType, vmapData);
|
||||
_mapCollisionData.GetStaticTree().GetAreaAndLiquidData(x, y, z, reqLiquidType, vmapData);
|
||||
// _dynamicTree.GetAreaAndLiquidData(x, y, z, phaseMask, reqLiquidType, dynData);
|
||||
|
||||
uint32 gridAreaId = 0;
|
||||
@@ -1547,7 +1540,7 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa
|
||||
}
|
||||
}
|
||||
|
||||
if ((checks & LINEOFSIGHT_CHECK_VMAP) && !VMAP::VMapFactory::createOrGetVMapMgr()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2, ignoreFlags))
|
||||
if ((checks & LINEOFSIGHT_CHECK_VMAP) && !_mapCollisionData.GetStaticTree().isInLineOfSight(x1, y1, z1, x2, y2, z2, ignoreFlags))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1560,7 +1553,7 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa
|
||||
ignoreFlags = VMAP::ModelIgnoreFlags::M2;
|
||||
}
|
||||
|
||||
if (!_dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask, ignoreFlags))
|
||||
if (!_mapCollisionData.GetDynamicTree().isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask, ignoreFlags))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1569,25 +1562,11 @@ bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, floa
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist)
|
||||
{
|
||||
G3D::Vector3 startPos(x1, y1, z1);
|
||||
G3D::Vector3 dstPos(x2, y2, z2);
|
||||
|
||||
G3D::Vector3 resultPos;
|
||||
bool result = _dynamicTree.GetObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist);
|
||||
|
||||
rx = resultPos.x;
|
||||
ry = resultPos.y;
|
||||
rz = resultPos.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
|
||||
{
|
||||
float h1, h2;
|
||||
h1 = GetHeight(x, y, z, vmap, maxSearchDist);
|
||||
h2 = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask);
|
||||
h2 = _mapCollisionData.GetDynamicTree().getHeight(x, y, z, maxSearchDist, phasemask);
|
||||
return std::max<float>(h1, h2);
|
||||
}
|
||||
|
||||
@@ -3056,8 +3035,7 @@ bool Map::CheckCollisionAndGetValidCoords(WorldObject const* source, float start
|
||||
// Unit is not on the ground, check for potential collision via vmaps
|
||||
if (notOnGround)
|
||||
{
|
||||
bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(source->GetMapId(),
|
||||
startX, startY, startZ + halfHeight,
|
||||
bool col = _mapCollisionData.GetStaticTree().GetObjectHitPos(startX, startY, startZ + halfHeight,
|
||||
destX, destY, destZ + halfHeight,
|
||||
destX, destY, destZ, -CONTACT_DISTANCE);
|
||||
|
||||
@@ -3071,7 +3049,7 @@ bool Map::CheckCollisionAndGetValidCoords(WorldObject const* source, float start
|
||||
}
|
||||
|
||||
// check dynamic collision
|
||||
bool col = source->GetMap()->GetObjectHitPos(source->GetPhaseMask(),
|
||||
bool col = _mapCollisionData.GetDynamicTree().GetObjectHitPos(source->GetPhaseMask(),
|
||||
startX, startY, startZ + halfHeight,
|
||||
destX, destY, destZ + halfHeight,
|
||||
destX, destY, destZ, -CONTACT_DISTANCE);
|
||||
|
||||
+11
-11
@@ -27,6 +27,7 @@
|
||||
#include "GameObjectModel.h"
|
||||
#include "GridDefines.h"
|
||||
#include "GridRefMgr.h"
|
||||
#include "MapCollisionData.h"
|
||||
#include "MapGridManager.h"
|
||||
#include "MapRefMgr.h"
|
||||
#include "ObjectDefines.h"
|
||||
@@ -230,8 +231,6 @@ public:
|
||||
|
||||
[[nodiscard]] Map const* GetParent() const { return m_parentMap; }
|
||||
|
||||
// pussywizard: movemaps, mmaps
|
||||
[[nodiscard]] std::shared_mutex& GetMMapLock() const { return *(const_cast<std::shared_mutex*>(&MMapLock)); }
|
||||
// pussywizard:
|
||||
std::unordered_set<Unit*> i_objectsForDelayedVisibility;
|
||||
void HandleDelayedVisibility();
|
||||
@@ -389,15 +388,14 @@ public:
|
||||
bool CanReachPositionAndGetValidCoords(WorldObject const* source, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const;
|
||||
bool CanReachPositionAndGetValidCoords(WorldObject const* source, float startX, float startY, float startZ, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const;
|
||||
bool CheckCollisionAndGetValidCoords(WorldObject const* source, float startX, float startY, float startZ, float &destX, float &destY, float &destZ, bool failOnCollision = true) const;
|
||||
void Balance() { _dynamicTree.balance(); }
|
||||
void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); }
|
||||
void InsertGameObjectModel(const GameObjectModel& model) { _dynamicTree.insert(model); }
|
||||
[[nodiscard]] bool ContainsGameObjectModel(const GameObjectModel& model) const { return _dynamicTree.contains(model);}
|
||||
[[nodiscard]] DynamicMapTree const& GetDynamicMapTree() const { return _dynamicTree; }
|
||||
bool GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist);
|
||||
void Balance() { _mapCollisionData.GetDynamicTree().balance(); }
|
||||
void RemoveGameObjectModel(const GameObjectModel& model) { _mapCollisionData.GetDynamicTree().remove(model); }
|
||||
void InsertGameObjectModel(const GameObjectModel& model) { _mapCollisionData.GetDynamicTree().insert(model); }
|
||||
[[nodiscard]] bool ContainsGameObjectModel(const GameObjectModel& model) const { return _mapCollisionData.GetDynamicTree().contains(model);}
|
||||
[[nodiscard]] DynamicMapTree const& GetDynamicMapTree() const { return _mapCollisionData.GetDynamicTree(); }
|
||||
[[nodiscard]] float GetGameObjectFloor(uint32 phasemask, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const
|
||||
{
|
||||
return _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask);
|
||||
return _mapCollisionData.GetDynamicTree().getHeight(x, y, z, maxSearchDist, phasemask);
|
||||
}
|
||||
/*
|
||||
RESPAWN TIMES
|
||||
@@ -524,6 +522,9 @@ public:
|
||||
return 0;
|
||||
};
|
||||
|
||||
MapCollisionData& GetMapCollisionData() { return _mapCollisionData; }
|
||||
MapCollisionData const& GetMapCollisionData() const { return _mapCollisionData; }
|
||||
|
||||
private:
|
||||
|
||||
template<class T> void InitializeObject(T* obj);
|
||||
@@ -551,15 +552,14 @@ protected:
|
||||
void AddToGrid(T* object, Cell const& cell);
|
||||
|
||||
std::mutex Lock;
|
||||
std::shared_mutex MMapLock;
|
||||
|
||||
MapGridManager _mapGridManager;
|
||||
MapEntry const* i_mapEntry;
|
||||
MapCollisionData _mapCollisionData;
|
||||
uint8 i_spawnMode;
|
||||
uint32 i_InstanceId;
|
||||
uint32 m_unloadTimer;
|
||||
float m_VisibleDistance;
|
||||
DynamicMapTree _dynamicTree;
|
||||
time_t _instanceResetPeriod; // pussywizard
|
||||
|
||||
MapRefMgr m_mapRefMgr;
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "DBCStores.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "DisableMgr.h"
|
||||
#include "MapCollisionData.h"
|
||||
#include "MapTree.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "VMapFactory.h"
|
||||
#include "VMapMgr2.h"
|
||||
#include "World.h"
|
||||
#include "WorldModel.h"
|
||||
|
||||
#include <G3D/Vector3.h>
|
||||
|
||||
MapCollisionData::MapCollisionData(Map const& map, Map const* parentMap) :
|
||||
_map(map), _staticVMapData(map.GetId())
|
||||
{
|
||||
if (parentMap)
|
||||
{
|
||||
// If we have a parent map, point our static tree and mmap nav mesh to the parent maps
|
||||
_staticVMapData._staticTree = parentMap->GetMapCollisionData().GetStaticTreeSharedPtr();
|
||||
_mmapData._navMesh = parentMap->GetMapCollisionData().GetMMapNavMeshSharedPtr();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are a base map create a new static tree and mmap nav mesh
|
||||
std::string const mapFileName = VMAP::VMapMgr2::getMapFileName(map.GetId());
|
||||
std::shared_ptr<VMAP::StaticMapTree> newTree = std::make_shared<VMAP::StaticMapTree>(map.GetId(), (sWorld->GetDataPath() + "vmaps"));
|
||||
if (newTree->InitMap(mapFileName))
|
||||
_staticVMapData._staticTree = newTree;
|
||||
|
||||
_mmapData._navMesh = MMAP::MMapMgr::LoadNavMesh(map.GetId());
|
||||
}
|
||||
}
|
||||
|
||||
int MapCollisionData::LoadVMapTile(uint32 tileX, uint32 tileY)
|
||||
{
|
||||
if (!VMAP::VMapFactory::createOrGetVMapMgr()->isMapLoadingEnabled() || !_staticVMapData._staticTree)
|
||||
return VMAP::VMAP_LOAD_RESULT_IGNORED;
|
||||
|
||||
if (!_staticVMapData._staticTree->LoadMapTile(tileX, tileY))
|
||||
return VMAP::VMAP_LOAD_RESULT_ERROR;
|
||||
|
||||
return VMAP::VMAP_LOAD_RESULT_OK;
|
||||
}
|
||||
|
||||
int MapCollisionData::LoadMMapTile(uint32 tileX, uint32 tileY)
|
||||
{
|
||||
if (!DisableMgr::IsPathfindingEnabled(&_map) || !_mmapData._navMesh)
|
||||
return MMAP::MMAP_LOAD_RESULT_IGNORED;
|
||||
|
||||
return MMAP::MMapMgr::LoadTile(_mmapData._navMesh.get(), _map.GetId(), tileX, tileY);
|
||||
}
|
||||
|
||||
bool StaticVMapCollisionData::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, VMAP::ModelIgnoreFlags ignoreFlags) const
|
||||
{
|
||||
#if defined(ENABLE_VMAP_CHECKS)
|
||||
if (!sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_LOS) || DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LOS))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
if (!_staticTree)
|
||||
return true;
|
||||
|
||||
G3D::Vector3 const pos1 = VMAP::VMapMgr2::convertPositionToInternalRep(x1, y1, z1);
|
||||
G3D::Vector3 const pos2 = VMAP::VMapMgr2::convertPositionToInternalRep(x2, y2, z2);
|
||||
if (pos1 != pos2)
|
||||
return _staticTree->isInLineOfSight(pos1, pos2, ignoreFlags);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StaticVMapCollisionData::GetObjectHitPos(float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const
|
||||
{
|
||||
#if defined(ENABLE_VMAP_CHECKS)
|
||||
if (sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_LOS) && !DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LOS))
|
||||
#endif
|
||||
{
|
||||
if (_staticTree)
|
||||
{
|
||||
G3D::Vector3 const pos1 = VMAP::VMapMgr2::convertPositionToInternalRep(x1, y1, z1);
|
||||
G3D::Vector3 const pos2 = VMAP::VMapMgr2::convertPositionToInternalRep(x2, y2, z2);
|
||||
G3D::Vector3 resultPos;
|
||||
bool result = _staticTree->GetObjectHitPos(pos1, pos2, resultPos, modifyDist);
|
||||
resultPos = VMAP::VMapMgr2::convertPositionToInternalRep(resultPos.x, resultPos.y, resultPos.z);
|
||||
rx = resultPos.x;
|
||||
ry = resultPos.y;
|
||||
rz = resultPos.z;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
rx = x2;
|
||||
ry = y2;
|
||||
rz = z2;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float StaticVMapCollisionData::getHeight(float x, float y, float z, float maxSearchDist) const
|
||||
{
|
||||
#if defined(ENABLE_VMAP_CHECKS)
|
||||
if (sWorld->getBoolConfig(CONFIG_VMAP_ENABLE_HEIGHT) && !DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_HEIGHT))
|
||||
#endif
|
||||
{
|
||||
if (_staticTree)
|
||||
{
|
||||
G3D::Vector3 const pos = VMAP::VMapMgr2::convertPositionToInternalRep(x, y, z);
|
||||
float height = _staticTree->getHeight(pos, maxSearchDist);
|
||||
if (height >= G3D::finf())
|
||||
return VMAP_INVALID_HEIGHT_VALUE; // No height
|
||||
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
return VMAP_INVALID_HEIGHT_VALUE;
|
||||
}
|
||||
|
||||
bool StaticVMapCollisionData::GetAreaAndLiquidData(float x, float y, float z, Optional<uint8> reqLiquidType, VMAP::AreaAndLiquidData& data) const
|
||||
{
|
||||
if (_staticTree)
|
||||
{
|
||||
VMAP::LocationInfo info;
|
||||
G3D::Vector3 const pos = VMAP::VMapMgr2::convertPositionToInternalRep(x, y, z);
|
||||
if (_staticTree->GetLocationInfo(pos, info))
|
||||
{
|
||||
data.floorZ = info.ground_Z;
|
||||
if (!DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_LIQUIDSTATUS))
|
||||
{
|
||||
uint32 liquidType = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc
|
||||
float liquidLevel;
|
||||
if (!reqLiquidType || (GetLiquidFlags(liquidType) & *reqLiquidType))
|
||||
if (info.hitInstance->GetLiquidLevel(pos, info, liquidLevel))
|
||||
data.liquidInfo.emplace(liquidType, liquidLevel);
|
||||
}
|
||||
|
||||
if (!DisableMgr::IsVMAPDisabledFor(_mapId, VMAP::VMAP_DISABLE_AREAFLAG))
|
||||
data.areaInfo.emplace(info.hitModel->GetWmoID(), info.hitInstance->adtId, info.rootId, info.hitModel->GetMogpFlags(), info.hitInstance->ID);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DynamicVMapCollisionData::GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const
|
||||
{
|
||||
G3D::Vector3 startPos(x1, y1, z1);
|
||||
G3D::Vector3 dstPos(x2, y2, z2);
|
||||
|
||||
G3D::Vector3 resultPos;
|
||||
bool result = DynamicMapTree::GetObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist);
|
||||
|
||||
rx = resultPos.x;
|
||||
ry = resultPos.y;
|
||||
rz = resultPos.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
dtNavMeshQuery const* MMapData::GetNavMeshQuery()
|
||||
{
|
||||
if (_navMesh && !_navMeshQuery)
|
||||
_navMeshQuery = MMAP::MMapMgr::CreateNavMeshQuery(_navMesh.get());
|
||||
|
||||
return _navMeshQuery.get();
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 MAP_COLLISION_DATA_H
|
||||
#define MAP_COLLISION_DATA_H
|
||||
|
||||
#include "DynamicTree.h"
|
||||
#include "MapTree.h"
|
||||
#include "MMapMgr.h"
|
||||
#include "IVMapMgr.h"
|
||||
|
||||
class Map;
|
||||
|
||||
namespace VMAP
|
||||
{
|
||||
enum class ModelIgnoreFlags : uint32;
|
||||
}
|
||||
|
||||
// Simple wrappers for vmap static & dynamic trees to enable clear separation of use for increased readability
|
||||
class StaticVMapCollisionData
|
||||
{
|
||||
friend class MapCollisionData;
|
||||
|
||||
public:
|
||||
StaticVMapCollisionData(uint32 mapId) : _mapId(mapId) {}
|
||||
|
||||
bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, VMAP::ModelIgnoreFlags ignoreFlags) const;
|
||||
bool GetObjectHitPos(float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const;
|
||||
float getHeight(float x, float y, float z, float maxSearchDist) const;
|
||||
bool GetAreaAndLiquidData(float x, float y, float z, Optional<uint8> reqLiquidType, VMAP::AreaAndLiquidData& data) const;
|
||||
protected:
|
||||
// _staticTree is a shared_ptr as it will point to a parent maps static tree (if exists) to save on memory
|
||||
std::shared_ptr<VMAP::StaticMapTree> _staticTree;
|
||||
uint32 _mapId;
|
||||
};
|
||||
|
||||
class DynamicVMapCollisionData : public DynamicMapTree
|
||||
{
|
||||
public:
|
||||
bool GetObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) const;
|
||||
};
|
||||
|
||||
class MMapData
|
||||
{
|
||||
friend class MapCollisionData;
|
||||
|
||||
public:
|
||||
dtNavMesh const* GetNavMesh() const { return _navMesh.get(); }
|
||||
dtNavMeshQuery const* GetNavMeshQuery();
|
||||
|
||||
protected:
|
||||
// _navMesh is a shared_ptr as it will point to a parent maps nav mesh (if exists) to save on memory
|
||||
std::shared_ptr<dtNavMesh> _navMesh;
|
||||
// navMeshQuery is not thread safe and needs its own instance per map
|
||||
MMAP::ManagedNavMeshQuery _navMeshQuery;
|
||||
};
|
||||
|
||||
// Map collision data holders (dynamic&static vmap, mmaps)
|
||||
class MapCollisionData
|
||||
{
|
||||
public:
|
||||
MapCollisionData(Map const& map, Map const* parentMap);
|
||||
~MapCollisionData() = default;
|
||||
|
||||
int LoadVMapTile(uint32 tileX, uint32 tileY);
|
||||
int LoadMMapTile(uint32 tileX, uint32 tileY);
|
||||
|
||||
DynamicVMapCollisionData& GetDynamicTree() { return _dynamicVMapData; }
|
||||
DynamicVMapCollisionData const& GetDynamicTree() const { return _dynamicVMapData; }
|
||||
StaticVMapCollisionData& GetStaticTree() { return _staticVMapData; }
|
||||
StaticVMapCollisionData const& GetStaticTree() const { return _staticVMapData; }
|
||||
MMapData& GetMMapData() { return _mmapData; }
|
||||
MMapData const& GetMMapData() const { return _mmapData; }
|
||||
|
||||
std::shared_ptr<VMAP::StaticMapTree> const GetStaticTreeSharedPtr() const { return _staticVMapData._staticTree; }
|
||||
std::shared_ptr<dtNavMesh> const GetMMapNavMeshSharedPtr() const { return _mmapData._navMesh; }
|
||||
|
||||
private:
|
||||
Map const& _map;
|
||||
|
||||
DynamicVMapCollisionData _dynamicVMapData;
|
||||
StaticVMapCollisionData _staticVMapData;
|
||||
MMapData _mmapData;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 AZEROTHCORE_SPAWNDATA_H
|
||||
#define AZEROTHCORE_SPAWNDATA_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <string>
|
||||
|
||||
enum SpawnObjectType : uint8
|
||||
{
|
||||
SPAWN_TYPE_CREATURE = 0,
|
||||
SPAWN_TYPE_GAMEOBJECT = 1,
|
||||
|
||||
SPAWN_TYPE_MAX
|
||||
};
|
||||
|
||||
enum SpawnObjectTypeMask : uint8
|
||||
{
|
||||
SPAWN_TYPEMASK_CREATURE = (1 << SPAWN_TYPE_CREATURE),
|
||||
SPAWN_TYPEMASK_GAMEOBJECT = (1 << SPAWN_TYPE_GAMEOBJECT),
|
||||
|
||||
SPAWN_TYPEMASK_ALL = SPAWN_TYPEMASK_CREATURE | SPAWN_TYPEMASK_GAMEOBJECT
|
||||
};
|
||||
|
||||
enum SpawnGroupFlags : uint32
|
||||
{
|
||||
SPAWNGROUP_FLAG_NONE = 0x00,
|
||||
SPAWNGROUP_FLAG_SYSTEM = 0x01,
|
||||
SPAWNGROUP_FLAG_MANUAL_SPAWN = 0x04,
|
||||
SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE = 0x08,
|
||||
SPAWNGROUP_FLAG_ESCORTQUESTNPC = 0x10,
|
||||
|
||||
SPAWNGROUP_FLAG_ALL = SPAWNGROUP_FLAG_SYSTEM |
|
||||
SPAWNGROUP_FLAG_MANUAL_SPAWN | SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE |
|
||||
SPAWNGROUP_FLAG_ESCORTQUESTNPC
|
||||
};
|
||||
|
||||
struct SpawnGroupTemplateData
|
||||
{
|
||||
uint32 groupId;
|
||||
std::string name;
|
||||
uint16 mapid;
|
||||
SpawnGroupFlags flags;
|
||||
};
|
||||
|
||||
struct SpawnData
|
||||
{
|
||||
SpawnObjectType const type;
|
||||
uint16 mapid{0};
|
||||
uint32 phaseMask{0};
|
||||
float posX{0.0f};
|
||||
float posY{0.0f};
|
||||
float posZ{0.0f};
|
||||
float orientation{0.0f};
|
||||
uint8 spawnMask{0};
|
||||
uint32 ScriptId{0};
|
||||
bool dbData{true};
|
||||
uint32 spawnGroupId{0};
|
||||
|
||||
protected:
|
||||
SpawnData(SpawnObjectType t) : type(t) {}
|
||||
};
|
||||
|
||||
#endif // AZEROTHCORE_SPAWNDATA_H
|
||||
@@ -815,6 +815,7 @@ enum AcoreStrings
|
||||
LANG_NO_REASON = 35407,
|
||||
LANG_UNAUTHORIZED = 35408,
|
||||
LANG_PINFO_CHR_MAP_WITH_AREA = 35409,
|
||||
LANG_PINFO_CHR_ONLINETIME = 35410,
|
||||
|
||||
LANG_CHARACTER_GENDER_MALE = 855,
|
||||
LANG_CHARACTER_GENDER_FEMALE = 856,
|
||||
@@ -1202,7 +1203,30 @@ enum AcoreStrings
|
||||
LANG_BF_QUEUE_PLAYER_QUEUE = 5123,
|
||||
LANG_BF_QUEUE_PLAYER_INVITED = 5124,
|
||||
LANG_BF_QUEUE_PLAYER_WAR = 5125,
|
||||
// Room for more strings 5126-9999
|
||||
|
||||
// Autobroadcast commands
|
||||
LANG_AUTOBROADCAST_LIST_HEADER = 5126,
|
||||
LANG_AUTOBROADCAST_LIST_ENTRY = 5127,
|
||||
LANG_AUTOBROADCAST_LIST_EMPTY = 5128,
|
||||
LANG_AUTOBROADCAST_ADD_SUCCESS = 5129,
|
||||
LANG_AUTOBROADCAST_REMOVE_SUCCESS = 5130,
|
||||
LANG_AUTOBROADCAST_NOT_FOUND = 5131,
|
||||
LANG_AUTOBROADCAST_LOCALE_SUCCESS = 5132,
|
||||
LANG_AUTOBROADCAST_LOCALE_ENTRY = 5133,
|
||||
LANG_AUTOBROADCAST_INVALID_LOCALE = 5134,
|
||||
|
||||
// Mail commands
|
||||
LANG_MAIL_LIST_HEADER = 5135,
|
||||
LANG_MAIL_LIST_ENTRY = 5136,
|
||||
LANG_MAIL_LIST_EMPTY = 5137,
|
||||
LANG_MAIL_RETURN_SUCCESS = 5138,
|
||||
LANG_MAIL_RETURN_NOT_FOUND = 5139,
|
||||
LANG_MAIL_RETURN_NOT_NORMAL = 5140,
|
||||
LANG_MAIL_RETURN_NO_SENDER = 5141,
|
||||
LANG_MAIL_RETURN_ALREADY_RETURNED = 5142,
|
||||
LANG_MAIL_RETURN_HOOK_BLOCKED = 5143,
|
||||
|
||||
// Room for more strings 5144-9999
|
||||
|
||||
// Level requirement notifications
|
||||
LANG_SAY_REQ = 6604,
|
||||
@@ -1390,6 +1414,37 @@ enum AcoreStrings
|
||||
|
||||
LANG_DEBUG_LFG_ON = 30096,
|
||||
LANG_DEBUG_LFG_OFF = 30097,
|
||||
LANG_DEBUG_LFG_CONF = 30098
|
||||
LANG_DEBUG_LFG_CONF = 30098,
|
||||
|
||||
// debug loot command
|
||||
LANG_DEBUG_LOOT_HEADER = 30099,
|
||||
LANG_DEBUG_LOOT_ITEM = 30100,
|
||||
LANG_DEBUG_LOOT_ITEM_QUEST = 30101,
|
||||
LANG_DEBUG_LOOT_GOLD = 30102,
|
||||
LANG_DEBUG_LOOT_EMPTY = 30103,
|
||||
LANG_DEBUG_LOOT_INVALID_TYPE = 30104,
|
||||
LANG_DEBUG_LOOT_NO_TEMPLATE = 30105,
|
||||
LANG_DEBUG_LOOT_HEADER_MULTI = 30106,
|
||||
LANG_DEBUG_LOOT_ITEM_MULTI = 30107,
|
||||
LANG_DEBUG_LOOT_ITEM_QUEST_MULTI = 30108,
|
||||
LANG_DEBUG_LOOT_GOLD_MULTI = 30109,
|
||||
|
||||
// debug factionchange command (30110-30125)
|
||||
LANG_DEBUG_FACTIONCHANGE_HEADER = 30110,
|
||||
LANG_DEBUG_FACTIONCHANGE_FLAG_FACTION = 30111,
|
||||
LANG_DEBUG_FACTIONCHANGE_FLAG_RACE = 30112,
|
||||
LANG_DEBUG_FACTIONCHANGE_FLAG_NONE = 30113,
|
||||
LANG_DEBUG_FACTIONCHANGE_GUILD_FAIL = 30114,
|
||||
LANG_DEBUG_FACTIONCHANGE_GUILD_OK = 30115,
|
||||
LANG_DEBUG_FACTIONCHANGE_ARENA_CAPTAIN_FAIL = 30116,
|
||||
LANG_DEBUG_FACTIONCHANGE_ARENA_CAPTAIN_OK = 30117,
|
||||
LANG_DEBUG_FACTIONCHANGE_MAIL_FAIL = 30118,
|
||||
LANG_DEBUG_FACTIONCHANGE_MAIL_OK = 30119,
|
||||
LANG_DEBUG_FACTIONCHANGE_AUCTION_FAIL = 30120,
|
||||
LANG_DEBUG_FACTIONCHANGE_AUCTION_OK = 30121,
|
||||
LANG_DEBUG_FACTIONCHANGE_GOLD_FAIL = 30122,
|
||||
LANG_DEBUG_FACTIONCHANGE_GOLD_OK = 30123,
|
||||
LANG_DEBUG_FACTIONCHANGE_GOLD_NOLIMIT = 30124,
|
||||
LANG_DEBUG_FACTIONCHANGE_NA = 30125
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -538,9 +538,9 @@ void MotionMaster::MovePath(uint32 path_id, ForcedMovement forcedMovement, PathS
|
||||
}
|
||||
|
||||
Movement::PointsArray points;
|
||||
for (auto& point : *path)
|
||||
for (auto const& node : path->Nodes)
|
||||
{
|
||||
points.push_back(G3D::Vector3(point.second.x, point.second.y, point.second.z));
|
||||
points.push_back(G3D::Vector3(node.X, node.Y, node.Z));
|
||||
}
|
||||
|
||||
// pass the new PointsArray* to the appropriate MoveSplinePath function
|
||||
@@ -879,10 +879,10 @@ void MotionMaster::MoveDistract(uint32 timer)
|
||||
|
||||
void MotionMaster::Mutate(MovementGenerator* m, MovementSlot slot)
|
||||
{
|
||||
bool const delayed = (_cleanFlag & MMCF_UPDATE);
|
||||
|
||||
while (MovementGenerator* curr = Impl[slot])
|
||||
{
|
||||
bool delayed = (_top == slot && (_cleanFlag & MMCF_UPDATE));
|
||||
|
||||
// clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete
|
||||
Impl[slot] = nullptr;
|
||||
while (!empty() && !top())
|
||||
@@ -918,7 +918,7 @@ void MotionMaster::MoveWaypoint(uint32 path_id, bool repeatable, PathSource path
|
||||
if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
|
||||
return;
|
||||
|
||||
Mutate(new WaypointMovementGenerator<Creature>(path_id, pathSource, repeatable), MOTION_SLOT_IDLE);
|
||||
Mutate(new WaypointMovementGenerator<Creature>(path_id, repeatable, pathSource), MOTION_SLOT_IDLE);
|
||||
|
||||
LOG_DEBUG("movement.motionmaster", "{} ({}) start moving over path(Id:{}, repeatable: {})",
|
||||
_owner->IsPlayer() ? "Player" : "Creature", _owner->GetGUID().ToString(), path_id, repeatable ? "YES" : "NO");
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "DetourCommon.h"
|
||||
#include "Geometry.h"
|
||||
#include "Log.h"
|
||||
#include "MMapFactory.h"
|
||||
#include "MMapMgr.h"
|
||||
#include "Map.h"
|
||||
#include "Metric.h"
|
||||
@@ -34,12 +33,10 @@ PathGenerator::PathGenerator(WorldObject const* owner) :
|
||||
{
|
||||
memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs));
|
||||
|
||||
uint32 mapId = _source->GetMapId();
|
||||
//if (sDisableMgr->IsPathfindingEnabled(_sourceUnit->FindMap()))
|
||||
{
|
||||
MMAP::MMapMgr* mmap = MMAP::MMapFactory::createOrGetMMapMgr();
|
||||
_navMesh = mmap->GetNavMesh(mapId);
|
||||
_navMeshQuery = mmap->GetNavMeshQuery(mapId, _source->GetInstanceId());
|
||||
_navMesh = _source->GetMap()->GetMapCollisionData().GetMMapData().GetNavMesh();
|
||||
_navMeshQuery = _source->GetMap()->GetMapCollisionData().GetMMapData().GetNavMeshQuery();
|
||||
}
|
||||
|
||||
CreateFilter();
|
||||
|
||||
@@ -259,12 +259,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
owner->SetInFront(target);
|
||||
|
||||
if (cOwner)
|
||||
{
|
||||
if (cOwner->IsWithinMeleeRange(target))
|
||||
cOwner->SetCannotReachTarget();
|
||||
else
|
||||
cOwner->SetCannotReachTarget(target->GetGUID());
|
||||
}
|
||||
cOwner->SetCannotReachTarget();
|
||||
|
||||
MovementInform(owner);
|
||||
}
|
||||
|
||||
@@ -27,45 +27,69 @@
|
||||
#include "Player.h"
|
||||
#include "Spell.h"
|
||||
#include "Transport.h"
|
||||
#include "World.h"
|
||||
#include "SmartScriptMgr.h"
|
||||
#include "World.h"
|
||||
|
||||
void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature)
|
||||
inline G3D::Vector3 PositionToVector3(Position const& p) { return { p.GetPositionX(), p.GetPositionY(), p.GetPositionZ() }; }
|
||||
|
||||
WaypointMovementGenerator<Creature>::WaypointMovementGenerator(uint32 pathId, bool repeating, PathSource pathSource) : PathMovementBase((WaypointPath const*)nullptr),
|
||||
_lastSplineId(0), _pathId(pathId), _waypointDelay(0),
|
||||
_waypointReached(true), _recalculateSpeed(false), _repeating(repeating), _loadedFromDB(true), _stalled(false), _hasBeenStalled(false), _done(false), _pathSource(pathSource),
|
||||
_smoothSplineLaunched(false), _lastPassedSplineIdx(0)
|
||||
{
|
||||
switch (i_pathSource)
|
||||
{
|
||||
case PathSource::WAYPOINT_MGR:
|
||||
{
|
||||
if (!path_id)
|
||||
path_id = creature->GetWaypointPath();
|
||||
}
|
||||
|
||||
i_path = sWaypointMgr->GetPath(path_id);
|
||||
break;
|
||||
}
|
||||
case PathSource::SMART_WAYPOINT_MGR:
|
||||
WaypointMovementGenerator<Creature>::WaypointMovementGenerator(WaypointPath& path, bool repeating) : PathMovementBase((WaypointPath const*)nullptr),
|
||||
_lastSplineId(0), _pathId(0), _waypointDelay(0),
|
||||
_waypointReached(true), _recalculateSpeed(false), _repeating(repeating), _loadedFromDB(false), _stalled(false), _hasBeenStalled(false), _done(false), _pathSource(PathSource::WAYPOINT_MGR),
|
||||
_smoothSplineLaunched(false), _lastPassedSplineIdx(0)
|
||||
{
|
||||
i_path = &path;
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature)
|
||||
{
|
||||
_done = false;
|
||||
|
||||
if (_loadedFromDB)
|
||||
{
|
||||
if (!_pathId)
|
||||
_pathId = creature->GetWaypointPath();
|
||||
|
||||
switch (_pathSource)
|
||||
{
|
||||
i_path = sSmartWaypointMgr->GetPath(path_id);
|
||||
break;
|
||||
default:
|
||||
case PathSource::WAYPOINT_MGR:
|
||||
i_path = sWaypointMgr->GetPath(_pathId);
|
||||
break;
|
||||
case PathSource::SMART_WAYPOINT_MGR:
|
||||
i_path = sSmartWaypointMgr->GetPath(_pathId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!i_path)
|
||||
{
|
||||
// No movement found for entry
|
||||
LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature {} ({}) doesn't have waypoint path id: {}",
|
||||
creature->GetName(), creature->GetGUID().ToString(), path_id);
|
||||
LOG_ERROR("sql.sql", "WaypointMovementGenerator::DoInitialize: creature {} ({}) doesn't have waypoint path id: {}",
|
||||
creature->GetName(), creature->GetGUID().ToString(), _pathId);
|
||||
return;
|
||||
}
|
||||
|
||||
i_currentNode = i_path->begin()->first;
|
||||
// Determine our first waypoint from the creature's stored waypoint
|
||||
if (CreatureData const* creatureData = creature->GetCreatureData())
|
||||
{
|
||||
if (i_path->Nodes.size() > creatureData->currentwaypoint)
|
||||
{
|
||||
creature->UpdateCurrentWaypointInfo(creatureData->currentwaypoint, i_path->Id);
|
||||
i_currentNode = creatureData->currentwaypoint;
|
||||
}
|
||||
}
|
||||
|
||||
StartMoveNow(creature);
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature)
|
||||
{
|
||||
LoadPath(creature);
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
|
||||
|
||||
// Inform AI
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->WaypointPathStarted(i_path->Id);
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature)
|
||||
@@ -75,133 +99,219 @@ void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature)
|
||||
|
||||
void WaypointMovementGenerator<Creature>::DoReset(Creature* creature)
|
||||
{
|
||||
if (stalled)
|
||||
// We did not reach our last waypoint before reset, treat this scenario as resuming movement.
|
||||
if (!_done && !_waypointReached)
|
||||
_hasBeenStalled = true;
|
||||
else if (_done)
|
||||
{
|
||||
return;
|
||||
}
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
|
||||
StartMoveNow(creature);
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
|
||||
{
|
||||
if (!i_path || i_path->empty())
|
||||
return;
|
||||
if (m_isArrivalDone)
|
||||
return;
|
||||
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
m_isArrivalDone = true;
|
||||
|
||||
auto currentNodeItr = i_path->find(i_currentNode);
|
||||
|
||||
if (currentNodeItr->second.event_id && urand(0, 99) < currentNodeItr->second.event_chance)
|
||||
{
|
||||
LOG_DEBUG("maps.script", "Creature movement start script {} at point {} for {}.",
|
||||
currentNodeItr->second.event_id, i_currentNode, creature->GetGUID().ToString());
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
creature->GetMap()->ScriptsStart(sWaypointScripts, currentNodeItr->second.event_id, creature, nullptr);
|
||||
}
|
||||
|
||||
// Inform script
|
||||
MovementInform(creature);
|
||||
creature->UpdateWaypointID(i_currentNode);
|
||||
|
||||
if (currentNodeItr->second.delay)
|
||||
{
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
Stop(currentNodeItr->second.delay);
|
||||
// mimic IdleMovementGenerator
|
||||
if (!creature->IsStopped())
|
||||
creature->StopMoving();
|
||||
}
|
||||
}
|
||||
|
||||
bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
|
||||
inline void UpdateHomePosition(Creature* creature, WaypointNode const& waypointNode)
|
||||
{
|
||||
if (!i_path || i_path->empty())
|
||||
return false;
|
||||
|
||||
// Xinef: Dont allow dead creatures to move
|
||||
if (!creature->IsAlive())
|
||||
return false;
|
||||
|
||||
if (Stopped())
|
||||
return true;
|
||||
float x = waypointNode.X;
|
||||
float y = waypointNode.Y;
|
||||
float z = waypointNode.Z;
|
||||
float o = creature->GetOrientation();
|
||||
|
||||
bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID();
|
||||
|
||||
if (m_isArrivalDone)
|
||||
if (!transportPath)
|
||||
creature->SetHomePosition(x, y, z, o);
|
||||
else
|
||||
{
|
||||
if (Transport* trans = (creature->GetTransport() ? creature->GetTransport()->ToMotionTransport() : nullptr))
|
||||
{
|
||||
auto currentNodeItr = i_path->find(i_currentNode);
|
||||
float x = currentNodeItr->second.x;
|
||||
float y = currentNodeItr->second.y;
|
||||
float z = currentNodeItr->second.z;
|
||||
float o = creature->GetOrientation();
|
||||
o -= trans->GetOrientation();
|
||||
creature->SetTransportHomePosition(x, y, z, o);
|
||||
trans->CalculatePassengerPosition(x, y, z, &o);
|
||||
creature->SetHomePosition(x, y, z, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!transportPath)
|
||||
creature->SetHomePosition(x, y, z, o);
|
||||
else
|
||||
void WaypointMovementGenerator<Creature>::ProcessWaypointArrival(Creature* creature, WaypointNode const& waypoint)
|
||||
{
|
||||
if (_waypointReached)
|
||||
return;
|
||||
|
||||
if (waypoint.Delay > 0)
|
||||
{
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
_waypointDelay = waypoint.Delay;
|
||||
}
|
||||
|
||||
bool pathEnded = (i_currentNode == i_path->Nodes.size() - 1) && !_repeating && !_done;
|
||||
if (pathEnded)
|
||||
_done = true;
|
||||
|
||||
UpdateHomePosition(creature, waypoint);
|
||||
|
||||
if (waypoint.EventId && urand(0, 99) < waypoint.EventChance)
|
||||
{
|
||||
LOG_DEBUG("maps.script", "Creature movement start script {} at point {} for {}.",
|
||||
waypoint.EventId, i_currentNode, creature->GetGUID().ToString());
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.EventId, creature, nullptr);
|
||||
}
|
||||
|
||||
// scripts can invalidate current path, store what we need
|
||||
uint32 const waypointId = waypoint.Id;
|
||||
uint32 const pathId = i_path->Id;
|
||||
|
||||
creature->UpdateWaypointID(waypointId);
|
||||
creature->UpdateCurrentWaypointInfo(waypointId, pathId);
|
||||
|
||||
// Inform AI
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
{
|
||||
AI->MovementInform(WAYPOINT_MOTION_TYPE, waypointId);
|
||||
AI->WaypointReached(waypointId, pathId);
|
||||
}
|
||||
|
||||
if (Unit* owner = creature->GetCharmerOrOwner())
|
||||
{
|
||||
if (UnitAI* AI = owner->GetAI())
|
||||
AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypointId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TempSummon* tempSummon = creature->ToTempSummon())
|
||||
if (Unit* owner2 = tempSummon->GetSummonerUnit())
|
||||
if (UnitAI* AI = owner2->GetAI())
|
||||
AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, waypointId);
|
||||
}
|
||||
|
||||
// Path end notifications fire after WaypointReached so that m_path_id
|
||||
// is still valid when SmartAI checks it for SMART_EVENT_WAYPOINT_REACHED.
|
||||
if (pathEnded)
|
||||
{
|
||||
creature->UpdateCurrentWaypointInfo(0, 0);
|
||||
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->PathEndReached(pathId);
|
||||
|
||||
// Re-fetch AI — PathEndReached may have despawned the creature or swapped its AI
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->WaypointPathEnded(waypointId, pathId);
|
||||
}
|
||||
|
||||
// All hooks called and infos updated. Time to increment the waypoint node id
|
||||
if (i_path && !i_path->Nodes.empty()) // ensure that the path has not been changed in one of the hooks.
|
||||
i_currentNode = (i_currentNode + 1) % i_path->Nodes.size();
|
||||
|
||||
_waypointReached = true;
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::StartMove(Creature* creature, bool relaunch /*= false*/)
|
||||
{
|
||||
// Formation checks. Do not launch a new spline when one of our formation members is currently in combat.
|
||||
if (!relaunch)
|
||||
{
|
||||
if (!IsAllowedToMove(creature) || (creature->IsFormationLeader() && !creature->IsFormationLeaderMoveAllowed()))
|
||||
{
|
||||
_waypointDelay = 1000;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Dont allow dead creatures to move
|
||||
if (!creature->IsAlive())
|
||||
return;
|
||||
|
||||
// Step two: node selection is done, build spline data
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode);
|
||||
bool const useTransportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID();
|
||||
|
||||
Movement::MoveSplineInit init(creature);
|
||||
//! If the creature is on transport, we assume waypoints set in DB are already transport offsets
|
||||
if (useTransportPath)
|
||||
init.DisableTransportPathTransformations();
|
||||
|
||||
if (waypoint.SmoothTransition && i_path->Nodes.size() > 2)
|
||||
{
|
||||
// Build a catmullrom spline segment, stopping at delay waypoints
|
||||
init.Path().push_back(PositionToVector3(creature->GetPosition()));
|
||||
|
||||
bool hasDelayInSegment = false;
|
||||
uint32 segmentNodes = 0;
|
||||
for (uint32 i = 0; i < i_path->Nodes.size(); ++i)
|
||||
{
|
||||
uint32 idx = (i_currentNode + i) % i_path->Nodes.size();
|
||||
WaypointNode const& node = i_path->Nodes.at(idx);
|
||||
init.Path().push_back(G3D::Vector3(node.X, node.Y, node.Z));
|
||||
segmentNodes++;
|
||||
|
||||
// Stop the segment at a waypoint with a delay
|
||||
if (node.Delay > 0)
|
||||
{
|
||||
if (Transport* trans = (creature->GetTransport() ? creature->GetTransport()->ToMotionTransport() : nullptr))
|
||||
{
|
||||
o -= trans->GetOrientation();
|
||||
creature->SetTransportHomePosition(x, y, z, o);
|
||||
trans->CalculatePassengerPosition(x, y, z, &o);
|
||||
creature->SetHomePosition(x, y, z, o);
|
||||
}
|
||||
else
|
||||
transportPath = false;
|
||||
// else if (vehicle) - this should never happen, vehicle offsets are const
|
||||
hasDelayInSegment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Xinef: moved the upper IF here
|
||||
uint32 lastPoint = i_path->rbegin()->first;
|
||||
if ((i_currentNode == lastPoint) && !repeating) // If that's our last waypoint
|
||||
// If no delays found and repeating, add wrap-around points for seamless loop
|
||||
if (!hasDelayInSegment && _repeating)
|
||||
{
|
||||
creature->AI()->PathEndReached(path_id);
|
||||
creature->GetMotionMaster()->Initialize();
|
||||
return false;
|
||||
for (uint32 i = 0; i < std::min<uint32>(3, i_path->Nodes.size()); ++i)
|
||||
{
|
||||
uint32 idx = (i_currentNode + i) % i_path->Nodes.size();
|
||||
WaypointNode const& node = i_path->Nodes.at(idx);
|
||||
init.Path().push_back(G3D::Vector3(node.X, node.Y, node.Z));
|
||||
}
|
||||
}
|
||||
|
||||
++i_currentNode;
|
||||
if (lastPoint < i_currentNode)
|
||||
i_currentNode = i_path->begin()->first;
|
||||
// Need at least 3 waypoints for a meaningful catmullrom spline
|
||||
if (segmentNodes >= 3)
|
||||
{
|
||||
init.SetFirstPointId(i_currentNode);
|
||||
init.SetSmooth();
|
||||
_smoothSplineLaunched = true;
|
||||
_lastPassedSplineIdx = i_currentNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too few points for catmullrom, fall back to linear point-to-point
|
||||
init.Path().clear();
|
||||
init.MoveTo(G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z));
|
||||
}
|
||||
}
|
||||
|
||||
// xinef: do not initialize motion if we got stunned in movementinform
|
||||
if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting())
|
||||
else if (!waypoint.SplinePoints.empty())
|
||||
{
|
||||
return true;
|
||||
// We have spline points in waypoint_data_addon table
|
||||
int32 splineIndex = 0;
|
||||
|
||||
auto itr = waypoint.SplinePoints.begin();
|
||||
if (splineIndex)
|
||||
std::advance(itr, splineIndex);
|
||||
|
||||
init.Path().reserve(waypoint.SplinePoints.size() - splineIndex);
|
||||
std::copy(itr, waypoint.SplinePoints.end(), std::back_inserter(init.Path()));
|
||||
|
||||
// Add starting vertex and destination
|
||||
init.Path().insert(init.Path().begin(), PositionToVector3(creature->GetPosition()));
|
||||
init.Path().insert(init.Path().end(), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z));
|
||||
}
|
||||
|
||||
auto currentNodeItr = i_path->find(i_currentNode);
|
||||
WaypointData const& node = currentNodeItr->second;
|
||||
|
||||
m_isArrivalDone = false;
|
||||
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
|
||||
Movement::Location formationDest(node.x, node.y, node.z, 0.0f);
|
||||
Movement::MoveSplineInit init(creature);
|
||||
|
||||
//! If creature is on transport, we assume waypoints set in DB are already transport offsets
|
||||
if (transportPath)
|
||||
else
|
||||
{
|
||||
init.DisableTransportPathTransformations();
|
||||
if (TransportBase* trans = creature->GetDirectTransport())
|
||||
trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation);
|
||||
// Smooth transition for short paths (<=2 nodes): use previous spline endpoint as start
|
||||
if (waypoint.SmoothTransition && !creature->movespline->Finalized() && _lastSplineId == creature->movespline->GetId())
|
||||
{
|
||||
init.MoveTo(creature->movespline->FinalDestination(), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z));
|
||||
if (!init.Path().empty())
|
||||
init.Path().insert(init.Path().begin(), PositionToVector3(creature->GetPosition()));
|
||||
}
|
||||
else
|
||||
init.MoveTo(PositionToVector3(creature->GetPosition()), G3D::Vector3(waypoint.X, waypoint.Y, waypoint.Z));
|
||||
}
|
||||
|
||||
float z = node.z;
|
||||
creature->UpdateAllowedPositionZ(node.x, node.y, z);
|
||||
//! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call
|
||||
//! but formationDest contains global coordinates
|
||||
init.MoveTo(node.x, node.y, z, true, true);
|
||||
if (waypoint.Orientation.has_value() && waypoint.Delay > 0)
|
||||
init.SetFacing(*waypoint.Orientation);
|
||||
|
||||
if (node.orientation.has_value() && node.delay > 0)
|
||||
init.SetFacing(*node.orientation);
|
||||
|
||||
switch (node.move_type)
|
||||
switch (waypoint.MoveType)
|
||||
{
|
||||
case WAYPOINT_MOVE_TYPE_LAND:
|
||||
init.SetAnimation(AnimTier::Ground);
|
||||
@@ -219,89 +329,214 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
|
||||
break;
|
||||
}
|
||||
|
||||
if (creature->CanFly())
|
||||
init.SetFly();
|
||||
|
||||
if (waypoint.Velocity > 0.f)
|
||||
init.SetVelocity(waypoint.Velocity);
|
||||
|
||||
init.Launch();
|
||||
|
||||
//Call for creature group update
|
||||
if (creature->GetFormation() && creature->GetFormation()->GetLeader() == creature && creature->GetFormation()->CanLeaderStartMoving())
|
||||
creature->GetFormation()->LeaderStartedMoving();
|
||||
if (!creature->movespline->Finalized())
|
||||
_lastSplineId = creature->movespline->GetId();
|
||||
|
||||
return true;
|
||||
// Inform formation
|
||||
creature->SignalFormationMovement();
|
||||
|
||||
// Inform AI
|
||||
if (!relaunch)
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->WaypointStarted(waypoint.Id, i_path->Id);
|
||||
|
||||
_waypointReached = false;
|
||||
_recalculateSpeed = false;
|
||||
_hasBeenStalled = false;
|
||||
}
|
||||
|
||||
bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff)
|
||||
{
|
||||
// Waypoint movement can be switched on/off
|
||||
// This is quite handy for escort quests and other stuff
|
||||
if (stalled)
|
||||
{
|
||||
Stop(1000);
|
||||
if (!creature || !creature->IsAlive())
|
||||
return true;
|
||||
}
|
||||
if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting())
|
||||
|
||||
if (_done || !i_path || i_path->Nodes.empty())
|
||||
return true;
|
||||
|
||||
// Stop movement if paused, rooted, or casting
|
||||
if (!IsAllowedToMove(creature) && !creature->movespline->Finalized())
|
||||
{
|
||||
creature->StopMoving();
|
||||
Stop(1000);
|
||||
_lastSplineId = 0;
|
||||
_smoothSplineLaunched = false;
|
||||
}
|
||||
|
||||
// Set home position to current position.
|
||||
if (!creature->movespline->Finalized())
|
||||
{
|
||||
bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID();
|
||||
if (!transportPath)
|
||||
creature->SetHomePosition(creature->GetPosition());
|
||||
}
|
||||
|
||||
// Smooth spline: track waypoint passages without rebuilding
|
||||
if (_smoothSplineLaunched && creature->movespline->GetId() == _lastSplineId)
|
||||
{
|
||||
int32 currentIdx = creature->movespline->currentPathIdx();
|
||||
|
||||
// Process passed waypoints
|
||||
while (_lastPassedSplineIdx < currentIdx)
|
||||
{
|
||||
_lastPassedSplineIdx++;
|
||||
WaypointNode const& passedWp = i_path->Nodes.at(i_currentNode);
|
||||
|
||||
UpdateHomePosition(creature, passedWp);
|
||||
|
||||
// Save data before AI callbacks — they can invalidate the reference
|
||||
uint32 const wpId = passedWp.Id;
|
||||
uint32 const wpPathId = i_path->Id;
|
||||
uint32 const wpDelay = passedWp.Delay;
|
||||
std::optional<float> const wpOrientation = passedWp.Orientation;
|
||||
|
||||
creature->UpdateWaypointID(wpId);
|
||||
creature->UpdateCurrentWaypointInfo(wpId, wpPathId);
|
||||
|
||||
if (passedWp.EventId && urand(0, 99) < passedWp.EventChance)
|
||||
{
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
creature->GetMap()->ScriptsStart(sWaypointScripts, passedWp.EventId, creature, nullptr);
|
||||
}
|
||||
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
{
|
||||
AI->MovementInform(WAYPOINT_MOTION_TYPE, wpId);
|
||||
AI->WaypointReached(wpId, wpPathId);
|
||||
}
|
||||
|
||||
// Advance node
|
||||
if (i_path && !i_path->Nodes.empty())
|
||||
i_currentNode = (i_currentNode + 1) % i_path->Nodes.size();
|
||||
|
||||
// If this waypoint has a delay, stop the spline and pause
|
||||
if (wpDelay > 0)
|
||||
{
|
||||
creature->StopMoving();
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
_waypointDelay = wpDelay;
|
||||
_waypointReached = true;
|
||||
_smoothSplineLaunched = false;
|
||||
if (wpOrientation.has_value())
|
||||
creature->SetFacingTo(*wpOrientation);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (creature->movespline->Finalized())
|
||||
{
|
||||
if (!_repeating)
|
||||
{
|
||||
// Path ended
|
||||
uint32 const endWpId = i_path->Nodes.at(i_currentNode).Id;
|
||||
uint32 const endPathId = i_path->Id;
|
||||
_done = true;
|
||||
_smoothSplineLaunched = false;
|
||||
creature->UpdateCurrentWaypointInfo(0, 0);
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->PathEndReached(endPathId);
|
||||
|
||||
// Re-fetch AI — PathEndReached may have despawned the creature or swapped its AI
|
||||
if (CreatureAI* AI = creature->AI())
|
||||
AI->WaypointPathEnded(endWpId, endPathId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Repeating: rebuild spline
|
||||
_smoothSplineLaunched = false;
|
||||
StartMove(creature);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// prevent a crash at empty waypoint path.
|
||||
if (!i_path || i_path->empty())
|
||||
return false;
|
||||
// Non-smooth: per-waypoint logic
|
||||
WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode);
|
||||
UpdateWaypointState(creature, waypoint);
|
||||
|
||||
// Xinef: Dont allow dead creatures to move
|
||||
if (!creature->IsAlive())
|
||||
return false;
|
||||
// Process movement preventing timers
|
||||
if (_waypointDelay > 0)
|
||||
{
|
||||
_waypointDelay -= diff;
|
||||
if (_waypointDelay > 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Stopped())
|
||||
if (_pauseTime.has_value())
|
||||
{
|
||||
if (CanMove(diff))
|
||||
return StartMove(creature);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (creature->movespline->Finalized())
|
||||
{
|
||||
OnArrived(creature);
|
||||
return StartMove(creature);
|
||||
}
|
||||
*_pauseTime -= diff;
|
||||
if (*_pauseTime > 0)
|
||||
return true;
|
||||
else
|
||||
_pauseTime.reset();
|
||||
}
|
||||
|
||||
// Timers are ready, let's try to move
|
||||
if (IsAllowedToMove(creature) && (_waypointReached || _recalculateSpeed || _hasBeenStalled))
|
||||
StartMove(creature, _recalculateSpeed || _hasBeenStalled);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature)
|
||||
void WaypointMovementGenerator<Creature>::Pause(uint32 timer /*= 0*/)
|
||||
{
|
||||
if (creature->AI())
|
||||
creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
|
||||
|
||||
if (Unit* owner = creature->GetCharmerOrOwner())
|
||||
{
|
||||
if (UnitAI* AI = owner->GetAI())
|
||||
AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, i_currentNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TempSummon* tempSummon = creature->ToTempSummon())
|
||||
if (Unit* owner = tempSummon->GetSummonerUnit())
|
||||
if (UnitAI* AI = owner->GetAI())
|
||||
AI->SummonMovementInform(creature, WAYPOINT_MOTION_TYPE, i_currentNode);
|
||||
}
|
||||
_stalled = timer ? false : true;
|
||||
_hasBeenStalled = !_waypointReached;
|
||||
_pauseTime = timer;
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::Pause(uint32 timer)
|
||||
void WaypointMovementGenerator<Creature>::Resume(uint32 overrideTimer /*= 0*/)
|
||||
{
|
||||
if (timer)
|
||||
i_nextMoveTime.Reset(timer);
|
||||
else
|
||||
{
|
||||
// No timer? Will be paused forever until ::Resume is called
|
||||
stalled = true;
|
||||
i_nextMoveTime.Reset(1);
|
||||
}
|
||||
_hasBeenStalled = !_waypointReached;
|
||||
_stalled = false;
|
||||
if (overrideTimer)
|
||||
_pauseTime = overrideTimer;
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::Resume(uint32 /*overrideTimer/*/)
|
||||
bool WaypointMovementGenerator<Creature>::GetResetPosition(float& x, float& y, float& z)
|
||||
{
|
||||
stalled = false;
|
||||
// prevent a crash at empty waypoint path.
|
||||
if (!i_path || i_path->Nodes.empty())
|
||||
return false;
|
||||
|
||||
ASSERT(i_currentNode < i_path->Nodes.size(), "WaypointMovementGenerator::GetResetPos: tried to reference a node id ({}) which is not included in path ({})", i_currentNode, i_path->Id);
|
||||
WaypointNode const& waypoint = i_path->Nodes.at(i_currentNode);
|
||||
|
||||
x = waypoint.X;
|
||||
y = waypoint.Y;
|
||||
z = waypoint.Z;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaypointMovementGenerator<Creature>::IsAllowedToMove(Creature* creature) const
|
||||
{
|
||||
if (_stalled || _done)
|
||||
return false;
|
||||
|
||||
if (_pauseTime.has_value())
|
||||
return false;
|
||||
|
||||
if (creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::UpdateWaypointState(Creature* creature, WaypointNode const& waypointNode)
|
||||
{
|
||||
if (creature->movespline->GetId() != _lastSplineId)
|
||||
return;
|
||||
|
||||
if (creature->movespline->Finalized())
|
||||
ProcessWaypointArrival(creature, waypointNode);
|
||||
}
|
||||
|
||||
//----------------------------------------------------//
|
||||
|
||||
@@ -54,49 +54,43 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
|
||||
public PathMovementBase<Creature, WaypointPath const*>
|
||||
{
|
||||
public:
|
||||
WaypointMovementGenerator(uint32 _path_id = 0, PathSource pathSource = PathSource::WAYPOINT_MGR, bool _repeating = true, bool _stalled = false)
|
||||
: PathMovementBase((WaypointPath const*)nullptr), i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating), stalled(_stalled), i_pathSource(pathSource) {}
|
||||
explicit WaypointMovementGenerator(uint32 pathId = 0, bool repeating = true, PathSource pathSource = PathSource::WAYPOINT_MGR);
|
||||
explicit WaypointMovementGenerator(WaypointPath& path, bool repeating = true);
|
||||
~WaypointMovementGenerator() { i_path = nullptr; }
|
||||
|
||||
void DoInitialize(Creature*);
|
||||
void DoFinalize(Creature*);
|
||||
void DoReset(Creature*);
|
||||
bool DoUpdate(Creature*, uint32 diff);
|
||||
void Pause(uint32 timer = 0);
|
||||
void Resume(uint32 overrideTimer/* = 0*/);
|
||||
|
||||
void MovementInform(Creature*);
|
||||
void unitSpeedChanged() override { _recalculateSpeed = true; }
|
||||
void Pause(uint32 timer = 0) override;
|
||||
void Resume(uint32 overrideTimer = 0) override;
|
||||
bool GetResetPosition(float& x, float& y, float& z) override;
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
|
||||
|
||||
// now path movement implmementation
|
||||
void LoadPath(Creature*);
|
||||
MovementGeneratorType GetMovementGeneratorType() override { return WAYPOINT_MOTION_TYPE; }
|
||||
|
||||
private:
|
||||
void Stop(int32 time) { i_nextMoveTime.Reset(time);}
|
||||
void ProcessWaypointArrival(Creature*, WaypointNode const&);
|
||||
void StartMove(Creature*, bool relaunch = false);
|
||||
bool IsAllowedToMove(Creature*) const;
|
||||
void UpdateWaypointState(Creature*, WaypointNode const&);
|
||||
|
||||
bool Stopped() { return !i_nextMoveTime.Passed();}
|
||||
uint32 _lastSplineId;
|
||||
uint32 _pathId;
|
||||
int32 _waypointDelay;
|
||||
std::optional<int32> _pauseTime;
|
||||
bool _waypointReached;
|
||||
|
||||
bool CanMove(int32 diff)
|
||||
{
|
||||
i_nextMoveTime.Update(diff);
|
||||
return i_nextMoveTime.Passed();
|
||||
}
|
||||
|
||||
void OnArrived(Creature*);
|
||||
bool StartMove(Creature*);
|
||||
|
||||
void StartMoveNow(Creature* creature)
|
||||
{
|
||||
i_nextMoveTime.Reset(0);
|
||||
StartMove(creature);
|
||||
}
|
||||
|
||||
TimeTrackerSmall i_nextMoveTime;
|
||||
bool m_isArrivalDone;
|
||||
uint32 path_id;
|
||||
bool repeating;
|
||||
bool stalled;
|
||||
PathSource i_pathSource;
|
||||
bool _recalculateSpeed;
|
||||
bool _repeating;
|
||||
bool _loadedFromDB;
|
||||
bool _stalled;
|
||||
bool _hasBeenStalled;
|
||||
bool _done;
|
||||
PathSource _pathSource;
|
||||
bool _smoothSplineLaunched;
|
||||
int32 _lastPassedSplineIdx;
|
||||
};
|
||||
|
||||
/** FlightPathMovementGenerator generates movement of the player for the paths
|
||||
|
||||
@@ -122,6 +122,7 @@ namespace Movement
|
||||
[[nodiscard]] Vector3 FinalDestination() const { return Initialized() ? spline.getPoint(spline.last()) : Vector3(); }
|
||||
[[nodiscard]] Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx + 1) : Vector3(); }
|
||||
[[nodiscard]] int32 currentPathIdx() const;
|
||||
[[nodiscard]] int32 MaxPathIdx() const { return spline.last() - 1; }
|
||||
|
||||
[[nodiscard]] bool HasAnimation() const { return splineflags.animation; }
|
||||
[[nodiscard]] uint8 GetAnimationType() const { return splineflags.animId; }
|
||||
|
||||
@@ -199,6 +199,25 @@ namespace Movement
|
||||
args.flags.EnableFacingAngle();
|
||||
}
|
||||
|
||||
void MoveSplineInit::MoveTo(Vector3 const& start, Vector3 const& dest, bool generatePath, bool forceDestination)
|
||||
{
|
||||
if (generatePath)
|
||||
{
|
||||
PathGenerator path(unit);
|
||||
bool result = path.CalculatePath(start.x, start.y, start.z, dest.x, dest.y, dest.z, forceDestination);
|
||||
if (result && !(path.GetPathType() & PATHFIND_NOPATH))
|
||||
{
|
||||
MovebyPath(path.GetPath());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
args.path_Idx_offset = 0;
|
||||
args.path.resize(2);
|
||||
TransportPathTransform transform(unit, args.TransformForTransport);
|
||||
args.path[1] = transform(dest);
|
||||
}
|
||||
|
||||
void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination)
|
||||
{
|
||||
if (generatePath)
|
||||
|
||||
@@ -99,6 +99,7 @@ namespace Movement
|
||||
|
||||
/* Initializes simple A to B motion, A is current unit's position, B is destination
|
||||
*/
|
||||
void MoveTo(Vector3 const& start, Vector3 const& destination, bool generatePath = true, bool forceDestination = false);
|
||||
void MoveTo(const Vector3& destination, bool generatePath = false, bool forceDestination = false);
|
||||
void MoveTo(float x, float y, float z, bool generatePath = false, bool forceDestination = false);
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 ACORE_WAYPOINTDEFINES_H
|
||||
#define ACORE_WAYPOINTDEFINES_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "G3D/Vector3.h"
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
enum WaypointMoveType
|
||||
{
|
||||
WAYPOINT_MOVE_TYPE_WALK,
|
||||
WAYPOINT_MOVE_TYPE_RUN,
|
||||
WAYPOINT_MOVE_TYPE_LAND,
|
||||
WAYPOINT_MOVE_TYPE_TAKEOFF,
|
||||
|
||||
WAYPOINT_MOVE_TYPE_MAX
|
||||
};
|
||||
|
||||
struct WaypointNode
|
||||
{
|
||||
WaypointNode() : Id(0), X(0.f), Y(0.f), Z(0.f), Velocity(0.f), Delay(0), EventId(0), MoveType(WAYPOINT_MOVE_TYPE_RUN), EventChance(0), SmoothTransition(false) { }
|
||||
WaypointNode(uint32 id, float x, float y, float z, std::optional<float> orientation = { }, float velocity = 0.f, uint32 delay = 0, bool smoothTransition = false) :
|
||||
Id(id), X(x), Y(y), Z(z), Orientation(orientation), Velocity(velocity), Delay(delay), SmoothTransition(smoothTransition)
|
||||
{
|
||||
EventId = 0;
|
||||
MoveType = WAYPOINT_MOVE_TYPE_WALK;
|
||||
EventChance = 100;
|
||||
}
|
||||
|
||||
uint32 Id;
|
||||
float X, Y, Z;
|
||||
std::optional<float> Orientation;
|
||||
float Velocity;
|
||||
uint32 Delay;
|
||||
uint32 EventId;
|
||||
uint32 MoveType;
|
||||
uint8 EventChance;
|
||||
bool SmoothTransition;
|
||||
std::vector<G3D::Vector3> SplinePoints;
|
||||
};
|
||||
|
||||
struct WaypointPath
|
||||
{
|
||||
WaypointPath() : Id(0) { }
|
||||
WaypointPath(uint32 _id, std::vector<WaypointNode>&& _nodes)
|
||||
{
|
||||
Id = _id;
|
||||
Nodes = _nodes;
|
||||
}
|
||||
|
||||
std::vector<WaypointNode> Nodes;
|
||||
uint32 Id;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -22,20 +22,6 @@
|
||||
#include "QueryResult.h"
|
||||
#include "Timer.h"
|
||||
|
||||
WaypointMgr::WaypointMgr()
|
||||
{
|
||||
}
|
||||
|
||||
WaypointMgr::~WaypointMgr()
|
||||
{
|
||||
for (WaypointPathContainer::iterator itr = _waypointStore.begin(); itr != _waypointStore.end(); ++itr)
|
||||
{
|
||||
itr->second.clear();
|
||||
}
|
||||
|
||||
_waypointStore.clear();
|
||||
}
|
||||
|
||||
WaypointMgr* WaypointMgr::instance()
|
||||
{
|
||||
static WaypointMgr instance;
|
||||
@@ -46,8 +32,8 @@ void WaypointMgr::Load()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
QueryResult result = WorldDatabase.Query("SELECT id, point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data ORDER BY id, point");
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11
|
||||
QueryResult result = WorldDatabase.Query("SELECT id, point, position_x, position_y, position_z, orientation, velocity, delay, smoothTransition, move_type, action, action_chance FROM waypoint_data ORDER BY id, point");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -61,66 +47,113 @@ void WaypointMgr::Load()
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
WaypointData data;
|
||||
|
||||
uint32 pathId = fields[0].Get<uint32>();
|
||||
WaypointPath& path = _waypointStore[pathId];
|
||||
|
||||
float x = fields[2].Get<float>();
|
||||
float y = fields[3].Get<float>();
|
||||
float z = fields[4].Get<float>();
|
||||
std::optional<float > o;
|
||||
std::optional<float> o;
|
||||
if (!fields[5].IsNull())
|
||||
o = fields[5].Get<float>();
|
||||
|
||||
float velocity = fields[6].Get<float>();
|
||||
|
||||
Acore::NormalizeMapCoord(x);
|
||||
Acore::NormalizeMapCoord(y);
|
||||
|
||||
data.id = fields[1].Get<uint32>();
|
||||
data.x = x;
|
||||
data.y = y;
|
||||
data.z = z;
|
||||
data.orientation = o;
|
||||
data.move_type = fields[6].Get<uint32>();
|
||||
WaypointNode waypoint;
|
||||
waypoint.Id = fields[1].Get<uint32>();
|
||||
waypoint.X = x;
|
||||
waypoint.Y = y;
|
||||
waypoint.Z = z;
|
||||
if (o.has_value())
|
||||
waypoint.Orientation = o;
|
||||
waypoint.Velocity = velocity;
|
||||
waypoint.Delay = fields[7].Get<uint32>();
|
||||
waypoint.SmoothTransition = fields[8].Get<bool>();
|
||||
waypoint.MoveType = fields[9].Get<uint32>();
|
||||
|
||||
if (data.move_type >= WAYPOINT_MOVE_TYPE_MAX)
|
||||
if (waypoint.MoveType >= WAYPOINT_MOVE_TYPE_MAX)
|
||||
{
|
||||
//LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", wp->id);
|
||||
LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", waypoint.Id);
|
||||
continue;
|
||||
}
|
||||
|
||||
data.delay = fields[7].Get<uint32>();
|
||||
data.event_id = fields[8].Get<uint32>();
|
||||
data.event_chance = fields[9].Get<int16>();
|
||||
waypoint.EventId = fields[10].Get<uint32>();
|
||||
waypoint.EventChance = fields[11].Get<int16>();
|
||||
|
||||
path.emplace(data.id, data);
|
||||
WaypointPath& path = _waypointStore[pathId];
|
||||
path.Id = pathId;
|
||||
path.Nodes.push_back(std::move(waypoint));
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
for (auto itr = _waypointStore.begin(); itr != _waypointStore.end(); )
|
||||
{
|
||||
uint32 first = itr->second.begin()->first;
|
||||
uint32 last = itr->second.rbegin()->first;
|
||||
if (last - first + 1 != itr->second.size())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has non-contiguous pointids, skipping", itr->first);
|
||||
itr = _waypointStore.erase(itr);
|
||||
}
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} waypoints in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void WaypointMgr::LoadWaypointAddons()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
// 0 1 2 3 4 5
|
||||
QueryResult result = WorldDatabase.Query("SELECT PathID, PointID, SplinePointIndex, PositionX, PositionY, PositionZ FROM waypoint_data_addon ORDER BY PathID, PointID, SplinePointIndex");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LOG_INFO("server.loading", ">> Loaded 0 waypoint addon data. DB table `waypoint_data_addon` is empty!");
|
||||
LOG_INFO("server.loading", " ");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 pathId = fields[0].Get<uint32>();
|
||||
|
||||
auto it = _waypointStore.find(pathId);
|
||||
if (it == _waypointStore.end())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Tried to load waypoint_data_addon data for PathID {} but there is no such path in waypoint_data. Ignoring.", pathId);
|
||||
continue;
|
||||
}
|
||||
|
||||
WaypointPath& path = it->second;
|
||||
uint32 pointId = fields[1].Get<uint32>();
|
||||
|
||||
auto itr = std::find_if(path.Nodes.begin(), path.Nodes.end(), [pointId](WaypointNode const& node)
|
||||
{
|
||||
return node.Id == pointId;
|
||||
});
|
||||
|
||||
if (itr == path.Nodes.end())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Tried to load waypoint_data_addon data for PointID {} of PathID {} but there is no such point in waypoint_data. Ignoring.", pointId, pathId);
|
||||
continue;
|
||||
}
|
||||
|
||||
float x = fields[3].Get<float>();
|
||||
float y = fields[4].Get<float>();
|
||||
float z = fields[5].Get<float>();
|
||||
|
||||
Acore::NormalizeMapCoord(x);
|
||||
Acore::NormalizeMapCoord(y);
|
||||
|
||||
itr->SplinePoints.push_back(G3D::Vector3(x, y, z));
|
||||
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} waypoint addon data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void WaypointMgr::ReloadPath(uint32 id)
|
||||
{
|
||||
WaypointPathContainer::iterator itr = _waypointStore.find(id);
|
||||
auto itr = _waypointStore.find(id);
|
||||
if (itr != _waypointStore.end())
|
||||
{
|
||||
_waypointStore.erase(itr);
|
||||
}
|
||||
|
||||
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID);
|
||||
|
||||
@@ -131,13 +164,10 @@ void WaypointMgr::ReloadPath(uint32 id)
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
WaypointPath& path = _waypointStore[id];
|
||||
|
||||
std::vector<WaypointNode> values;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
WaypointData data;
|
||||
|
||||
float x = fields[1].Get<float>();
|
||||
float y = fields[2].Get<float>();
|
||||
float z = fields[3].Get<float>();
|
||||
@@ -145,26 +175,34 @@ void WaypointMgr::ReloadPath(uint32 id)
|
||||
if (!fields[4].IsNull())
|
||||
o = fields[4].Get<float>();
|
||||
|
||||
float velocity = fields[5].Get<float>();
|
||||
|
||||
Acore::NormalizeMapCoord(x);
|
||||
Acore::NormalizeMapCoord(y);
|
||||
|
||||
data.id = fields[0].Get<uint32>();
|
||||
data.x = x;
|
||||
data.y = y;
|
||||
data.z = z;
|
||||
data.orientation = o;
|
||||
data.move_type = fields[5].Get<uint32>();
|
||||
WaypointNode waypoint;
|
||||
waypoint.Id = fields[0].Get<uint32>();
|
||||
waypoint.X = x;
|
||||
waypoint.Y = y;
|
||||
waypoint.Z = z;
|
||||
if (o.has_value())
|
||||
waypoint.Orientation = o;
|
||||
waypoint.Velocity = velocity;
|
||||
waypoint.Delay = fields[6].Get<uint32>();
|
||||
waypoint.SmoothTransition = fields[7].Get<bool>();
|
||||
waypoint.MoveType = fields[8].Get<uint32>();
|
||||
|
||||
if (data.move_type >= WAYPOINT_MOVE_TYPE_MAX)
|
||||
if (waypoint.MoveType >= WAYPOINT_MOVE_TYPE_MAX)
|
||||
{
|
||||
//LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", wp->id);
|
||||
LOG_ERROR("sql.sql", "Waypoint {} in waypoint_data has invalid move_type, ignoring", waypoint.Id);
|
||||
continue;
|
||||
}
|
||||
|
||||
data.delay = fields[6].Get<uint32>();
|
||||
data.event_id = fields[7].Get<uint32>();
|
||||
data.event_chance = fields[8].Get<uint8>();
|
||||
waypoint.EventId = fields[9].Get<uint32>();
|
||||
waypoint.EventChance = fields[10].Get<uint8>();
|
||||
|
||||
path.emplace(data.id, data);
|
||||
values.push_back(std::move(waypoint));
|
||||
} while (result->NextRow());
|
||||
|
||||
_waypointStore[id] = WaypointPath(id, std::move(values));
|
||||
}
|
||||
|
||||
@@ -18,35 +18,8 @@
|
||||
#ifndef ACORE_WAYPOINTMANAGER_H
|
||||
#define ACORE_WAYPOINTMANAGER_H
|
||||
|
||||
#include "Define.h"
|
||||
#include <optional>
|
||||
#include "WaypointDefines.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
enum WaypointMoveType
|
||||
{
|
||||
WAYPOINT_MOVE_TYPE_WALK,
|
||||
WAYPOINT_MOVE_TYPE_RUN,
|
||||
WAYPOINT_MOVE_TYPE_LAND,
|
||||
WAYPOINT_MOVE_TYPE_TAKEOFF,
|
||||
|
||||
WAYPOINT_MOVE_TYPE_MAX
|
||||
};
|
||||
|
||||
struct WaypointData
|
||||
{
|
||||
uint32 id;
|
||||
float x, y, z;
|
||||
std::optional<float> orientation;
|
||||
uint32 delay;
|
||||
uint32 event_id = 0;
|
||||
uint32 move_type = 0;
|
||||
uint8 event_chance = 0;
|
||||
};
|
||||
|
||||
typedef std::map<uint32, WaypointData> WaypointPath;
|
||||
typedef std::unordered_map<uint32, WaypointPath> WaypointPathContainer;
|
||||
|
||||
class WaypointMgr
|
||||
{
|
||||
@@ -59,10 +32,13 @@ public:
|
||||
// Loads all paths from database, should only run on startup
|
||||
void Load();
|
||||
|
||||
// Loads additional path data for waypoints from database. Should only be called on startup.
|
||||
void LoadWaypointAddons();
|
||||
|
||||
// Returns the path from a given id
|
||||
WaypointPath const* GetPath(uint32 id) const
|
||||
{
|
||||
WaypointPathContainer::const_iterator itr = _waypointStore.find(id);
|
||||
auto itr = _waypointStore.find(id);
|
||||
if (itr != _waypointStore.end())
|
||||
return &itr->second;
|
||||
|
||||
@@ -70,10 +46,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
WaypointMgr();
|
||||
~WaypointMgr();
|
||||
WaypointMgr() { }
|
||||
|
||||
WaypointPathContainer _waypointStore;
|
||||
std::unordered_map<uint32, WaypointPath> _waypointStore;
|
||||
};
|
||||
|
||||
#define sWaypointMgr WaypointMgr::instance()
|
||||
|
||||
@@ -137,6 +137,8 @@ public:
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
[[nodiscard]] float GetMaxValue() const { return _maxValue; }
|
||||
[[nodiscard]] float GetMinValue() const { return _minValue; }
|
||||
|
||||
protected:
|
||||
bool AddObject(uint32 type, uint32 entry, uint32 map, float x, float y, float z, float o,
|
||||
|
||||
@@ -379,8 +379,8 @@ void OpcodeTable::Initialize()
|
||||
/*0x0F8*/ DEFINE_HANDLER(CMSG_TRIGGER_CINEMATIC_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
|
||||
/*0x0F9*/ DEFINE_HANDLER(CMSG_OPENING_CINEMATIC, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
|
||||
/*0x0FA*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_CINEMATIC, STATUS_NEVER);
|
||||
/*0x0FB*/ DEFINE_HANDLER(CMSG_NEXT_CINEMATIC_CAMERA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleNextCinematicCamera );
|
||||
/*0x0FC*/ DEFINE_HANDLER(CMSG_COMPLETE_CINEMATIC, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleCompleteCinematic );
|
||||
/*0x0FB*/ DEFINE_HANDLER(CMSG_NEXT_CINEMATIC_CAMERA, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleNextCinematicCamera );
|
||||
/*0x0FC*/ DEFINE_HANDLER(CMSG_COMPLETE_CINEMATIC, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleCompleteCinematic );
|
||||
/*0x0FD*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_TUTORIAL_FLAGS, STATUS_NEVER);
|
||||
/*0x0FE*/ DEFINE_HANDLER(CMSG_TUTORIAL_FLAG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialFlag );
|
||||
/*0x0FF*/ DEFINE_HANDLER(CMSG_TUTORIAL_CLEAR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialClear );
|
||||
|
||||
@@ -1223,7 +1223,7 @@ bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventIn
|
||||
case SPELL_AURA_MECHANIC_IMMUNITY:
|
||||
case SPELL_AURA_MOD_MECHANIC_RESISTANCE:
|
||||
// compare mechanic
|
||||
if (!spellInfo || !(spellInfo->GetAllEffectsMechanicMask() & (1 << GetMiscValue())))
|
||||
if (!spellInfo || !(spellInfo->GetAllEffectsMechanicMask() & (UI64LIT(1) << GetMiscValue())))
|
||||
return false;
|
||||
break;
|
||||
case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK:
|
||||
@@ -3933,312 +3933,7 @@ void AuraEffect::HandleModStateImmunityMask(AuraApplication const* aurApp, uint8
|
||||
return;
|
||||
|
||||
Unit* target = aurApp->GetTarget();
|
||||
std::list <AuraType> aura_immunity_list;
|
||||
uint32 mechanic_immunity_list = 0;
|
||||
int32 miscVal = GetMiscValue();
|
||||
|
||||
switch (miscVal)
|
||||
{
|
||||
case 96:
|
||||
case 1615:
|
||||
{
|
||||
if (!GetAmount())
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
|
||||
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
|
||||
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
|
||||
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
|
||||
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
|
||||
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_CHARM);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 679:
|
||||
{
|
||||
if (GetId() == 57742)
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
|
||||
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
|
||||
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
|
||||
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
|
||||
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
|
||||
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1557:
|
||||
{
|
||||
if (GetId() == 64187)
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_STUN);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
|
||||
}
|
||||
else
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
|
||||
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
|
||||
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
|
||||
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
|
||||
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
|
||||
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1614:
|
||||
case 1694:
|
||||
{
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT);
|
||||
break;
|
||||
}
|
||||
case 1630:
|
||||
{
|
||||
if (!GetAmount())
|
||||
{
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT);
|
||||
}
|
||||
else
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
|
||||
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
|
||||
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
|
||||
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
|
||||
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
|
||||
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 477:
|
||||
case 1733:
|
||||
case 1632:
|
||||
{
|
||||
if (!GetAmount())
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
|
||||
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
|
||||
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
|
||||
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
|
||||
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
|
||||
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN) | (1 << MECHANIC_BANISH);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_BANISH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
|
||||
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 878:
|
||||
{
|
||||
if (GetAmount() == 1)
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_STUN)
|
||||
| (1 << MECHANIC_DISORIENTED) | (1 << MECHANIC_FREEZE);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (aura_immunity_list.empty())
|
||||
{
|
||||
// Roots, OK
|
||||
if (GetMiscValue() & (1 << 0))
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_SNARE);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
|
||||
}
|
||||
// Taunt, OK
|
||||
if (GetMiscValue() & (1 << 1))
|
||||
{
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT);
|
||||
}
|
||||
// Crowd-Control auras?
|
||||
if (GetMiscValue() & (1 << 2))
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
|
||||
}
|
||||
// Interrupt, OK
|
||||
if (GetMiscValue() & (1 << 3))
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_INTERRUPT);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_INTERRUPT, apply);
|
||||
}
|
||||
// Transform?
|
||||
if (GetMiscValue() & (1 << 4))
|
||||
{
|
||||
aura_immunity_list.push_back(SPELL_AURA_TRANSFORM);
|
||||
}
|
||||
// Stun auras breakable by damage (Incapacitate effects), OK
|
||||
if (GetMiscValue() & (1 << 5))
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_KNOCKOUT);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_KNOCKOUT, apply);
|
||||
}
|
||||
// // Slowing effects
|
||||
if (GetMiscValue() & (1 << 6))
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_SNARE);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
|
||||
}
|
||||
// Charm auras?, 90%
|
||||
if ((GetMiscValue() & (1 << 7)))
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_CHARM);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_CHARM);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_POSSESS);
|
||||
}
|
||||
// UNK
|
||||
// if ((GetMiscValue() & (1 << 8)))
|
||||
// {
|
||||
// }
|
||||
// Fear, OK
|
||||
if (GetMiscValue() & (1 << 9))
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_FEAR);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
|
||||
}
|
||||
// Stuns, OK
|
||||
if (GetMiscValue() & (1 << 10))
|
||||
{
|
||||
mechanic_immunity_list = (1 << MECHANIC_STUN);
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
|
||||
}
|
||||
}
|
||||
|
||||
// apply immunities
|
||||
for (std::list <AuraType>::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter)
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_STATE, *iter, apply);
|
||||
|
||||
// Patch 3.0.3 Bladestorm now breaks all snares and roots on the warrior when activated.
|
||||
if (GetId() == 46924)
|
||||
{
|
||||
// Knockback and hex
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, apply);
|
||||
}
|
||||
|
||||
if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
|
||||
{
|
||||
target->RemoveAurasWithMechanic(mechanic_immunity_list, AURA_REMOVE_BY_DEFAULT, GetId());
|
||||
for (std::list <AuraType>::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter)
|
||||
target->RemoveAurasByType(*iter);
|
||||
}
|
||||
m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply);
|
||||
}
|
||||
|
||||
void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
||||
@@ -4247,67 +3942,7 @@ void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8
|
||||
return;
|
||||
|
||||
Unit* target = aurApp->GetTarget();
|
||||
uint32 mechanic = 0;
|
||||
|
||||
switch (GetId())
|
||||
{
|
||||
case 46924: // BladeStorm
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
break;
|
||||
case 34471: // The Beast Within
|
||||
case 19574: // Bestial Wrath
|
||||
case 38484: // Bestial Wrath
|
||||
case 40081: // Free friend (Black Temple)
|
||||
mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_KNOCKOUT, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_BANISH, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SHACKLE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DAZE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
|
||||
break;
|
||||
case 42292: // PvP trinket
|
||||
case 59752: // Every Man for Himself
|
||||
case 65547: // PvP trinket for Faction Champions (ToC 25)
|
||||
case 53490: // Bullheaded
|
||||
case 46227: // Medalion of Immunity
|
||||
mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
|
||||
target->RemoveAurasByType(SPELL_AURA_PREVENTS_FLEEING); // xinef: Patch 2.3.0 PvP Trinkets: Insignia of the Alliance, Insignia of the Horde, Medallion of the Alliance, and Medallion of the Horde now clear the debuff from Judgement of Justice.
|
||||
// Actually we should apply immunities here, too, but the aura has only 100 ms duration, so there is practically no point
|
||||
break;
|
||||
case 54508: // Demonic Empowerment
|
||||
mechanic = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT);
|
||||
target->RemoveAurasByType(SPELL_AURA_MOD_STUN);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
|
||||
break;
|
||||
default:
|
||||
if (GetMiscValue() < 1)
|
||||
return;
|
||||
mechanic = 1 << GetMiscValue();
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, GetMiscValue(), apply);
|
||||
break;
|
||||
}
|
||||
|
||||
if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
|
||||
{
|
||||
// Xinef: exception for purely snare mechanic (eg. hands of freedom)!
|
||||
if (mechanic == (1 << MECHANIC_SNARE))
|
||||
target->RemoveMovementImpairingAuras(false);
|
||||
else
|
||||
target->RemoveAurasWithMechanic(mechanic, AURA_REMOVE_BY_DEFAULT, GetId());
|
||||
}
|
||||
m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply);
|
||||
}
|
||||
|
||||
void AuraEffect::HandleAuraModEffectImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
||||
@@ -4415,7 +4050,7 @@ void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 m
|
||||
|
||||
Unit* target = aurApp->GetTarget();
|
||||
|
||||
target->ApplySpellImmune(GetId(), IMMUNITY_DAMAGE, GetMiscValue(), apply);
|
||||
m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply);
|
||||
|
||||
if (apply)
|
||||
target->GetThreatMgr().EvaluateSuppressed();
|
||||
@@ -4427,8 +4062,7 @@ void AuraEffect::HandleAuraModDispelImmunity(AuraApplication const* aurApp, uint
|
||||
return;
|
||||
|
||||
Unit* target = aurApp->GetTarget();
|
||||
|
||||
target->ApplySpellDispelImmunity(m_spellInfo, DispelType(GetMiscValue()), (apply));
|
||||
m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply);
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
@@ -6643,7 +6277,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
|
||||
if (!target->IsAlive())
|
||||
return;
|
||||
|
||||
if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()) || target->IsTotem())
|
||||
if (target->IsImmunedToDamage(caster, GetSpellInfo()) || target->IsTotem())
|
||||
{
|
||||
SendTickImmune(target, caster);
|
||||
return;
|
||||
@@ -6795,7 +6429,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
|
||||
if (!target->IsAlive())
|
||||
return;
|
||||
|
||||
if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()))
|
||||
if (target->IsImmunedToDamage(caster, GetSpellInfo()))
|
||||
{
|
||||
SendTickImmune(target, caster);
|
||||
return;
|
||||
@@ -6905,7 +6539,7 @@ void AuraEffect::HandlePeriodicHealthFunnelAuraTick(Unit* target, Unit* caster)
|
||||
if (!caster || !caster->IsAlive() || !target->IsAlive())
|
||||
return;
|
||||
|
||||
if (target->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
|
||||
{
|
||||
SendTickImmune(target, caster);
|
||||
return;
|
||||
@@ -6934,7 +6568,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
|
||||
if (!target->IsAlive())
|
||||
return;
|
||||
|
||||
if (target->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
|
||||
{
|
||||
SendTickImmune(target, caster);
|
||||
return;
|
||||
@@ -7073,7 +6707,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con
|
||||
if (!caster || !caster->IsAlive() || !target->IsAlive() || !target->HasActivePowerType(PowerType))
|
||||
return;
|
||||
|
||||
if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()))
|
||||
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
|
||||
{
|
||||
SendTickImmune(target, caster);
|
||||
return;
|
||||
@@ -7148,7 +6782,7 @@ void AuraEffect::HandleObsModPowerAuraTick(Unit* target, Unit* caster) const
|
||||
if (!target->IsAlive() || !target->GetMaxPower(PowerType))
|
||||
return;
|
||||
|
||||
if (target->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
|
||||
{
|
||||
SendTickImmune(target, caster);
|
||||
return;
|
||||
@@ -7181,7 +6815,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons
|
||||
if (!target->IsAlive() || !target->GetMaxPower(PowerType))
|
||||
return;
|
||||
|
||||
if (target->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
|
||||
{
|
||||
SendTickImmune(target, caster);
|
||||
return;
|
||||
@@ -7212,7 +6846,7 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con
|
||||
if (!caster || !target->IsAlive() || !target->HasActivePowerType(PowerType))
|
||||
return;
|
||||
|
||||
if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()))
|
||||
if (target->IsImmunedToDamage(caster, GetSpellInfo()))
|
||||
{
|
||||
SendTickImmune(target, caster);
|
||||
return;
|
||||
@@ -7306,7 +6940,8 @@ void AuraEffect::HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEv
|
||||
Unit* triggerTarget = target == eventInfo.GetActor() ? eventInfo.GetActionTarget() : eventInfo.GetActor();
|
||||
if (!triggerTarget)
|
||||
return;
|
||||
if (triggerTarget->HasUnitState(UNIT_STATE_ISOLATED) || triggerTarget->IsImmunedToDamageOrSchool(GetSpellInfo()))
|
||||
|
||||
if (triggerTarget->IsImmunedToDamage(target, GetSpellInfo()))
|
||||
{
|
||||
SendTickImmune(triggerTarget, target);
|
||||
return;
|
||||
|
||||
@@ -552,7 +552,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply)
|
||||
if (IsArea())
|
||||
for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
|
||||
{
|
||||
if ((existing->second & (1 << effIndex)) && existing->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex))
|
||||
if ((existing->second & (1 << effIndex)) && existing->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, GetCaster()))
|
||||
existing->second &= ~(1 << effIndex);
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply)
|
||||
// check target immunities
|
||||
for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
|
||||
{
|
||||
if ((itr->second & (1 << effIndex)) && itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex))
|
||||
if ((itr->second & (1 << effIndex)) && itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, GetCaster()))
|
||||
itr->second &= ~(1 << effIndex);
|
||||
}
|
||||
if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo()) || !CanBeAppliedOn(itr->first))
|
||||
@@ -2822,7 +2822,7 @@ void UnitAura::FillTargetMap(std::map<Unit*, uint8>& targets, Unit* caster)
|
||||
{
|
||||
float radius = GetSpellInfo()->Effects[effIndex].CalcRadius(caster);
|
||||
|
||||
if (!GetUnitOwner()->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
if (!GetUnitOwner()->HasAuraState(AURA_STATE_BANISHED, GetSpellInfo(), caster))
|
||||
{
|
||||
switch (GetSpellInfo()->Effects[effIndex].Effect)
|
||||
{
|
||||
|
||||
+167
-129
@@ -1315,7 +1315,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge
|
||||
// Xinef: the distance should be increased by caster size, it is neglected in latter calculations
|
||||
std::list<WorldObject*> targets;
|
||||
float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
|
||||
SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions);
|
||||
SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions, Acore::WorldObjectSpellAreaTargetSearchReason::Area);
|
||||
|
||||
CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType);
|
||||
|
||||
@@ -1414,7 +1414,6 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
|
||||
{
|
||||
float distance = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
|
||||
Map* map = m_caster->GetMap();
|
||||
uint32 mapid = m_caster->GetMapId();
|
||||
uint32 phasemask = m_caster->GetPhaseMask();
|
||||
float collisionHeight = m_caster->GetCollisionHeight();
|
||||
float destz = 0.0f, startx = 0.0f, starty = 0.0f, startz = 0.0f, starto = 0.0f;
|
||||
@@ -1556,9 +1555,9 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
|
||||
//LOG_ERROR("spells", "total path > than distance in 3D , need to move back a bit for save distance, total path = {}, overdistance = {}", totalpath, overdistance);
|
||||
}
|
||||
|
||||
bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(mapid, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f);
|
||||
bool col = m_caster->GetMap()->GetMapCollisionData().GetStaticTree().GetObjectHitPos(prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f);
|
||||
// check dynamic collision
|
||||
bool dcol = m_caster->GetMap()->GetObjectHitPos(phasemask, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f);
|
||||
bool dcol = m_caster->GetMap()->GetMapCollisionData().GetDynamicTree().GetObjectHitPos(phasemask, prevX, prevY, prevZ + 0.5f, tstX, tstY, tstZ + 0.5f, tstX, tstY, tstZ, -0.5f);
|
||||
|
||||
// collision occured
|
||||
if (col || dcol || (overdistance > 0.0f && !map->IsInWater(phasemask, tstX, tstY, ground, collisionHeight)) || (fabs(prevZ - tstZ) > maxtravelDistZ && (tstZ > prevZ)))
|
||||
@@ -1620,9 +1619,9 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
|
||||
else
|
||||
{
|
||||
float z = pos.GetPositionZ();
|
||||
bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(mapid, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f);
|
||||
bool col = m_caster->GetMap()->GetMapCollisionData().GetStaticTree().GetObjectHitPos(pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f);
|
||||
// check dynamic collision
|
||||
bool dcol = m_caster->GetMap()->GetObjectHitPos(phasemask, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f);
|
||||
bool dcol = m_caster->GetMap()->GetMapCollisionData().GetDynamicTree().GetObjectHitPos(phasemask, pos.GetPositionX(), pos.GetPositionY(), z, destx, desty, z, destx, desty, z, -0.5f);
|
||||
|
||||
// collision occured
|
||||
if (col || dcol)
|
||||
@@ -2091,12 +2090,12 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec
|
||||
return target;
|
||||
}
|
||||
|
||||
void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList)
|
||||
void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason)
|
||||
{
|
||||
uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList);
|
||||
if (!containerTypeMask)
|
||||
return;
|
||||
Acore::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList);
|
||||
Acore::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList, searchReason);
|
||||
Acore::WorldObjectListSearcher<Acore::WorldObjectSpellAreaTargetCheck> searcher(m_caster, targets, check, containerTypeMask);
|
||||
SearchTargets<Acore::WorldObjectListSearcher<Acore::WorldObjectSpellAreaTargetCheck> > (searcher, containerTypeMask, m_caster, position, range);
|
||||
}
|
||||
@@ -2142,7 +2141,7 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar
|
||||
|
||||
WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target;
|
||||
std::list<WorldObject*> tempTargets;
|
||||
SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList);
|
||||
SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList, Acore::WorldObjectSpellAreaTargetSearchReason::Chain);
|
||||
tempTargets.remove(target);
|
||||
|
||||
// remove targets which are always invalid for chain spells
|
||||
@@ -2310,7 +2309,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
|
||||
|
||||
// Check for effect immune skip if immuned
|
||||
for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
|
||||
if (target->IsImmunedToSpellEffect(m_spellInfo, effIndex))
|
||||
if (target->IsImmunedToSpellEffect(m_spellInfo, effIndex, m_caster))
|
||||
effectMask &= ~(1 << effIndex);
|
||||
|
||||
ObjectGuid targetGUID = target->GetGUID();
|
||||
@@ -2593,6 +2592,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
|
||||
m_spellAura = nullptr; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied
|
||||
|
||||
if (m_originalCaster && missInfo != SPELL_MISS_EVADE && !m_originalCaster->IsFriendlyTo(effectUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || effectUnit->IsEngaged()))
|
||||
effectUnit->SetInCombatWith(m_originalCaster);
|
||||
|
||||
PrepareScriptHitHandlers();
|
||||
CallScriptBeforeHitHandlers(missInfo);
|
||||
|
||||
@@ -2774,83 +2776,93 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
// Fill base damage struct (unitTarget - is real spell target)
|
||||
SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo, m_spellSchoolMask);
|
||||
|
||||
// Add bonuses and fill damageInfo struct
|
||||
// Dancing Rune Weapon...
|
||||
if (m_caster->GetEntry() == 27893)
|
||||
// Check damage immunity
|
||||
if (unitTarget->IsImmunedToDamage(caster, m_spellInfo))
|
||||
{
|
||||
if (Unit* owner = m_caster->GetOwner())
|
||||
owner->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
|
||||
m_damage = 0;
|
||||
|
||||
// no packet found in sniffs
|
||||
}
|
||||
else
|
||||
caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
|
||||
|
||||
// xinef: override miss info after absorb / block calculations
|
||||
if (missInfo == SPELL_MISS_NONE && damageInfo.damage == 0)
|
||||
{
|
||||
//if (damageInfo.absorb > 0)
|
||||
// missInfo = SPELL_MISS_ABSORB;
|
||||
if (damageInfo.blocked)
|
||||
missInfo = SPELL_MISS_BLOCK;
|
||||
}
|
||||
|
||||
// Xinef: override with forced crit, only visual result
|
||||
if (GetSpellValue()->ForcedCritResult)
|
||||
{
|
||||
damageInfo.HitInfo |= SPELL_HIT_TYPE_CRIT;
|
||||
}
|
||||
|
||||
Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb);
|
||||
|
||||
// xinef: health leech handling
|
||||
if (m_spellInfo->HasEffect(SPELL_EFFECT_HEALTH_LEECH))
|
||||
{
|
||||
uint8 effIndex = EFFECT_0;
|
||||
for (; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
|
||||
if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_HEALTH_LEECH)
|
||||
break;
|
||||
|
||||
float healMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this);
|
||||
|
||||
// get max possible damage, don't count overkill for heal
|
||||
uint32 healthGain = uint32(-unitTarget->GetHealthGain(-int32(damageInfo.damage)) * healMultiplier);
|
||||
|
||||
if (m_caster->IsAlive())
|
||||
// Add bonuses and fill damageInfo struct
|
||||
// Dancing Rune Weapon...
|
||||
if (m_caster->GetEntry() == 27893)
|
||||
{
|
||||
healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL, effIndex);
|
||||
healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL);
|
||||
|
||||
HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellInfo->GetSchoolMask());
|
||||
m_caster->HealBySpell(healInfo);
|
||||
if (Unit* owner = m_caster->GetOwner())
|
||||
owner->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
|
||||
}
|
||||
else
|
||||
caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
|
||||
|
||||
// xinef: override miss info after absorb / block calculations
|
||||
if (missInfo == SPELL_MISS_NONE && damageInfo.damage == 0)
|
||||
{
|
||||
//if (damageInfo.absorb > 0)
|
||||
// missInfo = SPELL_MISS_ABSORB;
|
||||
if (damageInfo.blocked)
|
||||
missInfo = SPELL_MISS_BLOCK;
|
||||
}
|
||||
|
||||
// Xinef: override with forced crit, only visual result
|
||||
if (GetSpellValue()->ForcedCritResult)
|
||||
{
|
||||
damageInfo.HitInfo |= SPELL_HIT_TYPE_CRIT;
|
||||
}
|
||||
|
||||
Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb);
|
||||
|
||||
// xinef: health leech handling
|
||||
if (m_spellInfo->HasEffect(SPELL_EFFECT_HEALTH_LEECH))
|
||||
{
|
||||
uint8 effIndex = EFFECT_0;
|
||||
for (; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
|
||||
if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_HEALTH_LEECH)
|
||||
break;
|
||||
|
||||
float healMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this);
|
||||
|
||||
// get max possible damage, don't count overkill for heal
|
||||
uint32 healthGain = uint32(-unitTarget->GetHealthGain(-int32(damageInfo.damage)) * healMultiplier);
|
||||
|
||||
if (m_caster->IsAlive())
|
||||
{
|
||||
healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL, effIndex);
|
||||
healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL);
|
||||
|
||||
HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellInfo->GetSchoolMask());
|
||||
m_caster->HealBySpell(healInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Send log damage message to client
|
||||
caster->SendSpellNonMeleeDamageLog(&damageInfo);
|
||||
// Xinef: send info to target about reflect
|
||||
if (reflectedSpell)
|
||||
effectUnit->SendSpellNonMeleeReflectLog(&damageInfo, effectUnit);
|
||||
|
||||
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
|
||||
|
||||
caster->DealSpellDamage(&damageInfo, true, this);
|
||||
|
||||
// do procs after damage, eg healing effects
|
||||
// no need to check if target is alive, done in procdamageandspell
|
||||
|
||||
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
|
||||
if (canEffectTrigger)
|
||||
{
|
||||
DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, missInfo);
|
||||
uint32 hitMask = m_procEx | dmgInfo.GetHitMask();
|
||||
Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, &dmgInfo);
|
||||
|
||||
if (caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
|
||||
m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED))
|
||||
caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, dmgInfo.GetHitMask());
|
||||
}
|
||||
|
||||
m_damage = damageInfo.damage;
|
||||
}
|
||||
|
||||
// Send log damage message to client
|
||||
caster->SendSpellNonMeleeDamageLog(&damageInfo);
|
||||
// Xinef: send info to target about reflect
|
||||
if (reflectedSpell)
|
||||
effectUnit->SendSpellNonMeleeReflectLog(&damageInfo, effectUnit);
|
||||
|
||||
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
|
||||
|
||||
caster->DealSpellDamage(&damageInfo, true, this);
|
||||
|
||||
// do procs after damage, eg healing effects
|
||||
// no need to check if target is alive, done in procdamageandspell
|
||||
|
||||
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
|
||||
if (canEffectTrigger)
|
||||
{
|
||||
DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, missInfo);
|
||||
uint32 hitMask = m_procEx | dmgInfo.GetHitMask();
|
||||
Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, &dmgInfo);
|
||||
|
||||
if (caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
|
||||
m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED))
|
||||
caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, dmgInfo.GetHitMask());
|
||||
}
|
||||
|
||||
m_damage = damageInfo.damage;
|
||||
}
|
||||
// Passive spell hits/misses or active spells only misses (only triggers)
|
||||
else
|
||||
@@ -2887,16 +2899,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
if (!m_triggeredByAuraSpell.spellInfo || m_damage || (!(m_triggeredByAuraSpell.spellInfo->Effects[m_triggeredByAuraSpell.effectIndex].TriggerSpell == m_spellInfo->Id) && !(m_triggeredByAuraSpell.spellInfo->IsAuraEffectEqual(m_spellInfo))))
|
||||
m_caster->AtTargetAttacked(effectUnit, !(m_spellInfo->HasAttribute(SPELL_ATTR1_NO_THREAT) || m_spellInfo->HasAttribute(SPELL_ATTR3_SUPPRESS_TARGET_PROCS)));
|
||||
|
||||
// Unsure if there are more spells that are not supposed to stop enemy from
|
||||
// regenerating HP from food, so for now it stays as an ID.
|
||||
const uint32 SPELL_PREMEDITATION = 14183;
|
||||
if (m_spellInfo->Id != SPELL_PREMEDITATION)
|
||||
{
|
||||
if (!effectUnit->IsStandState())
|
||||
{
|
||||
effectUnit->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
}
|
||||
}
|
||||
// Spells that don't initiate combat shouldn't affect stand state (e.g. Premeditation, Storm Giant/Felreaver Passive)
|
||||
if (m_spellInfo->HasInitialAggro() && !effectUnit->IsStandState())
|
||||
effectUnit->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2943,7 +2948,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
|
||||
return SPELL_MISS_EVADE;
|
||||
|
||||
// For delayed spells immunity may be applied between missile launch and hit - check immunity for that case
|
||||
if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(this)) || unit->IsImmunedToSchool(this) || unit->IsImmunedToSpell(m_spellInfo, this)))
|
||||
if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(m_caster, m_spellInfo)) || unit->IsImmunedToSchool(this) || unit->IsImmunedToSpell(m_spellInfo, this)))
|
||||
{
|
||||
return SPELL_MISS_IMMUNE;
|
||||
}
|
||||
@@ -2954,7 +2959,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
|
||||
{
|
||||
if (effectMask & (1 << effectNumber))
|
||||
{
|
||||
if (unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber))
|
||||
if (unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber, m_caster))
|
||||
effectMask &= ~(1 << effectNumber);
|
||||
// Xinef: Buggs out polymorph
|
||||
// Xinef: And this is checked in MagicSpellHitResult, why we check resistance twice?
|
||||
@@ -3100,14 +3105,6 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
|
||||
|
||||
if (m_spellAura)
|
||||
{
|
||||
// Prevent aura application if target is banished and immuned
|
||||
if (m_targets.GetUnitTarget() && m_targets.GetUnitTarget()->IsImmunedToDamageOrSchool(m_spellAura->GetSpellInfo())
|
||||
&& m_targets.GetUnitTarget()->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
{
|
||||
m_spellAura->Remove();
|
||||
return SPELL_MISS_IMMUNE;
|
||||
}
|
||||
|
||||
// Set aura stack amount to desired value
|
||||
if (m_spellValue->AuraStackAmount > 1)
|
||||
{
|
||||
@@ -5610,7 +5607,7 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT
|
||||
}
|
||||
}
|
||||
|
||||
SpellCastResult Spell::CheckCast(bool strict)
|
||||
SpellCastResult Spell::CheckCast(bool strict, uint32* /*param1*/, uint32* /*param2*/)
|
||||
{
|
||||
// check death state
|
||||
if (!m_caster->IsAlive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && !(m_spellInfo->HasAttribute(SPELL_ATTR0_ALLOW_CAST_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell)))
|
||||
@@ -6019,10 +6016,9 @@ SpellCastResult Spell::CheckCast(bool strict)
|
||||
// xinef: Enraged Regeneration: While this is active, the warrior is blocked from using abilities that trigger being enraged (which would do nothing and waste the cooldowns).
|
||||
if (m_spellInfo->Mechanic && m_spellInfo->IsSelfCast())
|
||||
{
|
||||
SpellImmuneList const& mechanicList = m_caster->m_spellImmune[IMMUNITY_MECHANIC];
|
||||
for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
|
||||
if (itr->type == m_spellInfo->Mechanic)
|
||||
return SPELL_FAILED_DAMAGE_IMMUNE;
|
||||
auto const& mechanicList = m_caster->m_spellImmune[IMMUNITY_MECHANIC];
|
||||
if (mechanicList.count(m_spellInfo->Mechanic) > 0)
|
||||
return SPELL_FAILED_DAMAGE_IMMUNE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7083,18 +7079,18 @@ SpellCastResult Spell::CheckRange(bool strict)
|
||||
else if (!m_caster->IsWithinCombatRange(target, max_range))
|
||||
return SPELL_FAILED_OUT_OF_RANGE; //0x5A;
|
||||
|
||||
if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED && range_type == SPELL_RANGE_RANGED)
|
||||
{
|
||||
if (m_caster->IsWithinMeleeRange(target))
|
||||
return SPELL_FAILED_TOO_CLOSE;
|
||||
}
|
||||
|
||||
if (m_caster->IsPlayer() && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast<float>(M_PI), target) && !m_caster->IsWithinBoundaryRadius(target))
|
||||
return SPELL_FAILED_UNIT_NOT_INFRONT;
|
||||
}
|
||||
|
||||
// Xinef: check min range for self casts
|
||||
if (min_range && range_type != SPELL_RANGE_RANGED && m_caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0
|
||||
// Check min range - for ranged spells, min range is the spell's min range + melee range (no leeway)
|
||||
if (range_type == SPELL_RANGE_RANGED)
|
||||
{
|
||||
float minRangeCombined = min_range + m_caster->GetMeleeRange(target);
|
||||
if (m_caster->IsWithinRange(target, minRangeCombined))
|
||||
return SPELL_FAILED_TOO_CLOSE;
|
||||
}
|
||||
else if (min_range > 0 && m_caster->IsWithinCombatRange(target, min_range))
|
||||
return SPELL_FAILED_TOO_CLOSE;
|
||||
}
|
||||
|
||||
@@ -7162,7 +7158,7 @@ SpellCastResult Spell::CheckPower()
|
||||
return SPELL_CAST_OK;
|
||||
}
|
||||
|
||||
SpellCastResult Spell::CheckItems()
|
||||
SpellCastResult Spell::CheckItems(uint32* param1, uint32* param2)
|
||||
{
|
||||
Player* player = m_caster->ToPlayer();
|
||||
if (!player)
|
||||
@@ -7570,21 +7566,29 @@ SpellCastResult Spell::CheckItems()
|
||||
}
|
||||
case SPELL_EFFECT_PROSPECTING:
|
||||
{
|
||||
if (!m_targets.GetItemTarget())
|
||||
Item* item = m_targets.GetItemTarget();
|
||||
if (!item)
|
||||
return SPELL_FAILED_CANT_BE_PROSPECTED;
|
||||
//ensure item is a prospectable ore
|
||||
if (!(m_targets.GetItemTarget()->GetTemplate()->HasFlag(ITEM_FLAG_IS_PROSPECTABLE)))
|
||||
if (!(item->GetTemplate()->HasFlag(ITEM_FLAG_IS_PROSPECTABLE)))
|
||||
return SPELL_FAILED_CANT_BE_PROSPECTED;
|
||||
//prevent prospecting in trade slot
|
||||
if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID())
|
||||
if (item->GetOwnerGUID() != m_caster->GetGUID())
|
||||
return SPELL_FAILED_CANT_BE_PROSPECTED;
|
||||
//Check for enough skill in jewelcrafting
|
||||
uint32 item_prospectingskilllevel = m_targets.GetItemTarget()->GetTemplate()->RequiredSkillRank;
|
||||
uint32 item_prospectingskilllevel = item->GetTemplate()->RequiredSkillRank;
|
||||
if (item_prospectingskilllevel > player->GetSkillValue(SKILL_JEWELCRAFTING))
|
||||
return SPELL_FAILED_LOW_CASTLEVEL;
|
||||
//make sure the player has the required ores in inventory
|
||||
if (m_targets.GetItemTarget()->GetCount() < 5)
|
||||
if (item->GetCount() < 5)
|
||||
{
|
||||
if (param1 && param2)
|
||||
{
|
||||
*param1 = item->GetEntry();
|
||||
*param2 = 5;
|
||||
}
|
||||
return SPELL_FAILED_NEED_MORE_ITEMS;
|
||||
}
|
||||
|
||||
if (!LootTemplates_Prospecting.HaveLootFor(m_targets.GetItemTargetEntry()))
|
||||
return SPELL_FAILED_CANT_BE_PROSPECTED;
|
||||
@@ -7593,21 +7597,29 @@ SpellCastResult Spell::CheckItems()
|
||||
}
|
||||
case SPELL_EFFECT_MILLING:
|
||||
{
|
||||
if (!m_targets.GetItemTarget())
|
||||
Item* item = m_targets.GetItemTarget();
|
||||
if (!item)
|
||||
return SPELL_FAILED_CANT_BE_MILLED;
|
||||
//ensure item is a millable herb
|
||||
if (!(m_targets.GetItemTarget()->GetTemplate()->HasFlag(ITEM_FLAG_IS_MILLABLE)))
|
||||
if (!(item->GetTemplate()->HasFlag(ITEM_FLAG_IS_MILLABLE)))
|
||||
return SPELL_FAILED_CANT_BE_MILLED;
|
||||
//prevent milling in trade slot
|
||||
if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID())
|
||||
if (item->GetOwnerGUID() != m_caster->GetGUID())
|
||||
return SPELL_FAILED_CANT_BE_MILLED;
|
||||
//Check for enough skill in inscription
|
||||
uint32 item_millingskilllevel = m_targets.GetItemTarget()->GetTemplate()->RequiredSkillRank;
|
||||
uint32 item_millingskilllevel = item->GetTemplate()->RequiredSkillRank;
|
||||
if (item_millingskilllevel > player->GetSkillValue(SKILL_INSCRIPTION))
|
||||
return SPELL_FAILED_LOW_CASTLEVEL;
|
||||
//make sure the player has the required herbs in inventory
|
||||
if (m_targets.GetItemTarget()->GetCount() < 5)
|
||||
if (item->GetCount() < 5)
|
||||
{
|
||||
if (param1 && param2)
|
||||
{
|
||||
*param1 = item->GetEntry();
|
||||
*param2 = 5;
|
||||
}
|
||||
return SPELL_FAILED_NEED_MORE_ITEMS;
|
||||
}
|
||||
|
||||
if (!LootTemplates_Milling.HaveLootFor(m_targets.GetItemTargetEntry()))
|
||||
return SPELL_FAILED_CANT_BE_MILLED;
|
||||
@@ -8296,6 +8308,13 @@ void Spell::HandleLaunchPhase()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_originalCaster && target.missCondition != SPELL_MISS_EVADE)
|
||||
{
|
||||
Unit* targetUnit = m_caster->GetGUID() == target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID);
|
||||
if (targetUnit && !m_originalCaster->IsFriendlyTo(targetUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || targetUnit->IsEngaged()))
|
||||
m_originalCaster->SetInCombatWith(targetUnit, true);
|
||||
}
|
||||
|
||||
DoAllEffectOnLaunchTarget(target, multiplier);
|
||||
}
|
||||
|
||||
@@ -9060,8 +9079,8 @@ namespace Acore
|
||||
}
|
||||
|
||||
WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster,
|
||||
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList)
|
||||
: WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position)
|
||||
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason)
|
||||
: WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position), _searchReason(searchReason)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -9069,13 +9088,32 @@ namespace Acore
|
||||
{
|
||||
if (target->IsGameObject())
|
||||
{
|
||||
if (!target->ToGameObject()->IsInRange(_position->GetPositionX(), _position->GetPositionY(), _position->GetPositionZ(), _range))
|
||||
if (!target->ToGameObject()->IsInRange3d(_position->GetPositionX(), _position->GetPositionY(), _position->GetPositionZ(), _range))
|
||||
return false;
|
||||
}
|
||||
else if (!target->IsWithinDist3d(_position, _range))
|
||||
return false;
|
||||
else if (target->IsCreature() && target->ToCreature()->IsAvoidingAOE()) // pussywizard
|
||||
return false;
|
||||
else if (Creature* c = target->ToCreature())
|
||||
{
|
||||
if (c->IsAvoidingAOE()) // pussywizard
|
||||
return false;
|
||||
if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(c->GetCreatureTemplate()->CreatureImmunitiesId))
|
||||
{
|
||||
switch (_searchReason)
|
||||
{
|
||||
case Acore::WorldObjectSpellAreaTargetSearchReason::Area:
|
||||
if (immunities->ImmuneAoE)
|
||||
return false;
|
||||
break;
|
||||
case Acore::WorldObjectSpellAreaTargetSearchReason::Chain:
|
||||
if (immunities->ImmuneChain)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return WorldObjectSpellTargetCheck::operator ()(target);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,15 @@ class SpellEvent;
|
||||
class ByteBuffer;
|
||||
class BasicEvent;
|
||||
|
||||
namespace Acore
|
||||
{
|
||||
enum class WorldObjectSpellAreaTargetSearchReason
|
||||
{
|
||||
Area,
|
||||
Chain
|
||||
};
|
||||
}
|
||||
|
||||
#define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILLISECONDS)
|
||||
#define TRAJECTORY_MISSILE_SIZE 3.0f
|
||||
|
||||
@@ -444,7 +453,7 @@ public:
|
||||
template<class SEARCHER> void SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius);
|
||||
|
||||
WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList = nullptr);
|
||||
void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList);
|
||||
void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason = Acore::WorldObjectSpellAreaTargetSearchReason::Area);
|
||||
void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellTargetSelectionCategories selectCategory, ConditionList* condList, bool isChainHeal);
|
||||
|
||||
SpellCastResult prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura = nullptr);
|
||||
@@ -460,7 +469,7 @@ public:
|
||||
void TakeReagents();
|
||||
void TakeCastItem();
|
||||
|
||||
SpellCastResult CheckCast(bool strict);
|
||||
SpellCastResult CheckCast(bool strict, uint32* param1 = nullptr, uint32* param2 = nullptr);
|
||||
SpellCastResult CheckPetCast(Unit* target);
|
||||
|
||||
// handlers
|
||||
@@ -472,7 +481,7 @@ public:
|
||||
|
||||
void OnSpellLaunch();
|
||||
|
||||
SpellCastResult CheckItems();
|
||||
SpellCastResult CheckItems(uint32* param1 = nullptr, uint32* param2 = nullptr);
|
||||
SpellCastResult CheckSpellFocus();
|
||||
SpellCastResult CheckRange(bool strict);
|
||||
SpellCastResult CheckPower();
|
||||
@@ -827,8 +836,9 @@ namespace Acore
|
||||
{
|
||||
float _range;
|
||||
Position const* _position;
|
||||
Acore::WorldObjectSpellAreaTargetSearchReason _searchReason;
|
||||
WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster,
|
||||
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList);
|
||||
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason = Acore::WorldObjectSpellAreaTargetSearchReason::Area);
|
||||
bool operator()(WorldObject* target);
|
||||
};
|
||||
|
||||
|
||||
@@ -856,7 +856,7 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
|
||||
SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo();
|
||||
|
||||
// Pounce Bleed shouldn't be removed by Cloak of Shadows.
|
||||
if (spell->GetAllEffectsMechanicMask() & 1 << MECHANIC_BLEED)
|
||||
if (spell->GetAllEffectsMechanicMask() & (UI64LIT(1) << MECHANIC_BLEED))
|
||||
return;
|
||||
|
||||
bool dmgClassNone = false;
|
||||
@@ -1889,7 +1889,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex)
|
||||
if (!unitTarget->IsAlive())
|
||||
return;
|
||||
|
||||
if (unitTarget->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
if (unitTarget->IsImmunedToAuraPeriodicTick(m_caster, m_spellInfo))
|
||||
{
|
||||
m_caster->SendSpellDamageImmune(unitTarget, GetSpellInfo()->Id);
|
||||
return;
|
||||
@@ -3681,7 +3681,7 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/)
|
||||
if (!unitTarget || !unitTarget->IsAlive())
|
||||
return;
|
||||
|
||||
if (unitTarget->HasUnitState(UNIT_STATE_ISOLATED))
|
||||
if (unitTarget->IsImmunedToAuraPeriodicTick(m_caster, m_spellInfo))
|
||||
{
|
||||
m_caster->SendSpellDamageImmune(unitTarget, GetSpellInfo()->Id);
|
||||
return;
|
||||
@@ -5152,7 +5152,7 @@ void Spell::EffectDispelMechanic(SpellEffIndex effIndex)
|
||||
continue;
|
||||
if (roll_chance_i(aura->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster))))
|
||||
{
|
||||
if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & (1 << mechanic)))
|
||||
if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & (UI64LIT(1) << mechanic)))
|
||||
{
|
||||
dispel_list.push(std::make_pair(aura->GetId(), aura->GetCasterGUID()));
|
||||
|
||||
@@ -5943,6 +5943,9 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const*
|
||||
|
||||
if (caster->IsTotem())
|
||||
caster = caster->ToTotem()->GetOwner();
|
||||
else if (caster->IsPet())
|
||||
if (Unit* owner = caster->GetOwner())
|
||||
caster = owner;
|
||||
|
||||
// in another case summon new
|
||||
uint8 summonLevel = caster->GetLevel();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user