fix(Core/Movement): (#7008)

- Get zone/area IDs from vmap data in the liquid update
- Add new method Map::getFullVMapDataForPosition to get area info and liquid info in a single vmap lookup
- Adjust GetZoneId/GetAreaId on WorldObject to always return these cached fields.
- Clean up liquid state handling on Unit and Player
- Implemented getting area id from gameobject spawns.
- Removed old core related to getting movement flags dependent on environment.
- Movement flags are now processed more precisely and dynamically.

Original source: TrinityCore.

- Closes #5086
- Updates #2208.
This commit is contained in:
UltraNix 2021-08-25 12:41:20 +02:00 committed by GitHub
parent 909c3e5799
commit a8c0a2cc89
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 1086 additions and 883 deletions

View file

@ -8,9 +8,14 @@
#include "BoundingIntervalHierarchyWrapper.h"
#include "GameObjectModel.h"
#include "Log.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "RegularGrid.h"
#include "Timer.h"
#include "VMapFactory.h"
#include "VMapManager2.h"
#include "WorldModel.h"
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include <G3D/Vector3.h>
@ -145,6 +150,52 @@ struct DynamicTreeIntersectionCallback
bool didHit() const { return did_hit;}
};
struct DynamicTreeAreaInfoCallback
{
DynamicTreeAreaInfoCallback(uint32 phaseMask)
: _phaseMask(phaseMask) {}
void operator()(G3D::Vector3 const& p, GameObjectModel const& obj)
{
obj.IntersectPoint(p, _areaInfo, _phaseMask);
}
VMAP::AreaInfo const& GetAreaInfo() const
{
return _areaInfo;
}
private:
uint32 _phaseMask;
VMAP::AreaInfo _areaInfo;
};
struct DynamicTreeLocationInfoCallback
{
DynamicTreeLocationInfoCallback(uint32 phaseMask)
: _phaseMask(phaseMask), _hitModel(nullptr) {}
void operator()(G3D::Vector3 const& p, GameObjectModel const& obj)
{
if (obj.GetLocationInfo(p, _locationInfo, _phaseMask))
_hitModel = &obj;
}
VMAP::LocationInfo& GetLocationInfo()
{
return _locationInfo;
}
GameObjectModel const* GetHitModel() const
{
return _hitModel;
}
private:
uint32 _phaseMask;
VMAP::LocationInfo _locationInfo;
GameObjectModel const* _hitModel;
};
bool DynamicMapTree::GetIntersectionTime(const uint32 phasemask, const G3D::Ray& ray,
const G3D::Vector3& endPos, float& maxDist) const
{
@ -238,3 +289,41 @@ float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist,
return -G3D::finf();
}
}
bool DynamicMapTree::GetAreaInfo(float x, float y, float& z, uint32 phasemask, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
G3D::Vector3 v(x, y, z + 0.5f);
DynamicTreeAreaInfoCallback intersectionCallBack(phasemask);
impl->intersectPoint(v, intersectionCallBack);
if (intersectionCallBack.GetAreaInfo().result)
{
flags = intersectionCallBack.GetAreaInfo().flags;
adtId = intersectionCallBack.GetAreaInfo().adtId;
rootId = intersectionCallBack.GetAreaInfo().rootId;
groupId = intersectionCallBack.GetAreaInfo().groupId;
z = intersectionCallBack.GetAreaInfo().ground_Z;
return true;
}
return false;
}
void DynamicMapTree::GetAreaAndLiquidData(float x, float y, float z, uint32 phasemask, uint8 reqLiquidType, VMAP::AreaAndLiquidData& data) const
{
G3D::Vector3 v(x, y, z + 0.5f);
DynamicTreeLocationInfoCallback intersectionCallBack(phasemask);
impl->intersectPoint(v, intersectionCallBack);
if (intersectionCallBack.GetLocationInfo().hitModel)
{
data.floorZ = intersectionCallBack.GetLocationInfo().ground_Z;
uint32 liquidType = intersectionCallBack.GetLocationInfo().hitModel->GetLiquidType();
float liquidLevel;
if (!reqLiquidType || (dynamic_cast<VMAP::VMapManager2*>(VMAP::VMapFactory::createOrGetVMapManager())->GetLiquidFlagsPtr(liquidType) & reqLiquidType))
if (intersectionCallBack.GetHitModel()->GetLiquidLevel(v, intersectionCallBack.GetLocationInfo(), liquidLevel))
data.liquidInfo.emplace(liquidType, liquidLevel);
data.areaInfo.emplace(0,
intersectionCallBack.GetLocationInfo().rootId,
intersectionCallBack.GetLocationInfo().hitModel->GetWmoID(),
intersectionCallBack.GetLocationInfo().hitModel->GetMogpFlags());
}
}

View file

@ -15,6 +15,11 @@ namespace G3D
class Vector3;
}
namespace VMAP
{
struct AreaAndLiquidData;
}
class GameObjectModel;
struct DynTreeImpl;
@ -32,6 +37,9 @@ public:
bool GetIntersectionTime(uint32 phasemask, const G3D::Ray& ray,
const G3D::Vector3& endPos, float& maxDist) const;
bool GetAreaInfo(float x, float y, float& z, uint32 phasemask, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const;
void GetAreaAndLiquidData(float x, float y, float z, uint32 phasemask, uint8 reqLiquidType, VMAP::AreaAndLiquidData& data) const;
bool GetObjectHitPos(uint32 phasemask, const G3D::Vector3& pPos1,
const G3D::Vector3& pPos2, G3D::Vector3& pResultHitPos,
float pModifyDist) const;

View file

@ -8,6 +8,7 @@
#define _IVMAPMANAGER_H
#include "Define.h"
#include "Optional.h"
#include <string>
//===========================================================
@ -25,8 +26,33 @@ namespace VMAP
VMAP_LOAD_RESULT_IGNORED
};
#define VMAP_INVALID_HEIGHT -100000.0f // for check
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
#define VMAP_INVALID_HEIGHT -100000.0f // for check
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
struct AreaAndLiquidData
{
struct AreaInfo
{
AreaInfo(int32 _adtId, int32 _rootId, int32 _groupId, uint32 _flags)
: adtId(_adtId), rootId(_rootId), groupId(_groupId), mogpFlags(_flags) { }
int32 const adtId;
int32 const rootId;
int32 const groupId;
uint32 const mogpFlags;
};
struct LiquidInfo
{
LiquidInfo(uint32 _type, float _level)
: type(_type), level(_level) {}
uint32 const type;
float const level;
};
float floorZ = VMAP_INVALID_HEIGHT;
Optional<AreaInfo> areaInfo;
Optional<LiquidInfo> liquidInfo;
};
//===========================================================
class IVMapManager
@ -79,8 +105,10 @@ namespace VMAP
Query world model area info.
\param z gets adjusted to the ground height for which this are info is valid
*/
virtual bool GetAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const = 0;
virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float& level, float& floor, uint32& type) const = 0;
virtual bool GetAreaInfo(uint32 pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const = 0;
virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float& level, float& floor, uint32& type, uint32& mogpFlags) const = 0;
// get both area + liquid data in a single vmap lookup
virtual void GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const = 0;
};
}

View file

@ -255,7 +255,7 @@ namespace VMAP
return VMAP_INVALID_HEIGHT_VALUE;
}
bool VMapManager2::GetAreaInfo(unsigned int mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
bool VMapManager2::GetAreaInfo(uint32 mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
#if defined(ENABLE_VMAP_CHECKS)
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG))
@ -275,7 +275,7 @@ namespace VMAP
return false;
}
bool VMapManager2::GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const
bool VMapManager2::GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type, uint32& mogpFlags) const
{
#if defined(ENABLE_VMAP_CHECKS)
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS))
@ -291,6 +291,7 @@ namespace VMAP
floor = info.ground_Z;
ASSERT(floor < std::numeric_limits<float>::max());
type = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc
mogpFlags = info.hitModel->GetMogpFlags();
if (reqLiquidType && !(GetLiquidFlagsPtr(type) & reqLiquidType))
{
return false;
@ -306,6 +307,38 @@ namespace VMAP
return false;
}
void VMapManager2::GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const
{
if (IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS))
{
data.floorZ = z;
int32 adtId, rootId, groupId;
uint32 flags;
if (GetAreaInfo(mapId, x, y, data.floorZ, flags, adtId, rootId, groupId))
data.areaInfo.emplace(adtId, rootId, groupId, flags);
return;
}
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;
uint32 liquidType = info.hitModel->GetLiquidType();
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.hitInstance->adtId, info.rootId, info.hitModel->GetWmoID(), info.hitModel->GetMogpFlags());
}
}
}
WorldModel* VMapManager2::acquireModelInstance(const std::string& basepath, const std::string& filename)
{
//! Critical section, thread safe access to iLoadedModelFiles

View file

@ -117,8 +117,9 @@ namespace VMAP
bool processCommand(char* /*command*/) override { return false; } // for debug and extensions
bool GetAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const override;
bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const override;
bool GetAreaInfo(uint32 pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const override;
bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type, uint32& mogpFlags) const override;
void GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const override;
WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename);
void releaseModelInstance(const std::string& filename);

View file

@ -23,6 +23,7 @@ namespace VMAP
const ModelInstance* hitInstance{nullptr};
const GroupModel* hitModel{nullptr};
float ground_Z;
int32 rootId = -1;
};
class StaticMapTree

View file

@ -6,6 +6,7 @@
#include "GameObjectModel.h"
#include "Log.h"
#include "MapTree.h"
#include "Timer.h"
#include "VMapDefinitions.h"
#include "VMapFactory.h"
@ -146,6 +147,7 @@ bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> model
#endif
owner = std::move(modelOwner);
isWmo = it->second.isWmo;
return true;
}
@ -187,6 +189,69 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto
return hit;
}
void GameObjectModel::IntersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, uint32 ph_mask) const
{
if (!(phasemask & ph_mask) || !owner->IsSpawned() || !IsMapObject())
return;
if (!iBound.contains(point))
return;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z)
info.ground_Z = world_Z;
}
}
bool GameObjectModel::GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, uint32 ph_mask) const
{
if (!(phasemask & ph_mask) || !owner->IsSpawned() || !IsMapObject())
return false;
if (!iBound.contains(point))
return false;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->GetLocationInfo(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z)
{
info.ground_Z = world_Z;
return true;
}
}
return false;
}
bool GameObjectModel::GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const
{
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
//Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (info.hitModel->GetLiquidLevel(pModel, zDist))
{
// calculate world height (zDist in model coords):
// assume WMO not tilted (wouldn't make much sense anyway)
liqHeight = zDist * iScale + iPos.z;
return true;
}
return false;
}
bool GameObjectModel::UpdatePosition()
{
if (!iModel)

View file

@ -16,6 +16,8 @@
namespace VMAP
{
class WorldModel;
struct AreaInfo;
struct LocationInfo;
}
class GameObject;
@ -37,7 +39,7 @@ public:
class GameObjectModel
{
GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(nullptr) { }
GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(nullptr), isWmo(false) { }
public:
std::string name;
@ -53,8 +55,12 @@ public:
void enable(uint32 ph_mask) { phasemask = ph_mask; }
[[nodiscard]] bool isEnabled() const { return phasemask != 0; }
[[nodiscard]] bool IsMapObject() const { return isWmo; }
bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const;
void IntersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, uint32 ph_mask) const;
bool GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, uint32 ph_mask) const;
bool GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const;
static GameObjectModel* Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
@ -71,6 +77,7 @@ private:
float iScale;
VMAP::WorldModel* iModel;
std::unique_ptr<GameObjectModelOwnerBase> owner;
bool isWmo;
};
void LoadGameObjectModelList(std::string const& dataPath);

View file

@ -412,7 +412,6 @@ namespace VMAP
{
return false;
}
GModelRayCallback callback(triangles, vertices);
Vector3 rPos = pos - 0.1f * down;
float dist = G3D::inf();
G3D::Ray ray(rPos, down);
@ -554,6 +553,7 @@ namespace VMAP
groupTree.intersectPoint(p, callback);
if (callback.hit != groupModels.end())
{
info.rootId = RootWMOID;
info.hitModel = &(*callback.hit);
dist = callback.zDist;
return true;

View file

@ -531,7 +531,7 @@ void BattlefieldWG::OnCreatureCreate(Creature* creature)
case NPC_TAUNKA_SPIRIT_GUIDE:
{
TeamId teamId = (creature->GetEntry() == NPC_DWARVEN_SPIRIT_GUIDE ? TEAM_ALLIANCE : TEAM_HORDE);
uint8 graveyardId = GetSpiritGraveyardId(creature->GetAreaId(true));
uint8 graveyardId = GetSpiritGraveyardId(creature->GetAreaId());
// xinef: little workaround, there are 2 spirit guides in same area
if (creature->IsWithinDist2d(5103.0f, 3461.5f, 5.0f))
graveyardId = BATTLEFIELD_WG_GY_WORKSHOP_NW;

View file

@ -1004,7 +1004,7 @@ namespace lfg
currInternalInfoMap[sitr->first] = RBInternalInfo(guid, sitr->second.comment, !groupGuid.IsEmpty(), groupGuid, sitr->second.roles, encounterMask, instanceGuid,
1, p->getLevel(), p->getClass(), p->getRace(), p->GetAverageItemLevel(),
talents, p->m_last_area_id, p->GetArmor(), (uint32)std::max<int32>(0, spellDamage), (uint32)std::max<int32>(0, spellHeal),
talents, p->GetAreaId(), p->GetArmor(), (uint32)std::max<int32>(0, spellDamage), (uint32)std::max<int32>(0, spellHeal),
p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_CRIT_MELEE)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_CRIT_RANGED)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_CRIT_SPELL)), std::max<float>(0.0f, mp5), std::max<float>(0.0f, mp5combat),
std::max<uint32>(baseAP, rangedAP), (uint32)p->GetStat(STAT_AGILITY), p->GetMaxHealth(), maxPower, p->GetDefenseSkillValue(),
p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_DODGE)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_BLOCK)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_PARRY)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_HASTE_SPELL)), p->GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + static_cast<uint16>(CR_EXPERTISE)));

