TrinityCore
Spell.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 "Spell.h"
20#include "Battlefield.h"
21#include "BattlefieldMgr.h"
22#include "Battleground.h"
23#include "BattlePetMgr.h"
24#include "CellImpl.h"
25#include "CharmInfo.h"
26#include "CollectionMgr.h"
27#include "CombatLogPackets.h"
28#include "Common.h"
29#include "ConditionMgr.h"
30#include "Containers.h"
31#include "CreatureAI.h"
32#include "DB2Stores.h"
33#include "DatabaseEnv.h"
34#include "DisableMgr.h"
35#include "DynamicObject.h"
36#include "G3DPosition.hpp"
37#include "GameObjectAI.h"
38#include "GridNotifiersImpl.h"
39#include "Guild.h"
40#include "InstanceLockMgr.h"
41#include "InstanceScript.h"
42#include "Item.h"
43#include "Log.h"
44#include "Loot.h"
45#include "LootMgr.h"
46#include "MotionMaster.h"
47#include "ObjectAccessor.h"
48#include "ObjectMgr.h"
49#include "PathGenerator.h"
50#include "Pet.h"
51#include "PhasingHandler.h"
52#include "Player.h"
53#include "ScriptMgr.h"
54#include "SharedDefines.h"
55#include "SpellAuraEffects.h"
56#include "SpellHistory.h"
57#include "SpellInfo.h"
58#include "SpellMgr.h"
59#include "SpellPackets.h"
60#include "SpellScript.h"
61#include "TemporarySummon.h"
62#include "TradeData.h"
63#include "TraitPackets.h"
64#include "UniqueTrackablePtr.h"
65#include "Util.h"
66#include "VMapFactory.h"
67#include "Vehicle.h"
68#include "VMapManager2.h"
69#include "World.h"
70#include "WorldSession.h"
71#include <numeric>
72#include <sstream>
73
75
77{
78 _position.Relocate(0, 0, 0, 0);
80 _transportOffset.Relocate(0, 0, 0, 0);
81}
82
83SpellDestination::SpellDestination(float x, float y, float z, float orientation, uint32 mapId)
84{
85 _position.Relocate(x, y, z, orientation);
87 _position.m_mapId = mapId;
88 _transportOffset.Relocate(0, 0, 0, 0);
89}
90
92{
95 _transportOffset.Relocate(0, 0, 0, 0);
96}
97
99{
102 _transportOffset.Relocate(0, 0, 0, 0);
103}
104
106{
109 _position.Relocate(wObj);
110}
111
113{
114 if (!_transportGUID.IsEmpty())
115 {
116 Position offset;
117 _position.GetPositionOffsetTo(pos, offset);
119 }
120 _position.Relocate(pos);
121}
122
124{
125 if (!_transportGUID.IsEmpty())
127
129}
130
131SpellCastTargets::SpellCastTargets() : m_targetMask(0), m_objectTarget(nullptr), m_itemTarget(nullptr),
132 m_itemTargetEntry(0), m_pitch(0.0f), m_speed(0.0f)
133{
134}
135
137 m_targetMask(spellCastRequest.Target.Flags), m_objectTarget(nullptr), m_itemTarget(nullptr),
138 m_objectTargetGUID(spellCastRequest.Target.Unit), m_itemTargetGUID(spellCastRequest.Target.Item),
139 m_itemTargetEntry(0), m_pitch(0.0f), m_speed(0.0f), m_strTarget(spellCastRequest.Target.Name)
140{
141 if (spellCastRequest.Target.SrcLocation)
142 {
143 m_src._transportGUID = spellCastRequest.Target.SrcLocation->Transport;
144 Position* pos;
146 pos = &m_src._transportOffset;
147 else
148 pos = &m_src._position;
149
150 pos->Relocate(spellCastRequest.Target.SrcLocation->Location.Pos);
151 if (spellCastRequest.Target.Orientation)
152 pos->SetOrientation(*spellCastRequest.Target.Orientation);
153 }
154
155 if (spellCastRequest.Target.DstLocation)
156 {
157 m_dst._transportGUID = spellCastRequest.Target.DstLocation->Transport;
158 Position* pos;
160 pos = &m_dst._transportOffset;
161 else
162 pos = &m_dst._position;
163
164 pos->Relocate(spellCastRequest.Target.DstLocation->Location.Pos);
165 if (spellCastRequest.Target.Orientation)
166 pos->SetOrientation(*spellCastRequest.Target.Orientation);
167 }
168
169 SetPitch(spellCastRequest.MissileTrajectory.Pitch);
170 SetSpeed(spellCastRequest.MissileTrajectory.Speed);
171
172 Update(caster);
173}
174
176
178{
179 data.Flags = m_targetMask;
180
183
185 data.Item = m_itemTarget->GetGUID();
186
188 {
189 data.SrcLocation.emplace();
190 data.SrcLocation->Transport = m_src._transportGUID; // relative position guid here - transport for example
192 data.SrcLocation->Location = m_src._transportOffset;
193 else
194 data.SrcLocation->Location = m_src._position;
195 }
196
198 {
199 data.DstLocation.emplace();
200 data.DstLocation->Transport = m_dst._transportGUID; // relative position guid here - transport for example
202 data.DstLocation->Location = m_dst._transportOffset;
203 else
204 data.DstLocation->Location = m_dst._position;
205 }
206
208 data.Name = m_strTarget;
209}
210
212{
214 return m_objectTargetGUID;
215
216 return ObjectGuid::Empty;
217}
218
220{
221 if (m_objectTarget)
222 return m_objectTarget->ToUnit();
223
224 return nullptr;
225}
226
228{
229 if (!target)
230 return;
231
232 m_objectTarget = target;
233 m_objectTargetGUID = target->GetGUID();
235}
236
238{
240 return m_objectTargetGUID;
241
242 return ObjectGuid::Empty;
243}
244
246{
247 if (m_objectTarget)
249
250 return nullptr;
251}
252
254{
255 if (!target)
256 return;
257
258 m_objectTarget = target;
259 m_objectTargetGUID = target->GetGUID();
261}
262
264{
266 return m_objectTargetGUID;
267
268 return ObjectGuid::Empty;
269}
270
272{
273 if (m_objectTarget)
274 return m_objectTarget->ToCorpse();
275
276 return nullptr;
277}
278
280{
281 return m_objectTarget;
282}
283
285{
286 return m_objectTargetGUID;
287}
288
290{
291 m_objectTarget = nullptr;
294}
295
297{
298 if (!item)
299 return;
300
301 m_itemTarget = item;
302 m_itemTargetGUID = item->GetGUID();
303 m_itemTargetEntry = item->GetEntry();
305}
306
308{
312
313 Update(caster);
314}
315
317{
319 {
322 }
323}
324
326{
327 return &m_src;
328}
329
331{
332 return &m_src._position;
333}
334
335void SpellCastTargets::SetSrc(float x, float y, float z)
336{
337 m_src = SpellDestination(x, y, z);
339}
340
342{
343 m_src = SpellDestination(pos);
345}
346
348{
349 m_src = SpellDestination(wObj);
351}
352
354{
356 m_src.Relocate(pos);
357}
358
360{
362}
363
365{
366 return &m_dst;
367}
368
370{
371 return &m_dst._position;
372}
373
374void SpellCastTargets::SetDst(float x, float y, float z, float orientation, uint32 mapId)
375{
376 m_dst = SpellDestination(x, y, z, orientation, mapId);
378}
379
381{
382 m_dst = SpellDestination(pos);
384}
385
387{
388 m_dst = SpellDestination(wObj);
390}
391
393{
394 m_dst = spellDest;
396}
397
399{
400 m_dst = spellTargets.m_dst;
402}
403
405{
407 m_dst.Relocate(pos);
408}
409
411{
413 m_dst = spellDest;
414}
415
417{
419}
420
422{
424}
425
427{
429}
430
432{
434
435 m_itemTarget = nullptr;
436 if (caster->GetTypeId() == TYPEID_PLAYER)
437 {
438 Player* player = caster->ToPlayer();
442 {
444 if (TradeData* pTrade = player->GetTradeData())
445 m_itemTarget = pTrade->GetTraderData()->GetItem(TRADE_SLOT_NONTRADED);
446 }
447
448 if (m_itemTarget)
450 }
451
452 // update positions by transport move
454 {
456 {
457 m_src._position.Relocate(transport);
459 }
460 }
461
463 {
465 {
466 m_dst._position.Relocate(transport);
468 }
469 }
470}
471
472SpellValue::SpellValue(SpellInfo const* proto, WorldObject const* caster)
473{
474 memset(EffectBasePoints, 0, sizeof(EffectBasePoints));
475 for (SpellEffectInfo const& spellEffectInfo : proto->GetEffects())
476 EffectBasePoints[spellEffectInfo.EffectIndex] = spellEffectInfo.CalcBaseValue(caster, nullptr, 0, -1);
477
480 RadiusMod = 1.0f;
481 AuraStackAmount = 1;
482 CriticalChance = 0.0f;
483 DurationMul = 1;
484}
485
487{
488public:
489 explicit SpellEvent(Spell* spell);
490 ~SpellEvent();
491
492 bool Execute(uint64 e_time, uint32 p_time) override;
493 void Abort(uint64 e_time) override;
494 bool IsDeletable() const override;
495 Spell const* GetSpell() const { return m_Spell.get(); }
497
498 std::string GetDebugInfo() const { return m_Spell->GetDebugInfo(); }
499
500protected:
502};
503
504Spell::Spell(WorldObject* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID /*= ObjectGuid::Empty*/,
505 ObjectGuid originalCastId /*= ObjectGuid::Empty*/) :
506m_spellInfo(info), m_caster((info->HasAttribute(SPELL_ATTR6_ORIGINATE_FROM_CONTROLLER) && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster),
507m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr)
508{
510 m_fromClient = false;
511 m_selfContainer = nullptr;
513 m_executedCurrently = false;
514 m_delayStart = 0;
515 m_delayMoment = 0;
517
519 memset(m_damageMultipliers, 0, sizeof(m_damageMultipliers));
520
521 // Get data for type of attack
522 m_attackType = info->GetAttackType();
523
524 m_spellSchoolMask = info->GetSchoolMask(); // Can be override for some spell (wand shoot for example)
525
526 if (Player const* playerCaster = m_caster->ToPlayer())
527 {
528 // wand case
530 if ((playerCaster->GetClassMask() & CLASSMASK_WAND_USERS) != 0)
531 if (Item* pItem = playerCaster->GetWeaponForAttack(RANGED_ATTACK))
532 m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetTemplate()->GetDamageType());
533 }
534
535 if (Player const* modOwner = caster->GetSpellModOwner())
536 modOwner->ApplySpellMod(info, SpellModOp::Doses, m_spellValue->AuraStackAmount, this);
537
538 if (!originalCasterGUID.IsEmpty())
539 m_originalCasterGUID = originalCasterGUID;
540 else
542
545 else
546 {
549 m_originalCaster = nullptr;
550 }
551
553 _triggeredCastFlags = triggerFlags;
554
557
560
561 m_CastItem = nullptr;
563 m_castItemEntry = 0;
564 m_castItemLevel = -1;
565 m_castFlagsEx = 0;
566
569
570 unitTarget = nullptr;
571 itemTarget = nullptr;
572 gameObjTarget = nullptr;
573 m_corpseTarget = nullptr;
574 destTarget = nullptr;
575 damage = 0;
577 variance = 0.0f;
579 effectInfo = nullptr;
580 m_damage = 0;
581 m_healing = 0;
584 focusObject = nullptr;
586 m_originalCastId = originalCastId;
587 memset(m_misc.Raw.Data, 0, sizeof(m_misc.Raw.Data));
589 m_triggeredByAuraSpell = nullptr;
590 m_procChainLength = caster->IsUnit() ? caster->ToUnit()->GetProcChainLength() : 0;
591 _spellAura = nullptr;
592 _dynObjAura = nullptr;
593
594 //Auto Shot & Shoot (wand)
596
597 m_runesState = 0;
598 m_casttime = 0; // setup to correct value in Spell::prepare, must not be used before.
599 m_timer = 0; // will set to castime in prepare
600 m_channeledDuration = 0; // will be setup in Spell::handle_immediate
601 m_launchHandled = false;
602 m_immediateHandled = false;
603
605
607 m_empower = std::make_unique<EmpowerData>();
608
609 // Determine if spell can be reflected back to the caster
610 // Patch 1.2 notes: Spell Reflection no longer reflects abilities
611 m_canReflect = caster->IsUnit()
614 && !m_spellInfo->IsPassive();
615
616 for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
618}
619
621{
622 // unload scripts
623 for (auto itr = m_loadedScripts.begin(); itr != m_loadedScripts.end(); ++itr)
624 {
625 (*itr)->_Unload();
626 delete (*itr);
627 }
628
630 {
631 // Clean the reference to avoid later crash.
632 // If this error is repeating, we may have to add an ASSERT to better track down how we get into this case.
633 TC_LOG_ERROR("spells", "SPELL: deleting spell for spell ID {}. However, spell still referenced.", m_spellInfo->Id);
634 *m_selfContainer = nullptr;
635 }
636
639
640 delete m_spellValue;
641}
642
644{
645 m_targets = targets;
646
647 // this function tries to correct spell explicit targets for spell
648 // client doesn't send explicit targets correctly sometimes - we need to fix such spells serverside
649 // this also makes sure that we correctly send explicit targets to client (removes redundant data)
650 uint32 neededTargets = m_spellInfo->GetExplicitTargetMask();
651
652 if (WorldObject* target = m_targets.GetObjectTarget())
653 {
654 // check if object target is valid with needed target flags
655 // for unit case allow corpse target mask because player with not released corpse is a unit target
656 if ((target->ToUnit() && !(neededTargets & (TARGET_FLAG_UNIT_MASK | TARGET_FLAG_CORPSE_MASK)))
657 || (target->ToGameObject() && !(neededTargets & TARGET_FLAG_GAMEOBJECT_MASK))
658 || (target->ToCorpse() && !(neededTargets & TARGET_FLAG_CORPSE_MASK)))
660 }
661 else
662 {
663 // try to select correct unit target if not provided by client or by serverside cast
664 if (neededTargets & (TARGET_FLAG_UNIT_MASK))
665 {
666 Unit* unit = nullptr;
667 // try to use player selection as a target
668 if (Player* playerCaster = m_caster->ToPlayer())
669 {
670 // selection has to be found and to be valid target for the spell
671 if (Unit* selectedUnit = ObjectAccessor::GetUnit(*m_caster, playerCaster->GetTarget()))
673 unit = selectedUnit;
674 }
675 // try to use attacked unit as a target
676 else if ((m_caster->GetTypeId() == TYPEID_UNIT) && neededTargets & (TARGET_FLAG_UNIT_ENEMY | TARGET_FLAG_UNIT))
677 unit = m_caster->ToUnit()->GetVictim();
678
679 // didn't find anything - let's use self as target
680 if (!unit && neededTargets & (TARGET_FLAG_UNIT_RAID | TARGET_FLAG_UNIT_PARTY | TARGET_FLAG_UNIT_ALLY))
681 unit = m_caster->ToUnit();
682
684 }
685 }
686
687 // check if spell needs dst target
688 if (neededTargets & TARGET_FLAG_DEST_LOCATION)
689 {
690 // and target isn't set
691 if (!m_targets.HasDst())
692 {
693 // try to use unit target if provided
694 if (WorldObject* target = targets.GetObjectTarget())
695 m_targets.SetDst(*target);
696 // or use self if not available
697 else
699 }
700 }
701 else
703
704 if (neededTargets & TARGET_FLAG_SOURCE_LOCATION)
705 {
706 if (!targets.HasSrc())
708 }
709 else
711}
712
714{
715 // here go all explicit target changes made to explicit targets after spell prepare phase is finished
716 if (Unit* target = m_targets.GetUnitTarget())
717 {
718 // check for explicit target redirection, for Grounding Totem for example
721 {
722 Unit* redirect = nullptr;
723 switch (m_spellInfo->DmgClass)
724 {
727 break;
730 // should gameobjects cast damagetype melee/ranged spells this needs to be changed
731 redirect = ASSERT_NOTNULL(m_caster->ToUnit())->GetMeleeHitRedirectTarget(target, m_spellInfo);
732 break;
733 default:
734 break;
735 }
736 if (redirect && (redirect != target))
737 m_targets.SetUnitTarget(redirect);
738 }
739 }
740}
741
743{
744 // select targets for cast phase
746
747 uint32 processedAreaEffectsMask = 0;
748
749 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
750 {
751 // not call for empty effect.
752 // Also some spells use not used effect targets for store targets for dummy effect in triggered spells
753 if (!spellEffectInfo.IsEffect())
754 continue;
755
756 // set expected type of implicit targets to be sent to client
757 uint32 implicitTargetMask = GetTargetFlagMask(spellEffectInfo.TargetA.GetObjectType()) | GetTargetFlagMask(spellEffectInfo.TargetB.GetObjectType());
758 if (implicitTargetMask & TARGET_FLAG_UNIT)
760 if (implicitTargetMask & (TARGET_FLAG_GAMEOBJECT | TARGET_FLAG_GAMEOBJECT_ITEM))
762
763 SelectEffectImplicitTargets(spellEffectInfo, spellEffectInfo.TargetA, SpellTargetIndex::TargetA, processedAreaEffectsMask);
764 SelectEffectImplicitTargets(spellEffectInfo, spellEffectInfo.TargetB, SpellTargetIndex::TargetB, processedAreaEffectsMask);
765
766 // Select targets of effect based on effect type
767 // those are used when no valid target could be added for spell effect based on spell target type
768 // some spell effects use explicit target as a default target added to target map (like SPELL_EFFECT_LEARN_SPELL)
769 // some spell effects add target to target map only when target type specified (like SPELL_EFFECT_WEAPON)
770 // some spell effects don't add anything to target map (confirmed with sniffs) (like SPELL_EFFECT_DESTROY_ALL_TOTEMS)
771 SelectEffectTypeImplicitTargets(spellEffectInfo);
772
773 if (m_targets.HasDst())
774 AddDestTarget(*m_targets.GetDst(), spellEffectInfo.EffectIndex);
775
776 if (spellEffectInfo.TargetA.GetObjectType() == TARGET_OBJECT_TYPE_UNIT
777 || spellEffectInfo.TargetA.GetObjectType() == TARGET_OBJECT_TYPE_UNIT_AND_DEST
778 || spellEffectInfo.TargetB.GetObjectType() == TARGET_OBJECT_TYPE_UNIT
779 || spellEffectInfo.TargetB.GetObjectType() == TARGET_OBJECT_TYPE_UNIT_AND_DEST)
780 {
782 {
783 bool noTargetFound = std::none_of(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [effectMask = 1u << spellEffectInfo.EffectIndex](TargetInfo const& target)
784 {
785 return target.EffectMask & effectMask;
786 });
787
788 if (noTargetFound)
789 {
792 return;
793 }
794 }
796 {
797 bool anyNonImmuneTargetFound = std::any_of(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [effectMask = 1u << spellEffectInfo.EffectIndex](TargetInfo const& target)
798 {
799 return target.EffectMask & effectMask && target.MissCondition != SPELL_MISS_IMMUNE && target.MissCondition != SPELL_MISS_IMMUNE2;
800 });
801
802 if (!anyNonImmuneTargetFound)
803 {
806 return;
807 }
808 }
809 }
810
812 {
813 // maybe do this for all spells?
814 if (!focusObject && m_UniqueTargetInfo.empty() && m_UniqueGOTargetInfo.empty() && m_UniqueItemInfo.empty() && !m_targets.HasDst())
815 {
818 return;
819 }
820
821 uint32 mask = (1 << spellEffectInfo.EffectIndex);
822 for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
823 {
824 if (ihit->EffectMask & mask)
825 {
827 break;
828 }
829 }
830 }
831 }
832
833 if (m_targets.HasDst())
834 {
836 {
839 if (!(status & (LIQUID_MAP_WATER_WALK | LIQUID_MAP_IN_WATER)))
840 {
843 return;
844 }
845 }
846 }
847
849 m_delayMoment = dstDelay;
850}
851
853{
854 if (m_targets.HasDst())
855 {
856 if (m_targets.HasTraj())
857 {
858 float speed = m_targets.GetSpeedXY();
859 if (speed > 0.0f)
860 return uint64(std::floor((m_targets.GetDist2d() / speed + launchDelay) * 1000.0f));
861 }
863 return uint64(std::floor((m_spellInfo->Speed + launchDelay) * 1000.0f));
864 else if (m_spellInfo->Speed > 0.0f)
865 {
866 // We should not subtract caster size from dist calculation (fixes execution time desync with animation on client, eg. Malleable Goo cast by PP)
867 float dist = m_caster->GetExactDist(*m_targets.GetDstPos());
868 return uint64(std::floor((dist / m_spellInfo->Speed + launchDelay) * 1000.0f));
869 }
870
871 return uint64(std::floor(launchDelay * 1000.0f));
872 }
873
874 return 0;
875}
876
878{
880}
881
883{
884 m_delayMoment = hitDelay;
885
886 if (GetDelayStart())
888}
889
891{
892 auto itr = std::find_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [unit](Spell::TargetInfo const& targetInfo)
893 {
894 return targetInfo.TargetGUID == unit->GetGUID();
895 });
896
897 uint64 oldDelay = itr->TimeDelay;
898 itr->TimeDelay = hitDelay;
899
900 if (hitDelay && (!m_delayMoment || m_delayMoment > hitDelay))
901 m_delayMoment = hitDelay;
902 else if (m_delayMoment && oldDelay < hitDelay)
903 {
904 // if new hit delay is greater than old delay for this target we must check all other spell targets to see if m_delayMoment can be increased
905 auto minDelayTargetItr = std::min_element(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [](Spell::TargetInfo const& itr, Spell::TargetInfo const& smallest)
906 {
907 return itr.TimeDelay && itr.TimeDelay < smallest.TimeDelay;
908 });
909
910 m_delayMoment = minDelayTargetItr->TimeDelay;
911 }
912
913 if (GetDelayStart())
915}
916
917void Spell::SelectEffectImplicitTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType, SpellTargetIndex targetIndex, uint32& processedEffectMask)
918{
919 if (!targetType.GetTarget())
920 return;
921
922 uint32 effectMask = 1 << spellEffectInfo.EffectIndex;
923 // set the same target list for all effects
924 // some spells appear to need this, however this requires more research
925 switch (targetType.GetSelectionCategory())
926 {
931 {
932 // targets for effect already selected
933 if (effectMask & processedEffectMask)
934 return;
935 std::vector<SpellEffectInfo> const& effects = GetSpellInfo()->GetEffects();
936 // choose which targets we can select at once
937 for (uint32 j = spellEffectInfo.EffectIndex + 1; j < effects.size(); ++j)
938 {
939 if (effects[j].IsEffect() &&
940 spellEffectInfo.TargetA.GetTarget() == effects[j].TargetA.GetTarget() &&
941 spellEffectInfo.TargetB.GetTarget() == effects[j].TargetB.GetTarget() &&
942 spellEffectInfo.ImplicitTargetConditions == effects[j].ImplicitTargetConditions &&
943 spellEffectInfo.CalcRadius(m_caster, SpellTargetIndex::TargetA) == effects[j].CalcRadius(m_caster, SpellTargetIndex::TargetA) &&
944 spellEffectInfo.CalcRadius(m_caster, SpellTargetIndex::TargetB) == effects[j].CalcRadius(m_caster, SpellTargetIndex::TargetB) &&
945 spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes::PlayersOnly) == effects[j].EffectAttributes.HasFlag(SpellEffectAttributes::PlayersOnly) &&
947 {
948 effectMask |= 1 << j;
949 }
950 }
951 processedEffectMask |= effectMask;
952 break;
953 }
954 default:
955 break;
956 }
957
958 switch (targetType.GetSelectionCategory())
959 {
961 SelectImplicitChannelTargets(spellEffectInfo, targetType);
962 break;
964 SelectImplicitNearbyTargets(spellEffectInfo, targetType, targetIndex, effectMask);
965 break;
967 SelectImplicitConeTargets(spellEffectInfo, targetType, targetIndex, effectMask);
968 break;
970 SelectImplicitAreaTargets(spellEffectInfo, targetType, targetIndex, effectMask);
971 break;
973 // just in case there is no dest, explanation in SelectImplicitDestDestTargets
974 CheckDst();
975
976 SelectImplicitTrajTargets(spellEffectInfo, targetType);
977 break;
979 SelectImplicitLineTargets(spellEffectInfo, targetType, targetIndex, effectMask);
980 break;
982 switch (targetType.GetObjectType())
983 {
985 switch (targetType.GetReferenceType())
986 {
989 break;
990 default:
991 ABORT_MSG("Spell::SelectEffectImplicitTargets: received not implemented select target reference type for TARGET_TYPE_OBJECT_SRC");
992 break;
993 }
994 break;
996 switch (targetType.GetReferenceType())
997 {
999 SelectImplicitCasterDestTargets(spellEffectInfo, targetType, targetIndex);
1000 break;
1002 SelectImplicitTargetDestTargets(spellEffectInfo, targetType, targetIndex);
1003 break;
1005 SelectImplicitDestDestTargets(spellEffectInfo, targetType, targetIndex);
1006 break;
1007 default:
1008 ABORT_MSG("Spell::SelectEffectImplicitTargets: received not implemented select target reference type for TARGET_TYPE_OBJECT_DEST");
1009 break;
1010 }
1011 break;
1012 default:
1013 switch (targetType.GetReferenceType())
1014 {
1016 SelectImplicitCasterObjectTargets(spellEffectInfo, targetType);
1017 break;
1019 SelectImplicitTargetObjectTargets(spellEffectInfo, targetType);
1020 break;
1021 default:
1022 ABORT_MSG("Spell::SelectEffectImplicitTargets: received not implemented select target reference type for TARGET_TYPE_OBJECT");
1023 break;
1024 }
1025 break;
1026 }
1027 break;
1029 TC_LOG_DEBUG("spells", "SPELL: target type {}, found in spellID {}, effect {} is not implemented yet!", m_spellInfo->Id, uint32(spellEffectInfo.EffectIndex), targetType.GetTarget());
1030 break;
1031 default:
1032 ABORT_MSG("Spell::SelectEffectImplicitTargets: received not implemented select target category");
1033 break;
1034 }
1035}
1036
1038{
1040 {
1041 ABORT_MSG("Spell::SelectImplicitChannelTargets: received not implemented target reference type");
1042 return;
1043 }
1044
1046 if (!channeledSpell)
1047 {
1048 TC_LOG_DEBUG("spells", "Spell::SelectImplicitChannelTargets: cannot find channel spell for spell ID {}, effect {}", m_spellInfo->Id, uint32(spellEffectInfo.EffectIndex));
1049 return;
1050 }
1051 switch (targetType.GetTarget())
1052 {
1054 {
1055 for (ObjectGuid const& channelTarget : m_originalCaster->m_unitData->ChannelObjects)
1056 {
1057 WorldObject* target = ObjectAccessor::GetUnit(*m_caster, channelTarget);
1058 CallScriptObjectTargetSelectHandlers(target, spellEffectInfo.EffectIndex, targetType);
1059 // unit target may be no longer avalible - teleported out of map for example
1060 Unit* unitTarget = target ? target->ToUnit() : nullptr;
1061 if (unitTarget)
1062 AddUnitTarget(unitTarget, 1 << spellEffectInfo.EffectIndex);
1063 else
1064 TC_LOG_DEBUG("spells", "SPELL: cannot find channel spell target for spell ID {}, effect {}", m_spellInfo->Id, uint32(spellEffectInfo.EffectIndex));
1065 }
1066 break;
1067 }
1069 if (channeledSpell->m_targets.HasDst())
1070 m_targets.SetDst(channeledSpell->m_targets);
1071 else
1072 {
1073 auto const& channelObjects = m_originalCaster->m_unitData->ChannelObjects;
1074 WorldObject* target = !channelObjects.empty() ? ObjectAccessor::GetWorldObject(*m_caster, *channelObjects.begin()) : nullptr;
1075 if (target)
1076 {
1077 CallScriptObjectTargetSelectHandlers(target, spellEffectInfo.EffectIndex, targetType);
1078 if (target)
1079 {
1080 SpellDestination dest(*target);
1082 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1083
1084 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1085 m_targets.SetDst(dest);
1086 }
1087 }
1088 else
1089 TC_LOG_DEBUG("spells", "SPELL: cannot find channel spell destination for spell ID {}, effect {}", m_spellInfo->Id, uint32(spellEffectInfo.EffectIndex));
1090 }
1091 break;
1093 {
1094 SpellDestination dest(*channeledSpell->GetCaster());
1096 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1097
1098 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1099 m_targets.SetDst(dest);
1100 break;
1101 }
1102 default:
1103 ABORT_MSG("Spell::SelectImplicitChannelTargets: received not implemented target type");
1104 break;
1105 }
1106}
1107
1108void Spell::SelectImplicitNearbyTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType, SpellTargetIndex targetIndex, uint32 effMask)
1109{
1111 {
1112 ABORT_MSG("Spell::SelectImplicitNearbyTargets: received not implemented target reference type");
1113 return;
1114 }
1115
1116 float range = 0.0f;
1117 switch (targetType.GetCheckType())
1118 {
1119 case TARGET_CHECK_ENEMY:
1120 range = m_spellInfo->GetMaxRange(false, m_caster, this);
1121 break;
1122 case TARGET_CHECK_ALLY:
1123 case TARGET_CHECK_PARTY:
1124 case TARGET_CHECK_RAID:
1126 range = m_spellInfo->GetMaxRange(true, m_caster, this);
1127 break;
1128 case TARGET_CHECK_ENTRY:
1130 range = m_spellInfo->GetMaxRange(IsPositive(), m_caster, this);
1131 break;
1132 default:
1133 ABORT_MSG("Spell::SelectImplicitNearbyTargets: received not implemented selection check type");
1134 break;
1135 }
1136
1137 ConditionContainer* condList = spellEffectInfo.ImplicitTargetConditions.get();
1138
1139 // handle emergency case - try to use other provided targets if no conditions provided
1140 if (targetType.GetCheckType() == TARGET_CHECK_ENTRY && (!condList || condList->empty()))
1141 {
1142 TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: no conditions entry for target with TARGET_CHECK_ENTRY of spell ID {}, effect {} - selecting default targets", m_spellInfo->Id, uint32(spellEffectInfo.EffectIndex));
1143 switch (targetType.GetObjectType())
1144 {
1147 {
1148 if (focusObject)
1149 AddGOTarget(focusObject, effMask);
1150 else
1151 {
1154 }
1155 return;
1156 }
1157 break;
1160 {
1161 if (focusObject)
1162 {
1165 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1166
1167 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1168 m_targets.SetDst(dest);
1169 }
1170 else
1171 {
1174 }
1175 return;
1176 }
1177 if (targetType.GetTarget() == TARGET_DEST_NEARBY_ENTRY_OR_DB)
1178 {
1179 if (SpellTargetPosition const* st = sSpellMgr->GetSpellTargetPosition(m_spellInfo->Id, spellEffectInfo.EffectIndex))
1180 {
1182 if (st->target_mapId == m_caster->GetMapId() && m_caster->IsInDist(st->target_X, st->target_Y, st->target_Z, range))
1183 dest = SpellDestination(st->target_X, st->target_Y, st->target_Z, st->target_Orientation);
1184 else
1185 {
1186 float randomRadius = spellEffectInfo.CalcRadius(m_caster, targetIndex);
1187 if (randomRadius > 0.0f)
1188 MovePosition(dest._position, m_caster, randomRadius, targetType.CalcDirectionAngle());
1189 }
1190
1191 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1192 m_targets.SetDst(dest);
1193 return;
1194 }
1195 }
1196 break;
1197 default:
1198 break;
1199 }
1200 }
1201
1202 WorldObject* target = SearchNearbyTarget(spellEffectInfo, range, targetType.GetObjectType(), targetType.GetCheckType(), condList);
1203 float randomRadius = 0.0f;
1204 switch (targetType.GetTarget())
1205 {
1207 // if we are here then there was no db target
1208 if (!target)
1209 {
1210 target = m_caster;
1211 // radius is only meant to be randomized when using caster fallback
1212 randomRadius = spellEffectInfo.CalcRadius(m_caster, targetIndex);
1213 }
1214 break;
1215 default:
1216 break;
1217 }
1218
1219 if (!target)
1220 {
1221 TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: cannot find nearby target for spell ID {}, effect {}", m_spellInfo->Id, uint32(spellEffectInfo.EffectIndex));
1224 return;
1225 }
1226
1227 CallScriptObjectTargetSelectHandlers(target, spellEffectInfo.EffectIndex, targetType);
1228 if (!target)
1229 {
1230 TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id {} set NULL target, effect {}", m_spellInfo->Id, uint32(spellEffectInfo.EffectIndex));
1233 return;
1234 }
1235
1236 switch (targetType.GetObjectType())
1237 {
1239 if (Unit* unit = target->ToUnit())
1240 AddUnitTarget(unit, effMask, true, false);
1241 else
1242 {
1243 TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id {} set object of wrong type, expected unit, got {}, effect {}", m_spellInfo->Id, target->GetGUID().GetTypeName(), effMask);
1246 return;
1247 }
1248 break;
1250 if (GameObject* gobjTarget = target->ToGameObject())
1251 AddGOTarget(gobjTarget, effMask);
1252 else
1253 {
1254 TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id {} set object of wrong type, expected gameobject, got {}, effect {}", m_spellInfo->Id, target->GetGUID().GetTypeName(), effMask);
1257 return;
1258 }
1259 break;
1261 if (Corpse* corpseTarget = target->ToCorpse())
1262 AddCorpseTarget(corpseTarget, effMask);
1263 else
1264 {
1265 TC_LOG_DEBUG("spells", "Spell::SelectImplicitNearbyTargets: OnObjectTargetSelect script hook for spell Id {} set object of wrong type, expected corpse, got {}, effect {}", m_spellInfo->Id, target->GetGUID().GetTypeName(), effMask);
1268 return;
1269 }
1270 break;
1272 {
1273 SpellDestination dest(*target);
1274 if (randomRadius > 0.0f)
1275 MovePosition(dest._position, target, randomRadius, targetType.CalcDirectionAngle());
1276
1278 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1279
1280 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1281 m_targets.SetDst(dest);
1282 break;
1283 }
1284 default:
1285 ABORT_MSG("Spell::SelectImplicitNearbyTargets: received not implemented target object type");
1286 break;
1287 }
1288
1289 SelectImplicitChainTargets(spellEffectInfo, targetType, target, effMask);
1290}
1291
1292void Spell::SelectImplicitConeTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType, SpellTargetIndex targetIndex, uint32 effMask)
1293{
1294 Position coneSrc(*m_caster);
1295 float coneAngle = m_spellInfo->ConeAngle;
1296 switch (targetType.GetReferenceType())
1297 {
1299 break;
1303 break;
1304 default:
1305 break;
1306 }
1307
1308 switch (targetType.GetTarget())
1309 {
1311 if (coneAngle == 0.0f)
1312 coneAngle = 180.0f;
1313 break;
1314 default:
1315 break;
1316 }
1317
1318 std::list<WorldObject*> targets;
1319 SpellTargetObjectTypes objectType = targetType.GetObjectType();
1320 SpellTargetCheckTypes selectionType = targetType.GetCheckType();
1321 ConditionContainer* condList = spellEffectInfo.ImplicitTargetConditions.get();
1322 float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue->RadiusMod;
1323
1324 if (uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList))
1325 {
1326 float extraSearchRadius = radius > 0.0f ? EXTRA_CELL_SEARCH_RADIUS : 0.0f;
1327 Trinity::WorldObjectSpellConeTargetCheck check(coneSrc, DegToRad(coneAngle), m_spellInfo->Width ? m_spellInfo->Width : m_caster->GetCombatReach(), radius, m_caster, m_spellInfo, selectionType, condList, objectType);
1329 SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellConeTargetCheck> >(searcher, containerTypeMask, m_caster, m_caster, radius + extraSearchRadius);
1330
1331 CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType);
1332
1333 if (!targets.empty())
1334 {
1335 // Other special target selection goes here
1336 if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
1337 Trinity::Containers::RandomResize(targets, maxTargets);
1338
1339 for (WorldObject* itr : targets)
1340 {
1341 if (Unit* unit = itr->ToUnit())
1342 AddUnitTarget(unit, effMask, false);
1343 else if (GameObject* gObjTarget = itr->ToGameObject())
1344 AddGOTarget(gObjTarget, effMask);
1345 else if (Corpse* corpse = itr->ToCorpse())
1346 AddCorpseTarget(corpse, effMask);
1347 }
1348 }
1349 }
1350}
1351
1352void Spell::SelectImplicitAreaTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType, SpellTargetIndex targetIndex, uint32 effMask)
1353{
1354 WorldObject* referer = nullptr;
1355 switch (targetType.GetReferenceType())
1356 {
1360 referer = m_caster;
1361 break;
1363 referer = m_targets.GetUnitTarget();
1364 break;
1366 {
1367 referer = m_caster;
1368
1369 // find last added target for this effect
1370 for (auto ihit = m_UniqueTargetInfo.rbegin(); ihit != m_UniqueTargetInfo.rend(); ++ihit)
1371 {
1372 if (ihit->EffectMask & (1 << spellEffectInfo.EffectIndex))
1373 {
1374 referer = ObjectAccessor::GetUnit(*m_caster, ihit->TargetGUID);
1375 break;
1376 }
1377 }
1378 break;
1379 }
1380 default:
1381 ABORT_MSG("Spell::SelectImplicitAreaTargets: received not implemented target reference type");
1382 return;
1383 }
1384
1385 if (!referer)
1386 return;
1387
1388 Position const* center = nullptr;
1389 switch (targetType.GetReferenceType())
1390 {
1392 center = m_targets.GetSrcPos();
1393 break;
1395 center = m_targets.GetDstPos();
1396 break;
1400 center = referer;
1401 break;
1402 default:
1403 ABORT_MSG("Spell::SelectImplicitAreaTargets: received not implemented target reference type");
1404 return;
1405 }
1406
1407 float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue->RadiusMod;
1408 std::list<WorldObject*> targets;
1409 switch (targetType.GetTarget())
1410 {
1412 targets.push_back(m_caster);
1413 if (Unit* unit = m_caster->ToUnit())
1414 if (Vehicle const* vehicleKit = unit->GetVehicleKit())
1415 for (int8 seat = 0; seat < MAX_VEHICLE_SEATS; ++seat)
1416 if (Unit* passenger = vehicleKit->GetPassenger(seat))
1417 targets.push_back(passenger);
1418 break;
1420 if (Unit* targetedUnit = m_targets.GetUnitTarget())
1421 {
1422 if (!m_caster->IsUnit() || !m_caster->ToUnit()->IsInRaidWith(targetedUnit))
1423 targets.push_back(m_targets.GetUnitTarget());
1424 else
1425 SearchAreaTargets(targets, spellEffectInfo, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(),
1427 }
1428 break;
1430 targets.push_back(m_caster);
1431 SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(),
1433 break;
1435 if (Unit* unit = m_caster->ToUnit())
1436 for (ThreatReference const* threatRef : unit->GetThreatManager().GetUnsortedThreatList())
1437 if (Unit* threateningUnit = threatRef->GetVictim())
1438 targets.push_back(threateningUnit);
1439 break;
1441 if (Creature* creature = m_caster->ToCreature())
1442 for (ObjectGuid const& tapperGuid : creature->GetTapList())
1443 if (Player* tapper = ObjectAccessor::GetPlayer(*m_caster, tapperGuid))
1444 targets.push_back(tapper);
1445 break;
1446 default:
1447 SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(),
1449 break;
1450 }
1451
1453 {
1454 SpellDestination dest(*referer);
1456 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1457
1458 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1459
1460 m_targets.ModDst(dest);
1461 }
1462
1463 CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType);
1464
1466 targets.sort(Trinity::ObjectDistanceOrderPred(referer, false));
1467
1468 if (!targets.empty())
1469 {
1470 // Other special target selection goes here
1471 if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
1472 {
1474 Trinity::Containers::RandomResize(targets, maxTargets);
1475 else if (targets.size() > maxTargets)
1476 targets.resize(maxTargets);
1477 }
1478
1479 for (WorldObject* itr : targets)
1480 {
1481 if (Unit* unit = itr->ToUnit())
1482 AddUnitTarget(unit, effMask, false, true, center);
1483 else if (GameObject* gObjTarget = itr->ToGameObject())
1484 AddGOTarget(gObjTarget, effMask);
1485 else if (Corpse* corpse = itr->ToCorpse())
1486 AddCorpseTarget(corpse, effMask);
1487 }
1488 }
1489}
1490
1492{
1494
1495 switch (targetType.GetTarget())
1496 {
1497 case TARGET_DEST_CASTER:
1498 break;
1499 case TARGET_DEST_HOME:
1500 if (Player* playerCaster = m_caster->ToPlayer())
1501 dest = SpellDestination(playerCaster->m_homebind);
1502 break;
1503 case TARGET_DEST_DB:
1504 if (SpellTargetPosition const* st = sSpellMgr->GetSpellTargetPosition(m_spellInfo->Id, spellEffectInfo.EffectIndex))
1505 {
1508 dest = SpellDestination(st->target_X, st->target_Y, st->target_Z, st->target_Orientation, (int32)st->target_mapId);
1509 else if (st->target_mapId == m_caster->GetMapId())
1510 dest = SpellDestination(st->target_X, st->target_Y, st->target_Z, st->target_Orientation);
1511 }
1512 else
1513 {
1514 TC_LOG_DEBUG("spells", "SPELL: unknown target coordinates for spell ID {}", m_spellInfo->Id);
1515 if (WorldObject* target = m_targets.GetObjectTarget())
1516 dest = SpellDestination(*target);
1517 }
1518 break;
1520 {
1521 float minDist = m_spellInfo->GetMinRange(true);
1522 float maxDist = m_spellInfo->GetMaxRange(true);
1523 float dist = frand(minDist, maxDist);
1524 float x, y, z;
1525 float angle = rand_norm() * static_cast<float>(M_PI * 35.0f / 180.0f) - static_cast<float>(M_PI * 17.5f / 180.0f);
1527
1528 float ground = m_caster->GetMapHeight(x, y, z);
1529 float liquidLevel = VMAP_INVALID_HEIGHT_VALUE;
1530 LiquidData liquidData;
1531 if (m_caster->GetMap()->GetLiquidStatus(m_caster->GetPhaseShift(), x, y, z, {}, &liquidData, m_caster->GetCollisionHeight()))
1532 liquidLevel = liquidData.level;
1533
1534 if (liquidLevel <= ground) // When there is no liquid Map::GetWaterOrGroundLevel returns ground level
1535 {
1539 return;
1540 }
1541
1542 if (ground + 0.75 > liquidLevel)
1543 {
1547 return;
1548 }
1549
1550 dest = SpellDestination(x, y, liquidLevel, m_caster->GetOrientation());
1551 break;
1552 }
1555 {
1556 Unit* unitCaster = m_caster->ToUnit();
1557 if (!unitCaster)
1558 break;
1559
1560 float dist = spellEffectInfo.CalcRadius(unitCaster, targetIndex);
1561 float angle = targetType.CalcDirectionAngle();
1563 {
1564 angle = [&]()
1565 {
1567 {
1568 case MOVEMENTFLAG_NONE:
1574 return 0.0f;
1577 return static_cast<float>(M_PI);
1580 return static_cast<float>(M_PI / 2);
1582 return static_cast<float>(M_PI / 4);
1584 return static_cast<float>(3 * M_PI / 4);
1587 return static_cast<float>(-M_PI / 2);
1589 return static_cast<float>(-M_PI / 4);
1591 return static_cast<float>(-3 * M_PI / 4);
1592 default:
1593 return 0.0f;
1594 }
1595 }();
1596 }
1597
1598 Position pos = dest._position;
1599
1600 MovePosition(pos, unitCaster, dist, angle);
1601 dest.Relocate(pos);
1602 break;
1603 }
1607 break;
1609 if (Unit const* unitCaster = m_caster->ToUnit())
1610 if (TempSummon const* casterSummon = unitCaster->ToTempSummon())
1611 if (WorldObject const* summoner = casterSummon->GetSummoner())
1612 dest = SpellDestination(*summoner);
1613 break;
1614 default:
1615 {
1616 float dist = spellEffectInfo.CalcRadius(m_caster, targetIndex);
1617 float angle = targetType.CalcDirectionAngle();
1618 float objSize = m_caster->GetCombatReach();
1619
1620 switch (targetType.GetTarget())
1621 {
1623 dist = PET_FOLLOW_DIST;
1624 break;
1626 if (dist > objSize)
1627 dist = objSize + (dist - objSize);
1628 break;
1633 {
1634 static float const DefaultTotemDistance = 3.0f;
1635 if (!spellEffectInfo.HasRadius(targetIndex))
1636 dist = DefaultTotemDistance;
1637 break;
1638 }
1639 default:
1640 break;
1641 }
1642
1643 if (dist < objSize)
1644 dist = objSize;
1645
1646 Position pos = dest._position;
1647 MovePosition(pos, m_caster, dist, angle);
1648
1649 dest.Relocate(pos);
1650 break;
1651 }
1652 }
1653
1655 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1656
1657 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1658 m_targets.SetDst(dest);
1659}
1660
1662{
1663 ASSERT(m_targets.GetObjectTarget() && "Spell::SelectImplicitTargetDestTargets - no explicit object target available!");
1665
1666 SpellDestination dest(*target);
1667
1668 switch (targetType.GetTarget())
1669 {
1673 break;
1674 default:
1675 {
1676 float angle = targetType.CalcDirectionAngle();
1677 float dist = spellEffectInfo.CalcRadius(nullptr, targetIndex);
1678
1679 Position pos = dest._position;
1680 MovePosition(pos, target, dist, angle);
1681
1682 dest.Relocate(pos);
1683 break;
1684 }
1685 }
1686
1688 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1689
1690 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1691 m_targets.SetDst(dest);
1692}
1693
1694void Spell::SelectImplicitDestDestTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType, SpellTargetIndex targetIndex)
1695{
1696 // set destination to caster if no dest provided
1697 // can only happen if previous destination target could not be set for some reason
1698 // (not found nearby target, or channel target for example
1699 // maybe we should abort the spell in such case?
1700 CheckDst();
1701
1703
1704 switch (targetType.GetTarget())
1705 {
1709 case TARGET_DEST_DEST:
1710 break;
1713 break;
1715 {
1716 float dist = spellEffectInfo.CalcRadius(m_caster, targetIndex);
1717 Position pos = dest._position;
1718 float angle = pos.GetAbsoluteAngle(m_caster) - m_caster->GetOrientation();
1719
1720 MovePosition(pos, m_caster, dist, angle);
1722
1723 dest.Relocate(pos);
1724 break;
1725 }
1726 default:
1727 {
1728 float angle = targetType.CalcDirectionAngle();
1729 float dist = spellEffectInfo.CalcRadius(m_caster, targetIndex);
1730
1731 Position pos = dest._position;
1732 MovePosition(pos, m_caster, dist, angle);
1733
1734 dest.Relocate(pos);
1735 break;
1736 }
1737 }
1738
1740 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1741
1742 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1743 m_targets.ModDst(dest);
1744}
1745
1747{
1748 WorldObject* target = nullptr;
1749 bool checkIfValid = true;
1750
1751 switch (targetType.GetTarget())
1752 {
1753 case TARGET_UNIT_CASTER:
1754 target = m_caster;
1755 checkIfValid = false;
1756 break;
1757 case TARGET_UNIT_MASTER:
1758 target = m_caster->GetCharmerOrOwner();
1759 break;
1760 case TARGET_UNIT_PET:
1761 if (Unit* unitCaster = m_caster->ToUnit())
1762 target = unitCaster->GetGuardianPet();
1763 break;
1765 if (Unit* unitCaster = m_caster->ToUnit())
1766 if (unitCaster->IsSummon())
1767 target = unitCaster->ToTempSummon()->GetSummonerUnit();
1768 break;
1770 if (Unit* unitCaster = m_caster->ToUnit())
1771 target = unitCaster->GetVehicleBase();
1772 break;
1781 if (Creature* vehicleBase = m_caster->ToCreature())
1782 if (vehicleBase->IsVehicle())
1783 target = vehicleBase->GetVehicleKit()->GetPassenger(targetType.GetTarget() - TARGET_UNIT_PASSENGER_0);
1784 break;
1786 if (Creature* creatureCaster = m_caster->ToCreature())
1787 if (!creatureCaster->GetTapList().empty())
1788 target = ObjectAccessor::GetWorldObject(*creatureCaster, Trinity::Containers::SelectRandomContainerElement(creatureCaster->GetTapList()));
1789 break;
1791 if (Unit const* unitCaster = m_caster->ToUnit())
1792 target = ObjectAccessor::GetCreatureOrPetOrVehicle(*m_caster, unitCaster->GetCritterGUID());
1793 break;
1794 default:
1795 break;
1796 }
1797
1798 CallScriptObjectTargetSelectHandlers(target, spellEffectInfo.EffectIndex, targetType);
1799
1800 if (target)
1801 {
1802 if (Unit* unit = target->ToUnit())
1803 AddUnitTarget(unit, 1 << spellEffectInfo.EffectIndex, checkIfValid);
1804 else if (GameObject* go = target->ToGameObject())
1805 AddGOTarget(go, 1 << spellEffectInfo.EffectIndex);
1806 else if (Corpse* corpse = target->ToCorpse())
1807 AddCorpseTarget(corpse, 1 << spellEffectInfo.EffectIndex);
1808 }
1809}
1810
1812{
1813 ASSERT((m_targets.GetObjectTarget() || m_targets.GetItemTarget()) && "Spell::SelectImplicitTargetObjectTargets - no explicit object or item target available!");
1814
1816
1817 CallScriptObjectTargetSelectHandlers(target, spellEffectInfo.EffectIndex, targetType);
1818
1819 if (target)
1820 {
1821 if (Unit* unit = target->ToUnit())
1822 AddUnitTarget(unit, 1 << spellEffectInfo.EffectIndex, true, false);
1823 else if (GameObject* gobj = target->ToGameObject())
1824 AddGOTarget(gobj, 1 << spellEffectInfo.EffectIndex);
1825 else if (Corpse* corpse = target->ToCorpse())
1826 AddCorpseTarget(corpse, 1 << spellEffectInfo.EffectIndex);
1827
1828 SelectImplicitChainTargets(spellEffectInfo, targetType, target, 1 << spellEffectInfo.EffectIndex);
1829 }
1830 // Script hook can remove object target and we would wrongly land here
1831 else if (Item* item = m_targets.GetItemTarget())
1832 AddItemTarget(item, 1 << spellEffectInfo.EffectIndex);
1833}
1834
1835void Spell::SelectImplicitChainTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType, WorldObject* target, uint32 effMask)
1836{
1837 int32 maxTargets = spellEffectInfo.ChainTargets;
1838 if (Player* modOwner = m_caster->GetSpellModOwner())
1839 modOwner->ApplySpellMod(m_spellInfo, SpellModOp::ChainTargets, maxTargets, this);
1840
1841 if (maxTargets > 1)
1842 {
1843 // mark damage multipliers as used
1844 for (size_t k = spellEffectInfo.EffectIndex; k < m_spellInfo->GetEffects().size(); ++k)
1845 if (effMask & (1 << k))
1846 m_damageMultipliers[k] = 1.0f;
1847 m_applyMultiplierMask |= effMask;
1848
1849 std::list<WorldObject*> targets;
1850 SearchChainTargets(targets, maxTargets - 1, target, targetType.GetObjectType(), targetType.GetCheckType()
1851 , spellEffectInfo, targetType.GetTarget() == TARGET_UNIT_TARGET_CHAINHEAL_ALLY);
1852
1853 // Chain primary target is added earlier
1854 CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType);
1855
1857
1858 for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
1859 {
1860 if (Unit* unit = (*itr)->ToUnit())
1861 AddUnitTarget(unit, effMask, false, true, losPosition);
1862
1864 losPosition = *itr;
1865 }
1866 }
1867}
1868
1869float tangent(float x)
1870{
1871 x = std::tan(x);
1872 //if (x < std::numeric_limits<float>::max() && x > -std::numeric_limits<float>::max()) return x;
1873 //if (x >= std::numeric_limits<float>::max()) return std::numeric_limits<float>::max();
1874 //if (x <= -std::numeric_limits<float>::max()) return -std::numeric_limits<float>::max();
1875 if (x < 100000.0f && x > -100000.0f) return x;
1876 if (x >= 100000.0f) return 100000.0f;
1877 if (x <= 100000.0f) return -100000.0f;
1878 return 0.0f;
1879}
1880
1881void Spell::SelectImplicitTrajTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType)
1882{
1883 if (!m_targets.HasTraj())
1884 return;
1885
1886 float dist2d = m_targets.GetDist2d();
1887 if (!dist2d)
1888 return;
1889
1890 Position srcPos = *m_targets.GetSrcPos();
1892 float srcToDestDelta = m_targets.GetDstPos()->m_positionZ - srcPos.m_positionZ;
1893
1894 std::list<WorldObject*> targets;
1897 SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellTrajTargetCheck> > (searcher, GRID_MAP_TYPE_MASK_ALL, m_caster, &srcPos, dist2d);
1898 if (targets.empty())
1899 return;
1900
1902
1903 float b = tangent(m_targets.GetPitch());
1904 float a = (srcToDestDelta - dist2d * b) / (dist2d * dist2d);
1905 if (a > -0.0001f)
1906 a = 0.f;
1907
1908 // We should check if triggered spell has greater range (which is true in many cases, and initial spell has too short max range)
1909 // limit max range to 300 yards, sometimes triggered spells can have 50000yds
1910 float bestDist = m_spellInfo->GetMaxRange(false);
1911 if (SpellInfo const* triggerSpellInfo = sSpellMgr->GetSpellInfo(spellEffectInfo.TriggerSpell, GetCastDifficulty()))
1912 bestDist = std::min(std::max(bestDist, triggerSpellInfo->GetMaxRange(false)), std::min(dist2d, 300.0f));
1913
1914 // GameObjects don't cast traj
1915 Unit* unitCaster = ASSERT_NOTNULL(m_caster->ToUnit());
1916 for (auto itr = targets.begin(); itr != targets.end(); ++itr)
1917 {
1918 if (m_spellInfo->CheckTarget(unitCaster, *itr, true) != SPELL_CAST_OK)
1919 continue;
1920
1921 if (Unit* unit = (*itr)->ToUnit())
1922 {
1923 if (unitCaster == *itr || unitCaster->IsOnVehicle(unit) || unit->GetVehicle())
1924 continue;
1925
1926 if (Creature* creatureTarget = unit->ToCreature())
1927 {
1928 if (!(creatureTarget->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_COLLIDE_WITH_MISSILES))
1929 continue;
1930 }
1931 }
1932
1933 float const size = std::max((*itr)->GetCombatReach(), 1.0f);
1934 float const objDist2d = srcPos.GetExactDist2d(*itr);
1935 float const dz = (*itr)->GetPositionZ() - srcPos.m_positionZ;
1936
1937 float const horizontalDistToTraj = std::fabs(objDist2d * std::sin(srcPos.GetRelativeAngle(*itr)));
1938 float const sizeFactor = std::cos((horizontalDistToTraj / size) * (M_PI / 2.0f));
1939 float const distToHitPoint = std::max(objDist2d * std::cos(srcPos.GetRelativeAngle(*itr)) - size * sizeFactor, 0.0f);
1940 float const height = distToHitPoint * (a * distToHitPoint + b);
1941
1942 if (fabs(dz - height) > size + b / 2.0f + TRAJECTORY_MISSILE_SIZE)
1943 continue;
1944
1945 if (distToHitPoint < bestDist)
1946 {
1947 bestDist = distToHitPoint;
1948 break;
1949 }
1950 }
1951
1952 if (dist2d > bestDist)
1953 {
1954 float x = m_targets.GetSrcPos()->m_positionX + std::cos(unitCaster->GetOrientation()) * bestDist;
1955 float y = m_targets.GetSrcPos()->m_positionY + std::sin(unitCaster->GetOrientation()) * bestDist;
1956 float z = m_targets.GetSrcPos()->m_positionZ + bestDist * (a * bestDist + b);
1957
1958 SpellDestination dest(x, y, z, unitCaster->GetOrientation());
1960 dest._position.SetOrientation(spellEffectInfo.PositionFacing);
1961
1962 CallScriptDestinationTargetSelectHandlers(dest, spellEffectInfo.EffectIndex, targetType);
1963 m_targets.ModDst(dest);
1964 }
1965}
1966
1967void Spell::SelectImplicitLineTargets(SpellEffectInfo const& spellEffectInfo, SpellImplicitTargetInfo const& targetType, SpellTargetIndex targetIndex, uint32 effMask)
1968{
1969 std::list<WorldObject*> targets;
1970 SpellTargetObjectTypes objectType = targetType.GetObjectType();
1971 SpellTargetCheckTypes selectionType = targetType.GetCheckType();
1972 Position const* dst = nullptr;
1973 switch (targetType.GetReferenceType())
1974 {
1976 dst = m_targets.GetSrcPos();
1977 break;
1979 dst = m_targets.GetDstPos();
1980 break;
1982 dst = m_caster;
1983 break;
1985 dst = m_targets.GetUnitTarget();
1986 break;
1987 default:
1988 ABORT_MSG("Spell::SelectImplicitLineTargets: received not implemented target reference type");
1989 return;
1990 }
1991
1992 ConditionContainer* condList = spellEffectInfo.ImplicitTargetConditions.get();
1993 float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue->RadiusMod;
1994
1995 if (uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList))
1996 {
1997 Trinity::WorldObjectSpellLineTargetCheck check(m_caster, dst, m_spellInfo->Width ? m_spellInfo->Width : m_caster->GetCombatReach(), radius, m_caster, m_spellInfo, selectionType, condList, objectType);
1999 SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellLineTargetCheck>>(searcher, containerTypeMask, m_caster, m_caster, radius);
2000
2001 CallScriptObjectAreaTargetSelectHandlers(targets, spellEffectInfo.EffectIndex, targetType);
2002
2003 if (!targets.empty())
2004 {
2005 // Other special target selection goes here
2006 if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
2007 {
2008 if (maxTargets < targets.size())
2009 {
2011 targets.resize(maxTargets);
2012 }
2013 }
2014
2015 for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
2016 {
2017 if (Unit* unit = (*itr)->ToUnit())
2018 AddUnitTarget(unit, effMask, false);
2019 else if (GameObject* gObjTarget = (*itr)->ToGameObject())
2020 AddGOTarget(gObjTarget, effMask);
2021 else if (Corpse* corpse = (*itr)->ToCorpse())
2022 AddCorpseTarget(corpse, 1 << spellEffectInfo.EffectIndex);
2023 }
2024 }
2025 }
2026}
2027
2029{
2030 // special case for SPELL_EFFECT_SUMMON_RAF_FRIEND and SPELL_EFFECT_SUMMON_PLAYER, queue them on map for later execution
2031 switch (spellEffectInfo.Effect)
2032 {
2036 {
2039
2040 // scripts may modify the target - recheck
2041 if (target && target->GetTypeId() == TYPEID_PLAYER)
2042 {
2043 // target is not stored in target map for those spells
2044 // since we're completely skipping AddUnitTarget logic, we need to check immunity manually
2045 // eg. aura 21546 makes target immune to summons
2046 Player* player = target->ToPlayer();
2047 if (player->IsImmunedToSpellEffect(m_spellInfo, spellEffectInfo, nullptr))
2048 return;
2049
2050 target->GetMap()->AddFarSpellCallback([spell = this, &spellEffectInfo, targetGuid = target->GetGUID()](Map* map)
2051 {
2052 Player* player = ObjectAccessor::GetPlayer(map, targetGuid);
2053 if (!player)
2054 return;
2055
2056 // check immunity again in case it changed during update
2057 if (player->IsImmunedToSpellEffect(spell->GetSpellInfo(), spellEffectInfo, nullptr))
2058 return;
2059
2060 spell->HandleEffects(player, nullptr, nullptr, nullptr, spellEffectInfo, SPELL_EFFECT_HANDLE_HIT_TARGET);
2061 });
2062 }
2063 }
2064 return;
2065 default:
2066 break;
2067 }
2068
2069 // select spell implicit targets based on effect type
2070 if (!spellEffectInfo.GetImplicitTargetType())
2071 return;
2072
2073 uint32 targetMask = spellEffectInfo.GetMissingTargetMask();
2074
2075 if (!targetMask)
2076 return;
2077
2078 WorldObject* target = nullptr;
2079
2080 switch (spellEffectInfo.GetImplicitTargetType())
2081 {
2082 // add explicit object target or self to the target map
2084 // player which not released his spirit is Unit, but target flag for it is TARGET_FLAG_CORPSE_MASK
2086 {
2087 if (Unit* unit = m_targets.GetUnitTarget())
2088 target = unit;
2089 else if (targetMask & TARGET_FLAG_CORPSE_MASK)
2090 {
2091 if (Corpse* corpseTarget = m_targets.GetCorpseTarget())
2092 target = corpseTarget;
2093 }
2094 else //if (targetMask & TARGET_FLAG_UNIT_MASK)
2095 target = m_caster;
2096 }
2097 if (targetMask & TARGET_FLAG_ITEM_MASK)
2098 {
2099 if (Item* item = m_targets.GetItemTarget())
2100 AddItemTarget(item, 1 << spellEffectInfo.EffectIndex);
2101 return;
2102 }
2103 if (targetMask & TARGET_FLAG_GAMEOBJECT_MASK)
2104 target = m_targets.GetGOTarget();
2105 break;
2106 // add self to the target map
2108 if (targetMask & TARGET_FLAG_UNIT_MASK)
2109 target = m_caster;
2110 break;
2111 default:
2112 break;
2113 }
2114
2116
2117 if (target)
2118 {
2119 if (target->ToUnit())
2120 AddUnitTarget(target->ToUnit(), 1 << spellEffectInfo.EffectIndex, false);
2121 else if (target->ToGameObject())
2122 AddGOTarget(target->ToGameObject(), 1 << spellEffectInfo.EffectIndex);
2123 else if (target->ToCorpse())
2124 AddCorpseTarget(target->ToCorpse(), 1 << spellEffectInfo.EffectIndex);
2125 }
2126}
2127
2128uint32 Spell::GetSearcherTypeMask(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, SpellTargetObjectTypes objType, ConditionContainer const* condList)
2129{
2130 // this function selects which containers need to be searched for spell target
2132
2133 // filter searchers based on searched object type
2134 switch (objType)
2135 {
2139 break;
2144 break;
2148 break;
2149 default:
2150 break;
2151 }
2152
2156 retMask &= GRID_MAP_TYPE_MASK_PLAYER;
2158 retMask &= ~GRID_MAP_TYPE_MASK_PLAYER;
2159
2160 if (condList)
2161 retMask &= sConditionMgr->GetSearcherTypeMaskForConditionList(*condList);
2162 return retMask;
2163}
2164
2165template<class SEARCHER>
2166void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius)
2167{
2168 if (!containerMask)
2169 return;
2170
2171 // search world and grid for possible targets
2172 bool searchInGrid = (containerMask & (GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_GAMEOBJECT)) != 0;
2173 bool searchInWorld = (containerMask & (GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER | GRID_MAP_TYPE_MASK_CORPSE)) != 0;
2174 if (searchInGrid || searchInWorld)
2175 {
2176 float x, y;
2177 x = pos->GetPositionX();
2178 y = pos->GetPositionY();
2179
2181 Cell cell(p);
2182 cell.SetNoCreate();
2183
2184 Map* map = referer->GetMap();
2185
2186 if (searchInWorld)
2187 Cell::VisitWorldObjects(x, y, map, searcher, radius);
2188
2189 if (searchInGrid)
2190 Cell::VisitGridObjects(x, y, map, searcher, radius);
2191 }
2192}
2193
2194template TC_GAME_API void Spell::SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck>>(Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck>& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius);
2195
2196WorldObject* Spell::SearchNearbyTarget(SpellEffectInfo const& spellEffectInfo, float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList)
2197{
2198 WorldObject* target = nullptr;
2199 uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList);
2200 if (!containerTypeMask)
2201 return nullptr;
2202
2203 Trinity::WorldObjectSpellNearbyTargetCheck check(range, m_caster, m_spellInfo, selectionType, condList, objectType);
2206 SearchTargets<Trinity::WorldObjectLastSearcher<Trinity::WorldObjectSpellNearbyTargetCheck>>(searcher, containerTypeMask, m_caster, m_caster, range);
2207 return target;
2208}
2209
2210void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, SpellEffectInfo const& spellEffectInfo, float range, Position const* position, WorldObject* referer,
2211 SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList,
2213{
2214 uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList);
2215 if (!containerTypeMask)
2216 return;
2217
2218 float extraSearchRadius = range > 0.0f ? EXTRA_CELL_SEARCH_RADIUS : 0.0f;
2219 Trinity::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList, objectType, searchReason);
2222 SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck>>(searcher, containerTypeMask, m_caster, position, range + extraSearchRadius);
2223}
2224
2225void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType,
2226 SpellTargetCheckTypes selectType, SpellEffectInfo const& spellEffectInfo, bool isChainHeal)
2227{
2228 // max dist for jump target selection
2229 float jumpRadius = 0.0f;
2230 switch (m_spellInfo->DmgClass)
2231 {
2233 // 7.5y for multi shot
2234 jumpRadius = 7.5f;
2235 break;
2237 // 5y for swipe, cleave and similar
2238 jumpRadius = 5.0f;
2239 break;
2242 // 12.5y for chain heal spell since 3.2 patch
2243 if (isChainHeal)
2244 jumpRadius = 12.5f;
2245 // 10y as default for magic chain spells
2246 else
2247 jumpRadius = 10.0f;
2248 break;
2249 }
2250
2251 if (Player* modOwner = m_caster->GetSpellModOwner())
2252 modOwner->ApplySpellMod(m_spellInfo, SpellModOp::ChainJumpDistance, jumpRadius, this);
2253
2254 // max dist which spell can reach
2255 float searchRadius = [&]()
2256 {
2258 return GetMinMaxRange(false).second;
2259
2261 return jumpRadius;
2262
2263 return jumpRadius * chainTargets;
2264 }();
2265
2267 std::list<WorldObject*> tempTargets;
2268 SearchAreaTargets(tempTargets, spellEffectInfo, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions.get(),
2270 tempTargets.remove(target);
2271
2272 // remove targets which are always invalid for chain spells
2273 // for some spells allow only chain targets in front of caster (swipe for example)
2275 {
2276 tempTargets.remove_if([&](WorldObject* object)
2277 {
2278 return !m_caster->HasInArc(static_cast<float>(M_PI), object);
2279 });
2280 }
2281
2282 while (chainTargets)
2283 {
2284 // try to get unit for next chain jump
2285 std::list<WorldObject*>::iterator foundItr = tempTargets.end();
2286 // get unit with highest hp deficit in dist
2287 if (isChainHeal)
2288 {
2289 uint32 maxHPDeficit = 0;
2290 for (std::list<WorldObject*>::iterator itr = tempTargets.begin(); itr != tempTargets.end(); ++itr)
2291 {
2292 if (Unit* unit = (*itr)->ToUnit())
2293 {
2294 uint32 deficit = unit->GetMaxHealth() - unit->GetHealth();
2295 if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && chainSource->IsWithinDist(unit, jumpRadius) && IsWithinLOS(chainSource, unit, false, VMAP::ModelIgnoreFlags::M2))
2296 {
2297 foundItr = itr;
2298 maxHPDeficit = deficit;
2299 }
2300 }
2301 }
2302 }
2303 // get closest object
2304 else
2305 {
2306 for (std::list<WorldObject*>::iterator itr = tempTargets.begin(); itr != tempTargets.end(); ++itr)
2307 {
2308 bool isBestDistanceMatch = foundItr != tempTargets.end() ? chainSource->GetDistanceOrder(*itr, *foundItr) : chainSource->IsWithinDist(*itr, jumpRadius);
2309 if (!isBestDistanceMatch)
2310 continue;
2311
2312 if (!IsWithinLOS(chainSource, *itr, false, VMAP::ModelIgnoreFlags::M2))
2313 continue;
2314
2316 continue;
2317
2318 foundItr = itr;
2319 }
2320 }
2321 // not found any valid target - chain ends
2322 if (foundItr == tempTargets.end())
2323 break;
2324
2326 chainSource = *foundItr;
2327
2328 targets.push_back(*foundItr);
2329 tempTargets.erase(foundItr);
2330 --chainTargets;
2331 }
2332}
2333
2335{
2336 GameObject* focus = nullptr;
2340 return focus;
2341}
2342
2344{
2345 //==========================================================================================
2346 // Now fill data for trigger system, need know:
2347 // Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_hitMask)
2348 //==========================================================================================
2349
2351 // Get data for type of attack and fill base info for trigger
2352 switch (m_spellInfo->DmgClass)
2353 {
2356 if (m_attackType == OFF_ATTACK)
2358 else
2361 break;
2363 // Auto attack
2365 {
2368 }
2369 else // Ranged spell attack
2370 {
2373 }
2374 break;
2375 default:
2378 && m_spellInfo->HasAttribute(SPELL_ATTR2_AUTO_REPEAT)) // Wands auto attack
2379 {
2382 }
2383 // For other spells trigger procflags are set in Spell::TargetInfo::DoDamageAndTriggers
2384 // Because spell positivity is dependant on target
2385 }
2386}
2387
2389{
2390 m_UniqueTargetInfo.clear();
2391 m_UniqueGOTargetInfo.clear();
2392 m_UniqueItemInfo.clear();
2393 m_delayMoment = 0;
2394}
2395
2397{
2398 public:
2399 ProcReflectDelayed(Unit* owner, ObjectGuid casterGuid) : _victim(owner), _casterGuid(casterGuid) { }
2400
2401 bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override
2402 {
2404 if (!caster)
2405 return true;
2406
2407 ProcFlags const typeMaskActor = PROC_FLAG_NONE;
2410 ProcFlagsSpellPhase const spellPhaseMask = PROC_SPELL_PHASE_NONE;
2411 ProcFlagsHit const hitMask = PROC_HIT_REFLECT;
2412
2413 Unit::ProcSkillsAndAuras(caster, _victim, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, nullptr, nullptr, nullptr);
2414 return true;
2415 }
2416
2417 private:
2420};
2421
2422void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= true*/, bool implicit /*= true*/, Position const* losPosition /*= nullptr*/)
2423{
2424 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
2425 if (!spellEffectInfo.IsEffect() || !CheckEffectTarget(target, spellEffectInfo, losPosition))
2426 effectMask &= ~(1 << spellEffectInfo.EffectIndex);
2427
2428 // no effects left
2429 if (!effectMask)
2430 return;
2431
2432 if (checkIfValid)
2433 if (m_spellInfo->CheckTarget(m_caster, target, implicit) != SPELL_CAST_OK) // skip stealth checks for AOE
2434 return;
2435
2436 // Check for effect immune skip if immuned
2437 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
2438 if (target->IsImmunedToSpellEffect(m_spellInfo, spellEffectInfo, m_caster))
2439 effectMask &= ~(1 << spellEffectInfo.EffectIndex);
2440
2441 ObjectGuid targetGUID = target->GetGUID();
2442
2443 // Lookup target in already in list
2444 auto ihit = std::find_if(std::begin(m_UniqueTargetInfo), std::end(m_UniqueTargetInfo), [targetGUID](TargetInfo const& target) { return target.TargetGUID == targetGUID; });
2445 if (ihit != std::end(m_UniqueTargetInfo)) // Found in list
2446 {
2447 // Immune effects removed from mask
2448 ihit->EffectMask |= effectMask;
2449 return;
2450 }
2451
2452 // This is new target calculate data for him
2453
2454 // Get spell hit result on target
2455 TargetInfo targetInfo;
2456 targetInfo.TargetGUID = targetGUID; // Store target GUID
2457 targetInfo.EffectMask = effectMask; // Store all effects not immune
2458 targetInfo.IsAlive = target->IsAlive();
2459 targetInfo.Damage = 0;
2460 targetInfo.Healing = 0;
2461 targetInfo.IsCrit = false;
2462
2463 // Calculate hit result
2465 targetInfo.MissCondition = caster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target)));
2466
2467 // Spell have speed - need calculate incoming time
2468 // Incoming time is zero for self casts. At least I think so.
2469 if (m_caster != target)
2470 {
2471 float hitDelay = m_spellInfo->LaunchDelay;
2472 WorldObject const* missileSource = m_caster;
2474 {
2475 auto previousTargetItr = std::find_if(m_UniqueTargetInfo.rbegin(), m_UniqueTargetInfo.rend(), [effectMask](TargetInfo const& target)
2476 {
2477 return (target.EffectMask & effectMask) != 0;
2478 });
2479 if (previousTargetItr != std::rend(m_UniqueTargetInfo))
2480 {
2481 hitDelay = 0.0f; // this is not the first target in chain, LaunchDelay was already included
2482
2483 if (WorldObject* previousTarget = ObjectAccessor::GetWorldObject(*m_caster, previousTargetItr->TargetGUID))
2484 missileSource = previousTarget;
2485
2486 targetInfo.TimeDelay += previousTargetItr->TimeDelay;
2487 }
2488 }
2489
2491 hitDelay += m_spellInfo->Speed;
2492 else if (m_spellInfo->Speed > 0.0f)
2493 {
2494 // calculate spell incoming interval
2496 float dist = std::max(missileSource->GetDistance(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), 5.0f);
2497 hitDelay += dist / m_spellInfo->Speed;
2498 }
2499
2500 targetInfo.TimeDelay += uint64(std::floor(hitDelay * 1000.0f));
2501 }
2502 else
2503 targetInfo.TimeDelay = 0ULL;
2504
2505 // If target reflect spell back to caster
2506 if (targetInfo.MissCondition == SPELL_MISS_REFLECT)
2507 {
2508 // Calculate reflected spell result on caster (shouldn't be able to reflect gameobject spells)
2509 Unit* unitCaster = ASSERT_NOTNULL(m_caster->ToUnit());
2510 targetInfo.ReflectResult = unitCaster->SpellHitResult(unitCaster, m_spellInfo, false); // can't reflect twice
2511
2512 // Proc spell reflect aura when missile hits the original target
2514
2515 // Increase time interval for reflected spells by 1.5
2516 targetInfo.TimeDelay += targetInfo.TimeDelay >> 1;
2517 }
2518 else
2519 targetInfo.ReflectResult = SPELL_MISS_NONE;
2520
2521 // Calculate minimum incoming time
2522 if (targetInfo.TimeDelay && (!m_delayMoment || m_delayMoment > targetInfo.TimeDelay))
2523 m_delayMoment = targetInfo.TimeDelay;
2524
2525 // Add target to list
2526 m_UniqueTargetInfo.emplace_back(std::move(targetInfo));
2527}
2528
2530{
2531 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
2532 if (!spellEffectInfo.IsEffect() || !CheckEffectTarget(go, spellEffectInfo))
2533 effectMask &= ~(1 << spellEffectInfo.EffectIndex);
2534
2535 // no effects left
2536 if (!effectMask)
2537 return;
2538
2539 ObjectGuid targetGUID = go->GetGUID();
2540
2541 // Lookup target in already in list
2542 auto ihit = std::find_if(std::begin(m_UniqueGOTargetInfo), std::end(m_UniqueGOTargetInfo), [targetGUID](GOTargetInfo const& target) { return target.TargetGUID == targetGUID; });
2543 if (ihit != std::end(m_UniqueGOTargetInfo)) // Found in list
2544 {
2545 // Add only effect mask
2546 ihit->EffectMask |= effectMask;
2547 return;
2548 }
2549
2550 // This is new target calculate data for him
2551
2552 GOTargetInfo target;
2553 target.TargetGUID = targetGUID;
2554 target.EffectMask = effectMask;
2555
2556 // Spell have speed - need calculate incoming time
2557 if (static_cast<WorldObject*>(m_caster) != go)
2558 {
2559 float hitDelay = m_spellInfo->LaunchDelay;
2561 hitDelay += m_spellInfo->Speed;
2562 else if (m_spellInfo->Speed > 0.0f)
2563 {
2564 // calculate spell incoming interval
2565 float dist = std::max(m_caster->GetDistance(go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()), 5.0f);
2566 hitDelay += dist / m_spellInfo->Speed;
2567 }
2568
2569 target.TimeDelay = uint64(std::floor(hitDelay * 1000.0f));
2570 }
2571 else
2572 target.TimeDelay = 0ULL;
2573
2574 // Calculate minimum incoming time
2575 if (target.TimeDelay && (!m_delayMoment || m_delayMoment > target.TimeDelay))
2576 m_delayMoment = target.TimeDelay;
2577
2578 // Add target to list
2579 m_UniqueGOTargetInfo.emplace_back(std::move(target));
2580}
2581
2582void Spell::AddItemTarget(Item* item, uint32 effectMask)
2583{
2584 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
2585 if (!spellEffectInfo.IsEffect() || !CheckEffectTarget(item, spellEffectInfo))
2586 effectMask &= ~(1 << spellEffectInfo.EffectIndex);
2587
2588 // no effects left
2589 if (!effectMask)
2590 return;
2591
2592 // Lookup target in already in list
2593 auto ihit = std::find_if(std::begin(m_UniqueItemInfo), std::end(m_UniqueItemInfo), [item](ItemTargetInfo const& target) { return target.TargetItem == item; });
2594 if (ihit != std::end(m_UniqueItemInfo)) // Found in list
2595 {
2596 // Add only effect mask
2597 ihit->EffectMask |= effectMask;
2598 return;
2599 }
2600
2601 // This is new target add data
2602
2603 ItemTargetInfo target;
2604 target.TargetItem = item;
2605 target.EffectMask = effectMask;
2606
2607 m_UniqueItemInfo.emplace_back(std::move(target));
2608}
2609
2610void Spell::AddCorpseTarget(Corpse* corpse, uint32 effectMask)
2611{
2612 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
2613 if (!spellEffectInfo.IsEffect())
2614 effectMask &= ~(1 << spellEffectInfo.EffectIndex);
2615
2616 // no effects left
2617 if (!effectMask)
2618 return;
2619
2620 ObjectGuid targetGUID = corpse->GetGUID();
2621
2622 // Lookup target in already in list
2623 auto ihit = std::find_if(std::begin(m_UniqueCorpseTargetInfo), std::end(m_UniqueCorpseTargetInfo), [targetGUID](CorpseTargetInfo const& target) { return target.TargetGUID == targetGUID; });
2624 if (ihit != std::end(m_UniqueCorpseTargetInfo)) // Found in list
2625 {
2626 // Add only effect mask
2627 ihit->EffectMask |= effectMask;
2628 return;
2629 }
2630
2631 // This is new target calculate data for him
2632 CorpseTargetInfo target;
2633 target.TargetGUID = targetGUID;
2634 target.EffectMask = effectMask;
2635
2636 // Spell have speed - need calculate incoming time
2637 if (m_caster != corpse)
2638 {
2639 float hitDelay = m_spellInfo->LaunchDelay;
2641 hitDelay += m_spellInfo->Speed;
2642 else if (m_spellInfo->Speed > 0.0f)
2643 {
2644 // calculate spell incoming interval
2645 float dist = std::max(m_caster->GetDistance(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ()), 5.0f);
2646 hitDelay += dist / m_spellInfo->Speed;
2647 }
2648
2649 target.TimeDelay = uint64(std::floor(hitDelay * 1000.0f));
2650 }
2651 else
2652 target.TimeDelay = 0LL;
2653
2654 // Calculate minimum incoming time
2655 if (target.TimeDelay && (!m_delayMoment || m_delayMoment > target.TimeDelay))
2656 m_delayMoment = target.TimeDelay;
2657
2658 // Add target to list
2659 m_UniqueCorpseTargetInfo.emplace_back(std::move(target));
2660}
2661
2663{
2664 m_destTargets[effIndex] = dest;
2665}
2666
2668{
2669 int32 index = 0;
2670 for (TargetInfo const& uniqueTargetInfo : m_UniqueTargetInfo)
2671 {
2672 if (uniqueTargetInfo.MissCondition == SPELL_MISS_NONE && uniqueTargetInfo.EffectMask & (1 << effect))
2673 {
2674 if (uniqueTargetInfo.TargetGUID == target)
2675 break;
2676
2677 ++index;
2678 }
2679 }
2680
2681 return index;
2682}
2683
2685{
2686 return std::count_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [effect](TargetInfo const& targetInfo)
2687 {
2688 return targetInfo.MissCondition == SPELL_MISS_NONE && targetInfo.EffectMask & (1 << effect);
2689 });
2690}
2691
2693{
2694 return std::count_if(m_UniqueGOTargetInfo.begin(), m_UniqueGOTargetInfo.end(), [effect](GOTargetInfo const& targetInfo)
2695 {
2696 return targetInfo.EffectMask & (1 << effect);
2697 });
2698}
2699
2701{
2702 return std::count_if(m_UniqueItemInfo.begin(), m_UniqueItemInfo.end(), [effect](ItemTargetInfo const& targetInfo)
2703 {
2704 return targetInfo.EffectMask & (1 << effect);
2705 });
2706}
2707
2709{
2710 return std::count_if(m_UniqueCorpseTargetInfo.begin(), m_UniqueCorpseTargetInfo.end(), [effect](CorpseTargetInfo const& targetInfo)
2711 {
2712 return targetInfo.EffectMask & (1 << effect);
2713 });
2714}
2715
2717{
2718 Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID);
2719 if (!unit)
2720 return;
2721
2722 // Need init unitTarget by default unit (can changed in code on reflect)
2723 spell->unitTarget = unit;
2724
2725 // Reset damage/healing counter
2726 spell->m_damage = Damage;
2727 spell->m_healing = Healing;
2728
2729 _spellHitTarget = nullptr;
2731 _spellHitTarget = unit;
2733 _spellHitTarget = spell->m_caster->ToUnit();
2734
2736 unit->SetInCombatWith(spell->m_originalCaster);
2737
2738 // if target is flagged for pvp also flag caster if a player
2739 // but respect current pvp rules (buffing/healing npcs flagged for pvp only flags you if they are in combat)
2741 && unit->IsPvP() && (unit->IsInCombat() || unit->IsCharmedOwnedByPlayerOrPlayer()) && spell->m_caster->GetTypeId() == TYPEID_PLAYER; // need to check PvP state before spell effects, but act on it afterwards
2742
2743 if (_spellHitTarget)
2744 {
2745 SpellMissInfo missInfo = spell->PreprocessSpellHit(_spellHitTarget, *this);
2746 if (missInfo != SPELL_MISS_NONE)
2747 {
2748 if (missInfo != SPELL_MISS_MISS)
2749 spell->m_caster->SendSpellMiss(unit, spell->m_spellInfo->Id, missInfo);
2750 spell->m_damage = 0;
2751 spell->m_healing = 0;
2752 _spellHitTarget = nullptr;
2753 }
2754 }
2755
2756 spell->CallScriptOnHitHandlers();
2757
2758 // scripts can modify damage/healing for current target, save them
2759 Damage = spell->m_damage;
2760 Healing = spell->m_healing;
2761}
2762
2764{
2765 Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID);
2766 if (!unit)
2767 return;
2768
2769 // Need init unitTarget by default unit (can changed in code on reflect)
2770 // Or on missInfo != SPELL_MISS_NONE unitTarget undefined (but need in trigger subsystem)
2771 spell->unitTarget = unit;
2772 spell->targetMissInfo = MissCondition;
2773
2774 // Reset damage/healing counter
2775 spell->m_damage = Damage;
2776 spell->m_healing = Healing;
2777
2778 if (unit->IsAlive() != IsAlive && !spell->m_spellInfo->HasAttribute(SPELL_ATTR9_FORCE_CORPSE_TARGET))
2779 return;
2780
2782 return; // No missinfo in that case
2783
2784 if (_spellHitTarget)
2785 spell->DoSpellEffectHit(_spellHitTarget, spellEffectInfo, *this);
2786
2787 // scripts can modify damage/healing for current target, save them
2788 Damage = spell->m_damage;
2789 Healing = spell->m_healing;
2790}
2791
2793{
2794 Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID);
2795 if (!unit)
2796 return;
2797
2798 // other targets executed before this one changed pointer
2799 spell->unitTarget = unit;
2800 if (_spellHitTarget)
2801 spell->unitTarget = _spellHitTarget;
2802
2803 // Reset damage/healing counter
2804 spell->m_damage = Damage;
2805 spell->m_healing = Healing;
2806
2807 // Get original caster (if exist) and calculate damage/healing from him data
2808 // Skip if m_originalCaster not available
2809 Unit* caster = spell->m_originalCaster ? spell->m_originalCaster : spell->m_caster->ToUnit();
2810 if (caster)
2811 {
2812 // Fill base trigger info
2813 ProcFlagsInit procAttacker = spell->m_procAttacker;
2814 ProcFlagsInit procVictim = spell->m_procVictim;
2816 ProcFlagsHit hitMask = PROC_HIT_NONE;
2817
2818 // Spells with this flag cannot trigger if effect is cast on self
2819 bool const canEffectTrigger = spell->unitTarget->CanProc();
2820
2821 // Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now
2822 if (canEffectTrigger && !procAttacker && !procVictim)
2823 {
2824 bool positive = true;
2825 if (spell->m_damage > 0)
2826 positive = false;
2827 else if (!spell->m_healing)
2828 {
2829 for (uint8 i = 0; i < spell->m_spellInfo->GetEffects().size(); ++i)
2830 {
2831 // in case of immunity, check all effects to choose correct procFlags, as none has technically hit
2832 if (EffectMask && !(EffectMask & (1 << i)))
2833 continue;
2834
2835 if (!spell->m_spellInfo->IsPositiveEffect(i))
2836 {
2837 positive = false;
2838 break;
2839 }
2840 }
2841 }
2842
2843 switch (spell->m_spellInfo->DmgClass)
2844 {
2848 {
2849 if (positive)
2850 {
2851 procAttacker |= PROC_FLAG_DEAL_HELPFUL_PERIODIC;
2852 procVictim |= PROC_FLAG_TAKE_HELPFUL_PERIODIC;
2853 }
2854 else
2855 {
2856 procAttacker |= PROC_FLAG_DEAL_HARMFUL_PERIODIC;
2857 procVictim |= PROC_FLAG_TAKE_HARMFUL_PERIODIC;
2858 }
2859 }
2861 {
2862 if (positive)
2863 {
2864 procAttacker |= PROC_FLAG_DEAL_HELPFUL_ABILITY;
2865 procVictim |= PROC_FLAG_TAKE_HELPFUL_ABILITY;
2866 }
2867 else
2868 {
2869 procAttacker |= PROC_FLAG_DEAL_HARMFUL_ABILITY;
2870 procVictim |= PROC_FLAG_TAKE_HARMFUL_ABILITY;
2871 }
2872 }
2873 else
2874 {
2875 if (positive)
2876 {
2877 procAttacker |= PROC_FLAG_DEAL_HELPFUL_SPELL;
2878 procVictim |= PROC_FLAG_TAKE_HELPFUL_SPELL;
2879 }
2880 else
2881 {
2882 procAttacker |= PROC_FLAG_DEAL_HARMFUL_SPELL;
2883 procVictim |= PROC_FLAG_TAKE_HARMFUL_SPELL;
2884 }
2885 }
2886 break;
2887 }
2888 }
2889
2890 // All calculated do it!
2891 // Do healing
2892 bool hasHealing = false;
2893 std::unique_ptr<DamageInfo> spellDamageInfo;
2894 std::unique_ptr<HealInfo> healInfo;
2895 if (spell->m_healing > 0)
2896 {
2897 hasHealing = true;
2898 uint32 addhealth = spell->m_healing;
2899 if (IsCrit)
2900 {
2901 hitMask |= PROC_HIT_CRITICAL;
2902 addhealth = Unit::SpellCriticalHealingBonus(caster, spell->m_spellInfo, addhealth, nullptr);
2903 }
2904 else
2905 hitMask |= PROC_HIT_NORMAL;
2906
2907 healInfo = std::make_unique<HealInfo>(caster, spell->unitTarget, addhealth, spell->m_spellInfo, spell->m_spellInfo->GetSchoolMask());
2908 caster->HealBySpell(*healInfo, IsCrit);
2909 spell->unitTarget->GetThreatManager().ForwardThreatForAssistingMe(caster, float(healInfo->GetEffectiveHeal()) * 0.5f, spell->m_spellInfo);
2910 spell->m_healing = healInfo->GetEffectiveHeal();
2911
2912 procSpellType |= PROC_SPELL_TYPE_HEAL;
2913 }
2914
2915 // Do damage
2916 bool hasDamage = false;
2917 if (spell->m_damage > 0)
2918 {
2919 hasDamage = true;
2920 // Fill base damage struct (unitTarget - is real spell target)
2921 SpellNonMeleeDamage damageInfo(caster, spell->unitTarget, spell->m_spellInfo, spell->m_SpellVisual, spell->m_spellSchoolMask, spell->m_castId);
2922 // Check damage immunity
2923 if (spell->unitTarget->IsImmunedToDamage(caster, spell->m_spellInfo))
2924 {
2925 hitMask = PROC_HIT_IMMUNE;
2926 spell->m_damage = 0;
2927
2928 // no packet found in sniffs
2929 }
2930 else
2931 {
2932 caster->SetLastDamagedTargetGuid(spell->unitTarget->GetGUID());
2933
2934 // Add bonuses and fill damageInfo struct
2935 caster->CalculateSpellDamageTaken(&damageInfo, spell->m_damage, spell->m_spellInfo, spell->m_attackType, IsCrit, MissCondition == SPELL_MISS_BLOCK, spell);
2936 Unit::DealDamageMods(damageInfo.attacker, damageInfo.target, damageInfo.damage, &damageInfo.absorb);
2937
2938 hitMask |= createProcHitMask(&damageInfo, MissCondition);
2939 procVictim |= PROC_FLAG_TAKE_ANY_DAMAGE;
2940
2941 // sparring
2942 if (damageInfo.target != damageInfo.attacker)
2943 {
2944 if (Creature* victimCreature = damageInfo.target->ToCreature())
2945 damageInfo.damage = victimCreature->CalculateDamageForSparring(damageInfo.attacker, damageInfo.damage);
2946 }
2947 spell->m_damage = damageInfo.damage;
2948
2949 caster->DealSpellDamage(&damageInfo, true);
2950
2951 // Send log damage message to client
2952 caster->SendSpellNonMeleeDamageLog(&damageInfo);
2953 }
2954
2955 // Do triggers for unit
2956 if (canEffectTrigger)
2957 {
2958 spellDamageInfo = std::make_unique<DamageInfo>(damageInfo, SPELL_DIRECT_DAMAGE, spell->m_attackType, hitMask);
2959 procSpellType |= PROC_SPELL_TYPE_DAMAGE;
2960 }
2961 }
2962
2963 // Passive spell hits/misses or active spells only misses (only triggers)
2964 if (!hasHealing && !hasDamage)
2965 {
2966 // Fill base damage struct (unitTarget - is real spell target)
2967 SpellNonMeleeDamage damageInfo(caster, spell->unitTarget, spell->m_spellInfo, spell->m_SpellVisual, spell->m_spellSchoolMask);
2968 hitMask |= createProcHitMask(&damageInfo, MissCondition);
2969 // Do triggers for unit
2970 if (canEffectTrigger)
2971 {
2972 spellDamageInfo = std::make_unique<DamageInfo>(damageInfo, NODAMAGE, spell->m_attackType, hitMask);
2973 procSpellType |= PROC_SPELL_TYPE_NO_DMG_HEAL;
2974 }
2975
2976 // Failed Pickpocket, reveal rogue
2978 {
2979 Unit* unitCaster = ASSERT_NOTNULL(spell->m_caster->ToUnit());
2981 spell->unitTarget->ToCreature()->EngageWithTarget(unitCaster);
2982 }
2983 }
2984
2985 // Do triggers for unit
2986 if (canEffectTrigger)
2987 {
2988 Unit::ProcSkillsAndAuras(caster, spell->unitTarget, procAttacker, procVictim, procSpellType, PROC_SPELL_PHASE_HIT, hitMask, spell, spellDamageInfo.get(), healInfo.get());
2989
2990 // item spells (spell hit of non-damage spell may also activate items, for example seal of corruption hidden hit)
2991 if (caster->GetTypeId() == TYPEID_PLAYER && (procSpellType & (PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL)))
2992 {
2995 caster->ToPlayer()->CastItemCombatSpell(*spellDamageInfo);
2996 }
2997 }
2998
2999 // set hitmask for finish procs
3000 spell->m_hitMask |= hitMask;
3001 spell->m_procSpellType |= procSpellType;
3002
3003 // _spellHitTarget can be null if spell is missed in DoSpellHitOnUnit
3004 if (MissCondition != SPELL_MISS_EVADE && _spellHitTarget && !spell->m_caster->IsFriendlyTo(unit) && (!spell->IsPositive() || spell->m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)))
3005 {
3006 if (Unit* unitCaster = spell->m_caster->ToUnit())
3007 {
3008 unitCaster->AtTargetAttacked(unit, spell->m_spellInfo->HasInitialAggro());
3009
3011 if (Creature* targetCreature = unit->ToCreature())
3012 if (unitCaster->IsPlayer())
3013 targetCreature->SetTappedBy(unitCaster);
3014 }
3015
3018 }
3019
3020 // Check for SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER
3022 caster->CastSpell(unit, SPELL_INTERRUPT_NONPLAYER, spell);
3023 }
3024
3025 if (_spellHitTarget)
3026 {
3027 //AI functions
3028 if (Creature* cHitTarget = _spellHitTarget->ToCreature())
3029 if (CreatureAI* hitTargetAI = cHitTarget->AI())
3030 hitTargetAI->SpellHit(spell->m_caster, spell->m_spellInfo);
3031
3032 if (spell->m_caster->GetTypeId() == TYPEID_UNIT && spell->m_caster->ToCreature()->IsAIEnabled())
3033 spell->m_caster->ToCreature()->AI()->SpellHitTarget(_spellHitTarget, spell->m_spellInfo);
3034 else if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT && spell->m_caster->ToGameObject()->AI())
3035 spell->m_caster->ToGameObject()->AI()->SpellHitTarget(_spellHitTarget, spell->m_spellInfo);
3036
3037 if (HitAura)
3038 {
3039 if (AuraApplication* aurApp = HitAura->GetApplicationOfTarget(_spellHitTarget->GetGUID()))
3040 {
3041 // only apply unapplied effects (for reapply case)
3042 uint32 effMask = EffectMask & aurApp->GetEffectsToApply();
3043 for (uint8 i = 0; i < spell->m_spellInfo->GetEffects().size(); ++i)
3044 if ((effMask & (1 << i)) && aurApp->HasEffect(i))
3045 effMask &= ~(1 << i);
3046
3047 if (effMask)
3048 _spellHitTarget->_ApplyAura(aurApp, effMask);
3049
3050 if (aurApp->IsNeedClientUpdate() && aurApp->GetRemoveMode() == AURA_REMOVE_NONE)
3051 {
3052 aurApp->ClientUpdate(false);
3053 _spellHitTarget->RemoveVisibleAuraUpdate(aurApp);
3054 }
3055 }
3056 }
3057
3058 // Needs to be called after dealing damage/healing to not remove breaking on damage auras
3059 spell->DoTriggersOnSpellHit(_spellHitTarget);
3060 }
3061
3062 if (_enablePVP)
3063 spell->m_caster->ToPlayer()->UpdatePvP(true);
3064
3065 spell->_spellAura = HitAura;
3067 spell->_spellAura = nullptr;
3068}
3069
3071{
3072 GameObject* go = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToGameObject() : ObjectAccessor::GetGameObject(*spell->m_caster, TargetGUID);
3073 if (!go)
3074 return;
3075
3077
3078 spell->HandleEffects(nullptr, nullptr, go, nullptr, spellEffectInfo, SPELL_EFFECT_HANDLE_HIT_TARGET);
3079
3080 // AI functions
3081 if (go->AI())
3082 go->AI()->SpellHit(spell->m_caster, spell->m_spellInfo);
3083
3084 if (spell->m_caster->GetTypeId() == TYPEID_UNIT && spell->m_caster->ToCreature()->IsAIEnabled())
3085 spell->m_caster->ToCreature()->AI()->SpellHitTarget(go, spell->m_spellInfo);
3086 else if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT && spell->m_caster->ToGameObject()->AI())
3087 spell->m_caster->ToGameObject()->AI()->SpellHitTarget(go, spell->m_spellInfo);
3088
3089 spell->CallScriptOnHitHandlers();
3091}
3092
3094{
3096
3097 spell->HandleEffects(nullptr, TargetItem, nullptr, nullptr, spellEffectInfo, SPELL_EFFECT_HANDLE_HIT_TARGET);
3098
3099 spell->CallScriptOnHitHandlers();
3101}
3102
3104{
3105 Corpse* corpse = ObjectAccessor::GetCorpse(*spell->m_caster, TargetGUID);
3106 if (!corpse)
3107 return;
3108
3110
3111 spell->HandleEffects(nullptr, nullptr, nullptr, corpse, spellEffectInfo, SPELL_EFFECT_HANDLE_HIT_TARGET);
3112
3113 spell->CallScriptOnHitHandlers();
3115}
3116
3118{
3119 if (!unit)
3120 return SPELL_MISS_EVADE;
3121
3122 // Target may have begun evading between launch and hit phases - re-check now
3123 if (Creature* creatureTarget = unit->ToCreature())
3124 if (creatureTarget->IsEvadingAttacks())
3125 return SPELL_MISS_EVADE;
3126
3127 // For delayed spells immunity may be applied between missile launch and hit - check immunity for that case
3129 return SPELL_MISS_IMMUNE;
3130
3132
3133 if (Player* player = unit->ToPlayer())
3134 {
3135 player->FailCriteria(CriteriaFailEvent::BeSpellTarget, m_spellInfo->Id);
3136 player->StartCriteria(CriteriaStartEvent::BeSpellTarget, m_spellInfo->Id);
3137 player->UpdateCriteria(CriteriaType::BeSpellTarget, m_spellInfo->Id, 0, 0, m_caster);
3138 }
3139
3140 if (Player* player = m_caster->ToPlayer())
3141 player->UpdateCriteria(CriteriaType::LandTargetedSpellOnTarget, m_spellInfo->Id, 0, 0, unit);
3142
3143 if (m_caster != unit)
3144 {
3145 // Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells
3147 return SPELL_MISS_EVADE;
3148
3151 else if (m_caster->IsFriendlyTo(unit))
3152 {
3153 // for delayed spells ignore negative spells (after duel end) for friendly targets
3155 return SPELL_MISS_EVADE;
3156
3157 // assisting case, healing and resurrection
3159 {
3161 {
3162 playerOwner->SetContestedPvP();
3163 playerOwner->UpdatePvP(true);
3164 }
3165 }
3166
3168 {
3169 if (m_originalCaster->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) // only do explicit combat forwarding for PvP enabled units
3170 m_originalCaster->GetCombatManager().InheritCombatStatesFrom(unit); // for creature v creature combat, the threat forward does it for us
3172 }
3173 }
3174 }
3175
3176 // original caster for auras
3177 WorldObject* origCaster = m_caster;
3178 if (m_originalCaster)
3179 origCaster = m_originalCaster;
3180
3181 // check immunity due to diminishing returns
3183 {
3184 for (SpellEffectInfo const& auraSpellEffect : m_spellInfo->GetEffects())
3185 hitInfo.AuraBasePoints[auraSpellEffect.EffectIndex] = (m_spellValue->CustomBasePointsMask & (1 << auraSpellEffect.EffectIndex))
3186 ? m_spellValue->EffectBasePoints[auraSpellEffect.EffectIndex]
3187 : auraSpellEffect.CalcBaseValue(m_originalCaster, unit, m_castItemEntry, m_castItemLevel);
3188
3189 // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add
3191
3192 DiminishingLevels diminishLevel = DIMINISHING_LEVEL_1;
3193 if (hitInfo.DRGroup)
3194 {
3195 diminishLevel = unit->GetDiminishing(hitInfo.DRGroup);
3197 // Increase Diminishing on unit, current informations for actually casts will use values above
3198 if (type == DRTYPE_ALL || (type == DRTYPE_PLAYER && unit->IsAffectedByDiminishingReturns()))
3200 }
3201
3202 // Now Reduce spell duration using data received at spell hit
3203 // check whatever effects we're going to apply, diminishing returns only apply to negative aura effects
3204 hitInfo.Positive = true;
3205 if (origCaster == unit || !origCaster->IsFriendlyTo(unit))
3206 {
3207 for (SpellEffectInfo const& auraSpellEffect : m_spellInfo->GetEffects())
3208 {
3209 // mod duration only for effects applying aura!
3210 if (hitInfo.EffectMask & (1 << auraSpellEffect.EffectIndex) &&
3211 auraSpellEffect.IsUnitOwnedAuraEffect() &&
3212 !m_spellInfo->IsPositiveEffect(auraSpellEffect.EffectIndex))
3213 {
3214 hitInfo.Positive = false;
3215 break;
3216 }
3217 }
3218 }
3219
3221
3222 // unit is immune to aura if it was diminished to 0 duration
3223 if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(m_spellInfo, hitInfo.AuraDuration, origCaster, diminishLevel))
3224 if (std::all_of(std::begin(m_spellInfo->GetEffects()), std::end(m_spellInfo->GetEffects()), [](SpellEffectInfo const& effInfo) { return !effInfo.IsEffect() || effInfo.Effect == SPELL_EFFECT_APPLY_AURA; }))
3225 return SPELL_MISS_IMMUNE;
3226 }
3227
3228 return SPELL_MISS_NONE;
3229}
3230
3231void Spell::DoSpellEffectHit(Unit* unit, SpellEffectInfo const& spellEffectInfo, TargetInfo& hitInfo)
3232{
3233 if (uint32 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << spellEffectInfo.EffectIndex, unit))
3234 {
3235 WorldObject* caster = m_caster;
3236 if (m_originalCaster)
3237 caster = m_originalCaster;
3238
3239 if (caster)
3240 {
3241 bool refresh = false;
3242
3243 if (!hitInfo.HitAura)
3244 {
3245 bool const resetPeriodicTimer = (m_spellInfo->StackAmount < 2) && !(_triggeredCastFlags & TRIGGERED_DONT_RESET_PERIODIC_TIMER);
3246 uint32 const allAuraEffectMask = Aura::BuildEffectMaskForOwner(m_spellInfo, MAX_EFFECT_MASK, unit);
3247
3248 AuraCreateInfo createInfo(m_castId, m_spellInfo, GetCastDifficulty(), allAuraEffectMask, unit);
3249 createInfo
3250 .SetCasterGUID(caster->GetGUID())
3251 .SetBaseAmount(&hitInfo.AuraBasePoints[0])
3253 .SetPeriodicReset(resetPeriodicTimer)
3254 .SetOwnerEffectMask(aura_effmask)
3255 .IsRefresh = &refresh;
3256
3257 if (Aura* aura = Aura::TryRefreshStackOrCreate(createInfo, false))
3258 {
3259 hitInfo.HitAura = aura->ToUnitAura();
3260
3261 // Set aura stack amount to desired value
3263 {
3264 if (!refresh)
3266 else
3268 }
3269
3270 hitInfo.HitAura->SetDiminishGroup(hitInfo.DRGroup);
3271
3272 if (!m_spellValue->Duration)
3273 {
3274 hitInfo.AuraDuration = caster->ModSpellDuration(m_spellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, hitInfo.HitAura->GetEffectMask());
3275
3276 if (hitInfo.AuraDuration > 0)
3277 {
3279
3280 // Haste modifies duration of channeled spells
3281 if (m_spellInfo->IsChanneled())
3282 caster->ModSpellDurationTime(m_spellInfo, hitInfo.AuraDuration, this);
3284 {
3285 int32 origDuration = hitInfo.AuraDuration;
3286 hitInfo.AuraDuration = 0;
3287 for (AuraEffect const* auraEff : hitInfo.HitAura->GetAuraEffects())
3288 if (auraEff)
3289 if (int32 period = auraEff->GetPeriod()) // period is hastened by UNIT_MOD_CAST_SPEED
3290 hitInfo.AuraDuration = std::max(std::max(origDuration / period, 1) * period, hitInfo.AuraDuration);
3291
3292 // if there is no periodic effect
3293 if (!hitInfo.AuraDuration)
3294 hitInfo.AuraDuration = int32(origDuration * m_originalCaster->m_unitData->ModCastingSpeed);
3295 }
3296 }
3297 }
3298 else
3300
3301 if (hitInfo.AuraDuration != hitInfo.HitAura->GetMaxDuration())
3302 {
3303 hitInfo.HitAura->SetMaxDuration(hitInfo.AuraDuration);
3304 hitInfo.HitAura->SetDuration(hitInfo.AuraDuration);
3305 }
3306
3307 if (refresh)
3308 hitInfo.HitAura->AddStaticApplication(unit, aura_effmask);
3309 }
3310 }
3311 else
3312 hitInfo.HitAura->AddStaticApplication(unit, aura_effmask);
3313 }
3314 }
3315
3316 _spellAura = hitInfo.HitAura;
3317 HandleEffects(unit, nullptr, nullptr, nullptr, spellEffectInfo, SPELL_EFFECT_HANDLE_HIT_TARGET);
3318 _spellAura = nullptr;
3319}
3320
3322{
3323 // handle SPELL_AURA_ADD_TARGET_TRIGGER auras
3324 // this is executed after spell proc spells on target hit
3325 // spells are triggered for each hit spell target
3326 // info confirmed with retail sniffs of permafrost and shadow weaving
3327 if (!m_hitTriggerSpells.empty())
3328 {
3329 int32 _duration = 0;
3330 for (auto i = m_hitTriggerSpells.begin(); i != m_hitTriggerSpells.end(); ++i)
3331 {
3332 if (CanExecuteTriggersOnHit(unit, i->triggeredByAura) && roll_chance_i(i->chance))
3333 {
3334 m_caster->CastSpell(unit, i->triggeredSpell->Id, CastSpellExtraArgs(TRIGGERED_FULL_MASK)
3335 .SetTriggeringSpell(this)
3336 .SetCastDifficulty(i->triggeredSpell->Difficulty));
3337 TC_LOG_DEBUG("spells", "Spell {} triggered spell {} by SPELL_AURA_ADD_TARGET_TRIGGER aura", m_spellInfo->Id, i->triggeredSpell->Id);
3338
3339 // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration
3340 // set duration of current aura to the triggered spell
3341 if (i->triggeredSpell->GetDuration() == -1)
3342 {
3343 if (Aura* triggeredAur = unit->GetAura(i->triggeredSpell->Id, m_caster->GetGUID()))
3344 {
3345 // get duration from aura-only once
3346 if (!_duration)
3347 {
3348 Aura* aur = unit->GetAura(m_spellInfo->Id, m_caster->GetGUID());
3349 _duration = aur ? aur->GetDuration() : -1;
3350 }
3351 triggeredAur->SetDuration(_duration);
3352 }
3353 }
3354 }
3355 }
3356 }
3357
3358 // trigger linked auras remove/apply
3360 if (std::vector<int32> const* spellTriggered = sSpellMgr->GetSpellLinked(SPELL_LINK_HIT, m_spellInfo->Id))
3361 {
3362 for (std::vector<int32>::const_iterator i = spellTriggered->begin(); i != spellTriggered->end(); ++i)
3363 {
3364 if (*i < 0)
3365 unit->RemoveAurasDueToSpell(-(*i));
3366 else
3368 .SetOriginalCaster(m_caster->GetGUID())
3369 .SetTriggeringSpell(this));
3370 }
3371 }
3372}
3373
3375{
3376 // Not need check return true
3378 return true;
3379
3380 uint32 channelTargetEffectMask = m_channelTargetEffectMask;
3381 uint32 channelAuraMask = 0;
3382 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
3383 if (spellEffectInfo.IsEffect(SPELL_EFFECT_APPLY_AURA))
3384 channelAuraMask |= 1 << spellEffectInfo.EffectIndex;
3385
3386 channelAuraMask &= channelTargetEffectMask;
3387
3388 float range = 0;
3389 if (channelAuraMask)
3390 {
3392 if (Player* modOwner = m_caster->GetSpellModOwner())
3393 modOwner->ApplySpellMod(m_spellInfo, SpellModOp::Range, range, this);
3394
3395 // add little tolerance level
3396 range += std::min(MAX_SPELL_RANGE_TOLERANCE, range*0.1f); // 10% but no more than MAX_SPELL_RANGE_TOLERANCE
3397 }
3398
3399 for (TargetInfo& targetInfo : m_UniqueTargetInfo)
3400 {
3401 if (targetInfo.MissCondition == SPELL_MISS_NONE && (channelTargetEffectMask & targetInfo.EffectMask))
3402 {
3403 Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID);
3404 if (!unit)
3405 {
3406 if (Unit* unitCaster =m_caster->ToUnit())
3407 unitCaster->RemoveChannelObject(targetInfo.TargetGUID);
3408 continue;
3409 }
3410
3411 if (IsValidDeadOrAliveTarget(unit))
3412 {
3413 if (channelAuraMask & targetInfo.EffectMask)
3414 {
3416 {
3417 if (m_caster != unit && !m_caster->IsWithinDistInMap(unit, range))
3418 {
3419 targetInfo.EffectMask &= ~aurApp->GetEffectMask();
3420 unit->RemoveAura(aurApp);
3421 if (Unit* unitCaster = m_caster->ToUnit())
3422 unitCaster->RemoveChannelObject(targetInfo.TargetGUID);
3423 continue;
3424 }
3425 }
3426 else // aura is dispelled
3427 {
3428 if (Unit* unitCaster = m_caster->ToUnit())
3429 unitCaster->RemoveChannelObject(targetInfo.TargetGUID);
3430 continue;
3431 }
3432 }
3433
3434 channelTargetEffectMask &= ~targetInfo.EffectMask; // remove from need alive mask effect that have alive target
3435 }
3436 }
3437 }
3438
3439 // is all effects from m_needAliveTargetMask have alive targets
3440 return channelTargetEffectMask == 0;
3441}
3442
3443SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggeredByAura)
3444{
3445 if (m_CastItem)
3446 {
3449
3450 if (Player* owner = m_CastItem->GetOwner())
3452 else if (m_CastItem->GetOwnerGUID() == m_caster->GetGUID())
3454 else
3455 {
3459 }
3460 }
3461
3462 InitExplicitTargets(targets);
3463
3465
3466 if (triggeredByAura)
3467 {
3468 m_triggeredByAuraSpell = triggeredByAura->GetSpellInfo();
3469 m_castItemLevel = triggeredByAura->GetBase()->GetCastItemLevel();
3470 }
3471
3472 // create and add update event for this spell
3473 _spellEvent = new SpellEvent(this);
3475
3476 // check disables
3478 {
3482 }
3483
3484 // Prevent casting at cast another spell (ServerSide check)
3486 {
3490 }
3491
3492 LoadScripts();
3493
3494 // Fill cost data (do not use power for item casts)
3495 if (!m_CastItem)
3497
3498 int32 param1 = 0, param2 = 0;
3499 SpellCastResult result = CheckCast(true, &param1, &param2);
3500 // target is checked in too many locations and with different results to handle each of them
3501 // handle just the general SPELL_FAILED_BAD_TARGETS result which is the default result for most DBC target checks
3503 result = SPELL_CAST_OK;
3504 if (result != SPELL_CAST_OK)
3505 {
3506 // Periodic auras should be interrupted when aura triggers a spell which can't be cast
3507 // for example bladestorm aura should be removed on disarm as of patch 3.3.5
3508 // channeled periodic spells should be affected by this (arcane missiles, penance, etc)
3509 // a possible alternative sollution for those would be validating aura target on unit state change
3510 if (triggeredByAura && triggeredByAura->IsPeriodic() && !triggeredByAura->GetBase()->IsPassive())
3511 {
3512 SendChannelUpdate(0, result);
3513 triggeredByAura->GetBase()->SetDuration(0);
3514 }
3515
3516 if (param1 || param2)
3517 SendCastResult(result, &param1, &param2);
3518 else
3519 SendCastResult(result);
3520
3521 // queue autorepeat spells for future repeating
3524
3525 finish(result);
3526 return result;
3527 }
3528
3529 // Prepare data for triggers
3531
3535
3536 SpellCastResult movementResult = SPELL_CAST_OK;
3537 if (m_caster->IsUnit() && m_caster->ToUnit()->isMoving())
3538 movementResult = CheckMovement();
3539
3540 // Creatures focus their target when possible
3542 {
3543 // Channeled spells and some triggered spells do not focus a cast target. They face their target later on via channel object guid and via spell attribute or not at all
3544 bool const focusTarget = !m_spellInfo->IsChanneled() && !(_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING);
3545 if (focusTarget && m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget())
3547 else
3548 m_caster->ToCreature()->SetSpellFocus(this, nullptr);
3549 }
3550
3551 if (movementResult != SPELL_CAST_OK)
3552 {
3554 {
3555 SendCastResult(movementResult);
3556 finish(movementResult);
3557 return movementResult;
3558 }
3559 else
3560 {
3561 // Creatures (not controlled) give priority to spell casting over movement.
3562 // We assume that the casting is always valid and the current movement
3563 // is stopped immediately (because spells are updated before movement, so next Unit::Update would cancel the spell before stopping movement)
3564 // and future attempts are stopped by by Unit::IsMovementPreventedByCasting in movement generators to prevent casting interruption.
3566 }
3567 }
3568
3570
3571 // set timer base at cast time
3572 ReSetTimer();
3573
3574 TC_LOG_DEBUG("spells", "Spell::prepare: spell id {} source {} caster {} customCastFlags {} mask {}", m_spellInfo->Id, m_caster->GetEntry(), m_originalCaster ? m_originalCaster->GetEntry() : -1, _triggeredCastFlags, m_targets.GetTargetMask());
3575
3578
3581
3582 //Containers for channeled spells have to be set
3584 // Why check duration? 29350: channelled triggers channelled
3586 cast(true);
3587 else
3588 {
3589 // commented out !m_spellInfo->StartRecoveryTime, it forces instant spells with global cooldown to be processed in spell::update
3590 // as a result a spell that passed CheckCast and should be processed instantly may suffer from this delayed process
3591 // the easiest bug to observe is LoS check in AddUnitTarget, even if spell passed the CheckCast LoS check the situation can change in spell::update
3592 // because target could be relocated in the meantime, making the spell fly to the air (no targets can be registered, so no effects processed, nothing in combat log)
3593 bool willCastDirectly = !m_casttime && GetCurrentContainer() == CURRENT_GENERIC_SPELL;
3594
3595 if (Unit* unitCaster = m_caster->ToUnit())
3596 {
3597 // stealth must be removed at cast starting (at show channel bar)
3598 // skip triggered spell (item equip spell casting and other not explicit character casts/item uses)
3600 unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action, m_spellInfo);
3601
3602 // Do not register as current spell when requested to ignore cast in progress
3603 // We don't want to interrupt that other spell with cast time
3604 if (!willCastDirectly || !(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS))
3605 unitCaster->SetCurrentCastSpell(this);
3606 }
3608
3611
3612 // Call CreatureAI hook OnSpellStart
3613 if (Creature* caster = m_caster->ToCreature())
3614 if (caster->IsAIEnabled())
3615 caster->AI()->OnSpellStart(GetSpellInfo());
3616
3617 if (willCastDirectly)
3618 cast(true);
3619 }
3620
3621 return SPELL_CAST_OK;
3622}
3623
3625{
3627 return;
3628
3629 uint32 oldState = m_spellState;
3631
3632 m_autoRepeat = false;
3633 switch (oldState)
3634 {
3637 [[fallthrough]];
3639 SendInterrupted(0);
3641 break;
3642
3644 for (TargetInfo const& targetInfo : m_UniqueTargetInfo)
3645 if (targetInfo.MissCondition == SPELL_MISS_NONE)
3646 if (Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID))
3647 unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL);
3648
3650 SendInterrupted(0);
3652
3653 m_appliedMods.clear();
3654 break;
3655
3656 default:
3657 break;
3658 }
3659
3661 if (m_selfContainer && *m_selfContainer == this)
3662 *m_selfContainer = nullptr;
3663
3664 // originalcaster handles gameobjects/dynobjects for gob caster
3665 if (m_originalCaster)
3666 {
3668 if (m_spellInfo->IsChanneled()) // if not channeled then the object for the current cast wasn't summoned yet
3670 }
3671
3672 //set state back so finish will be processed
3673 m_spellState = oldState;
3674
3676}
3677
3678void Spell::cast(bool skipCheck)
3679{
3680 Player* modOwner = m_caster->GetSpellModOwner();
3681 Spell* lastSpellMod = nullptr;
3682 if (modOwner)
3683 {
3684 lastSpellMod = modOwner->m_spellModTakingSpell;
3685 if (lastSpellMod)
3686 modOwner->SetSpellModTakingSpell(lastSpellMod, false);
3687 }
3688
3689 _cast(skipCheck);
3690
3691 if (lastSpellMod)
3692 modOwner->SetSpellModTakingSpell(lastSpellMod, true);
3693}
3694
3695void Spell::_cast(bool skipCheck)
3696{
3697 // update pointers base at GUIDs to prevent access to non-existed already object
3698 if (!UpdatePointers())
3699 {
3700 // cancel the spell if UpdatePointers() returned false, something wrong happened there
3701 cancel();
3702 return;
3703 }
3704
3705 // cancel at lost explicit target during cast
3707 {
3708 cancel();
3709 return;
3710 }
3711
3712 if (Player* playerCaster = m_caster->ToPlayer())
3713 {
3714 // now that we've done the basic check, now run the scripts
3715 // should be done before the spell is actually executed
3716 sScriptMgr->OnPlayerSpellCast(playerCaster, this, skipCheck);
3717
3718 // As of 3.0.2 pets begin attacking their owner's target immediately
3719 // Let any pets know we've attacked something. Check DmgClass for harmful spells only
3720 // This prevents spells such as Hunter's Mark from triggering pet attack
3721 if (GetSpellInfo()->DmgClass != SPELL_DAMAGE_CLASS_NONE)
3722 if (Unit* target = m_targets.GetUnitTarget())
3723 for (Unit* controlled : playerCaster->m_Controlled)
3724 if (Creature* cControlled = controlled->ToCreature())
3725 if (CreatureAI* controlledAI = cControlled->AI())
3726 controlledAI->OwnerAttacked(target);
3727 }
3728
3730
3731 // Should this be done for original caster?
3732 Player* modOwner = m_caster->GetSpellModOwner();
3733 if (modOwner)
3734 {
3735 // Set spell which will drop charges for triggered cast spells
3736 // if not successfully cast, will be remove in finish(false)
3737 modOwner->SetSpellModTakingSpell(this, true);
3738 }
3739
3741
3742 // skip check if done already (for instant cast spells for example)
3743 if (!skipCheck)
3744 {
3745 auto cleanupSpell = [this, modOwner](SpellCastResult res, int32* p1 = nullptr, int32* p2 = nullptr)
3746 {
3747 SendCastResult(res, p1, p2);
3748 SendInterrupted(0);
3749
3750 if (modOwner)
3751 modOwner->SetSpellModTakingSpell(this, false);
3752
3753 finish(res);
3754 SetExecutedCurrently(false);
3755 };
3756
3757 int32 param1 = 0, param2 = 0;
3758 SpellCastResult castResult = CheckCast(false, &param1, &param2);
3759 if (castResult != SPELL_CAST_OK)
3760 {
3761 cleanupSpell(castResult, &param1, &param2);
3762 return;
3763 }
3764
3765 // additional check after cast bar completes (must not be in CheckCast)
3766 // if trade not complete then remember it in trade data
3768 {
3769 if (modOwner)
3770 {
3771 if (TradeData* my_trade = modOwner->GetTradeData())
3772 {
3773 if (!my_trade->IsInAcceptProcess())
3774 {
3775 // Spell will be cast after completing the trade. Silently ignore at this place
3776 my_trade->SetSpell(m_spellInfo->Id, m_CastItem);
3777 cleanupSpell(SPELL_FAILED_DONT_REPORT);
3778 return;
3779 }
3780 }
3781 }
3782 }
3783
3784 // check diminishing returns (again, only after finish cast bar, tested on retail)
3785 if (Unit* target = m_targets.GetUnitTarget())
3786 {
3787 uint32 aura_effmask = 0;
3788 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
3789 if (spellEffectInfo.IsUnitOwnedAuraEffect())
3790 aura_effmask |= 1 << spellEffectInfo.EffectIndex;
3791
3792 if (aura_effmask)
3793 {
3795 {
3797 if (type == DRTYPE_ALL || (type == DRTYPE_PLAYER && target->IsAffectedByDiminishingReturns()))
3798 {
3800 {
3801 if (target->HasStrongerAuraWithDR(m_spellInfo, caster))
3802 {
3803 cleanupSpell(SPELL_FAILED_AURA_BOUNCED);
3804 return;
3805 }
3806 }
3807 }
3808 }
3809 }
3810 }
3811 }
3812 // The spell focusing is making sure that we have a valid cast target guid when we need it so only check for a guid value here.
3813 if (Creature* creatureCaster = m_caster->ToCreature())
3814 if (!creatureCaster->GetTarget().IsEmpty() && !creatureCaster->HasUnitFlag(UNIT_FLAG_POSSESSED))
3815 if (WorldObject const* target = ObjectAccessor::GetUnit(*creatureCaster, creatureCaster->GetTarget()))
3816 creatureCaster->SetInFront(target);
3817
3819
3820 // Spell may be finished after target map check
3822 {
3823 SendInterrupted(0);
3824
3825 // cleanup after mod system
3826 // triggered spell pointer can be not removed in some cases
3828 m_caster->ToPlayer()->SetSpellModTakingSpell(this, false);
3829
3831 SetExecutedCurrently(false);
3832 return;
3833 }
3834
3835 if (Unit* unitCaster = m_caster->ToUnit())
3837 if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, unitCaster->GetPetGUID()))
3838 pet->DespawnOrUnsummon();
3839
3841
3843
3844 // traded items have trade slot instead of guid in m_itemTargetGUID
3845 // set to real guid to be sent later to the client
3847
3848 if (Player* player = m_caster->ToPlayer())
3849 {
3851 {
3852 player->StartCriteria(CriteriaStartEvent::UseItem, m_CastItem->GetEntry());
3853 player->UpdateCriteria(CriteriaType::UseItem, m_CastItem->GetEntry());
3854 }
3855
3856 player->FailCriteria(CriteriaFailEvent::CastSpell, m_spellInfo->Id);
3857 player->StartCriteria(CriteriaStartEvent::CastSpell, m_spellInfo->Id);
3858 player->UpdateCriteria(CriteriaType::CastSpell, m_spellInfo->Id);
3859 }
3860
3861 // Spells that don't create items can have this attribute - handle here
3863 if (Player* playerCaster = m_caster->ToPlayer())
3864 playerCaster->UpdateCraftSkill(m_spellInfo);
3865
3867 {
3868 // Powers have to be taken before SendSpellGo
3869 TakePower();
3870 TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot
3871 }
3872 else if (Item* targetItem = m_targets.GetItemTarget())
3873 {
3875 if (targetItem->GetOwnerGUID() != m_caster->GetGUID())
3876 TakeReagents();
3877 }
3878
3879 // CAST SPELL
3882
3884 {
3886 m_launchHandled = true;
3887 }
3888
3891
3892 // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
3893 SendSpellGo();
3894
3895 if (!m_spellInfo->IsChanneled())
3896 if (Creature* creatureCaster = m_caster->ToCreature())
3897 creatureCaster->ReleaseSpellFocus(this);
3898
3899 // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells
3901 {
3902 // Remove used for cast item if need (it can be already NULL after TakeReagents call
3903 // in case delayed spell remove item at cast delay start
3904 TakeCastItem();
3905
3906 // Okay, maps created, now prepare flags
3907 m_immediateHandled = false;
3909 SetDelayStart(0);
3910
3911 if (Unit* unitCaster = m_caster->ToUnit())
3912 if (unitCaster->HasUnitState(UNIT_STATE_CASTING) && !unitCaster->IsNonMeleeSpellCast(false, false, true))
3913 unitCaster->ClearUnitState(UNIT_STATE_CASTING);
3914 }
3915 else
3916 {
3917 // Immediate spell, no big deal
3919 }
3920
3922 m_scriptResult->SetResult(SPELL_CAST_OK);
3923
3925
3926 if (std::vector<int32> const* spell_triggered = sSpellMgr->GetSpellLinked(SPELL_LINK_CAST, m_spellInfo->Id))
3927 {
3928 for (int32 id : *spell_triggered)
3929 {
3930 if (id < 0)
3931 {
3932 if (Unit* unitCaster = m_caster->ToUnit())
3933 unitCaster->RemoveAurasDueToSpell(-id);
3934 }
3935 else
3937 .SetTriggeringSpell(this));
3938 }
3939 }
3940
3941 if (modOwner)
3942 {
3943 modOwner->SetSpellModTakingSpell(this, false);
3944
3945 //Clear spell cooldowns after every spell is cast if .cheat cooldown is enabled.
3947 {
3950 }
3951 }
3952
3953 SetExecutedCurrently(false);
3954
3955 if (!m_originalCaster)
3956 return;
3957
3958 // Handle procs on cast
3959 ProcFlagsInit procAttacker = m_procAttacker;
3960 if (!procAttacker)
3961 {
3963 {
3964 if (IsPositive())
3965 procAttacker |= PROC_FLAG_DEAL_HELPFUL_PERIODIC;
3966 else
3967 procAttacker |= PROC_FLAG_DEAL_HARMFUL_PERIODIC;
3968 }
3970 {
3971 if (IsPositive())
3972 procAttacker |= PROC_FLAG_DEAL_HELPFUL_ABILITY;
3973 else
3974 procAttacker |= PROC_FLAG_DEAL_HARMFUL_ABILITY;
3975 }
3976 else
3977 {
3978 if (IsPositive())
3979 procAttacker |= PROC_FLAG_DEAL_HELPFUL_SPELL;
3980 else
3981 procAttacker |= PROC_FLAG_DEAL_HARMFUL_SPELL;
3982 }
3983 }
3984
3985 procAttacker |= PROC_FLAG_2_CAST_SUCCESSFUL;
3986
3987 ProcFlagsHit hitMask = m_hitMask;
3988 if (!(hitMask & PROC_HIT_CRITICAL))
3989 hitMask |= PROC_HIT_NORMAL;
3990
3993
3994 Unit::ProcSkillsAndAuras(m_originalCaster, nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_CAST, hitMask, this, nullptr, nullptr);
3995
3996 // Call CreatureAI hook OnSpellCast
3997 if (Creature* caster = m_originalCaster->ToCreature())
3998 if (caster->IsAIEnabled())
3999 caster->AI()->OnSpellCast(GetSpellInfo());
4000}
4001
4002template <class Container>
4003void Spell::DoProcessTargetContainer(Container& targetContainer)
4004{
4005 for (TargetInfoBase& target : targetContainer)
4006 target.PreprocessTarget(this);
4007
4008 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
4009 for (TargetInfoBase& target : targetContainer)
4010 if (target.EffectMask & (1 << spellEffectInfo.EffectIndex))
4011 target.DoTargetSpellHit(this, spellEffectInfo);
4012
4013 for (TargetInfoBase& target : targetContainer)
4014 target.DoDamageAndTriggers(this);
4015}
4016
4018{
4019 // start channeling if applicable
4020 if (m_spellInfo->IsChanneled())
4021 {
4022 int32 duration = m_spellInfo->GetDuration();
4023 if (duration > 0 || m_spellValue->Duration > 0)
4024 {
4025 if (!m_spellValue->Duration)
4026 {
4027 int32 originalDuration = duration;
4028
4029 // First mod_duration then haste - see Missile Barrage
4030 // Apply duration mod
4031 if (Player* modOwner = m_caster->GetSpellModOwner())
4032 modOwner->ApplySpellMod(m_spellInfo, SpellModOp::Duration, duration);
4033
4034 duration *= m_spellValue->DurationMul;
4035
4036 // Apply haste mods
4037 m_caster->ModSpellDurationTime(m_spellInfo, duration, this);
4038
4039 if (IsEmpowerSpell())
4040 {
4041 float ratio = float(duration) / float(originalDuration);
4042 m_empower->StageDurations.resize(m_spellInfo->EmpowerStageThresholds.size());
4043 Milliseconds totalExceptLastStage = 0ms;
4044 for (std::size_t i = 0; i < m_empower->StageDurations.size() - 1; ++i)
4045 {
4046 m_empower->StageDurations[i] = Milliseconds(int64(m_spellInfo->EmpowerStageThresholds[i].count() * ratio));
4047 totalExceptLastStage += m_empower->StageDurations[i];
4048 }
4049
4050 m_empower->StageDurations.back() = Milliseconds(duration) - totalExceptLastStage;
4051
4052 if (Player const* playerCaster = m_caster->ToPlayer())
4053 m_empower->MinHoldTime = Milliseconds(int64(m_empower->StageDurations[0].count() * playerCaster->GetEmpowerMinHoldStagePercent()));
4054 else
4055 m_empower->MinHoldTime = m_empower->StageDurations[0];
4056
4058 }
4059 }
4060 else
4061 duration = *m_spellValue->Duration;
4062
4063 m_channeledDuration = duration;
4064 SendChannelStart(duration);
4065 }
4066 else if (duration == -1)
4067 SendChannelStart(duration);
4068
4069 if (duration != 0)
4070 {
4072 // GameObjects shouldn't cast channeled spells
4074 }
4075 }
4076
4078
4079 // process immediate effects (items, ground, etc.) also initialize some variables
4081
4082 // consider spell hit for some spells without target, so they may proc on finish phase correctly
4083 if (m_UniqueTargetInfo.empty())
4084 {
4087 }
4088 else
4090
4092
4094
4096
4097 // spell is finished, perform some last features of the spell here
4099
4100 // Remove used for cast item if need (it can be already NULL after TakeReagents call
4101 TakeCastItem();
4102
4104 finish(); // successfully finish spell cast (not last in case autorepeat or channel spell)
4105}
4106
4108{
4109 if (!UpdatePointers())
4110 {
4111 // finish the spell if UpdatePointers() returned false, something wrong happened there
4113 return 0;
4114 }
4115
4116 // when spell has a single missile we hit all targets (except caster) at the same time
4117 bool single_missile = m_targets.HasDst();
4118 bool ignoreTargetInfoTimeDelay = single_missile;
4119 uint64 next_time = 0;
4120
4121 if (!m_launchHandled)
4122 {
4123 uint64 launchMoment = uint64(std::floor(m_spellInfo->LaunchDelay * 1000.0f));
4124 if (launchMoment > t_offset)
4125 return launchMoment;
4126
4128 m_launchHandled = true;
4129 }
4130
4131 if (m_delayMoment > t_offset)
4132 {
4133 ignoreTargetInfoTimeDelay = false;
4134 next_time = m_delayMoment;
4135 }
4136
4137 Player* modOwner = m_caster->GetSpellModOwner();
4138 if (modOwner)
4139 modOwner->SetSpellModTakingSpell(this, true);
4140
4142
4143 if (!m_immediateHandled && m_delayMoment <= t_offset)
4144 {
4146 m_immediateHandled = true;
4147 }
4148
4149 // now recheck units targeting correctness (need before any effects apply to prevent adding immunity at first effect not allow apply second spell effect and similar cases)
4150 {
4151 std::vector<TargetInfo> delayedTargets;
4152 m_UniqueTargetInfo.erase(std::remove_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [&](TargetInfo& target) -> bool
4153 {
4154 if (ignoreTargetInfoTimeDelay || target.TimeDelay <= t_offset)
4155 {
4156 target.TimeDelay = t_offset;
4157 delayedTargets.emplace_back(std::move(target));
4158 return true;
4159 }
4160 else if (!single_missile && (next_time == 0 || target.TimeDelay < next_time))
4161 next_time = target.TimeDelay;
4162
4163 return false;
4164 }), m_UniqueTargetInfo.end());
4165
4166 DoProcessTargetContainer(delayedTargets);
4167 }
4168
4169 // now recheck gameobject targeting correctness
4170 {
4171 std::vector<GOTargetInfo> delayedGOTargets;
4172 m_UniqueGOTargetInfo.erase(std::remove_if(m_UniqueGOTargetInfo.begin(), m_UniqueGOTargetInfo.end(), [&](GOTargetInfo& goTarget) -> bool
4173 {
4174 if (ignoreTargetInfoTimeDelay || goTarget.TimeDelay <= t_offset)
4175 {
4176 goTarget.TimeDelay = t_offset;
4177 delayedGOTargets.emplace_back(std::move(goTarget));
4178 return true;
4179 }
4180 else if (!single_missile && (next_time == 0 || goTarget.TimeDelay < next_time))
4181 next_time = goTarget.TimeDelay;
4182
4183 return false;
4184 }), m_UniqueGOTargetInfo.end());
4185
4186 DoProcessTargetContainer(delayedGOTargets);
4187 }
4188
4190
4191 if (modOwner)
4192 modOwner->SetSpellModTakingSpell(this, false);
4193
4194 // All targets passed - need finish phase
4195 if (next_time == 0)
4196 {
4197 // spell is finished, perform some last features of the spell here
4199
4200 finish(); // successfully finish spell cast
4201
4202 // return zero, spell is finished now
4203 return 0;
4204 }
4205 else
4206 {
4207 // spell is unfinished, return next execution time
4208 return next_time;
4209 }
4210}
4211
4213{
4214 // handle some immediate features of the spell here
4216
4217 // handle effects with SPELL_EFFECT_HANDLE_HIT mode
4218 for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
4219 {
4220 // don't do anything for empty effect
4221 if (!spellEffectInfo.IsEffect())
4222 continue;
4223
4224 // call effect handlers to handle destination hit
4225 HandleEffects(nullptr, nullptr, nullptr, nullptr, spellEffectInfo, SPELL_EFFECT_HANDLE_HIT);
4226 }
4227
4228 // process items
4230}
4231
4233{
4234 if (Unit* unitCaster = m_caster->ToUnit())
4236 unitCaster->SetLastExtraAttackSpell(m_spellInfo->Id);
4237
4238 // Handle procs on finish
4239 if (!m_originalCaster)
4240 return;
4241
4242 ProcFlagsInit procAttacker = m_procAttacker;
4243 if (!procAttacker)
4244 {
4246 {
4247 if (IsPositive())
4248 procAttacker |= PROC_FLAG_DEAL_HELPFUL_PERIODIC;
4249 else
4250 procAttacker |= PROC_FLAG_DEAL_HARMFUL_PERIODIC;
4251 }
4253 {
4254 if (IsPositive())
4255 procAttacker |= PROC_FLAG_DEAL_HELPFUL_ABILITY;
4256 else
4257 procAttacker |= PROC_FLAG_DEAL_HARMFUL_ABILITY;
4258 }
4259 else
4260 {
4261 if (IsPositive())
4262 procAttacker |= PROC_FLAG_DEAL_HELPFUL_SPELL;
4263 else
4264 procAttacker |= PROC_FLAG_DEAL_HARMFUL_SPELL;
4265 }
4266 }
4267
4269}
4270
4272{
4273 if (!m_caster->IsUnit())
4274 return;
4275
4276 if (m_CastItem)
4278 else
4280
4281 if (IsAutoRepeat())
4283}
4284
4285void Spell::update(uint32 difftime)
4286{
4287 // update pointers based at it's GUIDs
4288 if (!UpdatePointers())
4289 {
4290 // cancel the spell if UpdatePointers() returned false, something wrong happened there
4291 cancel();
4292 return;
4293 }
4294
4296 {
4297 TC_LOG_DEBUG("spells", "Spell {} is cancelled due to removal of target.", m_spellInfo->Id);
4298 cancel();
4299 return;
4300 }
4301
4302 // check if the unit caster has moved before the spell finished
4303 if (m_timer != 0 && m_caster->IsUnit() && m_caster->ToUnit()->isMoving() && CheckMovement() != SPELL_CAST_OK)
4304 cancel();
4305
4306 switch (m_spellState)
4307 {
4309 {
4310 if (m_timer > 0)
4311 {
4312 if (difftime >= (uint32)m_timer)
4313 m_timer = 0;
4314 else
4315 m_timer -= difftime;
4316 }
4317
4319 // don't CheckCast for instant spells - done in spell::prepare, skip duplicate checks, needed for range checks for example
4320 cast(!m_casttime);
4321 break;
4322 }
4324 {
4325 if (m_timer)
4326 {
4327 // check if there are alive targets left
4329 {
4330 TC_LOG_DEBUG("spells", "Channeled spell {} is removed due to lack of targets", m_spellInfo->Id);
4331 m_timer = 0;
4332
4333 // Also remove applied auras
4334 for (TargetInfo const& target : m_UniqueTargetInfo)
4335 if (Unit* unit = m_caster->GetGUID() == target.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, target.TargetGUID))
4336 unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL);
4337 }
4338
4339 if (m_timer > 0)
4340 {
4341 if (difftime >= (uint32)m_timer)
4342 m_timer = 0;
4343 else
4344 m_timer -= difftime;
4345 }
4346 }
4347
4348 if (IsEmpowerSpell())
4349 {
4350 int32 completedStages = [&]() -> int32
4351 {
4353 for (std::size_t i = 0; i < m_empower->StageDurations.size(); ++i)
4354 {
4355 passed -= m_empower->StageDurations[i];
4356 if (passed < 0ms)
4357 return i;
4358 }
4359
4360 return m_empower->StageDurations.size();
4361 }();
4362
4363 if (completedStages != m_empower->CompletedStages)
4364 {
4366 empowerSetStage.CastID = m_castId;
4367 empowerSetStage.CasterGUID = m_caster->GetGUID();
4368 empowerSetStage.Stage = m_empower->CompletedStages;
4369 m_caster->SendMessageToSet(empowerSetStage.Write(), true);
4370
4371 m_empower->CompletedStages = completedStages;
4372 m_caster->ToUnit()->SetSpellEmpowerStage(completedStages);
4373
4375 }
4376
4378 {
4379 m_empower->IsReleased = true;
4380 m_timer = 0;
4383 }
4384 }
4385
4386 if (m_timer == 0)
4387 {
4389 finish();
4390
4391 // We call the hook here instead of in Spell::finish because we only want to call it for completed channeling. Everything else is handled by interrupts
4392 if (Creature* creatureCaster = m_caster->ToCreature())
4393 if (creatureCaster->IsAIEnabled())
4394 creatureCaster->AI()->OnChannelFinished(m_spellInfo);
4395 }
4396 break;
4397 }
4398 default:
4399 break;
4400 }
4401}
4402
4404{
4406 return;
4408
4410 m_scriptResult->SetResult(result);
4411
4412 if (!m_caster)
4413 return;
4414
4415 Unit* unitCaster = m_caster->ToUnit();
4416 if (!unitCaster)
4417 return;
4418
4419 // successful cast of the initial autorepeat spell is moved to idle state so that it is not deleted as long as autorepeat is active
4420 if (IsAutoRepeat() && unitCaster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL) == this)
4422
4423 if (m_spellInfo->IsChanneled())
4424 unitCaster->UpdateInterruptMask();
4425
4426 if (unitCaster->HasUnitState(UNIT_STATE_CASTING) && !unitCaster->IsNonMeleeSpellCast(false, false, true))
4428
4429 // Unsummon summon as possessed creatures on spell cancel
4430 if (m_spellInfo->IsChanneled() && unitCaster->GetTypeId() == TYPEID_PLAYER)
4431 {
4432 if (Unit* charm = unitCaster->GetCharmed())
4433 if (charm->GetTypeId() == TYPEID_UNIT
4434 && charm->ToCreature()->HasUnitTypeMask(UNIT_MASK_PUPPET)
4435 && charm->m_unitData->CreatedBySpell == int32(m_spellInfo->Id))
4436 ((Puppet*)charm)->UnSummon();
4437 }
4438
4439 if (Creature* creatureCaster = u