mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-23 05:36:23 +00:00
feat(Core/Movement): Improved pathfinding, collisions and movements (#4220)
Npc positioning Implemented slope check to avoid unwanted climbing for some kind of movements (backwards, repositioning etc.) Implemented backwards movement Re-implemented circle repositioning algorithm (smartest than retail, but with the same feeling) Fixed random position of summoned minions Improved pet following movement. Also, they attack NPC from behind now. Thanks to @Footman Swimming creatures Fixed max_z coordinate for swimming creatures. Now only part of their body is allowed to be out of the water level Fixed pathfinder for swimming creatures creating shortcuts for specific segments, now they swim underwater to reach the seashore instead of flying above the water level. Creatures with water InhabitType but no swimming flag now, when not in combat, will walk on water depth instead of swimming. Thanks @jackpoz for the original code UNIT_FLAG_SWIMMING in UpdateEnvironmentIfNeeded to show the swimming animation correctly when underwater Implemented HasEnoughWater check to avoid swimming creatures to go where the water level is too low but also to properly enable swimming animation only when a creature has enough water to swim. Walking creatures Extended the DetourNavMeshQuery adding area cost based on walkability (slope angle + source height) to find better paths at runtime instead of completely remove them from mmaps improve Z height in certain conditions (see #4205, #4203, #4247 ) Flying creatures Rewriting of the hover system Removed hacks and improved the UpdateEnvironmentIfNeeded. Now creatures can properly switch from flying to walk etc. Spells LOS on spell effect must be calculated on CollisionHeight and HitSpherePoint instead of position coords. Improved position for object/creature spawned via spells Improved checks for Fleeing movements (fear spells) Other improvements Implemented method to calculate the CollisionWidth from dbc (used by repositioning algorithm etc.) Improved raycast and collision checks Co-authored-by: Footman <p.alexej@freenet.de> Co-authored-by: Helias <stefanoborzi32@gmail.com> Co-authored-by: Francesco Borzì <borzifrancesco@gmail.com> Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com>
This commit is contained in:
@@ -90,7 +90,7 @@ void ConfusedMovementGenerator<T>::DoInitialize(T* unit)
|
||||
template<>
|
||||
void ConfusedMovementGenerator<Creature>::_InitSpecific(Creature* creature, bool& is_water_ok, bool& is_land_ok)
|
||||
{
|
||||
is_water_ok = creature->CanSwim();
|
||||
is_water_ok = creature->CanEnterWater();
|
||||
is_land_ok = creature->CanWalk();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ void EscortMovementGenerator<T>::DoInitialize(T* unit)
|
||||
Movement::MoveSplineInit init(unit);
|
||||
|
||||
if (m_precomputedPath.size() == 2) // xinef: simple case, just call move to
|
||||
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z);
|
||||
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z, true);
|
||||
else if (m_precomputedPath.size())
|
||||
init.MovebyPath(m_precomputedPath);
|
||||
|
||||
@@ -66,7 +66,7 @@ bool EscortMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
|
||||
if (m_precomputedPath.size() > 2)
|
||||
init.MovebyPath(m_precomputedPath);
|
||||
else if (m_precomputedPath.size() == 2)
|
||||
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z);
|
||||
init.MoveTo(m_precomputedPath[1].x, m_precomputedPath[1].y, m_precomputedPath[1].z, true);
|
||||
}
|
||||
|
||||
init.Launch();
|
||||
|
||||
@@ -30,18 +30,7 @@ void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
|
||||
return;
|
||||
|
||||
float x, y, z;
|
||||
if (!_getPoint(owner, x, y, z))
|
||||
return;
|
||||
|
||||
// Add LOS check for target point
|
||||
bool isInLOS = VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(owner->GetMapId(),
|
||||
owner->GetPositionX(),
|
||||
owner->GetPositionY(),
|
||||
owner->GetPositionZ() + 2.0f,
|
||||
x, y, z + 2.0f);
|
||||
|
||||
if (!isInLOS)
|
||||
{
|
||||
if (!_getPoint(owner, x, y, z)) {
|
||||
i_nextCheckTime.Reset(100);
|
||||
return;
|
||||
}
|
||||
@@ -60,12 +49,13 @@ bool FleeingMovementGenerator<T>::_getPoint(T* owner, float& x, float& y, float&
|
||||
if (!owner)
|
||||
return false;
|
||||
|
||||
const Map* _map = owner->GetBaseMap();
|
||||
|
||||
x = owner->GetPositionX();
|
||||
y = owner->GetPositionY();
|
||||
z = owner->GetPositionZ();
|
||||
|
||||
float temp_x, temp_y, angle;
|
||||
const Map* _map = owner->GetBaseMap();
|
||||
// primitive path-finding
|
||||
for (uint8 i = 0; i < 18; ++i)
|
||||
{
|
||||
@@ -151,40 +141,25 @@ bool FleeingMovementGenerator<T>::_getPoint(T* owner, float& x, float& y, float&
|
||||
|
||||
temp_x = x + distance * cos(angle);
|
||||
temp_y = y + distance * sin(angle);
|
||||
acore::NormalizeMapCoord(temp_x);
|
||||
acore::NormalizeMapCoord(temp_y);
|
||||
if (owner->IsWithinLOS(temp_x, temp_y, z))
|
||||
{
|
||||
bool is_water_now = _map->IsInWater(x, y, z);
|
||||
float temp_z = z;
|
||||
|
||||
if (is_water_now && _map->IsInWater(temp_x, temp_y, z))
|
||||
if (!_map->CanReachPositionAndGetValidCoords(owner, temp_x, temp_y, temp_z, true, true))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(temp_z - z) || distance / fabs(temp_z - z) > 1.0f)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
if (fabs(temp_z_left - temp_z) < 1.2f && fabs(temp_z_right - temp_z) < 1.2f)
|
||||
{
|
||||
// use new values
|
||||
x = temp_x;
|
||||
y = temp_y;
|
||||
z = temp_z;
|
||||
return true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
x = temp_x;
|
||||
y = temp_y;
|
||||
z = new_z;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i_to_distance_from_caster = 0.0f;
|
||||
@@ -322,7 +297,7 @@ void FleeingMovementGenerator<Creature>::_Init(Creature* owner)
|
||||
return;
|
||||
|
||||
//owner->SetTargetGuid(ObjectGuid());
|
||||
is_water_ok = owner->CanSwim();
|
||||
is_water_ok = owner->CanEnterWater();
|
||||
is_land_ok = owner->CanWalk();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@ void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner)
|
||||
owner->LoadCreaturesAddon(true);
|
||||
owner->AI()->JustReachedHome();
|
||||
}
|
||||
owner->m_targetsNotAcceptable.clear();
|
||||
|
||||
if (!owner->HasSwimmingFlagOutOfCombat())
|
||||
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SWIMMING);
|
||||
|
||||
owner->UpdateEnvironmentIfNeeded(2);
|
||||
}
|
||||
|
||||
@@ -51,6 +54,7 @@ void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner)
|
||||
init.SetFacing(o);
|
||||
}
|
||||
|
||||
owner->UpdateAllowedPositionZ(x, y, z);
|
||||
init.MoveTo(x, y, z, MMAP::MMapFactory::IsPathfindingEnabled(owner->FindMap()), true);
|
||||
init.SetWalk(false);
|
||||
init.Launch();
|
||||
|
||||
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
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef _PATH_GENERATOR_H
|
||||
@@ -22,138 +11,164 @@
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "MoveSplineInitArgs.h"
|
||||
#include <G3D/Vector3.h>
|
||||
#include "MMapFactory.h"
|
||||
#include "MMapManager.h"
|
||||
|
||||
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
|
||||
// I think we can safely cut those down even more
|
||||
#define MAX_PATH_LENGTH 74
|
||||
#define MAX_POINT_PATH_LENGTH 74
|
||||
#define MAX_PATH_LENGTH 74
|
||||
#define MAX_POINT_PATH_LENGTH 74
|
||||
|
||||
#define SMOOTH_PATH_STEP_SIZE 4.0f
|
||||
#define SMOOTH_PATH_SLOP 0.3f
|
||||
#define ALLOWED_DIST_FROM_POLY 2.5f
|
||||
#define ADDED_Z_FOR_POLY_LOOKUP 0.3f
|
||||
#define SMOOTH_PATH_STEP_SIZE 4.0f
|
||||
#define SMOOTH_PATH_SLOP 0.3f
|
||||
#define DISALLOW_TIME_AFTER_FAIL 3 // secs
|
||||
#define MAX_FIXABLE_Z_ERROR 7.0f
|
||||
|
||||
#define VERTEX_SIZE 3
|
||||
#define INVALID_POLYREF 0
|
||||
|
||||
enum PathType
|
||||
{
|
||||
PATHFIND_BLANK = 0x00, // path not built yet
|
||||
PATHFIND_NORMAL = 0x01, // normal path
|
||||
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_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_SHORT = 0x20, // path is longer or equal to its limited path length
|
||||
PATHFIND_BLANK = 0x00, // path not built yet
|
||||
PATHFIND_NORMAL = 0x01, // normal path
|
||||
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_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_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
|
||||
{
|
||||
public:
|
||||
explicit PathGenerator(Unit const* owner);
|
||||
~PathGenerator();
|
||||
public:
|
||||
explicit PathGenerator(WorldObject const* owner);
|
||||
~PathGenerator();
|
||||
|
||||
// Calculate the path from owner to given destination
|
||||
// return: true if new path was calculated, false otherwise (no change needed)
|
||||
bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
|
||||
// Calculate the path from owner to given destination
|
||||
// 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 x, float y, float z, float destX, float destY, float destZ, bool forceDest);
|
||||
[[nodiscard]] bool IsInvalidDestinationZ(Unit const* target) const;
|
||||
[[nodiscard]] bool IsWalkableClimb(float const* v1, float const* v2) const;
|
||||
[[nodiscard]] bool IsWalkableClimb(float x, float y, float z, float destX, float destY, float destZ) const;
|
||||
[[nodiscard]] static bool IsWalkableClimb(float x, float y, float z, float destX, float destY, float destZ, float sourceHeight);
|
||||
[[nodiscard]] bool IsWaterPath(Movement::PointsArray _pathPoints) const;
|
||||
[[nodiscard]] bool IsSwimmableSegment(float const* v1, float const* v2, bool checkSwim = true) const;
|
||||
[[nodiscard]] bool IsSwimmableSegment(float x, float y, float z, float destX, float destY, float destZ, bool checkSwim = true) const;
|
||||
[[nodiscard]] static float GetRequiredHeightToClimb(float x, float y, float z, float destX, float destY, float destZ, float sourceHeight);
|
||||
|
||||
// option setters - use optional
|
||||
void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; }
|
||||
void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance / SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); }
|
||||
// option setters - use optional
|
||||
|
||||
// result getters
|
||||
G3D::Vector3 const& GetStartPosition() const { return _startPosition; }
|
||||
G3D::Vector3 const& GetEndPosition() const { return _endPosition; }
|
||||
G3D::Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
|
||||
// when set, it skips paths with too high slopes (doesn't work with StraightPath enabled)
|
||||
void SetSlopeCheck(bool checkSlope) { _slopeCheck = checkSlope; }
|
||||
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 SetUseRaycast(bool useRaycast) { _useRaycast = useRaycast; }
|
||||
|
||||
Movement::PointsArray const& GetPath() const { return _pathPoints; }
|
||||
// result getters
|
||||
G3D::Vector3 const& GetStartPosition() const { return _startPosition; }
|
||||
G3D::Vector3 const& GetEndPosition() const { return _endPosition; }
|
||||
G3D::Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
|
||||
|
||||
PathType GetPathType() const { return _type; }
|
||||
float getPathLength() const
|
||||
{
|
||||
float len = 0.0f;
|
||||
float dx, dy, dz;
|
||||
uint32 size = _pathPoints.size();
|
||||
if (size)
|
||||
Movement::PointsArray const& GetPath() const { return _pathPoints; }
|
||||
|
||||
PathType GetPathType() const { return _type; }
|
||||
|
||||
// shortens the path until the destination is the specified distance from the target point
|
||||
void ShortenPathUntilDist(G3D::Vector3 const& point, float dist);
|
||||
|
||||
float getPathLength() const
|
||||
{
|
||||
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
|
||||
float len = 0.0f;
|
||||
float dx, dy, dz;
|
||||
uint32 size = _pathPoints.size();
|
||||
if (size)
|
||||
{
|
||||
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;
|
||||
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:
|
||||
dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
|
||||
uint32 _polyLength; // number of polygons in the path
|
||||
private:
|
||||
dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
|
||||
uint32 _polyLength; // number of polygons in the path
|
||||
|
||||
Movement::PointsArray _pathPoints; // our actual (x,y,z) path to the target
|
||||
PathType _type; // tells what kind of path this is
|
||||
Movement::PointsArray _pathPoints; // our actual (x,y,z) path to the target
|
||||
PathType _type; // tells what kind of path this is
|
||||
|
||||
bool _useStraightPath; // type of path will be generated
|
||||
bool _forceDestination; // when set, we will always arrive at given point
|
||||
uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
|
||||
bool _useStraightPath; // type of path will be generated (do not use it for movement paths)
|
||||
bool _forceDestination; // when set, we will always arrive at given point
|
||||
bool _slopeCheck; // when set, it skips paths with too high slopes (doesn't work with _useStraightPath)
|
||||
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 _endPosition; // {x, y, z} of the destination
|
||||
G3D::Vector3 _actualEndPosition; // {x, y, z} of the closest possible point to given destination
|
||||
G3D::Vector3 _startPosition; // {x, y, z} of current location
|
||||
G3D::Vector3 _endPosition; // {x, y, z} of the destination
|
||||
G3D::Vector3 _actualEndPosition; // {x, y, z} of the closest possible point to given destination
|
||||
|
||||
Unit const* const _sourceUnit; // the unit that is moving
|
||||
dtNavMesh const* _navMesh; // the nav mesh
|
||||
dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path
|
||||
WorldObject const* const _source; // the object that is moving
|
||||
dtNavMesh const* _navMesh; // the nav mesh
|
||||
dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path
|
||||
|
||||
dtQueryFilter _filter; // use single filter for all movements, update it when needed
|
||||
dtQueryFilterExt _filter; // use single filter for all movements, update it when needed
|
||||
|
||||
void SetStartPosition(G3D::Vector3 const& point) { _startPosition = point; }
|
||||
void SetEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; _endPosition = point; }
|
||||
void SetActualEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; }
|
||||
void SetStartPosition(G3D::Vector3 const& point) { _startPosition = point; }
|
||||
void SetEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; _endPosition = point; }
|
||||
void SetActualEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; }
|
||||
void NormalizePath();
|
||||
|
||||
void Clear()
|
||||
{
|
||||
_polyLength = 0;
|
||||
_pathPoints.clear();
|
||||
}
|
||||
void Clear()
|
||||
{
|
||||
_polyLength = 0;
|
||||
_pathPoints.clear();
|
||||
}
|
||||
|
||||
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;
|
||||
bool InRangeYZX(float const* v1, float const* v2, 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;
|
||||
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 GetPolyByLocation(float* Point, float* Distance) const;
|
||||
bool HaveTile(G3D::Vector3 const& p) const;
|
||||
dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = nullptr) const;
|
||||
dtPolyRef GetPolyByLocation(float const* Point, float* Distance) const;
|
||||
bool HaveTile(G3D::Vector3 const& p) const;
|
||||
|
||||
void BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos, ACE_RW_Thread_Mutex& lock);
|
||||
void BuildPointPath(float const* startPoint, float const* endPoint);
|
||||
void BuildShortcut();
|
||||
void BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos);
|
||||
void BuildPointPath(float const* startPoint, float const* endPoint);
|
||||
void BuildShortcut();
|
||||
|
||||
NavTerrain GetNavTerrain(float x, float y, float z);
|
||||
void CreateFilter();
|
||||
void UpdateFilter();
|
||||
NavTerrain GetNavTerrain(float x, float y, float z) const;
|
||||
void CreateFilter();
|
||||
void UpdateFilter();
|
||||
|
||||
// smooth path aux functions
|
||||
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,
|
||||
unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
|
||||
dtStatus FindSmoothPath(float const* startPos, float const* endPos,
|
||||
dtPolyRef const* polyPath, uint32 polyPathSize,
|
||||
float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
|
||||
// smooth path aux functions
|
||||
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,
|
||||
unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
|
||||
dtStatus FindSmoothPath(float const* startPos, float const* endPos,
|
||||
dtPolyRef const* polyPath, uint32 polyPathSize,
|
||||
float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
|
||||
|
||||
void AddFarFromPolyFlags(bool startFarFromPoly, bool endFarFromPoly);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -62,7 +62,7 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
|
||||
}
|
||||
|
||||
float ground = INVALID_HEIGHT;
|
||||
float levelZ = map->GetWaterOrGroundLevel(creature->GetPhaseMask(), x, y, z + 4.0f, &ground);
|
||||
float levelZ = creature->GetMapWaterOrGroundLevel(x, y, z, &ground);
|
||||
float newZ = INVALID_HEIGHT;
|
||||
|
||||
// flying creature
|
||||
@@ -71,15 +71,11 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
|
||||
// point underwater
|
||||
else if (ground < levelZ)
|
||||
{
|
||||
if (!creature->CanSwim())
|
||||
if (!creature->CanEnterWater())
|
||||
{
|
||||
if (ground < levelZ - 1.5f)
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
levelZ = ground;
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -99,6 +95,8 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
|
||||
}
|
||||
}
|
||||
|
||||
creature->UpdateAllowedPositionZ(x, y, newZ);
|
||||
|
||||
if (newZ > INVALID_HEIGHT)
|
||||
{
|
||||
// flying / swiming creature - dest not in los
|
||||
|
||||
@@ -4,373 +4,212 @@
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "ByteBuffer.h"
|
||||
#include "TargetedMovementGenerator.h"
|
||||
#include "Errors.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "World.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "Player.h"
|
||||
#include "Spell.h"
|
||||
#include "BattlegroundRV.h"
|
||||
#include "VehicleDefines.h"
|
||||
#include "Transport.h"
|
||||
#include "MapManager.h"
|
||||
#include "Pet.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
template<class T, typename D>
|
||||
void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool initial)
|
||||
static bool IsMutualChase(Unit* owner, Unit* target)
|
||||
{
|
||||
if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget()))
|
||||
return;
|
||||
if (target->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE)
|
||||
return false;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
|
||||
return;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel())
|
||||
return;
|
||||
|
||||
float x, y, z;
|
||||
bool isPlayerPet = owner->IsPet() && IS_PLAYER_GUID(owner->GetOwnerGUID());
|
||||
bool sameTransport = owner->GetTransport() && owner->GetTransport() == i_target->GetTransport();
|
||||
if (owner->GetMapId() == 631 && owner->GetTransport() && owner->GetTransport()->IsMotionTransport() && i_target->GetTransport() && i_target->GetTransport()->IsMotionTransport()) // for ICC, if both on a motion transport => don't use mmaps
|
||||
sameTransport = owner->GetTypeId() == TYPEID_UNIT && i_target->isInAccessiblePlaceFor(owner->ToCreature());
|
||||
bool useMMaps = MMAP::MMapFactory::IsPathfindingEnabled(owner->FindMap()) && !sameTransport;
|
||||
bool forceDest =
|
||||
// (owner->FindMap() && owner->FindMap()->IsDungeon() && !isPlayerPet) || // force in instances to prevent exploiting
|
||||
(owner->GetTypeId() == TYPEID_UNIT && ((owner->IsPet() && owner->HasUnitState(UNIT_STATE_FOLLOW)))) || // allow pets following their master to cheat while generating paths
|
||||
// ((Creature*)owner)->isWorldBoss() || ((Creature*)owner)->IsDungeonBoss())) || // force for all bosses, even not in instances
|
||||
(owner->GetMapId() == 572 && (owner->GetPositionX() < 1275.0f || i_target->GetPositionX() < 1275.0f)) || // pussywizard: Ruins of Lordaeron - special case (acid)
|
||||
sameTransport || // nothing to comment, can't find path on transports so allow it
|
||||
(i_target->GetTypeId() == TYPEID_PLAYER && i_target->ToPlayer()->IsGameMaster()) // for .npc follow*/
|
||||
; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to
|
||||
bool forcePoint = ((!isPlayerPet || owner->GetMapId() == 618) && (forceDest || !useMMaps)) || sameTransport;
|
||||
|
||||
if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature()) && !sameTransport && !forceDest && !forcePoint)
|
||||
{
|
||||
owner->ToCreature()->SetCannotReachTarget(true);
|
||||
return;
|
||||
}
|
||||
|
||||
lastOwnerXYZ.Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ());
|
||||
lastTargetXYZ.Relocate(i_target->GetPositionX(), i_target->GetPositionY(), i_target->GetPositionZ());
|
||||
|
||||
if (!i_offset)
|
||||
{
|
||||
if (i_target->IsWithinDistInMap(owner, CONTACT_DISTANCE))
|
||||
return;
|
||||
|
||||
float allowedRange = MELEE_RANGE;
|
||||
if ((!initial || (owner->movespline->Finalized() && this->GetMovementGeneratorType() == CHASE_MOTION_TYPE)) && i_target->IsWithinMeleeRange(owner, allowedRange) && i_target->IsWithinLOS(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ()))
|
||||
return;
|
||||
|
||||
bool inRange = i_target->GetRandomContactPoint(owner, x, y, z, forcePoint);
|
||||
if (useMMaps && !inRange && (!isPlayerPet || i_target->GetPositionZ() - z > 50.0f))
|
||||
{
|
||||
//useMMaps = false;
|
||||
owner->m_targetsNotAcceptable[i_target->GetGUID()] = MMapTargetData(sWorld->GetGameTime() + DISALLOW_TIME_AFTER_FAIL, owner, i_target.getTarget());
|
||||
return;
|
||||
}
|
||||
|
||||
// to nearest contact position
|
||||
i_target->GetContactPoint(owner, x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
float dist;
|
||||
float size;
|
||||
|
||||
// Pets need special handling.
|
||||
// We need to subtract GetObjectSize() because it gets added back further down the chain
|
||||
// and that makes pets too far away. Subtracting it allows pets to properly
|
||||
// be (GetCombatReach() + i_offset) away.
|
||||
// Only applies when i_target is pet's owner otherwise pets and mobs end up
|
||||
// doing a "dance" while fighting
|
||||
if (owner->IsPet() && i_target->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
dist = i_target->GetCombatReach();
|
||||
size = i_target->GetCombatReach() - i_target->GetObjectSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
dist = i_offset;
|
||||
size = owner->GetObjectSize();
|
||||
}
|
||||
|
||||
if ((!initial || (owner->movespline->Finalized() && this->GetMovementGeneratorType() == CHASE_MOTION_TYPE)) && i_target->IsWithinDistInMap(owner, dist) && i_target->IsWithinLOS(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ()))
|
||||
return;
|
||||
|
||||
float angle = i_angle;
|
||||
|
||||
if (i_target->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
Creature* creature = owner->ToCreature();
|
||||
|
||||
if (creature && creature->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET)
|
||||
{
|
||||
// fix distance and angle for vanity pets
|
||||
dist = 0.3f;
|
||||
angle = PET_FOLLOW_ANGLE + M_PI * 0.2f;
|
||||
size = i_target->GetCombatReach() - i_target->GetObjectSize();
|
||||
}
|
||||
}
|
||||
|
||||
// Xinef: Fix follow angle for hostile units
|
||||
if (angle == 0.0f && owner->GetVictim() && owner->GetVictim()->GetGUID() == i_target->GetGUID())
|
||||
angle = MapManager::NormalizeOrientation(i_target->GetAngle(owner) - i_target->GetOrientation());
|
||||
// to at i_offset distance from target and i_angle from target facing
|
||||
bool inRange = i_target->GetClosePoint(x, y, z, size, dist, angle, owner, forcePoint);
|
||||
if (!inRange && (forceDest || !useMMaps) && owner->HasUnitState(UNIT_STATE_FOLLOW) && fabs(i_target->GetPositionZ() - z) > 25.0f)
|
||||
{
|
||||
x = i_target->GetPositionX();
|
||||
y = i_target->GetPositionY();
|
||||
z = i_target->GetPositionZ();
|
||||
}
|
||||
}
|
||||
|
||||
D::_addUnitStateMove(owner);
|
||||
i_targetReached = false;
|
||||
i_recalculateTravel = false;
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
|
||||
if (useMMaps) // pussywizard
|
||||
{
|
||||
if (!i_path)
|
||||
i_path = new PathGenerator(owner);
|
||||
|
||||
if (!forceDest)
|
||||
{
|
||||
if (owner->GetMapId() == 618) // pussywizard: 618 Ring of Valor
|
||||
{
|
||||
if (Map* map = owner->FindMap())
|
||||
if (Battleground* bg = ((BattlegroundMap*)map)->GetBG())
|
||||
{
|
||||
Position dest = {x, y, z, 0.0f};
|
||||
if (GameObject* pillar = ((BattlegroundRV*)bg)->GetPillarAtPosition(&dest))
|
||||
{
|
||||
if ((pillar->GetGoState() == GO_STATE_READY && pillar->ToTransport()->GetPathProgress() == 0) || owner->GetPositionZ() > 31.0f || owner->GetTransGUID() == pillar->GetGUID())
|
||||
{
|
||||
if (pillar->GetGoState() == GO_STATE_READY && pillar->ToTransport()->GetPathProgress() == 0)
|
||||
z = std::max(z, 28.28f);
|
||||
else
|
||||
z = i_target->GetPositionZ();
|
||||
|
||||
init.MoveTo(x, y, z);
|
||||
if (i_angle == 0.f)
|
||||
init.SetFacing(i_target.getTarget());
|
||||
init.SetWalk(((D*)this)->EnableWalking());
|
||||
init.Launch();
|
||||
return;
|
||||
}
|
||||
if (pillar->GetGoState() == GO_STATE_ACTIVE || (pillar->GetGoState() == GO_STATE_READY && pillar->ToTransport()->GetPathProgress() > 0))
|
||||
{
|
||||
Position pos;
|
||||
owner->GetFirstCollisionPositionForTotem(pos, owner->GetExactDist2d(i_target.getTarget()), owner->GetAngle(i_target.getTarget()) - owner->GetOrientation(), false);
|
||||
x = pos.GetPositionX();
|
||||
y = pos.GetPositionY();
|
||||
z = 28.28f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!forceDest && getMSTimeDiff(lastPathingFailMSTime, World::GetGameTimeMS()) < 1000)
|
||||
{
|
||||
lastOwnerXYZ.Relocate(-5000.0f, -5000.0f, -5000.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = i_path->CalculatePath(x, y, z, forceDest);
|
||||
if (result)
|
||||
{
|
||||
float maxDist = MELEE_RANGE + owner->GetMeleeReach() + i_target->GetMeleeReach();
|
||||
if (!forceDest && (i_path->GetPathType() & PATHFIND_NOPATH || (!i_offset && !isPlayerPet && i_target->GetExactDistSq(i_path->GetActualEndPosition().x, i_path->GetActualEndPosition().y, i_path->GetActualEndPosition().z) > maxDist * maxDist)))
|
||||
{
|
||||
if (owner->GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
owner->ToCreature()->SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
lastPathingFailMSTime = World::GetGameTimeMS();
|
||||
owner->m_targetsNotAcceptable[i_target->GetGUID()] = MMapTargetData(sWorld->GetGameTime() + DISALLOW_TIME_AFTER_FAIL, owner, i_target.getTarget());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
owner->m_targetsNotAcceptable.erase(i_target->GetGUID());
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
|
||||
init.MovebyPath(i_path->GetPath());
|
||||
if (i_angle == 0.f)
|
||||
init.SetFacing(i_target.getTarget());
|
||||
init.SetWalk(((D*)this)->EnableWalking());
|
||||
init.Launch();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// evade first
|
||||
if (owner->GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
owner->ToCreature()->SetCannotReachTarget(true);
|
||||
}
|
||||
// then use normal MoveTo - if we have to
|
||||
}
|
||||
}
|
||||
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
|
||||
init.MoveTo(x, y, z);
|
||||
// Using the same condition for facing target as the one that is used for SetInFront on movement end
|
||||
// - applies to ChaseMovementGenerator mostly
|
||||
if (i_angle == 0.f)
|
||||
init.SetFacing(i_target.getTarget());
|
||||
|
||||
init.SetWalk(((D*)this)->EnableWalking());
|
||||
init.Launch();
|
||||
return target->GetVictim() == owner->GetVictim();
|
||||
}
|
||||
|
||||
template<class T, typename D>
|
||||
bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff)
|
||||
template<class T>
|
||||
bool ChaseMovementGenerator<T>::PositionOkay(T* owner, Unit* target, std::optional<float> maxDistance, std::optional<ChaseAngle> angle)
|
||||
{
|
||||
if (!i_target.isValid() || !i_target->IsInWorld())
|
||||
float const distSq = owner->GetExactDistSq(target);
|
||||
if (maxDistance && distSq > G3D::square(*maxDistance))
|
||||
return false;
|
||||
if (angle && !angle->IsAngleOkay(target->GetRelativeAngle(owner)))
|
||||
return false;
|
||||
if (!owner->IsWithinLOSInMap(target))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
{
|
||||
if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget()))
|
||||
return false;
|
||||
|
||||
if (!owner || !owner->IsAlive())
|
||||
return false;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
|
||||
// the owner might be unable to move (rooted or casting), or we have lost the target, pause movement
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsMovementPreventedByCasting()))
|
||||
{
|
||||
D::_clearUnitStateMove(owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
// prevent movement while casting spells with cast time or channel time
|
||||
if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel())
|
||||
{
|
||||
bool stop = true;
|
||||
if (Spell* spell = owner->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
|
||||
if (!(spell->GetSpellInfo()->ChannelInterruptFlags & (AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING)) && !(spell->GetSpellInfo()->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
|
||||
stop = false;
|
||||
|
||||
if (stop)
|
||||
i_path = nullptr;
|
||||
owner->StopMoving();
|
||||
_lastTargetPosition.reset();
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
{
|
||||
// Xinef: delay distance recheck in case of succeeding casts
|
||||
i_recheckDistance.Reset(300);
|
||||
if (!owner->IsStopped())
|
||||
owner->StopMoving();
|
||||
|
||||
return true;
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
}
|
||||
}
|
||||
|
||||
// prevent crash after creature killed pet
|
||||
if (static_cast<D*>(this)->_lostTarget(owner))
|
||||
{
|
||||
D::_clearUnitStateMove(owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
i_recheckDistanceForced.Update(time_diff);
|
||||
if (i_recheckDistanceForced.Passed())
|
||||
{
|
||||
i_recheckDistanceForced.Reset(2500);
|
||||
lastOwnerXYZ.Relocate(-5000.0f, -5000.0f, -5000.0f);
|
||||
}
|
||||
Creature* cOwner = owner->ToCreature();
|
||||
|
||||
bool forceDest =
|
||||
//(cOwner && (cOwner->isWorldBoss() || cOwner->IsDungeonBoss())) || // force for all bosses, even not in instances
|
||||
(i_target->GetTypeId() == TYPEID_PLAYER && i_target->ToPlayer()->IsGameMaster()) || // for .npc follow
|
||||
(owner->CanFly())
|
||||
; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to
|
||||
|
||||
|
||||
Unit* target = i_target.getTarget();
|
||||
|
||||
bool const mutualChase = IsMutualChase(owner, target);
|
||||
float const hitboxSum = owner->GetCombatReach() + target->GetCombatReach();
|
||||
float const minTarget = (_range ? _range->MinTolerance : 0.0f) + hitboxSum;
|
||||
float const maxRange = _range ? _range->MaxRange + hitboxSum : owner->GetMeleeRange(target); // melee range already includes hitboxes
|
||||
float const maxTarget = _range ? _range->MaxTolerance + hitboxSum : CONTACT_DISTANCE + hitboxSum;
|
||||
std::optional<ChaseAngle> angle = mutualChase ? std::optional<ChaseAngle>() : _angle;
|
||||
|
||||
i_recheckDistance.Update(time_diff);
|
||||
if (i_recheckDistance.Passed())
|
||||
{
|
||||
i_recheckDistance.Reset(50);
|
||||
//More distance let have better performance, less distance let have more sensitive reaction at target move.
|
||||
float allowed_dist_sq = i_target->GetObjectSize() + owner->GetObjectSize() + MELEE_RANGE - 0.5f;
|
||||
i_recheckDistance.Reset(100);
|
||||
|
||||
// xinef: if offset is negative (follow distance is smaller than just object sizes), reduce minimum allowed distance which is based purely on object sizes
|
||||
if (i_offset < 0.0f)
|
||||
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : std::optional<float>(), angle))
|
||||
{
|
||||
allowed_dist_sq += i_offset;
|
||||
allowed_dist_sq = std::max<float>(0.0f, allowed_dist_sq);
|
||||
i_recalculateTravel = false;
|
||||
i_path = nullptr;
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
{
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
owner->StopMoving();
|
||||
owner->SetInFront(target);
|
||||
MovementInform(owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
allowed_dist_sq *= allowed_dist_sq;
|
||||
|
||||
G3D::Vector3 dest = owner->movespline->FinalDestination();
|
||||
if (owner->movespline->onTransport)
|
||||
if (TransportBase* transport = owner->GetDirectTransport())
|
||||
transport->CalculatePassengerPosition(dest.x, dest.y, dest.z);
|
||||
|
||||
float dist = (dest - G3D::Vector3(i_target->GetPositionX(), i_target->GetPositionY(), i_target->GetPositionZ())).squaredLength();
|
||||
float targetMoveDistSq = i_target->GetExactDistSq(&lastTargetXYZ);
|
||||
if (dist >= allowed_dist_sq || (!i_offset && targetMoveDistSq >= 1.5f * 1.5f))
|
||||
if (targetMoveDistSq >= 0.1f * 0.1f || owner->GetExactDistSq(&lastOwnerXYZ) >= 0.1f * 0.1f)
|
||||
_setTargetLocation(owner, false);
|
||||
}
|
||||
|
||||
if (owner->movespline->Finalized())
|
||||
bool hasMoveState = owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||
if (hasMoveState && owner->movespline->Finalized())
|
||||
{
|
||||
static_cast<D*>(this)->MovementInform(owner);
|
||||
if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget()))
|
||||
owner->SetInFront(i_target.getTarget());
|
||||
i_recalculateTravel = false;
|
||||
i_path = nullptr;
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
owner->SetInFront(target);
|
||||
MovementInform(owner);
|
||||
|
||||
if (!i_targetReached)
|
||||
if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
|
||||
owner->Attack(this->i_target.getTarget(), true);
|
||||
}
|
||||
|
||||
if (_lastTargetPosition && i_target->GetPosition() == _lastTargetPosition.value() && mutualChase == _mutualChase)
|
||||
return true;
|
||||
|
||||
_lastTargetPosition = i_target->GetPosition();
|
||||
|
||||
if (PositionOkay(owner, target, maxRange, angle) && !hasMoveState)
|
||||
return true;
|
||||
|
||||
bool moveToward = !owner->IsInDist(target, maxRange);
|
||||
_mutualChase = mutualChase;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE))
|
||||
{
|
||||
// can we get to the target?
|
||||
if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
|
||||
{
|
||||
i_targetReached = true;
|
||||
static_cast<D*>(this)->_reachTarget(owner);
|
||||
cOwner->SetCannotReachTarget(true);
|
||||
cOwner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!i_path || moveToward != _movingTowards)
|
||||
i_path = new PathGenerator(owner);
|
||||
|
||||
float x, y, z;
|
||||
bool shortenPath;
|
||||
// if we want to move toward the target and there's no fixed angle...
|
||||
if (moveToward && !angle)
|
||||
{
|
||||
// ...we'll pathfind to the center, then shorten the path
|
||||
target->GetPosition(x, y, z);
|
||||
shortenPath = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i_recalculateTravel)
|
||||
_setTargetLocation(owner, false);
|
||||
if (target->GetTypeId() == TYPEID_PLAYER)
|
||||
shortenPath = false;
|
||||
// otherwise, we fall back to nearpoint finding
|
||||
target->GetNearPoint(owner, x, y, z, (moveToward ? maxTarget : minTarget) - hitboxSum, 0, angle ? target->ToAbsoluteAngle(angle->RelativeAngle) : target->GetAngle(owner));
|
||||
shortenPath = false;
|
||||
}
|
||||
|
||||
Unit* pOwner = owner->GetCharmerOrOwner();
|
||||
if (owner->IsHovering())
|
||||
owner->UpdateAllowedPositionZ(x, y, z);
|
||||
|
||||
if (pOwner && pOwner->GetTypeId() == TYPEID_PLAYER)
|
||||
bool success = i_path->CalculatePath(x, y, z, forceDest);
|
||||
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
||||
{
|
||||
// Update pet speed for players in order to avoid stuttering
|
||||
if (pOwner->IsFlying())
|
||||
owner->UpdateSpeed(MOVE_FLIGHT, true);
|
||||
else
|
||||
owner->UpdateSpeed(MOVE_RUN, true);
|
||||
if (cOwner)
|
||||
cOwner->SetCannotReachTarget(true);
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shortenPath)
|
||||
i_path->ShortenPathUntilDist(G3D::Vector3(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), maxTarget);
|
||||
|
||||
if (cOwner)
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
|
||||
bool walk = false;
|
||||
if (cOwner && !cOwner->IsPet())
|
||||
{
|
||||
walk = owner->IsWalking();
|
||||
}
|
||||
|
||||
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
i_recalculateTravel = true;
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(i_path->GetPath());
|
||||
init.SetFacing(target);
|
||||
init.SetWalk(walk);
|
||||
init.Launch();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------//
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::_reachTarget(T* owner)
|
||||
{
|
||||
if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
|
||||
owner->Attack(this->i_target.getTarget(), true);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChaseMovementGenerator<Player>::DoInitialize(Player* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
||||
_setTargetLocation(owner, true);
|
||||
_lastTargetPosition.reset();
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner)
|
||||
{
|
||||
_lastTargetPosition.reset();
|
||||
owner->SetWalk(false);
|
||||
owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
||||
_setTargetLocation(owner, true);
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::DoFinalize(T* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -380,29 +219,143 @@ void ChaseMovementGenerator<T>::DoReset(T* owner)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::MovementInform(T* /*unit*/)
|
||||
void ChaseMovementGenerator<T>::MovementInform(T* owner)
|
||||
{
|
||||
}
|
||||
if (owner->GetTypeId() != TYPEID_UNIT)
|
||||
return;
|
||||
|
||||
template<>
|
||||
void ChaseMovementGenerator<Creature>::MovementInform(Creature* unit)
|
||||
{
|
||||
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
|
||||
if (unit->AI())
|
||||
unit->AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
|
||||
if (CreatureAI* AI = owner->ToCreature()->AI())
|
||||
AI->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
|
||||
}
|
||||
|
||||
//-----------------------------------------------//
|
||||
template<>
|
||||
bool FollowMovementGenerator<Creature>::EnableWalking() const
|
||||
|
||||
template<class T>
|
||||
bool FollowMovementGenerator<T>::PositionOkay(T* owner, Unit* target, float range, std::optional<ChaseAngle> angle)
|
||||
{
|
||||
return i_target.isValid() && (i_target->IsWalking() || i_target->movespline->isWalking());
|
||||
if (owner->GetExactDistSq(target) > G3D::square(owner->GetCombatReach() + target->GetCombatReach() + range))
|
||||
return false;
|
||||
|
||||
return !owner->IsPet() || !angle || angle->IsAngleOkay(target->GetRelativeAngle(owner));
|
||||
}
|
||||
|
||||
template<>
|
||||
bool FollowMovementGenerator<Player>::EnableWalking() const
|
||||
template<class T>
|
||||
bool FollowMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
{
|
||||
return false;
|
||||
if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget()))
|
||||
return false;
|
||||
|
||||
if (!owner || !owner->IsAlive())
|
||||
return false;
|
||||
|
||||
Creature* cOwner = owner->ToCreature();
|
||||
Unit* target = i_target.getTarget();
|
||||
|
||||
// the owner might be unable to move (rooted or casting), or we have lost the target, pause movement
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || (cOwner && owner->ToCreature()->IsMovementPreventedByCasting()))
|
||||
{
|
||||
i_path = nullptr;
|
||||
owner->StopMoving();
|
||||
_lastTargetPosition.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool followingMaster = false;
|
||||
Pet* oPet = owner->ToPet();
|
||||
if (oPet)
|
||||
{
|
||||
if (target->GetGUID() == oPet->GetOwnerGUID())
|
||||
followingMaster = true;
|
||||
}
|
||||
|
||||
bool forceDest =
|
||||
(followingMaster) || // allow pets following their master to cheat while generating paths
|
||||
(i_target->GetTypeId() == TYPEID_PLAYER && i_target->ToPlayer()->IsGameMaster()) // for .npc follow
|
||||
; // closes "bool forceDest", that way it is more appropriate, so we can comment out crap whenever we need to
|
||||
|
||||
|
||||
i_recheckDistance.Update(time_diff);
|
||||
if (i_recheckDistance.Passed())
|
||||
{
|
||||
i_recheckDistance.Reset(100);
|
||||
|
||||
if (i_recalculateTravel && PositionOkay(owner, target, _range, _angle))
|
||||
{
|
||||
i_recalculateTravel = false;
|
||||
i_path = nullptr;
|
||||
owner->StopMoving();
|
||||
_lastTargetPosition.reset();
|
||||
MovementInform(owner);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE) && owner->movespline->Finalized())
|
||||
{
|
||||
i_recalculateTravel = false;
|
||||
i_path = nullptr;
|
||||
owner->ClearUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||
MovementInform(owner);
|
||||
}
|
||||
|
||||
Position targetPosition = i_target->GetPosition();
|
||||
|
||||
if (_lastTargetPosition && _lastTargetPosition->GetExactDistSq(&targetPosition) == 0.0f)
|
||||
return true;
|
||||
|
||||
_lastTargetPosition = targetPosition;
|
||||
|
||||
if (PositionOkay(owner, target, _range + PET_FOLLOW_DIST) && !owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE))
|
||||
return true;
|
||||
|
||||
if (!i_path)
|
||||
i_path = new PathGenerator(owner);
|
||||
|
||||
float x, y, z;
|
||||
// select angle
|
||||
float tAngle;
|
||||
float const curAngle = target->GetRelativeAngle(owner);
|
||||
if (!oPet)
|
||||
{
|
||||
// for non pets, keep the relative angle
|
||||
// decided during the summon
|
||||
tAngle = _angle.RelativeAngle;
|
||||
}
|
||||
else if (_angle.IsAngleOkay(curAngle))
|
||||
{
|
||||
tAngle = curAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
float const diffUpper = Position::NormalizeOrientation(curAngle - _angle.UpperBound());
|
||||
float const diffLower = Position::NormalizeOrientation(_angle.LowerBound() - curAngle);
|
||||
if (diffUpper < diffLower)
|
||||
tAngle = _angle.UpperBound();
|
||||
else
|
||||
tAngle = _angle.LowerBound();
|
||||
}
|
||||
|
||||
target->GetNearPoint(owner, x, y, z, _range, 0.f, target->ToAbsoluteAngle(tAngle));
|
||||
|
||||
bool success = i_path->CalculatePath(x, y, z, forceDest);
|
||||
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
||||
{
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||
|
||||
i_recalculateTravel = true;
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(i_path->GetPath());
|
||||
init.SetFacing(target->GetOrientation());
|
||||
init.SetWalk(target->IsWalking());
|
||||
init.Launch();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -424,20 +377,12 @@ void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner)
|
||||
owner->UpdateSpeed(MOVE_SWIM, true);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FollowMovementGenerator<Player>::DoInitialize(Player* owner)
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::DoInitialize(T* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
|
||||
_lastTargetPosition.reset();
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW);
|
||||
_updateSpeed(owner);
|
||||
_setTargetLocation(owner, true);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FollowMovementGenerator<Creature>::DoInitialize(Creature* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
|
||||
_updateSpeed(owner);
|
||||
_setTargetLocation(owner, true);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -454,38 +399,31 @@ void FollowMovementGenerator<T>::DoReset(T* owner)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::MovementInform(T* /*unit*/)
|
||||
void FollowMovementGenerator<T>::MovementInform(T* owner)
|
||||
{
|
||||
}
|
||||
if (owner->GetTypeId() != TYPEID_UNIT)
|
||||
return;
|
||||
|
||||
template<>
|
||||
void FollowMovementGenerator<Creature>::MovementInform(Creature* unit)
|
||||
{
|
||||
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
|
||||
if (unit->AI())
|
||||
unit->AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
|
||||
if (CreatureAI* AI = owner->ToCreature()->AI())
|
||||
AI->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
|
||||
}
|
||||
|
||||
//-----------------------------------------------//
|
||||
template void TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::_setTargetLocation(Player*, bool initial);
|
||||
template void TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::_setTargetLocation(Player*, bool initial);
|
||||
template void TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool initial);
|
||||
template void TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool initial);
|
||||
template bool TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::DoUpdate(Player*, uint32);
|
||||
template bool TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::DoUpdate(Player*, uint32);
|
||||
template bool TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
|
||||
template bool TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
|
||||
|
||||
template void ChaseMovementGenerator<Player>::_reachTarget(Player*);
|
||||
template void ChaseMovementGenerator<Creature>::_reachTarget(Creature*);
|
||||
template void ChaseMovementGenerator<Player>::DoFinalize(Player*);
|
||||
template void ChaseMovementGenerator<Creature>::DoFinalize(Creature*);
|
||||
template void ChaseMovementGenerator<Player>::DoReset(Player*);
|
||||
template void ChaseMovementGenerator<Creature>::DoReset(Creature*);
|
||||
template void ChaseMovementGenerator<Player>::MovementInform(Player*);
|
||||
template bool ChaseMovementGenerator<Player>::DoUpdate(Player*, uint32);
|
||||
template bool ChaseMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
|
||||
template void ChaseMovementGenerator<Unit>::MovementInform(Unit*);
|
||||
|
||||
template void FollowMovementGenerator<Player>::DoInitialize(Player*);
|
||||
template void FollowMovementGenerator<Creature>::DoInitialize(Creature*);
|
||||
template void FollowMovementGenerator<Player>::DoFinalize(Player*);
|
||||
template void FollowMovementGenerator<Creature>::DoFinalize(Creature*);
|
||||
template void FollowMovementGenerator<Player>::DoReset(Player*);
|
||||
template void FollowMovementGenerator<Creature>::DoReset(Creature*);
|
||||
template void FollowMovementGenerator<Player>::MovementInform(Player*);
|
||||
template bool FollowMovementGenerator<Player>::DoUpdate(Player*, uint32);
|
||||
template bool FollowMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Timer.h"
|
||||
#include "Unit.h"
|
||||
#include "PathGenerator.h"
|
||||
#include <optional>
|
||||
|
||||
class TargetedMovementGeneratorBase
|
||||
{
|
||||
@@ -20,90 +21,80 @@ public:
|
||||
void stopFollowing() { }
|
||||
protected:
|
||||
FollowerReference i_target;
|
||||
Position lastOwnerXYZ;
|
||||
Position lastTargetXYZ;
|
||||
};
|
||||
|
||||
template<class T, typename D>
|
||||
class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase
|
||||
{
|
||||
protected:
|
||||
TargetedMovementGeneratorMedium(Unit* target, float offset, float angle) :
|
||||
TargetedMovementGeneratorBase(target), i_path(nullptr), lastPathingFailMSTime(0),
|
||||
i_recheckDistance(0), i_recheckDistanceForced(2500), i_offset(offset), i_angle(angle),
|
||||
i_recalculateTravel(false), i_targetReached(false)
|
||||
{
|
||||
}
|
||||
~TargetedMovementGeneratorMedium() { delete i_path; }
|
||||
|
||||
public:
|
||||
bool DoUpdate(T*, uint32);
|
||||
Unit* GetTarget() const { return i_target.getTarget(); }
|
||||
|
||||
void unitSpeedChanged() { i_recalculateTravel = true; }
|
||||
bool IsReachable() const { return (i_path) ? (i_path->GetPathType() & PATHFIND_NORMAL) : true; }
|
||||
|
||||
protected:
|
||||
void _setTargetLocation(T* owner, bool initial);
|
||||
|
||||
PathGenerator* i_path;
|
||||
uint32 lastPathingFailMSTime;
|
||||
TimeTrackerSmall i_recheckDistance;
|
||||
TimeTrackerSmall i_recheckDistanceForced;
|
||||
float i_offset;
|
||||
float i_angle;
|
||||
bool i_recalculateTravel : 1;
|
||||
bool i_targetReached : 1;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class ChaseMovementGenerator : public TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >
|
||||
class ChaseMovementGenerator : public MovementGeneratorMedium<T, ChaseMovementGenerator<T>>, public TargetedMovementGeneratorBase
|
||||
{
|
||||
public:
|
||||
ChaseMovementGenerator(Unit* target)
|
||||
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target) {}
|
||||
ChaseMovementGenerator(Unit* target, float offset, float angle)
|
||||
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target, offset, angle) {}
|
||||
ChaseMovementGenerator(Unit* target, std::optional<ChaseRange> range = {}, std::optional<ChaseAngle> angle = {})
|
||||
: TargetedMovementGeneratorBase(target), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
|
||||
~ChaseMovementGenerator() {}
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
|
||||
|
||||
bool DoUpdate(T*, uint32);
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
void MovementInform(T*);
|
||||
|
||||
static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_CHASE_MOVE); }
|
||||
static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_CHASE_MOVE); }
|
||||
bool PositionOkay(T* owner, Unit* target, std::optional<float> maxDistance, std::optional<ChaseAngle> angle);
|
||||
|
||||
void unitSpeedChanged() { _lastTargetPosition.reset(); }
|
||||
Unit* GetTarget() const { return i_target.getTarget(); }
|
||||
|
||||
bool EnableWalking() const { return false;}
|
||||
bool _lostTarget(T* u) const { return u->GetVictim() != this->GetTarget(); }
|
||||
void _reachTarget(T*);
|
||||
bool HasLostTarget(Unit* unit) const { return unit->GetVictim() != this->GetTarget(); }
|
||||
|
||||
private:
|
||||
PathGenerator* i_path;
|
||||
TimeTrackerSmall i_recheckDistance;
|
||||
bool i_recalculateTravel;
|
||||
|
||||
std::optional<Position> _lastTargetPosition;
|
||||
std::optional<ChaseRange> const _range;
|
||||
std::optional<ChaseAngle> const _angle;
|
||||
bool _movingTowards = true;
|
||||
bool _mutualChase = true;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class FollowMovementGenerator : public TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >
|
||||
class FollowMovementGenerator : public MovementGeneratorMedium<T, FollowMovementGenerator<T>>, public TargetedMovementGeneratorBase
|
||||
{
|
||||
public:
|
||||
FollowMovementGenerator(Unit* target)
|
||||
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target) {}
|
||||
FollowMovementGenerator(Unit* target, float offset, float angle)
|
||||
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target, offset, angle) {}
|
||||
FollowMovementGenerator(Unit* target, float range, ChaseAngle angle)
|
||||
: TargetedMovementGeneratorBase(target), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
|
||||
~FollowMovementGenerator() {}
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
|
||||
|
||||
bool DoUpdate(T*, uint32);
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
void MovementInform(T*);
|
||||
|
||||
Unit* GetTarget() const { return i_target.getTarget(); }
|
||||
|
||||
void unitSpeedChanged() { _lastTargetPosition.reset(); }
|
||||
|
||||
bool PositionOkay(T* owner, Unit* target, float range, std::optional<ChaseAngle> angle = {});
|
||||
|
||||
static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
|
||||
static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
|
||||
bool EnableWalking() const;
|
||||
bool _lostTarget(T*) const { return false; }
|
||||
void _reachTarget(T*) {}
|
||||
private:
|
||||
|
||||
void _updateSpeed(T* owner);
|
||||
|
||||
private:
|
||||
PathGenerator* i_path;
|
||||
TimeTrackerSmall i_recheckDistance;
|
||||
bool i_recalculateTravel;
|
||||
|
||||
std::optional<Position> _lastTargetPosition;
|
||||
float _range;
|
||||
ChaseAngle _angle;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -158,9 +158,11 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
|
||||
trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation);
|
||||
}
|
||||
|
||||
float z = node->z;
|
||||
creature->UpdateAllowedPositionZ(node->x, node->y, z);
|
||||
//! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call
|
||||
//! but formationDest contains global coordinates
|
||||
init.MoveTo(node->x, node->y, node->z);
|
||||
init.MoveTo(node->x, node->y, z, true, true);
|
||||
|
||||
//! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table
|
||||
if (node->orientation && node->delay)
|
||||
@@ -236,11 +238,11 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di
|
||||
Stop(sWorld->getIntConfig(CONFIG_WAYPOINT_MOVEMENT_STOP_TIME_FOR_PLAYER) * IN_MILLISECONDS);
|
||||
else
|
||||
{
|
||||
bool finished = creature->movespline->Finalized();
|
||||
// xinef: code to detect pre-empetively if we should start movement to next waypoint
|
||||
// xinef: do not start pre-empetive movement if current node has delay or we are ending waypoint movement
|
||||
bool finished = creature->movespline->Finalized();
|
||||
if (!finished && !i_path->at(i_currentNode)->delay && ((i_currentNode != i_path->size() - 1) || repeating))
|
||||
finished = (creature->movespline->_Spline().length(creature->movespline->_currentSplineIdx() + 1) - creature->movespline->timePassed()) < 200;
|
||||
//if (!finished && !i_path->at(i_currentNode)->delay && ((i_currentNode != i_path->size() - 1) || repeating))
|
||||
// finished = (creature->movespline->_Spline().length(creature->movespline->_currentSplineIdx() + 1) - creature->movespline->timePassed()) < 200;
|
||||
|
||||
if (finished)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user