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.
This commit is contained in:
Treeston
2018-04-06 18:09:55 +02:00
committed by GitHub
parent 9f03743d2d
commit 2a84562dc8
28 changed files with 700 additions and 768 deletions

View File

@@ -89,6 +89,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);

View File

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

View File

@@ -1,6 +1,5 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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
@@ -16,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

View File

@@ -75,6 +75,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

View File

@@ -17,6 +17,7 @@
*/
#include "Unit.h"
#include "AbstractFollower.h"
#include "Battlefield.h"
#include "BattlefieldMgr.h"
#include "Battleground.h"
@@ -538,14 +539,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) || !InSamePhase(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);
@@ -8509,25 +8510,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
@@ -8551,11 +8534,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
@@ -8656,6 +8655,12 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate)
}
}
void Unit::RemoveAllFollowers()
{
while (!m_followingMe.empty())
(*m_followingMe.begin())->SetTarget(nullptr);
}
bool Unit::IsGhouled() const
{
return HasAura(SPELL_DK_RAISE_ALLY);
@@ -9437,6 +9442,8 @@ void Unit::RemoveFromWorld()
RemoveAreaAurasDueToLeaveWorld();
RemoveAllFollowers();
if (IsCharmed())
RemoveCharmedBy(nullptr);

View File

@@ -20,8 +20,6 @@
#define __UNIT_H
#include "Object.h"
#include "FollowerReference.h"
#include "FollowerRefManager.h"
#include "CombatManager.h"
#include "OptionalFwd.h"
#include "SpellAuraDefines.h"
@@ -61,6 +59,7 @@ enum InventorySlot
NULL_SLOT = 255
};
struct AbstractFollower;
struct FactionTemplateEntry;
struct LiquidData;
struct LiquidTypeEntry;
@@ -789,7 +788,8 @@ class TC_GAME_API Unit : public WorldObject
virtual void SetCanDualWield(bool value) { m_canDualWield = value; }
float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; }
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, uint8 damageIndex = 0) const = 0;
uint32 m_extraAttacks;
@@ -1526,8 +1526,9 @@ class TC_GAME_API Unit : public WorldObject
float CalculateSpellpowerCoefficientLevelPenalty(SpellInfo const* spellInfo) const;
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; }
@@ -1781,7 +1782,7 @@ class TC_GAME_API Unit : public WorldObject
friend class ThreatManager;
ThreatManager m_threatManager;
FollowerRefManager m_FollowingRefManager;
std::unordered_set<AbstractFollower*> m_followingMe;
Unit* m_comboTarget;
int8 m_comboPoints;

View File

@@ -1,19 +1,19 @@
/*
* 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/>.
*/
* 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 "LootItemStorage.h"
#include "DatabaseEnv.h"

View File

@@ -1,19 +1,19 @@
/*
* 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/>.
*/
* 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 __LOOTITEMSTORAGE_H
#define __LOOTITEMSTORAGE_H

View File

@@ -1,6 +1,5 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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
@@ -15,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);
}

View File

@@ -1,6 +1,5 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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
@@ -16,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

View File

