mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-30 00:53:46 +00:00
Using TC structure allowing easier patches importing
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "Creature.h"
|
||||
#include "MapManager.h"
|
||||
#include "ConfusedMovementGenerator.h"
|
||||
#include "VMapFactory.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "Player.h"
|
||||
|
||||
#ifdef MAP_BASED_RAND_GEN
|
||||
#define rand_norm() unit.rand_norm()
|
||||
#define urand(a, b) unit.urand(a, b)
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
void ConfusedMovementGenerator<T>::DoInitialize(T* unit)
|
||||
{
|
||||
unit->StopMoving();
|
||||
float const wander_distance = 4;
|
||||
float x = unit->GetPositionX();
|
||||
float y = unit->GetPositionY();
|
||||
float z = unit->GetPositionZ();
|
||||
|
||||
Map const* map = unit->GetBaseMap();
|
||||
|
||||
bool is_water_ok, is_land_ok;
|
||||
_InitSpecific(unit, is_water_ok, is_land_ok);
|
||||
|
||||
for (uint8 idx = 0; idx < MAX_CONF_WAYPOINTS + 1; ++idx)
|
||||
{
|
||||
float wanderX = x + (wander_distance * (float)rand_norm() - wander_distance/2);
|
||||
float wanderY = y + (wander_distance * (float)rand_norm() - wander_distance/2);
|
||||
|
||||
// prevent invalid coordinates generation
|
||||
Trinity::NormalizeMapCoord(wanderX);
|
||||
Trinity::NormalizeMapCoord(wanderY);
|
||||
|
||||
float new_z = map->GetHeight(unit->GetPhaseMask(), wanderX, wanderY, z, true);
|
||||
if (new_z <= INVALID_HEIGHT || fabs(z-new_z) > 3.0f) // pussywizard
|
||||
{
|
||||
i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
|
||||
i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
|
||||
i_waypoints[idx][2] = idx > 0 ? i_waypoints[idx-1][2] : z;
|
||||
continue;
|
||||
}
|
||||
else if (unit->IsWithinLOS(wanderX, wanderY, z))
|
||||
{
|
||||
bool is_water = map->IsInWater(wanderX, wanderY, z);
|
||||
|
||||
if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
|
||||
{
|
||||
//! Cannot use coordinates outside our InhabitType. Use the current or previous position.
|
||||
i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
|
||||
i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
|
||||
i_waypoints[idx][2] = idx > 0 ? i_waypoints[idx-1][2] : z;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//! Trying to access path outside line of sight. Skip this by using the current or previous position.
|
||||
i_waypoints[idx][0] = idx > 0 ? i_waypoints[idx-1][0] : x;
|
||||
i_waypoints[idx][1] = idx > 0 ? i_waypoints[idx-1][1] : y;
|
||||
i_waypoints[idx][2] = idx > 0 ? i_waypoints[idx-1][2] : z;
|
||||
continue;
|
||||
}
|
||||
|
||||
//unit->UpdateAllowedPositionZ(wanderX, wanderY, z);
|
||||
|
||||
//! Positions are fine - apply them to this waypoint
|
||||
i_waypoints[idx][0] = wanderX;
|
||||
i_waypoints[idx][1] = wanderY;
|
||||
i_waypoints[idx][2] = new_z;
|
||||
}
|
||||
|
||||
// Xinef: Call movement immediately to broadcast movement packet
|
||||
// Xinef: Initial timer is set to 1 so update with 1
|
||||
i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
|
||||
DoUpdate(unit, 1);
|
||||
|
||||
unit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
|
||||
unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ConfusedMovementGenerator<Creature>::_InitSpecific(Creature* creature, bool &is_water_ok, bool &is_land_ok)
|
||||
{
|
||||
is_water_ok = creature->CanSwim();
|
||||
is_land_ok = creature->CanWalk();
|
||||
}
|
||||
|
||||
template<>
|
||||
void ConfusedMovementGenerator<Player>::_InitSpecific(Player* , bool &is_water_ok, bool &is_land_ok)
|
||||
{
|
||||
is_water_ok = true;
|
||||
is_land_ok = true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ConfusedMovementGenerator<T>::DoReset(T* unit)
|
||||
{
|
||||
DoInitialize(unit);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool ConfusedMovementGenerator<T>::DoUpdate(T* unit, uint32 diff)
|
||||
{
|
||||
if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
|
||||
return true;
|
||||
|
||||
if (i_nextMoveTime.Passed())
|
||||
{
|
||||
// currently moving, update location
|
||||
unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
|
||||
|
||||
if (unit->movespline->Finalized())
|
||||
{
|
||||
i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
|
||||
i_nextMoveTime.Reset(urand(600, 1200)); // Guessed
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// waiting for next move
|
||||
i_nextMoveTime.Update(diff);
|
||||
if (i_nextMoveTime.Passed())
|
||||
{
|
||||
// start moving
|
||||
unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
|
||||
|
||||
ASSERT(i_nextMove <= MAX_CONF_WAYPOINTS);
|
||||
float x = i_waypoints[i_nextMove][0];
|
||||
float y = i_waypoints[i_nextMove][1];
|
||||
float z = i_waypoints[i_nextMove][2];
|
||||
Movement::MoveSplineInit init(unit);
|
||||
init.MoveTo(x, y, z);
|
||||
init.Launch();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void ConfusedMovementGenerator<Player>::DoFinalize(Player* unit)
|
||||
{
|
||||
unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
|
||||
unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
|
||||
unit->StopMoving();
|
||||
}
|
||||
|
||||
template<>
|
||||
void ConfusedMovementGenerator<Creature>::DoFinalize(Creature* unit)
|
||||
{
|
||||
unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
|
||||
unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
|
||||
if (unit->GetVictim())
|
||||
unit->SetTarget(unit->GetVictim()->GetGUID());
|
||||
}
|
||||
|
||||
template void ConfusedMovementGenerator<Player>::DoInitialize(Player*);
|
||||
template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature*);
|
||||
template void ConfusedMovementGenerator<Player>::DoReset(Player*);
|
||||
template void ConfusedMovementGenerator<Creature>::DoReset(Creature*);
|
||||
template bool ConfusedMovementGenerator<Player>::DoUpdate(Player*, uint32 diff);
|
||||
template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature*, uint32 diff);
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_CONFUSEDGENERATOR_H
|
||||
#define TRINITY_CONFUSEDGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#define MAX_CONF_WAYPOINTS 24 //! Allows a twelve second confusion if i_nextMove always is the absolute minimum timer.
|
||||
|
||||
template<class T>
|
||||
class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
explicit ConfusedMovementGenerator() : i_nextMoveTime(1) {}
|
||||
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
bool DoUpdate(T*, uint32);
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
|
||||
private:
|
||||
void _InitSpecific(T*, bool &, bool &);
|
||||
TimeTracker i_nextMoveTime;
|
||||
float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
|
||||
uint32 i_nextMove;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
Written by Xinef
|
||||
*/
|
||||
|
||||
#include "EscortMovementGenerator.h"
|
||||
#include "Errors.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "World.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "Player.h"
|
||||
|
||||
template<class T>
|
||||
void EscortMovementGenerator<T>::DoInitialize(T* unit)
|
||||
{
|
||||
if (!unit->IsStopped())
|
||||
unit->StopMoving();
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
i_recalculateSpeed = false;
|
||||
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);
|
||||
else if (m_precomputedPath.size())
|
||||
init.MovebyPath(m_precomputedPath);
|
||||
|
||||
init.Launch();
|
||||
|
||||
_splineId = unit->movespline->GetId();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool EscortMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
|
||||
{
|
||||
if (!unit)
|
||||
return false;
|
||||
|
||||
if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
|
||||
{
|
||||
unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
|
||||
bool arrived = unit->movespline->Finalized();
|
||||
|
||||
if (i_recalculateSpeed && !arrived)
|
||||
{
|
||||
i_recalculateSpeed = false;
|
||||
Movement::MoveSplineInit init(unit);
|
||||
|
||||
// xinef: speed changed during path execution, calculate remaining path and launch it once more
|
||||
if (m_precomputedPath.size())
|
||||
{
|
||||
uint32 offset = std::min(uint32(unit->movespline->_currentSplineIdx()), uint32(m_precomputedPath.size()));
|
||||
Movement::PointsArray::iterator offsetItr = m_precomputedPath.begin();
|
||||
std::advance(offsetItr, offset);
|
||||
m_precomputedPath.erase(m_precomputedPath.begin(), offsetItr);
|
||||
|
||||
// restore 0 element (current position)
|
||||
m_precomputedPath.insert(m_precomputedPath.begin(), G3D::Vector3(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()));
|
||||
|
||||
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.Launch();
|
||||
// Xinef: Override spline Id on recalculate launch
|
||||
_splineId = unit->movespline->GetId();
|
||||
}
|
||||
|
||||
return !arrived;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void EscortMovementGenerator<T>::DoFinalize(T* unit)
|
||||
{
|
||||
unit->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void EscortMovementGenerator<T>::DoReset(T* unit)
|
||||
{
|
||||
if (!unit->IsStopped())
|
||||
unit->StopMoving();
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
}
|
||||
|
||||
template void EscortMovementGenerator<Player>::DoInitialize(Player*);
|
||||
template void EscortMovementGenerator<Creature>::DoInitialize(Creature*);
|
||||
template void EscortMovementGenerator<Player>::DoFinalize(Player*);
|
||||
template void EscortMovementGenerator<Creature>::DoFinalize(Creature*);
|
||||
template void EscortMovementGenerator<Player>::DoReset(Player*);
|
||||
template void EscortMovementGenerator<Creature>::DoReset(Creature*);
|
||||
template bool EscortMovementGenerator<Player>::DoUpdate(Player* unit, uint32 diff);
|
||||
template bool EscortMovementGenerator<Creature>::DoUpdate(Creature* unit, uint32 diff);
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Written by Xinef
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_ESCORTMOVEMENTGENERATOR_H
|
||||
#define TRINITY_ESCORTMOVEMENTGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
|
||||
template<class T>
|
||||
class EscortMovementGenerator : public MovementGeneratorMedium< T, EscortMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
EscortMovementGenerator(Movement::PointsArray* _path = NULL) : i_recalculateSpeed(false)
|
||||
{
|
||||
if (_path)
|
||||
m_precomputedPath = *_path;
|
||||
}
|
||||
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
bool DoUpdate(T*, uint32);
|
||||
|
||||
void unitSpeedChanged() { i_recalculateSpeed = true; }
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return ESCORT_MOTION_TYPE; }
|
||||
|
||||
uint32 GetSplineId() const { return _splineId; }
|
||||
|
||||
private:
|
||||
bool i_recalculateSpeed;
|
||||
Movement::PointsArray m_precomputedPath;
|
||||
|
||||
uint32 _splineId;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "MapManager.h"
|
||||
#include "FleeingMovementGenerator.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "Player.h"
|
||||
|
||||
#define MIN_QUIET_DISTANCE 28.0f
|
||||
#define MAX_QUIET_DISTANCE 43.0f
|
||||
|
||||
template<class T>
|
||||
void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
|
||||
{
|
||||
if (!owner)
|
||||
return;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
|
||||
return;
|
||||
|
||||
if (!_setMoveData(owner))
|
||||
return;
|
||||
|
||||
float x, y, z;
|
||||
if (!_getPoint(owner, x, y, z))
|
||||
return;
|
||||
|
||||
owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MoveTo(x,y,z);
|
||||
init.SetWalk(false);
|
||||
init.Launch();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool FleeingMovementGenerator<T>::_getPoint(T* owner, float &x, float &y, float &z)
|
||||
{
|
||||
if (!owner)
|
||||
return false;
|
||||
|
||||
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)
|
||||
{
|
||||
if (i_only_forward && i > 2)
|
||||
break;
|
||||
|
||||
float distance = 5.0f;
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
angle = i_cur_angle;
|
||||
break;
|
||||
case 1:
|
||||
angle = i_cur_angle;
|
||||
distance /= 2;
|
||||
break;
|
||||
case 2:
|
||||
angle = i_cur_angle;
|
||||
distance /= 4;
|
||||
break;
|
||||
case 3:
|
||||
angle = i_cur_angle + static_cast<float>(M_PI/4);
|
||||
break;
|
||||
case 4:
|
||||
angle = i_cur_angle - static_cast<float>(M_PI/4);
|
||||
break;
|
||||
case 5:
|
||||
angle = i_cur_angle + static_cast<float>(M_PI/4);
|
||||
distance /= 2;
|
||||
break;
|
||||
case 6:
|
||||
angle = i_cur_angle - static_cast<float>(M_PI/4);
|
||||
distance /= 2;
|
||||
break;
|
||||
case 7:
|
||||
angle = i_cur_angle + static_cast<float>(M_PI/2);
|
||||
break;
|
||||
case 8:
|
||||
angle = i_cur_angle - static_cast<float>(M_PI/2);
|
||||
break;
|
||||
case 9:
|
||||
angle = i_cur_angle + static_cast<float>(M_PI/2);
|
||||
distance /= 2;
|
||||
break;
|
||||
case 10:
|
||||
angle = i_cur_angle - static_cast<float>(M_PI/2);
|
||||
distance /= 2;
|
||||
break;
|
||||
case 11:
|
||||
angle = i_cur_angle + static_cast<float>(M_PI/4);
|
||||
distance /= 4;
|
||||
break;
|
||||
case 12:
|
||||
angle = i_cur_angle - static_cast<float>(M_PI/4);
|
||||
distance /= 4;
|
||||
break;
|
||||
case 13:
|
||||
angle = i_cur_angle + static_cast<float>(M_PI/2);
|
||||
distance /= 4;
|
||||
break;
|
||||
case 14:
|
||||
angle = i_cur_angle - static_cast<float>(M_PI/2);
|
||||
distance /= 4;
|
||||
break;
|
||||
case 15:
|
||||
angle = i_cur_angle + static_cast<float>(3*M_PI/4);
|
||||
distance /= 2;
|
||||
break;
|
||||
case 16:
|
||||
angle = i_cur_angle - static_cast<float>(3*M_PI/4);
|
||||
distance /= 2;
|
||||
break;
|
||||
case 17:
|
||||
angle = i_cur_angle + static_cast<float>(M_PI);
|
||||
distance /= 2;
|
||||
break;
|
||||
default:
|
||||
angle = 0.0f;
|
||||
distance = 0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
temp_x = x + distance * cos(angle);
|
||||
temp_y = y + distance * sin(angle);
|
||||
Trinity::NormalizeMapCoord(temp_x);
|
||||
Trinity::NormalizeMapCoord(temp_y);
|
||||
if (owner->IsWithinLOS(temp_x, temp_y, z))
|
||||
{
|
||||
bool is_water_now = _map->IsInWater(x,y,z);
|
||||
|
||||
if (is_water_now && _map->IsInWater(temp_x,temp_y,z))
|
||||
{
|
||||
x = temp_x;
|
||||
y = temp_y;
|
||||
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;
|
||||
i_nextCheckTime.Reset(urand(500,1000));
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool FleeingMovementGenerator<T>::_setMoveData(T* owner)
|
||||
{
|
||||
float cur_dist_xyz = owner->GetDistance(i_caster_x, i_caster_y, i_caster_z);
|
||||
|
||||
if (i_to_distance_from_caster > 0.0f)
|
||||
{
|
||||
if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
|
||||
// if we reach lower distance
|
||||
(i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
|
||||
// if we can't be close
|
||||
(i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
|
||||
// if we reach bigger distance
|
||||
(cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
|
||||
(i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE))
|
||||
// if we leave 'quiet zone'
|
||||
{
|
||||
// we are very far or too close, stopping
|
||||
i_to_distance_from_caster = 0.0f;
|
||||
i_nextCheckTime.Reset(urand(500,1000));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// now we are running, continue
|
||||
i_last_distance_from_caster = cur_dist_xyz;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
float cur_dist;
|
||||
float angle_to_caster;
|
||||
|
||||
if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
|
||||
{
|
||||
cur_dist = fright->GetDistance(owner);
|
||||
if (cur_dist < cur_dist_xyz)
|
||||
{
|
||||
i_caster_x = fright->GetPositionX();
|
||||
i_caster_y = fright->GetPositionY();
|
||||
i_caster_z = fright->GetPositionZ();
|
||||
angle_to_caster = fright->GetAngle(owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_dist = cur_dist_xyz;
|
||||
angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_dist = cur_dist_xyz;
|
||||
angle_to_caster = owner->GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
|
||||
}
|
||||
|
||||
// if we too close may use 'path-finding' else just stop
|
||||
i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
|
||||
|
||||
//get angle and 'distance from caster' to run
|
||||
float angle;
|
||||
|
||||
if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
|
||||
{
|
||||
angle = (float)rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * static_cast<float>(M_PI/3) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
|
||||
i_to_distance_from_caster = MIN_QUIET_DISTANCE;
|
||||
i_only_forward = true;
|
||||
}
|
||||
else if (cur_dist < MIN_QUIET_DISTANCE)
|
||||
{
|
||||
angle = static_cast<float>(M_PI/6) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
|
||||
i_to_distance_from_caster = cur_dist*2/3 + (float)rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
|
||||
}
|
||||
else if (cur_dist > MAX_QUIET_DISTANCE)
|
||||
{
|
||||
angle = (float)rand_norm()*static_cast<float>(M_PI/3) + static_cast<float>(M_PI*2/3);
|
||||
i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = (float)rand_norm()*static_cast<float>(M_PI);
|
||||
i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
|
||||
}
|
||||
|
||||
int8 sign = (float)rand_norm() > 0.5f ? 1 : -1;
|
||||
i_cur_angle = sign*angle + angle_to_caster;
|
||||
|
||||
// current distance
|
||||
i_last_distance_from_caster = cur_dist;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FleeingMovementGenerator<T>::DoInitialize(T* owner)
|
||||
{
|
||||
if (!owner)
|
||||
return;
|
||||
|
||||
owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
|
||||
owner->AddUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
|
||||
|
||||
_Init(owner);
|
||||
|
||||
if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
|
||||
{
|
||||
i_caster_x = fright->GetPositionX();
|
||||
i_caster_y = fright->GetPositionY();
|
||||
i_caster_z = fright->GetPositionZ();
|
||||
}
|
||||
else
|
||||
{
|
||||
i_caster_x = owner->GetPositionX();
|
||||
i_caster_y = owner->GetPositionY();
|
||||
i_caster_z = owner->GetPositionZ();
|
||||
}
|
||||
|
||||
i_only_forward = true;
|
||||
i_cur_angle = 0.0f;
|
||||
i_last_distance_from_caster = 0.0f;
|
||||
i_to_distance_from_caster = 0.0f;
|
||||
_setTargetLocation(owner);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FleeingMovementGenerator<Creature>::_Init(Creature* owner)
|
||||
{
|
||||
if (!owner)
|
||||
return;
|
||||
|
||||
//owner->SetTargetGuid(ObjectGuid());
|
||||
is_water_ok = owner->CanSwim();
|
||||
is_land_ok = owner->CanWalk();
|
||||
}
|
||||
|
||||
template<>
|
||||
void FleeingMovementGenerator<Player>::_Init(Player* )
|
||||
{
|
||||
is_water_ok = true;
|
||||
is_land_ok = true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void FleeingMovementGenerator<Player>::DoFinalize(Player* owner)
|
||||
{
|
||||
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
|
||||
owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FleeingMovementGenerator<Creature>::DoFinalize(Creature* owner)
|
||||
{
|
||||
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
|
||||
owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
|
||||
if (owner->GetVictim())
|
||||
owner->SetTarget(owner->GetVictim()->GetGUID());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FleeingMovementGenerator<T>::DoReset(T* owner)
|
||||
{
|
||||
DoInitialize(owner);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool FleeingMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
{
|
||||
if (!owner || !owner->IsAlive())
|
||||
return false;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
i_nextCheckTime.Update(time_diff);
|
||||
if (i_nextCheckTime.Passed() && owner->movespline->Finalized())
|
||||
_setTargetLocation(owner);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template void FleeingMovementGenerator<Player>::DoInitialize(Player*);
|
||||
template void FleeingMovementGenerator<Creature>::DoInitialize(Creature*);
|
||||
template bool FleeingMovementGenerator<Player>::_setMoveData(Player*);
|
||||
template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature*);
|
||||
template bool FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&);
|
||||
template bool FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&);
|
||||
template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*);
|
||||
template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*);
|
||||
template void FleeingMovementGenerator<Player>::DoReset(Player*);
|
||||
template void FleeingMovementGenerator<Creature>::DoReset(Creature*);
|
||||
template bool FleeingMovementGenerator<Player>::DoUpdate(Player*, uint32);
|
||||
template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
|
||||
|
||||
void TimedFleeingMovementGenerator::Finalize(Unit* owner)
|
||||
{
|
||||
owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
|
||||
owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
|
||||
if (owner->GetVictim())
|
||||
owner->SetTarget(owner->GetVictim()->GetGUID());
|
||||
}
|
||||
|
||||
bool TimedFleeingMovementGenerator::Update(Unit* owner, uint32 time_diff)
|
||||
{
|
||||
if (!owner->IsAlive())
|
||||
return false;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
i_totalFleeTime.Update(time_diff);
|
||||
if (i_totalFleeTime.Passed())
|
||||
return false;
|
||||
|
||||
// This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, uint32) version
|
||||
// This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
|
||||
return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_FLEEINGMOVEMENTGENERATOR_H
|
||||
#define TRINITY_FLEEINGMOVEMENTGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
|
||||
template<class T>
|
||||
class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
|
||||
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
bool DoUpdate(T*, uint32);
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
|
||||
|
||||
private:
|
||||
void _setTargetLocation(T*);
|
||||
bool _getPoint(T*, float &x, float &y, float &z);
|
||||
bool _setMoveData(T* owner);
|
||||
void _Init(T* );
|
||||
|
||||
bool is_water_ok :1;
|
||||
bool is_land_ok :1;
|
||||
bool i_only_forward:1;
|
||||
|
||||
float i_caster_x;
|
||||
float i_caster_y;
|
||||
float i_caster_z;
|
||||
float i_last_distance_from_caster;
|
||||
float i_to_distance_from_caster;
|
||||
float i_cur_angle;
|
||||
uint64 i_frightGUID;
|
||||
TimeTracker i_nextCheckTime;
|
||||
};
|
||||
|
||||
class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature>
|
||||
{
|
||||
public:
|
||||
TimedFleeingMovementGenerator(uint64 fright, uint32 time) :
|
||||
FleeingMovementGenerator<Creature>(fright),
|
||||
i_totalFleeTime(time) {}
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; }
|
||||
bool Update(Unit*, uint32);
|
||||
void Finalize(Unit*);
|
||||
|
||||
private:
|
||||
TimeTracker i_totalFleeTime;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "HomeMovementGenerator.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MoveSpline.h"
|
||||
|
||||
void HomeMovementGenerator<Creature>::DoInitialize(Creature* owner)
|
||||
{
|
||||
_setTargetLocation(owner);
|
||||
}
|
||||
|
||||
void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_EVADE);
|
||||
if (arrived)
|
||||
{
|
||||
// Xinef: npc run by default
|
||||
//owner->SetWalk(true);
|
||||
owner->LoadCreaturesAddon(true);
|
||||
owner->AI()->JustReachedHome();
|
||||
}
|
||||
owner->m_targetsNotAcceptable.clear();
|
||||
owner->UpdateEnvironmentIfNeeded(2);
|
||||
}
|
||||
|
||||
void HomeMovementGenerator<Creature>::DoReset(Creature*)
|
||||
{
|
||||
}
|
||||
|
||||
void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner)
|
||||
{
|
||||
// Xinef: dont interrupt in any cast!
|
||||
//if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
|
||||
// return;
|
||||
Movement::MoveSplineInit init(owner);
|
||||
float x, y, z, o;
|
||||
|
||||
// Xinef: if there is motion generator on controlled slot, this one is not updated
|
||||
// Xinef: always get reset pos from idle slot
|
||||
MovementGenerator* gen = owner->GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE);
|
||||
if (owner->GetMotionMaster()->empty() || !gen || !gen->GetResetPosition(x, y, z))
|
||||
{
|
||||
owner->GetHomePosition(x, y, z, o);
|
||||
init.SetFacing(o);
|
||||
}
|
||||
|
||||
init.MoveTo(x, y, z, MMAP::MMapFactory::IsPathfindingEnabled(owner->FindMap()), true);
|
||||
init.SetWalk(false);
|
||||
init.Launch();
|
||||
|
||||
arrived = false;
|
||||
|
||||
owner->ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~(UNIT_STATE_POSSESSED | UNIT_STATE_EVADE | UNIT_STATE_IGNORE_PATHFINDING | UNIT_STATE_NO_ENVIRONMENT_UPD)));
|
||||
}
|
||||
|
||||
bool HomeMovementGenerator<Creature>::DoUpdate(Creature* owner, const uint32 /*time_diff*/)
|
||||
{
|
||||
arrived = owner->movespline->Finalized();
|
||||
if (arrived)
|
||||
return false;
|
||||
|
||||
if (i_recalculateTravel)
|
||||
{
|
||||
_setTargetLocation(owner);
|
||||
i_recalculateTravel = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_HOMEMOVEMENTGENERATOR_H
|
||||
#define TRINITY_HOMEMOVEMENTGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
|
||||
class Creature;
|
||||
|
||||
template < class T >
|
||||
class HomeMovementGenerator;
|
||||
|
||||
template <>
|
||||
class HomeMovementGenerator<Creature> : public MovementGeneratorMedium< Creature, HomeMovementGenerator<Creature> >
|
||||
{
|
||||
public:
|
||||
|
||||
HomeMovementGenerator() : arrived(false), i_recalculateTravel(false) {}
|
||||
~HomeMovementGenerator() {}
|
||||
|
||||
void DoInitialize(Creature*);
|
||||
void DoFinalize(Creature*);
|
||||
void DoReset(Creature*);
|
||||
bool DoUpdate(Creature*, const uint32);
|
||||
MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; }
|
||||
void unitSpeedChanged() { i_recalculateTravel = true; }
|
||||
|
||||
private:
|
||||
void _setTargetLocation(Creature*);
|
||||
bool arrived : 1;
|
||||
bool i_recalculateTravel : 1;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "IdleMovementGenerator.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "Creature.h"
|
||||
|
||||
IdleMovementGenerator si_idleMovement;
|
||||
|
||||
// StopMoving is needed to make unit stop if its last movement generator expires
|
||||
// But it should not be sent otherwise there are many redundent packets
|
||||
void IdleMovementGenerator::Initialize(Unit* owner)
|
||||
{
|
||||
Reset(owner);
|
||||
}
|
||||
|
||||
void IdleMovementGenerator::Reset(Unit* owner)
|
||||
{
|
||||
if (!owner->IsStopped())
|
||||
owner->StopMoving();
|
||||
}
|
||||
|
||||
void RotateMovementGenerator::Initialize(Unit* owner)
|
||||
{
|
||||
if (!owner->IsStopped())
|
||||
owner->StopMoving();
|
||||
|
||||
if (owner->GetVictim())
|
||||
owner->SetInFront(owner->GetVictim());
|
||||
|
||||
owner->AddUnitState(UNIT_STATE_ROTATING);
|
||||
|
||||
owner->AttackStop();
|
||||
}
|
||||
|
||||
bool RotateMovementGenerator::Update(Unit* owner, uint32 diff)
|
||||
{
|
||||
float angle = owner->GetOrientation();
|
||||
if (m_direction == ROTATE_DIRECTION_LEFT)
|
||||
{
|
||||
angle += (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
|
||||
while (angle >= static_cast<float>(M_PI * 2)) angle -= static_cast<float>(M_PI * 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
angle -= (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
|
||||
while (angle < 0) angle += static_cast<float>(M_PI * 2);
|
||||
}
|
||||
|
||||
owner->SetFacingTo(angle);
|
||||
|
||||
if (m_duration > diff)
|
||||
m_duration -= diff;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RotateMovementGenerator::Finalize(Unit* unit)
|
||||
{
|
||||
unit->ClearUnitState(UNIT_STATE_ROTATING);
|
||||
if (unit->GetTypeId() == TYPEID_UNIT)
|
||||
unit->ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
|
||||
}
|
||||
|
||||
void DistractMovementGenerator::Initialize(Unit* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_DISTRACTED);
|
||||
}
|
||||
|
||||
void DistractMovementGenerator::Finalize(Unit* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_DISTRACTED);
|
||||
}
|
||||
|
||||
bool DistractMovementGenerator::Update(Unit* owner, uint32 time_diff)
|
||||
{
|
||||
if (owner->IsInCombat() || time_diff > m_timer)
|
||||
return false;
|
||||
|
||||
m_timer -= time_diff;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssistanceDistractMovementGenerator::Finalize(Unit* unit)
|
||||
{
|
||||
unit->ClearUnitState(UNIT_STATE_DISTRACTED);
|
||||
unit->ToCreature()->SetReactState(REACT_AGGRESSIVE);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_IDLEMOVEMENTGENERATOR_H
|
||||
#define TRINITY_IDLEMOVEMENTGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
|
||||
class IdleMovementGenerator : public MovementGenerator
|
||||
{
|
||||
public:
|
||||
|
||||
void Initialize(Unit*);
|
||||
void Finalize(Unit*) { }
|
||||
void Reset(Unit*);
|
||||
bool Update(Unit*, uint32) { return true; }
|
||||
MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; }
|
||||
};
|
||||
|
||||
extern IdleMovementGenerator si_idleMovement;
|
||||
|
||||
class RotateMovementGenerator : public MovementGenerator
|
||||
{
|
||||
public:
|
||||
explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) {}
|
||||
|
||||
void Initialize(Unit*);
|
||||
void Finalize(Unit*);
|
||||
void Reset(Unit* owner) { Initialize(owner); }
|
||||
bool Update(Unit*, uint32);
|
||||
MovementGeneratorType GetMovementGeneratorType() { return ROTATE_MOTION_TYPE; }
|
||||
|
||||
private:
|
||||
uint32 m_duration, m_maxDuration;
|
||||
RotateDirection m_direction;
|
||||
};
|
||||
|
||||
class DistractMovementGenerator : public MovementGenerator
|
||||
{
|
||||
public:
|
||||
explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {}
|
||||
|
||||
void Initialize(Unit*);
|
||||
void Finalize(Unit*);
|
||||
void Reset(Unit* owner) { Initialize(owner); }
|
||||
bool Update(Unit*, uint32);
|
||||
MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; }
|
||||
|
||||
private:
|
||||
uint32 m_timer;
|
||||
};
|
||||
|
||||
class AssistanceDistractMovementGenerator : public DistractMovementGenerator
|
||||
{
|
||||
public:
|
||||
AssistanceDistractMovementGenerator(uint32 timer) :
|
||||
DistractMovementGenerator(timer) {}
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_DISTRACT_MOTION_TYPE; }
|
||||
void Finalize(Unit*);
|
||||
};
|
||||
|
||||
#endif
|
||||
1011
src/server/game/Movement/MovementGenerators/PathGenerator.cpp
Normal file
1011
src/server/game/Movement/MovementGenerators/PathGenerator.cpp
Normal file
File diff suppressed because it is too large
Load Diff
156
src/server/game/Movement/MovementGenerators/PathGenerator.h
Normal file
156
src/server/game/Movement/MovementGenerators/PathGenerator.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/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
|
||||
*/
|
||||
|
||||
#ifndef _PATH_GENERATOR_H
|
||||
#define _PATH_GENERATOR_H
|
||||
|
||||
#include "SharedDefines.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "MoveSplineInitArgs.h"
|
||||
#include "MMapFactory.h"
|
||||
#include "MMapManager.h"
|
||||
|
||||
class Unit;
|
||||
|
||||
// 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 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 DISALLOW_TIME_AFTER_FAIL 3 // secs
|
||||
#define MAX_FIXABLE_Z_ERROR 12.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
|
||||
};
|
||||
|
||||
class PathGenerator
|
||||
{
|
||||
public:
|
||||
explicit PathGenerator(Unit 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);
|
||||
|
||||
// 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); }
|
||||
|
||||
// result getters
|
||||
G3D::Vector3 const& GetStartPosition() const { return _startPosition; }
|
||||
G3D::Vector3 const& GetEndPosition() const { return _endPosition; }
|
||||
G3D::Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
|
||||
|
||||
Movement::PointsArray const& GetPath() const { return _pathPoints; }
|
||||
|
||||
PathType GetPathType() const { return _type; }
|
||||
float getPathLength() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
dtQueryFilter _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 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;
|
||||
|
||||
dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = NULL) const;
|
||||
dtPolyRef GetPolyByLocation(float* 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();
|
||||
|
||||
NavTerrain GetNavTerrain(float x, float y, float z);
|
||||
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);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#include "PointMovementGenerator.h"
|
||||
#include "Errors.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "World.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "Player.h"
|
||||
|
||||
//----- Point Movement Generator
|
||||
template<class T>
|
||||
void PointMovementGenerator<T>::DoInitialize(T* unit)
|
||||
{
|
||||
if (!unit->IsStopped())
|
||||
unit->StopMoving();
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
i_recalculateSpeed = false;
|
||||
Movement::MoveSplineInit init(unit);
|
||||
if (m_precomputedPath.size() > 2) // pussywizard: for charge
|
||||
init.MovebyPath(m_precomputedPath);
|
||||
else if (_generatePath)
|
||||
{
|
||||
PathGenerator path(unit);
|
||||
bool result = path.CalculatePath(i_x, i_y, i_z, _forceDestination);
|
||||
if (result && !(path.GetPathType() & PATHFIND_NOPATH) && path.GetPath().size() > 2)
|
||||
{
|
||||
m_precomputedPath = path.GetPath();
|
||||
init.MovebyPath(m_precomputedPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Xinef: fix strange client visual bug, moving on z coordinate only switches orientation by 180 degrees (visual only)
|
||||
if (G3D::fuzzyEq(unit->GetPositionX(), i_x) && G3D::fuzzyEq(unit->GetPositionY(), i_y))
|
||||
{
|
||||
i_x += 0.2f*cos(unit->GetOrientation());
|
||||
i_y += 0.2f*sin(unit->GetOrientation());
|
||||
}
|
||||
|
||||
init.MoveTo(i_x, i_y, i_z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Xinef: fix strange client visual bug, moving on z coordinate only switches orientation by 180 degrees (visual only)
|
||||
if (G3D::fuzzyEq(unit->GetPositionX(), i_x) && G3D::fuzzyEq(unit->GetPositionY(), i_y))
|
||||
{
|
||||
i_x += 0.2f*cos(unit->GetOrientation());
|
||||
i_y += 0.2f*sin(unit->GetOrientation());
|
||||
}
|
||||
|
||||
init.MoveTo(i_x, i_y, i_z);
|
||||
}
|
||||
if (speed > 0.0f)
|
||||
init.SetVelocity(speed);
|
||||
init.Launch();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
|
||||
{
|
||||
if (!unit)
|
||||
return false;
|
||||
|
||||
if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
|
||||
{
|
||||
unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
|
||||
if (i_recalculateSpeed && !unit->movespline->Finalized())
|
||||
{
|
||||
i_recalculateSpeed = false;
|
||||
Movement::MoveSplineInit init(unit);
|
||||
|
||||
// xinef: speed changed during path execution, calculate remaining path and launch it once more
|
||||
if (m_precomputedPath.size())
|
||||
{
|
||||
uint32 offset = std::min(uint32(unit->movespline->_currentSplineIdx()), uint32(m_precomputedPath.size()));
|
||||
Movement::PointsArray::iterator offsetItr = m_precomputedPath.begin();
|
||||
std::advance(offsetItr, offset);
|
||||
m_precomputedPath.erase(m_precomputedPath.begin(), offsetItr);
|
||||
|
||||
// restore 0 element (current position)
|
||||
m_precomputedPath.insert(m_precomputedPath.begin(), G3D::Vector3(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()));
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
init.MoveTo(i_x, i_y, i_z);
|
||||
if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit
|
||||
init.SetVelocity(speed);
|
||||
init.Launch();
|
||||
}
|
||||
|
||||
return !unit->movespline->Finalized();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void PointMovementGenerator<T>::DoFinalize(T* unit)
|
||||
{
|
||||
unit->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
|
||||
|
||||
if (unit->movespline->Finalized())
|
||||
MovementInform(unit);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void PointMovementGenerator<T>::DoReset(T* unit)
|
||||
{
|
||||
if (!unit->IsStopped())
|
||||
unit->StopMoving();
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void PointMovementGenerator<T>::MovementInform(T* /*unit*/)
|
||||
{
|
||||
}
|
||||
|
||||
template <> void PointMovementGenerator<Creature>::MovementInform(Creature* unit)
|
||||
{
|
||||
if (unit->AI())
|
||||
unit->AI()->MovementInform(POINT_MOTION_TYPE, id);
|
||||
}
|
||||
|
||||
template void PointMovementGenerator<Player>::DoInitialize(Player*);
|
||||
template void PointMovementGenerator<Creature>::DoInitialize(Creature*);
|
||||
template void PointMovementGenerator<Player>::DoFinalize(Player*);
|
||||
template void PointMovementGenerator<Creature>::DoFinalize(Creature*);
|
||||
template void PointMovementGenerator<Player>::DoReset(Player*);
|
||||
template void PointMovementGenerator<Creature>::DoReset(Creature*);
|
||||
template bool PointMovementGenerator<Player>::DoUpdate(Player*, uint32);
|
||||
template bool PointMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
|
||||
|
||||
void AssistanceMovementGenerator::Finalize(Unit* unit)
|
||||
{
|
||||
unit->ToCreature()->SetNoCallAssistance(false);
|
||||
unit->ToCreature()->CallAssistance();
|
||||
if (unit->IsAlive())
|
||||
unit->GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
|
||||
}
|
||||
|
||||
bool EffectMovementGenerator::Update(Unit* unit, uint32)
|
||||
{
|
||||
return !unit->movespline->Finalized();
|
||||
}
|
||||
|
||||
void EffectMovementGenerator::Finalize(Unit* unit)
|
||||
{
|
||||
if (unit->GetTypeId() != TYPEID_UNIT)
|
||||
return;
|
||||
|
||||
if (unit->GetTypeId() == TYPEID_UNIT && unit->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) && unit->movespline->isFalling()) // pussywizard
|
||||
unit->RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
|
||||
|
||||
// Need restore previous movement since we have no proper states system
|
||||
//if (unit->IsAlive() && !unit->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING))
|
||||
//{
|
||||
// if (Unit* victim = unit->GetVictim())
|
||||
// unit->GetMotionMaster()->MoveChase(victim);
|
||||
// else
|
||||
// unit->GetMotionMaster()->Initialize();
|
||||
//}
|
||||
|
||||
if (unit->ToCreature()->AI())
|
||||
unit->ToCreature()->AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_POINTMOVEMENTGENERATOR_H
|
||||
#define TRINITY_POINTMOVEMENTGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
#include "FollowerReference.h"
|
||||
|
||||
template<class T>
|
||||
class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
PointMovementGenerator(uint32 _id, float _x, float _y, float _z, float _speed = 0.0f, const Movement::PointsArray* _path = NULL, bool generatePath = false, bool forceDestination = false) : id(_id),
|
||||
i_x(_x), i_y(_y), i_z(_z), speed(_speed), _generatePath(generatePath), _forceDestination(forceDestination)
|
||||
{
|
||||
if (_path)
|
||||
m_precomputedPath = *_path;
|
||||
}
|
||||
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
bool DoUpdate(T*, uint32);
|
||||
|
||||
void MovementInform(T*);
|
||||
|
||||
void unitSpeedChanged() { i_recalculateSpeed = true; }
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
|
||||
|
||||
bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; }
|
||||
private:
|
||||
uint32 id;
|
||||
float i_x, i_y, i_z;
|
||||
float speed;
|
||||
bool i_recalculateSpeed;
|
||||
Movement::PointsArray m_precomputedPath;
|
||||
bool _generatePath;
|
||||
bool _forceDestination;
|
||||
};
|
||||
|
||||
class AssistanceMovementGenerator : public PointMovementGenerator<Creature>
|
||||
{
|
||||
public:
|
||||
AssistanceMovementGenerator(float _x, float _y, float _z) :
|
||||
PointMovementGenerator<Creature>(0, _x, _y, _z) {}
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
|
||||
void Finalize(Unit*);
|
||||
};
|
||||
|
||||
// Does almost nothing - just doesn't allows previous movegen interrupt current effect.
|
||||
class EffectMovementGenerator : public MovementGenerator
|
||||
{
|
||||
public:
|
||||
explicit EffectMovementGenerator(uint32 Id) : m_Id(Id) {}
|
||||
void Initialize(Unit*) {}
|
||||
void Finalize(Unit*);
|
||||
void Reset(Unit*) {}
|
||||
bool Update(Unit*, uint32);
|
||||
MovementGeneratorType GetMovementGeneratorType() { return EFFECT_MOTION_TYPE; }
|
||||
private:
|
||||
uint32 m_Id;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Originally written by Xinef - Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-AGPL
|
||||
*/
|
||||
|
||||
#include "Creature.h"
|
||||
#include "MapManager.h"
|
||||
#include "RandomMovementGenerator.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Map.h"
|
||||
#include "Util.h"
|
||||
#include "CreatureGroups.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "Spell.h"
|
||||
|
||||
template<>
|
||||
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
|
||||
{
|
||||
if (creature->_moveState != MAP_OBJECT_CELL_MOVE_NONE)
|
||||
return;
|
||||
|
||||
if (_validPointsVector[_currentPoint].empty())
|
||||
{
|
||||
if (_currentPoint == RANDOM_POINTS_NUMBER) // cant go anywhere from initial position, lets stay
|
||||
return;
|
||||
// go back to initial position and will never return to this point
|
||||
_currentPoint = RANDOM_POINTS_NUMBER;
|
||||
_currDestPosition.Relocate(_initialPosition);
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
Movement::MoveSplineInit init(creature);
|
||||
init.MoveTo(_currDestPosition.GetPositionX(), _currDestPosition.GetPositionY(), _currDestPosition.GetPositionZ());
|
||||
init.SetWalk(true);
|
||||
init.Launch();
|
||||
if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
|
||||
creature->GetFormation()->LeaderMoveTo(_currDestPosition.GetPositionX(), _currDestPosition.GetPositionY(), _currDestPosition.GetPositionZ(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 random = urand(0, _validPointsVector[_currentPoint].size()-1);
|
||||
std::vector<uint8>::iterator randomIter = _validPointsVector[_currentPoint].begin() + random;
|
||||
uint8 newPoint = *randomIter;
|
||||
uint16 pathIdx = uint16(_currentPoint*RANDOM_POINTS_NUMBER + newPoint);
|
||||
|
||||
// cant go anywhere from new point, so dont go there to not be stuck
|
||||
if (_validPointsVector[newPoint].empty())
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
return;
|
||||
}
|
||||
|
||||
Movement::PointsArray& finalPath = _preComputedPaths[pathIdx];
|
||||
if (finalPath.empty())
|
||||
{
|
||||
Map* map = creature->GetMap();
|
||||
float x = _destinationPoints[newPoint].x, y = _destinationPoints[newPoint].y, z = _destinationPoints[newPoint].z;
|
||||
// invalid coordinates
|
||||
if (!Trinity::IsValidMapCoord(x, y))
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
float ground = INVALID_HEIGHT;
|
||||
float levelZ = map->GetWaterOrGroundLevel(x, y, z+4.0f, &ground);
|
||||
float newZ = INVALID_HEIGHT;
|
||||
|
||||
// flying creature
|
||||
if (creature->CanFly())
|
||||
newZ = std::max<float>(levelZ, z + rand_norm()*_wanderDistance/2.0f);
|
||||
// point underwater
|
||||
else if (ground < levelZ)
|
||||
{
|
||||
if (!creature->CanSwim())
|
||||
{
|
||||
if (ground < levelZ - 1.5f)
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
levelZ = ground;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (levelZ > INVALID_HEIGHT)
|
||||
newZ = std::min<float>(levelZ-2.0f, z + rand_norm()*_wanderDistance/2.0f);
|
||||
newZ = std::max<float>(ground, newZ);
|
||||
}
|
||||
}
|
||||
// point on ground
|
||||
else
|
||||
{
|
||||
if (levelZ <= INVALID_HEIGHT || !creature->CanWalk())
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (newZ > INVALID_HEIGHT)
|
||||
{
|
||||
// flying / swiming creature - dest not in los
|
||||
if (!creature->IsWithinLOS(x, y, newZ))
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
finalPath.push_back(G3D::Vector3(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ()));
|
||||
finalPath.push_back(G3D::Vector3(x, y, newZ));
|
||||
}
|
||||
else // ground
|
||||
{
|
||||
bool result = _pathGenerator->CalculatePath(x, y, levelZ, false);
|
||||
if (result && !(_pathGenerator->GetPathType() & PATHFIND_NOPATH))
|
||||
{
|
||||
// generated path is too long
|
||||
float pathLen = _pathGenerator->getPathLength();
|
||||
if (pathLen*pathLen > creature->GetExactDistSq(x, y, levelZ) * MAX_PATH_LENGHT_FACTOR*MAX_PATH_LENGHT_FACTOR)
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
finalPath = _pathGenerator->GetPath();
|
||||
Movement::PointsArray::iterator itr = finalPath.begin();
|
||||
Movement::PointsArray::iterator itrNext = finalPath.begin()+1;
|
||||
float zDiff, distDiff;
|
||||
|
||||
for (; itrNext != finalPath.end(); ++itr, ++itrNext)
|
||||
{
|
||||
distDiff = sqrt(((*itr).x-(*itrNext).x)*((*itr).x-(*itrNext).x) + ((*itr).y-(*itrNext).y)*((*itr).y-(*itrNext).y));
|
||||
zDiff = fabs((*itr).z - (*itrNext).z);
|
||||
|
||||
// Xinef: tree climbing, cut as much as we can
|
||||
if (zDiff > 2.0f ||
|
||||
(G3D::fuzzyNe(zDiff, 0.0f) && distDiff / zDiff < 2.15f)) // ~25˚
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!map->isInLineOfSight((*itr).x, (*itr).y, (*itr).z+2.f, (*itrNext).x, (*itrNext).y, (*itrNext).z+2.f, creature->GetPhaseMask()))
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no valid path
|
||||
if (finalPath.size() < 2)
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_validPointsVector[_currentPoint].erase(randomIter);
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_currentPoint = newPoint;
|
||||
G3D::Vector3& finalPoint = finalPath[finalPath.size()-1];
|
||||
_currDestPosition.Relocate(finalPoint.x, finalPoint.y, finalPoint.z);
|
||||
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
++_moveCount;
|
||||
if (roll_chance_i((int32)_moveCount * 25 + 10))
|
||||
{
|
||||
_moveCount = 0;
|
||||
_nextMoveTime.Reset(urand(4000, 8000));
|
||||
}
|
||||
|
||||
Movement::MoveSplineInit init(creature);
|
||||
init.MovebyPath(finalPath);
|
||||
init.SetWalk(true);
|
||||
init.Launch();
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS))
|
||||
_preComputedPaths.erase(pathIdx);
|
||||
|
||||
//Call for creature group update
|
||||
if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
|
||||
creature->GetFormation()->LeaderMoveTo(finalPoint.x, finalPoint.y, finalPoint.z, false);
|
||||
}
|
||||
|
||||
template<>
|
||||
void RandomMovementGenerator<Creature>::DoInitialize(Creature* creature)
|
||||
{
|
||||
if (!creature->IsAlive())
|
||||
return;
|
||||
|
||||
if (!_wanderDistance)
|
||||
_wanderDistance = creature->GetRespawnRadius();
|
||||
|
||||
_nextMoveTime.Reset(creature->GetDBTableGUIDLow() && creature->GetRespawnRadius() == _wanderDistance ? urand(1, 5000) : 0);
|
||||
_wanderDistance = std::max((creature->GetRespawnRadius() == _wanderDistance && creature->GetInstanceId() == 0) ? (creature->CanFly() ? MIN_WANDER_DISTANCE_AIR : MIN_WANDER_DISTANCE_GROUND) : 0.0f, _wanderDistance);
|
||||
|
||||
if (G3D::fuzzyEq(_initialPosition.GetExactDist2d(0.0f, 0.0f), 0.0f))
|
||||
{
|
||||
_initialPosition.Relocate(creature);
|
||||
_destinationPoints.clear();
|
||||
for (uint8 i = 0; i < RANDOM_POINTS_NUMBER; ++i)
|
||||
{
|
||||
float angle = (M_PI*2.0f/(float)RANDOM_POINTS_NUMBER)*i;
|
||||
float factor = 0.5f + rand_norm()*0.5f;
|
||||
_destinationPoints.push_back(G3D::Vector3(_initialPosition.GetPositionX() + _wanderDistance*cos(angle)*factor, _initialPosition.GetPositionY() + _wanderDistance*sin(angle)*factor, _initialPosition.GetPositionZ()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!_pathGenerator)
|
||||
_pathGenerator = new PathGenerator(creature);
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
|
||||
}
|
||||
|
||||
template<>
|
||||
void RandomMovementGenerator<Creature>::DoReset(Creature* creature)
|
||||
{
|
||||
DoInitialize(creature);
|
||||
}
|
||||
|
||||
template<>
|
||||
void RandomMovementGenerator<Creature>::DoFinalize(Creature* creature)
|
||||
{
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
creature->SetWalk(false);
|
||||
}
|
||||
|
||||
template<>
|
||||
bool RandomMovementGenerator<Creature>::DoUpdate(Creature* creature, const uint32 diff)
|
||||
{
|
||||
if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
|
||||
{
|
||||
_nextMoveTime.Reset(0); // Expire the timer
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// xinef: if we got disable move flag, do not remove default generator - just prevent movement
|
||||
if (creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
|
||||
{
|
||||
_nextMoveTime.Reset(0); // Expire the timer
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// prevent movement while casting spells with cast time or channel time
|
||||
if (creature->HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
bool stop = true;
|
||||
if (Spell* spell = creature->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)
|
||||
{
|
||||
if (!creature->IsStopped())
|
||||
creature->StopMoving();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (creature->movespline->Finalized())
|
||||
{
|
||||
_nextMoveTime.Update(diff);
|
||||
if (_nextMoveTime.Passed())
|
||||
_setRandomLocation(creature);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool RandomMovementGenerator<Creature>::GetResetPosition(float& x, float& y, float& z)
|
||||
{
|
||||
if (_currentPoint < RANDOM_POINTS_NUMBER)
|
||||
_currDestPosition.GetPosition(x, y, z);
|
||||
else if (G3D::fuzzyNe(_initialPosition.GetExactDist2d(0.0f, 0.0f), 0.0f)) // if initial position is not 0.0f, 0.0f
|
||||
_initialPosition.GetPosition(x, y, z);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Originally written by Xinef - Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-AGPL
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_RANDOMMOTIONGENERATOR_H
|
||||
#define TRINITY_RANDOMMOTIONGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
#include "PathGenerator.h"
|
||||
|
||||
#define RANDOM_POINTS_NUMBER 12
|
||||
#define RANDOM_LINKS_COUNT 7
|
||||
#define MIN_WANDER_DISTANCE_GROUND 6.0f
|
||||
#define MIN_WANDER_DISTANCE_AIR 10.0f
|
||||
#define MAX_PATH_LENGHT_FACTOR 1.85f
|
||||
|
||||
template<class T>
|
||||
class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
RandomMovementGenerator(float spawnDist = 0.0f) : _nextMoveTime(0), _moveCount(0), _wanderDistance(spawnDist), _pathGenerator(NULL), _currentPoint(RANDOM_POINTS_NUMBER)
|
||||
{
|
||||
_initialPosition.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
_destinationPoints.reserve(RANDOM_POINTS_NUMBER);
|
||||
|
||||
for (uint8 i = 0; i < RANDOM_POINTS_NUMBER; ++i)
|
||||
{
|
||||
_validPointsVector[i].reserve(RANDOM_LINKS_COUNT);
|
||||
for (uint8 j = 0; j < RANDOM_LINKS_COUNT; ++j)
|
||||
_validPointsVector[i].push_back((i + j + RANDOM_POINTS_NUMBER/2 - RANDOM_LINKS_COUNT/2) % RANDOM_POINTS_NUMBER);
|
||||
}
|
||||
|
||||
_validPointsVector[RANDOM_POINTS_NUMBER].reserve(RANDOM_POINTS_NUMBER);
|
||||
for (uint8 i = 0; i < RANDOM_POINTS_NUMBER; ++i)
|
||||
_validPointsVector[RANDOM_POINTS_NUMBER].push_back(i);
|
||||
}
|
||||
|
||||
void _setRandomLocation(T*);
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
bool DoUpdate(T*, const uint32);
|
||||
bool GetResetPosition(float& x, float& y, float& z);
|
||||
MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
|
||||
|
||||
private:
|
||||
TimeTrackerSmall _nextMoveTime;
|
||||
uint8 _moveCount;
|
||||
float _wanderDistance;
|
||||
PathGenerator* _pathGenerator;
|
||||
std::vector<G3D::Vector3> _destinationPoints;
|
||||
std::vector<uint8> _validPointsVector[RANDOM_POINTS_NUMBER+1];
|
||||
uint8 _currentPoint;
|
||||
std::map<uint16, Movement::PointsArray> _preComputedPaths;
|
||||
Position _initialPosition, _currDestPosition;
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* 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 <cmath>
|
||||
|
||||
template<class T, typename D>
|
||||
void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool initial)
|
||||
{
|
||||
if (!i_target.isValid() || !i_target->IsInWorld() || !owner->IsInMap(i_target.getTarget()))
|
||||
return;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
|
||||
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
|
||||
bool forcePoint = ((!isPlayerPet || owner->GetMapId() == 618) && (forceDest || !useMMaps)) || sameTransport;
|
||||
|
||||
lastOwnerXYZ.Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ());
|
||||
lastTargetXYZ.Relocate(i_target->GetPositionX(), i_target->GetPositionY(), i_target->GetPositionZ());
|
||||
|
||||
if (!i_offset)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
// Xinef: Fix follow angle for hostile units
|
||||
float angle = i_angle;
|
||||
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)))
|
||||
{
|
||||
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());
|
||||
|
||||
init.MovebyPath(i_path->GetPath());
|
||||
if (i_angle == 0.f)
|
||||
init.SetFacing(i_target.getTarget());
|
||||
init.SetWalk(((D*)this)->EnableWalking());
|
||||
init.Launch();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if failed to generate, just use normal MoveTo
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
template<class T, typename D>
|
||||
bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T* owner, uint32 time_diff)
|
||||
{
|
||||
if (!i_target.isValid() || !i_target->IsInWorld())
|
||||
return false;
|
||||
|
||||
if (!owner || !owner->IsAlive())
|
||||
return false;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
|
||||
{
|
||||
D::_clearUnitStateMove(owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
// prevent movement while casting spells with cast time or channel time
|
||||
if (owner->HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
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)
|
||||
{
|
||||
// Xinef: delay distance recheck in case of succeeding casts
|
||||
i_recheckDistance.Reset(300);
|
||||
if (!owner->IsStopped())
|
||||
owner->StopMoving();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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)
|
||||
{
|
||||
allowed_dist_sq += i_offset;
|
||||
allowed_dist_sq = std::max<float>(0.0f, allowed_dist_sq);
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
static_cast<D*>(this)->MovementInform(owner);
|
||||
if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget()))
|
||||
owner->SetInFront(i_target.getTarget());
|
||||
|
||||
if (!i_targetReached)
|
||||
{
|
||||
i_targetReached = true;
|
||||
static_cast<D*>(this)->_reachTarget(owner);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i_recalculateTravel)
|
||||
_setTargetLocation(owner, false);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner)
|
||||
{
|
||||
owner->SetWalk(false);
|
||||
owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
||||
_setTargetLocation(owner, true);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::DoFinalize(T* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::DoReset(T* owner)
|
||||
{
|
||||
DoInitialize(owner);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::MovementInform(T* /*unit*/)
|
||||
{
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
//-----------------------------------------------//
|
||||
template<>
|
||||
bool FollowMovementGenerator<Creature>::EnableWalking() const
|
||||
{
|
||||
return i_target.isValid() && (i_target->IsWalking() || i_target->movespline->isWalking());
|
||||
}
|
||||
|
||||
template<>
|
||||
bool FollowMovementGenerator<Player>::EnableWalking() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
void FollowMovementGenerator<Player>::_updateSpeed(Player* /*owner*/)
|
||||
{
|
||||
// nothing to do for Player
|
||||
}
|
||||
|
||||
template<>
|
||||
void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner)
|
||||
{
|
||||
// pet only sync speed with owner
|
||||
/// Make sure we are not in the process of a map change (IsInWorld)
|
||||
if (!IS_PLAYER_GUID(owner->GetOwnerGUID()) || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID())
|
||||
return;
|
||||
|
||||
owner->UpdateSpeed(MOVE_RUN, true);
|
||||
owner->UpdateSpeed(MOVE_WALK, true);
|
||||
owner->UpdateSpeed(MOVE_SWIM, true);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FollowMovementGenerator<Player>::DoInitialize(Player* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
|
||||
_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>
|
||||
void FollowMovementGenerator<T>::DoFinalize(T* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
|
||||
_updateSpeed(owner);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::DoReset(T* owner)
|
||||
{
|
||||
DoInitialize(owner);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::MovementInform(T* /*unit*/)
|
||||
{
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
//-----------------------------------------------//
|
||||
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 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*);
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_TARGETEDMOVEMENTGENERATOR_H
|
||||
#define TRINITY_TARGETEDMOVEMENTGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
#include "FollowerReference.h"
|
||||
#include "Timer.h"
|
||||
#include "Unit.h"
|
||||
#include "PathGenerator.h"
|
||||
|
||||
class TargetedMovementGeneratorBase
|
||||
{
|
||||
public:
|
||||
TargetedMovementGeneratorBase(Unit* target) { i_target.link(target, this); }
|
||||
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(NULL), 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> >
|
||||
{
|
||||
public:
|
||||
ChaseMovementGenerator(Unit* target)
|
||||
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target) {}
|
||||
ChaseMovementGenerator(Unit* target, float offset, float angle)
|
||||
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target, offset, angle) {}
|
||||
~ChaseMovementGenerator() {}
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
|
||||
|
||||
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 EnableWalking() const { return false;}
|
||||
bool _lostTarget(T* u) const { return u->GetVictim() != this->GetTarget(); }
|
||||
void _reachTarget(T*);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class FollowMovementGenerator : public TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
FollowMovementGenerator(Unit* target)
|
||||
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target){}
|
||||
FollowMovementGenerator(Unit* target, float offset, float angle)
|
||||
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target, offset, angle) {}
|
||||
~FollowMovementGenerator() {}
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
|
||||
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
void MovementInform(T*);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
//Basic headers
|
||||
#include "WaypointMovementGenerator.h"
|
||||
//Extended headers
|
||||
#include "ObjectMgr.h"
|
||||
#include "World.h"
|
||||
#include "Transport.h"
|
||||
//Flightmaster grid preloading
|
||||
#include "MapManager.h"
|
||||
//Creature-specific headers
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "CreatureGroups.h"
|
||||
//Player-specific
|
||||
#include "Player.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "Spell.h"
|
||||
|
||||
void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature)
|
||||
{
|
||||
if (!path_id)
|
||||
path_id = creature->GetWaypointPath();
|
||||
|
||||
i_path = sWaypointMgr->GetPath(path_id);
|
||||
|
||||
if (!i_path)
|
||||
{
|
||||
// No movement found for entry
|
||||
sLog->outErrorDb("WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUIDLow(), path_id);
|
||||
return;
|
||||
}
|
||||
|
||||
StartMoveNow(creature);
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature)
|
||||
{
|
||||
LoadPath(creature);
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature)
|
||||
{
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
creature->SetWalk(false);
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::DoReset(Creature* creature)
|
||||
{
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
|
||||
StartMoveNow(creature);
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
|
||||
{
|
||||
if (!i_path || i_path->empty())
|
||||
return;
|
||||
if (m_isArrivalDone)
|
||||
return;
|
||||
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
m_isArrivalDone = true;
|
||||
|
||||
if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance)
|
||||
{
|
||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||
sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for " UI64FMTD ".", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID());
|
||||
#endif
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, NULL);
|
||||
}
|
||||
|
||||
// Inform script
|
||||
MovementInform(creature);
|
||||
creature->UpdateWaypointID(i_currentNode);
|
||||
|
||||
if (i_path->at(i_currentNode)->delay)
|
||||
{
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
Stop(i_path->at(i_currentNode)->delay);
|
||||
}
|
||||
}
|
||||
|
||||
bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
|
||||
{
|
||||
if (!i_path || i_path->empty())
|
||||
return false;
|
||||
|
||||
// Xinef: Dont allow dead creatures to move
|
||||
if (!creature->IsAlive())
|
||||
return false;
|
||||
|
||||
if (Stopped())
|
||||
return true;
|
||||
|
||||
bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID();
|
||||
|
||||
if (m_isArrivalDone)
|
||||
{
|
||||
// Xinef: not true... update this at every waypoint!
|
||||
//if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
|
||||
{
|
||||
float x = i_path->at(i_currentNode)->x;
|
||||
float y = i_path->at(i_currentNode)->y;
|
||||
float z = i_path->at(i_currentNode)->z;
|
||||
float o = creature->GetOrientation();
|
||||
|
||||
if (!transportPath)
|
||||
creature->SetHomePosition(x, y, z, o);
|
||||
else
|
||||
{
|
||||
if (Transport* trans = (creature->GetTransport() ? creature->GetTransport()->ToMotionTransport() : NULL))
|
||||
{
|
||||
o -= trans->GetOrientation();
|
||||
creature->SetTransportHomePosition(x, y, z, o);
|
||||
trans->CalculatePassengerPosition(x, y, z, &o);
|
||||
creature->SetHomePosition(x, y, z, o);
|
||||
}
|
||||
else
|
||||
transportPath = false;
|
||||
// else if (vehicle) - this should never happen, vehicle offsets are const
|
||||
}
|
||||
}
|
||||
|
||||
// Xinef: moved the upper IF here
|
||||
if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
|
||||
{
|
||||
creature->GetMotionMaster()->Initialize();
|
||||
return false;
|
||||
}
|
||||
|
||||
i_currentNode = (i_currentNode+1) % i_path->size();
|
||||
}
|
||||
|
||||
// xinef: do not initialize motion if we got stunned in movementinform
|
||||
if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
|
||||
return true;
|
||||
|
||||
WaypointData const* node = i_path->at(i_currentNode);
|
||||
|
||||
m_isArrivalDone = false;
|
||||
|
||||
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
|
||||
Movement::Location formationDest(node->x, node->y, node->z, 0.0f);
|
||||
Movement::MoveSplineInit init(creature);
|
||||
|
||||
//! If creature is on transport, we assume waypoints set in DB are already transport offsets
|
||||
if (transportPath)
|
||||
{
|
||||
init.DisableTransportPathTransformations();
|
||||
if (TransportBase* trans = creature->GetDirectTransport())
|
||||
trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation);
|
||||
}
|
||||
|
||||
//! 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);
|
||||
|
||||
//! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table
|
||||
if (node->orientation && node->delay)
|
||||
init.SetFacing(node->orientation);
|
||||
|
||||
switch (node->move_type)
|
||||
{
|
||||
case WAYPOINT_MOVE_TYPE_LAND:
|
||||
init.SetAnimation(Movement::ToGround);
|
||||
break;
|
||||
case WAYPOINT_MOVE_TYPE_TAKEOFF:
|
||||
init.SetAnimation(Movement::ToFly);
|
||||
break;
|
||||
case WAYPOINT_MOVE_TYPE_RUN:
|
||||
init.SetWalk(false);
|
||||
break;
|
||||
case WAYPOINT_MOVE_TYPE_WALK:
|
||||
init.SetWalk(true);
|
||||
break;
|
||||
}
|
||||
|
||||
init.Launch();
|
||||
|
||||
//Call for creature group update
|
||||
if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
|
||||
creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z, node->move_type == WAYPOINT_MOVE_TYPE_RUN);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff)
|
||||
{
|
||||
// Waypoint movement can be switched on/off
|
||||
// This is quite handy for escort quests and other stuff
|
||||
if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
|
||||
{
|
||||
creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
|
||||
return true;
|
||||
}
|
||||
// prevent a crash at empty waypoint path.
|
||||
if (!i_path || i_path->empty())
|
||||
return false;
|
||||
|
||||
// Xinef: Dont allow dead creatures to move
|
||||
if (!creature->IsAlive())
|
||||
return false;
|
||||
|
||||
// prevent movement while casting spells with cast time or channel time
|
||||
if (creature->HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
bool stop = true;
|
||||
if (Spell* spell = creature->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)
|
||||
{
|
||||
Stop(1000);
|
||||
if (!creature->IsStopped())
|
||||
creature->StopMoving();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Stopped())
|
||||
{
|
||||
if (CanMove(diff))
|
||||
return StartMove(creature);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (creature->IsStopped())
|
||||
Stop(STOP_TIME_FOR_PLAYER);
|
||||
else
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
OnArrived(creature);
|
||||
return StartMove(creature);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature)
|
||||
{
|
||||
if (creature->AI())
|
||||
creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
|
||||
}
|
||||
|
||||
//----------------------------------------------------//
|
||||
|
||||
uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const
|
||||
{
|
||||
if (i_currentNode >= i_path.size())
|
||||
return i_path.size();
|
||||
|
||||
uint32 curMapId = i_path[i_currentNode]->mapid;
|
||||
for (uint32 i = i_currentNode; i < i_path.size(); ++i)
|
||||
if (i_path[i]->mapid != curMapId)
|
||||
return i;
|
||||
|
||||
return i_path.size();
|
||||
}
|
||||
|
||||
void FlightPathMovementGenerator::LoadPath(Player* player)
|
||||
{
|
||||
_pointsForPathSwitch.clear();
|
||||
std::vector<uint32> const& taxi = player->m_taxi.GetPath();
|
||||
for (uint32 src = player->m_taxi.GetTaxiSegment(), dst = player->m_taxi.GetTaxiSegment()+1; dst < taxi.size(); src = dst++)
|
||||
{
|
||||
uint32 path, cost;
|
||||
sObjectMgr->GetTaxiPath(taxi[src], taxi[dst], path, cost);
|
||||
if (path > sTaxiPathNodesByPath.size())
|
||||
return;
|
||||
|
||||
TaxiPathNodeList const& nodes = sTaxiPathNodesByPath[path];
|
||||
if (!nodes.empty())
|
||||
{
|
||||
for (uint32 i = 0; i < nodes.size(); ++i)
|
||||
i_path.push_back(nodes[i]);
|
||||
}
|
||||
|
||||
_pointsForPathSwitch.push_back(uint32(i_path.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
void FlightPathMovementGenerator::DoInitialize(Player* player)
|
||||
{
|
||||
Reset(player);
|
||||
InitEndGridInfo();
|
||||
}
|
||||
|
||||
void FlightPathMovementGenerator::DoFinalize(Player* player)
|
||||
{
|
||||
// remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
|
||||
player->ClearUnitState(UNIT_STATE_IN_FLIGHT);
|
||||
|
||||
// xinef: this should be cleaned by CleanupAfterTaxiFlight(); function!
|
||||
player->Dismount();
|
||||
player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
|
||||
|
||||
if (player->m_taxi.empty())
|
||||
{
|
||||
player->getHostileRefManager().setOnlineOfflineState(true);
|
||||
// update z position to ground and orientation for landing point
|
||||
// this prevent cheating with landing point at lags
|
||||
// when client side flight end early in comparison server side
|
||||
player->StopMoving();
|
||||
player->SetFallInformation(time(NULL), player->GetPositionZ());
|
||||
}
|
||||
|
||||
player->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK);
|
||||
}
|
||||
|
||||
#define PLAYER_FLIGHT_SPEED 32.0f
|
||||
|
||||
void FlightPathMovementGenerator::DoReset(Player* player)
|
||||
{
|
||||
player->getHostileRefManager().setOnlineOfflineState(false);
|
||||
player->AddUnitState(UNIT_STATE_IN_FLIGHT);
|
||||
player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
|
||||
|
||||
Movement::MoveSplineInit init(player);
|
||||
uint32 end = GetPathAtMapEnd();
|
||||
for (uint32 i = GetCurrentNode(); i < end; ++i)
|
||||
{
|
||||
G3D::Vector3 vertice(i_path[i]->x, i_path[i]->y, i_path[i]->z);
|
||||
init.Path().push_back(vertice);
|
||||
}
|
||||
init.SetFirstPointId(GetCurrentNode());
|
||||
init.SetFly();
|
||||
init.SetVelocity(PLAYER_FLIGHT_SPEED);
|
||||
init.Launch();
|
||||
}
|
||||
|
||||
bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/)
|
||||
{
|
||||
// xinef: map was switched
|
||||
if (_mapSwitch)
|
||||
{
|
||||
DoInitialize(player);
|
||||
_mapSwitch = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 pointId = (uint32)player->movespline->currentPathIdx();
|
||||
if (pointId > i_currentNode)
|
||||
{
|
||||
bool departureEvent = true;
|
||||
do
|
||||
{
|
||||
if (i_currentNode >= i_path.size())
|
||||
{
|
||||
sLog->outMisc("TAXI NODE WAS GREATER THAN PATH SIZE, GUID: %u, POINTID: %u, NODESIZE: %lu, CURRENT: %u", player->GetGUIDLow(), pointId, i_path.size(), i_currentNode);
|
||||
player->CleanupAfterTaxiFlight();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (i_path[i_currentNode]->mapid != player->GetMapId())
|
||||
{
|
||||
sLog->outMisc("Player on different map, curmap: %u, pointmap: %u, nodesize: %lu, currentnode: %u", player->GetMapId(), i_path[i_currentNode]->mapid, i_path.size(), i_currentNode);
|
||||
player->CleanupAfterTaxiFlight();
|
||||
return false;
|
||||
}
|
||||
|
||||
DoEventIfAny(player, i_path[i_currentNode], departureEvent);
|
||||
|
||||
// xinef: erase any previous points
|
||||
uint32 curSize = _pointsForPathSwitch.size();
|
||||
while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front() <= i_currentNode)
|
||||
_pointsForPathSwitch.pop_front();
|
||||
|
||||
// xinef: switch destination only once
|
||||
if (curSize != _pointsForPathSwitch.size())
|
||||
player->m_taxi.NextTaxiDestination();
|
||||
|
||||
if (pointId == i_currentNode)
|
||||
break;
|
||||
|
||||
if (i_currentNode == _preloadTargetNode && player->GetMapId() == _endMapId)
|
||||
PreloadEndGrid();
|
||||
i_currentNode += (uint32)departureEvent;
|
||||
departureEvent = !departureEvent;
|
||||
|
||||
// xinef: map should be switched, do not rely on client packets QQ
|
||||
if (i_currentNode + 1 < i_path.size() && i_path[i_currentNode+1]->mapid != player->GetMapId())
|
||||
{
|
||||
++i_currentNode;
|
||||
_mapSwitch = true;
|
||||
player->TeleportTo(i_path[i_currentNode]->mapid, i_path[i_currentNode]->x, i_path[i_currentNode]->y, i_path[i_currentNode]->z, player->GetOrientation(), TELE_TO_NOT_LEAVE_TAXI);
|
||||
return true;
|
||||
}
|
||||
|
||||
// xinef: reached the end
|
||||
if (i_currentNode >= i_path.size() - 1)
|
||||
{
|
||||
player->CleanupAfterTaxiFlight();
|
||||
player->SetFallInformation(time(NULL), player->GetPositionZ());
|
||||
if (player->pvpInfo.IsHostile)
|
||||
player->CastSpell(player, 2479, true);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
|
||||
return i_currentNode < (i_path.size() - 1);
|
||||
}
|
||||
|
||||
void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
|
||||
{
|
||||
if (i_path.empty() || i_currentNode >= i_path.size())
|
||||
return;
|
||||
|
||||
uint32 map0 = i_path[i_currentNode]->mapid;
|
||||
for (size_t i = i_currentNode + 1; i < i_path.size(); ++i)
|
||||
{
|
||||
if (i_path[i]->mapid != map0)
|
||||
{
|
||||
i_currentNode = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlightPathMovementGenerator::DoEventIfAny(Player* player, TaxiPathNodeEntry const* node, bool departure)
|
||||
{
|
||||
if (uint32 eventid = departure ? node->departureEventID : node->arrivalEventID)
|
||||
{
|
||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||
sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node->index, node->path, player->GetName().c_str());
|
||||
#endif
|
||||
player->GetMap()->ScriptsStart(sEventScripts, eventid, player, player);
|
||||
}
|
||||
}
|
||||
|
||||
void FlightPathMovementGenerator::InitEndGridInfo()
|
||||
{
|
||||
/*! Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will
|
||||
be reinitialized for each flightmaster at the end of each spline (or stop) in the flight. */
|
||||
uint32 nodeCount = i_path.size(); //! Number of nodes in path.
|
||||
_endMapId = i_path[nodeCount - 1]->mapid; //! MapId of last node
|
||||
|
||||
// pussywizard:
|
||||
{
|
||||
_preloadTargetNode = nodeCount - 1;
|
||||
for (uint8 i = 3; i > 0; --i)
|
||||
if (nodeCount >= i && _endMapId == i_path[nodeCount - i]->mapid)
|
||||
{
|
||||
_preloadTargetNode = nodeCount - i;
|
||||
break;
|
||||
}
|
||||
//_preloadTargetNode = nodeCount - 3; // pussywizard: this can be on other map
|
||||
}
|
||||
|
||||
_endGridX = i_path[nodeCount - 1]->x;
|
||||
_endGridY = i_path[nodeCount - 1]->y;
|
||||
}
|
||||
|
||||
void FlightPathMovementGenerator::PreloadEndGrid()
|
||||
{
|
||||
// used to preload the final grid where the flightmaster is
|
||||
Map* endMap = sMapMgr->FindBaseNonInstanceMap(_endMapId);
|
||||
|
||||
// Load the grid
|
||||
if (endMap)
|
||||
{
|
||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||
sLog->outDetail("Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path.size()-1));
|
||||
#endif
|
||||
endMap->LoadGrid(_endGridX, _endGridY);
|
||||
}
|
||||
else {
|
||||
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
||||
sLog->outDetail("Unable to determine map to preload flightmaster grid");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_WAYPOINTMOVEMENTGENERATOR_H
|
||||
#define TRINITY_WAYPOINTMOVEMENTGENERATOR_H
|
||||
|
||||
/** @page PathMovementGenerator is used to generate movements
|
||||
* of waypoints and flight paths. Each serves the purpose
|
||||
* of generate activities so that it generates updated
|
||||
* packets for the players.
|
||||
*/
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
#include "WaypointManager.h"
|
||||
|
||||
#include "Player.h"
|
||||
|
||||
#define FLIGHT_TRAVEL_UPDATE 100
|
||||
#define STOP_TIME_FOR_PLAYER 2 * MINUTE * IN_MILLISECONDS // 3 Minutes
|
||||
#define TIMEDIFF_NEXT_WP 250
|
||||
|
||||
template<class T, class P>
|
||||
class PathMovementBase
|
||||
{
|
||||
public:
|
||||
PathMovementBase() : i_path(), i_currentNode(0) {}
|
||||
PathMovementBase(P path) : i_path(path), i_currentNode(0) {}
|
||||
virtual ~PathMovementBase() {};
|
||||
|
||||
uint32 GetCurrentNode() const { return i_currentNode; }
|
||||
|
||||
protected:
|
||||
P i_path;
|
||||
uint32 i_currentNode;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class WaypointMovementGenerator;
|
||||
|
||||
template<>
|
||||
class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Creature, WaypointMovementGenerator<Creature> >,
|
||||
public PathMovementBase<Creature, WaypointPath const*>
|
||||
{
|
||||
public:
|
||||
WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true)
|
||||
: PathMovementBase((WaypointPath const*)NULL), i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) {}
|
||||
~WaypointMovementGenerator() { i_path = NULL; }
|
||||
void DoInitialize(Creature*);
|
||||
void DoFinalize(Creature*);
|
||||
void DoReset(Creature*);
|
||||
bool DoUpdate(Creature*, uint32 diff);
|
||||
|
||||
void MovementInform(Creature*);
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
|
||||
|
||||
// now path movement implmementation
|
||||
void LoadPath(Creature*);
|
||||
|
||||
private:
|
||||
|
||||
void Stop(int32 time) { i_nextMoveTime.Reset(time);}
|
||||
|
||||
bool Stopped() { return !i_nextMoveTime.Passed();}
|
||||
|
||||
bool CanMove(int32 diff)
|
||||
{
|
||||
i_nextMoveTime.Update(diff);
|
||||
return i_nextMoveTime.Passed();
|
||||
}
|
||||
|
||||
void OnArrived(Creature*);
|
||||
bool StartMove(Creature*);
|
||||
|
||||
void StartMoveNow(Creature* creature)
|
||||
{
|
||||
i_nextMoveTime.Reset(0);
|
||||
StartMove(creature);
|
||||
}
|
||||
|
||||
TimeTrackerSmall i_nextMoveTime;
|
||||
bool m_isArrivalDone;
|
||||
uint32 path_id;
|
||||
bool repeating;
|
||||
};
|
||||
|
||||
/** FlightPathMovementGenerator generates movement of the player for the paths
|
||||
* and hence generates ground and activities for the player.
|
||||
*/
|
||||
class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, FlightPathMovementGenerator >,
|
||||
public PathMovementBase<Player, TaxiPathNodeList>
|
||||
{
|
||||
public:
|
||||
explicit FlightPathMovementGenerator(uint32 startNode = 0)
|
||||
{
|
||||
i_currentNode = startNode;
|
||||
_endGridX = 0.0f;
|
||||
_endGridY = 0.0f;
|
||||
_endMapId = 0;
|
||||
_preloadTargetNode = 0;
|
||||
_mapSwitch = false;
|
||||
}
|
||||
void LoadPath(Player* player);
|
||||
void DoInitialize(Player*);
|
||||
void DoReset(Player*);
|
||||
void DoFinalize(Player*);
|
||||
bool DoUpdate(Player*, uint32);
|
||||
MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; }
|
||||
|
||||
TaxiPathNodeList const& GetPath() { return i_path; }
|
||||
uint32 GetPathAtMapEnd() const;
|
||||
bool HasArrived() const { return (i_currentNode >= i_path.size()); }
|
||||
void SetCurrentNodeAfterTeleport();
|
||||
void SkipCurrentNode() { ++i_currentNode; }
|
||||
void DoEventIfAny(Player* player, TaxiPathNodeEntry const* node, bool departure);
|
||||
|
||||
void InitEndGridInfo();
|
||||
void PreloadEndGrid();
|
||||
|
||||
private:
|
||||
float _endGridX; //! X coord of last node location
|
||||
float _endGridY; //! Y coord of last node location
|
||||
uint32 _endMapId; //! map Id of last node location
|
||||
uint32 _preloadTargetNode; //! node index where preloading starts
|
||||
bool _mapSwitch;
|
||||
|
||||
std::deque<uint32> _pointsForPathSwitch; //! node indexes and costs where TaxiPath changes
|
||||
};
|
||||
#endif
|
||||
Reference in New Issue
Block a user