View file

@ -519,8 +519,6 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
SetControlled(true, UNIT_STATE_ROOT);
}
UpdateEnvironmentIfNeeded(3);
SetDetectionDistance(cInfo->detection_range);
LoadSpellTemplateImmunity();
@ -959,16 +957,23 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
Relocate(x, y, z, ang);
//oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0;
if (!CreateFromProto(guidlow, Entry, vehId, data))
return false;
if (!IsPositionValid())
{
LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow %d, entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, Entry, x, y, z, ang);
return false;
}
// area/zone id is needed immediately for ZoneScript::GetCreatureEntry hook before it is known which creature template to load (no model/scale available yet)
PositionFullTerrainStatus terrainData;
GetMap()->GetFullTerrainStatusForPosition(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ(), DEFAULT_COLLISION_HEIGHT, terrainData);
ProcessPositionDataChanged(terrainData);
//oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0;
if (!CreateFromProto(guidlow, Entry, vehId, data))
return false;
UpdateMovementFlags();
switch (GetCreatureTemplate()->rank)
{
case CREATURE_ELITE_RARE:
@ -1812,6 +1817,8 @@ void Creature::setDeathState(DeathState s, bool despawn)
if (HasUnitMovementFlag(MOVEMENTFLAG_FALLING))
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
UpdateMovementFlags();
SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag);
ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~(UNIT_STATE_IGNORE_PATHFINDING | UNIT_STATE_NO_ENVIRONMENT_UPD)));
SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
@ -1822,8 +1829,6 @@ void Creature::setDeathState(DeathState s, bool despawn)
LoadCreaturesAddon(true);
if (GetCreatureData() && GetPhaseMask() != GetCreatureData()->phaseMask)
SetPhaseMask(GetCreatureData()->phaseMask, false);
UpdateEnvironmentIfNeeded(3);
}
}
@ -2866,16 +2871,13 @@ void Creature::applyInhabitFlags()
if (IsLevitating())
{
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_FLY);
return;
}
if (IsHovering())
else if (IsHovering())
{
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_HOVER);
return;
}
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND);
else
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND);
}
}
@ -3042,6 +3044,84 @@ float Creature::GetAggroRange(Unit const* target) const
return (aggroRadius * aggroRate);
}
void Creature::UpdateMovementFlags()
{
// Do not update movement flags if creature is controlled by a player (charm/vehicle)
if (m_movedByPlayer)
return;
CreatureTemplate const* info = GetCreatureTemplate();
if (!info)
return;
// Creatures with CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE should control MovementFlags in your own scripts
if (info->flags_extra & CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE)
return;
float z = GetPositionZ();
float ground = GetFloorZ();
bool isInAir = false;
bool Swim = false;
bool canHover = CanHover();
LiquidData const& liquidData = GetLiquidData();
if (liquidData.Status == LIQUID_MAP_NO_WATER)
{
if (ground > INVALID_HEIGHT)
isInAir = G3D::fuzzyGt(z, ground + (canHover ? GetFloatValue(UNIT_FIELD_HOVERHEIGHT) : 0.0f) + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(z, ground - GROUND_HEIGHT_TOLERANCE); // Can be underground too, prevent the falling
else
isInAir = true;
}
else
{
switch (liquidData.Status)
{
case LIQUID_MAP_ABOVE_WATER:
isInAir = true;
break;
case LIQUID_MAP_WATER_WALK:
isInAir = true;
[[fallthrough]];
case LIQUID_MAP_IN_WATER:
Swim = z - liquidData.DepthLevel > GetCollisionHeight() * 0.75f; // Shallow water at ~75% of collision height
break;
case LIQUID_MAP_UNDER_WATER:
Swim = true;
break;
default:
break;
}
}
SetSwim(CanSwim() && Swim);
if (info->InhabitType & INHABIT_AIR)
{
if (isInAir && !IsFalling())
{
if (info->InhabitType & INHABIT_GROUND)
SetCanFly(true);
else
SetDisableGravity(true);
if (!HasAuraType(SPELL_AURA_HOVER))
SetHover(false);
}
else
{
SetCanFly(false);
SetDisableGravity(false);
if (IsAlive() && (CanHover() || HasAuraType(SPELL_AURA_HOVER)))
SetHover(true);
}
}
else if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY) && IsAlive() && (CanHover() || HasAuraType(SPELL_AURA_HOVER)))
SetHover(true);
}
void Creature::SetObjectScale(float scale)
{
Unit::SetObjectScale(scale);

View file

@ -151,6 +151,8 @@ public:
[[nodiscard]] bool HasSpell(uint32 spellID) const override;
void UpdateMovementFlags();
bool UpdateEntry(uint32 entry, const CreatureData* data = nullptr, bool changelevel = true );
bool UpdateStats(Stats stat) override;
bool UpdateAllStats() override;

View file

@ -40,7 +40,7 @@ enum CreatureFlagsExtra : uint32
CREATURE_FLAG_EXTRA_NO_XP = 0x00000040, // creature kill does not provide XP
CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature
CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and 'attack me' effects
CREATURE_FLAG_EXTRA_UNUSED_10 = 0x00000200, // TODO: Implement CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE (creature won't update movement flags)
CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE = 0x00000200, // creature won't update movement flags
CREATURE_FLAG_EXTRA_GHOST_VISIBILITY = 0x00000400, // creature will only be visible to dead players
CREATURE_FLAG_EXTRA_UNUSED_12 = 0x00000800, // TODO: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks)
CREATURE_FLAG_EXTRA_NO_SELL_VENDOR = 0x00001000, // players can't sell items to this vendor
@ -65,8 +65,7 @@ enum CreatureFlagsExtra : uint32
CREATURE_FLAG_EXTRA_UNUSED_32 = 0x80000000,
// Masks
CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_10 | CREATURE_FLAG_EXTRA_UNUSED_12 |
CREATURE_FLAG_EXTRA_UNUSED_25 | CREATURE_FLAG_EXTRA_UNUSED_26 |
CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_12 | CREATURE_FLAG_EXTRA_UNUSED_25 | CREATURE_FLAG_EXTRA_UNUSED_26 |
CREATURE_FLAG_EXTRA_UNUSED_27 | CREATURE_FLAG_EXTRA_UNUSED_28 | CREATURE_FLAG_EXTRA_UNUSED_32),
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~(CREATURE_FLAG_EXTRA_UNUSED | CREATURE_FLAG_EXTRA_DUNGEON_BOSS))
};

View file

@ -97,6 +97,8 @@ bool DynamicObject::CreateDynamicObject(ObjectGuid::LowType guidlow, Unit* caste
WorldObject::_Create(guidlow, HighGuid::DynamicObject, caster->GetPhaseMask());
UpdatePositionData();
SetEntry(spellId);
SetObjectScale(1);
SetGuidValue(DYNAMICOBJECT_CASTER, caster->GetGUID());

View file

@ -241,6 +241,8 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
SetPhaseMask(phaseMask, false);
UpdatePositionData();
SetZoneScript();
if (m_zoneScript)
{

View file

@ -950,8 +950,8 @@ WorldObject::WorldObject(bool isWorldObject) : WorldLocation(),
elunaEvents(nullptr),
#endif
LastUsedScriptID(0), m_name(""), m_isActive(false), m_isVisibilityDistanceOverride(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr),
m_staticFloorZ(INVALID_HEIGHT), m_transport(nullptr), m_currMap(nullptr), m_InstanceId(0),
m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), m_notifyflags(0), m_executed_notifies(0)
_zoneId(0), _areaId(0), _floorZ(INVALID_HEIGHT), _outdoors(false), _liquidData(), _updatePositionData(false), m_transport(nullptr),
m_currMap(nullptr), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), m_notifyflags(0), m_executed_notifies(0)
{
m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST);
m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE);
@ -1040,19 +1040,51 @@ void WorldObject::_Create(ObjectGuid::LowType guidlow, HighGuid guidhigh, uint32
SetPhaseMask(phaseMask, false);
}
uint32 WorldObject::GetZoneId(bool /*forceRecalc*/) const
void WorldObject::SetPositionDataUpdate()
{
return GetBaseMap()->GetZoneId(m_positionX, m_positionY, m_positionZ);
_updatePositionData = true;
// Calls immediately for charmed units
if (GetTypeId() == TYPEID_UNIT && ToUnit()->IsCharmedOwnedByPlayerOrPlayer())
UpdatePositionData();
}
uint32 WorldObject::GetAreaId(bool /*forceRecalc*/) const
void WorldObject::UpdatePositionData()
{
return GetBaseMap()->GetAreaId(m_positionX, m_positionY, m_positionZ);
_updatePositionData = false;
PositionFullTerrainStatus data;
GetMap()->GetFullTerrainStatusForPosition(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ(), GetCollisionHeight(), data);
ProcessPositionDataChanged(data);
}
void WorldObject::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool /*forceRecalc*/) const
void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& data)
{
GetBaseMap()->GetZoneAndAreaId(zoneid, areaid, m_positionX, m_positionY, m_positionZ);
_zoneId = _areaId = data.areaId;
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(_areaId))
if (area->zone)
_zoneId = area->zone;
_outdoors = data.outdoors;
_floorZ = data.floorZ;
_liquidData = data.liquidInfo;
}
void WorldObject::AddToWorld()
{
Object::AddToWorld();
GetMap()->GetZoneAndAreaId(GetPhaseMask(), _zoneId, _areaId, GetPositionX(), GetPositionY(), GetPositionZ());
}
void WorldObject::RemoveFromWorld()
{
if (!IsInWorld())
return;
DestroyForNearbyPlayers();
Object::RemoveFromWorld();
}
InstanceScript* WorldObject::GetInstanceScript()
@ -1470,7 +1502,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z, float* grou
if (max_z > INVALID_HEIGHT)
{
if (canSwim && unit->GetMap()->IsInWater(x, y, max_z - Z_OFFSET_FIND_HEIGHT))
if (canSwim && unit->GetMap()->IsInWater(unit->GetPhaseMask(), x, y, max_z - Z_OFFSET_FIND_HEIGHT, unit->GetCollisionHeight()))
{
// do not allow creatures to walk on
// water level while swimming
@ -2137,12 +2169,6 @@ void WorldObject::ResetMap()
//m_InstanceId = 0;
}
Map const* WorldObject::GetBaseMap() const
{
ASSERT(m_currMap);
return m_currMap->GetParent();
}
void WorldObject::AddObjectToRemoveList()
{
ASSERT(m_uint32Values);
@ -2307,7 +2333,7 @@ void WorldObject::SetZoneScript()
m_zoneScript = (ZoneScript*)map->ToInstanceMap()->GetInstanceScript();
else if (!map->IsBattlegroundOrArena())
{
uint32 zoneId = GetZoneId(true);
uint32 zoneId = GetZoneId();
if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(zoneId))
m_zoneScript = bf;
else
@ -3031,10 +3057,54 @@ float WorldObject::GetMapWaterOrGroundLevel(float x, float y, float z, float* gr
float WorldObject::GetFloorZ() const
{
if (!IsInWorld())
return m_staticFloorZ;
if (_updatePositionData)
const_cast<WorldObject*>(this)->UpdatePositionData();
return std::max<float>(m_staticFloorZ, GetMap()->GetGameObjectFloor(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ() + std::max(GetCollisionHeight(), Z_OFFSET_FIND_HEIGHT)));
if (!IsInWorld())
return _floorZ;
return std::max<float>(_floorZ, GetMap()->GetGameObjectFloor(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ() + std::max(GetCollisionHeight(), Z_OFFSET_FIND_HEIGHT)));
}
uint32 WorldObject::GetZoneId() const
{
if (_updatePositionData)
const_cast<WorldObject*>(this)->UpdatePositionData();
return _zoneId;
}
uint32 WorldObject::GetAreaId() const
{
if (_updatePositionData)
const_cast<WorldObject*>(this)->UpdatePositionData();
return _areaId;
}
void WorldObject::GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const
{
if (_updatePositionData)
const_cast<WorldObject*>(this)->UpdatePositionData();
zoneid = _zoneId;
areaid = _areaId;
}
bool WorldObject::IsOutdoors() const
{
if (_updatePositionData)
const_cast<WorldObject*>(this)->UpdatePositionData();
return _outdoors;
}
LiquidData const& WorldObject::GetLiquidData() const
{
if (_updatePositionData)
const_cast<WorldObject*>(this)->UpdatePositionData();
return _liquidData;
}
void WorldObject::AddAllowedLooter(ObjectGuid guid)

View file

@ -69,6 +69,8 @@ class Transport;
class StaticTransport;
class MotionTransport;
struct PositionFullTerrainStatus;
typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType;
typedef GuidUnorderedSet UpdatePlayerSet;
@ -750,15 +752,8 @@ public:
#endif
void _Create(ObjectGuid::LowType guidlow, HighGuid guidhigh, uint32 phaseMask);
void RemoveFromWorld() override
{
if (!IsInWorld())
return;
DestroyForNearbyPlayers();
Object::RemoveFromWorld();
}
void AddToWorld() override;
void RemoveFromWorld() override;
#ifdef ELUNA
ElunaEventProcessor* elunaEvents;
@ -817,9 +812,11 @@ public:
bool InSamePhase(WorldObject const* obj) const { return InSamePhase(obj->GetPhaseMask()); }
[[nodiscard]] bool InSamePhase(uint32 phasemask) const { return m_useCombinedPhases ? GetPhaseMask() & phasemask : GetPhaseMask() == phasemask; }
[[nodiscard]] virtual uint32 GetZoneId(bool forceRecalc = false) const;
[[nodiscard]] virtual uint32 GetAreaId(bool forceRecalc = false) const;
virtual void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc = false) const;
[[nodiscard]] uint32 GetZoneId() const;
[[nodiscard]] uint32 GetAreaId() const;
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const;
[[nodiscard]] bool IsOutdoors() const;
LiquidData const& GetLiquidData() const;
InstanceScript* GetInstanceScript();
@ -948,9 +945,6 @@ public:
[[nodiscard]] Map* FindMap() const { return m_currMap; }
//used to check all object's GetMap() calls when object is not in world!
//this function should be removed in nearest time...
[[nodiscard]] Map const* GetBaseMap() const;
void SetZoneScript();
void ClearZoneScript();
[[nodiscard]] ZoneScript* GetZoneScript() const { return m_zoneScript; }
@ -984,6 +978,9 @@ public:
void BuildUpdate(UpdateDataMapType& data_map, UpdatePlayerSet& player_set) override;
void GetCreaturesWithEntryInRange(std::list<Creature*>& creatureList, float radius, uint32 entry);
void SetPositionDataUpdate();
void UpdatePositionData();
void AddToObjectUpdate() override;
void RemoveFromObjectUpdate() override;
@ -1060,7 +1057,13 @@ protected:
const bool m_isWorldObject;
ZoneScript* m_zoneScript;
float m_staticFloorZ;
virtual void ProcessPositionDataChanged(PositionFullTerrainStatus const& data);
uint32 _zoneId;
uint32 _areaId;
float _floorZ;
bool _outdoors;
LiquidData _liquidData;
bool _updatePositionData;
// transports
Transport* m_transport;

View file

@ -666,6 +666,8 @@ bool Pet::CreateBaseAtCreature(Creature* creature)
SetDisplayId(creature->GetDisplayId());
UpdatePositionData();
if (CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family))
SetName(cFamily->Name[sWorld->GetDefaultDbcLocale()]);
else
@ -684,6 +686,8 @@ bool Pet::CreateBaseAtCreatureInfo(CreatureTemplate const* cinfo, Unit* owner)
Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation());
UpdatePositionData();
return true;
}