@@ -17,12 +17,14 @@
*/
#include "MotionMaster.h"
#include "ChaseMovementGenerator.h"
#include "ConfusedMovementGenerator.h"
#include "Creature.h"
#include "CreatureAISelector.h"
#include "DBCStores.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 "Unit.h"
#include "WaypointDefines.h"
#include "WaypointMovementGenerator.h"
@@ -219,7 +220,7 @@ void MotionMaster::MoveTargetedHome()
if (target)
{
TC_LOG_DEBUG("misc", "Following %s (GUID: %u).", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : ((Creature*)target)->GetSpawnId());
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
@@ -237,53 +238,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 (GUID: %u) follows %s (GUID: %u).", _owner->GetGUID().GetCounter(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : target->ToCreature()->GetSpawnId());
Mutate(new FollowMovementGenerator<Player>(target, dist, angle), slot);
}
else
{
TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) follows %s (GUID: %u).",
_owner->GetEntry(), _owner->GetGUID().GetCounter(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : target->ToCreature()->GetSpawnId());
Mutate(new FollowMovementGenerator<Creature>(target, dist, angle), slot);
}
TC_LOG_DEBUG("misc", "%s (Entry: %u, GUID: %u) follows %s (Entry %u, GUID: %u).",
_owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature",
_owner->GetEntry(), _owner->GetGUID().GetCounter(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetEntry(), target->GetGUID().GetCounter());
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 (GUID: %u) chase to %s (GUID: %u)",
_owner->GetGUID().GetCounter(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : target->ToCreature()->GetSpawnId());
Mutate(new ChaseMovementGenerator<Player>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
else
{
TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) chase to %s (GUID: %u)",
_owner->GetEntry(), _owner->GetGUID().GetCounter(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : target->ToCreature()->GetSpawnId());
Mutate(new ChaseMovementGenerator<Creature>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
TC_LOG_DEBUG("misc", "%s (Entry: %u, GUID: %u) chase to %s (Entry: %u, GUID: %u)",
_owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature",
_owner->GetEntry(), _owner->GetGUID().GetCounter(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetEntry(), target->GetGUID().GetCounter());
Mutate(new ChaseMovementGenerator(target, dist, angle), MOTION_SLOT_ACTIVE);
}
void MotionMaster::MoveConfused()

View File

@@ -21,6 +21,7 @@
#include "Common.h"
#include "Errors.h"
#include "ObjectDefines.h"
#include "ObjectGuid.h"
#include "Optional.h"
#include "Position.h"
@@ -84,6 +85,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);
}
};
class TC_GAME_API MotionMaster
{
public:
@@ -121,8 +151,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 = {})

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,418 +0,0 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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*);

View File

@@ -1,120 +0,0 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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

View File

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

View File

@@ -74,7 +74,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:

View File

@@ -1,19 +1,19 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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/>.
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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 TRINITYSERVER_MOVESPLINEINIT_H

View File

@@ -1,19 +1,19 @@
/*
* 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/>.
*/
* 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 "PetitionMgr.h"
#include "DatabaseEnv.h"

View File

@@ -1,19 +1,19 @@
/*
* 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/>.
*/
* 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 _PETITIONMGR_H
#define _PETITIONMGR_H

View File

@@ -27,6 +27,7 @@
#include "DBCStores.h"
#include "DisableMgr.h"
#include "DynamicObject.h"
#include "G3DPosition.hpp"
#include "GameObjectAI.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
@@ -5498,7 +5499,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;
}

View File

@@ -20,9 +20,11 @@
#include "ArenaTeamMgr.h"
#include "CellImpl.h"
#include "CharacterCache.h"
#include "ChaseMovementGenerator.h"
#include "Chat.h"
#include "DatabaseEnv.h"
#include "DisableMgr.h"
#include "FollowMovementGenerator.h"
#include "GridNotifiers.h"
#include "Group.h"
#include "GroupMgr.h"
@@ -43,7 +45,6 @@
#include "SpellAuras.h"
#include "SpellHistory.h"
#include "SpellMgr.h"
#include "TargetedMovementGenerator.h"
#include "Transport.h"
#include "Weather.h"
#include "WeatherMgr.h"
@@ -2164,11 +2165,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);
@@ -2180,11 +2177,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);

View File

@@ -34,7 +34,6 @@
#include "Player.h"
#include "PointMovementGenerator.h"
#include "RBAC.h"
#include "TargetedMovementGenerator.h"
class mmaps_commandscript : public CommandScript
{

View File

@@ -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"
@@ -36,7 +37,6 @@ EndScriptData */
#include "Pet.h"
#include "Player.h"
#include "RBAC.h"
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
#include "Transport.h"
#include "World.h"
#include "WorldSession.h"
@@ -1309,7 +1309,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)
{

View File

@@ -549,7 +549,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;
@@ -599,7 +599,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);
}
}