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