View file

@ -591,6 +591,8 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
InitTalentForLevel();
InitPrimaryProfessions(); // to max set before any spell added
UpdatePositionData();
// apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
SetFullHealth();
@ -876,7 +878,7 @@ void Player::HandleDrowning(uint32 time_diff)
}
// In dark water
if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER)
if (m_MirrorTimerFlags & UNDERWATER_INDARKWATER)
{
// Fatigue timer not activated - activate it
if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER)
@ -899,7 +901,7 @@ void Player::HandleDrowning(uint32 time_diff)
else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard
RepopAtGraveyard();
}
else if (!(m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER))
else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER))
SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER), m_MirrorTimer[FATIGUE_TIMER], -1);
}
}
@ -909,7 +911,7 @@ void Player::HandleDrowning(uint32 time_diff)
m_MirrorTimer[FATIGUE_TIMER] += 10 * time_diff;
if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !IsAlive())
StopMirrorTimer(FATIGUE_TIMER);
else if (m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER)
else if (m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER)
SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10);
}
@ -2066,31 +2068,6 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTy
return nullptr;
}
bool Player::IsInWater(bool allowAbove) const
{
if (m_isInWater || !allowAbove)
return m_isInWater;
float distsq = GetExactDistSq(&m_last_environment_position);
if (distsq < 3.0f * 3.0f)
return m_last_islittleabovewater_status;
else
{
LiquidData liqData;
liqData.level = INVALID_HEIGHT;
const_cast<Position*>(&m_last_environment_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
bool inWater = GetBaseMap()->IsInWater(GetPositionX(), GetPositionY(), GetPositionZ(), &liqData);
*(const_cast<bool*>(&m_last_islittleabovewater_status)) = inWater || (liqData.level > INVALID_HEIGHT && liqData.level > liqData.depth_level && liqData.level <= GetPositionZ() + 3.0f && liqData.level > GetPositionZ() - 1.0f);
return m_last_islittleabovewater_status;
}
}
bool Player::IsUnderWater() const
{
return IsInWater() &&
GetPositionZ() < GetBaseMap()->GetWaterLevel(GetPositionX(), GetPositionY()) - GetCollisionHeight();
}
bool Player::IsFalling() const
{
// Xinef: Added !IsInFlight check
@ -4355,7 +4332,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
// trigger update zone for alive state zone updates
uint32 newzone, newarea;
GetZoneAndAreaId(newzone, newarea, true);
GetZoneAndAreaId(newzone, newarea);
UpdateZone(newzone, newarea);
sOutdoorPvPMgr->HandlePlayerResurrects(this, newzone);
@ -4508,6 +4485,8 @@ Corpse* Player::CreateCorpse()
// register for player, but not show
GetMap()->AddCorpse(corpse);
UpdatePositionData();
// we do not need to save corpses for BG/arenas
if (!GetMap()->IsBattlegroundOrArena())
corpse->SaveToDB();
@ -5563,7 +5542,7 @@ void Player::CheckAreaExploreAndOutdoor()
return;
bool isOutdoor = IsOutdoors();
uint32 areaId = GetBaseMap()->GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor);
uint32 areaId = GetAreaId();
AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId);
if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor)
@ -6159,7 +6138,7 @@ uint32 Player::GetZoneIdFromDB(ObjectGuid guid)
if (!sMapStore.LookupEntry(map))
return 0;
zone = sMapMgr->GetZoneId(map, posx, posy, posz);
zone = sMapMgr->GetZoneId(PHASEMASK_NORMAL, map, posx, posy, posz);
if (zone > 0)
{
@ -6184,36 +6163,6 @@ uint32 Player::GetLevelFromStorage(ObjectGuid::LowType guid)
return 0;
}
uint32 Player::GetZoneId(bool forceRecalc) const
{
if (forceRecalc)
*(const_cast<uint32*>(&m_last_zone_id)) = WorldObject::GetZoneId();
return m_last_zone_id;
}
uint32 Player::GetAreaId(bool forceRecalc) const
{
if (forceRecalc)
*(const_cast<uint32*>(&m_last_area_id)) = WorldObject::GetAreaId();
return m_last_area_id;
}
void Player::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc) const
{
if (forceRecalc)
{
WorldObject::GetZoneAndAreaId(zoneid, areaid);
*(const_cast<uint32*>(&m_last_zone_id)) = zoneid;
*(const_cast<uint32*>(&m_last_area_id)) = areaid;
return;
}
zoneid = m_last_zone_id;
areaid = m_last_area_id;
}
//If players are too far away from the duel flag... they lose the duel
void Player::CheckDuelDistance(time_t currTime)
{
@ -10849,7 +10798,7 @@ void Player::SendInitialPacketsAfterAddToMap()
// update zone
uint32 newzone, newarea;
GetZoneAndAreaId(newzone, newarea, true);
GetZoneAndAreaId(newzone, newarea);
UpdateZone(newzone, newarea); // also call SendInitWorldStates();
if (HasAuraType(SPELL_AURA_MOD_STUN))
@ -13758,7 +13707,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
stmt->setUInt16(index++, GetZoneId(true));
stmt->setUInt16(index++, GetZoneId());
stmt->setUInt32(index++, uint32(m_deathExpireTime));
ss.str("");
@ -13896,7 +13845,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
stmt->setUInt16(index++, GetZoneId(true));
stmt->setUInt16(index++, GetZoneId());
stmt->setUInt32(index++, uint32(m_deathExpireTime));
ss.str("");

View file

@ -85,7 +85,7 @@ enum PlayerUnderwaterState
UNDERWATER_INWATER = 0x01, // terrain type is water and player is afflicted by it
UNDERWATER_INLAVA = 0x02, // terrain type is lava and player is afflicted by it
UNDERWATER_INSLIME = 0x04, // terrain type is lava and player is afflicted by it
UNDERWARER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it
UNDERWATER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it
UNDERWATER_EXIST_TIMERS = 0x10
};
@ -1021,8 +1021,7 @@ public:
void SetInWater(bool apply);
[[nodiscard]] bool IsInWater(bool allowAbove = false) const override;
[[nodiscard]] bool IsUnderWater() const override;
[[nodiscard]] bool IsInWater() const override { return m_isInWater; }
[[nodiscard]] bool IsFalling() const;
bool IsInAreaTriggerRadius(const AreaTrigger* trigger) const;
@ -1737,10 +1736,6 @@ public:
void UpdateZone(uint32 newZone, uint32 newArea);
void UpdateArea(uint32 newArea);
[[nodiscard]] uint32 GetZoneId(bool forceRecalc = false) const override;
[[nodiscard]] uint32 GetAreaId(bool forceRecalc = false) const override;
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc = false) const override;
void UpdateZoneDependentAuras(uint32 zone_id); // zones
void UpdateAreaDependentAuras(uint32 area_id); // subzones
@ -1905,7 +1900,8 @@ public:
bool UpdatePosition(float x, float y, float z, float orientation, bool teleport = false) override;
bool UpdatePosition(const Position& pos, bool teleport = false) { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); }
void UpdateUnderwaterState(Map* m, float x, float y, float z) override;
void ProcessTerrainStatusUpdate() override;
void SendMessageToSet(WorldPacket* data, bool self) override { SendMessageToSetInRange(data, GetVisibilityRange(), self, true); } // pussywizard!
void SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool includeMargin = false, Player const* skipped_rcvr = nullptr) override; // pussywizard!

View file

@ -5241,6 +5241,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
SetMap(map);
StoreRaidMapDifficulty();
UpdatePositionData();
SaveRecallPosition();
time_t now = time(nullptr);
@ -5772,7 +5774,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
if (result)
{
uint32 zoneId = GetZoneId(true);
uint32 zoneId = GetZoneId();
std::map<ObjectGuid::LowType, Bag*> bagMap; // fast guid lookup for bags
std::map<ObjectGuid::LowType, Item*> invalidBagMap; // fast guid lookup for bags

View file

@ -268,9 +268,7 @@ void Player::Update(uint32 p_time)
}
uint32 newzone, newarea;
GetZoneAndAreaId(newzone, newarea, true);
m_last_zone_id = newzone;
m_last_area_id = newarea;
GetZoneAndAreaId(newzone, newarea);
if (m_zoneUpdateId != newzone)
UpdateZone(newzone, newarea); // also update area
@ -354,7 +352,7 @@ void Player::Update(uint32 p_time)
}
// not auto-free ghost from body in instances
if (m_deathTimer > 0 && !GetBaseMap()->Instanceable() &&
if (m_deathTimer > 0 && !GetMap()->Instanceable() &&
!HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
{
if (p_time >= m_deathTimer)
@ -1975,96 +1973,6 @@ void Player::UpdateCorpseReclaimDelay()
m_deathExpireTime = now + DEATH_EXPIRE_STEP;
}
void Player::UpdateUnderwaterState(Map* m, float x, float y, float z)
{
// pussywizard: optimization
if (GetExactDistSq(&m_last_underwaterstate_position) < 3.0f * 3.0f)
return;
m_last_underwaterstate_position.Relocate(m_positionX, m_positionY,
m_positionZ);
if (!IsPositionValid()) // pussywizard: crashfix if calculated grid coords
// would be out of range 0-64
return;
LiquidData liquid_status;
ZLiquidStatus res = m->getLiquidStatus(
x, y, z, MAP_ALL_LIQUIDS, &liquid_status, GetCollisionHeight());
if (!res)
{
m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA |
UNDERWATER_INSLIME | UNDERWARER_INDARKWATER);
if (_lastLiquid && _lastLiquid->SpellId)
RemoveAurasDueToSpell(_lastLiquid->SpellId);
_lastLiquid = nullptr;
return;
}
if (uint32 liqEntry = liquid_status.entry)
{
LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry);
if (_lastLiquid && _lastLiquid->SpellId && _lastLiquid->Id != liqEntry)
RemoveAurasDueToSpell(_lastLiquid->SpellId);
if (liquid && liquid->SpellId)
{
if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER))
{
if (!HasAura(liquid->SpellId))
CastSpell(this, liquid->SpellId, true);
}
else
RemoveAurasDueToSpell(liquid->SpellId);
}
_lastLiquid = liquid;
}
else if (_lastLiquid && _lastLiquid->SpellId)
{
RemoveAurasDueToSpell(_lastLiquid->SpellId);
_lastLiquid = nullptr;
}
// All liquids type - check under water position
if (liquid_status.type_flags &
(MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA |
MAP_LIQUID_TYPE_SLIME))
{
if (res & LIQUID_MAP_UNDER_WATER)
m_MirrorTimerFlags |= UNDERWATER_INWATER;
else
m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
}
// Allow travel in dark water on taxi or transport
if ((liquid_status.type_flags & MAP_LIQUID_TYPE_DARK_WATER) &&
!IsInFlight() && !GetTransport())
m_MirrorTimerFlags |= UNDERWARER_INDARKWATER;
else
m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER;
// in lava check, anywhere in lava level
if (liquid_status.type_flags & MAP_LIQUID_TYPE_MAGMA)
{
if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER |
LIQUID_MAP_WATER_WALK))
m_MirrorTimerFlags |= UNDERWATER_INLAVA;
else
m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
}
// in slime check, anywhere in slime level
if (liquid_status.type_flags & MAP_LIQUID_TYPE_SLIME)
{
if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER |
LIQUID_MAP_WATER_WALK))
m_MirrorTimerFlags |= UNDERWATER_INSLIME;
else
m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
}
}
void Player::UpdateCharmedAI()
{
// Xinef: maybe passed as argument?
@ -2361,3 +2269,58 @@ void Player::SendUpdateWorldState(uint32 Field, uint32 Value)
data << Value;
GetSession()->SendPacket(&data);
}
void Player::ProcessTerrainStatusUpdate()
{
// process liquid auras using generic unit code
Unit::ProcessTerrainStatusUpdate();
LiquidData const& liquidData = GetLiquidData();
// player specific logic for mirror timers
if (liquidData.Status != LIQUID_MAP_NO_WATER)
{
// Breath bar state (under water in any liquid type)
if ((liquidData.Flags & MAP_ALL_LIQUIDS) != 0)
{
if ((liquidData.Status & LIQUID_MAP_UNDER_WATER) != 0)
m_MirrorTimerFlags |= UNDERWATER_INWATER;
else
m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
}
// Fatigue bar state (if not on flight path or transport)
if ((liquidData.Flags & MAP_LIQUID_TYPE_DARK_WATER) && !IsInFlight() && !GetTransport())
{
// Exclude also uncontrollable vehicles
Vehicle* vehicle = GetVehicle();
VehicleSeatEntry const* vehicleSeat = vehicle ? vehicle->GetSeatForPassenger(this) : nullptr;
if (!vehicleSeat || vehicleSeat->CanControl())
m_MirrorTimerFlags |= UNDERWATER_INDARKWATER;
else
m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER;
}
else
m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER;
// Lava state (any contact)
if (liquidData.Flags & MAP_LIQUID_TYPE_MAGMA)
{
if (liquidData.Status & MAP_LIQUID_STATUS_IN_CONTACT)
m_MirrorTimerFlags |= UNDERWATER_INLAVA;
else
m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
}
// Slime state (any contact)
if (liquidData.Flags & MAP_LIQUID_TYPE_SLIME)
{
if (liquidData.Status & MAP_LIQUID_STATUS_IN_CONTACT)
m_MirrorTimerFlags |= UNDERWATER_INSLIME;
else
m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
}
}
else
m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWATER_INDARKWATER);
}

