diff options
Diffstat (limited to 'src')
47 files changed, 2747 insertions, 2370 deletions
diff --git a/src/server/game/AI/CoreAI/GameObjectAI.h b/src/server/game/AI/CoreAI/GameObjectAI.h index 3c33dac40db..89d2958e0d8 100644 --- a/src/server/game/AI/CoreAI/GameObjectAI.h +++ b/src/server/game/AI/CoreAI/GameObjectAI.h @@ -71,8 +71,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*/) { } @@ -83,7 +83,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 78b276325de..d7cfec7b0b3 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -124,10 +124,12 @@ class TC_GAME_API CreatureAI : public UnitAI virtual void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) { } // 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 1aeb6a65db7..dd6726b0682 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -141,36 +141,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 at waypoint reached or PointMovement end - void MovementInform(uint32 /*type*/, uint32 /*id*/) override { } - - // Called when AI is temporarily replaced or put back when possess is applied or removed - void OnPossess(bool /*apply*/) { } - // ************* // Variables // ************* @@ -182,12 +155,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; @@ -275,8 +242,8 @@ struct TC_GAME_API ScriptedAI : public CreatureAI // return true for 25 man or 25 man heroic mode bool Is25ManRaid() const { return _difficulty & RAID_DIFFICULTY_MASK_25MAN; } - 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) { @@ -291,8 +258,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) { @@ -307,8 +274,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 768faad0af6..ff04a16da50 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -1023,9 +1023,9 @@ void SmartGameObjectAI::QuestReward(Player* player, Quest const* quest, uint32 o } // 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 e27ac2ad1e8..2164509ac52 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -261,7 +261,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, 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/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 0cc53de0cb5..6f9c1b19c3b 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -293,7 +293,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) } } -bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/, uint32 miscvalue2 /* = 0*/) const +bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, WorldObject const* target, uint32 miscvalue1 /*= 0*/, uint32 miscvalue2 /* = 0*/) const { switch (dataType) { @@ -322,13 +322,14 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un case ACHIEVEMENT_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 ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD: - if (target && !target->IsAlive()) - if (Player const* player = target->ToPlayer()) - if (player->GetDeathTimer() != 0) - // flag set == must be same team, not set == different team - return (player->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0); + if (!target || target->GetTypeId() != TYPEID_PLAYER) + return false; + if (Player const* player = target->ToPlayer()) + if (!player->IsAlive() && player->GetDeathTimer() != 0) + // flag set == must be same team, not set == different team + return (player->GetTeam() == source->GetTeam()) == (player_dead.own_team_flag != 0); return false; case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA: return source->HasAuraEffect(aura.spell_id, aura.effect_idx); @@ -339,19 +340,41 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un return area.id == zone_id || area.id == area_id; } case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA: - return target && target->HasAuraEffect(aura.spell_id, aura.effect_idx); + { + if (!target) + return false; + Unit const* unitTarget = target->ToUnit(); + if (!unitTarget) + return false; + return unitTarget->HasAuraEffect(aura.spell_id, aura.effect_idx); + } case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE: return CompareValues(ComparisionType(value.compType), miscvalue1, value.value); case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL: + { if (!target) return false; - return target->getLevel() >= level.minlevel; + Unit const* unitTarget = target->ToUnit(); + if (!unitTarget) + return false; + return unitTarget->getLevel() >= level.minlevel; + } case ACHIEVEMENT_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 ACHIEVEMENT_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 ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY: if (source->GetMap()->IsRaid()) if (source->GetMap()->Is25ManRaid() != ((difficulty.difficulty & RAID_DIFFICULTY_MASK_25MAN) != 0)) @@ -394,7 +417,11 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un dataType, criteria_id, map->GetId()); return false; } - return instance->CheckAchievementCriteriaMeet(criteria_id, source, target, miscvalue1); + + Unit const* unitTarget = nullptr; + if (target) + unitTarget = target->ToUnit(); + return instance->CheckAchievementCriteriaMeet(criteria_id, source, unitTarget, miscvalue1); } case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPPED_ITEM: { @@ -440,7 +467,7 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un return false; } -bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/, uint32 miscvalue2 /* = 0*/) const +bool AchievementCriteriaDataSet::Meets(Player const* source, WorldObject const* target, uint32 miscvalue1 /*= 0*/, uint32 miscvalue2 /* = 0*/) const { for (Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr) if (!itr->Meets(criteria_id, source, target, miscvalue1, miscvalue2)) @@ -714,7 +741,7 @@ void AchievementMgr::SendCriteriaUpdate(AchievementCriteriaEntry const* entry, C void AchievementMgr::CheckAllAchievementCriteria() { // suppress sending packets - for (uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i) + for (uint32 i = 0; i < ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i) UpdateAchievementCriteria(AchievementCriteriaTypes(i)); } @@ -723,7 +750,7 @@ static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 }; /** * this function will be called whenever the user might have done a criteria relevant action */ -void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/, Unit* unit /*= nullptr*/) +void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/, WorldObject* ref /*= nullptr*/) { if (type >= ACHIEVEMENT_CRITERIA_TYPE_TOTAL) { @@ -749,7 +776,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui if (!achievement) continue; - if (!CanUpdateCriteria(achievementCriteria, achievement, miscValue1, miscValue2, unit)) + if (!CanUpdateCriteria(achievementCriteria, achievement, miscValue1, miscValue2, ref)) continue; switch (type) @@ -761,7 +788,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui break; default: if (AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria)) - if (!data->Meets(GetPlayer(), unit, miscValue1, miscValue2)) + if (!data->Meets(GetPlayer(), ref, miscValue1, miscValue2)) continue; break; } @@ -925,7 +952,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui { // those requirements couldn't be found in the dbc AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); - if (!data || !data->Meets(GetPlayer(), unit, miscValue1)) + if (!data || !data->Meets(GetPlayer(), ref, miscValue1)) { // reset the progress as we have a win without the requirement. SetCriteriaProgress(achievementCriteria, 0); @@ -1613,7 +1640,7 @@ bool AchievementMgr::HasAchieved(uint32 achievementId) const return m_completedAchievements.find(achievementId) != m_completedAchievements.end(); } -bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, Unit const* unit) +bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, WorldObject const* ref) { if (DisableMgr::IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, criteria->ID, nullptr)) { @@ -1637,7 +1664,7 @@ bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, return false; } - if (!RequirementsSatisfied(criteria, achievement, miscValue1, miscValue2, unit)) + if (!RequirementsSatisfied(criteria, achievement, miscValue1, miscValue2, ref)) { TC_LOG_TRACE("achievement", "CanUpdateCriteria: (Id: %u Type %s) Requirements have not been satisfied", criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Type)); @@ -1683,7 +1710,7 @@ bool AchievementMgr::ConditionsSatisfied(AchievementCriteriaEntry const* criteri return true; } -bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, Unit const* unit) const +bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, WorldObject const* ref) const { switch (AchievementCriteriaTypes(achievementCriteria->Type)) { @@ -1923,7 +1950,7 @@ bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* achie 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; diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h index 91fa20adb14..44a27f35118 100644 --- a/src/server/game/Achievements/AchievementMgr.h +++ b/src/server/game/Achievements/AchievementMgr.h @@ -27,8 +27,8 @@ #include <unordered_map> #include <vector> -class Unit; class Player; +class WorldObject; class WorldPacket; typedef std::vector<AchievementCriteriaEntry const*> AchievementCriteriaEntryList; @@ -217,7 +217,7 @@ struct AchievementCriteriaData } bool IsValid(AchievementCriteriaEntry const* criteria); - bool Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscValue1 = 0, uint32 miscValue2 = 0) const; + bool Meets(uint32 criteria_id, Player const* source, WorldObject const* target, uint32 miscValue1 = 0, uint32 miscValue2 = 0) const; }; struct TC_GAME_API AchievementCriteriaDataSet @@ -225,7 +225,7 @@ struct TC_GAME_API AchievementCriteriaDataSet AchievementCriteriaDataSet() : criteria_id(0) { } typedef std::vector<AchievementCriteriaData> Storage; void Add(AchievementCriteriaData 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) {criteria_id = id;} private: uint32 criteria_id; @@ -281,7 +281,7 @@ class TC_GAME_API AchievementMgr void LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult); void SaveToDB(SQLTransaction& trans); void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint32 value, bool evenIfCriteriaComplete); - void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 = 0, uint32 miscValue2 = 0, Unit* unit = nullptr); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 = 0, uint32 miscValue2 = 0, WorldObject* ref = nullptr); void CompletedAchievement(AchievementEntry const* entry); void CheckAllAchievementCriteria(); void SendAllAchievementData() const; @@ -301,11 +301,11 @@ class TC_GAME_API AchievementMgr void CompletedCriteriaFor(AchievementEntry const* achievement); bool IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement); bool IsCompletedAchievement(AchievementEntry const* entry); - bool CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, Unit const* unit); + bool CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, WorldObject const* ref); void BuildAllDataPacket(WorldPacket* data) const; bool ConditionsSatisfied(AchievementCriteriaEntry const* criteria) const; - bool RequirementsSatisfied(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, Unit const* unit) const; + bool RequirementsSatisfied(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint32 miscValue1, uint32 miscValue2, WorldObject const* ref) const; Player* m_player; CriteriaProgressMap m_criteriaProgress; diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp index 6c56566ed44..ff20f9a8f01 100644 --- a/src/server/game/Conditions/DisableMgr.cpp +++ b/src/server/game/Conditions/DisableMgr.cpp @@ -295,7 +295,7 @@ void CheckQuestDisables() TC_LOG_INFO("server.loading", ">> Checked %u 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()) @@ -310,15 +310,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->ToCreature()->IsPet() && (spellFlags & SPELL_DISABLE_PET)))) || + (ref->GetTypeId() == TYPEID_GAMEOBJECT && (spellFlags & SPELL_DISABLE_GAMEOBJECT))) { if (spellFlags & SPELL_DISABLE_MAP) { std::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)) @@ -330,7 +331,7 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags if (spellFlags & SPELL_DISABLE_AREA) { std::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 } @@ -349,7 +350,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()) @@ -374,9 +375,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 b7f6aa6bd2a..1a1480bdcca 100644 --- a/src/server/game/Conditions/DisableMgr.h +++ b/src/server/game/Conditions/DisableMgr.h @@ -38,16 +38,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 @@ -58,7 +59,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/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index a0dfe97e94a..ac75d53fa9b 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -134,6 +134,15 @@ void Corpse::DeleteFromDB(ObjectGuid const& ownerGuid, SQLTransaction& trans) CharacterDatabase.ExecuteOrAppend(trans, stmt); } +uint32 Corpse::GetFaction() const +{ + // inherit faction from player race + uint32 const race = GetByteValue(CORPSE_FIELD_BYTES_1, 1); + + ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race); + return rEntry ? rEntry->FactionID : 0; +} + void Corpse::ResetGhostTime() { m_time = GameTime::GetGameTime(); diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index be86ee6bb00..e8128d88ec0 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -64,7 +64,8 @@ class TC_GAME_API Corpse : public WorldObject, public GridObject<Corpse> void DeleteFromDB(SQLTransaction& trans); static void DeleteFromDB(ObjectGuid const& ownerGuid, SQLTransaction& trans); - ObjectGuid GetOwnerGUID() const { return GetGuidValue(CORPSE_FIELD_OWNER); } + ObjectGuid GetOwnerGUID() const override { return GetGuidValue(CORPSE_FIELD_OWNER); } + uint32 GetFaction() const override; time_t const& GetGhostTime() const { return m_time; } void ResetGhostTime(); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d2ffa162436..1bd64266f4c 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2200,7 +2200,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; @@ -2221,7 +2221,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 { if (GetCreatureTemplate()->type == CREATURE_TYPE_MECHANICAL && spellInfo->Effects[index].Effect == SPELL_EFFECT_HEAL) return true; diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index c46e2022b01..c7360e1e9a3 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -122,8 +122,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool isCanTrainingAndResetTalentsOf(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 46d52328694..08903c21ae5 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -230,6 +230,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 12072d9bbd6..00fb20ac54f 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.h +++ b/src/server/game/Entities/DynamicObject/DynamicObject.h @@ -53,11 +53,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 GetUInt32Value(DYNAMICOBJECT_SPELLID); } SpellInfo const* GetSpellInfo() const; ObjectGuid GetCasterGUID() const { return GetGuidValue(DYNAMICOBJECT_CASTER); } + ObjectGuid GetOwnerGUID() const override { return GetCasterGUID(); } float GetRadius() const { return GetFloatValue(DYNAMICOBJECT_RADIUS); } protected: diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 5d8bd04e0ba..fbc8c260cdc 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -401,6 +401,8 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u void GameObject::Update(uint32 diff) { + m_Events.Update(diff); + if (AI()) AI()->UpdateAI(diff); else if (!AIM_Initialize()) @@ -695,8 +697,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.spellId) - CastSpell(target, goInfo->trap.spellId); + CastSpell(target, goInfo->trap.spellId, args); // Template value or 4 seconds m_cooldownTime = GameTime::GetGameTimeMS() + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4)) * IN_MILLISECONDS; @@ -1152,11 +1156,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) @@ -1993,67 +1992,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); - if (!spellInfo) - return; - - bool self = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (spellInfo->Effects[i].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); - - CastSpellExtraArgs args; - args.TriggerFlags = triggered; - if (Unit* owner = GetOwner()) - { - trigger->SetFaction(owner->GetFaction()); - if (owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) - trigger->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - // copy pvp state flags from owner - trigger->SetByteValue(UNIT_FIELD_BYTES_2, 1, owner->GetByteValue(UNIT_FIELD_BYTES_2, 1)); - // 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) { WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM, 8+4); @@ -2167,7 +2105,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; @@ -2186,10 +2124,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) { WorldPacket data(SMSG_DESTRUCTIBLE_BUILDING_DAMAGE, 8 + 8 + 8 + 4 + 4); data << GetPackGUID(); @@ -2213,11 +2149,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); @@ -2236,8 +2171,8 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* break; case GO_DESTRUCTIBLE_DAMAGED: { - EventInform(m_goInfo->building.damagedEvent, eventInvoker); - AI()->Damaged(eventInvoker, m_goInfo->building.damagedEvent); + EventInform(m_goInfo->building.damagedEvent, attackerOrHealer); + AI()->Damaged(attackerOrHealer, m_goInfo->building.damagedEvent); RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); @@ -2261,12 +2196,12 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* } case GO_DESTRUCTIBLE_DESTROYED: { - EventInform(m_goInfo->building.destroyedEvent, eventInvoker); - AI()->Destroyed(eventInvoker, m_goInfo->building.destroyedEvent); + EventInform(m_goInfo->building.destroyedEvent, attackerOrHealer); + AI()->Destroyed(attackerOrHealer, m_goInfo->building.destroyedEvent); - if (eventInvoker) - if (Battleground* bg = eventInvoker->GetBattleground()) - bg->DestroyGate(eventInvoker, this); + if (attackerOrHealer && attackerOrHealer->GetTypeId() == TYPEID_PLAYER) + if (Battleground* bg = attackerOrHealer->ToPlayer()->GetBattleground()) + bg->DestroyGate(attackerOrHealer->ToPlayer(), this); RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); @@ -2287,7 +2222,7 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* } case GO_DESTRUCTIBLE_REBUILDING: { - EventInform(m_goInfo->building.rebuildingEvent, eventInvoker); + EventInform(m_goInfo->building.rebuildingEvent, attackerOrHealer); RemoveFlag(GAMEOBJECT_FLAGS, 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 18c66ec1f39..f9024db5505 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -127,8 +127,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> m_spawnedByDefault = false; // all object with owner is despawned after delay SetGuidValue(OBJECT_FIELD_CREATED_BY, owner); } - ObjectGuid GetOwnerGUID() const { return GetGuidValue(OBJECT_FIELD_CREATED_BY); } - Unit* GetOwner() const; + ObjectGuid GetOwnerGUID() const override { return GetGuidValue(OBJECT_FIELD_CREATED_BY); } void SetSpellId(uint32 id) { @@ -235,14 +234,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 (HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED)) @@ -265,8 +262,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void SetDisplayId(uint32 displayid); uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); } - uint32 GetFaction() const { return GetUInt32Value(GAMEOBJECT_FACTION); } - void SetFaction(uint32 faction) { SetUInt32Value(GAMEOBJECT_FACTION, faction); } + uint32 GetFaction() const override { return GetUInt32Value(GAMEOBJECT_FACTION); } + void SetFaction(uint32 faction) override { SetUInt32Value(GAMEOBJECT_FACTION, 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 75a54f2ce36..da0b7c3ba15 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -34,6 +34,9 @@ #include "ObjectMgr.h" #include "OutdoorPvPMgr.h" #include "Player.h" +#include "ReputationMgr.h" +#include "SpellAuraEffects.h" +#include "SpellMgr.h" #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" @@ -1020,8 +1023,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), m_phaseMask(PHASEMASK_NORMAL), m_notifyflags(0) { @@ -2140,6 +2143,973 @@ Player* WorldObject::SelectNearestPlayer(float distance) const return target; } +ObjectGuid WorldObject::GetCharmerOrOwnerOrOwnGUID() const +{ + if (ObjectGuid guid = GetCharmerOrOwnerGUID()) + 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(SpellInfo const* spellInfo, uint8 effIndex, int32 const* basePoints /*= nullptr*/) const +{ + return spellInfo->Effects[effIndex].CalcValue(this, basePoints); +} + +float WorldObject::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const +{ + if (!spellInfo->RangeEntry) + return 0.f; + + if (spellInfo->RangeEntry->maxRangeFriend == spellInfo->RangeEntry->maxRangeHostile) + 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->minRangeFriend == spellInfo->RangeEntry->minRangeHostile) + 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->Id, SPELLMOD_ALL_EFFECTS, value); + switch (effIndex) + { + case EFFECT_0: + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_EFFECT1, value); + break; + case EFFECT_1: + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_EFFECT2, value); + break; + case EFFECT_2: + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_EFFECT3, value); + break; + } + } + return value; +} + +int32 WorldObject::CalcSpellDuration(SpellInfo const* spellInfo) const +{ + uint8 comboPoints = 0; + if (Unit const* unit = ToUnit()) + comboPoints = unit->GetComboPoints(); + + 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))) + { + if (unitTarget->HasAura(53042) && unitTarget->HasSpell(spellInfo->Effects[0].TriggerSpell)) + duration *= 2; + } + } + } + + // Glyphs which increase duration of selfcast buffs + if (unitTarget == this) + { + switch (spellInfo->SpellFamilyName) + { + case SPELLFAMILY_DRUID: + if (spellInfo->SpellFamilyFlags[0] & 0x100) + { + // Glyph of Thorns + if (AuraEffect* aurEff = unitTarget->GetAuraEffect(57862, EFFECT_0)) + duration += aurEff->GetAmount() * MINUTE * IN_MILLISECONDS; + } + break; + case SPELLFAMILY_PALADIN: + if ((spellInfo->SpellFamilyFlags[0] & 0x00000002) && spellInfo->SpellIconID == 298) + { + // Glyph of Blessing of Might + if (AuraEffect* aurEff = unitTarget->GetAuraEffect(57958, EFFECT_0)) + duration += aurEff->GetAmount() * MINUTE * IN_MILLISECONDS; + } + else if ((spellInfo->SpellFamilyFlags[0] & 0x00010000) && spellInfo->SpellIconID == 306) + { + // Glyph of Blessing of Wisdom + if (AuraEffect* aurEff = unitTarget->GetAuraEffect(57979, EFFECT_0)) + duration += aurEff->GetAmount() * MINUTE * IN_MILLISECONDS; + } + break; + } + } + + 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->Id, SPELLMOD_CASTING_TIME, 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->GetFloatValue(UNIT_MOD_CAST_SPEED)); + 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 (spellInfo->SpellVisual[0] == 3881 && 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->Id, SPELLMOD_CASTING_TIME, 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->GetFloatValue(UNIT_MOD_CAST_SPEED)); + 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*/, int32 /*skillDiff*/, uint32 /*spellId*/) 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; + + // Base hit chance from attacker and victim levels + int32 modHitChance; + if (leveldif < 3) + modHitChance = 96 - leveldif; + else + modHitChance = 94 - (leveldif - 2) * lchance; + + // Spellmod from SPELLMOD_RESIST_MISS_CHANCE + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance); + + // Increase from attacker SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT auras + if (Unit const* unit = ToUnit()) + modHitChance += unit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, schoolMask); + + // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras + modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); + // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura + if (spellInfo->IsAffectingArea()) + modHitChance -= victim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE); + + // Decrease hit chance from victim rating bonus + if (victim->GetTypeId() == TYPEID_PLAYER) + modHitChance -= int32(victim->ToPlayer()->GetRatingBonusValue(CR_HIT_TAKEN_SPELL)); + + 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; + + // Chance resist debuff + if (!spellInfo->IsPositive() && !spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES)) + { + bool hasAura = false; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (spellInfo->Effects[i].IsAura()) + { + hasAura = true; + break; + } + } + + if (hasAura) + resist_chance += victim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, static_cast<int32>(spellInfo->Dispel)) * 100; + + // resistance chance for binary spells, equals to average damage reduction of non-binary spell + if (spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL) && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_MAGIC)) + resist_chance += int32(Unit::CalculateAverageResistReduction(this, spellInfo->GetSchoolMask(), victim, spellInfo) * 10000.f); // 100 for spell calculations, and 100 for return value percentage + } + + // 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->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) + { + if (targetUnit && targetUnit->HasFlag(UNIT_FIELD_FLAGS, 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->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_IGNORE_REPUTATION)) + { + if (FactionEntry const* targetFactionEntry = sFactionStore.LookupEntry(targetFactionTemplateEntry->faction)) + { + if (targetFactionEntry->CanHaveReputation()) + { + // check contested flags + if ((targetFactionTemplateEntry->factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD) && + selfPlayerOwner->HasFlag(PLAYER_FLAGS, 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->factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD) && + targetPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) + return REP_HOSTILE; + if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry)) + return *repRank; + if (target->ToUnit() && !target->HasFlag(UNIT_FIELD_FLAGS_2, 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->factionFlags & 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->reputationListID >= 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->reputationListID >= 0) + return false; + + return my_faction->IsNeutralToAll(); +} + +void WorldObject::CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/) +{ + SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId); + 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; + + // CvC case - can attack each other only when one of them is hostile + if (ToUnit() && !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && target->ToUnit() && !target->HasFlag(UNIT_FIELD_FLAGS, 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 = ToUnit() && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; + Player const* playerAffectingTarget = target->ToUnit() && target->HasFlag(UNIT_FIELD_FLAGS, 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 ? target->ToUnit() : ToUnit()) + { + if (creature->IsContestedGuard() && player->HasFlag(PLAYER_FLAGS, 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 & FACTION_FLAG_AT_WAR)) + 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->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + return false; + + if (Player const* playerAttacker = ToPlayer()) + { + if (playerAttacker->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_UBER)) + return false; + } + + // check flags + if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)) + return false; + + if (unit && !unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToNPC()) + return false; + + if (unitTarget && !unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToNPC()) + return false; + + if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToPC()) + return false; + + if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToPC()) + return false; + + // check duel - before sanctuary checks + Player const* playerAffectingAttacker = unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; + Player const* playerAffectingTarget = unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, 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->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->HasFlag(UNIT_FIELD_FLAGS, 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->HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_UNK1) || + unitTarget->HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, 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->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + return false; + + if (!bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) + { + if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) + { + if (unitTarget && unitTarget->IsImmuneToPC()) + return false; + } + else + { + if (unitTarget && unitTarget->IsImmuneToNPC()) + return false; + } + } + + // PvP case + if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) + { + Player const* targetPlayerOwner = unitTarget->GetAffectingPlayer(); + if (HasFlag(UNIT_FIELD_FLAGS, 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->HasFlag(UNIT_FIELD_FLAGS, 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->Speed > 0.0f) + { + // Set up missile speed based delay + uint32 delay = uint32(std::floor(std::max<float>(victim->GetDistance(this), 5.0f) / spellInfo->Speed * 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; +} + 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 e4a210a2267..769e293f4ce 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -21,6 +21,7 @@ #include "Common.h" #include "Duration.h" +#include "EventProcessor.h" #include "GridReference.h" #include "GridRefManager.h" #include "ModelIgnoreFlags.h" @@ -29,6 +30,7 @@ #include "ObjectGuid.h" #include "Position.h" #include "SharedDefines.h" +#include "SpellDefines.h" #include "UpdateFields.h" #include "UpdateMask.h" #include <list> @@ -43,6 +45,9 @@ class GameObject; class InstanceScript; class Map; class Player; +class Spell; +class SpellCastTargets; +class SpellInfo; class TempSummon; class Transport; class Unit; @@ -50,6 +55,7 @@ class UpdateData; class WorldObject; class WorldPacket; class ZoneScript; +struct FactionTemplateEntry; struct PositionFullTerrainStatus; struct QuaternionData; @@ -112,7 +118,6 @@ class TC_GAME_API Object void ApplyModUInt32Value(uint16 index, int32 val, bool apply); void ApplyModInt32Value(uint16 index, int32 val, bool apply); - void ApplyModUInt64Value(uint16 index, int32 val, bool apply); void ApplyModPositiveFloatValue(uint16 index, float val, bool apply); void ApplyModSignedFloatValue(uint16 index, float val, bool apply); @@ -262,7 +267,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 _Create(ObjectGuid::LowType guidlow, HighGuid guidhigh, uint32 phaseMask); void AddToWorld() override; @@ -393,6 +398,59 @@ 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(SpellInfo const* spellInfo, uint8 effIndex, int32 const* basePoints = nullptr) 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, int32 skillDiff, uint32 spellId) 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); + template <typename Container> void GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange = 250.0f) const; @@ -453,11 +511,14 @@ 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; + protected: std::string m_name; bool m_isActive; bool m_isFarVisible; - const bool m_isWorldObject; + bool const m_isWorldObject; ZoneScript* m_zoneScript; // transports @@ -483,7 +544,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 uint32 m_phaseMask; // in area phase state diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 2dcf47bb590..c0a9397bda6 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1973,7 +1973,7 @@ void Player::SetObjectScale(float scale) SetFloatValue(UNIT_FIELD_COMBATREACH, scale * DEFAULT_PLAYER_COMBAT_REACH); } -bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const +bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const { // players are immune to taunt (the aura and the spell effect) if (spellInfo->Effects[index].IsAura(SPELL_AURA_MOD_TAUNT)) @@ -21743,7 +21743,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()); @@ -24878,9 +24878,9 @@ void Player::ResetAchievementCriteria(AchievementCriteriaCondition condition, ui m_achievementMgr->ResetAchievementCriteria(condition, value, evenIfCriteriaComplete); } -void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/, Unit* unit /*= nullptr*/) +void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 /*= 0*/, uint32 miscValue2 /*= 0*/, WorldObject* ref /*= nullptr*/) { - m_achievementMgr->UpdateAchievementCriteria(type, miscValue1, miscValue2, unit); + m_achievementMgr->UpdateAchievementCriteria(type, miscValue1, miscValue2, ref); } 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 e828bdb5488..9ca4caa2ee7 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -908,7 +908,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> static bool BuildEnumData(PreparedQueryResult result, WorldPacket* data); - 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 apply); @@ -1461,7 +1461,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() { @@ -2103,7 +2103,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool HasAchieved(uint32 achievementId) const; void ResetAchievements(); void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint32 value, bool evenIfCriteriaComplete = false); - void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 = 0, uint32 miscValue2 = 0, Unit* unit = nullptr); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 = 0, uint32 miscValue2 = 0, WorldObject* ref = nullptr); void StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost = 0); void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, 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 20eb437fff5..121460cc39b 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -149,7 +149,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 56c378c0fa4..baa376d43d4 100644 --- a/src/server/game/Entities/Totem/Totem.h +++ b/src/server/game/Entities/Totem/Totem.h @@ -55,7 +55,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 96fc1caf649..c8610ab2eef 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -873,35 +873,25 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons } } - if (damagetype != NODAMAGE && damage) + if (damagetype != NODAMAGE && damagetype != DOT && damage) { - if (victim != attacker && victim->GetTypeId() == TYPEID_PLAYER && // does not support creature push_back - (!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) - { - uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; - if (interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) - victim->InterruptNonMeleeSpells(false); - else if (interruptFlags & SPELL_INTERRUPT_FLAG_PUSH_BACK) - spell->Delayed(); - } + uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; + if (interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) + victim->InterruptNonMeleeSpells(false); + else + spell->Delayed(); } } if (Spell* spell = victim->m_currentSpells[CURRENT_CHANNELED_SPELL]) - { if (spell->getState() == SPELL_STATE_CASTING) - { - uint32 channelInterruptFlags = spell->m_spellInfo->ChannelInterruptFlags; - if (((channelInterruptFlags & CHANNEL_FLAG_DELAY) != 0) && (damagetype != DOT)) - spell->DelayedChannel(); - } - } + spell->DelayedChannel(); } } @@ -935,48 +925,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); - 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) @@ -1723,21 +1671,19 @@ void Unit::HandleEmoteCommand(uint32 anim_id) return uint32(damageResisted); } -/*static*/ float Unit::CalculateAverageResistReduction(Unit const* attacker, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo) +/*static*/ float Unit::CalculateAverageResistReduction(WorldObject const* caster, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo) { 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 @@ -1751,8 +1697,9 @@ void Unit::HandleEmoteCommand(uint32 anim_id) 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 BOSS_LEVEL = 83; static float const BOSS_RESISTANCE_CONSTANT = 510.0f; @@ -2352,12 +2299,12 @@ uint32 Unit::CalculateDamage(WeaponAttackType attType, bool normalized, bool add return urand(uint32(minDamage), uint32(maxDamage)); } -float Unit::CalculateSpellpowerCoefficientLevelPenalty(SpellInfo const* spellProto) const +float Unit::CalculateSpellpowerCoefficientLevelPenalty(SpellInfo const* spellInfo) const { - if (!spellProto->MaxLevel || getLevel() < spellProto->MaxLevel) + if (!spellInfo->MaxLevel || getLevel() < spellInfo->MaxLevel) return 1.0f; - return std::max(0.0f, std::min(1.0f, (22.0f + spellProto->MaxLevel - getLevel()) / 20.0f)); + return std::max(0.0f, std::min(1.0f, (22.0f + spellInfo->MaxLevel - getLevel()) / 20.0f)); } void Unit::SendMeleeAttackStart(Unit* victim) @@ -2599,152 +2546,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; - - // Base hit chance from attacker and victim levels - int32 modHitChance; - if (leveldif < 3) - modHitChance = 96 - leveldif; - else - modHitChance = 94 - (leveldif - 2) * lchance; - - // Spellmod from SPELLMOD_RESIST_MISS_CHANCE - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance); - - // Increase from attacker SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT auras - modHitChance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, schoolMask); - - // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras - modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); - // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura - if (spellInfo->IsAffectingArea()) - modHitChance -= victim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE); - - // Decrease hit chance from victim rating bonus - if (victim->GetTypeId() == TYPEID_PLAYER) - modHitChance -= int32(victim->ToPlayer()->GetRatingBonusValue(CR_HIT_TAKEN_SPELL)); - - 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; - - // Chance resist debuff - if (!spellInfo->IsPositive() && !spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES)) - { - bool hasAura = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (spellInfo->Effects[i].IsAura()) - { - hasAura = true; - break; - } - } - - if (hasAura) - resist_chance += victim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, static_cast<int32>(spellInfo->Dispel)) * 100; - - // resistance chance for binary spells, equals to average damage reduction of non-binary spell - if (spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL) && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_MAGIC)) - resist_chance += int32(Unit::CalculateAverageResistReduction(this, spellInfo->GetSchoolMask(), victim, spellInfo) * 10000.f); // 100 for spell calculations, and 100 for return value percentage - } - - // 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; -} - uint32 Unit::GetShieldBlockValue(uint32 soft_cap, uint32 hard_cap) const { uint32 value = GetShieldBlockValue(); @@ -3417,6 +3218,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()) { @@ -4009,7 +3818,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;) @@ -4037,7 +3846,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;) @@ -4071,38 +3880,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->GetSpellInfo(), effMask, stealer); - createInfo - .SetCasterGUID(aura->GetCasterGUID()) - .SetBaseAmount(baseDamage); + AuraCreateInfo createInfo(aura->GetSpellInfo(), 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, aura->GetCritChance(), aura->CanApplyResilience(), &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, aura->GetCritChance(), aura->CanApplyResilience(), &damage[0]); - newAura->ApplyForTargets(); } } @@ -4502,7 +4314,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 unit %u, new duration: %u ms", aura->GetId(), GetGUID().GetCounter(), aura->GetDuration()); } } } @@ -4623,7 +4434,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 { // we should not be able to dispel diseases if the target is affected by unholy blight if (dispelMask & (1 << DISPEL_DISEASE) && HasAura(50536)) @@ -5635,190 +5446,6 @@ void Unit::UpdateDisplayPower() SetPowerType(displayPower); } -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 (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) - { - if (target->HasFlag(UNIT_FIELD_FLAGS, 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 (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) - return *repRank; - if (!selfPlayerOwner->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_IGNORE_REPUTATION)) - { - if (FactionEntry const* targetFactionEntry = sFactionStore.LookupEntry(targetFactionTemplateEntry->faction)) - { - if (targetFactionEntry->CanHaveReputation()) - { - // check contested flags - if (targetFactionTemplateEntry->factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD - && selfPlayerOwner->HasFlag(PLAYER_FLAGS, 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->factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD - && targetPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) - return REP_HOSTILE; - if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry)) - return *repRank; - if (!target->HasFlag(UNIT_FIELD_FLAGS_2, 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->factionFlags & 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->reputationListID >= 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->reputationListID >= 0) - return false; - - return my_faction->IsNeutralToAll(); -} - void Unit::_addAttacker(Unit* pAttacker) { m_attackers.insert(pAttacker); @@ -6191,14 +5818,6 @@ void Unit::SetOwnerGUID(ObjectGuid owner) RemoveFieldNotifyFlag(UF_FLAG_OWNER); } -Unit* Unit::GetOwner() const -{ - if (ObjectGuid ownerGUID = GetOwnerGUID()) - return ObjectAccessor::GetUnit(*this, ownerGUID); - - return nullptr; -} - Unit* Unit::GetCharmer() const { if (ObjectGuid charmerGUID = GetCharmerGUID()) @@ -6207,27 +5826,7 @@ 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 +Minion* Unit::GetFirstMinion() const { if (ObjectGuid pet_guid = GetMinionGUID()) { @@ -6276,14 +5875,6 @@ Unit* Unit::GetCharmerOrOwner() const return GetCharmerGUID() ? 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); @@ -6596,36 +6187,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->Speed > 0.0f) - { - // Set up missile speed based delay - uint32 delay = uint32(std::floor(std::max<float>(victim->GetDistance(this), 5.0f) / spellInfo->Speed * 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) { AuraEffectList const& hitTriggerAuras = victim->GetAuraEffectsByType(SPELL_AURA_ADD_CASTER_HIT_TRIGGER); @@ -7064,7 +6625,7 @@ float Unit::SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, Damage // effect 1 m_amount int32 maxPercent = (*i)->GetAmount(); // effect 0 m_amount - int32 stepPercent = CalculateSpellDamage(this, (*i)->GetSpellInfo(), 0); + int32 stepPercent = CalculateSpellDamage((*i)->GetSpellInfo(), EFFECT_0); // count affliction effects and calc additional damage in percentage int32 modPercent = 0; AuraApplicationMap const& victimAuras = victim->GetAppliedAuras(); @@ -8067,7 +7628,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; @@ -8165,7 +7726,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 || !spellInfo->Effects[index].IsEffect()) return false; @@ -8764,251 +8325,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 (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && !target->HasFlag(UNIT_FIELD_FLAGS, 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 = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; - Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, 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->HasFlag(PLAYER_FLAGS, 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 & FACTION_FLAG_AT_WAR)) - 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->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - return false; - - if (Player const* playerAttacker = ToPlayer()) - { - if (playerAttacker->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_UBER)) - return false; - } - - // check flags - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)) - return false; - - if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && target->IsImmuneToNPC()) - return false; - - if (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && IsImmuneToNPC()) - return false; - - if (HasFlag(UNIT_FIELD_FLAGS, 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->HasFlag(UNIT_FIELD_FLAGS, 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 = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; - Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, 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->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && HasFlag(UNIT_FIELD_FLAGS, 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 HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_UNK1) - || target->HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, 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->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - return false; - - if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) - { - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) - { - if (target->IsImmuneToPC()) - return false; - } - else - { - if (target->IsImmuneToNPC()) - return false; - } - } - - // PvP case - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) - { - Player const* targetPlayerOwner = target->GetAffectingPlayer(); - if (HasFlag(UNIT_FIELD_FLAGS, 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 (HasFlag(UNIT_FIELD_FLAGS, 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; -} - int32 Unit::ModifyHealth(int32 dVal) { int32 gain = 0; @@ -9466,193 +8782,6 @@ void Unit::UpdatePetCombatState() //====================================================================== -float Unit::ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const -{ - if (Player* modOwner = GetSpellModOwner()) - { - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_ALL_EFFECTS, value); - switch (effect_index) - { - case EFFECT_0: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT1, value); - break; - case EFFECT_1: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT2, value); - break; - case EFFECT_2: - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT3, 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) const -{ - return spellProto->Effects[effect_index].CalcValue(this, basePoints, target); -} - -int32 Unit::CalcSpellDuration(SpellInfo const* spellProto) -{ - uint8 comboPoints = GetComboPoints(); - - 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))) - { - if (target->HasAura(53042) && target->HasSpell(spellProto->Effects[0].TriggerSpell)) - duration *= 2; - } - } - } - - // Glyphs which increase duration of selfcast buffs - if (target == this) - { - switch (spellProto->SpellFamilyName) - { - case SPELLFAMILY_DRUID: - if (spellProto->SpellFamilyFlags[0] & 0x100) - { - // Glyph of Thorns - if (AuraEffect* aurEff = GetAuraEffect(57862, 0)) - duration += aurEff->GetAmount() * MINUTE * IN_MILLISECONDS; - } - break; - case SPELLFAMILY_PALADIN: - if ((spellProto->SpellFamilyFlags[0] & 0x00000002) && spellProto->SpellIconID == 298) - { - // Glyph of Blessing of Might - if (AuraEffect* aurEff = GetAuraEffect(57958, 0)) - duration += aurEff->GetAmount() * MINUTE * IN_MILLISECONDS; - } - else if ((spellProto->SpellFamilyFlags[0] & 0x00010000) && spellProto->SpellIconID == 306) - { - // Glyph of Blessing of Wisdom - if (AuraEffect* aurEff = GetAuraEffect(57979, 0)) - duration += aurEff->GetAmount() * MINUTE * IN_MILLISECONDS; - } - break; - } - } - 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->Id, SPELLMOD_CASTING_TIME, 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) * GetFloatValue(UNIT_MOD_CAST_SPEED)); - else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) - castTime = int32(float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]); - else if (spellInfo->SpellVisual[0] == 3881 && 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->Id, SPELLMOD_CASTING_TIME, 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) * GetFloatValue(UNIT_MOD_CAST_SPEED)); - 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]; @@ -9677,7 +8806,7 @@ void Unit::IncrDiminishing(SpellInfo const* auraSpellInfo, bool triggered) diminish.hitCount = currentLevel + 1; } -bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, bool triggered, int32& duration, Unit* caster, DiminishingLevels previousLevel) const +bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, bool triggered, int32& duration, WorldObject* caster, DiminishingLevels previousLevel) const { DiminishingGroup const group = auraSpellInfo->GetDiminishingReturnsGroupForSpell(triggered); if (duration == -1 || group == DIMINISHING_NONE) @@ -9693,7 +8822,7 @@ bool Unit::ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, bool trigg 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; @@ -9759,28 +8888,6 @@ void Unit::ClearDiminishings() dim.Clear(); } -float Unit::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const -{ - if (!spellInfo->RangeEntry) - return 0; - if (spellInfo->RangeEntry->maxRangeFriend == spellInfo->RangeEntry->maxRangeHostile) - 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->minRangeFriend == spellInfo->RangeEntry->minRangeHostile) - return spellInfo->GetMinRange(); - if (!target) - return spellInfo->GetMinRange(true); - return spellInfo->GetMinRange(!IsHostileTo(target)); -} - uint32 Unit::GetCreatureType() const { if (GetTypeId() == TYPEID_PLAYER) @@ -11048,26 +10155,6 @@ ObjectGuid Unit::GetCharmerOrOwnerGUID() const return GetCharmerGUID() ? GetCharmerGUID() : GetOwnerGUID(); } -ObjectGuid Unit::GetCharmerOrOwnerOrOwnGUID() const -{ - if (ObjectGuid guid = GetCharmerOrOwnerGUID()) - 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(uint8 msg) { @@ -12723,9 +11810,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); } @@ -12734,6 +11818,9 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint8 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; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 9e7755521d8..c5a1f02c1a7 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -20,13 +20,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" @@ -362,15 +360,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; }; @@ -776,14 +774,10 @@ class TC_GAME_API Unit : public WorldObject virtual bool IsAffectedByDiminishingReturns() const { return (GetCharmerOrOwnerPlayerOrPlayerItself() != nullptr); } DiminishingLevels GetDiminishing(DiminishingGroup group) const; void IncrDiminishing(SpellInfo const* auraSpellInfo, bool triggered); - bool ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, bool triggered, int32& duration, Unit* caster, DiminishingLevels previousLevel) const; + bool ApplyDiminishingToDuration(SpellInfo const* auraSpellInfo, bool triggered, 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; } @@ -858,7 +852,7 @@ class TC_GAME_API Unit : public WorldObject uint32 GetResistance(SpellSchools school) const { return GetUInt32Value(UNIT_FIELD_RESISTANCES+school); } uint32 GetResistance(SpellSchoolMask mask) const; void SetResistance(SpellSchools school, int32 val) { SetStatInt32Value(UNIT_FIELD_RESISTANCES+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); uint32 GetHealth() const { return GetUInt32Value(UNIT_FIELD_HEALTH); } uint32 GetMaxHealth() const { return GetUInt32Value(UNIT_FIELD_MAXHEALTH); } @@ -898,17 +892,9 @@ class TC_GAME_API Unit : public WorldObject virtual void SetSheath(SheathState sheathed) { SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHEATH_STATE, sheathed); } // faction template id - uint32 GetFaction() const { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); } - void SetFaction(uint32 faction) { SetUInt32Value(UNIT_FIELD_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 GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); } + void SetFaction(uint32 faction) override { SetUInt32Value(UNIT_FIELD_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); @@ -982,10 +968,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, int32 skillDiff, uint32 spellId) 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, int32 skillDiff, uint32 spellId) 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; @@ -1081,12 +1065,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; @@ -1097,10 +1075,6 @@ class TC_GAME_API Unit : public WorldObject void EnergizeBySpell(Unit* victim, uint32 spellId, int32 damage, 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, uint8 effMask, Unit* target); void SetAuraStack(uint32 spellId, Unit* target, uint32 stack); @@ -1160,7 +1134,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 GetGuidValue(UNIT_FIELD_SUMMONEDBY); } + ObjectGuid GetOwnerGUID() const override { return GetGuidValue(UNIT_FIELD_SUMMONEDBY); } void SetOwnerGUID(ObjectGuid owner); ObjectGuid GetCreatorGUID() const { return GetGuidValue(UNIT_FIELD_CREATEDBY); } void SetCreatorGUID(ObjectGuid creator) { SetGuidValue(UNIT_FIELD_CREATEDBY, creator); } @@ -1175,21 +1149,14 @@ class TC_GAME_API Unit : public WorldObject ObjectGuid GetCritterGUID() const { return GetGuidValue(UNIT_FIELD_CRITTER); } 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<Creature*>& Minions, uint32 entry); @@ -1275,8 +1242,8 @@ class TC_GAME_API Unit : public WorldObject void RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, uint8 reqEffMask = 0, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); void RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); - 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(uint32 newPhase = 0x0); @@ -1317,7 +1284,7 @@ class TC_GAME_API Unit : public WorldObject AuraApplication * GetAuraApplicationOfRankedSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid itemCasterGUID = ObjectGuid::Empty, uint8 reqEffMask = 0, AuraApplication * except = nullptr) const; Aura* GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid itemCasterGUID = ObjectGuid::Empty, uint8 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; @@ -1415,10 +1382,7 @@ class TC_GAME_API Unit : public WorldObject float m_modSpellHitChance; int32 m_baseSpellCritChance; - float m_modAttackSpeedPct[3]; - - // Event handler - EventProcessor m_Events; + float m_modAttackSpeedPct[MAX_ATTACK]; // stat system void HandleStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float amount, bool apply); @@ -1513,7 +1477,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; @@ -1544,14 +1507,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); @@ -1565,13 +1528,7 @@ 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) 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); - float CalculateSpellpowerCoefficientLevelPenalty(SpellInfo const* spellProto) const; + float CalculateSpellpowerCoefficientLevelPenalty(SpellInfo const* spellInfo) const; void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); } void removeFollower(FollowerReference* /*pRef*/) { /* nothing to do yet */ } diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index d718749f248..7fb56b3e686 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -423,17 +423,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 b43a8bc5cdb..d4687d1b1ee 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -624,29 +624,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) - : AnyDeadUnitObjectInRangeCheck(searchObj, range), i_spellInfo(spellInfo), i_check(searchObj, searchObj, spellInfo, check, nullptr) + AnyDeadUnitSpellTargetInRangeCheck(WorldObject* searchObj, float range, SpellInfo const* spellInfo, SpellTargetCheckTypes check) + : AnyDeadUnitObjectInRangeCheck(searchObj, range), WorldObjectSpellTargetCheck(searchObj, searchObj, spellInfo, check, nullptr) { } 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 @@ -666,27 +663,26 @@ 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()->type != GAMEOBJECT_TYPE_SPELL_FOCUS) return false; - if (go->GetGOInfo()->spellFocus.focusId != i_focusId) + if (go->GetGOInfo()->spellFocus.focusId != _focusId) return false; if (!go->isSpawned()) return false; - float dist = go->GetGOInfo()->spellFocus.dist / 2.f; - - return go->IsWithinDistInMap(i_unit, dist); + float const dist = go->GetGOInfo()->spellFocus.dist / 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/Maps/Map.h b/src/server/game/Maps/Map.h index adccf8c00c5..8021cf440c2 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -474,7 +474,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 60eb4cf5e31..a949d0a099a 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/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index bf5956ec0bc..0db545aa67b 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -422,28 +422,32 @@ void AuraEffect::GetApplicationList(Container& applicationContainer) const int32 AuraEffect::CalculateAmount(Unit* caster) { // default amount calculation - int32 amount = m_spellInfo->Effects[m_effIndex].CalcValue(caster, &m_baseAmount, nullptr); + int32 amount = m_spellInfo->Effects[m_effIndex].CalcValue(caster, &m_baseAmount); // check item enchant aura cast if (!amount && caster) + { if (ObjectGuid itemGUID = GetBase()->GetCastItemGUID()) + { if (Player* playerCaster = caster->ToPlayer()) + { if (Item* castItem = playerCaster->GetItemByGuid(itemGUID)) + { if (castItem->GetItemSuffixFactor()) { - ItemRandomSuffixEntry const* item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(castItem->GetItemRandomPropertyId())); - if (item_rand_suffix) + if (ItemRandomSuffixEntry const* item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(castItem->GetItemRandomPropertyId()))) { - for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; k++) + for (uint8 k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k) { - SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->enchant_id[k]); - if (pEnchant) + if (SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand_suffix->enchant_id[k])) { - for (int t = 0; t < MAX_ITEM_ENCHANTMENT_EFFECTS; t++) - if (pEnchant->spellid[t] == m_spellInfo->Id) + for (uint8 t = 0; t < MAX_ITEM_ENCHANTMENT_EFFECTS; ++t) { - amount = uint32((item_rand_suffix->prefix[k]*castItem->GetItemSuffixFactor()) / 10000); - break; + if (pEnchant->spellid[t] == m_spellInfo->Id) + { + amount = uint32((item_rand_suffix->prefix[k] * castItem->GetItemSuffixFactor()) / 10000); + break; + } } } @@ -452,6 +456,10 @@ int32 AuraEffect::CalculateAmount(Unit* caster) } } } + } + } + } + } // custom amount calculations go here switch (GetAuraType()) @@ -1815,7 +1823,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); if (spellInfo && spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellIconID == 139) - Rage_val += target->CalculateSpellDamage(target, spellInfo, 0) * 10; + Rage_val += target->CalculateSpellDamage(spellInfo, EFFECT_0) * 10; } } if (target->GetPower(POWER_RAGE) > Rage_val) @@ -5629,7 +5637,7 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con void AuraEffect::HandleModAttackPowerOfArmorAuraTick(Unit* target, Unit* caster) const { - int32 const armorMod = m_spellInfo->Effects[m_effIndex].CalcValue(caster, &m_baseAmount, target); + int32 const armorMod = m_spellInfo->Effects[m_effIndex].CalcValue(caster, &m_baseAmount); const_cast<AuraEffect*>(this)->SetAmount(target->GetArmor() / armorMod); target->UpdateAttackPowerAndDamage(false); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index fbd5b3efcd4..1e1a4ec2acc 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -334,8 +334,6 @@ Aura* Aura::TryRefreshStackOrCreate(AuraCreateInfo& createInfo) Aura* Aura::TryCreate(AuraCreateInfo& createInfo) { - ASSERT(createInfo.Caster || createInfo.CasterGUID); - uint8 effMask = createInfo._auraEffectMask; if (createInfo._targetEffectMask) effMask = createInfo._targetEffectMask; @@ -349,17 +347,25 @@ Aura* Aura::TryCreate(AuraCreateInfo& createInfo) Aura* Aura::Create(AuraCreateInfo& createInfo) { - ASSERT(createInfo.Caster || createInfo.CasterGUID); - // try to get caster of aura if (createInfo.CasterGUID) { - 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 @@ -412,7 +418,7 @@ Aura* Aura::Create(AuraCreateInfo& createInfo) } Aura::Aura(AuraCreateInfo const& createInfo) : -m_spellInfo(createInfo._spellInfo), m_casterGuid(createInfo.CasterGUID.IsEmpty() ? createInfo.Caster->GetGUID() : createInfo.CasterGUID), +m_spellInfo(createInfo._spellInfo), m_casterGuid(createInfo.CasterGUID), m_castItemGuid(createInfo.CastItem ? createInfo.CastItem->GetGUID() : ObjectGuid::Empty), m_applyTime(GameTime::GetGameTime()), m_owner(createInfo._owner), m_timeCla(0), m_updateTargetMapInterval(0), _casterInfo(), m_procCharges(0), m_stackAmount(1), diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 145c385dcd0..8604a7f67ca 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -60,7 +60,7 @@ #include "WorldPacket.h" #include "WorldSession.h" -extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; +extern SpellEffectHandlerFn SpellEffectHandlers[TOTAL_SPELL_EFFECTS]; SpellDestination::SpellDestination() { @@ -454,9 +454,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 ? ((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) @@ -493,28 +493,6 @@ 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", "elevation: %f", m_elevation); -} - SpellValue::SpellValue(SpellInfo const* proto) { for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -529,7 +507,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; @@ -539,7 +517,7 @@ class TC_GAME_API SpellEvent : public BasicEvent 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(sSpellMgr->GetSpellForDifficultyFromSpell(info, caster)), m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster) , m_spellValue(new SpellValue(m_spellInfo)), _spellEvent(nullptr) @@ -563,11 +541,14 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO 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()->Damage[0].DamageType); + 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()->Damage[0].DamageType); + } if (originalCasterGUID) m_originalCasterGUID = originalCasterGUID; @@ -575,7 +556,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO 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); @@ -609,6 +590,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_glyphIndex = 0; m_preCastSpell = 0; m_triggeredByAuraSpell = nullptr; + unitCaster = nullptr; _spellAura = nullptr; _dynObjAura = nullptr; @@ -626,7 +608,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO // 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->isType(TYPEMASK_UNIT) && 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(); @@ -697,11 +679,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->ToCreature()->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); } @@ -739,10 +721,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: @@ -750,10 +732,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)) @@ -1200,7 +1182,8 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge // Other special target selection goes here if (uint32 maxTargets = m_spellValue->MaxAffectedTargets) { - maxTargets += m_caster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); + if (Unit* unitCaster = m_caster->ToUnit()) + maxTargets += unitCaster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); Trinity::Containers::RandomResize(targets, maxTargets); } @@ -1217,7 +1200,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: @@ -1283,7 +1266,8 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge // Other special target selection goes here if (uint32 maxTargets = m_spellValue->MaxAffectedTargets) { - maxTargets += m_caster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); + if (Unit* unitCaster = m_caster->ToUnit()) + maxTargets += unitCaster->GetTotalAuraModifierByAffectMask(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS, m_spellInfo); Trinity::Containers::RandomResize(targets, maxTargets); } @@ -1361,15 +1345,19 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici } case TARGET_DEST_CASTER_FRONT_LEAP: { - float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + break; + + float dist = m_spellInfo->Effects[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); @@ -1511,14 +1499,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: @@ -1528,8 +1519,9 @@ 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; default: break; @@ -1537,8 +1529,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) @@ -1627,7 +1624,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge float b = tangent(m_targets.GetElevation()); 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 @@ -1635,15 +1632,16 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge if (SpellInfo const* triggerSpellInfo = sSpellMgr->GetSpellInfo(m_spellInfo->Effects[effIndex].TriggerSpell)) 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()) @@ -1653,14 +1651,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; @@ -1674,11 +1672,11 @@ 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); } @@ -1691,9 +1689,9 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) { case SPELL_EFFECT_SUMMON_RAF_FRIEND: case SPELL_EFFECT_SUMMON_PLAYER: - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->GetTarget()) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->GetTarget()) { - 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 @@ -1821,7 +1819,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; @@ -1857,7 +1855,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) @@ -1968,7 +1966,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; } @@ -2082,7 +2080,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 @@ -2127,10 +2125,8 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= } // 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. @@ -2154,8 +2150,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)); @@ -2265,7 +2262,7 @@ void Spell::AddDestTarget(SpellDestination const& dest, uint32 effIndex) 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; @@ -2280,7 +2277,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) @@ -2293,7 +2290,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; } @@ -2308,7 +2305,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; @@ -2337,7 +2334,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; @@ -2352,7 +2349,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; @@ -2497,8 +2494,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(AURA_INTERRUPT_FLAG_TALK); - spell->unitTarget->ToCreature()->EngageWithTarget(spell->m_caster); + Unit* unitCaster = ASSERT_NOTNULL(spell->m_caster->ToUnit()); + unitCaster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK); + spell->unitTarget->ToCreature()->EngageWithTarget(unitCaster); } } @@ -2526,7 +2524,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); @@ -2540,11 +2539,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); @@ -2558,7 +2566,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; @@ -2566,8 +2574,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(); @@ -2635,17 +2654,23 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& playerOwner->UpdatePvP(true); } } - if (unit->IsInCombat() && m_spellInfo->HasInitialAggro()) + + if (m_originalCaster && unit->IsInCombat() && m_spellInfo->HasInitialAggro()) { - if (m_caster->HasFlag(UNIT_FIELD_FLAGS, 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->HasFlag(UNIT_FIELD_FLAGS, 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)) { // Select rank for aura with level requirements only in specific cases // Unit has to be target only of aura effect, both caster and target have to be players, target has to be other than unit target @@ -2683,7 +2708,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& // 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 (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { @@ -2701,7 +2726,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& hitInfo.AuraDuration = hitInfo.AuraSpellInfo->GetMaxDuration(); // unit is immune to aura if it was diminished to 0 duration - if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(hitInfo.AuraSpellInfo, triggered, hitInfo.AuraDuration, m_originalCaster, diminishLevel)) + if (!hitInfo.Positive && !unit->ApplyDiminishingToDuration(hitInfo.AuraSpellInfo, triggered, hitInfo.AuraDuration, origCaster, diminishLevel)) if (std::all_of(std::begin(hitInfo.AuraSpellInfo->Effects), std::end(hitInfo.AuraSpellInfo->Effects), [](SpellEffectInfo const& effInfo) { return !effInfo.IsEffect() || effInfo.Effect == SPELL_EFFECT_APPLY_AURA; })) return SPELL_MISS_IMMUNE; } @@ -2711,10 +2736,13 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) { - uint8 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit); - if (aura_effmask) + if (uint8 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, 1 << effIndex, unit)) { + WorldObject* caster = m_caster; if (m_originalCaster) + caster = m_originalCaster; + + if (caster) { bool refresh = false; @@ -2728,7 +2756,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) AuraCreateInfo createInfo(hitInfo.AuraSpellInfo, allAuraEffectMask, unit); createInfo - .SetCaster(m_originalCaster) + .SetCasterGUID(caster->GetGUID()) .SetBaseAmount(bp) .SetCastItem(m_CastItem) .SetPeriodicReset(resetPeriodicTimer) @@ -2754,13 +2782,13 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) _spellAura->SetDiminishGroup(hitInfo.DRGroup); - hitInfo.AuraDuration = m_originalCaster->ModSpellDuration(hitInfo.AuraSpellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); + hitInfo.AuraDuration = caster->ModSpellDuration(hitInfo.AuraSpellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, _spellAura->GetEffectMask()); // Haste modifies duration of channeled spells if (m_spellInfo->IsChanneled()) - m_originalCaster->ModSpellDurationTime(hitInfo.AuraSpellInfo, hitInfo.AuraDuration, this); + caster->ModSpellDurationTime(hitInfo.AuraSpellInfo, hitInfo.AuraDuration, this); // and duration of auras affected by SPELL_AURA_PERIODIC_HASTE - else if (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, hitInfo.AuraSpellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION)) + else if (m_originalCaster && (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, hitInfo.AuraSpellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))) hitInfo.AuraDuration = int32(hitInfo.AuraDuration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED)); if (hitInfo.AuraDuration != _spellAura->GetMaxDuration()) @@ -2795,9 +2823,10 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask) // Cast the serverside immunity shield marker m_caster->CastSpell(unit, 61988, true); - if (sSpellMgr->GetSpellInfo(m_preCastSpell)) - // Blizz seems to just apply aura without bothering to cast - m_caster->AddAura(m_preCastSpell, unit); + // Blizz seems to just apply aura without bothering to cast + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(m_preCastSpell)) + if (Unit* unitCaster = m_caster->ToUnit()) + unitCaster->AddAura(spellInfo, MAX_EFFECT_MASK, unit); } // handle SPELL_AURA_ADD_TARGET_TRIGGER auras @@ -2876,7 +2905,7 @@ 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) continue; @@ -2922,20 +2951,23 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered InitExplicitTargets(targets); // Fill aura scaling information - if (m_caster->IsControlledByPlayer() && !m_spellInfo->IsPassive() && m_spellInfo->SpellLevel && !m_spellInfo->IsChanneled() && !(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_SCALING)) + if (Unit* unitCaster = m_caster->ToUnit()) { - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (unitCaster->IsControlledByPlayer() && !m_spellInfo->IsPassive() && m_spellInfo->SpellLevel && !m_spellInfo->IsChanneled() && !(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_SCALING)) { - if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - // Change aura with ranks only if basepoints are taken from spellInfo and aura is positive - if (m_spellInfo->IsPositiveEffect(i)) + if (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) { - m_auraScaleMask |= (1 << i); - if (m_spellValue->EffectBasePoints[i] != m_spellInfo->Effects[i].BasePoints) + // Change aura with ranks only if basepoints are taken from spellInfo and aura is positive + if (m_spellInfo->IsPositiveEffect(i)) { - m_auraScaleMask = 0; - break; + m_auraScaleMask |= (1 << i); + if (m_spellValue->EffectBasePoints[i] != m_spellInfo->Effects[i].BasePoints) + { + m_auraScaleMask = 0; + break; + } } } } @@ -2951,20 +2983,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_cast_count) + // 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_cast_count) { - 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) @@ -3016,7 +3050,7 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered // don't allow channeled spells / spells with cast time to be cast while moving // 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) - if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) + 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 & SPELL_INTERRUPT_FLAG_MOVEMENT)) { // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel if (m_casttime || !m_spellInfo->IsMoveAllowedChannel()) @@ -3051,20 +3085,25 @@ 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()) + if (Unit* unitCaster = m_caster->ToUnit()) { - m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST); - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (m_spellInfo->Effects[i].GetUsedTargetObjectType() == TARGET_OBJECT_TYPE_UNIT) + // 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()) + { + unitCaster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CAST); + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_SPELL_ATTACK); - break; + if (m_spellInfo->Effects[i].GetUsedTargetObjectType() == TARGET_OBJECT_TYPE_UNIT) + { + unitCaster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_SPELL_ATTACK); + break; + } } - } + } - m_caster->SetCurrentCastSpell(this); + unitCaster->SetCurrentCastSpell(this); + } SendSpellStart(); if (!(_triggeredCastFlags & TRIGGERED_IGNORE_GCD)) @@ -3101,7 +3140,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); @@ -3119,9 +3158,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; @@ -3184,7 +3227,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(); @@ -3255,11 +3298,13 @@ void Spell::_cast(bool skipCheck) DiminishingReturnsType type = m_spellInfo->GetDiminishingReturnsGroupType(triggered); if (type == DRTYPE_ALL || (type == DRTYPE_PLAYER && target->IsAffectedByDiminishingReturns())) { - Unit* caster = m_originalCaster ? m_originalCaster : m_caster; - if (target->HasStrongerAuraWithDR(m_spellInfo, caster, triggered)) + if (Unit* caster = m_originalCaster ? m_originalCaster : m_caster->ToUnit()) { - cleanupSpell(SPELL_FAILED_AURA_BOUNCED); - return; + if (target->HasStrongerAuraWithDR(m_spellInfo, caster, triggered)) + { + cleanupSpell(SPELL_FAILED_AURA_BOUNCED); + return; + } } } } @@ -3272,7 +3317,7 @@ void Spell::_cast(bool skipCheck) if (m_caster->GetTypeId() == TYPEID_UNIT && !m_caster->HasFlag(UNIT_FIELD_FLAGS, 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(); @@ -3289,9 +3334,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(); @@ -3349,8 +3395,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 { @@ -3365,7 +3412,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); } @@ -3376,8 +3426,8 @@ 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)) - m_caster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); + if (m_originalCaster && modOwner->GetCommandStatus(CHEAT_COOLDOWN)) + m_originalCaster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); } SetExecutedCurrently(false); @@ -3433,17 +3483,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_channeledDuration = duration; SendChannelStart(duration); } else if (duration == -1) - { - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); SendChannelStart(duration); - } + + m_spellState = SPELL_STATE_CASTING; + + // GameObjects shouldn't cast channeled spells + ASSERT_NOTNULL(m_caster->ToUnit())->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); } PrepareTargetProcessing(); @@ -3589,20 +3638,23 @@ void Spell::_handle_immediate_phase() void Spell::_handle_finish_phase() { - // Take for real after all targets are processed - if (m_needComboPoints) - m_caster->ClearComboPoints(); + if (Unit* unitCaster = m_caster->ToUnit()) + { + // Take for real after all targets are processed + if (m_needComboPoints) + unitCaster->ClearComboPoints(); - // Real add combo points from effects - if (m_comboTarget && m_comboPointGain) - m_caster->AddComboPoints(m_comboTarget, m_comboPointGain); + // Real add combo points from effects + if (m_comboTarget && m_comboPointGain) + unitCaster->AddComboPoints(m_comboTarget, 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 @@ -3623,7 +3675,10 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { - m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); + if (m_caster->GetTypeId() == TYPEID_GAMEOBJECT) + return; + + m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); } void Spell::update(uint32 difftime) @@ -3644,9 +3699,9 @@ void Spell::update(uint32 difftime) } // check if the player caster has moved before the spell finished - if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && - (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)))) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0 && + m_caster->ToPlayer()->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && + (m_spellInfo->Effects[EFFECT_0].Effect != SPELL_EFFECT_STUCK || !m_caster->ToPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR))) { // don't cancel for melee, autorepeat, triggered and instant spells if (!m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->IsMoveAllowedChannel())) @@ -3654,7 +3709,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(); } } @@ -3688,7 +3743,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); } @@ -3715,44 +3770,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->GetUInt32Value(UNIT_CREATED_BY_SPELL) == 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->GetUInt32Value(UNIT_CREATED_BY_SPELL); + uint32 spell = unitCaster->GetUInt32Value(UNIT_CREATED_BY_SPELL); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell); if (spellInfo && spellInfo->SpellIconID == 2056) { - TC_LOG_DEBUG("spells", "Statue %d is unsummoned in spell %d finish", m_caster->GetGUID().GetCounter(), m_spellInfo->Id); - m_caster->setDeathState(JUST_DIED); + TC_LOG_DEBUG("spells", "Statue %d is unsummoned in spell %d finish", unitCaster->GetGUID().GetCounter(), m_spellInfo->Id); + unitCaster->setDeathState(JUST_DIED); return; } } @@ -3760,7 +3819,7 @@ void Spell::finish(bool ok) if (IsAutoActionResetSpell()) { bool found = false; - Unit::AuraEffectList const& vIgnoreReset = m_caster->GetAuraEffectsByType(SPELL_AURA_IGNORE_MELEE_RESET); + Unit::AuraEffectList const& vIgnoreReset = unitCaster->GetAuraEffectsByType(SPELL_AURA_IGNORE_MELEE_RESET); for (Unit::AuraEffectList::const_iterator i = vIgnoreReset.begin(); i != vIgnoreReset.end(); ++i) { if ((*i)->IsAffectedOnSpell(m_spellInfo)) @@ -3772,23 +3831,23 @@ void Spell::finish(bool ok) if (!found && !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(); } void Spell::WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) @@ -4024,8 +4083,14 @@ void Spell::SendSpellStart() //TC_LOG_DEBUG("spells", "Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_UNKNOWN_2; - 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; @@ -4035,7 +4100,7 @@ void Spell::SendSpellStart() if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) castFlags |= CAST_FLAG_AMMO; if ((m_caster->GetTypeId() == TYPEID_PLAYER || - (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) + (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) && m_spellInfo->PowerType != POWER_HEALTH) castFlags |= CAST_FLAG_POWER_LEFT_SELF; @@ -4057,7 +4122,7 @@ void Spell::SendSpellStart() m_targets.Write(data); if (castFlags & CAST_FLAG_POWER_LEFT_SELF) - data << uint32(m_caster->GetPower((Powers)m_spellInfo->PowerType)); + data << uint32(ASSERT_NOTNULL(m_caster->ToUnit())->GetPower((Powers)m_spellInfo->PowerType)); if (castFlags & CAST_FLAG_AMMO) WriteAmmoToPacket(&data); @@ -4089,12 +4154,12 @@ void Spell::SendSpellGo() castFlags |= CAST_FLAG_AMMO; // 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())) && m_spellInfo->PowerType != POWER_HEALTH) - 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) && m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNE && !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)) @@ -4113,7 +4178,6 @@ void Spell::SendSpellGo() castFlags |= CAST_FLAG_NO_GCD; WorldPacket data(SMSG_SPELL_GO, 50); // guess size - if (m_CastItem) data << m_CastItem->GetPackGUID(); else @@ -4130,7 +4194,7 @@ void Spell::SendSpellGo() m_targets.Write(data); if (castFlags & CAST_FLAG_POWER_LEFT_SELF) - data << uint32(m_caster->GetPower((Powers)m_spellInfo->PowerType)); + data << uint32(ASSERT_NOTNULL(m_caster->ToUnit())->GetPower((Powers)m_spellInfo->PowerType)); if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list { @@ -4174,7 +4238,14 @@ void Spell::SendSpellGo() data << uint8(0); } - m_caster->SendMessageToSet(&data, true); + // should be sent to self only + if (castFlags & CAST_FLAG_POWER_LEFT_SELF) + { + if (Player* player = m_caster->GetAffectingPlayer()) + player->SendDirectMessage(&data); + } + else + m_caster->SendMessageToSet(&data, true); } void Spell::WriteAmmoToPacket(WorldPacket* data) @@ -4202,7 +4273,7 @@ void Spell::WriteAmmoToPacket(WorldPacket* data) ammoInventoryType = pProto->InventoryType; } } - else if (m_caster->HasAura(46699)) // Requires No Ammo + else if (m_caster->ToPlayer()->HasAura(46699)) // Requires No Ammo { ammoDisplayID = 5996; // normal arrow ammoInventoryType = INVTYPE_AMMO; @@ -4210,9 +4281,9 @@ void Spell::WriteAmmoToPacket(WorldPacket* data) } } } - else + else if (m_caster->GetTypeId() == TYPEID_UNIT) { - for (uint8 i = 0; i < 3; ++i) + for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) { if (uint32 item_id = m_caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i)) { @@ -4428,59 +4499,70 @@ 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->SetChannelObjectGuid(ObjectGuid::Empty); - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, 0); + unitCaster->SetChannelObjectGuid(ObjectGuid::Empty); + unitCaster->SetUInt32Value(UNIT_CHANNEL_SPELL, 0); } WorldPacket data(MSG_CHANNEL_UPDATE, 8+4); - data << m_caster->GetPackGUID(); + data << unitCaster->GetPackGUID(); data << uint32(time); - m_caster->SendMessageToSet(&data, true); + unitCaster->SendMessageToSet(&data, true); } void Spell::SendChannelStart(uint32 duration) { + // GameObjects don't channel + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + ObjectGuid channelTarget = m_targets.GetObjectTargetGUID(); if (!channelTarget && !m_spellInfo->NeedsExplicitUnitTarget()) if (m_UniqueTargetInfo.size() + m_UniqueGOTargetInfo.size() == 1) // this is for TARGET_SELECT_CATEGORY_NEARBY channelTarget = !m_UniqueTargetInfo.empty() ? m_UniqueTargetInfo.front().TargetGUID : m_UniqueGOTargetInfo.front().TargetGUID; WorldPacket data(MSG_CHANNEL_START, (8+4+4)); - data << m_caster->GetPackGUID(); + data << unitCaster->GetPackGUID(); data << uint32(m_spellInfo->Id); data << uint32(duration); - m_caster->SendMessageToSet(&data, true); + unitCaster->SendMessageToSet(&data, true); m_timer = duration; if (channelTarget) { - m_caster->SetChannelObjectGuid(channelTarget); + unitCaster->SetChannelObjectGuid(channelTarget); - if (channelTarget != m_caster->GetGUID()) - if (Creature* creatureCaster = m_caster->ToCreature()) + if (channelTarget != unitCaster->GetGUID()) + if (Creature* creatureCaster = unitCaster->ToCreature()) if (!creatureCaster->IsFocusing(this)) creatureCaster->FocusTarget(this, ObjectAccessor::GetWorldObject(*creatureCaster, channelTarget)); } - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); + unitCaster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); } 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()); WorldPacket data(SMSG_RESURRECT_REQUEST, 8 + 4 + sentName.size() + 1 + 1 + 1); data << uint64(m_caster->GetGUID()); data << uint32(sentName.size() + 1); data << sentName; - data << uint8(m_caster->IsSpiritHealer()); // "you'll be afflicted with resurrection sickness" + data << uint8(m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsSpiritHealer()); // "you'll be afflicted with resurrection sickness" // override delay sent with SMSG_CORPSE_RECLAIM_DELAY, set instant resurrection for spells with this attribute data << uint8(!m_spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_RESURRECTION_TIMER)); target->SendDirectMessage(&data); @@ -4556,19 +4638,24 @@ 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; } Powers powerType = Powers(m_spellInfo->PowerType); bool hit = true; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { if (powerType == POWER_RAGE || powerType == POWER_ENERGY || powerType == POWER_RUNE) { @@ -4579,7 +4666,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->ToPlayer()->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); } } @@ -4598,7 +4685,7 @@ void Spell::TakePower() // health as power used if (powerType == POWER_HEALTH) { - m_caster->ModifyHealth(-(int32)m_powerCost); + unitCaster->ModifyHealth(-(int32)m_powerCost); return; } @@ -4608,40 +4695,45 @@ void Spell::TakePower() return; } - m_caster->ModifyPower(powerType, -m_powerCost); + unitCaster->ModifyPower(powerType, -m_powerCost); // Set the five second timer if (powerType == POWER_MANA && m_powerCost > 0) - m_caster->SetLastManaUse(GameTime::GetGameTimeMS()); + unitCaster->SetLastManaUse(GameTime::GetGameTimeMS()); } void Spell::TakeAmmo() { - if (m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER) - { - Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK); + // Only players use ammo + Player* player = m_caster->ToPlayer(); + if (!player) + return; - // wands don't have ammo - if (!pItem || pItem->IsBroken() || pItem->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_WAND) - return; + // only ranged + if (m_attackType != RANGED_ATTACK) + return; + + // wands don't have ammo + Item* item = player->GetWeaponForAttack(RANGED_ATTACK); + if (!item || item->IsBroken() || item->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_WAND) + return; - if (pItem->GetTemplate()->InventoryType == INVTYPE_THROWN) + if (item->GetTemplate()->InventoryType == INVTYPE_THROWN) + { + if (item->GetMaxStackCount() == 1) { - if (pItem->GetMaxStackCount() == 1) - { - // decrease durability for non-stackable throw weapon - m_caster->ToPlayer()->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); - } - else - { - // decrease items amount for stackable throw weapon - uint32 count = 1; - m_caster->ToPlayer()->DestroyItemCount(pItem, count, true); - } + // decrease durability for non-stackable throw weapon + player->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); + } + else + { + // decrease items amount for stackable throw weapon + uint32 count = 1; + player->DestroyItemCount(item, count, true); } - else if (uint32 ammo = m_caster->ToPlayer()->GetUInt32Value(PLAYER_AMMO_ID)) - m_caster->ToPlayer()->DestroyItemCount(ammo, 1, true); } + else if (uint32 ammo = player->GetUInt32Value(PLAYER_AMMO_ID)) + player->DestroyItemCount(ammo, 1, true); } SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) const @@ -4693,7 +4785,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) 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; SpellRuneCostEntry const* runeCostData = sSpellRuneCostStore.LookupEntry(m_spellInfo->RuneCostID); @@ -4833,6 +4925,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; @@ -4843,7 +4940,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; } @@ -4863,20 +4960,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())); @@ -4889,26 +4986,25 @@ 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(); - uint8 eff = m_spellInfo->Effects[i].Effect; - - TC_LOG_DEBUG("spells", "Spell: %u Effect : %u", m_spellInfo->Id, eff); + uint8 effect = m_spellInfo->Effects[i].Effect; + ASSERT(effect < TOTAL_SPELL_EFFECTS); // checked at startup // we do not need DamageMultiplier here. - damage = CalculateDamage(i, nullptr); + damage = CalculateDamage(i); - 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])((SpellEffIndex)i); - } + if (!preventDefault) + (this->*SpellEffectHandlers[effect])(effIndex); } 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 @@ -4925,7 +5021,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint 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; @@ -4953,95 +5049,98 @@ 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->IsAffectedOnSpell(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->IsAffectedOnSpell(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; + } } - } - if (m_caster->HasAuraTypeWithMiscvalue(SPELL_AURA_BLOCK_SPELL_FAMILY, m_spellInfo->SpellFamilyName)) - return SPELL_FAILED_SPELL_UNAVAILABLE; + if (unitCaster->HasAuraTypeWithMiscvalue(SPELL_AURA_BLOCK_SPELL_FAMILY, m_spellInfo->SpellFamilyName)) + return SPELL_FAILED_SPELL_UNAVAILABLE; - 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)->IsAffectedOnSpell(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)->IsAffectedOnSpell(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->CasterAuraStateNot && m_caster->HasAuraState(AuraStateType(m_spellInfo->CasterAuraStateNot), 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->CasterAuraStateNot && unitCaster->HasAuraState(AuraStateType(m_spellInfo->CasterAuraStateNot), m_spellInfo, unitCaster)) + return SPELL_FAILED_CASTER_AURASTATE; - // Note: spell 62473 requres casterAuraSpell = triggering spell - if (m_spellInfo->CasterAuraSpell && !m_caster->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->CasterAuraSpell, m_caster))) - return SPELL_FAILED_CASTER_AURASTATE; - if (m_spellInfo->ExcludeCasterAuraSpell && m_caster->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->ExcludeCasterAuraSpell, m_caster))) - return SPELL_FAILED_CASTER_AURASTATE; + // Note: spell 62473 requres casterAuraSpell = triggering spell + if (m_spellInfo->CasterAuraSpell && !unitCaster->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->CasterAuraSpell, unitCaster))) + return SPELL_FAILED_CASTER_AURASTATE; + if (m_spellInfo->ExcludeCasterAuraSpell && unitCaster->HasAura(sSpellMgr->GetSpellIdForDifficulty(m_spellInfo->ExcludeCasterAuraSpell, unitCaster))) + 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) - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature())) - { - // skip stuck spell to allow use it in falling case and apply spell limitations at movement - if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK) && - (IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0)) - 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) + if (unitCaster->GetTypeId() == TYPEID_PLAYER && unitCaster->ToPlayer()->isMoving() && (!unitCaster->IsCharmed() || !unitCaster->GetCharmerGUID().IsCreature())) + { + // skip stuck spell to allow use it in falling case and apply spell limitations at movement + if ((!unitCaster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[EFFECT_0].Effect != SPELL_EFFECT_STUCK) && + (IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0)) + 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 @@ -5070,8 +5169,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()); @@ -5081,7 +5180,7 @@ 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; @@ -5095,11 +5194,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_SKIP_CHECKCAST_LOS_CHECK) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) @@ -5119,23 +5219,26 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // check pet presence - for (int j = 0; j < MAX_SPELL_EFFECTS; ++j) + if (Unit* unitCaster = m_caster->ToUnit()) { - if (m_spellInfo->Effects[j].TargetA.GetTarget() == TARGET_UNIT_PET) + for (uint8 j = EFFECT_0; j < MAX_SPELL_EFFECTS; ++j) { - if (!m_caster->GetGuardianPet()) + if (m_spellInfo->Effects[j].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; @@ -5149,7 +5252,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_NOT_IN_ARENA; // 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); @@ -5160,13 +5263,15 @@ 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 @@ -5238,22 +5343,25 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint { if (Unit* target = m_targets.GetUnitTarget()) { - // do not allow to cast on hostile targets in sanctuary - if (!m_caster->IsFriendlyTo(target)) + if (Unit* unitCaster = m_caster->ToUnit()) { - if (m_caster->IsInSanctuary() || target->IsInSanctuary()) + // do not allow to cast on hostile targets in sanctuary + if (!unitCaster->IsFriendlyTo(target)) { - // fix for duels - Player* player = m_caster->ToPlayer(); - if (!player || !player->duel || target != player->duel->opponent) - return SPELL_FAILED_NOTHING_TO_DISPEL; + if (unitCaster->IsInSanctuary() || target->IsInSanctuary()) + { + // fix for duels + Player* player = unitCaster->ToPlayer(); + if (!player || !player->duel || target != player->duel->opponent) + return SPELL_FAILED_NOTHING_TO_DISPEL; + } } - } - DispelChargesList dispelList; - target->GetDispellableAuraList(m_caster, dispelMask, dispelList); - if (dispelList.empty()) - return SPELL_FAILED_NOTHING_TO_DISPEL; + DispelChargesList dispelList; + target->GetDispellableAuraList(unitCaster, dispelMask, dispelList); + if (dispelList.empty()) + return SPELL_FAILED_NOTHING_TO_DISPEL; + } } } @@ -5271,12 +5379,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(m_spellInfo->Effects[i].TriggerSpell); - if (!learn_spellproto) return SPELL_FAILED_NOT_KNOWN; @@ -5309,9 +5415,12 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_APPLY_GLYPH: { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_BAD_TARGETS; + uint32 glyphId = m_spellInfo->Effects[i].MiscValue; if (GlyphPropertiesEntry const* gp = sGlyphPropertiesStore.LookupEntry(glyphId)) - if (m_caster->HasAura(gp->SpellId)) + if (m_caster->ToPlayer()->HasAura(gp->SpellId)) return SPELL_FAILED_UNIQUE_GLYPH; break; } @@ -5325,7 +5434,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; @@ -5335,7 +5443,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!pet->GetCurrentFoodBenefitLevel(foodItem->GetTemplate()->ItemLevel)) return SPELL_FAILED_FOOD_LOWLEVEL; - if (m_caster->IsInCombat() || pet->IsInCombat()) + if (m_caster->ToPlayer()->IsInCombat() || pet->IsInCombat()) return SPELL_FAILED_AFFECTING_COMBAT; break; @@ -5352,14 +5460,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_CHARGE: { + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR) { // Warbringer - can't be handled in proc system - should be done before checkcast root check and charge effect process - if (strict && m_caster->IsScriptOverriden(m_spellInfo, 6953)) - m_caster->RemoveMovementImpairingAuras(true); + if (strict && unitCaster->IsScriptOverriden(m_spellInfo, 6953)) + unitCaster->RemoveMovementImpairingAuras(true); } - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && m_caster->HasUnitState(UNIT_STATE_ROOT)) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS) && unitCaster->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; if (GetSpellInfo()->NeedsExplicitUnitTarget()) @@ -5369,13 +5481,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 = Trinity::make_unique<PathGenerator>(m_caster); + m_preGeneratedPath = Trinity::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); @@ -5487,27 +5599,34 @@ 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(m_spellInfo->Effects[i].MiscValueB); if (!SummonProperties) break; + switch (SummonProperties->Category) { case SUMMON_CATEGORY_PET: - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && unitCaster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - // intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET + // intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET case SUMMON_CATEGORY_PUPPET: - if (m_caster->GetCharmGUID()) + if (unitCaster->GetCharmGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -5526,19 +5645,23 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } case SPELL_EFFECT_SUMMON_PET: { - if (m_caster->GetPetGUID()) //let warlock do a replacement summon + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->GetPetGUID()) //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()) + if (unitCaster->GetCharmGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -5546,7 +5669,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()); @@ -5583,22 +5707,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) @@ -5636,9 +5757,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; @@ -5648,7 +5773,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; } @@ -5685,16 +5814,20 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint case SPELL_AURA_MOD_CHARM: case SPELL_AURA_AOE_CHARM: { - if (m_caster->GetCharmerGUID()) + Unit* unitCaster = (m_originalCaster ? m_originalCaster : m_caster->ToUnit()); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + + if (unitCaster->GetCharmerGUID()) return SPELL_FAILED_CHARMED; if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_CHARM || m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_POSSESS) { - if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && unitCaster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if (m_caster->GetCharmGUID()) + if (unitCaster->GetCharmGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; } @@ -5712,7 +5845,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (target->GetOwner() && target->GetOwner()->GetTypeId() == TYPEID_PLAYER) return SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED; - int32 value = CalculateDamage(i, target); + int32 value = CalculateDamage(i); if (value && int32(target->getLevel()) > value) return SPELL_FAILED_HIGHLEVEL; } @@ -5721,20 +5854,23 @@ 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->AreaGroupId) + if (unitCaster->GetTypeId() == TYPEID_PLAYER && !allowMount && !m_spellInfo->AreaGroupId) return SPELL_FAILED_NO_MOUNTS_ALLOWED; - if (m_caster->IsInDisallowedMountForm()) + if (unitCaster->IsInDisallowedMountForm()) return SPELL_FAILED_NOT_SHAPESHIFT; - break; } case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS: @@ -5745,7 +5881,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: @@ -5775,7 +5910,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_targets.GetUnitTarget()->GetPowerType() != POWER_MANA) return SPELL_FAILED_BAD_TARGETS; - break; } default: @@ -5793,7 +5927,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; @@ -5810,15 +5943,18 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // check if caster has at least 1 combo point on target for spells that require combo points if (m_needComboPoints) { - if (m_spellInfo->NeedsExplicitUnitTarget()) + if (Unit* unitCaster = m_caster->ToUnit()) { - if (!m_caster->GetComboPoints(m_targets.GetUnitTarget())) - return SPELL_FAILED_NO_COMBO_POINTS; - } - else - { - if (!m_caster->GetComboPoints()) - return SPELL_FAILED_NO_COMBO_POINTS; + if (m_spellInfo->NeedsExplicitUnitTarget()) + { + if (!unitCaster->GetComboPoints(m_targets.GetUnitTarget())) + return SPELL_FAILED_NO_COMBO_POINTS; + } + else + { + if (!unitCaster->GetComboPoints()) + return SPELL_FAILED_NO_COMBO_POINTS; + } } } @@ -5828,7 +5964,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?) @@ -5860,7 +5997,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); @@ -5868,6 +6005,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; @@ -5886,22 +6027,22 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const // Glyph of Pain Suppression // there is no other way to handle it - if (m_spellInfo->Id == 33206 && !m_caster->HasAura(63248)) + if (m_spellInfo->Id == 33206 && !unitCaster->HasAura(63248)) usableWhileStunned = false; // Check whether the cast should be prevented by any state you might have. SpellCastResult result = SPELL_CAST_OK; // Get unit state - uint32 const unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); + uint32 const unitflag = unitCaster->GetUInt32Value(UNIT_FIELD_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()) + if (unitCaster->GetCharmerGUID()) { - 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; } */ @@ -5910,7 +6051,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(); @@ -5996,8 +6137,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; @@ -6055,9 +6200,9 @@ bool Spell::CheckSpellCancelsConfuse(uint32* param1) const return CheckSpellCancelsAuraEffect(SPELL_AURA_MOD_CONFUSE, param1); } -int32 Spell::CalculateDamage(uint8 i, Unit const* target) const +int32 Spell::CalculateDamage(uint8 effIndex) const { - return m_caster->CalculateSpellDamage(target, m_spellInfo, i, &m_spellValue->EffectBasePoints[i]); + return m_caster->CalculateSpellDamage(m_spellInfo, effIndex, m_spellValue->EffectBasePoints + effIndex); } bool Spell::CanAutoCast(Unit* target) @@ -6175,24 +6320,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->type & 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->type & 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); @@ -6201,14 +6351,13 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) const { rangeMod = m_caster->GetCombatReach() + (target ? target->GetCombatReach() : m_caster->GetCombatReach()); - if (minRange > 0.0f && !(m_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED)) minRange += rangeMod; } } - if (target && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking() && - (m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER)) + if (target && unitCaster && unitCaster->isMoving() && target->isMoving() && !unitCaster->IsWalking() && !target->IsWalking() && + ((m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE) || target->GetTypeId() == TYPEID_PLAYER)) rangeMod += 8.0f / 3.0f; } @@ -6221,11 +6370,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; @@ -6233,7 +6386,7 @@ SpellCastResult Spell::CheckPower() const // health as power used - need check health amount if (m_spellInfo->PowerType == POWER_HEALTH) { - if (int32(m_caster->GetHealth()) <= m_powerCost) + if (int32(unitCaster->GetHealth()) <= m_powerCost) return SPELL_FAILED_CASTER_AURASTATE; return SPELL_CAST_OK; } @@ -6254,7 +6407,7 @@ SpellCastResult Spell::CheckPower() const // Check power amount Powers powerType = Powers(m_spellInfo->PowerType); - if (int32(m_caster->GetPower(powerType)) < m_powerCost) + if (int32(unitCaster->GetPower(powerType)) < m_powerCost) return SPELL_FAILED_NO_POWER; else return SPELL_CAST_OK; @@ -6363,7 +6516,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. @@ -6449,24 +6602,24 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / case SPELL_EFFECT_CREATE_ITEM_2: { // 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() && m_spellInfo->Effects[i].ItemType) + Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player; + if (target->GetTypeId() == TYPEID_PLAYER && !IsTriggered() && m_spellInfo->Effects[i].ItemType) { ItemPosCountVec dest; - InventoryResult msg = target->ToPlayer()->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->Effects[i].ItemType, 1); if (msg != EQUIP_ERR_OK) { - ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(m_spellInfo->Effects[i].ItemType); + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(m_spellInfo->Effects[i].ItemType); /// @todo Needs review - if (pProto && !(pProto->ItemLimitCategory)) + if (itemTemplate && !itemTemplate->ItemLimitCategory) { player->SendEquipError(msg, nullptr, nullptr, m_spellInfo->Effects[i].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(m_spellInfo->Effects[i].ItemType))) { @@ -6474,7 +6627,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_DONT_REPORT; } else - player->CastSpell(m_caster, m_spellInfo->Effects[EFFECT_1].CalcValue(), false); // move this to anywhere + player->CastSpell(player, m_spellInfo->Effects[EFFECT_1].CalcValue(), false); // move this to anywhere return SPELL_FAILED_DONT_REPORT; } } @@ -6486,7 +6639,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / && (m_targets.GetItemTarget()->IsWeaponVellum() || m_targets.GetItemTarget()->IsArmorVellum())) { // 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()->Flags & ITEM_FLAG_NO_REAGENT_COST) @@ -6551,7 +6704,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; @@ -6566,7 +6719,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 = m_spellInfo->Effects[i].MiscValue; SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); @@ -6595,7 +6748,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_CANT_BE_DISENCHANTED; // prevent disenchanting in trade slot - if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID()) + if (m_targets.GetItemTarget()->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_DISENCHANTED; ItemTemplate const* itemProto = m_targets.GetItemTarget()->GetTemplate(); @@ -6626,7 +6779,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->Flags & 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()->RequiredSkillRank; @@ -6657,7 +6810,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->Flags & 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()->RequiredSkillRank; @@ -6685,15 +6838,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()->SubClass) + switch (item->GetTemplate()->SubClass) { case ITEM_SUBCLASS_WEAPON_THROWN: { - uint32 ammo = pItem->GetEntry(); + uint32 const ammo = item->GetEntry(); if (!player->HasItemCount(ammo)) return SPELL_FAILED_NO_AMMO; break; @@ -6702,11 +6855,11 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / case ITEM_SUBCLASS_WEAPON_BOW: case ITEM_SUBCLASS_WEAPON_CROSSBOW: { - uint32 ammo = player->GetUInt32Value(PLAYER_AMMO_ID); + uint32 const ammo = player->GetUInt32Value(PLAYER_AMMO_ID); if (!ammo) { // Requires No Ammo - if (m_caster->HasAura(46699)) + if (player->HasAura(46699)) break; // skip other checks return SPELL_FAILED_NO_AMMO; @@ -6720,7 +6873,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_NO_AMMO; // check ammo ws. weapon compatibility - switch (pItem->GetTemplate()->SubClass) + switch (item->GetTemplate()->SubClass) { case ITEM_SUBCLASS_WEAPON_BOW: case ITEM_SUBCLASS_WEAPON_CROSSBOW: @@ -6773,9 +6926,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()) @@ -6808,24 +6961,24 @@ 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 + // spells not losing casting time + if (!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_PUSH_BACK)) return; - // spells not loosing casting time (slam, dynamites, bombs..) - //if (!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE)) - // return; + 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->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if (Player* player = unitCaster->GetSpellModOwner()) + player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + delayReduce += unitCaster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -6839,21 +6992,27 @@ 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); - WorldPacket data(SMSG_SPELL_DELAYED, 8+4); - data << m_caster->GetPackGUID(); + data << unitCaster->GetPackGUID(); data << uint32(delaytime); - m_caster->SendMessageToSet(&data, true); + unitCaster->SendMessageToSet(&data, 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 (m_spellState != SPELL_STATE_CASTING) return; - if (isDelayableNoMore()) // Spells may only be delayed twice + // spells not losing channeling time + if (!(m_spellInfo->ChannelInterruptFlags & CHANNEL_FLAG_DELAY)) + return; + + if (IsDelayableNoMore()) // Spells may only be delayed twice return; //check pushback reduce @@ -6861,9 +7020,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->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); - delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; + if (Player* player = unitCaster->GetSpellModOwner()) + player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this); + delayReduce += unitCaster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100; if (delayReduce >= 100) return; @@ -6877,15 +7038,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); @@ -6894,7 +7053,7 @@ void Spell::DelayedChannel() 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); @@ -6969,7 +7128,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo return false; if (target->GetCharmerGUID()) return false; - if (int32 value = CalculateDamage(eff, target)) + if (int32 value = CalculateDamage(eff)) if ((int32)target->getLevel() > value) return false; break; @@ -7247,12 +7406,18 @@ void Spell::HandleLaunchPhase() HandleEffects(nullptr, nullptr, nullptr, i, SPELL_EFFECT_HANDLE_LAUNCH); } - bool usesAmmo = m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DIRECT_DAMAGE); - if (m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_ABILITY_CONSUME_NO_AMMO, m_spellInfo)) - usesAmmo = false; + bool usesAmmo; + if (Player* player = m_caster->ToPlayer()) + { + usesAmmo = m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DIRECT_DAMAGE); + if (player->HasAuraTypeWithAffectMask(SPELL_AURA_ABILITY_CONSUME_NO_AMMO, m_spellInfo)) + usesAmmo = false; - // do not consume ammo anymore for Hunter's volley spell - if (IsTriggered() && m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->IsTargetingArea()) + // do not consume ammo anymore for Hunter's volley spell + if (IsTriggered() && m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->IsTargetingArea()) + usesAmmo = false; + } + else usesAmmo = false; PrepareTargetProcessing(); @@ -7308,29 +7473,30 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uin 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, effIndex, SPELL_EFFECT_HANDLE_LAUNCH_TARGET); - if (m_damage > 0) + if (m_originalCaster && m_damage > 0) { if (m_spellInfo->Effects[effIndex].IsTargetingArea() || m_spellInfo->Effects[effIndex].IsAreaAuraEffect() || m_spellInfo->Effects[effIndex].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 uint32 targetAmount = m_UniqueTargetInfo.size(); @@ -7352,9 +7518,14 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uin targetInfo.Healing += m_healing; float critChance = m_spellValue->CriticalChance; - if (!critChance) - critChance = m_caster->SpellCritChanceDone(m_spellInfo, m_spellSchoolMask, m_attackType); - targetInfo.IsCrit = roll_chance_f(unit->SpellCritChanceTaken(m_caster, m_spellInfo, m_spellSchoolMask, critChance, m_attackType)); + if (m_originalCaster) + { + if (!critChance) + critChance = m_originalCaster->SpellCritChanceDone(m_spellInfo, m_spellSchoolMask, m_attackType); + critChance = unit->SpellCritChanceTaken(m_originalCaster, m_spellInfo, m_spellSchoolMask, critChance, m_attackType); + } + + targetInfo.IsCrit = roll_chance_f(critChance); } SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) @@ -7753,10 +7924,14 @@ 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->IsAffectedOnSpell(m_spellInfo)) @@ -7770,7 +7945,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, auraSpellInfo, auraSpellIdx, &auraBaseAmount); + int32 chance = unitCaster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, &auraBaseAmount); chance *= aurEff->GetBase()->GetStackAmount(); // build trigger and add to the list @@ -7786,23 +7961,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) + 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) return; if (m_caster->GetTypeId() == TYPEID_PLAYER) @@ -7823,47 +8005,44 @@ void Spell::TriggerGlobalCooldown() 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) : _caster(caster), _referer(referer), _spellInfo(spellInfo), - _targetSelectionType(selectionType), _condList(condList) +WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo), + _targetSelectionType(selectionType), _condSrcInfo(nullptr), _condList(condList) { if (condList) - _condSrcInfo = new ConditionSourceInfo(nullptr, caster); - else - _condSrcInfo = nullptr; + _condSrcInfo = Trinity::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()) { @@ -7874,6 +8053,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; } + Unit* refUnit = _referer->ToUnit(); if (unitTarget) { // do only faction checks here @@ -7882,7 +8062,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: @@ -7892,23 +8072,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; // nobreak; 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; default: @@ -7935,8 +8121,8 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return sConditionMgr->IsObjectMeetToConditions(*_condSrcInfo, *_condList); } -WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList) +WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, WorldObject* caster, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList), _range(range), _position(caster) { } bool WorldObjectSpellNearbyTargetCheck::operator()(WorldObject* target) @@ -7950,11 +8136,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) +WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster, + WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position) { } -bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) const { if (target->ToGameObject()) { @@ -7973,11 +8159,11 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) return WorldObjectSpellTargetCheck::operator ()(target); } -WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList), _coneAngle(coneAngle) { } -bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) +bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) const { if (_spellInfo->HasAttribute(SPELL_ATTR0_CU_CONE_BACK)) { @@ -7997,10 +8183,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) +WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, WorldObject* caster, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList), _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)) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 9ea93357e16..39cf1beeb53 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -189,8 +189,7 @@ class TC_GAME_API SpellCastTargets float GetSpeedXY() const { return m_speed * std::cos(m_elevation); } float GetSpeedZ() const { return m_speed * std::sin(m_elevation); } - void Update(Unit* caster); - void OutDebug() const; + void Update(WorldObject* caster); private: uint32 m_targetMask; @@ -376,7 +375,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); @@ -399,10 +398,10 @@ class TC_GAME_API Spell void SelectEffectTypeImplicitTargets(uint8 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(); @@ -443,7 +442,7 @@ class TC_GAME_API Spell bool CheckSpellCancelsFear(uint32* param1) const; bool CheckSpellCancelsConfuse(uint32* param1) const; - int32 CalculateDamage(uint8 i, Unit const* target) const; + int32 CalculateDamage(uint8 effIndex) const; void Delayed(); void DelayedChannel(); @@ -538,7 +537,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; } int32 GetPowerCost() const { return m_powerCost; } @@ -560,7 +559,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; @@ -579,12 +578,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; } @@ -609,6 +608,7 @@ class TC_GAME_API Spell SpellMissInfo targetMissInfo; SpellEffectHandleMode effectHandleMode; // used in effects handlers + Unit* unitCaster; UnitAura* _spellAura; DynObjAura* _dynObjAura; @@ -754,7 +754,6 @@ class TC_GAME_API Spell // effect helpers void SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numSummons); - void CalculateJumpSpeeds(uint8 i, float dist, float & speedxy, float & speedz); SpellCastResult CanOpenLock(uint32 effIndex, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue); // ------------------------------------------- @@ -783,25 +782,28 @@ namespace Trinity { struct TC_GAME_API WorldObjectSpellTargetCheck { - Unit* _caster; - Unit* _referer; - SpellInfo const* _spellInfo; - SpellTargetCheckTypes _targetSelectionType; - ConditionSourceInfo* _condSrcInfo; - ConditionContainer* _condList; - - WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList); - ~WorldObjectSpellTargetCheck(); - bool operator()(WorldObject* target); + protected: + WorldObject* _caster; + WorldObject* _referer; + SpellInfo const* _spellInfo; + SpellTargetCheckTypes _targetSelectionType; + std::unique_ptr<ConditionSourceInfo> _condSrcInfo; + ConditionContainer const* _condList; + + WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList); + ~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); + WorldObjectSpellNearbyTargetCheck(float range, WorldObject* caster, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList); + bool operator()(WorldObject* target); }; @@ -809,29 +811,32 @@ namespace Trinity { float _range; Position const* _position; - WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList); - bool operator()(WorldObject* target); + WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster, + WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList); + + bool operator()(WorldObject* target) const; }; struct TC_GAME_API WorldObjectSpellConeTargetCheck : public WorldObjectSpellAreaTargetCheck { float _coneAngle; - WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList); - bool operator()(WorldObject* target); + WorldObjectSpellConeTargetCheck(float coneAngle, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList); + + 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); - bool operator()(WorldObject* target); + WorldObjectSpellTrajTargetCheck(float range, Position const* position, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList); + + 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 14eb0cc31cf..d48cca42437 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -14,6 +14,7 @@ * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. */ + #ifndef TRINITY_SPELLDEFINES_H #define TRINITY_SPELLDEFINES_H @@ -181,7 +182,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 7c64440eea9..bdc262ef0bd 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -60,7 +60,7 @@ #include "WorldPacket.h" #include "WorldSession.h" -pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= +SpellEffectHandlerFn SpellEffectHandlers[TOTAL_SPELL_EFFECTS] = { &Spell::EffectNULL, // 0 &Spell::EffectInstaKill, // 1 SPELL_EFFECT_INSTAKILL @@ -286,7 +286,7 @@ void Spell::EffectInstaKill(SpellEffIndex /*effIndex*/) data << uint32(m_spellInfo->Id); m_caster->SendMessageToSet(&data, 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*/) @@ -302,12 +302,13 @@ 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); - uint32 absorb = damageInfo.GetAbsorb(); - uint32 resist = damageInfo.GetResist(); - m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_spellInfo->GetSchoolMask(), absorb, resist, false, 0, false); + uint32 const absorb = damageInfo.GetAbsorb(); + uint32 const resist = damageInfo.GetResist(); + if (unitCaster) + unitCaster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_spellInfo->GetSchoolMask(), absorb, resist, false, 0, false); } } @@ -328,17 +329,27 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) { uint32 count = 0; for (auto ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + { + if (ihit->MissCondition != SPELL_MISS_NONE) + continue; + if (ihit->EffectMask & (1 << effIndex)) ++count; + } - damage /= count; // divide to all targets + // divide to all targets + if (count) + damage /= count; } + ///@todo: move those to scripts switch (m_spellInfo->Id) // better way to check unknown { // Consumption case 28865: - damage = (((InstanceMap*)m_caster->GetMap())->GetDifficulty() == REGULAR_DIFFICULTY ? 2750 : 4250); + damage = 2750; + if (m_caster->GetMap()->IsHeroic()) + damage = 4250; break; // percent from health with min case 25599: // Thundercrash @@ -351,16 +362,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; } } @@ -368,28 +380,34 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } case SPELLFAMILY_WARRIOR: { + if (!unitCaster) + break; + // Shield Slam - if (m_spellInfo->SpellFamilyFlags[1] & 0x200 && m_spellInfo->GetCategory() == 1209) + if ((m_spellInfo->SpellFamilyFlags[1] & 0x200) && m_spellInfo->GetCategory() == 1209) { - uint8 level = m_caster->getLevel(); - uint32 block_value = m_caster->GetShieldBlockValue(uint32(float(level) * 24.5f), uint32(float(level) * 34.5f)); - damage += int32(m_caster->ApplyEffectModifiers(m_spellInfo, effIndex, float(block_value))); + uint8 level = unitCaster->getLevel(); + uint32 block_value = unitCaster->GetShieldBlockValue(uint32(float(level) * 24.5f), uint32(float(level) * 34.5f)); + damage += int32(unitCaster->ApplyEffectModifiers(m_spellInfo, effIndex, float(block_value))); } // Victory Rush else if (m_spellInfo->SpellFamilyFlags[1] & 0x100) - 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(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; } case SPELLFAMILY_WARLOCK: { + if (!unitCaster) + break; + // Incinerate Rank 1 & 2 if ((m_spellInfo->SpellFamilyFlags[1] & 0x000040) && m_spellInfo->SpellIconID == 2128) { @@ -411,7 +429,7 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) { // for caster applied auras only if ((*i)->GetSpellInfo()->SpellFamilyName != SPELLFAMILY_WARLOCK || - (*i)->GetCasterGUID() != m_caster->GetGUID()) + (*i)->GetCasterGUID() != unitCaster->GetGUID()) continue; // Immolate @@ -431,15 +449,15 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) { // Calculate damage of Immolate/Shadowflame tick int32 pdamage = aura->GetAmount(); - pdamage = unitTarget->SpellDamageBonusTaken(m_caster, aura->GetSpellInfo(), pdamage, DOT); + pdamage = unitTarget->SpellDamageBonusTaken(unitCaster, aura->GetSpellInfo(), pdamage, DOT); // And multiply by amount of ticks to get damage potential pdamage *= aura->GetSpellInfo()->GetMaxTicks(); - int32 pct_dir = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_1); + int32 pct_dir = unitCaster->CalculateSpellDamage(m_spellInfo, EFFECT_1); damage += CalculatePct(pdamage, pct_dir); - int32 pct_dot = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_2); + int32 pct_dot = unitCaster->CalculateSpellDamage(m_spellInfo, EFFECT_2); int32 const dotBasePoints = CalculatePct(pdamage, pct_dot); ASSERT(m_spellInfo->GetMaxTicks() > 0); @@ -447,8 +465,8 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) apply_direct_bonus = false; // Glyph of Conflagrate - if (!m_caster->HasAura(56235)) - unitTarget->RemoveAurasDueToSpell(aura->GetId(), m_caster->GetGUID()); + if (!unitCaster->HasAura(56235)) + unitTarget->RemoveAurasDueToSpell(aura->GetId(), unitCaster->GetGUID()); break; } @@ -456,16 +474,16 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) // Shadow Bite else if (m_spellInfo->SpellFamilyFlags[1] & 0x400000) { - if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet()) + if (unitCaster->GetTypeId() == TYPEID_UNIT && unitCaster->IsPet()) { - if (Player* owner = m_caster->GetOwner()->ToPlayer()) + if (Player* owner = unitCaster->GetOwner()->ToPlayer()) { if (AuraEffect* aurEff = owner->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_WARLOCK, 214, 0)) { int32 bp0 = aurEff->GetId() == 54037 ? 4 : 8; CastSpellExtraArgs args(TRIGGERED_FULL_MASK); args.AddSpellMod(SPELLVALUE_BASE_POINT0, bp0); - m_caster->CastSpell(m_caster, 54425, args); + unitCaster->CastSpell(nullptr, 54425, args); } } } @@ -474,19 +492,22 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } case SPELLFAMILY_PRIEST: { + if (!unitCaster) + break; + // Improved Mind Blast (Mind Blast in shadow form bonus) - if (m_caster->GetShapeshiftForm() == FORM_SHADOW && (m_spellInfo->SpellFamilyFlags[0] & 0x00002000)) + if (unitCaster->GetShapeshiftForm() == FORM_SHADOW && (m_spellInfo->SpellFamilyFlags[0] & 0x00002000)) { - Unit::AuraEffectList const& ImprMindBlast = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_FLAT_MODIFIER); + Unit::AuraEffectList const& ImprMindBlast = unitCaster->GetAuraEffectsByType(SPELL_AURA_ADD_FLAT_MODIFIER); for (Unit::AuraEffectList::const_iterator i = ImprMindBlast.begin(); i != ImprMindBlast.end(); ++i) { if ((*i)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_PRIEST && ((*i)->GetSpellInfo()->SpellIconID == 95)) { - int chance = (*i)->GetSpellInfo()->Effects[EFFECT_1].CalcValue(m_caster); + // Mind Trauma + int32 const chance = (*i)->GetSpellInfo()->Effects[EFFECT_1].CalcValue(unitCaster); if (roll_chance_i(chance)) - // Mind Trauma - m_caster->CastSpell(unitTarget, 48301, true); + unitCaster->CastSpell(unitTarget, 48301, true); break; } } @@ -495,21 +516,24 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } case SPELLFAMILY_DRUID: { + if (!unitCaster) + break; + // Ferocious Bite - if (m_caster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags[0] & 0x000800000) && m_spellInfo->SpellVisual[0] == 6587) + if (unitCaster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags[0] & 0x000800000) && m_spellInfo->SpellVisual[0] == 6587) { // converts each extra point of energy into ($f1+$AP/410) additional damage - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + float ap = unitCaster->GetTotalAttackPowerValue(BASE_ATTACK); float multiple = ap / 410 + m_spellInfo->Effects[effIndex].DamageMultiplier; - int32 energy = -(m_caster->ModifyPower(POWER_ENERGY, -30)); + int32 energy = -(unitCaster->ModifyPower(POWER_ENERGY, -30)); damage += int32(energy * multiple); - damage += int32(CalculatePct(m_caster->ToPlayer()->GetComboPoints() * ap, 7)); + damage += int32(CalculatePct(unitCaster->ToPlayer()->GetComboPoints() * ap, 7)); } // Wrath else if (m_spellInfo->SpellFamilyFlags[0] & 0x00000001) { // Improved Insect Swarm - if (AuraEffect const* aurEff = m_caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 1771, 0)) + if (AuraEffect const* aurEff = unitCaster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 1771, 0)) if (unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00200000, 0, 0)) AddPct(damage, aurEff->GetAmount()); } @@ -517,16 +541,19 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } case SPELLFAMILY_ROGUE: { + if (!unitCaster) + break; + // Envenom if (m_spellInfo->SpellFamilyFlags[1] & 0x00000008) { - if (Player* player = m_caster->ToPlayer()) + if (Player* player = unitCaster->ToPlayer()) { // consume from stack dozes not more that have combo-points if (uint32 combo = player->GetComboPoints()) { // Lookup for Deadly poison (only attacker applied) - if (AuraEffect const* aurEff = unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_ROGUE, 0x00010000, 0, 0, m_caster->GetGUID())) + if (AuraEffect const* aurEff = unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_ROGUE, 0x00010000, 0, 0, unitCaster->GetGUID())) { // count consumed deadly poison doses at target bool needConsume = true; @@ -542,7 +569,7 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) { if ((*iter)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_ROGUE && (*iter)->GetSpellInfo()->SpellIconID == 1960) { - uint32 chance = (*iter)->GetSpellInfo()->Effects[EFFECT_2].CalcValue(m_caster); + uint32 chance = (*iter)->GetSpellInfo()->Effects[EFFECT_2].CalcValue(unitCaster); if (chance && roll_chance_i(chance)) needConsume = false; @@ -553,14 +580,14 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) if (needConsume) for (uint32 i = 0; i < doses; ++i) - unitTarget->RemoveAuraFromStack(spellId, m_caster->GetGUID()); + unitTarget->RemoveAuraFromStack(spellId, unitCaster->GetGUID()); damage *= doses; damage += int32(player->GetTotalAttackPowerValue(BASE_ATTACK) * 0.09f * combo); } // Eviscerate and Envenom Bonus Damage (item set effect) - if (m_caster->HasAura(37169)) + if (unitCaster->HasAura(37169)) damage += combo * 40; } } @@ -568,15 +595,15 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) // Eviscerate else if (m_spellInfo->SpellFamilyFlags[0] & 0x00020000) { - if (Player* player = m_caster->ToPlayer()) + if (Player* player = unitCaster->ToPlayer()) { if (uint32 combo = player->GetComboPoints()) { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + float ap = unitCaster->GetTotalAttackPowerValue(BASE_ATTACK); damage += std::lroundf(ap * combo * 0.07f); // Eviscerate and Envenom Bonus Damage (item set effect) - if (m_caster->HasAura(37169)) + if (unitCaster->HasAura(37169)) damage += combo*40; } } @@ -585,10 +612,13 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } case SPELLFAMILY_HUNTER: { + if (!unitCaster) + break; + //Gore if (m_spellInfo->SpellIconID == 1578) { - if (m_caster->HasAura(57627)) // Charge 6 sec post-affect + if (unitCaster->HasAura(57627)) // Charge 6 sec post-affect damage *= 2; } // Steady Shot @@ -610,7 +640,7 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) if (found) damage += m_spellInfo->Effects[EFFECT_1].CalcValue(); - if (Player* caster = m_caster->ToPlayer()) + if (Player* caster = unitCaster->ToPlayer()) { // Add Ammo and Weapon damage plus RAP * 0.1 float dmg_min = 0.f; @@ -632,8 +662,11 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } case SPELLFAMILY_PALADIN: { + if (!unitCaster) + break; + // Hammer of the Righteous - if (m_spellInfo->SpellFamilyFlags[1]&0x00040000) + if (m_spellInfo->SpellFamilyFlags[1] & 0x00040000) { float minTotal = 0.f; float maxTotal = 0.f; @@ -641,22 +674,22 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) float tmpMin, tmpMax; for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) { - m_caster->CalculateMinMaxDamage(BASE_ATTACK, false, false, tmpMin, tmpMax, i); + unitCaster->CalculateMinMaxDamage(BASE_ATTACK, false, false, tmpMin, tmpMax, i); minTotal += tmpMin; maxTotal += tmpMax; } float average = (minTotal + maxTotal) / 2; // Add main hand dps * effect[2] amount - int32 count = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_2); - damage += count * int32(average * IN_MILLISECONDS) / m_caster->GetAttackTime(BASE_ATTACK); + int32 count = unitCaster->CalculateSpellDamage(m_spellInfo, EFFECT_2); + damage += count * int32(average * IN_MILLISECONDS) / unitCaster->GetAttackTime(BASE_ATTACK); break; } // Shield of Righteousness if (m_spellInfo->SpellFamilyFlags[EFFECT_1] & 0x100000) { - uint8 level = m_caster->getLevel(); - uint32 block_value = m_caster->GetShieldBlockValue(uint32(float(level) * 29.5f), uint32(float(level) * 39.5f)); + uint8 level = unitCaster->getLevel(); + uint32 block_value = unitCaster->GetShieldBlockValue(uint32(float(level) * 29.5f), uint32(float(level) * 39.5f)); damage += CalculatePct(block_value, m_spellInfo->Effects[EFFECT_1].CalcValue()); break; } @@ -664,23 +697,26 @@ 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, 0, 0, 0x00000002, m_caster->GetGUID())) + if (unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DEATHKNIGHT, 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 && damage > 0 && apply_direct_bonus) + if (unitCaster && damage > 0 && apply_direct_bonus) { - damage = m_originalCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effIndex, { }); - damage = unitTarget->SpellDamageBonusTaken(m_originalCaster, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE); + damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effIndex, { }); + damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE); } m_damage += damage; @@ -768,10 +804,12 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) // Mirror Image case 58832: { - // Glyph of Mirror Image - if (m_caster->HasAura(63093)) - m_caster->CastSpell(m_caster, 65047, true); // Mirror Image + if (!unitCaster) + break; + // Glyph of Mirror Image + if (unitCaster->HasAura(63093)) + unitCaster->CastSpell(nullptr, 65047, true); // Mirror Image break; } // Demonic Empowerment -- succubus @@ -842,7 +880,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); @@ -886,7 +929,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); @@ -971,20 +1017,35 @@ void Spell::EffectTriggerRitualOfSummoning(SpellEffIndex effIndex) m_caster->CastSpell(nullptr, spellInfo->Id, false); } +inline void CalculateJumpSpeeds(SpellInfo const* spellInfo, uint8 i, float dist, float& speedXY, float& speedZ) +{ + if (spellInfo->Effects[i].MiscValue) + speedZ = spellInfo->Effects[i].MiscValue / 10.f; + else if (spellInfo->Effects[i].MiscValueB) + speedZ = spellInfo->Effects[i].MiscValueB / 10.f; + 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(effIndex, m_caster->GetExactDist2d(unitTarget), speedXY, speedZ); - m_caster->GetMotionMaster()->MoveJump(*unitTarget, speedXY, speedZ, EVENT_JUMP, false); + CalculateJumpSpeeds(m_spellInfo, effIndex, unitCaster->GetExactDist2d(unitTarget), speedXY, speedZ); + unitCaster->GetMotionMaster()->MoveJump(*unitTarget, speedXY, speedZ, EVENT_JUMP, false); } void Spell::EffectJumpDest(SpellEffIndex effIndex) @@ -992,26 +1053,18 @@ 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(effIndex, m_caster->GetExactDist2d(destTarget), speedXY, speedZ); - m_caster->GetMotionMaster()->MoveJump(*destTarget, speedXY, speedZ, EVENT_JUMP, !m_targets.GetObjectTargetGUID().IsEmpty()); -} - -void Spell::CalculateJumpSpeeds(uint8 i, float dist, float & speedXY, float & speedZ) -{ - if (m_spellInfo->Effects[i].MiscValue) - speedZ = float(m_spellInfo->Effects[i].MiscValue)/10; - else if (m_spellInfo->Effects[i].MiscValueB) - speedZ = float(m_spellInfo->Effects[i].MiscValueB)/10; - else - speedZ = 10.0f; - speedXY = dist * 10.0f / speedZ; + CalculateJumpSpeeds(m_spellInfo, effIndex, unitCaster->GetExactDist2d(destTarget), speedXY, speedZ); + unitCaster->GetMotionMaster()->MoveJump(*destTarget, speedXY, speedZ, EVENT_JUMP, !m_targets.GetObjectTargetGUID().IsEmpty()); } void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) @@ -1192,13 +1245,15 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex) return; Powers powerType = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (!unitTarget || !unitTarget->IsAlive() || unitTarget->GetPowerType() != powerType || damage < 0) return; // add spell damage bonus - damage = m_caster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effIndex, { }); - damage = unitTarget->SpellDamageBonusTaken(m_caster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); + if (unitCaster) + { + damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effIndex, { }); + damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); + } // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) int32 power = damage; @@ -1207,16 +1262,14 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex) int32 newDamage = -(unitTarget->ModifyPower(powerType, -int32(power))); - float gainMultiplier = 0.0f; - // Don't restore from self drain - if (m_caster != unitTarget) + float gainMultiplier = 0.f; + if (unitCaster && unitCaster != unitTarget) { - gainMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this); - - int32 gain = int32(newDamage* gainMultiplier); + gainMultiplier = m_spellInfo->Effects[effIndex].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); } @@ -1271,14 +1324,14 @@ void Spell::EffectPowerBurn(SpellEffIndex effIndex) return; Powers powerType = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (!unitTarget || !unitTarget->IsAlive() || unitTarget->GetPowerType() != powerType || damage < 0) return; // burn x% of target's mana, up to maximum of 2x% of caster's mana (Mana Burn) - if (m_spellInfo->Id == 8129) + ///@todo: move this to scripts + if (unitCaster && m_spellInfo->Id == 8129) { - int32 maxDamage = int32(CalculatePct(m_caster->GetMaxPower(powerType), damage * 2)); + int32 maxDamage = int32(CalculatePct(unitCaster->GetMaxPower(powerType), damage * 2)); damage = int32(CalculatePct(unitTarget->GetMaxPower(powerType), damage)); damage = std::min(damage, maxDamage); } @@ -1291,7 +1344,7 @@ void Spell::EffectPowerBurn(SpellEffIndex effIndex) int32 newDamage = -(unitTarget->ModifyPower(powerType, -power)); // NO - Not a typo - EffectPowerBurn uses effect value multiplier - not effect damage multiplier - float dmgMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this); + float dmgMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(unitCaster, this); // add log data before multiplication (need power amount, not damage) ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, powerType, newDamage, 0.0f); @@ -1306,115 +1359,107 @@ 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 ? 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) + // 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)) { - if (Player* player = m_caster->ToPlayer()) - if (player->HasSkill(SKILL_ENGINEERING)) - AddPct(addhealth, 25); + damageAmount += aurEff->GetAmount(); + unitCaster->RemoveAurasDueToSpell(45062); } - // Swiftmend - consumes Regrowth or Rejuvenation - else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND, m_spellInfo, m_caster)) - { - Unit::AuraEffectList const& RejorRegr = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_HEAL); - // find most short by duration - AuraEffect* targetAura = nullptr; - for (Unit::AuraEffectList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) - { - if ((*i)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID - && (*i)->GetSpellInfo()->SpellFamilyFlags[0] & 0x50) - { - if (!targetAura || (*i)->GetBase()->GetDuration() < targetAura->GetBase()->GetDuration()) - targetAura = *i; - } - } - if (!targetAura) + 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); + } + // Swiftmend - consumes Regrowth or Rejuvenation + else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND, m_spellInfo, unitCaster)) + { + Unit::AuraEffectList const& RejorRegr = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_HEAL); + // find most short by duration + AuraEffect* targetAura = nullptr; + for (Unit::AuraEffectList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) + { + if ((*i)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID + && (*i)->GetSpellInfo()->SpellFamilyFlags[0] & 0x50) { - TC_LOG_ERROR("spells", "Target (%s) has the aurastate AURA_STATE_SWIFTMEND, but no matching aura.", unitTarget->GetGUID().ToString().c_str()); - return; + if (!targetAura || (*i)->GetBase()->GetDuration() < targetAura->GetBase()->GetDuration()) + targetAura = *i; } + } - int32 tickheal = targetAura->GetAmount(); - unitTarget->SpellHealingBonusTaken(m_caster, targetAura->GetSpellInfo(), tickheal, DOT); + if (!targetAura) + { + TC_LOG_ERROR("spells", "Target (%s) has the aurastate AURA_STATE_SWIFTMEND, but no matching aura.", unitTarget->GetGUID().ToString().c_str()); + return; + } - //int32 tickheal = targetAura->GetSpellInfo()->EffectBasePoints[idx] + 1; - //It is said that talent bonus should not be included + int32 tickheal = targetAura->GetAmount(); + unitTarget->SpellHealingBonusTaken(unitCaster, targetAura->GetSpellInfo(), tickheal, DOT); - int32 tickcount = 0; - // Rejuvenation - if (targetAura->GetSpellInfo()->SpellFamilyFlags[0] & 0x10) - tickcount = 4; - // Regrowth - else // if (targetAura->GetSpellInfo()->SpellFamilyFlags[0] & 0x40) - tickcount = 6; + int32 tickcount = 0; + // Rejuvenation + if (targetAura->GetSpellInfo()->SpellFamilyFlags[0] & 0x10) + tickcount = 4; + // Regrowth + else // if (targetAura->GetSpellInfo()->SpellFamilyFlags[0] & 0x40) + tickcount = 6; - addhealth += tickheal * tickcount; + addhealth += tickheal * tickcount; - // Glyph of Swiftmend - if (!caster->HasAura(54824)) - unitTarget->RemoveAura(targetAura->GetId(), targetAura->GetCasterGUID()); + // Glyph of Swiftmend + if (!unitCaster->HasAura(54824)) + unitTarget->RemoveAura(targetAura->GetId(), targetAura->GetCasterGUID()); + } + // Nourish + else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[1] & 0x2000000) + { + addhealth = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effIndex, { }); - //addhealth += tickheal * tickcount; - //addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth, HEAL, unitTarget); - } - // Nourish - else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[1] & 0x2000000) + // Glyph of Nourish + if (AuraEffect const* aurEff = unitCaster->GetAuraEffect(62971, 0)) { - addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effIndex, { }); - - // Glyph of Nourish - if (AuraEffect const* aurEff = m_caster->GetAuraEffect(62971, 0)) + uint32 auraCount = 0; + Unit::AuraEffectList const& periodicHeals = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_HEAL); + for (AuraEffect const* hot : periodicHeals) { - uint32 auraCount = 0; - Unit::AuraEffectList const& periodicHeals = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_HEAL); - for (AuraEffect const* hot : periodicHeals) - { - if (m_caster->GetGUID() == hot->GetCasterGUID()) - ++auraCount; - } - - AddPct(addhealth, aurEff->GetAmount() * auraCount); + if (unitCaster->GetGUID() == hot->GetCasterGUID()) + ++auraCount; } + + AddPct(addhealth, aurEff->GetAmount() * auraCount); } - // 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, effIndex, { }); - else - addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effIndex, { }); + } + // 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, effIndex, { }); + else + addhealth = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effIndex, { }); - addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL); + addhealth = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, addhealth, HEAL); - // Remove Grievious bite if fully healed - if (unitTarget->HasAura(48920) && (unitTarget->GetHealth() + addhealth >= unitTarget->GetMaxHealth())) - unitTarget->RemoveAura(48920); + // Remove Grievious bite if fully healed + if (unitTarget->HasAura(48920) && (unitTarget->GetHealth() + addhealth >= unitTarget->GetMaxHealth())) + unitTarget->RemoveAura(48920); - m_healing += addhealth; - } + m_healing += addhealth; } void Spell::EffectHealPct(SpellEffIndex effIndex) @@ -1425,12 +1470,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, effIndex, { }); + heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL); + } - uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, unitTarget->CountPctFromMaxHealth(damage), HEAL, effIndex, { }); - m_healing += unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL); + m_healing += heal; } void Spell::EffectHealMechanical(SpellEffIndex effIndex) @@ -1441,12 +1488,14 @@ 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, effIndex, { }); + heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL); + } - uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, uint32(damage), HEAL, effIndex, { }); - m_healing += unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL); + m_healing += heal; } void Spell::EffectHealthLeech(SpellEffIndex effIndex) @@ -1457,24 +1506,27 @@ void Spell::EffectHealthLeech(SpellEffIndex effIndex) if (!unitTarget || !unitTarget->IsAlive() || damage < 0) return; - damage = m_caster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effIndex, { }); - damage = unitTarget->SpellDamageBonusTaken(m_caster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); + if (unitCaster) + { + damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effIndex, { }); + damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); + } TC_LOG_DEBUG("spells", "HealthLeech :%i", damage); - float healMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this); + float healMultiplier = m_spellInfo->Effects[effIndex].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, effIndex, { }); - healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL); + healthGain = unitCaster->SpellHealingBonusDone(unitCaster, m_spellInfo, healthGain, HEAL, effIndex, { }); + 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); } } @@ -1671,6 +1723,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 (m_spellInfo->Effects[i].Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA) @@ -1678,15 +1733,14 @@ void Spell::EffectPersistentAA(SpellEffIndex effIndex) ASSERT(!_dynObjAura); - Unit* caster = m_caster->GetEntry() == WORLD_TRIGGER ? m_originalCaster : m_caster; - float radius = m_spellInfo->Effects[effIndex].CalcRadius(caster); + float radius = m_spellInfo->Effects[effIndex].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->Id, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL)) + if (!dynObj->CreateDynamicObject(unitCaster->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), unitCaster, m_spellInfo->Id, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL)) { delete dynObj; return; @@ -1694,7 +1748,7 @@ void Spell::EffectPersistentAA(SpellEffIndex effIndex) AuraCreateInfo createInfo(m_spellInfo, MAX_EFFECT_MASK, dynObj); createInfo - .SetCaster(caster) + .SetCaster(unitCaster) .SetBaseAmount(m_spellValue->EffectBasePoints); if (Aura* aura = Aura::TryCreate(createInfo)) @@ -1714,8 +1768,9 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget) + if (!unitCaster || !unitTarget) return; + if (!unitTarget->IsAlive()) return; @@ -1723,7 +1778,6 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) return; Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->GetPowerType() != power && m_spellInfo->SpellFamilyName != SPELLFAMILY_POTION && !m_spellInfo->HasAttribute(SPELL_ATTR7_CAN_RESTORE_SECONDARY_POWER)) return; @@ -1732,20 +1786,21 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) return; // Some level depends spells - int level_multiplier = 0; - int level_diff = 0; + ///@todo: move this to scripts + int32 level_multiplier = 0; + int32 level_diff = 0; switch (m_spellInfo->Id) { case 9512: // Restore Energy - level_diff = m_caster->getLevel() - 40; + level_diff = unitCaster->getLevel() - 40; level_multiplier = 2; break; case 24571: // Blood Fury - level_diff = m_caster->getLevel() - 60; + level_diff = unitCaster->getLevel() - 60; level_multiplier = 10; break; case 24532: // Burst of Energy - level_diff = m_caster->getLevel() - 60; + level_diff = unitCaster->getLevel() - 60; level_multiplier = 4; break; case 31930: // Judgements of the Wise @@ -1758,7 +1813,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) 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; @@ -1776,7 +1831,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) if (damage < 0) return; - m_caster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power); + unitCaster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power); } void Spell::EffectEnergizePct(SpellEffIndex effIndex) @@ -1784,8 +1839,9 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget) + if (!unitCaster || !unitTarget) return; + if (!unitTarget->IsAlive()) return; @@ -1798,11 +1854,11 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) return; 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) @@ -1823,7 +1879,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; } @@ -1846,7 +1902,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) case GAMEOBJECT_TYPE_SPELL_FOCUS: // triggering linked GO if (uint32 trapEntry = gameObjTarget->GetGOInfo()->spellFocus.linkedTrapId) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, m_caster); + gameObjTarget->TriggeringLinkedGameObject(trapEntry, player); return; case GAMEOBJECT_TYPE_CHEST: @@ -1859,7 +1915,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) // triggering linked GO if (uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrapId) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, m_caster); + gameObjTarget->TriggeringLinkedGameObject(trapEntry, player); // Don't return, let loots been taken default: @@ -2129,11 +2185,12 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) return; } - if (!m_originalCaster) - return; + WorldObject* caster = m_caster; + if (m_originalCaster) + caster = m_originalCaster; int32 duration = m_spellInfo->GetDuration(); - if (Player* modOwner = m_originalCaster->GetSpellModOwner()) + if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); TempSummon* summon = nullptr; @@ -2174,11 +2231,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); break; } + switch (properties->Type) { case SUMMON_TYPE_PET: @@ -2187,21 +2246,29 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) case SUMMON_TYPE_MINION: SummonGuardian(effIndex, entry, properties, numSummons); 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 SUMMON_TYPE_VEHICLE: case SUMMON_TYPE_VEHICLE2: - 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 SUMMON_TYPE_LIGHTWELL: case SUMMON_TYPE_TOTEM: { - 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); if (!summon || !summon->IsTotem()) return; // Mana Tide Totem if (m_spellInfo->Id == 16190) - damage = m_caster->CountPctFromMaxHealth(10); + damage = unitCaster->CountPctFromMaxHealth(10); if (damage) // if not spell info, DB values used { @@ -2212,7 +2279,10 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) } case SUMMON_TYPE_MINIPET: { - 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); if (!summon || !summon->HasUnitTypeMask(UNIT_MASK_MINION)) return; @@ -2236,16 +2306,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); + summon = caster->SummonCreature(entry, pos, summonType, duration); if (!summon) continue; if (properties->Category == SUMMON_CATEGORY_ALLY) { - summon->SetOwnerGUID(m_originalCaster->GetGUID()); - summon->SetFaction(m_originalCaster->GetFaction()); + summon->SetOwnerGUID(caster->GetGUID()); + summon->SetFaction(caster->GetFaction()); summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); } @@ -2253,18 +2323,28 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) } return; } - }//switch + } break; + } case SUMMON_CATEGORY_PET: SummonGuardian(effIndex, entry, properties, numSummons); break; case SUMMON_CATEGORY_PUPPET: - 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 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; @@ -2284,19 +2364,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); } } @@ -2484,17 +2565,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 = m_spellInfo->Effects[effIndex].CalcRadius(); int32 duration = m_spellInfo->GetDuration(); // 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->Id, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) + if (!dynObj->CreateDynamicObject(player->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), player, m_spellInfo->Id, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) { delete dynObj; return; @@ -2863,7 +2945,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (m_caster->GetPetGUID()) + if (!unitCaster || unitCaster->GetPetGUID()) return; if (!unitTarget) @@ -2877,21 +2959,21 @@ 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; // "kill" original creature creatureTarget->DespawnOrUnsummon(); - uint8 level = (creatureTarget->getLevel() < (m_caster->getLevel() - 5)) ? (m_caster->getLevel() - 5) : creatureTarget->getLevel(); + uint8 level = (creatureTarget->getLevel() < (unitCaster->getLevel() - 5)) ? (unitCaster->getLevel() - 5) : creatureTarget->getLevel(); // prepare visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); @@ -2903,14 +2985,14 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) pet->SetUInt32Value(UNIT_FIELD_LEVEL, level); // caster have pet now - m_caster->SetMinion(pet, true); + unitCaster->SetMinion(pet, true); pet->InitTalentForLevel(); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { pet->SavePetToDB(PET_SAVE_AS_CURRENT); - m_caster->ToPlayer()->PetSpellInitialize(); + unitCaster->ToPlayer()->PetSpellInitialize(); } } @@ -2920,11 +3002,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 = m_spellInfo->Effects[effIndex].MiscValue; @@ -2986,7 +3068,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); @@ -3033,6 +3115,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()) @@ -3042,7 +3127,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; @@ -3050,11 +3135,11 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) // Hand of Reckoning if (m_spellInfo->Id == 62124) - m_caster->CastSpell(unitTarget, 67485, true); + unitCaster->CastSpell(unitTarget, 67485, true); if (!mgr.IsThreatListEmpty()) // Set threat equal to highest threat currently on target - mgr.MatchUnitThreatToHighestThreat(m_caster); + mgr.MatchUnitThreatToHighestThreat(unitCaster); } void Spell::EffectWeaponDmg(SpellEffIndex effIndex) @@ -3062,6 +3147,9 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET) return; + if (!unitCaster) + return; + if (!unitTarget || !unitTarget->IsAlive()) return; @@ -3093,17 +3181,17 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) // Devastate (player ones) if (m_spellInfo->SpellFamilyFlags[1] & 0x40) { - m_caster->CastSpell(unitTarget, 58567, true); + unitCaster->CastSpell(unitTarget, 58567, true); // 58388 - Glyph of Devastate dummy aura. - if (m_caster->HasAura(58388)) - m_caster->CastSpell(unitTarget, 58567, true); + if (unitCaster->HasAura(58388)) + unitCaster->CastSpell(unitTarget, 58567, true); - if (Aura* aur = unitTarget->GetAura(58567, m_caster->GetGUID())) - fixed_bonus += (aur->GetStackAmount() - 1) * CalculateDamage(EFFECT_2, unitTarget); // subtract 1 so fixed bonus is not applied twice + if (Aura* aur = unitTarget->GetAura(58567, unitCaster->GetGUID())) + fixed_bonus += (aur->GetStackAmount() - 1) * CalculateDamage(EFFECT_2); // subtract 1 so fixed bonus is not applied twice } else if (m_spellInfo->SpellFamilyFlags[0] & 0x8000000) // Mocking Blow { - if (unitTarget->IsImmunedToSpellEffect(m_spellInfo, EFFECT_1, m_caster) || unitTarget->GetTypeId() == TYPEID_PLAYER) + if (unitTarget->IsImmunedToSpellEffect(m_spellInfo, EFFECT_1, unitCaster) || unitTarget->GetTypeId() == TYPEID_PLAYER) { m_damage = 0; return; @@ -3122,8 +3210,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) AddComboPointGain(unitTarget, 1); // 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()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) totalDamagePercentMod *= 1.5f; } @@ -3132,7 +3220,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) { bool found = false; // fast check - if (unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON, m_spellInfo, m_caster)) + if (unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON, m_spellInfo, unitCaster)) found = true; // full aura scan else @@ -3158,8 +3246,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) // Seal of Command Unleashed if (m_spellInfo->Id == 20467) { - spell_bonus += int32(0.08f * m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); - spell_bonus += int32(0.13f * m_caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask())); + spell_bonus += int32(0.08f * unitCaster->GetTotalAttackPowerValue(BASE_ATTACK)); + spell_bonus += int32(0.13f * unitCaster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask())); } break; } @@ -3167,8 +3255,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: @@ -3180,7 +3268,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) // Shred, Maul - Rend and Tear else if (m_spellInfo->SpellFamilyFlags[0] & 0x00008800 && unitTarget->HasAuraState(AURA_STATE_BLEEDING)) { - if (AuraEffect const* rendAndTear = m_caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 2859, 0)) + if (AuraEffect const* rendAndTear = unitCaster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 2859, 0)) AddPct(totalDamagePercentMod, rendAndTear->GetAmount()); } break; @@ -3189,7 +3277,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) { // Kill Shot - bonus damage from Ranged Attack Power if (m_spellInfo->SpellFamilyFlags[1] & 0x800000) - spell_bonus += int32(0.4f * m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)); + spell_bonus += int32(0.4f * unitCaster->GetTotalAttackPowerValue(RANGED_ATTACK)); break; } case SPELLFAMILY_DEATHKNIGHT: @@ -3198,21 +3286,21 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) if (m_spellInfo->SpellFamilyFlags[0] & 0x1) { // Glyph of Plague Strike - if (AuraEffect const* aurEff = m_caster->GetAuraEffect(58657, EFFECT_0)) + if (AuraEffect const* aurEff = unitCaster->GetAuraEffect(58657, EFFECT_0)) AddPct(totalDamagePercentMod, aurEff->GetAmount()); break; } // Blood Strike if (m_spellInfo->SpellFamilyFlags[0] & 0x400000) { - float bonusPct = m_spellInfo->Effects[EFFECT_2].CalcValue() * unitTarget->GetDiseasesByCaster(m_caster->GetGUID()) / 2.0f; + float bonusPct = m_spellInfo->Effects[EFFECT_2].CalcValue() * unitTarget->GetDiseasesByCaster(unitCaster->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); // Glyph of Blood Strike - if (m_caster->GetAuraEffect(59332, EFFECT_0)) + if (unitCaster->GetAuraEffect(59332, EFFECT_0)) if (unitTarget->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) AddPct(totalDamagePercentMod, 20); break; @@ -3221,8 +3309,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) if (m_spellInfo->SpellFamilyFlags[0] & 0x10) { // Glyph of Death Strike - if (AuraEffect const* aurEff = m_caster->GetAuraEffect(59336, EFFECT_0)) - if (uint32 runic = std::min<uint32>(m_caster->GetPower(POWER_RUNIC_POWER), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue())) + if (AuraEffect const* aurEff = unitCaster->GetAuraEffect(59336, EFFECT_0)) + if (uint32 runic = std::min<uint32>(unitCaster->GetPower(POWER_RUNIC_POWER), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue())) AddPct(totalDamagePercentMod, runic); break; } @@ -3231,14 +3319,14 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) { bool consumeDiseases = true; // Annihilation - if (AuraEffect const* aurEff = m_caster->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, 2710, EFFECT_0)) + if (AuraEffect const* aurEff = unitCaster->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, 2710, EFFECT_0)) // Do not consume diseases if roll sucesses if (roll_chance_i(aurEff->GetAmount())) consumeDiseases = false; - float bonusPct = m_spellInfo->Effects[EFFECT_2].CalcValue() * unitTarget->GetDiseasesByCaster(m_caster->GetGUID(), consumeDiseases) / 2.0f; + float bonusPct = m_spellInfo->Effects[EFFECT_2].CalcValue() * unitTarget->GetDiseasesByCaster(unitCaster->GetGUID(), consumeDiseases) / 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); break; @@ -3246,15 +3334,15 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) // Blood-Caked Strike - Blood-Caked Blade if (m_spellInfo->SpellIconID == 1736) { - AddPct(totalDamagePercentMod, unitTarget->GetDiseasesByCaster(m_caster->GetGUID()) * 50.0f); + AddPct(totalDamagePercentMod, unitTarget->GetDiseasesByCaster(unitCaster->GetGUID()) * 50.0f); break; } // Heart Strike if (m_spellInfo->SpellFamilyFlags[0] & 0x1000000) { - float bonusPct = m_spellInfo->Effects[EFFECT_2].CalcValue() * unitTarget->GetDiseasesByCaster(m_caster->GetGUID()); + float bonusPct = m_spellInfo->Effects[EFFECT_2].CalcValue() * unitTarget->GetDiseasesByCaster(unitCaster->GetGUID()); // 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); @@ -3272,14 +3360,14 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) { case SPELL_EFFECT_WEAPON_DAMAGE: case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - fixed_bonus += CalculateDamage(j, unitTarget); + fixed_bonus += CalculateDamage(j); break; case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: - fixed_bonus += CalculateDamage(j, unitTarget); + fixed_bonus += CalculateDamage(j); normalized = true; break; case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - ApplyPct(weaponDamagePercentMod, CalculateDamage(j, unitTarget)); + ApplyPct(weaponDamagePercentMod, CalculateDamage(j)); break; default: break; // not weapon damage effect, just skip @@ -3300,14 +3388,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 (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) @@ -3333,15 +3421,15 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) weaponDamage = int32(weaponDamage * totalDamagePercentMod); // apply spellmod to Done damage - if (Player* modOwner = m_caster->GetSpellModOwner()) + if (Player* modOwner = unitCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DAMAGE, weaponDamage); // prevent negative damage weaponDamage = std::max(weaponDamage, 0); // Add melee damage bonuses (also check for negative) - weaponDamage = m_caster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, m_spellInfo); - m_damage += unitTarget->MeleeDamageBonusTaken(m_caster, weaponDamage, m_attackType, m_spellInfo); + weaponDamage = unitCaster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, m_spellInfo); + m_damage += unitTarget->MeleeDamageBonusTaken(unitCaster, weaponDamage, m_attackType, m_spellInfo); } void Spell::EffectThreat(SpellEffIndex /*effIndex*/) @@ -3349,13 +3437,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*/) @@ -3363,6 +3454,9 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; + if (!unitCaster) + return; + if (!unitTarget || !unitTarget->IsAlive()) return; @@ -3370,7 +3464,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(); @@ -3400,11 +3494,11 @@ void Spell::EffectInterruptCast(SpellEffIndex effIndex) && ((i == CURRENT_GENERIC_SPELL && curSpellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT) || (i == CURRENT_CHANNELED_SPELL && curSpellInfo->ChannelInterruptFlags & CHANNEL_INTERRUPT_FLAG_INTERRUPT))) { - if (m_originalCaster) + if (unitCaster) { int32 duration = m_spellInfo->GetDuration(); unitTarget->GetSpellHistory()->LockSpellSchool(curSpellInfo->GetSchoolMask(), unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effIndex)); - Unit::ProcSkillsAndAuras(m_originalCaster, 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); + 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); } ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo->Id); unitTarget->InterruptSpell(CurrentSpellTypes(i), false); @@ -3471,7 +3565,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: @@ -3481,6 +3575,9 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) // Glyph of Scourge Strike case 69961: { + if (!unitCaster) + return; + Unit::AuraEffectList const& mPeriodic = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); for (Unit::AuraEffectList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) { @@ -3488,7 +3585,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) SpellInfo const* spellInfo = aurEff->GetSpellInfo(); // search our Blood Plague and Frost Fever on target if (spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && spellInfo->SpellFamilyFlags[2] & 0x2 && - aurEff->GetCasterGUID() == m_caster->GetGUID()) + aurEff->GetCasterGUID() == unitCaster->GetGUID()) { uint32 countMin = aurEff->GetBase()->GetMaxDuration(); uint32 countMax = spellInfo->GetMaxDuration(); @@ -3496,7 +3593,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) // this Glyph countMax += 9000; // talent Epidemic - if (AuraEffect const* epidemic = m_caster->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_DEATHKNIGHT, 234, EFFECT_0)) + if (AuraEffect const* epidemic = unitCaster->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_DEATHKNIGHT, 234, EFFECT_0)) countMax += epidemic->GetAmount(); if (countMin < countMax) @@ -3519,8 +3616,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; } @@ -3655,14 +3751,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 @@ -3736,8 +3831,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) @@ -3793,14 +3888,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->Effects[EFFECT_0].CalcValue()); } } @@ -3809,7 +3907,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; @@ -3828,11 +3926,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; @@ -3987,29 +4085,29 @@ void Spell::EffectDuel(SpellEffIndex effIndex) 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() }; - Map* map = m_caster->GetMap(); + Map* map = caster->GetMap(); QuaternionData rot = QuaternionData::fromEulerAnglesZYX(pos.GetOrientation(), 0.f, 0.f); - if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id, map, m_caster->GetPhaseMask(), pos, rot, 0, GO_STATE_READY)) + if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id, map, caster->GetPhaseMask(), pos, rot, 0, GO_STATE_READY)) { delete pGameObj; return; } - pGameObj->SetFaction(m_caster->GetFaction()); - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()+1); + pGameObj->SetFaction(caster->GetFaction()); + pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, caster->getLevel() + 1); int32 duration = m_spellInfo->GetDuration(); pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); ExecuteLogEffectSummonObject(effIndex, pGameObj); - m_caster->AddGameObject(pGameObj); + caster->AddGameObject(pGameObj); map->AddToMap(pGameObj); //END @@ -4095,10 +4193,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*/) @@ -4140,7 +4241,7 @@ void Spell::EffectApplyGlyph(SpellEffIndex effIndex) case 4: minLevel = 70; break; case 5: minLevel = 80; break; } - if (minLevel && m_caster->getLevel() < minLevel) + if (minLevel && player->getLevel() < minLevel) { SendCastResult(SPELL_FAILED_GLYPH_SOCKET_LOCKED); return; @@ -4170,7 +4271,7 @@ void Spell::EffectApplyGlyph(SpellEffIndex effIndex) } } - player->CastSpell(m_caster, gp->SpellId, true); + player->CastSpell(player, gp->SpellId, true); player->SetGlyph(m_glyphIndex, glyph); player->SendTalentsInfoData(false); } @@ -4319,50 +4420,53 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; + if (!unitCaster) + return; + uint32 go_id = m_spellInfo->Effects[effIndex].MiscValue; uint8 slot = m_spellInfo->Effects[effIndex].Effect - SPELL_EFFECT_SUMMON_OBJECT_SLOT1; - if (ObjectGuid guid = m_caster->m_ObjectSlot[slot]) + if (ObjectGuid guid = unitCaster->m_ObjectSlot[slot]) { - 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(); } GameObject* go = new GameObject(); - float x, y, z; // If dest location if present if (m_targets.HasDst()) 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(); - QuaternionData rot = QuaternionData::fromEulerAnglesZYX(m_caster->GetOrientation(), 0.f, 0.f); - if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), go_id, map, m_caster->GetPhaseMask(), Position(x, y, z, m_caster->GetOrientation()), rot, 255, GO_STATE_READY)) + Map* map = unitCaster->GetMap(); + QuaternionData rot = QuaternionData::fromEulerAnglesZYX(unitCaster->GetOrientation(), 0.f, 0.f); + if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), go_id, map, unitCaster->GetPhaseMask(), Position(x, y, z, unitCaster->GetOrientation()), rot, 255, GO_STATE_READY)) { delete go; return; } - //pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); + go->SetFaction(unitCaster->GetFaction()); + go->SetUInt32Value(GAMEOBJECT_LEVEL, unitCaster->getLevel()); int32 duration = m_spellInfo->GetDuration(); - 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) @@ -4491,23 +4595,26 @@ 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 - WorldPacket data(SMSG_BREAK_TARGET, m_caster->GetPackGUID().size()); - data << m_caster->GetPackGUID(); - Trinity::MessageDistDelivererToHostile notifierBreak(m_caster, &data, dist); - Cell::VisitWorldObjects(m_caster, notifierBreak, dist); + WorldPacket data(SMSG_BREAK_TARGET, unitCaster->GetPackGUID().size()); + data << unitCaster->GetPackGUID(); + Trinity::MessageDistDelivererToHostile notifierBreak(unitCaster, &data, dist); + Cell::VisitWorldObjects(unitCaster, notifierBreak, dist); // and selection data.Initialize(SMSG_CLEAR_TARGET, 8); - data << uint64(m_caster->GetGUID()); - Trinity::MessageDistDelivererToHostile notifierClear(m_caster, &data, dist); - Cell::VisitWorldObjects(m_caster, notifierClear, dist); + data << uint64(unitCaster->GetGUID()); + Trinity::MessageDistDelivererToHostile notifierClear(unitCaster, &data, dist); + Cell::VisitWorldObjects(unitCaster, 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); @@ -4520,11 +4627,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; @@ -4539,12 +4643,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); @@ -4562,7 +4665,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(); @@ -4572,14 +4677,13 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/) creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - m_caster->ToPlayer()->SendLoot(creature->GetGUID(), LOOT_SKINNING); - - int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel-10)*10 : targetLevel*5; + player->SendLoot(creature->GetGUID(), LOOT_SKINNING); - int32 skillValue = m_caster->ToPlayer()->GetPureSkillValue(skill); + int32 const reqValue = targetLevel < 10 ? 0 : (targetLevel < 20 ? (targetLevel - 10) * 10 : targetLevel * 5); + int32 const skillValue = player->GetPureSkillValue(skill); // Double chances for elites - m_caster->ToPlayer()->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1); + player->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1); } void Spell::EffectCharge(SpellEffIndex /*effIndex*/) @@ -4587,11 +4691,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; // Spell is not using explicit target - no generated path @@ -4599,17 +4706,17 @@ void Spell::EffectCharge(SpellEffIndex /*effIndex*/) { //unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ); Position pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetCombatReach(), unitTarget->GetRelativeAngle(m_caster)); - m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed); + unitCaster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed); } else - m_caster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed); + unitCaster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed); } if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET) { // 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); } } @@ -4618,18 +4725,21 @@ void Spell::EffectChargeDest(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH) return; + if (!unitCaster) + return; + if (m_targets.HasDst()) { 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); } } @@ -4641,7 +4751,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; @@ -4669,9 +4779,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); } @@ -4869,13 +4977,16 @@ 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->GetUInt32Value(UNIT_CREATED_BY_SPELL); @@ -4883,7 +4994,7 @@ void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/) if (spellInfo) { mana += spellInfo->ManaCost; - mana += int32(CalculatePct(m_caster->GetCreateMana(), spellInfo->ManaCostPercentage)); + mana += int32(CalculatePct(unitCaster->GetCreateMana(), spellInfo->ManaCostPercentage)); } totem->ToTotem()->UnSummon(); } @@ -4893,7 +5004,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); } } @@ -4960,10 +5071,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) @@ -4971,10 +5082,11 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - uint32 name_id = m_spellInfo->Effects[effIndex].MiscValue; + if (!unitCaster) + return; + uint32 name_id = m_spellInfo->Effects[effIndex].MiscValue; GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(name_id); - if (!goinfo) { TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u) does not exist and is not created by spell (ID: %u) cast.", name_id, m_spellInfo->Id); @@ -4988,8 +5100,8 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) //FIXME: this can be better check for most objects but still hack else if (m_spellInfo->Effects[effIndex].HasRadius() && m_spellInfo->Speed == 0) { - float dis = m_spellInfo->Effects[effIndex].CalcRadius(m_originalCaster); - m_caster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); + float dis = m_spellInfo->Effects[effIndex].CalcRadius(unitCaster); + unitCaster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); } else { @@ -4998,32 +5110,31 @@ 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_SUMMONING_RITUAL) - m_caster->GetPosition(fx, fy, fz); + unitCaster->GetPosition(fx, fy, fz); GameObject* pGameObj = new GameObject; - Position pos = { fx, fy, fz, m_caster->GetOrientation() }; - QuaternionData rot = QuaternionData::fromEulerAnglesZYX(m_caster->GetOrientation(), 0.f, 0.f); - if (!pGameObj->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), name_id, cMap, m_caster->GetPhaseMask(), pos, rot, 255, GO_STATE_READY)) + Position pos = { fx, fy, fz, unitCaster->GetOrientation() }; + QuaternionData rot = QuaternionData::fromEulerAnglesZYX(unitCaster->GetOrientation(), 0.f, 0.f); + if (!pGameObj->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), name_id, cMap, unitCaster->GetPhaseMask(), pos, rot, 255, GO_STATE_READY)) { delete pGameObj; return; } int32 duration = m_spellInfo->GetDuration(); - switch (goinfo->type) { case GAMEOBJECT_TYPE_FISHINGNODE: { - m_caster->SetChannelObjectGuid(pGameObj->GetGUID()); - m_caster->AddGameObject(pGameObj); // will removed at spell cancel + unitCaster->SetChannelObjectGuid(pGameObj->GetGUID()); + unitCaster->AddGameObject(pGameObj); // 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) @@ -5041,15 +5152,15 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) } case GAMEOBJECT_TYPE_SUMMONING_RITUAL: { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) { - pGameObj->AddUniqueUse(m_caster->ToPlayer()); - m_caster->AddGameObject(pGameObj); // will be removed at spell cancel + pGameObj->AddUniqueUse(unitCaster->ToPlayer()); + unitCaster->AddGameObject(pGameObj); // will be removed at spell cancel } break; } case GAMEOBJECT_TYPE_DUEL_ARBITER: // 52991 - m_caster->AddGameObject(pGameObj); + unitCaster->AddGameObject(pGameObj); break; case GAMEOBJECT_TYPE_FISHINGHOLE: case GAMEOBJECT_TYPE_CHEST: @@ -5059,15 +5170,15 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); - pGameObj->SetOwnerGUID(m_caster->GetGUID()); + pGameObj->SetOwnerGUID(unitCaster->GetGUID()); - //pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); + //pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, unitCaster->getLevel()); pGameObj->SetSpellId(m_spellInfo->Id); ExecuteLogEffectSummonObject(effIndex, pGameObj); TC_LOG_DEBUG("spells", "AddObject at SpellEfects.cpp EffectTransmitted"); - //m_caster->AddGameObject(pGameObj); + //unitCaster->AddGameObject(pGameObj); //m_ObjToDel.push_back(pGameObj); cMap->AddToMap(pGameObj); @@ -5075,9 +5186,9 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) if (GameObject* linkedTrap = pGameObj->GetLinkedTrap()) { linkedTrap->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); - //linkedTrap->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); + //linkedTrap->SetUInt32Value(GAMEOBJECT_LEVEL, unitCaster->getLevel()); linkedTrap->SetSpellId(m_spellInfo->Id); - linkedTrap->SetOwnerGUID(m_caster->GetGUID()); + linkedTrap->SetOwnerGUID(unitCaster->GetGUID()); ExecuteLogEffectSummonObject(effIndex, linkedTrap); } @@ -5470,8 +5581,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*/) @@ -5482,15 +5596,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*/) @@ -5509,41 +5619,38 @@ 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(m_spellInfo->Effects[effIndex].MiscValue), player, true); + gameObjTarget->SetDestructibleState(GameObjectDestructibleState(m_spellInfo->Effects[effIndex].MiscValue), m_caster, true); } void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* properties, uint32 numGuardians) { - 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->RequiredSkill == 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->GetDuration(); - if (Player* modOwner = m_originalCaster->GetSpellModOwner()) + if (Player* modOwner = unitCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); //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; @@ -5551,9 +5658,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); + TempSummon* summon = map->SummonCreature(entry, pos, properties, duration, unitCaster, m_spellInfo->Id); if (!summon) return; @@ -5561,14 +5668,14 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* ((Guardian*)summon)->InitStatsForLevel(level); if (properties && properties->Category == 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) { - if (uint32 weapon = m_caster->GetUInt32Value(PLAYER_VISIBLE_ITEM_16_ENTRYID)) + if (uint32 weapon = unitCaster->GetUInt32Value(PLAYER_VISIBLE_ITEM_16_ENTRYID)) { summon->SetDisplayId(11686); // modelid2 summon->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, weapon); @@ -5687,16 +5794,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 = m_spellInfo->Effects[effIndex].MiscValue + 132; uint32 n_buttons = m_spellInfo->Effects[effIndex].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; @@ -5710,18 +5817,18 @@ 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_ATTR7_SUMMON_PLAYER_TOTEM)) continue; - uint32 cost = spellInfo->CalcPowerCost(m_caster, spellInfo->GetSchoolMask()); - if (m_caster->GetPower(POWER_MANA) < cost) + uint32 cost = spellInfo->CalcPowerCost(player, spellInfo->GetSchoolMask()); + if (player->GetPower(POWER_MANA) < cost) continue; TriggerCastFlags triggerFlags = TriggerCastFlags(TRIGGERED_IGNORE_GCD | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY); - m_caster->CastSpell(m_caster, spell_id, triggerFlags); + player->CastSpell(player, spell_id, triggerFlags); } } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index cf8ef564413..63dadd6ede3 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -395,16 +395,20 @@ bool SpellEffectInfo::IsUnitOwnedAuraEffect() const return IsAreaAuraEffect() || Effect == SPELL_EFFECT_APPLY_AURA; } -int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const* /*target*/) const +int32 SpellEffectInfo::CalcValue(WorldObject const* caster /*= nullptr*/, int32 const* bp /*= nullptr*/) const { float basePointsPerLevel = RealPointsPerLevel; int32 basePoints = bp ? *bp : BasePoints; int32 randomPoints = int32(DieSides); + Unit const* casterUnit = nullptr; + if (caster) + casterUnit = caster->ToUnit(); + // base amount modification based on spell lvl vs caster lvl - 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); else if (level < int32(_spellInfo->BaseLevel)) @@ -433,17 +437,21 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const float value = float(basePoints); // random damage - if (caster) + if (casterUnit) { // bonus amount from combo points - if (uint8 comboPoints = caster->GetComboPoints()) + if (uint8 comboPoints = casterUnit->GetComboPoints()) value += PointsPerComboPoint * comboPoints; + } + if (caster) value = caster->ApplyEffectModifiers(_spellInfo, _effIndex, value); + if (casterUnit) + { // amount multiplication based on caster's level - if (!caster->IsControlledByPlayer() && - _spellInfo->SpellLevel && _spellInfo->SpellLevel != caster->getLevel() && + if (!casterUnit->IsControlledByPlayer() && + _spellInfo->SpellLevel && _spellInfo->SpellLevel != casterUnit->getLevel() && !basePointsPerLevel && _spellInfo->HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)) { bool canEffectScale = false; @@ -487,7 +495,7 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const if (canEffectScale) { GtNPCManaCostScalerEntry const* spellScaler = sGtNPCManaCostScalerStore.LookupEntry(_spellInfo->SpellLevel - 1); - GtNPCManaCostScalerEntry const* casterScaler = sGtNPCManaCostScalerStore.LookupEntry(caster->getLevel() - 1); + GtNPCManaCostScalerEntry const* casterScaler = sGtNPCManaCostScalerStore.LookupEntry(casterUnit->getLevel() - 1); if (spellScaler && casterScaler) value *= casterScaler->ratio / spellScaler->ratio; } @@ -505,7 +513,7 @@ int32 SpellEffectInfo::CalcBaseValue(int32 value) const return value - 1; } -float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const +float SpellEffectInfo::CalcValueMultiplier(WorldObject* caster, Spell* spell /*= nullptr*/) const { float multiplier = ValueMultiplier; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr)) @@ -514,7 +522,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 = DamageMultiplier * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr)) @@ -528,7 +536,7 @@ bool SpellEffectInfo::HasRadius() const return RadiusEntry != nullptr; } -float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const +float SpellEffectInfo::CalcRadius(WorldObject* caster /*= nullptr*/, Spell* spell /*= nullptr*/) const { if (!HasRadius()) return 0.0f; @@ -536,8 +544,11 @@ float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const float radius = RadiusEntry->RadiusMin; if (caster) { - radius += RadiusEntry->RadiusPerLevel * caster->getLevel(); + if (Unit* casterUnit = caster->ToUnit()) + radius += RadiusEntry->RadiusPerLevel * casterUnit->getLevel(); + radius = std::min(radius, RadiusEntry->RadiusMax); + if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_RADIUS, radius, spell); } @@ -1619,7 +1630,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; @@ -1721,7 +1732,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; @@ -1741,13 +1752,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 (TargetAuraStateNot && unitTarget->HasAuraState(AuraStateType(TargetAuraStateNot), this, caster)) - return SPELL_FAILED_TARGET_AURASTATE; + if (TargetAuraStateNot && unitTarget->HasAuraState(AuraStateType(TargetAuraStateNot), this, unitCaster)) + return SPELL_FAILED_TARGET_AURASTATE; + } } if (TargetAuraSpell && !unitTarget->HasAura(sSpellMgr->GetSpellIdForDifficulty(TargetAuraSpell, caster))) @@ -1763,7 +1777,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) @@ -1778,19 +1792,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; } @@ -3030,7 +3045,7 @@ uint32 SpellInfo::GetAllowedMechanicMask() const return _allowedMechanicMask; } -float SpellInfo::GetMinRange(bool positive) const +float SpellInfo::GetMinRange(bool positive /*= false*/) const { if (!RangeEntry) return 0.0f; @@ -3039,7 +3054,7 @@ float SpellInfo::GetMinRange(bool positive) const return RangeEntry->minRangeHostile; } -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; @@ -3132,17 +3147,22 @@ uint32 SpellInfo::GetRecoveryTime() const return RecoveryTime > CategoryRecoveryTime ? RecoveryTime : CategoryRecoveryTime; } -int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, Spell* spell) const +int32 SpellInfo::CalcPowerCost(WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell) const { + // gameobject casts don't use power + Unit const* unitCaster = caster->ToUnit(); + if (!unitCaster) + return 0; + // Spell drain all exist power on cast (Only paladin lay of Hands) if (HasAttribute(SPELL_ATTR1_DRAIN_ALL_POWER)) { // If power type - health drain all if (PowerType == POWER_HEALTH) - return caster->GetHealth(); + return unitCaster->GetHealth(); // Else drain all power if (PowerType < MAX_POWERS) - return caster->GetPower(Powers(PowerType)); + return unitCaster->GetPower(Powers(PowerType)); TC_LOG_ERROR("spells", "SpellInfo::CalcPowerCost: Unknown power type '%d' in spell %d", PowerType, Id); return 0; } @@ -3156,16 +3176,16 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S { // health as power used case POWER_HEALTH: - powerCost += int32(CalculatePct(caster->GetCreateHealth(), ManaCostPercentage)); + powerCost += int32(CalculatePct(unitCaster->GetCreateHealth(), ManaCostPercentage)); break; case POWER_MANA: - powerCost += int32(CalculatePct(caster->GetCreateMana(), ManaCostPercentage)); + powerCost += int32(CalculatePct(unitCaster->GetCreateMana(), ManaCostPercentage)); break; case POWER_RAGE: case POWER_FOCUS: case POWER_ENERGY: case POWER_HAPPINESS: - powerCost += int32(CalculatePct(caster->GetMaxPower(Powers(PowerType)), ManaCostPercentage)); + powerCost += int32(CalculatePct(unitCaster->GetMaxPower(Powers(PowerType)), ManaCostPercentage)); break; case POWER_RUNE: case POWER_RUNIC_POWER: @@ -3178,37 +3198,37 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S } SpellSchools school = GetFirstSchoolInMask(schoolMask); // Flat mod from caster auras by spell school - powerCost += caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school); + powerCost += unitCaster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school); // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) if (HasAttribute(SPELL_ATTR4_SPELL_VS_EXTEND_COST)) { uint32 speed = 0; - if (SpellShapeshiftEntry const* ss = sSpellShapeshiftStore.LookupEntry(caster->GetShapeshiftForm())) + if (SpellShapeshiftEntry const* ss = sSpellShapeshiftStore.LookupEntry(unitCaster->GetShapeshiftForm())) speed = ss->attackSpeed; else - speed = caster->GetAttackTime(GetAttackType()); + speed = unitCaster->GetAttackTime(GetAttackType()); powerCost += speed / 100; } // Apply cost mod by spell - if (Player* modOwner = caster->GetSpellModOwner()) + if (Player* modOwner = unitCaster->GetSpellModOwner()) modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost, spell); - if (!caster->IsControlledByPlayer()) + if (!unitCaster->IsControlledByPlayer()) { if (HasAttribute(SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)) { GtNPCManaCostScalerEntry const* spellScaler = sGtNPCManaCostScalerStore.LookupEntry(SpellLevel - 1); - GtNPCManaCostScalerEntry const* casterScaler = sGtNPCManaCostScalerStore.LookupEntry(caster->getLevel() - 1); + GtNPCManaCostScalerEntry const* casterScaler = sGtNPCManaCostScalerStore.LookupEntry(unitCaster->getLevel() - 1); if (spellScaler && casterScaler) powerCost *= casterScaler->ratio / spellScaler->ratio; } } // PCT mod from user auras by school - powerCost = int32(powerCost * (1.0f + caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER + school))); + powerCost = int32(powerCost * (1.0f + unitCaster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER + school))); if (powerCost < 0) powerCost = 0; return powerCost; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 92b31f6af02..a0089b7eb7f 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -274,13 +274,13 @@ public: bool IsAreaAuraEffect() const; bool IsUnitOwnedAuraEffect() const; - int32 CalcValue(Unit const* caster = nullptr, int32 const* basePoints = nullptr, Unit const* target = nullptr) const; + int32 CalcValue(WorldObject const* caster = nullptr, int32 const* basePoints = nullptr) const; int32 CalcBaseValue(int32 value) const; - float CalcValueMultiplier(Unit* caster, Spell* spell = nullptr) const; - float CalcDamageMultiplier(Unit* caster, Spell* spell = nullptr) const; + float CalcValueMultiplier(WorldObject* caster, Spell* spell = nullptr) const; + float CalcDamageMultiplier(WorldObject* caster, Spell* spell = nullptr) const; bool HasRadius() 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; @@ -475,8 +475,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, bool strict = true) 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; @@ -494,7 +494,7 @@ 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 GetDuration() const; int32 GetMaxDuration() const; @@ -504,7 +504,7 @@ class TC_GAME_API SpellInfo uint32 CalcCastTime(Spell* spell = nullptr) const; uint32 GetRecoveryTime() const; - int32 CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; + int32 CalcPowerCost(WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; bool IsRanked() const; uint8 GetRank() const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index e85989c6183..bea1c1b5da3 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -174,7 +174,7 @@ void SpellMgr::SetSpellDifficultyId(uint32 spellId, uint32 id) mSpellDifficultySearcherMap[spellId] = id; } -uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit const* caster) const +uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, WorldObject const* caster) const { if (!GetSpellInfo(spellId)) return spellId; @@ -216,7 +216,7 @@ uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit const* caster) con return uint32(difficultyEntry->SpellID[mode]); } -SpellInfo const* SpellMgr::GetSpellForDifficultyFromSpell(SpellInfo const* spell, Unit const* caster) const +SpellInfo const* SpellMgr::GetSpellForDifficultyFromSpell(SpellInfo const* spell, WorldObject const* caster) const { if (!spell) return nullptr; diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 134ea36b6ba..698234e2da2 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -591,8 +591,8 @@ class TC_GAME_API SpellMgr // Spell difficulty uint32 GetSpellDifficultyId(uint32 spellId) const; void SetSpellDifficultyId(uint32 spellId, uint32 id); - uint32 GetSpellIdForDifficulty(uint32 spellId, Unit const* caster) const; - SpellInfo const* GetSpellForDifficultyFromSpell(SpellInfo const* spell, Unit const* caster) const; + uint32 GetSpellIdForDifficulty(uint32 spellId, WorldObject const* caster) const; + SpellInfo const* GetSpellForDifficultyFromSpell(SpellInfo const* spell, WorldObject const* caster) const; // Spell Ranks table SpellChainNode const* GetSpellChainNode(uint32 spell_id) const; diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index a7f852eb884..c923a3c06c6 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -425,12 +425,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 @@ -1046,7 +1051,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 3b5385eefa9..6b79c222d6b 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -377,6 +377,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; @@ -855,6 +856,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 acf05493000..fa0015c02d2 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 1d58d2bf336..7286ae41ca5 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -1308,7 +1308,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_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 238637a13f2..deddf88b3dc 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -691,7 +691,7 @@ class spell_dk_corpse_explosion : public SpellScriptLoader } else if (effIndex == EFFECT_1) { - args.AddSpellBP0(GetSpell()->CalculateDamage(EFFECT_0, nullptr)); + args.AddSpellBP0(GetSpell()->CalculateDamage(EFFECT_0)); GetCaster()->CastSpell(target, GetEffectValue(), args); } } @@ -1136,7 +1136,7 @@ class spell_dk_death_strike : public SpellScriptLoader int32 bp = int32(count * caster->CountPctFromMaxHealth(int32(GetSpellInfo()->Effects[EFFECT_0].DamageMultiplier))); // Improved Death Strike if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_DEATHKNIGHT, DK_ICON_ID_IMPROVED_DEATH_STRIKE, 0)) - AddPct(bp, caster->CalculateSpellDamage(caster, aurEff->GetSpellInfo(), 2)); + AddPct(bp, caster->CalculateSpellDamage(aurEff->GetSpellInfo(), EFFECT_2)); // @todo castspell refactor note: this is not triggered - is this intended?? CastSpellExtraArgs args; @@ -1277,7 +1277,7 @@ class spell_dk_hysteria : public AuraScript void PeriodicTick(AuraEffect const* aurEff) { - uint32 const damage = GetTarget()->CountPctFromMaxHealth(GetTarget()->CalculateSpellDamage(nullptr, GetSpellInfo(), aurEff->GetEffIndex())); + uint32 const damage = GetTarget()->CountPctFromMaxHealth(GetTarget()->CalculateSpellDamage(GetSpellInfo(), aurEff->GetEffIndex())); Unit::DealDamage(GetTarget(), GetTarget(), damage, nullptr, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); } diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index 9f8775e9bb9..1a0ffe8318a 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -505,7 +505,7 @@ class spell_dru_frenzied_regeneration : public AuraScript return; int32 const mod = std::min(static_cast<int32>(rage), 100); - int32 const regen = CalculatePct(GetTarget()->GetMaxHealth(), GetTarget()->CalculateSpellDamage(nullptr, GetSpellInfo(), EFFECT_1) * mod / 100.f); + int32 const regen = CalculatePct(GetTarget()->GetMaxHealth(), GetTarget()->CalculateSpellDamage(GetSpellInfo(), EFFECT_1) * mod / 100.f); CastSpellExtraArgs args(aurEff); args.AddSpellBP0(regen); GetTarget()->CastSpell(nullptr, SPELL_DRUID_FRENZIED_REGENERATION_HEAL, args); diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 7e4d4b029a9..b27267571d8 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -1369,7 +1369,7 @@ class spell_hun_sniper_training : public SpellScriptLoader if (Player* playerTarget = GetUnitOwner()->ToPlayer()) { int32 baseAmount = aurEff->GetBaseAmount(); - int32 amount = playerTarget->CalculateSpellDamage(playerTarget, GetSpellInfo(), aurEff->GetEffIndex(), &baseAmount); + int32 amount = playerTarget->CalculateSpellDamage(GetSpellInfo(), aurEff->GetEffIndex(), &baseAmount); GetEffect(EFFECT_0)->SetAmount(amount); } } @@ -1381,7 +1381,7 @@ class spell_hun_sniper_training : public SpellScriptLoader { int32 baseAmount = aurEff->GetBaseAmount(); int32 amount = playerTarget->isMoving() ? - playerTarget->CalculateSpellDamage(playerTarget, GetSpellInfo(), aurEff->GetEffIndex(), &baseAmount) : + playerTarget->CalculateSpellDamage(GetSpellInfo(), aurEff->GetEffIndex(), &baseAmount) : aurEff->GetAmount() - 1; aurEff->SetAmount(amount); } diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index 2746dc65144..316bb1fe3f3 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -425,7 +425,7 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader case CREATURE_FAMILY_VOIDWALKER: { SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER); - int32 hp = targetCreature->CountPctFromMaxHealth(GetCaster()->CalculateSpellDamage(targetCreature, spellInfo, 0)); + int32 hp = targetCreature->CountPctFromMaxHealth(GetCaster()->CalculateSpellDamage(spellInfo, EFFECT_0)); CastSpellExtraArgs args(TRIGGERED_FULL_MASK); args.AddSpellBP0(hp); targetCreature->CastSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER, args); @@ -1475,15 +1475,17 @@ class spell_warl_unstable_affliction : public SpellScriptLoader { if (AuraEffect const* aurEff = GetEffect(EFFECT_0)) { - 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); + } } } } |