Using TC structure allowing easier patches importing

This commit is contained in:
Yehonal
2017-12-20 20:48:35 +01:00
parent 0fc4a6a153
commit 17332304fd
1197 changed files with 0 additions and 69 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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*);

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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