feat(Core/CreatureAI): improve npc position during the combat (#3369)
+ tangent equation to find correct angle and distance when moving + implemented proper backward * Improved performance + random angle margin * chore: add tollerance calculation in instance * improved LOS checks with movements * implemented collisions using raycast (imported by TC) + improved collision detection for CanReachPositionAndGetCoords + improved collision check + set correct flags for the backward movement + first implementation of slope angle (to improve) Co-authored-by: Yehonal <yehonal.azeroth@gmail.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
|||||||
#include "DBCStores.h"
|
#include "DBCStores.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "VMapDefinitions.h"
|
#include "VMapDefinitions.h"
|
||||||
|
#include "GridDefines.h"
|
||||||
|
|
||||||
using G3D::Vector3;
|
using G3D::Vector3;
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ namespace VMAP
|
|||||||
Vector3 VMapManager2::convertPositionToInternalRep(float x, float y, float z) const
|
Vector3 VMapManager2::convertPositionToInternalRep(float x, float y, float z) const
|
||||||
{
|
{
|
||||||
Vector3 pos;
|
Vector3 pos;
|
||||||
const float mid = 0.5f * 64.0f * 533.33333333f;
|
const float mid = 0.5f * MAX_NUMBER_OF_GRIDS * SIZE_OF_GRIDS;
|
||||||
pos.x = mid - x;
|
pos.x = mid - x;
|
||||||
pos.y = mid - y;
|
pos.y = mid - y;
|
||||||
pos.z = z;
|
pos.z = z;
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GEOMETRY_H
|
||||||
|
#define _GEOMETRY_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
inline float getAngle(float startX, float startY, float destX, float destY)
|
||||||
|
{
|
||||||
|
auto dx = destX - startX;
|
||||||
|
auto dy = destY - startY;
|
||||||
|
|
||||||
|
auto ang = atan2(dy, dx);
|
||||||
|
ang = (ang >= 0) ? ang : 2 * M_PI + ang;
|
||||||
|
return ang;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float getSlopeAngle(float startX, float startY, float startZ, float destX, float destY, float destZ)
|
||||||
|
{
|
||||||
|
auto a = (startX * destX + startY * destY + startZ * destZ);
|
||||||
|
auto b = sqrt(pow(startX,2.0f) + pow(startY,2.0f) + pow(startZ, 2.0f));
|
||||||
|
auto c = sqrt(pow(destX,2.0f) + pow(destY,2.0f) + pow(destZ, 2.0f));
|
||||||
|
|
||||||
|
auto ang = acos(a / (b * c));
|
||||||
|
|
||||||
|
if (isnan(ang))
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ang;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float getSlopeAngleAbs(float startX, float startY, float startZ, float destX, float destY, float destZ)
|
||||||
|
{
|
||||||
|
return abs(getSlopeAngle(startX, startY, startZ, destX, destY, destZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -257,6 +257,40 @@ bool CreatureAI::_EnterEvadeMode()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CreatureAI::MoveCircleChecks()
|
||||||
|
{
|
||||||
|
Unit *victim = me->GetVictim();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!victim ||
|
||||||
|
!me->IsFreeToMove() ||
|
||||||
|
!me->IsWithinMeleeRange(victim) ||
|
||||||
|
(victim->GetTypeId() != TYPEID_PLAYER && !victim->IsPet())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
me->GetMotionMaster()->MoveCircleTarget(me->GetVictim());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreatureAI::MoveBackwardsChecks() {
|
||||||
|
Unit *victim = me->GetVictim();
|
||||||
|
|
||||||
|
if (
|
||||||
|
!victim ||
|
||||||
|
!me->IsFreeToMove() ||
|
||||||
|
(victim->GetTypeId() != TYPEID_PLAYER && !victim->IsPet())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float moveDist = CalculatePct(me->GetCombatReach() + victim->GetCombatReach(), 100);
|
||||||
|
|
||||||
|
me->GetMotionMaster()->MoveBackwards(victim, moveDist);
|
||||||
|
}
|
||||||
|
|
||||||
Creature* CreatureAI::DoSummon(uint32 entry, const Position& pos, uint32 despawnTime, TempSummonType summonType)
|
Creature* CreatureAI::DoSummon(uint32 entry, const Position& pos, uint32 despawnTime, TempSummonType summonType)
|
||||||
{
|
{
|
||||||
return me->SummonCreature(entry, pos, summonType, despawnTime);
|
return me->SummonCreature(entry, pos, summonType, despawnTime);
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ public:
|
|||||||
|
|
||||||
~CreatureAI() override {}
|
~CreatureAI() override {}
|
||||||
|
|
||||||
|
void MoveCircleChecks();
|
||||||
|
void MoveBackwardsChecks();
|
||||||
|
|
||||||
/// == Reactions At =================================
|
/// == Reactions At =================================
|
||||||
|
|
||||||
// Called if IsVisible(Unit* who) is true at each who move, reaction at visibility zone enter
|
// Called if IsVisible(Unit* who) is true at each who move, reaction at visibility zone enter
|
||||||
|
|||||||
@@ -586,6 +586,36 @@ void Creature::Update(uint32 diff)
|
|||||||
RemoveCharmAuras();
|
RemoveCharmAuras();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Circling the target
|
||||||
|
if (diff >= m_moveCircleMovementTime)
|
||||||
|
{
|
||||||
|
AI()->MoveCircleChecks();
|
||||||
|
m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL * 2, MOVE_CIRCLE_CHECK_INTERVAL * 3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_moveCircleMovementTime -= diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are closer than 50% of the combat reach we are going to reposition the victim
|
||||||
|
if (Unit *victim = GetVictim();
|
||||||
|
victim && GetDistance(victim->GetPosition()) < CalculatePct(GetCombatReach() + victim->GetCombatReach(), 50)) {
|
||||||
|
if (diff >= m_moveBackwardsMovementTime)
|
||||||
|
{
|
||||||
|
AI()->MoveBackwardsChecks();
|
||||||
|
m_moveBackwardsMovementTime = urand(MOVE_BACKWARDS_CHECK_INTERVAL/2, MOVE_BACKWARDS_CHECK_INTERVAL * 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_moveBackwardsMovementTime -= diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_moveBackwardsMovementTime = MOVE_BACKWARDS_CHECK_INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!IsInEvadeMode() && IsAIEnabled)
|
if (!IsInEvadeMode() && IsAIEnabled)
|
||||||
{
|
{
|
||||||
// do not allow the AI to be changed during update
|
// do not allow the AI to be changed during update
|
||||||
@@ -655,6 +685,20 @@ void Creature::Update(uint32 diff)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Creature::IsFreeToMove()
|
||||||
|
{
|
||||||
|
uint32 moveFlags = m_movementInfo.GetMovementFlags();
|
||||||
|
// Do not reposition ourself when we are not allowed to move
|
||||||
|
if ((IsMovementPreventedByCasting() || isMoving() || !CanFreeMove()) &&
|
||||||
|
(GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE ||
|
||||||
|
moveFlags & MOVEMENTFLAG_SPLINE_ENABLED))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Creature::Regenerate(Powers power)
|
void Creature::Regenerate(Powers power)
|
||||||
{
|
{
|
||||||
uint32 curValue = GetPower(power);
|
uint32 curValue = GetPower(power);
|
||||||
@@ -1522,8 +1566,10 @@ bool Creature::CanStartAttack(Unit const* who) const
|
|||||||
|
|
||||||
// This set of checks is should be done only for creatures
|
// This set of checks is should be done only for creatures
|
||||||
if ((HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC) && who->GetTypeId() != TYPEID_PLAYER) || // flag is valid only for non player characters
|
if ((HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC) && who->GetTypeId() != TYPEID_PLAYER) || // flag is valid only for non player characters
|
||||||
(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) && who->GetTypeId() == TYPEID_PLAYER)) // immune to PC and target is a player, return false
|
(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC) && who->GetTypeId() == TYPEID_PLAYER)) // immune to PC and target is a player, return false
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (Unit* owner = who->GetOwner())
|
if (Unit* owner = who->GetOwner())
|
||||||
if (owner->GetTypeId() == TYPEID_PLAYER && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC)) // immune to PC and target has player owner
|
if (owner->GetTypeId() == TYPEID_PLAYER && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC)) // immune to PC and target has player owner
|
||||||
@@ -2833,6 +2879,16 @@ void Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target)
|
|||||||
SetInFront(target);
|
SetInFront(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Creature::HasSpellFocus(Spell const* focusSpell) const
|
||||||
|
{
|
||||||
|
if (isDead()) // dead creatures cannot focus
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return focusSpell ? (focusSpell == _spellFocusInfo.Spell) : (_spellFocusInfo.Spell || _spellFocusInfo.Delay);
|
||||||
|
}
|
||||||
|
|
||||||
void Creature::ReleaseFocus(Spell const* focusSpell)
|
void Creature::ReleaseFocus(Spell const* focusSpell)
|
||||||
{
|
{
|
||||||
// focused to something else
|
// focused to something else
|
||||||
@@ -2890,3 +2946,25 @@ float Creature::GetAttackDistance(Unit const* player) const
|
|||||||
|
|
||||||
return (retDistance * aggroRate);
|
return (retDistance * aggroRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Creature::IsMovementPreventedByCasting() const
|
||||||
|
{
|
||||||
|
Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL];
|
||||||
|
// first check if currently a movement allowed channel is active and we're not casting
|
||||||
|
if (!!spell && spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive() && spell->GetSpellInfo()->IsMoveAllowedChannel())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasSpellFocus())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasUnitState(UNIT_STATE_CASTING))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -510,6 +510,15 @@ public:
|
|||||||
bool SetWaterWalking(bool enable, bool packetOnly = false) override;
|
bool SetWaterWalking(bool enable, bool packetOnly = false) override;
|
||||||
bool SetFeatherFall(bool enable, bool packetOnly = false) override;
|
bool SetFeatherFall(bool enable, bool packetOnly = false) override;
|
||||||
bool SetHover(bool enable, bool packetOnly = false) override;
|
bool SetHover(bool enable, bool packetOnly = false) override;
|
||||||
|
bool HasSpellFocus(Spell const* focusSpell = nullptr) const;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
::Spell const* Spell = nullptr;
|
||||||
|
uint32 Delay = 0; // ms until the creature's target should snap back (0 = no snapback scheduled)
|
||||||
|
uint64 Target; // the creature's "real" target while casting
|
||||||
|
float Orientation = 0.0f; // the creature's "real" orientation while casting
|
||||||
|
} _spellFocusInfo;
|
||||||
|
|
||||||
[[nodiscard]] uint32 GetShieldBlockValue() const override
|
[[nodiscard]] uint32 GetShieldBlockValue() const override
|
||||||
{
|
{
|
||||||
@@ -714,11 +723,18 @@ public:
|
|||||||
void SetTarget(uint64 guid) override;
|
void SetTarget(uint64 guid) override;
|
||||||
void FocusTarget(Spell const* focusSpell, WorldObject const* target);
|
void FocusTarget(Spell const* focusSpell, WorldObject const* target);
|
||||||
void ReleaseFocus(Spell const* focusSpell);
|
void ReleaseFocus(Spell const* focusSpell);
|
||||||
|
bool IsMovementPreventedByCasting() const;
|
||||||
|
|
||||||
// Part of Evade mechanics
|
// Part of Evade mechanics
|
||||||
[[nodiscard]] time_t GetLastDamagedTime() const { return _lastDamagedTime; }
|
[[nodiscard]] time_t GetLastDamagedTime() const { return _lastDamagedTime; }
|
||||||
void SetLastDamagedTime(time_t val) { _lastDamagedTime = val; }
|
void SetLastDamagedTime(time_t val) { _lastDamagedTime = val; }
|
||||||
|
|
||||||
|
bool IsFreeToMove();
|
||||||
|
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 1500;
|
||||||
|
static constexpr uint32 MOVE_BACKWARDS_CHECK_INTERVAL = 2000;
|
||||||
|
uint32 m_moveCircleMovementTime = MOVE_CIRCLE_CHECK_INTERVAL;
|
||||||
|
uint32 m_moveBackwardsMovementTime = MOVE_BACKWARDS_CHECK_INTERVAL;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, const CreatureData* data = nullptr);
|
bool CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, const CreatureData* data = nullptr);
|
||||||
bool InitEntry(uint32 entry, const CreatureData* data = nullptr);
|
bool InitEntry(uint32 entry, const CreatureData* data = nullptr);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
#include "Geometry.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
#include "Opcodes.h"
|
#include "Opcodes.h"
|
||||||
@@ -1150,13 +1151,19 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz, LineOfSightChecks ch
|
|||||||
{
|
{
|
||||||
if (IsInWorld())
|
if (IsInWorld())
|
||||||
{
|
{
|
||||||
|
oz += GetCollisionHeight();
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
if (GetTypeId() == TYPEID_PLAYER)
|
if (GetTypeId() == TYPEID_PLAYER)
|
||||||
|
{
|
||||||
GetPosition(x, y, z);
|
GetPosition(x, y, z);
|
||||||
|
z += GetCollisionHeight();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
GetHitSpherePointFor({ ox, oy, oz }, x, y, z);
|
GetHitSpherePointFor({ ox, oy, oz }, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
return GetMap()->isInLineOfSight(x, y, z + 2.0f, ox, oy, oz + 2.0f, GetPhaseMask(), checks);
|
return GetMap()->isInLineOfSight(x, y, z, ox, oy, oz, GetPhaseMask(), checks);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1302,12 +1309,7 @@ float Position::GetAngle(const Position* obj) const
|
|||||||
// Return angle in range 0..2*pi
|
// Return angle in range 0..2*pi
|
||||||
float Position::GetAngle(const float x, const float y) const
|
float Position::GetAngle(const float x, const float y) const
|
||||||
{
|
{
|
||||||
float dx = x - GetPositionX();
|
return getAngle(GetPositionX(), GetPositionY(), x, y);
|
||||||
float dy = y - GetPositionY();
|
|
||||||
|
|
||||||
float ang = atan2(dy, dx);
|
|
||||||
ang = (ang >= 0) ? ang : 2 * M_PI + ang;
|
|
||||||
return ang;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Position::GetSinCos(const float x, const float y, float& vsin, float& vcos) const
|
void Position::GetSinCos(const float x, const float y, float& vsin, float& vcos) const
|
||||||
@@ -2680,23 +2682,83 @@ void WorldObject::MovePosition(Position& pos, float dist, float angle)
|
|||||||
pos.m_orientation = m_orientation;
|
pos.m_orientation = m_orientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldObject::MovePositionToFirstCollision(Position& pos, float dist, float angle)
|
Position WorldObject::GetFirstCollisionPosition(float startX, float startY, float startZ, float destX, float destY)
|
||||||
{
|
{
|
||||||
angle += m_orientation;
|
auto dx = destX - startX;
|
||||||
|
auto dy = destY - startY;
|
||||||
|
|
||||||
|
auto ang = atan2(dy, dx);
|
||||||
|
ang = (ang >= 0) ? ang : 2 * M_PI + ang;
|
||||||
|
Position pos = Position(startX, startY, startZ, ang);
|
||||||
|
|
||||||
|
auto distance = pos.GetExactDist2d(destX,destY);
|
||||||
|
|
||||||
|
MovePositionToFirstCollision(pos, distance, ang);
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
Position WorldObject::GetFirstCollisionPosition(float destX, float destY, float destZ)
|
||||||
|
{
|
||||||
|
Position pos = GetPosition();
|
||||||
|
auto distance = GetExactDistSq(destX,destY,destZ);
|
||||||
|
|
||||||
|
auto dx = destX - pos.GetPositionX();
|
||||||
|
auto dy = destY - pos.GetPositionY();
|
||||||
|
|
||||||
|
auto ang = atan2(dy, dx);
|
||||||
|
ang = (ang >= 0) ? ang : 2 * M_PI + ang;
|
||||||
|
|
||||||
|
MovePositionToFirstCollision(pos, distance, ang);
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
Position WorldObject::GetFirstCollisionPosition(float dist, float angle)
|
||||||
|
{
|
||||||
|
Position pos = GetPosition();
|
||||||
|
GetFirstCollisionPosition(pos, dist, angle);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \return true -> collision, false -> no collision
|
||||||
|
*/
|
||||||
|
bool WorldObject::MovePositionToFirstCollision(Position& pos, float dist, float angle)
|
||||||
|
{
|
||||||
|
angle += pos.GetOrientation();
|
||||||
float destx, desty, destz;
|
float destx, desty, destz;
|
||||||
destx = pos.m_positionX + dist * cos(angle);
|
destx = pos.m_positionX + dist * cos(angle);
|
||||||
desty = pos.m_positionY + dist * sin(angle);
|
desty = pos.m_positionY + dist * sin(angle);
|
||||||
destz = pos.m_positionZ;
|
destz = pos.m_positionZ;
|
||||||
if (isType(TYPEMASK_UNIT | TYPEMASK_PLAYER) && !ToUnit()->IsInWater())
|
|
||||||
destz += 2.0f;
|
|
||||||
|
|
||||||
// Prevent invalid coordinates here, position is unchanged
|
// Prevent invalid coordinates here, position is unchanged
|
||||||
if (!acore::IsValidMapCoord(destx, desty))
|
if (!acore::IsValidMapCoord(destx, desty))
|
||||||
{
|
{
|
||||||
sLog->outCrash("WorldObject::MovePositionToFirstCollision invalid coordinates X: %f and Y: %f were passed!", destx, desty);
|
sLog->outCrash("WorldObject::MovePositionToFirstCollision invalid coordinates X: %f and Y: %f were passed!", destx, desty);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use a detour raycast to get our first collision point
|
||||||
|
PathGenerator path(this);
|
||||||
|
path.SetUseRaycast(true);
|
||||||
|
bool result = path.CalculatePath(destx, desty, destz, false);
|
||||||
|
|
||||||
|
// Check for valid path types before we proceed
|
||||||
|
if (!(path.GetPathType() & PATHFIND_NOT_USING_PATH))
|
||||||
|
{
|
||||||
|
if (path.GetPathType() & ~(PATHFIND_NORMAL | PATHFIND_SHORTCUT | PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY_END)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// collision check
|
||||||
|
bool col = (!result || (path.GetPathType() & PATHFIND_SHORTCUT) || (path.GetPathType() & PATHFIND_FARFROMPOLY));
|
||||||
|
|
||||||
|
G3D::Vector3 endPos = path.GetPath().back();
|
||||||
|
destx = endPos.x;
|
||||||
|
desty = endPos.y;
|
||||||
|
destz = endPos.z;
|
||||||
|
|
||||||
// Xinef: ugly hack for dalaran arena
|
// Xinef: ugly hack for dalaran arena
|
||||||
float selfAddition = 1.5f;
|
float selfAddition = 1.5f;
|
||||||
float allowedDiff = 6.0f;
|
float allowedDiff = 6.0f;
|
||||||
@@ -2710,10 +2772,8 @@ void WorldObject::MovePositionToFirstCollision(Position& pos, float dist, float
|
|||||||
else
|
else
|
||||||
UpdateAllowedPositionZ(destx, desty, destz);
|
UpdateAllowedPositionZ(destx, desty, destz);
|
||||||
|
|
||||||
bool col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(GetMapId(), pos.m_positionX, pos.m_positionY, pos.m_positionZ + selfAddition, destx, desty, destz + 0.5f, destx, desty, destz, -0.5f);
|
|
||||||
|
|
||||||
// collision occured
|
// collision occured
|
||||||
if (col)
|
if (VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(GetMapId(), pos.m_positionX, pos.m_positionY, pos.m_positionZ + selfAddition, destx, desty, destz + 0.5f, destx, desty, destz, -0.5f))
|
||||||
{
|
{
|
||||||
// move back a bit
|
// move back a bit
|
||||||
if (pos.GetExactDist2d(destx, desty) > CONTACT_DISTANCE)
|
if (pos.GetExactDist2d(destx, desty) > CONTACT_DISTANCE)
|
||||||
@@ -2723,13 +2783,11 @@ void WorldObject::MovePositionToFirstCollision(Position& pos, float dist, float
|
|||||||
}
|
}
|
||||||
|
|
||||||
newDist = sqrt((pos.m_positionX - destx) * (pos.m_positionX - destx) + (pos.m_positionY - desty) * (pos.m_positionY - desty));
|
newDist = sqrt((pos.m_positionX - destx) * (pos.m_positionX - destx) + (pos.m_positionY - desty) * (pos.m_positionY - desty));
|
||||||
|
col = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check dynamic collision
|
// check dynamic collision, Collided with a gameobject
|
||||||
col = GetMap()->getObjectHitPos(GetPhaseMask(), pos.m_positionX, pos.m_positionY, pos.m_positionZ + selfAddition, destx, desty, destz + 0.5f, destx, desty, destz, -0.5f);
|
if (GetMap()->getObjectHitPos(GetPhaseMask(), pos.m_positionX, pos.m_positionY, pos.m_positionZ + selfAddition, destx, desty, destz + 0.5f, destx, desty, destz, -0.5f))
|
||||||
|
|
||||||
// Collided with a gameobject
|
|
||||||
if (col)
|
|
||||||
{
|
{
|
||||||
// move back a bit
|
// move back a bit
|
||||||
if (pos.GetExactDist2d(destx, desty) > CONTACT_DISTANCE)
|
if (pos.GetExactDist2d(destx, desty) > CONTACT_DISTANCE)
|
||||||
@@ -2738,6 +2796,7 @@ void WorldObject::MovePositionToFirstCollision(Position& pos, float dist, float
|
|||||||
desty -= CONTACT_DISTANCE * sin(angle);
|
desty -= CONTACT_DISTANCE * sin(angle);
|
||||||
}
|
}
|
||||||
newDist = sqrt((pos.m_positionX - destx) * (pos.m_positionX - destx) + (pos.m_positionY - desty) * (pos.m_positionY - desty));
|
newDist = sqrt((pos.m_positionX - destx) * (pos.m_positionX - destx) + (pos.m_positionY - desty) * (pos.m_positionY - desty));
|
||||||
|
col = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
float step = newDist / 10.0f;
|
float step = newDist / 10.0f;
|
||||||
@@ -2779,6 +2838,28 @@ void WorldObject::MovePositionToFirstCollision(Position& pos, float dist, float
|
|||||||
|
|
||||||
pos.Relocate(destx, desty, destz);
|
pos.Relocate(destx, desty, destz);
|
||||||
pos.m_orientation = m_orientation;
|
pos.m_orientation = m_orientation;
|
||||||
|
|
||||||
|
// position has no ground under it (or is too far away)
|
||||||
|
/* if (ground <= INVALID_HEIGHT)
|
||||||
|
{
|
||||||
|
if (Unit const* unit = ToUnit())
|
||||||
|
{
|
||||||
|
// unit can fly, ignore.
|
||||||
|
if (unit->CanFly())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fall back to gridHeight if any
|
||||||
|
float gridHeight = GetMap()->GetHeight(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
|
||||||
|
if (gridHeight > INVALID_HEIGHT)
|
||||||
|
{
|
||||||
|
pos.m_positionZ = gridHeight + unit->GetHoverHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
|
||||||
|
return col;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldObject::MovePositionToFirstCollisionForTotem(Position& pos, float dist, float angle, bool forGameObject)
|
void WorldObject::MovePositionToFirstCollisionForTotem(Position& pos, float dist, float angle, bool forGameObject)
|
||||||
@@ -3088,3 +3169,8 @@ uint64 WorldObject::GetTransGUID() const
|
|||||||
return GetTransport()->GetGUID();
|
return GetTransport()->GetGUID();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float WorldObject::GetMapHeight(float x, float y, float z, bool vmap/* = true*/, float distanceToSearch/* = DEFAULT_HEIGHT_SEARCH*/) const
|
||||||
|
{
|
||||||
|
return GetMap()->GetHeight(GetPhaseMask(), x, y, z, vmap, distanceToSearch);
|
||||||
|
}
|
||||||
|
|||||||
@@ -796,7 +796,10 @@ public:
|
|||||||
GetPosition(&pos);
|
GetPosition(&pos);
|
||||||
MovePosition(pos, dist, angle);
|
MovePosition(pos, dist, angle);
|
||||||
}
|
}
|
||||||
void MovePositionToFirstCollision(Position& pos, float dist, float angle);
|
bool MovePositionToFirstCollision(Position& pos, float dist, float angle);
|
||||||
|
Position GetFirstCollisionPosition(float startX, float startY, float startZ, float destX, float destY);
|
||||||
|
Position GetFirstCollisionPosition(float destX, float destY, float destZ);
|
||||||
|
Position GetFirstCollisionPosition(float dist, float angle);
|
||||||
void GetFirstCollisionPosition(Position& pos, float dist, float angle)
|
void GetFirstCollisionPosition(Position& pos, float dist, float angle)
|
||||||
{
|
{
|
||||||
GetPosition(&pos);
|
GetPosition(&pos);
|
||||||
@@ -1060,6 +1063,9 @@ public:
|
|||||||
[[nodiscard]] virtual float GetStationaryY() const { return GetPositionY(); }
|
[[nodiscard]] virtual float GetStationaryY() const { return GetPositionY(); }
|
||||||
[[nodiscard]] virtual float GetStationaryZ() const { return GetPositionZ(); }
|
[[nodiscard]] virtual float GetStationaryZ() const { return GetPositionZ(); }
|
||||||
[[nodiscard]] virtual float GetStationaryO() const { return GetOrientation(); }
|
[[nodiscard]] virtual float GetStationaryO() const { return GetOrientation(); }
|
||||||
|
float GetMapHeight(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f) const; // DEFAULT_HEIGHT_SEARCH in map.h
|
||||||
|
|
||||||
|
virtual float GetCollisionHeight() const { return 0.0f; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
|||||||
@@ -2561,40 +2561,6 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] bool CanFly() const override { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY); }
|
[[nodiscard]] bool CanFly() const override { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY); }
|
||||||
|
|
||||||
//! Return collision height sent to client
|
|
||||||
float GetCollisionHeight(bool mounted)
|
|
||||||
{
|
|
||||||
if (mounted)
|
|
||||||
{
|
|
||||||
CreatureDisplayInfoEntry const* mountDisplayInfo = sCreatureDisplayInfoStore.LookupEntry(GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID));
|
|
||||||
if (!mountDisplayInfo)
|
|
||||||
return GetCollisionHeight(false);
|
|
||||||
|
|
||||||
CreatureModelDataEntry const* mountModelData = sCreatureModelDataStore.LookupEntry(mountDisplayInfo->ModelId);
|
|
||||||
if (!mountModelData)
|
|
||||||
return GetCollisionHeight(false);
|
|
||||||
|
|
||||||
CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId());
|
|
||||||
ASSERT(displayInfo);
|
|
||||||
CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId);
|
|
||||||
ASSERT(modelData);
|
|
||||||
|
|
||||||
float scaleMod = GetFloatValue(OBJECT_FIELD_SCALE_X); // 99% sure about this
|
|
||||||
|
|
||||||
return scaleMod * mountModelData->MountHeight + modelData->CollisionHeight * 0.5f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//! Dismounting case - use basic default model data
|
|
||||||
CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId());
|
|
||||||
ASSERT(displayInfo);
|
|
||||||
CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId);
|
|
||||||
ASSERT(modelData);
|
|
||||||
|
|
||||||
return modelData->CollisionHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OURS
|
// OURS
|
||||||
// saving
|
// saving
|
||||||
void AdditionalSavingAddMask(uint8 mask) { m_additionalSaveTimer = 2000; m_additionalSaveMask |= mask; }
|
void AdditionalSavingAddMask(uint8 mask) { m_additionalSaveTimer = 2000; m_additionalSaveMask |= mask; }
|
||||||
|
|||||||
@@ -153,9 +153,28 @@ ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget,
|
|||||||
#pragma warning(disable:4355)
|
#pragma warning(disable:4355)
|
||||||
#endif
|
#endif
|
||||||
Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject),
|
Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject),
|
||||||
m_movedByPlayer(nullptr), m_lastSanctuaryTime(0), IsAIEnabled(false), NeedChangeAI(false),
|
m_movedByPlayer(nullptr),
|
||||||
m_ControlledByPlayer(false), m_CreatedByPlayer(false), movespline(new Movement::MoveSpline()), i_AI(nullptr), i_disabledAI(nullptr), m_realRace(0), m_race(0), m_AutoRepeatFirstCast(false), m_procDeep(0), m_removedAurasCount(0),
|
m_lastSanctuaryTime(0),
|
||||||
i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_ThreatManager(this), m_vehicle(nullptr), m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_HostileRefManager(this)
|
IsAIEnabled(false),
|
||||||
|
NeedChangeAI(false),
|
||||||
|
m_ControlledByPlayer(false),
|
||||||
|
m_CreatedByPlayer(false),
|
||||||
|
movespline(new Movement::MoveSpline()),
|
||||||
|
i_AI(nullptr),
|
||||||
|
i_disabledAI(nullptr),
|
||||||
|
m_realRace(0),
|
||||||
|
m_race(0),
|
||||||
|
m_AutoRepeatFirstCast(false),
|
||||||
|
m_procDeep(0),
|
||||||
|
m_removedAurasCount(0),
|
||||||
|
i_motionMaster(new MotionMaster(this)),
|
||||||
|
m_regenTimer(0),
|
||||||
|
m_ThreatManager(this),
|
||||||
|
m_vehicle(nullptr),
|
||||||
|
m_vehicleKit(nullptr),
|
||||||
|
m_unitTypeMask(UNIT_MASK_NONE),
|
||||||
|
m_HostileRefManager(this),
|
||||||
|
m_comboTarget(nullptr)
|
||||||
{
|
{
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(default:4355)
|
#pragma warning(default:4355)
|
||||||
@@ -577,6 +596,21 @@ bool Unit::IsWithinMeleeRange(const Unit* obj, float dist) const
|
|||||||
return distsq < maxdist * maxdist;
|
return distsq < maxdist * maxdist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Unit::IsWithinRange(Unit const* obj, float dist) const
|
||||||
|
{
|
||||||
|
if (!obj || !IsInMap(obj) || !InSamePhase(obj))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dx = GetPositionX() - obj->GetPositionX();
|
||||||
|
auto dy = GetPositionY() - obj->GetPositionY();
|
||||||
|
auto dz = GetPositionZ() - obj->GetPositionZ();
|
||||||
|
auto distsq = dx * dx + dy * dy + dz * dz;
|
||||||
|
|
||||||
|
return distsq <= dist * dist;
|
||||||
|
}
|
||||||
|
|
||||||
bool Unit::GetRandomContactPoint(const Unit* obj, float& x, float& y, float& z, bool force) const
|
bool Unit::GetRandomContactPoint(const Unit* obj, float& x, float& y, float& z, bool force) const
|
||||||
{
|
{
|
||||||
float combat_reach = GetCombatReach();
|
float combat_reach = GetCombatReach();
|
||||||
@@ -2189,6 +2223,122 @@ void Unit::AttackerStateUpdate (Unit* victim, WeaponAttackType attType, bool ext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Position* Unit::GetMeleeAttackPoint(Unit* attacker)
|
||||||
|
{
|
||||||
|
if (!attacker) // only player & pets to save CPU
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttackerSet attackers = getAttackers();
|
||||||
|
|
||||||
|
if (attackers.size() <= 1) // if the attackers are not more than one
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
float meleeReach = attacker->GetMeleeReach();
|
||||||
|
|
||||||
|
if (meleeReach <= 0)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentAngle, minDistance = 0;
|
||||||
|
Unit *refUnit = nullptr;
|
||||||
|
uint32 validAttackers=0;
|
||||||
|
|
||||||
|
for (const auto& otherAttacker: attackers)
|
||||||
|
{
|
||||||
|
// if the otherAttacker is not valid, skip
|
||||||
|
if (!otherAttacker ||
|
||||||
|
otherAttacker->GetGUID() == attacker->GetGUID() ||
|
||||||
|
!otherAttacker->IsWithinMeleeRange(this) ||
|
||||||
|
otherAttacker->isMoving()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float tempDist = attacker->GetExactDist2d(otherAttacker) - (attacker->GetObjectSize()/2) - (otherAttacker->GetObjectSize()/2);
|
||||||
|
|
||||||
|
if (tempDist == 0 || minDistance == 0 || tempDist < minDistance)
|
||||||
|
{
|
||||||
|
minDistance = tempDist;
|
||||||
|
currentAngle = GetAngle(otherAttacker);
|
||||||
|
refUnit = otherAttacker;
|
||||||
|
}
|
||||||
|
|
||||||
|
validAttackers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto attackerSize = attacker->GetObjectSize();
|
||||||
|
|
||||||
|
// in instance: the more attacker there are, the higher will be the tollerance
|
||||||
|
// outside: creatures should not intersecate
|
||||||
|
float distanceTollerance = attacker->GetMap()->IsDungeon() ? -attackerSize * tanh(validAttackers / 5.0f) : 0.0f;
|
||||||
|
|
||||||
|
if (!refUnit || minDistance > distanceTollerance)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ray = attackerSize > refUnit->GetObjectSize() ? attackerSize / 2.0f : refUnit->GetObjectSize() / 2.0f;
|
||||||
|
|
||||||
|
// Equation of tangent point to get the ideal angle to
|
||||||
|
// move away from collisions with another unit during combat
|
||||||
|
// NOTE: it works only when there's enough space between the
|
||||||
|
// attacker and the victim. We use a simpler one otherwise.
|
||||||
|
if (GetExactDist2d(refUnit) > ray)
|
||||||
|
{
|
||||||
|
double refUnitX = refUnit->GetPositionX();
|
||||||
|
double refUnitY = refUnit->GetPositionY();
|
||||||
|
double victimX = GetPositionX();
|
||||||
|
double victimY = GetPositionY();
|
||||||
|
|
||||||
|
// calculate tangent star
|
||||||
|
double a = 4.0f * ( pow(ray,2.0f) - pow(refUnitX,2.0f) + (2.0f * refUnitX * victimX) - pow(victimX,2.0f) );
|
||||||
|
double b = 8.0f * ( (refUnitX * refUnitY) + (victimX * victimY) - (victimX * refUnitY) - (refUnitX * victimY) );
|
||||||
|
double c = 4.0f * (- pow(victimY,2.0f) - pow(refUnitY,2.0f) + (2.0f*victimY*refUnitY) + pow(ray,2.0f));
|
||||||
|
|
||||||
|
if (a == 0) // should not happen
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
double sq = sqrt(pow(b,2.0f)-4.0f*a*c);
|
||||||
|
|
||||||
|
double m1 = (-b + sq) / (2.0f*a);
|
||||||
|
double m2 = (-b - sq) / (2.0f*a);
|
||||||
|
|
||||||
|
// tangents
|
||||||
|
double xT1 = ((-1.0f) * (m1*(victimY - m1*victimX - refUnitY) - refUnitX) ) / (1.0f + pow(m1,2.0f));
|
||||||
|
double xT2 = ((-1.0f) * (m2*(victimY - m2*victimX - refUnitY) - refUnitX) ) / (1.0f + pow(m2,2.0f));
|
||||||
|
|
||||||
|
double yT1 = m1*(xT1 - victimX) + victimY;
|
||||||
|
double yT2 = m2*(xT2 - victimX) + victimY;
|
||||||
|
|
||||||
|
double distance = sqrt(pow(yT2-yT1,2.0f) + pow(xT2-xT1,2.0f));
|
||||||
|
double exactDist = GetExactDist2d(xT1, yT1);
|
||||||
|
|
||||||
|
double ortDist = sqrt(pow(exactDist,2.0f) - pow(distance/2.0f,2.0f));
|
||||||
|
|
||||||
|
double angle = frand(0.1f,0.3f) + currentAngle + 2.0f * atan(distance / (2.0f * ortDist)) * (urand(0, 1) ? -1 : 1);
|
||||||
|
|
||||||
|
float x, y, z;
|
||||||
|
GetNearPoint(attacker, x, y, z, attackerSize, 0.0f, angle);
|
||||||
|
|
||||||
|
return new Position(x,y,z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float angle = frand(0.1f,0.3f) + currentAngle + atan(attackerSize / (meleeReach)) * (urand(0, 1) ? -1 : 1);
|
||||||
|
|
||||||
|
float x, y, z;
|
||||||
|
GetNearPoint(attacker, x, y, z, attackerSize, 0.0f, angle);
|
||||||
|
|
||||||
|
return new Position(x,y,z);
|
||||||
|
}
|
||||||
|
|
||||||
void Unit::HandleProcExtraAttackFor(Unit* victim)
|
void Unit::HandleProcExtraAttackFor(Unit* victim)
|
||||||
{
|
{
|
||||||
while (m_extraAttacks)
|
while (m_extraAttacks)
|
||||||
@@ -12554,7 +12704,7 @@ void Unit::Mount(uint32 mount, uint32 VehicleId, uint32 creatureEntry)
|
|||||||
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
|
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
|
||||||
data.append(GetPackGUID());
|
data.append(GetPackGUID());
|
||||||
data << uint32(sWorld->GetGameTime()); // Packet counter
|
data << uint32(sWorld->GetGameTime()); // Packet counter
|
||||||
data << player->GetCollisionHeight(true);
|
data << player->GetCollisionHeight();
|
||||||
player->GetSession()->SendPacket(&data);
|
player->GetSession()->SendPacket(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12574,7 +12724,7 @@ void Unit::Dismount()
|
|||||||
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
|
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
|
||||||
data.append(GetPackGUID());
|
data.append(GetPackGUID());
|
||||||
data << uint32(sWorld->GetGameTime()); // Packet counter
|
data << uint32(sWorld->GetGameTime()); // Packet counter
|
||||||
data << thisPlayer->GetCollisionHeight(false);
|
data << thisPlayer->GetCollisionHeight();
|
||||||
thisPlayer->GetSession()->SendPacket(&data);
|
thisPlayer->GetSession()->SendPacket(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19862,3 +20012,32 @@ bool Unit::IsInCombatWith(Unit const* who) const
|
|||||||
// Nothing found, false.
|
// Nothing found, false.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! Return collision height sent to client
|
||||||
|
float Unit::GetCollisionHeight() const
|
||||||
|
{
|
||||||
|
uint32 nativeDisplayId = GetNativeDisplayId();
|
||||||
|
if (IsMounted())
|
||||||
|
{
|
||||||
|
if (CreatureDisplayInfoEntry const* mountDisplayInfo = sCreatureDisplayInfoStore.LookupEntry(GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID));
|
||||||
|
CreatureModelDataEntry const* mountModelData = sCreatureModelDataStore.LookupEntry(mountDisplayInfo->ModelId))
|
||||||
|
{
|
||||||
|
CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(nativeDisplayId);
|
||||||
|
ASSERT(displayInfo);
|
||||||
|
CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId);
|
||||||
|
ASSERT(modelData);
|
||||||
|
|
||||||
|
float scaleMod = GetFloatValue(OBJECT_FIELD_SCALE_X); // 99% sure about this
|
||||||
|
|
||||||
|
return scaleMod * mountModelData->MountHeight + modelData->CollisionHeight * 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Dismounting case - use basic default model data
|
||||||
|
CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(nativeDisplayId);
|
||||||
|
ASSERT(displayInfo);
|
||||||
|
CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId);
|
||||||
|
ASSERT(modelData);
|
||||||
|
|
||||||
|
return modelData->CollisionHeight;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1255,6 +1255,26 @@ private:
|
|||||||
GlobalCooldownMgr _GlobalCooldownMgr;
|
GlobalCooldownMgr _GlobalCooldownMgr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AttackPosition {
|
||||||
|
AttackPosition(Position pos) : _pos(pos), _taken(false) {}
|
||||||
|
bool operator==(const int val)
|
||||||
|
{
|
||||||
|
return !val;
|
||||||
|
};
|
||||||
|
int operator=(const int val)
|
||||||
|
{
|
||||||
|
if (!val)
|
||||||
|
{
|
||||||
|
// _pos = NULL;
|
||||||
|
_taken = false;
|
||||||
|
return 0; // NULL
|
||||||
|
}
|
||||||
|
return 0; // NULL
|
||||||
|
};
|
||||||
|
Position _pos;
|
||||||
|
bool _taken;
|
||||||
|
};
|
||||||
|
|
||||||
// for clearing special attacks
|
// for clearing special attacks
|
||||||
#define REACTIVE_TIMER_START 5000
|
#define REACTIVE_TIMER_START 5000
|
||||||
|
|
||||||
@@ -1405,6 +1425,7 @@ public:
|
|||||||
virtual void SetCanDualWield(bool value) { m_canDualWield = value; }
|
virtual void SetCanDualWield(bool value) { m_canDualWield = value; }
|
||||||
[[nodiscard]] float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; }
|
[[nodiscard]] float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; }
|
||||||
[[nodiscard]] float GetMeleeReach() const { float reach = m_floatValues[UNIT_FIELD_COMBATREACH]; return reach > MIN_MELEE_REACH ? reach : MIN_MELEE_REACH; }
|
[[nodiscard]] float GetMeleeReach() const { float reach = m_floatValues[UNIT_FIELD_COMBATREACH]; return reach > MIN_MELEE_REACH ? reach : MIN_MELEE_REACH; }
|
||||||
|
[[nodiscard]] bool IsWithinRange(Unit const* obj, float dist) const;
|
||||||
bool IsWithinCombatRange(const Unit* obj, float dist2compare) const;
|
bool IsWithinCombatRange(const Unit* obj, float dist2compare) const;
|
||||||
bool IsWithinMeleeRange(const Unit* obj, float dist = MELEE_RANGE) const;
|
bool IsWithinMeleeRange(const Unit* obj, float dist = MELEE_RANGE) const;
|
||||||
bool GetRandomContactPoint(const Unit* target, float& x, float& y, float& z, bool force = false) const;
|
bool GetRandomContactPoint(const Unit* target, float& x, float& y, float& z, bool force = false) const;
|
||||||
@@ -1434,6 +1455,7 @@ public:
|
|||||||
bool AttackStop();
|
bool AttackStop();
|
||||||
void RemoveAllAttackers();
|
void RemoveAllAttackers();
|
||||||
[[nodiscard]] AttackerSet const& getAttackers() const { return m_attackers; }
|
[[nodiscard]] AttackerSet const& getAttackers() const { return m_attackers; }
|
||||||
|
[[nodiscard]] Position* GetMeleeAttackPoint(Unit* attacker);
|
||||||
[[nodiscard]] bool isAttackingPlayer() const;
|
[[nodiscard]] bool isAttackingPlayer() const;
|
||||||
[[nodiscard]] Unit* GetVictim() const { return m_attacking; }
|
[[nodiscard]] Unit* GetVictim() const { return m_attacking; }
|
||||||
|
|
||||||
@@ -2174,7 +2196,7 @@ public:
|
|||||||
|
|
||||||
uint32 GetDisplayId() { return GetUInt32Value(UNIT_FIELD_DISPLAYID); }
|
uint32 GetDisplayId() { return GetUInt32Value(UNIT_FIELD_DISPLAYID); }
|
||||||
virtual void SetDisplayId(uint32 modelId);
|
virtual void SetDisplayId(uint32 modelId);
|
||||||
uint32 GetNativeDisplayId() { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); }
|
[[nodiscard]] uint32 GetNativeDisplayId() const { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); }
|
||||||
void RestoreDisplayId();
|
void RestoreDisplayId();
|
||||||
void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); }
|
void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); }
|
||||||
void setTransForm(uint32 spellid) { m_transform = spellid;}
|
void setTransForm(uint32 spellid) { m_transform = spellid;}
|
||||||
@@ -2451,6 +2473,8 @@ public:
|
|||||||
// Movement info
|
// Movement info
|
||||||
Movement::MoveSpline* movespline;
|
Movement::MoveSpline* movespline;
|
||||||
|
|
||||||
|
[[nodiscard]] float GetCollisionHeight() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit Unit (bool isWorldObject);
|
explicit Unit (bool isWorldObject);
|
||||||
|
|
||||||
@@ -2566,6 +2590,7 @@ private:
|
|||||||
HostileRefManager m_HostileRefManager;
|
HostileRefManager m_HostileRefManager;
|
||||||
|
|
||||||
FollowerRefManager m_FollowingRefManager;
|
FollowerRefManager m_FollowingRefManager;
|
||||||
|
Unit* m_comboTarget;
|
||||||
|
|
||||||
ComboPointHolderSet m_ComboPointHolders;
|
ComboPointHolderSet m_ComboPointHolders;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Map.h"
|
#include "Map.h"
|
||||||
|
#include "Geometry.h"
|
||||||
#include "Battleground.h"
|
#include "Battleground.h"
|
||||||
#include "CellImpl.h"
|
#include "CellImpl.h"
|
||||||
#include "DynamicTree.h"
|
#include "DynamicTree.h"
|
||||||
@@ -3391,3 +3392,121 @@ void Map::SetZoneOverrideLight(uint32 zoneId, uint32 lightId, uint32 fadeInTime)
|
|||||||
player->SendDirectMessage(&data);
|
player->SendDirectMessage(&data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \param maxDeviationAngle the maximum deviation that a creature can take to reach the destination
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool Map::CanReachPositionAndGetCoords(Unit* who, PathGenerator path, bool checkCollision /*= true */, float maxHeight/* = 3.0f */, float maxSlopeAngle/* = M_PI/2 */, float maxDeviationAngle /*= M_PI*2 */) const
|
||||||
|
{
|
||||||
|
double deviation = 0.0f;
|
||||||
|
G3D::Vector3 prevPath = path.GetStartPosition();
|
||||||
|
for (auto & vector : path.GetPath())
|
||||||
|
{
|
||||||
|
float x = vector.x;
|
||||||
|
float y = vector.y;
|
||||||
|
float z = vector.z;
|
||||||
|
|
||||||
|
deviation += who->GetRelativeAngle(x, y);
|
||||||
|
|
||||||
|
// to reach the position the Unit must deviate
|
||||||
|
// the straight path with a higher margin than the one desired
|
||||||
|
// in this case we return false
|
||||||
|
if (deviation > maxDeviationAngle)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ang = getAngle(prevPath.x, prevPath.y, x, y);
|
||||||
|
|
||||||
|
if (CanReachPositionAndGetCoords(who, prevPath.x, prevPath.y, prevPath.z, ang, x, y, z, checkCollision, maxHeight, maxSlopeAngle))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevPath = vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Map::CanReachPositionAndGetCoords(Unit* who, float &destX, float &destY, float &destZ, bool checkCollision /*= true */, float maxHeight/* = 3.0f */, float maxSlopeAngle/* = M_PI/2 */) const
|
||||||
|
{
|
||||||
|
return CanReachPositionAndGetCoords(who, who->GetPositionX(), who->GetPositionY(), who->GetPositionZ(), who->GetOrientation(), destX, destY, destZ, checkCollision, maxHeight, maxSlopeAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief validate the new destination
|
||||||
|
*
|
||||||
|
* Check if a given unit can reach a specific point and set the correct Z coord based on difference in height
|
||||||
|
*
|
||||||
|
* \param maxHeight the desired max Height for the calculated Z coord. If the new Z exceed the maxHeight, this method returns false
|
||||||
|
|
||||||
|
* \return true if the destination is valid, false otherwise
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
bool Map::CanReachPositionAndGetCoords(Unit* who, float startX, float startY, float startZ, float startAngle, float &destX, float &destY, float &destZ, bool checkCollision /*= true */, float maxHeight/* = 3.0f */, float maxSlopeAngle/* = M_PI/2 */) const
|
||||||
|
{
|
||||||
|
acore::NormalizeMapCoord(destX);
|
||||||
|
acore::NormalizeMapCoord(destY);
|
||||||
|
|
||||||
|
const Map* _map = who->GetBaseMap();
|
||||||
|
|
||||||
|
// check map geometry for possible collision
|
||||||
|
if (checkCollision)
|
||||||
|
{
|
||||||
|
Position pos = Position(startX, startY, startZ, startAngle);
|
||||||
|
|
||||||
|
auto distance = pos.GetExactDist2d(destX,destY);
|
||||||
|
|
||||||
|
auto collided = who->MovePositionToFirstCollision(pos, distance, pos.GetRelativeAngle(destX,destY));
|
||||||
|
|
||||||
|
destX = pos.GetPositionX();
|
||||||
|
destY = pos.GetPositionY();
|
||||||
|
destZ = pos.GetPositionZ();
|
||||||
|
|
||||||
|
if (collided)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// otherwise calculate a new z
|
||||||
|
destZ = _map->GetHeight(who->GetPhaseMask(), destX, destY, destZ, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (destZ <= INVALID_HEIGHT || (destZ - startZ) > maxHeight)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float slopeAngle = getSlopeAngleAbs(startX, startY, startZ, destX, destY, destZ);
|
||||||
|
|
||||||
|
if (slopeAngle > maxSlopeAngle)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if water environment
|
||||||
|
bool is_water_now = _map->IsInWater(startX, startY, startZ);
|
||||||
|
|
||||||
|
if (!isInLineOfSight(startX, startY, startZ, destX, destY, destZ, who->GetPhaseMask(), LINEOFSIGHT_ALL_CHECKS))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_water_next = _map->IsInWater(destX, destY, destZ);
|
||||||
|
|
||||||
|
// if not compatible inhabit type for the next position, return false
|
||||||
|
if ((is_water_now && !is_water_next && who->GetTypeId() == TYPEID_UNIT && !((Creature*)who)->CanWalk()) ||
|
||||||
|
(!is_water_now && is_water_next && !who->CanSwim()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "MapRefManager.h"
|
#include "MapRefManager.h"
|
||||||
#include "DynamicTree.h"
|
#include "DynamicTree.h"
|
||||||
#include "GameObjectModel.h"
|
#include "GameObjectModel.h"
|
||||||
|
#include "PathGenerator.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "DataMap.h"
|
#include "DataMap.h"
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
@@ -50,6 +51,7 @@ class BattlegroundMap;
|
|||||||
class Transport;
|
class Transport;
|
||||||
class StaticTransport;
|
class StaticTransport;
|
||||||
class MotionTransport;
|
class MotionTransport;
|
||||||
|
class PathGenerator;
|
||||||
namespace acore
|
namespace acore
|
||||||
{
|
{
|
||||||
struct ObjectUpdater;
|
struct ObjectUpdater;
|
||||||
@@ -473,6 +475,9 @@ public:
|
|||||||
float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false, float maxSearchDist = 50.0f) const;
|
float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false, float maxSearchDist = 50.0f) const;
|
||||||
[[nodiscard]] float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
|
[[nodiscard]] float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
|
||||||
[[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks) const;
|
[[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks) const;
|
||||||
|
bool CanReachPositionAndGetCoords(Unit* who, PathGenerator path, bool checkCollision = true, float maxHeight = 3.0f, float maxSlopeAngle = M_PI/2, float maxDeviationAngle = M_PI*2) const;
|
||||||
|
bool CanReachPositionAndGetCoords(Unit* who, float &destX, float &destY, float &destZ, bool checkCollision = true, float maxHeight = 3.0f, float maxSlopeAngle = M_PI/2) const;
|
||||||
|
bool CanReachPositionAndGetCoords(Unit* who, float startX, float startY, float startZ, float startAngle, float &destX, float &destY, float &destZ, bool checkCollision = true, float maxHeight = 3.0f, float maxSlopeAngle = M_PI/2) const;
|
||||||
void Balance() { _dynamicTree.balance(); }
|
void Balance() { _dynamicTree.balance(); }
|
||||||
void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); }
|
void RemoveGameObjectModel(const GameObjectModel& model) { _dynamicTree.remove(model); }
|
||||||
void InsertGameObjectModel(const GameObjectModel& model) { _dynamicTree.insert(model); }
|
void InsertGameObjectModel(const GameObjectModel& model) { _dynamicTree.insert(model); }
|
||||||
|
|||||||
@@ -320,12 +320,81 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MotionMaster::MoveBackwards(Unit* target, float dist)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position const& pos = target->GetPosition();
|
||||||
|
float angle = target->GetAngle(_owner);
|
||||||
|
G3D::Vector3 point;
|
||||||
|
point.x = pos.m_positionX + dist * cosf(angle);
|
||||||
|
point.y = pos.m_positionY + dist * sinf(angle);
|
||||||
|
point.z = pos.m_positionZ;
|
||||||
|
|
||||||
|
//if (_owner->IsFlying())
|
||||||
|
// point.z = pos.m_positionZ;
|
||||||
|
//else
|
||||||
|
// point.z = _owner->GetMapHeight(point.x, point.y, point.z);
|
||||||
|
|
||||||
|
if (_owner->GetMap()->CanReachPositionAndGetCoords(_owner, point.x, point.y, point.z, true, 6.0f, M_PI/4))
|
||||||
|
{
|
||||||
|
Movement::MoveSplineInit init(_owner);
|
||||||
|
init.MoveTo(point.x, point.y, point.z, true);
|
||||||
|
init.SetFacing(target);
|
||||||
|
init.SetOrientationInversed();
|
||||||
|
init.Launch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotionMaster::MoveCircleTarget(Unit* target)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position* point = target->GetMeleeAttackPoint(_owner);
|
||||||
|
if (point == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_owner->IsFlying()) {
|
||||||
|
// Dont do anything yet might add later
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
point->m_positionZ = _owner->GetMapHeight(point->m_positionX, point->m_positionY, point->m_positionZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Map* _map = _owner->GetBaseMap();
|
||||||
|
|
||||||
|
float x = point->m_positionX;
|
||||||
|
float y = point->m_positionY;
|
||||||
|
float z = point->m_positionZ;
|
||||||
|
|
||||||
|
if (_map->CanReachPositionAndGetCoords(_owner, x, y, z, true, 6.0f, M_PI/3))
|
||||||
|
{
|
||||||
|
Movement::MoveSplineInit init(_owner);
|
||||||
|
init.SetSmooth();
|
||||||
|
init.MoveTo(x, y, z, true);
|
||||||
|
init.SetWalk(true);
|
||||||
|
init.SetFacing(target);
|
||||||
|
init.Launch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot)
|
void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot)
|
||||||
{
|
{
|
||||||
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
|
// Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE
|
||||||
// ignore movement request if target not exist
|
// ignore movement request if target not exist
|
||||||
if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
|
if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//_owner->AddUnitState(UNIT_STATE_FOLLOW);
|
//_owner->AddUnitState(UNIT_STATE_FOLLOW);
|
||||||
if (_owner->GetTypeId() == TYPEID_PLAYER)
|
if (_owner->GetTypeId() == TYPEID_PLAYER)
|
||||||
|
|||||||
@@ -165,6 +165,8 @@ public:
|
|||||||
void MoveRandom(float wanderDistance = 0.0f);
|
void MoveRandom(float wanderDistance = 0.0f);
|
||||||
void MoveFollow(Unit* target, float dist, float angle, MovementSlot slot = MOTION_SLOT_ACTIVE);
|
void MoveFollow(Unit* target, float dist, float angle, MovementSlot slot = MOTION_SLOT_ACTIVE);
|
||||||
void MoveChase(Unit* target, float dist = 0.0f, float angle = 0.0f);
|
void MoveChase(Unit* target, float dist = 0.0f, float angle = 0.0f);
|
||||||
|
void MoveCircleTarget(Unit* target);
|
||||||
|
void MoveBackwards(Unit* target, float dist);
|
||||||
void MoveConfused();
|
void MoveConfused();
|
||||||
void MoveFleeing(Unit* enemy, uint32 time = 0);
|
void MoveFleeing(Unit* enemy, uint32 time = 0);
|
||||||
void MovePoint(uint32 id, const Position& pos, bool generatePath = true, bool forceDestination = true)
|
void MovePoint(uint32 id, const Position& pos, bool generatePath = true, bool forceDestination = true)
|
||||||
|
|||||||
@@ -60,12 +60,13 @@ bool FleeingMovementGenerator<T>::_getPoint(T* owner, float& x, float& y, float&
|
|||||||
if (!owner)
|
if (!owner)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const Map* _map = owner->GetBaseMap();
|
||||||
|
|
||||||
x = owner->GetPositionX();
|
x = owner->GetPositionX();
|
||||||
y = owner->GetPositionY();
|
y = owner->GetPositionY();
|
||||||
z = owner->GetPositionZ();
|
z = owner->GetPositionZ();
|
||||||
|
|
||||||
float temp_x, temp_y, angle;
|
float temp_x, temp_y, angle;
|
||||||
const Map* _map = owner->GetBaseMap();
|
|
||||||
// primitive path-finding
|
// primitive path-finding
|
||||||
for (uint8 i = 0; i < 18; ++i)
|
for (uint8 i = 0; i < 18; ++i)
|
||||||
{
|
{
|
||||||
@@ -151,37 +152,20 @@ bool FleeingMovementGenerator<T>::_getPoint(T* owner, float& x, float& y, float&
|
|||||||
|
|
||||||
temp_x = x + distance * cos(angle);
|
temp_x = x + distance * cos(angle);
|
||||||
temp_y = y + distance * sin(angle);
|
temp_y = y + distance * sin(angle);
|
||||||
acore::NormalizeMapCoord(temp_x);
|
float temp_z = z;
|
||||||
acore::NormalizeMapCoord(temp_y);
|
|
||||||
if (owner->IsWithinLOS(temp_x, temp_y, z))
|
if (_map->CanReachPositionAndGetCoords(owner, temp_x, temp_y, temp_z, true, 3.0f, M_PI/4))
|
||||||
{
|
{
|
||||||
bool is_water_now = _map->IsInWater(x, y, z);
|
if (!(temp_z - z) || distance / fabs(temp_z - z) > 1.0f)
|
||||||
|
|
||||||
if (is_water_now && _map->IsInWater(temp_x, temp_y, z))
|
|
||||||
{
|
{
|
||||||
x = temp_x;
|
float temp_z_left = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle + static_cast<float>(M_PI / 2)), temp_y + 1.0f * sin(angle + static_cast<float>(M_PI / 2)), z, true);
|
||||||
y = temp_y;
|
float temp_z_right = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle - static_cast<float>(M_PI / 2)), temp_y + 1.0f * sin(angle - static_cast<float>(M_PI / 2)), z, true);
|
||||||
return true;
|
if (fabs(temp_z_left - temp_z) < 1.2f && fabs(temp_z_right - temp_z) < 1.2f)
|
||||||
}
|
|
||||||
float new_z = _map->GetHeight(owner->GetPhaseMask(), temp_x, temp_y, z, true);
|
|
||||||
|
|
||||||
if (new_z <= INVALID_HEIGHT || fabs(z - new_z) > 3.0f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool is_water_next = _map->IsInWater(temp_x, temp_y, new_z);
|
|
||||||
|
|
||||||
if ((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!(new_z - z) || distance / fabs(new_z - z) > 1.0f)
|
|
||||||
{
|
|
||||||
float new_z_left = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle + static_cast<float>(M_PI / 2)), temp_y + 1.0f * sin(angle + static_cast<float>(M_PI / 2)), z, true);
|
|
||||||
float new_z_right = _map->GetHeight(owner->GetPhaseMask(), temp_x + 1.0f * cos(angle - static_cast<float>(M_PI / 2)), temp_y + 1.0f * sin(angle - static_cast<float>(M_PI / 2)), z, true);
|
|
||||||
if (fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
|
|
||||||
{
|
{
|
||||||
|
// use new values
|
||||||
x = temp_x;
|
x = temp_x;
|
||||||
y = temp_y;
|
y = temp_y;
|
||||||
z = new_z;
|
z = temp_z;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||||
* it under the terms of the GNU General Public License as published by
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _PATH_GENERATOR_H
|
#ifndef _PATH_GENERATOR_H
|
||||||
@@ -22,139 +11,153 @@
|
|||||||
#include "DetourNavMesh.h"
|
#include "DetourNavMesh.h"
|
||||||
#include "DetourNavMeshQuery.h"
|
#include "DetourNavMeshQuery.h"
|
||||||
#include "MoveSplineInitArgs.h"
|
#include "MoveSplineInitArgs.h"
|
||||||
|
#include <G3D/Vector3.h>
|
||||||
#include "MMapFactory.h"
|
#include "MMapFactory.h"
|
||||||
#include "MMapManager.h"
|
#include "MMapManager.h"
|
||||||
|
|
||||||
class Unit;
|
class Unit;
|
||||||
|
class WorldObject;
|
||||||
|
|
||||||
// 74*4.0f=296y number_of_points*interval = max_path_len
|
// 74*4.0f=296y number_of_points*interval = max_path_len
|
||||||
// this is way more than actual evade range
|
// this is way more than actual evade range
|
||||||
// I think we can safely cut those down even more
|
// I think we can safely cut those down even more
|
||||||
#define MAX_PATH_LENGTH 74
|
#define MAX_PATH_LENGTH 74
|
||||||
#define MAX_POINT_PATH_LENGTH 74
|
#define MAX_POINT_PATH_LENGTH 74
|
||||||
|
|
||||||
#define SMOOTH_PATH_STEP_SIZE 4.0f
|
#define SMOOTH_PATH_STEP_SIZE 4.0f
|
||||||
#define SMOOTH_PATH_SLOP 0.3f
|
#define SMOOTH_PATH_SLOP 0.3f
|
||||||
#define ALLOWED_DIST_FROM_POLY 2.5f
|
|
||||||
#define ADDED_Z_FOR_POLY_LOOKUP 0.3f
|
|
||||||
#define DISALLOW_TIME_AFTER_FAIL 3 // secs
|
#define DISALLOW_TIME_AFTER_FAIL 3 // secs
|
||||||
#define MAX_FIXABLE_Z_ERROR 7.0f
|
|
||||||
|
|
||||||
#define VERTEX_SIZE 3
|
#define VERTEX_SIZE 3
|
||||||
#define INVALID_POLYREF 0
|
#define INVALID_POLYREF 0
|
||||||
|
|
||||||
enum PathType
|
enum PathType
|
||||||
{
|
{
|
||||||
PATHFIND_BLANK = 0x00, // path not built yet
|
PATHFIND_BLANK = 0x00, // path not built yet
|
||||||
PATHFIND_NORMAL = 0x01, // normal path
|
PATHFIND_NORMAL = 0x01, // normal path
|
||||||
PATHFIND_SHORTCUT = 0x02, // travel through obstacles, terrain, air, etc (old behavior)
|
PATHFIND_SHORTCUT = 0x02, // travel through obstacles, terrain, air, etc (old behavior)
|
||||||
PATHFIND_INCOMPLETE = 0x04, // we have partial path to follow - getting closer to target
|
PATHFIND_INCOMPLETE = 0x04, // we have partial path to follow - getting closer to target
|
||||||
PATHFIND_NOPATH = 0x08, // no valid path at all or error in generating one
|
PATHFIND_NOPATH = 0x08, // no valid path at all or error in generating one
|
||||||
PATHFIND_NOT_USING_PATH = 0x10, // used when we are either flying/swiming or on map w/o mmaps
|
PATHFIND_NOT_USING_PATH = 0x10, // used when we are either flying/swiming or on map w/o mmaps
|
||||||
PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length
|
PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length
|
||||||
|
PATHFIND_FARFROMPOLY_START = 0x40, // start position is far from the mmap poligon
|
||||||
|
PATHFIND_FARFROMPOLY_END = 0x80, // end positions is far from the mmap poligon
|
||||||
|
PATHFIND_FARFROMPOLY = PATHFIND_FARFROMPOLY_START | PATHFIND_FARFROMPOLY_END, // start or end positions are far from the mmap poligon
|
||||||
};
|
};
|
||||||
|
|
||||||
class PathGenerator
|
class PathGenerator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PathGenerator(Unit const* owner);
|
explicit PathGenerator(WorldObject const* owner);
|
||||||
~PathGenerator();
|
~PathGenerator();
|
||||||
|
|
||||||
// Calculate the path from owner to given destination
|
// Calculate the path from owner to given destination
|
||||||
// return: true if new path was calculated, false otherwise (no change needed)
|
// return: true if new path was calculated, false otherwise (no change needed)
|
||||||
bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
|
bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
|
||||||
|
bool IsInvalidDestinationZ(Unit const* target) const;
|
||||||
|
|
||||||
// option setters - use optional
|
// option setters - use optional
|
||||||
void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; }
|
void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; }
|
||||||
void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance / SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); }
|
void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); }
|
||||||
|
void SetUseRaycast(bool useRaycast) { _useRaycast = useRaycast; }
|
||||||
|
|
||||||
// result getters
|
// result getters
|
||||||
G3D::Vector3 const& GetStartPosition() const { return _startPosition; }
|
G3D::Vector3 const& GetStartPosition() const { return _startPosition; }
|
||||||
G3D::Vector3 const& GetEndPosition() const { return _endPosition; }
|
G3D::Vector3 const& GetEndPosition() const { return _endPosition; }
|
||||||
G3D::Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
|
G3D::Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
|
||||||
|
|
||||||
Movement::PointsArray const& GetPath() const { return _pathPoints; }
|
Movement::PointsArray const& GetPath() const { return _pathPoints; }
|
||||||
|
|
||||||
PathType GetPathType() const { return _type; }
|
PathType GetPathType() const { return _type; }
|
||||||
float getPathLength() const
|
|
||||||
{
|
// shortens the path until the destination is the specified distance from the target point
|
||||||
float len = 0.0f;
|
void ShortenPathUntilDist(G3D::Vector3 const& point, float dist);
|
||||||
float dx, dy, dz;
|
|
||||||
uint32 size = _pathPoints.size();
|
float getPathLength() const
|
||||||
if (size)
|
|
||||||
{
|
{
|
||||||
dx = _pathPoints[0].x - _startPosition.x;
|
float len = 0.0f;
|
||||||
dy = _pathPoints[0].y - _startPosition.y;
|
float dx, dy, dz;
|
||||||
dz = _pathPoints[0].z - _startPosition.z;
|
uint32 size = _pathPoints.size();
|
||||||
len += sqrt( dx * dx + dy * dy + dz * dz );
|
if (size)
|
||||||
}
|
{
|
||||||
else
|
dx = _pathPoints[0].x - _startPosition.x;
|
||||||
|
dy = _pathPoints[0].y - _startPosition.y;
|
||||||
|
dz = _pathPoints[0].z - _startPosition.z;
|
||||||
|
len += sqrt( dx * dx + dy * dy + dz * dz );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32 i = 1; i < size; ++i)
|
||||||
|
{
|
||||||
|
dx = _pathPoints[i].x - _pathPoints[i - 1].x;
|
||||||
|
dy = _pathPoints[i].y - _pathPoints[i - 1].y;
|
||||||
|
dz = _pathPoints[i].z - _pathPoints[i - 1].z;
|
||||||
|
len += sqrt( dx * dx + dy * dy + dz * dz );
|
||||||
|
}
|
||||||
return len;
|
return len;
|
||||||
for (uint32 i = 1; i < size; ++i)
|
|
||||||
{
|
|
||||||
dx = _pathPoints[i].x - _pathPoints[i - 1].x;
|
|
||||||
dy = _pathPoints[i].y - _pathPoints[i - 1].y;
|
|
||||||
dz = _pathPoints[i].z - _pathPoints[i - 1].z;
|
|
||||||
len += sqrt( dx * dx + dy * dy + dz * dz );
|
|
||||||
}
|
}
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
|
dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
|
||||||
uint32 _polyLength; // number of polygons in the path
|
uint32 _polyLength; // number of polygons in the path
|
||||||
|
|
||||||
Movement::PointsArray _pathPoints; // our actual (x,y,z) path to the target
|
Movement::PointsArray _pathPoints; // our actual (x,y,z) path to the target
|
||||||
PathType _type; // tells what kind of path this is
|
PathType _type; // tells what kind of path this is
|
||||||
|
|
||||||
bool _useStraightPath; // type of path will be generated
|
bool _useStraightPath; // type of path will be generated
|
||||||
bool _forceDestination; // when set, we will always arrive at given point
|
bool _forceDestination; // when set, we will always arrive at given point
|
||||||
uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
|
uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
|
||||||
|
bool _useRaycast; // use raycast if true for a straight line path
|
||||||
|
|
||||||
G3D::Vector3 _startPosition; // {x, y, z} of current location
|
G3D::Vector3 _startPosition; // {x, y, z} of current location
|
||||||
G3D::Vector3 _endPosition; // {x, y, z} of the destination
|
G3D::Vector3 _endPosition; // {x, y, z} of the destination
|
||||||
G3D::Vector3 _actualEndPosition; // {x, y, z} of the closest possible point to given destination
|
G3D::Vector3 _actualEndPosition; // {x, y, z} of the closest possible point to given destination
|
||||||
|
|
||||||
Unit const* const _sourceUnit; // the unit that is moving
|
WorldObject const* const _source; // the object that is moving
|
||||||
dtNavMesh const* _navMesh; // the nav mesh
|
dtNavMesh const* _navMesh; // the nav mesh
|
||||||
dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path
|
dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path
|
||||||
|
|
||||||
dtQueryFilter _filter; // use single filter for all movements, update it when needed
|
dtQueryFilter _filter; // use single filter for all movements, update it when needed
|
||||||
|
|
||||||
void SetStartPosition(G3D::Vector3 const& point) { _startPosition = point; }
|
void SetStartPosition(G3D::Vector3 const& point) { _startPosition = point; }
|
||||||
void SetEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; _endPosition = point; }
|
void SetEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; _endPosition = point; }
|
||||||
void SetActualEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; }
|
void SetActualEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; }
|
||||||
|
void NormalizePath();
|
||||||
|
|
||||||
void Clear()
|
void Clear()
|
||||||
{
|
{
|
||||||
_polyLength = 0;
|
_polyLength = 0;
|
||||||
_pathPoints.clear();
|
_pathPoints.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InRange(G3D::Vector3 const& p1, G3D::Vector3 const& p2, float r, float h) const;
|
bool InRange(G3D::Vector3 const& p1, G3D::Vector3 const& p2, float r, float h) const;
|
||||||
float Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) const;
|
float Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) const;
|
||||||
bool InRangeYZX(float const* v1, float const* v2, float r, float h) const;
|
bool InRangeYZX(float const* v1, float const* v2, float r, float h) const;
|
||||||
|
|
||||||
dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = nullptr) const;
|
dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = nullptr) const;
|
||||||
dtPolyRef GetPolyByLocation(float* Point, float* Distance) const;
|
dtPolyRef GetPolyByLocation(float const* Point, float* Distance) const;
|
||||||
bool HaveTile(G3D::Vector3 const& p) const;
|
bool HaveTile(G3D::Vector3 const& p) const;
|
||||||
|
|
||||||
void BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos, ACE_RW_Thread_Mutex& lock);
|
void BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos);
|
||||||
void BuildPointPath(float const* startPoint, float const* endPoint);
|
void BuildPointPath(float const* startPoint, float const* endPoint);
|
||||||
void BuildShortcut();
|
void BuildShortcut();
|
||||||
|
|
||||||
NavTerrain GetNavTerrain(float x, float y, float z);
|
NavTerrain GetNavTerrain(float x, float y, float z);
|
||||||
void CreateFilter();
|
void CreateFilter();
|
||||||
void UpdateFilter();
|
void UpdateFilter();
|
||||||
|
|
||||||
// smooth path aux functions
|
// smooth path aux functions
|
||||||
uint32 FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited);
|
uint32 FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited);
|
||||||
bool GetSteerTarget(float const* startPos, float const* endPos, float minTargetDist, dtPolyRef const* path, uint32 pathSize, float* steerPos,
|
bool GetSteerTarget(float const* startPos, float const* endPos, float minTargetDist, dtPolyRef const* path, uint32 pathSize, float* steerPos,
|
||||||
unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
|
unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
|
||||||
dtStatus FindSmoothPath(float const* startPos, float const* endPos,
|
dtStatus FindSmoothPath(float const* startPos, float const* endPos,
|
||||||
dtPolyRef const* polyPath, uint32 polyPathSize,
|
dtPolyRef const* polyPath, uint32 polyPathSize,
|
||||||
float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
|
float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
|
||||||
|
|
||||||
|
void AddFarFromPolyFlags(bool startFarFromPoly, bool endFarFromPoly);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -79,7 +79,16 @@ namespace Movement
|
|||||||
move_spline.onTransport = transport;
|
move_spline.onTransport = transport;
|
||||||
|
|
||||||
uint32 moveFlags = unit->m_movementInfo.GetMovementFlags();
|
uint32 moveFlags = unit->m_movementInfo.GetMovementFlags();
|
||||||
moveFlags |= (MOVEMENTFLAG_SPLINE_ENABLED | MOVEMENTFLAG_FORWARD);
|
moveFlags |= MOVEMENTFLAG_SPLINE_ENABLED;
|
||||||
|
|
||||||
|
if (!args.flags.orientationInversed)
|
||||||
|
{
|
||||||
|
moveFlags = (moveFlags & ~(MOVEMENTFLAG_BACKWARD)) | MOVEMENTFLAG_FORWARD;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
moveFlags = (moveFlags & ~(MOVEMENTFLAG_FORWARD)) | MOVEMENTFLAG_BACKWARD;
|
||||||
|
}
|
||||||
|
|
||||||
if (moveFlags & MOVEMENTFLAG_ROOT)
|
if (moveFlags & MOVEMENTFLAG_ROOT)
|
||||||
moveFlags &= ~MOVEMENTFLAG_MASK_MOVING;
|
moveFlags &= ~MOVEMENTFLAG_MASK_MOVING;
|
||||||
|
|||||||
@@ -1235,6 +1235,11 @@ bool SpellInfo::IsChanneled() const
|
|||||||
return (AttributesEx & (SPELL_ATTR1_CHANNELED_1 | SPELL_ATTR1_CHANNELED_2));
|
return (AttributesEx & (SPELL_ATTR1_CHANNELED_1 | SPELL_ATTR1_CHANNELED_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SpellInfo::IsMoveAllowedChannel() const
|
||||||
|
{
|
||||||
|
return IsChanneled() && (HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING) || (!(ChannelInterruptFlags & (AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING))));
|
||||||
|
}
|
||||||
|
|
||||||
bool SpellInfo::NeedsComboPoints() const
|
bool SpellInfo::NeedsComboPoints() const
|
||||||
{
|
{
|
||||||
return (AttributesEx & (SPELL_ATTR1_REQ_COMBO_POINTS1 | SPELL_ATTR1_REQ_COMBO_POINTS2));
|
return (AttributesEx & (SPELL_ATTR1_REQ_COMBO_POINTS1 | SPELL_ATTR1_REQ_COMBO_POINTS2));
|
||||||
|
|||||||
@@ -437,6 +437,7 @@ public:
|
|||||||
bool IsPositive() const;
|
bool IsPositive() const;
|
||||||
bool IsPositiveEffect(uint8 effIndex) const;
|
bool IsPositiveEffect(uint8 effIndex) const;
|
||||||
bool IsChanneled() const;
|
bool IsChanneled() const;
|
||||||
|
bool IsMoveAllowedChannel() const;
|
||||||
bool NeedsComboPoints() const;
|
bool NeedsComboPoints() const;
|
||||||
bool IsBreakingStealth() const;
|
bool IsBreakingStealth() const;
|
||||||
bool IsRangedWeaponSpell() const;
|
bool IsRangedWeaponSpell() const;
|
||||||
|
|||||||
@@ -85,9 +85,11 @@ public:
|
|||||||
if (para && strcmp(para, "true") == 0)
|
if (para && strcmp(para, "true") == 0)
|
||||||
useStraightPath = true;
|
useStraightPath = true;
|
||||||
|
|
||||||
bool useStraightLine = false;
|
bool useRaycast = false;
|
||||||
if (para && strcmp(para, "line") == 0)
|
if (para && (strcmp(para, "line") == 0 || strcmp(para, "ray") == 0 || strcmp(para, "raycast") == 0))
|
||||||
useStraightLine = true;
|
{
|
||||||
|
useRaycast = true;
|
||||||
|
}
|
||||||
|
|
||||||
// unit locations
|
// unit locations
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
@@ -96,11 +98,12 @@ public:
|
|||||||
// path
|
// path
|
||||||
PathGenerator path(target);
|
PathGenerator path(target);
|
||||||
path.SetUseStraightPath(useStraightPath);
|
path.SetUseStraightPath(useStraightPath);
|
||||||
|
path.SetUseRaycast(useRaycast);
|
||||||
bool result = path.CalculatePath(x, y, z, false);
|
bool result = path.CalculatePath(x, y, z, false);
|
||||||
|
|
||||||
Movement::PointsArray const& pointPath = path.GetPath();
|
Movement::PointsArray const& pointPath = path.GetPath();
|
||||||
handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str());
|
handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str());
|
||||||
handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : useStraightLine ? "Raycast" : "SmoothPath");
|
handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : useRaycast ? "Raycast" : "SmoothPath");
|
||||||
handler->PSendSysMessage("Result: %s - Length: " SZFMTD " - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType());
|
handler->PSendSysMessage("Result: %s - Length: " SZFMTD " - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType());
|
||||||
|
|
||||||
G3D::Vector3 const& start = path.GetStartPosition();
|
G3D::Vector3 const& start = path.GetStartPosition();
|
||||||
|
|||||||
Reference in New Issue
Block a user