TrinityCore
Loading...
Searching...
No Matches
UnitAI.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore 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 "UnitAI.h"
19#include "Containers.h"
20#include "Creature.h"
21#include "CreatureAIImpl.h"
22#include "MotionMaster.h"
23#include "Player.h"
24#include "Spell.h"
25#include "SpellAuraEffects.h"
26#include "SpellAuras.h"
27#include "SpellInfo.h"
28#include "SpellMgr.h"
29
31{
32 if (victim && me->Attack(victim, true))
33 me->GetMotionMaster()->MoveChase(victim);
34}
35
37{
38 if (!me->isDead())
39 Reset();
40}
41
42void UnitAI::OnCharmed(bool isNew)
43{
44 if (!isNew)
46}
47
48void UnitAI::AttackStartCaster(Unit* victim, float dist)
49{
50 if (victim && me->Attack(victim, false))
51 me->GetMotionMaster()->MoveChase(victim, dist);
52}
53
55{
57 return;
58
59 Unit* victim = me->GetVictim();
60
61 if (!me->IsWithinMeleeRange(victim))
62 return;
63
64 //Make sure our attack is ready and we aren't currently casting before checking distance
65 if (me->isAttackReady())
66 {
67 me->AttackerStateUpdate(victim);
69 }
70
72 {
75 }
76}
77
79{
81 return true;
82
83 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell))
84 {
85 if (me->IsWithinCombatRange(me->GetVictim(), spellInfo->GetMaxRange(false)))
86 {
87 me->CastSpell(me->GetVictim(), spell, false);
89 return true;
90 }
91 }
92
93 return false;
94}
95
96Unit* UnitAI::SelectTarget(SelectTargetMethod targetType, uint32 position, float dist, bool playerOnly, bool withTank, int32 aura)
97{
98 return SelectTarget(targetType, position, DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
99}
100
101void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 offset, float dist, bool playerOnly, bool withTank, int32 aura)
102{
103 SelectTargetList(targetList, num, targetType, offset, DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
104}
105
107{
108 Unit* target = nullptr;
109
110 switch (AISpellInfo[spellId].target)
111 {
112 default:
113 case AITARGET_SELF:
114 target = me;
115 break;
116 case AITARGET_VICTIM:
117 target = me->GetVictim();
118 break;
119 case AITARGET_ENEMY:
120 {
121 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId))
122 {
123 bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS);
124 target = SelectTarget(SelectTargetMethod::Random, 0, spellInfo->GetMaxRange(false), playerOnly);
125 }
126 break;
127 }
128 case AITARGET_ALLY:
129 target = me;
130 break;
131 case AITARGET_BUFF:
132 target = me;
133 break;
134 case AITARGET_DEBUFF:
135 {
136 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId))
137 {
138 bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS);
139 float range = spellInfo->GetMaxRange(false);
140
141 DefaultTargetSelector targetSelector(me, range, playerOnly, true, -(int32)spellId);
142 if (!(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM)
143 && targetSelector(me->GetVictim()))
144 target = me->GetVictim();
145 else
146 target = SelectTarget(SelectTargetMethod::Random, 0, targetSelector);
147 }
148 break;
149 }
150 }
151
152 if (target)
153 return me->CastSpell(target, spellId, false);
154
156}
157
159{
162
163 return me->CastSpell(victim, spellId, args);
164}
165
167{
168 if (Unit* victim = me->GetVictim())
169 return DoCast(victim, spellId, args);
170
172}
173
174float UnitAI::DoGetSpellMaxRange(uint32 spellId, bool positive)
175{
176 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
177 return spellInfo ? spellInfo->GetMaxRange(positive) : 0;
178}
179
180#define UPDATE_TARGET(a) {if (AIInfo->target<a) AIInfo->target=a;}
181
183{
184 AISpellInfo = new AISpellInfoType[sSpellMgr->GetSpellInfoStoreSize()];
185
187 for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i, ++AIInfo)
188 {
189 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i);
190 if (!spellInfo)
191 continue;
192
194 AIInfo->condition = AICOND_DIE;
195 else if (spellInfo->IsPassive() || spellInfo->GetDuration() == -1)
196 AIInfo->condition = AICOND_AGGRO;
197 else
198 AIInfo->condition = AICOND_COMBAT;
199
200 if (AIInfo->cooldown < spellInfo->RecoveryTime)
201 AIInfo->cooldown = spellInfo->RecoveryTime;
202
203 if (spellInfo->GetMaxRange(false))
204 {
205 for (SpellEffectInfo const& effect : spellInfo->GetEffects())
206 {
207 uint32 targetType = effect.TargetA.GetTarget();
208
209 if (targetType == TARGET_UNIT_TARGET_ENEMY
210 || targetType == TARGET_DEST_TARGET_ENEMY)
212 else if (targetType == TARGET_UNIT_DEST_AREA_ENEMY)
214
215 if (effect.Effect == SPELL_EFFECT_APPLY_AURA)
216 {
217 if (targetType == TARGET_UNIT_TARGET_ENEMY)
219 else if (spellInfo->IsPositive())
221 }
222 }
223 }
224 AIInfo->realCooldown = spellInfo->RecoveryTime + spellInfo->StartRecoveryTime;
225 AIInfo->maxRange = spellInfo->GetMaxRange(false) * 3 / 4;
226 }
227}
228
229Unit* UnitAI::FinalizeTargetSelection(std::list<Unit*>& targetList, SelectTargetMethod targetType)
230{
231 // maybe nothing fulfills the predicate
232 if (targetList.empty())
233 return nullptr;
234
235 switch (targetType)
236 {
241 return targetList.front();
244 default:
245 break;
246 }
247
248 return nullptr;
249}
250
251bool UnitAI::PrepareTargetListSelection(std::list<Unit*>& targetList, SelectTargetMethod targetType, uint32 offset)
252{
253 targetList.clear();
255 // shortcut: we're gonna ignore the first <offset> elements, and there's at most <offset> elements, so we ignore them all - nothing to do here
256 if (mgr.GetThreatListSize() <= offset)
257 return false;
258
259 if (targetType == SelectTargetMethod::MaxDistance || targetType == SelectTargetMethod::MinDistance)
260 {
261 for (ThreatReference const* ref : mgr.GetUnsortedThreatList())
262 {
263 if (ref->IsOffline())
264 continue;
265
266 targetList.push_back(ref->GetVictim());
267 }
268 }
269 else
270 {
271 Unit* currentVictim = mgr.GetCurrentVictim();
272 if (currentVictim)
273 targetList.push_back(currentVictim);
274
275 for (ThreatReference const* ref : mgr.GetSortedThreatList())
276 {
277 if (ref->IsOffline())
278 continue;
279
280 Unit* thisTarget = ref->GetVictim();
281 if (thisTarget != currentVictim)
282 targetList.push_back(thisTarget);
283 }
284 }
285
286 // shortcut: the list isn't gonna get any larger
287 if (targetList.size() <= offset)
288 {
289 targetList.clear();
290 return false;
291 }
292
293 // right now, list is unsorted for DISTANCE types - re-sort by SelectTargetMethod::MaxDistance
294 if (targetType == SelectTargetMethod::MaxDistance || targetType == SelectTargetMethod::MinDistance)
296
297 // now the list is MAX sorted, reverse for MIN types
298 if (targetType == SelectTargetMethod::MinThreat)
299 targetList.reverse();
300
301 // ignore the first <offset> elements
302 while (offset)
303 {
304 targetList.pop_front();
305 --offset;
306 }
307
308 return true;
309}
310
311void UnitAI::FinalizeTargetListSelection(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType)
312{
313 if (targetList.size() <= num)
314 return;
315
316 if (targetType == SelectTargetMethod::Random)
317 Trinity::Containers::RandomResize(targetList, num);
318 else
319 targetList.resize(num);
320}
321
322std::string UnitAI::GetDebugInfo() const
323{
324 std::stringstream sstr;
325 sstr << std::boolalpha
326 << "Me: " << (me ? me->GetDebugInfo() : "NULL");
327 return sstr.str();
328}
329
330DefaultTargetSelector::DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, bool withTank, int32 aura)
331 : _me(unit), _dist(dist), _playerOnly(playerOnly), _exception(!withTank ? unit->GetThreatManager().GetLastVictim() : nullptr), _aura(aura)
332{
333}
334
336{
337 if (!_me)
338 return false;
339
340 if (!target)
341 return false;
342
343 if (_exception && target == _exception)
344 return false;
345
346 if (_playerOnly && (target->GetTypeId() != TYPEID_PLAYER))
347 return false;
348
349 if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
350 return false;
351
352 if (_dist < 0.0f && _me->IsWithinCombatRange(target, -_dist))
353 return false;
354
355 if (_aura)
356 {
357 if (_aura > 0)
358 {
359 if (!target->HasAura(_aura))
360 return false;
361 }
362 else
363 {
364 if (target->HasAura(-_aura))
365 return false;
366 }
367 }
368
369 return true;
370}
371
373 _caster(caster), _spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(sSpellMgr->GetSpellInfo(spellId), caster))
374{
376}
377
378bool SpellTargetSelector::operator()(Unit const* target) const
379{
380 if (!target || _spellInfo->CheckTarget(_caster, target) != SPELL_CAST_OK)
381 return false;
382
383 // copypasta from Spell::CheckRange
384 float minRange = 0.0f;
385 float maxRange = 0.0f;
386 float rangeMod = 0.0f;
388 {
390 {
391 rangeMod = _caster->GetCombatReach() + 4.0f / 3.0f;
392 rangeMod += target->GetCombatReach();
393
394 rangeMod = std::max(rangeMod, NOMINAL_MELEE_RANGE);
395 }
396 else
397 {
398 float meleeRange = 0.0f;
400 {
401 meleeRange = _caster->GetCombatReach() + 4.0f / 3.0f;
402 meleeRange += target->GetCombatReach();
403
404 meleeRange = std::max(meleeRange, NOMINAL_MELEE_RANGE);
405 }
406
407 minRange = _caster->GetSpellMinRangeForTarget(target, _spellInfo) + meleeRange;
408 maxRange = _caster->GetSpellMaxRangeForTarget(target, _spellInfo);
409
410 rangeMod = _caster->GetCombatReach();
411 rangeMod += target->GetCombatReach();
412
413 if (minRange > 0.0f && !(_spellInfo->RangeEntry->Flags & SPELL_RANGE_RANGED))
414 minRange += rangeMod;
415 }
416
417 if (_caster->isMoving() && target->isMoving() && !_caster->IsWalking() && !target->IsWalking() &&
419 rangeMod += 8.0f / 3.0f;
420 }
421
422 maxRange += rangeMod;
423
424 minRange *= minRange;
425 maxRange *= maxRange;
426
427 if (target != _caster)
428 {
429 if (_caster->GetExactDistSq(target) > maxRange)
430 return false;
431
432 if (minRange > 0.0f && _caster->GetExactDistSq(target) < minRange)
433 return false;
434 }
435
436 return true;
437}
438
440{
441 if (!target)
442 return false;
443
444 if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
445 return false;
446
447 if (Unit* currentVictim = _source->GetThreatManager().GetCurrentVictim())
448 return target != currentVictim;
449
450 return target != _source->GetVictim();
451}
452
453bool PowerUsersSelector::operator()(Unit const* target) const
454{
455 if (!_me || !target)
456 return false;
457
458 if (target->GetPowerType() != _power)
459 return false;
460
461 if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
462 return false;
463
464 if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
465 return false;
466
467 if (_dist < 0.0f && _me->IsWithinCombatRange(target, -_dist))
468 return false;
469
470 return true;
471}
472
474{
475 if (!_me || !target)
476 return false;
477
478 if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
479 return false;
480
481 if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
482 return false;
483
484 if (_inLos && !_me->IsWithinLOSInMap(target))
485 return false;
486
487 return true;
488}
@ AITARGET_ALLY
@ AITARGET_BUFF
@ AITARGET_SELF
@ AITARGET_VICTIM
@ AITARGET_ENEMY
@ AITARGET_DEBUFF
@ AICOND_COMBAT
@ AICOND_AGGRO
@ AICOND_DIE
int32_t int32
Definition Define.h:129
uint32_t uint32
Definition Define.h:133
#define ASSERT
Definition Errors.h:68
#define NOMINAL_MELEE_RANGE
@ TYPEID_PLAYER
Definition ObjectGuid.h:39
@ SPELL_EFFECT_APPLY_AURA
@ TARGET_UNIT_DEST_AREA_ENEMY
@ TARGET_DEST_TARGET_ENEMY
@ TARGET_UNIT_TARGET_ENEMY
@ SPELL_ATTR3_ONLY_TARGET_PLAYERS
@ OFF_ATTACK
@ SPELL_ATTR0_CASTABLE_WHILE_DEAD
SpellCastResult
@ SPELL_FAILED_BAD_TARGETS
@ SPELL_CAST_OK
@ SPELL_FAILED_SPELL_IN_PROGRESS
@ TRIGGERED_IGNORE_CAST_IN_PROGRESS
Will ignore aura scaling.
@ AURA_INTERRUPT_FLAG_NOT_VICTIM
#define sSpellMgr
Definition SpellMgr.h:738
@ SPELL_RANGE_MELEE
Definition Spell.h:115
@ SPELL_RANGE_RANGED
Definition Spell.h:116
#define UPDATE_TARGET(a)
Definition UnitAI.cpp:180
SelectTargetMethod
Definition UnitAI.h:49
@ UNIT_STATE_CASTING
Definition Unit.h:235
void MoveChase(Unit *target, Optional< ChaseRange > dist={}, Optional< ChaseAngle > angle={})
TypeID GetTypeId() const
Definition Object.h:93
float GetMaxRange(bool positive=false, WorldObject *caster=nullptr, Spell *spell=nullptr) const
uint32 RecoveryTime
Definition SpellInfo.h:317
bool IsPassive() const
SpellRangeEntry const * RangeEntry
Definition SpellInfo.h:338
SpellCastResult CheckTarget(WorldObject const *caster, WorldObject const *target, bool implicit=true) const
bool HasAttribute(SpellAttr0 attribute) const
Definition SpellInfo.h:375
int32 GetDuration() const
uint32 StartRecoveryTime
Definition SpellInfo.h:320
bool IsPositive() const
std::array< SpellEffectInfo, MAX_SPELL_EFFECTS > const & GetEffects() const
Definition SpellInfo.h:482
Unit * GetCurrentVictim()
Trinity::IteratorPair< ThreatListIterator, std::nullptr_t > GetUnsortedThreatList() const
Trinity::IteratorPair< ThreatListIterator, std::nullptr_t > GetSortedThreatList() const
size_t GetThreatListSize() const
void AttackStartCaster(Unit *victim, float dist)
Definition UnitAI.cpp:48
static void FillAISpellInfo()
Definition UnitAI.cpp:182
bool PrepareTargetListSelection(std::list< Unit * > &targetList, SelectTargetMethod targetType, uint32 offset)
Definition UnitAI.cpp:251
void DoMeleeAttackIfReady()
Definition UnitAI.cpp:54
virtual void Reset()
Definition UnitAI.h:145
virtual void InitializeAI()
Definition UnitAI.cpp:36
SpellCastResult DoCastVictim(uint32 spellId, CastSpellExtraArgs const &args={})
Definition UnitAI.cpp:166
void SelectTargetList(std::list< Unit * > &targetList, uint32 num, SelectTargetMethod targetType, uint32 offset=0, float dist=0.0f, bool playerOnly=false, bool withTank=true, int32 aura=0)
Definition UnitAI.cpp:101
virtual void OnCharmed(bool isNew)
Definition UnitAI.cpp:42
float DoGetSpellMaxRange(uint32 spellId, bool positive=false)
Definition UnitAI.cpp:174
virtual std::string GetDebugInfo() const
Definition UnitAI.cpp:322
Unit * SelectTarget(SelectTargetMethod targetType, uint32 offset=0, float dist=0.0f, bool playerOnly=false, bool withTank=true, int32 aura=0)
Definition UnitAI.cpp:96
bool DoSpellAttackIfReady(uint32 spell)
Definition UnitAI.cpp:78
Unit *const me
Definition UnitAI.h:134
Unit * FinalizeTargetSelection(std::list< Unit * > &targetList, SelectTargetMethod targetType)
Definition UnitAI.cpp:229
static AISpellInfoType * AISpellInfo
Definition UnitAI.h:252
virtual void AttackStart(Unit *)
Definition UnitAI.cpp:30
void FinalizeTargetListSelection(std::list< Unit * > &targetList, uint32 num, SelectTargetMethod targetType)
Definition UnitAI.cpp:311
SpellCastResult DoCast(uint32 spellId)
Definition UnitAI.cpp:106
Definition Unit.h:769
bool IsWithinMeleeRange(Unit const *obj) const
Definition Unit.h:844
ThreatManager & GetThreatManager()
Definition Unit.h:1155
bool IsWithinCombatRange(Unit const *obj, float dist2compare) const
Definition Unit.cpp:603
bool haveOffhandWeapon() const
Definition Unit.cpp:510
MotionMaster * GetMotionMaster()
Definition Unit.h:1667
Powers GetPowerType() const
Definition Unit.h:931
std::string GetDebugInfo() const override
Definition Unit.cpp:13950
float GetCombatReach() const override
Definition Unit.h:839
bool HasAura(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, ObjectGuid itemCasterGUID=ObjectGuid::Empty, uint8 reqEffMask=0) const
Definition Unit.cpp:4535
bool Attack(Unit *victim, bool meleeAttack)
Definition Unit.cpp:5535
bool isMoving() const
Definition Unit.h:1759
Unit * GetVictim() const
Definition Unit.h:859
bool HasUnitState(const uint32 f) const
Definition Unit.h:876
void AttackerStateUpdate(Unit *victim, WeaponAttackType attType=BASE_ATTACK, bool extra=false)
Definition Unit.cpp:2071
void ScheduleAIChange()
Definition Unit.cpp:9555
bool isAttackReady(WeaponAttackType type=BASE_ATTACK) const
Definition Unit.h:835
void resetAttackTimer(WeaponAttackType type=BASE_ATTACK)
Definition Unit.cpp:598
bool IsWalking() const
Definition Unit.h:1219
bool isDead() const
Definition Unit.h:1236
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition Object.cpp:2832
bool IsWithinLOSInMap(WorldObject const *obj, LineOfSightChecks checks=LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags ignoreFlags=VMAP::ModelIgnoreFlags::Nothing) const
Definition Object.cpp:1226
float GetSpellMinRangeForTarget(Unit const *target, SpellInfo const *spellInfo) const
Definition Object.cpp:2267
float GetSpellMaxRangeForTarget(Unit const *target, SpellInfo const *spellInfo) const
Definition Object.cpp:2253
auto SelectRandomContainerElement(C const &container) -> typename std::add_const< decltype(*std::begin(container))>::type &
Definition Containers.h:108
void RandomResize(C &container, std::size_t requestedSize)
Definition Containers.h:66
AICondition condition
TriggerCastFlags TriggerFlags
DefaultTargetSelector(Unit const *unit, float dist, bool playerOnly, bool withMainTank, int32 aura)
Definition UnitAI.cpp:330
Unit const * _me
Definition UnitAI.h:70
bool operator()(Unit const *target) const
Definition UnitAI.cpp:335
Unit const * _exception
Definition UnitAI.h:73
Unit const * _me
Definition UnitAI.h:125
bool operator()(Unit const *target) const
Definition UnitAI.cpp:473
bool operator()(Unit const *target) const
Definition UnitAI.cpp:439
float GetExactDistSq(float x, float y, float z) const
Definition Position.h:113
float const _dist
Definition UnitAI.h:114
Unit const * _me
Definition UnitAI.h:112
Powers const _power
Definition UnitAI.h:113
bool operator()(Unit const *target) const
Definition UnitAI.cpp:453
bool const _playerOnly
Definition UnitAI.h:115
SpellInfo const * _spellInfo
Definition UnitAI.h:87
bool operator()(Unit const *target) const
Definition UnitAI.cpp:378
SpellTargetSelector(Unit *caster, uint32 spellId)
Definition UnitAI.cpp:372
Unit const * _caster
Definition UnitAI.h:86