View file

@ -91,6 +91,7 @@ bool MotionTransport::CreateMoTrans(ObjectGuid::LowType guidlow, uint32 entry, u
SetTransportPathRotation(0.0f, 0.0f, 0.0f, 1.0f);
m_model = CreateModel();
return true;
}
@ -676,6 +677,8 @@ bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* m
SetPhaseMask(phaseMask, false);
UpdatePositionData();
SetZoneScript();
if (m_zoneScript)
{

View file

@ -284,18 +284,6 @@ Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject),
m_delayed_unit_relocation_timer = 0;
m_delayed_unit_ai_notify_timer = 0;
bRequestForcedVisibilityUpdate = false;
m_last_underwaterstate_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
m_last_environment_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
m_last_isinwater_status = false;
m_last_islittleabovewater_status = false;
m_last_isunderwater_status = false;
m_is_updating_environment = false;
m_last_area_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
m_last_zone_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
m_last_area_id = 0;
m_last_zone_id = 0;
m_last_outdoors_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
m_last_outdoors_status = true; // true by default
m_applyResilience = false;
_instantCast = false;
@ -528,8 +516,13 @@ void Unit::UpdateSplineMovement(uint32 t_diff)
bool arrived = movespline->Finalized();
if (arrived)
{
DisableSpline();
if (movespline->HasAnimation() && GetTypeId() == TYPEID_UNIT && IsAlive())
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, movespline->GetAnimationType());
}
// pussywizard: update always! not every 400ms, because movement generators need the actual position
//m_movesplineTimer.Update(t_diff);
//if (m_movesplineTimer.Passed() || arrived)
@ -3636,230 +3629,48 @@ bool Unit::isInAccessiblePlaceFor(Creature const* c) const
if (IsInWater())
return IsUnderWater() ? c->CanEnterWater() : (c->CanEnterWater() || c->CanFly());
else
return c->CanWalk() || c->CanFly() || (c->CanSwim() && IsInWater(true));
return c->CanWalk() || c->CanFly() || (c->CanSwim() && IsInWater());
}
void Unit::UpdateEnvironmentIfNeeded(const uint8 option)
void Unit::ProcessPositionDataChanged(PositionFullTerrainStatus const& data)
{
if (m_is_updating_environment)
WorldObject::ProcessPositionDataChanged(data);
ProcessTerrainStatusUpdate();
}
void Unit::ProcessTerrainStatusUpdate()
{
if (GetTypeId() == TYPEID_UNIT)
ToCreature()->UpdateMovementFlags();
if (IsFlying() || (!IsControlledByPlayer()))
return;
if (GetTypeId() != TYPEID_UNIT || !IsAlive() || (!IsInWorld() && option != 3) || !FindMap() || IsDuringRemoveFromWorld() || !IsPositionValid())
return;
LiquidData const& liquidData = GetLiquidData();
if (option <= 2 && GetMotionMaster()->GetCleanFlags() != MMCF_NONE)
// remove appropriate auras if we are swimming/not swimming respectively
if (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING)
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_ABOVEWATER);
else
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
// liquid aura handling
LiquidTypeEntry const* curLiquid = nullptr;
if ((liquidData.Status & MAP_LIQUID_STATUS_SWIMMING))
curLiquid = sLiquidTypeStore.LookupEntry(liquidData.Entry);
if (curLiquid != _lastLiquid)
{
if (option == 2)
m_last_environment_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
return;
if (_lastLiquid && _lastLiquid->SpellId)
RemoveAurasDueToSpell(_lastLiquid->SpellId);
// Set _lastLiquid before casting liquid spell to avoid infinite loops
_lastLiquid = curLiquid;
Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
if (curLiquid && curLiquid->SpellId && (!player || !player->IsGameMaster()))
CastSpell(this, curLiquid->SpellId, true);
}
// run environment checks everytime the unit moves
// more than it's average radius
// TODO: find better solution here
float radiusWidth = GetCollisionRadius();
float radiusHeight = GetCollisionHeight() / 2;
float radiusAvg = (radiusWidth + radiusHeight) / 2;
if (option <= 1 && GetExactDistSq(&m_last_environment_position) < radiusAvg * radiusAvg)
return;
m_last_environment_position.Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
m_staticFloorZ = GetMap()->GetHeight(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ());
m_is_updating_environment = true;
bool changed = false;
Map* baseMap = const_cast<Map*>(GetBaseMap());
Creature* c = this->ToCreature();
if (!c || !baseMap)
{
m_is_updating_environment = false;
return;
}
bool canChangeFlying = option == 3 || ((c->GetScriptId() == 0 || GetInstanceId() == 0) && GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE);
bool canFallGround = option == 0 && canChangeFlying && GetInstanceId() == 0 && !IsInCombat() && !GetVehicle() && !GetTransport() && !HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && !c->IsTrigger() && !c->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) && GetMotionMaster()->GetCurrentMovementGeneratorType() <= RANDOM_MOTION_TYPE && !HasUnitState(UNIT_STATE_EVADE) && !IsControlledByPlayer();
float x = GetPositionX(), y = GetPositionY(), z = GetPositionZ();
bool isInAir = true;
float ground_z = z;
LiquidData liquidData;
liquidData.level = INVALID_HEIGHT;
ZLiquidStatus liquidStatus = baseMap->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData);
// IsInWater
bool enoughWater = baseMap->HasEnoughWater(this, liquidData);
m_last_isinwater_status = (liquidStatus & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) && enoughWater;
m_last_islittleabovewater_status = (liquidData.level > INVALID_HEIGHT && liquidData.level > liquidData.depth_level && liquidData.level <= z + 3.0f && liquidData.level > z - 1.0f);
// IsUnderWater
m_last_isunderwater_status = (liquidStatus & LIQUID_MAP_UNDER_WATER) && enoughWater;
// UpdateUnderwaterState
if (IsPet() || IsVehicle())
{
if (option == 1) // Unit::IsInWater, Unit::IsUnderwater, adding/removing auras can cause crashes (eg threat change while iterating threat table), so skip
m_last_environment_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
else
{
if (!liquidStatus)
{
if (_lastLiquid && _lastLiquid->SpellId)
RemoveAurasDueToSpell(_lastLiquid->SpellId);
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
_lastLiquid = nullptr;
}
else if (uint32 liqEntry = liquidData.entry)
{
LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry);
if (_lastLiquid && _lastLiquid->SpellId && _lastLiquid->Id != liqEntry)
RemoveAurasDueToSpell(_lastLiquid->SpellId);
if (liquid && liquid->SpellId)
{
if (liquidStatus & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER))
{
if (!HasAura(liquid->SpellId))
CastSpell(this, liquid->SpellId, true);
}
else
RemoveAurasDueToSpell(liquid->SpellId);
}
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_ABOVEWATER);
_lastLiquid = liquid;
}
else if (_lastLiquid && _lastLiquid->SpellId)
{
RemoveAurasDueToSpell(_lastLiquid->SpellId);
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
_lastLiquid = nullptr;
}
}
}
bool canUpdateEnvironment = !HasUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD);
bool flyingBarelyInWater = false;
// Refresh being in water
if (m_last_isinwater_status)
{
if (!c->CanFly() || enoughWater)
{
if (canUpdateEnvironment && c->CanSwim() && (!HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) || !HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY)))
{
SetSwim(true);
changed = true;
}
isInAir = false;
}
else
{
m_last_isinwater_status = false;
flyingBarelyInWater = true;
}
}
if (!m_last_isinwater_status)
{
if (canUpdateEnvironment && c->CanWalk() && HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING))
{
SetSwim(false);
changed = true;
}
}
// if not in water, check whether in air or not
if (isInAir)
{
if (GetMap()->GetGrid(x, y))
{
float temp = GetFloorZ();
if (temp > INVALID_HEIGHT)
{
ground_z = (c->CanSwim() && liquidData.level > INVALID_HEIGHT) ? liquidData.level : temp;
bool canHover = c->CanHover();
isInAir = flyingBarelyInWater || (G3D::fuzzyGt(GetPositionZ(), ground_z + (canHover ? GetFloatValue(UNIT_FIELD_HOVERHEIGHT) : 0.0f) + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(GetPositionZ(), ground_z - GROUND_HEIGHT_TOLERANCE)); // Can be underground too, prevent the falling
}
else
isInAir = true;
}
else
{
m_is_updating_environment = false;
return;
}
}
if (canUpdateEnvironment && canChangeFlying)
{
// xinef: summoned vehicles are treated as always in air, fixes flying on such units
if (IsVehicle() && !c->GetSpawnId())
isInAir = true;
// xinef: triggers with inhabit type air are treated as always in air
if (c->IsTrigger() && c->CanFly())
isInAir = true;
if (c->GetOwnerGUID().IsPlayer() && c->CanFly() && IsVehicle() && !c->GetSpawnId()) // mainly for oculus drakes
{
if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY) || !HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
{
SetCanFly(true);
SetDisableGravity(true);
changed = true;
}
}
else if (c->CanFly() && isInAir && !c->IsFalling())
{
if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY) || !HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
{
SetCanFly(true);
SetDisableGravity(true);
changed = true;
}
if (IsHovering() && !HasAuraType(SPELL_AURA_HOVER))
{
SetHover(false);
changed = true;
}
}
else
{
if (HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY) || HasUnitMovementFlag(MOVEMENTFLAG_FLYING))
{
SetCanFly(false);
RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
changed = true;
}
if (!IsHovering() && IsAlive() && (c->CanHover() || HasAuraType(SPELL_AURA_HOVER)))
{
SetHover(true);
changed = true;
}
if (HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY) && !HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING))
{
SetDisableGravity(false);
changed = true;
}
}
if (isInAir && !c->CanFly() && option >= 2)
m_last_environment_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f);
}
if (!isInAir && HasUnitMovementFlag(MOVEMENTFLAG_FALLING))
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
if (changed)
propagateSpeedChange();
if (canUpdateEnvironment && canFallGround && !c->CanFly() && !c->IsFalling() && !m_last_isinwater_status && (c->GetUnitMovementFlags() & (MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_HOVER | MOVEMENTFLAG_SWIMMING)) == 0 && z - ground_z > 5.0f && z - ground_z < 80.0f)
GetMotionMaster()->MoveFall();
m_is_updating_environment = false;
}
SafeUnitPointer::~SafeUnitPointer()
@ -3906,20 +3717,14 @@ void Unit::HandleSafeUnitPointersOnDelete(Unit* thisUnit)
thisUnit->SafeUnitPointerSet.clear();
}
bool Unit::IsInWater(bool allowAbove) const
bool Unit::IsInWater() const
{
const_cast<Unit*>(this)->UpdateEnvironmentIfNeeded(1);
return m_last_isinwater_status || (allowAbove && m_last_islittleabovewater_status);
return (GetLiquidData().Status & MAP_LIQUID_STATUS_SWIMMING) != 0;
}
bool Unit::IsUnderWater() const
{
const_cast<Unit*>(this)->UpdateEnvironmentIfNeeded(1);
return m_last_isunderwater_status;
}
void Unit::UpdateUnderwaterState(Map* /*m*/, float /*x*/, float /*y*/, float /*z*/)
{
return GetLiquidData().Status == LIQUID_MAP_UNDER_WATER;
}
void Unit::DeMorph()
@ -12996,8 +12801,6 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy, uint32 duration)
if (Creature* creature = ToCreature())
{
creature->UpdateEnvironmentIfNeeded(2);
// Set home position at place of engaging combat for escorted creatures
if ((IsAIEnabled && creature->AI()->IsEscorted()) ||
GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE ||
@ -19584,62 +19387,6 @@ bool ConflagrateAuraStateDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time
return true;
}
uint32 Unit::GetZoneId(bool forceRecalc) const
{
// xinef: optimization, zone calculated every few yards
if (!forceRecalc && GetExactDistSq(&m_last_zone_position) < 4.0f * 4.0f)
return m_last_zone_id;
else
{
const_cast<Position*>(&m_last_zone_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
*(const_cast<uint32*>(&m_last_zone_id)) = WorldObject::GetZoneId();
return m_last_zone_id;
}
}
uint32 Unit::GetAreaId(bool forceRecalc) const
{
// xinef: optimization, area calculated every few yards
if (!forceRecalc && GetExactDistSq(&m_last_area_position) < 4.0f * 4.0f)
return m_last_area_id;
else
{
const_cast<Position*>(&m_last_area_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
*(const_cast<uint32*>(&m_last_area_id)) = WorldObject::GetAreaId();
return m_last_area_id;
}
}
void Unit::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc) const
{
// xinef: optimization, zone and area calculated every few yards
if (!forceRecalc && GetExactDistSq(&m_last_area_position) < 4.0f * 4.0f && GetExactDistSq(&m_last_zone_position) < 4.0f * 4.0f)
{
zoneid = m_last_zone_id;
areaid = m_last_area_id;
return;
}
const_cast<Position*>(&m_last_zone_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
const_cast<Position*>(&m_last_area_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
WorldObject::GetZoneAndAreaId(zoneid, areaid);
*(const_cast<uint32*>(&m_last_zone_id)) = zoneid;
*(const_cast<uint32*>(&m_last_area_id)) = areaid;
}
bool Unit::IsOutdoors() const
{
// xinef: optimization, outdoor status calculated every few yards
if (GetExactDistSq(&m_last_outdoors_position) < 4.0f * 4.0f)
return m_last_outdoors_status;
else
{
const_cast<Position*>(&m_last_outdoors_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
*(const_cast<bool*>(&m_last_outdoors_status)) = GetMap()->IsOutdoors(GetPositionX(), GetPositionY(), GetPositionZ());
return m_last_outdoors_status;
}
}
void Unit::ExecuteDelayedUnitRelocationEvent()
{
this->RemoveFromNotify(NOTIFY_VISIBILITY_CHANGED);

View file

@ -1772,10 +1772,8 @@ public:
bool IsValidAssistTarget(Unit const* target) const;
bool _IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const;
void UpdateEnvironmentIfNeeded(const uint8 option);
[[nodiscard]] virtual bool IsInWater(bool allowAbove = false) const;
[[nodiscard]] virtual bool IsInWater() const;
[[nodiscard]] virtual bool IsUnderWater() const;
virtual void UpdateUnderwaterState(Map* m, float x, float y, float z);
bool isInAccessiblePlaceFor(Creature const* c) const;
void SendHealSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, uint32 OverHeal, uint32 Absorb, bool critical = false);
@ -2454,25 +2452,6 @@ public:
bool bRequestForcedVisibilityUpdate;
void ExecuteDelayedUnitRelocationEvent();
void ExecuteDelayedUnitAINotifyEvent();
// IsInWater, UpdateUnderwaterState, etc. optimizations
Position m_last_underwaterstate_position;
Position m_last_environment_position;
bool m_last_isinwater_status;
bool m_last_islittleabovewater_status;
bool m_last_isunderwater_status;
bool m_is_updating_environment;
// GetZone / GetArea optimization
Position m_last_area_position;
Position m_last_zone_position;
uint32 m_last_area_id;
uint32 m_last_zone_id;
Position m_last_outdoors_position;
bool m_last_outdoors_status;
[[nodiscard]] bool IsOutdoors() const;
[[nodiscard]] uint32 GetZoneId(bool forceRecalc = false) const override;
[[nodiscard]] uint32 GetAreaId(bool forceRecalc = false) const override;
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc = false) const override;
// cooldowns
[[nodiscard]] virtual bool HasSpellCooldown(uint32 /*spell_id*/) const { return false; }
@ -2503,6 +2482,9 @@ public:
[[nodiscard]] float GetCollisionWidth() const override;
[[nodiscard]] float GetCollisionRadius() const override;
void ProcessPositionDataChanged(PositionFullTerrainStatus const& data) override;
virtual void ProcessTerrainStatusUpdate();
protected:
explicit Unit (bool isWorldObject);

View file

@ -1955,8 +1955,8 @@ void ObjectMgr::LoadCreatures()
if (sWorld->getBoolConfig(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA))
{
uint32 zoneId = sMapMgr->GetZoneId(data.mapid, data.posX, data.posY, data.posZ);
uint32 areaId = sMapMgr->GetAreaId(data.mapid, data.posX, data.posY, data.posZ);
uint32 zoneId = sMapMgr->GetZoneId(data.phaseMask, data.mapid, data.posX, data.posY, data.posZ);
uint32 areaId = sMapMgr->GetAreaId(data.phaseMask, data.mapid, data.posX, data.posY, data.posZ);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
@ -2256,8 +2256,8 @@ void ObjectMgr::LoadGameobjects()
if (sWorld->getBoolConfig(CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA))
{
uint32 zoneId = sMapMgr->GetZoneId(data.mapid, data.posX, data.posY, data.posZ);
uint32 areaId = sMapMgr->GetAreaId(data.mapid, data.posX, data.posY, data.posZ);
uint32 zoneId = sMapMgr->GetZoneId(data.phaseMask, data.mapid, data.posX, data.posY, data.posZ);
uint32 areaId = sMapMgr->GetAreaId(data.phaseMask, data.mapid, data.posX, data.posY, data.posZ);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);

View file

@ -1135,7 +1135,7 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar)
pCurrChar->CleanupChannels();
pCurrChar->SendInitialPacketsAfterAddToMap();
uint32 currZone, currArea;
pCurrChar->GetZoneAndAreaId(currZone, currArea, false);
pCurrChar->GetZoneAndAreaId(currZone, currArea);
pCurrChar->SendInitWorldStates(currZone, currArea);
pCurrChar->SetInGameTime(World::GetGameTimeMS());

View file

@ -511,7 +511,7 @@ void WorldSession::HandleZoneUpdateOpcode(WorldPacket& recv_data)
// use server size data
uint32 newzone, newarea;
GetPlayer()->GetZoneAndAreaId(newzone, newarea, true);
GetPlayer()->GetZoneAndAreaId(newzone, newarea);
GetPlayer()->UpdateZone(newzone, newarea);
//GetPlayer()->SendInitWorldStates(true, newZone);
}

View file

@ -90,6 +90,8 @@ void WorldSession::HandleMoveWorldportAck()
GetPlayer()->ResetMap();
GetPlayer()->SetMap(newMap);
GetPlayer()->UpdatePositionData();
GetPlayer()->SendInitialPacketsBeforeAddToMap();
if (!GetPlayer()->GetMap()->AddPlayerToMap(GetPlayer()))
{
@ -216,7 +218,7 @@ void WorldSession::HandleMoveWorldportAck()
// update zone immediately, otherwise leave channel will cause crash in mtmap
uint32 newzone, newarea;
GetPlayer()->GetZoneAndAreaId(newzone, newarea, true);
GetPlayer()->GetZoneAndAreaId(newzone, newarea);
GetPlayer()->UpdateZone(newzone, newarea);
// honorless target
@ -273,7 +275,7 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recvData)
if (oldPos.GetExactDist2d(plMover) > 100.0f)
{
uint32 newzone, newarea;
plMover->GetZoneAndAreaId(newzone, newarea, true);
plMover->GetZoneAndAreaId(newzone, newarea);
plMover->UpdateZone(newzone, newarea);
// new zone
@ -482,7 +484,8 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData)
if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater())
{
// now client not include swimming flag in case jumping under water
plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ()));
plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetMap()->IsUnderWater(plrMover->GetPhaseMask(), movementInfo.pos.GetPositionX(),
movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ(), plrMover->GetCollisionHeight()));
}
bool jumpopcode = false;

