mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Movement: Replace old TargetedMovementGenerator into ChaseMovementGenerator and FollowMovementGenerator, full rewrite for both.
- Chase to angle is now functional. Pets use this to chase behind the target. Closes #19925.
- Chase to arbitrary range interval works. Not used anywhere, but you can technically make hunter-like mobs.
- Pets now follow the hunter cleanly and without stutter stepping. Also fix some other things. Closes #8924.
(cherry picked from commit 2a84562dc8)
This commit is contained in:
@@ -97,6 +97,9 @@ inline T RoundToInterval(T& num, T floor, T ceil)
|
||||
return num = std::min(std::max(num, floor), ceil);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T square(T x) { return x*x; }
|
||||
|
||||
// UTF8 handling
|
||||
TC_COMMON_API bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr);
|
||||
|
||||
|
||||
@@ -482,7 +482,7 @@ void PetAI::DoAttack(Unit* target, bool chase)
|
||||
ClearCharmInfoFlags();
|
||||
me->GetCharmInfo()->SetIsCommandAttack(oldCmdAttack); // For passive pets commanded to attack so they will use spells
|
||||
me->GetMotionMaster()->Clear();
|
||||
me->GetMotionMaster()->MoveChase(target, me->GetPetChaseDistance());
|
||||
me->GetMotionMaster()->MoveChase(target, me->GetPetChaseDistance(), (float)M_PI);
|
||||
}
|
||||
else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
|
||||
{
|
||||
|
||||
@@ -15,21 +15,15 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Unit.h"
|
||||
#include "TargetedMovementGenerator.h"
|
||||
#include "FollowerReference.h"
|
||||
#ifndef TRINITY_G3DPOSITION_HPP
|
||||
#define TRINITY_G3DPOSITION_HPP
|
||||
|
||||
void FollowerReference::targetObjectBuildLink()
|
||||
{
|
||||
getTarget()->addFollower(this);
|
||||
}
|
||||
#include "Position.h"
|
||||
#include <G3D/Vector3.h>
|
||||
#include "Errors.h"
|
||||
|
||||
void FollowerReference::targetObjectDestroyLink()
|
||||
{
|
||||
getTarget()->removeFollower(this);
|
||||
}
|
||||
inline G3D::Vector3 PositionToVector3(Position p) { return { p.m_positionX, p.m_positionY, p.m_positionZ }; }
|
||||
inline G3D::Vector3 PositionToVector3(Position const* p) { return { ASSERT_NOTNULL(p)->m_positionX, p->m_positionY, p->m_positionZ }; }
|
||||
inline Position Vector3ToPosition(G3D::Vector3 v) { return { v.x, v.y, v.z }; }
|
||||
|
||||
void FollowerReference::sourceObjectDestroyLink()
|
||||
{
|
||||
GetSource()->stopFollowing();
|
||||
}
|
||||
#endif
|
||||
@@ -77,6 +77,6 @@ enum PetTalk
|
||||
};
|
||||
|
||||
#define PET_FOLLOW_DIST 1.0f
|
||||
#define PET_FOLLOW_ANGLE float(M_PI/2)
|
||||
#define PET_FOLLOW_ANGLE float(M_PI)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "Unit.h"
|
||||
#include "AbstractFollower.h"
|
||||
#include "Battlefield.h"
|
||||
#include "BattlefieldMgr.h"
|
||||
#include "Battleground.h"
|
||||
@@ -559,14 +560,14 @@ bool Unit::IsWithinCombatRange(Unit const* obj, float dist2compare) const
|
||||
return distsq < maxdist * maxdist;
|
||||
}
|
||||
|
||||
bool Unit::IsWithinMeleeRange(Unit const* obj) const
|
||||
bool Unit::IsWithinMeleeRangeAt(Position const& pos, Unit const* obj) const
|
||||
{
|
||||
if (!obj || !IsInMap(obj) || !IsInPhase(obj))
|
||||
return false;
|
||||
|
||||
float dx = GetPositionX() - obj->GetPositionX();
|
||||
float dy = GetPositionY() - obj->GetPositionY();
|
||||
float dz = GetPositionZ() - obj->GetPositionZ();
|
||||
float dx = pos.GetPositionX() - obj->GetPositionX();
|
||||
float dy = pos.GetPositionY() - obj->GetPositionY();
|
||||
float dz = pos.GetPositionZ() - obj->GetPositionZ();
|
||||
float distsq = dx*dx + dy*dy + dz*dz;
|
||||
|
||||
float maxdist = GetMeleeRange(obj) + GetTotalAuraModifier(SPELL_AURA_MOD_AUTOATTACK_RANGE);
|
||||
@@ -8020,25 +8021,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype)
|
||||
{
|
||||
// Set creature speed rate
|
||||
if (GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
Unit* pOwner = GetCharmerOrOwner();
|
||||
if ((IsPet() || IsGuardian()) && !IsInCombat() && pOwner) // Must check for owner or crash on "Tame Beast"
|
||||
{
|
||||
// For every yard over 5, increase speed by 0.01
|
||||
// to help prevent pet from lagging behind and despawning
|
||||
float dist = GetDistance(pOwner);
|
||||
float base_rate = 1.00f; // base speed is 100% of owner speed
|
||||
|
||||
if (dist < 5)
|
||||
dist = 5;
|
||||
|
||||
float mult = base_rate + ((dist - 5) * 0.01f);
|
||||
|
||||
speed *= pOwner->GetSpeedRate(mtype) * mult; // pets derive speed from owner when not in combat
|
||||
}
|
||||
else
|
||||
speed *= ToCreature()->GetCreatureTemplate()->speed_run; // at this point, MOVE_WALK is never reached
|
||||
}
|
||||
speed *= ToCreature()->GetCreatureTemplate()->speed_run; // at this point, MOVE_WALK is never reached
|
||||
|
||||
// Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
|
||||
/// @todo possible affect only on MOVE_RUN
|
||||
@@ -8073,11 +8056,27 @@ void Unit::UpdateSpeed(UnitMoveType mtype)
|
||||
break;
|
||||
}
|
||||
|
||||
// for creature case, we check explicit if mob searched for assistance
|
||||
if (GetTypeId() == TYPEID_UNIT)
|
||||
if (Creature* creature = ToCreature())
|
||||
{
|
||||
if (ToCreature()->HasSearchedAssistance())
|
||||
// for creature case, we check explicit if mob searched for assistance
|
||||
if (creature->HasSearchedAssistance())
|
||||
speed *= 0.66f; // best guessed value, so this will be 33% reduction. Based off initial speed, mob can then "run", "walk fast" or "walk".
|
||||
|
||||
if (creature->HasUnitTypeMask(UNIT_MASK_MINION) && !creature->IsInCombat())
|
||||
{
|
||||
MovementGenerator* top = creature->GetMotionMaster()->top();
|
||||
if (top && top->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE)
|
||||
{
|
||||
Unit* followed = ASSERT_NOTNULL(dynamic_cast<AbstractFollower*>(top))->GetTarget();
|
||||
if (followed && followed->GetGUID() == GetOwnerGUID() && !followed->IsInCombat())
|
||||
{
|
||||
float ownerSpeed = followed->GetSpeedRate(mtype);
|
||||
if (speed < ownerSpeed || creature->IsWithinDist3d(followed, 10.0f))
|
||||
speed = ownerSpeed;
|
||||
speed *= std::min(std::max(1.0f, 0.75f + (GetDistance(followed) - PET_FOLLOW_DIST) * 0.05f), 1.3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply strongest slow aura mod to speed
|
||||
@@ -8170,6 +8169,12 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate)
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::RemoveAllFollowers()
|
||||
{
|
||||
while (!m_followingMe.empty())
|
||||
(*m_followingMe.begin())->SetTarget(nullptr);
|
||||
}
|
||||
|
||||
void Unit::setDeathState(DeathState s)
|
||||
{
|
||||
// Death state needs to be updated before RemoveAllAurasOnDeath() is called, to prevent entering combat
|
||||
@@ -9076,6 +9081,8 @@ void Unit::RemoveFromWorld()
|
||||
|
||||
RemoveAreaAurasDueToLeaveWorld();
|
||||
|
||||
RemoveAllFollowers();
|
||||
|
||||
if (!GetCharmerGUID().IsEmpty())
|
||||
{
|
||||
TC_LOG_FATAL("entities.unit", "Unit %u has charmer guid when removed from world", GetEntry());
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#define __UNIT_H
|
||||
|
||||
#include "Object.h"
|
||||
#include "FollowerReference.h"
|
||||
#include "FollowerRefManager.h"
|
||||
#include "CombatManager.h"
|
||||
#include "OptionalFwd.h"
|
||||
#include "SpellAuraDefines.h"
|
||||
@@ -65,6 +63,7 @@ enum InventorySlot
|
||||
NULL_SLOT = 255
|
||||
};
|
||||
|
||||
struct AbstractFollower;
|
||||
struct FactionTemplateEntry;
|
||||
struct LiquidData;
|
||||
struct LiquidTypeEntry;
|
||||
@@ -793,7 +792,8 @@ class TC_GAME_API Unit : public WorldObject
|
||||
float GetBoundingRadius() const { return m_unitData->BoundingRadius; }
|
||||
void SetBoundingRadius(float boundingRadius) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::BoundingRadius), boundingRadius); }
|
||||
bool IsWithinCombatRange(Unit const* obj, float dist2compare) const;
|
||||
bool IsWithinMeleeRange(Unit const* obj) const;
|
||||
bool IsWithinMeleeRange(Unit const* obj) const { return IsWithinMeleeRangeAt(GetPosition(), obj); }
|
||||
bool IsWithinMeleeRangeAt(Position const& pos, Unit const* obj) const;
|
||||
float GetMeleeRange(Unit const* target) const;
|
||||
virtual SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK) const = 0;
|
||||
bool IsWithinBoundaryRadius(const Unit* obj) const;
|
||||
@@ -1684,8 +1684,9 @@ class TC_GAME_API Unit : public WorldObject
|
||||
void SetSpeed(UnitMoveType mtype, float newValue);
|
||||
void SetSpeedRate(UnitMoveType mtype, float rate);
|
||||
|
||||
void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); }
|
||||
void removeFollower(FollowerReference* /*pRef*/) { /* nothing to do yet */ }
|
||||
void FollowerAdded(AbstractFollower* f) { m_followingMe.insert(f); }
|
||||
void FollowerRemoved(AbstractFollower* f) { m_followingMe.erase(f); }
|
||||
void RemoveAllFollowers();
|
||||
|
||||
MotionMaster* GetMotionMaster() { return i_motionMaster; }
|
||||
MotionMaster const* GetMotionMaster() const { return i_motionMaster; }
|
||||
@@ -1953,7 +1954,7 @@ class TC_GAME_API Unit : public WorldObject
|
||||
friend class ThreatManager;
|
||||
ThreatManager m_threatManager;
|
||||
|
||||
FollowerRefManager m_FollowingRefManager;
|
||||
std::unordered_set<AbstractFollower*> m_followingMe;
|
||||
|
||||
bool m_cleanupDone; // lock made to not add stuff after cleanup before delete
|
||||
bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world
|
||||
|
||||
@@ -14,17 +14,18 @@
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "AbstractFollower.h"
|
||||
#include "Unit.h"
|
||||
|
||||
#ifndef _FOLLOWERREFMANAGER
|
||||
#define _FOLLOWERREFMANAGER
|
||||
|
||||
#include "RefManager.h"
|
||||
|
||||
class Unit;
|
||||
class TargetedMovementGeneratorBase;
|
||||
|
||||
class FollowerRefManager : public RefManager<Unit, TargetedMovementGeneratorBase>
|
||||
void AbstractFollower::SetTarget(Unit* unit)
|
||||
{
|
||||
if (unit == _target)
|
||||
return;
|
||||
|
||||
};
|
||||
#endif
|
||||
if (_target)
|
||||
_target->FollowerRemoved(this);
|
||||
_target = unit;
|
||||
if (_target)
|
||||
_target->FollowerAdded(this);
|
||||
}
|
||||
@@ -15,19 +15,22 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _FOLLOWERREFERENCE_H
|
||||
#define _FOLLOWERREFERENCE_H
|
||||
#ifndef TRINITY_ABSTRACTFOLLOWER_H
|
||||
#define TRINITY_ABSTRACTFOLLOWER_H
|
||||
|
||||
#include "Reference.h"
|
||||
|
||||
class TargetedMovementGeneratorBase;
|
||||
class Unit;
|
||||
|
||||
class FollowerReference : public Reference<Unit, TargetedMovementGeneratorBase>
|
||||
struct AbstractFollower
|
||||
{
|
||||
protected:
|
||||
void targetObjectBuildLink() override;
|
||||
void targetObjectDestroyLink() override;
|
||||
void sourceObjectDestroyLink() override;
|
||||
public:
|
||||
AbstractFollower(Unit* target = nullptr) { SetTarget(target); }
|
||||
~AbstractFollower() { SetTarget(nullptr); }
|
||||
|
||||
void SetTarget(Unit* unit);
|
||||
Unit* GetTarget() const { return _target; }
|
||||
|
||||
private:
|
||||
Unit* _target = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -16,12 +16,14 @@
|
||||
*/
|
||||
|
||||
#include "MotionMaster.h"
|
||||
#include "ChaseMovementGenerator.h"
|
||||
#include "ConfusedMovementGenerator.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAISelector.h"
|
||||
#include "DB2Stores.h"
|
||||
#include "FleeingMovementGenerator.h"
|
||||
#include "FlightPathMovementGenerator.h"
|
||||
#include "FollowMovementGenerator.h"
|
||||
#include "FormationMovementGenerator.h"
|
||||
#include "HomeMovementGenerator.h"
|
||||
#include "IdleMovementGenerator.h"
|
||||
@@ -36,7 +38,6 @@
|
||||
#include "RandomMovementGenerator.h"
|
||||
#include "ScriptSystem.h"
|
||||
#include "SplineChainMovementGenerator.h"
|
||||
#include "TargetedMovementGenerator.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
|
||||
inline MovementGenerator* GetIdleMovementGenerator()
|
||||
@@ -217,7 +218,7 @@ void MotionMaster::MoveTargetedHome()
|
||||
if (target)
|
||||
{
|
||||
TC_LOG_DEBUG("misc", "Following %s", target->GetGUID().ToString().c_str());
|
||||
Mutate(new FollowMovementGenerator<Creature>(target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
|
||||
Mutate(new FollowMovementGenerator(target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -235,47 +236,33 @@ void MotionMaster::MoveRandom(float spawndist)
|
||||
}
|
||||
}
|
||||
|
||||
void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot)
|
||||
void MotionMaster::MoveFollow(Unit* target, float dist, ChaseAngle angle, MovementSlot slot)
|
||||
{
|
||||
// ignore movement request if target not exist
|
||||
if (!target || target == _owner)
|
||||
return;
|
||||
|
||||
//_owner->AddUnitState(UNIT_STATE_FOLLOW);
|
||||
if (_owner->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
TC_LOG_DEBUG("misc", "Player (%s) follows (%s)", _owner->GetGUID().ToString().c_str(), target->GetGUID().ToString().c_str());
|
||||
Mutate(new FollowMovementGenerator<Player>(target, dist, angle), slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_DEBUG("misc", "Creature (Entry: %u %s) follows (%s)", _owner->GetEntry(), _owner->GetGUID().ToString().c_str(), target->GetGUID().ToString().c_str());
|
||||
Mutate(new FollowMovementGenerator<Creature>(target, dist, angle), slot);
|
||||
}
|
||||
TC_LOG_DEBUG("misc", "%s (Entry: %u, %s) follows %s (Entry %u, %s).",
|
||||
_owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature",
|
||||
_owner->GetEntry(), _owner->GetGUID().ToString().c_str(),
|
||||
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
|
||||
target->GetEntry(), target->GetGUID().ToString().c_str());
|
||||
|
||||
Mutate(new FollowMovementGenerator(target, dist, angle), slot);
|
||||
}
|
||||
|
||||
void MotionMaster::MoveChase(Unit* target, float dist, float angle)
|
||||
void MotionMaster::MoveChase(Unit* target, Optional<ChaseRange> dist, Optional<ChaseAngle> angle)
|
||||
{
|
||||
// ignore movement request if target not exist
|
||||
if (!target || target == _owner)
|
||||
return;
|
||||
|
||||
//_owner->ClearUnitState(UNIT_STATE_FOLLOW);
|
||||
if (_owner->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
TC_LOG_DEBUG("misc", "Player (%s) chase (%s)",
|
||||
_owner->GetGUID().ToString().c_str(),
|
||||
target->GetGUID().ToString().c_str());
|
||||
Mutate(new ChaseMovementGenerator<Player>(target, dist, angle), MOTION_SLOT_ACTIVE);
|
||||
}
|
||||
else
|
||||
{
|
||||
TC_LOG_DEBUG("misc", "Creature (Entry: %u %s) chase %s",
|
||||
_owner->GetEntry(),
|
||||
_owner->GetGUID().ToString().c_str(),
|
||||
target->GetGUID().ToString().c_str());
|
||||
Mutate(new ChaseMovementGenerator<Creature>(target, dist, angle), MOTION_SLOT_ACTIVE);
|
||||
}
|
||||
TC_LOG_DEBUG("misc", "%s (Entry: %u, %s) chase to %s (Entry: %u, %s)",
|
||||
_owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature",
|
||||
_owner->GetEntry(), _owner->GetGUID().ToString().c_str(),
|
||||
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
|
||||
target->GetEntry(), target->GetGUID().ToString().c_str());
|
||||
Mutate(new ChaseMovementGenerator(target, dist, angle), MOTION_SLOT_ACTIVE);
|
||||
}
|
||||
|
||||
void MotionMaster::MoveConfused()
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "Errors.h"
|
||||
#include "ObjectDefines.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Optional.h"
|
||||
#include "Position.h"
|
||||
@@ -93,6 +94,35 @@ enum RotateDirection
|
||||
ROTATE_DIRECTION_RIGHT
|
||||
};
|
||||
|
||||
struct ChaseRange
|
||||
{
|
||||
ChaseRange(float range) : MinRange(range > CONTACT_DISTANCE ? 0 : range - CONTACT_DISTANCE), MinTolerance(range), MaxRange(range + CONTACT_DISTANCE), MaxTolerance(range) {}
|
||||
ChaseRange(float min, float max) : MinRange(min), MinTolerance(std::min(min + CONTACT_DISTANCE, (min + max) / 2)), MaxRange(max), MaxTolerance(std::max(max - CONTACT_DISTANCE, MinTolerance)) {}
|
||||
ChaseRange(float min, float tMin, float tMax, float max) : MinRange(min), MinTolerance(tMin), MaxRange(max), MaxTolerance(tMax) {}
|
||||
|
||||
// this contains info that informs how we should path!
|
||||
float MinRange; // we have to move if we are within this range... (min. attack range)
|
||||
float MinTolerance; // ...and if we are, we will move this far away
|
||||
float MaxRange; // we have to move if we are outside this range... (max. attack range)
|
||||
float MaxTolerance; // ...and if we are, we will move into this range
|
||||
};
|
||||
|
||||
struct ChaseAngle
|
||||
{
|
||||
ChaseAngle(float angle, float tol = M_PI_4) : RelativeAngle(Position::NormalizeOrientation(angle)), Tolerance(tol) {}
|
||||
|
||||
float RelativeAngle; // we want to be at this angle relative to the target (0 = front, M_PI = back)
|
||||
float Tolerance; // but we'll tolerate anything within +- this much
|
||||
|
||||
float UpperBound() const { return Position::NormalizeOrientation(RelativeAngle + Tolerance); }
|
||||
float LowerBound() const { return Position::NormalizeOrientation(RelativeAngle - Tolerance); }
|
||||
bool IsAngleOkay(float relAngle) const
|
||||
{
|
||||
float const diff = std::abs(relAngle - RelativeAngle);
|
||||
return (std::min(diff, float(2 * M_PI) - diff) <= Tolerance);
|
||||
}
|
||||
};
|
||||
|
||||
struct JumpArrivalCastArgs
|
||||
{
|
||||
uint32 SpellId;
|
||||
@@ -136,8 +166,9 @@ class TC_GAME_API MotionMaster
|
||||
void MoveIdle();
|
||||
void MoveTargetedHome();
|
||||
void MoveRandom(float spawndist = 0.0f);
|
||||
void MoveFollow(Unit* target, float dist, float angle, MovementSlot slot = MOTION_SLOT_ACTIVE);
|
||||
void MoveChase(Unit* target, float dist = 0.0f, float angle = 0.0f);
|
||||
void MoveFollow(Unit* target, float dist, ChaseAngle angle, MovementSlot slot = MOTION_SLOT_ACTIVE);
|
||||
void MoveChase(Unit* target, Optional<ChaseRange> dist = {}, Optional<ChaseAngle> angle = {});
|
||||
void MoveChase(Unit* target, float dist, float angle = 0.0f) { MoveChase(target, Optional<ChaseRange>(dist), Optional<ChaseAngle>(angle)); }
|
||||
void MoveConfused();
|
||||
void MoveFleeing(Unit* enemy, uint32 time = 0);
|
||||
void MovePoint(uint32 id, Position const& pos, bool generatePath = true, Optional<float> finalOrient = {})
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ChaseMovementGenerator.h"
|
||||
#include "Creature.h"
|
||||
#include "G3DPosition.hpp"
|
||||
#include "MoveSpline.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Unit.h"
|
||||
#include "Util.h"
|
||||
|
||||
static bool IsMutualChase(Unit* owner, Unit* target)
|
||||
{
|
||||
MovementGenerator const* gen = target->GetMotionMaster()->top();
|
||||
if (!gen || gen->GetMovementGeneratorType() != CHASE_MOTION_TYPE)
|
||||
return false;
|
||||
return (static_cast<ChaseMovementGenerator const*>(gen)->GetTarget() == owner);
|
||||
}
|
||||
|
||||
static bool PositionOkay(Unit* owner, Unit* target, Optional<float> minDistance, Optional<float> maxDistance, Optional<ChaseAngle> angle)
|
||||
{
|
||||
float const distSq = owner->GetExactDistSq(target);
|
||||
if (minDistance && distSq < square(*minDistance))
|
||||
return false;
|
||||
if (maxDistance && distSq > square(*maxDistance))
|
||||
return false;
|
||||
return !angle || angle->IsAngleOkay(target->GetRelativeAngle(owner));
|
||||
}
|
||||
|
||||
ChaseMovementGenerator::ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range, Optional<ChaseAngle> angle) : AbstractFollower(ASSERT_NOTNULL(target)), _range(range), _angle(angle) {}
|
||||
ChaseMovementGenerator::~ChaseMovementGenerator() = default;
|
||||
|
||||
void ChaseMovementGenerator::Initialize(Unit* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
owner->SetWalk(false);
|
||||
}
|
||||
|
||||
bool ChaseMovementGenerator::Update(Unit* owner, uint32 diff)
|
||||
{
|
||||
// owner might be dead or gone (can we even get nullptr here?)
|
||||
if (!owner || !owner->IsAlive())
|
||||
return false;
|
||||
|
||||
// our target might have gone away
|
||||
Unit* const target = GetTarget();
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
// the owner might've selected a different target (feels like we shouldn't check this here...)
|
||||
if (owner->GetVictim() != target)
|
||||
return false;
|
||||
|
||||
// the owner might be unable to move (rooted or casting), pause movement
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting())
|
||||
{
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool const mutualChase = IsMutualChase(owner, target);
|
||||
float const hitboxSum = owner->GetCombatReach() + target->GetCombatReach();
|
||||
float const minRange = _range ? _range->MinRange + hitboxSum : CONTACT_DISTANCE;
|
||||
float const minTarget = (_range ? _range->MinTolerance : 0.0f) + hitboxSum;
|
||||
float const maxRange = _range ? _range->MaxRange + hitboxSum : owner->GetMeleeRange(target); // melee range already includes hitboxes
|
||||
float const maxTarget = _range ? _range->MaxTolerance + hitboxSum : CONTACT_DISTANCE + hitboxSum;
|
||||
Optional<ChaseAngle> angle = mutualChase ? Optional<ChaseAngle>() : _angle;
|
||||
|
||||
// if we're already moving, periodically check if we're already in the expected range...
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE))
|
||||
{
|
||||
if (_rangeCheckTimer > diff)
|
||||
_rangeCheckTimer -= diff;
|
||||
else
|
||||
{
|
||||
_rangeCheckTimer = RANGE_CHECK_INTERVAL;
|
||||
if (PositionOkay(owner, target, _movingTowards ? Optional<float>() : minTarget, _movingTowards ? maxTarget : Optional<float>(), angle))
|
||||
{
|
||||
_path = nullptr;
|
||||
owner->StopMoving();
|
||||
owner->SetInFront(target);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we're done moving, we want to clean up
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized())
|
||||
{
|
||||
_path = nullptr;
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
owner->SetInFront(target);
|
||||
}
|
||||
|
||||
// if the target moved, we have to consider whether to adjust
|
||||
if (_lastTargetPosition != target->GetPosition() || mutualChase != _mutualChase)
|
||||
{
|
||||
_lastTargetPosition = target->GetPosition();
|
||||
_mutualChase = mutualChase;
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, minRange, maxRange, angle))
|
||||
{
|
||||
Creature* const cOwner = owner->ToCreature();
|
||||
// can we get to the target?
|
||||
if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
|
||||
{
|
||||
cOwner->SetCannotReachTarget(true);
|
||||
cOwner->StopMoving();
|
||||
_path = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// figure out which way we want to move
|
||||
bool const moveToward = !owner->IsInDist(target, maxRange);
|
||||
|
||||
// make a new path if we have to...
|
||||
if (!_path || moveToward != _movingTowards)
|
||||
_path = std::make_unique<PathGenerator>(owner);
|
||||
|
||||
float x, y, z;
|
||||
bool shortenPath;
|
||||
// if we want to move toward the target and there's no fixed angle...
|
||||
if (moveToward && !angle)
|
||||
{
|
||||
// ...we'll pathfind to the center, then shorten the path
|
||||
target->GetPosition(x, y, z);
|
||||
shortenPath = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, we fall back to nearpoint finding
|
||||
target->GetNearPoint(owner, x, y, z, (moveToward ? maxTarget : minTarget) - hitboxSum, angle ? target->ToAbsoluteAngle(angle->RelativeAngle) : target->GetAbsoluteAngle(owner));
|
||||
shortenPath = false;
|
||||
}
|
||||
|
||||
if (owner->IsHovering())
|
||||
owner->UpdateAllowedPositionZ(x, y, z);
|
||||
|
||||
bool success = _path->CalculatePath(x, y, z);
|
||||
if (!success || (_path->GetPathType() & PATHFIND_NOPATH))
|
||||
{
|
||||
if (cOwner)
|
||||
cOwner->SetCannotReachTarget(true);
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shortenPath)
|
||||
_path->ShortenPathUntilDist(PositionToVector3(target), maxTarget);
|
||||
|
||||
if (cOwner)
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(_path->GetPath());
|
||||
init.SetWalk(false);
|
||||
init.SetFacing(target);
|
||||
|
||||
init.Launch();
|
||||
}
|
||||
}
|
||||
|
||||
// and then, finally, we're done for the tick
|
||||
return true;
|
||||
}
|
||||
|
||||
void ChaseMovementGenerator::Finalize(Unit* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
cOwner->SetCannotReachTarget(false);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_CHASEMOVEMENTGENERATOR_H
|
||||
#define TRINITY_CHASEMOVEMENTGENERATOR_H
|
||||
|
||||
#include "AbstractFollower.h"
|
||||
#include "MovementGenerator.h"
|
||||
#include "Optional.h"
|
||||
|
||||
class PathGenerator;
|
||||
class Unit;
|
||||
|
||||
class ChaseMovementGenerator : public MovementGenerator, public AbstractFollower
|
||||
{
|
||||
public:
|
||||
MovementGeneratorType GetMovementGeneratorType() const override { return CHASE_MOTION_TYPE; }
|
||||
|
||||
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {});
|
||||
~ChaseMovementGenerator();
|
||||
|
||||
void Initialize(Unit* owner) override;
|
||||
void Reset(Unit* owner) override { Initialize(owner); }
|
||||
bool Update(Unit* owner, uint32 diff) override;
|
||||
void Finalize(Unit* owner) override;
|
||||
|
||||
void UnitSpeedChanged() override { _lastTargetPosition.Relocate(0.0f,0.0f,0.0f); }
|
||||
|
||||
private:
|
||||
static constexpr uint32 RANGE_CHECK_INTERVAL = 100; // time (ms) until we attempt to recalculate
|
||||
|
||||
Optional<ChaseRange> const _range;
|
||||
Optional<ChaseAngle> const _angle;
|
||||
|
||||
std::unique_ptr<PathGenerator> _path;
|
||||
Position _lastTargetPosition;
|
||||
uint32 _rangeCheckTimer = RANGE_CHECK_INTERVAL;
|
||||
bool _movingTowards = true;
|
||||
bool _mutualChase = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "FollowMovementGenerator.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Pet.h"
|
||||
#include "Unit.h"
|
||||
#include "Util.h"
|
||||
|
||||
FollowMovementGenerator::FollowMovementGenerator(Unit* target, float range, ChaseAngle angle) : AbstractFollower(ASSERT_NOTNULL(target)), _range(range), _angle(angle) {}
|
||||
FollowMovementGenerator::~FollowMovementGenerator() {}
|
||||
|
||||
static bool PositionOkay(Unit* owner, Unit* target, float range, Optional<ChaseAngle> angle = {})
|
||||
{
|
||||
if (owner->GetExactDistSq(target) > square(owner->GetCombatReach() + target->GetCombatReach() + range))
|
||||
return false;
|
||||
return !angle || angle->IsAngleOkay(target->GetRelativeAngle(owner));
|
||||
}
|
||||
|
||||
void FollowMovementGenerator::Initialize(Unit* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW);
|
||||
UpdatePetSpeed(owner);
|
||||
}
|
||||
|
||||
bool FollowMovementGenerator::Update(Unit* owner, uint32 diff)
|
||||
{
|
||||
// owner might be dead or gone
|
||||
if (!owner->IsAlive())
|
||||
return false;
|
||||
|
||||
// our target might have gone away
|
||||
Unit* const target = GetTarget();
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting())
|
||||
{
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE))
|
||||
{
|
||||
if (_checkTimer > diff)
|
||||
_checkTimer -= diff;
|
||||
else
|
||||
{
|
||||
_checkTimer = CHECK_INTERVAL;
|
||||
if (PositionOkay(owner, target, _range, _angle))
|
||||
{
|
||||
_path = nullptr;
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE) && owner->movespline->Finalized())
|
||||
{
|
||||
_path = nullptr;
|
||||
owner->ClearUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||
}
|
||||
|
||||
if (_lastTargetPosition.GetExactDistSq(target->GetPosition()) > 0.0f)
|
||||
{
|
||||
_lastTargetPosition = target->GetPosition();
|
||||
if (owner->HasUnitState(UNIT_STATE_FOLLOW_MOVE) || !PositionOkay(owner, target, _range + FOLLOW_RANGE_TOLERANCE))
|
||||
{
|
||||
if (!_path)
|
||||
_path = std::make_unique<PathGenerator>(owner);
|
||||
|
||||
float x, y, z;
|
||||
|
||||
// select angle
|
||||
float tAngle;
|
||||
float const curAngle = target->GetRelativeAngle(owner);
|
||||
if (_angle.IsAngleOkay(curAngle))
|
||||
tAngle = curAngle;
|
||||
else
|
||||
{
|
||||
float const diffUpper = Position::NormalizeOrientation(curAngle - _angle.UpperBound());
|
||||
float const diffLower = Position::NormalizeOrientation(_angle.LowerBound() - curAngle);
|
||||
if (diffUpper < diffLower)
|
||||
tAngle = _angle.UpperBound();
|
||||
else
|
||||
tAngle = _angle.LowerBound();
|
||||
}
|
||||
|
||||
target->GetNearPoint(owner, x, y, z, _range, target->ToAbsoluteAngle(tAngle));
|
||||
|
||||
if (owner->IsHovering())
|
||||
owner->UpdateAllowedPositionZ(x, y, z);
|
||||
|
||||
// pets are allowed to "cheat" on pathfinding when following their master
|
||||
bool allowShortcut = false;
|
||||
if (Pet* oPet = owner->ToPet())
|
||||
if (target->GetGUID() == oPet->GetOwnerGUID())
|
||||
allowShortcut = true;
|
||||
|
||||
bool success = _path->CalculatePath(x, y, z, allowShortcut);
|
||||
if (!success || (_path->GetPathType() & PATHFIND_NOPATH))
|
||||
{
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(_path->GetPath());
|
||||
init.SetWalk(target->IsWalking());
|
||||
init.SetFacing(target->GetOrientation());
|
||||
init.Launch();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FollowMovementGenerator::Finalize(Unit* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
|
||||
UpdatePetSpeed(owner);
|
||||
}
|
||||
|
||||
void FollowMovementGenerator::UpdatePetSpeed(Unit* owner)
|
||||
{
|
||||
if (Pet* oPet = owner->ToPet())
|
||||
if (!GetTarget() || GetTarget()->GetGUID() == owner->GetOwnerGUID())
|
||||
{
|
||||
oPet->UpdateSpeed(MOVE_RUN);
|
||||
oPet->UpdateSpeed(MOVE_WALK);
|
||||
oPet->UpdateSpeed(MOVE_SWIM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_FOLLOWMOVEMENTGENERATOR_H
|
||||
#define TRINITY_FOLLOWMOVEMENTGENERATOR_H
|
||||
|
||||
#include "AbstractFollower.h"
|
||||
#include "MovementGenerator.h"
|
||||
|
||||
class PathGenerator;
|
||||
class Unit;
|
||||
|
||||
class FollowMovementGenerator : public MovementGenerator, public AbstractFollower
|
||||
{
|
||||
public:
|
||||
MovementGeneratorType GetMovementGeneratorType() const override { return FOLLOW_MOTION_TYPE; }
|
||||
|
||||
FollowMovementGenerator(Unit* target, float range, ChaseAngle angle);
|
||||
~FollowMovementGenerator();
|
||||
|
||||
void Initialize(Unit* owner) override;
|
||||
void Reset(Unit* owner) override { Initialize(owner); }
|
||||
bool Update(Unit* owner, uint32 diff) override;
|
||||
void Finalize(Unit* owner) override;
|
||||
|
||||
void UnitSpeedChanged() override { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
|
||||
|
||||
private:
|
||||
static constexpr uint32 CHECK_INTERVAL = 500;
|
||||
// static inline const when?
|
||||
#define FOLLOW_RANGE_TOLERANCE 1.0f
|
||||
|
||||
void UpdatePetSpeed(Unit* owner);
|
||||
|
||||
float const _range;
|
||||
ChaseAngle const _angle;
|
||||
|
||||
uint32 _checkTimer = CHECK_INTERVAL;
|
||||
std::unique_ptr<PathGenerator> _path;
|
||||
Position _lastTargetPosition;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,417 +0,0 @@
|
||||
/*
|
||||
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TargetedMovementGenerator.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "MoveSpline.h"
|
||||
#include "MoveSplineInit.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Player.h"
|
||||
#include "VehicleDefines.h"
|
||||
#include "World.h"
|
||||
|
||||
template<class T, typename D>
|
||||
TargetedMovementGenerator<T, D>::~TargetedMovementGenerator()
|
||||
{
|
||||
delete _path;
|
||||
}
|
||||
|
||||
template<class T, typename D>
|
||||
bool TargetedMovementGenerator<T, D>::DoUpdate(T* owner, uint32 diff)
|
||||
{
|
||||
if (!IsTargetValid() || !GetTarget()->IsInWorld())
|
||||
return false;
|
||||
|
||||
if (!owner || !owner->IsAlive())
|
||||
return false;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting() || HasLostTarget(owner))
|
||||
{
|
||||
_interrupt = true;
|
||||
owner->StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_interrupt || _recalculateTravel)
|
||||
{
|
||||
_interrupt = false;
|
||||
SetTargetLocation(owner, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool targetMoved = false;
|
||||
_timer.Update(diff);
|
||||
if (!_interrupt && _timer.Passed())
|
||||
{
|
||||
_timer.Reset(100);
|
||||
|
||||
// the allowed distance between target and mover before the mover needs to reposition in order to keep attacking
|
||||
float distance = GetMaxDistanceBeforeRepositioning(owner);
|
||||
if (owner->IsPet() && (owner->GetCharmerOrOwnerGUID() == GetTarget()->GetGUID()))
|
||||
distance = 1.f + GetTarget()->GetCombatReach(); // pet following owner
|
||||
|
||||
G3D::Vector3 destination = owner->movespline->FinalDestination();
|
||||
if (owner->movespline->onTransport)
|
||||
if (TransportBase* transport = owner->GetDirectTransport())
|
||||
transport->CalculatePassengerPosition(destination.x, destination.y, destination.z);
|
||||
|
||||
// First check distance
|
||||
targetMoved = !GetTarget()->IsInDist(destination.x, destination.y, destination.z, distance);
|
||||
|
||||
// then, if the target is in range, check also Line of Sight.
|
||||
if (!targetMoved)
|
||||
targetMoved = !GetTarget()->IsWithinLOSInMap(owner);
|
||||
}
|
||||
|
||||
if (targetMoved)
|
||||
SetTargetLocation(owner, true);
|
||||
else if (_speedChanged)
|
||||
SetTargetLocation(owner, false);
|
||||
|
||||
if (!_targetReached && owner->movespline->Finalized())
|
||||
{
|
||||
MovementInform(owner);
|
||||
if (_angle == 0.f && !owner->HasInArc(0.01f, GetTarget()))
|
||||
owner->SetInFront(GetTarget());
|
||||
|
||||
if (!_targetReached)
|
||||
{
|
||||
_targetReached = true;
|
||||
ReachTarget(owner);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T, typename D>
|
||||
void TargetedMovementGenerator<T, D>::SetTargetLocation(T* owner, bool updateDestination)
|
||||
{
|
||||
if (!IsTargetValid() || !GetTarget()->IsInWorld())
|
||||
return;
|
||||
|
||||
if (!owner || !owner->IsAlive())
|
||||
return;
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting() || HasLostTarget(owner))
|
||||
{
|
||||
_interrupt = true;
|
||||
owner->StopMoving();
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner->GetTypeId() == TYPEID_UNIT && !GetTarget()->isInAccessiblePlaceFor(owner->ToCreature()))
|
||||
{
|
||||
owner->ToCreature()->SetCannotReachTarget(true);
|
||||
return;
|
||||
}
|
||||
|
||||
float x, y, z;
|
||||
if (updateDestination || !_path)
|
||||
{
|
||||
if (!_offset)
|
||||
{
|
||||
if (GetTarget()->IsWithinDistInMap(owner, CONTACT_DISTANCE))
|
||||
return;
|
||||
|
||||
GetTarget()->GetNearPoint(owner, x, y, z, CONTACT_DISTANCE, GetTarget()->GetAbsoluteAngle(owner));
|
||||
}
|
||||
else
|
||||
{
|
||||
float distance = _offset + 1.0f;
|
||||
|
||||
if (owner->IsPet() && GetTarget()->GetTypeId() == TYPEID_PLAYER)
|
||||
distance = 1.0f;
|
||||
|
||||
if (GetTarget()->IsWithinDistInMap(owner, distance))
|
||||
return;
|
||||
|
||||
GetTarget()->GetClosePoint(x, y, z, _offset, _angle);
|
||||
}
|
||||
|
||||
if (owner->IsHovering())
|
||||
owner->UpdateAllowedPositionZ(x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the destination has not changed, we just need to refresh the path (usually speed change)
|
||||
G3D::Vector3 end = _path->GetEndPosition();
|
||||
x = end.x;
|
||||
y = end.y;
|
||||
z = end.z;
|
||||
}
|
||||
|
||||
if (!_path)
|
||||
_path = new PathGenerator(owner);
|
||||
|
||||
// allow pets to use shortcut if no path found when following their master
|
||||
bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsPet() && owner->HasUnitState(UNIT_STATE_FOLLOW));
|
||||
|
||||
bool result = _path->CalculatePath(x, y, z, forceDest);
|
||||
if (!result || (_path->GetPathType() & PATHFIND_NOPATH))
|
||||
{
|
||||
// can't reach target
|
||||
_recalculateTravel = true;
|
||||
if (owner->GetTypeId() == TYPEID_UNIT)
|
||||
owner->ToCreature()->SetCannotReachTarget(true);
|
||||
return;
|
||||
}
|
||||
|
||||
_targetReached = false;
|
||||
_recalculateTravel = false;
|
||||
_speedChanged = false;
|
||||
|
||||
AddUnitStateMove(owner);
|
||||
|
||||
if (owner->GetTypeId() == TYPEID_UNIT)
|
||||
owner->ToCreature()->SetCannotReachTarget(false);
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(_path->GetPath());
|
||||
init.SetWalk(EnableWalking());
|
||||
// Using the same condition for facing target as the one that is used for SetInFront on movement end
|
||||
// - applies to ChaseMovementGenerator mostly
|
||||
if (_angle == 0.f)
|
||||
init.SetFacing(GetTarget());
|
||||
|
||||
init.Launch();
|
||||
}
|
||||
|
||||
template<class T, typename D>
|
||||
bool TargetedMovementGenerator<T, D>::IsReachable() const
|
||||
{
|
||||
return (_path) ? (_path->GetPathType() & PATHFIND_NORMAL) : true;
|
||||
}
|
||||
|
||||
//---- ChaseMovementGenerator
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::DoInitialize(T*) { }
|
||||
|
||||
template<>
|
||||
void ChaseMovementGenerator<Player>::DoInitialize(Player* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
SetTargetLocation(owner, true);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner)
|
||||
{
|
||||
owner->SetWalk(false);
|
||||
owner->AddUnitState(UNIT_STATE_CHASE);
|
||||
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>::ClearUnitStateMove(T* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::AddUnitStateMove(T* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool ChaseMovementGenerator<T>::HasLostTarget(T* owner) const
|
||||
{
|
||||
return owner->GetVictim() != TargetedMovementGeneratorBase::GetTarget();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::ReachTarget(T* owner)
|
||||
{
|
||||
ClearUnitStateMove(owner);
|
||||
|
||||
if (owner->IsWithinMeleeRange(TargetedMovementGeneratorBase::GetTarget()))
|
||||
owner->Attack(TargetedMovementGeneratorBase::GetTarget(), true);
|
||||
|
||||
if (owner->GetTypeId() == TYPEID_UNIT)
|
||||
owner->ToCreature()->SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ChaseMovementGenerator<T>::MovementInform(T*) { }
|
||||
|
||||
template<class T>
|
||||
float ChaseMovementGenerator<T>::GetMaxDistanceBeforeRepositioning(T* owner)
|
||||
{
|
||||
// the notion of melee range and melee attack is clearly separated from the notion of the movement.
|
||||
// As a seperation of concern refactoring, this value should be passed into parameter by the caller (during creation of ChaseMoveGen) instead of defining it
|
||||
// here (or a callback should be used since this value depends on dynamic fields (combat reach).
|
||||
return owner->GetMeleeRange(TargetedMovementGeneratorBase::GetTarget());
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChaseMovementGenerator<Creature>::MovementInform(Creature* owner)
|
||||
{
|
||||
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
|
||||
if (owner->AI())
|
||||
owner->AI()->MovementInform(CHASE_MOTION_TYPE, GetTarget()->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
//---- FollowMovementGenerator
|
||||
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::UpdateSpeed(T*) { }
|
||||
|
||||
template<>
|
||||
void FollowMovementGenerator<Player>::UpdateSpeed(Player* /*owner*/) { }
|
||||
|
||||
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 (!owner->IsPet() || !owner->IsInWorld() || !IsTargetValid() || GetTarget()->GetGUID() != owner->GetOwnerGUID())
|
||||
return;
|
||||
|
||||
owner->UpdateSpeed(MOVE_RUN);
|
||||
owner->UpdateSpeed(MOVE_WALK);
|
||||
owner->UpdateSpeed(MOVE_SWIM);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::DoInitialize(T* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW);
|
||||
UpdateSpeed(owner);
|
||||
TargetedMovementGenerator<T, FollowMovementGenerator<T>>::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>::ClearUnitStateMove(T* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::AddUnitStateMove(T* owner)
|
||||
{
|
||||
owner->AddUnitState(UNIT_STATE_FOLLOW_MOVE);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::ReachTarget(T* owner)
|
||||
{
|
||||
ClearUnitStateMove(owner);
|
||||
}
|
||||
|
||||
template<>
|
||||
bool FollowMovementGenerator<Creature>::EnableWalking() const
|
||||
{
|
||||
return IsTargetValid() && GetTarget()->IsWalking();
|
||||
}
|
||||
|
||||
template<>
|
||||
bool FollowMovementGenerator<Player>::EnableWalking() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void FollowMovementGenerator<T>::MovementInform(T*) { }
|
||||
|
||||
template<class T>
|
||||
float FollowMovementGenerator<T>::GetMaxDistanceBeforeRepositioning(T* owner)
|
||||
{
|
||||
return owner->GetCombatReach() + TargetedMovementGeneratorBase::GetTarget()->GetCombatReach() + MOVE_FOLLOW_REPOSITIONING_DISTANCE;
|
||||
}
|
||||
|
||||
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, GetTarget()->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
//-----------------------------------------------//
|
||||
|
||||
template TargetedMovementGenerator<Player, ChaseMovementGenerator<Player> >::~TargetedMovementGenerator();
|
||||
template TargetedMovementGenerator<Player, FollowMovementGenerator<Player> >::~TargetedMovementGenerator();
|
||||
template TargetedMovementGenerator<Creature, ChaseMovementGenerator<Creature> >::~TargetedMovementGenerator();
|
||||
template TargetedMovementGenerator<Creature, FollowMovementGenerator<Creature> >::~TargetedMovementGenerator();
|
||||
template bool TargetedMovementGenerator<Player, ChaseMovementGenerator<Player> >::DoUpdate(Player*, uint32);
|
||||
template bool TargetedMovementGenerator<Player, FollowMovementGenerator<Player> >::DoUpdate(Player*, uint32);
|
||||
template bool TargetedMovementGenerator<Creature, ChaseMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
|
||||
template bool TargetedMovementGenerator<Creature, FollowMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
|
||||
template void TargetedMovementGenerator<Player, ChaseMovementGenerator<Player> >::SetTargetLocation(Player*, bool);
|
||||
template void TargetedMovementGenerator<Player, FollowMovementGenerator<Player> >::SetTargetLocation(Player*, bool);
|
||||
template void TargetedMovementGenerator<Creature, ChaseMovementGenerator<Creature> >::SetTargetLocation(Creature*, bool);
|
||||
template void TargetedMovementGenerator<Creature, FollowMovementGenerator<Creature> >::SetTargetLocation(Creature*, bool);
|
||||
|
||||
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>::ClearUnitStateMove(Player*);
|
||||
template void ChaseMovementGenerator<Creature>::ClearUnitStateMove(Creature*);
|
||||
template void ChaseMovementGenerator<Player>::AddUnitStateMove(Player*);
|
||||
template void ChaseMovementGenerator<Creature>::AddUnitStateMove(Creature*);
|
||||
template bool ChaseMovementGenerator<Player>::HasLostTarget(Player*) const;
|
||||
template bool ChaseMovementGenerator<Creature>::HasLostTarget(Creature*) const;
|
||||
template void ChaseMovementGenerator<Player>::ReachTarget(Player*);
|
||||
template void ChaseMovementGenerator<Creature>::ReachTarget(Creature*);
|
||||
template void ChaseMovementGenerator<Player>::MovementInform(Player*);
|
||||
template float ChaseMovementGenerator<Player>::GetMaxDistanceBeforeRepositioning(Player*);
|
||||
template float ChaseMovementGenerator<Creature>::GetMaxDistanceBeforeRepositioning(Creature*);
|
||||
|
||||
template void FollowMovementGenerator<Player>::DoInitialize(Player*);
|
||||
template void FollowMovementGenerator<Creature>::DoInitialize(Creature*);
|
||||
template void FollowMovementGenerator<Player>::DoFinalize(Player*);
|
||||
template void FollowMovementGenerator<Creature>::DoFinalize(Creature*);
|
||||
template void FollowMovementGenerator<Player>::DoReset(Player*);
|
||||
template void FollowMovementGenerator<Creature>::DoReset(Creature*);
|
||||
template void FollowMovementGenerator<Player>::ClearUnitStateMove(Player*);
|
||||
template void FollowMovementGenerator<Creature>::ClearUnitStateMove(Creature*);
|
||||
template void FollowMovementGenerator<Player>::AddUnitStateMove(Player*);
|
||||
template void FollowMovementGenerator<Creature>::AddUnitStateMove(Creature*);
|
||||
template void FollowMovementGenerator<Player>::ReachTarget(Player*);
|
||||
template void FollowMovementGenerator<Creature>::ReachTarget(Creature*);
|
||||
template void FollowMovementGenerator<Player>::MovementInform(Player*);
|
||||
template float FollowMovementGenerator<Player>::GetMaxDistanceBeforeRepositioning(Player*);
|
||||
template float FollowMovementGenerator<Creature>::GetMaxDistanceBeforeRepositioning(Creature*);
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_TARGETEDMOVEMENTGENERATOR_H
|
||||
#define TRINITY_TARGETEDMOVEMENTGENERATOR_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
#include "FollowerReference.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#define MOVE_FOLLOW_REPOSITIONING_DISTANCE 1.5f
|
||||
|
||||
class TargetedMovementGeneratorBase
|
||||
{
|
||||
public:
|
||||
TargetedMovementGeneratorBase(Unit* target)
|
||||
{
|
||||
_target.link(target, this);
|
||||
}
|
||||
|
||||
bool IsTargetValid() const { return _target.isValid(); }
|
||||
Unit* GetTarget() const { return _target.getTarget(); }
|
||||
void stopFollowing() { }
|
||||
|
||||
private:
|
||||
FollowerReference _target;
|
||||
};
|
||||
|
||||
template<class T, typename D>
|
||||
class TargetedMovementGenerator : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase
|
||||
{
|
||||
public:
|
||||
explicit TargetedMovementGenerator(Unit* target, float offset, float angle) : TargetedMovementGeneratorBase(target), _path(nullptr), _timer(0), _offset(offset), _angle(angle), _recalculateTravel(false), _speedChanged(false), _targetReached(false), _interrupt(false) { }
|
||||
~TargetedMovementGenerator();
|
||||
|
||||
bool DoUpdate(T*, uint32);
|
||||
|
||||
void UnitSpeedChanged() override { _speedChanged = true; }
|
||||
|
||||
virtual void ClearUnitStateMove(T*) { }
|
||||
virtual void AddUnitStateMove(T*) { }
|
||||
virtual bool HasLostTarget(T*) const { return false; }
|
||||
virtual void ReachTarget(T*) { }
|
||||
virtual bool EnableWalking() const { return false; }
|
||||
virtual void MovementInform(T*) { }
|
||||
virtual float GetMaxDistanceBeforeRepositioning(T*) { return 0.0f; }
|
||||
|
||||
bool IsReachable() const;
|
||||
void SetTargetLocation(T* owner, bool updateDestination);
|
||||
|
||||
private:
|
||||
PathGenerator* _path;
|
||||
TimeTrackerSmall _timer;
|
||||
float _offset;
|
||||
float _angle;
|
||||
bool _recalculateTravel;
|
||||
bool _speedChanged;
|
||||
bool _targetReached;
|
||||
bool _interrupt;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class ChaseMovementGenerator : public TargetedMovementGenerator<T, ChaseMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
explicit ChaseMovementGenerator(Unit* target, float offset, float angle) : TargetedMovementGenerator<T, ChaseMovementGenerator<T> >(target, offset, angle) { }
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() const override { return CHASE_MOTION_TYPE; }
|
||||
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
|
||||
void ClearUnitStateMove(T*) override;
|
||||
void AddUnitStateMove(T*) override;
|
||||
bool HasLostTarget(T*) const override;
|
||||
void ReachTarget(T*) override;
|
||||
void MovementInform(T*) override;
|
||||
float GetMaxDistanceBeforeRepositioning(T*) override;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class FollowMovementGenerator : public TargetedMovementGenerator<T, FollowMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
explicit FollowMovementGenerator(Unit* target, float offset, float angle) : TargetedMovementGenerator<T, FollowMovementGenerator<T> >(target, offset, angle) { }
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() const override { return FOLLOW_MOTION_TYPE; }
|
||||
|
||||
void DoInitialize(T*);
|
||||
void DoFinalize(T*);
|
||||
void DoReset(T*);
|
||||
|
||||
void ClearUnitStateMove(T*) override;
|
||||
void AddUnitStateMove(T*) override;
|
||||
bool HasLostTarget(T*) const override { return false; }
|
||||
void ReachTarget(T*) override;
|
||||
bool EnableWalking() const override;
|
||||
void MovementInform(T*) override;
|
||||
float GetMaxDistanceBeforeRepositioning(T*) override;
|
||||
private:
|
||||
void UpdateSpeed(T* owner);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -904,43 +904,50 @@ float PathGenerator::Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) c
|
||||
return (p1 - p2).squaredLength();
|
||||
}
|
||||
|
||||
void PathGenerator::ReducePathLenghtByDist(float dist)
|
||||
void PathGenerator::ShortenPathUntilDist(G3D::Vector3 const& target, float dist)
|
||||
{
|
||||
if (GetPathType() == PATHFIND_BLANK)
|
||||
if (GetPathType() == PATHFIND_BLANK || _pathPoints.size() < 2)
|
||||
{
|
||||
TC_LOG_ERROR("maps", "PathGenerator::ReducePathLenghtByDist called before path was built");
|
||||
TC_LOG_ERROR("maps", "PathGenerator::ReducePathLengthByDist called before path was successfully built");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pathPoints.size() < 2) // path building failure
|
||||
float const distSq = dist * dist;
|
||||
|
||||
// the first point of the path must be outside the specified range
|
||||
// (this should have really been checked by the caller...)
|
||||
if ((_pathPoints[0] - target).squaredLength() < distSq)
|
||||
return;
|
||||
|
||||
uint32 i = _pathPoints.size();
|
||||
G3D::Vector3 nextVec = _pathPoints[--i];
|
||||
while (i > 0)
|
||||
// check if we even need to do anything
|
||||
if ((*_pathPoints.rbegin() - target).squaredLength() >= distSq)
|
||||
return;
|
||||
|
||||
size_t i = _pathPoints.size()-1;
|
||||
// find the first i s.t.:
|
||||
// - _pathPoints[i] is still too close
|
||||
// - _pathPoints[i-1] is too far away
|
||||
// => the end point is somewhere on the line between the two
|
||||
while (1)
|
||||
{
|
||||
G3D::Vector3 currVec = _pathPoints[--i];
|
||||
G3D::Vector3 diffVec = (nextVec - currVec);
|
||||
float len = diffVec.length();
|
||||
if (len > dist)
|
||||
// we know that pathPoints[i] is too close already (from the previous iteration)
|
||||
if ((_pathPoints[i-1] - target).squaredLength() >= distSq)
|
||||
break; // bingo!
|
||||
|
||||
if (!--i)
|
||||
{
|
||||
float step = dist / len;
|
||||
// same as nextVec
|
||||
_pathPoints[i + 1] -= diffVec * step;
|
||||
_sourceUnit->UpdateAllowedPositionZ(_pathPoints[i + 1].x, _pathPoints[i + 1].y, _pathPoints[i + 1].z);
|
||||
_pathPoints.resize(i + 2);
|
||||
break;
|
||||
}
|
||||
else if (i == 0) // at second point
|
||||
{
|
||||
_pathPoints[1] = _pathPoints[0];
|
||||
// no point found that fulfills the condition
|
||||
_pathPoints[0] = _pathPoints[1];
|
||||
_pathPoints.resize(2);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
dist -= len;
|
||||
nextVec = currVec; // we're going backwards
|
||||
}
|
||||
|
||||
// ok, _pathPoints[i] is too close, _pathPoints[i-1] is not, so our target point is somewhere between the two...
|
||||
// ... settle for a guesstimate since i'm not confident in doing trig on every chase motion tick...
|
||||
// (@todo review this)
|
||||
_pathPoints[i] += (_pathPoints[i - 1] - _pathPoints[i]).direction() * (dist - (_pathPoints[i] - target).length());
|
||||
_pathPoints.resize(i+1);
|
||||
}
|
||||
|
||||
bool PathGenerator::IsInvalidDestinationZ(Unit const* target) const
|
||||
|
||||
@@ -73,7 +73,8 @@ class TC_GAME_API PathGenerator
|
||||
|
||||
PathType GetPathType() const { return _type; }
|
||||
|
||||
void ReducePathLenghtByDist(float dist); // path must be already built
|
||||
// shortens the path until the destination is the specified distance from the target point
|
||||
void ShortenPathUntilDist(G3D::Vector3 const& point, float dist);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DisableMgr.h"
|
||||
#include "DynamicObject.h"
|
||||
#include "G3DPosition.hpp"
|
||||
#include "GameObjectAI.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "Guild.h"
|
||||
@@ -5639,7 +5640,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint
|
||||
else if (m_preGeneratedPath->IsInvalidDestinationZ(target)) // Check position z, if in a straight line
|
||||
return SPELL_FAILED_NOPATH;
|
||||
|
||||
m_preGeneratedPath->ReducePathLenghtByDist(objSize); // move back
|
||||
m_preGeneratedPath->ShortenPathUntilDist(PositionToVector3(target), objSize); // move back
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
#include "ArenaTeamMgr.h"
|
||||
#include "CellImpl.h"
|
||||
#include "CharacterCache.h"
|
||||
#include "ChaseMovementGenerator.h"
|
||||
#include "Chat.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DB2Stores.h"
|
||||
#include "DisableMgr.h"
|
||||
#include "FollowMovementGenerator.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "Group.h"
|
||||
#include "GroupMgr.h"
|
||||
@@ -48,7 +50,6 @@
|
||||
#include "SpellAuras.h"
|
||||
#include "SpellHistory.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "TargetedMovementGenerator.h"
|
||||
#include "Transport.h"
|
||||
#include "Weather.h"
|
||||
#include "WeatherMgr.h"
|
||||
@@ -2236,11 +2237,7 @@ public:
|
||||
break;
|
||||
case CHASE_MOTION_TYPE:
|
||||
{
|
||||
Unit* target = nullptr;
|
||||
if (unit->GetTypeId() == TYPEID_PLAYER)
|
||||
target = static_cast<ChaseMovementGenerator<Player> const*>(movementGenerator)->GetTarget();
|
||||
else
|
||||
target = static_cast<ChaseMovementGenerator<Creature> const*>(movementGenerator)->GetTarget();
|
||||
Unit* target = static_cast<ChaseMovementGenerator const*>(movementGenerator)->GetTarget();
|
||||
|
||||
if (!target)
|
||||
handler->SendSysMessage(LANG_MOVEGENS_CHASE_NULL);
|
||||
@@ -2252,11 +2249,7 @@ public:
|
||||
}
|
||||
case FOLLOW_MOTION_TYPE:
|
||||
{
|
||||
Unit* target = nullptr;
|
||||
if (unit->GetTypeId() == TYPEID_PLAYER)
|
||||
target = static_cast<FollowMovementGenerator<Player> const*>(movementGenerator)->GetTarget();
|
||||
else
|
||||
target = static_cast<FollowMovementGenerator<Creature> const*>(movementGenerator)->GetTarget();
|
||||
Unit* target = static_cast<FollowMovementGenerator const*>(movementGenerator)->GetTarget();
|
||||
|
||||
if (!target)
|
||||
handler->SendSysMessage(LANG_MOVEGENS_FOLLOW_NULL);
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#include "Player.h"
|
||||
#include "PointMovementGenerator.h"
|
||||
#include "RBAC.h"
|
||||
#include "TargetedMovementGenerator.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
class mmaps_commandscript : public CommandScript
|
||||
|
||||
@@ -27,6 +27,7 @@ EndScriptData */
|
||||
#include "CreatureAI.h"
|
||||
#include "CreatureGroups.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "FollowMovementGenerator.h"
|
||||
#include "GameTime.h"
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
@@ -37,7 +38,6 @@ EndScriptData */
|
||||
#include "PhasingHandler.h"
|
||||
#include "Player.h"
|
||||
#include "RBAC.h"
|
||||
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
|
||||
#include "Transport.h"
|
||||
#include "World.h"
|
||||
#include "WorldSession.h"
|
||||
@@ -1391,7 +1391,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
FollowMovementGenerator<Creature> const* mgen = static_cast<FollowMovementGenerator<Creature> const*>((creature->GetMotionMaster()->top()));
|
||||
FollowMovementGenerator const* mgen = static_cast<FollowMovementGenerator const*>((creature->GetMotionMaster()->top()));
|
||||
|
||||
if (mgen->GetTarget() != player)
|
||||
{
|
||||
|
||||
@@ -545,7 +545,7 @@ public:
|
||||
if (ShadowBolt_Timer <= diff)
|
||||
{
|
||||
if (!me->IsWithinDist(me->GetVictim(), 45.0f))
|
||||
me->GetMotionMaster()->MoveChase(me->GetVictim(), VEKLOR_DIST, 0);
|
||||
me->GetMotionMaster()->MoveChase(me->GetVictim(), VEKLOR_DIST);
|
||||
else
|
||||
DoCastVictim(SPELL_SHADOWBOLT);
|
||||
ShadowBolt_Timer = 2000;
|
||||
@@ -595,7 +595,7 @@ public:
|
||||
// VL doesn't melee
|
||||
if (me->Attack(who, false))
|
||||
{
|
||||
me->GetMotionMaster()->MoveChase(who, VEKLOR_DIST, 0);
|
||||
me->GetMotionMaster()->MoveChase(who, VEKLOR_DIST);
|
||||
AddThreat(who, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user