OregonCore  revision 3611e8a-git
Your Favourite TBC server
TargetedMovementGenerator.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "ByteBuffer.h"
20 #include "Errors.h"
21 #include "Creature.h"
22 #include "CreatureAI.h"
23 #include "World.h"
24 #include "Unit.h"
25 #include "Player.h"
26 #include "Pet.h"
27 #include "MoveSplineInit.h"
28 #include "MoveSpline.h"
29 #include <cmath>
30 
31 template<class T, typename D>
32 void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T& owner, bool updateDestination)
33 {
34  if (!i_target.isValid() || !i_target->IsInWorld())
35  return;
36 
37  if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) || owner.isDead())
38  return;
39 
40  if (owner.GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner.ToCreature()))
41  return;
42 
43  float x, y, z;
44 
45  // i_path can be NULL in case this is the first call for this MMGen (via Update)
46  // Can happen for example if no path was created on MMGen-Initialize because of the owner being stunned
47  if (updateDestination || !i_path)
48  {
49  owner.GetPosition(x, y, z);
50 
51  // prevent redundant micro-movement for pets, other followers.
52  if (!RequiresNewPosition(owner, x, y, z))
53  {
54  if (!owner.movespline->Finalized())
55  return;
56  }
57  else
58  {
59  if (!i_offset)
60  {
61  // to nearest random contact position
62  i_target->GetRandomContactPoint(&owner, x, y, z, 0, CONTACT_DISTANCE);
63 
64  // Sometimes target is available only from certain angles
65  // in that case we use the exact location (blizzlike hahavior)
66  if (fabsf(i_target->GetPositionZ() - z) > owner.GetObjectSize())
67  i_target->GetPosition(x, y, z);
68  }
69  else if (!i_angle && !owner.HasUnitState(UNIT_STATE_FOLLOW))
70  {
71  // caster chase
72  i_target->GetContactPoint(&owner, x, y, z, i_offset * urand(80, 95) * 0.01f);
73  }
74  else
75  {
76  // to at i_offset distance from target and i_angle from target facing
77  i_target->GetClosePoint(x, y, z, owner.GetObjectSize(), i_offset, i_angle);
78 
79  // Sometimes target is available only from certain angles
80  // in that case we use the exact location (blizzlike hahavior)
81  if (fabsf(i_target->GetPositionZ() - z) > (owner.GetObjectSize() + i_offset))
82  i_target->GetPosition(x, y, z);
83  }
84  }
85  }
86  else
87  {
88  // the destination has not changed, we just need to refresh the path (usually speed change)
89  G3D::Vector3 end = i_path->getEndPosition();
90  x = end.x;
91  y = end.y;
92  z = end.z;
93  }
94 
95  if (!i_path)
96  i_path = new PathInfo(&owner);
97 
98  // allow pets following their master to cheat while generating paths
99  bool forceDest = (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->HasUnitTypeMask(UNIT_MASK_MINION) &&
100  owner.HasUnitState(UNIT_STATE_FOLLOW));
101 
102  bool result = i_path->Update(x, y, z, forceDest);
103  if (!result || (i_path->getPathType() & PATHFIND_NOPATH))
104  {
105  // Cant reach target
106  m_speedChanged = true;
107  return;
108  }
109 
110  i_targetReached = false;
111  m_speedChanged = false;
112  owner.AddUnitState(UNIT_STATE_CHASE);
113 
114  Movement::MoveSplineInit init(owner);
115  init.MovebyPath(i_path->getFullPath());
116  init.SetWalk(((D*)this)->EnableWalking(owner));
117  static_cast<D*>(this)->_updateSpeed(owner);
118 
119  init.Launch();
120 }
121 
122 template<class T, typename D>
124 {
125  if (!i_target.isValid() || !i_target->IsInWorld())
126  return false;
127 
128  if (!owner.IsAlive())
129  return true;
130 
132  return true;
133 
134  // prevent movement while casting spells with cast time or channel time
135  if (owner.IsNonMeleeSpellCast(false, false, true) || owner.HasUnitState(UNIT_STATE_CASTING))
136  {
137  if (!owner.IsStopped())
138  owner.StopMoving();
139  return true;
140  }
141 
142  if (!owner.HasAuraType(SPELL_AURA_MOD_INVISIBILITY) && !owner.CanSeeOrDetect(i_target.getTarget(), true))
143  {
144  owner.AttackStop();
145  if (owner.GetOwner())
146  owner.GetMotionMaster()->MoveFollow(owner.GetOwner(), PET_FOLLOW_DIST, owner.GetFollowAngle());
147  else
148  owner.StopMoving();
149 
150  return true;
151  }
152 
153  // prevent crash after creature killed pet
154  if (!owner.HasUnitState(UNIT_STATE_FOLLOW) && owner.GetVictim() != i_target.getTarget())
155  return true;
156 
157  if (i_path && i_path->getPathType() & PATHFIND_NOPATH)
158  {
159  if (Creature* me = owner.ToCreature())
160  {
161  if (m_evadeTimer <= time_diff)
162  {
163  if (me->AI())
164  me->AI()->EnterEvadeMode();
165  }
166  else
167  m_evadeTimer -= time_diff;
168  }
169  return true;
170  }
171 
172  bool targetMoved = false;
173  i_recheckDistance.Update(time_diff);
174  if (i_recheckDistance.Passed())
175  {
176  i_recheckDistance.Reset(this->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE ? 50 : 100);
177  G3D::Vector3 dest = owner.movespline->FinalDestination();
178  targetMoved = RequiresNewPosition(owner, dest.x, dest.y, dest.z);
179  }
180 
181  if (m_speedChanged || targetMoved)
182  _setTargetLocation(owner, targetMoved);
183 
184  if (owner.movespline->Finalized())
185  {
186  if (i_angle == 0.f && !owner.HasInArc(0.01f, i_target.getTarget()))
187  owner.SetInFront(i_target.getTarget());
188 
189  if (!i_targetReached)
190  {
191  i_targetReached = true;
192  static_cast<D*>(this)->_reachTarget(owner);
193  }
194  }
195 
196  // Implemented for PetAI to handle resetting flags when pet owner reached
197  if (owner.movespline->Finalized())
198  MovementInform(owner);
199 
200  return true;
201 }
202 
203 template<class T, typename D>
204 bool TargetedMovementGeneratorMedium<T, D>::RequiresNewPosition(T& owner, float x, float y, float z) const
205 {
206  bool targetMoved;
207 
208  //More distance let have better performance, less distance let have more sensitive reaction at target move.
209  float dist = owner.GetCombatReach() + sWorld.getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
210 
211  // GetClosePoint() will always return a point on the ground, so we need to
212  // handle the difference in elevation when the creature is flying
213  if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
214  targetMoved = i_target->GetDistanceSqr(x, y, z) > dist * dist;
215  else
216  targetMoved = i_target->GetDistance2d(x, y) > dist;
217 
218  // then, if the target is in range, check also Line of Sight.
219  if (!targetMoved)
220  targetMoved = !i_target->IsWithinLOSInMap(&owner);
221 
222  return targetMoved;
223 }
224 
225 template<class T>
227 {
228  if (owner.IsWithinMeleeRange(this->i_target.getTarget()))
229  owner.Attack(this->i_target.getTarget(), true);
230 }
231 
232 template<>
234 {
235  owner.AddUnitState(UNIT_STATE_CHASE); // _MOVE set in _SetTargetLocation after required checks
236  _setTargetLocation(owner, true);
237  m_evadeTimer = urand(4000, 8000);
238 }
239 
240 template<>
242 {
243  owner.SetWalk(false); // Chase movement is running
244  owner.AddUnitState(UNIT_STATE_CHASE); // _MOVE set in _SetTargetLocation after required checks
245  _setTargetLocation(owner, true);
246  m_evadeTimer = urand(4000, 8000);
247 }
248 
249 template<class T>
251 {
252  owner.ClearUnitState(UNIT_STATE_CHASE);
253 }
254 
255 template<class T>
257 {
258  Initialize(owner);
259 }
260 
261 template<class T>
263 {
264 }
265 
266 template<>
268 {
269  return false;
270 }
271 
272 template<>
274 {
275  return i_target.isValid() && i_target->IsWalking() &&
276  i_target->GetDistance(owner) < (owner.GetCombatReach() + (sWorld.getRate(RATE_TARGET_POS_RECALCULATION_RANGE) * 2));
277 }
278 
279 template<>
281 {
282  // nothing to do for Player
283 }
284 
285 template<>
287 {
288  // pet only sync speed with owner
289  if (!((Creature&)u).HasUnitTypeMask(UNIT_MASK_MINION) || !i_target.isValid() || i_target->GetGUID() != u.GetCharmerOrOwnerGUID())
290  return;
291 
292  u.UpdateSpeed(MOVE_RUN, true);
293  u.UpdateSpeed(MOVE_WALK, true);
294  u.UpdateSpeed(MOVE_SWIM, true);
295  u.UpdateSpeed(MOVE_FLIGHT, true);
296 }
297 
298 template<>
300 {
301  owner.AddUnitState(UNIT_STATE_FOLLOW); // _MOVE set in _SetTargetLocation after required checks
302  _updateSpeed(owner);
303  _setTargetLocation(owner, true);
304  m_evadeTimer = urand(4000, 8000);
305 }
306 
307 template<>
309 {
310  owner.AddUnitState(UNIT_STATE_FOLLOW); // _MOVE set in _SetTargetLocation after required checks
311  _updateSpeed(owner);
312  _setTargetLocation(owner, true);
313  m_evadeTimer = urand(4000, 8000);
314 }
315 
316 template<class T>
318 {
319  owner.ClearUnitState(UNIT_STATE_FOLLOW);
320  _updateSpeed(owner);
321 }
322 
323 template<class T>
325 {
326  Initialize(owner);
327 }
328 
329 template<class T>
331 {
332 }
333 
334 template<>
336 {
337  // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
338  unit.AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
339 }
340 
341 //-----------------------------------------------//
342 template void TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::_setTargetLocation(Player&, bool);
343 template void TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::_setTargetLocation(Player&, bool);
354 
363 
bool SetWalk(bool enable) override
Definition: Creature.cpp:2514
bool EnableWalking(T &) const
void AddUnitState(uint32 f)
Definition: Unit.h:998
void UpdateSpeed(UnitMoveType mtype, bool forced)
Definition: Unit.cpp:9806
#define PET_FOLLOW_DIST
uint64 GetCharmerOrOwnerGUID() const
Definition: Unit.h:1496
void MovebyPath(const PointsArray &path, int32 pointId=0)
float GetCombatReach() const
Definition: Unit.h:941
bool RequiresNewPosition(T &owner, float x, float y, float z) const
CreatureAI * AI() const
Definition: Creature.h:548
void _setTargetLocation(T &, bool updateDestination)
void SetWalk(bool enable)
Definition: Unit.h:461
Creature * ToCreature()
Definition: Object.h:389
#define sWorld
Definition: World.h:860
#define CONTACT_DISTANCE
Definition: Object.h:34
ACE_UINT32 uint32
Definition: Define.h:71
Definition: Player.h:922
virtual void MovementInform(uint32, uint32)
Definition: CreatureAI.h:145
uint32 urand(uint32 min, uint32 max)
Definition: Util.cpp:71