View file

@ -175,6 +175,7 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
if (pet->IsCritter())
{
pet->UpdatePositionData();
map->AddToMap(pet->ToCreature(), true);
pet->SetLoading(false); // xinef, mine
return PET_LOAD_OK;
@ -186,6 +187,7 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
pet->GetCharmInfo()->SetPetNumber(pet_number, false);
pet->SetDisplayId(fields[3].GetUInt32());
pet->UpdatePositionData();
pet->SetNativeDisplayId(fields[3].GetUInt32());
pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
pet->SetName(fields[8].GetString());

View file

@ -43,6 +43,9 @@ u_map_magic MapAreaMagic = { {'A', 'R', 'E', 'A'} };
u_map_magic MapHeightMagic = { {'M', 'H', 'G', 'T'} };
u_map_magic MapLiquidMagic = { {'M', 'L', 'I', 'Q'} };
static uint16 const holetab_h[4] = { 0x1111, 0x2222, 0x4444, 0x8888 };
static uint16 const holetab_v[4] = { 0x000F, 0x00F0, 0x0F00, 0xF000 };
Map::~Map()
{
// UnloadAll must be called before deleting the map
@ -980,7 +983,7 @@ void Map::PlayerRelocation(Player* player, float x, float y, float z, float o)
player->Relocate(x, y, z, o);
if (player->IsVehicle())
player->GetVehicleKit()->RelocatePassengers();
player->UpdatePositionData();
player->UpdateObjectVisibility(false);
}
@ -1002,7 +1005,7 @@ void Map::CreatureRelocation(Creature* creature, float x, float y, float z, floa
creature->Relocate(x, y, z, o);
if (creature->IsVehicle())
creature->GetVehicleKit()->RelocatePassengers();
creature->UpdatePositionData();
creature->UpdateObjectVisibility(false);
}
@ -1023,7 +1026,7 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float
go->Relocate(x, y, z, o);
go->UpdateModelPosition();
go->SetPositionDataUpdate();
go->UpdateObjectVisibility(false);
}
@ -1043,7 +1046,7 @@ void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float
RemoveDynamicObjectFromMoveList(dynObj);
dynObj->Relocate(x, y, z, o);
dynObj->SetPositionDataUpdate();
dynObj->UpdateObjectVisibility(false);
}
@ -1307,6 +1310,7 @@ GridMap::GridMap()
_liquidEntry = nullptr;
_liquidFlags = nullptr;
_liquidMap = nullptr;
_holes = nullptr;
}
GridMap::~GridMap()
@ -1354,6 +1358,13 @@ bool GridMap::loadData(char* filename)
fclose(in);
return false;
}
// loadup holes data (if any. check header.holesOffset)
if (header.holesSize && !loadHolesData(in, header.holesOffset, header.holesSize))
{
LOG_ERROR("maps", "Error loading map holes data\n");
fclose(in);
return false;
}
fclose(in);
return true;
}
@ -1372,6 +1383,7 @@ void GridMap::unloadData()
delete[] _liquidEntry;
delete[] _liquidFlags;
delete[] _liquidMap;
delete[] _holes;
_areaMap = nullptr;
m_V9 = nullptr;
m_V8 = nullptr;
@ -1380,6 +1392,7 @@ void GridMap::unloadData()
_liquidEntry = nullptr;
_liquidFlags = nullptr;
_liquidMap = nullptr;
_holes = nullptr;
_gridGetHeight = &GridMap::getHeightFromFlat;
}
@ -1491,6 +1504,18 @@ bool GridMap::loadLiquidData(FILE* in, uint32 offset, uint32 /*size*/)
return true;
}
bool GridMap::loadHolesData(FILE* in, uint32 offset, uint32 /*size*/)
{
if (fseek(in, offset, SEEK_SET) != 0)
return false;
_holes = new uint16[16 * 16];
if (fread(_holes, sizeof(uint16), 16 * 16, in) != 16 * 16)
return false;
return true;
}
uint16 GridMap::getArea(float x, float y) const
{
if (!_areaMap)
@ -1523,6 +1548,9 @@ float GridMap::getHeightFromFloat(float x, float y) const
x_int &= (MAP_RESOLUTION - 1);
y_int &= (MAP_RESOLUTION - 1);
if (isHole(x_int, y_int))
return INVALID_HEIGHT;
// Height stored as: h5 - its v8 grid, h1-h4 - its v9 grid
// +--------------> X
// | h1-------h2 Coordinates is:
@ -1605,6 +1633,9 @@ float GridMap::getHeightFromUint8(float x, float y) const
x_int &= (MAP_RESOLUTION - 1);
y_int &= (MAP_RESOLUTION - 1);
if (isHole(x_int, y_int))
return INVALID_HEIGHT;
int32 a, b, c;
uint8* V9_h1_ptr = &m_uint8_V9[x_int * 128 + x_int + y_int];
if (x + y < 1)
@ -1672,6 +1703,9 @@ float GridMap::getHeightFromUint16(float x, float y) const
x_int &= (MAP_RESOLUTION - 1);
y_int &= (MAP_RESOLUTION - 1);
if (isHole(x_int, y_int))
return INVALID_HEIGHT;
int32 a, b, c;
uint16* V9_h1_ptr = &m_uint16_V9[x_int * 128 + x_int + y_int];
if (x + y < 1)
@ -1724,6 +1758,21 @@ float GridMap::getHeightFromUint16(float x, float y) const
return (float)((a * x) + (b * y) + c) * _gridIntHeightMultiplier + _gridHeight;
}
bool GridMap::isHole(int row, int col) const
{
if (!_holes)
return false;
int cellRow = row / 8; // 8 squares per cell
int cellCol = col / 8;
int holeRow = row % 8 / 2;
int holeCol = (col - (cellCol * 8)) / 2;
uint16 hole = _holes[cellRow * 16 + cellCol];
return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
}
float GridMap::getMinHeight(float x, float y) const
{
if (!_minHeight)
@ -1803,113 +1852,96 @@ float GridMap::getLiquidLevel(float x, float y) const
return _liquidMap[cx_int * _liquidWidth + cy_int];
}
// Why does this return LIQUID data?
uint8 GridMap::getTerrainType(float x, float y) const
{
if (!_liquidFlags)
return 0;
x = 16 * (32 - x / SIZE_OF_GRIDS);
y = 16 * (32 - y / SIZE_OF_GRIDS);
int lx = (int)x & 15;
int ly = (int)y & 15;
return _liquidFlags[lx * 16 + ly];
}
// Get water state on map
inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, float collisionHeight, LiquidData* data)
inline LiquidData const GridMap::GetLiquidData(float x, float y, float z, float collisionHeight, uint8 ReqLiquidType) const
{
LiquidData liquidData;
// Check water type (if no water return)
if (!_liquidType && !_liquidFlags)
return LIQUID_MAP_NO_WATER;
// Get cell
float cx = MAP_RESOLUTION * (32 - x / SIZE_OF_GRIDS);
float cy = MAP_RESOLUTION * (32 - y / SIZE_OF_GRIDS);
int x_int = (int)cx & (MAP_RESOLUTION - 1);
int y_int = (int)cy & (MAP_RESOLUTION - 1);
// Check water type in cell
int idx = (x_int >> 3) * 16 + (y_int >> 3);
uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidType;
uint32 entry = 0;
if (_liquidEntry)
if (_liquidType || _liquidFlags)
{
if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(_liquidEntry[idx]))
{
entry = liquidEntry->Id;
type &= MAP_LIQUID_TYPE_DARK_WATER;
uint32 liqTypeIdx = liquidEntry->Type;
if (entry < 21)
{
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y)))
{
uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
if (!overrideLiquid && area->zone)
{
area = sAreaTableStore.LookupEntry(area->zone);
if (area)
overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
}
// Get cell
float cx = MAP_RESOLUTION * (32 - x / SIZE_OF_GRIDS);
float cy = MAP_RESOLUTION * (32 - y / SIZE_OF_GRIDS);
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid))
int x_int = (int) cx & (MAP_RESOLUTION - 1);
int y_int = (int) cy & (MAP_RESOLUTION - 1);
// Check water type in cell
int idx = (x_int >> 3) * 16 + (y_int >> 3);
uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidType;
uint32 entry = 0;
if (_liquidEntry)
{
if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(_liquidEntry[idx]))
{
entry = liquidEntry->Id;
type &= MAP_LIQUID_TYPE_DARK_WATER;
uint32 liqTypeIdx = liquidEntry->Type;
if (entry < 21)
{
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y)))
{
entry = overrideLiquid;
liqTypeIdx = liq->Type;
uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
if (!overrideLiquid && area->zone)
{
area = sAreaTableStore.LookupEntry(area->zone);
if (area)
overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
}
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid))
{
entry = overrideLiquid;
liqTypeIdx = liq->Type;
}
}
}
}
type |= 1 << liqTypeIdx;
type |= 1 << liqTypeIdx;
}
}
// Check req liquid type mask
if (type != 0 && (!ReqLiquidType || (ReqLiquidType & type) != 0))
{
// Check water level:
// Check water height map
int lx_int = x_int - _liquidOffY;
int ly_int = y_int - _liquidOffX;
if (lx_int >= 0 && lx_int < _liquidHeight && ly_int >= 0 && ly_int < _liquidWidth)
{
// Get water level
float liquid_level = _liquidMap ? _liquidMap[lx_int * _liquidWidth + ly_int] : _liquidLevel;
// Get ground level (sub 0.2 for fix some errors)
float ground_level = getHeight(x, y);
// Check water level and ground level
if (liquid_level >= ground_level && z >= ground_level - 2)
{
// All ok in water -> store data
liquidData.Entry = entry;
liquidData.Flags = type;
liquidData.Level = liquid_level;
liquidData.DepthLevel = ground_level;
// For speed check as int values
float delta = liquid_level - z;
if (delta > collisionHeight)
liquidData.Status = LIQUID_MAP_UNDER_WATER;
else if (delta > 0.2f)
liquidData.Status = LIQUID_MAP_IN_WATER;
else if (delta > -0.2f)
liquidData.Status = LIQUID_MAP_WATER_WALK;
else
liquidData.Status = LIQUID_MAP_ABOVE_WATER;
}
}
}
}
if (type == 0)
return LIQUID_MAP_NO_WATER;
// Check req liquid type mask
if (ReqLiquidType && !(ReqLiquidType & type))
return LIQUID_MAP_NO_WATER;
// Check water level:
// Check water height map
int lx_int = x_int - _liquidOffY;
int ly_int = y_int - _liquidOffX;
if (lx_int < 0 || lx_int >= _liquidHeight)
return LIQUID_MAP_NO_WATER;
if (ly_int < 0 || ly_int >= _liquidWidth)
return LIQUID_MAP_NO_WATER;
// Get water level
float liquid_level = _liquidMap ? _liquidMap[lx_int * _liquidWidth + ly_int] : _liquidLevel;
// Get ground level (sub 0.2 for fix some errors)
float ground_level = getHeight(x, y);
// Check water level and ground level
if (liquid_level < ground_level || z < ground_level - 2)
return LIQUID_MAP_NO_WATER;
// All ok in water -> store data
if (data)
{
data->entry = entry;
data->type_flags = type;
data->level = liquid_level;
data->depth_level = ground_level;
}
// For speed check as int values
float delta = liquid_level - z;
if (delta > collisionHeight) // Under water
return LIQUID_MAP_UNDER_WATER;
if (delta > 0.0f) // In water
return LIQUID_MAP_IN_WATER;
if (delta > -0.1f) // Walk on water
return LIQUID_MAP_WATER_WALK;
// Above water
return LIQUID_MAP_ABOVE_WATER;
return liquidData;
}
GridMap* Map::GetGrid(float x, float y)
@ -1933,17 +1965,15 @@ float Map::GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, fl
if (ground)
*ground = ground_z;
LiquidData liquid_status;
ZLiquidStatus res = getLiquidStatus(x, y, ground_z, MAP_ALL_LIQUIDS, &liquid_status, collisionHeight);
switch (res)
LiquidData const& liquidData = const_cast<Map*>(this)->GetLiquidData(phasemask, x, y, ground_z, collisionHeight, MAP_ALL_LIQUIDS);
switch (liquidData.Status)
{
case LIQUID_MAP_ABOVE_WATER:
return std::max<float>(liquid_status.level, ground_z);
return std::max<float>(liquidData.Level, ground_z);
case LIQUID_MAP_NO_WATER:
return ground_z;
default:
return liquid_status.level;
return liquidData.Level;
}
}
@ -2030,115 +2060,99 @@ float Map::GetMinHeight(float x, float y) const
return -500.0f;
}
inline bool IsOutdoorWMO(uint32 mogpFlags, int32 /*adtId*/, int32 /*rootId*/, int32 /*groupId*/, WMOAreaTableEntry const* wmoEntry, AreaTableEntry const* atEntry)
static inline bool IsInWMOInterior(uint32 mogpFlags)
{
bool outdoor = true;
if (wmoEntry && atEntry)
{
if (atEntry->flags & AREA_FLAG_OUTSIDE)
return true;
if (atEntry->flags & AREA_FLAG_INSIDE)
return false;
}
outdoor = mogpFlags & 0x8;
if (wmoEntry)
{
if (wmoEntry->Flags & 4)
return true;
if ((wmoEntry->Flags & 2) != 0)
outdoor = false;
}
return outdoor;
return (mogpFlags & 0x2000) != 0;
}
bool Map::IsOutdoors(float x, float y, float z) const
{
uint32 mogpFlags;
int32 adtId, rootId, groupId;
// no wmo found? -> outside by default
if (!GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId))
return true;
AreaTableEntry const* atEntry = 0;
WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId);
if (wmoEntry)
{
LOG_DEBUG("maps", "Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId);
atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId);
}
return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry);
}
bool Map::GetAreaInfo(float x, float y, float z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
bool Map::GetAreaInfo(uint32 phaseMask, float x, float y, float z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
float vmap_z = z;
float dynamic_z = z;
float check_z = z;
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
if (vmgr->GetAreaInfo(GetId(), x, y, vmap_z, flags, adtId, rootId, groupId))
uint32 vflags;
int32 vadtId;
int32 vrootId;
int32 vgroupId;
uint32 dflags;
int32 dadtId;
int32 drootId;
int32 dgroupId;
bool hasVmapAreaInfo = vmgr->GetAreaInfo(GetId(), x, y, vmap_z, vflags, vadtId, vrootId, vgroupId);
bool hasDynamicAreaInfo = _dynamicTree.GetAreaInfo(x, y, dynamic_z, phaseMask, dflags, dadtId, drootId, dgroupId);
auto useVmap = [&]() { check_z = vmap_z; flags = vflags; adtId = vadtId; rootId = vrootId; groupId = vgroupId; };
auto useDyn = [&]() { check_z = dynamic_z; flags = dflags; adtId = dadtId; rootId = drootId; groupId = dgroupId; };
if (hasVmapAreaInfo)
{
if (hasDynamicAreaInfo && dynamic_z > vmap_z)
useDyn();
else
useVmap();
}
else if (hasDynamicAreaInfo)
{
useDyn();
}
if (hasVmapAreaInfo || hasDynamicAreaInfo)
{
// check if there's terrain between player height and object height
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
{
float _mapheight = gmap->getHeight(x, y);
float mapHeight = gmap->getHeight(x, y);
// z + 2.0f condition taken from GetHeight(), not sure if it's such a great choice...
if (z + 2.0f > _mapheight && _mapheight > vmap_z)
if (z + 2.0f > mapHeight && mapHeight > check_z)
return false;
}
return true;
}
return false;
}
uint32 Map::GetAreaId(float x, float y, float z, bool* isOutdoors) const
uint32 Map::GetAreaId(uint32 phaseMask, float x, float y, float z) const
{
uint32 mogpFlags;
int32 adtId, rootId, groupId;
WMOAreaTableEntry const* wmoEntry = 0;
AreaTableEntry const* atEntry = 0;
bool haveAreaInfo = false;
float vmapZ = 0.f;
bool hasVmapArea = GetAreaInfo(phaseMask, x, y, vmapZ, mogpFlags, adtId, rootId, groupId);
if (GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId))
uint32 gridAreaId = 0;
float gridMapHeight = INVALID_HEIGHT;
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
{
haveAreaInfo = true;
wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId);
if (wmoEntry)
atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId);
gridAreaId = gmap->getArea(x, y);
gridMapHeight = gmap->getHeight(x, y);
}
uint16 areaId = 0;
if (atEntry)
areaId = atEntry->ID;
else
// floor is the height we are closer to (but only if above)
if (hasVmapArea && G3D::fuzzyGe(z, vmapZ - GROUND_HEIGHT_TOLERANCE) && (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || vmapZ > gridMapHeight))
{
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
areaId = gmap->getArea(x, y);
// this used while not all *.map files generated (instances)
if (!areaId)
areaId = i_mapEntry->linked_zone;
}
// wmo found
if (WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId))
areaId = wmoEntry->areaId;
if (isOutdoors)
{
if (haveAreaInfo)
*isOutdoors = IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry);
else
*isOutdoors = true;
if (!areaId)
areaId = gridAreaId;
}
else
areaId = gridAreaId;
if (!areaId)
areaId = i_mapEntry->linked_zone;
return areaId;
}
uint32 Map::GetAreaId(float x, float y, float z) const
uint32 Map::GetZoneId(uint32 phaseMask, float x, float y, float z) const
{
return GetAreaId(x, y, z, nullptr);
}
uint32 Map::GetZoneId(float x, float y, float z) const
{
uint32 areaId = GetAreaId(x, y, z);
uint32 areaId = GetAreaId(phaseMask, x, y, z);
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId))
if (area->zone)
return area->zone;
@ -2146,105 +2160,239 @@ uint32 Map::GetZoneId(float x, float y, float z) const
return areaId;
}
void Map::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const
void Map::GetZoneAndAreaId(uint32 phaseMask, uint32& zoneid, uint32& areaid, float x, float y, float z) const
{
areaid = zoneid = GetAreaId(x, y, z);
areaid = zoneid = GetAreaId(phaseMask, x, y, z);
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaid))
if (area->zone)
zoneid = area->zone;
}
uint8 Map::GetTerrainType(float x, float y) const
LiquidData const Map::GetLiquidData(uint32 phaseMask, float x, float y, float z, float collisionHeight, uint8 ReqLiquidType)
{
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
return gmap->getTerrainType(x, y);
else
return 0;
}
LiquidData liquidData;
ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data, float collisionHeight) const
{
ZLiquidStatus result = LIQUID_MAP_NO_WATER;
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
float liquid_level = INVALID_HEIGHT;
float ground_level = INVALID_HEIGHT;
uint32 liquid_type = 0;
if (vmgr->GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type))
uint32 mogpFlags = 0;
bool useGridLiquid = true;
if (vmgr->GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type, mogpFlags))
{
LOG_DEBUG("maps", "getLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type);
useGridLiquid = !IsInWMOInterior(mogpFlags);
LOG_DEBUG("maps", "GetLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type);
// Check water level and ground level
if (liquid_level > ground_level && z > ground_level - 2)
if (liquid_level > ground_level && G3D::fuzzyGe(z, ground_level - GROUND_HEIGHT_TOLERANCE))
{
// All ok in water -> store data
if (data)
// hardcoded in client like this
if (GetId() == 530 && liquid_type == 2)
liquid_type = 15;
uint32 liquidFlagType = 0;
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(liquid_type))
liquidFlagType = liq->Type;
if (liquid_type && liquid_type < 21)
{
// hardcoded in client like this
if (GetId() == 530 && liquid_type == 2)
liquid_type = 15;
uint32 liquidFlagType = 0;
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(liquid_type))
liquidFlagType = liq->Type;
if (liquid_type && liquid_type < 21)
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(phaseMask, x, y, z)))
{
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(x, y, z)))
uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType];
if (!overrideLiquid && area->zone)
{
uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType];
if (!overrideLiquid && area->zone)
{
area = sAreaTableStore.LookupEntry(area->zone);
if (area)
overrideLiquid = area->LiquidTypeOverride[liquidFlagType];
}
area = sAreaTableStore.LookupEntry(area->zone);
if (area)
overrideLiquid = area->LiquidTypeOverride[liquidFlagType];
}
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid))
{
liquid_type = overrideLiquid;
liquidFlagType = liq->Type;
}
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid))
{
liquid_type = overrideLiquid;
liquidFlagType = liq->Type;
}
}
data->level = liquid_level;
data->depth_level = ground_level;
data->entry = liquid_type;
data->type_flags = 1 << liquidFlagType;
}
float delta = liquid_level - z;
// Get position delta
if (delta > collisionHeight) // Under water
return LIQUID_MAP_UNDER_WATER;
if (delta > 0.0f) // In water
return LIQUID_MAP_IN_WATER;
if (delta > -0.1f) // Walk on water
return LIQUID_MAP_WATER_WALK;
result = LIQUID_MAP_ABOVE_WATER;
liquidData.Level = liquid_level;
liquidData.DepthLevel = ground_level;
liquidData.Entry = liquid_type;
liquidData.Flags = 1 << liquidFlagType;
}
float delta = liquid_level - z;
// Get position delta
if (delta > collisionHeight)
liquidData.Status = LIQUID_MAP_UNDER_WATER;
if (delta > 0.2f)
liquidData.Status = LIQUID_MAP_IN_WATER;
if (delta > -0.2f)
liquidData.Status = LIQUID_MAP_WATER_WALK;
else
liquidData.Status = LIQUID_MAP_ABOVE_WATER;
}
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
if (useGridLiquid)
{
LiquidData map_data;
ZLiquidStatus map_result = gmap->getLiquidStatus(x, y, z, ReqLiquidType, collisionHeight, &map_data);
// Not override LIQUID_MAP_ABOVE_WATER with LIQUID_MAP_NO_WATER:
if (map_result != LIQUID_MAP_NO_WATER && (map_data.level > ground_level))
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
{
if (data)
LiquidData const& map_data = gmap->GetLiquidData(x, y, z, collisionHeight, ReqLiquidType);
// Not override LIQUID_MAP_ABOVE_WATER with LIQUID_MAP_NO_WATER:
if (map_data.Status != LIQUID_MAP_NO_WATER && (map_data.Level > ground_level))
{
// hardcoded in client like this
if (GetId() == 530 && map_data.entry == 2)
map_data.entry = 15;
uint32 liquidEntry = map_data.Entry;
if (GetId() == 530 && liquidEntry == 2)
liquidEntry = 15;
*data = map_data;
liquidData = map_data;
liquidData.Entry = liquidEntry;
}
return map_result;
}
}
return result;
return liquidData;
}
void Map::GetFullTerrainStatusForPosition(uint32 phaseMask, float x, float y, float z, float collisionHeight, PositionFullTerrainStatus& data, uint8 reqLiquidType)
{
GridMap* gmap = GetGrid(x, y);
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
VMAP::AreaAndLiquidData vmapData;
VMAP::AreaAndLiquidData dynData;
VMAP::AreaAndLiquidData* wmoData = nullptr;
vmgr->GetAreaAndLiquidData(GetId(), x, y, z, reqLiquidType, vmapData);
_dynamicTree.GetAreaAndLiquidData(x, y, z, phaseMask, reqLiquidType, dynData);
uint32 gridAreaId = 0;
float gridMapHeight = INVALID_HEIGHT;
if (gmap)
{
gridAreaId = gmap->getArea(x, y);
gridMapHeight = gmap->getHeight(x, y);
}
bool useGridLiquid = true;
// floor is the height we are closer to (but only if above)
data.floorZ = VMAP_INVALID_HEIGHT;
if (gridMapHeight > INVALID_HEIGHT && G3D::fuzzyGe(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE))
data.floorZ = gridMapHeight;
if (vmapData.floorZ > VMAP_INVALID_HEIGHT && G3D::fuzzyGe(z, vmapData.floorZ - GROUND_HEIGHT_TOLERANCE) &&
(G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || vmapData.floorZ > gridMapHeight))
{
data.floorZ = vmapData.floorZ;
wmoData = &vmapData;
}
// NOTE: Objects will not detect a case when a wmo providing area/liquid despawns from under them
// but this is fine as these kind of objects are not meant to be spawned and despawned a lot
// example: Lich King platform
if (dynData.floorZ > VMAP_INVALID_HEIGHT && G3D::fuzzyGe(z, dynData.floorZ - GROUND_HEIGHT_TOLERANCE) &&
(G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || dynData.floorZ > gridMapHeight) &&
(G3D::fuzzyLt(z, vmapData.floorZ - GROUND_HEIGHT_TOLERANCE) || dynData.floorZ > vmapData.floorZ))
{
data.floorZ = dynData.floorZ;
wmoData = &dynData;
}
if (wmoData)
{
if (wmoData->areaInfo)
{
// wmo found
WMOAreaTableEntry const* wmoEntry = GetWMOAreaTableEntryByTripple(wmoData->areaInfo->rootId, wmoData->areaInfo->adtId, wmoData->areaInfo->groupId);
data.outdoors = (wmoData->areaInfo->mogpFlags & 0x8) != 0;
if (wmoEntry)
{
data.areaId = wmoEntry->areaId;
if (wmoEntry->Flags & 4)
data.outdoors = true;
else if (wmoEntry->Flags & 2)
data.outdoors = false;
}
if (!data.areaId)
data.areaId = gridAreaId;
useGridLiquid = !IsInWMOInterior(wmoData->areaInfo->mogpFlags);
}
}
else
{
data.outdoors = true;
data.areaId = gridAreaId;
if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(data.areaId))
data.outdoors = (areaEntry->flags & (AREA_FLAG_INSIDE | AREA_FLAG_OUTSIDE)) != AREA_FLAG_INSIDE;
}
if (!data.areaId)
data.areaId = i_mapEntry->linked_zone;
AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(data.areaId);
// liquid processing
if (wmoData && wmoData->liquidInfo && wmoData->liquidInfo->level > wmoData->floorZ)
{
uint32 liquidType = wmoData->liquidInfo->type;
if (GetId() == 530 && liquidType == 2) // gotta love blizzard hacks
liquidType = 15;
uint32 liquidFlagType = 0;
if (LiquidTypeEntry const* liquidData = sLiquidTypeStore.LookupEntry(liquidType))
liquidFlagType = liquidData->Type;
if (liquidType && liquidType < 21 && areaEntry)
{
uint32 overrideLiquid = areaEntry->LiquidTypeOverride[liquidFlagType];
if (!overrideLiquid && areaEntry->zone)
{
AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(areaEntry->zone);
if (zoneEntry)
overrideLiquid = zoneEntry->LiquidTypeOverride[liquidFlagType];
}
if (LiquidTypeEntry const* overrideData = sLiquidTypeStore.LookupEntry(overrideLiquid))
{
liquidType = overrideLiquid;
liquidFlagType = overrideData->Type;
}
}
data.liquidInfo.Level = wmoData->liquidInfo->level;
data.liquidInfo.DepthLevel = wmoData->floorZ;
data.liquidInfo.Entry = liquidType;
data.liquidInfo.Flags = 1 << liquidFlagType;
// Get position delta
float delta = wmoData->liquidInfo->level - z;
if (delta > collisionHeight)
data.liquidInfo.Status = LIQUID_MAP_UNDER_WATER;
else if (delta > 0.2f)
data.liquidInfo.Status = LIQUID_MAP_IN_WATER;
else if (delta > -0.2f)
data.liquidInfo.Status = LIQUID_MAP_WATER_WALK;
else
data.liquidInfo.Status = LIQUID_MAP_ABOVE_WATER;
}
// look up liquid data from grid map
if (gmap && useGridLiquid)
{
LiquidData const& gridLiquidData = gmap->GetLiquidData(x, y, z, collisionHeight, reqLiquidType);
if (gridLiquidData.Status != LIQUID_MAP_NO_WATER && (!wmoData || gridLiquidData.Level > wmoData->floorZ))
{
uint32 liquidEntry = gridLiquidData.Entry;
if (GetId() == 530 && liquidEntry == 2)
liquidEntry = 15;
data.liquidInfo = gridLiquidData;
data.liquidInfo.Entry = liquidEntry;
}
}
}
float Map::GetWaterLevel(float x, float y) const
@ -2288,30 +2436,28 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr
return std::max<float>(h1, h2);
}
bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const
bool Map::IsInWater(uint32 phaseMask, float x, float y, float pZ, float collisionHeight) const
{
LiquidData liquid_status;
LiquidData* liquid_ptr = data ? data : &liquid_status;
return getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr) & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER);
LiquidData const& liquidData = const_cast<Map*>(this)->GetLiquidData(phaseMask, x, y, pZ, collisionHeight, MAP_ALL_LIQUIDS);
return (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING) != 0;
}
bool Map::IsUnderWater(float x, float y, float z) const
bool Map::IsUnderWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const
{
return getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN) & LIQUID_MAP_UNDER_WATER;
LiquidData const& liquidData = const_cast<Map*>(this)->GetLiquidData(phaseMask, x, y, z, collisionHeight, MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN);
return liquidData.Status == LIQUID_MAP_UNDER_WATER;
}
bool Map::HasEnoughWater(WorldObject const* searcher, float x, float y, float z) const
{
LiquidData liquidData;
liquidData.level = INVALID_HEIGHT;
ZLiquidStatus liquidStatus = getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData);
return (liquidStatus & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) && HasEnoughWater(searcher, liquidData);
LiquidData const& liquidData = const_cast<Map*>(this)->GetLiquidData(searcher->GetPhaseMask(), x, y, z, searcher->GetCollisionHeight(), MAP_ALL_LIQUIDS);
return (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING) != 0 && HasEnoughWater(searcher, liquidData);
}
bool Map::HasEnoughWater(WorldObject const* searcher, LiquidData liquidData) const
bool Map::HasEnoughWater(WorldObject const* searcher, LiquidData const& liquidData) const
{
float minHeightInWater = searcher->GetMinHeightInWater();
return liquidData.level > INVALID_HEIGHT && liquidData.level > liquidData.depth_level && liquidData.level - liquidData.depth_level >= minHeightInWater;
return liquidData.Level > INVALID_HEIGHT && liquidData.Level > liquidData.DepthLevel && liquidData.Level - liquidData.DepthLevel >= minHeightInWater;
}
char const* Map::GetMapName() const
@ -3421,6 +3567,8 @@ Corpse* Map::ConvertCorpseToBones(ObjectGuid const ownerGuid, bool insignia /*=
AddCorpse(bones);
bones->UpdatePositionData();
// add bones in grid store if grid loaded where corpse placed
AddToMap(bones);
}
@ -3459,7 +3607,7 @@ void Map::RemoveOldCorpses()
void Map::SendZoneDynamicInfo(Player* player)
{
uint32 zoneId = GetZoneId(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
uint32 zoneId = player->GetZoneId();
ZoneDynamicInfoMap::const_iterator itr = _zoneDynamicInfo.find(zoneId);
if (itr == _zoneDynamicInfo.end())
return;
@ -3671,7 +3819,7 @@ bool Map::CheckCollisionAndGetValidCoords(const WorldObject* source, float start
return false;
}
bool isWaterNext = IsInWater(destX, destY, destZ);
bool isWaterNext = IsInWater(source->GetPhaseMask(), destX, destY, destZ, source->GetCollisionHeight());
PathGenerator path(source);
@ -3784,6 +3932,8 @@ void Map::LoadCorpseData()
}
AddCorpse(corpse);
corpse->UpdatePositionData();
} while (result->NextRow());
}

View file

@ -76,6 +76,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
uint32 holesOffset;
uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@ -115,7 +117,7 @@ struct map_liquidHeader
float liquidLevel;
};
enum ZLiquidStatus
enum LiquidStatus
{
LIQUID_MAP_NO_WATER = 0x00000000,
LIQUID_MAP_ABOVE_WATER = 0x00000001,
@ -124,6 +126,9 @@ enum ZLiquidStatus
LIQUID_MAP_UNDER_WATER = 0x00000008
};
#define MAP_LIQUID_STATUS_SWIMMING (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)
#define MAP_LIQUID_STATUS_IN_CONTACT (MAP_LIQUID_STATUS_SWIMMING | LIQUID_MAP_WATER_WALK)
#define MAP_LIQUID_TYPE_NO_WATER 0x00
#define MAP_LIQUID_TYPE_WATER 0x01
#define MAP_LIQUID_TYPE_OCEAN 0x02
@ -135,12 +140,30 @@ enum ZLiquidStatus
#define MAP_LIQUID_TYPE_DARK_WATER 0x10
#define MAP_LIQUID_TYPE_WMO_WATER 0x20
#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface
#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE
#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT
#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations
#define MIN_UNLOAD_DELAY 1 // immediate unload
struct LiquidData
{
uint32 type_flags;
uint32 entry;
float level;
float depth_level;
LiquidData() : Entry(0), Flags(0), Level(INVALID_HEIGHT), DepthLevel(INVALID_HEIGHT), Status(LIQUID_MAP_NO_WATER) { }
uint32 Entry;
uint32 Flags;
float Level;
float DepthLevel;
LiquidStatus Status;
};
struct PositionFullTerrainStatus
{
PositionFullTerrainStatus() : areaId(0), floorZ(INVALID_HEIGHT), outdoors(false) { }
uint32 areaId;
float floorZ;
bool outdoors;
LiquidData liquidInfo;
};
enum LineOfSightChecks
@ -186,10 +209,13 @@ class GridMap
uint8 _liquidOffY;
uint8 _liquidWidth;
uint8 _liquidHeight;
uint16* _holes;
bool loadAreaData(FILE* in, uint32 offset, uint32 size);
bool loadHeightData(FILE* in, uint32 offset, uint32 size);
bool loadLiquidData(FILE* in, uint32 offset, uint32 size);
bool loadHolesData(FILE* in, uint32 offset, uint32 size);
bool isHole(int row, int col) const;
// Get height functions and pointers
typedef float (GridMap::*GetHeightPtr) (float x, float y) const;
@ -209,8 +235,7 @@ public:
[[nodiscard]] inline float getHeight(float x, float y) const {return (this->*_gridGetHeight)(x, y);}
[[nodiscard]] float getMinHeight(float x, float y) const;
[[nodiscard]] float getLiquidLevel(float x, float y) const;
[[nodiscard]] uint8 getTerrainType(float x, float y) const;
ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, float collisionHeight, LiquidData* data = nullptr);
LiquidData const GetLiquidData(float x, float y, float z, float collisionHeight, uint8 ReqLiquidType) const;
};
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push, N), also any gcc version not support it at some platform
@ -249,12 +274,6 @@ struct ZoneDynamicInfo
#pragma pack(pop)
#endif
#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface
#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE
#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT
#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations
#define MIN_UNLOAD_DELAY 1 // immediate unload
typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType;
typedef std::unordered_map<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap;
typedef std::set<MotionTransport*> TransportsContainer;
@ -352,22 +371,19 @@ public:
[[nodiscard]] float GetMinHeight(float x, float y) const;
Transport* GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject = nullptr);
ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr, float collisionHeight = DEFAULT_COLLISION_HEIGHT) const;
void GetFullTerrainStatusForPosition(uint32 phaseMask, float x, float y, float z, float collisionHeight, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS);
LiquidData const GetLiquidData(uint32 phaseMask, float x, float y, float z, float collisionHeight, uint8 ReqLiquidType);
uint32 GetAreaId(float x, float y, float z, bool* isOutdoors) const;
bool GetAreaInfo(float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const;
[[nodiscard]] uint32 GetAreaId(float x, float y, float z) const;
[[nodiscard]] uint32 GetZoneId(float x, float y, float z) const;
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const;
[[nodiscard]] bool GetAreaInfo(uint32 phaseMask, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const;
[[nodiscard]] uint32 GetAreaId(uint32 phaseMask, float x, float y, float z) const;
[[nodiscard]] uint32 GetZoneId(uint32 phaseMask, float x, float y, float z) const;
void GetZoneAndAreaId(uint32 phaseMask, uint32& zoneid, uint32& areaid, float x, float y, float z) const;
[[nodiscard]] bool IsOutdoors(float x, float y, float z) const;
[[nodiscard]] uint8 GetTerrainType(float x, float y) const;
[[nodiscard]] float GetWaterLevel(float x, float y) const;
bool IsInWater(float x, float y, float z, LiquidData* data = nullptr) const;
[[nodiscard]] bool IsUnderWater(float x, float y, float z) const;
bool IsInWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const;
[[nodiscard]] bool IsUnderWater(uint32 phaseMask, float x, float y, float z, float collisionHeight) const;
[[nodiscard]] bool HasEnoughWater(WorldObject const* searcher, float x, float y, float z) const;
[[nodiscard]] bool HasEnoughWater(WorldObject const* searcher, LiquidData liquidData) const;
[[nodiscard]] bool HasEnoughWater(WorldObject const* searcher, LiquidData const& liquidData) const;
void MoveAllCreaturesInMoveList();
void MoveAllGameObjectsInMoveList();

View file

@ -37,26 +37,26 @@ public:
return (iter == i_maps.end() ? nullptr : iter->second);
}
[[nodiscard]] uint32 GetAreaId(uint32 mapid, float x, float y, float z) const
[[nodiscard]] uint32 GetAreaId(uint32 phaseMask, uint32 mapid, float x, float y, float z) const
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
return m->GetAreaId(x, y, z);
return m->GetAreaId(phaseMask, x, y, z);
}
[[nodiscard]] uint32 GetAreaId(uint32 mapid, Position const& pos) const { return GetAreaId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
[[nodiscard]] uint32 GetAreaId(WorldLocation const& loc) const { return GetAreaId(loc.GetMapId(), loc); }
[[nodiscard]] uint32 GetAreaId(uint32 phaseMask, uint32 mapid, Position const& pos) const { return GetAreaId(phaseMask, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
[[nodiscard]] uint32 GetAreaId(uint32 phaseMask, WorldLocation const& loc) const { return GetAreaId(phaseMask, loc.GetMapId(), loc); }
[[nodiscard]] uint32 GetZoneId(uint32 mapid, float x, float y, float z) const
[[nodiscard]] uint32 GetZoneId(uint32 phaseMask, uint32 mapid, float x, float y, float z) const
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
return m->GetZoneId(x, y, z);
return m->GetZoneId(phaseMask, x, y, z);
}
[[nodiscard]] uint32 GetZoneId(uint32 mapid, Position const& pos) const { return GetZoneId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
[[nodiscard]] uint32 GetZoneId(WorldLocation const& loc) const { return GetZoneId(loc.GetMapId(), loc); }
[[nodiscard]] uint32 GetZoneId(uint32 phaseMask, uint32 mapid, Position const& pos) const { return GetZoneId(phaseMask, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); }
[[nodiscard]] uint32 GetZoneId(uint32 phaseMask, WorldLocation const& loc) const { return GetZoneId(phaseMask, loc.GetMapId(), loc); }
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z)
void GetZoneAndAreaId(uint32 phaseMask, uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z)
{
Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid);
m->GetZoneAndAreaId(zoneid, areaid, x, y, z);
m->GetZoneAndAreaId(phaseMask, zoneid, areaid, x, y, z);
}
void Initialize(void);

View file

@ -83,7 +83,7 @@ GraveyardStruct const* Graveyard::GetDefaultGraveyard(TeamId teamId)
GraveyardStruct const* Graveyard::GetClosestGraveyard(float x, float y, float z, uint32 MapId, TeamId teamId)
{
// search for zone associated closest graveyard
uint32 zoneId = sMapMgr->GetZoneId(MapId, x, y, z);
uint32 zoneId = sMapMgr->GetZoneId(PHASEMASK_NORMAL, MapId, x, y, z);
if (!zoneId)
{

View file

@ -132,11 +132,6 @@ void MotionMaster::UpdateMotion(uint32 diff)
}
_cleanFlag &= ~MMCF_INUSE;
if (_owner->GetTypeId() == TYPEID_PLAYER)
_owner->UpdateUnderwaterState(_owner->GetMap(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ());
else
_owner->UpdateEnvironmentIfNeeded(0);
}
void MotionMaster::DirectClean(bool reset)

View file

@ -26,7 +26,7 @@ void ConfusedMovementGenerator<T>::DoInitialize(T* unit)
float y = unit->GetPositionY();
float z = unit->GetPositionZ();
Map const* map = unit->GetBaseMap();
Map const* map = unit->GetMap();
bool is_water_ok, is_land_ok;
_InitSpecific(unit, is_water_ok, is_land_ok);
@ -50,7 +50,7 @@ void ConfusedMovementGenerator<T>::DoInitialize(T* unit)
}
else if (unit->IsWithinLOS(wanderX, wanderY, z))
{
bool is_water = map->IsInWater(wanderX, wanderY, z);
bool is_water = map->IsInWater(unit->GetPhaseMask(), wanderX, wanderY, z, unit->GetCollisionHeight());
if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
{

View file

@ -48,7 +48,7 @@ bool FleeingMovementGenerator<T>::_getPoint(T* owner, float& x, float& y, float&
if (!owner)
return false;
const Map* _map = owner->GetBaseMap();
const Map* _map = owner->GetMap();
x = owner->GetPositionX();
y = owner->GetPositionY();

View file

@ -29,8 +29,6 @@ void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner)
if (!owner->HasSwimmingFlagOutOfCombat())
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING);
owner->UpdateEnvironmentIfNeeded(2);
}
void HomeMovementGenerator<Creature>::DoReset(Creature*)

View file

@ -197,8 +197,8 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con
{
bool buildShotrcut = false;
bool isUnderWaterStart = _source->GetMap()->IsUnderWater(startPos.x, startPos.y, startPos.z);
bool isUnderWaterEnd = _source->GetMap()->IsUnderWater(endPos.x, endPos.y, endPos.z);
bool isUnderWaterStart = _source->GetMap()->IsUnderWater(_source->GetPhaseMask(), startPos.x, startPos.y, startPos.z, _source->GetCollisionHeight());
bool isUnderWaterEnd = _source->GetMap()->IsUnderWater(_source->GetPhaseMask(), endPos.x, endPos.y, endPos.z, _source->GetCollisionHeight());
bool isFarUnderWater = startFarFromPoly ? isUnderWaterStart : isUnderWaterEnd;
Unit const* _sourceUnit = _source->ToUnit();
@ -565,9 +565,9 @@ void PathGenerator::BuildPointPath(const float* startPoint, const float* endPoin
uint32 newPointCount = 0;
for (uint32 i = 0; i < pointCount; ++i) {
G3D::Vector3 vector = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
ZLiquidStatus status = _source->GetMap()->getLiquidStatus(vector.x, vector.y, vector.z, MAP_ALL_LIQUIDS, nullptr);
LiquidData const& liquidData = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), vector.x, vector.y, vector.z, _source->GetCollisionHeight(), MAP_ALL_LIQUIDS);
// One of the points is not in the water
if (status == LIQUID_MAP_UNDER_WATER)
if (liquidData.Status == LIQUID_MAP_UNDER_WATER)
{
// if the first point is under water
// then set a proper z for it
@ -699,11 +699,11 @@ void PathGenerator::UpdateFilter()
NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z) const
{
LiquidData data;
ZLiquidStatus liquidStatus = _source->GetMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data);
if (liquidStatus == LIQUID_MAP_NO_WATER)
LiquidData const& liquidData = _source->GetMap()->GetLiquidData(_source->GetPhaseMask(), x, y, z, _source->GetCollisionHeight(), MAP_ALL_LIQUIDS);
if (liquidData.Status == LIQUID_MAP_NO_WATER)
return NAV_GROUND;
switch (data.type_flags)
switch (data.Flags)
{
case MAP_LIQUID_TYPE_WATER:
case MAP_LIQUID_TYPE_OCEAN:
@ -1149,8 +1149,8 @@ bool PathGenerator::IsSwimmableSegment(float const* v1, float const* v2, bool ch
bool PathGenerator::IsSwimmableSegment(float x, float y, float z, float destX, float destY, float destZ, bool checkSwim) const
{
Creature const* _sourceCreature = _source->ToCreature();
return _source->GetMap()->IsInWater(x, y, z) &&
_source->GetMap()->IsInWater(destX, destY, destZ) &&
return _source->GetMap()->IsInWater(_source->GetPhaseMask(), x, y, z, _source->GetCollisionHeight()) &&
_source->GetMap()->IsInWater(_source->GetPhaseMask(), destX, destY, destZ, _source->GetCollisionHeight()) &&
(!checkSwim || !_sourceCreature || _sourceCreature->CanSwim());
}

View file

@ -110,6 +110,9 @@ namespace Movement
[[nodiscard]] Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx + 1, false) : Vector3(); }
[[nodiscard]] int32 currentPathIdx() const;
[[nodiscard]] bool HasAnimation() const { return splineflags.animation; }
[[nodiscard]] uint8 GetAnimationType() const { return splineflags.animId; }
bool onTransport;
[[nodiscard]] std::string ToString() const;
[[nodiscard]] bool HasStarted() const

View file

@ -5784,7 +5784,7 @@ void AuraEffect::HandlePreventResurrection(AuraApplication const* aurApp, uint8
if (apply)
aurApp->GetTarget()->RemoveByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER);
else if (!aurApp->GetTarget()->GetBaseMap()->Instanceable())
else if (!aurApp->GetTarget()->GetMap()->Instanceable())
aurApp->GetTarget()->SetByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER);
}

View file

@ -1327,9 +1327,9 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
float ground = m_caster->GetMapHeight(x, y, z, true);
float liquidLevel = VMAP_INVALID_HEIGHT_VALUE;
LiquidData liquidData;
if (m_caster->GetMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData, m_caster->GetCollisionHeight()))
liquidLevel = liquidData.level;
LiquidData const& liquidData = m_caster->GetMap()->GetLiquidData(m_caster->GetPhaseMask(), x, y, z, m_caster->GetCollisionHeight(), MAP_ALL_LIQUIDS);
if (liquidData.Status)
liquidLevel = liquidData.Level;
if (liquidLevel <= ground) // When there is no liquid Map::GetWaterOrGroundLevel returns ground level
{

View file

@ -444,7 +444,6 @@ public:
Map2ZoneCoordinates(zoneX, zoneY, zoneId);
Map const* map = object->GetMap();
float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT);
float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ());
@ -459,7 +458,7 @@ public:
if (haveVMap)
{
if (map->IsOutdoors(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()))
if (object->IsOutdoors())
handler->PSendSysMessage("You are outdoors");
else
handler->PSendSysMessage("You are indoors");
@ -476,11 +475,11 @@ public:
cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(),
zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap);
LiquidData liquidStatus;
ZLiquidStatus status = map->getLiquidStatus(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), MAP_ALL_LIQUIDS, &liquidStatus);
LiquidData const& liquidData = object->GetLiquidData();
if (liquidData.Status)
handler->PSendSysMessage(LANG_LIQUID_STATUS, liquidData.Level, liquidData.DepthLevel, liquidData.Entry, liquidData.Flags, liquidData.Status);
if (status)
handler->PSendSysMessage(LANG_LIQUID_STATUS, liquidStatus.level, liquidStatus.depth_level, liquidStatus.entry, liquidStatus.type_flags, status);
if (object->GetTransport())
handler->PSendSysMessage("Transport offset: %.2f, %.2f, %.2f, %.2f", object->m_movementInfo.transport.pos.GetPositionX(), object->m_movementInfo.transport.pos.GetPositionY(), object->m_movementInfo.transport.pos.GetPositionZ(), object->m_movementInfo.transport.pos.GetOrientation());

View file

@ -191,7 +191,7 @@ public:
handler->PSendSysMessage(LANG_TELEPORTING_TO, nameLink.c_str(), handler->GetAcoreString(LANG_OFFLINE), tele->name.c_str());
Player::SavePositionInDB(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation,
sMapMgr->GetZoneId(tele->mapId, tele->position_x, tele->position_y, tele->position_z), target_guid);
sMapMgr->GetZoneId(PHASEMASK_NORMAL, tele->mapId, tele->position_x, tele->position_y, tele->position_z), target_guid);
}
return true;

View file

@ -2171,7 +2171,7 @@ public:
Position pos;
caster->GetPosition(&pos);
caster->GetNearPosition(pos, 5.0f, 0.0f);
pos.m_positionZ = caster->GetBaseMap()->GetHeight(caster->GetPhaseMask(), pos.GetPositionX(), pos.GetPositionY(), caster->GetPositionZ(), true, 50.0f);
pos.m_positionZ = caster->GetMap()->GetHeight(caster->GetPhaseMask(), pos.GetPositionX(), pos.GetPositionY(), caster->GetPositionZ(), true, 50.0f);
pos.m_positionZ += 0.1f;
caster->SendMeleeAttackStop(caster->GetVictim());
caster->GetMotionMaster()->MoveLand(POINT_LAND, pos, 7.0f);

View file

@ -2004,6 +2004,7 @@ struct VehicleSeatEntry
VEHICLE_SEAT_FLAG_B_USABLE_FORCED_3 | VEHICLE_SEAT_FLAG_B_USABLE_FORCED_4)));
}
[[nodiscard]] bool IsEjectable() const { return m_flagsB & VEHICLE_SEAT_FLAG_B_EJECTABLE; }
[[nodiscard]] bool CanControl() const { return (m_flags & VEHICLE_SEAT_FLAG_CAN_CONTROL) != 0; }
};
struct WMOAreaTableEntry