TrinityCore
Unit.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 "Unit.h"
19#include "AbstractFollower.h"
20#include "Battlefield.h"
21#include "BattlefieldMgr.h"
22#include "Battleground.h"
23#include "BattlegroundPackets.h"
24#include "BattlegroundScore.h"
25#include "BattlePetMgr.h"
26#include "CellImpl.h"
27#include "CharacterCache.h"
28#include "CharmInfo.h"
29#include "ChatPackets.h"
30#include "ChatTextBuilder.h"
31#include "CombatLogPackets.h"
32#include "CombatPackets.h"
33#include "Common.h"
34#include "ConditionMgr.h"
35#include "Containers.h"
36#include "Creature.h"
37#include "CreatureAI.h"
38#include "CreatureAIImpl.h"
39#include "CreatureAIFactory.h"
40#include "CreatureGroups.h"
41#include "DB2Stores.h"
42#include "Formulas.h"
43#include "GameObjectAI.h"
44#include "GameTime.h"
45#include "GridNotifiersImpl.h"
46#include "Group.h"
47#include "InstanceScript.h"
48#include "Item.h"
49#include "ItemBonusMgr.h"
50#include "KillRewarder.h"
51#include "ListUtils.h"
52#include "Log.h"
53#include "Loot.h"
54#include "LootMgr.h"
55#include "LootPackets.h"
56#include "MiscPackets.h"
57#include "MotionMaster.h"
58#include "MovementGenerator.h"
59#include "MovementPackets.h"
60#include "MoveSpline.h"
61#include "MoveSplineInit.h"
62#include "ObjectAccessor.h"
63#include "ObjectMgr.h"
64#include "Opcodes.h"
65#include "OutdoorPvP.h"
66#include "PartyPackets.h"
67#include "Pet.h"
68#include "PetPackets.h"
69#include "PhasingHandler.h"
70#include "Player.h"
71#include "PlayerAI.h"
72#include "QuestDef.h"
73#include "Spell.h"
74#include "ScheduledChangeAI.h"
75#include "SpellAuraEffects.h"
76#include "SpellAuras.h"
77#include "SpellHistory.h"
78#include "SpellInfo.h"
79#include "SpellMgr.h"
80#include "SpellPackets.h"
81#include "StringConvert.h"
82#include "TemporarySummon.h"
83#include "Totem.h"
84#include "Transport.h"
85#include "Util.h"
86#include "Vehicle.h"
87#include "VehiclePackets.h"
88#include "Vignette.h"
89#include "VignettePackets.h"
90#include "World.h"
91#include "WorldPacket.h"
92#include "WorldSession.h"
93#include <queue>
94#include <sstream>
95#include <cmath>
96
98{
99 2.5f, // MOVE_WALK
100 7.0f, // MOVE_RUN
101 4.5f, // MOVE_RUN_BACK
102 4.722222f, // MOVE_SWIM
103 2.5f, // MOVE_SWIM_BACK
104 3.141594f, // MOVE_TURN_RATE
105 7.0f, // MOVE_FLIGHT
106 4.5f, // MOVE_FLIGHT_BACK
107 3.14f // MOVE_PITCH_RATE
108};
109
111{
112 2.5f, // MOVE_WALK
113 7.0f, // MOVE_RUN
114 4.5f, // MOVE_RUN_BACK
115 4.722222f, // MOVE_SWIM
116 2.5f, // MOVE_SWIM_BACK
117 3.141594f, // MOVE_TURN_RATE
118 7.0f, // MOVE_FLIGHT
119 4.5f, // MOVE_FLIGHT_BACK
120 3.14f // MOVE_PITCH_RATE
121};
122
123DispelableAura::DispelableAura(Aura* aura, int32 dispelChance, uint8 dispelCharges) :
124 _aura(aura), _chance(dispelChance), _charges(dispelCharges)
125{
126}
127
129
131{
132 return roll_chance_i(_chance);
133}
134
135DamageInfo::DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType)
136 : m_attacker(attacker), m_victim(victim), m_damage(damage), m_originalDamage(damage), m_spellInfo(spellInfo), m_schoolMask(schoolMask), m_damageType(damageType), m_attackType(attackType),
137 m_absorb(0), m_resist(0), m_block(0), m_hitMask(PROC_HIT_NONE)
138{
139}
140
142 : m_attacker(dmgInfo.Attacker), m_victim(dmgInfo.Target), m_damage(dmgInfo.Damage), m_originalDamage(dmgInfo.Damage), m_spellInfo(nullptr), m_schoolMask(SpellSchoolMask(dmgInfo.DamageSchoolMask)),
143 m_damageType(DIRECT_DAMAGE), m_attackType(dmgInfo.AttackType), m_absorb(dmgInfo.Absorb), m_resist(dmgInfo.Resist), m_block(dmgInfo.Blocked), m_hitMask(PROC_HIT_NONE)
144{
145 switch (dmgInfo.TargetState)
146 {
149 break;
152 break;
153 }
154
157
158 if (dmgInfo.HitInfo & HITINFO_FULL_RESIST)
160
161 if (m_block)
163
164 bool const damageNullified = (dmgInfo.HitInfo & (HITINFO_FULL_ABSORB | HITINFO_FULL_RESIST)) != 0 ||
166 switch (dmgInfo.HitOutCome)
167 {
168 case MELEE_HIT_MISS:
170 break;
171 case MELEE_HIT_DODGE:
173 break;
174 case MELEE_HIT_PARRY:
176 break;
177 case MELEE_HIT_EVADE:
179 break;
180 case MELEE_HIT_BLOCK:
183 case MELEE_HIT_NORMAL:
184 if (!damageNullified)
186 break;
187 case MELEE_HIT_CRIT:
188 if (!damageNullified)
190 break;
191 }
192}
193
194DamageInfo::DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, ProcFlagsHit hitMask)
195 : m_attacker(spellNonMeleeDamage.attacker), m_victim(spellNonMeleeDamage.target), m_damage(spellNonMeleeDamage.damage), m_originalDamage(spellNonMeleeDamage.originalDamage),
196 m_spellInfo(spellNonMeleeDamage.Spell), m_schoolMask(SpellSchoolMask(spellNonMeleeDamage.schoolMask)), m_damageType(damageType),
197 m_attackType(attackType), m_absorb(spellNonMeleeDamage.absorb), m_resist(spellNonMeleeDamage.resist), m_block(spellNonMeleeDamage.blocked), m_hitMask(hitMask)
198{
199 if (spellNonMeleeDamage.blocked)
201 if (spellNonMeleeDamage.absorb)
203}
204
206{
207 amount = std::max(amount, -static_cast<int32>(GetDamage()));
208 m_damage += amount;
209}
210
212{
213 amount = std::min(amount, GetDamage());
214 m_absorb += amount;
215 m_damage -= amount;
217}
218
220{
221 amount = std::min(amount, GetDamage());
222 m_resist += amount;
223 m_damage -= amount;
224 if (!m_damage)
225 {
228 }
229}
230
232{
233 amount = std::min(amount, GetDamage());
234 m_block += amount;
235 m_damage -= amount;
237 if (!m_damage)
238 {
241 }
242}
243
245{
246 return m_hitMask;
247}
248
249HealInfo::HealInfo(Unit* healer, Unit* target, uint32 heal, SpellInfo const* spellInfo, SpellSchoolMask schoolMask)
250 : _healer(healer), _target(target), _heal(heal), _originalHeal(heal), _effectiveHeal(0), _absorb(0), _spellInfo(spellInfo), _schoolMask(schoolMask), _hitMask(0)
251{
252}
253
255{
256 amount = std::min(amount, GetHeal());
257 _absorb += amount;
258 _heal -= amount;
259 amount = std::min(amount, GetEffectiveHeal());
260 _effectiveHeal -= amount;
262}
263
265{
266 return _hitMask;
267}
268
269ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget,
270 ProcFlagsInit const& typeMask, ProcFlagsSpellType spellTypeMask,
271 ProcFlagsSpellPhase spellPhaseMask, ProcFlagsHit hitMask,
272 Spell* spell, DamageInfo* damageInfo,
273 HealInfo* healInfo) :
274 _actor(actor), _actionTarget(actionTarget), _procTarget(procTarget),
275 _typeMask(typeMask), _spellTypeMask(spellTypeMask),
276 _spellPhaseMask(spellPhaseMask), _hitMask(hitMask), _spell(spell),
277 _damageInfo(damageInfo), _healInfo(healInfo)
278{ }
279
281{
282 if (_spell)
283 return _spell->GetSpellInfo();
284 if (_damageInfo)
285 return _damageInfo->GetSpellInfo();
286 if (_healInfo)
287 return _healInfo->GetSpellInfo();
288 return nullptr;
289}
290
292{
293 if (_spell)
294 return _spell->GetSpellInfo()->GetSchoolMask();
295 if (_damageInfo)
296 return _damageInfo->GetSchoolMask();
297 if (_healInfo)
298 return _healInfo->GetSchoolMask();
300}
301
302SpellNonMeleeDamage::SpellNonMeleeDamage(Unit* _attacker, Unit* _target, SpellInfo const* _spellInfo, SpellCastVisual spellVisual, uint32 _schoolMask, ObjectGuid _castId)
303 : target(_target), attacker(_attacker), castId(_castId), Spell(_spellInfo), SpellVisual(spellVisual), damage(0), originalDamage(0),
304 schoolMask(_schoolMask), absorb(0), resist(0), periodicLog(false), blocked(0), HitInfo(0), cleanDamage(0), fullBlock(false), preHitHealth(_target->GetHealth())
305{
306}
307
308Unit::Unit(bool isWorldObject) :
309 WorldObject(isWorldObject), m_lastSanctuaryTime(0), LastCharmerGUID(), movespline(std::make_unique<Movement::MoveSpline>()),
310 m_ControlledByPlayer(false), m_procDeep(0), m_procChainLength(0), m_transformSpell(0),
311 m_removedAurasCount(0), m_interruptMask(SpellAuraInterruptFlags::None), m_interruptMask2(SpellAuraInterruptFlags2::None),
312 m_unitMovedByMe(nullptr), m_playerMovingMe(nullptr), m_charmer(nullptr), m_charmed(nullptr),
313 i_motionMaster(std::make_unique<MotionMaster>(this)), m_regenTimer(0), m_vehicle(nullptr),
314 m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_combatManager(this),
315 m_threatManager(this), m_aiLocked(false), _playHoverAnim(false), _aiAnimKitId(0), _movementAnimKitId(0), _meleeAnimKitId(0),
316 _spellHistory(std::make_unique<SpellHistory>(this))
317{
320
322
323 m_baseAttackSpeed = { };
324 m_attackTimer = { };
325 m_modAttackSpeedPct.fill(1.0f);
326
327 m_canDualWield = false;
328
330
331 m_state = 0;
333
334 m_currentSpells = { };
335
337
338 m_canModifyStats = false;
339
340 for (uint8 i = 0; i < UNIT_MOD_END; ++i)
341 {
347 }
348 // implement 50% base damage from offhand
350
351 for (uint8 i = 0; i < MAX_ATTACK; ++i)
352 {
355 }
356
357 m_createStats = { };
358 m_floatStatPosBuff = { };
359 m_floatStatNegBuff = { };
360
361 m_attacking = nullptr;
362 m_modMeleeHitChance = 0.0f;
364 m_modSpellHitChance = 0.0f;
366
367 m_speed_rate.fill(1.0f);
368
369 // remove aurastates allowing special moves
370 m_reactiveTimer = { };
371
372 m_cleanupDone = false;
374
376
377 _lastLiquid = nullptr;
378
379 _oldFactionId = 0;
380 _isWalkingBeforeCharm = false;
381 _instantCast = false;
382 _isCombatDisallowed = false;
383
385}
386
388// Methods of class Unit
390{
391 // set current spells as deletable
392 for (size_t i = 0; i < m_currentSpells.size(); ++i)
393 {
394 if (m_currentSpells[i])
395 {
396 m_currentSpells[i]->SetReferencedFromCurrent(false);
397 m_currentSpells[i] = nullptr;
398 }
399 }
400
402
404
407 ASSERT(m_attackers.empty());
408 ASSERT(m_sharedVision.empty());
409 ASSERT(m_Controlled.empty());
410 ASSERT(m_appliedAuras.empty());
411 ASSERT(m_ownedAuras.empty());
412 ASSERT(m_removedAuras.empty());
413 ASSERT(m_dynObj.empty());
414 ASSERT(m_gameObj.empty());
415 ASSERT(m_areaTrigger.empty());
418}
419
421{
422 // WARNING! Order of execution here is important, do not change.
423 // Spells must be processed with event system BEFORE they go to _UpdateSpells.
424 // Or else we may have some SPELL_STATE_FINISHED spells stalled in pointers, that is bad.
425 WorldObject::Update(p_time);
426
427 if (!IsInWorld())
428 return;
429
430 _UpdateSpells(p_time);
431
432 // If this is set during update SetCantProc(false) call is missing somewhere in the code
433 // Having this would prevent spells from being proced, so let's crash
435
436 m_combatManager.Update(p_time);
437
440 {
441 while (!extraAttacksTargets.empty())
442 {
443 auto itr = extraAttacksTargets.begin();
444 ObjectGuid targetGuid = itr->first;
445 uint32 count = itr->second;
446 extraAttacksTargets.erase(itr);
447 if (Unit* victim = ObjectAccessor::GetUnit(*this, targetGuid))
448 HandleProcExtraAttackFor(victim, count);
449 }
451 }
452
453 auto spellPausesCombatTimer = [&](CurrentSpellTypes type)
454 {
456 };
457
458 if (!spellPausesCombatTimer(CURRENT_GENERIC_SPELL) && !spellPausesCombatTimer(CURRENT_CHANNELED_SPELL))
459 {
460 if (uint32 base_att = getAttackTimer(BASE_ATTACK))
461 setAttackTimer(BASE_ATTACK, (p_time >= base_att ? 0 : base_att - p_time));
462 if (uint32 off_att = getAttackTimer(OFF_ATTACK))
463 setAttackTimer(OFF_ATTACK, (p_time >= off_att ? 0 : off_att - p_time));
464 if (uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
465 setAttackTimer(RANGED_ATTACK, (p_time >= ranged_att ? 0 : ranged_att - p_time));
466 }
467
468 // update abilities available only for fraction of time
469 UpdateReactives(p_time);
470
471 if (IsAlive())
472 {
479 }
480
481 UpdateSplineMovement(p_time);
482 i_motionMaster->Update(p_time);
483
484 // Wait with the aura interrupts until we have updated our movement generators and position
485 if (GetTypeId() == TYPEID_PLAYER)
487 else if (!movespline->Finalized())
489
490 // All position info based actions have been executed, reset info
492
495 RefreshAI();
496}
497
499{
500 if (Player const* player = ToPlayer())
501 return player->GetWeaponForAttack(OFF_ATTACK, true) != nullptr;
502
503 return CanDualWield();
504}
505
506void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath, bool forceDestination)
507{
508 std::function<void(Movement::MoveSplineInit&)> initializer = [=](Movement::MoveSplineInit& init)
509 {
510 init.MoveTo(x, y, z, generatePath, forceDestination);
511 init.SetVelocity(speed);
512 };
514}
515
517{
519
520 switch (type)
521 {
524 break;
527 break;
528 default:
529 break;
530 }
531
532 if (IsAlive())
534}
535
537{
539
540 switch (type)
541 {
544 break;
545 default:
546 break;
547 }
548
549 GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr)
550 {
551 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first, DIFFICULTY_NONE);
552
554 }, true);
555}
556
558{
559 if (movespline->Finalized())
560 return;
561
562 movespline->updateState(t_diff);
563 bool arrived = movespline->Finalized();
564
565 if (movespline->isCyclic())
566 {
569 {
570 m_splineSyncTimer.Reset(5000); // Retail value, do not change
571
573 flightSplineSync.Guid = GetGUID();
574 flightSplineSync.SplineDist = movespline->timePassed() / movespline->Duration();
575 SendMessageToSet(flightSplineSync.Write(), true);
576 }
577 }
578
579 if (arrived)
580 {
582
583 if (Optional<AnimTier> animTier = movespline->GetAnimation())
584 SetAnimTier(*animTier);
585 }
586
588}
589
591{
592 Movement::Location loc = movespline->ComputePosition();
593
594 if (movespline->onTransport)
595 {
597 pos.m_positionX = loc.x;
598 pos.m_positionY = loc.y;
599 pos.m_positionZ = loc.z;
601
602 if (TransportBase* transport = GetDirectTransport())
603 transport->CalculatePassengerPosition(loc.x, loc.y, loc.z, &loc.orientation);
604 else
605 return;
606 }
607
610
611 UpdatePosition(loc.x, loc.y, loc.z, loc.orientation);
612}
613
615{
616 // TODO: Check if orientation transport offset changed instead of only global orientation
619
622}
623
625{
627 movespline->_Interrupt();
628}
629
631{
633}
634
635bool Unit::IsWithinCombatRange(Unit const* obj, float dist2compare) const
636{
637 if (!obj || !IsInMap(obj) || !InSamePhase(obj))
638 return false;
639
640 float dx = GetPositionX() - obj->GetPositionX();
641 float dy = GetPositionY() - obj->GetPositionY();
642 float dz = GetPositionZ() - obj->GetPositionZ();
643 float distsq = dx * dx + dy * dy + dz * dz;
644
645 float sizefactor = GetCombatReach() + obj->GetCombatReach();
646 float maxdist = dist2compare + sizefactor;
647
648 return distsq < maxdist * maxdist;
649}
650
651bool Unit::IsWithinMeleeRangeAt(Position const& pos, Unit const* obj) const
652{
653 if (!obj || !IsInMap(obj) || !InSamePhase(obj))
654 return false;
655
656 float dx = pos.GetPositionX() - obj->GetPositionX();
657 float dy = pos.GetPositionY() - obj->GetPositionY();
658 float dz = pos.GetPositionZ() - obj->GetPositionZ();
659 float distsq = dx*dx + dy*dy + dz*dz;
660
662
663 return distsq <= maxdist * maxdist;
664}
665
666float Unit::GetMeleeRange(Unit const* target) const
667{
668 float range = GetCombatReach() + target->GetCombatReach() + 4.0f / 3.0f;
669 return std::max(range, NOMINAL_MELEE_RANGE);
670}
671
672bool Unit::IsWithinBoundaryRadius(const Unit* obj) const
673{
674 if (!obj || !IsInMap(obj) || !InSamePhase(obj))
675 return false;
676
677 float objBoundaryRadius = std::max(obj->GetBoundingRadius(), MIN_MELEE_REACH);
678
679 return IsInDist(obj, objBoundaryRadius);
680}
681
683{
684 m_visibleAuras.insert(aurApp);
685 m_visibleAurasToUpdate.insert(aurApp);
687}
688
690{
691 m_visibleAuras.erase(aurApp);
692 m_visibleAurasToUpdate.erase(aurApp);
694}
695
697{
700 for (AuraApplication const* aurApp : m_interruptableAuras)
701 {
702 m_interruptMask |= aurApp->GetBase()->GetSpellInfo()->AuraInterruptFlags;
703 m_interruptMask2 |= aurApp->GetBase()->GetSpellInfo()->AuraInterruptFlags2;
704 }
705
707 {
708 if (spell->getState() == SPELL_STATE_CASTING)
709 {
710 m_interruptMask |= spell->m_spellInfo->ChannelInterruptFlags;
711 m_interruptMask2 |= spell->m_spellInfo->ChannelInterruptFlags2;
712 }
713 }
714}
715
716bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, flag128 familyFlags) const
717{
718 for (AuraEffect const* aura : GetAuraEffectsByType(auraType))
719 if (aura->GetSpellInfo()->SpellFamilyName == familyName && aura->GetSpellInfo()->SpellFamilyFlags & familyFlags)
720 return true;
721 return false;
722}
723
725{
726 AuraEffectList const& auras = GetAuraEffectsByType(type);
727 for (AuraEffectList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
728 if ((!excludeAura || excludeAura != (*itr)->GetSpellInfo()->Id) && //Avoid self interrupt of channeled Crowd Control spells like Seduction
729 (*itr)->GetSpellInfo()->HasAuraInterruptFlag(SpellAuraInterruptFlags::Damage))
730 return true;
731 return false;
732}
733
734bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) const
735{
736 uint32 excludeAura = 0;
737 if (Spell* currentChanneledSpell = excludeCasterChannel ? excludeCasterChannel->GetCurrentSpell(CURRENT_CHANNELED_SPELL) : nullptr)
738 excludeAura = currentChanneledSpell->GetSpellInfo()->Id; //Avoid self interrupt of channeled Crowd Control spells like Seduction
739
746}
747
748/*static*/ void Unit::DealDamageMods(Unit const* attacker, Unit const* victim, uint32& damage, uint32* absorb)
749{
750 if (!victim || !victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()))
751 {
752 if (absorb)
753 *absorb += damage;
754 damage = 0;
755 return;
756 }
757
758 if (attacker)
759 damage *= attacker->GetDamageMultiplierForTarget(victim);
760}
761
763{
764 AuraEffectVector effects;
765 std::copy(list.begin(), list.end(), std::back_inserter(effects));
766 return effects;
767}
768
769/*static*/ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss)
770{
771 uint32 damageDone = damage;
772 uint32 damageTaken = damage;
773 if (attacker)
774 damageTaken = damage / victim->GetHealthMultiplierForTarget(attacker);
775
776 // call script hooks
777 {
778 uint32 tmpDamage = damageTaken;
779
780 // sparring
781 if (Creature* victimCreature = victim->ToCreature())
782 tmpDamage = victimCreature->CalculateDamageForSparring(attacker, tmpDamage);
783
784 if (UnitAI* victimAI = victim->GetAI())
785 victimAI->DamageTaken(attacker, tmpDamage, damagetype, spellProto);
786
787 if (UnitAI* attackerAI = attacker ? attacker->GetAI() : nullptr)
788 attackerAI->DamageDealt(victim, tmpDamage, damagetype);
789
790 // Hook for OnDamage Event
791 sScriptMgr->OnDamage(attacker, victim, tmpDamage);
792
793 // if any script modified damage, we need to also apply the same modification to unscaled damage value
794 if (tmpDamage != damageTaken)
795 {
796 if (attacker)
797 damageDone = tmpDamage * victim->GetHealthMultiplierForTarget(attacker);
798 else
799 damageDone = tmpDamage;
800
801 damageTaken = tmpDamage;
802 }
803 }
804
805 // Signal to pets that their owner was attacked - except when DOT.
806 if (attacker != victim && damagetype != DOT)
807 {
808 for (Unit* controlled : victim->m_Controlled)
809 if (Creature* cControlled = controlled->ToCreature())
810 if (CreatureAI* controlledAI = cControlled->AI())
811 controlledAI->OwnerAttackedBy(attacker);
812 }
813
814 if (Player* player = victim->ToPlayer())
815 if (player->GetCommandStatus(CHEAT_GOD))
816 return 0;
817
818 if (damagetype != NODAMAGE)
819 {
820 // interrupting auras with SpellAuraInterruptFlags::Damage before checking !damage (absorbed damage breaks that type of auras)
821 if (spellProto)
822 {
825 }
826 else
828
829 if (!damageTaken && damagetype != DOT && cleanDamage && cleanDamage->absorbed_damage)
830 if (victim != attacker && victim->GetTypeId() == TYPEID_PLAYER)
831 if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL])
832 if (spell->getState() == SPELL_STATE_PREPARING && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageAbsorb))
833 victim->InterruptNonMeleeSpells(false);
834
835 // We're going to call functions which can modify content of the list during iteration over it's elements
836 // Let's copy the list so we can prevent iterator invalidation
838 // copy damage to casters of this aura
839 for (auto i = vCopyDamageCopy.begin(); i != vCopyDamageCopy.end(); ++i)
840 {
841 // Check if aura was removed during iteration - we don't need to work on such auras
842 if (!((*i)->GetBase()->IsAppliedOnTarget(victim->GetGUID())))
843 continue;
844 // check damage school mask
845 if (((*i)->GetMiscValue() & damageSchoolMask) == 0)
846 continue;
847
848 Unit* shareDamageTarget = (*i)->GetCaster();
849 if (!shareDamageTarget)
850 continue;
851 SpellInfo const* spell = (*i)->GetSpellInfo();
852
853 uint32 share = CalculatePct(damageDone, (*i)->GetAmount());
854
856 Unit::DealDamageMods(attacker, shareDamageTarget, share, nullptr);
857 Unit::DealDamage(attacker, shareDamageTarget, share, nullptr, NODAMAGE, spell->GetSchoolMask(), spell, false);
858 }
859 }
860
861 // Rage from Damage made (only from direct weapon damage)
862 if (attacker && cleanDamage && (cleanDamage->attackType == BASE_ATTACK || cleanDamage->attackType == OFF_ATTACK) && damagetype == DIRECT_DAMAGE && attacker != victim && attacker->GetPowerType() == POWER_RAGE)
863 {
864 uint32 rage = uint32(attacker->GetBaseAttackTime(cleanDamage->attackType) / 1000.f * 1.75f);
865 if (cleanDamage->attackType == OFF_ATTACK)
866 rage /= 2;
867 attacker->RewardRage(rage);
868 }
869
870 if (!damageDone)
871 return 0;
872
873 uint32 health = victim->GetHealth();
874
875 // duel ends when player has 1 or less hp
876 bool duel_hasEnded = false;
877 bool duel_wasMounted = false;
878 if (victim->GetTypeId() == TYPEID_PLAYER && victim->ToPlayer()->duel && damageTaken >= (health-1))
879 {
880 if (!attacker)
881 return 0;
882
883 // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
884 if (victim->ToPlayer()->duel->Opponent == attacker->GetControllingPlayer())
885 damageTaken = health - 1;
886
887 duel_hasEnded = true;
888 }
889 else if (victim->IsCreature() && victim != attacker && damageTaken >= health && victim->ToCreature()->HasFlag(CREATURE_STATIC_FLAG_UNKILLABLE))
890 {
891 damageTaken = health - 1;
892
893 // If we had damage (aka health was not 1 already) trigger OnHealthDepleted
894 if (damageTaken > 0)
895 {
896 if (CreatureAI* victimAI = victim->ToCreature()->AI())
897 victimAI->OnHealthDepleted(attacker, false);
898 }
899 }
900 else if (victim->IsVehicle() && damageTaken >= (health-1) && victim->GetCharmer() && victim->GetCharmer()->GetTypeId() == TYPEID_PLAYER)
901 {
902 Player* victimRider = victim->GetCharmer()->ToPlayer();
903
904 if (victimRider && victimRider->duel && victimRider->duel->IsMounted)
905 {
906 if (!attacker)
907 return 0;
908
909 // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
910 if (victimRider->duel->Opponent == attacker->GetControllingPlayer())
911 damageTaken = health - 1;
912
913 duel_wasMounted = true;
914 duel_hasEnded = true;
915 }
916 }
917
918 if (attacker && attacker != victim)
919 {
920 if (Player* killer = attacker->ToPlayer())
921 {
922 // in bg, count dmg if victim is also a player
923 if (victim->GetTypeId() == TYPEID_PLAYER && !(spellProto && spellProto->HasAttribute(SPELL_ATTR7_DO_NOT_COUNT_FOR_PVP_SCOREBOARD)))
924 if (Battleground* bg = killer->GetBattleground())
925 bg->UpdatePlayerScore(killer, SCORE_DAMAGE_DONE, damageDone);
926
927 killer->UpdateCriteria(CriteriaType::DamageDealt, health > damageDone ? damageDone : health, 0, 0, victim);
928 killer->UpdateCriteria(CriteriaType::HighestDamageDone, damageDone);
929 }
930 }
931
932 if (victim->GetTypeId() == TYPEID_PLAYER)
934
935 if (victim->GetTypeId() != TYPEID_PLAYER && (!victim->IsControlledByPlayer() || victim->IsVehicle()))
936 {
937 victim->ToCreature()->SetTappedBy(attacker);
938
939 if (!attacker || attacker->IsControlledByPlayer())
940 victim->ToCreature()->LowerPlayerDamageReq(health < damageTaken ? health : damageTaken);
941 }
942
943 bool killed = false;
944 bool skipSettingDeathState = false;
945
946 if (health <= damageTaken)
947 {
948 killed = true;
949
950 if (victim->GetTypeId() == TYPEID_PLAYER && victim != attacker)
952
953 if (damagetype != NODAMAGE && damagetype != SELF_DAMAGE && victim->HasAuraType(SPELL_AURA_SCHOOL_ABSORB_OVERKILL))
954 {
956 DamageInfo damageInfo = DamageInfo(attacker, victim, damageTaken, spellProto, damageSchoolMask, damagetype,
957 cleanDamage ? cleanDamage->attackType : BASE_ATTACK);
958 for (AuraEffect* absorbAurEff : vAbsorbOverkill)
959 {
960 Aura* base = absorbAurEff->GetBase();
961 AuraApplication const* aurApp = base->GetApplicationOfTarget(victim->GetGUID());
962 if (!aurApp)
963 continue;
964
965 if (!(absorbAurEff->GetMiscValue() & damageInfo.GetSchoolMask()))
966 continue;
967
968 // cannot absorb over limit
969 if (damageTaken >= victim->CountPctFromMaxHealth(100 + absorbAurEff->GetMiscValueB()))
970 continue;
971
972 // absorb all damage by default
973 uint32 currentAbsorb = damageInfo.GetDamage();
974
975 // This aura type is used both by Spirit of Redemption (death not really prevented, must grant all credit immediately) and Cheat Death (death prevented)
976 // repurpose PreventDefaultAction for this
977 bool deathFullyPrevented = false;
978
979 absorbAurEff->GetBase()->CallScriptEffectAbsorbHandlers(absorbAurEff, aurApp, damageInfo, currentAbsorb, deathFullyPrevented);
980
981 // absorb must be smaller than the damage itself
982 currentAbsorb = std::min(currentAbsorb, damageInfo.GetDamage());
983
984 // if nothing is absorbed (for example because of a scripted cooldown) then skip this aura and proceed with dying
985 if (!currentAbsorb)
986 continue;
987
988 damageInfo.AbsorbDamage(currentAbsorb);
989
990 if (deathFullyPrevented)
991 killed = false;
992
993 skipSettingDeathState = true;
994
995 if (currentAbsorb)
996 {
998 absorbLog.Attacker = attacker ? attacker->GetGUID() : ObjectGuid::Empty;
999 absorbLog.Victim = victim->GetGUID();
1000 absorbLog.Caster = base->GetCasterGUID();
1001 absorbLog.AbsorbedSpellID = spellProto ? spellProto->Id : 0;
1002 absorbLog.AbsorbSpellID = base->GetId();
1003 absorbLog.Absorbed = currentAbsorb;
1004 absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
1005 absorbLog.LogData.Initialize(victim);
1006 victim->SendCombatLogMessage(&absorbLog);
1007 }
1008 }
1009
1010 damageTaken = damageInfo.GetDamage();
1011 }
1012 }
1013
1014 if (spellProto && spellProto->HasAttribute(SPELL_ATTR3_NO_DURABILITY_LOSS))
1015 durabilityLoss = false;
1016
1017 if (killed)
1018 Unit::Kill(attacker, victim, durabilityLoss, skipSettingDeathState);
1019 else
1020 {
1021 if (victim->GetTypeId() == TYPEID_PLAYER)
1023
1024 victim->ModifyHealth(-(int32)damageTaken);
1025
1026 if (damagetype == DIRECT_DAMAGE || damagetype == SPELL_DIRECT_DAMAGE)
1029 if (victim->GetTypeId() != TYPEID_PLAYER)
1030 {
1031 // Part of Evade mechanics. DoT's and Thorns / Retribution Aura do not contribute to this
1032 if (damagetype != DOT && damageTaken > 0 && !victim->GetOwnerGUID().IsPlayer() && (!spellProto || !spellProto->HasAura(SPELL_AURA_DAMAGE_SHIELD)))
1035 if (attacker && (!spellProto || !spellProto->HasAttribute(SPELL_ATTR4_NO_HARMFUL_THREAT)))
1036 victim->GetThreatManager().AddThreat(attacker, float(damageTaken), spellProto);
1037 }
1038 else // victim is a player
1039 {
1040 // random durability for items (HIT TAKEN)
1041 if (durabilityLoss && roll_chance_f(sWorld->getRate(RATE_DURABILITY_LOSS_DAMAGE)))
1042 {
1045 }
1046 }
1047
1048 if (attacker && attacker->GetTypeId() == TYPEID_PLAYER)
1049 {
1050 // random durability for items (HIT DONE)
1051 if (durabilityLoss && roll_chance_f(sWorld->getRate(RATE_DURABILITY_LOSS_DAMAGE)))
1052 {
1054 attacker->ToPlayer()->DurabilityPointLossForEquipSlot(slot);
1055 }
1056 }
1057
1058 if (damagetype != NODAMAGE && damagetype != DOT)
1059 {
1060 if (victim != attacker && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR6_NO_PUSHBACK) || spellProto->HasAttribute(SPELL_ATTR7_DONT_CAUSE_SPELL_PUSHBACK) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC))))
1061 {
1062 if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL])
1063 {
1064 if (spell->getState() == SPELL_STATE_PREPARING)
1065 {
1066 auto isCastInterrupted = [&]()
1067 {
1068 if (!damageTaken)
1069 return spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::ZeroDamageCancels);
1070
1071 if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly)))
1072 return true;
1073
1074 if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancels))
1075 return true;
1076
1077 return false;
1078 };
1079
1080 auto isCastDelayed = [&]()
1081 {
1082 if (!damageTaken)
1083 return false;
1084
1085 if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushbackPlayerOnly)))
1086 return true;
1087
1088 if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushback))
1089 return true;
1090
1091 return false;
1092 };
1093
1094 if (isCastInterrupted())
1095 victim->InterruptNonMeleeSpells(false);
1096 else if (isCastDelayed())
1097 spell->Delayed();
1098 }
1099 }
1100
1101 if (damageTaken && victim->IsPlayer())
1102 if (Spell* spell = victim->m_currentSpells[CURRENT_CHANNELED_SPELL])
1103 if (spell->getState() == SPELL_STATE_CASTING && spell->m_spellInfo->HasChannelInterruptFlag(SpellAuraInterruptFlags::DamageChannelDuration))
1104 spell->DelayedChannel();
1105 }
1106 }
1107
1108 // last damage from duel opponent
1109 if (duel_hasEnded)
1110 {
1111 Player* he = duel_wasMounted ? victim->GetCharmer()->ToPlayer() : victim->ToPlayer();
1112
1113 ASSERT_NODEBUGINFO(he && he->duel);
1114
1115 if (duel_wasMounted) // In this case victim == mount
1116 victim->SetHealth(1);
1117 else
1118 he->SetHealth(1);
1119
1120 he->duel->Opponent->CombatStopWithPets(true);
1121 he->CombatStopWithPets(true);
1122
1123 he->CastSpell(he, 7267, true); // beg
1125 }
1126 }
1127
1128 // make player victims stand up automatically
1129 if (victim->GetStandState() && victim->IsPlayer())
1131
1132 return damageTaken;
1133}
1134
1135void Unit::CastStop(uint32 except_spellid)
1136{
1138 if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id != except_spellid)
1140}
1141
1142void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType, bool crit /*= false*/, bool blocked /*= false*/, Spell* spell /*= nullptr*/)
1143{
1144 if (damage < 0)
1145 return;
1146
1147 Unit* victim = damageInfo->target;
1148 if (!victim || !victim->IsAlive())
1149 return;
1150
1151 SpellSchoolMask damageSchoolMask = SpellSchoolMask(damageInfo->schoolMask);
1152
1153 // Spells with SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS ignore resilience because their damage is based off another spell's damage.
1155 {
1156 if (Unit::IsDamageReducedByArmor(damageSchoolMask, spellInfo))
1157 damage = Unit::CalcArmorReducedDamage(damageInfo->attacker, victim, damage, spellInfo, attackType);
1158
1159 // Per-school calc
1160 switch (spellInfo->DmgClass)
1161 {
1162 // Melee and Ranged Spells
1165 {
1166 if (crit)
1167 {
1168 damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT;
1169
1170 // Calculate crit bonus
1171 uint32 crit_bonus = damage;
1172 // Apply crit_damage bonus for melee spells
1173 if (Player* modOwner = GetSpellModOwner())
1174 modOwner->ApplySpellMod(spellInfo, SpellModOp::CritDamageAndHealing, crit_bonus);
1175 damage += crit_bonus;
1176
1177 // Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
1178 float critPctDamageMod = (GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, spellInfo->GetSchoolMask()) - 1.0f) * 100;
1179
1180 if (critPctDamageMod != 0)
1181 AddPct(damage, critPctDamageMod);
1182 }
1183
1184 // Spell weapon based damage CAN BE crit & blocked at same time
1185 if (blocked)
1186 {
1187 // double blocked amount if block is critical
1188 uint32 value = victim->GetBlockPercent(GetLevel());
1189 if (victim->IsBlockCritical())
1190 value *= 2; // double blocked percent
1191 damageInfo->blocked = CalculatePct(damage, value);
1192 if (damage <= int32(damageInfo->blocked))
1193 {
1194 damageInfo->blocked = uint32(damage);
1195 damageInfo->fullBlock = true;
1196 }
1197 damage -= damageInfo->blocked;
1198 }
1199
1200 if (CanApplyResilience())
1201 Unit::ApplyResilience(victim, &damage);
1202 break;
1203 }
1204 // Magical Attacks
1207 {
1208 // If crit add critical bonus
1209 if (crit)
1210 {
1211 damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT;
1212 damage = Unit::SpellCriticalDamageBonus(this, spellInfo, damage, victim);
1213 }
1214
1215 if (CanApplyResilience())
1216 Unit::ApplyResilience(victim, &damage);
1217 break;
1218
1219 }
1220 default:
1221 break;
1222 }
1223 }
1224
1225 // Script Hook For CalculateSpellDamageTaken -- Allow scripts to change the Damage post class mitigation calculations
1226 sScriptMgr->ModifySpellDamageTaken(damageInfo->target, damageInfo->attacker, damage, spellInfo);
1227
1228 // Calculate absorb resist
1229 if (damage < 0)
1230 damage = 0;
1231
1232 damageInfo->damage = damage;
1233 damageInfo->originalDamage = damage;
1235 Unit::CalcAbsorbResist(dmgInfo, spell);
1236 damageInfo->absorb = dmgInfo.GetAbsorb();
1237 damageInfo->resist = dmgInfo.GetResist();
1238
1239 if (damageInfo->absorb)
1240 damageInfo->HitInfo |= (damageInfo->damage - damageInfo->absorb == 0 ? HITINFO_FULL_ABSORB : HITINFO_PARTIAL_ABSORB);
1241
1242 if (damageInfo->resist)
1243 damageInfo->HitInfo |= (damageInfo->damage - damageInfo->resist == 0 ? HITINFO_FULL_RESIST : HITINFO_PARTIAL_RESIST);
1244
1245 damageInfo->damage = dmgInfo.GetDamage();
1246}
1247
1248void Unit::DealSpellDamage(SpellNonMeleeDamage const* damageInfo, bool durabilityLoss)
1249{
1250 if (!damageInfo)
1251 return;
1252
1253 Unit* victim = damageInfo->target;
1254 if (!victim)
1255 return;
1256
1257 if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()))
1258 return;
1259
1260 if (!damageInfo->Spell)
1261 {
1262 TC_LOG_DEBUG("entities.unit", "Unit::DealSpellDamage has no spell");
1263 return;
1264 }
1265
1266 // Call default DealDamage
1267 CleanDamage cleanDamage(damageInfo->cleanDamage, damageInfo->absorb, BASE_ATTACK, MELEE_HIT_NORMAL);
1268 Unit::DealDamage(this, victim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), damageInfo->Spell, durabilityLoss);
1269}
1270
1272void Unit::CalculateMeleeDamage(Unit* victim, CalcDamageInfo* damageInfo, WeaponAttackType attackType /*= BASE_ATTACK*/)
1273{
1274 damageInfo->Attacker = this;
1275 damageInfo->Target = victim;
1276
1277 damageInfo->DamageSchoolMask = GetMeleeDamageSchoolMask(attackType);
1278 damageInfo->Damage = 0;
1279 damageInfo->OriginalDamage = 0;
1280 damageInfo->Absorb = 0;
1281 damageInfo->Resist = 0;
1282
1283 damageInfo->Blocked = 0;
1284 damageInfo->HitInfo = 0;
1285 damageInfo->TargetState = 0;
1286
1287 damageInfo->AttackType = attackType;
1288 damageInfo->ProcAttacker = PROC_FLAG_NONE;
1289 damageInfo->ProcVictim = PROC_FLAG_NONE;
1290 damageInfo->CleanDamage = 0;
1291 damageInfo->HitOutCome = MELEE_HIT_EVADE;
1292
1293 if (!victim)
1294 return;
1295
1296 if (!IsAlive() || !victim->IsAlive())
1297 return;
1298
1299 // Select HitInfo/procAttacker/procVictim flag based on attack type
1300 switch (attackType)
1301 {
1302 case BASE_ATTACK:
1305 break;
1306 case OFF_ATTACK:
1309 damageInfo->HitInfo = HITINFO_OFFHAND;
1310 break;
1311 default:
1312 return;
1313 }
1314
1315 // Physical Immune check
1316 if (damageInfo->Target->IsImmunedToDamage(SpellSchoolMask(damageInfo->DamageSchoolMask)))
1317 {
1318 damageInfo->HitInfo |= HITINFO_NORMALSWING;
1319 damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE;
1320
1321 damageInfo->Damage = 0;
1322 damageInfo->CleanDamage = 0;
1323 return;
1324 }
1325
1326 uint32 damage = 0;
1327 damage += CalculateDamage(damageInfo->AttackType, false, true);
1328 // Add melee damage bonus
1329 damage = MeleeDamageBonusDone(damageInfo->Target, damage, damageInfo->AttackType, DIRECT_DAMAGE, nullptr, MECHANIC_NONE, SpellSchoolMask(damageInfo->DamageSchoolMask));
1330 damage = damageInfo->Target->MeleeDamageBonusTaken(this, damage, damageInfo->AttackType, DIRECT_DAMAGE, nullptr, SpellSchoolMask(damageInfo->DamageSchoolMask));
1331
1332 // Script Hook For CalculateMeleeDamage -- Allow scripts to change the Damage pre class mitigation calculations
1333 sScriptMgr->ModifyMeleeDamage(damageInfo->Target, damageInfo->Attacker, damage);
1334
1335 // Calculate armor reduction
1337 {
1338 damageInfo->Damage = Unit::CalcArmorReducedDamage(damageInfo->Attacker, damageInfo->Target, damage, nullptr, damageInfo->AttackType);
1339 damageInfo->CleanDamage += damage - damageInfo->Damage;
1340 }
1341 else
1342 damageInfo->Damage = damage;
1343
1344 damageInfo->HitOutCome = RollMeleeOutcomeAgainst(damageInfo->Target, damageInfo->AttackType);
1345
1346 switch (damageInfo->HitOutCome)
1347 {
1348 case MELEE_HIT_EVADE:
1350 damageInfo->TargetState = VICTIMSTATE_EVADES;
1351 damageInfo->OriginalDamage = damageInfo->Damage;
1352
1353 damageInfo->Damage = 0;
1354 damageInfo->CleanDamage = 0;
1355 return;
1356 case MELEE_HIT_MISS:
1357 damageInfo->HitInfo |= HITINFO_MISS;
1358 damageInfo->TargetState = VICTIMSTATE_INTACT;
1359 damageInfo->OriginalDamage = damageInfo->Damage;
1360
1361 damageInfo->Damage = 0;
1362 damageInfo->CleanDamage = 0;
1363 break;
1364 case MELEE_HIT_NORMAL:
1365 damageInfo->TargetState = VICTIMSTATE_HIT;
1366 damageInfo->OriginalDamage = damageInfo->Damage;
1367 break;
1368 case MELEE_HIT_CRIT:
1369 {
1370 damageInfo->HitInfo |= HITINFO_CRITICALHIT;
1371 damageInfo->TargetState = VICTIMSTATE_HIT;
1372
1373 // Crit bonus calc
1374 damageInfo->Damage *= 2;
1375
1376 // Increase crit damage from SPELL_AURA_MOD_CRIT_DAMAGE_BONUS
1378
1379 if (mod != 0)
1380 AddPct(damageInfo->Damage, mod);
1381
1382 damageInfo->OriginalDamage = damageInfo->Damage;
1383 break;
1384 }
1385 case MELEE_HIT_PARRY:
1386 damageInfo->TargetState = VICTIMSTATE_PARRY;
1387 damageInfo->CleanDamage += damageInfo->Damage;
1388
1389 damageInfo->OriginalDamage = damageInfo->Damage;
1390 damageInfo->Damage = 0;
1391 break;
1392 case MELEE_HIT_DODGE:
1393 damageInfo->TargetState = VICTIMSTATE_DODGE;
1394 damageInfo->CleanDamage += damageInfo->Damage;
1395
1396 damageInfo->OriginalDamage = damageInfo->Damage;
1397 damageInfo->Damage = 0;
1398 break;
1399 case MELEE_HIT_BLOCK:
1400 damageInfo->TargetState = VICTIMSTATE_HIT;
1401 damageInfo->HitInfo |= HITINFO_BLOCK;
1402 // 30% damage blocked, double blocked amount if block is critical
1403 damageInfo->Blocked = CalculatePct(damageInfo->Damage, damageInfo->Target->GetBlockPercent(GetLevel()));
1404 if (damageInfo->Target->IsBlockCritical())
1405 damageInfo->Blocked *= 2;
1406
1407 damageInfo->OriginalDamage = damageInfo->Damage;
1408 damageInfo->Damage -= damageInfo->Blocked;
1409 damageInfo->CleanDamage += damageInfo->Blocked;
1410 break;
1411 case MELEE_HIT_GLANCING:
1412 {
1413 damageInfo->HitInfo |= HITINFO_GLANCING;
1414 damageInfo->TargetState = VICTIMSTATE_HIT;
1415 int32 leveldif = int32(victim->GetLevel()) - int32(GetLevel());
1416 if (leveldif > 3)
1417 leveldif = 3;
1418
1419 damageInfo->OriginalDamage = damageInfo->Damage;
1420 float reducePercent = 1.f - leveldif * 0.1f;
1421 damageInfo->CleanDamage += damageInfo->Damage - uint32(reducePercent * damageInfo->Damage);
1422 damageInfo->Damage = uint32(reducePercent * damageInfo->Damage);
1423 break;
1424 }
1425 case MELEE_HIT_CRUSHING:
1426 damageInfo->HitInfo |= HITINFO_CRUSHING;
1427 damageInfo->TargetState = VICTIMSTATE_HIT;
1428 // 150% normal damage
1429 damageInfo->Damage += (damageInfo->Damage / 2);
1430 damageInfo->OriginalDamage = damageInfo->Damage;
1431 break;
1432 default:
1433 break;
1434 }
1435
1436 // Always apply HITINFO_AFFECTS_VICTIM in case its not a miss
1437 if (!(damageInfo->HitInfo & HITINFO_MISS))
1438 damageInfo->HitInfo |= HITINFO_AFFECTS_VICTIM;
1439
1440 int32 resilienceReduction = damageInfo->Damage;
1442 Unit::ApplyResilience(victim, &resilienceReduction);
1443 resilienceReduction = damageInfo->Damage - resilienceReduction;
1444 damageInfo->Damage -= resilienceReduction;
1445 damageInfo->CleanDamage += resilienceReduction;
1446 damageInfo->OriginalDamage -= resilienceReduction;
1447
1448 // Calculate absorb resist
1449 if (int32(damageInfo->Damage) > 0)
1450 {
1452 // Calculate absorb & resists
1453 DamageInfo dmgInfo(*damageInfo);
1454 Unit::CalcAbsorbResist(dmgInfo);
1455 damageInfo->Absorb = dmgInfo.GetAbsorb();
1456 damageInfo->Resist = dmgInfo.GetResist();
1457
1458 if (damageInfo->Absorb)
1459 damageInfo->HitInfo |= (damageInfo->Damage - damageInfo->Absorb == 0 ? HITINFO_FULL_ABSORB : HITINFO_PARTIAL_ABSORB);
1460
1461 if (damageInfo->Resist)
1462 damageInfo->HitInfo |= (damageInfo->Damage - damageInfo->Resist == 0 ? HITINFO_FULL_RESIST : HITINFO_PARTIAL_RESIST);
1463
1464 damageInfo->Damage = dmgInfo.GetDamage();
1465 }
1466 else // Impossible get negative result but....
1467 damageInfo->Damage = 0;
1468}
1469
1470void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
1471{
1472 Unit* victim = damageInfo->Target;
1473
1474 if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()))
1475 return;
1476
1477 if (damageInfo->TargetState == VICTIMSTATE_PARRY &&
1479 {
1480 // Get attack timers
1481 float offtime = float(victim->getAttackTimer(OFF_ATTACK));
1482 float basetime = float(victim->getAttackTimer(BASE_ATTACK));
1483 // Reduce attack time
1484 if (victim->haveOffhandWeapon() && offtime < basetime)
1485 {
1486 float percent20 = victim->GetBaseAttackTime(OFF_ATTACK) * 0.20f;
1487 float percent60 = 3.0f * percent20;
1488 if (offtime > percent20 && offtime <= percent60)
1489 victim->setAttackTimer(OFF_ATTACK, uint32(percent20));
1490 else if (offtime > percent60)
1491 {
1492 offtime -= 2.0f * percent20;
1493 victim->setAttackTimer(OFF_ATTACK, uint32(offtime));
1494 }
1495 }
1496 else
1497 {
1498 float percent20 = victim->GetBaseAttackTime(BASE_ATTACK) * 0.20f;
1499 float percent60 = 3.0f * percent20;
1500 if (basetime > percent20 && basetime <= percent60)
1501 victim->setAttackTimer(BASE_ATTACK, uint32(percent20));
1502 else if (basetime > percent60)
1503 {
1504 basetime -= 2.0f * percent20;
1505 victim->setAttackTimer(BASE_ATTACK, uint32(basetime));
1506 }
1507 }
1508 }
1509
1510 // Call default DealDamage
1511 CleanDamage cleanDamage(damageInfo->CleanDamage, damageInfo->Absorb, damageInfo->AttackType, damageInfo->HitOutCome);
1512 Unit::DealDamage(this, victim, damageInfo->Damage, &cleanDamage, DIRECT_DAMAGE, SpellSchoolMask(damageInfo->DamageSchoolMask), nullptr, durabilityLoss);
1513
1514 // If this is a creature and it attacks from behind it has a probability to daze it's victim
1515 if ((damageInfo->HitOutCome == MELEE_HIT_CRIT || damageInfo->HitOutCome == MELEE_HIT_CRUSHING || damageInfo->HitOutCome == MELEE_HIT_NORMAL || damageInfo->HitOutCome == MELEE_HIT_GLANCING) &&
1516 GetTypeId() != TYPEID_PLAYER && !ToCreature()->IsControlledByPlayer() && !victim->HasInArc(float(M_PI), this)
1517 && (victim->GetTypeId() == TYPEID_PLAYER || !victim->ToCreature()->isWorldBoss())&& !victim->IsVehicle())
1518 {
1519 // 20% base chance
1520 float chance = 20.0f;
1521
1522 // there is a newbie protection, at level 10 just 7% base chance; assuming linear function
1523 if (victim->GetLevel() < 30)
1524 chance = 0.65f * victim->GetLevelForTarget(this) + 0.5f;
1525
1526 uint32 const victimDefense = victim->GetMaxSkillValueForLevel(this);
1527 uint32 const attackerMeleeSkill = GetMaxSkillValueForLevel();
1528
1529 chance *= attackerMeleeSkill / float(victimDefense) * 0.16f;
1530
1531 // -probability is between 0% and 40%
1532 RoundToInterval(chance, 0.0f, 40.0f);
1533 if (roll_chance_f(chance))
1534 CastSpell(victim, 1604 /*SPELL_DAZED*/, true);
1535 }
1536
1537 if (GetTypeId() == TYPEID_PLAYER)
1538 {
1539 DamageInfo dmgInfo(*damageInfo);
1540 ToPlayer()->CastItemCombatSpell(dmgInfo);
1541 }
1542
1543 // Do effect if any damage done to target
1544 if (damageInfo->Damage)
1545 {
1546 // We're going to call functions which can modify content of the list during iteration over it's elements
1547 // Let's copy the list so we can prevent iterator invalidation
1549 for (AuraEffect const* aurEff : vDamageShieldsCopy)
1550 {
1551 SpellInfo const* spellInfo = aurEff->GetSpellInfo();
1552
1553 // Damage shield can be resisted...
1554 SpellMissInfo missInfo = victim->SpellHitResult(this, spellInfo, false);
1555 if (missInfo != SPELL_MISS_NONE)
1556 {
1557 victim->SendSpellMiss(this, spellInfo->Id, missInfo);
1558 continue;
1559 }
1560
1561 // ...or immuned
1562 if (IsImmunedToDamage(spellInfo))
1563 {
1564 victim->SendSpellDamageImmune(this, spellInfo->Id, false);
1565 continue;
1566 }
1567
1568 uint32 damage = aurEff->GetAmount();
1569 if (Unit* caster = aurEff->GetCaster())
1570 {
1571 damage = caster->SpellDamageBonusDone(this, spellInfo, damage, SPELL_DIRECT_DAMAGE, aurEff->GetSpellEffectInfo());
1572 damage = SpellDamageBonusTaken(caster, spellInfo, damage, SPELL_DIRECT_DAMAGE);
1573 }
1574
1575 DamageInfo damageInfo(this, victim, damage, spellInfo, spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE, BASE_ATTACK);
1576 victim->CalcAbsorbResist(damageInfo);
1577 damage = damageInfo.GetDamage();
1578 Unit::DealDamageMods(victim, this, damage, nullptr);
1579
1581 damageShield.Attacker = victim->GetGUID();
1582 damageShield.Defender = GetGUID();
1583 damageShield.SpellID = spellInfo->Id;
1584 damageShield.TotalDamage = damage;
1585 damageShield.OriginalDamage = damageInfo.GetOriginalDamage();
1586 damageShield.OverKill = std::max(int32(damage) - int32(GetHealth()), 0);
1587 damageShield.SchoolMask = spellInfo->SchoolMask;
1588 damageShield.LogAbsorbed = damageInfo.GetAbsorb();
1589
1590 Unit::DealDamage(victim, this, damage, nullptr, SPELL_DIRECT_DAMAGE, spellInfo->GetSchoolMask(), spellInfo, true);
1591
1592 damageShield.LogData.Initialize(this);
1593 victim->SendCombatLogMessage(&damageShield);
1594 }
1595 }
1596}
1597
1598void Unit::HandleEmoteCommand(Emote emoteId, Player* target /*=nullptr*/, Trinity::IteratorPair<int32 const*> spellVisualKitIds /*= {}*/, int32 sequenceVariation /*= 0*/)
1599{
1601 packet.Guid = GetGUID();
1602 packet.EmoteID = emoteId;
1603
1604 if (EmotesEntry const* emotesEntry = sEmotesStore.LookupEntry(emoteId))
1605 if (emotesEntry->AnimID == ANIM_MOUNT_SPECIAL || emotesEntry->AnimID == ANIM_MOUNT_SELF_SPECIAL)
1606 std::copy(spellVisualKitIds.begin(), spellVisualKitIds.end(), std::back_inserter(packet.SpellVisualKitIDs));
1607
1608 packet.SequenceVariation = sequenceVariation;
1609
1610 if (target)
1611 target->SendDirectMessage(packet.Write());
1612 else
1613 SendMessageToSet(packet.Write(), true);
1614}
1615
1616/*static*/ bool Unit::IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo const* spellInfo /*= nullptr*/)
1617{
1618 // only physical spells damage gets reduced by armor
1619 if ((schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0)
1620 return false;
1621
1622 return !spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_ARMOR);
1623}
1624
1625/*static*/ uint32 Unit::CalcArmorReducedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, WeaponAttackType /*attackType*/ /*= MAX_ATTACK*/, uint8 attackerLevel /*= 0*/)
1626{
1627 float armor = float(victim->GetArmor());
1628
1629 if (attacker)
1630 {
1631 armor *= victim->GetArmorMultiplierForTarget(attacker);
1632
1633 // bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
1634 int32 armorBypassPct = 0;
1636 for (AuraEffectList::const_iterator i = reductionAuras.begin(); i != reductionAuras.end(); ++i)
1637 if ((*i)->GetCasterGUID() == attacker->GetGUID())
1638 armorBypassPct += (*i)->GetAmount();
1639 armor = CalculatePct(armor, 100 - std::min(armorBypassPct, 100));
1640
1641 // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
1643
1644 if (spellInfo)
1645 if (Player* modOwner = attacker->GetSpellModOwner())
1646 modOwner->ApplySpellMod(spellInfo, SpellModOp::TargetResistance, armor);
1647
1649 for (AuraEffect const* aurEff : resIgnoreAuras)
1650 {
1651 if (aurEff->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL && aurEff->IsAffectingSpell(spellInfo))
1652 armor = std::floor(AddPct(armor, -aurEff->GetAmount()));
1653 }
1654
1655 // Apply Player CR_ARMOR_PENETRATION rating
1656 if (attacker->GetTypeId() == TYPEID_PLAYER)
1657 {
1658 float arpPct = attacker->ToPlayer()->GetRatingBonusValue(CR_ARMOR_PENETRATION);
1659
1660 // no more than 100%
1661 RoundToInterval(arpPct, 0.f, 100.f);
1662
1663 float maxArmorPen = 0.f;
1664 if (victim->GetLevelForTarget(attacker) < 60)
1665 maxArmorPen = float(400 + 85 * victim->GetLevelForTarget(attacker));
1666 else
1667 maxArmorPen = 400 + 85 * victim->GetLevelForTarget(attacker) + 4.5f * 85 * (victim->GetLevelForTarget(attacker) - 59);
1668
1669 // Cap armor penetration to this number
1670 maxArmorPen = std::min((armor + maxArmorPen) / 3.f, armor);
1671 // Figure out how much armor do we ignore
1672 armor -= CalculatePct(maxArmorPen, arpPct);
1673 }
1674 }
1675
1676 if (G3D::fuzzyLe(armor, 0.0f))
1677 return damage;
1678
1679 Classes attackerClass = CLASS_WARRIOR;
1680 if (attacker)
1681 {
1682 attackerLevel = attacker->GetLevelForTarget(victim);
1683 attackerClass = Classes(attacker->GetClass());
1684 }
1685
1686 // Expansion and ContentTuningID necessary? Does Player get a ContentTuningID too ?
1687 float armorConstant = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::ArmorConstant, attackerLevel, -2, 0, attackerClass, 0);
1688
1689 if (!(armor + armorConstant))
1690 return damage;
1691
1692 float mitigation = std::min(armor / (armor + armorConstant), 0.85f);
1693 return uint32(std::max(damage * (1.0f - mitigation), 0.0f));
1694}
1695
1696/*static*/ uint32 Unit::CalcSpellResistedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellSchoolMask schoolMask, SpellInfo const* spellInfo)
1697{
1698 // Magic damage, check for resists
1699 if (!(schoolMask & SPELL_SCHOOL_MASK_MAGIC))
1700 return 0;
1701
1702 // Npcs can have holy resistance
1703 if ((schoolMask & SPELL_SCHOOL_MASK_HOLY) && victim->GetTypeId() != TYPEID_UNIT)
1704 return 0;
1705
1706 float const averageResist = Unit::CalculateAverageResistReduction(attacker, schoolMask, victim, spellInfo);
1707 float discreteResistProbability[11] = { };
1708 if (averageResist <= 0.1f)
1709 {
1710 discreteResistProbability[0] = 1.0f - 7.5f * averageResist;
1711 discreteResistProbability[1] = 5.0f * averageResist;
1712 discreteResistProbability[2] = 2.5f * averageResist;
1713 }
1714 else
1715 {
1716 for (uint32 i = 0; i < 11; ++i)
1717 discreteResistProbability[i] = std::max(0.5f - 2.5f * std::fabs(0.1f * i - averageResist), 0.0f);
1718 }
1719
1720 float roll = rand_norm();
1721 float probabilitySum = 0.0f;
1722
1723 uint32 resistance = 0;
1724 for (; resistance < 11; ++resistance)
1725 if (roll < (probabilitySum += discreteResistProbability[resistance]))
1726 break;
1727
1728 float damageResisted = damage * resistance / 10.f;
1729 if (damageResisted > 0.0f) // if any damage was resisted
1730 {
1731 int32 ignoredResistance = 0;
1732
1733 if (attacker)
1734 ignoredResistance += attacker->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_IGNORE_TARGET_RESIST, schoolMask);
1735
1736 ignoredResistance = std::min<int32>(ignoredResistance, 100);
1737 ApplyPct(damageResisted, 100 - ignoredResistance);
1738
1739 // Spells with melee and magic school mask, decide whether resistance or armor absorb is higher
1740 if (spellInfo && spellInfo->HasAttribute(SPELL_ATTR0_CU_SCHOOLMASK_NORMAL_WITH_MAGIC))
1741 {
1742 uint32 damageAfterArmor = Unit::CalcArmorReducedDamage(attacker, victim, damage, spellInfo, spellInfo->GetAttackType());
1743 float armorReduction = damage - damageAfterArmor;
1744
1745 // pick the lower one, the weakest resistance counts
1746 damageResisted = std::min(damageResisted, armorReduction);
1747 }
1748 }
1749
1750 damageResisted = std::max(damageResisted, 0.f);
1751 return uint32(damageResisted);
1752}
1753
1754/*static*/ float Unit::CalculateAverageResistReduction(WorldObject const* caster, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo /*= nullptr*/)
1755{
1756 float victimResistance = float(victim->GetResistance(schoolMask));
1757 if (caster)
1758 {
1759 // pets inherit 100% of masters penetration
1760 if (Player const* player = caster->GetSpellModOwner())
1761 {
1762 victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
1763 victimResistance -= float(player->GetSpellPenetrationItemMod());
1764 }
1765 else if (Unit const* unitCaster = caster->ToUnit())
1766 victimResistance += float(unitCaster->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
1767 }
1768
1769 // holy resistance exists in pve and comes from level difference, ignore template values
1770 if (schoolMask & SPELL_SCHOOL_MASK_HOLY)
1771 victimResistance = 0.0f;
1772
1773 // Chaos Bolt exception, ignore all target resistances (unknown attribute?)
1774 if (spellInfo && spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && spellInfo->Id == 116858)
1775 victimResistance = 0.0f;
1776
1777 victimResistance = std::max(victimResistance, 0.0f);
1778
1779 // level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration
1780 // gameobject caster -- should it have level based resistance?
1781 if (caster && caster->GetTypeId() != TYPEID_GAMEOBJECT && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL)))
1782 victimResistance += std::max((float(victim->GetLevelForTarget(caster)) - float(caster->GetLevelForTarget(victim))) * 5.0f, 0.0f);
1783
1784 static uint32 const bossLevel = 83;
1785 static float const bossResistanceConstant = 510.0f;
1786 uint32 level = caster ? victim->GetLevelForTarget(caster) : victim->GetLevel();
1787 float resistanceConstant = 0.0f;
1788
1789 if (level == bossLevel)
1790 resistanceConstant = bossResistanceConstant;
1791 else
1792 resistanceConstant = level * 5.0f;
1793
1794 return victimResistance / (victimResistance + resistanceConstant);
1795}
1796
1797/*static*/ void Unit::CalcAbsorbResist(DamageInfo& damageInfo, Spell* spell /*= nullptr*/)
1798{
1799 if (!damageInfo.GetVictim() || !damageInfo.GetVictim()->IsAlive() || !damageInfo.GetDamage())
1800 return;
1801
1802 uint32 resistedDamage = Unit::CalcSpellResistedDamage(damageInfo.GetAttacker(), damageInfo.GetVictim(), damageInfo.GetDamage(), damageInfo.GetSchoolMask(), damageInfo.GetSpellInfo());
1803
1804 // Ignore Absorption Auras
1805 float auraAbsorbMod = 0.f;
1806 if (Unit* attacker = damageInfo.GetAttacker())
1808
1809 RoundToInterval(auraAbsorbMod, 0.0f, 100.0f);
1810
1811 int32 absorbIgnoringDamage = CalculatePct(damageInfo.GetDamage(), auraAbsorbMod);
1812
1813 if (spell)
1814 spell->CallScriptOnResistAbsorbCalculateHandlers(damageInfo, resistedDamage, absorbIgnoringDamage);
1815
1816 damageInfo.ResistDamage(resistedDamage);
1817
1818 // We're going to call functions which can modify content of the list during iteration over it's elements
1819 // Let's copy the list so we can prevent iterator invalidation
1821 std::sort(vSchoolAbsorbCopy.begin(), vSchoolAbsorbCopy.end(), Trinity::AbsorbAuraOrderPred());
1822
1823 // absorb without mana cost
1824 for (auto itr = vSchoolAbsorbCopy.begin(); (itr != vSchoolAbsorbCopy.end()) && (damageInfo.GetDamage() > 0); ++itr)
1825 {
1826 AuraEffect* absorbAurEff = *itr;
1827 // Check if aura was removed during iteration - we don't need to work on such auras
1828 AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(damageInfo.GetVictim()->GetGUID());
1829 if (!aurApp)
1830 continue;
1831 if (!(absorbAurEff->GetMiscValue() & damageInfo.GetSchoolMask()))
1832 continue;
1833
1834 // get amount which can be still absorbed by the aura
1835 int32 currentAbsorb = absorbAurEff->GetAmount();
1836 // aura with infinite absorb amount - let the scripts handle absorbtion amount, set here to 0 for safety
1837 if (currentAbsorb < 0)
1838 currentAbsorb = 0;
1839
1841 damageInfo.ModifyDamage(-absorbIgnoringDamage);
1842
1843 uint32 tempAbsorb = uint32(currentAbsorb);
1844
1845 bool defaultPrevented = false;
1846
1847 absorbAurEff->GetBase()->CallScriptEffectAbsorbHandlers(absorbAurEff, aurApp, damageInfo, tempAbsorb, defaultPrevented);
1848 currentAbsorb = tempAbsorb;
1849
1850 if (!defaultPrevented)
1851 {
1852 // absorb must be smaller than the damage itself
1853 currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(damageInfo.GetDamage()));
1854
1855 damageInfo.AbsorbDamage(currentAbsorb);
1856
1857 tempAbsorb = currentAbsorb;
1858 absorbAurEff->GetBase()->CallScriptEffectAfterAbsorbHandlers(absorbAurEff, aurApp, damageInfo, tempAbsorb);
1859
1860 // Check if our aura is using amount to count damage
1861 if (absorbAurEff->GetAmount() >= 0)
1862 {
1863 // Reduce shield amount
1864 absorbAurEff->ChangeAmount(absorbAurEff->GetAmount() - currentAbsorb);
1865 // Aura cannot absorb anything more - remove it
1866 if (absorbAurEff->GetAmount() <= 0)
1867 absorbAurEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
1868 }
1869 }
1870
1872 damageInfo.ModifyDamage(absorbIgnoringDamage);
1873
1874 if (currentAbsorb)
1875 {
1877 absorbLog.Attacker = damageInfo.GetAttacker() ? damageInfo.GetAttacker()->GetGUID() : ObjectGuid::Empty;
1878 absorbLog.Victim = damageInfo.GetVictim()->GetGUID();
1879 absorbLog.Caster = absorbAurEff->GetBase()->GetCasterGUID();
1880 absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() ? damageInfo.GetSpellInfo()->Id : 0;
1881 absorbLog.AbsorbSpellID = absorbAurEff->GetId();
1882 absorbLog.Absorbed = currentAbsorb;
1883 absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
1884 absorbLog.LogData.Initialize(damageInfo.GetVictim());
1885 damageInfo.GetVictim()->SendCombatLogMessage(&absorbLog);
1886 }
1887 }
1888
1889 // absorb by mana cost
1891 for (auto itr = vManaShieldCopy.begin(); (itr != vManaShieldCopy.end()) && (damageInfo.GetDamage() > 0); ++itr)
1892 {
1893 AuraEffect* absorbAurEff = *itr;
1894 // Check if aura was removed during iteration - we don't need to work on such auras
1895 AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(damageInfo.GetVictim()->GetGUID());
1896 if (!aurApp)
1897 continue;
1898 // check damage school mask
1899 if (!(absorbAurEff->GetMiscValue() & damageInfo.GetSchoolMask()))
1900 continue;
1901
1902 // get amount which can be still absorbed by the aura
1903 int32 currentAbsorb = absorbAurEff->GetAmount();
1904 // aura with infinite absorb amount - let the scripts handle absorbtion amount, set here to 0 for safety
1905 if (currentAbsorb < 0)
1906 currentAbsorb = 0;
1907
1909 damageInfo.ModifyDamage(-absorbIgnoringDamage);
1910
1911 uint32 tempAbsorb = currentAbsorb;
1912
1913 bool defaultPrevented = false;
1914
1915 absorbAurEff->GetBase()->CallScriptEffectManaShieldHandlers(absorbAurEff, aurApp, damageInfo, tempAbsorb, defaultPrevented);
1916 currentAbsorb = tempAbsorb;
1917
1918 if (!defaultPrevented)
1919 {
1920 // absorb must be smaller than the damage itself
1921 currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(damageInfo.GetDamage()));
1922
1923 int32 manaReduction = currentAbsorb;
1924
1925 // lower absorb amount by talents
1926 if (float manaMultiplier = absorbAurEff->GetSpellEffectInfo().CalcValueMultiplier(absorbAurEff->GetCaster()))
1927 manaReduction = int32(float(manaReduction) * manaMultiplier);
1928
1929 int32 manaTaken = -damageInfo.GetVictim()->ModifyPower(POWER_MANA, -manaReduction);
1930
1931 // take case when mana has ended up into account
1932 currentAbsorb = currentAbsorb ? int32(float(currentAbsorb) * (float(manaTaken) / float(manaReduction))) : 0;
1933
1934 damageInfo.AbsorbDamage(currentAbsorb);
1935
1936 tempAbsorb = currentAbsorb;
1937 absorbAurEff->GetBase()->CallScriptEffectAfterManaShieldHandlers(absorbAurEff, aurApp, damageInfo, tempAbsorb);
1938
1939 // Check if our aura is using amount to count damage
1940 if (absorbAurEff->GetAmount() >= 0)
1941 {
1942 absorbAurEff->ChangeAmount(absorbAurEff->GetAmount() - currentAbsorb);
1943 if ((absorbAurEff->GetAmount() <= 0))
1944 absorbAurEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
1945 }
1946 }
1947
1949 damageInfo.ModifyDamage(absorbIgnoringDamage);
1950
1951 if (currentAbsorb)
1952 {
1954 absorbLog.Attacker = damageInfo.GetAttacker() ? damageInfo.GetAttacker()->GetGUID() : ObjectGuid::Empty;
1955 absorbLog.Victim = damageInfo.GetVictim()->GetGUID();
1956 absorbLog.Caster = absorbAurEff->GetBase()->GetCasterGUID();
1957 absorbLog.AbsorbedSpellID = damageInfo.GetSpellInfo() ? damageInfo.GetSpellInfo()->Id : 0;
1958 absorbLog.AbsorbSpellID = absorbAurEff->GetId();
1959 absorbLog.Absorbed = currentAbsorb;
1960 absorbLog.OriginalDamage = damageInfo.GetOriginalDamage();
1961 absorbLog.LogData.Initialize(damageInfo.GetVictim());
1962 damageInfo.GetVictim()->SendCombatLogMessage(&absorbLog);
1963 }
1964 }
1965
1966 // split damage auras - only when not damaging self
1967 if (damageInfo.GetVictim() != damageInfo.GetAttacker())
1968 {
1969 // We're going to call functions which can modify content of the list during iteration over it's elements
1970 // Let's copy the list so we can prevent iterator invalidation
1972 for (auto itr = vSplitDamagePctCopy.begin(); itr != vSplitDamagePctCopy.end() && damageInfo.GetDamage() > 0; ++itr)
1973 {
1974 // Check if aura was removed during iteration - we don't need to work on such auras
1975 AuraApplication const* aurApp = (*itr)->GetBase()->GetApplicationOfTarget(damageInfo.GetVictim()->GetGUID());
1976 if (!aurApp)
1977 continue;
1978
1979 // check damage school mask
1980 if (!((*itr)->GetMiscValue() & damageInfo.GetSchoolMask()))
1981 continue;
1982
1983 // Damage can be splitted only if aura has an alive caster
1984 Unit* caster = (*itr)->GetCaster();
1985 if (!caster || (caster == damageInfo.GetVictim()) || !caster->IsInWorld() || !caster->IsAlive())
1986 continue;
1987
1988 uint32 splitDamage = CalculatePct(damageInfo.GetDamage(), (*itr)->GetAmount());
1989
1990 (*itr)->GetBase()->CallScriptEffectSplitHandlers((*itr), aurApp, damageInfo, splitDamage);
1991
1992 // absorb must be smaller than the damage itself
1993 splitDamage = RoundToInterval(splitDamage, uint32(0), uint32(damageInfo.GetDamage()));
1994
1995 damageInfo.AbsorbDamage(splitDamage);
1996
1997 // check if caster is immune to damage
1998 if (caster->IsImmunedToDamage(damageInfo.GetSchoolMask()))
1999 {
2000 damageInfo.GetVictim()->SendSpellMiss(caster, (*itr)->GetSpellInfo()->Id, SPELL_MISS_IMMUNE);
2001 continue;
2002 }
2003
2004 uint32 split_absorb = 0;
2005 Unit::DealDamageMods(damageInfo.GetAttacker(), caster, splitDamage, &split_absorb);
2006
2007 // sparring
2008 if (Creature* victimCreature = damageInfo.GetVictim()->ToCreature())
2009 {
2010 if (victimCreature->ShouldFakeDamageFrom(damageInfo.GetAttacker()))
2011 damageInfo.ModifyDamage(damageInfo.GetDamage() * -1);
2012 }
2013
2014 SpellNonMeleeDamage log(damageInfo.GetAttacker(), caster, (*itr)->GetSpellInfo(), (*itr)->GetBase()->GetSpellVisual(), damageInfo.GetSchoolMask(), (*itr)->GetBase()->GetCastId());
2015 CleanDamage cleanDamage = CleanDamage(splitDamage, 0, BASE_ATTACK, MELEE_HIT_NORMAL);
2016 Unit::DealDamage(damageInfo.GetAttacker(), caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, damageInfo.GetSchoolMask(), (*itr)->GetSpellInfo(), false);
2017 log.damage = splitDamage;
2018 log.originalDamage = splitDamage;
2019 log.absorb = split_absorb;
2020 caster->SendSpellNonMeleeDamageLog(&log);
2021
2022 // break 'Fear' and similar auras
2024 }
2025 }
2026}
2027
2028/*static*/ void Unit::CalcHealAbsorb(HealInfo& healInfo)
2029{
2030 if (!healInfo.GetHeal())
2031 return;
2032
2034 for (auto i = vHealAbsorb.begin(); i != vHealAbsorb.end() && healInfo.GetHeal() > 0; ++i)
2035 {
2036 AuraEffect* absorbAurEff = *i;
2037 // Check if aura was removed during iteration - we don't need to work on such auras
2038 AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(healInfo.GetTarget()->GetGUID());
2039 if (!aurApp)
2040 continue;
2041 if (!(absorbAurEff->GetMiscValue() & healInfo.GetSchoolMask()))
2042 continue;
2043
2044 // get amount which can be still absorbed by the aura
2045 int32 currentAbsorb = absorbAurEff->GetAmount();
2046 // aura with infinite absorb amount - let the scripts handle absorbtion amount, set here to 0 for safety
2047 if (currentAbsorb < 0)
2048 currentAbsorb = 0;
2049
2050 uint32 tempAbsorb = uint32(currentAbsorb);
2051
2052 bool defaultPrevented = false;
2053
2054 absorbAurEff->GetBase()->CallScriptEffectAbsorbHandlers(absorbAurEff, aurApp, healInfo, tempAbsorb, defaultPrevented);
2055 currentAbsorb = tempAbsorb;
2056
2057 if (!defaultPrevented)
2058 {
2059 // absorb must be smaller than the heal itself
2060 currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(healInfo.GetHeal()));
2061
2062 healInfo.AbsorbHeal(currentAbsorb);
2063
2064 tempAbsorb = currentAbsorb;
2065 absorbAurEff->GetBase()->CallScriptEffectAfterAbsorbHandlers(absorbAurEff, aurApp, healInfo, tempAbsorb);
2066
2067 // Check if our aura is using amount to count heal
2068 if (absorbAurEff->GetAmount() >= 0)
2069 {
2070 // Reduce shield amount
2071 absorbAurEff->ChangeAmount(absorbAurEff->GetAmount() - currentAbsorb);
2072 // Aura cannot absorb anything more - remove it
2073 if (absorbAurEff->GetAmount() <= 0)
2074 absorbAurEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
2075 }
2076 }
2077
2078 if (currentAbsorb)
2079 {
2081 absorbLog.Healer = healInfo.GetHealer() ? healInfo.GetHealer()->GetGUID() : ObjectGuid::Empty;
2082 absorbLog.Target = healInfo.GetTarget()->GetGUID();
2083 absorbLog.AbsorbCaster = absorbAurEff->GetBase()->GetCasterGUID();
2084 absorbLog.AbsorbedSpellID = healInfo.GetSpellInfo() ? healInfo.GetSpellInfo()->Id : 0;
2085 absorbLog.AbsorbSpellID = absorbAurEff->GetId();
2086 absorbLog.Absorbed = currentAbsorb;
2087 absorbLog.OriginalHeal = healInfo.GetOriginalHeal();
2088 healInfo.GetTarget()->SendMessageToSet(absorbLog.Write(), true);
2089 }
2090 }
2091}
2092
2094{
2096 return;
2097
2099 return;
2100
2101 if (IsCreature() && !ToCreature()->CanMelee())
2102 return;
2103
2105 {
2107 if (!channeledSpell || !channeledSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_ALLOW_ACTIONS_DURING_CHANNEL))
2108 return;
2109 }
2110
2111 Unit* victim = GetVictim();
2112 if (!victim)
2113 return;
2114
2115 auto getAutoAttackError = [&]() -> Optional<AttackSwingErr>
2116 {
2117 if (!IsWithinMeleeRange(victim))
2119
2120 //120 degrees of radiant range, if player is not in boundary radius
2121 if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
2123
2124 return {};
2125 };
2126
2128 {
2129 Optional<AttackSwingErr> autoAttackError = getAutoAttackError();
2130 if (!autoAttackError)
2131 {
2132 // prevent base and off attack in same time, delay attack at 0.2 sec
2133 if (haveOffhandWeapon())
2136
2137 // do attack
2140 }
2141 else
2143
2144 if (Player* attackerPlayer = ToPlayer())
2145 attackerPlayer->SetAttackSwingError(autoAttackError);
2146 }
2147
2149 {
2150 Optional<AttackSwingErr> autoAttackError = getAutoAttackError();
2151 if (!autoAttackError)
2152 {
2153 // prevent base and off attack in same time, delay attack at 0.2 sec
2156
2157 // do attack
2160 }
2161 else
2163 }
2164}
2165
2166void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra)
2167{
2169 return;
2170
2172 return;
2173
2175 return;
2176
2177 if (!victim->IsAlive())
2178 return;
2179
2180 if ((attType == BASE_ATTACK || attType == OFF_ATTACK) && !IsWithinLOSInMap(victim))
2181 return;
2182
2183 AtTargetAttacked(victim, true);
2185
2186 if (attType != BASE_ATTACK && attType != OFF_ATTACK)
2187 return; // ignore ranged case
2188
2189 if (!extra && _lastExtraAttackSpell)
2191
2192 // melee attack spell cast at main hand attack only - no normal melee dmg dealt
2193 if (attType == BASE_ATTACK && m_currentSpells[CURRENT_MELEE_SPELL] && !extra)
2195 else
2196 {
2197 // attack can be redirected to another target
2198 victim = GetMeleeHitRedirectTarget(victim);
2199
2201 uint32 meleeAttackSpellId = 0;
2202 if (attType == BASE_ATTACK)
2203 {
2204 if (!meleeAttackOverrides.empty())
2205 meleeAttackSpellId = meleeAttackOverrides.front()->GetSpellEffectInfo().TriggerSpell;
2206 }
2207 else
2208 {
2209 auto itr = std::find_if(meleeAttackOverrides.begin(), meleeAttackOverrides.end(), [&](AuraEffect const* aurEff)
2210 {
2211 return aurEff->GetSpellEffectInfo().MiscValue != 0;
2212 });
2213 if (itr != meleeAttackOverrides.end())
2214 meleeAttackSpellId = (*itr)->GetSpellEffectInfo().MiscValue;
2215 }
2216
2217 if (!meleeAttackSpellId)
2218 {
2219 CalcDamageInfo damageInfo;
2220 CalculateMeleeDamage(victim, &damageInfo, attType);
2221 // Send log damage message to client
2222 Unit::DealDamageMods(damageInfo.Attacker, victim, damageInfo.Damage, &damageInfo.Absorb);
2223
2224 // sparring
2225 if (Creature* victimCreature = victim->ToCreature())
2226 {
2227 if (victimCreature->ShouldFakeDamageFrom(damageInfo.Attacker))
2228 damageInfo.HitInfo |= HITINFO_FAKE_DAMAGE;
2229 }
2230
2231 SendAttackStateUpdate(&damageInfo);
2232
2233 _lastDamagedTargetGuid = victim->GetGUID();
2234
2235 DealMeleeDamage(&damageInfo, true);
2236
2237 DamageInfo dmgInfo(damageInfo);
2238 Unit::ProcSkillsAndAuras(damageInfo.Attacker, damageInfo.Target, damageInfo.ProcAttacker, damageInfo.ProcVictim, PROC_SPELL_TYPE_NONE, PROC_SPELL_PHASE_NONE, dmgInfo.GetHitMask(), nullptr, &dmgInfo, nullptr);
2239
2240 TC_LOG_DEBUG("entities.unit", "AttackerStateUpdate: {} attacked {} for {} dmg, absorbed {}, blocked {}, resisted {}.",
2241 GetGUID().ToString(), victim->GetGUID().ToString(), damageInfo.Damage, damageInfo.Absorb, damageInfo.Blocked, damageInfo.Resist);
2242 }
2243 else
2244 {
2245 CastSpell(victim, meleeAttackSpellId, true);
2246
2248 if (attType == OFF_ATTACK)
2249 hitInfo |= HITINFO_OFFHAND;
2250
2251 SendAttackStateUpdate(hitInfo, victim, 0, GetMeleeDamageSchoolMask(), 0, 0, 0, VICTIMSTATE_HIT, 0);
2252 }
2253 }
2254}
2255
2257{
2258 while (count)
2259 {
2260 --count;
2261 AttackerStateUpdate(victim, BASE_ATTACK, true);
2262 }
2263}
2264
2266{
2268 if (!targetGUID)
2269 {
2270 ObjectGuid selection = GetTarget();
2271 if (!selection.IsEmpty())
2272 targetGUID = selection; // Spell was cast directly (not triggered by aura)
2273 else
2274 return;
2275 }
2276
2277 extraAttacksTargets[targetGUID] += count;
2278}
2279
2281{
2282 if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())
2283 return MELEE_HIT_EVADE;
2284
2285 // Miss chance based on melee
2286 int32 miss_chance = int32(MeleeSpellMissChance(victim, attType, nullptr) * 100.0f);
2287
2288 // Critical hit chance
2290
2291 int32 dodge_chance = int32(GetUnitDodgeChance(attType, victim) * 100.0f);
2292 int32 block_chance = int32(GetUnitBlockChance(attType, victim) * 100.0f);
2293 int32 parry_chance = int32(GetUnitParryChance(attType, victim) * 100.0f);
2294
2295 // melee attack table implementation
2296 // outcome priority:
2297 // 1. > 2. > 3. > 4. > 5. > 6. > 7. > 8.
2298 // MISS > DODGE > PARRY > GLANCING > BLOCK > CRIT > CRUSHING > HIT
2299
2300 int32 sum = 0, tmp = 0;
2301 int32 roll = urand(0, 9999);
2302
2303 int32 attackerLevel = GetLevelForTarget(victim);
2304 int32 victimLevel = GetLevelForTarget(this);
2305
2306 // check if attack comes from behind, nobody can parry or block if attacker is behind
2307 bool canParryOrBlock = victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION);
2308
2309 // only creatures can dodge if attacker is behind
2310 bool canDodge = victim->GetTypeId() != TYPEID_PLAYER || canParryOrBlock;
2311
2312 // if victim is casting or cc'd it can't avoid attacks
2313 if (victim->IsNonMeleeSpellCast(false, false, true) || victim->HasUnitState(UNIT_STATE_CONTROLLED))
2314 {
2315 canDodge = false;
2316 canParryOrBlock = false;
2317 }
2318
2319 // 1. MISS
2320 tmp = miss_chance;
2321 if (tmp > 0 && roll < (sum += tmp))
2322 return MELEE_HIT_MISS;
2323
2324 // always crit against a sitting target (except 0 crit chance)
2325 if (victim->GetTypeId() == TYPEID_PLAYER && crit_chance > 0 && !victim->IsStandState())
2326 return MELEE_HIT_CRIT;
2327
2328 // 2. DODGE
2329 if (canDodge)
2330 {
2331 tmp = dodge_chance;
2332 if (tmp > 0 // check if unit _can_ dodge
2333 && roll < (sum += tmp))
2334 return MELEE_HIT_DODGE;
2335 }
2336
2337 // 3. PARRY
2338 if (canParryOrBlock)
2339 {
2340 tmp = parry_chance;
2341 if (tmp > 0 // check if unit _can_ parry
2342 && roll < (sum += tmp))
2343 return MELEE_HIT_PARRY;
2344 }
2345
2346 // 4. GLANCING
2347 // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon)
2348 if ((GetTypeId() == TYPEID_PLAYER || IsPet()) &&
2349 victim->GetTypeId() != TYPEID_PLAYER && !victim->IsPet() &&
2350 attackerLevel + 3 < victimLevel)
2351 {
2352 // cap possible value (with bonuses > max skill)
2353 tmp = (10 + 10 * (victimLevel - attackerLevel)) * 100;
2354 if (tmp > 0 && roll < (sum += tmp))
2355 return MELEE_HIT_GLANCING;
2356 }
2357
2358 // 5. BLOCK
2359 if (canParryOrBlock)
2360 {
2361 tmp = block_chance;
2362 if (tmp > 0 // check if unit _can_ block
2363 && roll < (sum += tmp))
2364 return MELEE_HIT_BLOCK;
2365 }
2366
2367 // 6.CRIT
2368 tmp = crit_chance;
2369 if (tmp > 0 && roll < (sum += tmp))
2370 return MELEE_HIT_CRIT;
2371
2372 // 7. CRUSHING
2373 // mobs can score crushing blows if they're 4 or more levels above victim
2374 if (attackerLevel >= victimLevel + 4 &&
2375 // can be from by creature (if can) or from controlled player that considered as creature
2378 {
2379 // add 2% chance per level, min. is 15%
2380 tmp = attackerLevel - victimLevel * 1000 - 1500;
2381 if (roll < (sum += tmp))
2382 {
2383 TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRUSHING <{}, {})", sum-tmp, sum);
2384 return MELEE_HIT_CRUSHING;
2385 }
2386 }
2387
2388 // 8. HIT
2389 return MELEE_HIT_NORMAL;
2390}
2391
2392uint32 Unit::CalculateDamage(WeaponAttackType attType, bool normalized, bool addTotalPct) const
2393{
2394 float minDamage = 0.0f;
2395 float maxDamage = 0.0f;
2396
2397 if (normalized || !addTotalPct)
2398 {
2399 CalculateMinMaxDamage(attType, normalized, addTotalPct, minDamage, maxDamage);
2400 if (IsInFeralForm() && attType == BASE_ATTACK)
2401 {
2402 float minOffhandDamage = 0.0f;
2403 float maxOffhandDamage = 0.0f;
2404 CalculateMinMaxDamage(OFF_ATTACK, normalized, addTotalPct, minOffhandDamage, maxOffhandDamage);
2405 minDamage += minOffhandDamage;
2406 maxDamage += maxOffhandDamage;
2407 }
2408 }
2409 else
2410 {
2411 switch (attType)
2412 {
2413 case RANGED_ATTACK:
2414 minDamage = m_unitData->MinRangedDamage;
2415 maxDamage = m_unitData->MaxRangedDamage;
2416 break;
2417 case BASE_ATTACK:
2418 minDamage = m_unitData->MinDamage;
2419 maxDamage = m_unitData->MaxDamage;
2420 if (IsInFeralForm())
2421 {
2422 minDamage += m_unitData->MinOffHandDamage;
2423 maxDamage += m_unitData->MaxOffHandDamage;
2424 }
2425 break;
2426 case OFF_ATTACK:
2427 minDamage = m_unitData->MinOffHandDamage;
2428 maxDamage = m_unitData->MaxOffHandDamage;
2429 break;
2430 default:
2431 break;
2432 }
2433 }
2434
2435 minDamage = std::max(0.f, minDamage);
2436 maxDamage = std::max(0.f, maxDamage);
2437
2438 if (minDamage > maxDamage)
2439 std::swap(minDamage, maxDamage);
2440
2441 return urand(uint32(minDamage), uint32(maxDamage));
2442}
2443
2445{
2447 packet.Attacker = GetGUID();
2448 packet.Victim = victim->GetGUID();
2449 SendMessageToSet(packet.Write(), true);
2450}
2451
2453{
2454 SendMessageToSet(WorldPackets::Combat::SAttackStop(this, victim).Write(), true);
2455
2456 if (victim)
2457 TC_LOG_DEBUG("entities.unit", "{} stopped attacking {}", GetGUID().ToString(), victim->GetGUID().ToString());
2458 else
2459 TC_LOG_DEBUG("entities.unit", "{} stopped attacking", GetGUID().ToString());
2460}
2461
2463{
2465 return true;
2466 return false;
2467}
2468
2470{
2471 if (!spellInfo)
2472 return 0;
2473
2474 int32 resistMech = 0;
2475 for (SpellEffectInfo const& effect : spellInfo->GetEffects())
2476 {
2477 if (!effect.IsEffect())
2478 break;
2479
2480 int32 effectMech = spellInfo->GetEffectMechanic(effect.EffectIndex);
2481 if (effectMech)
2482 {
2484 if (resistMech < temp)
2485 resistMech = temp;
2486 }
2487 }
2488
2489 return std::max(resistMech, 0);
2490}
2491
2492bool Unit::CanUseAttackType(uint8 attacktype) const
2493{
2494 switch (attacktype)
2495 {
2496 case BASE_ATTACK:
2498 case OFF_ATTACK:
2500 case RANGED_ATTACK:
2502 default:
2503 return true;
2504 }
2505}
2506
2507// Melee based spells hit result calculations
2509{
2510 if (spellInfo->HasAttribute(SPELL_ATTR3_NO_AVOIDANCE))
2511 return SPELL_MISS_NONE;
2512
2513 WeaponAttackType attType = BASE_ATTACK;
2514
2515 // Check damage class instead of attack type to correctly handle judgements
2516 // - they are meele, but can't be dodged/parried/deflected because of ranged dmg class
2517 if (spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED)
2518 attType = RANGED_ATTACK;
2519
2520 uint32 roll = urand(0, 9999);
2521
2522 uint32 missChance = uint32(MeleeSpellMissChance(victim, attType, spellInfo) * 100.0f);
2523
2524 // Roll miss
2525 uint32 tmp = missChance;
2526 if (roll < tmp)
2527 return SPELL_MISS_MISS;
2528
2529 // Chance resist mechanic
2530 int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100;
2531 tmp += resist_chance;
2532 if (roll < tmp)
2533 return SPELL_MISS_RESIST;
2534
2535 // Same spells cannot be parried/dodged
2537 return SPELL_MISS_NONE;
2538
2539 bool canDodge = !spellInfo->HasAttribute(SPELL_ATTR7_NO_ATTACK_DODGE);
2540 bool canParry = !spellInfo->HasAttribute(SPELL_ATTR7_NO_ATTACK_PARRY);
2541 bool canBlock = !spellInfo->HasAttribute(SPELL_ATTR8_NO_ATTACK_BLOCK);
2542
2543 // if victim is casting or cc'd it can't avoid attacks
2544 if (victim->IsNonMeleeSpellCast(false, false, true) || victim->HasUnitState(UNIT_STATE_CONTROLLED))
2545 {
2546 canDodge = false;
2547 canParry = false;
2548 canBlock = false;
2549 }
2550
2551 // Ranged attacks can only miss, resist and deflect and get blocked
2552 if (attType == RANGED_ATTACK)
2553 {
2554 canParry = false;
2555 canDodge = false;
2556
2557 // only if in front
2558 if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)))
2559 {
2560 int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100;
2561 tmp += deflect_chance;
2562 if (roll < tmp)
2563 return SPELL_MISS_DEFLECT;
2564 }
2565 }
2566
2567 // Check for attack from behind
2568 if (!victim->HasInArc(float(M_PI), this))
2569 {
2571 {
2572 // Can't dodge from behind in PvP (but its possible in PvE)
2573 if (victim->GetTypeId() == TYPEID_PLAYER)
2574 canDodge = false;
2575 // Can't parry or block
2576 canParry = false;
2577 canBlock = false;
2578 }
2579 else // Only deterrence as of 3.3.5
2580 {
2582 canParry = false;
2583 }
2584 }
2585
2586 // Ignore combat result aura
2588 for (AuraEffect const* aurEff : ignore)
2589 {
2590 if (!aurEff->IsAffectingSpell(spellInfo))
2591 continue;
2592
2593 switch (aurEff->GetMiscValue())
2594 {
2595 case MELEE_HIT_DODGE:
2596 canDodge = false;
2597 break;
2598 case MELEE_HIT_BLOCK:
2599 canBlock = false;
2600 break;
2601 case MELEE_HIT_PARRY:
2602 canParry = false;
2603 break;
2604 default:
2605 TC_LOG_DEBUG("entities.unit", "Spell {} SPELL_AURA_IGNORE_COMBAT_RESULT has unhandled state {}", aurEff->GetId(), aurEff->GetMiscValue());
2606 break;
2607 }
2608 }
2609
2610 if (canDodge)
2611 {
2612 // Roll dodge
2613 int32 dodgeChance = int32(GetUnitDodgeChance(attType, victim) * 100.0f);
2614 if (dodgeChance < 0)
2615 dodgeChance = 0;
2616
2617 if (roll < (tmp += dodgeChance))
2618 return SPELL_MISS_DODGE;
2619 }
2620
2621 if (canParry)
2622 {
2623 // Roll parry
2624 int32 parryChance = int32(GetUnitParryChance(attType, victim) * 100.0f);
2625 if (parryChance < 0)
2626 parryChance = 0;
2627
2628 tmp += parryChance;
2629 if (roll < tmp)
2630 return SPELL_MISS_PARRY;
2631 }
2632
2633 if (canBlock)
2634 {
2635 int32 blockChance = int32(GetUnitBlockChance(attType, victim) * 100.0f);
2636 if (blockChance < 0)
2637 blockChance = 0;
2638 tmp += blockChance;
2639
2640 if (roll < tmp)
2641 return SPELL_MISS_BLOCK;
2642 }
2643
2644 return SPELL_MISS_NONE;
2645}
2646
2647float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const
2648{
2649 int32 const levelDiff = victim->GetLevelForTarget(this) - GetLevelForTarget(victim);
2650
2651 float chance = 0.0f;
2652 float levelBonus = 0.0f;
2653 if (Player const* playerVictim = victim->ToPlayer())
2654 chance = playerVictim->m_activePlayerData->DodgePercentage;
2655 else
2656 {
2657 if (!victim->IsTotem())
2658 {
2659 chance = 3.0f;
2661
2662 if (levelDiff > 0)
2663 levelBonus = 1.5f * levelDiff;
2664 }
2665 }
2666
2667 chance += levelBonus;
2668
2669 // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE
2671
2672 // reduce dodge by SPELL_AURA_MOD_ENEMY_DODGE
2674
2675 // Reduce dodge chance by attacker expertise rating
2676 if (GetTypeId() == TYPEID_PLAYER)
2677 chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType);
2678 else
2680 return std::max(chance, 0.0f);
2681}
2682
2683float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const
2684{
2685 int32 const levelDiff = victim->GetLevelForTarget(this) - GetLevelForTarget(victim);
2686
2687 float chance = 0.0f;
2688 float levelBonus = 0.0f;
2689 if (Player const* playerVictim = victim->ToPlayer())
2690 {
2691 if (playerVictim->CanParry())
2692 {
2693 Item* tmpitem = playerVictim->GetWeaponForAttack(BASE_ATTACK, true);
2694 if (!tmpitem)
2695 tmpitem = playerVictim->GetWeaponForAttack(OFF_ATTACK, true);
2696
2697 if (tmpitem)
2698 chance = playerVictim->m_activePlayerData->ParryPercentage;
2699 }
2700 }
2701 else
2702 {
2704 {
2705 chance = 6.0f;
2707
2708 if (levelDiff > 0)
2709 levelBonus = 1.5f * levelDiff;
2710 }
2711 }
2712
2713 chance += levelBonus;
2714
2715 // Reduce parry chance by attacker expertise rating
2716 if (GetTypeId() == TYPEID_PLAYER)
2717 chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType);
2718 else
2720 return std::max(chance, 0.0f);
2721}
2722
2724{
2725 float miss_chance = 5.0f;
2726
2727 return miss_chance;
2728}
2729
2730float Unit::GetUnitBlockChance(WeaponAttackType /*attType*/, Unit const* victim) const
2731{
2732 int32 const levelDiff = victim->GetLevelForTarget(this) - GetLevelForTarget(victim);
2733
2734 float chance = 0.0f;
2735 float levelBonus = 0.0f;
2736 if (Player const* playerVictim = victim->ToPlayer())
2737 {
2738 if (playerVictim->CanBlock())
2739 {
2740 Item* tmpitem = playerVictim->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
2741 if (tmpitem && !tmpitem->IsBroken() && tmpitem->GetTemplate()->GetInventoryType() == INVTYPE_SHIELD)
2742 chance = playerVictim->m_activePlayerData->BlockPercentage;
2743 }
2744 }
2745 else
2746 {
2748 {
2749 chance = 3.0f;
2751
2752 if (levelDiff > 0)
2753 levelBonus = 1.5f * levelDiff;
2754 }
2755 }
2756
2757 chance += levelBonus;
2758 return std::max(chance, 0.0f);
2759}
2760
2762{
2763 float chance = 0.0f;
2764 if (Player const* thisPlayer = ToPlayer())
2765 {
2766 switch (attackType)
2767 {
2768 case BASE_ATTACK:
2769 chance = thisPlayer->m_activePlayerData->CritPercentage;
2770 break;
2771 case OFF_ATTACK:
2772 chance = thisPlayer->m_activePlayerData->OffhandCritPercentage;
2773 break;
2774 case RANGED_ATTACK:
2775 chance = thisPlayer->m_activePlayerData->RangedCritPercentage;
2776 break;
2777 // Just for good manner
2778 default:
2779 chance = 0.0f;
2780 break;
2781 }
2782 }
2783 else
2784 {
2785 if (!(ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT))
2786 {
2787 chance = 5.0f;
2790 }
2791 }
2792
2793 return chance;
2794}
2795
2796float Unit::GetUnitCriticalChanceTaken(Unit const* attacker, WeaponAttackType attackType, float critDone) const
2797{
2798 float chance = critDone;
2799
2800 // flat aura mods
2801 if (attackType != RANGED_ATTACK)
2803
2805 {
2806 return !HealthBelowPct(aurEff->GetMiscValueB());
2807 });
2808
2809 chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [attacker](AuraEffect const* aurEff) -> bool
2810 {
2811 return aurEff->GetCasterGUID() == attacker->GetGUID();
2812 });
2813
2814 if (TempSummon const* tempSummon = attacker->ToTempSummon())
2815 {
2816 chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER_PET, [tempSummon](AuraEffect const* aurEff) -> bool
2817 {
2818 return aurEff->GetCasterGUID() == tempSummon->GetSummonerGUID();
2819 });
2820 }
2821
2823
2824 return std::max(chance, 0.0f);
2825}
2826
2827float Unit::GetUnitCriticalChanceAgainst(WeaponAttackType attackType, Unit const* victim) const
2828{
2829 float chance = GetUnitCriticalChanceDone(attackType);
2830 return victim->GetUnitCriticalChanceTaken(this, attackType, chance);
2831}
2832
2834{
2835 while (!m_removedAuras.empty())
2836 {
2837 delete m_removedAuras.front();
2838 m_removedAuras.pop_front();
2839 }
2840
2842}
2843
2845{
2846 if (!_spellHistory->IsPaused())
2847 _spellHistory->Update();
2848
2851
2852 // remove finished spells from current pointers
2853 for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
2854 {
2855 if (m_currentSpells[i] && m_currentSpells[i]->getState() == SPELL_STATE_FINISHED)
2856 {
2857 m_currentSpells[i]->SetReferencedFromCurrent(false);
2858 m_currentSpells[i] = nullptr; // remove pointer
2859 }
2860 }
2861
2862 // m_auraUpdateIterator can be updated in indirect called code at aura remove to skip next planned to update but removed auras
2864 {
2865 Aura* i_aura = m_auraUpdateIterator->second;
2866 ++m_auraUpdateIterator; // need shift to next for allow update if need into aura update
2867 i_aura->UpdateOwner(time, this);
2868 }
2869
2870 // remove expired auras - do that after updates(used in scripts?)
2871 for (AuraMap::iterator i = m_ownedAuras.begin(); i != m_ownedAuras.end();)
2872 {
2873 if (i->second->IsExpired())
2875 else if (i->second->GetSpellInfo()->IsChanneled() && i->second->GetCasterGUID() != GetGUID() && !ObjectAccessor::GetWorldObject(*this, i->second->GetCasterGUID()))
2876 RemoveOwnedAura(i, AURA_REMOVE_BY_CANCEL); // remove channeled auras when caster is not on the same map
2877 else
2878 ++i;
2879 }
2880
2881 for (AuraApplication* visibleAura : m_visibleAurasToUpdate)
2882 visibleAura->ClientUpdate();
2883
2884 m_visibleAurasToUpdate.clear();
2885
2887
2888 if (!m_gameObj.empty())
2889 {
2890 GameObjectList::iterator itr;
2891 for (itr = m_gameObj.begin(); itr != m_gameObj.end();)
2892 {
2893 if (!(*itr)->isSpawned())
2894 {
2895 (*itr)->SetOwnerGUID(ObjectGuid::Empty);
2896 (*itr)->SetRespawnTime(0);
2897 (*itr)->Delete();
2898 m_gameObj.erase(itr++);
2899 }
2900 else
2901 ++itr;
2902 }
2903 }
2904}
2905
2907{
2908 SpellInfo const* autoRepeatSpellInfo = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo;
2909
2910 // check "realtime" interrupts
2911 // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect
2912 if ((isMoving() && m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CheckMovement() != SPELL_CAST_OK) || IsNonMeleeSpellCast(false, false, true, autoRepeatSpellInfo->Id == 75))
2913 {
2914 // cancel wand shoot
2915 if (autoRepeatSpellInfo->Id != 75)
2917 return;
2918 }
2919
2920 // castroutine
2922 {
2923 // Check if able to cast
2924 SpellCastResult result = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CheckCast(true);
2925 if (result != SPELL_CAST_OK)
2926 {
2927 if (autoRepeatSpellInfo->Id != 75)
2929 else if (GetTypeId() == TYPEID_PLAYER)
2930 Spell::SendCastResult(ToPlayer(), autoRepeatSpellInfo, m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_SpellVisual, m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_castId, result);
2931
2932 return;
2933 }
2934
2935 // we want to shoot
2936 Spell* spell = new Spell(this, autoRepeatSpellInfo, TRIGGERED_IGNORE_GCD);
2938 }
2939}
2940
2942{
2943 ASSERT(pSpell); // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells
2944
2945 CurrentSpellTypes CSpellType = pSpell->GetCurrentContainer();
2946
2947 if (pSpell == m_currentSpells[CSpellType]) // avoid breaking self
2948 return;
2949
2950 // special breakage effects:
2951 switch (CSpellType)
2952 {
2954 {
2956
2957 // generic spells always break channeled not delayed spells
2960
2961 // autorepeat breaking
2963 {
2964 // break autorepeat if not Auto Shot
2965 if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 75)
2967 }
2968 if (pSpell->GetCastTime() > 0)
2970
2971 break;
2972 }
2974 {
2975 // channel spells always break generic non-delayed and any channeled spells
2978
2979 // it also does break autorepeat if not Auto Shot
2981 m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 75)
2984
2985 break;
2986 }
2988 {
2989 if (m_currentSpells[CSpellType] && m_currentSpells[CSpellType]->getState() == SPELL_STATE_IDLE)
2990 m_currentSpells[CSpellType]->setState(SPELL_STATE_FINISHED);
2991
2992 // only Auto Shoot does not break anything
2993 if (pSpell->GetSpellInfo()->Id != 75)
2994 {
2995 // generic autorepeats break generic non-delayed and channeled non-delayed spells
2998 }
2999
3000 break;
3001 }
3002 default:
3003 break; // other spell types don't break anything now
3004 }
3005
3006 // current spell (if it is still here) may be safely deleted now
3007 if (m_currentSpells[CSpellType])
3008 m_currentSpells[CSpellType]->SetReferencedFromCurrent(false);
3009
3010 // set new current spell
3011 m_currentSpells[CSpellType] = pSpell;
3012 pSpell->SetReferencedFromCurrent(true);
3013
3014 pSpell->m_selfContainer = &(m_currentSpells[pSpell->GetCurrentContainer()]);
3015}
3016
3017void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool withInstant)
3018{
3019 //TC_LOG_DEBUG("entities.unit", "Interrupt spell for unit {}.", GetEntry());
3020 Spell* spell = m_currentSpells[spellType];
3021 if (spell
3022 && (withDelayed || spell->getState() != SPELL_STATE_DELAYED)
3023 && (withInstant || spell->GetCastTime() > 0 || spell->getState() == SPELL_STATE_CASTING))
3024 {
3025 // for example, do not let self-stun aura interrupt itself
3026 if (!spell->IsInterruptable())
3027 return;
3028
3029 // send autorepeat cancel message for autorepeat spells
3030 if (spellType == CURRENT_AUTOREPEAT_SPELL)
3031 if (GetTypeId() == TYPEID_PLAYER)
3033
3034 if (spell->getState() != SPELL_STATE_FINISHED)
3035 spell->cancel();
3036 else
3037 {
3038 m_currentSpells[spellType] = nullptr;
3039 spell->SetReferencedFromCurrent(false);
3040 }
3041
3042 if (GetTypeId() == TYPEID_UNIT && IsAIEnabled())
3043 ToCreature()->AI()->OnSpellFailed(spell->GetSpellInfo());
3044 }
3045}
3046
3047void Unit::FinishSpell(CurrentSpellTypes spellType, SpellCastResult result /*= SPELL_CAST_OK*/)
3048{
3049 Spell* spell = m_currentSpells[spellType];
3050 if (!spell)
3051 return;
3052
3053 if (spellType == CURRENT_CHANNELED_SPELL)
3054 spell->SendChannelUpdate(0);
3055
3056 spell->finish(result);
3057}
3058
3059bool Unit::IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled, bool skipAutorepeat, bool isAutoshoot, bool skipInstant) const
3060{
3061 // We don't do loop here to explicitly show that melee spell is excluded.
3062 // Maybe later some special spells will be excluded too.
3063
3064 // generic spells are cast when they are not finished and not delayed
3067 (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED))
3068 {
3069 if (!skipInstant || m_currentSpells[CURRENT_GENERIC_SPELL]->GetCastTime())
3070 {
3071 if (!isAutoshoot || !(m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS)))
3072 return true;
3073 }
3074 }
3075 // channeled spells may be delayed, but they are still considered cast
3076 if (!skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] &&
3078 {
3079 if (!isAutoshoot || !(m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS)))
3080 return true;
3081 }
3082 // autorepeat spells may be finished or delayed, but they are still considered cast
3083 if (!skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
3084 return true;
3085
3086 return false;
3087}
3088
3089void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id, bool withInstant)
3090{
3091 // generic spells are interrupted if they are not finished or delayed
3092 if (m_currentSpells[CURRENT_GENERIC_SPELL] && (!spell_id || m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Id == spell_id))
3093 InterruptSpell(CURRENT_GENERIC_SPELL, withDelayed, withInstant);
3094
3095 // autorepeat spells are interrupted if they are not finished or delayed
3096 if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id == spell_id))
3097 InterruptSpell(CURRENT_AUTOREPEAT_SPELL, withDelayed, withInstant);
3098
3099 // channeled spells are interrupted if they are not finished, even if they are delayed
3100 if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id == spell_id))
3102}
3103
3105{
3106 for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
3107 if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id == spell_id)
3108 return m_currentSpells[i];
3109 return nullptr;
3110}
3111
3113{
3114 if (Spell const* spell = FindCurrentSpellBySpellId(spell_id))
3115 return spell->GetCastTime();
3116 return 0;
3117}
3118
3120{
3121 // can always move when not casting
3123 return false;
3124
3126 if (CanCastSpellWhileMoving(spell->GetSpellInfo()) || spell->getState() == SPELL_STATE_FINISHED ||
3127 !spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement))
3128 return false;
3129
3130 // channeled spells during channel stage (after the initial cast timer) allow movement with a specific spell attribute
3132 if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive())
3133 if (spell->GetSpellInfo()->IsMoveAllowedChannel() || CanCastSpellWhileMoving(spell->GetSpellInfo()))
3134 return false;
3135
3136 // prohibit movement for all other spell casts
3137 return true;
3138}
3139
3140bool Unit::CanCastSpellWhileMoving(SpellInfo const* spellInfo) const
3141{
3143 return true;
3144
3146 return true;
3147
3148 for (uint32 label : spellInfo->Labels)
3150 return true;
3151
3152 return false;
3153}
3154
3155bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const
3156{
3157 return IsWithinDistInMap(target, distance) && HasInArc(arc, target);
3158}
3159
3160bool Unit::isInBackInMap(Unit const* target, float distance, float arc) const
3161{
3162 return IsWithinDistInMap(target, distance) && !HasInArc(2 * float(M_PI) - arc, target);
3163}
3164
3166{
3167 // Aquatic creatures are not allowed to leave liquids
3168 if (!IsInWater() && c->IsAquatic())
3169 return false;
3170
3171 // Underwater special case. Some creatures may not go below liquid surfaces
3172 if (IsUnderWater() && c->CannotPenetrateWater())
3173 return false;
3174
3175 // Water checks
3176 if (IsInWater() && !c->CanEnterWater())
3177 return false;
3178
3179 // Some creatures are tied to the ocean floor and cannot chase swimming targets.
3181 return false;
3182
3183 return true;
3184}
3185
3187{
3189}
3190
3192{
3194}
3195
3197{
3199}
3200
3202{
3203 ZLiquidStatus oldLiquidStatus = GetLiquidStatus();
3205 ProcessTerrainStatusUpdate(oldLiquidStatus, data.liquidInfo);
3206}
3207
3209{
3210 if (!IsControlledByPlayer())
3211 return;
3212
3213 // remove appropriate auras if we are swimming/not swimming respectively
3214 if (IsInWater())
3216 else
3218
3219 // liquid aura handling
3220 LiquidTypeEntry const* curLiquid = nullptr;
3221 if (IsInWater() && newLiquidData)
3222 curLiquid = sLiquidTypeStore.LookupEntry(newLiquidData->entry);
3223 if (curLiquid != _lastLiquid)
3224 {
3228
3229 // Set _lastLiquid before casting liquid spell to avoid infinite loops
3230 _lastLiquid = curLiquid;
3231
3232 if (curLiquid && curLiquid->SpellID && (!player || !player->IsGameMaster()))
3233 CastSpell(this, curLiquid->SpellID, true);
3234 }
3235
3236 // mount capability depends on liquid state change
3237 if (oldLiquidStatus != GetLiquidStatus())
3239}
3240
3242{
3244}
3245
3247{
3248 ASSERT(!createInfo.CasterGUID.IsEmpty() || createInfo.Caster);
3249
3250 // Check if these can stack anyway
3251 if (!createInfo.CasterGUID && !createInfo.GetSpellInfo()->IsStackableOnOneSlotWithDifferentCasters())
3252 createInfo.CasterGUID = createInfo.Caster->GetGUID();
3253
3254 // passive and Incanter's Absorption and auras with different type can stack with themselves any number of times
3255 if (!createInfo.GetSpellInfo()->IsMultiSlotAura())
3256 {
3257 // check if cast item changed
3258 ObjectGuid castItemGUID = createInfo.CastItemGUID;
3259
3260 // find current aura from spell and change it's stackamount, or refresh it's duration
3261 if (Aura* foundAura = GetOwnedAura(createInfo.GetSpellInfo()->Id, createInfo.GetSpellInfo()->IsStackableOnOneSlotWithDifferentCasters() ? ObjectGuid::Empty : createInfo.CasterGUID, createInfo.GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_ENCHANT_PROC) ? castItemGUID : ObjectGuid::Empty, 0))
3262 {
3263 // effect masks do not match
3264 // extremely rare case
3265 // let's just recreate aura
3266 if (createInfo.GetAuraEffectMask() != foundAura->GetEffectMask())
3267 return nullptr;
3268
3269 // update basepoints with new values - effect amount will be recalculated in ModStackAmount
3270 for (SpellEffectInfo const& spellEffectInfo : createInfo.GetSpellInfo()->GetEffects())
3271 {
3272 AuraEffect const* auraEff = foundAura->GetEffect(spellEffectInfo.EffectIndex);
3273 if (!auraEff)
3274 continue;
3275
3276 int32 bp;
3277 if (createInfo.BaseAmount)
3278 bp = *(createInfo.BaseAmount + spellEffectInfo.EffectIndex);
3279 else
3280 bp = int32(spellEffectInfo.BasePoints);
3281
3282 int32* oldBP = const_cast<int32*>(&(auraEff->m_baseAmount));
3283 if (spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes::AuraPointsStack))
3284 *oldBP += bp;
3285 else
3286 *oldBP = bp;
3287 }
3288
3289 // correct cast item guid if needed
3290 if (castItemGUID != foundAura->GetCastItemGUID())
3291 {
3292 ObjectGuid* oldGUID = const_cast<ObjectGuid*>(&foundAura->m_castItemGuid);
3293 *oldGUID = castItemGUID;
3294 uint32* oldItemId = const_cast<uint32*>(&foundAura->m_castItemId);
3295 *oldItemId = createInfo.CastItemId;
3296 int32* oldItemLevel = const_cast<int32*>(&foundAura->m_castItemLevel);
3297 *oldItemLevel = createInfo.CastItemLevel;
3298 }
3299
3300 // try to increase stack amount
3301 foundAura->ModStackAmount(1, AURA_REMOVE_BY_DEFAULT, createInfo.ResetPeriodicTimer);
3302 return foundAura;
3303 }
3304 }
3305
3306 return nullptr;
3307}
3308
3309void Unit::_AddAura(UnitAura* aura, Unit* caster)
3310{
3312 m_ownedAuras.emplace(aura->GetId(), aura);
3313
3314 _RemoveNoStackAurasDueToAura(aura, true);
3315
3316 if (aura->IsRemoved())
3317 return;
3318
3319 aura->SetIsSingleTarget(caster && aura->GetSpellInfo()->IsSingleTarget());
3320 if (aura->IsSingleTarget())
3321 {
3323 /* @HACK: Player is not in world during loading auras.
3324 * Single target auras are not saved or loaded from database
3325 * but may be created as a result of aura links.
3326 */
3327
3328 std::vector<Aura*> aurasSharingLimit;
3329 // remove other single target auras
3330 for (Aura* scAura : caster->GetSingleCastAuras())
3331 if (scAura->IsSingleTargetWith(aura))
3332 aurasSharingLimit.push_back(scAura);
3333
3334 // register single target aura
3335 caster->GetSingleCastAuras().push_front(aura);
3336
3337 uint32 maxOtherAuras = aura->GetSpellInfo()->MaxAffectedTargets - 1;
3338 while (aurasSharingLimit.size() > maxOtherAuras)
3339 {
3340 aurasSharingLimit.back()->Remove();
3341 aurasSharingLimit.pop_back();
3342 }
3343 }
3344}
3345
3346// creates aura application instance and registers it in lists
3347// aura application effects are handled separately to prevent aura list corruption
3349{
3350 // can't apply aura on unit which is going to be deleted - to not create a memory leak
3352
3353 // just return if the aura has been already removed
3354 // this can happen if OnEffectHitTarget() script hook killed the unit or the aura owner (which can be different)
3355 if (aura->IsRemoved())
3356 {
3357 TC_LOG_ERROR("spells", "Unit::_CreateAuraApplication() called with a removed aura. Check if OnEffectHitTarget() is triggering any spell with apply aura effect (that's not allowed!)\nUnit: {}\nAura: {}", GetDebugInfo(), aura->GetDebugInfo());
3358 return nullptr;
3359 }
3360
3361 // aura mustn't be already applied on target
3362 ASSERT (!aura->IsAppliedOnTarget(GetGUID()) && "Unit::_CreateAuraApplication: aura musn't be applied on target");
3363
3364 SpellInfo const* aurSpellInfo = aura->GetSpellInfo();
3365 uint32 aurId = aurSpellInfo->Id;
3366
3367 // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
3368 if (!IsAlive() && !aurSpellInfo->IsDeathPersistent() &&
3369 (GetTypeId() != TYPEID_PLAYER || !ToPlayer()->GetSession()->PlayerLoading()))
3370 return nullptr;
3371
3372 Unit* caster = aura->GetCaster();
3373
3374 AuraApplication * aurApp = new AuraApplication(this, caster, aura, effMask);
3375 m_appliedAuras.insert(AuraApplicationMap::value_type(aurId, aurApp));
3376
3377 if (aurSpellInfo->HasAnyAuraInterruptFlag())
3378 {
3379 m_interruptableAuras.push_front(aurApp);
3380 AddInterruptMask(aurSpellInfo->AuraInterruptFlags, aurSpellInfo->AuraInterruptFlags2);
3381 }
3382
3383 if (AuraStateType aState = aura->GetSpellInfo()->GetAuraState())
3384 m_auraStateAuras.insert(AuraStateAurasMap::value_type(aState, aurApp));
3385
3386 aura->_ApplyForTarget(this, caster, aurApp);
3387 return aurApp;
3388}
3389
3390void Unit::_ApplyAuraEffect(Aura* aura, uint8 effIndex)
3391{
3392 ASSERT(aura);
3393 ASSERT(aura->HasEffect(effIndex));
3395 ASSERT(aurApp);
3396 if (!aurApp->GetEffectMask())
3397 _ApplyAura(aurApp, 1 << effIndex);
3398 else
3399 aurApp->_HandleEffect(effIndex, true);
3400}
3401
3402// handles effects of aura application
3403// should be done after registering aura in lists
3405{
3406 Aura* aura = aurApp->GetBase();
3407
3408 _RemoveNoStackAurasDueToAura(aura, false);
3409
3410 if (aurApp->GetRemoveMode())
3411 return;
3412
3413 // Update target aura state flag
3414 if (AuraStateType aState = aura->GetSpellInfo()->GetAuraState())
3415 {
3416 uint32 aStateMask = (1 << (aState - 1));
3417 // force update so the new caster registers it
3418 if ((aStateMask & PER_CASTER_AURA_STATE_MASK) && *m_unitData->AuraState & aStateMask)
3420 else
3421 ModifyAuraState(aState, true);
3422 }
3423
3424 if (aurApp->GetRemoveMode())
3425 return;
3426
3427 // Sitdown on apply aura req seated
3430
3431 Unit* caster = aura->GetCaster();
3432
3433 if (aurApp->GetRemoveMode())
3434 return;
3435
3436 aura->HandleAuraSpecificMods(aurApp, caster, true, false);
3437
3438 // apply effects of the aura
3439 for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
3440 {
3441 if (effMask & 1 << i && (!aurApp->GetRemoveMode()))
3442 aurApp->_HandleEffect(i, true);
3443 }
3444
3445 if (Player* player = ToPlayer())
3446 {
3447 if (sConditionMgr->IsSpellUsedInSpellClickConditions(aurApp->GetBase()->GetId()))
3448 player->UpdateVisibleObjectInteractions(false, true, false, false);
3449
3450 player->FailCriteria(CriteriaFailEvent::GainAura, aurApp->GetBase()->GetId());
3451 player->StartCriteria(CriteriaStartEvent::GainAura, aurApp->GetBase()->GetId());
3452 player->UpdateCriteria(CriteriaType::GainAura, aurApp->GetBase()->GetId());
3453 }
3454}
3455
3456// removes aura application from lists and unapplies effects
3457void Unit::_UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveMode removeMode)
3458{
3459 AuraApplication * aurApp = i->second;
3460 ASSERT(aurApp);
3461 ASSERT(!aurApp->GetRemoveMode());
3462 ASSERT(aurApp->GetTarget() == this);
3463
3464 aurApp->SetRemoveMode(removeMode);
3465 Aura* aura = aurApp->GetBase();
3466 TC_LOG_DEBUG("spells", "Aura {} now is remove mode {}", aura->GetId(), removeMode);
3467
3468 // dead loop is killing the server probably
3469 ASSERT(m_removedAurasCount < 0xFFFFFFFF);
3470
3472
3473 Unit* caster = aura->GetCaster();
3474
3475 // Remove all pointers from lists here to prevent possible pointer invalidation on spellcast/auraapply/auraremove
3476 m_appliedAuras.erase(i);
3477
3479 {
3482 }
3483
3484 bool auraStateFound = false;
3485 AuraStateType auraState = aura->GetSpellInfo()->GetAuraState();
3486 if (auraState)
3487 {
3488 bool canBreak = false;
3489 // Get mask of all aurastates from remaining auras
3490 for (AuraStateAurasMap::iterator itr = m_auraStateAuras.lower_bound(auraState); itr != m_auraStateAuras.upper_bound(auraState) && !(auraStateFound && canBreak);)
3491 {
3492 if (itr->second == aurApp)
3493 {
3494 m_auraStateAuras.erase(itr);
3495 itr = m_auraStateAuras.lower_bound(auraState);
3496 canBreak = true;
3497 continue;
3498 }
3499 auraStateFound = true;
3500 ++itr;
3501 }
3502 }
3503
3504 aurApp->_Remove();
3505 aura->_UnapplyForTarget(this, caster, aurApp);
3506
3507 // remove effects of the spell - needs to be done after removing aura from lists
3508 for (uint8 itr = 0; itr < MAX_SPELL_EFFECTS; ++itr)
3509 {
3510 if (aurApp->HasEffect(itr))
3511 aurApp->_HandleEffect(itr, false);
3512 }
3513
3514 // all effect mustn't be applied
3515 ASSERT(!aurApp->GetEffectMask());
3516
3517 // Remove totem at next update if totem loses its aura
3518 if (aurApp->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE && GetTypeId() == TYPEID_UNIT && IsTotem())
3519 {
3520 if (ToTotem()->GetSpell() == aura->GetId() && ToTotem()->GetTotemType() == TOTEM_PASSIVE)
3522 }
3523
3524 // Remove aurastates only if needed and were not found
3525 if (auraState)
3526 {
3527 if (!auraStateFound)
3528 ModifyAuraState(auraState, false);
3529 else
3530 {
3531 // update for casters, some shouldn't 'see' the aura state
3532 uint32 aStateMask = (1 << (auraState - 1));
3533 if ((aStateMask & PER_CASTER_AURA_STATE_MASK) != 0)
3535 }
3536 }
3537
3538 aura->HandleAuraSpecificMods(aurApp, caster, false, false);
3539
3540 if (Player* player = ToPlayer())
3541 {
3542 if (sConditionMgr->IsSpellUsedInSpellClickConditions(aurApp->GetBase()->GetId()))
3543 player->UpdateVisibleObjectInteractions(false, true, false, false);
3544
3545 player->FailCriteria(CriteriaFailEvent::LoseAura, aurApp->GetBase()->GetId());
3546 }
3547
3548 i = m_appliedAuras.begin();
3549}
3550
3552{
3553 // aura can be removed from unit only if it's applied on it, shouldn't happen
3554 ASSERT(aurApp->GetBase()->GetApplicationOfTarget(GetGUID()) == aurApp);
3555
3556 uint32 spellId = aurApp->GetBase()->GetId();
3557 AuraApplicationMapBoundsNonConst range = m_appliedAuras.equal_range(spellId);
3558
3559 for (AuraApplicationMap::iterator iter = range.first; iter != range.second;)
3560 {
3561 if (iter->second == aurApp)
3562 {
3563 _UnapplyAura(iter, removeMode);
3564 return;
3565 }
3566 else
3567 ++iter;
3568 }
3569 ABORT();
3570}
3571
3573{
3574 SpellInfo const* spellProto = aura->GetSpellInfo();
3575
3576 // passive spell special case (only non stackable with ranks)
3577 if (spellProto->IsPassiveStackableWithRanks())
3578 return;
3579
3580 if (!IsHighestExclusiveAura(aura))
3581 {
3582 aura->Remove();
3583 return;
3584 }
3585
3586 if (owned)
3587 RemoveOwnedAuras([aura](Aura const* ownedAura) { return !aura->CanStackWith(ownedAura); }, AURA_REMOVE_BY_DEFAULT);
3588 else
3589 RemoveAppliedAuras([aura](AuraApplication const* appliedAura) { return !aura->CanStackWith(appliedAura->GetBase()); }, AURA_REMOVE_BY_DEFAULT);
3590}
3591
3593{
3594 if (apply)
3595 {
3596 m_modAuras[aurEff->GetAuraType()].push_front(aurEff);
3597 if (Player* player = ToPlayer())
3598 {
3599 player->StartCriteria(CriteriaStartEvent::GainAuraEffect, aurEff->GetAuraType());
3600 player->FailCriteria(CriteriaFailEvent::GainAuraEffect, aurEff->GetAuraType());
3601 }
3602 }
3603 else
3605}
3606
3607// All aura base removes should go through this function!
3608void Unit::RemoveOwnedAura(AuraMap::iterator& i, AuraRemoveMode removeMode)
3609{
3610 Aura* aura = i->second;
3611 ASSERT(!aura->IsRemoved());
3612
3613 // if unit currently update aura list then make safe update iterator shift to next
3614 if (m_auraUpdateIterator == i)
3616
3617 m_ownedAuras.erase(i);
3618 m_removedAuras.push_front(aura);
3619
3620 // Unregister single target aura
3621 if (aura->IsSingleTarget())
3622 aura->UnregisterSingleTarget();
3623
3624 aura->_Remove(removeMode);
3625
3626 i = m_ownedAuras.begin();
3627}
3628
3629void Unit::RemoveOwnedAura(uint32 spellId, ObjectGuid casterGUID, uint32 reqEffMask, AuraRemoveMode removeMode)
3630{
3631 for (AuraMap::iterator itr = m_ownedAuras.lower_bound(spellId); itr != m_ownedAuras.upper_bound(spellId);)
3632 if (((itr->second->GetEffectMask() & reqEffMask) == reqEffMask) && (!casterGUID || itr->second->GetCasterGUID() == casterGUID))
3633 {
3634 RemoveOwnedAura(itr, removeMode);
3635 itr = m_ownedAuras.lower_bound(spellId);
3636 }
3637 else
3638 ++itr;
3639}
3640
3642{
3643 if (aura->IsRemoved())
3644 return;
3645
3646 ASSERT(aura->GetOwner() == this);
3647
3648 if (removeMode == AURA_REMOVE_NONE)
3649 {
3650 TC_LOG_ERROR("spells", "Unit::RemoveOwnedAura() called with unallowed removeMode AURA_REMOVE_NONE, spellId {}", aura->GetId());
3651 return;
3652 }
3653
3654 uint32 spellId = aura->GetId();
3655 AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId);
3656
3657 for (AuraMap::iterator itr = range.first; itr != range.second; ++itr)
3658 {
3659 if (itr->second == aura)
3660 {
3661 RemoveOwnedAura(itr, removeMode);
3662 return;
3663 }
3664 }
3665
3666 ABORT();
3667}
3668
3669Aura* Unit::GetOwnedAura(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask, Aura* except) const
3670{
3671 AuraMapBounds range = m_ownedAuras.equal_range(spellId);
3672 for (AuraMap::const_iterator itr = range.first; itr != range.second; ++itr)
3673 {
3674 if (((itr->second->GetEffectMask() & reqEffMask) == reqEffMask)
3675 && (!casterGUID || itr->second->GetCasterGUID() == casterGUID)
3676 && (!itemCasterGUID || itr->second->GetCastItemGUID() == itemCasterGUID)
3677 && (!except || except != itr->second))
3678 {
3679 return itr->second;
3680 }
3681 }
3682 return nullptr;
3683}
3684
3685void Unit::RemoveAura(AuraApplicationMap::iterator &i, AuraRemoveMode mode)
3686{
3687 AuraApplication * aurApp = i->second;
3688 // Do not remove aura which is already being removed
3689 if (aurApp->GetRemoveMode())
3690 return;
3691 Aura* aura = aurApp->GetBase();
3692 _UnapplyAura(i, mode);
3693 // Remove aura - for Area and Target auras
3694 if (aura->GetOwner() == this)
3695 aura->Remove(mode);
3696}
3697
3698void Unit::RemoveAura(uint32 spellId, ObjectGuid caster, uint32 reqEffMask, AuraRemoveMode removeMode)
3699{
3700 AuraApplicationMapBoundsNonConst range = m_appliedAuras.equal_range(spellId);
3701 for (AuraApplicationMap::iterator iter = range.first; iter != range.second;)
3702 {
3703 Aura const* aura = iter->second->GetBase();
3704 if (((aura->GetEffectMask() & reqEffMask) == reqEffMask)
3705 && (!caster || aura->GetCasterGUID() == caster))
3706 {
3707 RemoveAura(iter, removeMode);
3708 return;
3709 }
3710 else
3711 ++iter;
3712 }
3713}
3714
3716{
3717 // we've special situation here, RemoveAura called while during aura removal
3718 // this kind of call is needed only when aura effect removal handler
3719 // or event triggered by it expects to remove
3720 // not yet removed effects of an aura
3721 if (aurApp->GetRemoveMode())
3722 {
3723 // remove remaining effects of an aura
3724 for (uint8 itr = 0; itr < MAX_SPELL_EFFECTS; ++itr)
3725 {
3726 if (aurApp->HasEffect(itr))
3727 aurApp->_HandleEffect(itr, false);
3728 }
3729 return;
3730 }
3731 // no need to remove
3732 if (aurApp->GetBase()->GetApplicationOfTarget(GetGUID()) != aurApp || aurApp->GetBase()->IsRemoved())
3733 return;
3734
3735 uint32 spellId = aurApp->GetBase()->GetId();
3736 AuraApplicationMapBoundsNonConst range = m_appliedAuras.equal_range(spellId);
3737
3738 for (AuraApplicationMap::iterator iter = range.first; iter != range.second;)
3739 {
3740 if (aurApp == iter->second)
3741 {
3742 RemoveAura(iter, mode);
3743 return;
3744 }
3745 else
3746 ++iter;
3747 }
3748}
3749
3751{
3752 if (aura->IsRemoved())
3753 return;
3754 if (AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID()))
3755 RemoveAura(aurApp, mode);
3756}
3757
3758void Unit::RemoveAppliedAuras(std::function<bool(AuraApplication const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
3759{
3760 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
3761 {
3762 if (check(iter->second))
3763 {
3764 RemoveAura(iter, removeMode);
3765 continue;
3766 }
3767 ++iter;
3768 }
3769}
3770
3771void Unit::RemoveOwnedAuras(std::function<bool(Aura const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
3772{
3773 for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
3774 {
3775 if (check(iter->second))
3776 {
3777 RemoveOwnedAura(iter, removeMode);
3778 continue;
3779 }
3780 ++iter;
3781 }
3782}
3783
3784void Unit::RemoveAppliedAuras(uint32 spellId, std::function<bool(AuraApplication const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
3785{
3786 for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
3787 {
3788 if (check(iter->second))
3789 {
3790 RemoveAura(iter, removeMode);
3791 iter = m_appliedAuras.lower_bound(spellId);
3792 continue;
3793 }
3794 ++iter;
3795 }
3796}
3797
3798void Unit::RemoveOwnedAuras(uint32 spellId, std::function<bool(Aura const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
3799{
3800 for (AuraMap::iterator iter = m_ownedAuras.lower_bound(spellId); iter != m_ownedAuras.upper_bound(spellId);)
3801 {
3802 if (check(iter->second))
3803 {
3804 RemoveOwnedAura(iter, removeMode);
3805 iter = m_ownedAuras.lower_bound(spellId);
3806 continue;
3807 }
3808 ++iter;
3809 }
3810}
3811
3812void Unit::RemoveAurasByType(AuraType auraType, std::function<bool(AuraApplication const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
3813{
3814 for (AuraEffectList::iterator iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end();)
3815 {
3816 Aura* aura = (*iter)->GetBase();
3818 ASSERT(aurApp);
3819
3820 ++iter;
3821 if (check(aurApp))
3822 {
3823 uint32 removedAuras = m_removedAurasCount;
3824 RemoveAura(aurApp, removeMode);
3825 if (m_removedAurasCount > removedAuras + 1)
3826 iter = m_modAuras[auraType].begin();
3827 }
3828 }
3829}
3830
3831void Unit::RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID, uint32 reqEffMask, AuraRemoveMode removeMode)
3832{
3833 for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
3834 {
3835 Aura const* aura = iter->second->GetBase();
3836 if (((aura->GetEffectMask() & reqEffMask) == reqEffMask)
3837 && (!casterGUID || aura->GetCasterGUID() == casterGUID))
3838 {
3839 RemoveAura(iter, removeMode);
3840 iter = m_appliedAuras.lower_bound(spellId);
3841 }
3842 else
3843 ++iter;
3844 }
3845}
3846
3847void Unit::RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID, AuraRemoveMode removeMode, uint16 num)
3848{
3849 AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId);
3850 for (AuraMap::iterator iter = range.first; iter != range.second;)
3851 {
3852 Aura* aura = iter->second;
3853 if ((aura->GetType() == UNIT_AURA_TYPE)
3854 && (!casterGUID || aura->GetCasterGUID() == casterGUID))
3855 {
3856 aura->ModStackAmount(-num, removeMode);
3857 return;
3858 }
3859 else
3860 ++iter;
3861 }
3862}
3863
3864void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, WorldObject* dispeller, uint8 chargesRemoved /*= 1*/)
3865{
3866 AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId);
3867 for (AuraMap::iterator iter = range.first; iter != range.second;)
3868 {
3869 Aura* aura = iter->second;
3870 if (aura->GetCasterGUID() == casterGUID)
3871 {
3872 DispelInfo dispelInfo(dispeller, dispellerSpellId, chargesRemoved);
3873
3874 // Call OnDispel hook on AuraScript
3875 aura->CallScriptDispel(&dispelInfo);
3876
3879 else
3881
3882 // Call AfterDispel hook on AuraScript
3883 aura->CallScriptAfterDispel(&dispelInfo);
3884
3885 return;
3886 }
3887 else
3888 ++iter;
3889 }
3890}
3891
3892void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, WorldObject* stealer, int32 stolenCharges /*= 1*/)
3893{
3894 AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId);
3895 for (AuraMap::iterator iter = range.first; iter != range.second;)
3896 {
3897 Aura* aura = iter->second;
3898 if (aura->GetCasterGUID() == casterGUID)
3899 {
3900 int32 damage[MAX_SPELL_EFFECTS];
3901 int32 baseDamage[MAX_SPELL_EFFECTS];
3902 uint32 effMask = 0;
3903 uint32 recalculateMask = 0;
3904 Unit* caster = aura->GetCaster();
3905 for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
3906 {
3907 if (aura->GetEffect(i))
3908 {
3909 baseDamage[i] = aura->GetEffect(i)->GetBaseAmount();
3910 damage[i] = aura->GetEffect(i)->GetAmount();
3911 effMask |= 1 << i;
3912 if (aura->GetEffect(i)->CanBeRecalculated())
3913 recalculateMask |= 1 << i;
3914 }
3915 else
3916 {
3917 baseDamage[i] = 0;
3918 damage[i] = 0;
3919 }
3920 }
3921
3923 // Cast duration to unsigned to prevent permanent aura's such as Righteous Fury being permanently added to caster
3924 uint32 dur = std::min(2u * MINUTE * IN_MILLISECONDS, uint32(aura->GetDuration()));
3925
3926 if (Unit* unitStealer = stealer->ToUnit())
3927 {
3928 if (Aura* oldAura = unitStealer->GetAura(aura->GetId(), aura->GetCasterGUID()))
3929 {
3930 if (stealCharge)
3931 oldAura->ModCharges(stolenCharges);
3932 else
3933 oldAura->ModStackAmount(stolenCharges);
3934 oldAura->SetDuration(int32(dur));
3935 }
3936 else
3937 {
3938 // single target state must be removed before aura creation to preserve existing single target aura
3939 if (aura->IsSingleTarget())
3940 aura->UnregisterSingleTarget();
3941
3942 AuraCreateInfo createInfo(aura->GetCastId(), aura->GetSpellInfo(), aura->GetCastDifficulty(), effMask, unitStealer);
3943 createInfo
3945 .SetBaseAmount(baseDamage);
3946
3947 if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo))
3948 {
3949 // created aura must not be single target aura,, so stealer won't loose it on recast
3950 if (newAura->IsSingleTarget())
3951 {
3952 newAura->UnregisterSingleTarget();
3953 // bring back single target aura status to the old aura
3954 aura->SetIsSingleTarget(true);
3955 caster->GetSingleCastAuras().push_front(aura);
3956 }
3957 // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate
3958 newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? stolenCharges : aura->GetCharges(), stolenCharges, recalculateMask, &damage[0]);
3959 newAura->ApplyForTargets();
3960 }
3961 }
3962 }
3963
3964 if (stealCharge)
3965 aura->ModCharges(-stolenCharges, AURA_REMOVE_BY_ENEMY_SPELL);
3966 else
3967 aura->ModStackAmount(-stolenCharges, AURA_REMOVE_BY_ENEMY_SPELL);
3968
3969 return;
3970 }
3971 else
3972 ++iter;
3973 }
3974}
3975
3977{
3978 for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
3979 {
3980 if (iter->second->GetBase()->GetCastItemGUID() == castItemGuid)
3981 {
3982 RemoveAura(iter);
3983 iter = m_appliedAuras.lower_bound(spellId);
3984 }
3985 else
3986 ++iter;
3987 }
3988}
3989
3990void Unit::RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID, Aura* except, bool negative, bool positive)
3991{
3992 for (AuraEffectList::iterator iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end();)
3993 {
3994 Aura* aura = (*iter)->GetBase();
3996 ASSERT(aurApp);
3997
3998 ++iter;
3999 if (aura != except && (!casterGUID || aura->GetCasterGUID() == casterGUID)
4000 && ((negative && !aurApp->IsPositive()) || (positive && aurApp->IsPositive())))
4001 {
4002 uint32 removedAuras = m_removedAurasCount;
4003 RemoveAura(aurApp);
4004 if (m_removedAurasCount > removedAuras + 1)
4005 iter = m_modAuras[auraType].begin();
4006 }
4007 }
4008}
4009
4011{
4012 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
4013 {
4014 SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo();
4015 if (spell->Attributes & flags)
4016 RemoveAura(iter);
4017 else
4018 ++iter;
4019 }
4020}
4021
4022void Unit::RemoveNotOwnSingleTargetAuras(bool onPhaseChange /*= false*/)
4023{
4024 // single target auras from other casters
4025 // Iterate m_ownedAuras - aura is marked as single target in Unit::AddAura (and pushed to m_ownedAuras).
4026 // m_appliedAuras will NOT contain the aura before first Unit::Update after adding it to m_ownedAuras.
4027 // Quickly removing such an aura will lead to it not being unregistered from caster's single cast auras container
4028 // leading to assertion failures if the aura was cast on a player that can
4029 // (and is changing map at the point where this function is called).
4030 // Such situation occurs when player is logging in inside an instance and fails the entry check for any reason.
4031 // The aura that was loaded from db (indirectly, via linked casts) gets removed before it has a chance
4032 // to register in m_appliedAuras
4033 for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
4034 {
4035 Aura const* aura = iter->second;
4036
4037 if (aura->GetCasterGUID() != GetGUID() && aura->IsSingleTarget())
4038 {
4039 if (!onPhaseChange)
4040 RemoveOwnedAura(iter);
4041 else
4042 {
4043 Unit* caster = aura->GetCaster();
4044 if (!caster || !caster->InSamePhase(this))
4045 RemoveOwnedAura(iter);
4046 else
4047 ++iter;
4048 }
4049 }
4050 else
4051 ++iter;
4052 }
4053
4054 // single target auras at other targets
4055 AuraList& scAuras = GetSingleCastAuras();
4056 for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();)
4057 {
4058 Aura* aura = *iter;
4059 if (aura->GetUnitOwner() != this && (!onPhaseChange || !aura->GetUnitOwner()->InSamePhase(this)))
4060 {
4061 aura->Remove();
4062 iter = scAuras.begin();
4063 }
4064 else
4065 ++iter;
4066 }
4067}
4068
4069template<typename InterruptFlag>
4070bool IsInterruptFlagIgnoredForSpell(InterruptFlag /*flag*/, Unit const* /*unit*/, SpellInfo const* /*auraSpellInfo*/, SpellInfo const* /*interruptSource*/)
4071{
4072 return false;
4073}
4074
4075template<>
4076bool IsInterruptFlagIgnoredForSpell(SpellAuraInterruptFlags flag, Unit const* unit, SpellInfo const* auraSpellInfo, SpellInfo const* interruptSource)
4077{
4078 switch (flag)
4079 {
4081 return unit->CanCastSpellWhileMoving(auraSpellInfo);
4084 if (interruptSource)
4085 {
4086 if (interruptSource->HasAttribute(SPELL_ATTR1_ALLOW_WHILE_STEALTHED) && auraSpellInfo->Dispel == DISPEL_STEALTH)
4087 return true;
4088
4089 if (interruptSource->HasAttribute(SPELL_ATTR2_ALLOW_WHILE_INVISIBLE) && auraSpellInfo->Dispel == DISPEL_INVISIBILITY)
4090 return true;
4091 }
4092 break;
4093 default:
4094 break;
4095 }
4096
4097 return false;
4098}
4099
4100template <typename InterruptFlags>
4101void Unit::RemoveAurasWithInterruptFlags(InterruptFlags flag, SpellInfo const* source)
4102{
4103 if (!HasInterruptFlag(flag))
4104 return;
4105
4106 // interrupt auras
4107 for (AuraApplicationList::iterator iter = m_interruptableAuras.begin(); iter != m_interruptableAuras.end();)
4108 {
4109 Aura* aura = (*iter)->GetBase();
4110 ++iter;
4111 if (aura->GetSpellInfo()->HasAuraInterruptFlag(flag)
4112 && (!source || aura->GetId() != source->Id)
4113 && !IsInterruptFlagIgnoredForSpell(flag, this, aura->GetSpellInfo(), source))
4114 {
4115 uint32 removedAuras = m_removedAurasCount;
4117 if (m_removedAurasCount > removedAuras + 1)
4118 iter = m_interruptableAuras.begin();
4119 }
4120 }
4121
4122 // interrupt channeled spell
4124 if (spell->getState() == SPELL_STATE_CASTING
4125 && spell->GetSpellInfo()->HasChannelInterruptFlag(flag)
4126 && (!source || spell->GetSpellInfo()->Id != source->Id)
4127 && !IsInterruptFlagIgnoredForSpell(flag, this, spell->GetSpellInfo(), source))
4129
4131}
4132
4135
4136void Unit::RemoveAurasWithFamily(SpellFamilyNames family, flag128 const& familyFlag, ObjectGuid casterGUID)
4137{
4138 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
4139 {
4140 Aura const* aura = iter->second->GetBase();
4141 if (!casterGUID || aura->GetCasterGUID() == casterGUID)
4142 {
4143 SpellInfo const* spell = aura->GetSpellInfo();
4144 if (spell->SpellFamilyName == uint32(family) && spell->SpellFamilyFlags & familyFlag)
4145 {
4146 RemoveAura(iter);
4147 continue;
4148 }
4149 }
4150 ++iter;
4151 }
4152}
4153
4155{
4156 if (withRoot)
4158
4160}
4161
4162void Unit::RemoveAurasWithMechanic(uint64 mechanicMaskToRemove, AuraRemoveMode removeMode, uint32 exceptSpellId, bool withEffectMechanics)
4163{
4164 std::vector<Aura*> aurasToUpdateTargets;
4165 RemoveAppliedAuras([=, &aurasToUpdateTargets](AuraApplication const* aurApp)
4166 {
4167 Aura* aura = aurApp->GetBase();
4168 if (exceptSpellId && aura->GetId() == exceptSpellId)
4169 return false;
4170
4171 uint64 appliedMechanicMask = aura->GetSpellInfo()->GetSpellMechanicMaskByEffectMask(aurApp->GetEffectMask());
4172 if (!(appliedMechanicMask & mechanicMaskToRemove))
4173 return false;
4174
4175 // spell mechanic matches required mask for removal
4176 if ((UI64LIT(1) << aura->GetSpellInfo()->Mechanic) & mechanicMaskToRemove || withEffectMechanics)
4177 return true;
4178
4179 // effect mechanic matches required mask for removal - don't remove, only update targets
4180 aurasToUpdateTargets.push_back(aura);
4181 return false;
4182 }, removeMode);
4183
4184 for (Aura* aura : aurasToUpdateTargets)
4185 {
4186 aura->UpdateTargetMap(aura->GetCaster());
4187
4188 // Fully remove the aura if all effects were removed
4189 if (!aura->IsPassive() && aura->GetOwner() == this && !aura->GetApplicationOfTarget(GetGUID()))
4190 aura->Remove(removeMode);
4191 }
4192}
4193
4195{
4196 uint64 mechanic_mask = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT);
4197 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
4198 {
4199 Aura const* aura = iter->second->GetBase();
4200 if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & mechanic_mask) && !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_AURA_CC))
4201 {
4202 RemoveAura(iter);
4203 continue;
4204 }
4205 ++iter;
4206 }
4207}
4208
4210{
4211 // make sure that all area auras not applied on self are removed - prevent access to deleted pointer later
4212 for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
4213 {
4214 Aura* aura = iter->second;
4215 ++iter;
4216 Aura::ApplicationMap const& appMap = aura->GetApplicationMap();
4217 for (Aura::ApplicationMap::const_iterator itr = appMap.begin(); itr!= appMap.end();)
4218 {
4219 AuraApplication * aurApp = itr->second;
4220 ++itr;
4221 Unit* target = aurApp->GetTarget();
4222 if (target == this)
4223 continue;
4224 target->RemoveAura(aurApp);
4225 // things linked on aura remove may apply new area aura - so start from the beginning
4226 iter = m_ownedAuras.begin();
4227 }
4228 }
4229
4230 // remove area auras owned by others
4231 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
4232 {
4233 if (iter->second->GetBase()->GetOwner() != this)
4234 {
4235 RemoveAura(iter);
4236 }
4237 else
4238 ++iter;
4239 }
4240}
4241
4243{
4244 // this may be a dead loop if some events on aura remove will continiously apply aura on remove
4245 // we want to have all auras removed, so use your brain when linking events
4246 for (int counter = 0; !m_appliedAuras.empty() || !m_ownedAuras.empty(); counter++)
4247 {
4248 AuraApplicationMap::iterator aurAppIter;
4249 for (aurAppIter = m_appliedAuras.begin(); aurAppIter != m_appliedAuras.end();)
4251
4252 AuraMap::iterator aurIter;
4253 for (aurIter = m_ownedAuras.begin(); aurIter != m_ownedAuras.end();)
4254 RemoveOwnedAura(aurIter);
4255
4256 const int maxIteration = 50;
4257 // give this loop a few tries, if there are still auras then log as much information as possible
4258 if (counter >= maxIteration)
4259 {
4260 std::stringstream sstr;
4261 sstr << "Unit::RemoveAllAuras() iterated " << maxIteration << " times already but there are still "
4262 << m_appliedAuras.size() << " m_appliedAuras and " << m_ownedAuras.size() << " m_ownedAuras. Details:" << "\n";
4263 sstr << GetDebugInfo() << "\n";
4264
4265 if (!m_appliedAuras.empty())
4266 {
4267 sstr << "m_appliedAuras:" << "\n";
4268
4269 for (std::pair<uint32 const, AuraApplication*>& auraAppPair : m_appliedAuras)
4270 sstr << auraAppPair.second->GetDebugInfo() << "\n";
4271 }
4272
4273 if (!m_ownedAuras.empty())
4274 {
4275 sstr << "m_ownedAuras:" << "\n";
4276
4277 for (auto const& [spellId, aura] : m_ownedAuras)
4278 sstr << aura->GetDebugInfo() << "\n";
4279 }
4280
4281 TC_LOG_ERROR("entities.unit", "{}", sstr.str());
4282 ABORT_MSG("%s", sstr.str().c_str());
4283
4284 break;
4285 }
4286 }
4287}
4288
4290{
4291 // in join, remove positive buffs, on end, remove negative
4292 // used to remove positive visible auras in arenas
4293 RemoveAppliedAuras([](AuraApplication const* aurApp)
4294 {
4295 Aura const* aura = aurApp->GetBase();
4296 return (!aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_ALLOW_ENTERING_ARENA) // don't remove stances, shadowform, pally/hunter auras
4297 && !aura->IsPassive() // don't remove passive auras
4298 && (aurApp->IsPositive() || !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR3_ALLOW_AURA_WHILE_DEAD))) || // not negative death persistent auras
4299 aura->GetSpellInfo()->HasAttribute(SPELL_ATTR5_REMOVE_ENTERING_ARENA); // special marker, always remove
4300 });
4301}
4302
4304{
4305 if (IsCharmedOwnedByPlayerOrPlayer()) // if it is a player owned creature it should not remove the aura
4306 return;
4307
4308 // don't remove vehicle auras, passengers aren't supposed to drop off the vehicle
4309 // don't remove clone caster on evade (to be verified)
4310 auto evadeAuraCheck = [](Aura const* aura)
4311 {
4312 if (aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE))
4313 return false;
4314
4315 if (aura->HasEffectType(SPELL_AURA_CLONE_CASTER))
4316 return false;
4317
4318 if (aura->GetSpellInfo()->HasAttribute(SPELL_ATTR1_AURA_STAYS_AFTER_COMBAT))
4319 return false;
4320
4321 return true;
4322 };
4323
4324 auto evadeAuraApplicationCheck = [&evadeAuraCheck](AuraApplication const* aurApp)
4325 {
4326 return evadeAuraCheck(aurApp->GetBase());
4327 };
4328
4329 RemoveAppliedAuras(evadeAuraApplicationCheck);
4330 RemoveOwnedAuras(evadeAuraCheck);
4331}
4332
4334{
4335 // used just after dieing to remove all visible auras
4336 // and disable the mods for the passive ones
4337 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
4338 {
4339 Aura const* aura = iter->second->GetBase();
4340 if (!aura->IsPassive() && !aura->IsDeathPersistent())
4342 else
4343 ++iter;
4344 }
4345
4346 for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
4347 {
4348 Aura* aura = iter->second;
4349 if (!aura->IsPassive() && !aura->IsDeathPersistent())
4351 else
4352 ++iter;
4353 }
4354}
4355
4357{
4358 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
4359 {
4360 Aura const* aura = iter->second->GetBase();
4361 if (!aura->IsPassive() && aura->GetSpellInfo()->IsRequiringDeadTarget())
4363 else
4364 ++iter;
4365 }
4366
4367 for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
4368 {
4369 Aura* aura = iter->second;
4370 if (!aura->IsPassive() && aura->GetSpellInfo()->IsRequiringDeadTarget())
4372 else
4373 ++iter;
4374 }
4375}
4376
4378{
4379 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
4380 {
4381 Aura const* aura = iter->second->GetBase();
4382 if (aura->GetSpellInfo()->HasAura(type))
4383 ++iter;
4384 else
4386 }
4387
4388 for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
4389 {
4390 Aura* aura = iter->second;
4391 if (aura->GetSpellInfo()->HasAura(type))
4392 ++iter;
4393 else
4395 }
4396}
4397
4399{
4400 for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
4401 {
4402 Aura const* aura = iter->second->GetBase();
4403 if (aura->GetSpellInfo()->HasAura(type1) || aura->GetSpellInfo()->HasAura(type2))
4404 ++iter;
4405 else
4407 }
4408
4409 for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
4410 {
4411 Aura* aura = iter->second;
4412 if (aura->GetSpellInfo()->HasAura(type1) || aura->GetSpellInfo()->HasAura(type2))
4413 ++iter;
4414 else
4416 }
4417}
4418
4420{
4421 for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
4422 {
4423 Aura* aura = iter->second;
4424 if (aura->GetCasterGUID() == casterGUID && aura->GetSpellInfo()->IsGroupBuff())
4425 {
4426 RemoveOwnedAura(iter);
4427 continue;
4428 }
4429 ++iter;
4430 }
4431}
4432
4433void Unit::DelayOwnedAuras(uint32 spellId, ObjectGuid caster, int32 delaytime)
4434{
4435 AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId);
4436 for (; range.first != range.second; ++range.first)
4437 {
4438 Aura* aura = range.first->second;
4439 if (!caster || aura->GetCasterGUID() == caster)
4440 {
4441 if (aura->GetDuration() < delaytime)
4442 aura->SetDuration(0);
4443 else
4444 aura->SetDuration(aura->GetDuration() - delaytime);
4445
4446 // update for out of range group members (on 1 slot use)
4448 }
4449 }
4450}
4451
4453{
4454 for (AuraApplicationMap::iterator i = m_appliedAuras.begin(); i != m_appliedAuras.end(); ++i)
4455 (*i).second->GetBase()->HandleAllEffects(i->second, AURA_EFFECT_HANDLE_STAT, false);
4456}
4457
4459{
4460 for (AuraApplicationMap::iterator i = m_appliedAuras.begin(); i != m_appliedAuras.end(); ++i)
4461 (*i).second->GetBase()->HandleAllEffects(i->second, AURA_EFFECT_HANDLE_STAT, true);
4462}
4463
4464AuraEffect* Unit::GetAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid caster) const
4465{
4466 AuraApplicationMapBounds range = m_appliedAuras.equal_range(spellId);
4467 for (AuraApplicationMap::const_iterator itr = range.first; itr != range.second; ++itr)
4468 {
4469 if (itr->second->HasEffect(effIndex)
4470 && (!caster || itr->second->GetBase()->GetCasterGUID() == caster))
4471 {
4472 return itr->second->GetBase()->GetEffect(effIndex);
4473 }
4474 }
4475 return nullptr;
4476}
4477
4479{
4480 uint32 rankSpell = sSpellMgr->GetFirstSpellInChain(spellId);
4481 while (rankSpell)
4482 {
4483 if (AuraEffect* aurEff = GetAuraEffect(rankSpell, effIndex, caster))
4484 return aurEff;
4485 rankSpell = sSpellMgr->GetNextSpellInChain(rankSpell);
4486 }
4487 return nullptr;
4488}
4489
4490AuraEffect* Unit::GetAuraEffect(AuraType type, SpellFamilyNames family, flag128 const& familyFlag, ObjectGuid casterGUID) const
4491{
4492 AuraEffectList const& auras = GetAuraEffectsByType(type);
4493 for (AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
4494 {
4495 SpellInfo const* spell = (*i)->GetSpellInfo();
4496 if (spell->SpellFamilyName == uint32(family) && spell->SpellFamilyFlags & familyFlag)
4497 {
4498 if (!casterGUID.IsEmpty() && (*i)->GetCasterGUID() != casterGUID)
4499 continue;
4500 return (*i);
4501 }
4502 }
4503 return nullptr;
4504}
4505
4506AuraApplication * Unit::GetAuraApplication(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask, AuraApplication * except) const
4507{
4508 return GetAuraApplication(spellId, [&](AuraApplication const* app)
4509 {
4510 Aura const* aura = app->GetBase();
4511
4512 if (((aura->GetEffectMask() & reqEffMask) == reqEffMask)
4513 && (!casterGUID || aura->GetCasterGUID() == casterGUID)
4514 && (!itemCasterGUID || aura->GetCastItemGUID() == itemCasterGUID)
4515 && (!except || except != app))
4516 {
4517 return true;
4518 }
4519
4520 return false;
4521 });
4522}
4523
4524AuraApplication* Unit::GetAuraApplication(uint32 spellId, std::function<bool(AuraApplication const*)> const& predicate) const
4525{
4526 for (AuraApplicationMap::value_type const& pair : Trinity::Containers::MapEqualRange(m_appliedAuras, spellId))
4527 if (predicate(pair.second))
4528 return pair.second;
4529
4530 return nullptr;
4531}
4532
4533AuraApplication* Unit::GetAuraApplication(uint32 spellId, std::function<bool(Aura const*)> const& predicate) const
4534{
4535 for (AuraApplicationMap::value_type const& pair : Trinity::Containers::MapEqualRange(m_appliedAuras, spellId))
4536 if (predicate(pair.second->GetBase()))
4537 return pair.second;
4538
4539 return nullptr;
4540}
4541
4542AuraApplication* Unit::GetAuraApplication(std::function<bool(AuraApplication const*)> const& predicate) const
4543{
4544 for (AuraApplicationMap::value_type const& pair : m_appliedAuras)
4545 if (predicate(pair.second))
4546 return pair.second;
4547
4548 return nullptr;
4549}
4550
4551AuraApplication* Unit::GetAuraApplication(std::function<bool(Aura const*)> const& predicate) const
4552{
4553 for (AuraApplicationMap::value_type const& pair : m_appliedAuras)
4554 if (predicate(pair.second->GetBase()))
4555 return pair.second;
4556
4557 return nullptr;
4558}
4559
4560Aura* Unit::GetAura(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask) const
4561{
4562 AuraApplication* aurApp = GetAuraApplication(spellId, casterGUID, itemCasterGUID, reqEffMask);
4563 return aurApp ? aurApp->GetBase() : nullptr;
4564}
4565
4566Aura* Unit::GetAura(uint32 spellId, std::function<bool(Aura const*)> const& predicate) const
4567{
4568 AuraApplication* aurApp = GetAuraApplication(spellId, predicate);
4569 return aurApp ? aurApp->GetBase() : nullptr;
4570}
4571
4572Aura* Unit::GetAura(std::function<bool(Aura const*)> const& predicate) const
4573{
4574 AuraApplication* aurApp = GetAuraApplication(predicate);
4575 return aurApp ? aurApp->GetBase() : nullptr;
4576}
4577
4578AuraApplication * Unit::GetAuraApplicationOfRankedSpell(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask, AuraApplication* except) const
4579{
4580 uint32 rankSpell = sSpellMgr->GetFirstSpellInChain(spellId);
4581 while (rankSpell)
4582 {
4583 if (AuraApplication * aurApp = GetAuraApplication(rankSpell, casterGUID, itemCasterGUID, reqEffMask, except))
4584 return aurApp;
4585 rankSpell = sSpellMgr->GetNextSpellInChain(rankSpell);
4586 }
4587 return nullptr;
4588}
4589
4590Aura* Unit::GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask) const
4591{
4592 AuraApplication * aurApp = GetAuraApplicationOfRankedSpell(spellId, casterGUID, itemCasterGUID, reqEffMask);
4593 return aurApp ? aurApp->GetBase() : nullptr;
4594}
4595
4596void Unit::GetDispellableAuraList(WorldObject const* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect /*= false*/) const
4597{
4598 AuraMap const& auras = GetOwnedAuras();
4599 for (auto itr = auras.begin(); itr != auras.end(); ++itr)
4600 {
4601 Aura* aura = itr->second;
4602 AuraApplication const* aurApp = aura->GetApplicationOfTarget(GetGUID());
4603 if (!aurApp)
4604 continue;
4605
4606 // don't try to remove passive auras
4607 if (aura->IsPassive())
4608 continue;
4609
4610 if (aura->GetSpellInfo()->GetDispelMask() & dispelMask)
4611 {
4612 // do not remove positive auras if friendly target
4613 // negative auras if non-friendly
4614 // unless we're reflecting (dispeller eliminates one of it's benefitial buffs)
4615 if (isReflect != (aurApp->IsPositive() == IsFriendlyTo(caster)))
4616 continue;
4617
4618 // 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance."
4619 int32 chance = aura->CalcDispelChance(this, !IsFriendlyTo(caster));
4620 if (!chance)
4621 continue;
4622
4623 // The charges / stack amounts don't count towards the total number of auras that can be dispelled.
4624 // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell
4625 // Polymorph instead of 1 / (5 + 1) -> 16%.
4626 bool const dispelCharges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_REMOVES_CHARGES);
4627 uint8 charges = dispelCharges ? aura->GetCharges() : aura->GetStackAmount();
4628 if (charges > 0)
4629 dispelList.emplace_back(aura, chance, charges);
4630 }
4631 }
4632}
4633
4634bool Unit::HasAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid caster) const
4635{
4636 AuraApplicationMapBounds range = m_appliedAuras.equal_range(spellId);
4637 for (AuraApplicationMap::const_iterator itr = range.first; itr != range.second; ++itr)
4638 {
4639 if (itr->second->HasEffect(effIndex)
4640 && (!caster || itr->second->GetBase()->GetCasterGUID() == caster))
4641 {
4642 return true;
4643 }
4644 }
4645 return false;
4646}
4647
4649{
4650 uint32 count = 0;
4651 AuraApplicationMapBounds range = m_appliedAuras.equal_range(spellId);
4652
4653 for (AuraApplicationMap::const_iterator itr = range.first; itr != range.second; ++itr)
4654 {
4655 if (itr->second->GetBase()->GetStackAmount() == 0)
4656 ++count;
4657 else
4658 count += (uint32)itr->second->GetBase()->GetStackAmount();
4659 }
4660
4661 return count;
4662}
4663
4664bool Unit::HasAura(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask) const
4665{
4666 return GetAuraApplication(spellId, casterGUID, itemCasterGUID, reqEffMask) != nullptr;
4667}
4668
4669bool Unit::HasAura(std::function<bool(Aura const*)> const& predicate) const
4670{
4671 return GetAuraApplication(predicate) != nullptr;
4672}
4673
4674bool Unit::HasAuraType(AuraType auraType) const
4675{
4676 return (!m_modAuras[auraType].empty());
4677}
4678
4680{
4681 for (AuraEffect const* eff : GetAuraEffectsByType(auraType))
4682 if (caster == eff->GetCasterGUID())
4683 return true;
4684 return false;
4685}
4686
4687bool Unit::HasAuraTypeWithMiscvalue(AuraType auraType, int32 miscvalue) const
4688{
4689 AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auraType);
4690 for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i)
4691 if (miscvalue == (*i)->GetMiscValue())
4692 return true;
4693 return false;
4694}
4695
4696bool Unit::HasAuraTypeWithAffectMask(AuraType auraType, SpellInfo const* affectedSpell) const
4697{
4698 AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auraType);
4699 for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i)
4700 if ((*i)->IsAffectingSpell(affectedSpell))
4701 return true;
4702 return false;
4703}
4704
4705bool Unit::HasAuraTypeWithValue(AuraType auraType, int32 value) const
4706{
4707 AuraEffectList const& mTotalAuraList = GetAuraEffectsByType(auraType);
4708 for (AuraEffectList::const_iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i)
4709 if (value == (*i)->GetAmount())
4710 return true;
4711 return false;
4712}
4713
4714bool Unit::HasAuraTypeWithTriggerSpell(AuraType auratype, uint32 triggerSpell) const
4715{
4716 for (AuraEffect const* aura : GetAuraEffectsByType(auratype))
4717 if (aura->GetSpellEffectInfo().TriggerSpell == triggerSpell)
4718 return true;
4719 return false;
4720}
4721
4722template <typename InterruptFlags>
4723bool Unit::HasNegativeAuraWithInterruptFlag(InterruptFlags flag, ObjectGuid guid) const
4724{
4725 if (!HasInterruptFlag(flag))
4726 return false;
4727
4728 for (AuraApplicationList::const_iterator iter = m_interruptableAuras.begin(); iter != m_interruptableAuras.end(); ++iter)
4729 if (!(*iter)->IsPositive() && (*iter)->GetBase()->GetSpellInfo()->HasAuraInterruptFlag(flag) &&
4730 (!guid || (*iter)->GetBase()->GetCasterGUID() == guid))
4731 return true;
4732
4733 return false;
4734}
4735
4738
4739bool Unit::HasAuraWithMechanic(uint64 mechanicMask) const
4740{
4741 for (AuraApplicationMap::const_iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
4742 {
4743 SpellInfo const* spellInfo = iter->second->GetBase()->GetSpellInfo();
4744 if (spellInfo->Mechanic && (mechanicMask & (UI64LIT(1) << spellInfo->Mechanic)))
4745 return true;
4746
4747 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
4748 if (iter->second->HasEffect(spellEffectInfo.EffectIndex) && spellEffectInfo.IsEffect() && spellEffectInfo.Mechanic)
4749 if (mechanicMask & (UI64LIT(1) << spellEffectInfo.Mechanic))
4750 return true;
4751 }
4752
4753 return false;
4754}
4755
4756bool Unit::HasStrongerAuraWithDR(SpellInfo const* auraSpellInfo, Unit* caster) const
4757{
4758 DiminishingGroup diminishGroup = auraSpellInfo->GetDiminishingReturnsGroupForSpell();
4759 DiminishingLevels level = GetDiminishing(diminishGroup);
4760 for (auto itr = m_appliedAuras.begin(); itr != m_appliedAuras.end(); ++itr)
4761 {
4762 SpellInfo const* spellInfo = itr->second->GetBase()->GetSpellInfo();
4763 if (spellInfo->GetDiminishingReturnsGroupForSpell() != diminishGroup)
4764 continue;
4765
4766 int32 existingDuration = itr->second->GetBase()->GetDuration();
4767 int32 newDuration = auraSpellInfo->GetMaxDuration();
4768 ApplyDiminishingToDuration(auraSpellInfo, newDuration, caster, level);
4769 if (newDuration > 0 && newDuration < existingDuration)
4770 return true;
4771 }
4772
4773 return false;
4774}
4775
4777{
4779 for (AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
4780 {
4781 if ((*i)->GetMiscValue() == script)
4782 if ((*i)->IsAffectingSpell(spell))
4783 return (*i);
4784 }
4785 return nullptr;
4786}
4787
4789{
4790 static const AuraType diseaseAuraTypes[] =
4791 {
4792 SPELL_AURA_PERIODIC_DAMAGE, // Frost Fever and Blood Plague
4793 SPELL_AURA_LINKED // Crypt Fever and Ebon Plague
4794 };
4795
4796 uint32 diseases = 0;
4797 for (AuraType aType : diseaseAuraTypes)
4798 {
4799 for (auto itr = m_modAuras[aType].begin(); itr != m_modAuras[aType].end();)
4800 {
4801 // Get auras with dis