diff options
author | ariel- <ariel-@users.noreply.github.com> | 2018-02-10 16:43:01 -0300 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2021-08-28 15:59:11 +0200 |
commit | 962f6d7988b9003e550f6745be7cff812e9d8efa (patch) | |
tree | ee6ab5872b947afb00f4ca99e87c7dddea35bdb3 /src | |
parent | 65dca120d34febdaa84a63e17f638ab0fa59b3df (diff) |
Core/Spells: rework part 5: GameObject casting
Closes #21330
Closes #18885
Ref #18752
(cherry picked from commit 45c5e1b9d63796d168339a44f63418f220cf2403)
Diffstat (limited to 'src')
46 files changed, 2739 insertions, 2383 deletions
diff --git a/src/server/game/AI/CoreAI/GameObjectAI.h b/src/server/game/AI/CoreAI/GameObjectAI.h index e50f35d9581..41c29d93366 100644 --- a/src/server/game/AI/CoreAI/GameObjectAI.h +++ b/src/server/game/AI/CoreAI/GameObjectAI.h @@ -28,6 +28,7 @@ class Player; class Quest; class SpellInfo; class Unit; +class WorldObject; enum class QuestGiverStatus : uint32; class TC_GAME_API GameObjectAI @@ -73,8 +74,8 @@ class TC_GAME_API GameObjectAI // prevents achievement tracking if returning true virtual bool OnReportUse(Player* /*player*/) { return false; } - virtual void Destroyed(Player* /*player*/, uint32 /*eventId*/) { } - virtual void Damaged(Player* /*player*/, uint32 /*eventId*/) { } + virtual void Destroyed(WorldObject* /*attacker*/, uint32 /*eventId*/) { } + virtual void Damaged(WorldObject* /*attacker*/, uint32 /*eventId*/) { } virtual uint32 GetData(uint32 /*id*/) const { return 0; } virtual void SetData64(uint32 /*id*/, uint64 /*value*/) { } @@ -85,7 +86,14 @@ class TC_GAME_API GameObjectAI virtual void OnLootStateChanged(uint32 /*state*/, Unit* /*unit*/) { } virtual void OnStateChanged(uint32 /*state*/) { } virtual void EventInform(uint32 /*eventId*/) { } - virtual void SpellHit(Unit* /*unit*/, SpellInfo const* /*spellInfo*/) { } + + // Called when hit by a spell + virtual void SpellHit(Unit* /*caster*/, SpellInfo const* /*spellInfo*/) { } + virtual void SpellHit(GameObject* /*caster*/, SpellInfo const* /*spellInfo*/) { } + + // Called when spell hits a target + virtual void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spellInfo*/) { } + virtual void SpellHitTarget(GameObject* /*target*/, SpellInfo const* /*spellInfo*/) { } }; class TC_GAME_API NullGameObjectAI : public GameObjectAI diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 79b949b920f..fe557f76529 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -113,10 +113,12 @@ class TC_GAME_API CreatureAI : public UnitAI virtual void JustUnregisteredAreaTrigger(AreaTrigger* /*areaTrigger*/) { } // Called when hit by a spell - virtual void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) { } + virtual void SpellHit(Unit* /*caster*/, SpellInfo const* /*spellInfo*/) { } + virtual void SpellHit(GameObject* /*caster*/, SpellInfo const* /*spellInfo*/) { } // Called when spell hits a target - virtual void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) { } + virtual void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spellInfo*/) { } + virtual void SpellHitTarget(GameObject* /*target*/, SpellInfo const* /*spellInfo*/) { } virtual bool IsEscorted() const { return false; } diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 4e41082f561..b30bcd6a83b 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -142,33 +142,9 @@ struct TC_GAME_API ScriptedAI : public CreatureAI void AttackStartNoMove(Unit* target); - // Called at any Damage from any attacker (before damage apply) - void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) override { } - //Called at World update tick virtual void UpdateAI(uint32 diff) override; - //Called at creature death - void JustDied(Unit* /*killer*/) override { } - - //Called at creature killing another unit - void KilledUnit(Unit* /*victim*/) override { } - - // Called when the creature summon successfully other creature - void JustSummoned(Creature* /*summon*/) override { } - - // Called when a summoned creature is despawned - void SummonedCreatureDespawn(Creature* /*summon*/) override { } - - // Called when hit by a spell - void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) override { } - - // Called when spell hits a target - void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) override { } - - // Called when AI is temporarily replaced or put back when possess is applied or removed - void OnPossess(bool /*apply*/) { } - // ************* // Variables // ************* @@ -180,12 +156,6 @@ struct TC_GAME_API ScriptedAI : public CreatureAI //Pure virtual functions // ************* - //Called at creature reset either by death or evade - void Reset() override { } - - //Called at creature aggro either by MoveInLOS or Attack Start - void JustEngagedWith(Unit* /*who*/) override { } - // Called before JustEngagedWith even before the creature is in combat. void AttackStart(Unit* /*target*/) override; @@ -273,8 +243,8 @@ struct TC_GAME_API ScriptedAI : public CreatureAI // return true for 25 man or 25 man heroic mode bool Is25ManRaid() const { return _difficulty == DIFFICULTY_25_N || _difficulty == DIFFICULTY_25_HC; } - template<class T> inline - const T& DUNGEON_MODE(const T& normal5, const T& heroic10) const + template <class T> + inline T const& DUNGEON_MODE(T const& normal5, T const& heroic10) const { switch (_difficulty) { @@ -289,8 +259,8 @@ struct TC_GAME_API ScriptedAI : public CreatureAI return heroic10; } - template<class T> inline - const T& RAID_MODE(const T& normal10, const T& normal25) const + template <class T> + inline T const& RAID_MODE(T const& normal10, T const& normal25) const { switch (_difficulty) { @@ -305,8 +275,8 @@ struct TC_GAME_API ScriptedAI : public CreatureAI return normal25; } - template<class T> inline - const T& RAID_MODE(const T& normal10, const T& normal25, const T& heroic10, const T& heroic25) const + template <class T> + inline T const& RAID_MODE(T const& normal10, T const& normal25, T const& heroic10, T const& heroic25) const { switch (_difficulty) { diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 943748e6be2..4dc48f45395 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -1025,9 +1025,9 @@ void SmartGameObjectAI::QuestReward(Player* player, Quest const* quest, LootItem } // Called when the gameobject is destroyed (destructible buildings only). -void SmartGameObjectAI::Destroyed(Player* player, uint32 eventId) +void SmartGameObjectAI::Destroyed(WorldObject* attacker, uint32 eventId) { - GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, player, eventId, 0, false, nullptr, me); + GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, attacker ? attacker->ToUnit() : nullptr, eventId, 0, false, nullptr, me); } void SmartGameObjectAI::SetData(uint32 id, uint32 value, Unit* invoker) diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 591b10ba70f..f0967af9109 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -262,7 +262,7 @@ class TC_GAME_API SmartGameObjectAI : public GameObjectAI bool GossipSelectCode(Player* player, uint32 menuId, uint32 gossipListId, char const* code) override; void QuestAccept(Player* player, Quest const* quest) override; void QuestReward(Player* player, Quest const* quest, LootItemType type, uint32 opt) override; - void Destroyed(Player* player, uint32 eventId) override; + void Destroyed(WorldObject* attacker, uint32 eventId) override; void SetData(uint32 id, uint32 value, Unit* invoker); void SetData(uint32 id, uint32 value) override { SetData(id, value, nullptr); } void SetTimedActionList(SmartScriptHolder& e, uint32 entry, Unit* invoker); diff --git a/src/server/game/Achievements/CriteriaHandler.cpp b/src/server/game/Achievements/CriteriaHandler.cpp index baf0ca54f38..e53429ccac2 100644 --- a/src/server/game/Achievements/CriteriaHandler.cpp +++ b/src/server/game/Achievements/CriteriaHandler.cpp @@ -296,7 +296,7 @@ bool CriteriaData::IsValid(Criteria const* criteria) } } -bool CriteriaData::Meets(uint32 criteriaId, Player const* source, Unit const* target, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/) const +bool CriteriaData::Meets(uint32 criteriaId, Player const* source, WorldObject const* target, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/) const { switch (DataType) { @@ -325,11 +325,18 @@ bool CriteriaData::Meets(uint32 criteriaId, Player const* source, Unit const* ta case CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: if (!target || target->GetTypeId() != TYPEID_PLAYER) return false; - return !target->HealthAbovePct(Health.Percent); + return !target->ToPlayer()->HealthAbovePct(Health.Percent); case CRITERIA_DATA_TYPE_S_AURA: return source->HasAuraEffect(Aura.SpellId, uint8(Aura.EffectIndex)); case CRITERIA_DATA_TYPE_T_AURA: - return target && target->HasAuraEffect(Aura.SpellId, uint8(Aura.EffectIndex)); + { + if (!target) + return false; + Unit const* unitTarget = target->ToUnit(); + if (!unitTarget) + return false; + return unitTarget->HasAuraEffect(Aura.SpellId, uint8(Aura.EffectIndex)); + } case CRITERIA_DATA_TYPE_VALUE: return CompareValues(ComparisionType(Value.ComparisonType), miscValue1, Value.Value); case CRITERIA_DATA_TYPE_T_LEVEL: @@ -337,11 +344,21 @@ bool CriteriaData::Meets(uint32 criteriaId, Player const* source, Unit const* ta return false; return target->GetLevelForTarget(source) >= Level.Min; case CRITERIA_DATA_TYPE_T_GENDER: + { if (!target) return false; - return target->getGender() == Gender.Gender; + Unit const* unitTarget = target->ToUnit(); + if (!unitTarget) + return false; + return unitTarget->getGender() == Gender.Gender; + } case CRITERIA_DATA_TYPE_SCRIPT: - return sScriptMgr->OnCriteriaCheck(ScriptId, const_cast<Player*>(source), const_cast<Unit*>(target)); + { + Unit const* unitTarget = nullptr; + if (target) + unitTarget = target->ToUnit(); + return sScriptMgr->OnCriteriaCheck(ScriptId, const_cast<Player*>(source), const_cast<Unit*>(unitTarget)); + } case CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: return source->GetMap()->GetPlayersCountExceptGMs() <= MapPlayers.MaxCount; case CRITERIA_DATA_TYPE_T_TEAM: @@ -381,7 +398,11 @@ bool CriteriaData::Meets(uint32 criteriaId, Player const* source, Unit const* ta DataType, criteriaId, map->GetId()); return false; } - return instance->CheckAchievementCriteriaMeet(criteriaId, source, target, miscValue1); + + Unit const* unitTarget = nullptr; + if (target) + unitTarget = target->ToUnit(); + return instance->CheckAchievementCriteriaMeet(criteriaId, source, unitTarget, miscValue1); } case CRITERIA_DATA_TYPE_S_EQUIPPED_ITEM: { @@ -415,7 +436,7 @@ bool CriteriaData::Meets(uint32 criteriaId, Player const* source, Unit const* ta return false; } -bool CriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/) const +bool CriteriaDataSet::Meets(Player const* source, WorldObject const* target, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/) const { for (CriteriaData const& data : _storage) if (!data.Meets(_criteriaId, source, target, miscValue1, miscValue2)) @@ -439,7 +460,7 @@ void CriteriaHandler::Reset() /** * this function will be called whenever the user might have done a criteria relevant action */ -void CriteriaHandler::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, Unit const* unit /*= nullptr*/, Player* referencePlayer /*= nullptr*/) +void CriteriaHandler::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, WorldObject const* ref /*= nullptr*/, Player* referencePlayer /*= nullptr*/) { if (type >= CRITERIA_TYPE_TOTAL) { @@ -468,12 +489,12 @@ void CriteriaHandler::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0 for (Criteria const* criteria : criteriaList) { CriteriaTreeList const* trees = sCriteriaMgr->GetCriteriaTreesByCriteria(criteria->ID); - if (!CanUpdateCriteria(criteria, trees, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) + if (!CanUpdateCriteria(criteria, trees, miscValue1, miscValue2, miscValue3, ref, referencePlayer)) continue; // requirements not found in the dbc if (CriteriaDataSet const* data = sCriteriaMgr->GetCriteriaDataSet(criteria)) - if (!data->Meets(referencePlayer, unit, uint32(miscValue1), uint32(miscValue2))) + if (!data->Meets(referencePlayer, ref, uint32(miscValue1), uint32(miscValue2))) continue; switch (type) @@ -1253,7 +1274,7 @@ bool CriteriaHandler::IsCompletedCriteria(Criteria const* criteria, uint64 requi return false; } -bool CriteriaHandler::CanUpdateCriteria(Criteria const* criteria, CriteriaTreeList const* trees, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) +bool CriteriaHandler::CanUpdateCriteria(Criteria const* criteria, CriteriaTreeList const* trees, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject const* ref, Player* referencePlayer) { if (DisableMgr::IsDisabledFor(DISABLE_TYPE_CRITERIA, criteria->ID, nullptr)) { @@ -1274,13 +1295,13 @@ bool CriteriaHandler::CanUpdateCriteria(Criteria const* criteria, CriteriaTreeLi if (!treeRequirementPassed) return false; - if (!RequirementsSatisfied(criteria, miscValue1, miscValue2, miscValue3, unit, referencePlayer)) + if (!RequirementsSatisfied(criteria, miscValue1, miscValue2, miscValue3, ref, referencePlayer)) { TC_LOG_TRACE("criteria", "CriteriaHandler::CanUpdateCriteria: (Id: %u Type %s) Requirements not satisfied", criteria->ID, CriteriaMgr::GetCriteriaTypeString(criteria->Entry->Type)); return false; } - if (criteria->Modifier && !ModifierTreeSatisfied(criteria->Modifier, miscValue1, miscValue2, unit, referencePlayer)) + if (criteria->Modifier && !ModifierTreeSatisfied(criteria->Modifier, miscValue1, miscValue2, ref, referencePlayer)) { TC_LOG_TRACE("criteria", "CriteriaHandler::CanUpdateCriteria: (Id: %u Type %s) Requirements have not been satisfied", criteria->ID, CriteriaMgr::GetCriteriaTypeString(criteria->Entry->Type)); return false; @@ -1317,7 +1338,7 @@ bool CriteriaHandler::ConditionsSatisfied(Criteria const* criteria, Player* refe return true; } -bool CriteriaHandler::RequirementsSatisfied(Criteria const* criteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) const +bool CriteriaHandler::RequirementsSatisfied(Criteria const* criteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject const* ref, Player* referencePlayer) const { switch (CriteriaTypes(criteria->Entry->Type)) { @@ -1428,7 +1449,7 @@ bool CriteriaHandler::RequirementsSatisfied(Criteria const* criteria, uint64 mis break; } case CRITERIA_TYPE_KILLED_BY_PLAYER: - if (!miscValue1 || !unit || unit->GetTypeId() != TYPEID_PLAYER) + if (!miscValue1 || !ref || ref->GetTypeId() != TYPEID_PLAYER) return false; break; case CRITERIA_TYPE_DEATHS_FROM: @@ -1451,7 +1472,7 @@ bool CriteriaHandler::RequirementsSatisfied(Criteria const* criteria, uint64 mis } if (CriteriaDataSet const* data = sCriteriaMgr->GetCriteriaDataSet(criteria)) - if (!data->Meets(referencePlayer, unit)) + if (!data->Meets(referencePlayer, ref)) return false; break; } @@ -1554,7 +1575,7 @@ bool CriteriaHandler::RequirementsSatisfied(Criteria const* criteria, uint64 mis return false; // map specific case (BG in fact) expected player targeted damage/heal - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) + if (!ref || ref->GetTypeId() != TYPEID_PLAYER) return false; } break; @@ -1618,24 +1639,24 @@ bool CriteriaHandler::RequirementsSatisfied(Criteria const* criteria, uint64 mis return true; } -bool CriteriaHandler::ModifierTreeSatisfied(ModifierTreeNode const* tree, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const +bool CriteriaHandler::ModifierTreeSatisfied(ModifierTreeNode const* tree, uint64 miscValue1, uint64 miscValue2, WorldObject const* ref, Player* referencePlayer) const { switch (ModifierTreeOperator(tree->Entry->Operator)) { case ModifierTreeOperator::SingleTrue: - return tree->Entry->Type && ModifierSatisfied(tree->Entry, miscValue1, miscValue2, unit, referencePlayer); + return tree->Entry->Type && ModifierSatisfied(tree->Entry, miscValue1, miscValue2, ref, referencePlayer); case ModifierTreeOperator::SingleFalse: - return tree->Entry->Type && !ModifierSatisfied(tree->Entry, miscValue1, miscValue2, unit, referencePlayer); + return tree->Entry->Type && !ModifierSatisfied(tree->Entry, miscValue1, miscValue2, ref, referencePlayer); case ModifierTreeOperator::All: for (ModifierTreeNode const* node : tree->Children) - if (!ModifierTreeSatisfied(node, miscValue1, miscValue2, unit, referencePlayer)) + if (!ModifierTreeSatisfied(node, miscValue1, miscValue2, ref, referencePlayer)) return false; return true; case ModifierTreeOperator::Some: { int8 requiredAmount = std::max<int8>(tree->Entry->Amount, 1); for (ModifierTreeNode const* node : tree->Children) - if (ModifierTreeSatisfied(node, miscValue1, miscValue2, unit, referencePlayer)) + if (ModifierTreeSatisfied(node, miscValue1, miscValue2, ref, referencePlayer)) if (!--requiredAmount) return true; @@ -1648,7 +1669,7 @@ bool CriteriaHandler::ModifierTreeSatisfied(ModifierTreeNode const* tree, uint64 return false; } -bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const +bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint64 miscValue1, uint64 miscValue2, WorldObject const* ref, Player* referencePlayer) const { uint32 reqValue = modifier->Asset; uint32 secondaryAsset = modifier->SecondaryAsset; @@ -1679,19 +1700,19 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 break; } case ModifierTreeType::TargetCreatureId: // 4 - if (!unit || unit->GetEntry() != reqValue) + if (!ref || ref->GetEntry() != reqValue) return false; break; case ModifierTreeType::TargetIsPlayer: // 5 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) + if (!ref || ref->GetTypeId() != TYPEID_PLAYER) return false; break; case ModifierTreeType::TargetIsDead: // 6 - if (!unit || unit->IsAlive()) + if (!ref || !ref->IsUnit() || ref->ToUnit()->IsAlive()) return false; break; case ModifierTreeType::TargetIsOppositeFaction: // 7 - if (!unit || !referencePlayer->IsHostileTo(unit)) + if (!ref || !referencePlayer->IsHostileTo(ref)) return false; break; case ModifierTreeType::PlayerHasAura: // 8 @@ -1703,15 +1724,15 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetHasAura: // 10 - if (!unit || !unit->HasAura(reqValue)) + if (!ref || !ref->IsUnit() || !ref->ToUnit()->HasAura(reqValue)) return false; break; case ModifierTreeType::TargetHasAuraEffect: // 11 - if (!unit || !unit->HasAuraType(AuraType(reqValue))) + if (!ref || !ref->IsUnit() || !ref->ToUnit()->HasAuraType(AuraType(reqValue))) return false; break; case ModifierTreeType::TargetHasAuraState: // 12 - if (!unit || !unit->HasAuraState(AuraStateType(reqValue))) + if (!ref || !ref->IsUnit() || !ref->ToUnit()->HasAuraState(AuraStateType(reqValue))) return false; break; case ModifierTreeType::PlayerHasAuraState: // 13 @@ -1748,10 +1769,10 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 } case ModifierTreeType::TargetIsInArea: // 18 { - if (!unit) + if (!ref) return false; uint32 zoneId, areaId; - unit->GetZoneAndAreaId(zoneId, areaId); + ref->GetZoneAndAreaId(zoneId, areaId); if (zoneId != reqValue && areaId != reqValue) return false; break; @@ -1768,15 +1789,15 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 break; } case ModifierTreeType::PlayerToTargetLevelDeltaGreaterThan: // 21 - if (!unit || referencePlayer->getLevel() < unit->getLevel() + reqValue) + if (!ref || !ref->IsUnit() || referencePlayer->getLevel() < ref->ToUnit()->getLevel() + reqValue) return false; break; case ModifierTreeType::TargetToPlayerLevelDeltaGreaterThan: // 22 - if (!unit || referencePlayer->getLevel() + reqValue < unit->getLevel()) + if (!ref || !ref->IsUnit() || referencePlayer->getLevel() + reqValue < ref->ToUnit()->getLevel()) return false; break; case ModifierTreeType::PlayerLevelEqualTargetLevel: // 23 - if (!unit || referencePlayer->getLevel() != unit->getLevel()) + if (!ref || !ref->IsUnit() || referencePlayer->getLevel() != ref->ToUnit()->getLevel()) return false; break; case ModifierTreeType::PlayerInArenaWithTeamSize: // 24 @@ -1795,11 +1816,11 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetRace: // 27 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER || unit->getRace() != reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->getRace() != reqValue) return false; break; case ModifierTreeType::TargetClass: // 28 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER || unit->getClass() != reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->getClass() != reqValue) return false; break; case ModifierTreeType::LessThanTappers: // 29 @@ -1808,17 +1829,17 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 break; case ModifierTreeType::CreatureType: // 30 { - if (!unit) + if (!ref) return false; - if (unit->GetTypeId() != TYPEID_UNIT || unit->GetCreatureType() != reqValue) + if (!ref->IsUnit() || ref->ToUnit()->GetCreatureType() != reqValue) return false; break; } case ModifierTreeType::CreatureFamily: // 31 { - if (!unit) + if (!ref) return false; - if (unit->GetTypeId() != TYPEID_UNIT || unit->ToCreature()->GetCreatureTemplate()->family != CreatureFamily(reqValue)) + if (!ref->IsCreature() || ref->ToCreature()->GetCreatureTemplate()->family != CreatureFamily(reqValue)) return false; break; } @@ -1856,7 +1877,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetLevelEqual: // 40 - if (!unit || unit->GetLevelForTarget(referencePlayer) != reqValue) + if (!ref || ref->GetLevelForTarget(referencePlayer) != reqValue) return false; break; case ModifierTreeType::PlayerIsInZone: // 41 @@ -1871,9 +1892,9 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 } case ModifierTreeType::TargetIsInZone: // 42 { - if (!unit) + if (!ref) return false; - uint32 zoneId = unit->GetAreaId(); + uint32 zoneId = ref->GetAreaId(); if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId)) if (areaEntry->Flags[0] & AREA_FLAG_UNK9) zoneId = areaEntry->ParentAreaID; @@ -1894,15 +1915,15 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetHealthBelowPercent: // 46 - if (!unit || unit->GetHealthPct() > float(reqValue)) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetHealthPct() > float(reqValue)) return false; break; case ModifierTreeType::TargetHealthAbovePercent: // 47 - if (!unit || unit->GetHealthPct() < float(reqValue)) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetHealthPct() < float(reqValue)) return false; break; case ModifierTreeType::TargetHealthEqualsPercent: // 48 - if (!unit || unit->GetHealthPct() != float(reqValue)) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetHealthPct() != float(reqValue)) return false; break; case ModifierTreeType::PlayerHealthBelowValue: // 49 @@ -1918,24 +1939,24 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetHealthBelowValue: // 52 - if (!unit || unit->GetHealth() > reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetHealth() > reqValue) return false; break; case ModifierTreeType::TargetHealthAboveValue: // 53 - if (!unit || unit->GetHealth() < reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetHealth() < reqValue) return false; break; case ModifierTreeType::TargetHealthEqualsValue: // 54 - if (!unit || unit->GetHealth() != reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetHealth() != reqValue) return false; break; case ModifierTreeType::TargetIsPlayerAndMeetsCondition: // 55 { - if (!unit || !unit->IsPlayer()) + if (!ref || !ref->IsPlayer()) return false; PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(reqValue); - if (!playerCondition || !ConditionMgr::IsPlayerMeetingCondition(unit->ToPlayer(), playerCondition)) + if (!playerCondition || !ConditionMgr::IsPlayerMeetingCondition(ref->ToPlayer(), playerCondition)) return false; break; } @@ -1995,7 +2016,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetLevelEqualOrGreaterThan: // 70 - if (!unit || unit->getLevel() < reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->getLevel() < reqValue) return false; break; case ModifierTreeType::PlayerLevelEqualOrLessThan: // 71 @@ -2003,12 +2024,12 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetLevelEqualOrLessThan: // 72 - if (!unit || unit->getLevel() > reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->getLevel() > reqValue) return false; break; case ModifierTreeType::ModifierTree: // 73 if (ModifierTreeNode const* nextModifierTree = sCriteriaMgr->GetModifierTree(reqValue)) - return ModifierTreeSatisfied(nextModifierTree, miscValue1, miscValue2, unit, referencePlayer); + return ModifierTreeSatisfied(nextModifierTree, miscValue1, miscValue2, ref, referencePlayer); return false; case ModifierTreeType::PlayerScenario: // 74 { @@ -2288,9 +2309,12 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 break; case ModifierTreeType::TargetThreatListSizeLessThan: // 120 { - if (!unit || !unit->CanHaveThreatList()) + if (!ref) + return false; + Unit const* unitRef = ref->ToUnit(); + if (!unitRef || !unitRef->CanHaveThreatList()) return false; - if (unit->GetThreatManager().GetThreatListSize() >= reqValue) + if (unitRef->GetThreatManager().GetThreatListSize() >= reqValue) return false; break; } @@ -3301,9 +3325,9 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 } case ModifierTreeType::TargetVisibleRace: // 253 { - if (!unit) + if (!ref || !ref->IsUnit()) return false; - CreatureDisplayInfoEntry const* creatureDisplayInfo = sCreatureDisplayInfoStore.LookupEntry(unit->GetDisplayId()); + CreatureDisplayInfoEntry const* creatureDisplayInfo = sCreatureDisplayInfoStore.LookupEntry(ref->ToUnit()->GetDisplayId()); if (!creatureDisplayInfo) return false; CreatureDisplayInfoExtraEntry const* creatureDisplayInfoExtra = sCreatureDisplayInfoExtraStore.LookupEntry(creatureDisplayInfo->ExtendedDisplayInfoID); @@ -3338,7 +3362,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetAuraStackCountEqual: // 256 - if (!unit || unit->GetAuraCount(secondaryAsset) != reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetAuraCount(secondaryAsset) != reqValue) return false; break; case ModifierTreeType::PlayerAuraStackCountEqualOrGreaterThan: // 257 @@ -3346,7 +3370,7 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetAuraStackCountEqualOrGreaterThan: // 258 - if (!unit || unit->GetAuraCount(secondaryAsset) < reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetAuraCount(secondaryAsset) < reqValue) return false; break; case ModifierTreeType::PlayerHasAzeriteEssenceRankLessThan: // 259 @@ -3429,9 +3453,9 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 } case ModifierTreeType::TargetLevelWithinContentTuning: // 269 { - if (!unit) + if (!ref || !ref->IsUnit()) return false; - uint8 level = unit->getLevel(); + uint8 level = ref->ToUnit()->getLevel(); if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(reqValue, 0)) { if (secondaryAsset) @@ -3458,9 +3482,9 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 } case ModifierTreeType::TargetLevelWithinOrAboveContentTuning: // 273 { - if (!unit) + if (!ref || !ref->IsUnit()) return false; - uint8 level = unit->getLevel(); + uint8 level = ref->ToUnit()->getLevel(); if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(reqValue, 0)) return secondaryAsset ? level >= levels->MinLevelWithDelta : level >= levels->MinLevel; return false; @@ -3570,10 +3594,10 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 } case ModifierTreeType::TargetIsInAreaGroup: // 299 { - if (!unit) + if (!ref) return false; std::vector<uint32> areas = sDB2Manager.GetAreasForGroup(reqValue); - if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(unit->GetAreaId())) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(ref->GetAreaId())) for (uint32 areaInGroup : areas) if (areaInGroup == area->ID || areaInGroup == area->ParentAreaID) return true; @@ -3660,9 +3684,9 @@ bool CriteriaHandler::ModifierSatisfied(ModifierTreeEntry const* modifier, uint6 return false; break; case ModifierTreeType::TargetCovenant: // 314 - if (!unit || !unit->IsPlayer()) + if (!ref || !ref->IsPlayer()) return false; - if (unit->ToPlayer()->m_playerData->CovenantID != int32(reqValue)) + if (ref->ToPlayer()->m_playerData->CovenantID != int32(reqValue)) return false; break; case ModifierTreeType::PlayerHasTBCCollectorsEdition: // 315 diff --git a/src/server/game/Achievements/CriteriaHandler.h b/src/server/game/Achievements/CriteriaHandler.h index c6bc3d5822a..1d4dca27813 100644 --- a/src/server/game/Achievements/CriteriaHandler.h +++ b/src/server/game/Achievements/CriteriaHandler.h @@ -29,7 +29,7 @@ #include <ctime> class Player; -class Unit; +class WorldObject; class WorldPacket; struct AchievementEntry; struct CriteriaEntry; @@ -239,14 +239,14 @@ struct CriteriaData } bool IsValid(Criteria const* criteria); - bool Meets(uint32 criteriaId, Player const* source, Unit const* target, uint32 miscValue1 = 0, uint32 miscValue2 = 0) const; + bool Meets(uint32 criteriaId, Player const* source, WorldObject const* target, uint32 miscValue1 = 0, uint32 miscValue2 = 0) const; }; struct CriteriaDataSet { CriteriaDataSet() : _criteriaId(0) { } void Add(CriteriaData const& data) { _storage.push_back(data); } - bool Meets(Player const* source, Unit const* target, uint32 miscValue1 = 0, uint32 miscValue2 = 0) const; + bool Meets(Player const* source, WorldObject const* target, uint32 miscValue1 = 0, uint32 miscValue2 = 0) const; void SetCriteriaId(uint32 id) { _criteriaId = id; } private: uint32 _criteriaId; @@ -271,7 +271,7 @@ public: virtual void Reset(); - void UpdateCriteria(CriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, Unit const* unit = nullptr, Player* referencePlayer = nullptr); + void UpdateCriteria(CriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, WorldObject const* ref = nullptr, Player* referencePlayer = nullptr); virtual void SendAllData(Player const* receiver) const = 0; @@ -294,15 +294,15 @@ protected: virtual void AfterCriteriaTreeUpdate(CriteriaTree const* /*tree*/, Player* /*referencePlayer*/) { } bool IsCompletedCriteria(Criteria const* criteria, uint64 requiredAmount); - bool CanUpdateCriteria(Criteria const* criteria, CriteriaTreeList const* trees, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer); + bool CanUpdateCriteria(Criteria const* criteria, CriteriaTreeList const* trees, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject const* ref, Player* referencePlayer); virtual void SendPacket(WorldPacket const* data) const = 0; bool ConditionsSatisfied(Criteria const* criteria, Player* referencePlayer) const; - bool RequirementsSatisfied(Criteria const* criteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer) const; + bool RequirementsSatisfied(Criteria const* criteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject const* ref, Player* referencePlayer) const; virtual bool RequiredAchievementSatisfied(uint32 /*achievementId*/) const { return false; } - bool ModifierTreeSatisfied(ModifierTreeNode const* parent, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const; - bool ModifierSatisfied(ModifierTreeEntry const* modifier, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const; + bool ModifierTreeSatisfied(ModifierTreeNode const* parent, uint64 miscValue1, uint64 miscValue2, WorldObject const* ref, Player* referencePlayer) const; + bool ModifierSatisfied(ModifierTreeEntry const* modifier, uint64 miscValue1, uint64 miscValue2, WorldObject const* ref, Player* referencePlayer) const; virtual std::string GetOwnerInfo() const = 0; virtual CriteriaList const& GetCriteriaByType(CriteriaTypes type, uint32 asset) const = 0; diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp index e9a179eff46..1c1d9f4cb99 100644 --- a/src/server/game/Conditions/DisableMgr.cpp +++ b/src/server/game/Conditions/DisableMgr.cpp @@ -272,7 +272,7 @@ void CheckQuestDisables() TC_LOG_INFO("server.loading", ">> Checked " SZFMTD " quest disables in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags) +bool IsDisabledFor(DisableType type, uint32 entry, WorldObject const* ref, uint8 flags /*= 0*/) { ASSERT(type < MAX_DISABLE_TYPES); if (m_DisableMap[type].empty()) @@ -287,15 +287,16 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags case DISABLE_TYPE_SPELL: { uint8 spellFlags = itr->second.flags; - if (unit) + if (ref) { - if ((spellFlags & SPELL_DISABLE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || - (unit->GetTypeId() == TYPEID_UNIT && ((unit->IsPet() && spellFlags & SPELL_DISABLE_PET) || spellFlags & SPELL_DISABLE_CREATURE))) + if ((ref->GetTypeId() == TYPEID_PLAYER && (spellFlags & SPELL_DISABLE_PLAYER)) || + (ref->GetTypeId() == TYPEID_UNIT && ((spellFlags & SPELL_DISABLE_CREATURE) || (ref->ToUnit()->IsPet() && (spellFlags & SPELL_DISABLE_PET)))) || + (ref->GetTypeId() == TYPEID_GAMEOBJECT && (spellFlags & SPELL_DISABLE_GAMEOBJECT))) { if (spellFlags & SPELL_DISABLE_MAP) { std::unordered_set<uint32> const& mapIds = itr->second.params[0]; - if (mapIds.find(unit->GetMapId()) != mapIds.end()) + if (mapIds.find(ref->GetMapId()) != mapIds.end()) return true; // Spell is disabled on current map if (!(spellFlags & SPELL_DISABLE_AREA)) @@ -307,7 +308,7 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags if (spellFlags & SPELL_DISABLE_AREA) { std::unordered_set<uint32> const& areaIds = itr->second.params[1]; - if (areaIds.find(unit->GetAreaId()) != areaIds.end()) + if (areaIds.find(ref->GetAreaId()) != areaIds.end()) return true; // Spell is disabled in this area return false; // Spell is disabled in another area, but not this one, return false } @@ -326,7 +327,7 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags } case DISABLE_TYPE_MAP: case DISABLE_TYPE_LFG_MAP: - if (Player const* player = unit->ToPlayer()) + if (Player const* player = ref->ToPlayer()) { MapEntry const* mapEntry = sMapStore.LookupEntry(entry); if (mapEntry->IsDungeon()) @@ -353,9 +354,9 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags } return false; case DISABLE_TYPE_QUEST: - if (!unit) + if (!ref) return true; - if (Player const* player = unit->ToPlayer()) + if (Player const* player = ref->ToPlayer()) if (player->IsGameMaster()) return false; return true; diff --git a/src/server/game/Conditions/DisableMgr.h b/src/server/game/Conditions/DisableMgr.h index 8e0ce34f8ae..75681945e33 100644 --- a/src/server/game/Conditions/DisableMgr.h +++ b/src/server/game/Conditions/DisableMgr.h @@ -20,7 +20,7 @@ #include "Define.h" -class Unit; +class WorldObject; enum DisableType { @@ -39,16 +39,17 @@ enum DisableType enum SpellDisableTypes { - SPELL_DISABLE_PLAYER = 0x1, - SPELL_DISABLE_CREATURE = 0x2, - SPELL_DISABLE_PET = 0x4, - SPELL_DISABLE_DEPRECATED_SPELL = 0x8, + SPELL_DISABLE_PLAYER = 0x01, + SPELL_DISABLE_CREATURE = 0x02, + SPELL_DISABLE_PET = 0x04, + SPELL_DISABLE_DEPRECATED_SPELL = 0x08, SPELL_DISABLE_MAP = 0x10, SPELL_DISABLE_AREA = 0x20, SPELL_DISABLE_LOS = 0x40, + SPELL_DISABLE_GAMEOBJECT = 0x80, MAX_SPELL_DISABLE_TYPE = ( SPELL_DISABLE_PLAYER | SPELL_DISABLE_CREATURE | SPELL_DISABLE_PET | SPELL_DISABLE_DEPRECATED_SPELL | SPELL_DISABLE_MAP | SPELL_DISABLE_AREA | - SPELL_DISABLE_LOS) + SPELL_DISABLE_LOS | SPELL_DISABLE_GAMEOBJECT ) }; enum MMapDisableTypes @@ -59,7 +60,7 @@ enum MMapDisableTypes namespace DisableMgr { TC_GAME_API void LoadDisables(); - TC_GAME_API bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags = 0); + TC_GAME_API bool IsDisabledFor(DisableType type, uint32 entry, WorldObject const* ref, uint8 flags = 0); TC_GAME_API void CheckQuestDisables(); TC_GAME_API bool IsVMAPDisabledFor(uint32 entry, uint8 flags); TC_GAME_API bool IsPathfindingEnabled(uint32 mapId); diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index 773226e43fe..046d194efb0 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -500,6 +500,14 @@ Unit* AreaTrigger::GetTarget() const return ObjectAccessor::GetUnit(*this, _targetGuid); } +uint32 AreaTrigger::GetFaction() const +{ + if (Unit const* caster = GetCaster()) + return caster->GetFaction(); + + return 0; +} + void AreaTrigger::UpdatePolygonOrientation() { float newOrientation = GetOrientation(); diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.h b/src/server/game/Entities/AreaTrigger/AreaTrigger.h index f0f41a44f09..e00f8842ea6 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.h +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.h @@ -92,10 +92,13 @@ class TC_GAME_API AreaTrigger : public WorldObject, public GridObject<AreaTrigge AreaTriggerTemplate const* GetTemplate() const; uint32 GetScriptId() const; + ObjectGuid GetOwnerGUID() const override { return GetCasterGuid(); } ObjectGuid const& GetCasterGuid() const { return m_areaTriggerData->Caster; } Unit* GetCaster() const; Unit* GetTarget() const; + uint32 GetFaction() const override; + Position const& GetRollPitchYaw() const { return _rollPitchYaw; } Position const& GetTargetRollPitchYaw() const { return _targetRollPitchYaw; } void InitSplineOffsets(std::vector<Position> const& offsets, uint32 timeToTarget); @@ -112,7 +115,6 @@ class TC_GAME_API AreaTrigger : public WorldObject, public GridObject<AreaTrigge UF::UpdateField<UF::AreaTriggerData, 0, TYPEID_AREATRIGGER> m_areaTriggerData; - protected: void _UpdateDuration(int32 newDuration); float GetProgress() const; diff --git a/src/server/game/Entities/Conversation/Conversation.h b/src/server/game/Entities/Conversation/Conversation.h index 821bfcaa30f..aa180000016 100644 --- a/src/server/game/Entities/Conversation/Conversation.h +++ b/src/server/game/Entities/Conversation/Conversation.h @@ -56,6 +56,8 @@ class TC_GAME_API Conversation : public WorldObject, public GridObject<Conversat void AddParticipant(ObjectGuid const& participantGuid); ObjectGuid const& GetCreatorGuid() const { return _creatorGuid; } + ObjectGuid GetOwnerGUID() const override { return GetCreatorGuid(); } + uint32 GetFaction() const override { return 0; } float GetStationaryX() const override { return _stationaryPosition.GetPositionX(); } float GetStationaryY() const override { return _stationaryPosition.GetPositionY(); } diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index 2135fb3358d..22636982bcf 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -77,7 +77,7 @@ class TC_GAME_API Corpse : public WorldObject, public GridObject<Corpse> void AddCorpseDynamicFlag(CorpseDynFlags dynamicFlags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::DynamicFlags), dynamicFlags); } void RemoveCorpseDynamicFlag(CorpseDynFlags dynamicFlags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::DynamicFlags), dynamicFlags); } void SetCorpseDynamicFlags(CorpseDynFlags dynamicFlags) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::DynamicFlags), dynamicFlags); } - ObjectGuid GetOwnerGUID() const { return m_corpseData->Owner; } + ObjectGuid GetOwnerGUID() const override { return m_corpseData->Owner; } void SetOwnerGUID(ObjectGuid owner) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::Owner), owner); } void SetPartyGUID(ObjectGuid partyGuid) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::PartyGUID), partyGuid); } void SetGuildGUID(ObjectGuid guildGuid) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::GuildGUID), guildGuid); } @@ -87,6 +87,8 @@ class TC_GAME_API Corpse : public WorldObject, public GridObject<Corpse> void SetSex(uint8 sex) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::Sex), sex); } void SetFlags(uint32 flags) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::Flags), flags); } void SetFactionTemplate(int32 factionTemplate) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::FactionTemplate), factionTemplate); } + uint32 GetFaction() const override { return m_corpseData->FactionTemplate; } + void SetFaction(uint32 faction) override { SetFactionTemplate(faction); } void SetItem(uint32 slot, uint32 item) { SetUpdateFieldValue(m_values.ModifyValue(&Corpse::m_corpseData).ModifyValue(&UF::CorpseData::Items, slot), item); } template<typename Iter> diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 1e5a42060cb..f59f4e48b6a 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2355,7 +2355,7 @@ void Creature::LoadTemplateImmunities() } } -bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const +bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const { if (!spellInfo) return false; @@ -2379,7 +2379,7 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const return Unit::IsImmunedToSpell(spellInfo, caster); } -bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const +bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const { SpellEffectInfo const* effect = spellInfo->GetEffect(index); if (!effect) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 138a061eec3..1463e1b6975 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -127,8 +127,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool CanResetTalents(Player* player) const; bool CanCreatureAttack(Unit const* victim, bool force = true) const; void LoadTemplateImmunities(); - bool IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const override; - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const override; + bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override; bool isElite() const; bool isWorldBoss() const; diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index 550ca700d90..4a9cceedb0a 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -226,6 +226,12 @@ void DynamicObject::RemoveCasterViewpoint() } } +uint32 DynamicObject::GetFaction() const +{ + ASSERT(_caster); + return _caster->GetFaction(); +} + void DynamicObject::BindToCaster() { ASSERT(!_caster); diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h index f2de544ed53..fd3d6a8aa0d 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.h +++ b/src/server/game/Entities/DynamicObject/DynamicObject.h @@ -61,11 +61,13 @@ class TC_GAME_API DynamicObject : public WorldObject, public GridObject<DynamicO void SetCasterViewpoint(); void RemoveCasterViewpoint(); Unit* GetCaster() const { return _caster; } + uint32 GetFaction() const override; void BindToCaster(); void UnbindFromCaster(); uint32 GetSpellId() const { return m_dynamicObjectData->SpellID; } SpellInfo const* GetSpellInfo() const; ObjectGuid GetCasterGUID() const { return m_dynamicObjectData->Caster; } + ObjectGuid GetOwnerGUID() const override { return GetCasterGUID(); } float GetRadius() const { return m_dynamicObjectData->Radius; } UF::UpdateField<UF::DynamicObjectData, 0, TYPEID_DYNAMICOBJECT> m_dynamicObjectData; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 821dc454323..6a13100d63d 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -517,6 +517,8 @@ GameObject* GameObject::CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map* void GameObject::Update(uint32 diff) { + m_Events.Update(diff); + if (AI()) AI()->UpdateAI(diff); else if (!AIM_Initialize()) @@ -825,8 +827,10 @@ void GameObject::Update(uint32 diff) else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID)) { // Some traps do not have a spell but should be triggered + CastSpellExtraArgs args; + args.SetOriginalCaster(GetOwnerGUID()); if (goInfo->trap.spell) - CastSpell(target, goInfo->trap.spell); + CastSpell(target, goInfo->trap.spell, args); // Template value or 4 seconds m_cooldownTime = GameTime::GetGameTimeMS() + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4)) * IN_MILLISECONDS; @@ -1307,11 +1311,6 @@ bool GameObject::IsDestructibleBuilding() const return gInfo->type == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING; } -Unit* GameObject::GetOwner() const -{ - return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); -} - void GameObject::SaveRespawnTime(uint32 forceDelay, bool savetodb) { if (m_goData && (forceDelay || m_respawnTime > GameTime::GetGameTime()) && m_spawnedByDefault) @@ -2211,68 +2210,6 @@ void GameObject::Use(Unit* user) CastSpell(user, spellId); } -void GameObject::CastSpell(Unit* target, uint32 spellId, bool triggered /* = true*/) -{ - CastSpell(target, spellId, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE); -} - -void GameObject::CastSpell(Unit* target, uint32 spellId, TriggerCastFlags triggered) -{ - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, GetMap()->GetDifficultyID()); - if (!spellInfo) - return; - - bool self = false; - for (SpellEffectInfo const* effect : spellInfo->GetEffects()) - { - if (effect && effect->TargetA.GetTarget() == TARGET_UNIT_CASTER) - { - self = true; - break; - } - } - - if (self) - { - if (target) - target->CastSpell(target, spellInfo->Id, triggered); - return; - } - - //summon world trigger - Creature* trigger = SummonTrigger(GetPositionX(), GetPositionY(), GetPositionZ(), 0, spellInfo->CalcCastTime() + 100); - if (!trigger) - return; - - // remove immunity flags, to allow spell to target anything - trigger->SetImmuneToAll(false); - PhasingHandler::InheritPhaseShift(trigger, this); - - CastSpellExtraArgs args; - args.TriggerFlags = triggered; - if (Unit* owner = GetOwner()) - { - trigger->SetFaction(owner->GetFaction()); - if (owner->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - trigger->AddUnitFlag(UNIT_FLAG_PVP_ATTACKABLE); - // copy pvp state flags from owner - trigger->SetPvpFlags(owner->GetPvpFlags()); - // needed for GO casts for proper target validation checks - trigger->SetOwnerGUID(owner->GetGUID()); - - args.OriginalCaster = owner->GetGUID(); - trigger->CastSpell(target ? target : trigger, spellInfo->Id, args); - } - else - { - trigger->SetFaction(spellInfo->IsPositive() ? FACTION_FRIENDLY : FACTION_MONSTER); - // Set owner guid for target if no owner available - needed by trigger auras - // - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell()) - args.OriginalCaster = target ? target->GetGUID() : ObjectGuid::Empty; - trigger->CastSpell(target ? target : trigger, spellInfo->Id, args); - } -} - void GameObject::SendCustomAnim(uint32 anim) { WorldPackets::GameObject::GameObjectCustomAnim customAnim; @@ -2380,7 +2317,7 @@ void GameObject::SetWorldRotationAngles(float z_rot, float y_rot, float x_rot) SetWorldRotation(quat.x, quat.y, quat.z, quat.w); } -void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/) +void GameObject::ModifyHealth(int32 change, WorldObject* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/) { if (!m_goValue.Building.MaxHealth || !change) return; @@ -2399,10 +2336,8 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/ // Set the health bar, value = 255 * healthPct; SetGoAnimProgress(m_goValue.Building.Health * 255 / m_goValue.Building.MaxHealth); - Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr; - // dealing damage, send packet - if (player) + if (Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr) { WorldPackets::GameObject::DestructibleBuildingDamage packet; packet.Caster = attackerOrHealer->GetGUID(); // todo: this can be a GameObject @@ -2425,11 +2360,10 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/ if (newState == GetDestructibleState()) return; - /// @todo: pass attackerOrHealer instead of player - SetDestructibleState(newState, player, false); + SetDestructibleState(newState, attackerOrHealer, false); } -void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= nullptr*/, bool setHealth /*= false*/) +void GameObject::SetDestructibleState(GameObjectDestructibleState state, WorldObject* attackerOrHealer /*= nullptr*/, bool setHealth /*= false*/) { // the user calling this must know he is already operating on destructible gameobject ASSERT(GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING); @@ -2448,8 +2382,8 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* break; case GO_DESTRUCTIBLE_DAMAGED: { - EventInform(m_goInfo->destructibleBuilding.DamagedEvent, eventInvoker); - AI()->Damaged(eventInvoker, m_goInfo->destructibleBuilding.DamagedEvent); + EventInform(m_goInfo->destructibleBuilding.DamagedEvent, attackerOrHealer); + AI()->Damaged(attackerOrHealer, m_goInfo->destructibleBuilding.DamagedEvent); RemoveFlag(GO_FLAG_DESTROYED); AddFlag(GO_FLAG_DAMAGED); @@ -2473,11 +2407,12 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* } case GO_DESTRUCTIBLE_DESTROYED: { - EventInform(m_goInfo->destructibleBuilding.DestroyedEvent, eventInvoker); - AI()->Destroyed(eventInvoker, m_goInfo->destructibleBuilding.DestroyedEvent); - if (eventInvoker) - if (Battleground* bg = eventInvoker->GetBattleground()) - bg->DestroyGate(eventInvoker, this); + EventInform(m_goInfo->destructibleBuilding.DestroyedEvent, attackerOrHealer); + AI()->Destroyed(attackerOrHealer, m_goInfo->destructibleBuilding.DestroyedEvent); + + if (attackerOrHealer && attackerOrHealer->GetTypeId() == TYPEID_PLAYER) + if (Battleground* bg = attackerOrHealer->ToPlayer()->GetBattleground()) + bg->DestroyGate(attackerOrHealer->ToPlayer(), this); RemoveFlag(GO_FLAG_DAMAGED); AddFlag(GO_FLAG_DESTROYED); @@ -2498,7 +2433,7 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* } case GO_DESTRUCTIBLE_REBUILDING: { - EventInform(m_goInfo->destructibleBuilding.RebuildingEvent, eventInvoker); + EventInform(m_goInfo->destructibleBuilding.RebuildingEvent, attackerOrHealer); RemoveFlag(GameObjectFlags(GO_FLAG_DAMAGED | GO_FLAG_DESTROYED)); uint32 modelId = m_goInfo->displayId; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index bb1efe37da0..d16120e0edc 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -140,8 +140,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> m_spawnedByDefault = false; // all object with owner is despawned after delay SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::CreatedBy), owner); } - ObjectGuid GetOwnerGUID() const { return m_gameObjectData->CreatedBy; } - Unit* GetOwner() const; + ObjectGuid GetOwnerGUID() const override { return m_gameObjectData->CreatedBy; } void SetSpellId(uint32 id) { @@ -249,14 +248,12 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> GameObject* LookupFishingHoleAround(float range); - void CastSpell(Unit* target, uint32 spell, bool triggered = true); - void CastSpell(Unit* target, uint32 spell, TriggerCastFlags triggered); void SendCustomAnim(uint32 anim); bool IsInRange(float x, float y, float z, float radius) const; - void ModifyHealth(int32 change, Unit* attackerOrHealer = nullptr, uint32 spellId = 0); + void ModifyHealth(int32 change, WorldObject* attackerOrHealer = nullptr, uint32 spellId = 0); // sets GameObject type 33 destruction flags and optionally default health for that state - void SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker = nullptr, bool setHealth = false); + void SetDestructibleState(GameObjectDestructibleState state, WorldObject* attackerOrHealer = nullptr, bool setHealth = false); GameObjectDestructibleState GetDestructibleState() const { if ((*m_gameObjectData->Flags & GO_FLAG_DESTROYED)) @@ -280,8 +277,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> uint32 GetDisplayId() const { return m_gameObjectData->DisplayID; } uint8 GetNameSetId() const; - uint32 GetFaction() const { return m_gameObjectData->FactionTemplate; } - void SetFaction(uint32 faction) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::FactionTemplate), faction); } + uint32 GetFaction() const override { return m_gameObjectData->FactionTemplate; } + void SetFaction(uint32 faction) override { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::FactionTemplate), faction); } GameObjectModel* m_model; void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr) const; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 985d079826c..a2b4067291d 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -21,6 +21,7 @@ #include "BattlefieldMgr.h" #include "CellImpl.h" #include "CinematicMgr.h" +#include "CombatLogPacketsCommon.h" #include "Common.h" #include "Creature.h" #include "GameTime.h" @@ -36,8 +37,9 @@ #include "OutdoorPvPMgr.h" #include "PhasingHandler.h" #include "Player.h" -#include "SharedDefines.h" +#include "ReputationMgr.h" #include "SpellAuraEffects.h" +#include "SpellMgr.h" #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" @@ -848,8 +850,8 @@ void MovementInfo::OutDebug() TC_LOG_DEBUG("misc", "splineElevation: %f", splineElevation); } -WorldObject::WorldObject(bool isWorldObject) : WorldLocation(), LastUsedScriptID(0), -m_name(""), m_isActive(false), m_isFarVisible(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr), +WorldObject::WorldObject(bool isWorldObject) : Object(), WorldLocation(), LastUsedScriptID(0), +m_movementInfo(), m_name(), m_isActive(false), m_isFarVisible(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr), m_transport(nullptr), m_zoneId(0), m_areaId(0), m_staticFloorZ(VMAP_INVALID_HEIGHT), m_currMap(nullptr), m_InstanceId(0), _dbPhase(0), m_notifyflags(0) { @@ -1658,6 +1660,36 @@ void WorldObject::SendMessageToSet(WorldPacket const* data, Player const* skippe Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); } +struct CombatLogSender +{ + WorldPackets::CombatLog::CombatLogServerPacket const* i_message; + + explicit CombatLogSender(WorldPackets::CombatLog::CombatLogServerPacket* msg) + : i_message(msg) + { + msg->Write(); + } + + void operator()(Player const* player) const + { + if (player->IsAdvancedCombatLoggingEnabled()) + player->SendDirectMessage(i_message->GetFullLogPacket()); + else + player->SendDirectMessage(i_message->GetBasicLogPacket()); + } +}; + +void WorldObject::SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const +{ + CombatLogSender combatLogSender(combatLog); + + if (Player const* self = ToPlayer()) + combatLogSender(self); + + Trinity::MessageDistDeliverer<CombatLogSender> notifier(this, combatLogSender, GetVisibilityRange()); + Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); +} + void WorldObject::SetMap(Map* map) { ASSERT(map); @@ -1986,6 +2018,958 @@ Player* WorldObject::SelectNearestPlayer(float distance) const return target; } +ObjectGuid WorldObject::GetCharmerOrOwnerOrOwnGUID() const +{ + ObjectGuid guid = GetCharmerOrOwnerGUID(); + if (!guid.IsEmpty()) + return guid; + return GetGUID(); +} + +Unit* WorldObject::GetOwner() const +{ + return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); +} + +Unit* WorldObject::GetCharmerOrOwner() const +{ + if (Unit const* unit = ToUnit()) + return unit->GetCharmerOrOwner(); + else if (GameObject const* go = ToGameObject()) + return go->GetOwner(); + + return nullptr; +} + +Unit* WorldObject::GetCharmerOrOwnerOrSelf() const +{ + if (Unit* u = GetCharmerOrOwner()) + return u; + + return const_cast<WorldObject*>(this)->ToUnit(); +} + +Player* WorldObject::GetCharmerOrOwnerPlayerOrPlayerItself() const +{ + ObjectGuid guid = GetCharmerOrOwnerGUID(); + if (guid.IsPlayer()) + return ObjectAccessor::GetPlayer(*this, guid); + + return const_cast<WorldObject*>(this)->ToPlayer(); +} + +Player* WorldObject::GetAffectingPlayer() const +{ + if (!GetCharmerOrOwnerGUID()) + return const_cast<WorldObject*>(this)->ToPlayer(); + + if (Unit* owner = GetCharmerOrOwner()) + return owner->GetCharmerOrOwnerPlayerOrPlayerItself(); + + return nullptr; +} + +Player* WorldObject::GetSpellModOwner() const +{ + if (Player* player = const_cast<WorldObject*>(this)->ToPlayer()) + return player; + + if (GetTypeId() == TYPEID_UNIT) + { + Creature const* creature = ToCreature(); + if (creature->IsPet() || creature->IsTotem()) + { + if (Unit* owner = creature->GetOwner()) + return owner->ToPlayer(); + } + } + else if (GetTypeId() == TYPEID_GAMEOBJECT) + { + GameObject const* go = ToGameObject(); + if (Unit* owner = go->GetOwner()) + return owner->ToPlayer(); + } + + return nullptr; +} + +// function uses real base points (typically value - 1) +int32 WorldObject::CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effIndex, int32 const* basePoints /*= nullptr*/, float* variance /*= nullptr*/, uint32 castItemId /*= 0*/, int32 itemLevel /*= -1*/) const +{ + SpellEffectInfo const* effect = spellProto->GetEffect(effIndex); + if (variance) + *variance = 0.0f; + + return effect ? effect->CalcValue(this, basePoints, target, variance, castItemId, itemLevel) : 0; +} + +float WorldObject::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const +{ + if (!spellInfo->RangeEntry) + return 0.f; + + if (spellInfo->RangeEntry->RangeMax[0] == spellInfo->RangeEntry->RangeMax[1]) + return spellInfo->GetMaxRange(); + + if (!target) + return spellInfo->GetMaxRange(true); + + return spellInfo->GetMaxRange(!IsHostileTo(target)); +} + +float WorldObject::GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const +{ + if (!spellInfo->RangeEntry) + return 0.f; + + if (spellInfo->RangeEntry->RangeMin[0] == spellInfo->RangeEntry->RangeMin[1]) + return spellInfo->GetMinRange(); + + if (!target) + return spellInfo->GetMinRange(true); + + return spellInfo->GetMinRange(!IsHostileTo(target)); +} + +float WorldObject::ApplyEffectModifiers(SpellInfo const* spellInfo, uint8 effIndex, float value) const +{ + if (Player* modOwner = GetSpellModOwner()) + { + modOwner->ApplySpellMod(spellInfo, SpellModOp::Points, value); + switch (effIndex) + { + case EFFECT_0: + modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex0, value); + break; + case EFFECT_1: + modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex1, value); + break; + case EFFECT_2: + modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex2, value); + break; + case EFFECT_3: + modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex3, value); + break; + case EFFECT_4: + modOwner->ApplySpellMod(spellInfo, SpellModOp::PointsIndex4, value); + break; + } + } + return value; +} + +int32 WorldObject::CalcSpellDuration(SpellInfo const* spellInfo) const +{ + uint8 comboPoints = 0; + if (Unit const* unit = ToUnit()) + comboPoints = unit->GetPower(POWER_COMBO_POINTS); + + int32 minduration = spellInfo->GetDuration(); + int32 maxduration = spellInfo->GetMaxDuration(); + + int32 duration; + if (comboPoints && minduration != -1 && minduration != maxduration) + duration = minduration + int32((maxduration - minduration) * comboPoints / 5); + else + duration = minduration; + + return duration; +} + +int32 WorldObject::ModSpellDuration(SpellInfo const* spellInfo, WorldObject const* target, int32 duration, bool positive, uint32 effectMask) const +{ + // don't mod permanent auras duration + if (duration < 0) + return duration; + + // some auras are not affected by duration modifiers + if (spellInfo->HasAttribute(SPELL_ATTR7_IGNORE_DURATION_MODS)) + return duration; + + // cut duration only of negative effects + Unit const* unitTarget = target->ToUnit(); + if (!unitTarget) + return duration; + + if (!positive) + { + int32 mechanicMask = spellInfo->GetSpellMechanicMaskByEffectMask(effectMask); + auto mechanicCheck = [mechanicMask](AuraEffect const* aurEff) -> bool + { + if (mechanicMask & (1 << aurEff->GetMiscValue())) + return true; + return false; + }; + + // Find total mod value (negative bonus) + int32 durationMod_always = unitTarget->GetTotalAuraModifier(SPELL_AURA_MECHANIC_DURATION_MOD, mechanicCheck); + // Find max mod (negative bonus) + int32 durationMod_not_stack = unitTarget->GetMaxNegativeAuraModifier(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, mechanicCheck); + + // Select strongest negative mod + int32 durationMod = std::min(durationMod_always, durationMod_not_stack); + if (durationMod != 0) + AddPct(duration, durationMod); + + // there are only negative mods currently + durationMod_always = unitTarget->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL, spellInfo->Dispel); + durationMod_not_stack = unitTarget->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK, spellInfo->Dispel); + + durationMod = std::min(durationMod_always, durationMod_not_stack); + if (durationMod != 0) + AddPct(duration, durationMod); + } + else + { + // else positive mods here, there are no currently + // when there will be, change GetTotalAuraModifierByMiscValue to GetMaxPositiveAuraModifierByMiscValue + + // Mixology - duration boost + if (unitTarget->GetTypeId() == TYPEID_PLAYER) + { + if (spellInfo->SpellFamilyName == SPELLFAMILY_POTION && ( + sSpellMgr->IsSpellMemberOfSpellGroup(spellInfo->Id, SPELL_GROUP_ELIXIR_BATTLE) || + sSpellMgr->IsSpellMemberOfSpellGroup(spellInfo->Id, SPELL_GROUP_ELIXIR_GUARDIAN))) + { + SpellEffectInfo const* effect = spellInfo->GetEffect(EFFECT_0); + if (unitTarget->HasAura(53042) && effect && unitTarget->HasSpell(effect->TriggerSpell)) + duration *= 2; + } + } + } + + return std::max(duration, 0); +} + +void WorldObject::ModSpellCastTime(SpellInfo const* spellInfo, int32& castTime, Spell* spell /*= nullptr*/) const +{ + if (!spellInfo || castTime < 0) + return; + + // called from caster + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo, SpellModOp::ChangeCastTime, castTime, spell); + + Unit const* unitCaster = ToUnit(); + if (!unitCaster) + return; + + if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && + ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) + castTime = unitCaster->CanInstantCast() ? 0 : int32(float(castTime) * unitCaster->m_unitData->ModCastingSpeed); + else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) + castTime = int32(float(castTime) * unitCaster->m_modAttackSpeedPct[RANGED_ATTACK]); + else if (IsPartOfSkillLine(SKILL_COOKING, spellInfo->Id) && unitCaster->HasAura(67556)) // cooking with Chef Hat. + castTime = 500; +} + +void WorldObject::ModSpellDurationTime(SpellInfo const* spellInfo, int32& duration, Spell* spell /*= nullptr*/) const +{ + if (!spellInfo || duration < 0) + return; + + if (spellInfo->IsChanneled() && !spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) + return; + + // called from caster + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo, SpellModOp::ChangeCastTime, duration, spell); + + Unit const* unitCaster = ToUnit(); + if (!unitCaster) + return; + + if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && + ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) + duration = int32(float(duration) * unitCaster->m_unitData->ModCastingSpeed); + else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) + duration = int32(float(duration) * unitCaster->m_modAttackSpeedPct[RANGED_ATTACK]); +} + +float WorldObject::MeleeSpellMissChance(Unit const* /*victim*/, WeaponAttackType /*attType*/, SpellInfo const* /*spellInfo*/) const +{ + return 0.0f; +} + +SpellMissInfo WorldObject::MeleeSpellHitResult(Unit* /*victim*/, SpellInfo const* /*spellInfo*/) const +{ + return SPELL_MISS_NONE; +} + +SpellMissInfo WorldObject::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const +{ + // Can`t miss on dead target (on skinning for example) + if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) + return SPELL_MISS_NONE; + + SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); + // PvP - PvE spell misschances per leveldif > 2 + int32 lchance = victim->GetTypeId() == TYPEID_PLAYER ? 7 : 11; + int32 thisLevel = GetLevelForTarget(victim); + if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger()) + thisLevel = std::max<int32>(thisLevel, spellInfo->SpellLevel); + int32 leveldif = int32(victim->GetLevelForTarget(this)) - thisLevel; + int32 levelBasedHitDiff = leveldif; + + // Base hit chance from attacker and victim levels + int32 modHitChance = 100; + if (levelBasedHitDiff >= 0) + { + if (victim->GetTypeId() != TYPEID_PLAYER) + { + modHitChance = 94 - 3 * std::min(levelBasedHitDiff, 3); + levelBasedHitDiff -= 3; + } + else + { + modHitChance = 96 - std::min(levelBasedHitDiff, 2); + levelBasedHitDiff -= 2; + } + if (levelBasedHitDiff > 0) + modHitChance -= lchance * std::min(levelBasedHitDiff, 7); + } + else + modHitChance = 97 - levelBasedHitDiff; + + // Spellmod from SpellModOp::HitChance + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo, SpellModOp::HitChance, modHitChance); + + // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects + if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) + { + // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras + modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); + } + + int32 HitChance = modHitChance * 100; + // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings + if (Unit const* unit = ToUnit()) + HitChance += int32(unit->m_modSpellHitChance * 100.0f); + + RoundToInterval(HitChance, 0, 10000); + + int32 tmp = 10000 - HitChance; + + int32 rand = irand(0, 9999); + if (tmp > 0 && rand < tmp) + return SPELL_MISS_MISS; + + // Chance resist mechanic (select max value from every mechanic spell effect) + int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100; + + // Roll chance + if (resist_chance > 0 && rand < (tmp += resist_chance)) + return SPELL_MISS_RESIST; + + // cast by caster in front of victim + if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))) + { + int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100; + if (deflect_chance > 0 && rand < (tmp += deflect_chance)) + return SPELL_MISS_DEFLECT; + } + + return SPELL_MISS_NONE; +} + +// Calculate spell hit result can be: +// Every spell can: Evade/Immune/Reflect/Sucesful hit +// For melee based spells: +// Miss +// Dodge +// Parry +// For spells +// Resist +SpellMissInfo WorldObject::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect /*= false*/) const +{ + // All positive spells can`t miss + /// @todo client not show miss log for this spells - so need find info for this in dbc and use it! + if (spellInfo->IsPositive() && !IsHostileTo(victim)) // prevent from affecting enemy by "positive" spell + return SPELL_MISS_NONE; + + if (this == victim) + return SPELL_MISS_NONE; + + // Return evade for units in evade mode + if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()) + return SPELL_MISS_EVADE; + + // Try victim reflect spell + if (canReflect) + { + int32 reflectchance = victim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS); + reflectchance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_REFLECT_SPELLS_SCHOOL, spellInfo->GetSchoolMask()); + + if (reflectchance > 0 && roll_chance_i(reflectchance)) + return SPELL_MISS_REFLECT; + } + + if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) + return SPELL_MISS_NONE; + + // Check for immune + if (victim->IsImmunedToSpell(spellInfo, this)) + return SPELL_MISS_IMMUNE; + + // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply + // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit + if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(spellInfo)) + return SPELL_MISS_IMMUNE; + + switch (spellInfo->DmgClass) + { + case SPELL_DAMAGE_CLASS_RANGED: + case SPELL_DAMAGE_CLASS_MELEE: + return MeleeSpellHitResult(victim, spellInfo); + case SPELL_DAMAGE_CLASS_NONE: + return SPELL_MISS_NONE; + case SPELL_DAMAGE_CLASS_MAGIC: + return MagicSpellHitResult(victim, spellInfo); + } + return SPELL_MISS_NONE; +} + +FactionTemplateEntry const* WorldObject::GetFactionTemplateEntry() const +{ + FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(GetFaction()); + if (!entry) + { + if (Player const* player = ToPlayer()) + TC_LOG_ERROR("entities", "Player %s has invalid faction (faction template id) #%u", player->GetName().c_str(), GetFaction()); + else if (Creature const* creature = ToCreature()) + TC_LOG_ERROR("entities", "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, GetFaction()); + else if (GameObject const* go = ToGameObject()) + TC_LOG_ERROR("entities", "GameObject (template id: %u) has invalid faction (faction template id) #%u", go->GetGOInfo()->entry, GetFaction()); + else + TC_LOG_ERROR("entities", "WorldObject (name: %s, type: %u) has invalid faction (faction template id) #%u", GetName().c_str(), uint32(GetTypeId()), GetFaction()); + } + + return entry; +} + +// function based on function Unit::UnitReaction from 13850 client +ReputationRank WorldObject::GetReactionTo(WorldObject const* target) const +{ + // always friendly to self + if (this == target) + return REP_FRIENDLY; + + // always friendly to charmer or owner + if (GetCharmerOrOwnerOrSelf() == target->GetCharmerOrOwnerOrSelf()) + return REP_FRIENDLY; + + Player const* selfPlayerOwner = GetAffectingPlayer(); + Player const* targetPlayerOwner = target->GetAffectingPlayer(); + + // check forced reputation to support SPELL_AURA_FORCE_REACTION + if (selfPlayerOwner) + { + if (FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry()) + if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) + return *repRank; + } + else if (targetPlayerOwner) + { + if (FactionTemplateEntry const* selfFactionTemplateEntry = GetFactionTemplateEntry()) + if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(selfFactionTemplateEntry)) + return *repRank; + } + + Unit const* unit = ToUnit(); + Unit const* targetUnit = target->ToUnit(); + if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + { + if (targetUnit && targetUnit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + { + if (selfPlayerOwner && targetPlayerOwner) + { + // always friendly to other unit controlled by player, or to the player himself + if (selfPlayerOwner == targetPlayerOwner) + return REP_FRIENDLY; + + // duel - always hostile to opponent + if (selfPlayerOwner->duel && selfPlayerOwner->duel->opponent == targetPlayerOwner && selfPlayerOwner->duel->startTime != 0) + return REP_HOSTILE; + + // same group - checks dependant only on our faction - skip FFA_PVP for example + if (selfPlayerOwner->IsInRaidWith(targetPlayerOwner)) + return REP_FRIENDLY; // return true to allow config option AllowTwoSide.Interaction.Group to work + // however client seems to allow mixed group parties, because in 13850 client it works like: + // return GetFactionReactionTo(GetFactionTemplateEntry(), target); + } + + // check FFA_PVP + if (unit->IsFFAPvP() && targetUnit->IsFFAPvP()) + return REP_HOSTILE; + + if (selfPlayerOwner) + { + if (FactionTemplateEntry const* targetFactionTemplateEntry = targetUnit->GetFactionTemplateEntry()) + { + if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) + return *repRank; + if (!selfPlayerOwner->HasUnitFlag2(UNIT_FLAG2_IGNORE_REPUTATION)) + { + if (FactionEntry const* targetFactionEntry = sFactionStore.LookupEntry(targetFactionTemplateEntry->Faction)) + { + if (targetFactionEntry->CanHaveReputation()) + { + // check contested flags + if (targetFactionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD + && selfPlayerOwner->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP)) + return REP_HOSTILE; + + // if faction has reputation, hostile state depends only from AtWar state + if (selfPlayerOwner->GetReputationMgr().IsAtWar(targetFactionEntry)) + return REP_HOSTILE; + return REP_FRIENDLY; + } + } + } + } + } + } + } + + // do checks dependant only on our faction + return WorldObject::GetFactionReactionTo(GetFactionTemplateEntry(), target); +} + +/*static*/ ReputationRank WorldObject::GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, WorldObject const* target) +{ + // always neutral when no template entry found + if (!factionTemplateEntry) + return REP_NEUTRAL; + + FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry(); + if (!targetFactionTemplateEntry) + return REP_NEUTRAL; + + if (Player const* targetPlayerOwner = target->GetAffectingPlayer()) + { + // check contested flags + if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD + && targetPlayerOwner->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP)) + return REP_HOSTILE; + if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry)) + return *repRank; + if (target->IsUnit() && !target->ToUnit()->HasUnitFlag2(UNIT_FLAG2_IGNORE_REPUTATION)) + { + if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->Faction)) + { + if (factionEntry->CanHaveReputation()) + { + // CvP case - check reputation, don't allow state higher than neutral when at war + ReputationRank repRank = targetPlayerOwner->GetReputationMgr().GetRank(factionEntry); + if (targetPlayerOwner->GetReputationMgr().IsAtWar(factionEntry)) + repRank = std::min(REP_NEUTRAL, repRank); + return repRank; + } + } + } + } + + // common faction based check + if (factionTemplateEntry->IsHostileTo(targetFactionTemplateEntry)) + return REP_HOSTILE; + if (factionTemplateEntry->IsFriendlyTo(targetFactionTemplateEntry)) + return REP_FRIENDLY; + if (targetFactionTemplateEntry->IsFriendlyTo(factionTemplateEntry)) + return REP_FRIENDLY; + if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_HOSTILE_BY_DEFAULT) + return REP_HOSTILE; + // neutral by default + return REP_NEUTRAL; +} + +bool WorldObject::IsHostileTo(WorldObject const* target) const +{ + return GetReactionTo(target) <= REP_HOSTILE; +} + +bool WorldObject::IsFriendlyTo(WorldObject const* target) const +{ + return GetReactionTo(target) >= REP_FRIENDLY; +} + +bool WorldObject::IsHostileToPlayers() const +{ + FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); + if (!my_faction->Faction) + return false; + + FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction); + if (raw_faction && raw_faction->ReputationIndex >= 0) + return false; + + return my_faction->IsHostileToPlayers(); +} + +bool WorldObject::IsNeutralToAll() const +{ + FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); + if (!my_faction->Faction) + return true; + + FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction); + if (raw_faction && raw_faction->ReputationIndex >= 0) + return false; + + return my_faction->IsNeutralToAll(); +} + +void WorldObject::CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/) +{ + SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId, args.CastDifficulty != DIFFICULTY_NONE ? args.CastDifficulty : GetMap()->GetDifficultyID()); + if (!info) + { + TC_LOG_ERROR("entities.unit", "CastSpell: unknown spell %u by caster %s", spellId, GetGUID().ToString().c_str()); + return; + } + + Spell* spell = new Spell(this, info, args.TriggerFlags, args.OriginalCaster); + for (auto const& pair : args.SpellValueOverrides) + spell->SetSpellValue(pair.first, pair.second); + + spell->m_CastItem = args.CastItem; + spell->prepare(targets, args.TriggeringAura); +} + +void WorldObject::CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/) +{ + SpellCastTargets targets; + if (target) + { + if (Unit* unitTarget = target->ToUnit()) + targets.SetUnitTarget(unitTarget); + else if (GameObject* goTarget = target->ToGameObject()) + targets.SetGOTarget(goTarget); + else + { + TC_LOG_ERROR("entities.unit", "CastSpell: Invalid target %s passed to spell cast by %s", target->GetGUID().ToString().c_str(), GetGUID().ToString().c_str()); + return; + } + } + CastSpell(targets, spellId, args); +} + +void WorldObject::CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/) +{ + SpellCastTargets targets; + targets.SetDst(dest); + CastSpell(targets, spellId, args); +} + +// function based on function Unit::CanAttack from 13850 client +bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const +{ + ASSERT(target); + + // can't attack self + if (this == target) + return false; + + // can't attack GMs + if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) + return false; + + Unit const* unit = ToUnit(); + Unit const* targetUnit = target->ToUnit(); + + // CvC case - can attack each other only when one of them is hostile + if (unit && !unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && targetUnit && !targetUnit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + return IsHostileTo(target) || target->IsHostileTo(this); + + // PvP, PvC, CvP case + // can't attack friendly targets + if (IsFriendlyTo(target) || target->IsFriendlyTo(this)) + return false; + + Player const* playerAffectingAttacker = unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; + Player const* playerAffectingTarget = targetUnit && targetUnit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr; + + // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) + if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget)) + { + Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget; + + if (Unit const* creature = playerAffectingAttacker ? targetUnit : unit) + { + if (creature->IsContestedGuard() && player->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP)) + return true; + + if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry()) + { + if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate))) + if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->Faction)) + if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry)) + if (!repState->Flags.HasFlag(ReputationFlags::AtWar)) + return false; + + } + } + } + + Creature const* creatureAttacker = ToCreature(); + if (creatureAttacker && (creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) + return false; + + if (!bySpell) + spellCheck = false; + + if (spellCheck && !IsValidSpellAttackTarget(target, bySpell)) + return false; + + return true; +} + +bool WorldObject::IsValidSpellAttackTarget(WorldObject const* target, SpellInfo const* bySpell) const +{ + ASSERT(target); + ASSERT(bySpell); + + // can't attack unattackable units + Unit const* unitTarget = target->ToUnit(); + if (unitTarget && unitTarget->HasUnitState(UNIT_STATE_UNATTACKABLE)) + return false; + + Unit const* unit = ToUnit(); + // visibility checks (only units) + if (unit) + { + // can't attack invisible + if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) + { + if (!unit->CanSeeOrDetect(target, bySpell->IsAffectingArea())) + return false; + + /* + else if (!obj) + { + // ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player + bool const ignoreStealthCheck = (bySpell && bySpell->IsAffectingArea()) || + (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && IsInCombatWith(target)); + + if (!CanSeeOrDetect(target, ignoreStealthCheck)) + return false; + } + */ + } + } + + // can't attack dead + if (!bySpell->IsAllowingDeadTarget() && unitTarget && !unitTarget->IsAlive()) + return false; + + // can't attack untargetable + if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return false; + + if (Player const* playerAttacker = ToPlayer()) + { + if (playerAttacker->HasPlayerFlag(PLAYER_FLAGS_UBER)) + return false; + } + + // check flags + if (unitTarget && unitTarget->HasUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16))) + return false; + + if (unit && !unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToNPC()) + return false; + + if (unitTarget && !unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToNPC()) + return false; + + if (!bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG)) + { + if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToPC()) + return false; + + if (unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToPC()) + return false; + } + + // check duel - before sanctuary checks + Player const* playerAffectingAttacker = unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; + Player const* playerAffectingTarget = unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr; + if (playerAffectingAttacker && playerAffectingTarget) + if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0) + return true; + + // PvP case - can't attack when attacker or target are in sanctuary + // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp + if (unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && (unitTarget->IsInSanctuary() || unit->IsInSanctuary())) + return false; + + // additional checks - only PvP case + if (playerAffectingAttacker && playerAffectingTarget) + { + if (unitTarget->IsPvP()) + return true; + + if (unit->IsFFAPvP() && unitTarget->IsFFAPvP()) + return true; + + return unit->HasPvpFlag(UNIT_BYTE2_FLAG_UNK1) || + unitTarget->HasPvpFlag(UNIT_BYTE2_FLAG_UNK1); + } + + return true; +} + +// function based on function Unit::CanAssist from 13850 client +bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const +{ + ASSERT(target); + + // can assist to self + if (this == target) + return true; + + // can't assist GMs + if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) + return false; + + // can't assist non-friendly targets + if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))) + return false; + + if (!bySpell) + spellCheck = false; + + if (spellCheck && !IsValidSpellAssistTarget(target, bySpell)) + return false; + + return true; +} + +bool WorldObject::IsValidSpellAssistTarget(WorldObject const* target, SpellInfo const* bySpell) const +{ + ASSERT(target); + ASSERT(bySpell); + + // can't assist unattackable units + Unit const* unitTarget = target->ToUnit(); + if (unitTarget && unitTarget->HasUnitState(UNIT_STATE_UNATTACKABLE)) + return false; + + // can't assist own vehicle or passenger + Unit const* unit = ToUnit(); + if (unit && unitTarget && unit->GetVehicle()) + { + if (unit->IsOnVehicle(unitTarget)) + return false; + + if (unit->GetVehicleBase()->IsOnVehicle(unitTarget)) + return false; + } + + // can't assist invisible + if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE) && !CanSeeOrDetect(target, bySpell->IsAffectingArea())) + return false; + + // can't assist dead + if (!bySpell->IsAllowingDeadTarget() && unitTarget && !unitTarget->IsAlive()) + return false; + + // can't assist untargetable + if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return false; + + if (!bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) + { + if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + { + if (unitTarget && unitTarget->IsImmuneToPC()) + return false; + } + else + { + if (unitTarget && unitTarget->IsImmuneToNPC()) + return false; + } + } + + // PvP case + if (unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + { + Player const* targetPlayerOwner = target->GetAffectingPlayer(); + if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + { + Player const* selfPlayerOwner = GetAffectingPlayer(); + if (selfPlayerOwner && targetPlayerOwner) + { + // can't assist player which is dueling someone + if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner->duel) + return false; + } + // can't assist player in ffa_pvp zone from outside + if (unitTarget->IsFFAPvP() && unit && !unit->IsFFAPvP()) + return false; + + // can't assist player out of sanctuary from sanctuary if has pvp enabled + if (unitTarget->IsPvP()) + if (unit && unit->IsInSanctuary() && !unitTarget->IsInSanctuary()) + return false; + } + } + // PvC case - player can assist creature only if has specific type flags + // !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && + else if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + { + if (!bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) + if (unitTarget && !unitTarget->IsPvP()) + if (Creature const* creatureTarget = target->ToCreature()) + return ((creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)); + } + + return true; +} + +Unit* WorldObject::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo) +{ + // Patch 1.2 notes: Spell Reflection no longer reflects abilities + if (spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REDIRECTED) || spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) + return victim; + + Unit::AuraEffectList const& magnetAuras = victim->GetAuraEffectsByType(SPELL_AURA_SPELL_MAGNET); + for (AuraEffect const* aurEff : magnetAuras) + { + if (Unit* magnet = aurEff->GetBase()->GetCaster()) + { + if (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK && IsValidAttackTarget(magnet, spellInfo)) + { + /// @todo handle this charge drop by proc in cast phase on explicit target + if (spellInfo->HasHitDelay()) + { + // Set up missile speed based delay + float hitDelay = spellInfo->LaunchDelay; + if (spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION)) + hitDelay += spellInfo->Speed; + else if (spellInfo->Speed > 0.0f) + hitDelay += std::max(victim->GetDistance(this), 5.0f) / spellInfo->Speed; + + uint32 delay = uint32(std::floor(hitDelay * 1000.0f)); + // Schedule charge drop + aurEff->GetBase()->DropChargeDelayed(delay, AURA_REMOVE_BY_EXPIRE); + } + else + aurEff->GetBase()->DropCharge(AURA_REMOVE_BY_EXPIRE); + + return magnet; + } + } + } + return victim; +} + +uint32 WorldObject::GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const +{ + return spellInfo->GetSpellXSpellVisualId(this); +} + template <typename Container> void WorldObject::GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange /*= 250.0f*/) const { diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 5f46af426d8..4e0d9005b07 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -20,6 +20,7 @@ #include "Common.h" #include "Duration.h" +#include "EventProcessor.h" #include "GridReference.h" #include "GridRefManager.h" #include "ModelIgnoreFlags.h" @@ -47,6 +48,9 @@ class Map; class Object; class Player; class Scenario; +class Spell; +class SpellCastTargets; +class SpellInfo; class TempSummon; class Transport; class Unit; @@ -54,9 +58,18 @@ class UpdateData; class WorldObject; class WorldPacket; class ZoneScript; +struct FactionTemplateEntry; struct PositionFullTerrainStatus; struct QuaternionData; +namespace WorldPackets +{ + namespace CombatLog + { + class CombatLogServerPacket; + } +} + typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType; struct CreateObjectBits @@ -400,7 +413,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation public: virtual ~WorldObject(); - virtual void Update (uint32 /*time_diff*/) { } + virtual void Update(uint32 /*time_diff*/) { } void AddToWorld() override; void RemoveFromWorld() override; @@ -491,6 +504,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation virtual void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self) const; virtual void SendMessageToSet(WorldPacket const* data, Player const* skipped_rcvr) const; + void SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const; + virtual uint8 GetLevelForTarget(WorldObject const* /*target*/) const { return 1; } void PlayDistanceSound(uint32 soundId, Player* target = nullptr); @@ -538,6 +553,61 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const; Player* SelectNearestPlayer(float distance) const; + virtual ObjectGuid GetOwnerGUID() const = 0; + virtual ObjectGuid GetCharmerOrOwnerGUID() const { return GetOwnerGUID(); } + ObjectGuid GetCharmerOrOwnerOrOwnGUID() const; + + Unit* GetOwner() const; + Unit* GetCharmerOrOwner() const; + Unit* GetCharmerOrOwnerOrSelf() const; + Player* GetCharmerOrOwnerPlayerOrPlayerItself() const; + Player* GetAffectingPlayer() const; + + Player* GetSpellModOwner() const; + int32 CalculateSpellDamage(Unit const* target, SpellInfo const* spellInfo, uint8 effIndex, int32 const* basePoints = nullptr, float* variance = nullptr, uint32 castItemId = 0, int32 itemLevel = -1) const; + + // target dependent range checks + float GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const; + float GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const; + + float ApplyEffectModifiers(SpellInfo const* spellInfo, uint8 effIndex, float value) const; + int32 CalcSpellDuration(SpellInfo const* spellInfo) const; + int32 ModSpellDuration(SpellInfo const* spellInfo, WorldObject const* target, int32 duration, bool positive, uint32 effectMask) const; + void ModSpellCastTime(SpellInfo const* spellInfo, int32& castTime, Spell* spell = nullptr) const; + void ModSpellDurationTime(SpellInfo const* spellInfo, int32& durationTime, Spell* spell = nullptr) const; + + virtual float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const; + virtual SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; + SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; + SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false) const; + + virtual uint32 GetFaction() const = 0; + virtual void SetFaction(uint32 /*faction*/) { } + FactionTemplateEntry const* GetFactionTemplateEntry() const; + + ReputationRank GetReactionTo(WorldObject const* target) const; + static ReputationRank GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, WorldObject const* target); + + bool IsHostileTo(WorldObject const* target) const; + bool IsHostileToPlayers() const; + bool IsFriendlyTo(WorldObject const* target) const; + bool IsNeutralToAll() const; + + // CastSpell's third arg can be a variety of things - check out CastSpellExtraArgs' constructors! + void CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args = { }); + void CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args = { }); + void CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args = { }); + + bool IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const; + bool IsValidSpellAttackTarget(WorldObject const* target, SpellInfo const* bySpell) const; + + bool IsValidAssistTarget(WorldObject const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const; + bool IsValidSpellAssistTarget(WorldObject const* target, SpellInfo const* bySpell) const; + + Unit* GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo); + + virtual uint32 GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const; + template <typename Container> void GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange = 250.0f) const; @@ -599,6 +669,9 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation float GetMapWaterOrGroundLevel(float x, float y, float z, float* ground = nullptr) const; float GetMapHeight(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f) const; // DEFAULT_HEIGHT_SEARCH in map.h + // Event handler + EventProcessor m_Events; + virtual uint16 GetAIAnimKitId() const { return 0; } virtual uint16 GetMovementAnimKitId() const { return 0; } virtual uint16 GetMeleeAnimKitId() const { return 0; } @@ -614,7 +687,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation bool m_isActive; bool m_isFarVisible; Optional<float> m_visibilityDistanceOverride; - const bool m_isWorldObject; + bool const m_isWorldObject; ZoneScript* m_zoneScript; // transports @@ -640,7 +713,6 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation private: Map* m_currMap; // current object's Map location - //uint32 m_mapId; // object at map with map_id uint32 m_InstanceId; // in map copy with instance id PhaseShift _phaseShift; PhaseShift _suppressedPhaseShift; // contains phases for current area but not applied due to conditions diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 62bcd92b3d3..166b650f7e9 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1663,7 +1663,7 @@ void Player::SetObjectScale(float scale) SendMovementSetCollisionHeight(scale * GetCollisionHeight(), WorldPackets::Movement::UpdateCollisionHeightReason::Scale); } -bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const +bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const { SpellEffectInfo const* effect = spellInfo->GetEffect(index); if (!effect || !effect->IsEffect()) @@ -23424,7 +23424,7 @@ void Player::UpdatePotionCooldown(Spell* spell) m_lastPotionId = 0; } -void Player::SetResurrectRequestData(Unit* caster, uint32 health, uint32 mana, uint32 appliedAura) +void Player::SetResurrectRequestData(WorldObject const* caster, uint32 health, uint32 mana, uint32 appliedAura) { ASSERT(!IsResurrectRequested()); _resurrectionData.reset(new ResurrectionData()); @@ -24082,45 +24082,6 @@ Player* Player::GetSelectedPlayer() const return nullptr; } -void Player::AddComboPoints(int8 count, Spell* spell) -{ - if (!count) - return; - - int8 comboPoints = spell ? spell->m_comboPointGain : GetPower(POWER_COMBO_POINTS); - - comboPoints += count; - - if (comboPoints > 5) - comboPoints = 5; - else if (comboPoints < 0) - comboPoints = 0; - - if (!spell) - SetPower(POWER_COMBO_POINTS, comboPoints); - else - spell->m_comboPointGain = comboPoints; -} - -void Player::GainSpellComboPoints(int8 count) -{ - if (!count) - return; - - int8 cp = GetPower(POWER_COMBO_POINTS); - - cp += count; - if (cp > 5) cp = 5; - else if (cp < 0) cp = 0; - - SetPower(POWER_COMBO_POINTS, cp); -} - -void Player::ClearComboPoints() -{ - SetPower(POWER_COMBO_POINTS, 0); -} - bool Player::IsInGroup(ObjectGuid groupGuid) const { if (Group const* group = GetGroup()) @@ -26626,10 +26587,10 @@ void Player::ResetCriteria(CriteriaFailEvent condition, int32 failAsset, bool ev m_questObjectiveCriteriaMgr->ResetCriteria(condition, failAsset, evenIfCriteriaComplete); } -void Player::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, Unit* unit /*= nullptr*/) +void Player::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, WorldObject* ref /*= nullptr*/) { - m_achievementMgr->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); - m_questObjectiveCriteriaMgr->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); + m_achievementMgr->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, this); + m_questObjectiveCriteriaMgr->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, this); // Update only individual achievement criteria here, otherwise we may get multiple updates // from a single boss kill @@ -26637,10 +26598,10 @@ void Player::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0*/, uint6 return; if (Scenario* scenario = GetScenario()) - scenario->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); + scenario->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, this); if (Guild* guild = sGuildMgr->GetGuildById(GetGuildId())) - guild->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); + guild->UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, this); } void Player::CompletedAchievement(AchievementEntry const* entry) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index ed747c885ab..0fd50b7b5f9 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1085,7 +1085,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void Update(uint32 time) override; - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override; void SetInWater(bool inWater) override; @@ -1664,11 +1664,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SetTarget(ObjectGuid const& /*guid*/) override { } /// Used for serverside target changes, does not apply to players void SetSelection(ObjectGuid const& guid) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Target), guid); } - uint32 GetComboPoints() const { return uint32(GetPower(POWER_COMBO_POINTS)); } - void AddComboPoints(int8 count, Spell* spell = nullptr); - void GainSpellComboPoints(int8 count); - void ClearComboPoints(); - void SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError = 0, ObjectGuid::LowType item_guid = UI64LIT(0), uint32 item_count = 0) const; void SendNewMail() const; void UpdateNextMailTimeAndUnreads(); @@ -1816,7 +1811,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; } void UpdatePotionCooldown(Spell* spell = nullptr); - void SetResurrectRequestData(Unit* caster, uint32 health, uint32 mana, uint32 appliedAura); + void SetResurrectRequestData(WorldObject const* caster, uint32 health, uint32 mana, uint32 appliedAura); void ClearResurrectRequestData() { _resurrectionData.reset(); @@ -2499,7 +2494,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool HasAchieved(uint32 achievementId) const; void ResetAchievements(); void ResetCriteria(CriteriaFailEvent condition, int32 failAsset, bool evenIfCriteriaComplete = false); - void UpdateCriteria(CriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, Unit* unit = nullptr); + void UpdateCriteria(CriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, WorldObject* ref = nullptr); void StartCriteriaTimer(CriteriaStartEvent startEvent, uint32 entry, uint32 timeLost = 0); void RemoveCriteriaTimer(CriteriaStartEvent startEvent, uint32 entry); void CompletedAchievement(AchievementEntry const* entry); diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index b5822d11f8a..38f30551a63 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -137,7 +137,7 @@ void Totem::UnSummon(uint32 msTime) AddObjectToRemoveList(); } -bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const +bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const { /// @todo possibly all negative auras immune? if (GetEntry() == 5925) diff --git a/src/server/game/Entities/Totem/Totem.h b/src/server/game/Entities/Totem/Totem.h index 92f29789384..54fc53a09d0 100644 --- a/src/server/game/Entities/Totem/Totem.h +++ b/src/server/game/Entities/Totem/Totem.h @@ -50,7 +50,7 @@ class TC_GAME_API Totem : public Minion void UpdateAttackPowerAndDamage(bool /*ranged*/) override { } void UpdateDamagePhysical(WeaponAttackType /*attType*/) override { } - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override; protected: TotemType m_type; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index b75fc274072..6fa98f4bb55 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -940,50 +940,46 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons } } - if (damagetype != NODAMAGE) + if (damagetype != NODAMAGE && damagetype != DOT) { - if (victim != attacker - && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC)))) + if (victim != attacker && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC)))) { - if (damagetype != DOT) + if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) { - if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) + if (spell->getState() == SPELL_STATE_PREPARING) { - if (spell->getState() == SPELL_STATE_PREPARING) + auto isCastInterrupted = [&]() { - auto isCastInterrupted = [&]() - { - if (!damage) - return spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::ZeroDamageCancels); + if (!damage) + return spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::ZeroDamageCancels); - if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly))) - return true; + if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly))) + return true; - if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancels)) - return true; + if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancels)) + return true; - return false; - }; + return false; + }; - auto isCastDelayed = [&]() - { - if (!damage) - return false; + auto isCastDelayed = [&]() + { + if (!damage) + return false; - if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushbackPlayerOnly))) - return true; + if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushbackPlayerOnly))) + return true; - if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushback)) - return true; + if (spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushback)) + return true; - return false; - }; + return false; + }; - if (isCastInterrupted()) - victim->InterruptNonMeleeSpells(false); - else if (isCastDelayed()) - spell->Delayed(); - } + if (isCastInterrupted()) + victim->InterruptNonMeleeSpells(false); + else if (isCastDelayed()) + spell->Delayed(); } if (damage && victim->IsPlayer()) @@ -1024,48 +1020,6 @@ void Unit::CastStop(uint32 except_spellid) InterruptSpell(CurrentSpellTypes(i), false); } -void Unit::CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args) -{ - SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId, args.CastDifficulty != DIFFICULTY_NONE ? args.CastDifficulty : GetMap()->GetDifficultyID()); - if (!info) - { - TC_LOG_ERROR("entities.unit", "CastSpell: unknown spell %u by caster %s", spellId, GetGUID().ToString().c_str()); - return; - } - - Spell* spell = new Spell(this, info, args.TriggerFlags, args.OriginalCaster); - for (auto const& pair : args.SpellValueOverrides) - spell->SetSpellValue(pair.first, pair.second); - - spell->m_CastItem = args.CastItem; - spell->prepare(targets, args.TriggeringAura); -} - -void Unit::CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args) -{ - SpellCastTargets targets; - if (target) - { - if (Unit* unitTarget = target->ToUnit()) - targets.SetUnitTarget(unitTarget); - else if (GameObject* goTarget = target->ToGameObject()) - targets.SetGOTarget(goTarget); - else - { - TC_LOG_ERROR("entities.unit", "CastSpell: Invalid target %s passed to spell cast by %s", target->GetGUID().ToString().c_str(), GetGUID().ToString().c_str()); - return; - } - } - CastSpell(targets, spellId, args); -} - -void Unit::CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args) -{ - SpellCastTargets targets; - targets.SetDst(dest); - CastSpell(targets, spellId, args); -} - void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType, bool crit) { if (damage < 0) @@ -1720,21 +1674,19 @@ void Unit::HandleEmoteCommand(uint32 anim_id, Trinity::IteratorPair<int32 const* return uint32(damageResisted); } -/*static*/ float Unit::CalculateAverageResistReduction(Unit const* attacker, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo /*= nullptr*/) +/*static*/ float Unit::CalculateAverageResistReduction(WorldObject const* caster, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo /*= nullptr*/) { float victimResistance = float(victim->GetResistance(schoolMask)); - if (attacker) + if (caster) { // pets inherit 100% of masters penetration - // excluding traps - Player const* player = attacker->GetSpellModOwner(); - if (player && attacker->GetEntry() != WORLD_TRIGGER) + if (Player const* player = caster->GetSpellModOwner()) { victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); victimResistance -= float(player->GetSpellPenetrationItemMod()); } - else - victimResistance += float(attacker->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); + else if (Unit const* unitCaster = caster->ToUnit()) + victimResistance += float(unitCaster->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); } // holy resistance exists in pve and comes from level difference, ignore template values @@ -1748,12 +1700,13 @@ void Unit::HandleEmoteCommand(uint32 anim_id, Trinity::IteratorPair<int32 const* victimResistance = std::max(victimResistance, 0.0f); // level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration - if (attacker && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL))) - victimResistance += std::max((float(victim->GetLevelForTarget(attacker)) - float(attacker->GetLevelForTarget(victim))) * 5.0f, 0.0f); + // gameobject caster -- should it have level based resistance? + if (caster && caster->GetTypeId() != TYPEID_GAMEOBJECT && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL))) + victimResistance += std::max((float(victim->GetLevelForTarget(caster)) - float(caster->GetLevelForTarget(victim))) * 5.0f, 0.0f); static uint32 const bossLevel = 83; static float const bossResistanceConstant = 510.0f; - uint32 level = attacker ? victim->GetLevelForTarget(attacker) : victim->getLevel(); + uint32 level = caster ? victim->GetLevelForTarget(caster) : victim->getLevel(); float resistanceConstant = 0.0f; if (level == bossLevel) @@ -2555,139 +2508,6 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo return SPELL_MISS_NONE; } -SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const -{ - // Can`t miss on dead target (on skinning for example) - if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) - return SPELL_MISS_NONE; - - SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); - // PvP - PvE spell misschances per leveldif > 2 - int32 lchance = victim->GetTypeId() == TYPEID_PLAYER ? 7 : 11; - int32 thisLevel = GetLevelForTarget(victim); - if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger()) - thisLevel = std::max<int32>(thisLevel, spellInfo->SpellLevel); - int32 leveldif = int32(victim->GetLevelForTarget(this)) - thisLevel; - int32 levelBasedHitDiff = leveldif; - - // Base hit chance from attacker and victim levels - int32 modHitChance = 100; - if (levelBasedHitDiff >= 0) - { - if (victim->GetTypeId() != TYPEID_PLAYER) - { - modHitChance = 94 - 3 * std::min(levelBasedHitDiff, 3); - levelBasedHitDiff -= 3; - } - else - { - modHitChance = 96 - std::min(levelBasedHitDiff, 2); - levelBasedHitDiff -= 2; - } - if (levelBasedHitDiff > 0) - modHitChance -= lchance * std::min(levelBasedHitDiff, 7); - } - else - modHitChance = 97 - levelBasedHitDiff; - - // Spellmod from SpellModOp::HitChance - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo, SpellModOp::HitChance, modHitChance); - - // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects - if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) - { - // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras - modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); - } - - int32 HitChance = modHitChance * 100; - // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings - HitChance += int32(m_modSpellHitChance * 100.0f); - - RoundToInterval(HitChance, 0, 10000); - - int32 tmp = 10000 - HitChance; - - int32 rand = irand(0, 9999); - if (tmp > 0 && rand < tmp) - return SPELL_MISS_MISS; - - // Chance resist mechanic (select max value from every mechanic spell effect) - int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100; - - // Roll chance - if (resist_chance > 0 && rand < (tmp += resist_chance)) - return SPELL_MISS_RESIST; - - // cast by caster in front of victim - if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))) - { - int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100; - if (deflect_chance > 0 && rand < (tmp += deflect_chance)) - return SPELL_MISS_DEFLECT; - } - - return SPELL_MISS_NONE; -} - -// Calculate spell hit result can be: -// Every spell can: Evade/Immune/Reflect/Sucesful hit -// For melee based spells: -// Miss -// Dodge -// Parry -// For spells -// Resist -SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect /*= false*/) -{ - // All positive spells can`t miss - /// @todo client not show miss log for this spells - so need find info for this in dbc and use it! - if (spellInfo->IsPositive() && !IsHostileTo(victim)) // prevent from affecting enemy by "positive" spell - return SPELL_MISS_NONE; - - if (this == victim) - return SPELL_MISS_NONE; - - // Return evade for units in evade mode - if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()) - return SPELL_MISS_EVADE; - - // Try victim reflect spell - if (canReflect) - { - int32 reflectchance = victim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS); - reflectchance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_REFLECT_SPELLS_SCHOOL, spellInfo->GetSchoolMask()); - - if (reflectchance > 0 && roll_chance_i(reflectchance)) - return SPELL_MISS_REFLECT; - } - - if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) - return SPELL_MISS_NONE; - - // Check for immune - if (victim->IsImmunedToSpell(spellInfo, this)) - return SPELL_MISS_IMMUNE; - - // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply - // returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit - if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(spellInfo)) - return SPELL_MISS_IMMUNE; - - switch (spellInfo->DmgClass) - { - case SPELL_DAMAGE_CLASS_RANGED: - case SPELL_DAMAGE_CLASS_MELEE: - return MeleeSpellHitResult(victim, spellInfo); - case SPELL_DAMAGE_CLASS_NONE: - return SPELL_MISS_NONE; - case SPELL_DAMAGE_CLASS_MAGIC: - return MagicSpellHitResult(victim, spellInfo); - } - return SPELL_MISS_NONE; -} - float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const { int32 const levelDiff = victim->GetLevelForTarget(this) - GetLevelForTarget(victim); @@ -3263,6 +3083,14 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(AuraCreateInfo& createInfo) if (!createInfo.CasterGUID && !createInfo.GetSpellInfo()->IsStackableOnOneSlotWithDifferentCasters()) createInfo.CasterGUID = createInfo.Caster->GetGUID(); + // world gameobjects can't own auras and they send empty casterguid + // checked on sniffs with spell 22247 + if (createInfo.CasterGUID.IsGameObject()) + { + createInfo.Caster = nullptr; + createInfo.CasterGUID.Clear(); + } + // passive and Incanter's Absorption and auras with different type can stack with themselves any number of times if (!createInfo.GetSpellInfo()->IsMultiSlotAura()) { @@ -3863,7 +3691,7 @@ void Unit::RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID, AuraRemove } } -void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, Unit* dispeller, uint8 chargesRemoved/*= 1*/) +void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, WorldObject* dispeller, uint8 chargesRemoved /*= 1*/) { AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId); for (AuraMap::iterator iter = range.first; iter != range.second;) @@ -3891,7 +3719,7 @@ void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId } } -void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, Unit* stealer) +void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, WorldObject* stealer) { AuraMapBoundsNonConst range = m_ownedAuras.equal_range(spellId); for (AuraMap::iterator iter = range.first; iter != range.second;) @@ -3925,38 +3753,41 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U // Cast duration to unsigned to prevent permanent aura's such as Righteous Fury being permanently added to caster uint32 dur = std::min(2u * MINUTE * IN_MILLISECONDS, uint32(aura->GetDuration())); - if (Aura* oldAura = stealer->GetAura(aura->GetId(), aura->GetCasterGUID())) + if (Unit* unitStealer = stealer->ToUnit()) { - if (stealCharge) - oldAura->ModCharges(1); + if (Aura* oldAura = unitStealer->GetAura(aura->GetId(), aura->GetCasterGUID())) + { + if (stealCharge) + oldAura->ModCharges(1); + else + oldAura->ModStackAmount(1); + oldAura->SetDuration(int32(dur)); + } else - oldAura->ModStackAmount(1); - oldAura->SetDuration(int32(dur)); - } - else - { - // single target state must be removed before aura creation to preserve existing single target aura - if (aura->IsSingleTarget()) - aura->UnregisterSingleTarget(); + { + // single target state must be removed before aura creation to preserve existing single target aura + if (aura->IsSingleTarget()) + aura->UnregisterSingleTarget(); - AuraCreateInfo createInfo(aura->GetCastGUID(), aura->GetSpellInfo(), aura->GetCastDifficulty(), effMask, stealer); - createInfo - .SetCasterGUID(aura->GetCasterGUID()) - .SetBaseAmount(baseDamage); + AuraCreateInfo createInfo(aura->GetCastGUID(), aura->GetSpellInfo(), aura->GetCastDifficulty(), effMask, unitStealer); + createInfo + .SetCasterGUID(aura->GetCasterGUID()) + .SetBaseAmount(baseDamage); - if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo)) - { - // created aura must not be single target aura,, so stealer won't loose it on recast - if (newAura->IsSingleTarget()) + if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo)) { - newAura->UnregisterSingleTarget(); - // bring back single target aura status to the old aura - aura->SetIsSingleTarget(true); - caster->GetSingleCastAuras().push_back(aura); + // created aura must not be single target aura,, so stealer won't loose it on recast + if (newAura->IsSingleTarget()) + { + newAura->UnregisterSingleTarget(); + // bring back single target aura status to the old aura + aura->SetIsSingleTarget(true); + caster->GetSingleCastAuras().push_back(aura); + } + // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate + newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]); + newAura->ApplyForTargets(); } - // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate - newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]); - newAura->ApplyForTargets(); } } @@ -4380,7 +4211,6 @@ void Unit::DelayOwnedAuras(uint32 spellId, ObjectGuid caster, int32 delaytime) // update for out of range group members (on 1 slot use) aura->SetNeedClientUpdateForTargets(); - TC_LOG_DEBUG("spells", "Aura %u partially interrupted on %s, new duration: %u ms", aura->GetId(), GetGUID().ToString().c_str(), aura->GetDuration()); } } } @@ -4529,7 +4359,7 @@ Aura* Unit::GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID, ObjectGu return aurApp ? aurApp->GetBase() : nullptr; } -void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect /*= false*/) const +void Unit::GetDispellableAuraList(WorldObject const* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect /*= false*/) const { AuraMap const& auras = GetOwnedAuras(); for (auto itr = auras.begin(); itr != auras.end(); ++itr) @@ -5555,187 +5385,6 @@ void Unit::SetSheath(SheathState sheathed) RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Sheathing); } -FactionTemplateEntry const* Unit::GetFactionTemplateEntry() const -{ - FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(GetFaction()); - if (!entry) - { - if (Player const* player = ToPlayer()) - TC_LOG_ERROR("entities.unit", "Player %s has invalid faction (faction template id) #%u", player->GetName().c_str(), GetFaction()); - else if (Creature const* creature = ToCreature()) - TC_LOG_ERROR("entities.unit", "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, GetFaction()); - else - TC_LOG_ERROR("entities.unit", "Unit (name=%s, type=%u) has invalid faction (faction template id) #%u", GetName().c_str(), uint32(GetTypeId()), GetFaction()); - - ABORT(); - } - return entry; -} - -// function based on function Unit::UnitReaction from 13850 client -ReputationRank Unit::GetReactionTo(Unit const* target) const -{ - // always friendly to self - if (this == target) - return REP_FRIENDLY; - - // always friendly to charmer or owner - if (GetCharmerOrOwnerOrSelf() == target->GetCharmerOrOwnerOrSelf()) - return REP_FRIENDLY; - - Player const* selfPlayerOwner = GetAffectingPlayer(); - Player const* targetPlayerOwner = target->GetAffectingPlayer(); - - // check forced reputation to support SPELL_AURA_FORCE_REACTION - if (selfPlayerOwner) - { - if (FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry()) - if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) - return *repRank; - } - else if (targetPlayerOwner) - { - if (FactionTemplateEntry const* selfFactionTemplateEntry = GetFactionTemplateEntry()) - if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(selfFactionTemplateEntry)) - return *repRank; - } - - if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - { - if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - { - if (selfPlayerOwner && targetPlayerOwner) - { - // always friendly to other unit controlled by player, or to the player himself - if (selfPlayerOwner == targetPlayerOwner) - return REP_FRIENDLY; - - // duel - always hostile to opponent - if (selfPlayerOwner->duel && selfPlayerOwner->duel->opponent == targetPlayerOwner && selfPlayerOwner->duel->startTime != 0) - return REP_HOSTILE; - - // same group - checks dependant only on our faction - skip FFA_PVP for example - if (selfPlayerOwner->IsInRaidWith(targetPlayerOwner)) - return REP_FRIENDLY; // return true to allow config option AllowTwoSide.Interaction.Group to work - // however client seems to allow mixed group parties, because in 13850 client it works like: - // return GetFactionReactionTo(GetFactionTemplateEntry(), target); - } - - // check FFA_PVP - if (IsFFAPvP() && target->IsFFAPvP()) - return REP_HOSTILE; - - if (selfPlayerOwner) - { - if (FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry()) - { - if (!selfPlayerOwner->HasUnitFlag2(UNIT_FLAG2_IGNORE_REPUTATION)) - { - if (FactionEntry const* targetFactionEntry = sFactionStore.LookupEntry(targetFactionTemplateEntry->Faction)) - { - if (targetFactionEntry->CanHaveReputation()) - { - // check contested flags - if (targetFactionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD - && selfPlayerOwner->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP)) - return REP_HOSTILE; - - // if faction has reputation, hostile state depends only from AtWar state - if (selfPlayerOwner->GetReputationMgr().IsAtWar(targetFactionEntry)) - return REP_HOSTILE; - return REP_FRIENDLY; - } - } - } - } - } - } - } - // do checks dependant only on our faction - return GetFactionReactionTo(GetFactionTemplateEntry(), target); -} - -ReputationRank Unit::GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target) -{ - // always neutral when no template entry found - if (!factionTemplateEntry) - return REP_NEUTRAL; - - FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry(); - - if (Player const* targetPlayerOwner = target->GetAffectingPlayer()) - { - // check contested flags - if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD - && targetPlayerOwner->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP)) - return REP_HOSTILE; - if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry)) - return *repRank; - if (!target->HasUnitFlag2(UNIT_FLAG2_IGNORE_REPUTATION)) - { - if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->Faction)) - { - if (factionEntry->CanHaveReputation()) - { - // CvP case - check reputation, don't allow state higher than neutral when at war - ReputationRank repRank = targetPlayerOwner->GetReputationMgr().GetRank(factionEntry); - if (targetPlayerOwner->GetReputationMgr().IsAtWar(factionEntry)) - repRank = std::min(REP_NEUTRAL, repRank); - return repRank; - } - } - } - } - - // common faction based check - if (factionTemplateEntry->IsHostileTo(targetFactionTemplateEntry)) - return REP_HOSTILE; - if (factionTemplateEntry->IsFriendlyTo(targetFactionTemplateEntry)) - return REP_FRIENDLY; - if (targetFactionTemplateEntry->IsFriendlyTo(factionTemplateEntry)) - return REP_FRIENDLY; - if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_HOSTILE_BY_DEFAULT) - return REP_HOSTILE; - // neutral by default - return REP_NEUTRAL; -} - -bool Unit::IsHostileTo(Unit const* unit) const -{ - return GetReactionTo(unit) <= REP_HOSTILE; -} - -bool Unit::IsFriendlyTo(Unit const* unit) const -{ - return GetReactionTo(unit) >= REP_FRIENDLY; -} - -bool Unit::IsHostileToPlayers() const -{ - FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); - if (!my_faction->Faction) - return false; - - FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction); - if (raw_faction && raw_faction->ReputationIndex >= 0) - return false; - - return my_faction->IsHostileToPlayers(); -} - -bool Unit::IsNeutralToAll() const -{ - FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); - if (!my_faction->Faction) - return true; - - FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction); - if (raw_faction && raw_faction->ReputationIndex >= 0) - return false; - - return my_faction->IsNeutralToAll(); -} - void Unit::_addAttacker(Unit* pAttacker) { m_attackers.insert(pAttacker); @@ -6108,15 +5757,6 @@ void Unit::SetOwnerGUID(ObjectGuid owner) player->SendDirectMessage(&packet); } -Unit* Unit::GetOwner() const -{ - ObjectGuid ownerGUID = GetOwnerGUID(); - if (!ownerGUID.IsEmpty()) - return ObjectAccessor::GetUnit(*this, ownerGUID); - - return nullptr; -} - Unit* Unit::GetCharmer() const { ObjectGuid charmerGUID = GetCharmerGUID(); @@ -6126,26 +5766,6 @@ Unit* Unit::GetCharmer() const return nullptr; } -Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself() const -{ - ObjectGuid guid = GetCharmerOrOwnerGUID(); - if (guid.IsPlayer()) - return ObjectAccessor::GetPlayer(*this, guid); - - return const_cast<Unit*>(this)->ToPlayer(); -} - -Player* Unit::GetAffectingPlayer() const -{ - if (!GetCharmerOrOwnerGUID()) - return const_cast<Unit*>(this)->ToPlayer(); - - if (Unit* owner = GetCharmerOrOwner()) - return owner->GetCharmerOrOwnerPlayerOrPlayerItself(); - - return nullptr; -} - Minion* Unit::GetFirstMinion() const { ObjectGuid pet_guid = GetMinionGUID(); @@ -6198,14 +5818,6 @@ Unit* Unit::GetCharmerOrOwner() const return !GetCharmerGUID().IsEmpty() ? GetCharmer() : GetOwner(); } -Unit* Unit::GetCharmerOrOwnerOrSelf() const -{ - if (Unit* u = GetCharmerOrOwner()) - return u; - - return (Unit*)this; -} - void Unit::SetMinion(Minion *minion, bool apply) { TC_LOG_DEBUG("entities.unit", "SetMinion %u for %u, apply %u", minion->GetEntry(), GetEntry(), apply); @@ -6521,42 +6133,6 @@ bool Unit::IsMagnet() const return false; } -Unit* Unit::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo) -{ - // Patch 1.2 notes: Spell Reflection no longer reflects abilities - if (spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REDIRECTED) || spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)) - return victim; - - Unit::AuraEffectList const& magnetAuras = victim->GetAuraEffectsByType(SPELL_AURA_SPELL_MAGNET); - for (Unit::AuraEffectList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr) - { - if (Unit* magnet = (*itr)->GetBase()->GetCaster()) - if (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK - && IsValidAttackTarget(magnet, spellInfo)) - { - /// @todo handle this charge drop by proc in cast phase on explicit target - if (spellInfo->HasHitDelay()) - { - // Set up missile speed based delay - float hitDelay = spellInfo->LaunchDelay; - if (spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION)) - hitDelay += spellInfo->Speed; - else if (spellInfo->Speed > 0.0f) - hitDelay += std::max(victim->GetDistance(this), 5.0f) / spellInfo->Speed; - - uint32 delay = uint32(std::floor(hitDelay * 1000.0f)); - // Schedule charge drop - (*itr)->GetBase()->DropChargeDelayed(delay, AURA_REMOVE_BY_EXPIRE); - } - else - (*itr)->GetBase()->DropCharge(AURA_REMOVE_BY_EXPIRE); - - return magnet; - } - } - return victim; -} - Unit* Unit::GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo /*= nullptr*/) { AuraEffectList const& interceptAuras = victim->GetAuraEffectsByType(SPELL_AURA_INTERCEPT_MELEE_RANGED_ATTACKS); @@ -7559,7 +7135,7 @@ bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const return false; } -bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const +bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const { if (!spellInfo) return false; @@ -7655,7 +7231,7 @@ uint32 Unit::GetMechanicImmunityMask() const return mask; } -bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const +bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const { if (!spellInfo) return false; @@ -8262,252 +7838,6 @@ bool Unit::isTargetableForAttack(bool checkFakeDeath) const return !HasUnitState(UNIT_STATE_UNATTACKABLE) && (!checkFakeDeath || !HasUnitState(UNIT_STATE_DIED)); } -// function based on function Unit::CanAttack from 13850 client -bool Unit::IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell /*= nullptr*/, WorldObject const* obj /*= nullptr*/, bool spellCheck /*= true*/) const -{ - ASSERT(target); - - // can't attack self - if (this == target) - return false; - - // can't attack GMs - if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) - return false; - - // CvC case - can attack each other only when one of them is hostile - if (!HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && !target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - return IsHostileTo(target) || target->IsHostileTo(this); - - // PvP, PvC, CvP case - // can't attack friendly targets - if (IsFriendlyTo(target) || target->IsFriendlyTo(this)) - return false; - - Player const* playerAffectingAttacker = HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; - Player const* playerAffectingTarget = target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr; - - // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) - if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget)) - { - Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget; - Unit const* creature = playerAffectingAttacker ? target : this; - - if (creature->IsContestedGuard() && player->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP)) - return true; - - if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry()) - { - if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate))) - if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->Faction)) - if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry)) - if (!repState->Flags.HasFlag(ReputationFlags::AtWar)) - return false; - - } - } - - Creature const* creatureAttacker = ToCreature(); - if (creatureAttacker && (creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) - return false; - - if (spellCheck && !IsValidSpellAttackTarget(target, bySpell, obj)) - return false; - - return true; -} - -bool Unit::IsValidSpellAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj /*= nullptr*/) const -{ - // can't attack unattackable units - if (target->HasUnitState(UNIT_STATE_UNATTACKABLE)) - return false; - - // visibility checks - // skip visibility check for GO casts, needs removal when go cast is implemented. Also ignore for gameobject and dynauras - if (GetEntry() != WORLD_TRIGGER && (!obj || !obj->isType(TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT))) - { - // can't attack invisible - if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) - { - if (obj && !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())) - return false; - else if (!obj) - { - // ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player - bool const ignoreStealthCheck = (bySpell && bySpell->IsAffectingArea()) || - (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && IsInCombatWith(target)); - - if (!CanSeeOrDetect(target, ignoreStealthCheck)) - return false; - } - } - } - - // can't attack dead - if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->IsAlive()) - return false; - - // can't attack untargetable - if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) - && target->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - return false; - - if (Player const* playerAttacker = ToPlayer()) - { - if (playerAttacker->HasPlayerFlag(PLAYER_FLAGS_UBER)) - return false; - } - - // check flags - if (target->HasUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16))) - return false; - - if (!HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && target->IsImmuneToNPC()) - return false; - - if (!target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && IsImmuneToNPC()) - return false; - - if ((!bySpell || bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG)) - && HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && target->IsImmuneToPC()) - return false; - - // check if this is a world trigger cast - GOs are using world triggers to cast their spells, so we need to ignore their immunity flag here, this is a temp workaround, needs removal when go cast is implemented properly - if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && IsImmuneToPC()) - if (GetEntry() != WORLD_TRIGGER && (!obj || !obj->isType(TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT))) - return false; - - - // check duel - before sanctuary checks - Player const* playerAffectingAttacker = HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; - Player const* playerAffectingTarget = target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr; - if (playerAffectingAttacker && playerAffectingTarget) - if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0) - return true; - - // PvP case - can't attack when attacker or target are in sanctuary - // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp - if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && (target->IsInSanctuary() || IsInSanctuary())) - return false; - - // additional checks - only PvP case - if (playerAffectingAttacker && playerAffectingTarget) - { - if (target->IsPvP()) - return true; - - if (IsFFAPvP() && target->IsFFAPvP()) - return true; - - return HasPvpFlag(UNIT_BYTE2_FLAG_UNK1) - || target->HasPvpFlag(UNIT_BYTE2_FLAG_UNK1); - } - - return true; -} - -// function based on function Unit::CanAssist from 13850 client -bool Unit::IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const -{ - ASSERT(target); - - // can assist to self - if (this == target) - return true; - - // can't assist GMs - if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) - return false; - - // can't assist non-friendly targets - if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))) - return false; - - if (spellCheck && !IsValidSpellAssistTarget(target, bySpell)) - return false; - - return true; -} - -bool Unit::IsValidSpellAssistTarget(Unit const* target, SpellInfo const* bySpell) const -{ - // can't assist unattackable units - if (target->HasUnitState(UNIT_STATE_UNATTACKABLE)) - return false; - - // can't assist own vehicle or passenger - if (m_vehicle) - { - if (IsOnVehicle(target)) - return false; - - if (m_vehicle->GetBase()->IsOnVehicle(target)) - return false; - } - - // can't assist invisible - if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())) - return false; - - // can't assist dead - if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->IsAlive()) - return false; - - // can't assist untargetable - if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && target->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - return false; - - if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) - { - if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - { - if (target->IsImmuneToPC()) - return false; - } - else - { - if (target->IsImmuneToNPC()) - return false; - } - } - - // PvP case - if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - { - Player const* targetPlayerOwner = target->GetAffectingPlayer(); - if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - { - Player const* selfPlayerOwner = GetAffectingPlayer(); - if (selfPlayerOwner && targetPlayerOwner) - { - // can't assist player which is dueling someone - if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner->duel) - return false; - } - // can't assist player in ffa_pvp zone from outside - if (target->IsFFAPvP() && !IsFFAPvP()) - return false; - - // can't assist player out of sanctuary from sanctuary if has pvp enabled - if (target->IsPvP()) - if (IsInSanctuary() && !target->IsInSanctuary()) - return false; - } - } - // PvC case - player can assist creature only if has specific type flags - // !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && - else if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - { - if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) - if (!target->IsPvP()) - if (Creature const* creatureTarget = target->ToCreature()) - return creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT || creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST; - } - - return true; -} - int64 Unit::ModifyHealth(int64 dVal) { int64 gain = 0; @@ -8998,175 +8328,6 @@ void Unit::UpdatePetCombatState() //====================================================================== -float Unit::ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const -{ - if (Player* modOwner = GetSpellModOwner()) - { - modOwner->ApplySpellMod(spellProto, SpellModOp::Points, value); - switch (effect_index) - { - case EFFECT_0: - modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex0, value); - break; - case EFFECT_1: - modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex1, value); - break; - case EFFECT_2: - modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex2, value); - break; - case EFFECT_3: - modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex3, value); - break; - case EFFECT_4: - modOwner->ApplySpellMod(spellProto, SpellModOp::PointsIndex4, value); - break; - } - } - return value; -} - -// function uses real base points (typically value - 1) -int32 Unit::CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effect_index, int32 const* basePoints /*= nullptr*/, float* variance /*= nullptr*/, uint32 castItemId /*= 0*/, int32 itemLevel /*= -1*/) const -{ - SpellEffectInfo const* effect = spellProto->GetEffect(effect_index); - if (variance) - *variance = 0.0f; - - return effect ? effect->CalcValue(this, basePoints, target, variance, castItemId, itemLevel) : 0; -} - -int32 Unit::CalcSpellDuration(SpellInfo const* spellProto) -{ - uint32 comboPoints = m_playerMovingMe ? m_playerMovingMe->GetComboPoints() : 0; - - int32 minduration = spellProto->GetDuration(); - int32 maxduration = spellProto->GetMaxDuration(); - - int32 duration; - - if (comboPoints && minduration != -1 && minduration != maxduration) - duration = minduration + int32((maxduration - minduration) * comboPoints / 5); - else - duration = minduration; - - return duration; -} - -int32 Unit::ModSpellDuration(SpellInfo const* spellProto, Unit const* target, int32 duration, bool positive, uint32 effectMask) -{ - // don't mod permanent auras duration - if (duration < 0) - return duration; - - // some auras are not affected by duration modifiers - if (spellProto->HasAttribute(SPELL_ATTR7_IGNORE_DURATION_MODS)) - return duration; - - // cut duration only of negative effects - if (!positive) - { - int32 mechanic = spellProto->GetSpellMechanicMaskByEffectMask(effectMask); - - int32 durationMod; - int32 durationMod_always = 0; - int32 durationMod_not_stack = 0; - - for (uint8 i = 1; i <= MECHANIC_ENRAGED; ++i) - { - if (!(mechanic & 1<<i)) - continue; - // Find total mod value (negative bonus) - int32 new_durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD, i); - // Find max mod (negative bonus) - int32 new_durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, i); - // Check if mods applied before were weaker - if (new_durationMod_always < durationMod_always) - durationMod_always = new_durationMod_always; - if (new_durationMod_not_stack < durationMod_not_stack) - durationMod_not_stack = new_durationMod_not_stack; - } - - // Select strongest negative mod - if (durationMod_always > durationMod_not_stack) - durationMod = durationMod_not_stack; - else - durationMod = durationMod_always; - - if (durationMod != 0) - AddPct(duration, durationMod); - - // there are only negative mods currently - durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL, spellProto->Dispel); - durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK, spellProto->Dispel); - - durationMod = 0; - if (durationMod_always > durationMod_not_stack) - durationMod += durationMod_not_stack; - else - durationMod += durationMod_always; - - if (durationMod != 0) - AddPct(duration, durationMod); - } - else - { - // else positive mods here, there are no currently - // when there will be, change GetTotalAuraModifierByMiscValue to GetTotalPositiveAuraModifierByMiscValue - - // Mixology - duration boost - if (target->GetTypeId() == TYPEID_PLAYER) - { - if (spellProto->SpellFamilyName == SPELLFAMILY_POTION && ( - sSpellMgr->IsSpellMemberOfSpellGroup(spellProto->Id, SPELL_GROUP_ELIXIR_BATTLE) || - sSpellMgr->IsSpellMemberOfSpellGroup(spellProto->Id, SPELL_GROUP_ELIXIR_GUARDIAN))) - { - SpellEffectInfo const* effect = spellProto->GetEffect(EFFECT_0); - if (target->HasAura(53042) && effect && target->HasSpell(effect->TriggerSpell)) - duration *= 2; - } - } - } - - return std::max(duration, 0); -} - -void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* spell) -{ - if (!spellInfo || castTime < 0) - return; - - // called from caster - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo, SpellModOp::ChangeCastTime, castTime, spell); - - if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && - ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) - castTime = CanInstantCast() ? 0 : int32(float(castTime) * m_unitData->ModCastingSpeed); - else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) - castTime = int32(float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]); - else if (IsPartOfSkillLine(SKILL_COOKING, spellInfo->Id) && HasAura(67556)) // cooking with Chef Hat. - castTime = 500; -} - -void Unit::ModSpellDurationTime(SpellInfo const* spellInfo, int32& duration, Spell* spell) -{ - if (!spellInfo || duration < 0) - return; - - if (spellInfo->IsChanneled() && !spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) - return; - - // called from caster - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo, SpellModOp::ChangeCastTime, duration, spell); - - if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && - ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) - duration = int32(float(duration) * m_unitData->ModCastingSpeed); - else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) - duration = int32(float(duration) * m_modAttackSpeedPct[RANGED_ATTACK]); -} - DiminishingLevels Unit::GetDiminishing(DiminishingGroup group) const { DiminishingReturn const& diminish = m_Diminishing[group]; @@ -9191,7 +8352,7 @@ void Unit::IncrDiminishing(SpellInfo const* auraSpellInfo) diminish.hitCount = currentLevel + 1; } -bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, Unit* caster, DiminishingLevels previousLevel) const +bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, WorldObject* caster, DiminishingLevels previousLevel) const { DiminishingGroup const group = auraSpellInfo->GetDiminishingReturnsGroupForSpell(); if (duration == -1 || group == DIMINISHING_NONE) @@ -9206,7 +8367,7 @@ bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& dur if (limitDuration > 0 && duration > limitDuration) { Unit const* target = targetOwner ? targetOwner : this; - Unit const* source = casterOwner ? casterOwner : caster; + WorldObject const* source = casterOwner ? casterOwner : caster; if (target->IsAffectedByDiminishingReturns() && source->GetTypeId() == TYPEID_PLAYER) duration = limitDuration; @@ -9295,28 +8456,6 @@ void Unit::ClearDiminishings() dim.Clear(); } -float Unit::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const -{ - if (!spellInfo->RangeEntry) - return 0; - if (spellInfo->RangeEntry->RangeMax[0] == spellInfo->RangeEntry->RangeMax[1]) - return spellInfo->GetMaxRange(); - if (!target) - return spellInfo->GetMaxRange(true); - return spellInfo->GetMaxRange(!IsHostileTo(target)); -} - -float Unit::GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const -{ - if (!spellInfo->RangeEntry) - return 0; - if (spellInfo->RangeEntry->RangeMin[0] == spellInfo->RangeEntry->RangeMin[1]) - return spellInfo->GetMinRange(); - if (!target) - return spellInfo->GetMinRange(true); - return spellInfo->GetMinRange(!IsHostileTo(target)); -} - uint32 Unit::GetCreatureType() const { if (GetTypeId() == TYPEID_PLAYER) @@ -10626,28 +9765,6 @@ ObjectGuid Unit::GetCharmerOrOwnerGUID() const return !GetCharmerGUID().IsEmpty() ? GetCharmerGUID() : GetOwnerGUID(); } -ObjectGuid Unit::GetCharmerOrOwnerOrOwnGUID() const -{ - ObjectGuid guid = GetCharmerOrOwnerGUID(); - if (!guid.IsEmpty()) - return guid; - - return GetGUID(); -} - -Player* Unit::GetSpellModOwner() const -{ - if (Player* player = const_cast<Unit*>(this)->ToPlayer()) - return player; - - if (HasUnitTypeMask(UNIT_MASK_PET | UNIT_MASK_TOTEM | UNIT_MASK_GUARDIAN)) - { - if (Unit* owner = GetOwner()) - return owner->ToPlayer(); - } - return nullptr; -} - ///----------Pet responses methods----------------- void Unit::SendPetActionFeedback(PetActionFeedback msg, uint32 spellId) { @@ -10847,6 +9964,45 @@ void Unit::RestoreDisplayId(bool ignorePositiveAurasPreventingMounting /*= false SetDisplayId(GetNativeDisplayId()); } +void Unit::AddComboPoints(int8 count, Spell* spell) +{ + if (!count) + return; + + int8 comboPoints = spell ? spell->m_comboPointGain : GetPower(POWER_COMBO_POINTS); + + comboPoints += count; + + if (comboPoints > 5) + comboPoints = 5; + else if (comboPoints < 0) + comboPoints = 0; + + if (!spell) + SetPower(POWER_COMBO_POINTS, comboPoints); + else + spell->m_comboPointGain = comboPoints; +} + +void Unit::GainSpellComboPoints(int8 count) +{ + if (!count) + return; + + int8 cp = GetPower(POWER_COMBO_POINTS); + + cp += count; + if (cp > 5) cp = 5; + else if (cp < 0) cp = 0; + + SetPower(POWER_COMBO_POINTS, cp); +} + +void Unit::ClearComboPoints() +{ + SetPower(POWER_COMBO_POINTS, 0); +} + void Unit::ClearAllReactives() { for (uint8 i = 0; i < MAX_REACTIVE; ++i) @@ -12289,9 +11445,6 @@ Aura* Unit::AddAura(uint32 spellId, Unit* target) if (!spellInfo) return nullptr; - if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD)) - return nullptr; - return AddAura(spellInfo, MAX_EFFECT_MASK, target); } @@ -12300,6 +11453,9 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target) if (!spellInfo) return nullptr; + if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD)) + return nullptr; + if (target->IsImmunedToSpell(spellInfo, this)) return nullptr; @@ -14213,37 +13369,7 @@ uint32 Unit::GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const } } - return spellInfo->GetSpellXSpellVisualId(this); -} - -struct CombatLogSender -{ - WorldPackets::CombatLog::CombatLogServerPacket const* i_message; - - explicit CombatLogSender(WorldPackets::CombatLog::CombatLogServerPacket* msg) - : i_message(msg) - { - msg->Write(); - } - - void operator()(Player const* player) const - { - if (player->IsAdvancedCombatLoggingEnabled()) - player->SendDirectMessage(i_message->GetFullLogPacket()); - else - player->SendDirectMessage(i_message->GetBasicLogPacket()); - } -}; - -void Unit::SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const -{ - CombatLogSender combatLogSender(combatLog); - - if (Player const* self = ToPlayer()) - combatLogSender(self); - - Trinity::MessageDistDeliverer<CombatLogSender> notifier(this, combatLogSender, GetVisibilityRange()); - Cell::VisitWorldObjects(this, notifier, GetVisibilityRange()); + return WorldObject::GetCastSpellXSpellVisualId(spellInfo); } bool Unit::VisibleAuraSlotCompare::operator()(AuraApplication* left, AuraApplication* right) const diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index ff5c76a1bb5..3e5614ff30a 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -19,13 +19,11 @@ #define __UNIT_H #include "Object.h" -#include "EventProcessor.h" #include "FollowerReference.h" #include "FollowerRefManager.h" #include "CombatManager.h" #include "OptionalFwd.h" #include "SpellAuraDefines.h" -#include "SpellDefines.h" #include "ThreatManager.h" #include "Timer.h" #include "UnitDefines.h" @@ -105,13 +103,6 @@ namespace Movement class MoveSpline; struct SpellEffectExtraData; } -namespace WorldPackets -{ - namespace CombatLog - { - class CombatLogServerPacket; - } -} typedef std::list<Unit*> UnitList; @@ -383,15 +374,15 @@ enum MeleeHitOutcome : uint8 class DispelInfo { public: - explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : - _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { } + explicit DispelInfo(WorldObject* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : + _dispeller(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) { } - Unit* GetDispeller() const { return _dispellerUnit; } + WorldObject* GetDispeller() const { return _dispeller; } uint32 GetDispellerSpellId() const { return _dispellerSpell; } uint8 GetRemovedCharges() const { return _chargesRemoved; } void SetRemovedCharges(uint8 amount) { _chargesRemoved = amount; } private: - Unit* _dispellerUnit; + WorldObject* _dispeller; uint32 _dispellerSpell; uint8 _chargesRemoved; }; @@ -781,19 +772,13 @@ class TC_GAME_API Unit : public WorldObject void CleanupBeforeRemoveFromMap(bool finalCleanup); void CleanupsBeforeDelete(bool finalCleanup = true) override; // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units) - void SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const; - virtual bool IsAffectedByDiminishingReturns() const { return (GetCharmerOrOwnerPlayerOrPlayerItself() != nullptr); } DiminishingLevels GetDiminishing(DiminishingGroup group) const; void IncrDiminishing(SpellInfo const* auraSpellInfo); - bool ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, Unit* caster, DiminishingLevels previousLevel) const; + bool ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, int32& duration, WorldObject* caster, DiminishingLevels previousLevel) const; void ApplyDiminishingAura(DiminishingGroup group, bool apply); void ClearDiminishings(); - // target dependent range checks - float GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const; - float GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const; - virtual void Update(uint32 time) override; void setAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; } @@ -881,7 +866,7 @@ class TC_GAME_API Unit : public WorldObject int32 GetResistance(SpellSchoolMask mask) const; void SetResistance(SpellSchools school, int32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Resistances, school), val); } void SetBonusResistanceMod(SpellSchools school, int32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::BonusResistanceMods, school), val); } - static float CalculateAverageResistReduction(Unit const* attacker, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr); + static float CalculateAverageResistReduction(WorldObject const* caster, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr); uint64 GetHealth() const { return m_unitData->Health; } uint64 GetMaxHealth() const { return m_unitData->MaxHealth; } @@ -959,17 +944,9 @@ class TC_GAME_API Unit : public WorldObject void SetSheath(SheathState sheathed); // faction template id - uint32 GetFaction() const { return m_unitData->FactionTemplate; } - void SetFaction(uint32 faction) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::FactionTemplate), faction); } - FactionTemplateEntry const* GetFactionTemplateEntry() const; - - ReputationRank GetReactionTo(Unit const* target) const; - ReputationRank static GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target); + uint32 GetFaction() const override { return m_unitData->FactionTemplate; } + void SetFaction(uint32 faction) override { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::FactionTemplate), faction); } - bool IsHostileTo(Unit const* unit) const; - bool IsHostileToPlayers() const; - bool IsFriendlyTo(Unit const* unit) const; - bool IsNeutralToAll() const; bool IsInPartyWith(Unit const* unit) const; bool IsInRaidWith(Unit const* unit) const; void GetPartyMembers(std::list<Unit*> &units); @@ -1057,10 +1034,8 @@ class TC_GAME_API Unit : public WorldObject int32 CalculateAOEAvoidance(int32 damage, uint32 schoolMask, ObjectGuid const& casterGuid) const; - float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const; - SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; - SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; - SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false); + float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const override; + SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const override; float GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const; float GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const; @@ -1161,12 +1136,6 @@ class TC_GAME_API Unit : public WorldObject bool isTargetableForAttack(bool checkFakeDeath = true) const; - bool IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell = nullptr, WorldObject const* obj = nullptr, bool spellCheck = true) const; - bool IsValidSpellAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj = nullptr) const; - - bool IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const; - bool IsValidSpellAssistTarget(Unit const* target, SpellInfo const* bySpell) const; - virtual bool IsInWater() const; virtual bool IsUnderWater() const; bool isInAccessiblePlaceFor(Creature const* c) const; @@ -1176,10 +1145,6 @@ class TC_GAME_API Unit : public WorldObject void SendEnergizeSpellLog(Unit* victim, uint32 spellId, int32 damage, int32 overEnergize, Powers powerType); void EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damage, Powers powerType); - // CastSpell's third arg can be a variety of things - check out CastSpellExtraArgs' constructors! - void CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args = {}); - void CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args = {}); - void CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args = {}); Aura* AddAura(uint32 spellId, Unit* target); Aura* AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target); void SetAuraStack(uint32 spellId, Unit* target, uint32 stack); @@ -1256,7 +1221,7 @@ class TC_GAME_API Unit : public WorldObject DeathState getDeathState() const { return m_deathState; } virtual void setDeathState(DeathState s); // overwrited in Creature/Player/Pet - ObjectGuid GetOwnerGUID() const { return m_unitData->SummonedBy; } + ObjectGuid GetOwnerGUID() const override { return m_unitData->SummonedBy; } void SetOwnerGUID(ObjectGuid owner); ObjectGuid GetCreatorGUID() const { return m_unitData->CreatedBy; } void SetCreatorGUID(ObjectGuid creator) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::CreatedBy), creator); } @@ -1276,21 +1241,14 @@ class TC_GAME_API Unit : public WorldObject void SetDemonCreatorGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::DemonCreator), guid); } bool IsControlledByPlayer() const { return m_ControlledByPlayer; } - ObjectGuid GetCharmerOrOwnerGUID() const; - ObjectGuid GetCharmerOrOwnerOrOwnGUID() const; + ObjectGuid GetCharmerOrOwnerGUID() const override; bool IsCharmedOwnedByPlayerOrPlayer() const { return GetCharmerOrOwnerOrOwnGUID().IsPlayer(); } - Player* GetSpellModOwner() const; - - Unit* GetOwner() const; Guardian* GetGuardianPet() const; Minion* GetFirstMinion() const; Unit* GetCharmer() const; Unit* GetCharm() const; Unit* GetCharmerOrOwner() const; - Unit* GetCharmerOrOwnerOrSelf() const; - Player* GetCharmerOrOwnerPlayerOrPlayerItself() const; - Player* GetAffectingPlayer() const; void SetMinion(Minion *minion, bool apply); void GetAllMinionsByEntry(std::list<TempSummon*>& Minions, uint32 entry); @@ -1378,8 +1336,8 @@ class TC_GAME_API Unit : public WorldObject void RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); void RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT, uint16 num = 1); - void RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, Unit* dispeller, uint8 chargesRemoved = 1); - void RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, Unit* stealer); + void RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, WorldObject* dispeller, uint8 chargesRemoved = 1); + void RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, WorldObject* stealer); void RemoveAurasDueToItemSpell(uint32 spellId, ObjectGuid castItemGuid); void RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID = ObjectGuid::Empty, Aura* except = nullptr, bool negative = true, bool positive = true); void RemoveNotOwnSingleTargetAuras(bool onPhaseChange = false); @@ -1427,7 +1385,7 @@ class TC_GAME_API Unit : public WorldObject AuraApplication* GetAuraApplicationOfRankedSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid itemCasterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0, AuraApplication* except = nullptr) const; Aura* GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid itemCasterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0) const; - void GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect = false) const; + void GetDispellableAuraList(WorldObject const* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect = false) const; bool HasAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid caster = ObjectGuid::Empty) const; uint32 GetAuraCount(uint32 spellId) const; @@ -1530,7 +1488,7 @@ class TC_GAME_API Unit : public WorldObject Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; int32 GetCurrentSpellCastTime(uint32 spell_id) const; virtual SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const; - uint32 GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const; + uint32 GetCastSpellXSpellVisualId(SpellInfo const* spellInfo) const override; virtual bool IsFocusing(Spell const* /*focusSpell*/ = nullptr, bool /*withDelay*/ = false) { return false; } virtual bool IsMovementPreventedByCasting() const; @@ -1559,9 +1517,6 @@ class TC_GAME_API Unit : public WorldObject float m_modAttackSpeedPct[MAX_ATTACK]; uint32 m_attackTimer[MAX_ATTACK]; - // Event handler - EventProcessor m_Events; - // stat system void HandleStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float amount, bool apply); void ApplyStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float amount); @@ -1688,7 +1643,6 @@ class TC_GAME_API Unit : public WorldObject bool HasAuraState(AuraStateType flag, SpellInfo const* spellProto = nullptr, Unit const* Caster = nullptr) const; void UnsummonAllTotems(); bool IsMagnet() const; - Unit* GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo); Unit* GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo = nullptr); int32 SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) const; @@ -1716,14 +1670,14 @@ class TC_GAME_API Unit : public WorldObject uint32 GetRemainingPeriodicAmount(ObjectGuid caster, uint32 spellId, AuraType auraType, uint8 effectIndex = 0) const; void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply); - virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster) const; // redefined in Creature + virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const; uint32 GetSchoolImmunityMask() const; uint32 GetDamageImmunityMask() const; uint32 GetMechanicImmunityMask() const; bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const; bool IsImmunedToDamage(SpellInfo const* spellInfo) const; - virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const; // redefined in Creature + virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const; static bool IsDamageReducedByArmor(SpellSchoolMask damageSchoolMask, SpellInfo const* spellInfo = nullptr, int8 effIndex = -1); static uint32 CalcArmorReducedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK, uint8 attackerLevel = 0); @@ -1737,13 +1691,6 @@ class TC_GAME_API Unit : public WorldObject void SetSpeed(UnitMoveType mtype, float newValue); void SetSpeedRate(UnitMoveType mtype, float rate); - float ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const; - int32 CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effect_index, int32 const* basePoints = nullptr, float* variance = nullptr, uint32 castItemId = 0, int32 itemLevel = -1) const; - int32 CalcSpellDuration(SpellInfo const* spellProto); - int32 ModSpellDuration(SpellInfo const* spellProto, Unit const* target, int32 duration, bool positive, uint32 effectMask); - void ModSpellCastTime(SpellInfo const* spellProto, int32& castTime, Spell* spell = nullptr); - void ModSpellDurationTime(SpellInfo const* spellProto, int32& castTime, Spell* spell = nullptr); - void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); } void removeFollower(FollowerReference* /*pRef*/) { /* nothing to do yet */ } @@ -1771,6 +1718,12 @@ class TC_GAME_API Unit : public WorldObject void SetControlled(bool apply, UnitState state); void ApplyControlStatesIfNeeded(); + ///-----------Combo point system------------------- + uint32 GetComboPoints() const { return uint32(GetPower(POWER_COMBO_POINTS)); } + void AddComboPoints(int8 count, Spell* spell = nullptr); + void GainSpellComboPoints(int8 count); + void ClearComboPoints(); + ///----------Pet responses methods----------------- void SendPetActionFeedback(PetActionFeedback msg, uint32 spellId); void SendPetTalk(uint32 pettalk); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 35059a07aa9..287976bf774 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -286,17 +286,17 @@ bool AnyDeadUnitObjectInRangeCheck::operator()(Creature* u) bool AnyDeadUnitSpellTargetInRangeCheck::operator()(Player* u) { - return AnyDeadUnitObjectInRangeCheck::operator()(u) && i_check(u); + return AnyDeadUnitObjectInRangeCheck::operator()(u) && WorldObjectSpellTargetCheck::operator()(u); } bool AnyDeadUnitSpellTargetInRangeCheck::operator()(Corpse* u) { - return AnyDeadUnitObjectInRangeCheck::operator()(u) && i_check(u); + return AnyDeadUnitObjectInRangeCheck::operator()(u) && WorldObjectSpellTargetCheck::operator()(u); } bool AnyDeadUnitSpellTargetInRangeCheck::operator()(Creature* u) { - return AnyDeadUnitObjectInRangeCheck::operator()(u) && i_check(u); + return AnyDeadUnitObjectInRangeCheck::operator()(u) && WorldObjectSpellTargetCheck::operator()(u); } template void ObjectUpdater::Visit<Creature>(CreatureMapType&); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 31fb37fd15e..09ee2936b0d 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -670,29 +670,26 @@ namespace Trinity class TC_GAME_API AnyDeadUnitObjectInRangeCheck { public: - AnyDeadUnitObjectInRangeCheck(Unit* searchObj, float range) : i_searchObj(searchObj), i_range(range) { } + AnyDeadUnitObjectInRangeCheck(WorldObject* searchObj, float range) : i_searchObj(searchObj), i_range(range) { } bool operator()(Player* u); bool operator()(Corpse* u); bool operator()(Creature* u); template<class NOT_INTERESTED> bool operator()(NOT_INTERESTED*) { return false; } protected: - Unit const* const i_searchObj; + WorldObject const* const i_searchObj; float i_range; }; - class TC_GAME_API AnyDeadUnitSpellTargetInRangeCheck : public AnyDeadUnitObjectInRangeCheck + class TC_GAME_API AnyDeadUnitSpellTargetInRangeCheck : public AnyDeadUnitObjectInRangeCheck, public WorldObjectSpellTargetCheck { public: - AnyDeadUnitSpellTargetInRangeCheck(Unit* searchObj, float range, SpellInfo const* spellInfo, SpellTargetCheckTypes check, SpellTargetObjectTypes objectType) - : AnyDeadUnitObjectInRangeCheck(searchObj, range), i_spellInfo(spellInfo), i_check(searchObj, searchObj, spellInfo, check, nullptr, objectType) + AnyDeadUnitSpellTargetInRangeCheck(WorldObject* searchObj, float range, SpellInfo const* spellInfo, SpellTargetCheckTypes check, SpellTargetObjectTypes objectType) + : AnyDeadUnitObjectInRangeCheck(searchObj, range), WorldObjectSpellTargetCheck(searchObj, searchObj, spellInfo, check, nullptr, objectType) { } bool operator()(Player* u); bool operator()(Corpse* u); bool operator()(Creature* u); template<class NOT_INTERESTED> bool operator()(NOT_INTERESTED*) { return false; } - protected: - SpellInfo const* i_spellInfo; - WorldObjectSpellTargetCheck i_check; }; // WorldObject do classes @@ -712,24 +709,23 @@ namespace Trinity class GameObjectFocusCheck { public: - GameObjectFocusCheck(Unit const* unit, uint32 focusId) : i_unit(unit), i_focusId(focusId) { } + GameObjectFocusCheck(WorldObject const* caster, uint32 focusId) : _caster(caster), _focusId(focusId) { } bool operator()(GameObject* go) const { - if (go->GetGOInfo()->GetSpellFocusType() != i_focusId) + if (go->GetGOInfo()->GetSpellFocusType() != _focusId) return false; if (!go->isSpawned()) return false; - float dist = go->GetGOInfo()->GetSpellFocusRadius() / 2.f; - - return go->IsWithinDistInMap(i_unit, dist); + float const dist = go->GetGOInfo()->GetSpellFocusRadius() / 2.f; + return go->IsWithinDistInMap(_caster, dist); } private: - Unit const* i_unit; - uint32 i_focusId; + WorldObject const* _caster; + uint32 _focusId; }; // Find the nearest Fishing hole and return true only if source object is in range of hole diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index a28f4b5baf2..39b9d37e551 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -3521,9 +3521,9 @@ bool Guild::HasAchieved(uint32 achievementId) const return m_achievementMgr.HasAchieved(achievementId); } -void Guild::UpdateCriteria(CriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit* unit, Player* player) +void Guild::UpdateCriteria(CriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject* ref, Player* player) { - m_achievementMgr.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, unit, player); + m_achievementMgr.UpdateCriteria(type, miscValue1, miscValue2, miscValue3, ref, player); } void Guild::HandleNewsSetSticky(WorldSession* session, uint32 newsId, bool sticky) const diff --git a/src/server/game/Guilds/Guild.h b/src/server/game/Guilds/Guild.h index 7a1bdaf79e5..56fef1bdc70 100644 --- a/src/server/game/Guilds/Guild.h +++ b/src/server/game/Guilds/Guild.h @@ -841,7 +841,7 @@ class TC_GAME_API Guild void ResetTimes(bool weekly); bool HasAchieved(uint32 achievementId) const; - void UpdateCriteria(CriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit* unit, Player* player); + void UpdateCriteria(CriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject* ref, Player* player); protected: ObjectGuid::LowType m_id; diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 44c46c9219c..212a6d46d30 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -423,7 +423,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType> PlayerList const& GetPlayers() const { return m_mapRefManager; } //per-map script storage - void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target); + void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo>> const& scripts, uint32 id, Object* source, Object* target); void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target); // must called with AddToWorld diff --git a/src/server/game/Maps/MapScripts.cpp b/src/server/game/Maps/MapScripts.cpp index 05dd6a2a42d..ad2c744b844 100644 --- a/src/server/game/Maps/MapScripts.cpp +++ b/src/server/game/Maps/MapScripts.cpp @@ -33,7 +33,7 @@ #include "World.h" /// Put scripts in the execution queue -void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target) +void Map::ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo>> const& scripts, uint32 id, Object* source, Object* target) { ///- Find the script map ScriptMapMap::const_iterator s = scripts.find(id); diff --git a/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp b/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp index c03f304a215..a065e619e98 100644 --- a/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp +++ b/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp @@ -38,21 +38,24 @@ void SpellCastLogData::Initialize(Unit const* unit) void SpellCastLogData::Initialize(Spell const* spell) { - Health = spell->GetCaster()->GetHealth(); - AttackPower = spell->GetCaster()->GetTotalAttackPowerValue(spell->GetCaster()->getClass() == CLASS_HUNTER ? RANGED_ATTACK : BASE_ATTACK); - SpellPower = spell->GetCaster()->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_SPELL); - Armor = spell->GetCaster()->GetArmor(); - Powers primaryPowerType = spell->GetCaster()->GetPowerType(); - bool primaryPowerAdded = false; - for (SpellPowerCost const& cost : spell->GetPowerCost()) + if (Unit const* unitCaster = spell->GetCaster()->ToUnit()) { - PowerData.emplace_back(int32(cost.Power), spell->GetCaster()->GetPower(Powers(cost.Power)), int32(cost.Amount)); - if (cost.Power == primaryPowerType) - primaryPowerAdded = true; - } + Health = unitCaster->GetHealth(); + AttackPower = unitCaster->GetTotalAttackPowerValue(unitCaster->getClass() == CLASS_HUNTER ? RANGED_ATTACK : BASE_ATTACK); + SpellPower = unitCaster->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_SPELL); + Armor = unitCaster->GetArmor(); + Powers primaryPowerType = unitCaster->GetPowerType(); + bool primaryPowerAdded = false; + for (SpellPowerCost const& cost : spell->GetPowerCost()) + { + PowerData.emplace_back(int32(cost.Power), unitCaster->GetPower(Powers(cost.Power)), int32(cost.Amount)); + if (cost.Power == primaryPowerType) + primaryPowerAdded = true; + } - if (!primaryPowerAdded) - PowerData.insert(PowerData.begin(), SpellLogPowerData(int32(primaryPowerType), spell->GetCaster()->GetPower(primaryPowerType), 0)); + if (!primaryPowerAdded) + PowerData.insert(PowerData.begin(), SpellLogPowerData(int32(primaryPowerType), unitCaster->GetPower(primaryPowerType), 0)); + } } template<class T, class U> diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 7ea0658e916..881828cbaa5 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -362,8 +362,6 @@ Aura* Aura::TryRefreshStackOrCreate(AuraCreateInfo& createInfo) Aura* Aura::TryCreate(AuraCreateInfo& createInfo) { - ASSERT(createInfo.Caster || !createInfo.CasterGUID.IsEmpty()); - uint32 effMask = createInfo._auraEffectMask; if (createInfo._targetEffectMask) effMask = createInfo._targetEffectMask; @@ -377,17 +375,25 @@ Aura* Aura::TryCreate(AuraCreateInfo& createInfo) Aura* Aura::Create(AuraCreateInfo& createInfo) { - ASSERT(createInfo.Caster || !createInfo.CasterGUID.IsEmpty()); - // try to get caster of aura if (!createInfo.CasterGUID.IsEmpty()) { - if (createInfo._owner->GetGUID() == createInfo.CasterGUID) - createInfo.Caster = createInfo._owner->ToUnit(); + // world gameobjects can't own auras and they send empty casterguid + // checked on sniffs with spell 22247 + if (createInfo.CasterGUID.IsGameObject()) + { + createInfo.Caster = nullptr; + createInfo.CasterGUID.Clear(); + } else - createInfo.Caster = ObjectAccessor::GetUnit(*createInfo._owner, createInfo.CasterGUID); + { + if (createInfo._owner->GetGUID() == createInfo.CasterGUID) + createInfo.Caster = createInfo._owner->ToUnit(); + else + createInfo.Caster = ObjectAccessor::GetUnit(*createInfo._owner, createInfo.CasterGUID); + } } - else + else if (createInfo.Caster) createInfo.CasterGUID = createInfo.Caster->GetGUID(); // check if aura can be owned by owner @@ -440,7 +446,7 @@ Aura* Aura::Create(AuraCreateInfo& createInfo) } Aura::Aura(AuraCreateInfo const& createInfo) : -m_spellInfo(createInfo._spellInfo), m_castDifficulty(createInfo._castDifficulty), m_castGuid(createInfo._castId), m_casterGuid(createInfo.CasterGUID.IsEmpty() ? createInfo.Caster->GetGUID() : createInfo.CasterGUID), +m_spellInfo(createInfo._spellInfo), m_castDifficulty(createInfo._castDifficulty), m_castGuid(createInfo._castId), m_casterGuid(createInfo.CasterGUID), m_castItemGuid(createInfo.CastItemGUID), m_castItemId(createInfo.CastItemId), m_castItemLevel(createInfo.CastItemLevel), m_spellVisual({ createInfo.Caster ? createInfo.Caster->GetCastSpellXSpellVisualId(createInfo._spellInfo) : createInfo._spellInfo->GetSpellXSpellVisualId(), 0 }), m_applyTime(GameTime::GetGameTime()), m_owner(createInfo._owner), m_timeCla(0), m_updateTargetMapInterval(0), diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index de06b241220..968c0e0ca1e 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -57,7 +57,7 @@ #include "WorldSession.h" #include <numeric> -extern NonDefaultConstructible<pEffect> SpellEffects[TOTAL_SPELL_EFFECTS]; +extern NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EFFECTS]; SpellDestination::SpellDestination() { @@ -429,9 +429,9 @@ bool SpellCastTargets::HasDst() const return (GetTargetMask() & TARGET_FLAG_DEST_LOCATION) != 0; } -void SpellCastTargets::Update(Unit* caster) +void SpellCastTargets::Update(WorldObject* caster) { - m_objectTarget = !m_objectTargetGUID.IsEmpty() ? ((m_objectTargetGUID == caster->GetGUID()) ? caster : ObjectAccessor::GetWorldObject(*caster, m_objectTargetGUID)) : nullptr; + m_objectTarget = (m_objectTargetGUID == caster->GetGUID()) ? caster : ObjectAccessor::GetWorldObject(*caster, m_objectTargetGUID); m_itemTarget = nullptr; if (caster->GetTypeId() == TYPEID_PLAYER) @@ -470,29 +470,7 @@ void SpellCastTargets::Update(Unit* caster) } } -void SpellCastTargets::OutDebug() const -{ - if (!m_targetMask) - TC_LOG_DEBUG("spells", "No targets"); - - TC_LOG_DEBUG("spells", "target mask: %u", m_targetMask); - if (m_targetMask & (TARGET_FLAG_UNIT_MASK | TARGET_FLAG_CORPSE_MASK | TARGET_FLAG_GAMEOBJECT_MASK)) - TC_LOG_DEBUG("spells", "Object target: %s", m_objectTargetGUID.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_ITEM) - TC_LOG_DEBUG("spells", "Item target: %s", m_itemTargetGUID.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_TRADE_ITEM) - TC_LOG_DEBUG("spells", "Trade item target: %s", m_itemTargetGUID.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_SOURCE_LOCATION) - TC_LOG_DEBUG("spells", "Source location: transport guid:%s trans offset: %s position: %s", m_src._transportGUID.ToString().c_str(), m_src._transportOffset.ToString().c_str(), m_src._position.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_DEST_LOCATION) - TC_LOG_DEBUG("spells", "Destination location: transport guid:%s trans offset: %s position: %s", m_dst._transportGUID.ToString().c_str(), m_dst._transportOffset.ToString().c_str(), m_dst._position.ToString().c_str()); - if (m_targetMask & TARGET_FLAG_STRING) - TC_LOG_DEBUG("spells", "String: %s", m_strTarget.c_str()); - TC_LOG_DEBUG("spells", "speed: %f", m_speed); - TC_LOG_DEBUG("spells", "pitch: %f", m_pitch); -} - -SpellValue::SpellValue(SpellInfo const* proto, Unit const* caster) +SpellValue::SpellValue(SpellInfo const* proto, WorldObject const* caster) { memset(EffectBasePoints, 0, sizeof(EffectBasePoints)); for (SpellEffectInfo const* effect : proto->GetEffects()) @@ -511,7 +489,7 @@ class TC_GAME_API SpellEvent : public BasicEvent { public: SpellEvent(Spell* spell); - virtual ~SpellEvent(); + ~SpellEvent(); bool Execute(uint64 e_time, uint32 p_time) override; void Abort(uint64 e_time) override; @@ -522,7 +500,7 @@ protected: Spell* m_Spell; }; -Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID) : +Spell::Spell(WorldObject* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID) : m_spellInfo(info), m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster), m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) { @@ -544,11 +522,14 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) m_spellSchoolMask = info->GetSchoolMask(); // Can be override for some spell (wand shoot for example) - if (m_attackType == RANGED_ATTACK) + if (Player const* playerCaster = m_caster->ToPlayer()) + { // wand case - if ((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId() == TYPEID_PLAYER) - if (Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK)) - m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetTemplate()->GetDamageType()); + if (m_attackType == RANGED_ATTACK) + if ((playerCaster->getClassMask() & CLASSMASK_WAND_USERS) != 0) + if (Item* pItem = playerCaster->GetWeaponForAttack(RANGED_ATTACK)) + m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetTemplate()->GetDamageType()); + } if (Player const* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(info, SpellModOp::Doses, m_spellValue->AuraStackAmount, this); @@ -559,7 +540,7 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) m_originalCasterGUID = m_caster->GetGUID(); if (m_originalCasterGUID == m_caster->GetGUID()) - m_originalCaster = m_caster; + m_originalCaster = m_caster->ToUnit(); else { m_originalCaster = ObjectAccessor::GetUnit(*m_caster, m_originalCasterGUID); @@ -597,6 +578,7 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) memset(m_misc.Raw.Data, 0, sizeof(m_misc.Raw.Data)); m_SpellVisual.SpellXSpellVisualID = caster->GetCastSpellXSpellVisualId(m_spellInfo); m_triggeredByAuraSpell = nullptr; + unitCaster = nullptr; _spellAura = nullptr; _dynObjAura = nullptr; @@ -614,7 +596,7 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) // Determine if spell can be reflected back to the caster // Patch 1.2 notes: Spell Reflection no longer reflects abilities - m_canReflect = m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) + m_canReflect = caster->IsUnit() && m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) && !m_spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REFLECTED) && !m_spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) && !m_spellInfo->IsPassive(); @@ -681,11 +663,11 @@ void Spell::InitExplicitTargets(SpellCastTargets const& targets) } // try to use attacked unit as a target else if ((m_caster->GetTypeId() == TYPEID_UNIT) && neededTargets & (TARGET_FLAG_UNIT_ENEMY | TARGET_FLAG_UNIT)) - unit = m_caster->GetVictim(); + unit = m_caster->ToUnit()->GetVictim(); // didn't find anything - let's use self as target if (!unit && neededTargets & (TARGET_FLAG_UNIT_RAID | TARGET_FLAG_UNIT_PARTY | TARGET_FLAG_UNIT_ALLY)) - unit = m_caster; + unit = m_caster->ToUnit(); m_targets.SetUnitTarget(unit); } @@ -723,10 +705,10 @@ void Spell::SelectExplicitTargets() if (Unit* target = m_targets.GetUnitTarget()) { // check for explicit target redirection, for Grounding Totem for example - if (m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT_ENEMY - || (m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT && !m_caster->IsFriendlyTo(target))) + if ((m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT_ENEMY) || + ((m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT) && !m_caster->IsFriendlyTo(target))) { - Unit* redirect; + Unit* redirect = nullptr; switch (m_spellInfo->DmgClass) { case SPELL_DAMAGE_CLASS_MAGIC: @@ -734,10 +716,10 @@ void Spell::SelectExplicitTargets() break; case SPELL_DAMAGE_CLASS_MELEE: case SPELL_DAMAGE_CLASS_RANGED: - redirect = m_caster->GetMeleeHitRedirectTarget(target, m_spellInfo); + // should gameobjects cast damagetype melee/ranged spells this needs to be changed + redirect = ASSERT_NOTNULL(m_caster->ToUnit())->GetMeleeHitRedirectTarget(target, m_spellInfo); break; default: - redirect = nullptr; break; } if (redirect && (redirect != target)) @@ -1203,7 +1185,7 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask) { - Unit* referer = nullptr; + WorldObject* referer = nullptr; switch (targetType.GetReferenceType()) { case TARGET_REFERENCE_TYPE_SRC: @@ -1264,7 +1246,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge case TARGET_UNIT_TARGET_ALLY_OR_RAID: if (Unit* targetedUnit = m_targets.GetUnitTarget()) { - if (!m_caster->IsInRaidWith(targetedUnit)) + if (!m_caster->IsUnit() || !m_caster->ToUnit()->IsInRaidWith(targetedUnit)) { targets.push_back(m_targets.GetUnitTarget()); @@ -1384,15 +1366,19 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici } case TARGET_DEST_CASTER_FRONT_LEAP: { - float dist = m_spellInfo->GetEffect(effIndex)->CalcRadius(m_caster); + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + break; + + float dist = m_spellInfo->GetEffect(effIndex)->CalcRadius(unitCaster); float angle = targetType.CalcDirectionAngle(); Position pos = dest._position; - m_caster->MovePositionToFirstCollision(pos, dist, angle); + unitCaster->MovePositionToFirstCollision(pos, dist, angle); // Generate path to that point. if (!m_preGeneratedPath) - m_preGeneratedPath = std::make_unique<PathGenerator>(m_caster); + m_preGeneratedPath = std::make_unique<PathGenerator>(unitCaster); m_preGeneratedPath->SetPathLengthLimit(dist); @@ -1412,9 +1398,10 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici m_caster->UpdateAllowedPositionZ(dest._position.GetPositionX(), dest._position.GetPositionY(), dest._position.m_positionZ); break; case TARGET_DEST_SUMMONER: - if (TempSummon const* casterSummon = m_caster->ToTempSummon()) - if (Unit const* summoner = casterSummon->GetSummoner()) - dest = SpellDestination(*summoner); + if (Unit const* unitCaster = m_caster->ToUnit()) + if (TempSummon const* casterSummon = unitCaster->ToTempSummon()) + if (Unit const* summoner = casterSummon->GetSummoner()) + dest = SpellDestination(*summoner); break; default: { @@ -1552,14 +1539,17 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli target = m_caster->GetCharmerOrOwner(); break; case TARGET_UNIT_PET: - target = m_caster->GetGuardianPet(); + if (Unit* unitCaster = m_caster->ToUnit()) + target = unitCaster->GetGuardianPet(); break; case TARGET_UNIT_SUMMONER: - if (m_caster->IsSummon()) - target = m_caster->ToTempSummon()->GetSummoner(); + if (Unit* unitCaster = m_caster->ToUnit()) + if (unitCaster->IsSummon()) + target = unitCaster->ToTempSummon()->GetSummoner(); break; case TARGET_UNIT_VEHICLE: - target = m_caster->GetVehicleBase(); + if (Unit* unitCaster = m_caster->ToUnit()) + target = unitCaster->GetVehicleBase(); break; case TARGET_UNIT_PASSENGER_0: case TARGET_UNIT_PASSENGER_1: @@ -1569,11 +1559,13 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli case TARGET_UNIT_PASSENGER_5: case TARGET_UNIT_PASSENGER_6: case TARGET_UNIT_PASSENGER_7: - if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsVehicle()) - target = m_caster->GetVehicleKit()->GetPassenger(targetType.GetTarget() - TARGET_UNIT_PASSENGER_0); + if (Creature* vehicleBase = m_caster->ToCreature()) + if (vehicleBase->IsVehicle()) + target = vehicleBase->GetVehicleKit()->GetPassenger(targetType.GetTarget() - TARGET_UNIT_PASSENGER_0); break; case TARGET_UNIT_OWN_CRITTER: - target = ObjectAccessor::GetCreatureOrPetOrVehicle(*m_caster, m_caster->GetCritterGUID()); + if (Unit const* unitCaster = m_caster->ToUnit()) + target = ObjectAccessor::GetCreatureOrPetOrVehicle(*m_caster, unitCaster->GetCritterGUID()); break; default: break; @@ -1581,8 +1573,13 @@ void Spell::SelectImplicitCasterObjectTargets(SpellEffIndex effIndex, SpellImpli CallScriptObjectTargetSelectHandlers(target, effIndex, targetType); - if (target && target->ToUnit()) - AddUnitTarget(target->ToUnit(), 1 << effIndex, checkIfValid); + if (target) + { + if (Unit* unit = target->ToUnit()) + AddUnitTarget(unit, 1 << effIndex, checkIfValid); + else if (GameObject* go = target->ToGameObject()) + AddGOTarget(go, 1 << effIndex); + } } void Spell::SelectImplicitTargetObjectTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) @@ -1676,7 +1673,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge float b = tangent(m_targets.GetPitch()); float a = (srcToDestDelta - dist2d * b) / (dist2d * dist2d); if (a > -0.0001f) - a = 0; + a = 0.f; // We should check if triggered spell has greater range (which is true in many cases, and initial spell has too short max range) // limit max range to 300 yards, sometimes triggered spells can have 50000yds @@ -1684,15 +1681,16 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge if (SpellInfo const* triggerSpellInfo = sSpellMgr->GetSpellInfo(effect->TriggerSpell, GetCastDifficulty())) bestDist = std::min(std::max(bestDist, triggerSpellInfo->GetMaxRange(false)), std::min(dist2d, 300.0f)); - std::list<WorldObject*>::const_iterator itr = targets.begin(); - for (; itr != targets.end(); ++itr) + // GameObjects don't cast traj + Unit* unitCaster = ASSERT_NOTNULL(m_caster->ToUnit()); + for (auto itr = targets.begin(); itr != targets.end(); ++itr) { - if (m_spellInfo->CheckTarget(m_caster, *itr, true) != SPELL_CAST_OK) + if (m_spellInfo->CheckTarget(unitCaster, *itr, true) != SPELL_CAST_OK) continue; if (Unit* unit = (*itr)->ToUnit()) { - if (m_caster == *itr || m_caster->IsOnVehicle(unit) || unit->GetVehicle()) + if (unitCaster == *itr || unitCaster->IsOnVehicle(unit) || unit->GetVehicle()) continue; if (Creature* creatureTarget = unit->ToCreature()) @@ -1702,14 +1700,14 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge } } - const float size = std::max((*itr)->GetCombatReach(), 1.0f); - const float objDist2d = srcPos.GetExactDist2d(*itr); - const float dz = (*itr)->GetPositionZ() - srcPos.m_positionZ; + float const size = std::max((*itr)->GetCombatReach(), 1.0f); + float const objDist2d = srcPos.GetExactDist2d(*itr); + float const dz = (*itr)->GetPositionZ() - srcPos.m_positionZ; - const float horizontalDistToTraj = std::fabs(objDist2d * std::sin(srcPos.GetRelativeAngle(*itr))); - const float sizeFactor = std::cos((horizontalDistToTraj / size) * (M_PI / 2.0f)); - const float distToHitPoint = std::max(objDist2d * std::cos(srcPos.GetRelativeAngle(*itr)) - size * sizeFactor, 0.0f); - const float height = distToHitPoint * (a * distToHitPoint + b); + float const horizontalDistToTraj = std::fabs(objDist2d * std::sin(srcPos.GetRelativeAngle(*itr))); + float const sizeFactor = std::cos((horizontalDistToTraj / size) * (M_PI / 2.0f)); + float const distToHitPoint = std::max(objDist2d * std::cos(srcPos.GetRelativeAngle(*itr)) - size * sizeFactor, 0.0f); + float const height = distToHitPoint * (a * distToHitPoint + b); if (fabs(dz - height) > size + b / 2.0f + TRAJECTORY_MISSILE_SIZE) continue; @@ -1723,17 +1721,14 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge if (dist2d > bestDist) { - float x = m_targets.GetSrcPos()->m_positionX + std::cos(m_caster->GetOrientation()) * bestDist; - float y = m_targets.GetSrcPos()->m_positionY + std::sin(m_caster->GetOrientation()) * bestDist; + float x = m_targets.GetSrcPos()->m_positionX + std::cos(unitCaster->GetOrientation()) * bestDist; + float y = m_targets.GetSrcPos()->m_positionY + std::sin(unitCaster->GetOrientation()) * bestDist; float z = m_targets.GetSrcPos()->m_positionZ + bestDist * (a * bestDist + b); - SpellDestination dest(x, y, z, m_caster->GetOrientation()); + SpellDestination dest(x, y, z, unitCaster->GetOrientation()); CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); m_targets.ModDst(dest); } - - if (Vehicle* veh = m_caster->GetVehicleKit()) - veh->SetLastShootPos(*m_targets.GetDstPos()); } void Spell::SelectImplicitLineTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType, uint32 effMask) @@ -1809,9 +1804,9 @@ void Spell::SelectEffectTypeImplicitTargets(uint32 effIndex) { case SPELL_EFFECT_SUMMON_RAF_FRIEND: case SPELL_EFFECT_SUMMON_PLAYER: - if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_caster->GetTarget().IsEmpty()) + if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_caster->ToPlayer()->GetTarget().IsEmpty()) { - WorldObject* target = ObjectAccessor::FindPlayer(m_caster->GetTarget()); + WorldObject* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetTarget()); CallScriptObjectTargetSelectHandlers(target, SpellEffIndex(effIndex), SpellImplicitTargetInfo()); // scripts may modify the target - recheck @@ -1939,7 +1934,7 @@ uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionConta } template<class SEARCHER> -void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius) +void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius) { if (!containerMask) return; @@ -1979,7 +1974,7 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec return target; } -void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) { uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); if (!containerTypeMask) @@ -2093,7 +2088,7 @@ GameObject* Spell::SearchSpellFocus() GameObject* focus = nullptr; Trinity::GameObjectFocusCheck check(m_caster, m_spellInfo->RequiresSpellFocus); Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> searcher(m_caster, focus, check); - SearchTargets<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> > (searcher, GRID_MAP_TYPE_MASK_GAMEOBJECT, m_caster, m_caster, m_caster->GetVisibilityRange()); + SearchTargets(searcher, GRID_MAP_TYPE_MASK_GAMEOBJECT, m_caster, m_caster, m_caster->GetVisibilityRange()); return focus; } @@ -2210,7 +2205,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= return; if (checkIfValid) - if (m_spellInfo->CheckTarget(m_caster, target, implicit || m_caster->GetEntry() == WORLD_TRIGGER) != SPELL_CAST_OK) // skip stealth checks for GO casts + if (m_spellInfo->CheckTarget(m_caster, target, implicit) != SPELL_CAST_OK) // skip stealth checks for AOE return; // Check for effect immune skip if immuned @@ -2241,10 +2236,8 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= targetInfo.IsCrit = false; // Calculate hit result - if (m_originalCaster) - targetInfo.MissCondition = m_originalCaster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target))); - else - targetInfo.MissCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE; + WorldObject* caster = m_originalCaster ? m_originalCaster : m_caster; + targetInfo.MissCondition = caster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target))); // Spell have speed - need calculate incoming time // Incoming time is zero for self casts. At least I think so. @@ -2269,8 +2262,9 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= // If target reflect spell back to caster if (targetInfo.MissCondition == SPELL_MISS_REFLECT) { - // Calculate reflected spell result on caster - targetInfo.ReflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, false); // can't reflect twice + // Calculate reflected spell result on caster (shouldn't be able to reflect gameobject spells) + Unit* unitCaster = ASSERT_NOTNULL(m_caster->ToUnit()); + targetInfo.ReflectResult = unitCaster->SpellHitResult(unitCaster, m_spellInfo, false); // can't reflect twice // Proc spell reflect aura when missile hits the original target target->m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target->m_Events.CalculateTime(targetInfo.TimeDelay)); @@ -2385,7 +2379,7 @@ int64 Spell::GetUnitTargetCountForEffect(SpellEffIndex effect) const { return std::count_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [effect](TargetInfo const& targetInfo) { - return targetInfo.EffectMask & (1 << effect); + return targetInfo.MissCondition == SPELL_MISS_MISS && targetInfo.EffectMask & (1 << effect); }); } @@ -2407,7 +2401,7 @@ int64 Spell::GetItemTargetCountForEffect(SpellEffIndex effect) const void Spell::TargetInfo::PreprocessTarget(Spell* spell) { - Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); if (!unit) return; @@ -2422,7 +2416,7 @@ void Spell::TargetInfo::PreprocessTarget(Spell* spell) if (MissCondition == SPELL_MISS_NONE) _spellHitTarget = unit; else if (MissCondition == SPELL_MISS_REFLECT && ReflectResult == SPELL_MISS_NONE) - _spellHitTarget = spell->m_caster; + _spellHitTarget = spell->m_caster->ToUnit(); _enablePVP = false; // need to check PvP state before spell effects, but act on it afterwards if (_spellHitTarget) @@ -2435,7 +2429,7 @@ void Spell::TargetInfo::PreprocessTarget(Spell* spell) if (missInfo != SPELL_MISS_NONE) { if (missInfo != SPELL_MISS_MISS) - spell->m_caster->SendSpellMiss(unit, spell->m_spellInfo->Id, missInfo); + spell->m_caster->ToUnit()->SendSpellMiss(unit, spell->m_spellInfo->Id, missInfo); spell->m_damage = 0; _spellHitTarget = nullptr; } @@ -2450,7 +2444,7 @@ void Spell::TargetInfo::PreprocessTarget(Spell* spell) void Spell::TargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) { - Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); if (!unit) return; @@ -2479,7 +2473,7 @@ void Spell::TargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) { - Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); + Unit* unit = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToUnit() : ObjectAccessor::GetUnit(*spell->m_caster, TargetGUID); if (!unit) return; @@ -2494,7 +2488,7 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) // Get original caster (if exist) and calculate damage/healing from him data // Skip if m_originalCaster not available - Unit* caster = spell->m_originalCaster ? spell->m_originalCaster : spell->m_caster; + Unit* caster = spell->m_originalCaster ? spell->m_originalCaster : spell->m_caster->ToUnit(); if (!caster) return; @@ -2640,8 +2634,9 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) // Failed Pickpocket, reveal rogue if (MissCondition == SPELL_MISS_RESIST && spell->m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && spell->unitTarget->GetTypeId() == TYPEID_UNIT) { - spell->m_caster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting); - spell->unitTarget->ToCreature()->EngageWithTarget(spell->m_caster); + Unit* unitCaster = ASSERT_NOTNULL(spell->m_caster->ToUnit()); + unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting); + spell->unitTarget->ToCreature()->EngageWithTarget(unitCaster); } } @@ -2669,7 +2664,8 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) // _spellHitTarget can be null if spell is missed in DoSpellHitOnUnit if (MissCondition != SPELL_MISS_EVADE && _spellHitTarget && !spell->m_caster->IsFriendlyTo(unit) && (!spell->IsPositive() || spell->m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL))) { - spell->m_caster->AttackedTarget(unit, spell->m_spellInfo->HasInitialAggro()); + if (Unit* unitCaster = spell->m_caster->ToUnit()) + unitCaster->AttackedTarget(unit, spell->m_spellInfo->HasInitialAggro()); if (!unit->IsStandState()) unit->SetStandState(UNIT_STAND_STATE_STAND); @@ -2683,11 +2679,20 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) { //AI functions if (_spellHitTarget->GetTypeId() == TYPEID_UNIT) + { if (_spellHitTarget->ToCreature()->IsAIEnabled) - _spellHitTarget->ToCreature()->AI()->SpellHit(spell->m_caster, spell->m_spellInfo); + { + if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT) + _spellHitTarget->ToCreature()->AI()->SpellHit(spell->m_caster->ToGameObject(), spell->m_spellInfo); + else + _spellHitTarget->ToCreature()->AI()->SpellHit(spell->m_caster->ToUnit(), spell->m_spellInfo); + } + } if (spell->m_caster->GetTypeId() == TYPEID_UNIT && spell->m_caster->ToCreature()->IsAIEnabled) spell->m_caster->ToCreature()->AI()->SpellHitTarget(_spellHitTarget, spell->m_spellInfo); + else if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT && spell->m_caster->ToGameObject()->AI()) + spell->m_caster->ToGameObject()->AI()->SpellHitTarget(_spellHitTarget, spell->m_spellInfo); // Needs to be called after dealing damage/healing to not remove breaking on damage auras spell->DoTriggersOnSpellHit(_spellHitTarget, EffectMask); @@ -2701,7 +2706,7 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) void Spell::GOTargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) { - GameObject* go = ObjectAccessor::GetGameObject(*spell->m_caster, TargetGUID); + GameObject* go = spell->m_caster->GetGUID() == TargetGUID ? spell->m_caster->ToGameObject() : ObjectAccessor::GetGameObject(*spell->m_caster, TargetGUID); if (!go) return; @@ -2709,8 +2714,19 @@ void Spell::GOTargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) spell->HandleEffects(nullptr, nullptr, go, effIndex, SPELL_EFFECT_HANDLE_HIT_TARGET); + //AI functions if (go->AI()) - go->AI()->SpellHit(spell->m_caster, spell->m_spellInfo); + { + if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT) + go->AI()->SpellHit(spell->m_caster->ToGameObject(), spell->m_spellInfo); + else + go->AI()->SpellHit(spell->m_caster->ToUnit(), spell->m_spellInfo); + } + + if (spell->m_caster->GetTypeId() == TYPEID_UNIT && spell->m_caster->ToCreature()->IsAIEnabled) + spell->m_caster->ToCreature()->AI()->SpellHitTarget(go, spell->m_spellInfo); + else if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT && spell->m_caster->ToGameObject()->AI()) + spell->m_caster->ToGameObject()->AI()->SpellHitTarget(go, spell->m_spellInfo); spell->CallScriptOnHitHandlers(); spell->CallScriptAfterHitHandlers(); @@ -2778,17 +2794,23 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) playerOwner->UpdatePvP(true); } } - if (unit->IsInCombat() && m_spellInfo->HasInitialAggro()) + + if (m_originalCaster && unit->IsInCombat() && m_spellInfo->HasInitialAggro()) { - if (m_caster->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) // only do explicit combat forwarding for PvP enabled units - m_caster->GetCombatManager().InheritCombatStatesFrom(unit); // for creature v creature combat, the threat forward does it for us - unit->GetThreatManager().ForwardThreatForAssistingMe(m_caster, 0.0f, nullptr, true); + if (m_originalCaster->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) // only do explicit combat forwarding for PvP enabled units + m_originalCaster->GetCombatManager().InheritCombatStatesFrom(unit); // for creature v creature combat, the threat forward does it for us + unit->GetThreatManager().ForwardThreatForAssistingMe(m_originalCaster, 0.0f, nullptr, true); } } } + // original caster for auras + WorldObject* origCaster = m_caster; + if (m_originalCaster) + origCaster = m_originalCaster; + // check immunity due to diminishing returns - if (m_originalCaster && Aura::BuildEffectMaskForOwner(m_spellInfo, MAX_EFFECT_MASK, unit)) + if (Aura::BuildEffectMaskForOwner(m_spellInfo, MAX_EFFECT_MASK, unit)) { for (SpellEffectInfo const* auraSpellEffect : m_spellInfo->GetEffects()) if (auraSpellEffect) @@ -2812,7 +2834,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) // Now Reduce spell duration using data received at spell hit // check whatever effects we're going to apply, diminishing returns only apply to negative aura effects hitInfo.Positive = true; - if (m_originalCaster == unit || !m_originalCaster->IsFriendlyTo(unit)) + if (origCaster == unit || !origCaster->IsFriendlyTo(unit)) { for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) { @@ -2831,7 +2853,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) hitInfo.AuraDuration = m_spellInfo->GetMaxDuration(); // unit is immune to aura if it was diminished to 0 duration - if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(m_spellInfo, hitInfo.AuraDuration, m_originalCaster, diminishLevel)) + if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(m_spellInfo, hitInfo.AuraDuration, origCaster, diminishLevel)) if (std::all_of(std::begin(m_spellInfo->GetEffects()), std::end(m_spellInfo->GetEffects()), [](SpellEffectInfo const* effInfo) { return !effInfo || !effInfo->IsEffect() || effInfo->IsEffect(SPELL_EFFECT_APPLY_AURA); })) return SPELL_MISS_IMMUNE; } @@ -2841,10 +2863,13 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) { - uint8 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit); - if (aura_effmask) + if (uint32 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit)) { + WorldObject* caster = m_caster; if (m_originalCaster) + caster = m_originalCaster; + + if (caster) { bool refresh = false; @@ -2855,7 +2880,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) AuraCreateInfo createInfo(m_castId, m_spellInfo, GetCastDifficulty(), allAuraEffectMask, unit); createInfo - .SetCaster(m_originalCaster) + .SetCasterGUID(caster->GetGUID()) .SetBaseAmount(&hitInfo.AuraBasePoints[0]) .SetCastItem(m_castItemGUID, m_castItemEntry, m_castItemLevel) .SetPeriodicReset(resetPeriodicTimer) @@ -2881,7 +2906,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) _spellAura->SetDiminishGroup(hitInfo.DRGroup); - hitInfo.AuraDuration = m_originalCaster->ModSpellDuration(m_spellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); + hitInfo.AuraDuration = caster->ModSpellDuration(m_spellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); if (hitInfo.AuraDuration > 0) { @@ -2889,7 +2914,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) // Haste modifies duration of channeled spells if (m_spellInfo->IsChanneled()) - m_originalCaster->ModSpellDurationTime(m_spellInfo, hitInfo.AuraDuration, this); + caster->ModSpellDurationTime(m_spellInfo, hitInfo.AuraDuration, this); else if (m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) { int32 origDuration = hitInfo.AuraDuration; @@ -2996,10 +3021,11 @@ bool Spell::UpdateChanneledTargetList() { if (targetInfo.MissCondition == SPELL_MISS_NONE && (channelTargetEffectMask & targetInfo.EffectMask)) { - Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); + Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); if (!unit) { - m_caster->RemoveChannelObject(targetInfo.TargetGUID); + if (Unit* unitCaster =m_caster->ToUnit()) + unitCaster->RemoveChannelObject(targetInfo.TargetGUID); continue; } @@ -3013,13 +3039,15 @@ bool Spell::UpdateChanneledTargetList() { targetInfo.EffectMask &= ~aurApp->GetEffectMask(); unit->RemoveAura(aurApp); - m_caster->RemoveChannelObject(targetInfo.TargetGUID); + if (Unit* unitCaster = m_caster->ToUnit()) + unitCaster->RemoveChannelObject(targetInfo.TargetGUID); continue; } } else // aura is dispelled { - m_caster->RemoveChannelObject(targetInfo.TargetGUID); + if (Unit* unitCaster = m_caster->ToUnit()) + unitCaster->RemoveChannelObject(targetInfo.TargetGUID); continue; } } @@ -3066,20 +3094,22 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered _spellEvent = new SpellEvent(this); m_caster->m_Events.AddEvent(_spellEvent, m_caster->m_Events.CalculateTime(1)); - //Prevent casting at cast another spell (ServerSide check) - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS) && m_caster->IsNonMeleeSpellCast(false, true, true, m_spellInfo->Id == 75) && !m_castId.IsEmpty()) + // check disables + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster)) { - SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); + SendCastResult(SPELL_FAILED_SPELL_UNAVAILABLE); finish(false); return; } - if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster)) + // Prevent casting at cast another spell (ServerSide check) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS) && m_caster->ToUnit() && m_caster->ToUnit()->IsNonMeleeSpellCast(false, true, true, m_spellInfo->Id == 75) && !m_castId.IsEmpty()) { - SendCastResult(SPELL_FAILED_SPELL_UNAVAILABLE); + SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); finish(false); return; } + LoadScripts(); // Fill cost data (do not use power for item casts) @@ -3087,7 +3117,7 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask, this); // Set combo point requirement - if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem || !m_caster->m_playerMovingMe) + if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem) m_needComboPoints = false; uint32 param1 = 0, param2 = 0; @@ -3137,8 +3167,8 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered // exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && - m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) + if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->ToPlayer()->IsCharmed() && m_caster->ToPlayer()->GetCharmerGUID().IsCreature()) && m_caster->ToPlayer()->isMoving() && + m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && !m_caster->ToPlayer()->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) { // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel if (m_casttime || !m_spellInfo->IsMoveAllowedChannel()) @@ -3150,7 +3180,7 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered } // focus if not controlled creature - if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) + if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->ToUnit()->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) { if (!(m_spellInfo->IsNextMeleeSwingSpell() || IsAutoRepeat())) { @@ -3176,12 +3206,15 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered cast(true); else { - // stealth must be removed at cast starting (at show channel bar) - // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && m_spellInfo->IsBreakingStealth() && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS)) - m_caster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action); + if (Unit* unitCaster = m_caster->ToUnit()) + { + // stealth must be removed at cast starting (at show channel bar) + // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && m_spellInfo->IsBreakingStealth() && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS)) + unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action); - m_caster->SetCurrentCastSpell(this); + unitCaster->SetCurrentCastSpell(this); + } SendSpellStart(); if (!(_triggeredCastFlags & TRIGGERED_IGNORE_GCD)) @@ -3218,7 +3251,7 @@ void Spell::cancel() case SPELL_STATE_CASTING: for (TargetInfo const& targetInfo : m_UniqueTargetInfo) if (targetInfo.MissCondition == SPELL_MISS_NONE) - if (Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID)) + if (Unit* unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID)) unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL); SendChannelUpdate(0); @@ -3236,9 +3269,13 @@ void Spell::cancel() if (m_selfContainer && *m_selfContainer == this) *m_selfContainer = nullptr; - m_caster->RemoveDynObject(m_spellInfo->Id); - if (m_spellInfo->IsChanneled()) // if not channeled then the object for the current cast wasn't summoned yet - m_caster->RemoveGameObject(m_spellInfo->Id, true); + // originalcaster handles gameobjects/dynobjects for gob caster + if (m_originalCaster) + { + m_originalCaster->RemoveDynObject(m_spellInfo->Id); + if (m_spellInfo->IsChanneled()) // if not channeled then the object for the current cast wasn't summoned yet + m_originalCaster->RemoveGameObject(m_spellInfo->Id, true); + } //set state back so finish will be processed m_spellState = oldState; @@ -3301,7 +3338,7 @@ void Spell::_cast(bool skipCheck) if (!(_triggeredCastFlags & TRIGGERED_IGNORE_SET_FACING)) if (m_caster->GetTypeId() == TYPEID_UNIT && m_targets.GetObjectTarget() && m_caster != m_targets.GetObjectTarget()) - m_caster->SetInFront(m_targets.GetObjectTarget()); + m_caster->ToCreature()->SetInFront(m_targets.GetObjectTarget()); // Should this be done for original caster? Player* modOwner = m_caster->GetSpellModOwner(); @@ -3371,11 +3408,13 @@ void Spell::_cast(bool skipCheck) DiminishingReturnsType type = m_spellInfo->GetDiminishingReturnsGroupType(); if (type == DRTYPE_ALL || (type == DRTYPE_PLAYER && target->IsAffectedByDiminishingReturns())) { - Unit* caster = m_originalCaster ? m_originalCaster : m_caster; - if (target->HasStrongerAuraWithDR(m_spellInfo, caster)) + if (Unit* caster = m_originalCaster ? m_originalCaster : m_caster->ToUnit()) { - cleanupSpell(SPELL_FAILED_AURA_BOUNCED); - return; + if (target->HasStrongerAuraWithDR(m_spellInfo, caster)) + { + cleanupSpell(SPELL_FAILED_AURA_BOUNCED); + return; + } } } } @@ -3385,10 +3424,10 @@ void Spell::_cast(bool skipCheck) // if the spell allows the creature to turn while casting, then adjust server-side orientation to face the target now // client-side orientation is handled by the client itself, as the cast target is targeted due to Creature::FocusTarget - if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) + if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->ToUnit()->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) if (!m_spellInfo->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST)) if (WorldObject* objTarget = m_targets.GetObjectTarget()) - m_caster->SetInFront(objTarget); + m_caster->ToCreature()->SetInFront(objTarget); SelectSpellTargets(); @@ -3407,9 +3446,10 @@ void Spell::_cast(bool skipCheck) return; } - if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) - if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, m_caster->GetPetGUID())) - pet->DespawnOrUnsummon(); + if (Unit* unitCaster = m_caster->ToUnit()) + if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) + if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, unitCaster->GetPetGUID())) + pet->DespawnOrUnsummon(); PrepareTriggersExecutedOnHit(); @@ -3472,8 +3512,9 @@ void Spell::_cast(bool skipCheck) m_spellState = SPELL_STATE_DELAYED; SetDelayStart(0); - if (m_caster->HasUnitState(UNIT_STATE_CASTING) && !m_caster->IsNonMeleeSpellCast(false, false, true)) - m_caster->ClearUnitState(UNIT_STATE_CASTING); + if (Unit* unitCaster = m_caster->ToUnit()) + if (unitCaster->HasUnitState(UNIT_STATE_CASTING) && !unitCaster->IsNonMeleeSpellCast(false, false, true)) + unitCaster->ClearUnitState(UNIT_STATE_CASTING); } else { @@ -3488,7 +3529,10 @@ void Spell::_cast(bool skipCheck) for (int32 id : *spell_triggered) { if (id < 0) - m_caster->RemoveAurasDueToSpell(-id); + { + if (Unit* unitCaster = m_caster->ToUnit()) + unitCaster->RemoveAurasDueToSpell(-id); + } else m_caster->CastSpell(m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster, id, true); } @@ -3499,10 +3543,10 @@ void Spell::_cast(bool skipCheck) modOwner->SetSpellModTakingSpell(this, false); //Clear spell cooldowns after every spell is cast if .cheat cooldown is enabled. - if (modOwner->GetCommandStatus(CHEAT_COOLDOWN)) + if (m_originalCaster && modOwner->GetCommandStatus(CHEAT_COOLDOWN)) { - m_caster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); - m_caster->GetSpellHistory()->RestoreCharge(m_spellInfo->ChargeCategoryId); + m_originalCaster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); + m_originalCaster->GetSpellHistory()->RestoreCharge(m_spellInfo->ChargeCategoryId); } } @@ -3569,17 +3613,16 @@ void Spell::handle_immediate() // Apply haste mods m_caster->ModSpellDurationTime(m_spellInfo, duration, this); - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags, m_spellInfo->ChannelInterruptFlags2); m_channeledDuration = duration; SendChannelStart(duration); } else if (duration == -1) - { - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags, m_spellInfo->ChannelInterruptFlags2); SendChannelStart(duration); - } + + m_spellState = SPELL_STATE_CASTING; + + // GameObjects shouldn't cast channeled spells + ASSERT_NOTNULL(m_caster->ToUnit())->AddInterruptMask(m_spellInfo->ChannelInterruptFlags, m_spellInfo->ChannelInterruptFlags2); } PrepareTargetProcessing(); @@ -3744,23 +3787,23 @@ void Spell::_handle_immediate_phase() void Spell::_handle_finish_phase() { - if (m_caster->m_playerMovingMe) + if (Unit* unitCaster = m_caster->ToUnit()) { // Take for real after all targets are processed if (m_needComboPoints) - m_caster->m_playerMovingMe->ClearComboPoints(); + unitCaster->ClearComboPoints(); // Real add combo points from effects if (m_comboPointGain) - m_caster->m_playerMovingMe->GainSpellComboPoints(m_comboPointGain); - } + unitCaster->AddComboPoints(m_comboPointGain); - if (m_caster->m_extraAttacks && m_spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) - { - if (Unit* victim = ObjectAccessor::GetUnit(*m_caster, m_targets.GetOrigUnitTargetGUID())) - m_caster->HandleProcExtraAttackFor(victim); - else - m_caster->m_extraAttacks = 0; + if (unitCaster->m_extraAttacks && m_spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) + { + if (Unit* victim = ObjectAccessor::GetUnit(*unitCaster, m_targets.GetOrigUnitTargetGUID())) + unitCaster->HandleProcExtraAttackFor(victim); + else + unitCaster->m_extraAttacks = 0; + } } // Handle procs on finish @@ -3781,10 +3824,13 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { + if (!m_caster->IsUnit()) + return; + if (m_CastItem) - m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); + m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); else - m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_castItemEntry, this); + m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_castItemEntry, this); } void Spell::update(uint32 difftime) @@ -3808,9 +3854,9 @@ void Spell::update(uint32 difftime) // with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect SpellEffectInfo const* effect = m_spellInfo->GetEffect(EFFECT_0); if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - m_caster->isMoving() && (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && - ((effect && effect->Effect != SPELL_EFFECT_STUCK) || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)) && - !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) + m_caster->ToPlayer()->isMoving() && (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && + ((effect && effect->Effect != SPELL_EFFECT_STUCK) || !m_caster->ToPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)) && + !m_caster->ToPlayer()->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) { // don't cancel for melee, autorepeat, triggered and instant spells if (!m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->IsMoveAllowedChannel())) @@ -3818,7 +3864,7 @@ void Spell::update(uint32 difftime) // if charmed by creature, trust the AI not to cheat and allow the cast to proceed // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now // however, checking what type of movement the spline is for every single spline would be really expensive - if (!m_caster->GetCharmerGUID().IsCreature()) + if (!m_caster->ToPlayer()->GetCharmerGUID().IsCreature()) cancel(); } } @@ -3852,7 +3898,7 @@ void Spell::update(uint32 difftime) // Also remove applied auras for (TargetInfo const& target : m_UniqueTargetInfo) - if (Unit* unit = m_caster->GetGUID() == target.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.TargetGUID)) + if (Unit* unit = m_caster->GetGUID() == target.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, target.TargetGUID)) unit->RemoveOwnedAura(m_spellInfo->Id, m_originalCasterGUID, 0, AURA_REMOVE_BY_CANCEL); } @@ -3879,44 +3925,48 @@ void Spell::update(uint32 difftime) void Spell::finish(bool ok) { + if (m_spellState == SPELL_STATE_FINISHED) + return; + m_spellState = SPELL_STATE_FINISHED; + if (!m_caster) return; - if (m_spellState == SPELL_STATE_FINISHED) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) return; - m_spellState = SPELL_STATE_FINISHED; if (m_spellInfo->IsChanneled()) - m_caster->UpdateInterruptMask(); + unitCaster->UpdateInterruptMask(); - if (m_caster->HasUnitState(UNIT_STATE_CASTING) && !m_caster->IsNonMeleeSpellCast(false, false, true)) - m_caster->ClearUnitState(UNIT_STATE_CASTING); + if (unitCaster->HasUnitState(UNIT_STATE_CASTING) && !unitCaster->IsNonMeleeSpellCast(false, false, true)) + unitCaster->ClearUnitState(UNIT_STATE_CASTING); // Unsummon summon as possessed creatures on spell cancel - if (m_spellInfo->IsChanneled() && m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_spellInfo->IsChanneled() && unitCaster->GetTypeId() == TYPEID_PLAYER) { - if (Unit* charm = m_caster->GetCharm()) + if (Unit* charm = unitCaster->GetCharm()) if (charm->GetTypeId() == TYPEID_UNIT && charm->ToCreature()->HasUnitTypeMask(UNIT_MASK_PUPPET) && charm->m_unitData->CreatedBySpell == int32(m_spellInfo->Id)) ((Puppet*)charm)->UnSummon(); } - if (Creature* creatureCaster = m_caster->ToCreature()) + if (Creature* creatureCaster = unitCaster->ToCreature()) creatureCaster->ReleaseFocus(this); if (!ok) return; - if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsSummon()) + if (unitCaster->GetTypeId() == TYPEID_UNIT && unitCaster->IsSummon()) { // Unsummon statue - uint32 spell = m_caster->m_unitData->CreatedBySpell; + uint32 spell = unitCaster->m_unitData->CreatedBySpell; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell, GetCastDifficulty()); if (spellInfo && spellInfo->IconFileDataId == 134230) { - TC_LOG_DEBUG("spells", "Statue %s is unsummoned in spell %d finish", m_caster->GetGUID().ToString().c_str(), m_spellInfo->Id); - m_caster->setDeathState(JUST_DIED); + TC_LOG_DEBUG("spells", "Statue %s is unsummoned in spell %d finish", unitCaster->GetGUID().ToString().c_str(), m_spellInfo->Id); + unitCaster->setDeathState(JUST_DIED); return; } } @@ -3925,23 +3975,23 @@ void Spell::finish(bool ok) { if (!m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) { - m_caster->resetAttackTimer(BASE_ATTACK); - if (m_caster->haveOffhandWeapon()) - m_caster->resetAttackTimer(OFF_ATTACK); - m_caster->resetAttackTimer(RANGED_ATTACK); + unitCaster->resetAttackTimer(BASE_ATTACK); + if (unitCaster->haveOffhandWeapon()) + unitCaster->resetAttackTimer(OFF_ATTACK); + unitCaster->resetAttackTimer(RANGED_ATTACK); } } // potions disabled by client, send event "not in combat" if need - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { if (!m_triggeredByAuraSpell) - m_caster->ToPlayer()->UpdatePotionCooldown(this); + unitCaster->ToPlayer()->UpdatePotionCooldown(this); } // Stop Attack for some spells if (m_spellInfo->HasAttribute(SPELL_ATTR0_STOP_ATTACK_TARGET)) - m_caster->AttackStop(); + unitCaster->AttackStop(); } template<class T> @@ -4211,8 +4261,14 @@ void Spell::SendSpellStart() TC_LOG_DEBUG("spells", "Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY; - uint32 schoolImmunityMask = m_caster->GetSchoolImmunityMask(); - uint32 mechanicImmunityMask = m_caster->GetMechanicImmunityMask(); + uint32 schoolImmunityMask = 0; + uint32 mechanicImmunityMask = 0; + if (Unit* unitCaster = m_caster->ToUnit()) + { + schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); + mechanicImmunityMask = unitCaster->GetMechanicImmunityMask(); + } + if (schoolImmunityMask || mechanicImmunityMask) castFlags |= CAST_FLAG_IMMUNITY; @@ -4223,7 +4279,7 @@ void Spell::SendSpellStart() castFlags |= CAST_FLAG_PROJECTILE; if ((m_caster->GetTypeId() == TYPEID_PLAYER || - (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) + (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) && std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellPowerCost const& cost) { return cost.Power != POWER_HEALTH; }) != m_powerCost.end()) castFlags |= CAST_FLAG_POWER_LEFT_SELF; @@ -4255,7 +4311,7 @@ void Spell::SendSpellStart() { WorldPackets::Spells::SpellPowerData powerData; powerData.Type = cost.Power; - powerData.Cost = m_caster->GetPower(cost.Power); + powerData.Cost = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(cost.Power); castData.RemainingPower.push_back(powerData); } } @@ -4324,12 +4380,12 @@ void Spell::SendSpellGo() castFlags |= CAST_FLAG_PROJECTILE; // arrows/bullets visual if ((m_caster->GetTypeId() == TYPEID_PLAYER || - (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) + (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) && std::find_if(m_powerCost.begin(), m_powerCost.end(), [](SpellPowerCost const& cost) { return cost.Power != POWER_HEALTH; }) != m_powerCost.end()) - castFlags |= CAST_FLAG_POWER_LEFT_SELF; // should only be sent to self, but the current messaging doesn't make that possible + castFlags |= CAST_FLAG_POWER_LEFT_SELF; if ((m_caster->GetTypeId() == TYPEID_PLAYER) - && (m_caster->getClass() == CLASS_DEATH_KNIGHT) + && (m_caster->ToPlayer()->getClass() == CLASS_DEATH_KNIGHT) && HasPowerTypeCost(POWER_RUNES) && !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)) { @@ -4345,7 +4401,6 @@ void Spell::SendSpellGo() WorldPackets::Spells::SpellGo packet; WorldPackets::Spells::SpellCastData& castData = packet.Cast; - if (m_CastItem) castData.CasterGUID = m_CastItem->GetGUID(); else @@ -4370,7 +4425,7 @@ void Spell::SendSpellGo() { WorldPackets::Spells::SpellPowerData powerData; powerData.Type = cost.Power; - powerData.Cost = m_caster->GetPower(cost.Power); + powerData.Cost = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(cost.Power); castData.RemainingPower.push_back(powerData); } } @@ -4450,26 +4505,26 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) uint32 ammoInventoryType = 0; uint32 ammoDisplayID = 0; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (Player const* playerCaster = m_caster->ToPlayer()) { - Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK); + Item* pItem = playerCaster->GetWeaponForAttack(RANGED_ATTACK); if (pItem) { ammoInventoryType = pItem->GetTemplate()->GetInventoryType(); if (ammoInventoryType == INVTYPE_THROWN) - ammoDisplayID = pItem->GetDisplayId(m_caster->ToPlayer()); - else if (m_caster->HasAura(46699)) // Requires No Ammo + ammoDisplayID = pItem->GetDisplayId(playerCaster); + else if (playerCaster->HasAura(46699)) // Requires No Ammo { ammoDisplayID = 5996; // normal arrow ammoInventoryType = INVTYPE_AMMO; } } } - else + else if (Unit const* unitCaster = m_caster->ToUnit()) { - for (uint8 i = 0; i < 3; ++i) + for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) { - if (uint32 item_id = m_caster->GetVirtualItemId(i)) + if (uint32 item_id = unitCaster->GetVirtualItemId(i)) { if (ItemEntry const* itemEntry = sItemStore.LookupEntry(item_id)) { @@ -4478,7 +4533,7 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) switch (itemEntry->SubclassID) { case ITEM_SUBCLASS_WEAPON_THROWN: - ammoDisplayID = sDB2Manager.GetItemDisplayId(item_id, m_caster->GetVirtualItemAppearanceMod(i)); + ammoDisplayID = sDB2Manager.GetItemDisplayId(item_id, unitCaster->GetVirtualItemAppearanceMod(i)); ammoInventoryType = itemEntry->InventoryType; break; case ITEM_SUBCLASS_WEAPON_BOW: @@ -4532,6 +4587,8 @@ void Spell::SendSpellExecuteLog() spellLogEffect.FeedPetTargets = std::move(_feedPetTargets[effect->EffectIndex]); } + spellExecuteLog.LogData.Initialize(this); + if (!spellExecuteLog.Effects.empty()) m_caster->SendCombatLogMessage(&spellExecuteLog); } @@ -4647,30 +4704,40 @@ void Spell::SendInterrupted(uint8 result) void Spell::SendChannelUpdate(uint32 time) { + // GameObjects don't channel + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + if (time == 0) { - m_caster->ClearChannelObjects(); - m_caster->SetChannelSpellId(0); - m_caster->SetChannelVisual({}); + unitCaster->ClearChannelObjects(); + unitCaster->SetChannelSpellId(0); + unitCaster->SetChannelVisual({}); } WorldPackets::Spells::SpellChannelUpdate spellChannelUpdate; - spellChannelUpdate.CasterGUID = m_caster->GetGUID(); + spellChannelUpdate.CasterGUID = unitCaster->GetGUID(); spellChannelUpdate.TimeRemaining = time; - m_caster->SendMessageToSet(spellChannelUpdate.Write(), true); + unitCaster->SendMessageToSet(spellChannelUpdate.Write(), true); } void Spell::SendChannelStart(uint32 duration) { + // GameObjects don't channel + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + WorldPackets::Spells::SpellChannelStart spellChannelStart; - spellChannelStart.CasterGUID = m_caster->GetGUID(); + spellChannelStart.CasterGUID = unitCaster->GetGUID(); spellChannelStart.SpellID = m_spellInfo->Id; spellChannelStart.Visual = m_SpellVisual; spellChannelStart.ChannelDuration = duration; - m_caster->SendMessageToSet(spellChannelStart.Write(), true); + unitCaster->SendMessageToSet(spellChannelStart.Write(), true); - uint32 schoolImmunityMask = m_caster->GetSchoolImmunityMask(); - uint32 mechanicImmunityMask = m_caster->GetMechanicImmunityMask(); + uint32 schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); + uint32 mechanicImmunityMask = unitCaster->GetMechanicImmunityMask(); if (schoolImmunityMask || mechanicImmunityMask) { @@ -4701,35 +4768,36 @@ void Spell::SendChannelStart(uint32 duration) for (TargetInfo const& target : m_UniqueTargetInfo) { if (target.EffectMask & channelAuraMask) - m_caster->AddChannelObject(target.TargetGUID); + unitCaster->AddChannelObject(target.TargetGUID); if (m_UniqueTargetInfo.size() == 1 && m_UniqueGOTargetInfo.empty()) - if(target.TargetGUID != m_caster->GetGUID()) - if (Creature* creatureCaster = m_caster->ToCreature()) + if(target.TargetGUID != unitCaster->GetGUID()) + if (Creature* creatureCaster = unitCaster->ToCreature()) if (!creatureCaster->IsFocusing(this)) creatureCaster->FocusTarget(this, ObjectAccessor::GetWorldObject(*creatureCaster, target.TargetGUID)); } for (GOTargetInfo const& target : m_UniqueGOTargetInfo) if (target.EffectMask & channelAuraMask) - m_caster->AddChannelObject(target.TargetGUID); + unitCaster->AddChannelObject(target.TargetGUID); - m_caster->SetChannelSpellId(m_spellInfo->Id); - m_caster->SetChannelVisual(m_SpellVisual); + unitCaster->SetChannelSpellId(m_spellInfo->Id); + unitCaster->SetChannelVisual(m_SpellVisual); } void Spell::SendResurrectRequest(Player* target) { // get resurrector name for creature resurrections, otherwise packet will be not accepted // for player resurrections the name is looked up by guid - std::string const sentName(m_caster->GetTypeId() == TYPEID_PLAYER - ? "" : m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex())); + std::string sentName; + if (m_caster->GetTypeId() != TYPEID_PLAYER) + sentName = m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex()); WorldPackets::Spells::ResurrectRequest resurrectRequest; resurrectRequest.ResurrectOffererGUID = m_caster->GetGUID(); resurrectRequest.ResurrectOffererVirtualRealmAddress = GetVirtualRealmAddress(); resurrectRequest.Name = sentName; - resurrectRequest.Sickness = m_caster->GetTypeId() != TYPEID_PLAYER && m_caster->IsSpiritHealer(); // "you'll be afflicted with resurrection sickness" + resurrectRequest.Sickness = m_caster->IsUnit() && m_caster->ToUnit()->IsSpiritHealer(); // "you'll be afflicted with resurrection sickness" resurrectRequest.UseTimer = !m_spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_RESURRECTION_TIMER); if (Pet* pet = target->GetPet()) if (CharmInfo* charmInfo = pet->GetCharmInfo()) @@ -4809,13 +4877,18 @@ void Spell::TakeCastItem() void Spell::TakePower() { + // GameObjects don't use power + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + if (m_CastItem || m_triggeredByAuraSpell) return; //Don't take power if the spell is cast while .cheat power is enabled. - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { - if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_POWER)) + if (unitCaster->ToPlayer()->GetCommandStatus(CHEAT_POWER)) return; } @@ -4823,7 +4896,7 @@ void Spell::TakePower() { Powers powerType = Powers(cost.Power); bool hit = true; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { if (powerType == POWER_RAGE || powerType == POWER_ENERGY || powerType == POWER_RUNES) { @@ -4835,7 +4908,7 @@ void Spell::TakePower() { hit = false; //lower spell cost on fail (by talent aura) - if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) + if (Player* modOwner = unitCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo, SpellModOp::PowerCostOnMiss, cost.Amount); } } @@ -4854,7 +4927,7 @@ void Spell::TakePower() // health as power used if (powerType == POWER_HEALTH) { - m_caster->ModifyHealth(-cost.Amount); + unitCaster->ModifyHealth(-cost.Amount); continue; } @@ -4864,7 +4937,7 @@ void Spell::TakePower() continue; } - m_caster->ModifyPower(powerType, -cost.Amount); + unitCaster->ModifyPower(powerType, -cost.Amount); } } @@ -4898,7 +4971,7 @@ SpellCastResult Spell::CheckRuneCost() const void Spell::TakeRunePower(bool didHit) { - if (m_caster->GetTypeId() != TYPEID_PLAYER || m_caster->getClass() != CLASS_DEATH_KNIGHT) + if (m_caster->GetTypeId() != TYPEID_PLAYER || m_caster->ToPlayer()->getClass() != CLASS_DEATH_KNIGHT) return; Player* player = m_caster->ToPlayer(); @@ -4972,6 +5045,11 @@ void Spell::TakeReagents() void Spell::HandleThreatSpells() { + // wild GameObject spells don't cause threat + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return; + if (m_UniqueTargetInfo.empty()) return; @@ -4982,7 +5060,7 @@ void Spell::HandleThreatSpells() if (SpellThreatEntry const* threatEntry = sSpellMgr->GetSpellThreatEntry(m_spellInfo->Id)) { if (threatEntry->apPctMod != 0.0f) - threat += threatEntry->apPctMod * m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + threat += threatEntry->apPctMod * unitCaster->GetTotalAttackPowerValue(BASE_ATTACK); threat += threatEntry->flatMod; } @@ -5002,20 +5080,20 @@ void Spell::HandleThreatSpells() if (ihit->MissCondition != SPELL_MISS_NONE) threatToAdd = 0.0f; - Unit* target = ObjectAccessor::GetUnit(*m_caster, ihit->TargetGUID); + Unit* target = ObjectAccessor::GetUnit(*unitCaster, ihit->TargetGUID); if (!target) continue; // positive spells distribute threat among all units that are in combat with target, like healing if (IsPositive()) - target->GetThreatManager().ForwardThreatForAssistingMe(m_caster, threatToAdd, m_spellInfo); + target->GetThreatManager().ForwardThreatForAssistingMe(unitCaster, threatToAdd, m_spellInfo); // for negative spells threat gets distributed among affected targets else { if (!target->CanHaveThreatList()) continue; - target->GetThreatManager().AddThreat(m_caster, threatToAdd, m_spellInfo, true); + target->GetThreatManager().AddThreat(unitCaster, threatToAdd, m_spellInfo, true); } } TC_LOG_DEBUG("spells", "Spell %u, added an additional %f threat for %s %u target(s)", m_spellInfo->Id, threat, IsPositive() ? "assisting" : "harming", uint32(m_UniqueTargetInfo.size())); @@ -5028,6 +5106,7 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT itemTarget = pItemTarget; gameObjTarget = pGOTarget; destTarget = &m_destTargets[i]._position; + unitCaster = m_originalCaster ? m_originalCaster : m_caster->ToUnit(); effectInfo = m_spellInfo->GetEffect(i); if (!effectInfo) @@ -5035,18 +5114,15 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT TC_LOG_ERROR("spells", "Spell: %u HandleEffects at EffectIndex: %u missing effect", m_spellInfo->Id, i); return; } - uint32 eff = effectInfo->Effect; - - TC_LOG_DEBUG("spells", "Spell: %u Effect: %u", m_spellInfo->Id, eff); + uint32 effect = effectInfo->Effect; damage = CalculateDamage(i, unitTarget, &variance); - bool preventDefault = CallScriptEffectHandlers((SpellEffIndex)i, mode); + SpellEffIndex effIndex = static_cast<SpellEffIndex>(i); + bool preventDefault = CallScriptEffectHandlers(effIndex, mode); - if (!preventDefault && eff < TOTAL_SPELL_EFFECTS) - { - (this->*SpellEffects[eff].Value)((SpellEffIndex)i); - } + if (!preventDefault) + (this->*SpellEffectHandlers[effect].Value)(effIndex); } /*static*/ Spell const* Spell::ExtractSpellFromEvent(BasicEvent* event) @@ -5060,28 +5136,28 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) { // check death state - if (!m_caster->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) + if (m_caster->ToUnit() && !m_caster->ToUnit()->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) return SPELL_FAILED_CASTER_DEAD; // check cooldowns to prevent cheating if (!m_spellInfo->IsPassive()) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (Player const* playerCaster = m_caster->ToPlayer()) { //can cast triggered (by aura only?) spells while have this flag if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE)) { // These two auras check SpellFamilyName defined by db2 class data instead of current spell SpellFamilyName - if (m_caster->HasAuraType(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES) + if (playerCaster->HasAuraType(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES) && !m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !m_spellInfo->HasEffect(SPELL_EFFECT_ATTACK) && !m_spellInfo->HasAttribute(SPELL_ATTR12_IGNORE_CASTING_DISABLED) - && !m_caster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(m_caster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) + && !playerCaster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(playerCaster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; - if (m_caster->HasAuraType(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES)) + if (playerCaster->HasAuraType(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES)) { - if (!m_caster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(m_caster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) + if (!playerCaster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(playerCaster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) { if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->IsNextMeleeSwingSpell() @@ -5098,11 +5174,11 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // check if we are using a potion in combat for the 2nd+ time. Cooldown is added only after caster gets out of combat - if (!IsIgnoringCooldowns() && m_caster->ToPlayer()->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) + if (!IsIgnoringCooldowns() && playerCaster->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) return SPELL_FAILED_NOT_READY; } - if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) + if (m_caster->ToUnit() && !m_caster->ToUnit()->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) { if (m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; @@ -5111,7 +5187,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } } - if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && !m_caster->HasUnitFlag2(UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) + if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && m_caster->IsUnit() && !m_caster->ToUnit()->HasUnitFlag2(UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) { m_customError = SPELL_CUSTOM_ERROR_GM_ONLY; return SPELL_FAILED_CUSTOM_ERROR; @@ -5130,94 +5206,97 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_caster->GetTypeId() == TYPEID_PLAYER && VMAP::VMapFactory::createOrGetVMapManager()->isLineOfSightCalcEnabled()) { if (m_spellInfo->HasAttribute(SPELL_ATTR0_OUTDOORS_ONLY) && - !m_caster->IsOutdoors()) + !m_caster->IsOutdoors()) return SPELL_FAILED_ONLY_OUTDOORS; if (m_spellInfo->HasAttribute(SPELL_ATTR0_INDOORS_ONLY) && - m_caster->IsOutdoors()) + m_caster->IsOutdoors()) return SPELL_FAILED_ONLY_INDOORS; } - // only check at first call, Stealth auras are already removed at second call - // for now, ignore triggered spells - if (strict && !(_triggeredCastFlags & TRIGGERED_IGNORE_SHAPESHIFT)) + if (Unit* unitCaster = m_caster->ToUnit()) { - bool checkForm = true; - // Ignore form req aura - Unit::AuraEffectList const& ignore = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); - for (AuraEffect const* aurEff : ignore) + // only check at first call, Stealth auras are already removed at second call + // for now, ignore triggered spells + if (strict && !(_triggeredCastFlags & TRIGGERED_IGNORE_SHAPESHIFT)) { - if (!aurEff->IsAffectingSpell(m_spellInfo)) - continue; + bool checkForm = true; + // Ignore form req aura + Unit::AuraEffectList const& ignore = unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); + for (AuraEffect const* aurEff : ignore) + { + if (!aurEff->IsAffectingSpell(m_spellInfo)) + continue; - checkForm = false; - break; - } + checkForm = false; + break; + } - if (checkForm) - { - // Cannot be used in this stance/form - SpellCastResult shapeError = m_spellInfo->CheckShapeshift(m_caster->GetShapeshiftForm()); - if (shapeError != SPELL_CAST_OK) - return shapeError; + if (checkForm) + { + // Cannot be used in this stance/form + SpellCastResult shapeError = m_spellInfo->CheckShapeshift(unitCaster->GetShapeshiftForm()); + if (shapeError != SPELL_CAST_OK) + return shapeError; - if ((m_spellInfo->HasAttribute(SPELL_ATTR0_ONLY_STEALTHED)) && !(m_caster->HasStealthAura())) - return SPELL_FAILED_ONLY_STEALTHED; + if (m_spellInfo->HasAttribute(SPELL_ATTR0_ONLY_STEALTHED) && !(unitCaster->HasStealthAura())) + return SPELL_FAILED_ONLY_STEALTHED; + } } - } - bool reqCombat = true; - Unit::AuraEffectList const& stateAuras = m_caster->GetAuraEffectsByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); - for (Unit::AuraEffectList::const_iterator j = stateAuras.begin(); j != stateAuras.end(); ++j) - { - if ((*j)->IsAffectingSpell(m_spellInfo)) + bool reqCombat = true; + Unit::AuraEffectList const& stateAuras = unitCaster->GetAuraEffectsByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE); + for (Unit::AuraEffectList::const_iterator j = stateAuras.begin(); j != stateAuras.end(); ++j) { - m_needComboPoints = false; - if ((*j)->GetMiscValue() == 1) + if ((*j)->IsAffectingSpell(m_spellInfo)) { - reqCombat = false; - break; + m_needComboPoints = false; + if ((*j)->GetMiscValue() == 1) + { + reqCombat = false; + break; + } } } - } - // caster state requirements - // not for triggered spells (needed by execute) - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE)) - { - if (m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraStateType(m_spellInfo->CasterAuraState), m_spellInfo, m_caster)) - return SPELL_FAILED_CASTER_AURASTATE; - if (m_spellInfo->ExcludeCasterAuraState && m_caster->HasAuraState(AuraStateType(m_spellInfo->ExcludeCasterAuraState), m_spellInfo, m_caster)) - return SPELL_FAILED_CASTER_AURASTATE; + // caster state requirements + // not for triggered spells (needed by execute) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE)) + { + if (m_spellInfo->CasterAuraState && !unitCaster->HasAuraState(AuraStateType(m_spellInfo->CasterAuraState), m_spellInfo, unitCaster)) + return SPELL_FAILED_CASTER_AURASTATE; + if (m_spellInfo->ExcludeCasterAuraState && unitCaster->HasAuraState(AuraStateType(m_spellInfo->ExcludeCasterAuraState), m_spellInfo, unitCaster)) + return SPELL_FAILED_CASTER_AURASTATE; - // Note: spell 62473 requres casterAuraSpell = triggering spell - if (m_spellInfo->CasterAuraSpell && !m_caster->HasAura(m_spellInfo->CasterAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; - if (m_spellInfo->ExcludeCasterAuraSpell && m_caster->HasAura(m_spellInfo->ExcludeCasterAuraSpell)) - return SPELL_FAILED_CASTER_AURASTATE; + // Note: spell 62473 requres casterAuraSpell = triggering spell + if (m_spellInfo->CasterAuraSpell && !unitCaster->HasAura(m_spellInfo->CasterAuraSpell)) + return SPELL_FAILED_CASTER_AURASTATE; + if (m_spellInfo->ExcludeCasterAuraSpell && unitCaster->HasAura(m_spellInfo->ExcludeCasterAuraSpell)) + return SPELL_FAILED_CASTER_AURASTATE; - if (reqCombat && m_caster->IsInCombat() && !m_spellInfo->CanBeUsedInCombat()) - return SPELL_FAILED_AFFECTING_COMBAT; - } + if (reqCombat && unitCaster->IsInCombat() && !m_spellInfo->CanBeUsedInCombat()) + return SPELL_FAILED_AFFECTING_COMBAT; + } - // cancel autorepeat spells if cast start when moving - // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) - // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) - { - // skip stuck spell to allow use it in falling case and apply spell limitations at movement - SpellEffectInfo const* effect = m_spellInfo->GetEffect(EFFECT_0); - if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || (effect && effect->Effect != SPELL_EFFECT_STUCK)) && - (IsAutoRepeat() || m_spellInfo->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing))) - return SPELL_FAILED_MOVING; - } + // cancel autorepeat spells if cast start when moving + // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) + // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect + if (unitCaster->GetTypeId() == TYPEID_PLAYER && unitCaster->ToPlayer()->isMoving() && (!unitCaster->IsCharmed() || !unitCaster->GetCharmerGUID().IsCreature()) && !unitCaster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) + { + // skip stuck spell to allow use it in falling case and apply spell limitations at movement + SpellEffectInfo const* effect = m_spellInfo->GetEffect(EFFECT_0); + if ((!unitCaster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || (effect && effect->Effect != SPELL_EFFECT_STUCK)) && + (IsAutoRepeat() || m_spellInfo->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing))) + return SPELL_FAILED_MOVING; + } - // Check vehicle flags - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE)) - { - SpellCastResult vehicleCheck = m_spellInfo->CheckVehicle(m_caster); - if (vehicleCheck != SPELL_CAST_OK) - return vehicleCheck; + // Check vehicle flags + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE)) + { + SpellCastResult vehicleCheck = m_spellInfo->CheckVehicle(unitCaster); + if (vehicleCheck != SPELL_CAST_OK) + return vehicleCheck; + } } // check spell cast conditions from database @@ -5246,8 +5325,8 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!(m_spellInfo->IsPassive() && (!m_targets.GetUnitTarget() || m_targets.GetUnitTarget() == m_caster))) { // Check explicit target for m_originalCaster - todo: get rid of such workarounds - Unit* caster = m_caster; - if (m_originalCaster && m_caster->GetEntry() != WORLD_TRIGGER) // Do a simplified check for gameobject casts + WorldObject* caster = m_caster; + if (m_originalCaster) caster = m_originalCaster; SpellCastResult castResult = m_spellInfo->CheckExplicitTarget(caster, m_targets.GetObjectTarget(), m_targets.GetItemTarget()); @@ -5257,20 +5336,23 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (Unit* target = m_targets.GetUnitTarget()) { - SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, m_caster->GetEntry() == WORLD_TRIGGER); // skip stealth checks for GO casts + SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, m_caster->GetTypeId() == TYPEID_GAMEOBJECT); // skip stealth checks for GO casts if (castResult != SPELL_CAST_OK) return castResult; // If it's not a melee spell, check if vision is obscured by SPELL_AURA_INTERFERE_TARGETTING if (m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE) { - for (auto const& itr : m_caster->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING)) - if (!m_caster->IsFriendlyTo(itr->GetCaster()) && !target->HasAura(itr->GetId(), itr->GetCasterGUID())) - return SPELL_FAILED_VISION_OBSCURED; + if (Unit const* unitCaster = m_caster->ToUnit()) + { + for (AuraEffect const* auraEff : unitCaster->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING)) + if (!unitCaster->IsFriendlyTo(auraEff->GetCaster()) && !target->HasAura(auraEff->GetId(), auraEff->GetCasterGUID())) + return SPELL_FAILED_VISION_OBSCURED; - for (auto const& itr : target->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING)) - if (!m_caster->IsFriendlyTo(itr->GetCaster()) && (!target->HasAura(itr->GetId(), itr->GetCasterGUID()) || !m_caster->HasAura(itr->GetId(), itr->GetCasterGUID()))) - return SPELL_FAILED_VISION_OBSCURED; + for (AuraEffect const* auraEff : target->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING)) + if (!unitCaster->IsFriendlyTo(auraEff->GetCaster()) && (!target->HasAura(auraEff->GetId(), auraEff->GetCasterGUID()) || !unitCaster->HasAura(auraEff->GetId(), auraEff->GetCasterGUID()))) + return SPELL_FAILED_VISION_OBSCURED; + } } if (target != m_caster) @@ -5283,11 +5365,12 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if ((m_spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER)) && !target->HasInArc(static_cast<float>(M_PI), m_caster)) return SPELL_FAILED_NOT_INFRONT; - if (m_caster->GetEntry() != WORLD_TRIGGER) // Ignore LOS for gameobjects casts (wrongly cast by a trigger) + // Ignore LOS for gameobjects casts + if (m_caster->GetTypeId() != TYPEID_GAMEOBJECT) { WorldObject* losTarget = m_caster; if (IsTriggered() && m_triggeredByAuraSpell) - if (DynamicObject* dynObj = m_caster->GetDynObject(m_triggeredByAuraSpell->Id)) + if (DynamicObject* dynObj = m_caster->ToUnit()->GetDynObject(m_triggeredByAuraSpell->Id)) losTarget = dynObj; if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !m_spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) @@ -5307,23 +5390,26 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // check pet presence - for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) + if (Unit* unitCaster = m_caster->ToUnit()) { - if (effect && effect->TargetA.GetTarget() == TARGET_UNIT_PET) + for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) { - if (!m_caster->GetGuardianPet()) + if (effect && effect->TargetA.GetTarget() == TARGET_UNIT_PET) { - if (m_triggeredByAuraSpell) // not report pet not existence for triggered spells - return SPELL_FAILED_DONT_REPORT; - else - return SPELL_FAILED_NO_PET; + if (!unitCaster->GetGuardianPet()) + { + if (m_triggeredByAuraSpell) // not report pet not existence for triggered spells + return SPELL_FAILED_DONT_REPORT; + else + return SPELL_FAILED_NO_PET; + } + break; } - break; } } // Spell cast only in battleground - if ((m_spellInfo->HasAttribute(SPELL_ATTR3_BATTLEGROUND)) && m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_spellInfo->HasAttribute(SPELL_ATTR3_BATTLEGROUND) && m_caster->GetTypeId() == TYPEID_PLAYER) if (!m_caster->ToPlayer()->InBattleground()) return SPELL_FAILED_ONLY_BATTLEGROUNDS; @@ -5337,7 +5423,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // zone check - if (m_caster->GetTypeId() == TYPEID_UNIT || !m_caster->ToPlayer()->IsGameMaster()) + if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_caster->ToPlayer()->IsGameMaster()) { uint32 zone, area; m_caster->GetZoneAndAreaId(zone, area); @@ -5348,19 +5434,21 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // not let players cast spells at mount (and let do it to creatures) - if (m_caster->IsMounted() && m_caster->GetTypeId() == TYPEID_PLAYER && !(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE) && - !m_spellInfo->IsPassive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_MOUNTED)) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE)) { - if (m_caster->IsInFlight()) - return SPELL_FAILED_NOT_ON_TAXI; - else - return SPELL_FAILED_NOT_MOUNTED; + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->IsMounted() && !m_spellInfo->IsPassive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_MOUNTED)) + { + if (m_caster->ToPlayer()->IsInFlight()) + return SPELL_FAILED_NOT_ON_TAXI; + else + return SPELL_FAILED_NOT_MOUNTED; + } } // check spell focus object if (m_spellInfo->RequiresSpellFocus) { - if (!m_caster->HasAuraTypeWithMiscvalue(SPELL_AURA_PROVIDE_SPELL_FOCUS, m_spellInfo->RequiresSpellFocus)) + if (!m_caster->IsUnit() || !m_caster->ToUnit()->HasAuraTypeWithMiscvalue(SPELL_AURA_PROVIDE_SPELL_FOCUS, m_spellInfo->RequiresSpellFocus)) { focusObject = SearchSpellFocus(); if (!focusObject) @@ -5436,12 +5524,10 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint break; Pet* pet = m_caster->ToPlayer()->GetPet(); - if (!pet) return SPELL_FAILED_NO_PET; SpellInfo const* learn_spellproto = sSpellMgr->GetSpellInfo(effect->TriggerSpell, DIFFICULTY_NONE); - if (!learn_spellproto) return SPELL_FAILED_NOT_KNOWN; @@ -5549,7 +5635,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_BAD_TARGETS; Pet* pet = m_caster->ToPlayer()->GetPet(); - if (!pet) return SPELL_FAILED_NO_PET; @@ -5559,14 +5644,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (foodItem->GetTemplate()->GetBaseItemLevel() + 30 <= pet->getLevel()) return SPELL_FAILED_FOOD_LOWLEVEL; - if (m_caster->IsInCombat() || pet->IsInCombat()) + if (m_caster->ToPlayer()->IsInCombat() || pet->IsInCombat()) return SPELL_FAILED_AFFECTING_COMBAT; break; } case SPELL_EFFECT_CHARGE: { - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && m_caster->HasUnitState(UNIT_STATE_ROOT)) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && unitCaster->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; if (GetSpellInfo()->NeedsExplicitUnitTarget()) @@ -5576,13 +5665,13 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_DONT_REPORT; // first we must check to see if the target is in LoS. A path can usually be built but LoS matters for charge spells - if (!target->IsWithinLOSInMap(m_caster)) //Do full LoS/Path check. Don't exclude m2 + if (!target->IsWithinLOSInMap(unitCaster)) //Do full LoS/Path check. Don't exclude m2 return SPELL_FAILED_LINE_OF_SIGHT; float objSize = target->GetCombatReach(); - float range = m_spellInfo->GetMaxRange(true, m_caster, this) * 1.5f + objSize; // can't be overly strict + float range = m_spellInfo->GetMaxRange(true, unitCaster, this) * 1.5f + objSize; // can't be overly strict - m_preGeneratedPath = std::make_unique<PathGenerator>(m_caster); + m_preGeneratedPath = std::make_unique<PathGenerator>(unitCaster); m_preGeneratedPath->SetPathLengthLimit(range); // first try with raycast, if it fails fall back to normal path float targetObjectSize = std::min(target->GetCombatReach(), 4.0f); @@ -5683,28 +5772,35 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_RESURRECT_PET: { - Creature* pet = m_caster->GetGuardianPet(); + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + Creature* pet = unitCaster->GetGuardianPet(); if (pet && pet->IsAlive()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - break; } // This is generic summon effect case SPELL_EFFECT_SUMMON: { + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + break; + SummonPropertiesEntry const* SummonProperties = sSummonPropertiesStore.LookupEntry(effect->MiscValueB); if (!SummonProperties) break; + switch (SummonProperties->Control) { case SUMMON_CATEGORY_PET: - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && !m_caster->GetPetGUID().IsEmpty()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && !unitCaster->GetPetGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; /* fallthrough */ // intentional, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET case SUMMON_CATEGORY_PUPPET: - if (!m_caster->GetCharmGUID().IsEmpty()) + if (!unitCaster->GetCharmGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -5723,19 +5819,23 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_SUMMON_PET: { - if (!m_caster->GetPetGUID().IsEmpty()) //let warlock do a replacement summon + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (!unitCaster->GetPetGUID().IsEmpty()) //let warlock do a replacement summon { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player) - if (Pet* pet = m_caster->ToPlayer()->GetPet()) + if (Pet* pet = unitCaster->ToPlayer()->GetPet()) pet->CastSpell(pet, 32752, pet->GetGUID()); } else if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) return SPELL_FAILED_ALREADY_HAVE_SUMMON; } - if (!m_caster->GetCharmGUID().IsEmpty()) + if (!unitCaster->GetCharmGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -5743,7 +5843,8 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint { if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; - if (!m_caster->GetTarget()) + + if (!m_caster->ToPlayer()->GetTarget()) return SPELL_FAILED_BAD_TARGETS; Player* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetTarget()); @@ -5780,22 +5881,19 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_BAD_TARGETS; Player* playerCaster = m_caster->ToPlayer(); - // - if (!(playerCaster->GetTarget())) + if (!playerCaster->GetTarget()) return SPELL_FAILED_BAD_TARGETS; Player* target = playerCaster->GetSelectedPlayer(); - if (!target || !(target->GetSession()->GetRecruiterId() == playerCaster->GetSession()->GetAccountId() || target->GetSession()->GetAccountId() == playerCaster->GetSession()->GetRecruiterId())) return SPELL_FAILED_BAD_TARGETS; - break; } case SPELL_EFFECT_LEAP: case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER: { - //Do not allow to cast it before BG starts. + //Do not allow to cast it before BG starts. if (m_caster->GetTypeId() == TYPEID_PLAYER) if (Battleground const* bg = m_caster->ToPlayer()->GetBattleground()) if (bg->GetStatus() != STATUS_IN_PROGRESS) @@ -5810,9 +5908,13 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_LEAP_BACK: { - if (m_caster->HasUnitState(UNIT_STATE_ROOT)) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->HasUnitState(UNIT_STATE_ROOT)) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) return SPELL_FAILED_ROOTED; else return SPELL_FAILED_DONT_REPORT; @@ -5822,7 +5924,11 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint case SPELL_EFFECT_JUMP: case SPELL_EFFECT_JUMP_DEST: { - if (m_caster->HasUnitState(UNIT_STATE_ROOT)) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; break; } @@ -5833,7 +5939,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!player) return SPELL_FAILED_TARGET_NOT_PLAYER; - if (!spec || (spec->ClassID != m_caster->getClass() && !spec->IsPetSpecialization())) + if (!spec || (spec->ClassID != player->getClass() && !spec->IsPetSpecialization())) return SPELL_FAILED_NO_SPEC; if (spec->IsPetSpecialization()) @@ -5851,12 +5957,15 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_REMOVE_TALENT: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* playerCaster = m_caster->ToPlayer(); + if (!playerCaster) return SPELL_FAILED_BAD_TARGETS; + TalentEntry const* talent = sTalentStore.LookupEntry(m_misc.TalentId); if (!talent) return SPELL_FAILED_DONT_REPORT; - if (m_caster->GetSpellHistory()->HasCooldown(talent->SpellID)) + + if (playerCaster->GetSpellHistory()->HasCooldown(talent->SpellID)) { if (param1) *param1 = talent->SpellID; @@ -5867,14 +5976,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint case SPELL_EFFECT_GIVE_ARTIFACT_POWER: case SPELL_EFFECT_GIVE_ARTIFACT_POWER_NO_BONUS: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* playerCaster = m_caster->ToPlayer(); + if (!playerCaster) return SPELL_FAILED_BAD_TARGETS; - Aura* artifactAura = m_caster->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE); + + Aura* artifactAura = playerCaster->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE); if (!artifactAura) return SPELL_FAILED_NO_ARTIFACT_EQUIPPED; - Item* artifact = m_caster->ToPlayer()->GetItemByGuid(artifactAura->GetCastItemGUID()); + + Item* artifact = playerCaster->GetItemByGuid(artifactAura->GetCastItemGUID()); if (!artifact) return SPELL_FAILED_NO_ARTIFACT_EQUIPPED; + if (effect->Effect == SPELL_EFFECT_GIVE_ARTIFACT_POWER) { ArtifactEntry const* artifactEntry = sArtifactStore.LookupEntry(artifact->GetTemplate()->GetArtifactID()); @@ -5911,16 +6024,20 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint case SPELL_AURA_MOD_CHARM: case SPELL_AURA_AOE_CHARM: { - if (!m_caster->GetCharmerGUID().IsEmpty()) + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (!unitCaster->GetCharmerGUID().IsEmpty()) return SPELL_FAILED_CHARMED; if (effect->ApplyAuraName == SPELL_AURA_MOD_CHARM || effect->ApplyAuraName == SPELL_AURA_MOD_POSSESS) { - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && !m_caster->GetPetGUID().IsEmpty()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && !unitCaster->GetPetGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if (!m_caster->GetCharmGUID().IsEmpty()) + if (!unitCaster->GetCharmGUID().IsEmpty()) return SPELL_FAILED_ALREADY_HAVE_CHARM; } @@ -5947,23 +6064,26 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_AURA_MOUNTED: { - if (m_caster->IsInWater() && m_spellInfo->HasAura(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->IsInWater() && m_spellInfo->HasAura(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)) return SPELL_FAILED_ONLY_ABOVEWATER; // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells - bool allowMount = !m_caster->GetMap()->IsDungeon() || m_caster->GetMap()->IsBattlegroundOrArena(); - InstanceTemplate const* it = sObjectMgr->GetInstanceTemplate(m_caster->GetMapId()); + bool allowMount = !unitCaster->GetMap()->IsDungeon() || unitCaster->GetMap()->IsBattlegroundOrArena(); + InstanceTemplate const* it = sObjectMgr->GetInstanceTemplate(unitCaster->GetMapId()); if (it) allowMount = it->AllowMount; - if (m_caster->GetTypeId() == TYPEID_PLAYER && !allowMount && !m_spellInfo->RequiredAreasID) + if (unitCaster->GetTypeId() == TYPEID_PLAYER && !allowMount && !m_spellInfo->RequiredAreasID) return SPELL_FAILED_NO_MOUNTS_ALLOWED; - if (m_caster->IsInDisallowedMountForm()) + if (unitCaster->IsInDisallowedMountForm()) { SendMountResult(MountResult::Shapeshifted); // mount result gets sent before the cast result return SPELL_FAILED_DONT_REPORT; } - break; } case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS: @@ -5974,7 +6094,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // can be cast at non-friendly unit or own pet/charm if (m_caster->IsFriendlyTo(m_targets.GetUnitTarget())) return SPELL_FAILED_TARGET_FRIENDLY; - break; } case SPELL_AURA_FLY: @@ -6004,7 +6123,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_targets.GetUnitTarget()->GetPowerType() != POWER_MANA) return SPELL_FAILED_BAD_TARGETS; - break; } default: @@ -6022,7 +6140,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_NOT_TRADING; TradeData* my_trade = m_caster->ToPlayer()->GetTradeData(); - if (!my_trade) return SPELL_FAILED_NOT_TRADING; @@ -6046,7 +6163,8 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint SpellCastResult Spell::CheckPetCast(Unit* target) { - if (m_caster->HasUnitState(UNIT_STATE_CASTING) && !(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS)) //prevent spellcast interruption by another spellcast + Unit* unitCaster = m_caster->ToUnit(); + if (unitCaster && unitCaster->HasUnitState(UNIT_STATE_CASTING) && !(_triggeredCastFlags & TRIGGERED_IGNORE_CAST_IN_PROGRESS)) //prevent spellcast interruption by another spellcast return SPELL_FAILED_SPELL_IN_PROGRESS; // dead owner (pets still alive when owners ressed?) @@ -6071,7 +6189,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target) // Check if spell is affected by GCD if (m_spellInfo->StartRecoveryCategory > 0) - if (m_caster->GetCharmInfo() && m_caster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo)) + if (unitCaster && unitCaster->GetCharmInfo() && unitCaster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo)) return SPELL_FAILED_NOT_READY; return CheckCast(true); @@ -6079,6 +6197,10 @@ SpellCastResult Spell::CheckPetCast(Unit* target) SpellCastResult Spell::CheckCasterAuras(uint32* param1) const { + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return SPELL_CAST_OK; + // spells totally immuned to caster auras (wsg flag drop, give marks etc) if (m_spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CASTER_AURAS)) return SPELL_CAST_OK; @@ -6099,15 +6221,15 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const SpellCastResult result = SPELL_CAST_OK; // Get unit state - uint32 const unitflag = m_caster->m_unitData->Flags; + uint32 const unitflag = unitCaster->m_unitData->Flags; // this check should only be done when player does cast directly // (ie not when it's called from a script) Breaks for example PlayerAI when charmed /* - if (!m_caster->GetCharmerGUID().IsEmpty()) + if (!unitCaster->GetCharmerGUID().IsEmpty()) { - if (Unit* charmer = m_caster->GetCharmer()) - if (charmer->GetUnitBeingMoved() != m_caster && !CheckSpellCancelsCharm(param1)) + if (Unit* charmer = unitCaster->GetCharmer()) + if (charmer->GetUnitBeingMoved() != unitCaster && !CheckSpellCancelsCharm(param1)) result = SPELL_FAILED_CHARMED; } */ @@ -6116,7 +6238,7 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const auto mechanicCheck = [&](AuraType type) -> SpellCastResult { bool foundNotMechanic = false; - Unit::AuraEffectList const& auras = m_caster->GetAuraEffectsByType(type); + Unit::AuraEffectList const& auras = unitCaster->GetAuraEffectsByType(type); for (AuraEffect const* aurEff : auras) { uint32 const mechanicMask = aurEff->GetSpellInfo()->GetAllEffectsMechanicMask(); @@ -6192,7 +6314,7 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const else if (!CheckSpellCancelsConfuse(param1)) result = SPELL_FAILED_CONFUSED; } - else if (m_caster->HasUnitFlag2(UNIT_FLAG2_NO_ACTIONS) && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_NO_ACTIONS && !CheckSpellCancelsNoActions(param1)) + else if (unitCaster->HasUnitFlag2(UNIT_FLAG2_NO_ACTIONS) && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_NO_ACTIONS && !CheckSpellCancelsNoActions(param1)) result = SPELL_FAILED_NO_ACTIONS; // Attr must make flag drop spell totally immune from all effects @@ -6204,8 +6326,12 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const bool Spell::CheckSpellCancelsAuraEffect(AuraType auraType, uint32* param1) const { + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return false; + // Checking auras is needed now, because you are prevented by some state but the spell grants immunity. - Unit::AuraEffectList const& auraEffects = m_caster->GetAuraEffectsByType(auraType); + Unit::AuraEffectList const& auraEffects = unitCaster->GetAuraEffectsByType(auraType); if (auraEffects.empty()) return true; @@ -6298,10 +6424,10 @@ SpellCastResult Spell::CheckArenaAndRatedBattlegroundCastRules() return SPELL_CAST_OK; } -int32 Spell::CalculateDamage(uint8 i, Unit const* target, float* var /*= nullptr*/) const +int32 Spell::CalculateDamage(uint8 effIndex, Unit const* target, float* var /*= nullptr*/) const { - bool needRecalculateBasePoints = !(m_spellValue->CustomBasePointsMask & (1 << i)); - return m_caster->CalculateSpellDamage(target, m_spellInfo, i, needRecalculateBasePoints ? nullptr : &m_spellValue->EffectBasePoints[i], var, m_castItemEntry, m_castItemLevel); + bool needRecalculateBasePoints = !(m_spellValue->CustomBasePointsMask & (1 << effIndex)); + return m_caster->CalculateSpellDamage(target, m_spellInfo, effIndex, needRecalculateBasePoints ? nullptr : &m_spellValue->EffectBasePoints[effIndex], var, m_castItemEntry, m_castItemLevel); } bool Spell::CanAutoCast(Unit* target) @@ -6403,7 +6529,7 @@ SpellCastResult Spell::CheckRange(bool strict) const if (m_caster->GetTypeId() == TYPEID_PLAYER && (((m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast<float>(M_PI), target)) - && !m_caster->IsWithinBoundaryRadius(target))) + && !m_caster->ToPlayer()->IsWithinBoundaryRadius(target))) return SPELL_FAILED_UNIT_NOT_INFRONT; } @@ -6423,24 +6549,29 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) const float rangeMod = 0.0f; float minRange = 0.0f; float maxRange = 0.0f; + if (strict && m_spellInfo->IsNextMeleeSwingSpell()) - { - maxRange = 100.0f; - return std::pair<float, float>(minRange, maxRange); - } + return { 0.0f, 100.0f }; + Unit* unitCaster = m_caster->ToUnit(); if (m_spellInfo->RangeEntry) { Unit* target = m_targets.GetUnitTarget(); if (m_spellInfo->RangeEntry->Flags & SPELL_RANGE_MELEE) { - rangeMod = m_caster->GetMeleeRange(target ? target : m_caster); // when the target is not a unit, take the caster's combat reach as the target's combat reach. + // when the target is not a unit, take the caster's combat reach as the target's combat reach. + if (unitCaster) + rangeMod = unitCaster->GetMeleeRange(target ? target : unitCaster); } else { float meleeRange = 0.0f; if (m_spellInfo->RangeEntry->Flags & SPELL_RANGE_RANGED) - meleeRange = m_caster->GetMeleeRange(target ? target : m_caster); // when the target is not a unit, take the caster's combat reach as the target's combat reach. + { + // when the target is not a unit, take the caster's combat reach as the target's combat reach. + if (unitCaster) + meleeRange = unitCaster->GetMeleeRange(target ? target : unitCaster); + } minRange = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo) + meleeRange; maxRange = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo); @@ -6454,8 +6585,8 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) const } } - if (target && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking() && - (m_spellInfo->RangeEntry->Flags & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER)) + if (target && unitCaster && unitCaster->isMoving() && target->isMoving() && !unitCaster->IsWalking() && !target->IsWalking() && + ((m_spellInfo->RangeEntry->Flags & SPELL_RANGE_MELEE) || target->GetTypeId() == TYPEID_PLAYER)) rangeMod += 8.0f / 3.0f; } @@ -6468,11 +6599,15 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) const maxRange += rangeMod; - return std::pair<float, float>(minRange, maxRange); + return { minRange, maxRange }; } SpellCastResult Spell::CheckPower() const { + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_CAST_OK; + // item cast not used power if (m_CastItem) return SPELL_CAST_OK; @@ -6482,7 +6617,7 @@ SpellCastResult Spell::CheckPower() const // health as power used - need check health amount if (cost.Power == POWER_HEALTH) { - if (int32(m_caster->GetHealth()) <= cost.Amount) + if (int64(unitCaster->GetHealth()) <= cost.Amount) return SPELL_FAILED_CASTER_AURASTATE; continue; } @@ -6502,7 +6637,7 @@ SpellCastResult Spell::CheckPower() const } // Check power amount - if (int32(m_caster->GetPower(cost.Power)) < cost.Amount) + if (int32(unitCaster->GetPower(cost.Power)) < cost.Amount) return SPELL_FAILED_NO_POWER; } @@ -6612,7 +6747,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / // Not own traded item (in trader trade slot) requires reagents even if triggered spell if (!checkReagents) if (Item* targetItem = m_targets.GetItemTarget()) - if (targetItem->GetOwnerGUID() != m_caster->GetGUID()) + if (targetItem->GetOwnerGUID() != player->GetGUID()) checkReagents = true; // check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case. @@ -6707,24 +6842,24 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / case SPELL_EFFECT_CREATE_LOOT: { // m_targets.GetUnitTarget() means explicit cast, otherwise we dont check for possible equip error - Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster; - if (target && target->GetTypeId() == TYPEID_PLAYER && !IsTriggered() && effect->ItemType) + Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player; + if (target->GetTypeId() == TYPEID_PLAYER && !IsTriggered() && effect->ItemType) { ItemPosCountVec dest; - InventoryResult msg = target->ToPlayer()->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, effect->ItemType, 1); if (msg != EQUIP_ERR_OK) { - ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(effect->ItemType); + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(effect->ItemType); /// @todo Needs review - if (pProto && !(pProto->GetItemLimitCategory())) + if (itemTemplate && !(itemTemplate->GetItemLimitCategory())) { player->SendEquipError(msg, nullptr, nullptr, effect->ItemType); return SPELL_FAILED_DONT_REPORT; } else { - if (!(m_spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (m_spellInfo->SpellFamilyFlags[0] & 0x40000000))) + // Conjure Food/Water/Refreshment spells + if (m_spellInfo->SpellFamilyName != SPELLFAMILY_MAGE || (!(m_spellInfo->SpellFamilyFlags[0] & 0x40000000))) return SPELL_FAILED_TOO_MANY_OF_ITEM; else if (!(target->ToPlayer()->HasItemCount(effect->ItemType))) { @@ -6732,7 +6867,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_DONT_REPORT; } else if (SpellEffectInfo const* efi = m_spellInfo->GetEffect(EFFECT_1)) - player->CastSpell(m_caster, efi->CalcValue(), false); // move this to anywhere + player->CastSpell(player, efi->CalcValue(), false); // move this to anywhere return SPELL_FAILED_DONT_REPORT; } } @@ -6744,7 +6879,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / && (m_targets.GetItemTarget()->IsVellum())) { // cannot enchant vellum for other player - if (m_targets.GetItemTarget()->GetOwner() != m_caster) + if (m_targets.GetItemTarget()->GetOwner() != player) return SPELL_FAILED_NOT_TRADEABLE; // do not allow to enchant vellum from scroll made by vellum-prevent exploit if (m_CastItem && m_CastItem->GetTemplate()->GetFlags() & ITEM_FLAG_NO_REAGENT_COST) @@ -6806,7 +6941,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / } // Not allow enchant in trade slot for some enchant type - if (targetItem->GetOwner() != m_caster) + if (targetItem->GetOwner() != player) { if (!enchantEntry) return SPELL_FAILED_ERROR; @@ -6821,7 +6956,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!item) return SPELL_FAILED_ITEM_NOT_FOUND; // Not allow enchant in trade slot for some enchant type - if (item->GetOwner() != m_caster) + if (item->GetOwner() != player) { uint32 enchant_id = effect->MiscValue; SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); @@ -6851,7 +6986,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_CANT_BE_DISENCHANTED; // prevent disenchanting in trade slot - if (item->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_DISENCHANTED; ItemTemplate const* itemProto = item->GetTemplate(); @@ -6874,7 +7009,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->GetFlags() & ITEM_FLAG_IS_PROSPECTABLE)) return SPELL_FAILED_CANT_BE_PROSPECTED; //prevent prospecting in trade slot - if (item->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_PROSPECTED; //Check for enough skill in jewelcrafting uint32 item_prospectingskilllevel = item->GetTemplate()->GetRequiredSkillRank(); @@ -6905,7 +7040,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->GetFlags() & ITEM_FLAG_IS_MILLABLE)) return SPELL_FAILED_CANT_BE_MILLED; //prevent milling in trade slot - if (item->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_MILLED; //Check for enough skill in inscription uint32 item_millingskilllevel = item->GetTemplate()->GetRequiredSkillRank(); @@ -6933,15 +7068,15 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (m_attackType != RANGED_ATTACK) break; - Item* pItem = player->GetWeaponForAttack(m_attackType); - if (!pItem || pItem->IsBroken()) + Item* item = player->GetWeaponForAttack(m_attackType); + if (!item || item->IsBroken()) return SPELL_FAILED_EQUIPPED_ITEM; - switch (pItem->GetTemplate()->GetSubClass()) + switch (item->GetTemplate()->GetSubClass()) { case ITEM_SUBCLASS_WEAPON_THROWN: { - uint32 ammo = pItem->GetEntry(); + uint32 const ammo = item->GetEntry(); if (!player->HasItemCount(ammo)) return SPELL_FAILED_NO_AMMO; break; @@ -7011,9 +7146,9 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / // check weapon presence in slots for main/offhand weapons if (!(_triggeredCastFlags & TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT) && m_spellInfo->EquippedItemClass >= 0) { - auto weaponCheck = [this](WeaponAttackType attackType) -> SpellCastResult + auto weaponCheck = [&](WeaponAttackType attackType) -> SpellCastResult { - Item const* item = m_caster->ToPlayer()->GetWeaponForAttack(attackType); + Item const* item = player->GetWeaponForAttack(attackType); // skip spell if no weapon in slot or broken if (!item || item->IsBroken()) @@ -7046,20 +7181,20 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / void Spell::Delayed() // only called in DealDamage() { - if (!m_caster)// || m_caster->GetTypeId() != TYPEID_PLAYER) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) return; - //if (m_spellState == SPELL_STATE_DELAYED) - // return; // spell is active and can't be time-backed - - if (isDelayableNoMore()) // Spells may only be delayed twice + if (IsDelayableNoMore()) // Spells may only be delayed twice return; //check pushback reduce int32 delaytime = 500; // spellcasting delay is normally 500ms + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo, SpellModOp::ResistPushback, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if (Player* player = unitCaster->GetSpellModOwner()) + player->ApplySpellMod(m_spellInfo, SpellModOp::ResistPushback, delayReduce, this); + delayReduce += unitCaster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -7073,21 +7208,23 @@ void Spell::Delayed() // only called in DealDamage() else m_timer += delaytime; - TC_LOG_DEBUG("spells", "Spell %u partially interrupted for (%d) ms at damage", m_spellInfo->Id, delaytime); - WorldPackets::Spells::SpellDelayed spellDelayed; - spellDelayed.Caster = m_caster->GetGUID(); + spellDelayed.Caster = unitCaster->GetGUID(); spellDelayed.ActualDelay = delaytime; - m_caster->SendMessageToSet(spellDelayed.Write(), true); + unitCaster->SendMessageToSet(spellDelayed.Write(), true); } void Spell::DelayedChannel() { - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) return; - if (isDelayableNoMore()) // Spells may only be delayed twice + if (m_spellState != SPELL_STATE_CASTING) + return; + + if (IsDelayableNoMore()) // Spells may only be delayed twice return; //check pushback reduce @@ -7095,9 +7232,11 @@ void Spell::DelayedChannel() int32 duration = ((m_channeledDuration > 0) ? m_channeledDuration : m_spellInfo->GetDuration()); int32 delaytime = CalculatePct(duration, 25); // channeling delay is normally 25% of its time per hit + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo, SpellModOp::ResistPushback, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if (Player* player = unitCaster->GetSpellModOwner()) + player->ApplySpellMod(m_spellInfo, SpellModOp::ResistPushback, delayReduce, this); + delayReduce += unitCaster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -7111,15 +7250,13 @@ void Spell::DelayedChannel() else m_timer -= delaytime; - TC_LOG_DEBUG("spells", "Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer); - for (TargetInfo const& targetInfo : m_UniqueTargetInfo) if (targetInfo.MissCondition == SPELL_MISS_NONE) - if (Unit* unit = (m_caster->GetGUID() == targetInfo.TargetGUID) ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID)) + if (Unit* unit = (unitCaster->GetGUID() == targetInfo.TargetGUID) ? unitCaster : ObjectAccessor::GetUnit(*unitCaster, targetInfo.TargetGUID)) unit->DelayOwnedAuras(m_spellInfo->Id, m_originalCasterGUID, delaytime); // partially interrupt persistent area auras - if (DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id)) + if (DynamicObject* dynObj = unitCaster->GetDynObject(m_spellInfo->Id)) dynObj->Delay(delaytime); SendChannelUpdate(m_timer); @@ -7136,7 +7273,7 @@ bool Spell::HasPowerTypeCost(Powers power) const bool Spell::UpdatePointers() { if (m_originalCasterGUID == m_caster->GetGUID()) - m_originalCaster = m_caster; + m_originalCaster = m_caster->ToUnit(); else { m_originalCaster = ObjectAccessor::GetUnit(*m_caster, m_originalCasterGUID); @@ -7341,7 +7478,7 @@ bool Spell::IsProcDisabled() const bool Spell::IsChannelActive() const { - return m_caster->GetChannelSpellId() != 0; + return m_caster->IsUnit() && m_caster->ToUnit()->GetChannelSpellId() != 0; } bool Spell::IsAutoActionResetSpell() const @@ -7544,29 +7681,30 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, Spe Unit* unit = nullptr; // In case spell hit target, do all effect on that target if (targetInfo.MissCondition == SPELL_MISS_NONE) - unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); + unit = m_caster->GetGUID() == targetInfo.TargetGUID ? m_caster->ToUnit() : ObjectAccessor::GetUnit(*m_caster, targetInfo.TargetGUID); // In case spell reflect from target, do all effect on caster (if hit) else if (targetInfo.MissCondition == SPELL_MISS_REFLECT && targetInfo.ReflectResult == SPELL_MISS_NONE) - unit = m_caster; + unit = m_caster->ToUnit(); + if (!unit) return; // This will only cause combat - the target will engage once the projectile hits (in DoAllEffectOnTarget) - if (targetInfo.MissCondition != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || unit->IsEngaged())) - m_caster->SetInCombatWith(unit); + if (m_originalCaster && targetInfo.MissCondition != SPELL_MISS_EVADE && !m_originalCaster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || unit->IsEngaged())) + m_originalCaster->SetInCombatWith(unit); m_damage = 0; m_healing = 0; HandleEffects(unit, nullptr, nullptr, effect->EffectIndex, SPELL_EFFECT_HANDLE_LAUNCH_TARGET); - if (m_damage > 0) + if (m_originalCaster && m_damage > 0) { if (effect->IsTargetingArea() || effect->IsAreaAuraEffect() || effect->IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA)) { - m_damage = unit->CalculateAOEAvoidance(m_damage, m_spellInfo->SchoolMask, m_caster->GetGUID()); + m_damage = unit->CalculateAOEAvoidance(m_damage, m_spellInfo->SchoolMask, m_originalCaster->GetGUID()); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_originalCaster->GetTypeId() == TYPEID_PLAYER) { // cap damage of player AOE int64 targetAmount = GetUnitTargetCountForEffect(SpellEffIndex(effect->EffectIndex)); @@ -7588,9 +7726,14 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, Spe targetInfo.Healing += m_healing; float critChance = m_spellValue->CriticalChance; - if (!critChance) - critChance = m_caster->SpellCritChanceDone(this, nullptr, m_spellSchoolMask, m_attackType); - targetInfo.IsCrit = roll_chance_f(unit->SpellCritChanceTaken(m_caster, this, nullptr, m_spellSchoolMask, critChance, m_attackType)); + if (m_originalCaster) + { + if (!critChance) + critChance = m_originalCaster->SpellCritChanceDone(this, nullptr, m_spellSchoolMask, m_attackType); + critChance = unit->SpellCritChanceTaken(m_originalCaster, this, nullptr, m_spellSchoolMask, critChance, m_attackType); + } + + targetInfo.IsCrit = roll_chance_f(critChance); } SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) @@ -7598,6 +7741,10 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk if (!lockId) // possible case for GO and maybe for items. return SPELL_CAST_OK; + Unit const* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + // Get LockInfo LockEntry const* lockInfo = sLockStore.LookupEntry(lockId); @@ -7637,10 +7784,10 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk // castitem check: rogue using skeleton keys. the skill values should not be added in this case. skillValue = 0; - if (!m_CastItem && m_caster->GetTypeId() == TYPEID_PLAYER) - skillValue = m_caster->ToPlayer()->GetSkillValue(skillId); + if (!m_CastItem && unitCaster->GetTypeId() == TYPEID_PLAYER) + skillValue = unitCaster->ToPlayer()->GetSkillValue(skillId); else if (lockInfo->Index[j] == LOCKTYPE_LOCKPICKING) - skillValue = m_caster->getLevel() * 5; + skillValue = unitCaster->getLevel() * 5; // skill bonus provided by casting spell (mostly item spells) // add the effect base points modifier from the spell cast (cheat lock / skeleton key etc.) @@ -7964,10 +8111,14 @@ bool Spell::CanExecuteTriggersOnHit(uint32 effMask, SpellInfo const* triggeredBy void Spell::PrepareTriggersExecutedOnHit() { + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + // handle SPELL_AURA_ADD_TARGET_TRIGGER auras: // save auras which were present on spell caster on cast, to prevent triggered auras from affecting caster // and to correctly calculate proc chance when combopoints are present - Unit::AuraEffectList const& targetTriggers = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER); + Unit::AuraEffectList const& targetTriggers = unitCaster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER); for (AuraEffect const* aurEff : targetTriggers) { if (!aurEff->IsAffectingSpell(m_spellInfo)) @@ -7979,7 +8130,7 @@ void Spell::PrepareTriggersExecutedOnHit() // this possibly needs fixing int32 auraBaseAmount = aurEff->GetBaseAmount(); // proc chance is stored in effect amount - int32 chance = m_caster->CalculateSpellDamage(nullptr, aurEff->GetSpellInfo(), aurEff->GetEffIndex(), &auraBaseAmount); + int32 chance = unitCaster->CalculateSpellDamage(nullptr, aurEff->GetSpellInfo(), aurEff->GetEffIndex(), &auraBaseAmount); chance *= aurEff->GetBase()->GetStackAmount(); // build trigger and add to the list @@ -7995,23 +8146,30 @@ enum GCDLimits MAX_GCD = 1500 }; -bool Spell::HasGlobalCooldown() const +bool CanHaveGlobalCooldown(WorldObject const* caster) { // Only players or controlled units have global cooldown - if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) + if (caster->GetTypeId() != TYPEID_PLAYER && (caster->GetTypeId() != TYPEID_UNIT || !const_cast<WorldObject*>(caster)->ToCreature()->GetCharmInfo())) return false; - return m_caster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo); + return true; +} + +bool Spell::HasGlobalCooldown() const +{ + if (!CanHaveGlobalCooldown(m_caster)) + return false; + + return m_caster->ToUnit()->GetSpellHistory()->HasGlobalCooldown(m_spellInfo); } void Spell::TriggerGlobalCooldown() { - int32 gcd = m_spellInfo->StartRecoveryTime; - if (!gcd || !m_spellInfo->StartRecoveryCategory) + if (!CanHaveGlobalCooldown(m_caster)) return; - // Only players or controlled units have global cooldown - if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) + int32 gcd = m_spellInfo->StartRecoveryTime; + if (!gcd || !m_spellInfo->StartRecoveryCategory) return; if (m_caster->GetTypeId() == TYPEID_PLAYER) @@ -8035,58 +8193,55 @@ void Spell::TriggerGlobalCooldown() // Apply haste rating if (gcd > MIN_GCD && ((m_spellInfo->StartRecoveryCategory == 133 && !isMeleeOrRangedSpell))) { - gcd = int32(float(gcd) * m_caster->m_unitData->ModSpellHaste); + gcd = int32(float(gcd) * m_caster->ToUnit()->m_unitData->ModSpellHaste); RoundToInterval<int32>(gcd, MIN_GCD, MAX_GCD); } - if (gcd > MIN_GCD && m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN, m_spellInfo)) + if (gcd > MIN_GCD && m_caster->ToUnit()->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN, m_spellInfo)) { - gcd = int32(float(gcd) * m_caster->m_unitData->ModHasteRegen); + gcd = int32(float(gcd) * m_caster->ToUnit()->m_unitData->ModHasteRegen); RoundToInterval<int32>(gcd, MIN_GCD, MAX_GCD); } } - m_caster->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); + m_caster->ToUnit()->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); } void Spell::CancelGlobalCooldown() { - if (!m_spellInfo->StartRecoveryTime) + if (!CanHaveGlobalCooldown(m_caster)) return; - // Cancel global cooldown when interrupting current cast - if (m_caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) + if (!m_spellInfo->StartRecoveryTime) return; - // Only players or controlled units have global cooldown - if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) + // Cancel global cooldown when interrupting current cast + if (m_caster->ToUnit()->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) return; - m_caster->GetSpellHistory()->CancelGlobalCooldown(m_spellInfo); + m_caster->ToUnit()->GetSpellHistory()->CancelGlobalCooldown(m_spellInfo); } namespace Trinity { -WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) : - _caster(caster), _referer(referer), _spellInfo(spellInfo), _targetSelectionType(selectionType), _condList(condList), _objectType(objectType) +WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : _caster(caster), _referer(referer), _spellInfo(spellInfo), + _targetSelectionType(selectionType), _condSrcInfo(nullptr), _condList(condList), _objectType(objectType) { if (condList) - _condSrcInfo = new ConditionSourceInfo(nullptr, caster); - else - _condSrcInfo = nullptr; + _condSrcInfo = std::make_unique<ConditionSourceInfo>(nullptr, caster); } WorldObjectSpellTargetCheck::~WorldObjectSpellTargetCheck() { - delete _condSrcInfo; } -bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const { if (_spellInfo->CheckTarget(_caster, target, true) != SPELL_CAST_OK) return false; + Unit* unitTarget = target->ToUnit(); if (Corpse* corpseTarget = target->ToCorpse()) { @@ -8097,6 +8252,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; } + Unit* refUnit = _referer->ToUnit(); if (unitTarget) { // do only faction checks here @@ -8105,7 +8261,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) case TARGET_CHECK_ENEMY: if (unitTarget->IsTotem()) return false; - if (!_caster->IsValidAttackTarget(unitTarget, _spellInfo, nullptr, false)) + if (!_caster->IsValidAttackTarget(unitTarget, _spellInfo, false)) return false; break; case TARGET_CHECK_ALLY: @@ -8115,23 +8271,29 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; break; case TARGET_CHECK_PARTY: + if (!refUnit) + return false; if (unitTarget->IsTotem()) return false; if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) return false; - if (!_referer->IsInPartyWith(unitTarget)) + if (!refUnit->IsInPartyWith(unitTarget)) return false; break; case TARGET_CHECK_RAID_CLASS: - if (_referer->getClass() != unitTarget->getClass()) + if (!refUnit) + return false; + if (refUnit->getClass() != unitTarget->getClass()) return false; /* fallthrough */ case TARGET_CHECK_RAID: + if (!refUnit) + return false; if (unitTarget->IsTotem()) return false; if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) return false; - if (!_referer->IsInRaidWith(unitTarget)) + if (!refUnit->IsInRaidWith(unitTarget)) return false; break; case TARGET_CHECK_SUMMONED: @@ -8141,7 +8303,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; break; case TARGET_CHECK_THREAT: - if (_referer->GetThreatManager().GetThreat(unitTarget, true) <= 0.0f) + if (!_referer->IsUnit() || _referer->ToUnit()->GetThreatManager().GetThreat(unitTarget, true) <= 0.0f) return false; break; case TARGET_CHECK_TAP: @@ -8185,8 +8347,8 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return sConditionMgr->IsObjectMeetToConditions(*_condSrcInfo, *_condList); } -WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) +WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, WorldObject* caster, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList, objectType), _range(range), _position(caster) { } bool WorldObjectSpellNearbyTargetCheck::operator()(WorldObject* target) @@ -8200,11 +8362,11 @@ bool WorldObjectSpellNearbyTargetCheck::operator()(WorldObject* target) return false; } -WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) +WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster, + WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList, objectType), _range(range), _position(position) { } -bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) const { if (target->ToGameObject()) { @@ -8223,11 +8385,11 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) return WorldObjectSpellTargetCheck::operator ()(target); } -WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float lineWidth, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) +WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float lineWidth, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList, objectType), _coneAngle(coneAngle), _lineWidth(lineWidth) { } -bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) const { if (_spellInfo->HasAttribute(SPELL_ATTR0_CU_CONE_BACK)) { @@ -8241,7 +8403,7 @@ bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) } else { - if (!_caster->IsWithinBoundaryRadius(target->ToUnit())) + if (!_caster->IsUnit() || !_caster->ToUnit()->IsWithinBoundaryRadius(target->ToUnit())) // ConeAngle > 0 -> select targets in front // ConeAngle < 0 -> select targets in back if (_caster->HasInArc(_coneAngle, target) != G3D::fuzzyGe(_coneAngle, 0.f)) @@ -8250,10 +8412,10 @@ bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) return WorldObjectSpellAreaTargetCheck::operator ()(target); } -WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) +WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, WorldObject* caster, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList, objectType), _range(range), _position(position) { } -bool WorldObjectSpellTrajTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellTrajTargetCheck::operator()(WorldObject* target) const { // return all targets on missile trajectory (0 - size of a missile) if (!_caster->HasInLine(target, target->GetCombatReach(), TRAJECTORY_MISSILE_SIZE)) @@ -8265,11 +8427,11 @@ bool WorldObjectSpellTrajTargetCheck::operator()(WorldObject* target) return WorldObjectSpellTargetCheck::operator ()(target); } -WorldObjectSpellLineTargetCheck::WorldObjectSpellLineTargetCheck(Position const* srcPosition, Position const* dstPosition, float lineWidth, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType) +WorldObjectSpellLineTargetCheck::WorldObjectSpellLineTargetCheck(Position const* srcPosition, Position const* dstPosition, float lineWidth, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType) : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList, objectType), _srcPosition(srcPosition), _dstPosition(dstPosition), _lineWidth(lineWidth) { } -bool WorldObjectSpellLineTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellLineTargetCheck::operator()(WorldObject* target) const { float angle = _caster->GetOrientation(); if (*_srcPosition != *_dstPosition) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index ef971cccac2..a72a83ecd06 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -275,8 +275,7 @@ class TC_GAME_API SpellCastTargets float GetSpeedXY() const { return m_speed * std::cos(m_pitch); } float GetSpeedZ() const { return m_speed * std::sin(m_pitch); } - void Update(Unit* caster); - void OutDebug() const; + void Update(WorldObject* caster); std::string GetTargetString() const { return m_strTarget; } private: @@ -301,7 +300,7 @@ class TC_GAME_API SpellCastTargets struct SpellValue { - explicit SpellValue(SpellInfo const* proto, Unit const* caster); + explicit SpellValue(SpellInfo const* proto, WorldObject const* caster); int32 EffectBasePoints[MAX_SPELL_EFFECTS]; uint32 CustomBasePointsMask; uint32 MaxAffectedTargets; @@ -496,7 +495,7 @@ class TC_GAME_API Spell typedef std::unordered_set<Aura*> UsedSpellMods; - Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID = ObjectGuid::Empty); + Spell(WorldObject* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID = ObjectGuid::Empty); ~Spell(); void InitExplicitTargets(SpellCastTargets const& targets); @@ -520,10 +519,10 @@ class TC_GAME_API Spell void SelectEffectTypeImplicitTargets(uint32 effIndex); uint32 GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionContainer* condList); - template<class SEARCHER> void SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius); + template<class SEARCHER> void SearchTargets(SEARCHER& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius); WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList = nullptr); - void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList); + void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList); void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, ConditionContainer* condList, bool isChainHeal); GameObject* SearchSpellFocus(); @@ -565,7 +564,7 @@ class TC_GAME_API Spell bool CheckSpellCancelsConfuse(uint32* param1) const; bool CheckSpellCancelsNoActions(uint32* param1) const; - int32 CalculateDamage(uint8 i, Unit const* target, float* var = nullptr) const; + int32 CalculateDamage(uint8 effIndex, Unit const* target, float* var = nullptr) const; void Delayed(); void DelayedChannel(); @@ -685,7 +684,7 @@ class TC_GAME_API Spell CurrentSpellTypes GetCurrentContainer() const; - Unit* GetCaster() const { return m_caster; } + WorldObject* GetCaster() const { return m_caster; } Unit* GetOriginalCaster() const { return m_originalCaster; } SpellInfo const* GetSpellInfo() const { return m_spellInfo; } Difficulty GetCastDifficulty() const; @@ -717,7 +716,7 @@ class TC_GAME_API Spell void SendLoot(ObjectGuid guid, LootType loottype); std::pair<float, float> GetMinMaxRange(bool strict) const; - Unit* const m_caster; + WorldObject* const m_caster; SpellValue* const m_spellValue; @@ -737,12 +736,12 @@ class TC_GAME_API Spell uint8 m_runesState; uint8 m_delayAtDamageCount; - bool isDelayableNoMore() + bool IsDelayableNoMore() { if (m_delayAtDamageCount >= 2) return true; - m_delayAtDamageCount++; + ++m_delayAtDamageCount; return false; } @@ -770,6 +769,7 @@ class TC_GAME_API Spell SpellEffectHandleMode effectHandleMode; SpellEffectInfo const* effectInfo; // used in effects handlers + Unit* unitCaster; UnitAura* _spellAura; DynObjAura* _dynObjAura; @@ -912,7 +912,6 @@ class TC_GAME_API Spell // effect helpers void SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numSummons, ObjectGuid privateObjectOwner); - void CalculateJumpSpeeds(SpellEffectInfo const* effInfo, float dist, float& speedxy, float& speedz); void UpdateSpellCastDataTargets(WorldPackets::Spells::SpellCastData& data); void UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& data); @@ -948,26 +947,29 @@ namespace Trinity { struct TC_GAME_API WorldObjectSpellTargetCheck { - Unit* _caster; - Unit* _referer; - SpellInfo const* _spellInfo; - SpellTargetCheckTypes _targetSelectionType; - ConditionSourceInfo* _condSrcInfo; - ConditionContainer* _condList; + protected: + WorldObject* _caster; + WorldObject* _referer; + SpellInfo const* _spellInfo; + SpellTargetCheckTypes _targetSelectionType; + std::unique_ptr<ConditionSourceInfo> _condSrcInfo; + ConditionContainer const* _condList; SpellTargetObjectTypes _objectType; - WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType); - ~WorldObjectSpellTargetCheck(); - bool operator()(WorldObject* target); + WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType); + ~WorldObjectSpellTargetCheck(); + + bool operator()(WorldObject* target) const; }; struct TC_GAME_API WorldObjectSpellNearbyTargetCheck : public WorldObjectSpellTargetCheck { float _range; Position const* _position; - WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType); + WorldObjectSpellNearbyTargetCheck(float range, WorldObject* caster, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType); + bool operator()(WorldObject* target); }; @@ -975,27 +977,30 @@ namespace Trinity { float _range; Position const* _position; - WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType); - bool operator()(WorldObject* target); + WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster, + WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType); + + bool operator()(WorldObject* target) const; }; struct TC_GAME_API WorldObjectSpellConeTargetCheck : public WorldObjectSpellAreaTargetCheck { float _coneAngle; float _lineWidth; - WorldObjectSpellConeTargetCheck(float coneAngle, float lineWidth, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType); - bool operator()(WorldObject* target); + WorldObjectSpellConeTargetCheck(float coneAngle, float lineWidth, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType); + + bool operator()(WorldObject* target) const; }; struct TC_GAME_API WorldObjectSpellTrajTargetCheck : public WorldObjectSpellTargetCheck { float _range; Position const* _position; - WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType); - bool operator()(WorldObject* target); + WorldObjectSpellTrajTargetCheck(float range, Position const* position, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType); + + bool operator()(WorldObject* target) const; }; struct TC_GAME_API WorldObjectSpellLineTargetCheck : public WorldObjectSpellAreaTargetCheck @@ -1003,12 +1008,13 @@ namespace Trinity Position const* _srcPosition; Position const* _dstPosition; float _lineWidth; - WorldObjectSpellLineTargetCheck(Position const* srcPosition, Position const* dstPosition, float lineWidth, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList, SpellTargetObjectTypes objectType); - bool operator()(WorldObject* target); + WorldObjectSpellLineTargetCheck(Position const* srcPosition, Position const* dstPosition, float lineWidth, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType); + + bool operator()(WorldObject* target) const; }; } -typedef void(Spell::*pEffect)(SpellEffIndex effIndex); +typedef void(Spell::*SpellEffectHandlerFn)(SpellEffIndex effIndex); #endif diff --git a/src/server/game/Spells/SpellDefines.h b/src/server/game/Spells/SpellDefines.h index 77801e9a43f..907e867480e 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -279,7 +279,7 @@ struct TC_GAME_API CastSpellExtraArgs struct { friend struct CastSpellExtraArgs; - friend class Unit; + friend class WorldObject; private: void AddMod(SpellValueMod mod, int32 val) { data.push_back({ mod, val }); } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 1551517dcb5..f076af9662a 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -78,7 +78,7 @@ #include "WorldPacket.h" #include "WorldSession.h" -NonDefaultConstructible<pEffect> SpellEffects[TOTAL_SPELL_EFFECTS] = +NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EFFECTS] = { &Spell::EffectNULL, // 0 &Spell::EffectInstaKill, // 1 SPELL_EFFECT_INSTAKILL @@ -425,7 +425,7 @@ void Spell::EffectInstaKill(SpellEffIndex /*effIndex*/) data.SpellID = m_spellInfo->Id; m_caster->SendMessageToSet(data.Write(), true); - Unit::DealDamage(m_caster, unitTarget, unitTarget->GetHealth(), nullptr, NODAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); + Unit::DealDamage(unitCaster, unitTarget, unitTarget->GetHealth(), nullptr, NODAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); } void Spell::EffectEnvironmentalDMG(SpellEffIndex /*effIndex*/) @@ -441,16 +441,17 @@ void Spell::EffectEnvironmentalDMG(SpellEffIndex /*effIndex*/) unitTarget->ToPlayer()->EnvironmentalDamage(DAMAGE_FIRE, damage); else { - DamageInfo damageInfo(m_caster, unitTarget, damage, m_spellInfo, m_spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE, BASE_ATTACK); + DamageInfo damageInfo(unitCaster, unitTarget, damage, m_spellInfo, m_spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE, BASE_ATTACK); Unit::CalcAbsorbResist(damageInfo); - SpellNonMeleeDamage log(m_caster, unitTarget, m_spellInfo, m_SpellVisual, m_spellInfo->GetSchoolMask(), m_castId); + SpellNonMeleeDamage log(unitCaster, unitTarget, m_spellInfo, m_SpellVisual, m_spellInfo->GetSchoolMask(), m_castId); log.damage = damageInfo.GetDamage(); log.originalDamage = damage; log.absorb = damageInfo.GetAbsorb(); log.resist = damageInfo.GetResist(); - m_caster->SendSpellNonMeleeDamageLog(&log); + if (unitCaster) + unitCaster->SendSpellNonMeleeDamageLog(&log); } } @@ -475,11 +476,14 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) { case SPELLFAMILY_GENERIC: { + ///@todo: move those to scripts switch (m_spellInfo->Id) // better way to check unknown { // Consumption case 28865: - damage = (m_caster->GetMap()->GetDifficultyID() == DIFFICULTY_NONE ? 2750 : 4250); + damage = 2750; + if (m_caster->GetMap()->IsHeroic()) + damage = 4250; break; // percent from health with min case 25599: // Thundercrash @@ -492,16 +496,17 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) // arcane charge. must only affect demons (also undead?) case 45072: { - if (unitTarget->GetCreatureType() != CREATURE_TYPE_DEMON - && unitTarget->GetCreatureType() != CREATURE_TYPE_UNDEAD) + if (!(unitTarget->GetCreatureTypeMask() & CREATURE_TYPEMASK_DEMON_OR_UNDEAD)) return; break; } // Gargoyle Strike case 51963: { + damage = 60; // about +4 base spell dmg per level - damage = (m_caster->getLevel() - 60) * 4 + 60; + if (unitCaster && unitCaster->getLevel() >= 60) + damage += (unitCaster->getLevel() - 60) * 4; break; } } @@ -509,15 +514,18 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } case SPELLFAMILY_WARRIOR: { + if (!unitCaster) + break; + // Victory Rush if (m_spellInfo->Id == 34428) - ApplyPct(damage, m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); + ApplyPct(damage, unitCaster->GetTotalAttackPowerValue(BASE_ATTACK)); // Shockwave else if (m_spellInfo->Id == 46968) { - int32 pct = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, 2); + int32 pct = unitCaster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_2); if (pct > 0) - damage += int32(CalculatePct(m_caster->GetTotalAttackPowerValue(BASE_ATTACK), pct)); + damage += int32(CalculatePct(unitCaster->GetTotalAttackPowerValue(BASE_ATTACK), pct)); break; } break; @@ -536,24 +544,27 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } case SPELLFAMILY_DEATHKNIGHT: { + if (!unitCaster) + break; + // Blood Boil - bonus for diseased targets if (m_spellInfo->SpellFamilyFlags[0] & 0x00040000) { - if (unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT, flag128(0, 0, 0x00000002), m_caster->GetGUID())) + if (unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT, flag128(0, 0, 0x00000002), unitCaster->GetGUID())) { damage += m_damage / 2; - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.035f); + damage += int32(unitCaster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.035f); } } break; } } - if (m_originalCaster && apply_direct_bonus) + if (unitCaster && apply_direct_bonus) { - uint32 bonus = m_originalCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effectInfo); + uint32 bonus = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effectInfo); damage = bonus + uint32(bonus * variance); - damage = unitTarget->SpellDamageBonusTaken(m_originalCaster, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE); + damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE); } m_damage += damage; @@ -702,7 +713,12 @@ void Spell::EffectTriggerSpell(SpellEffIndex /*effIndex*/) if (Unit* target = m_targets.GetUnitTarget()) targets.SetUnitTarget(target); else - targets.SetUnitTarget(m_caster); + { + if (Unit* unit = m_caster->ToUnit()) + targets.SetUnitTarget(unit); + else if (GameObject* go = m_caster->ToGameObject()) + targets.SetGOTarget(go); + } } CastSpellExtraArgs args(m_originalCasterGUID); @@ -746,7 +762,10 @@ void Spell::EffectTriggerMissileSpell(SpellEffIndex /*effIndex*/) if (spellInfo->GetExplicitTargetMask() & TARGET_FLAG_DEST_LOCATION) targets.SetDst(m_targets); - targets.SetUnitTarget(m_caster); + if (Unit* unit = m_caster->ToUnit()) + targets.SetUnitTarget(unit); + else if (GameObject* go = m_caster->ToGameObject()) + targets.SetGOTarget(go); } CastSpellExtraArgs args(m_originalCasterGUID); @@ -831,23 +850,38 @@ void Spell::EffectTriggerRitualOfSummoning(SpellEffIndex /*effIndex*/) m_caster->CastSpell(nullptr, spellInfo->Id, false); } +inline void CalculateJumpSpeeds(SpellEffectInfo const* effInfo, float dist, float& speedXY, float& speedZ) +{ + if (effInfo->MiscValue) + speedZ = float(effInfo->MiscValue) / 10; + else if (effInfo->MiscValueB) + speedZ = float(effInfo->MiscValueB) / 10; + else + speedZ = 10.0f; + + speedXY = dist * 10.0f / speedZ; +} + void Spell::EffectJump(SpellEffIndex /*effIndex*/) { if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET) return; - if (m_caster->IsInFlight()) + if (!unitCaster) + return; + + if (unitCaster->IsInFlight()) return; if (!unitTarget) return; float speedXY, speedZ; - CalculateJumpSpeeds(effectInfo, m_caster->GetExactDist2d(unitTarget), speedXY, speedZ); + CalculateJumpSpeeds(effectInfo, unitCaster->GetExactDist2d(unitTarget), speedXY, speedZ); JumpArrivalCastArgs arrivalCast; arrivalCast.SpellId = effectInfo->TriggerSpell; arrivalCast.Target = unitTarget->GetGUID(); - m_caster->GetMotionMaster()->MoveJump(*unitTarget, speedXY, speedZ, EVENT_JUMP, false, &arrivalCast); + unitCaster->GetMotionMaster()->MoveJump(*unitTarget, speedXY, speedZ, EVENT_JUMP, false, &arrivalCast); } void Spell::EffectJumpDest(SpellEffIndex /*effIndex*/) @@ -855,29 +889,20 @@ void Spell::EffectJumpDest(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH) return; - if (m_caster->IsInFlight()) + if (!unitCaster) + return; + + if (unitCaster->IsInFlight()) return; if (!m_targets.HasDst()) return; float speedXY, speedZ; - CalculateJumpSpeeds(effectInfo, m_caster->GetExactDist2d(destTarget), speedXY, speedZ); + CalculateJumpSpeeds(effectInfo, unitCaster->GetExactDist2d(destTarget), speedXY, speedZ); JumpArrivalCastArgs arrivalCast; arrivalCast.SpellId = effectInfo->TriggerSpell; - m_caster->GetMotionMaster()->MoveJump(*destTarget, speedXY, speedZ, EVENT_JUMP, !m_targets.GetObjectTargetGUID().IsEmpty(), &arrivalCast); -} - -void Spell::CalculateJumpSpeeds(SpellEffectInfo const* effInfo, float dist, float& speedXY, float& speedZ) -{ - if (effInfo->MiscValue) - speedZ = float(effInfo->MiscValue) / 10; - else if (effInfo->MiscValueB) - speedZ = float(effInfo->MiscValueB) / 10; - else - speedZ = 10.0f; - - speedXY = dist * 10.0f / speedZ; + unitCaster->GetMotionMaster()->MoveJump(*destTarget, speedXY, speedZ, EVENT_JUMP, !m_targets.GetObjectTargetGUID().IsEmpty(), &arrivalCast); } void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) @@ -1065,27 +1090,27 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex) return; Powers powerType = Powers(effectInfo->MiscValue); - if (!unitTarget || !unitTarget->IsAlive() || unitTarget->GetPowerType() != powerType || damage < 0) return; // add spell damage bonus - uint32 bonus = m_caster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effectInfo); - damage = bonus + uint32(bonus * variance); - damage = unitTarget->SpellDamageBonusTaken(m_caster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); + if (unitCaster) + { + uint32 bonus = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effectInfo); + damage = bonus + uint32(bonus * variance); + damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); + } int32 newDamage = -(unitTarget->ModifyPower(powerType, -damage)); - float gainMultiplier = 0.0f; - // Don't restore from self drain - if (m_caster != unitTarget) + float gainMultiplier = 0.f; + if (unitCaster && unitCaster != unitTarget) { - gainMultiplier = effectInfo->CalcValueMultiplier(m_originalCaster, this); - - int32 gain = int32(newDamage* gainMultiplier); + gainMultiplier = effectInfo->CalcValueMultiplier(unitCaster, this); + int32 const gain = int32(newDamage * gainMultiplier); - m_caster->EnergizeBySpell(m_caster, m_spellInfo, gain, powerType); + unitCaster->EnergizeBySpell(unitCaster, m_spellInfo, gain, powerType); } ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, newDamage, gainMultiplier); } @@ -1140,14 +1165,13 @@ void Spell::EffectPowerBurn(SpellEffIndex effIndex) return; Powers powerType = Powers(effectInfo->MiscValue); - if (!unitTarget || !unitTarget->IsAlive() || unitTarget->GetPowerType() != powerType || damage < 0) return; int32 newDamage = -(unitTarget->ModifyPower(powerType, -damage)); // NO - Not a typo - EffectPowerBurn uses effect value multiplier - not effect damage multiplier - float dmgMultiplier = effectInfo->CalcValueMultiplier(m_originalCaster, this); + float dmgMultiplier = effectInfo->CalcValueMultiplier(unitCaster, this); // add log data before multiplication (need power amount, not damage) ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, newDamage, 0.0f); @@ -1162,54 +1186,52 @@ void Spell::EffectHeal(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET) return; - if (unitTarget && unitTarget->IsAlive() && damage >= 0) - { - // Try to get original caster - Unit* caster = !m_originalCasterGUID.IsEmpty() ? m_originalCaster : m_caster; - - // Skip if m_originalCaster not available - if (!caster) - return; + if (!unitTarget || !unitTarget->IsAlive() || damage < 0) + return; - int32 addhealth = damage; + // Skip if m_originalCaster not available + if (!unitCaster) + return; - // Vessel of the Naaru (Vial of the Sunwell trinket) - if (m_spellInfo->Id == 45064) - { - // Amount of heal - depends from stacked Holy Energy - int32 damageAmount = 0; - if (AuraEffect const* aurEff = m_caster->GetAuraEffect(45062, 0)) - { - damageAmount += aurEff->GetAmount(); - m_caster->RemoveAurasDueToSpell(45062); - } + int32 addhealth = damage; - addhealth += damageAmount; - } - // Runic Healing Injector (heal increased by 25% for engineers - 3.2.0 patch change) - else if (m_spellInfo->Id == 67489) - { - if (Player* player = m_caster->ToPlayer()) - if (player->HasSkill(SKILL_ENGINEERING)) - AddPct(addhealth, 25); - } - // Death Pact - return pct of max health to caster - else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00080000) - addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, int32(caster->CountPctFromMaxHealth(damage)), HEAL, effectInfo); - else + // Vessel of the Naaru (Vial of the Sunwell trinket) + ///@todo: move this to scripts + if (m_spellInfo->Id == 45064) + { + // Amount of heal - depends from stacked Holy Energy + int32 damageAmount = 0; + if (AuraEffect const* aurEff = unitCaster->GetAuraEffect(45062, 0)) { - uint32 bonus = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effectInfo); - addhealth = bonus + uint32(bonus * variance); + damageAmount += aurEff->GetAmount(); + unitCaster->RemoveAurasDueToSpell(45062); } - addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL); + addhealth += damageAmount; + } + // Runic Healing Injector (heal increased by 25% for engineers - 3.2.0 patch change) + else if (m_spellInfo->Id == 67489) + { + if (Player* player = unitCaster->ToPlayer()) + if (player->HasSkill(SKILL_ENGINEERING)) + AddPct(addhealth, 25); + } + // Death Pact - return pct of max health to caster + else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00080000) + addhealth = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, int32(unitCaster->CountPctFromMaxHealth(damage)), HEAL, effectInfo); + else + { + uint32 bonus = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effectInfo); + addhealth = bonus + uint32(bonus * variance); + } - // Remove Grievious bite if fully healed - if (unitTarget->HasAura(48920) && (unitTarget->GetHealth() + addhealth >= unitTarget->GetMaxHealth())) - unitTarget->RemoveAura(48920); + addhealth = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, addhealth, HEAL); - m_healing += addhealth; - } + // Remove Grievious bite if fully healed + if (unitTarget->HasAura(48920) && (unitTarget->GetHealth() + addhealth >= unitTarget->GetMaxHealth())) + unitTarget->RemoveAura(48920); + + m_healing += addhealth; } void Spell::EffectHealPct(SpellEffIndex /*effIndex*/) @@ -1220,12 +1242,14 @@ void Spell::EffectHealPct(SpellEffIndex /*effIndex*/) if (!unitTarget || !unitTarget->IsAlive() || damage < 0) return; - // Skip if m_originalCaster not available - if (!m_originalCaster) - return; + uint32 heal = unitTarget->CountPctFromMaxHealth(damage); + if (unitCaster) + { + heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, effectInfo); + heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL); + } - uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, unitTarget->CountPctFromMaxHealth(damage), HEAL, effectInfo); - m_healing += unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL); + m_healing += heal; } void Spell::EffectHealMechanical(SpellEffIndex /*effIndex*/) @@ -1236,14 +1260,15 @@ void Spell::EffectHealMechanical(SpellEffIndex /*effIndex*/) if (!unitTarget || !unitTarget->IsAlive() || damage < 0) return; - // Skip if m_originalCaster not available - if (!m_originalCaster) - return; + uint32 heal = damage; + if (unitCaster) + heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, effectInfo); - uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, uint32(damage), HEAL, effectInfo); heal += uint32(heal * variance); + if (unitCaster) + heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL); - m_healing += unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL); + m_healing += heal; } void Spell::EffectHealthLeech(SpellEffIndex /*effIndex*/) @@ -1254,25 +1279,30 @@ void Spell::EffectHealthLeech(SpellEffIndex /*effIndex*/) if (!unitTarget || !unitTarget->IsAlive() || damage < 0) return; - uint32 bonus = m_caster->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effectInfo); + uint32 bonus = 0; + if (unitCaster) + bonus = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effectInfo); + damage = bonus + uint32(bonus * variance); - damage = unitTarget->SpellDamageBonusTaken(m_caster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); + + if (unitCaster) + damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); TC_LOG_DEBUG("spells", "HealthLeech :%i", damage); - float healMultiplier = effectInfo->CalcValueMultiplier(m_originalCaster, this); + float healMultiplier = effectInfo->CalcValueMultiplier(unitCaster, this); m_damage += damage; // get max possible damage, don't count overkill for heal uint32 healthGain = uint32(-unitTarget->GetHealthGain(-damage) * healMultiplier); - if (m_caster->IsAlive()) + if (unitCaster && unitCaster->IsAlive()) { - healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL, effectInfo); - healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL); + healthGain = unitCaster->SpellHealingBonusDone(unitCaster, m_spellInfo, healthGain, HEAL, effectInfo); + healthGain = unitCaster->SpellHealingBonusTaken(unitCaster, m_spellInfo, healthGain, HEAL); - HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellSchoolMask); - m_caster->HealBySpell(healInfo); + HealInfo healInfo(unitCaster, unitCaster, healthGain, m_spellInfo, m_spellSchoolMask); + unitCaster->HealBySpell(healInfo); } } @@ -1446,6 +1476,9 @@ void Spell::EffectPersistentAA(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; + if (!unitCaster) + return; + // only handle at last effect for (uint8 i = effIndex + 1; i < MAX_SPELL_EFFECTS; ++i) if (SpellEffectInfo const* otherEffect = m_spellInfo->GetEffect(i)) @@ -1454,15 +1487,14 @@ void Spell::EffectPersistentAA(SpellEffIndex effIndex) ASSERT(!_dynObjAura); - Unit* caster = m_caster->GetEntry() == WORLD_TRIGGER ? m_originalCaster : m_caster; - float radius = effectInfo->CalcRadius(caster); + float radius = effectInfo->CalcRadius(unitCaster); // Caster not in world, might be spell triggered from aura removal - if (!caster->IsInWorld()) + if (!unitCaster->IsInWorld()) return; DynamicObject* dynObj = new DynamicObject(false); - if (!dynObj->CreateDynamicObject(caster->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), caster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL, m_SpellVisual)) + if (!dynObj->CreateDynamicObject(unitCaster->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), unitCaster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL, m_SpellVisual)) { delete dynObj; return; @@ -1470,7 +1502,7 @@ void Spell::EffectPersistentAA(SpellEffIndex effIndex) AuraCreateInfo createInfo(m_castId, m_spellInfo, GetCastDifficulty(), MAX_EFFECT_MASK, dynObj); createInfo - .SetCaster(caster) + .SetCaster(unitCaster) .SetBaseAmount(m_spellValue->EffectBasePoints) .SetCastItem(m_castItemGUID, m_castItemEntry, m_castItemLevel); @@ -1491,8 +1523,9 @@ void Spell::EffectEnergize(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget) + if (!unitCaster || !unitTarget) return; + if (!unitTarget->IsAlive()) return; @@ -1500,7 +1533,6 @@ void Spell::EffectEnergize(SpellEffIndex /*effIndex*/) return; Powers power = Powers(effectInfo->MiscValue); - if (unitTarget->GetMaxPower(power) == 0) return; @@ -1509,15 +1541,15 @@ void Spell::EffectEnergize(SpellEffIndex /*effIndex*/) { case 24571: // Blood Fury // Instantly increases your rage by ${(300-10*$max(0,$PL-60))/10}. - damage -= 10 * std::max(0, std::min(30, m_caster->getLevel() - 60)); + damage -= 10 * std::max(0, std::min(30, unitCaster->getLevel() - 60)); break; case 24532: // Burst of Energy // Instantly increases your energy by ${60-4*$max(0,$min(15,$PL-60))}. - damage -= 4 * std::max(0, std::min(15, m_caster->getLevel() - 60)); + damage -= 4 * std::max(0, std::min(15, unitCaster->getLevel() - 60)); break; case 67490: // Runic Mana Injector (mana gain increased by 25% for engineers - 3.2.0 patch change) { - if (Player* player = m_caster->ToPlayer()) + if (Player* player = unitCaster->ToPlayer()) if (player->HasSkill(SKILL_ENGINEERING)) AddPct(damage, 25); break; @@ -1526,7 +1558,7 @@ void Spell::EffectEnergize(SpellEffIndex /*effIndex*/) break; } - m_caster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power); + unitCaster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power); } void Spell::EffectEnergizePct(SpellEffIndex /*effIndex*/) @@ -1534,8 +1566,9 @@ void Spell::EffectEnergizePct(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget) + if (!unitCaster || !unitTarget) return; + if (!unitTarget->IsAlive()) return; @@ -1544,11 +1577,11 @@ void Spell::EffectEnergizePct(SpellEffIndex /*effIndex*/) Powers power = Powers(effectInfo->MiscValue); uint32 maxPower = unitTarget->GetMaxPower(power); - if (maxPower == 0) + if (!maxPower) return; - uint32 gain = CalculatePct(maxPower, damage); - m_caster->EnergizeBySpell(unitTarget, m_spellInfo, gain, power); + uint32 const gain = CalculatePct(maxPower, damage); + unitCaster->EnergizeBySpell(unitTarget, m_spellInfo, gain, power); } void Spell::SendLoot(ObjectGuid guid, LootType loottype) @@ -1569,7 +1602,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) // special case, already has GossipHello inside so return and avoid calling twice if (gameObjTarget->GetGoType() == GAMEOBJECT_TYPE_GOOBER) { - gameObjTarget->Use(m_caster); + gameObjTarget->Use(player); return; } @@ -1592,7 +1625,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) case GAMEOBJECT_TYPE_SPELL_FOCUS: // triggering linked GO if (uint32 trapEntry = gameObjTarget->GetGOInfo()->spellFocus.linkedTrap) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, m_caster); + gameObjTarget->TriggeringLinkedGameObject(trapEntry, player); return; case GAMEOBJECT_TYPE_CHEST: @@ -1605,7 +1638,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) // triggering linked GO if (uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrap) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, m_caster); + gameObjTarget->TriggeringLinkedGameObject(trapEntry, player); // Don't return, let loots been taken default: @@ -1878,25 +1911,26 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) return; } - if (!m_originalCaster) - return; + WorldObject* caster = m_caster; + if (m_originalCaster) + caster = m_originalCaster; ObjectGuid privateObjectOwner = [&]() { if (!(properties->Flags & (SUMMON_PROP_FLAG_PERSONAL_SPAWN | SUMMON_PROP_FLAG_PERSONAL_GROUP_SPAWN))) return ObjectGuid::Empty; - if (m_originalCaster->IsPrivateObject()) - return m_originalCaster->GetPrivateObjectOwner(); + if (caster->IsPrivateObject()) + return caster->GetPrivateObjectOwner(); if (properties->Flags & SUMMON_PROP_FLAG_PERSONAL_GROUP_SPAWN) - if (m_originalCaster->IsPlayer() && m_originalCaster->ToPlayer()->GetGroup()) - return m_originalCaster->ToPlayer()->GetGroup()->GetGUID(); + if (caster->IsPlayer() && m_originalCaster->ToPlayer()->GetGroup()) + return caster->ToPlayer()->GetGroup()->GetGUID(); - return m_originalCaster->GetGUID(); + return caster->GetGUID(); }(); - int32 duration = m_spellInfo->CalcDuration(m_originalCaster); + int32 duration = m_spellInfo->CalcDuration(caster); TempSummon* summon = nullptr; @@ -1936,11 +1970,13 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) case SUMMON_CATEGORY_WILD: case SUMMON_CATEGORY_ALLY: case SUMMON_CATEGORY_UNK: + { if (properties->Flags & 512) { SummonGuardian(effIndex, entry, properties, numSummons, privateObjectOwner); break; } + switch (SummonTitle(properties->Title)) { case SummonTitle::Pet: @@ -1949,15 +1985,23 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) case SummonTitle::Minion: SummonGuardian(effIndex, entry, properties, numSummons, privateObjectOwner); break; - // Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE) + // Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE) case SummonTitle::Vehicle: case SummonTitle::Mount: - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id); + { + if (!unitCaster) + return; + + summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id); break; + } case SummonTitle::Lightwell: case SummonTitle::Totem: { - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, privateObjectOwner); + if (!unitCaster) + return; + + summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner); if (!summon || !summon->IsTotem()) return; @@ -1970,7 +2014,10 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) } case SummonTitle::Companion: { - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, privateObjectOwner); + if (!unitCaster) + return; + + summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner); if (!summon || !summon->HasUnitTypeMask(UNIT_MASK_MINION)) return; @@ -1995,16 +2042,16 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) pos = *destTarget; else // randomize position for multiple summons - pos = m_caster->GetRandomPoint(*destTarget, radius); + pos = caster->GetRandomPoint(*destTarget, radius); - summon = m_originalCaster->SummonCreature(entry, pos, summonType, duration, 0, privateObjectOwner); + summon = caster->SummonCreature(entry, pos, summonType, duration, 0, privateObjectOwner); if (!summon) continue; if (properties->Control == SUMMON_CATEGORY_ALLY) { - summon->SetOwnerGUID(m_originalCaster->GetGUID()); - summon->SetFaction(m_originalCaster->GetFaction()); + summon->SetOwnerGUID(caster->GetGUID()); + summon->SetFaction(caster->GetFaction()); summon->SetCreatedBySpell(m_spellInfo->Id); } @@ -2012,18 +2059,28 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) } return; } - }//switch + } break; + } case SUMMON_CATEGORY_PET: SummonGuardian(effIndex, entry, properties, numSummons, privateObjectOwner); break; case SUMMON_CATEGORY_PUPPET: - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, privateObjectOwner); + { + if (!unitCaster) + return; + + summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner); break; + } case SUMMON_CATEGORY_VEHICLE: + { + if (!unitCaster) + return; + // Summoning spells (usually triggered by npc_spellclick) that spawn a vehicle and that cause the clicker // to cast a ride vehicle spell on the summoned unit. - summon = m_originalCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_caster, m_spellInfo->Id); + summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id); if (!summon || !summon->IsVehicle()) return; @@ -2043,19 +2100,20 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) if (basePoints > 0 && basePoints < MAX_VEHICLE_SEATS) args.AddSpellMod(SPELLVALUE_BASE_POINT0, basePoints); - m_originalCaster->CastSpell(summon, spellId, args); + unitCaster->CastSpell(summon, spellId, args); uint32 faction = properties->Faction; if (!faction) - faction = m_originalCaster->GetFaction(); + faction = unitCaster->GetFaction(); summon->SetFaction(faction); break; + } } if (summon) { - summon->SetCreatorGUID(m_originalCaster->GetGUID()); + summon->SetCreatorGUID(caster->GetGUID()); ExecuteLogEffectSummonObject(effIndex, summon); } } @@ -2230,17 +2288,18 @@ void Spell::EffectAddFarsight(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* player = m_caster->ToPlayer(); + if (!player) return; float radius = effectInfo->CalcRadius(); int32 duration = m_spellInfo->CalcDuration(m_caster); // Caster not in world, might be spell triggered from aura removal - if (!m_caster->IsInWorld()) + if (!player->IsInWorld()) return; DynamicObject* dynObj = new DynamicObject(true); - if (!dynObj->CreateDynamicObject(m_caster->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), m_caster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS, m_SpellVisual)) + if (!dynObj->CreateDynamicObject(player->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), player, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS, m_SpellVisual)) { delete dynObj; return; @@ -2540,7 +2599,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!m_caster->GetPetGUID().IsEmpty()) + if (!unitCaster || !unitCaster->GetPetGUID().IsEmpty()) return; if (!unitTarget) @@ -2554,14 +2613,14 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) if (creatureTarget->IsPet()) return; - if (m_caster->getClass() != CLASS_HUNTER) + if (unitCaster->getClass() != CLASS_HUNTER) return; // cast finish successfully //SendChannelUpdate(0); finish(); - Pet* pet = m_caster->CreateTamedPetFrom(creatureTarget, m_spellInfo->Id); + Pet* pet = unitCaster->CreateTamedPetFrom(creatureTarget, m_spellInfo->Id); if (!pet) // in very specific state like near world end/etc. return; @@ -2580,12 +2639,12 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) pet->SetLevel(level); // caster have pet now - m_caster->SetMinion(pet, true); + unitCaster->SetMinion(pet, true); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { pet->SavePetToDB(PET_SAVE_AS_CURRENT); - m_caster->ToPlayer()->PetSpellInitialize(); + unitCaster->ToPlayer()->PetSpellInitialize(); } } @@ -2595,11 +2654,11 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) return; Player* owner = nullptr; - if (m_originalCaster) + if (unitCaster) { - owner = m_originalCaster->ToPlayer(); - if (!owner && m_originalCaster->IsTotem()) - owner = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself(); + owner = unitCaster->ToPlayer(); + if (!owner && unitCaster->IsTotem()) + owner = unitCaster->GetCharmerOrOwnerPlayerOrPlayerItself(); } uint32 petentry = effectInfo->MiscValue; @@ -2655,7 +2714,7 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) if (m_caster->GetTypeId() == TYPEID_UNIT) { - if (m_caster->IsTotem()) + if (m_caster->ToCreature()->IsTotem()) pet->SetReactState(REACT_AGGRESSIVE); else pet->SetReactState(REACT_DEFENSIVE); @@ -2702,6 +2761,9 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; + if (!unitCaster) + return; + // this effect use before aura Taunt apply for prevent taunt already attacking target // for spell as marked "non effective at already attacking target" if (!unitTarget || !unitTarget->CanHaveThreatList()) @@ -2711,7 +2773,7 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) } ThreatManager& mgr = unitTarget->GetThreatManager(); - if (mgr.GetCurrentVictim() == m_caster) + if (mgr.GetCurrentVictim() == unitCaster) { SendCastResult(SPELL_FAILED_DONT_REPORT); return; @@ -2719,7 +2781,7 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) if (!mgr.IsThreatListEmpty()) // Set threat equal to highest threat currently on target - mgr.MatchUnitThreatToHighestThreat(m_caster); + mgr.MatchUnitThreatToHighestThreat(unitCaster); } void Spell::EffectWeaponDmg(SpellEffIndex /*effIndex*/) @@ -2727,6 +2789,9 @@ void Spell::EffectWeaponDmg(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET) return; + if (!unitCaster) + return; + if (!unitTarget || !unitTarget->IsAlive()) return; @@ -2782,8 +2847,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex /*effIndex*/) if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->AddComboPoints(1, this); // 50% more damage with daggers - if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (Item* item = m_caster->ToPlayer()->GetWeaponForAttack(m_attackType, true)) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) + if (Item* item = unitCaster->ToPlayer()->GetWeaponForAttack(m_attackType, true)) if (item->GetTemplate()->GetSubClass() == ITEM_SUBCLASS_WEAPON_DAGGER) totalDamagePercentMod *= 1.5f; } @@ -2793,8 +2858,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex /*effIndex*/) { // Skyshatter Harness item set bonus // Stormstrike - if (AuraEffect* aurEff = m_caster->IsScriptOverriden(m_spellInfo, 5634)) - m_caster->CastSpell(m_caster, 38430, aurEff); + if (AuraEffect* aurEff = unitCaster->IsScriptOverriden(m_spellInfo, 5634)) + unitCaster->CastSpell(nullptr, 38430, aurEff); break; } case SPELLFAMILY_DRUID: @@ -2811,7 +2876,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex /*effIndex*/) { // Kill Shot - bonus damage from Ranged Attack Power if (m_spellInfo->SpellFamilyFlags[1] & 0x800000) - spell_bonus += int32(0.45f * m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)); + spell_bonus += int32(0.45f * unitCaster->GetTotalAttackPowerValue(RANGED_ATTACK)); break; } case SPELLFAMILY_DEATHKNIGHT: @@ -2823,7 +2888,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex /*effIndex*/) { float bonusPct = effect->CalcValue(m_caster) * unitTarget->GetDiseasesByCaster(m_caster->GetGUID()) / 2.0f; // Death Knight T8 Melee 4P Bonus - if (AuraEffect const* aurEff = m_caster->GetAuraEffect(64736, EFFECT_0)) + if (AuraEffect const* aurEff = unitCaster->GetAuraEffect(64736, EFFECT_0)) AddPct(bonusPct, aurEff->GetAmount()); AddPct(totalDamagePercentMod, bonusPct); } @@ -2871,14 +2936,14 @@ void Spell::EffectWeaponDmg(SpellEffIndex /*effIndex*/) case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break; } - float weapon_total_pct = m_caster->GetPctModifierValue(unitMod, TOTAL_PCT); + float weapon_total_pct = unitCaster->GetPctModifierValue(unitMod, TOTAL_PCT); if (fixed_bonus) fixed_bonus = int32(fixed_bonus * weapon_total_pct); if (spell_bonus) spell_bonus = int32(spell_bonus * weapon_total_pct); } - int32 weaponDamage = m_caster->CalculateDamage(m_attackType, normalized, addPctMods); + int32 weaponDamage = unitCaster->CalculateDamage(m_attackType, normalized, addPctMods); // Sequence is important for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) @@ -2909,8 +2974,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex /*effIndex*/) weaponDamage = std::max(weaponDamage, 0); // Add melee damage bonuses (also check for negative) - weaponDamage = m_caster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, SPELL_DIRECT_DAMAGE, m_spellInfo); - m_damage += unitTarget->MeleeDamageBonusTaken(m_caster, weaponDamage, m_attackType, SPELL_DIRECT_DAMAGE, m_spellInfo); + weaponDamage = unitCaster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, SPELL_DIRECT_DAMAGE, m_spellInfo); + m_damage += unitTarget->MeleeDamageBonusTaken(unitCaster, weaponDamage, m_attackType, SPELL_DIRECT_DAMAGE, m_spellInfo); } void Spell::EffectThreat(SpellEffIndex /*effIndex*/) @@ -2918,13 +2983,16 @@ void Spell::EffectThreat(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget || !m_caster->IsAlive()) + if (!unitCaster || !unitCaster->IsAlive()) + return; + + if (!unitTarget) return; if (!unitTarget->CanHaveThreatList()) return; - unitTarget->GetThreatManager().AddThreat(m_caster, float(damage), m_spellInfo, true); + unitTarget->GetThreatManager().AddThreat(unitCaster, float(damage), m_spellInfo, true); } void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/) @@ -2932,6 +3000,9 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; + if (!unitCaster) + return; + if (!unitTarget || !unitTarget->IsAlive()) return; @@ -2939,7 +3010,7 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/) // damage == 0 - heal for caster max health if (damage == 0) - addhealth = m_caster->GetMaxHealth(); + addhealth = unitCaster->GetMaxHealth(); else addhealth = unitTarget->GetMaxHealth() - unitTarget->GetHealth(); @@ -2967,15 +3038,15 @@ void Spell::EffectInterruptCast(SpellEffIndex effIndex) || (spell->getState() == SPELL_STATE_PREPARING && spell->GetCastTime() > 0.0f)) && curSpellInfo->CanBeInterrupted(m_caster, unitTarget)) { - if (m_originalCaster) + if (unitCaster) { int32 duration = m_spellInfo->GetDuration(); unitTarget->GetSpellHistory()->LockSpellSchool(curSpellInfo->GetSchoolMask(), unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effIndex)); if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) - Unit::ProcSkillsAndAuras(m_originalCaster, unitTarget, PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, + Unit::ProcSkillsAndAuras(unitCaster, unitTarget, PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_HIT, PROC_HIT_INTERRUPT, nullptr, nullptr, nullptr); else if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE) - Unit::ProcSkillsAndAuras(m_originalCaster, unitTarget, PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS, PROC_FLAG_TAKEN_SPELL_MELEE_DMG_CLASS, + Unit::ProcSkillsAndAuras(unitCaster, unitTarget, PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS, PROC_FLAG_TAKEN_SPELL_MELEE_DMG_CLASS, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_HIT, PROC_HIT_INTERRUPT, nullptr, nullptr, nullptr); } ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo->Id); @@ -3041,7 +3112,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) return; /// @todo we must implement hunter pet summon at login there (spell 6962) - + /// @todo: move this to scripts switch (m_spellInfo->SpellFamilyName) { case SPELLFAMILY_GENERIC: @@ -3059,8 +3130,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) if (!itemTarget && m_caster->GetTypeId() != TYPEID_PLAYER) return; - uint32 spell_id = roll_chance_i(20) ? 8854 : 8855; - + uint32 const spell_id = roll_chance_i(20) ? 8854 : 8855; m_caster->CastSpell(m_caster, spell_id, true); return; } @@ -3195,14 +3265,13 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) unitTarget->CastSpell(unitTarget, spellId, true); break; } - // 5, 000 Gold + // 5,000 Gold case 46642: { if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; unitTarget->ToPlayer()->ModifyMoney(5000 * GOLD); - break; } // Death Knight Initiate Visual @@ -3276,8 +3345,8 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) unitTarget->ToTempSummon()->UnSummon(); return; case 52479: // Gift of the Harvester - if (unitTarget && m_originalCaster) - m_originalCaster->CastSpell(unitTarget, urand(0, 1) ? damage : 52505, true); + if (unitTarget && unitCaster) + unitCaster->CastSpell(unitTarget, urand(0, 1) ? damage : 52505, true); return; case 53110: // Devour Humanoid if (unitTarget) @@ -3334,14 +3403,17 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) } case 62482: // Grab Crate { + if (!unitCaster) + return; + if (unitTarget) { - if (Unit* seat = m_caster->GetVehicleBase()) + if (Unit* seat = unitCaster->GetVehicleBase()) { if (Unit* parent = seat->GetVehicleBase()) { /// @todo a hack, range = 11, should after some time cast, otherwise too far - m_caster->CastSpell(parent, 62496, true); + unitCaster->CastSpell(parent, 62496, true); unitTarget->CastSpell(parent, m_spellInfo->GetEffect(EFFECT_0)->CalcValue()); // DIFFICULTY_NONE, so effect always valid } } @@ -3350,7 +3422,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) } case 60123: // Lightwell { - if (m_caster->GetTypeId() != TYPEID_UNIT || !m_caster->IsSummon()) + if (m_caster->GetTypeId() != TYPEID_UNIT || !m_caster->ToCreature()->IsSummon()) return; uint32 spell_heal; @@ -3369,11 +3441,11 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) } // proc a spellcast - if (Aura* chargesAura = m_caster->GetAura(59907)) + if (Aura* chargesAura = m_caster->ToCreature()->GetAura(59907)) { - m_caster->CastSpell(unitTarget, spell_heal, m_caster->ToTempSummon()->GetSummonerGUID()); + m_caster->CastSpell(unitTarget, spell_heal, m_caster->ToCreature()->ToTempSummon()->GetSummonerGUID()); if (chargesAura->ModCharges(-1)) - m_caster->ToTempSummon()->UnSummon(); + m_caster->ToCreature()->ToTempSummon()->UnSummon(); } return; @@ -3501,13 +3573,13 @@ void Spell::EffectDuel(SpellEffIndex effIndex) } //CREATE DUEL FLAG OBJECT - Map* map = m_caster->GetMap(); + Map* map = caster->GetMap(); Position const pos = { - m_caster->GetPositionX() + (unitTarget->GetPositionX() - m_caster->GetPositionX()) / 2, - m_caster->GetPositionY() + (unitTarget->GetPositionY() - m_caster->GetPositionY()) / 2, - m_caster->GetPositionZ(), - m_caster->GetOrientation() + caster->GetPositionX() + (unitTarget->GetPositionX() - caster->GetPositionX()) / 2, + caster->GetPositionY() + (unitTarget->GetPositionY() - caster->GetPositionY()) / 2, + caster->GetPositionZ(), + caster->GetOrientation() }; QuaternionData rot = QuaternionData::fromEulerAnglesZYX(pos.GetOrientation(), 0.f, 0.f); @@ -3515,17 +3587,17 @@ void Spell::EffectDuel(SpellEffIndex effIndex) if (!go) return; - PhasingHandler::InheritPhaseShift(go, m_caster); + PhasingHandler::InheritPhaseShift(go, caster); - go->SetFaction(m_caster->GetFaction()); - go->SetLevel(m_caster->getLevel()+1); - int32 duration = m_spellInfo->CalcDuration(m_caster); + go->SetFaction(caster->GetFaction()); + go->SetLevel(caster->getLevel() + 1); + int32 duration = m_spellInfo->CalcDuration(caster); go->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); go->SetSpellId(m_spellInfo->Id); ExecuteLogEffectSummonObject(effIndex, go); - m_caster->AddGameObject(go); + caster->AddGameObject(go); map->AddToMap(go); //END @@ -3612,10 +3684,13 @@ void Spell::EffectSummonPlayer(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; + if (!unitCaster) + return; + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - unitTarget->ToPlayer()->SendSummonRequestFrom(m_caster); + unitTarget->ToPlayer()->SendSummonRequestFrom(unitCaster); } void Spell::EffectActivateObject(SpellEffIndex /*effIndex*/) @@ -3824,18 +3899,21 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; + if (!unitCaster) + return; + uint8 slot = effectInfo->Effect - SPELL_EFFECT_SUMMON_OBJECT_SLOT1; - ObjectGuid guid = m_caster->m_ObjectSlot[slot]; + ObjectGuid guid = unitCaster->m_ObjectSlot[slot]; if (!guid.IsEmpty()) { - if (GameObject* obj = m_caster->GetMap()->GetGameObject(guid)) + if (GameObject* obj = unitCaster->GetMap()->GetGameObject(guid)) { // Recast case - null spell id to make auras not be removed on object remove from world if (m_spellInfo->Id == obj->GetSpellId()) obj->SetSpellId(0); - m_caster->RemoveGameObject(obj, true); + unitCaster->RemoveGameObject(obj, true); } - m_caster->m_ObjectSlot[slot].Clear(); + unitCaster->m_ObjectSlot[slot].Clear(); } float x, y, z; @@ -3844,7 +3922,7 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) destTarget->GetPosition(x, y, z); // Summon in random point all other units if location present else - m_caster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS); + unitCaster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS); Map* map = m_caster->GetMap(); Position pos = Position(x, y, z, m_caster->GetOrientation()); @@ -3856,17 +3934,18 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) PhasingHandler::InheritPhaseShift(go, m_caster); - //go->SetLevel(m_caster->getLevel()); + go->SetFaction(unitCaster->GetFaction()); + go->SetLevel(unitCaster->getLevel()); int32 duration = m_spellInfo->CalcDuration(m_caster); - go->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); + go->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); go->SetSpellId(m_spellInfo->Id); - m_caster->AddGameObject(go); + unitCaster->AddGameObject(go); ExecuteLogEffectSummonObject(effIndex, go); map->AddToMap(go); - m_caster->m_ObjectSlot[slot] = go->GetGUID(); + unitCaster->m_ObjectSlot[slot] = go->GetGUID(); } void Spell::EffectResurrect(SpellEffIndex effIndex) @@ -3995,25 +4074,28 @@ void Spell::EffectForceDeselect(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - float dist = m_caster->GetVisibilityRange(); + if (!unitCaster) + return; + + float dist = unitCaster->GetVisibilityRange(); // clear focus Trinity::PacketSenderOwning<WorldPackets::Combat::BreakTarget> breakTarget; breakTarget.Data.UnitGUID = m_caster->GetGUID(); breakTarget.Data.Write(); - Trinity::MessageDistDelivererToHostile<Trinity::PacketSenderOwning<WorldPackets::Combat::BreakTarget>> notifierBreak(m_caster, breakTarget, dist); + Trinity::MessageDistDelivererToHostile<Trinity::PacketSenderOwning<WorldPackets::Combat::BreakTarget>> notifierBreak(unitCaster, breakTarget, dist); Cell::VisitWorldObjects(m_caster, notifierBreak, dist); // and selection Trinity::PacketSenderOwning<WorldPackets::Spells::ClearTarget> clearTarget; clearTarget.Data.Guid = m_caster->GetGUID(); clearTarget.Data.Write(); - Trinity::MessageDistDelivererToHostile<Trinity::PacketSenderOwning<WorldPackets::Spells::ClearTarget>> notifierClear(m_caster, clearTarget, dist); + Trinity::MessageDistDelivererToHostile<Trinity::PacketSenderOwning<WorldPackets::Spells::ClearTarget>> notifierClear(unitCaster, clearTarget, dist); Cell::VisitWorldObjects(m_caster, notifierClear, dist); // we should also force pets to remove us from current target Unit::AttackerSet attackerSet; - for (Unit::AttackerSet::const_iterator itr = m_caster->getAttackers().begin(); itr != m_caster->getAttackers().end(); ++itr) + for (Unit::AttackerSet::const_iterator itr = unitCaster->getAttackers().begin(); itr != unitCaster->getAttackers().end(); ++itr) if ((*itr)->GetTypeId() == TYPEID_UNIT && !(*itr)->CanHaveThreatList()) attackerSet.insert(*itr); @@ -4026,11 +4108,8 @@ void Spell::EffectSelfResurrect(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - if (!m_caster || m_caster->IsAlive()) - return; - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - if (!m_caster->IsInWorld()) + Player* player = m_caster->ToPlayer(); + if (!player || !player->IsInWorld() || player->IsAlive()) return; uint32 health = 0; @@ -4045,12 +4124,11 @@ void Spell::EffectSelfResurrect(SpellEffIndex /*effIndex*/) // percent case else { - health = m_caster->CountPctFromMaxHealth(damage); - if (m_caster->GetMaxPower(POWER_MANA) > 0) - mana = CalculatePct(m_caster->GetMaxPower(POWER_MANA), damage); + health = player->CountPctFromMaxHealth(damage); + if (player->GetMaxPower(POWER_MANA) > 0) + mana = CalculatePct(player->GetMaxPower(POWER_MANA), damage); } - Player* player = m_caster->ToPlayer(); player->ResurrectPlayer(0.0f); player->SetHealth(health); @@ -4069,7 +4147,9 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/) if (unitTarget->GetTypeId() != TYPEID_UNIT) return; - if (m_caster->GetTypeId() != TYPEID_PLAYER) + + Player* player = m_caster->ToPlayer(); + if (!player) return; Creature* creature = unitTarget->ToCreature(); @@ -4079,7 +4159,7 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/) creature->RemoveUnitFlag(UNIT_FLAG_SKINNABLE); creature->AddDynamicFlag(UNIT_DYNFLAG_LOOTABLE); - m_caster->ToPlayer()->SendLoot(creature->GetGUID(), LOOT_SKINNING); + player->SendLoot(creature->GetGUID(), LOOT_SKINNING); if (skill == SKILL_SKINNING) { @@ -4123,11 +4203,14 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) if (!unitTarget) return; + if (!unitCaster) + return; + if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET) { // charge changes fall time - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->SetFallInformation(0, m_caster->GetPositionZ()); + if (unitCaster->GetTypeId() == TYPEID_PLAYER) + unitCaster->ToPlayer()->SetFallInformation(0, unitCaster->GetPositionZ()); float speed = G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) ? m_spellInfo->Speed : SPEED_CHARGE; @@ -4146,7 +4229,7 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) if (G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) && m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION)) speed = pos.GetExactDist(m_caster) / speed; - m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed, EVENT_CHARGE, false, unitTarget, spellEffectExtraData.get_ptr()); + unitCaster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed, EVENT_CHARGE, false, unitTarget, spellEffectExtraData.get_ptr()); } else { @@ -4156,7 +4239,7 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) speed = Position(pos.x, pos.y, pos.z).GetExactDist(m_caster) / speed; } - m_caster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed, unitTarget, spellEffectExtraData.get_ptr()); + unitCaster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed, unitTarget, spellEffectExtraData.get_ptr()); } } @@ -4164,7 +4247,7 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) { // not all charge effects used in negative spells if (!m_spellInfo->IsPositive() && m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->Attack(unitTarget, true); + unitCaster->Attack(unitTarget, true); if (effectInfo->TriggerSpell) m_caster->CastSpell(unitTarget, effectInfo->TriggerSpell, m_originalCasterGUID); @@ -4176,18 +4259,21 @@ void Spell::EffectChargeDest(SpellEffIndex /*effIndex*/) if (!destTarget) return; + if (!unitCaster) + return; + if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH) { Position pos = destTarget->GetPosition(); - if (!m_caster->IsWithinLOS(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ())) + if (!unitCaster->IsWithinLOS(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ())) { - float angle = m_caster->GetRelativeAngle(pos.GetPositionX(), pos.GetPositionY()); - float dist = m_caster->GetDistance(pos); - pos = m_caster->GetFirstCollisionPosition(dist, angle); + float angle = unitCaster->GetRelativeAngle(pos.GetPositionX(), pos.GetPositionY()); + float dist = unitCaster->GetDistance(pos); + pos = unitCaster->GetFirstCollisionPosition(dist, angle); } - m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ); + unitCaster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ); } else if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT) { @@ -4204,7 +4290,7 @@ void Spell::EffectKnockBack(SpellEffIndex /*effIndex*/) if (!unitTarget) return; - if (m_caster->GetTypeId() == TYPEID_PLAYER || m_caster->GetOwnerGUID().IsPlayer() || m_caster->IsHunterPet()) + if (m_caster->GetAffectingPlayer()) if (Creature* creatureTarget = unitTarget->ToCreature()) if (creatureTarget->isWorldBoss() || creatureTarget->IsDungeonBoss()) return; @@ -4232,9 +4318,7 @@ void Spell::EffectKnockBack(SpellEffIndex /*effIndex*/) return; } else //if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_KNOCK_BACK) - { m_caster->GetPosition(x, y); - } unitTarget->KnockbackFrom(x, y, speedxy, speedz); } @@ -4454,20 +4538,23 @@ void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; + if (!unitCaster) + return; + int32 mana = 0; for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot) { - if (!m_caster->m_SummonSlot[slot]) + if (!unitCaster->m_SummonSlot[slot]) continue; - Creature* totem = m_caster->GetMap()->GetCreature(m_caster->m_SummonSlot[slot]); + Creature* totem = unitCaster->GetMap()->GetCreature(unitCaster->m_SummonSlot[slot]); if (totem && totem->IsTotem()) { uint32 spell_id = totem->m_unitData->CreatedBySpell; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id, GetCastDifficulty()); if (spellInfo) { - std::vector<SpellPowerCost> costs = spellInfo->CalcPowerCost(m_caster, spellInfo->GetSchoolMask()); + std::vector<SpellPowerCost> costs = spellInfo->CalcPowerCost(unitCaster, spellInfo->GetSchoolMask()); auto m = std::find_if(costs.begin(), costs.end(), [](SpellPowerCost const& cost) { return cost.Power == POWER_MANA; }); if (m != costs.end()) mana += m->Amount; @@ -4483,7 +4570,7 @@ void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/) { CastSpellExtraArgs args(TRIGGERED_FULL_MASK); args.AddSpellMod(SPELLVALUE_BASE_POINT0, mana); - m_caster->CastSpell(m_caster, 39104, args); + unitCaster->CastSpell(unitCaster, 39104, args); } } @@ -4550,10 +4637,10 @@ void Spell::EffectModifyThreatPercent(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget) + if (!unitCaster || !unitTarget) return; - unitTarget->GetThreatManager().ModifyThreatByPercent(m_caster, damage); + unitTarget->GetThreatManager().ModifyThreatByPercent(unitCaster, damage); } void Spell::EffectTransmitted(SpellEffIndex effIndex) @@ -4561,9 +4648,11 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - uint32 name_id = effectInfo->MiscValue; + if (!unitCaster) + return; - Unit::AuraEffectList const& overrideSummonedGameObjects = m_caster->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_SUMMONED_OBJECT); + uint32 name_id = effectInfo->MiscValue; + Unit::AuraEffectList const& overrideSummonedGameObjects = unitCaster->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_SUMMONED_OBJECT); for (AuraEffect const* aurEff : overrideSummonedGameObjects) { if (uint32(aurEff->GetMiscValue()) == name_id) @@ -4587,8 +4676,8 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) //FIXME: this can be better check for most objects but still hack else if (effectInfo->HasRadius() && m_spellInfo->Speed == 0) { - float dis = effectInfo->CalcRadius(m_originalCaster); - m_caster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); + float dis = effectInfo->CalcRadius(unitCaster); + unitCaster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); } else { @@ -4597,34 +4686,33 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) float max_dis = m_spellInfo->GetMaxRange(true); float dis = (float)rand_norm() * (max_dis - min_dis) + min_dis; - m_caster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); + unitCaster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); } - Map* cMap = m_caster->GetMap(); + Map* cMap = unitCaster->GetMap(); // if gameobject is summoning object, it should be spawned right on caster's position if (goinfo->type == GAMEOBJECT_TYPE_RITUAL) - m_caster->GetPosition(fx, fy, fz); + unitCaster->GetPosition(fx, fy, fz); - Position pos = { fx, fy, fz, m_caster->GetOrientation() }; - QuaternionData rot = QuaternionData::fromEulerAnglesZYX(m_caster->GetOrientation(), 0.f, 0.f); + Position pos = { fx, fy, fz, unitCaster->GetOrientation() }; + QuaternionData rot = QuaternionData::fromEulerAnglesZYX(unitCaster->GetOrientation(), 0.f, 0.f); GameObject* go = GameObject::CreateGameObject(name_id, cMap, pos, rot, 255, GO_STATE_READY); if (!go) return; PhasingHandler::InheritPhaseShift(go, m_caster); - int32 duration = m_spellInfo->CalcDuration(m_caster); switch (goinfo->type) { case GAMEOBJECT_TYPE_FISHINGNODE: { - go->SetFaction(m_caster->GetFaction()); + go->SetFaction(unitCaster->GetFaction()); ObjectGuid bobberGuid = go->GetGUID(); // client requires fishing bobber guid in channel object slot 0 to be usable - m_caster->SetChannelObject(0, bobberGuid); - m_caster->AddGameObject(go); // will removed at spell cancel + unitCaster->SetChannelObject(0, bobberGuid); + unitCaster->AddGameObject(go); // will removed at spell cancel // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo)) // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME) @@ -4642,15 +4730,15 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) } case GAMEOBJECT_TYPE_RITUAL: { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { - go->AddUniqueUse(m_caster->ToPlayer()); - m_caster->AddGameObject(go); // will be removed at spell cancel + go->AddUniqueUse(unitCaster->ToPlayer()); + unitCaster->AddGameObject(go); // will be removed at spell cancel } break; } case GAMEOBJECT_TYPE_DUEL_ARBITER: // 52991 - m_caster->AddGameObject(go); + unitCaster->AddGameObject(go); break; case GAMEOBJECT_TYPE_FISHINGHOLE: case GAMEOBJECT_TYPE_CHEST: @@ -4660,15 +4748,15 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) go->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); - go->SetOwnerGUID(m_caster->GetGUID()); + go->SetOwnerGUID(unitCaster->GetGUID()); - //go->SetLevel(m_caster->getLevel()); + //go->SetLevel(unitCaster->getLevel()); go->SetSpellId(m_spellInfo->Id); ExecuteLogEffectSummonObject(effIndex, go); TC_LOG_DEBUG("spells", "AddObject at SpellEfects.cpp EffectTransmitted"); - //m_caster->AddGameObject(go); + //unitCaster->AddGameObject(go); //m_ObjToDel.push_back(go); cMap->AddToMap(go); @@ -4678,9 +4766,9 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) PhasingHandler::InheritPhaseShift(linkedTrap, m_caster); linkedTrap->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); - //linkedTrap->SetLevel(m_caster->getLevel()); + //linkedTrap->SetLevel(unitCaster->getLevel()); linkedTrap->SetSpellId(m_spellInfo->Id); - linkedTrap->SetOwnerGUID(m_caster->GetGUID()); + linkedTrap->SetOwnerGUID(unitCaster->GetGUID()); ExecuteLogEffectSummonObject(effIndex, linkedTrap); } @@ -5012,8 +5100,11 @@ void Spell::EffectRedirectThreat(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; + if (!unitCaster) + return; + if (unitTarget) - m_caster->GetThreatManager().RegisterRedirectThreat(m_spellInfo->Id, unitTarget->GetGUID(), uint32(damage)); + unitCaster->GetThreatManager().RegisterRedirectThreat(m_spellInfo->Id, unitTarget->GetGUID(), uint32(damage)); } void Spell::EffectGameObjectDamage(SpellEffIndex /*effIndex*/) @@ -5024,15 +5115,11 @@ void Spell::EffectGameObjectDamage(SpellEffIndex /*effIndex*/) if (!gameObjTarget) return; - Unit* caster = m_originalCaster; - if (!caster) - return; - - FactionTemplateEntry const* casterFaction = caster->GetFactionTemplateEntry(); + FactionTemplateEntry const* casterFaction = m_caster->GetFactionTemplateEntry(); FactionTemplateEntry const* targetFaction = sFactionTemplateStore.LookupEntry(gameObjTarget->GetFaction()); // Do not allow to damage GO's of friendly factions (ie: Wintergrasp Walls/Ulduar Storm Beacons) if (!targetFaction || (casterFaction && !casterFaction->IsFriendlyTo(targetFaction))) - gameObjTarget->ModifyHealth(-damage, caster, GetSpellInfo()->Id); + gameObjTarget->ModifyHealth(-damage, m_caster, GetSpellInfo()->Id); } void Spell::EffectGameObjectRepair(SpellEffIndex /*effIndex*/) @@ -5051,38 +5138,35 @@ void Spell::EffectGameObjectSetDestructionState(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!gameObjTarget || !m_originalCaster) + if (!gameObjTarget) return; - Player* player = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself(); - gameObjTarget->SetDestructibleState(GameObjectDestructibleState(effectInfo->MiscValue), player, true); + gameObjTarget->SetDestructibleState(GameObjectDestructibleState(effectInfo->MiscValue), m_caster, true); } void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numGuardians, ObjectGuid privateObjectOwner) { - Unit* caster = m_originalCaster; - if (!caster) + if (!unitCaster) return; - if (caster->IsTotem()) - caster = caster->ToTotem()->GetOwner(); + if (unitCaster->IsTotem()) + unitCaster = unitCaster->ToTotem()->GetOwner(); // in another case summon new - uint8 level = caster->getLevel(); + uint8 level = unitCaster->getLevel(); // level of pet summoned using engineering item based at engineering skill level - if (m_CastItem && caster->GetTypeId() == TYPEID_PLAYER) + if (m_CastItem && unitCaster->GetTypeId() == TYPEID_PLAYER) if (ItemTemplate const* proto = m_CastItem->GetTemplate()) if (proto->GetRequiredSkill() == SKILL_ENGINEERING) - if (uint16 skill202 = caster->ToPlayer()->GetSkillValue(SKILL_ENGINEERING)) + if (uint16 skill202 = unitCaster->ToPlayer()->GetSkillValue(SKILL_ENGINEERING)) level = skill202 / 5; float radius = 5.0f; int32 duration = m_spellInfo->CalcDuration(m_originalCaster); //TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; - Map* map = caster->GetMap(); - + Map* map = unitCaster->GetMap(); for (uint32 count = 0; count < numGuardians; ++count) { Position pos; @@ -5090,9 +5174,9 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* pos = *destTarget; else // randomize position for multiple summons - pos = m_caster->GetRandomPoint(*destTarget, radius); + pos = unitCaster->GetRandomPoint(*destTarget, radius); - TempSummon* summon = map->SummonCreature(entry, pos, properties, duration, caster, m_spellInfo->Id, 0, privateObjectOwner); + TempSummon* summon = map->SummonCreature(entry, pos, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner); if (!summon) return; @@ -5100,10 +5184,10 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* ((Guardian*)summon)->InitStatsForLevel(level); if (properties && properties->Control == SUMMON_CATEGORY_ALLY) - summon->SetFaction(caster->GetFaction()); + summon->SetFaction(unitCaster->GetFaction()); if (summon->HasUnitTypeMask(UNIT_MASK_MINION) && m_targets.HasDst()) - ((Minion*)summon)->SetFollowAngle(m_caster->GetAngle(summon)); + ((Minion*)summon)->SetFollowAngle(unitCaster->GetAngle(summon)); if (summon->GetEntry() == 27893) { @@ -5246,16 +5330,16 @@ void Spell::EffectCastButtons(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* player = m_caster->ToPlayer(); + if (!player) return; - Player* p_caster = m_caster->ToPlayer(); uint32 button_id = effectInfo->MiscValue + 132; uint32 n_buttons = effectInfo->MiscValueB; for (; n_buttons; --n_buttons, ++button_id) { - ActionButton const* ab = p_caster->GetActionButton(button_id); + ActionButton const* ab = player->GetActionButton(button_id); if (!ab || ab->GetType() != ACTION_BUTTON_SPELL) continue; @@ -5269,7 +5353,7 @@ void Spell::EffectCastButtons(SpellEffIndex /*effIndex*/) if (!spellInfo) continue; - if (!p_caster->HasSpell(spell_id) || p_caster->GetSpellHistory()->HasCooldown(spell_id)) + if (!player->HasSpell(spell_id) || player->GetSpellHistory()->HasCooldown(spell_id)) continue; if (!spellInfo->HasAttribute(SPELL_ATTR9_SUMMON_PLAYER_TOTEM)) @@ -5454,12 +5538,12 @@ void Spell::EffectCreateAreaTrigger(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - if (!m_targets.HasDst()) + if (!unitCaster || !m_targets.HasDst()) return; int32 duration = GetSpellInfo()->CalcDuration(GetCaster()); - AreaTrigger::CreateAreaTrigger(effectInfo->MiscValue, GetCaster(), nullptr, GetSpellInfo(), destTarget->GetPosition(), duration, m_SpellVisual, m_castId); + AreaTrigger::CreateAreaTrigger(effectInfo->MiscValue, unitCaster, nullptr, GetSpellInfo(), destTarget->GetPosition(), duration, m_SpellVisual, m_castId); } void Spell::EffectRemoveTalent(SpellEffIndex /*effIndex*/) @@ -5520,10 +5604,10 @@ void Spell::EffectCreateConversation(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - if (!m_targets.HasDst()) + if (!unitCaster || !m_targets.HasDst()) return; - Conversation::CreateConversation(effectInfo->MiscValue, GetCaster(), destTarget->GetPosition(), { GetCaster()->GetGUID() }, GetSpellInfo()); + Conversation::CreateConversation(effectInfo->MiscValue, unitCaster, destTarget->GetPosition(), { GetCaster()->GetGUID() }, GetSpellInfo()); } void Spell::EffectAddGarrisonFollower(SpellEffIndex /*effIndex*/) @@ -5717,11 +5801,12 @@ void Spell::EffectGiveArtifactPower(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET) return; - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* playerCaster = m_caster->ToPlayer(); + if (!playerCaster) return; - if (Aura* artifactAura = m_caster->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE)) - if (Item* artifact = m_caster->ToPlayer()->GetItemByGuid(artifactAura->GetCastItemGUID())) + if (Aura* artifactAura = playerCaster->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE)) + if (Item* artifact = playerCaster->GetItemByGuid(artifactAura->GetCastItemGUID())) artifact->GiveArtifactXp(damage, m_CastItem, uint32(effectInfo->MiscValue)); } @@ -5860,10 +5945,10 @@ void Spell::EffectCreatePrivateConversation(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + if (!unitCaster || !unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - Conversation::CreateConversation(effectInfo->MiscValue, GetCaster(), unitTarget->GetPosition(), { unitTarget->GetGUID() }, GetSpellInfo()); + Conversation::CreateConversation(effectInfo->MiscValue, unitCaster, unitTarget->GetPosition(), { unitTarget->GetGUID() }, GetSpellInfo()); } void Spell::EffectSendChatMessage(SpellEffIndex /*effIndex*/) @@ -5871,10 +5956,13 @@ void Spell::EffectSendChatMessage(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; + if (!unitCaster) + return; + uint32 broadcastTextId = effectInfo->MiscValue; if (!sBroadcastTextStore.LookupEntry(broadcastTextId)) return; ChatMsg chatType = ChatMsg(effectInfo->MiscValueB); - m_caster->Talk(broadcastTextId, chatType, CreatureTextMgr::GetRangeForChatType(chatType), unitTarget); + unitCaster->Talk(broadcastTextId, chatType, CreatureTextMgr::GetRangeForChatType(chatType), unitTarget); } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 6f74c90ece9..e343eba170d 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -452,7 +452,7 @@ bool SpellEffectInfo::IsUnitOwnedAuraEffect() const return IsAreaAuraEffect() || Effect == SPELL_EFFECT_APPLY_AURA || Effect == SPELL_EFFECT_APPLY_AURA_ON_PET; } -int32 SpellEffectInfo::CalcValue(Unit const* caster /*= nullptr*/, int32 const* bp /*= nullptr*/, Unit const* target /*= nullptr*/, float* variance /*= nullptr*/, uint32 castItemId /*= 0*/, int32 itemLevel /*= -1*/) const +int32 SpellEffectInfo::CalcValue(WorldObject const* caster /*= nullptr*/, int32 const* bp /*= nullptr*/, Unit const* target /*= nullptr*/, float* variance /*= nullptr*/, uint32 castItemId /*= 0*/, int32 itemLevel /*= -1*/) const { float basePointsPerLevel = RealPointsPerLevel; // TODO: this needs to be a float, not rounded @@ -460,6 +460,10 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster /*= nullptr*/, int32 const* float value = bp ? *bp : basePoints; float comboDamage = PointsPerResource; + Unit const* casterUnit = nullptr; + if (caster) + casterUnit = caster->ToUnit(); + if (Scaling.Variance) { float delta = fabs(Scaling.Variance * 0.5f); @@ -478,9 +482,9 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster /*= nullptr*/, int32 const* } else if (GetScalingExpectedStat() == ExpectedStatType::None) { - if (caster && basePointsPerLevel != 0.0f) + if (casterUnit && basePointsPerLevel != 0.0f) { - int32 level = int32(caster->getLevel()); + int32 level = int32(casterUnit->getLevel()); if (level > int32(_spellInfo->MaxLevel) && _spellInfo->MaxLevel > 0) level = int32(_spellInfo->MaxLevel); @@ -493,28 +497,29 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster /*= nullptr*/, int32 const* } // random damage - if (caster) + if (casterUnit) { // bonus amount from combo points - if (caster->m_playerMovingMe && comboDamage) - if (uint32 comboPoints = caster->m_playerMovingMe->GetComboPoints()) + if (comboDamage) + if (uint32 comboPoints = casterUnit->GetComboPoints()) value += comboDamage * comboPoints; + } + if (caster) value = caster->ApplyEffectModifiers(_spellInfo, EffectIndex, value); - } return int32(round(value)); } -int32 SpellEffectInfo::CalcBaseValue(Unit const* caster, Unit const* target, uint32 itemId, int32 itemLevel) const +int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* target, uint32 itemId, int32 itemLevel) const { if (Scaling.Coefficient != 0.0f) { uint32 level = _spellInfo->SpellLevel; if (target && _spellInfo->IsPositiveEffect(EffectIndex) && (Effect == SPELL_EFFECT_APPLY_AURA)) level = target->getLevel(); - else if (caster) - level = caster->getLevel(); + else if (caster && caster->IsUnit()) + level = caster->ToUnit()->getLevel(); if (_spellInfo->BaseLevel && !_spellInfo->HasAttribute(SPELL_ATTR11_SCALES_WITH_ITEM_LEVEL) && _spellInfo->HasAttribute(SPELL_ATTR10_USE_SPELL_BASE_LEVEL_FOR_SCALING)) level = _spellInfo->BaseLevel; @@ -591,7 +596,7 @@ int32 SpellEffectInfo::CalcBaseValue(Unit const* caster, Unit const* target, uin if (ContentTuningEntry const* contentTuning = sContentTuningStore.LookupEntry(contentTuningId)) expansion = contentTuning->ExpansionID; - int32 level = caster ? int32(caster->getLevel()) : 1; + int32 level = caster && caster->IsUnit() ? int32(caster->ToUnit()->getLevel()) : 1; value = sDB2Manager.EvaluateExpectedStat(stat, level, expansion, 0, CLASS_NONE) * BasePoints / 100.0f; } @@ -599,7 +604,7 @@ int32 SpellEffectInfo::CalcBaseValue(Unit const* caster, Unit const* target, uin } } -float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const +float SpellEffectInfo::CalcValueMultiplier(WorldObject* caster, Spell* spell /*= nullptr*/) const { float multiplier = Amplitude; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr)) @@ -607,7 +612,7 @@ float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const return multiplier; } -float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const +float SpellEffectInfo::CalcDamageMultiplier(WorldObject* caster, Spell* spell /*= nullptr*/) const { float multiplierPercent = ChainAmplitude * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr)) @@ -625,7 +630,7 @@ bool SpellEffectInfo::HasMaxRadius() const return MaxRadiusEntry != nullptr; } -float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const +float SpellEffectInfo::CalcRadius(WorldObject* caster /*= nullptr*/, Spell* spell /*= nullptr*/) const { const SpellRadiusEntry* entry = RadiusEntry; if (!HasRadius() && HasMaxRadius()) @@ -642,8 +647,11 @@ float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const if (caster) { - radius += entry->RadiusPerLevel * caster->getLevel(); + if (Unit* casterUnit = caster->ToUnit()) + radius += entry->RadiusPerLevel * casterUnit->getLevel(); + radius = std::min(radius, entry->RadiusMax); + if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(_spellInfo, SpellModOp::Radius, radius, spell); } @@ -1354,13 +1362,13 @@ bool SpellInfo::HasTargetType(::Targets target) const return false; } -bool SpellInfo::CanBeInterrupted(Unit* interruptCaster, Unit* interruptTarget) const +bool SpellInfo::CanBeInterrupted(WorldObject const* interruptCaster, Unit const* interruptTarget) const { return HasAttribute(SPELL_ATTR7_CAN_ALWAYS_BE_INTERRUPTED) || HasChannelInterruptFlag(SpellAuraInterruptFlags::Damage | SpellAuraInterruptFlags::EnteringCombat) || (interruptTarget->IsPlayer() && InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly)) || InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancels) - || interruptCaster->HasAuraTypeWithMiscvalue(SPELL_AURA_ALLOW_INTERRUPT_SPELL, Id) + || (interruptCaster->IsUnit() && interruptCaster->ToUnit()->HasAuraTypeWithMiscvalue(SPELL_AURA_ALLOW_INTERRUPT_SPELL, Id)) || (!(interruptTarget->GetMechanicImmunityMask() & (1 << MECHANIC_INTERRUPT)) && !interruptTarget->HasAuraTypeWithAffectMask(SPELL_AURA_PREVENT_INTERRUPT, this) && PreventionType & SPELL_PREVENTION_TYPE_SILENCE); @@ -2086,7 +2094,7 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a return SPELL_CAST_OK; } -SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* target, bool implicit) const +SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject const* target, bool implicit /*= true*/) const { if (HasAttribute(SPELL_ATTR1_CANT_TARGET_SELF) && caster == target) return SPELL_FAILED_BAD_TARGETS; @@ -2188,7 +2196,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta } // check GM mode and GM invisibility - only for player casts (npc casts are controlled by AI) and negative spells - if (unitTarget != caster && (caster->IsControlledByPlayer() || !IsPositive()) && unitTarget->GetTypeId() == TYPEID_PLAYER) + if (unitTarget != caster && (caster->GetAffectingPlayer() || !IsPositive()) && unitTarget->GetTypeId() == TYPEID_PLAYER) { if (!unitTarget->ToPlayer()->IsVisible()) return SPELL_FAILED_BM_OR_INVISGOD; @@ -2208,13 +2216,16 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta him, because it would be it's passenger, there's no such case where this gets to fail legitimacy, this problem cannot be solved from within the check in other way since target type cannot be called for the spell currently Spell examples: [ID - 52864 Devour Water, ID - 52862 Devour Wind, ID - 49370 Wyrmrest Defender: Destabilize Azure Dragonshrine Effect] */ - if (!caster->IsVehicle() && !(caster->GetCharmerOrOwner() == target)) + if (Unit const* unitCaster = caster->ToUnit()) { - if (TargetAuraState && !unitTarget->HasAuraState(AuraStateType(TargetAuraState), this, caster)) - return SPELL_FAILED_TARGET_AURASTATE; + if (!unitCaster->IsVehicle() && !(unitCaster->GetCharmerOrOwner() == target)) + { + if (TargetAuraState && !unitTarget->HasAuraState(AuraStateType(TargetAuraState), this, unitCaster)) + return SPELL_FAILED_TARGET_AURASTATE; - if (ExcludeTargetAuraState && unitTarget->HasAuraState(AuraStateType(ExcludeTargetAuraState), this, caster)) - return SPELL_FAILED_TARGET_AURASTATE; + if (ExcludeTargetAuraState && unitTarget->HasAuraState(AuraStateType(ExcludeTargetAuraState), this, unitCaster)) + return SPELL_FAILED_TARGET_AURASTATE; + } } if (TargetAuraSpell && !unitTarget->HasAura(TargetAuraSpell)) @@ -2237,7 +2248,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta return SPELL_CAST_OK; } -SpellCastResult SpellInfo::CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget) const +SpellCastResult SpellInfo::CheckExplicitTarget(WorldObject const* caster, WorldObject const* target, Item const* itemTarget /*= nullptr*/) const { uint32 neededTargets = GetExplicitTargetMask(); if (!target) @@ -2252,19 +2263,20 @@ SpellCastResult SpellInfo::CheckExplicitTarget(Unit const* caster, WorldObject c { if (neededTargets & (TARGET_FLAG_UNIT_ENEMY | TARGET_FLAG_UNIT_ALLY | TARGET_FLAG_UNIT_RAID | TARGET_FLAG_UNIT_PARTY | TARGET_FLAG_UNIT_MINIPET | TARGET_FLAG_UNIT_PASSENGER)) { + Unit const* unitCaster = caster->ToUnit(); if (neededTargets & TARGET_FLAG_UNIT_ENEMY) if (caster->IsValidAttackTarget(unitTarget, this)) return SPELL_CAST_OK; - if (neededTargets & TARGET_FLAG_UNIT_ALLY - || (neededTargets & TARGET_FLAG_UNIT_PARTY && caster->IsInPartyWith(unitTarget)) - || (neededTargets & TARGET_FLAG_UNIT_RAID && caster->IsInRaidWith(unitTarget))) + if ((neededTargets & TARGET_FLAG_UNIT_ALLY) + || ((neededTargets & TARGET_FLAG_UNIT_PARTY) && unitCaster && unitCaster->IsInPartyWith(unitTarget)) + || ((neededTargets & TARGET_FLAG_UNIT_RAID) && unitCaster && unitCaster->IsInRaidWith(unitTarget))) if (caster->IsValidAssistTarget(unitTarget, this)) return SPELL_CAST_OK; - if (neededTargets & TARGET_FLAG_UNIT_MINIPET) - if (unitTarget->GetGUID() == caster->GetCritterGUID()) + if ((neededTargets & TARGET_FLAG_UNIT_MINIPET) && unitCaster) + if (unitTarget->GetGUID() == unitCaster->GetCritterGUID()) return SPELL_CAST_OK; - if (neededTargets & TARGET_FLAG_UNIT_PASSENGER) - if (unitTarget->IsOnVehicle(caster)) + if ((neededTargets & TARGET_FLAG_UNIT_PASSENGER) && unitCaster) + if (unitTarget->IsOnVehicle(unitCaster)) return SPELL_CAST_OK; return SPELL_FAILED_BAD_TARGETS; } @@ -3695,14 +3707,14 @@ uint32 SpellInfo::GetAllowedMechanicMask() const return _allowedMechanicMask; } -float SpellInfo::GetMinRange(bool positive) const +float SpellInfo::GetMinRange(bool positive /*= false*/) const { if (!RangeEntry) return 0.0f; return RangeEntry->RangeMin[positive ? 1 : 0]; } -float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const +float SpellInfo::GetMaxRange(bool positive /*= false*/, WorldObject* caster /*= nullptr*/, Spell* spell /*= nullptr*/) const { if (!RangeEntry) return 0.0f; @@ -3714,7 +3726,7 @@ float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const return range; } -int32 SpellInfo::CalcDuration(Unit* caster /*= nullptr*/) const +int32 SpellInfo::CalcDuration(WorldObject const* caster /*= nullptr*/) const { int32 duration = GetDuration(); @@ -3802,8 +3814,13 @@ uint32 SpellInfo::GetRecoveryTime() const return RecoveryTime > CategoryRecoveryTime ? RecoveryTime : CategoryRecoveryTime; } -Optional<SpellPowerCost> SpellInfo::CalcPowerCost(Powers powerType, bool optionalCost, Unit const* caster, SpellSchoolMask schoolMask, Spell* spell /*= nullptr*/) const +Optional<SpellPowerCost> SpellInfo::CalcPowerCost(Powers powerType, bool optionalCost, WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell /*= nullptr*/) const { + // gameobject casts don't use power + Unit const* unitCaster = caster->ToUnit(); + if (!unitCaster) + return {}; + auto itr = std::find_if(PowerCosts.cbegin(), PowerCosts.cend(), [powerType](SpellPowerEntry const* spellPowerEntry) { return spellPowerEntry && spellPowerEntry->PowerType == powerType; @@ -3814,9 +3831,14 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(Powers powerType, bool optiona return CalcPowerCost(*itr, optionalCost, caster, schoolMask, spell); } -Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, bool optionalCost, Unit const* caster, SpellSchoolMask schoolMask, Spell* spell /*= nullptr*/) const +Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, bool optionalCost, WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell /*= nullptr*/) const { - if (power->RequiredAuraSpellID && !caster->HasAura(power->RequiredAuraSpellID)) + // gameobject casts don't use power + Unit const* unitCaster = caster->ToUnit(); + if (!unitCaster) + return {}; + + if (power->RequiredAuraSpellID && !unitCaster->HasAura(power->RequiredAuraSpellID)) return {}; // Spell drain all exist power on cast (Only paladin lay of Hands) @@ -3827,7 +3849,7 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, { SpellPowerCost cost; cost.Power = POWER_HEALTH; - cost.Amount = caster->GetHealth(); + cost.Amount = unitCaster->GetHealth(); return cost; } // Else drain all power @@ -3835,7 +3857,7 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, { SpellPowerCost cost; cost.Power = Powers(power->PowerType); - cost.Amount = caster->GetPower(cost.Power); + cost.Amount = unitCaster->GetPower(cost.Power); return cost; } @@ -3856,12 +3878,12 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, // health as power used case POWER_HEALTH: if (G3D::fuzzyEq(power->PowerCostPct, 0.0f)) - powerCost += int32(CalculatePct(caster->GetMaxHealth(), power->PowerCostMaxPct)); + powerCost += int32(CalculatePct(unitCaster->GetMaxHealth(), power->PowerCostMaxPct)); else - powerCost += int32(CalculatePct(caster->GetMaxHealth(), power->PowerCostPct)); + powerCost += int32(CalculatePct(unitCaster->GetMaxHealth(), power->PowerCostPct)); break; case POWER_MANA: - powerCost += int32(CalculatePct(caster->GetCreateMana(), power->PowerCostPct)); + powerCost += int32(CalculatePct(unitCaster->GetCreateMana(), power->PowerCostPct)); break; case POWER_ALTERNATE_POWER: TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", power->PowerType, Id); @@ -3883,7 +3905,7 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, else { powerCost = int32(power->OptionalCost); - powerCost += caster->GetTotalAuraModifier(SPELL_AURA_MOD_ADDITIONAL_POWER_COST, [this, power](AuraEffect const* aurEff) -> bool + powerCost += unitCaster->GetTotalAuraModifier(SPELL_AURA_MOD_ADDITIONAL_POWER_COST, [this, power](AuraEffect const* aurEff) -> bool { return aurEff->GetMiscValue() == power->PowerType && aurEff->IsAffectingSpell(this); @@ -3896,7 +3918,7 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, if (HasAttribute(SPELL_ATTR4_SPELL_VS_EXTEND_COST)) { uint32 speed = 0; - if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(caster->GetShapeshiftForm())) + if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(unitCaster->GetShapeshiftForm())) speed = ss->CombatRoundTime; else { @@ -3904,7 +3926,7 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, if (!HasAttribute(SPELL_ATTR3_MAIN_HAND) && HasAttribute(SPELL_ATTR3_REQ_OFFHAND)) slot = OFF_ATTACK; - speed = caster->GetBaseAttackTime(slot); + speed = unitCaster->GetBaseAttackTime(slot); } powerCost += speed / 100; @@ -3915,7 +3937,7 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, if (!optionalCost) { // Flat mod from caster auras by spell school and power type - for (AuraEffect const* aura : caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL)) + for (AuraEffect const* aura : unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL)) { if (!(aura->GetMiscValue() & schoolMask)) continue; @@ -3928,7 +3950,7 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, } // PCT mod from user auras by spell school and power type - for (auto schoolCostPct : caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT)) + for (auto schoolCostPct : unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT)) { if (!(schoolCostPct->GetMiscValue() & schoolMask)) continue; @@ -3941,7 +3963,7 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, } // Apply cost mod by spell - if (Player* modOwner = caster->GetSpellModOwner()) + if (Player* modOwner = unitCaster->GetSpellModOwner()) { Optional<SpellModOp> mod; switch (power->OrderIndex) @@ -3974,19 +3996,19 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, } } - if (!caster->IsControlledByPlayer() && G3D::fuzzyEq(power->PowerCostPct, 0.0f) && SpellLevel && power->PowerType == POWER_MANA) + if (!unitCaster->IsControlledByPlayer() && G3D::fuzzyEq(power->PowerCostPct, 0.0f) && SpellLevel && power->PowerType == POWER_MANA) { if (HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)) { GtNpcManaCostScalerEntry const* spellScaler = sNpcManaCostScalerGameTable.GetRow(SpellLevel); - GtNpcManaCostScalerEntry const* casterScaler = sNpcManaCostScalerGameTable.GetRow(caster->getLevel()); + GtNpcManaCostScalerEntry const* casterScaler = sNpcManaCostScalerGameTable.GetRow(unitCaster->getLevel()); if (spellScaler && casterScaler) powerCost *= casterScaler->Scaler / spellScaler->Scaler; } } if (power->PowerType == POWER_MANA) - powerCost = float(powerCost) * (1.0f + caster->m_unitData->ManaCostMultiplier); + powerCost = float(powerCost) * (1.0f + unitCaster->m_unitData->ManaCostMultiplier); // power cost cannot become negative if initially positive if (initiallyNegative != (powerCost < 0)) @@ -3998,41 +4020,44 @@ Optional<SpellPowerCost> SpellInfo::CalcPowerCost(SpellPowerEntry const* power, return cost; } -std::vector<SpellPowerCost> SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, Spell* spell) const +std::vector<SpellPowerCost> SpellInfo::CalcPowerCost(WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell) const { std::vector<SpellPowerCost> costs; - costs.reserve(MAX_POWERS_PER_SPELL); - - auto getOrCreatePowerCost = [&](Powers powerType) -> SpellPowerCost& + if (caster->IsUnit()) { - auto itr = std::find_if(costs.begin(), costs.end(), [powerType](SpellPowerCost const& cost) - { - return cost.Power == powerType; - }); - if (itr != costs.end()) - return *itr; - - SpellPowerCost cost; - cost.Power = powerType; - cost.Amount = 0; - costs.push_back(cost); - return costs.back(); - }; + costs.reserve(MAX_POWERS_PER_SPELL); - for (SpellPowerEntry const* power : PowerCosts) - { - if (!power) - continue; + auto getOrCreatePowerCost = [&](Powers powerType) -> SpellPowerCost& + { + auto itr = std::find_if(costs.begin(), costs.end(), [powerType](SpellPowerCost const& cost) + { + return cost.Power == powerType; + }); + if (itr != costs.end()) + return *itr; - if (Optional<SpellPowerCost> cost = CalcPowerCost(power, false, caster, schoolMask, spell)) - getOrCreatePowerCost(cost->Power).Amount += cost->Amount; + SpellPowerCost cost; + cost.Power = powerType; + cost.Amount = 0; + costs.push_back(cost); + return costs.back(); + }; - if (Optional<SpellPowerCost> optionalCost = CalcPowerCost(power, true, caster, schoolMask, spell)) + for (SpellPowerEntry const* power : PowerCosts) { - SpellPowerCost& cost = getOrCreatePowerCost(optionalCost->Power); - int32 remainingPower = caster->GetPower(optionalCost->Power) - cost.Amount; - if (remainingPower > 0) - cost.Amount += std::min(optionalCost->Amount, remainingPower); + if (!power) + continue; + + if (Optional<SpellPowerCost> cost = CalcPowerCost(power, false, caster, schoolMask, spell)) + getOrCreatePowerCost(cost->Power).Amount += cost->Amount; + + if (Optional<SpellPowerCost> optionalCost = CalcPowerCost(power, true, caster, schoolMask, spell)) + { + SpellPowerCost& cost = getOrCreatePowerCost(optionalCost->Power); + int32 remainingPower = caster->ToUnit()->GetPower(optionalCost->Power) - cost.Amount; + if (remainingPower > 0) + cost.Amount += std::min(optionalCost->Amount, remainingPower); + } } } @@ -4266,7 +4291,7 @@ bool SpellInfo::IsHighRankOf(SpellInfo const* spellInfo) const return false; } -uint32 SpellInfo::GetSpellXSpellVisualId(Unit const* caster /*= nullptr*/) const +uint32 SpellInfo::GetSpellXSpellVisualId(WorldObject const* caster /*= nullptr*/) const { for (SpellXSpellVisualEntry const* visual : _visuals) { @@ -4278,7 +4303,7 @@ uint32 SpellInfo::GetSpellXSpellVisualId(Unit const* caster /*= nullptr*/) const return 0; } -uint32 SpellInfo::GetSpellVisual(Unit const* caster /*= nullptr*/) const +uint32 SpellInfo::GetSpellVisual(WorldObject const* caster /*= nullptr*/) const { if (SpellXSpellVisualEntry const* visual = sSpellXSpellVisualStore.LookupEntry(GetSpellXSpellVisualId(caster))) { diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 54ec93a8471..dfc169ab570 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -314,14 +314,14 @@ public: bool IsAreaAuraEffect() const; bool IsUnitOwnedAuraEffect() const; - int32 CalcValue(Unit const* caster = nullptr, int32 const* basePoints = nullptr, Unit const* target = nullptr, float* variance = nullptr, uint32 castItemId = 0, int32 itemLevel = -1) const; - int32 CalcBaseValue(Unit const* caster, Unit const* target, uint32 itemId, int32 itemLevel) const; - float CalcValueMultiplier(Unit* caster, Spell* spell = nullptr) const; - float CalcDamageMultiplier(Unit* caster, Spell* spell = nullptr) const; + int32 CalcValue(WorldObject const* caster = nullptr, int32 const* basePoints = nullptr, Unit const* target = nullptr, float* variance = nullptr, uint32 castItemId = 0, int32 itemLevel = -1) const; + int32 CalcBaseValue(WorldObject const* caster, Unit const* target, uint32 itemId, int32 itemLevel) const; + float CalcValueMultiplier(WorldObject* caster, Spell* spell = nullptr) const; + float CalcDamageMultiplier(WorldObject* caster, Spell* spell = nullptr) const; bool HasRadius() const; bool HasMaxRadius() const; - float CalcRadius(Unit* caster = nullptr, Spell* = nullptr) const; + float CalcRadius(WorldObject* caster = nullptr, Spell* = nullptr) const; uint32 GetProvidedTargetMask() const; uint32 GetMissingTargetMask(bool srcSet = false, bool destSet = false, uint32 mask = 0) const; @@ -500,7 +500,7 @@ class TC_GAME_API SpellInfo bool HasAttribute(SpellAttr14 attribute) const { return !!(AttributesEx14 & attribute); } bool HasAttribute(SpellCustomAttributes customAttribute) const { return !!(AttributesCu & customAttribute); } - bool CanBeInterrupted(Unit* interruptCaster, Unit* interruptTarget) const; + bool CanBeInterrupted(WorldObject const* interruptCaster, Unit const* interruptTarget) const; bool HasAnyAuraInterruptFlag() const; bool HasAuraInterruptFlag(SpellAuraInterruptFlags flag) const { return AuraInterruptFlags.HasFlag(flag); } @@ -564,8 +564,8 @@ class TC_GAME_API SpellInfo SpellCastResult CheckShapeshift(uint32 form) const; SpellCastResult CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = nullptr) const; - SpellCastResult CheckTarget(Unit const* caster, WorldObject const* target, bool implicit = true) const; - SpellCastResult CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget = nullptr) const; + SpellCastResult CheckTarget(WorldObject const* caster, WorldObject const* target, bool implicit = true) const; + SpellCastResult CheckExplicitTarget(WorldObject const* caster, WorldObject const* target, Item const* itemTarget = nullptr) const; SpellCastResult CheckVehicle(Unit const* caster) const; bool CheckTargetCreatureType(Unit const* target) const; @@ -583,9 +583,9 @@ class TC_GAME_API SpellInfo SpellSpecificType GetSpellSpecific() const; float GetMinRange(bool positive = false) const; - float GetMaxRange(bool positive = false, Unit* caster = nullptr, Spell* spell = nullptr) const; + float GetMaxRange(bool positive = false, WorldObject* caster = nullptr, Spell* spell = nullptr) const; - int32 CalcDuration(Unit* caster = nullptr) const; + int32 CalcDuration(WorldObject const* caster = nullptr) const; int32 GetDuration() const; int32 GetMaxDuration() const; @@ -594,9 +594,9 @@ class TC_GAME_API SpellInfo uint32 CalcCastTime(Spell* spell = nullptr) const; uint32 GetRecoveryTime() const; - Optional<SpellPowerCost> CalcPowerCost(Powers powerType, bool optionalCost, Unit const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; - Optional<SpellPowerCost> CalcPowerCost(SpellPowerEntry const* power, bool optionalCost, Unit const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; - std::vector<SpellPowerCost> CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; + Optional<SpellPowerCost> CalcPowerCost(Powers powerType, bool optionalCost, WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; + Optional<SpellPowerCost> CalcPowerCost(SpellPowerEntry const* power, bool optionalCost, WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; + std::vector<SpellPowerCost> CalcPowerCost(WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; float CalcProcPPM(Unit* caster, int32 itemLevel) const; @@ -611,8 +611,8 @@ class TC_GAME_API SpellInfo bool IsDifferentRankOf(SpellInfo const* spellInfo) const; bool IsHighRankOf(SpellInfo const* spellInfo) const; - uint32 GetSpellXSpellVisualId(Unit const* caster = nullptr) const; - uint32 GetSpellVisual(Unit const* caster = nullptr) const; + uint32 GetSpellXSpellVisualId(WorldObject const* caster = nullptr) const; + uint32 GetSpellVisual(WorldObject const* caster = nullptr) const; SpellEffectInfoVector const& GetEffects() const { return _effects; } SpellEffectInfo const* GetEffect(uint32 index) const { return index < _effects.size() ? _effects[index] : nullptr; } diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index 577273d4ff7..04327bf59c1 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -467,12 +467,17 @@ bool SpellScript::IsInEffectHook() const Unit* SpellScript::GetCaster() const { - return m_spell->GetCaster(); + return m_spell->GetCaster()->ToUnit(); +} + +GameObject* SpellScript::GetGObjCaster() const +{ + return m_spell->GetCaster()->ToGameObject(); } Unit* SpellScript::GetOriginalCaster() const { - return m_spell->GetOriginalCaster(); + return m_spell->GetOriginalCaster(); } SpellInfo const* SpellScript::GetSpellInfo() const @@ -1161,7 +1166,16 @@ ObjectGuid AuraScript::GetCasterGUID() const Unit* AuraScript::GetCaster() const { - return m_aura->GetCaster(); + if (WorldObject* caster = m_aura->GetCaster()) + return caster->ToUnit(); + return nullptr; +} + +GameObject* AuraScript::GetGObjCaster() const +{ + if (WorldObject* caster = m_aura->GetCaster()) + return caster->ToGameObject(); + return nullptr; } WorldObject* AuraScript::GetOwner() const diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 575e5721bbe..9457957b539 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -410,6 +410,7 @@ class TC_GAME_API SpellScript : public _SpellScript // // methods useable during all spell handling phases Unit* GetCaster() const; + GameObject* GetGObjCaster() const; Unit* GetOriginalCaster() const; SpellInfo const* GetSpellInfo() const; SpellValue const* GetSpellValue() const; @@ -933,6 +934,8 @@ class TC_GAME_API AuraScript : public _SpellScript ObjectGuid GetCasterGUID() const; // returns unit which cast the aura or NULL if not avalible (caster logged out for example) Unit* GetCaster() const; + // returns gameobject which cast the aura or NULL if not available + GameObject* GetGObjCaster() const; // returns object on which aura was cast, target for non-area auras, area aura source for area auras WorldObject* GetOwner() const; // returns owner if it's unit or unit derived object, NULL otherwise (only for persistent area auras NULL is returned) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index 69b2bf0d4c7..a1b4698a37c 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -1865,12 +1865,12 @@ class spell_icc_sprit_alarm : public SpellScriptLoader return; } - if (GameObject* trap = GetCaster()->FindNearestGameObject(trapId, 5.0f)) + if (GameObject* trap = GetGObjCaster()->FindNearestGameObject(trapId, 5.0f)) trap->SetRespawnTime(trap->GetGOInfo()->GetAutoCloseTime() / IN_MILLISECONDS); std::list<Creature*> wards; - GetCaster()->GetCreatureListWithEntryInGrid(wards, NPC_DEATHBOUND_WARD, 150.0f); - wards.sort(Trinity::ObjectDistanceOrderPred(GetCaster())); + GetGObjCaster()->GetCreatureListWithEntryInGrid(wards, NPC_DEATHBOUND_WARD, 150.0f); + wards.sort(Trinity::ObjectDistanceOrderPred(GetGObjCaster())); for (std::list<Creature*>::iterator itr = wards.begin(); itr != wards.end(); ++itr) { if ((*itr)->IsAlive() && (*itr)->HasAura(SPELL_STONEFORM)) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index e4aec26c651..6db930756a2 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -1310,7 +1310,7 @@ class go_ulduar_tower : public GameObjectScript InstanceScript* instance; - void Destroyed(Player* /*player*/, uint32 /*eventId*/) override + void Destroyed(WorldObject* /*attacker*/, uint32 /*eventId*/) override { switch (me->GetEntry()) { diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index f11802d79a5..737012cf41f 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -907,15 +907,17 @@ class spell_warl_unstable_affliction : public SpellScriptLoader { if (AuraEffect const* aurEff = GetEffect(EFFECT_1)) { - Unit* target = dispelInfo->GetDispeller(); - int32 bp = aurEff->GetAmount(); - bp = target->SpellDamageBonusTaken(caster, aurEff->GetSpellInfo(), bp, DOT); - bp *= 9; - - // backfire damage and silence - CastSpellExtraArgs args(aurEff); - args.AddSpellBP0(bp); - caster->CastSpell(target, SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL, args); + if (Unit* target = dispelInfo->GetDispeller()->ToUnit()) + { + int32 bp = aurEff->GetAmount(); + bp = target->SpellDamageBonusTaken(caster, aurEff->GetSpellInfo(), bp, DOT); + bp *= 9; + + // backfire damage and silence + CastSpellExtraArgs args(aurEff); + args.AddSpellBP0(bp); + caster->CastSpell(target, SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL, args); + } } } } |