From b6fac86b71dfb5851390fc950e190b122ac7e775 Mon Sep 17 00:00:00 2001 From: ariel- Date: Sat, 3 Sep 2022 08:06:23 +0200 Subject: [PATCH] Core/Spells: rework part 5: GameObject casting --- src/server/game/AI/CoreAI/GameObjectAI.h | 17 +- src/server/game/AI/CreatureAI.h | 6 +- .../game/AI/ScriptedAI/ScriptedCreature.h | 45 +- src/server/game/AI/SmartScripts/SmartAI.cpp | 4 +- src/server/game/AI/SmartScripts/SmartAI.h | 2 +- .../game/Achievements/AchievementMgr.cpp | 92 +- src/server/game/Achievements/AchievementMgr.h | 16 +- src/server/game/Conditions/DisableMgr.cpp | 15 +- src/server/game/Conditions/DisableMgr.h | 15 +- .../game/Entities/AreaTrigger/AreaTrigger.h | 3 + src/server/game/Entities/Corpse/Corpse.cpp | 9 + src/server/game/Entities/Corpse/Corpse.h | 3 +- .../game/Entities/Creature/Creature.cpp | 10 +- src/server/game/Entities/Creature/Creature.h | 4 +- .../Entities/DynamicObject/DynamicObject.cpp | 6 + .../Entities/DynamicObject/DynamicObject.h | 2 + .../game/Entities/GameObject/GameObject.cpp | 101 +- .../game/Entities/GameObject/GameObject.h | 13 +- src/server/game/Entities/Object/Object.cpp | 924 ++++++++++++- src/server/game/Entities/Object/Object.h | 64 +- src/server/game/Entities/Player/Player.cpp | 98 +- src/server/game/Entities/Player/Player.h | 17 +- src/server/game/Entities/Totem/Totem.cpp | 2 +- src/server/game/Entities/Totem/Totem.h | 2 +- src/server/game/Entities/Unit/Unit.cpp | 1139 +++------------- src/server/game/Entities/Unit/Unit.h | 89 +- .../game/Grids/Notifiers/GridNotifiers.cpp | 6 +- .../game/Grids/Notifiers/GridNotifiers.h | 26 +- src/server/game/Guilds/Guild.cpp | 2 +- src/server/game/Guilds/Guild.h | 2 +- src/server/game/Spells/Auras/SpellAuras.cpp | 26 +- src/server/game/Spells/Spell.cpp | 1159 ++++++++++------- src/server/game/Spells/Spell.h | 59 +- src/server/game/Spells/SpellDefines.h | 2 +- src/server/game/Spells/SpellEffects.cpp | 760 ++++++----- src/server/game/Spells/SpellInfo.cpp | 172 +-- src/server/game/Spells/SpellInfo.h | 24 +- src/server/game/Spells/SpellMgr.cpp | 4 +- src/server/game/Spells/SpellMgr.h | 4 +- src/server/game/Spells/SpellScript.cpp | 20 +- src/server/game/Spells/SpellScript.h | 3 + .../IcecrownCitadel/icecrown_citadel.cpp | 6 +- .../Ulduar/Ulduar/boss_flame_leviathan.cpp | 2 +- 43 files changed, 2638 insertions(+), 2337 deletions(-) diff --git a/src/server/game/AI/CoreAI/GameObjectAI.h b/src/server/game/AI/CoreAI/GameObjectAI.h index 71dc70829d..df95e1173e 100644 --- a/src/server/game/AI/CoreAI/GameObjectAI.h +++ b/src/server/game/AI/CoreAI/GameObjectAI.h @@ -24,8 +24,9 @@ #include "QuestDef.h" class GameObject; -class Unit; class SpellInfo; +class Unit; +class WorldObject; class TC_GAME_API GameObjectAI { @@ -72,8 +73,9 @@ 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*/) { } @@ -84,7 +86,14 @@ class TC_GAME_API GameObjectAI virtual void OnLootStateChanged(uint32 /*state*/, Unit* /*unit*/) { } virtual void OnStateChanged(uint32 /*state*/) { } virtual void EventInform(uint32 /*eventId*/) { } - virtual void SpellHit(Unit* /*unit*/, SpellInfo const* /*spellInfo*/) { } + + // Called when hit by a spell + virtual void SpellHit(Unit* /*caster*/, SpellInfo const* /*spellInfo*/) { } + virtual void SpellHit(GameObject* /*caster*/, SpellInfo const* /*spellInfo*/) { } + + // Called when spell hits a target + virtual void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spellInfo*/) { } + virtual void SpellHitTarget(GameObject* /*target*/, SpellInfo const* /*spellInfo*/) { } }; class TC_GAME_API NullGameObjectAI : public GameObjectAI diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index acc55f601b..432c3a4e41 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -142,10 +142,12 @@ class TC_GAME_API CreatureAI : public UnitAI virtual void JustUnregisteredDynObject(DynamicObject* /*dynObject*/) { } // 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*/) { } // Should return true if the NPC is currently being escorted 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 54b0a840f7..4d2c0837ed 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -140,36 +140,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 // ************* @@ -181,12 +154,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; @@ -274,8 +241,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 inline - const T& DUNGEON_MODE(const T& normal5, const T& heroic10) const + template + inline T const& DUNGEON_MODE(T const& normal5, T const& heroic10) const { switch (_difficulty) { @@ -290,8 +257,8 @@ struct TC_GAME_API ScriptedAI : public CreatureAI return heroic10; } - template inline - const T& RAID_MODE(const T& normal10, const T& normal25) const + template + inline T const& RAID_MODE(T const& normal10, T const& normal25) const { switch (_difficulty) { @@ -306,8 +273,8 @@ struct TC_GAME_API ScriptedAI : public CreatureAI return normal25; } - template inline - const T& RAID_MODE(const T& normal10, const T& normal25, const T& heroic10, const T& heroic25) const + template + 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 662c7e21db..e59b127b59 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -1045,9 +1045,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 63b160f7f6..9e0584f9ac 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 SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker); diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index ab6dd4d767..c8047c0f62 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -275,7 +275,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) { @@ -304,23 +304,40 @@ 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_S_AURA: return source->HasAuraEffect(aura.spell_id, aura.effect_idx); 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) + if (!target || !target->IsUnit()) return false; - return target->getLevel() >= level.minlevel; + return target->ToUnit()->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(source), const_cast(target)); + { + Unit const* unitTarget = nullptr; + if (target) + unitTarget = target->ToUnit(); + return sScriptMgr->OnCriteriaCheck(ScriptId, const_cast(source), const_cast(unitTarget)); + } case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount; case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: @@ -360,7 +377,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: { @@ -395,7 +416,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)) @@ -1125,7 +1146,7 @@ template<> bool IsGuild() { return true; } * this function will be called whenever the user might have done a criteria relevant action */ template -void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, Unit const* unit /*= nullptr*/, Player* referencePlayer /*= nullptr*/, GameObject* go /*= nullptr*/) +void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, WorldObject const* ref /*= nullptr*/, Player* referencePlayer /*= nullptr*/, GameObject* go /*= nullptr*/) { if (type >= ACHIEVEMENT_CRITERIA_TYPE_TOTAL) { @@ -1165,7 +1186,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, continue; } - if (!CanUpdateCriteria(achievementCriteria, achievement, miscValue1, miscValue2, miscValue3, unit, referencePlayer, go)) + if (!CanUpdateCriteria(achievementCriteria, achievement, miscValue1, miscValue2, miscValue3, ref, referencePlayer, go)) continue; switch (type) @@ -1177,7 +1198,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, break; default: if (AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria)) - if (!data->Meets(referencePlayer, unit, miscValue1, miscValue2)) + if (!data->Meets(referencePlayer, ref, miscValue1, miscValue2)) continue; break; } @@ -2330,7 +2351,7 @@ CompletedAchievementData* AchievementMgr::GetCompletedDataForAchievement(uint } template -bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer, GameObject* go) +bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject const* ref, Player* referencePlayer, GameObject* go) { if (DisableMgr::IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, criteria->ID, nullptr)) { @@ -2361,14 +2382,14 @@ bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criter return false; } - if (!RequirementsSatisfied(criteria, miscValue1, miscValue2, miscValue3, unit, referencePlayer, go)) + if (!RequirementsSatisfied(criteria, miscValue1, miscValue2, miscValue3, ref, referencePlayer, go)) { TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Requirements not satisfied", criteria->Description, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Type)); return false; } - if (!AdditionalRequirementsSatisfied(criteria, miscValue1, miscValue2, unit, referencePlayer)) + if (!AdditionalRequirementsSatisfied(criteria, miscValue1, miscValue2, ref, referencePlayer)) { TC_LOG_TRACE("achievement", "CanUpdateCriteria: %s (Id: %u Type %s) Additional requirements not satisfied", criteria->Description, criteria->ID, AchievementGlobalMgr::GetCriteriaTypeString(criteria->Type)); @@ -2417,7 +2438,7 @@ bool AchievementMgr::ConditionsSatisfied(AchievementCriteriaEntry const* crit } template -bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* achievementCriteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer, GameObject* go) const +bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* achievementCriteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject const* ref, Player* referencePlayer, GameObject* go) const { switch (AchievementCriteriaTypes(achievementCriteria->Type)) { @@ -2590,7 +2611,7 @@ bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* ac return false; break; case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: - if (!miscValue1 || !unit || unit->GetTypeId() != TYPEID_PLAYER) + if (!miscValue1 || !ref || ref->GetTypeId() != TYPEID_PLAYER) return false; break; case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: @@ -2613,7 +2634,7 @@ bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* ac } if (AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria)) - if (!data->Meets(referencePlayer, unit)) + if (!data->Meets(referencePlayer, ref)) return false; break; } @@ -2721,7 +2742,7 @@ bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* ac 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; @@ -2794,7 +2815,7 @@ bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* ac } template -bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const +bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 miscValue2, WorldObject const* ref, Player* referencePlayer) const { for (uint8 i = 0; i < MAX_ADDITIONAL_CRITERIA_CONDITIONS; ++i) { @@ -2812,19 +2833,19 @@ bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry break; } case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_CREATURE_ENTRY: // 4 - if (!unit || unit->GetEntry() != reqValue) + if (!ref || ref->GetEntry() != reqValue) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_MUST_BE_PLAYER: // 5 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER) + if (!ref || ref->GetTypeId() != TYPEID_PLAYER) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_MUST_BE_DEAD: // 6 - if (!unit || unit->IsAlive()) + if (!ref || !ref->IsUnit() || ref->ToUnit()->IsAlive()) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_MUST_BE_ENEMY: // 7 - if (!unit || !referencePlayer->IsHostileTo(unit)) + if (!ref || !referencePlayer->IsHostileTo(ref)) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_SOURCE_HAS_AURA: // 8 @@ -2832,11 +2853,11 @@ bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_HAS_AURA: // 10 - if (!unit || !unit->HasAura(reqValue)) + if (!ref || !ref->IsUnit() || !ref->ToUnit()->HasAura(reqValue)) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_HAS_AURA_TYPE: // 11 - if (!unit || !unit->HasAuraType(AuraType(reqValue))) + if (!ref || !ref->IsUnit() || !ref->ToUnit()->HasAuraType(AuraType(reqValue))) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_ITEM_QUALITY_MIN: // 14 @@ -2865,10 +2886,10 @@ bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry } case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_AREA_OR_ZONE: // 18 { - if (!unit) + if (!ref) return false; uint32 zoneId, areaId; - unit->GetZoneAndAreaId(zoneId, areaId); + ref->GetZoneAndAreaId(zoneId, areaId); if (zoneId != reqValue && areaId != reqValue) return false; break; @@ -2886,11 +2907,11 @@ bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_RACE: // 27 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER || unit->getRace() != reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->getRace() != reqValue) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_CLASS: // 28 - if (!unit || unit->GetTypeId() != TYPEID_PLAYER || unit->getClass() != reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->getClass() != reqValue) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_MAX_GROUP_MEMBERS: // 29 @@ -2899,10 +2920,9 @@ bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_CREATURE_TYPE: // 30 { - if (!unit) + if (!ref) return false; - Creature const* const creature = unit->ToCreature(); - if (!creature || creature->GetCreatureType() != reqValue) + if (!ref->IsUnit() || ref->ToUnit()->GetCreatureType() != reqValue) return false; break; } @@ -2936,15 +2956,15 @@ bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_LEVEL: // 40 - if (!unit || unit->getLevel() != reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->getLevel() != reqValue) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_ZONE: // 41 - if (!unit || unit->GetZoneId() != reqValue) + if (!ref || ref->GetZoneId() != reqValue) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_TARGET_HEALTH_PERCENT_BELOW: // 46 - if (!unit || unit->GetHealthPct() >= reqValue) + if (!ref || !ref->IsUnit() || ref->ToUnit()->GetHealthPct() >= reqValue) return false; break; case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_REQUIRES_GUILD_GROUP: // 61 diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h index 390a427885..272824b492 100644 --- a/src/server/game/Achievements/AchievementMgr.h +++ b/src/server/game/Achievements/AchievementMgr.h @@ -26,9 +26,9 @@ #include #include -class Unit; -class Player; class GameObject; +class Player; +class WorldObject; class WorldPacket; typedef std::vector AchievementCriteriaEntryList; @@ -201,7 +201,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 @@ -209,7 +209,7 @@ struct TC_GAME_API AchievementCriteriaDataSet AchievementCriteriaDataSet() : criteria_id(0) { } typedef std::vector 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; @@ -267,7 +267,7 @@ class TC_GAME_API AchievementMgr void LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult); void SaveToDB(CharacterDatabaseTransaction& trans); void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint64 value, bool evenIfCriteriaComplete); - void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, Unit const* unit = nullptr, Player* referencePlayer = nullptr, GameObject* go = nullptr); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, WorldObject const* ref = nullptr, Player* referencePlayer = nullptr, GameObject* go = nullptr); void CompletedAchievement(AchievementEntry const* entry, Player* referencePlayer); void CheckAllAchievementCriteria(Player* referencePlayer); void SendAllAchievementData(Player* receiver) const; @@ -291,12 +291,12 @@ class TC_GAME_API AchievementMgr void CompletedCriteriaFor(AchievementEntry const* achievement, Player* referencePlayer); bool IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement); bool IsCompletedAchievement(AchievementEntry const* entry); - bool CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer, GameObject* go = nullptr); + bool CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject const* ref, Player* referencePlayer, GameObject* go = nullptr); void SendPacket(WorldPacket const* data) const; bool ConditionsSatisfied(AchievementCriteriaEntry const* criteria, Player* referencePlayer) const; - bool RequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit const* unit, Player* referencePlayer, GameObject* go = nullptr) const; - bool AdditionalRequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 miscValue2, Unit const* unit, Player* referencePlayer) const; + bool RequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject const* red, Player* referencePlayer, GameObject* go = nullptr) const; + bool AdditionalRequirementsSatisfied(AchievementCriteriaEntry const* criteria, uint64 miscValue1, uint64 miscValue2, WorldObject const* red, Player* referencePlayer) const; T* _owner; CriteriaProgressMap m_criteriaProgress; diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp index 97d8085cdf..abe3f8a220 100644 --- a/src/server/game/Conditions/DisableMgr.cpp +++ b/src/server/game/Conditions/DisableMgr.cpp @@ -294,7 +294,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()) @@ -309,15 +309,16 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags case DISABLE_TYPE_SPELL: { uint8 spellFlags = itr->second.flags; - if (unit) + if (ref) { - if ((spellFlags & SPELL_DISABLE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || - (unit->GetTypeId() == TYPEID_UNIT && ((unit->IsPet() && spellFlags & SPELL_DISABLE_PET) || spellFlags & SPELL_DISABLE_CREATURE))) + if ((ref->GetTypeId() == TYPEID_PLAYER && (spellFlags & SPELL_DISABLE_PLAYER)) || + (ref->GetTypeId() == TYPEID_UNIT && ((spellFlags & SPELL_DISABLE_CREATURE) || (ref->ToUnit()->IsPet() && (spellFlags & SPELL_DISABLE_PET)))) || + (ref->GetTypeId() == TYPEID_GAMEOBJECT && (spellFlags & SPELL_DISABLE_GAMEOBJECT))) { if (spellFlags & SPELL_DISABLE_MAP) { std::set 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)) @@ -329,7 +330,7 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags if (spellFlags & SPELL_DISABLE_AREA) { std::set 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 } @@ -348,7 +349,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()) diff --git a/src/server/game/Conditions/DisableMgr.h b/src/server/game/Conditions/DisableMgr.h index a6421b8f3f..b17e624687 100644 --- a/src/server/game/Conditions/DisableMgr.h +++ b/src/server/game/Conditions/DisableMgr.h @@ -20,7 +20,7 @@ #include "Define.h" -class Unit; +class WorldObject; enum DisableType { @@ -37,16 +37,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 @@ -57,7 +58,7 @@ enum MMapDisableTypes namespace DisableMgr { TC_GAME_API void LoadDisables(); - TC_GAME_API bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags = 0); + TC_GAME_API bool IsDisabledFor(DisableType type, uint32 entry, WorldObject const* ref, uint8 flags = 0); TC_GAME_API void CheckQuestDisables(); TC_GAME_API bool IsVMAPDisabledFor(uint32 entry, uint8 flags); TC_GAME_API bool IsPathfindingEnabled(uint32 mapId); diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.h b/src/server/game/Entities/AreaTrigger/AreaTrigger.h index 2f8adf7340..e76e5da122 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.h +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.h @@ -41,6 +41,9 @@ class AreaTrigger : public WorldObject, public GridObject void SetDuration(int32 newDuration) { _duration = newDuration; } void Delay(int32 delaytime) { SetDuration(GetDuration() - delaytime); } + ObjectGuid GetOwnerGUID() const override { return ObjectGuid::Empty; } + uint32 GetFaction() const override { return 0; } + protected: int32 _duration; }; diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index 5ccbd0aa07..0dd523717a 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -147,6 +147,15 @@ void Corpse::DeleteFromDB(ObjectGuid const& ownerGuid, CharacterDatabaseTransact 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 87d073199a..6d6101cb9e 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -63,7 +63,8 @@ class TC_GAME_API Corpse : public WorldObject, public GridObject void DeleteFromDB(CharacterDatabaseTransaction& trans); static void DeleteFromDB(ObjectGuid const& ownerGuid, CharacterDatabaseTransaction& 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 d5cbd294da..1d2826409f 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2150,7 +2150,7 @@ void Creature::LoadTemplateImmunities() } } -bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster, Optional effectMask /*= {}*/) const +bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const { if (!spellInfo) return false; @@ -2161,9 +2161,6 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster, Option if (!spellInfo->Effects[i].IsEffect()) continue; - if (effectMask && !(*effectMask & (1 << i))) - continue; - if (!IsImmunedToSpellEffect(spellInfo, i, caster)) { immunedToAllEffects = false; @@ -2176,8 +2173,11 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster, Option 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 (!spellInfo->Effects[index].IsEffect()) + return true; + 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 d2c5766f56..1ae8a5e1ab 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -129,8 +129,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool IsClassTrainerOf(Player const* player) const; bool CanCreatureAttack(Unit const* victim, bool force = true) const; void LoadTemplateImmunities(); - bool IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster, Optional effectMask = {}) 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 a522d4e531..119f1fac55 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -269,6 +269,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 6a43df5037..80243e85e3 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.h +++ b/src/server/game/Entities/DynamicObject/DynamicObject.h @@ -55,11 +55,13 @@ class TC_GAME_API DynamicObject : public WorldObject, public GridObjectUpdateAI(diff); else if (!AIM_Initialize()) @@ -1068,8 +1070,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; @@ -1521,11 +1525,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) { if (m_goData && (forceDelay || m_respawnTime > GameTime::GetGameTime()) && m_spawnedByDefault) @@ -2408,69 +2407,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); - PhasingHandler::InheritPhaseShift(trigger, this); - - CastSpellExtraArgs args; - args.TriggerFlags = triggered; - if (Unit* owner = GetOwner()) - { - trigger->SetFaction(owner->GetFaction()); - if (owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) - trigger->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - // copy pvp state flags from owner - trigger->SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, owner->GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG)); - // 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); @@ -2585,7 +2521,7 @@ QuaternionData GameObject::GetWorldRotation() const return localRotation; } -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; @@ -2604,10 +2540,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(); @@ -2635,11 +2569,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); @@ -2659,8 +2592,8 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* case GO_DESTRUCTIBLE_DAMAGED: { if (GetGOInfo()->building.damagedEvent) - GameEvents::Trigger(GetGOInfo()->building.damagedEvent, eventInvoker, this); - AI()->Damaged(eventInvoker, m_goInfo->building.damageEvent); + GameEvents::Trigger(GetGOInfo()->building.damagedEvent, attackerOrHealer, this); + AI()->Damaged(attackerOrHealer, m_goInfo->building.damageEvent); RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); @@ -2685,12 +2618,12 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* case GO_DESTRUCTIBLE_DESTROYED: { if (GetGOInfo()->building.destroyedEvent) - GameEvents::Trigger(GetGOInfo()->building.destroyedEvent, eventInvoker, this); - AI()->Destroyed(eventInvoker, m_goInfo->building.destroyedEvent); + GameEvents::Trigger(GetGOInfo()->building.destroyedEvent, attackerOrHealer, this); + 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); @@ -2712,7 +2645,7 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* case GO_DESTRUCTIBLE_REBUILDING: { if (GetGOInfo()->building.rebuildingEvent) - GameEvents::Trigger(GetGOInfo()->building.rebuildingEvent, eventInvoker, this); + GameEvents::Trigger(GetGOInfo()->building.rebuildingEvent, attackerOrHealer, this); 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 24a22123f6..a0a12687bf 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -160,8 +160,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject 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) { @@ -273,14 +272,12 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject 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)) @@ -301,8 +298,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject 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 a085e14c8c..2411741148 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -42,8 +42,10 @@ #include "PhasingHandler.h" #include "PathGenerator.h" #include "Player.h" -#include "SharedDefines.h" +#include "ReputationMgr.h" #include "SpellAuraEffects.h" +#include "SpellDefines.h" +#include "SpellMgr.h" #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" @@ -1143,8 +1145,8 @@ bool Object::PrintIndexError(uint32 index, bool set) const return false; } -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_outdoors(true), m_liquidStatus(LIQUID_MAP_NO_WATER), m_wmoGroupID(0), m_currMap(nullptr), m_InstanceId(0), _dbPhase(0), m_notifyflags(0), m_aiAnimKitId(0), m_movementAnimKitId(0), m_meleeAnimKitId(0) @@ -2324,6 +2326,922 @@ Player* WorldObject::SelectNearestPlayer(float distance) const return target; } +ObjectGuid WorldObject::GetCharmerOrOwnerOrOwnGUID() const +{ + ObjectGuid guid = GetCharmerOrOwnerGUID(); + if (!guid.IsEmpty()) + return guid; + return GetGUID(); +} + +Unit* WorldObject::GetOwner() const +{ + return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); +} + +Unit* WorldObject::GetCharmerOrOwner() const +{ + if (Unit const* unit = ToUnit()) + return unit->GetCharmerOrOwner(); + else if (GameObject const* go = ToGameObject()) + return go->GetOwner(); + + return nullptr; +} + +Unit* WorldObject::GetCharmerOrOwnerOrSelf() const +{ + if (Unit* u = GetCharmerOrOwner()) + return u; + + return const_cast(this)->ToUnit(); +} + +Player* WorldObject::GetCharmerOrOwnerPlayerOrPlayerItself() const +{ + ObjectGuid guid = GetCharmerOrOwnerGUID(); + if (guid.IsPlayer()) + return ObjectAccessor::GetPlayer(*this, guid); + + return const_cast(this)->ToPlayer(); +} + +Player* WorldObject::GetAffectingPlayer() const +{ + if (!GetCharmerOrOwnerGUID()) + return const_cast(this)->ToPlayer(); + + if (Unit* owner = GetCharmerOrOwner()) + return owner->GetCharmerOrOwnerPlayerOrPlayerItself(); + + return nullptr; +} + +Player* WorldObject::GetSpellModOwner() const +{ + if (Player* player = const_cast(this)->ToPlayer()) + return player; + + if (GetTypeId() == TYPEID_UNIT) + { + Creature const* creature = ToCreature(); + if (creature->HasUnitTypeMask(UNIT_MASK_PET | UNIT_MASK_TOTEM | UNIT_MASK_GUARDIAN | UNIT_MASK_HUNTER_PET | UNIT_MASK_MINION)) + { + if (Unit* owner = creature->GetOwner()) + return owner->ToPlayer(); + } + } + else if (GetTypeId() == TYPEID_GAMEOBJECT) + { + GameObject const* go = ToGameObject(); + if (Unit* owner = go->GetOwner()) + return owner->ToPlayer(); + } + + return nullptr; +} + +// function uses real base points (typically value - 1) +int32 WorldObject::CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effectIndex, int32 const* basePoints) const +{ + return spellProto->Effects[effectIndex].CalcValue(this, basePoints, target); +} + +float WorldObject::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const +{ + if (!spellInfo->RangeEntry) + return 0; + if (spellInfo->RangeEntry->RangeMax[1] == spellInfo->RangeEntry->RangeMax[0]) + 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; + if (spellInfo->RangeEntry->RangeMin[1] == spellInfo->RangeEntry->RangeMin[0]) + return spellInfo->GetMinRange(); + if (!target) + return spellInfo->GetMinRange(true); + return spellInfo->GetMinRange(!IsHostileTo(target)); +} + +float WorldObject::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; +} + +int32 WorldObject::ModSpellDuration(SpellInfo const* spellInfo, WorldObject 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 (spellInfo->HasAttribute(SPELL_ATTR7_IGNORE_DURATION_MODS)) + return duration; + + // cut duration only of negative effects + Unit const* unitTarget = target->ToUnit(); + if (!unitTarget) + return duration; + + // cut duration only of negative effects + 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; + } + } + } + return std::max(duration, 0); +} + +void WorldObject::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); + + Unit const* unitCaster = ToUnit(); + if (!unitCaster) + return; + + if (!(spellInfo->HasAttribute(SPELL_ATTR0_IS_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && + ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) + castTime = int32(float(castTime) * GetFloatValue(UNIT_MOD_CAST_SPEED)); + else if (spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !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) +{ + if (!spellInfo || duration < 0) + return; + + if (spellInfo->IsChanneled() && !spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC)) + 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_IS_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) || 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_USES_RANGED_SLOT) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) + duration = int32(float(duration) * unitCaster->m_modAttackSpeedPct[RANGED_ATTACK]); +} + +float WorldObject::MeleeSpellMissChance(Unit const* /*victim*/, WeaponAttackType /*attType*/, SpellInfo const* /*spellInfo*/) const +{ + return 0.0f; +} + +SpellMissInfo WorldObject::MeleeSpellHitResult(Unit* /*victim*/, SpellInfo const* /*spellInfo*/) const +{ + return SPELL_MISS_NONE; +} + +SpellMissInfo WorldObject::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const +{ + // Can`t miss on dead target (on skinning for example) + if (!victim->IsAlive() && !victim->IsPlayer()) + return SPELL_MISS_NONE; + + if (spellInfo->HasAttribute(SPELL_ATTR7_CANT_MISS)) + return SPELL_MISS_NONE; + + SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); + + // PvP - PvE spell misschances per leveldif > 2 + int32 lchance = victim->IsPlayer() ? 7 : 11; + int32 thisLevel = getLevelForTarget(victim); + if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger()) + thisLevel = std::max(thisLevel, spellInfo->SpellLevel); + int32 leveldif = int32(victim->getLevelForTarget(this)) - thisLevel; + int32 levelBasedHitDiff = leveldif; + + // Base hit chance from attacker and victim levels + int32 modHitChance = 100; + if (levelBasedHitDiff >= 0) + { + if (victim->IsPlayer()) + { + modHitChance = 94 - 3 * std::min(levelBasedHitDiff, 3); + levelBasedHitDiff -= 3; + } + else + { + modHitChance = 96 - std::min(levelBasedHitDiff, 2); + levelBasedHitDiff -= 2; + } + if (levelBasedHitDiff > 0) + modHitChance -= lchance * std::min(levelBasedHitDiff, 7); + } + else + modHitChance = 97 - levelBasedHitDiff; + + // Spellmod from SPELLMOD_RESIST_MISS_CHANCE + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance); + + // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras + modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); + + // Decrease hit chance from victim rating bonus + if (victim->IsPlayer()) + 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 + // 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)) + { + // 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 +{ + // 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; + + // 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; + + 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_PLAYER_CONTROLLED)) + { + if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + { + 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->IsHostileToPvpActivePlayers() + && 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 (Player const* targetPlayerOwner = target->GetAffectingPlayer()) + { + // check contested flags + if (factionTemplateEntry->IsHostileToPvpActivePlayers() + && 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; + // neutral by default + return REP_NEUTRAL; +} + +bool WorldObject::IsHostileTo(WorldObject const* target) const +{ + return GetReactionTo(target) <= REP_HOSTILE; +} + +bool WorldObject::IsFriendlyTo(WorldObject const* target) const +{ + return GetReactionTo(target) >= REP_FRIENDLY; +} + +bool WorldObject::IsHostileToPlayers() const +{ + FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); + if (!my_faction->Faction) + return false; + + FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction); + if (raw_faction && raw_faction->ReputationIndex >= 0) + return false; + + return my_faction->IsHostileToPlayers(); +} + +bool WorldObject::IsNeutralToAll() const +{ + FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); + if (!my_faction->Faction) + return true; + + FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction); + if (raw_faction && raw_faction->ReputationIndex >= 0) + return false; + + return my_faction->IsNeutralToAll(); +} + +void WorldObject::CastSpell(SpellCastTargets const& targets, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/) +{ + SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId); + if (!info) + { + TC_LOG_ERROR("entities.unit", "CastSpell: unknown spell %u by caster %s", spellId, GetGUID().ToString().c_str()); + return; + } + + Spell* spell = new Spell(this, info, args.TriggerFlags, args.OriginalCaster); + for (auto const& pair : args.SpellValueOverrides) + spell->SetSpellValue(pair.first, pair.second); + + spell->m_CastItem = args.CastItem; + spell->prepare(targets, args.TriggeringAura); +} + +void WorldObject::CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/) +{ + SpellCastTargets targets; + if (target) + { + if (Unit* unitTarget = target->ToUnit()) + targets.SetUnitTarget(unitTarget); + else if (GameObject* goTarget = target->ToGameObject()) + targets.SetGOTarget(goTarget); + else + { + TC_LOG_ERROR("entities.unit", "CastSpell: Invalid target %s passed to spell cast by %s", target->GetGUID().ToString().c_str(), GetGUID().ToString().c_str()); + return; + } + } + CastSpell(targets, spellId, args); +} + +void WorldObject::CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args /*= { }*/) +{ + SpellCastTargets targets; + targets.SetDst(dest); + CastSpell(targets, spellId, args); +} + +// function based on function Unit::CanAttack from 13850 client +bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const +{ + ASSERT(target); + + // can't attack self + if (this == target) + return false; + + // can't attack GMs + if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) + return false; + + Unit const* unit = ToUnit(); + Unit const* targetUnit = target->ToUnit(); + + // CvC case - can attack each other only when one of them is hostile + if (unit && !unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && targetUnit && !targetUnit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + return IsHostileTo(target) || target->IsHostileTo(this); + + // PvP, PvC, CvP case + // can't attack friendly targets + if (IsFriendlyTo(target) || target->IsFriendlyTo(this)) + return false; + + Player const* playerAffectingAttacker = unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr; + Player const* playerAffectingTarget = targetUnit && targetUnit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? target->GetAffectingPlayer() : nullptr; + + // Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar) + if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget)) + { + Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget; + + if (Unit const* creature = playerAffectingAttacker ? targetUnit : unit) + { + if (creature->IsContestedGuard() && player->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 || !bySpell->IsAllowingDeadTarget()) && unitTarget && !unitTarget->IsAlive()) + return false; + + // can't attack untargetable + if ((!bySpell || !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_NOT_ATTACKABLE_2)) + return false; + + if (unit && !unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unitTarget->IsImmuneToNPC()) + return false; + + if (!bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG)) + { + if (unitTarget && !unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unit && unit->IsImmuneToNPC()) + return false; + + if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unitTarget->IsImmuneToPC()) + return false; + } + + // check duel - before sanctuary checks + Player const* playerAffectingAttacker = unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr; + Player const* playerAffectingTarget = unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? 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_PLAYER_CONTROLLED) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && (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(); + + // can't assist unattackable units + 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 || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && unitTarget && unitTarget->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_PLAYER_CONTROLLED)) + { + if (unitTarget && unitTarget->IsImmuneToPC()) + return false; + } + else + { + if (unitTarget && unitTarget->IsImmuneToNPC()) + return false; + } + } + + // PvP case + if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + { + Player const* targetPlayerOwner = target->GetAffectingPlayer(); + if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + { + 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_PLAYER_CONTROLLED)) + { + 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_IS_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REDIRECTED) || spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) + 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(victim->GetDistance(this), 5.0f) / spellInfo->Speed * 1000.0f)); + // Schedule charge drop + aurEff->GetBase()->DropChargeDelayed(delay, AuraRemoveFlags::Expired); + } + else + aurEff->GetBase()->DropCharge(AuraRemoveFlags::Expired); + + return magnet; + } + } + } + return victim; +} + template 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 26af0542f0..c2e2c9dcb6 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -20,6 +20,7 @@ #include "Common.h" #include "Duration.h" +#include "EventProcessor.h" #include "GridReference.h" #include "GridRefManager.h" #include "ModelIgnoreFlags.h" @@ -30,6 +31,7 @@ #include "PhaseShift.h" #include "Position.h" #include "SharedDefines.h" +#include "SpellDefines.h" #include "UpdateFields.h" #include "UpdateMask.h" #include @@ -45,6 +47,9 @@ class GameObject; class InstanceScript; class Map; class Player; +class Spell; +class SpellCastTargets; +class SpellInfo; class TempSummon; class TransportBase; class Unit; @@ -52,6 +57,7 @@ class UpdateData; class WorldObject; class WorldPacket; class ZoneScript; +struct FactionTemplateEntry; struct PositionFullTerrainStatus; struct QuaternionData; enum ZLiquidStatus : uint32; @@ -418,6 +424,58 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const; Player* SelectNearestPlayer(float distance) const; + virtual ObjectGuid GetOwnerGUID() const = 0; + virtual ObjectGuid GetCharmerOrOwnerGUID() const { return GetOwnerGUID(); } + ObjectGuid GetCharmerOrOwnerOrOwnGUID() const; + + Unit* GetOwner() const; + Unit* GetCharmerOrOwner() const; + Unit* GetCharmerOrOwnerOrSelf() const; + Player* GetCharmerOrOwnerPlayerOrPlayerItself() const; + Player* GetAffectingPlayer() const; + + Player* GetSpellModOwner() const; + int32 CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, 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* spellProto, uint8 effect_index, float value) const; + int32 ModSpellDuration(SpellInfo const* spellProto, WorldObject 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); + + virtual float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo = nullptr) 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 void GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange = 250.0f) const; @@ -481,6 +539,9 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation float GetMapWaterOrGroundLevel(float x, float y, float z, float* ground = nullptr) const; float GetMapHeight(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f) const; // DEFAULT_HEIGHT_SEARCH in map.h + // Event handler + EventProcessor m_Events; + uint16 GetAIAnimKitId() const { return m_aiAnimKitId; } void SetAIAnimKitId(uint16 animKitId); uint16 GetMovementAnimKitId() const { return m_movementAnimKitId; } @@ -499,7 +560,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation bool m_isActive; bool m_isFarVisible; Optional m_visibilityDistanceOverride; - const bool m_isWorldObject; + bool const m_isWorldObject; ZoneScript* m_zoneScript; // transports (gameobjects only) @@ -527,7 +588,6 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation private: Map* m_currMap; // current object's Map location - //uint32 m_mapId; // object at map with map_id uint32 m_InstanceId; // in map copy with instance id PhaseShift _phaseShift; PhaseShift _suppressedPhaseShift; // contains phases for current area but not applied due to conditions diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 12baca33eb..03257cf506 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1798,7 +1798,7 @@ void Player::SetObjectScale(float scale) SendMovementSetCollisionHeight(GetCollisionHeight(), UPDATE_COLLISION_HEIGHT_SCALE); } -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)) @@ -22632,7 +22632,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 = new ResurrectionData(); @@ -23285,94 +23285,6 @@ Player* Player::GetSelectedPlayer() const return nullptr; } -void Player::SendComboPoints() -{ - - Unit* combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget); - if (combotarget) - { - WorldPacket data; - Player* movingMe = GetCharmerOrSelfPlayer(); - if (movingMe != this) - { - data.Initialize(SMSG_PET_UPDATE_COMBO_POINTS, movingMe->GetPackGUID().size()+combotarget->GetPackGUID().size()+1); - data << movingMe->GetPackGUID(); - } - else - data.Initialize(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size()+1); - data << combotarget->GetPackGUID(); - data << uint8(m_comboPoints); - SendDirectMessage(&data); - } -} - -void Player::AddComboPoints(Unit* target, int8 count, Spell* spell) -{ - if (!count) - return; - - int8 * comboPoints = spell ? &spell->m_comboPointGain : &m_comboPoints; - - // without combo points lost (duration checked in aura) - RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS); - - if (target->GetGUID() == m_comboTarget) - *comboPoints += count; - else - { - if (m_comboTarget) - if (Unit* target2 = ObjectAccessor::GetUnit(*this, m_comboTarget)) - target2->RemoveComboPointHolder(GetGUID()); - - // Spells will always add value to m_comboPoints eventualy, so it must be cleared first - if (spell) - m_comboPoints = 0; - - m_comboTarget = target->GetGUID(); - *comboPoints = count; - - target->AddComboPointHolder(GetGUID()); - } - - if (*comboPoints > 5) - *comboPoints = 5; - else if (*comboPoints < 0) - *comboPoints = 0; - - if (!spell) - SendComboPoints(); -} - -void Player::GainSpellComboPoints(int8 count) -{ - if (!count) - return; - - m_comboPoints += count; - if (m_comboPoints > 5) m_comboPoints = 5; - else if (m_comboPoints < 0) m_comboPoints = 0; - - SendComboPoints(); -} - -void Player::ClearComboPoints() -{ - if (!m_comboTarget) - return; - - // without combopoints lost (duration checked in aura) - RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS); - - m_comboPoints = 0; - - SendComboPoints(); - - if (Unit* target = ObjectAccessor::GetUnit(*this, m_comboTarget)) - target->RemoveComboPointHolder(GetGUID()); - - m_comboTarget.Clear(); -} - bool Player::IsInGroup(ObjectGuid groupGuid) const { if (Group const* group = GetGroup()) @@ -26046,9 +25958,9 @@ void Player::ResetAchievementCriteria(AchievementCriteriaCondition condition, ui m_achievementMgr->ResetAchievementCriteria(condition, value, evenIfCriteriaComplete); } -void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, Unit* unit /*= nullptr*/, GameObject* go /*= nullptr*/) +void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 /*= 0*/, uint64 miscValue2 /*= 0*/, uint64 miscValue3 /*= 0*/, WorldObject* ref /*= nullptr*/, GameObject* go /*= nullptr*/) { - m_achievementMgr->UpdateAchievementCriteria(type, miscValue1, miscValue2, miscValue3, unit, this); + m_achievementMgr->UpdateAchievementCriteria(type, miscValue1, miscValue2, miscValue3, ref, this); Guild* guild = sGuildMgr->GetGuildById(GetGuildId()); if (!guild) return; @@ -26058,7 +25970,7 @@ void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 mis if (sAchievementMgr->IsGroupCriteriaType(type)) return; - guild->UpdateAchievementCriteria(type, miscValue1, miscValue2, miscValue3, unit, this, go); + guild->UpdateAchievementCriteria(type, miscValue1, miscValue2, miscValue3, ref, this, go); } 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 9de69bbe72..0bc5b61228 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1031,7 +1031,7 @@ class TC_GAME_API Player : public Unit, public GridObject void Update(uint32 time) override; - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override; bool IsInAreaTriggerRadius(AreaTriggerEntry const* trigger) const; @@ -1521,14 +1521,6 @@ class TC_GAME_API Player : public Unit, public GridObject void SetTarget(ObjectGuid /*guid*/) override { } /// Used for serverside target changes, does not apply to players void SetSelection(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_TARGET, guid); } - uint8 GetComboPoints() const { return m_comboPoints; } - ObjectGuid GetComboTarget() const { return m_comboTarget; } - - void AddComboPoints(Unit* target, int8 count, Spell* spell = nullptr); - void GainSpellComboPoints(int8 count); - void ClearComboPoints(); - void SendComboPoints(); - void SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError = 0, ObjectGuid::LowType item_guid = 0, uint32 item_count = 0) const; void SendNewMail() const; void UpdateNextMailTimeAndUnreads(); @@ -1663,7 +1655,7 @@ class TC_GAME_API Player : public Unit, public GridObject 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() { delete _resurrectionData; @@ -2329,7 +2321,7 @@ class TC_GAME_API Player : public Unit, public GridObject bool HasAchieved(uint32 achievementId) const; void ResetAchievements(); void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint64 value, bool evenIfCriteriaComplete = false); - void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, Unit* unit = nullptr, GameObject* go = nullptr); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1 = 0, uint64 miscValue2 = 0, uint64 miscValue3 = 0, WorldObject* ref = nullptr, GameObject* go = nullptr); void StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost = 0); void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry); void CompletedAchievement(AchievementEntry const* entry); @@ -2565,9 +2557,6 @@ class TC_GAME_API Player : public Unit, public GridObject uint32 m_ExtraFlags; - ObjectGuid m_comboTarget; - int8 m_comboPoints; - QuestStatusMap m_QuestStatus; QuestStatusSaveMap m_QuestStatusSave; diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index e3ee9042f3..41131cc58c 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -145,7 +145,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 a965b3e7a0..2748e1b9a1 100644 --- a/src/server/game/Entities/Totem/Totem.h +++ b/src/server/game/Entities/Totem/Totem.h @@ -57,7 +57,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 56ea150ad0..764f855eb9 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -959,48 +959,44 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons } } - if (damagetype != NODAMAGE) + if (damagetype != NODAMAGE && damagetype != DOT) { - if (victim != attacker - && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC)))) + if (victim != attacker && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC)))) { - if (damagetype != DOT) + if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) { - if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) + if (spell->getState() == SPELL_STATE_PREPARING) { - if (spell->getState() == SPELL_STATE_PREPARING) + auto isCastInterrupted = [&]() { - auto isCastInterrupted = [&]() - { - if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly))) - return true; + if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamageCancelsPlayerOnly))) + return true; + return false; + }; + + auto isCastDelayed = [&]() + { + if (!damage) return false; - }; - auto isCastDelayed = [&]() - { - if (!damage) - return false; + if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushbackPlayerOnly))) + return true; - if ((victim->IsPlayer() && spell->m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::DamagePushbackPlayerOnly))) - return true; + return false; + }; - return false; - }; - - if (isCastInterrupted()) - victim->InterruptNonMeleeSpells(false); - else if (isCastDelayed()) - spell->Delayed(); - } + if (isCastInterrupted()) + victim->InterruptNonMeleeSpells(false); + else if (isCastDelayed()) + spell->Delayed(); } - } - if (damage && victim->IsPlayer()) - if (Spell* spell = victim->m_currentSpells[CURRENT_CHANNELED_SPELL]) - if (spell->getState() == SPELL_STATE_CASTING && spell->m_spellInfo->HasChannelInterruptFlag(SpellAuraInterruptFlags::DamageChannelDuration)) - spell->DelayedChannel(); + if (damage && victim->IsPlayer()) + if (Spell* spell = victim->m_currentSpells[CURRENT_CHANNELED_SPELL]) + if (spell->getState() == SPELL_STATE_CASTING && spell->m_spellInfo->HasChannelInterruptFlag(SpellAuraInterruptFlags::DamageChannelDuration)) + spell->DelayedChannel(); + } } } @@ -1034,49 +1030,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) @@ -1718,22 +1671,19 @@ static float GetArmorReduction(float armor, uint8 attackerLevel) 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 @@ -1747,8 +1697,9 @@ static float GetArmorReduction(float armor, uint8 attackerLevel) 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; @@ -2373,7 +2324,7 @@ bool Unit::CanUseAttackType(uint8 attacktype) const } // Melee based spells hit result calculations -SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) +SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const { WeaponAttackType attType = BASE_ATTACK; @@ -2512,153 +2463,6 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo return SPELL_MISS_NONE; } -SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) -{ - // Can`t miss on dead target (on skinning for example) - if (!victim->IsAlive() && !victim->IsPlayer()) - return SPELL_MISS_NONE; - - if (spellInfo->HasAttribute(SPELL_ATTR7_CANT_MISS)) - return SPELL_MISS_NONE; - - SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); - - // PvP - PvE spell misschances per leveldif > 2 - int32 lchance = victim->IsPlayer() ? 7 : 11; - int32 thisLevel = getLevelForTarget(victim); - if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger()) - thisLevel = std::max(thisLevel, spellInfo->SpellLevel); - int32 leveldif = int32(victim->getLevelForTarget(this)) - thisLevel; - int32 levelBasedHitDiff = leveldif; - - // Base hit chance from attacker and victim levels - int32 modHitChance = 100; - if (levelBasedHitDiff >= 0) - { - if (victim->IsPlayer()) - { - modHitChance = 94 - 3 * std::min(levelBasedHitDiff, 3); - levelBasedHitDiff -= 3; - } - else - { - modHitChance = 96 - std::min(levelBasedHitDiff, 2); - levelBasedHitDiff -= 2; - } - if (levelBasedHitDiff > 0) - modHitChance -= lchance * std::min(levelBasedHitDiff, 7); - } - else - modHitChance = 97 - levelBasedHitDiff; - - // Spellmod from SPELLMOD_RESIST_MISS_CHANCE - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance); - - // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras - modHitChance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); - - // Decrease hit chance from victim rating bonus - if (victim->IsPlayer()) - 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 - // 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)) - { - // 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*/, Optional effectMask /*= nullptr*/) -{ - // Check for immune - if (victim->IsImmunedToSpell(spellInfo, this, effectMask)) - 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; - - // 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; - - switch (spellInfo->DmgClass) - { - case SPELL_DAMAGE_CLASS_RANGED: - case SPELL_DAMAGE_CLASS_MELEE: - return MeleeSpellHitResult(victim, spellInfo); - case SPELL_DAMAGE_CLASS_NONE: - return SPELL_MISS_NONE; - case SPELL_DAMAGE_CLASS_MAGIC: - return MagicSpellHitResult(victim, spellInfo); - } - return SPELL_MISS_NONE; -} - float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const { int32 attackerSkill = GetMaxSkillValueForLevel(nullptr); @@ -3239,6 +3043,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()) { @@ -3828,7 +3640,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;) @@ -3856,7 +3668,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;) @@ -3885,38 +3697,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); - else - oldAura->ModStackAmount(1); - oldAura->SetDuration(int32(dur)); - } - else - { - // limited target state must be removed before aura creation to preserve existing limited target aura - if (aura->IsLimitedTarget()) - aura->UnregisterLimitedTarget(); - - AuraCreateInfo createInfo(aura->GetSpellInfo(), effMask, stealer); - createInfo - .SetCasterGUID(aura->GetCasterGUID()) - .SetBaseAmount(baseDamage); - - if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo)) + if (Aura* oldAura = unitStealer->GetAura(aura->GetId(), aura->GetCasterGUID())) { - // created aura must not be limited target aura, so stealer won't loose it on recast - if (newAura->IsLimitedTarget()) + if (stealCharge) + oldAura->ModCharges(1); + else + oldAura->ModStackAmount(1); + oldAura->SetDuration(int32(dur)); + } + else + { + // limited target state must be removed before aura creation to preserve existing limited target aura + if (aura->IsLimitedTarget()) + aura->UnregisterLimitedTarget(); + + AuraCreateInfo createInfo(aura->GetSpellInfo(), effMask, stealer); + createInfo + .SetCasterGUID(aura->GetCasterGUID()) + .SetBaseAmount(baseDamage); + + if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo)) { - newAura->UnregisterLimitedTarget(); - // bring back single target aura status to the old aura - aura->SetIsLimitedTarget(true); - caster->GetLimitedCastAuras(aura->GetId()).push_back(aura); + // created aura must not be limited target aura, so stealer won't loose it on recast + if (newAura->IsLimitedTarget()) + { + newAura->UnregisterLimitedTarget(); + // bring back single target aura status to the old aura + aura->SetIsLimitedTarget(true); + caster->GetLimitedCastAuras(aura->GetId()).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(); } } @@ -4339,7 +4154,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()); } } } @@ -4470,7 +4284,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)) @@ -5463,188 +5277,6 @@ void Unit::SetSheath(SheathState sheathed) RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Sheathing); } -FactionTemplateEntry const* Unit::GetFactionTemplateEntry() const -{ - FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(GetFaction()); - if (!entry) - { - if (Player const* player = ToPlayer()) - TC_LOG_ERROR("entities.unit", "Player %s has invalid faction (faction template id) #%u", player->GetName().c_str(), GetFaction()); - else if (Creature const* creature = ToCreature()) - TC_LOG_ERROR("entities.unit", "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, GetFaction()); - else - TC_LOG_ERROR("entities.unit", "Unit (name=%s, type=%u) has invalid faction (faction template id) #%u", GetName().c_str(), uint32(GetTypeId()), GetFaction()); - - ABORT(); - } - return entry; -} - -// function based on function Unit::UnitReaction from 13850 client -ReputationRank Unit::GetReactionTo(Unit const* target) const -{ - // always friendly to self - if (this == target) - return REP_FRIENDLY; - - // always friendly to charmer or owner - if (GetCharmerOrOwnerOrSelf() == target->GetCharmerOrOwnerOrSelf()) - return REP_FRIENDLY; - - Player const* selfPlayerOwner = GetAffectingPlayer(); - Player const* targetPlayerOwner = target->GetAffectingPlayer(); - - // check forced reputation to support SPELL_AURA_FORCE_REACTION - if (selfPlayerOwner) - { - if (FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry()) - if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) - return *repRank; - } - else if (targetPlayerOwner) - { - if (FactionTemplateEntry const* selfFactionTemplateEntry = GetFactionTemplateEntry()) - if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(selfFactionTemplateEntry)) - return *repRank; - } - - - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) - { - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) - { - 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->IsHostileToPvpActivePlayers() - && 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->IsHostileToPvpActivePlayers() - && 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; - // neutral by default - return REP_NEUTRAL; -} - -bool Unit::IsHostileTo(Unit const* unit) const -{ - return GetReactionTo(unit) <= REP_HOSTILE; -} - -bool Unit::IsFriendlyTo(Unit const* unit) const -{ - return GetReactionTo(unit) >= REP_FRIENDLY; -} - -bool Unit::IsHostileToPlayers() const -{ - FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); - if (!my_faction->Faction) - return false; - - FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction); - if (raw_faction && raw_faction->ReputationIndex >= 0) - return false; - - return my_faction->IsHostileToPlayers(); -} - -bool Unit::IsNeutralToAll() const -{ - FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); - if (!my_faction->Faction) - return true; - - FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction); - if (raw_faction && raw_faction->ReputationIndex >= 0) - return false; - - return my_faction->IsNeutralToAll(); -} - void Unit::_addAttacker(Unit* pAttacker) { m_attackers.insert(pAttacker); @@ -6006,35 +5638,6 @@ void Unit::SetOwnerGUID(ObjectGuid owner) RemoveFieldNotifyFlag(UF_FLAG_OWNER); } -Unit* Unit::GetOwner() const -{ - // Fall back to creator guid of owner guid is not available - if (ObjectGuid guid = GetOwnerOrCreatorGUID()) - return ObjectAccessor::GetUnit(*this, guid); - - return nullptr; -} - -Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself() const -{ - ObjectGuid guid = GetCharmerOrOwnerGUID(); - if (guid.IsPlayer()) - return ObjectAccessor::GetPlayer(*this, guid); - - return const_cast(this)->ToPlayer(); -} - -Player* Unit::GetAffectingPlayer() const -{ - if (!GetCharmerOrOwnerGUID()) - return const_cast(this)->ToPlayer(); - - if (Unit* owner = GetCharmerOrOwner()) - return owner->GetCharmerOrOwnerPlayerOrPlayerItself(); - - return nullptr; -} - Player* Unit::GetControllingPlayer() const { if (ObjectGuid guid = GetCharmerOrOwnerGUID()) @@ -6077,14 +5680,6 @@ Guardian* Unit::GetGuardianPet() const return nullptr; } -Unit* Unit::GetCharmerOrOwnerOrSelf() const -{ - if (Unit* u = GetCharmerOrOwner()) - return u; - - return (Unit*)this; -} - void Unit::SetMinion(Minion* minion, bool apply) { if (!minion) @@ -6382,36 +5977,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_IS_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REDIRECTED) || spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) - 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(victim->GetDistance(this), 5.0f) / spellInfo->Speed * 1000.0f)); - // Schedule charge drop - (*itr)->GetBase()->DropChargeDelayed(delay, AuraRemoveFlags::Expired); - } - else - (*itr)->GetBase()->DropCharge(AuraRemoveFlags::Expired); - - return magnet; - } - } - return victim; -} - Unit* Unit::GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo) { AuraEffectList const& hitTriggerAuras = victim->GetAuraEffectsByType(SPELL_AURA_ADD_CASTER_HIT_TRIGGER); @@ -7759,7 +7324,7 @@ bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const return false; } -bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster, Optional effectMask /*= nullptr*/) const +bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const { if (!spellInfo) return false; @@ -7795,9 +7360,6 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Unit* caster, OptionalEffects[i].IsEffect()) continue; - if (effectMask && !(effectMask.value() & (1 << i))) - continue; - if (!IsImmunedToSpellEffect(spellInfo, i, caster)) { immuneToAllEffects = false; @@ -7860,7 +7422,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; @@ -8487,250 +8049,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_PLAYER_CONTROLLED) && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) - 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_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr; - Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? 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_NOT_ATTACKABLE_2)) - return false; - - if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && target->IsImmuneToNPC()) - return false; - - if (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && IsImmuneToNPC()) - return false; - - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && 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_PLAYER_CONTROLLED) && 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_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr; - Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? 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_PLAYER_CONTROLLED) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && (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_PLAYER_CONTROLLED)) - { - if (target->IsImmuneToPC()) - return false; - } - else - { - if (target->IsImmuneToNPC()) - return false; - } - } - - // PvP case - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) - { - Player const* targetPlayerOwner = target->GetAffectingPlayer(); - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) - { - 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_PLAYER_CONTROLLED)) - { - 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; @@ -9316,165 +8634,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::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<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; - } - } - 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); - - float castSpeedMod = 1; - castSpeedMod = castSpeedMod - GetTotalAuraMultiplier(SPELL_AURA_HASTE_SPELLS) + 1.0f; - - if (!(spellInfo->HasAttribute(SPELL_ATTR0_IS_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && - ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) - castTime = int32(float(castTime) * GetFloatValue(UNIT_MOD_CAST_SPEED)); - else if (spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) - castTime = int32(float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK] * castSpeedMod); - 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_SPELL_HASTE_AFFECTS_PERIODIC)) - return; - - // called from caster - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, duration, spell); - - if (!(spellInfo->HasAttribute(SPELL_ATTR0_IS_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) || 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_USES_RANGED_SLOT) && !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]; @@ -9499,7 +8658,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) @@ -9515,7 +8674,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; @@ -9581,28 +8740,6 @@ void Unit::ClearDiminishings() dim.Clear(); } -float Unit::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const -{ - if (!spellInfo->RangeEntry) - return 0; - if (spellInfo->RangeEntry->RangeMax[1] == spellInfo->RangeEntry->RangeMax[0]) - return spellInfo->GetMaxRange(); - if (!target) - return spellInfo->GetMaxRange(true); - return spellInfo->GetMaxRange(!IsHostileTo(target)); -} - -float Unit::GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const -{ - if (!spellInfo->RangeEntry) - return 0; - if (spellInfo->RangeEntry->RangeMin[1] == spellInfo->RangeEntry->RangeMin[0]) - return spellInfo->GetMinRange(); - if (!target) - return spellInfo->GetMinRange(true); - return spellInfo->GetMinRange(!IsHostileTo(target)); -} - uint32 Unit::GetCreatureType() const { if (GetTypeId() == TYPEID_PLAYER) @@ -11028,25 +10165,6 @@ void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProc SetCantProc(false); } -ObjectGuid Unit::GetCharmerOrOwnerOrOwnGUID() const -{ - if (ObjectGuid guid = GetCharmerOrOwnerGUID()) - return guid; - return GetGUID(); -} - -Player* Unit::GetSpellModOwner() const -{ - if (Player* player = const_cast(this)->ToPlayer()) - return player; - - if (HasUnitTypeMask(UNIT_MASK_PET | UNIT_MASK_TOTEM | UNIT_MASK_GUARDIAN | UNIT_MASK_HUNTER_PET | UNIT_MASK_MINION)) - if (Unit* owner = GetOwner()) - return owner->ToPlayer(); - - return nullptr; -} - ///----------Pet responses methods----------------- void Unit::SendPetActionFeedback(uint8 msg) { @@ -11243,6 +10361,95 @@ void Unit::RestoreDisplayId() SetDisplayId(GetNativeDisplayId()); } +void Unit::SendComboPoints() +{ + if (!IsPlayer()) + return; + + if (Unit* combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget)) + { + WorldPacket data; + Player* movingMe = GetCharmerOrSelfPlayer(); + if (movingMe != this) + { + data.Initialize(SMSG_PET_UPDATE_COMBO_POINTS, movingMe->GetPackGUID().size() + combotarget->GetPackGUID().size() + 1); + data << movingMe->GetPackGUID(); + } + else + data.Initialize(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size() + 1); + data << combotarget->GetPackGUID(); + data << uint8(m_comboPoints); + ToPlayer()->SendDirectMessage(&data); + } +} + +void Unit::AddComboPoints(Unit* target, int8 count, Spell* spell) +{ + if (!count) + return; + + int8* comboPoints = spell ? &spell->m_comboPointGain : &m_comboPoints; + + // without combo points lost (duration checked in aura) + RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS); + + if (target->GetGUID() == m_comboTarget) + *comboPoints += count; + else + { + if (m_comboTarget) + if (Unit* target2 = ObjectAccessor::GetUnit(*this, m_comboTarget)) + target2->RemoveComboPointHolder(GetGUID()); + + // Spells will always add value to m_comboPoints eventualy, so it must be cleared first + if (spell) + m_comboPoints = 0; + + m_comboTarget = target->GetGUID(); + *comboPoints = count; + + target->AddComboPointHolder(GetGUID()); + } + + if (*comboPoints > 5) + *comboPoints = 5; + else if (*comboPoints < 0) + *comboPoints = 0; + + if (!spell) + SendComboPoints(); +} + +void Unit::GainSpellComboPoints(int8 count) +{ + if (!count) + return; + + m_comboPoints += count; + if (m_comboPoints > 5) m_comboPoints = 5; + else if (m_comboPoints < 0) m_comboPoints = 0; + + SendComboPoints(); +} + +void Unit::ClearComboPoints() +{ + if (!m_comboTarget) + return; + + // without combopoints lost (duration checked in aura) + RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS); + + m_comboPoints = 0; + + SendComboPoints(); + + if (Unit* target = ObjectAccessor::GetUnit(*this, m_comboTarget)) + target->RemoveComboPointHolder(GetGUID()); + + m_comboTarget.Clear(); +} + void Unit::ClearComboPointHolders() { while (!m_ComboPointHolders.empty()) @@ -12692,9 +11899,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); } @@ -12703,6 +11907,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 4a06c80690..2072803445 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -19,7 +19,6 @@ #define __UNIT_H #include "Object.h" -#include "EventProcessor.h" #include "CombatManager.h" #include "SpellAuraDefines.h" #include "SpellDefines.h" @@ -403,15 +402,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; }; @@ -855,14 +854,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; } @@ -943,7 +938,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); } @@ -994,17 +989,9 @@ class TC_GAME_API Unit : public WorldObject virtual void SetSheath(SheathState 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; + uint32 GetFaction() const override { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); } + void SetFaction(uint32 faction) override { SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction); } - ReputationRank GetReactionTo(Unit const* target) const; - ReputationRank static GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target); - - 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 &units); @@ -1073,10 +1060,8 @@ class TC_GAME_API Unit : public WorldObject int32 CalculateAOEAvoidance(int32 damage, uint32 schoolMask, ObjectGuid const& casterGuid) const; - float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo = nullptr) const; - SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo); - SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo); - SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false, Optional effectMask = {}); + float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo = nullptr) 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; @@ -1169,12 +1154,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; - bool IsInWater() const; bool IsUnderWater() const; bool isInAccessiblePlaceFor(Creature const* c) const; @@ -1185,11 +1164,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); @@ -1254,7 +1228,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); } @@ -1274,20 +1248,12 @@ class TC_GAME_API Unit : public WorldObject bool IsControlledByPlayer() const { return m_ControlledByPlayer; } Player* GetControllingPlayer() const; - ObjectGuid GetCharmerOrOwnerGUID() const { return IsCharmed() ? GetCharmerGUID() : GetOwnerGUID(); } + ObjectGuid GetCharmerOrOwnerGUID() const override { return IsCharmed() ? GetCharmerGUID() : GetOwnerGUID(); } bool IsCharmedOwnedByPlayerOrPlayer() const { return GetCharmerOrOwnerOrOwnGUID().IsPlayer(); } - ObjectGuid GetCharmerOrOwnerOrOwnGUID() const; - - Unit* GetOwner() const; Guardian* GetGuardianPet() const; Minion* GetFirstMinion() const; Unit* GetCharmerOrOwner() const { return IsCharmed() ? GetCharmer() : GetOwner(); } - Unit* GetCharmerOrOwnerOrSelf() const; - Player* GetCharmerOrOwnerPlayerOrPlayerItself() const; - Player* GetAffectingPlayer() const; - - Player* GetSpellModOwner() const; void SetMinion(Minion *minion, bool apply); void GetAllMinionsByEntry(std::list& Minions, uint32 entry); @@ -1378,8 +1344,8 @@ class TC_GAME_API Unit : public WorldObject void RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, uint8 reqEffMask = 0, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); void RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); - 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 RemoveNotOwnLimitedTargetAuras(bool onPhaseChange = false); @@ -1422,7 +1388,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; @@ -1524,10 +1490,6 @@ class TC_GAME_API Unit : public WorldObject float m_modAttackSpeedPct[3]; - // Event handler - EventProcessor m_Events; - - // stat system // stat system void HandleStatFlatModifier(UnitMods unitMod, UnitModifierFlatType modifierType, float amount, bool apply); void ApplyStatPctModifier(UnitMods unitMod, UnitModifierPctType modifierType, float amount); @@ -1631,7 +1593,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, bool withSpellPowerPctMod = false) const; @@ -1659,14 +1620,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, Optional effectMask = {}) const; // redefined in Creature + virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const; // redefined in Creature 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; // redefined in Creature static bool IsDamageReducedByArmor(SpellSchoolMask damageSchoolMask, SpellInfo const* spellInfo = nullptr, int8 effIndex = -1); static uint32 CalcArmorReducedDamage(Unit const* attacker, Unit* victim, const uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK, uint8 attackerLevel = 0); @@ -1683,12 +1644,6 @@ class TC_GAME_API Unit : public WorldObject void SetSpeedRateReal(UnitMoveType mtype, float rate); public: - 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 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); - // Makes the unit follow the given target. Use this function above using the MotionMaster::MoveFollow for default follow behaivior. void FollowTarget(Unit* target); @@ -1727,9 +1682,19 @@ class TC_GAME_API Unit : public WorldObject void SetControlled(bool apply, UnitState state); void ApplyControlStatesIfNeeded(); + ///-----------Combo point system------------------- + void AddComboPoints(Unit* target, int8 count, Spell* spell = nullptr); + void GainSpellComboPoints(int8 count); + void ClearComboPoints(); + void SendComboPoints(); void AddComboPointHolder(ObjectGuid lowguid) { m_ComboPointHolders.insert(lowguid); } void RemoveComboPointHolder(ObjectGuid lowguid) { m_ComboPointHolders.erase(lowguid); } void ClearComboPointHolders(); + uint8 GetComboPoints() const { return m_comboPoints; } + ObjectGuid GetComboTarget() const { return m_comboTarget; } + + ObjectGuid m_comboTarget; + int8 m_comboPoints; ///----------Pet responses methods----------------- void SendPetActionFeedback (uint8 msg); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 02fb61277e..d9ee7c4269 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -425,17 +425,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(CreatureMapType&); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index f12609382d..5335b33d44 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -636,29 +636,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 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 bool operator()(NOT_INTERESTED*) { return false; } - protected: - SpellInfo const* i_spellInfo; - WorldObjectSpellTargetCheck i_check; }; // WorldObject do classes @@ -678,25 +675,24 @@ 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/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index 7b84437552..a9513b71ff 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -3981,7 +3981,7 @@ bool Guild::HasAchieved(uint32 achievementId) const return m_achievementMgr->HasAchieved(achievementId); } -void Guild::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit* unit, Player* player, GameObject* go) +void Guild::UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject* unit, Player* player, GameObject* go) { m_achievementMgr->UpdateAchievementCriteria(type, miscValue1, miscValue2, miscValue3, unit, player, go); } diff --git a/src/server/game/Guilds/Guild.h b/src/server/game/Guilds/Guild.h index 403ba42366..6adbbbc346 100644 --- a/src/server/game/Guilds/Guild.h +++ b/src/server/game/Guilds/Guild.h @@ -891,7 +891,7 @@ public: void ResetTimes(bool weekly); bool HasAchieved(uint32 achievementId) const; - void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, Unit* unit, Player* player, GameObject* go = nullptr); + void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint64 miscValue1, uint64 miscValue2, uint64 miscValue3, WorldObject* ref, Player* player, GameObject* go = nullptr); inline void SetAchievementPointsFor(ObjectGuid guid, uint32 achievementPoint) { diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 5ae822748c..da639116c3 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -348,8 +348,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; @@ -363,17 +361,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.CasterGUID.IsEmpty()) { - if (createInfo._owner->GetGUID() == createInfo.CasterGUID) - createInfo.Caster = createInfo._owner->ToUnit(); + // world gameobjects can't own auras and they send empty casterguid + // checked on sniffs with spell 22247 + if (createInfo.CasterGUID.IsGameObject()) + { + createInfo.Caster = nullptr; + createInfo.CasterGUID.Clear(); + } else - createInfo.Caster = ObjectAccessor::GetUnit(*createInfo._owner, createInfo.CasterGUID); + { + if (createInfo._owner->GetGUID() == createInfo.CasterGUID) + createInfo.Caster = createInfo._owner->ToUnit(); + else + createInfo.Caster = ObjectAccessor::GetUnit(*createInfo._owner, createInfo.CasterGUID); + } } - else + else if (createInfo.Caster) createInfo.CasterGUID = createInfo.Caster->GetGUID(); // check if aura can be owned by owner @@ -425,7 +431,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 a140890d57..59c17fa79c 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -63,7 +63,7 @@ #include "WorldPacket.h" #include "WorldSession.h" -extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; +extern SpellEffectHandlerFn SpellEffectHandlers[TOTAL_SPELL_EFFECTS]; SpellDestination::SpellDestination() { @@ -492,9 +492,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) @@ -531,28 +531,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) @@ -579,7 +557,7 @@ protected: Spell* m_Spell; }; -Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID) : +Spell::Spell(WorldObject* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, ObjectGuid originalCasterGUID) : m_spellInfo(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) @@ -621,11 +599,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()->GetDamageType()); + if (m_attackType == RANGED_ATTACK) + if ((playerCaster->getClassMask() & CLASSMASK_WAND_USERS) != 0) + if (Item* pItem = playerCaster->GetWeaponForAttack(RANGED_ATTACK)) + m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetTemplate()->GetDamageType()); + } if (originalCasterGUID) m_originalCasterGUID = originalCasterGUID; @@ -633,7 +614,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); @@ -667,6 +648,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_cast_count = 0; m_glyphIndex = 0; m_triggeredByAuraSpell = nullptr; + unitCaster = nullptr; _spellAura = nullptr; _dynObjAura = nullptr; @@ -684,7 +666,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_IS_ABILITY) + m_canReflect = caster->IsUnit() && m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR0_IS_ABILITY) && !m_spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REFLECTED) && !m_spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !m_spellInfo->IsPassive(); @@ -755,11 +737,11 @@ void Spell::InitExplicitTargets(SpellCastTargets const& targets) } // try to use attacked unit as a target else if ((m_caster->GetTypeId() == TYPEID_UNIT) && neededTargets & (TARGET_FLAG_UNIT_ENEMY | TARGET_FLAG_UNIT)) - unit = m_caster->GetVictim(); + unit = m_caster->ToUnit()->GetVictim(); // didn't find anything - let's use self as target if (!unit && neededTargets & (TARGET_FLAG_UNIT_RAID | TARGET_FLAG_UNIT_PARTY | TARGET_FLAG_UNIT_ALLY)) - unit = m_caster; + unit = m_caster->ToUnit(); m_targets.SetUnitTarget(unit); } @@ -797,10 +779,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: @@ -808,10 +790,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)) @@ -1305,7 +1287,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: @@ -1378,7 +1360,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge case TARGET_UNIT_TARGET_ALLY_OR_RAID: if (Unit* targetedUnit = m_targets.GetUnitTarget()) { - if (!m_caster->IsInRaidWith(targetedUnit)) + if (!m_caster->IsUnit() || !m_caster->ToUnit()->IsInRaidWith(targetedUnit)) targets.push_back(m_targets.GetUnitTarget()); else SearchAreaTargets(targets, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions); @@ -1619,14 +1601,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: @@ -1636,8 +1621,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; case TARGET_UNIT_TARGET_TAP_LIST: if (Creature* creatureCaster = m_caster->ToCreature()) @@ -1649,8 +1635,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) @@ -1741,7 +1732,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 @@ -1749,15 +1740,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::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()) @@ -1767,14 +1759,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; @@ -1788,17 +1780,14 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex, SpellImplicitTarge if (dist2d > bestDist) { - float x = m_targets.GetSrcPos()->m_positionX + std::cos(m_caster->GetOrientation()) * bestDist; - float y = m_targets.GetSrcPos()->m_positionY + std::sin(m_caster->GetOrientation()) * bestDist; + float x = m_targets.GetSrcPos()->m_positionX + std::cos(unitCaster->GetOrientation()) * bestDist; + float y = m_targets.GetSrcPos()->m_positionY + std::sin(unitCaster->GetOrientation()) * bestDist; float z = m_targets.GetSrcPos()->m_positionZ + bestDist * (a * bestDist + b); - SpellDestination dest(x, y, z, m_caster->GetOrientation()); + SpellDestination dest(x, y, z, unitCaster->GetOrientation()); CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); m_targets.ModDst(dest); } - - if (Vehicle* veh = m_caster->GetVehicleKit()) - veh->SetLastShootPos(*m_targets.GetDstPos()); } void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) @@ -1808,9 +1797,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 @@ -1938,7 +1927,7 @@ uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionConta } template -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; @@ -1974,7 +1963,7 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec return target; } -void Spell::SearchAreaTargets(std::list& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +void Spell::SearchAreaTargets(std::list& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) { uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); if (!containerTypeMask) @@ -2085,7 +2074,7 @@ GameObject* Spell::SearchSpellFocus() GameObject* focus = nullptr; Trinity::GameObjectFocusCheck check(m_caster, m_spellInfo->RequiresSpellFocus); Trinity::GameObjectSearcher searcher(m_caster, focus, check); - SearchTargets > (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; } @@ -2193,7 +2182,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 @@ -2238,10 +2227,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. @@ -2269,8 +2256,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)); @@ -2434,7 +2422,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; @@ -2449,7 +2437,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) @@ -2462,7 +2450,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; } @@ -2477,7 +2465,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; @@ -2506,7 +2494,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; @@ -2521,7 +2509,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; @@ -2681,8 +2669,9 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) // Failed Pickpocket, reveal rogue if (MissCondition == SPELL_MISS_RESIST && spell->m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && spell->unitTarget->GetTypeId() == TYPEID_UNIT) { - spell->m_caster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting); - spell->unitTarget->ToCreature()->EngageWithTarget(spell->m_caster); + Unit* unitCaster = ASSERT_NOTNULL(spell->m_caster->ToUnit()); + unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Interacting); + spell->unitTarget->ToCreature()->EngageWithTarget(unitCaster); } } @@ -2732,11 +2721,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); @@ -2750,7 +2748,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; @@ -2758,8 +2756,19 @@ void Spell::GOTargetInfo::DoTargetSpellHit(Spell* spell, uint8 effIndex) spell->HandleEffects(nullptr, nullptr, go, nullptr, 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(); @@ -2851,8 +2860,13 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& } } + // 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 @@ -2890,7 +2904,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) { @@ -2908,7 +2922,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; } @@ -2918,10 +2932,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; @@ -2935,7 +2952,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) @@ -2961,11 +2978,11 @@ 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_ATTR8_HASTE_AFFECTS_DURATION)) hitInfo.AuraDuration = int32(hitInfo.AuraDuration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED)); @@ -3074,7 +3091,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; @@ -3120,20 +3137,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 || m_spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA_2) + 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_spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA_2) { - 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; + } } } } @@ -3149,23 +3169,20 @@ 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; } - // Don't allow to interrupt channeled spells that are not allowed to be cancelled - if (Spell* curSpell = m_caster->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + // 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) { - if (curSpell->m_spellInfo->HasAttribute(SPELL_ATTR0_NO_AURA_CANCEL) && !m_spellInfo->HasAttribute(SPELL_ATTR4_CAN_CAST_WHILE_CASTING)) - { - SendCastResult(SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW); - finish(false); - return; - } + SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); + finish(false); + return; } if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster)) @@ -3180,7 +3197,7 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered m_powerCost = m_CastItem ? 0 : m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask, this); // Set combo point requirement - if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem || !m_caster->IsMovedByClient()) + if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem) m_needComboPoints = false; MountResult mountResult = MountResult::Ok; @@ -3229,15 +3246,15 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered m_casttime = 0; // Set cast time to 0 if .cheat casttime is enabled. } else - m_casttime = m_spellInfo->CalcCastTime(m_caster->getLevel(), this); + m_casttime = m_spellInfo->CalcCastTime(m_caster->IsUnit() ? m_caster->ToUnit()->getLevel() : 0, this); // 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) // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() && - m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo) && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature())) - if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) + if (((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && + m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) && !m_caster->ToPlayer()->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo) && (!m_caster->ToPlayer()->IsCharmed() || !m_caster->ToPlayer()->GetCharmerGUID().IsCreature())) + if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && (!m_caster->ToPlayer()->IsCharmed() || !m_caster->ToPlayer()->GetCharmerGUID().IsCreature()) && m_caster->ToPlayer()->isMoving() && m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement)) { // 1. Has casttime, 2. Or doesn't have flag to allow movement during channel if (m_casttime || !m_spellInfo->IsMoveAllowedChannel()) @@ -3275,12 +3292,15 @@ void Spell::prepare(SpellCastTargets const& targets, AuraEffect const* triggered cast(true); else { - // stealth must be removed at cast starting (at show channel bar) - // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && m_spellInfo->IsBreakingStealth() && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS)) - m_caster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action); + if (Unit* unitCaster = m_caster->ToUnit()) + { + // stealth must be removed at cast starting (at show channel bar) + // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && m_spellInfo->IsBreakingStealth() && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS)) + unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action); - m_caster->SetCurrentCastSpell(this); + unitCaster->SetCurrentCastSpell(this); + } SendSpellStart(); if (!(_triggeredCastFlags & TRIGGERED_IGNORE_GCD)) @@ -3336,7 +3356,7 @@ void Spell::cancel(Spell* interruptingSpell /* = nullptr */) } if (removeOwnedAura) - 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, AuraRemoveFlags::ByCancel); } @@ -3359,9 +3379,13 @@ void Spell::cancel(Spell* interruptingSpell /* = nullptr */) 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; @@ -3491,11 +3515,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; + } } } } @@ -3518,9 +3544,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(); @@ -3578,8 +3605,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 { @@ -3594,7 +3622,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); } @@ -3605,8 +3636,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); @@ -3675,17 +3706,18 @@ void Spell::handle_immediate() int32 duration = m_spellInfo->CalcDuration(m_caster); if (duration > 0) { - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags, m_spellInfo->ChannelInterruptFlags2); m_channeledDuration = duration; SendChannelStart(duration); } else if (duration == -1) { - m_spellState = SPELL_STATE_CASTING; - m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags, m_spellInfo->ChannelInterruptFlags2); SendChannelStart(duration); } + + m_spellState = SPELL_STATE_CASTING; + + // GameObjects shouldn't cast channeled spells + ASSERT_NOTNULL(m_caster->ToUnit())->AddInterruptMask(m_spellInfo->ChannelInterruptFlags, m_spellInfo->ChannelInterruptFlags2); } PrepareTargetProcessing(); @@ -3854,27 +3886,30 @@ void Spell::_handle_immediate_phase() void Spell::_handle_finish_phase() { - if (m_caster->IsMovedByClient()) + if (Unit* unitCaster = m_caster->ToUnit()) { - Player* mover = m_caster->GetGameClientMovingMe()->GetBasePlayer(); - // Take for real after all targets are processed - if (m_needComboPoints) - mover->ClearComboPoints(); + if (unitCaster->IsMovedByClient()) + { + Player* mover = unitCaster->GetGameClientMovingMe()->GetBasePlayer(); + // Take for real after all targets are processed + if (m_needComboPoints) + mover->ClearComboPoints(); - // Real add combo points from effects - if (m_comboPointGain) - mover->GainSpellComboPoints(m_comboPointGain); + // Real add combo points from effects + if (m_comboPointGain) + mover->GainSpellComboPoints(m_comboPointGain); - if (m_spellInfo->PowerType == POWER_HOLY_POWER && mover->getClass() == CLASS_PALADIN) - HandleHolyPower(mover); - } + if (m_spellInfo->PowerType == POWER_HOLY_POWER && mover->getClass() == CLASS_PALADIN) + HandleHolyPower(mover); + } - 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(*m_caster, m_targets.GetOrigUnitTargetGUID())) + unitCaster->HandleProcExtraAttackFor(victim); + else + unitCaster->m_extraAttacks = 0; + } } // Handle procs on finish @@ -3908,7 +3943,10 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { - m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); + if (!m_caster->IsUnit()) + return; + + m_caster->ToUnit()->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); } void Spell::update(uint32 difftime) @@ -3931,9 +3969,9 @@ void Spell::update(uint32 difftime) // check if the player caster has moved before the spell finished // with the exception of spells affected with SPELL_AURA_CAST_WHILE_WALKING effect if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - m_caster->isMoving() && (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement) && - (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR))) && - !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) + m_caster->ToPlayer()->isMoving() && (m_spellInfo->InterruptFlags.HasFlag(SpellInterruptFlags::Movement) && + (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->ToPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR))) && + !m_caster->ToPlayer()->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo)) { // don't cancel for melee, autorepeat, triggered and instant spells if (!m_spellInfo->IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->IsMoveAllowedChannel())) @@ -3941,7 +3979,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(); } } @@ -3975,7 +4013,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, AuraRemoveFlags::ByCancel); } @@ -4007,50 +4045,54 @@ void Spell::update(uint32 difftime) void Spell::finish(bool ok, bool sendChannelUpdate /* = false */) { - if (!m_caster) - return; - if (m_spellState == SPELL_STATE_FINISHED) return; m_spellState = SPELL_STATE_FINISHED; + if (!m_caster) + return; + + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + if (m_spellInfo->IsChanneled()) - m_caster->UpdateInterruptMask(); + unitCaster->UpdateInterruptMask(); if (sendChannelUpdate) SendChannelUpdate(0); - 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->GetCharmed()) + if (Unit* charm = unitCaster->GetCharmed()) 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->ReleaseSpellFocus(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); // Avoid infinite loops with setDeathState(JUST_DIED) being called over and over // It might make sense to do this check in Unit::setDeathState() and all overloaded functions - if (m_caster->getDeathState() != JUST_DIED) - m_caster->setDeathState(JUST_DIED); + if (unitCaster->getDeathState() != JUST_DIED) + unitCaster->setDeathState(JUST_DIED); return; } } @@ -4058,7 +4100,7 @@ void Spell::finish(bool ok, bool sendChannelUpdate /* = false */) 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)->IsAffectingSpell(m_spellInfo)) @@ -4070,23 +4112,23 @@ void Spell::finish(bool ok, bool sendChannelUpdate /* = false */) 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_CANCELS_AUTO_ATTACK_COMBAT)) - 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*/) @@ -4319,8 +4361,14 @@ void Spell::SendSpellStart() //TC_LOG_DEBUG("spells", "Sending SMSG_SPELL_START id=%u", m_spellInfo->Id); uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY; - uint32 schoolImmunityMask = m_casttime != 0 ? m_caster->GetSchoolImmunityMask() : 0; - uint32 mechanicImmunityMask = m_casttime != 0 ? m_spellInfo->GetMechanicImmunityMask(m_caster) : 0; + uint32 schoolImmunityMask = 0; + uint32 mechanicImmunityMask = 0; + + if (Unit* unitCaster = m_caster->ToUnit()) + { + schoolImmunityMask = m_casttime != 0 ? unitCaster->GetSchoolImmunityMask() : 0; + mechanicImmunityMask = m_casttime != 0 ? unitCaster->GetMechanicImmunityMask() : 0; + } if (schoolImmunityMask || mechanicImmunityMask) castFlags |= CAST_FLAG_IMMUNITY; @@ -4332,7 +4380,7 @@ void Spell::SendSpellStart() castFlags |= CAST_FLAG_PROJECTILE; if ((m_caster->GetTypeId() == TYPEID_PLAYER || - (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) + (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) && m_spellInfo->PowerType != POWER_HEALTH) castFlags |= CAST_FLAG_POWER_LEFT_SELF; @@ -4361,7 +4409,7 @@ void Spell::SendSpellStart() m_targets.Write(castData.Target); if (castFlags & CAST_FLAG_POWER_LEFT_SELF) - castData.RemainingPower = m_caster->GetPower(Powers(m_spellInfo->PowerType)); + castData.RemainingPower = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower(Powers(m_spellInfo->PowerType)); if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list { @@ -4427,12 +4475,12 @@ void Spell::SendSpellGo() castFlags |= CAST_FLAG_PROJECTILE; // arrows/bullets visual if ((m_caster->GetTypeId() == TYPEID_PLAYER || - (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet())) + (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsPet())) && 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)) @@ -4476,7 +4524,7 @@ void Spell::SendSpellGo() } if (castFlags & CAST_FLAG_POWER_LEFT_SELF) - castData.RemainingPower = m_caster->GetPower((Powers)m_spellInfo->PowerType); + castData.RemainingPower = ASSERT_NOTNULL(m_caster->ToUnit())->GetPower((Powers)m_spellInfo->PowerType); if (castFlags & CAST_FLAG_RUNE_LIST) // rune cooldowns list { @@ -4576,7 +4624,7 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) uint32 ammoInventoryType = 0; uint32 ammoDisplayID = 0; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (Player const* playerCaster = m_caster->ToPlayer()) { Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK); if (pItem) @@ -4584,18 +4632,18 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) ammoInventoryType = pItem->GetTemplate()->GetInventoryType(); if (ammoInventoryType == INVTYPE_THROWN) ammoDisplayID = pItem->GetTemplate()->GetDisplayID(); - else if (m_caster->HasAura(46699)) // Requires No Ammo + else if (playerCaster->HasAura(46699)) // Requires No Ammo { ammoDisplayID = 5996; // normal arrow ammoInventoryType = INVTYPE_AMMO; } } } - else + else if (Unit const* unitCaster = m_caster->ToUnit()) { for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i) { - if (uint32 item_id = m_caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i)) + if (uint32 item_id = unitCaster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i)) { if (ItemEntry const* itemEntry = sItemStore.LookupEntry(item_id)) { @@ -4632,7 +4680,10 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo) void Spell::UpdateSpellHealPrediction(WorldPackets::Spells::SpellHealPrediction& predict) { - Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster; + Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster->ToUnit(); + if (!target) + return; + predict.Type = 0; /* @@ -4647,38 +4698,41 @@ void Spell::UpdateSpellHealPrediction(WorldPackets::Spells::SpellHealPrediction& } */ - for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) + if (Unit* unitCaster = m_caster->ToUnit()) { - SpellEffectInfo const& effect = m_spellInfo->Effects[effIndex]; - if (!effect.IsEffect()) - continue; - - switch (effect.Effect) + for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) { - case SPELL_EFFECT_HEAL: - case SPELL_EFFECT_HEAL_PCT: - predict.Points += m_caster->SpellHealingBonusDone(target, m_spellInfo, effect.CalcValue(m_caster, nullptr, target), HEAL, effIndex); - break; - case SPELL_EFFECT_APPLY_AURA: + SpellEffectInfo const& effect = m_spellInfo->Effects[effIndex]; + if (!effect.IsEffect()) + continue; + + switch (effect.Effect) { - switch (effect.ApplyAuraName) + case SPELL_EFFECT_HEAL: + case SPELL_EFFECT_HEAL_PCT: + predict.Points += unitCaster->SpellHealingBonusDone(target, m_spellInfo, effect.CalcValue(unitCaster, nullptr, target), HEAL, effIndex); + break; + case SPELL_EFFECT_APPLY_AURA: { - case SPELL_AURA_PERIODIC_HEAL: - predict.Points += m_caster->SpellHealingBonusDone(target, m_spellInfo, effect.CalcValue(m_caster, nullptr, target), HEAL, effIndex) * m_spellInfo->GetMaxTicks(); - break; - case SPELL_AURA_PERIODIC_TRIGGER_SPELL: - if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(effect.TriggerSpell)) - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (spell->Effects[i].Effect == SPELL_EFFECT_HEAL || spell->Effects[i].Effect == SPELL_EFFECT_HEAL_PCT) - predict.Points += m_caster->SpellHealingBonusDone(target, spell, spell->Effects[i].CalcValue(m_caster, nullptr, target), HEAL, i) * m_spellInfo->GetMaxTicks(); - break; - default: - break; + switch (effect.ApplyAuraName) + { + case SPELL_AURA_PERIODIC_HEAL: + predict.Points += unitCaster->SpellHealingBonusDone(target, m_spellInfo, effect.CalcValue(unitCaster, nullptr, target), HEAL, effIndex) * m_spellInfo->GetMaxTicks(); + break; + case SPELL_AURA_PERIODIC_TRIGGER_SPELL: + if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(effect.TriggerSpell)) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (spell->Effects[i].Effect == SPELL_EFFECT_HEAL || spell->Effects[i].Effect == SPELL_EFFECT_HEAL_PCT) + predict.Points += unitCaster->SpellHealingBonusDone(target, spell, spell->Effects[i].CalcValue(unitCaster, nullptr, target), HEAL, i) * m_spellInfo->GetMaxTicks(); + break; + default: + break; + } + break; } - break; + default: + break; } - default: - break; } } } @@ -4841,14 +4895,19 @@ 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); @@ -4856,6 +4915,11 @@ void Spell::SendChannelUpdate(uint32 time) 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()) { @@ -4879,8 +4943,8 @@ void Spell::SendChannelStart(uint32 duration) channelTarget = m_caster->GetGUID(); uint32 castFlags = CAST_FLAG_HAS_TRAJECTORY; - uint32 schoolImmunityMask = m_caster->GetSchoolImmunityMask(); - uint32 mechanicImmunityMask = m_spellInfo->GetMechanicImmunityMask(m_caster); + uint32 schoolImmunityMask = unitCaster->GetSchoolImmunityMask(); + uint32 mechanicImmunityMask = m_spellInfo->GetMechanicImmunityMask(unitCaster); if (schoolImmunityMask || mechanicImmunityMask) castFlags |= CAST_FLAG_IMMUNITY; @@ -4915,7 +4979,7 @@ void Spell::SendChannelStart(uint32 duration) m_timer = duration; if (channelTarget) { - m_caster->SetChannelObjectGuid(channelTarget); + unitCaster->SetChannelObjectGuid(channelTarget); if (Creature* creatureCaster = m_caster->ToCreature()) if (!creatureCaster->HasSpellFocus(this)) @@ -4928,12 +4992,14 @@ void Spell::SendChannelStart(uint32 duration) void Spell::SendResurrectRequest(Player* target) { // Get creature name for resurrection. Player names are being received via ResurrectOffererGUID and send a nul-terminator only. - std::string const sentName(m_caster->IsPlayer() ? "" : m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex())); + std::string sentName; + if (m_caster->GetTypeId() != TYPEID_PLAYER) + sentName = m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex()); WorldPackets::Spells::ResurrectRequest resurrectRequest; resurrectRequest.ResurrectOffererGUID = m_caster->GetGUID(); resurrectRequest.Name = sentName; - resurrectRequest.Sickness = !m_caster->IsPlayer() && m_caster->IsSpiritHealer(); // "you'll be afflicted with resurrection sickness" + resurrectRequest.Sickness = m_caster->IsUnit() && m_caster->ToUnit()->IsSpiritHealer(); // "you'll be afflicted with resurrection sickness" resurrectRequest.UseTimer = !m_spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_RESURRECTION_TIMER); resurrectRequest.SpellID = m_spellInfo->Id; target->SendDirectMessage(resurrectRequest.Write()); @@ -5006,19 +5072,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 && m_spellInfo->HasAttribute(SPELL_ATTR1_DISCOUNT_POWER_ON_MISS)) + if (unitCaster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR1_DISCOUNT_POWER_ON_MISS)) { if (ObjectGuid targetGUID = m_targets.GetUnitTargetGUID()) { @@ -5030,7 +5101,7 @@ void Spell::TakePower() { hit = false; //lower spell cost on fail (by talent aura) - if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner()) + if (Player* modOwner = unitCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); } } @@ -5059,7 +5130,7 @@ void Spell::TakePower() // health as power used if (powerType == POWER_HEALTH) { - m_caster->ModifyHealth(-(int32)m_powerCost); + unitCaster->ModifyHealth(-(int32)m_powerCost); return; } @@ -5069,7 +5140,7 @@ void Spell::TakePower() return; } - m_caster->ModifyPower(powerType, -m_powerCost); + unitCaster->ModifyPower(powerType, -m_powerCost); } void Spell::TakeAmmo() @@ -5151,7 +5222,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) const void Spell::TakeRunePower(SpellMissInfo hitInfo) { - if (!m_caster->IsPlayer() || m_caster->getClass() != CLASS_DEATH_KNIGHT) + if (!m_caster->IsPlayer() || m_caster->ToPlayer()->getClass() != CLASS_DEATH_KNIGHT) return; SpellRuneCostEntry const* runeCostData = sSpellRuneCostStore.LookupEntry(m_spellInfo->RuneCostID); @@ -5235,7 +5306,7 @@ void Spell::TakeRunePower(SpellMissInfo hitInfo) { if (int32 runicPowerGain = runeCostData->RunicPower * sWorld->getRate(RATE_POWER_RUNICPOWER_INCOME)) { - Unit::AuraEffectList const& bonusPct = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_RUNE_REGEN_SPEED); + Unit::AuraEffectList const& bonusPct = player->GetAuraEffectsByType(SPELL_AURA_MOD_RUNE_REGEN_SPEED); for (auto i = bonusPct.begin(); i != bonusPct.end(); ++i) { // Improved Frost Presence @@ -5311,6 +5382,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; @@ -5321,7 +5397,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; } @@ -5341,20 +5417,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())); @@ -5384,7 +5460,7 @@ void Spell::HandleHolyPower(Player* caster) if (hit) { modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, m_powerCost, this); - m_caster->ModifyPower(POWER_HOLY_POWER, -m_powerCost); + caster->ModifyPower(POWER_HOLY_POWER, -m_powerCost); } } @@ -5396,19 +5472,19 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGoT gameObjTarget = pGoTarget; corpseTarget = pCorpseTarget; destTarget = &m_destTargets[i]._position; + unitCaster = m_originalCaster ? m_originalCaster : m_caster->ToUnit(); - uint8 eff = m_spellInfo->Effects[i].Effect; + uint8 effect = m_spellInfo->Effects[i].Effect; - TC_LOG_DEBUG("spells", "Spell: %u Effect : %u", m_spellInfo->Id, eff); + TC_LOG_DEBUG("spells", "Spell: %u Effect : %u", m_spellInfo->Id, effect); damage = CalculateDamage(i, unitTarget); - bool preventDefault = CallScriptEffectHandlers((SpellEffIndex)i, mode); + SpellEffIndex effIndex = static_cast(i); + bool preventDefault = CallScriptEffectHandlers(effIndex, mode); - if (!preventDefault && eff < TOTAL_SPELL_EFFECTS) - { - (this->*SpellEffects[eff])((SpellEffIndex)i); - } + if (!preventDefault) + (this->*SpellEffectHandlers[effect])(effIndex); } /*static*/ Spell const* Spell::ExtractSpellFromEvent(BasicEvent* event) @@ -5422,7 +5498,7 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGoT SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/, MountResult* mountResult /*= nullptr*/) { // check death state - if (!m_caster->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_ALLOW_CAST_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) + if (m_caster->ToUnit() && !m_caster->ToUnit()->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_ALLOW_CAST_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) return SPELL_FAILED_CASTER_DEAD; // Prevent cheating in case the player has an immunity effect and tries to interact with a non-allowed gameobject. The error message is handled by the client so we don't report anything here @@ -5435,21 +5511,21 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // check cooldowns to prevent cheating if (!m_spellInfo->IsPassive()) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (Player const* playerCaster = m_caster->ToPlayer()) { //can cast triggered (by aura only?) spells while have this flag if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE)) { // These two auras check SpellFamilyName defined by dbc class data instead of current spell SpellFamilyName - if (m_caster->HasAuraType(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES) + if (playerCaster->HasAuraType(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES) && !m_spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !m_spellInfo->HasEffect(SPELL_EFFECT_ATTACK) - && !m_caster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(m_caster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) + && !playerCaster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(playerCaster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; - if (m_caster->HasAuraType(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES)) + if (playerCaster->HasAuraType(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES)) { - if (!m_caster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(m_caster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) + if (!playerCaster->HasAuraTypeWithFamilyFlags(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES, sChrClassesStore.AssertEntry(playerCaster->getClass())->SpellClassSet, m_spellInfo->SpellFamilyFlags)) { if (m_spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) || m_spellInfo->IsNextMeleeSwingSpell() @@ -5466,11 +5542,11 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // check if we are using a potion in combat for the 2nd+ time. Cooldown is added only after caster gets out of combat - if (!IsIgnoringCooldowns() && m_caster->ToPlayer()->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) + if (!IsIgnoringCooldowns() && playerCaster->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) return SPELL_FAILED_NOT_READY; } - if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) + if (m_caster->ToUnit() && !m_caster->ToUnit()->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry, IsIgnoringCooldowns())) { if (IsTriggered() || m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; @@ -5479,7 +5555,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } } - if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && !m_caster->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) + if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && m_caster->ToUnit() && !m_caster->ToUnit()->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) { m_customError = SPELL_CUSTOM_ERROR_GM_ONLY; return SPELL_FAILED_CUSTOM_ERROR; @@ -5512,90 +5588,93 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!instance->GetCombatResurrectionCharges()) return SPELL_FAILED_IN_COMBAT_RES_LIMIT_REACHED; - // only check at first call, Stealth auras are already removed at second call - // for now, ignore triggered spells - if (strict && !(_triggeredCastFlags & TRIGGERED_IGNORE_SHAPESHIFT)) + if (Unit* unitCaster = m_caster->ToUnit()) { - bool checkForm = true; - // Ignore form req aura - Unit::AuraEffectList const& ignore = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); - for (AuraEffect const* aurEff : ignore) + // only check at first call, Stealth auras are already removed at second call + // for now, ignore triggered spells + if (strict && !(_triggeredCastFlags & TRIGGERED_IGNORE_SHAPESHIFT)) { - if (!aurEff->IsAffectingSpell(m_spellInfo)) - continue; - - 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 (m_spellInfo->HasAttribute(SPELL_ATTR0_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) - return SPELL_FAILED_ONLY_STEALTHED; - } - } - - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_BLOCKED_SPELL_FAMILY)) - for (AuraEffect const* blocked : m_caster->GetAuraEffectsByType(SPELL_AURA_BLOCK_SPELL_FAMILY)) - if (!blocked->GetMiscValue() || uint32(blocked->GetMiscValue()) == 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)->IsAffectingSpell(m_spellInfo)) - { - m_needComboPoints = false; - if ((*j)->GetMiscValue() == 1) + bool checkForm = true; + // Ignore form req aura + Unit::AuraEffectList const& ignore = unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); + for (AuraEffect const* aurEff : ignore) { - reqCombat=false; + if (!aurEff->IsAffectingSpell(m_spellInfo)) + continue; + + checkForm = false; break; } + + 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) && !(unitCaster->HasStealthAura())) + return SPELL_FAILED_ONLY_STEALTHED; + } } - } - // 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; + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_BLOCKED_SPELL_FAMILY)) + for (AuraEffect const* blocked : unitCaster->GetAuraEffectsByType(SPELL_AURA_BLOCK_SPELL_FAMILY)) + if (!blocked->GetMiscValue() || uint32(blocked->GetMiscValue()) == m_spellInfo->SpellFamilyName) + return SPELL_FAILED_SPELL_UNAVAILABLE; - // 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; + 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) + { + if ((*j)->IsAffectingSpell(m_spellInfo)) + { + m_needComboPoints = false; + if ((*j)->GetMiscValue() == 1) + { + reqCombat = false; + break; + } + } + } - if (reqCombat && m_caster->IsInCombat() && !m_spellInfo->CanBeUsedInCombat()) - return SPELL_FAILED_AFFECTING_COMBAT; - } + // 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; - // cancel autorepeat spells if cast start when moving - // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) - // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && !m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo) && (!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->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing))) - return SPELL_FAILED_MOVING; - } + // 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; - // 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; + if (reqCombat && unitCaster->IsInCombat() && !m_spellInfo->CanBeUsedInCombat()) + return SPELL_FAILED_AFFECTING_COMBAT; + } + + // cancel autorepeat spells if cast start when moving + // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) + // Do not cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect + if (unitCaster->GetTypeId() == TYPEID_PLAYER && unitCaster->ToPlayer()->isMoving() && !unitCaster->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, m_spellInfo) && (!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[0].Effect != SPELL_EFFECT_STUCK) && + (IsAutoRepeat() || m_spellInfo->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing))) + return SPELL_FAILED_MOVING; + } + + // Check vehicle flags + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE)) + { + SpellCastResult vehicleCheck = m_spellInfo->CheckVehicle(unitCaster); + if (vehicleCheck != SPELL_CAST_OK) + return vehicleCheck; + } } // check spell cast conditions from database @@ -5627,8 +5706,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()); @@ -5638,7 +5717,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; @@ -5652,11 +5731,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(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)) @@ -5676,23 +5756,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 = 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; @@ -5706,7 +5789,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // zone check - if (m_caster->GetTypeId() == TYPEID_UNIT || !m_caster->ToPlayer()->IsGameMaster()) + if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_caster->ToPlayer()->IsGameMaster()) { uint32 zone, area; m_caster->GetZoneAndAreaId(zone, area); @@ -5717,13 +5800,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_ALLOW_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_ALLOW_WHILE_MOUNTED)) + { + if (m_caster->ToPlayer()->IsInFlight()) + return SPELL_FAILED_NOT_ON_TAXI; + else + return SPELL_FAILED_NOT_MOUNTED; + } } // check spell focus object @@ -5854,7 +5939,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint { 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; } @@ -5878,7 +5963,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (!pet->GetCurrentFoodBenefitLevel(foodItem->GetTemplate()->GetBaseItemLevel())) return SPELL_FAILED_FOOD_LOWLEVEL; - if (m_caster->IsInCombat() || pet->IsInCombat()) + if (m_caster->ToPlayer()->IsInCombat() || pet->IsInCombat()) return SPELL_FAILED_AFFECTING_COMBAT; break; @@ -5895,14 +5980,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()) @@ -5912,13 +6001,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(m_caster); + m_preGeneratedPath = Trinity::make_unique(unitCaster); m_preGeneratedPath->SetPathLengthLimit(range); // first try with raycast, if it fails fall back to normal path @@ -6022,8 +6111,11 @@ 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; @@ -6032,17 +6124,21 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // 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->Control) { 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; [[fallthrough]]; // check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET*/ case SUMMON_CATEGORY_PUPPET: - if (m_caster->GetCharmedGUID()) + if (unitCaster->GetCharmedGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -6061,19 +6157,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->GetCharmedGUID()) + if (unitCaster->GetCharmedGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } @@ -6082,7 +6182,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // Single target summoning case if (m_targets.GetOrigUnitTargetGUID().IsEmpty()) { - if (!m_caster->GetTarget()) + if (!m_caster->ToPlayer()->GetTarget()) return SPELL_FAILED_BAD_TARGETS; Player* target = ObjectAccessor::FindPlayer(m_caster->ToPlayer()->GetTarget()); @@ -6120,8 +6220,7 @@ 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(); @@ -6135,7 +6234,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint 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) @@ -6150,7 +6249,11 @@ 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) return SPELL_FAILED_ROOTED; @@ -6160,12 +6263,17 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint break; } case SPELL_EFFECT_TALENT_SPEC_SELECT: + { + Player* player = m_caster->ToPlayer(); + if (!player) + return SPELL_FAILED_TARGET_NOT_PLAYER; + // can't change during already started arena/battleground - if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (Battleground const* bg = m_caster->ToPlayer()->GetBattleground()) - if (bg->GetStatus() == STATUS_IN_PROGRESS) - return SPELL_FAILED_NOT_IN_BATTLEGROUND; + if (Battleground const* bg = player->ToPlayer()->GetBattleground()) + if (bg->GetStatus() == STATUS_IN_PROGRESS) + return SPELL_FAILED_NOT_IN_BATTLEGROUND; break; + } default: break; } @@ -6184,16 +6292,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->GetCharmedGUID()) + if (unitCaster->GetCharmedGUID()) return SPELL_FAILED_ALREADY_HAVE_CHARM; } @@ -6220,18 +6332,22 @@ 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()) { if (mountResult) { @@ -6282,7 +6398,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_targets.GetUnitTarget() && m_targets.GetUnitTarget()->GetPowerType() != POWER_MANA) return SPELL_FAILED_BAD_TARGETS; - else if (!m_targets.GetUnitTarget() && m_caster->GetPowerType() != POWER_MANA) + else if (!m_targets.GetUnitTarget() && m_caster->ToPlayer()->GetPowerType() != POWER_MANA) return SPELL_FAILED_BAD_TARGETS; break; @@ -6335,7 +6451,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?) @@ -6355,7 +6472,7 @@ SpellCastResult Spell::CheckPetCast(Unit* target) // check power requirement // this would be zero until ::prepare normally, we set it here (it gets reset in ::prepare) - m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask); + m_powerCost = m_spellInfo->CalcPowerCost(unitCaster, m_spellSchoolMask); SpellCastResult failReason = CheckPower(); if (failReason != SPELL_CAST_OK) return failReason; @@ -6367,7 +6484,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); @@ -6375,6 +6492,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; @@ -6394,7 +6515,7 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const // Glyph of Pain Suppression // Allow Pain Suppression and Guardian Spirit to be cast while stunned // there is no other way to handle it - if ((m_spellInfo->Id == 33206 || m_spellInfo->Id == 47788) && !m_caster->HasAura(63248)) + if ((m_spellInfo->Id == 33206 || m_spellInfo->Id == 47788) && !unitCaster->HasAura(63248)) usableWhileStunned = false; // Check whether the cast should be prevented by any state you might have. @@ -6418,7 +6539,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(); @@ -6504,8 +6625,13 @@ 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; @@ -6594,9 +6720,9 @@ SpellCastResult Spell::CheckArenaAndRatedBattlegroundCastRules() const return SPELL_CAST_OK; } -int32 Spell::CalculateDamage(uint8 i, Unit const* target) const +int32 Spell::CalculateDamage(uint8 effIndex, Unit const* target) const { - return m_caster->CalculateSpellDamage(target, m_spellInfo, i, &m_spellValue->EffectBasePoints[i]); + return m_caster->CalculateSpellDamage(target, m_spellInfo, effIndex, &m_spellValue->EffectBasePoints[effIndex]); } bool Spell::CanAutoCast(Unit* target) @@ -6694,7 +6820,7 @@ SpellCastResult Spell::CheckRange(bool strict) const if (m_caster->GetTypeId() == TYPEID_PLAYER && (((m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast(M_PI), target)) - && !m_caster->IsWithinBoundaryRadius(target))) + && !m_caster->ToPlayer()->IsWithinBoundaryRadius(target))) return SPELL_FAILED_UNIT_NOT_INFRONT; } @@ -6720,21 +6846,29 @@ std::pair Spell::GetMinMaxRange(bool strict) const float rangeMod = 0.0f; float minRange = 0.0f; float maxRange = 0.0f; + if (strict && m_spellInfo->IsNextMeleeSwingSpell()) return { 0.0f, 100.0f }; + Unit* unitCaster = m_caster->ToUnit(); if (m_spellInfo->RangeEntry) { Unit* target = m_targets.GetUnitTarget(); if (m_spellInfo->RangeEntry->Flags & SPELL_RANGE_MELEE) { - rangeMod = m_caster->GetMeleeRange(target ? target : m_caster); // when the target is not a unit, take the caster's combat reach as the target's combat reach. + // when the target is not a unit, take the caster's combat reach as the target's combat reach. + if (unitCaster) + rangeMod = unitCaster->GetMeleeRange(target ? target : unitCaster); } else { float meleeRange = 0.0f; if (m_spellInfo->RangeEntry->Flags & SPELL_RANGE_RANGED) - meleeRange = m_caster->GetMeleeRange(target ? target : m_caster); // when the target is not a unit, take the caster's combat reach as the target's combat reach. + { + // when the target is not a unit, take the caster's combat reach as the target's combat reach. + if (unitCaster) + meleeRange = unitCaster->GetMeleeRange(target ? target : unitCaster); + } minRange = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo) + meleeRange; maxRange = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo); @@ -6748,8 +6882,8 @@ std::pair Spell::GetMinMaxRange(bool strict) const } } - if (target && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking() && - (m_spellInfo->RangeEntry->Flags & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER)) + if (target && unitCaster && unitCaster->isMoving() && target->isMoving() && !unitCaster->IsWalking() && !target->IsWalking() && + ((m_spellInfo->RangeEntry->Flags & SPELL_RANGE_MELEE) || target->GetTypeId() == TYPEID_PLAYER)) rangeMod += 8.0f / 3.0f; } @@ -6767,6 +6901,10 @@ std::pair Spell::GetMinMaxRange(bool strict) const 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; @@ -6774,7 +6912,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 (int64(unitCaster->GetHealth()) <= m_powerCost) return SPELL_FAILED_CASTER_AURASTATE; return SPELL_CAST_OK; } @@ -6795,7 +6933,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; @@ -6904,7 +7042,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. @@ -6954,7 +7092,7 @@ 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; + Unit* target = m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : player; if (target && target->GetTypeId() == TYPEID_PLAYER && !IsTriggered() && m_spellInfo->Effects[i].ItemType) { ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(m_spellInfo->Effects[i].ItemType); @@ -6975,7 +7113,8 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / } 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))) { @@ -6983,7 +7122,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; } } @@ -6995,7 +7134,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / && (m_targets.GetItemTarget()->IsVellum())) { // cannot enchant vellum for other player - if (m_targets.GetItemTarget()->GetOwner() != m_caster) + if (m_targets.GetItemTarget()->GetOwner() != player) return SPELL_FAILED_NOT_TRADEABLE; // do not allow to enchant vellum from scroll made by vellum-prevent exploit if (m_CastItem && m_CastItem->GetTemplate()->GetFlags() & ITEM_FLAG_NO_REAGENT_COST) @@ -7060,7 +7199,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; @@ -7075,7 +7214,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* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); @@ -7104,7 +7243,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(); @@ -7135,7 +7274,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->GetFlags() & ITEM_FLAG_IS_PROSPECTABLE)) return SPELL_FAILED_CANT_BE_PROSPECTED; //prevent prospecting in trade slot - if (item->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_PROSPECTED; //Check for enough skill in jewelcrafting uint32 item_prospectingskilllevel = item->GetTemplate()->GetRequiredSkillRank(); @@ -7166,7 +7305,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (!(item->GetTemplate()->GetFlags() & ITEM_FLAG_IS_MILLABLE)) return SPELL_FAILED_CANT_BE_MILLED; //prevent milling in trade slot - if (item->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != player->GetGUID()) return SPELL_FAILED_CANT_BE_MILLED; //Check for enough skill in inscription uint32 item_millingskilllevel = item->GetTemplate()->GetRequiredSkillRank(); @@ -7194,15 +7333,15 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / if (m_attackType != RANGED_ATTACK) break; - Item* pItem = player->GetWeaponForAttack(m_attackType); - if (!pItem || pItem->IsBroken()) + Item* item = player->GetWeaponForAttack(m_attackType); + if (!item || item->IsBroken()) return SPELL_FAILED_EQUIPPED_ITEM; - switch (pItem->GetTemplate()->GetSubClass()) + switch (item->GetTemplate()->GetSubClass()) { case ITEM_SUBCLASS_WEAPON_THROWN: { - uint32 ammo = pItem->GetEntry(); + uint32 const ammo = item->GetEntry(); if (!player->HasItemCount(ammo)) return SPELL_FAILED_NO_AMMO; break; @@ -7244,7 +7383,7 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / // main hand weapon required if (m_spellInfo->HasAttribute(SPELL_ATTR3_MAIN_HAND)) { - Item* item = m_caster->ToPlayer()->GetWeaponForAttack(BASE_ATTACK); + Item* item = player->GetWeaponForAttack(BASE_ATTACK); // skip spell if no weapon in slot or broken if (!item || item->IsBroken()) @@ -7275,20 +7414,20 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / void Spell::Delayed() // only called in DealDamage() { - if (!m_caster)// || m_caster->GetTypeId() != TYPEID_PLAYER) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) return; - //if (m_spellState == SPELL_STATE_DELAYED) - // return; // spell is active and can't be time-backed - - if (isDelayableNoMore()) // Spells may only be delayed twice + if (IsDelayableNoMore()) // Spells may only be delayed twice return; //check pushback reduce int32 delaytime = 500; // spellcasting delay is normally 500ms + int32 delayReduce = 100; // must be initialized to 100 for percent modifiers - m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->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; @@ -7302,21 +7441,23 @@ void Spell::Delayed() // only called in DealDamage() else m_timer += delaytime; - TC_LOG_DEBUG("spells", "Spell %u partially interrupted for (%d) ms at damage", m_spellInfo->Id, delaytime); - WorldPackets::Spells::SpellDelayed packet; - packet.Caster = m_caster->GetGUID(); + packet.Caster = unitCaster->GetGUID(); packet.ActualDelay = delaytime; - m_caster->SendMessageToSet(packet.Write(), true); + unitCaster->SendMessageToSet(packet.Write(), true); } void Spell::DelayedChannel() { - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING) + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) return; - if (isDelayableNoMore()) // Spells may only be delayed twice + if (m_spellState != SPELL_STATE_CASTING) + return; + + if (IsDelayableNoMore()) // Spells may only be delayed twice return; //check pushback reduce @@ -7325,8 +7466,9 @@ void Spell::DelayedChannel() 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; @@ -7340,15 +7482,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); @@ -7357,7 +7497,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); @@ -7546,7 +7686,7 @@ bool Spell::IsProcDisabled() const bool Spell::IsChannelActive() const { - return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; + return m_caster->IsUnit() && m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; } bool Spell::IsAutoActionResetSpell() const @@ -7780,10 +7920,11 @@ 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; @@ -7812,13 +7953,13 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uin HandleEffects(unit, nullptr, 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(); @@ -7839,10 +7980,17 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uin targetInfo.Damage += m_damage; 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) @@ -7850,6 +7998,10 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk if (!lockId) // possible case for GO and maybe for items. return SPELL_CAST_OK; + Unit const* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return SPELL_FAILED_BAD_TARGETS; + // Get LockInfo LockEntry const* lockInfo = sLockStore.LookupEntry(lockId); @@ -7884,12 +8036,12 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk reqSkillValue = lockInfo->Skill[j]; // castitem check: rogue using skeleton keys. the skill values should not be added in this case. - skillValue = m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER ? - 0 : m_caster->ToPlayer()->GetSkillValue(skillId); + skillValue = m_CastItem || unitCaster->GetTypeId() != TYPEID_PLAYER ? + 0 : unitCaster->ToPlayer()->GetSkillValue(skillId); // Pickpocketing is based on Playerlevels if (m_spellInfo->Id == 1804 && skillId == SKILL_LOCKPICKING) - skillValue = 5 * m_caster->getLevel(); + skillValue = 5 * unitCaster->getLevel(); // skill bonus provided by casting spell (mostly item spells) // add the effect base points modifier from the spell cast (cheat lock / skeleton key etc.) @@ -8242,10 +8394,14 @@ bool Spell::CanExecuteTriggersOnHit(uint8 effMask, SpellInfo const* triggeredByA void Spell::PrepareTriggersExecutedOnHit() { + Unit* unitCaster = m_caster->ToUnit(); + if (!unitCaster) + return; + // handle SPELL_AURA_ADD_TARGET_TRIGGER auras: // save auras which were present on spell caster on cast, to prevent triggered auras from affecting caster // and to correctly calculate proc chance when combopoints are present - Unit::AuraEffectList const& targetTriggers = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER); + Unit::AuraEffectList const& targetTriggers = unitCaster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER); for (AuraEffect const* aurEff : targetTriggers) { if (!aurEff->IsAffectingSpell(m_spellInfo)) @@ -8259,7 +8415,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(nullptr, auraSpellInfo, auraSpellIdx, &auraBaseAmount); chance *= aurEff->GetBase()->GetStackAmount(); // build trigger and add to the list @@ -8275,27 +8431,33 @@ 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(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() { - if (!m_spellInfo->StartRecoveryCategory) + if (!CanHaveGlobalCooldown(m_caster)) return; - if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_COOLDOWN)) - return; + int32 gcd = m_spellInfo->StartRecoveryTime; + if (!gcd || !m_spellInfo->StartRecoveryCategory) + return; // Global cooldown can't leave range 1..1.5 secs - int32 gcd = m_spellInfo->StartRecoveryTime; - // gcd modifier auras are applied only to own spells and only players have such mods if (Player* modOwner = m_caster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, gcd, this); @@ -8305,49 +8467,47 @@ void Spell::TriggerGlobalCooldown() m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE && m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_RANGED && !m_spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !m_spellInfo->HasAttribute(SPELL_ATTR0_IS_ABILITY)) { - gcd = int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_SPEED)); + gcd = int32(float(gcd) * m_caster->ToUnit()->GetFloatValue(UNIT_MOD_CAST_SPEED)); RoundToInterval(gcd, MIN_GCD, MAX_GCD); } if (gcd) - m_caster->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); + m_caster->ToUnit()->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); } void Spell::CancelGlobalCooldown() { + if (!CanHaveGlobalCooldown(m_caster)) + return; + if (!m_spellInfo->StartRecoveryTime) return; // Cancel global cooldown when interrupting current cast - if (m_caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) + if (m_caster->ToUnit()->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) return; - // Only players or controlled units have global cooldown - if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) - 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), +WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo), _targetSelectionType(selectionType), _condList(condList) { if (condList) - _condSrcInfo = new ConditionSourceInfo(nullptr, caster); + _condSrcInfo = std::make_unique(nullptr, caster); else _condSrcInfo = nullptr; } 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; @@ -8361,6 +8521,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; } + Unit* refUnit = _referer->ToUnit(); if (unitTarget) { // do only faction checks here @@ -8369,7 +8530,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) case TARGET_CHECK_ENEMY: if (unitTarget->IsTotem()) return false; - if (!target->IsCorpse() && !_caster->IsValidAttackTarget(unitTarget, _spellInfo, nullptr, false)) + if (!target->IsCorpse() && !_caster->IsValidAttackTarget(unitTarget, _spellInfo, false)) return false; break; case TARGET_CHECK_ALLY: @@ -8379,23 +8540,29 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; break; case TARGET_CHECK_PARTY: + if (!refUnit) + return false; if (unitTarget->IsTotem()) return false; if (!target->IsCorpse() && !_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) return false; - if (!_referer->IsInPartyWith(unitTarget)) + if (!refUnit->IsInPartyWith(unitTarget)) return false; break; case TARGET_CHECK_RAID_CLASS: - if (_referer->getClass() != unitTarget->getClass()) + if (!refUnit) + return false; + if (refUnit->getClass() != unitTarget->getClass()) return false; [[fallthrough]]; case TARGET_CHECK_RAID: + if (!refUnit) + return false; if (unitTarget->IsTotem()) return false; if (!target->IsCorpse() && !_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) return false; - if (!_referer->IsInRaidWith(unitTarget)) + if (!refUnit->IsInRaidWith(unitTarget)) return false; break; case TARGET_CHECK_SUMMONED: @@ -8405,7 +8572,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) return false; break; case TARGET_CHECK_THREAT: - if (_referer->GetThreatManager().GetThreat(unitTarget, true) <= 0.0f) + if (!_referer->IsUnit() || _referer->ToUnit()->GetThreatManager().GetThreat(unitTarget, true) <= 0.0f) return false; break; case TARGET_CHECK_TAP: @@ -8438,8 +8605,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) @@ -8453,8 +8620,8 @@ 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) @@ -8468,7 +8635,7 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) } else if (target->ToUnit()) { - float hitboxSum = (_spellInfo->HasAttribute(SPELL_ATTR5_TREAT_AS_AREA_EFFECT) && _spellInfo->SpellFamilyName != SPELLFAMILY_GENERIC) ? target->ToUnit()->GetMeleeRange(_caster) : 0.f; + float hitboxSum = (_caster->IsUnit(), _spellInfo->HasAttribute(SPELL_ATTR5_TREAT_AS_AREA_EFFECT) && _spellInfo->SpellFamilyName != SPELLFAMILY_GENERIC) ? target->ToUnit()->GetMeleeRange(_caster->ToUnit()) : 0.f; bool isInsideCylinder = target->IsWithinDist2d(_position, _range + hitboxSum) && std::abs(target->GetPositionZ() - _position->GetPositionZ()) <= (_range + hitboxSum); if (!isInsideCylinder) return false; @@ -8477,8 +8644,8 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) return WorldObjectSpellTargetCheck::operator ()(target); } -WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(Position const& coneSrc, float coneAngle, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(Position const& coneSrc, float coneAngle, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList), _coneSrc(coneSrc), _coneAngle(coneAngle) { } bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) @@ -8495,7 +8662,7 @@ bool WorldObjectSpellConeTargetCheck::operator()(WorldObject* target) } else { - if (!_caster->IsWithinBoundaryRadius(target->ToUnit())) + if (!_caster->IsUnit() || !_caster->ToUnit()->IsWithinBoundaryRadius(target->ToUnit())) // ConeAngle > 0 -> select targets in front // ConeAngle < 0 -> select targets in back if (_coneSrc.HasInArc(_coneAngle, target) != G3D::fuzzyGe(_coneAngle, 0.f)) @@ -8504,7 +8671,7 @@ 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) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 909f9d53ca..dd32ffcbcf 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -211,8 +211,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; @@ -411,7 +410,7 @@ class TC_GAME_API Spell typedef std::unordered_set 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); @@ -434,10 +433,10 @@ class TC_GAME_API Spell void SelectEffectTypeImplicitTargets(uint8 effIndex); uint32 GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionContainer* condList); - template void SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius); + template 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& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList); + void SearchAreaTargets(std::list& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList); void SearchChainTargets(std::list& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, ConditionContainer* condList, bool isChainHeal); GameObject* SearchSpellFocus(); @@ -479,7 +478,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, Unit const* target) const; void Delayed(); void DelayedChannel(); @@ -564,7 +563,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; } @@ -586,7 +585,7 @@ class TC_GAME_API Spell void SendLoot(ObjectGuid guid, LootType loottype); std::pair GetMinMaxRange(bool strict) const; - Unit* const m_caster; + WorldObject* const m_caster; SpellValue* const m_spellValue; @@ -605,12 +604,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; } @@ -636,6 +635,7 @@ class TC_GAME_API Spell SpellMissInfo targetMissInfo; SpellEffectHandleMode effectHandleMode; // used in effects handlers + Unit* unitCaster; UnitAura* _spellAura; DynObjAura* _dynObjAura; @@ -792,7 +792,6 @@ class TC_GAME_API Spell // effect helpers void CalculateJumpSpeeds(uint8 i, float dist, float & speedxy, float & speedz); - void UpdateSpellCastDataTargets(WorldPackets::Spells::SpellHitInfo& data); void UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& data); void UpdateSpellHealPrediction(WorldPackets::Spells::SpellHealPrediction& predict); @@ -824,25 +823,28 @@ namespace Trinity { struct TC_GAME_API WorldObjectSpellTargetCheck { - Unit* _caster; - Unit* _referer; + protected: + WorldObject* _caster; + WorldObject* _referer; SpellInfo const* _spellInfo; SpellTargetCheckTypes _targetSelectionType; - ConditionSourceInfo* _condSrcInfo; - ConditionContainer* _condList; + std::unique_ptr _condSrcInfo; + ConditionContainer const* _condList; - WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer* condList); + WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList); ~WorldObjectSpellTargetCheck(); - bool operator()(WorldObject* target); + + 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); }; @@ -850,8 +852,9 @@ namespace Trinity { float _range; Position const* _position; - WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList); + WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster, + WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList); + bool operator()(WorldObject* target); }; @@ -859,8 +862,9 @@ namespace Trinity { Position _coneSrc; float _coneAngle; - WorldObjectSpellConeTargetCheck(Position const& coneSrc, float coneAngle, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList); + WorldObjectSpellConeTargetCheck(Position const& coneSrc, float coneAngle, float range, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList); + bool operator()(WorldObject* target); }; @@ -868,12 +872,13 @@ namespace Trinity { float _range; Position const* _position; - WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer* condList); + WorldObjectSpellTrajTargetCheck(float range, Position const* position, WorldObject* caster, + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList); + bool operator()(WorldObject* target); }; } -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 4ccd3d0b28..76b73d45f7 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -210,7 +210,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 33326e426c..1ffd55fa74 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -71,7 +71,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 @@ -293,8 +293,8 @@ void Spell::EffectResurrectNew(SpellEffIndex effIndex) uint32 mana = m_spellInfo->Effects[effIndex].MiscValue; // SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER bonus - if (m_caster->IsPlayer() && m_caster->ToPlayer()->GetGuildId() == player->GetGuildId() && !m_caster->IsInCombat()) - AddPct(health, m_caster->GetTotalAuraModifier(SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER)); + if (m_caster->IsPlayer() && m_caster->ToPlayer()->GetGuildId() == player->GetGuildId() && !unitCaster->IsInCombat()) + AddPct(health, unitCaster->GetTotalAuraModifier(SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER)); ExecuteLogEffectResurrect(effIndex, player); player->SetResurrectRequestData(m_caster, health, mana, 0); @@ -322,7 +322,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*/) @@ -338,12 +338,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); } } @@ -364,18 +365,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); - break; + damage = 2750; + if (m_caster->GetMap()->IsHeroic()) + damage = 4250; // percent from health with min case 25599: // Thundercrash { @@ -387,53 +397,59 @@ 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; } case 88082: // Fireball (Mirror Image) case 59638: // Frost Bolt (Mirror Image) case 88084: // Arcane Blast (Mirror Image) // Mage Mirror Images deal 50% of the coefficient's spell power bonus as bonus damage - if (Unit* owner = m_caster->ToTempSummon()->GetSummoner()) - damage = CalculatePct(owner->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effIndex), 50); + if (unitCaster) + if (Unit* owner = unitCaster->ToTempSummon()->GetSummoner()) + damage = CalculatePct(owner->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effIndex), 50); + break; + default: break; } break; } 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())) { damage += m_damage / 2; - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.035f); + damage += int32(unitCaster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.035f); } } // Merciless Combat (Icy Touch and Howling Blast) if (m_spellInfo->SpellFamilyFlags[0] & 0x2 || m_spellInfo->SpellFamilyFlags[1] & 0x2) if (unitTarget->GetHealthPct() < 35.0f) - if (AuraEffect const* mercilessCombat = m_caster->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, 2656, 0)) + if (AuraEffect const* mercilessCombat = unitCaster->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, 2656, 0)) AddPct(damage, mercilessCombat->GetAmount()); 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; @@ -542,7 +558,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); @@ -586,10 +607,10 @@ void Spell::EffectTriggerMissileSpell(SpellEffIndex effIndex) if (spellInfo->GetExplicitTargetMask() & TARGET_FLAG_DEST_LOCATION) targets.SetDst(m_targets); - 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); @@ -724,7 +745,10 @@ 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) @@ -732,7 +756,7 @@ void Spell::EffectJump(SpellEffIndex effIndex) float speedXY, speedZ; CalculateJumpSpeeds(effIndex, m_caster->GetExactDist2d(unitTarget), speedXY, speedZ); - m_caster->GetMotionMaster()->MoveJump(*unitTarget, speedXY, speedZ, EVENT_JUMP, false); + unitCaster->GetMotionMaster()->MoveJump(*unitTarget, speedXY, speedZ, EVENT_JUMP, false); } void Spell::EffectJumpDest(SpellEffIndex effIndex) @@ -740,7 +764,10 @@ 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()) @@ -748,12 +775,15 @@ void Spell::EffectJumpDest(SpellEffIndex effIndex) float speedXY, speedZ; CalculateJumpSpeeds(effIndex, m_caster->GetExactDist2d(destTarget), speedXY, speedZ); - m_caster->GetMotionMaster()->MoveJump(*destTarget, speedXY, speedZ, EVENT_JUMP, !m_targets.GetObjectTargetGUID().IsEmpty()); + unitCaster->GetMotionMaster()->MoveJump(*destTarget, speedXY, speedZ, EVENT_JUMP, !m_targets.GetObjectTargetGUID().IsEmpty()); } void Spell::CalculateJumpSpeeds(uint8 i, float dist, float & speedXY, float & speedZ) { - float runSpeed = m_caster->IsControlledByPlayer() ? playerBaseMoveSpeed[MOVE_RUN] : baseMoveSpeed[MOVE_RUN]; + if (!unitCaster) + return; + + float runSpeed = unitCaster->IsControlledByPlayer() ? playerBaseMoveSpeed[MOVE_RUN] : baseMoveSpeed[MOVE_RUN]; if (Creature* creature = m_caster->ToCreature()) runSpeed *= creature->GetCreatureTemplate()->speed_run; @@ -961,21 +991,22 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex) 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); + } int32 newDamage = -(unitTarget->ModifyPower(powerType, -damage)); - float gainMultiplier = 0.0f; - // Don't restore from self drain - if (m_caster != unitTarget) + float gainMultiplier = 0.f; + if (unitCaster && unitCaster != unitTarget) { - gainMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this); + gainMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(unitCaster, this); + int32 const gain = int32(newDamage * gainMultiplier); - int32 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); } @@ -1032,9 +1063,10 @@ void Spell::EffectPowerBurn(SpellEffIndex effIndex) 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); } @@ -1042,7 +1074,7 @@ void Spell::EffectPowerBurn(SpellEffIndex effIndex) int32 newDamage = -(unitTarget->ModifyPower(powerType, -damage)); // 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); @@ -1057,79 +1089,76 @@ void Spell::EffectHeal(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET) return; - if (unitTarget && unitTarget->IsAlive() && damage >= 0) + if (!unitTarget || !unitTarget->IsAlive() || damage < 0) + return; + + // Skip if m_originalCaster not available + if (!unitCaster) + return; + + int32 addhealth = damage; + + // Vessel of the Naaru (Vial of the Sunwell trinket) + if (m_spellInfo->Id == 45064) { - // Try to get original caster - Unit* caster = m_originalCasterGUID ? m_originalCaster : m_caster; - - // Skip if m_originalCaster not available - if (!caster) - return; - - int32 addhealth = damage; - - // 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 = unitCaster->GetAuraEffect(45062, 0)) { - // 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); - } - - addhealth += damageAmount; + damageAmount += aurEff->GetAmount(); + unitCaster->RemoveAurasDueToSpell(45062); } - // Runic Healing Injector (heal increased by 25% for engineers - 3.2.0 patch change) - else if (m_spellInfo->Id == 67489) - { - if (Player* player = m_caster->ToPlayer()) - if (player->HasSkill(SKILL_ENGINEERING)) - AddPct(addhealth, 25); - } - // 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) - { - TC_LOG_ERROR("spells", "Target (%s) has the aurastate AURA_STATE_SWIFTMEND, but no matching aura.", unitTarget->GetGUID().ToString().c_str()); - return; - } - - // Glyph of Swiftmend - if (!caster->HasAura(54824)) - unitTarget->RemoveAura(targetAura->GetId(), targetAura->GetCasterGUID()); - - 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 = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, int32(caster->CountPctFromMaxHealth(damage)), HEAL, effIndex); - else - addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effIndex); - - addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL); - - // Remove Grievious bite if fully healed - if (unitTarget->HasAura(48920) && (unitTarget->GetHealth() + addhealth >= unitTarget->GetMaxHealth())) - unitTarget->RemoveAura(48920); - - m_healing += addhealth; + 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) + { + if (!targetAura || (*i)->GetBase()->GetDuration() < targetAura->GetBase()->GetDuration()) + targetAura = *i; + } + } + + if (!targetAura) + { + TC_LOG_ERROR("spells", "Target (%s) has the aurastate AURA_STATE_SWIFTMEND, but no matching aura.", unitTarget->GetGUID().ToString().c_str()); + return; + } + + // Glyph of Swiftmend + if (!unitCaster->HasAura(54824)) + unitTarget->RemoveAura(targetAura->GetId(), targetAura->GetCasterGUID()); + + addhealth = unitCaster->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(unitCaster, m_spellInfo, addhealth, HEAL); + + // Remove Grievious bite if fully healed + if (unitTarget->HasAura(48920) && (unitTarget->GetHealth() + addhealth >= unitTarget->GetMaxHealth())) + unitTarget->RemoveAura(48920); + + m_healing += addhealth; } void Spell::EffectHealPct(SpellEffIndex effIndex) @@ -1144,8 +1173,14 @@ void Spell::EffectHealPct(SpellEffIndex effIndex) if (!m_originalCaster) return; - uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, unitTarget->CountPctFromMaxHealth(damage), HEAL, effIndex); - m_healing += unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL); + uint32 heal = unitTarget->CountPctFromMaxHealth(damage); + if (unitCaster) + { + heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, effIndex); + heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL); + } + + m_healing += heal; } void Spell::EffectHealMechanical(SpellEffIndex effIndex) @@ -1160,9 +1195,14 @@ void Spell::EffectHealMechanical(SpellEffIndex effIndex) if (!m_originalCaster) return; - uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, uint32(damage), HEAL, effIndex); + uint32 heal = damage; + if (unitCaster) + { + heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, effIndex); + heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL); + } - m_healing += unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL); + m_healing += heal; } void Spell::EffectHealthLeech(SpellEffIndex effIndex) @@ -1173,24 +1213,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); } } @@ -1405,6 +1448,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) @@ -1412,15 +1458,13 @@ 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(), caster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL)) + if (!dynObj->CreateDynamicObject(unitCaster->GetMap()->GenerateLowGuid(), unitCaster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL)) { delete dynObj; return; @@ -1428,7 +1472,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)) @@ -1449,8 +1493,9 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget) + if (!unitCaster || !unitTarget) return; + if (!unitTarget->IsAlive()) return; @@ -1458,25 +1503,25 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) return; Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetMaxPower(power) == 0) 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 @@ -1486,7 +1531,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; @@ -1501,7 +1546,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) if (damage < 0 && power != POWER_ECLIPSE && power != POWER_ALTERNATE_POWER) return; - m_caster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power); + unitCaster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power); } void Spell::EffectEnergizePct(SpellEffIndex effIndex) @@ -1509,8 +1554,9 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget) + if (!unitCaster || !unitTarget) return; + if (!unitTarget->IsAlive()) return; @@ -1519,11 +1565,11 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); uint32 maxPower = unitTarget->GetMaxPower(power); - if (maxPower == 0) + if (!maxPower) return; - uint32 gain = CalculatePct(maxPower, damage); - m_caster->EnergizeBySpell(unitTarget, m_spellInfo, gain, power); + uint32 const gain = CalculatePct(maxPower, damage); + unitCaster->EnergizeBySpell(unitTarget, m_spellInfo, gain, power); } void Spell::SendLoot(ObjectGuid guid, LootType loottype) @@ -1544,7 +1590,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; } @@ -1567,7 +1613,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: @@ -1580,7 +1626,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 break; @@ -1866,32 +1912,33 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) return; } - if (!m_originalCaster) - return; + WorldObject* caster = m_caster; + if (m_originalCaster) + caster = m_originalCaster; ObjectGuid privateObjectOwner = [&]() { if (!(properties->Flags & (SUMMON_PROP_FLAG_PERSONAL_SPAWN | SUMMON_PROP_FLAG_PERSONAL_GROUP_SPAWN))) return ObjectGuid::Empty; - if (m_originalCaster->IsPrivateObject()) - return m_originalCaster->GetPrivateObjectOwner(); + if (caster->IsPrivateObject()) + return caster->GetPrivateObjectOwner(); if (properties->Flags & SUMMON_PROP_FLAG_PERSONAL_GROUP_SPAWN) - if (m_originalCaster->IsPlayer() && m_originalCaster->ToPlayer()->GetGroup()) - return m_originalCaster->ToPlayer()->GetGroup()->GetGUID(); + if (caster->IsPlayer() && caster->ToPlayer()->GetGroup()) + return caster->ToPlayer()->GetGroup()->GetGUID(); - return m_originalCaster->GetGUID(); + return caster->GetGUID(); }(); int32 duration = m_spellInfo->GetDuration(); - if (Player* modOwner = m_originalCaster->GetSpellModOwner()) + if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); SummonCreatureExtraArgs extraArgs; extraArgs.SummonProperties = properties; extraArgs.SummonDuration = duration; - extraArgs.Summoner = m_originalCaster; + extraArgs.Summoner = unitCaster; extraArgs.SummonSpellId = m_spellInfo->Id; extraArgs.PrivateObjectOwner = privateObjectOwner; @@ -1938,27 +1985,30 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) } } - for (uint32 i = 0; i < summonCount; ++i) + if (unitCaster) { - Position dest = *destTarget; - if (summonCount > 1) + for (uint32 i = 0; i < summonCount; ++i) { - // Multiple summons are summoned at random points within the destination radius - float radius = m_spellInfo->Effects[effIndex].CalcRadius(); - dest = m_caster->GetRandomPoint(*destTarget, radius); - } - - if (TempSummon* summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, extraArgs)) - { - ExecuteLogEffectSummonObject(effIndex, summon); - - if (summonCount == 1 && summon->IsVehicle()) + Position dest = *destTarget; + if (summonCount > 1) { - if (useHardcodedRideSpell) - m_originalCaster->CastSpell(summon, VEHICLE_SPELL_RIDE_HARDCODED, CastSpellExtraArgs(true).AddSpellBP0(extraArgs.SeatNumber)); - else if (extraArgs.RideSpell) - m_originalCaster->CastSpell(summon, extraArgs.RideSpell, true); + // Multiple summons are summoned at random points within the destination radius + float radius = m_spellInfo->Effects[effIndex].CalcRadius(); + dest = unitCaster->GetRandomPoint(*destTarget, radius); + } + if (TempSummon* summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, extraArgs)) + { + ExecuteLogEffectSummonObject(effIndex, summon); + + if (summonCount == 1 && summon->IsVehicle()) + { + if (useHardcodedRideSpell) + unitCaster->CastSpell(summon, VEHICLE_SPELL_RIDE_HARDCODED, CastSpellExtraArgs(true).AddSpellBP0(extraArgs.SeatNumber)); + else if (extraArgs.RideSpell) + unitCaster->CastSpell(summon, extraArgs.RideSpell, true); + + } } } } @@ -2149,17 +2199,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(), m_caster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) + if (!dynObj->CreateDynamicObject(player->GetMap()->GenerateLowGuid(), player, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) { delete dynObj; return; @@ -2462,7 +2513,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) @@ -2476,18 +2527,18 @@ 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; - uint8 level = m_caster->getLevel(); + uint8 level = unitCaster->getLevel(); // prepare visual effect for levelup pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1); @@ -2496,16 +2547,16 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) pet->GetMap()->AddToMap(pet->ToCreature()); // 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_NEW_PET); if (pet->GetSlot() <= PET_SLOT_LAST_ACTIVE_SLOT) { - m_caster->ToPlayer()->GetSession()->SendPetAdded(pet->GetSlot(), pet->GetCharmInfo()->GetPetNumber(), creatureTarget->GetEntry(), creatureTarget->getLevel(), pet->GetName()); - m_caster->ToPlayer()->PetSpellInitialize(); + unitCaster->ToPlayer()->GetSession()->SendPetAdded(pet->GetSlot(), pet->GetCharmInfo()->GetPetNumber(), creatureTarget->GetEntry(), creatureTarget->getLevel(), pet->GetName()); + unitCaster->ToPlayer()->PetSpellInitialize(); } else { @@ -2526,11 +2577,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(); } // SUMMON_PET SummonPet's entries are at MiscValue, HunterPetSlot at BasePoints @@ -2605,7 +2656,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); @@ -2647,7 +2698,7 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!m_caster) + if (!unitCaster) return; // this effect use before aura Taunt apply for prevent taunt already attacking target @@ -2660,8 +2711,8 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) // Hand of Reckoning can hit some entities that can't have a threat list (including players' pets) if (m_spellInfo->Id == 62124) - if (unitTarget->GetTypeId() != TYPEID_PLAYER && unitTarget->GetTarget() != m_caster->GetGUID()) - m_caster->CastSpell(unitTarget, 67485, true); + if (unitTarget->GetTypeId() != TYPEID_PLAYER && unitTarget->GetTarget() != unitCaster->GetGUID()) + unitCaster->CastSpell(unitTarget, 67485, true); if (!unitTarget->CanHaveThreatList()) { @@ -2678,7 +2729,7 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) if (!mgr.IsThreatListEmpty()) // Set threat equal to highest threat currently on target - mgr.MatchUnitThreatToHighestThreat(m_caster); + mgr.MatchUnitThreatToHighestThreat(unitCaster); } void Spell::EffectWeaponDmg(SpellEffIndex effIndex) @@ -2686,6 +2737,9 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET) return; + if (!unitCaster) + return; + if (!unitTarget || !unitTarget->IsAlive()) return; @@ -2734,11 +2788,11 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) // Hemorrhage if (m_spellInfo->SpellFamilyFlags[0] & 0x2000000) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->AddComboPoints(unitTarget, 1, this); + if (unitCaster->GetTypeId() == TYPEID_PLAYER) + unitCaster->ToPlayer()->AddComboPoints(unitTarget, 1, this); // 45% more damage with daggers - if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (Item* item = m_caster->ToPlayer()->GetWeaponForAttack(m_attackType, true)) + if (unitCaster->GetTypeId() == TYPEID_PLAYER) + if (Item* item = unitCaster->ToPlayer()->GetWeaponForAttack(m_attackType, true)) if (item->GetTemplate()->GetSubClass() == ITEM_SUBCLASS_WEAPON_DAGGER) totalDamagePercentMod *= 1.45f; } @@ -2748,19 +2802,19 @@ 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(unitCaster, 38430, aurEff); break; } case SPELLFAMILY_DRUID: { // Mangle (Cat): CP if (m_spellInfo->SpellFamilyFlags[1] & 0x400) - m_caster->CastSpell(unitTarget, 34071, true); + unitCaster->CastSpell(unitTarget, 34071, true); // 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; @@ -2772,8 +2826,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) { // Glyph of Death Strike // 2% more damage per 5 runic power, up to a maximum of 40% - if (AuraEffect const* aurEff = m_caster->GetAuraEffect(59336, EFFECT_0)) - if (uint32 runic = std::min(uint32(m_caster->GetPower(POWER_RUNIC_POWER) / 2.5f), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(m_caster))) + if (AuraEffect const* aurEff = unitCaster->GetAuraEffect(59336, EFFECT_0)) + if (uint32 runic = std::min(uint32(unitCaster->GetPower(POWER_RUNIC_POWER) / 2.5f), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(unitCaster))) AddPct(totalDamagePercentMod, runic); } @@ -2791,7 +2845,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) float bonusAmount = bonusPct * diseaseCount; // 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(bonusAmount, aurEff->GetAmount()); AddPct(totalDamagePercentMod, bonusAmount); @@ -2799,12 +2853,12 @@ 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); // Merciless Combat (Obliterate and Frost Strike) if (m_spellInfo->SpellFamilyFlags[1] & 0x20000 || m_spellInfo->SpellFamilyFlags[1] & 0x4) if (unitTarget->GetHealthPct() < 35.0f) - if (AuraEffect const* mercilessCombat = m_caster->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, 2656, 0)) + if (AuraEffect const* mercilessCombat = unitCaster->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, 2656, 0)) AddPct(totalDamagePercentMod, mercilessCombat->GetAmount()); break; } @@ -2815,14 +2869,14 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) // Felstorm and Legion Strike if (m_spellInfo->Id == 89753) { - if (m_caster->IsPet()) - if (Unit* owner = m_caster->GetOwner()) + if (unitCaster->IsPet()) + if (Unit* owner = unitCaster->GetOwner()) m_damage += ((owner->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_SHADOW) * 0.5f) * 2) * 0.231f; } else if (m_spellInfo->Id == 30213) { - if (m_caster->IsPet()) - if (Unit* owner = m_caster->GetOwner()) + if (unitCaster->IsPet()) + if (Unit* owner = unitCaster->GetOwner()) m_damage += ((owner->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_SHADOW) * 0.5f) * 2) * 0.264f; } } @@ -2845,12 +2899,12 @@ 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); } - int32 weaponDamage = useWeaponDamage ? m_caster->CalculateDamage(m_attackType, normalized, addPctMods) : 0; + int32 weaponDamage = useWeaponDamage ? unitCaster->CalculateDamage(m_attackType, normalized, addPctMods) : 0; switch (effectType) { @@ -2876,8 +2930,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) weaponDamage = std::max(weaponDamage, 0); // Add melee damage bonuses (also check for negative) - weaponDamage = m_caster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, m_spellInfo, !useWeaponDamage); - m_damage += unitTarget->MeleeDamageBonusTaken(m_caster, weaponDamage, m_attackType, m_spellInfo); + weaponDamage = unitCaster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, m_spellInfo, !useWeaponDamage); + m_damage += unitTarget->MeleeDamageBonusTaken(unitCaster, weaponDamage, m_attackType, m_spellInfo); } void Spell::EffectThreat(SpellEffIndex /*effIndex*/) @@ -2885,13 +2939,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*/) @@ -2899,6 +2956,9 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; + if (!unitCaster) + return; + if (!unitTarget || !unitTarget->IsAlive()) return; @@ -2906,7 +2966,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(); @@ -3027,7 +3087,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: @@ -3037,6 +3097,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) { @@ -3044,7 +3107,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(); @@ -3052,7 +3115,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) @@ -3075,7 +3138,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; } @@ -3362,14 +3425,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()); } } @@ -3378,7 +3444,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; @@ -3397,11 +3463,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; @@ -3513,13 +3579,16 @@ void Spell::EffectAddComboPoints(SpellEffIndex /*effIndex*/) if (!unitTarget) return; - if (!m_caster->IsMovedByClient()) + if (!unitCaster) + return; + + if (!unitCaster->IsMovedByClient()) return; if (damage <= 0) return; - m_caster->GetGameClientMovingMe()->GetBasePlayer()->AddComboPoints(unitTarget, damage, this); + unitCaster->GetGameClientMovingMe()->GetBasePlayer()->AddComboPoints(unitTarget, damage, this); } void Spell::EffectDuel(SpellEffIndex effIndex) @@ -3559,30 +3628,30 @@ 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(); if (!pGameObj->Create(map->GenerateLowGuid(), gameobject_id, map, pos, QuaternionData(), 0, GO_STATE_READY)) { delete pGameObj; return; } - PhasingHandler::InheritPhaseShift(pGameObj, m_caster); + PhasingHandler::InheritPhaseShift(pGameObj, caster); - 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 @@ -3665,13 +3734,16 @@ void Spell::EffectSummonPlayer(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH) return; + if (!unitCaster) + return; + if (m_targets.GetOrigUnitTargetGUID().IsEmpty()) { - if (Player* target = ObjectAccessor::FindPlayer(m_caster->GetTarget())) - target->SendSummonRequestFrom(m_caster); + if (Player* target = ObjectAccessor::FindPlayer(unitCaster->GetTarget())) + target->SendSummonRequestFrom(unitCaster); } else if (Player* target = ObjectAccessor::FindPlayer(m_targets.GetOrigUnitTargetGUID())) - target->SendSummonRequestFrom(m_caster); + target->SendSummonRequestFrom(unitCaster); } void Spell::EffectActivateObject(SpellEffIndex effIndex) @@ -3694,10 +3766,12 @@ void Spell::EffectActivateObject(SpellEffIndex effIndex) break; case GameObjectActions::Disturb: // What's the difference with Open? case GameObjectActions::Open: - gameObjTarget->Use(m_caster); + if (unitCaster) + gameObjTarget->Use(unitCaster); break; case GameObjectActions::OpenAndUnlock: - gameObjTarget->UseDoorOrButton(0, false, m_caster); + if (unitCaster) + gameObjTarget->UseDoorOrButton(0, false, unitCaster); [[fallthrough]]; case GameObjectActions::Unlock: case GameObjectActions::Lock: @@ -3719,7 +3793,8 @@ void Spell::EffectActivateObject(SpellEffIndex effIndex) gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); break; case GameObjectActions::Destroy: - gameObjTarget->UseDoorOrButton(0, true, m_caster); + if (unitCaster) + gameObjTarget->UseDoorOrButton(0, true, unitCaster); break; case GameObjectActions::UseArtKit0: case GameObjectActions::UseArtKit1: @@ -3750,7 +3825,8 @@ void Spell::EffectActivateObject(SpellEffIndex effIndex) break; break; case GameObjectActions::OpenAndPlayAnimKit: - gameObjTarget->Use(m_caster); + if (unitCaster) + gameObjTarget->Use(unitCaster); gameObjTarget->PlayAnimKit(m_spellInfo->Effects[effIndex].MiscValueB); break; case GameObjectActions::CloseAndPlayAnimKit: @@ -3810,7 +3886,7 @@ void Spell::EffectApplyGlyph(SpellEffIndex effIndex) case 8: minLevel = 75; break; } - if (minLevel && m_caster->getLevel() < minLevel) + if (minLevel && player->getLevel() < minLevel) { SendCastResult(SPELL_FAILED_GLYPH_SOCKET_LOCKED); return; @@ -3997,19 +4073,22 @@ 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(); @@ -4021,8 +4100,8 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) // Summon in random point all other units if location present else { - m_caster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS); - o = m_caster->GetOrientation(); + unitCaster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS); + o = unitCaster->GetOrientation(); } Map* map = m_caster->GetMap(); @@ -4038,13 +4117,13 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) int32 duration = m_spellInfo->GetDuration(); 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::EffectSummonDynObj(SpellEffIndex effIndex) @@ -4056,9 +4135,9 @@ void Spell::EffectSummonDynObj(SpellEffIndex effIndex) if (!player) return; - float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); + float radius = m_spellInfo->Effects[effIndex].CalcRadius(player); DynamicObject* dynObj = new DynamicObject(false); - if (!dynObj->CreateDynamicObject(m_caster->GetMap()->GenerateLowGuid(), m_caster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_RAID_MARKER)) + if (!dynObj->CreateDynamicObject(player->GetMap()->GenerateLowGuid(), player, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_RAID_MARKER)) { delete dynObj; return; @@ -4083,9 +4162,9 @@ void Spell::EffectSummonRaidMarker(SpellEffIndex effIndex) uint32 slotMask = 1 << m_spellInfo->Effects[effIndex].BasePoints; - float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); + float radius = m_spellInfo->Effects[effIndex].CalcRadius(player); DynamicObject* dynObj = new DynamicObject(false); - if (!dynObj->CreateDynamicObject(m_caster->GetMap()->GenerateLowGuid(), m_caster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_RAID_MARKER)) + if (!dynObj->CreateDynamicObject(player->GetMap()->GenerateLowGuid(), player, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_RAID_MARKER)) { delete dynObj; return; @@ -4124,8 +4203,8 @@ void Spell::EffectResurrect(SpellEffIndex effIndex) uint32 mana = CalculatePct(player->GetMaxPower(POWER_MANA), damage); // SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER bonus - if (m_caster->IsPlayer() && m_caster->ToPlayer()->GetGuildId() == player->GetGuildId() && !m_caster->IsInCombat()) - AddPct(health, m_caster->GetTotalAuraModifier(SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER)); + if (m_caster->IsPlayer() && m_caster->ToPlayer()->GetGuildId() == player->GetGuildId() && !m_caster->ToPlayer()->IsInCombat()) + AddPct(health, m_caster->ToPlayer()->GetTotalAuraModifier(SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER)); ExecuteLogEffectResurrect(effIndex, player); @@ -4234,23 +4313,26 @@ void Spell::EffectForceDeselect(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; + if (!unitCaster) + return; + float dist = m_caster->GetVisibilityRange(); // clear focus WorldPacket data(SMSG_BREAK_TARGET, m_caster->GetPackGUID().size()); data << m_caster->GetPackGUID(); - Trinity::MessageDistDelivererToHostile notifierBreak(m_caster, &data, dist); + Trinity::MessageDistDelivererToHostile notifierBreak(unitCaster, &data, dist); Cell::VisitWorldObjects(m_caster, notifierBreak, dist); // and selection data.Initialize(SMSG_CLEAR_TARGET, 8); data << uint64(m_caster->GetGUID()); - Trinity::MessageDistDelivererToHostile notifierClear(m_caster, &data, dist); + Trinity::MessageDistDelivererToHostile notifierClear(unitCaster, &data, dist); Cell::VisitWorldObjects(m_caster, notifierBreak, 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); @@ -4263,11 +4345,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; @@ -4282,12 +4361,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); @@ -4306,7 +4384,8 @@ 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(); @@ -4316,7 +4395,7 @@ 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); + player->SendLoot(creature->GetGUID(), LOOT_SKINNING); int32 reqValue = 1; if (targetLevel <= 10) @@ -4343,31 +4422,34 @@ 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 if (!m_preGeneratedPath) { //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); + Position pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetCombatReach(), unitTarget->GetRelativeAngle(unitCaster)); + 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); } else 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); + if (!m_spellInfo->IsPositive() && unitCaster->GetTypeId() == TYPEID_PLAYER) + unitCaster->Attack(unitTarget, true); if (int32 spellId = GetSpellInfo()->Effects[effIndex].TriggerSpell) - m_caster->CastSpell(unitTarget, spellId, CastSpellExtraArgs(TRIGGERED_FULL_MASK).SetOriginalCaster(m_originalCasterGUID)); + unitCaster->CastSpell(unitTarget, spellId, CastSpellExtraArgs(TRIGGERED_FULL_MASK).SetOriginalCaster(m_originalCasterGUID)); } } @@ -4376,23 +4458,26 @@ void Spell::EffectChargeDest(SpellEffIndex effIndex) if (!m_targets.HasDst()) return; + if (!unitCaster) + return; + if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH) { Position pos = destTarget->GetPosition(); float speed = G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) ? m_spellInfo->Speed : SPEED_CHARGE; - 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, speed); + unitCaster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speed); } else if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT) if (int32 spellId = GetSpellInfo()->Effects[effIndex].TriggerSpell) - m_caster->CastSpell(nullptr, spellId, CastSpellExtraArgs(TRIGGERED_FULL_MASK).SetOriginalCaster(m_originalCasterGUID)); + unitCaster->CastSpell(nullptr, spellId, CastSpellExtraArgs(TRIGGERED_FULL_MASK).SetOriginalCaster(m_originalCasterGUID)); } void Spell::EffectKnockBack(SpellEffIndex effIndex) @@ -4403,7 +4488,7 @@ void Spell::EffectKnockBack(SpellEffIndex effIndex) if (!unitTarget) return; - if (m_caster->GetTypeId() == TYPEID_PLAYER || m_caster->GetOwnerOrCreatorGUID().IsPlayer() || m_caster->IsHunterPet()) + if (m_caster->GetAffectingPlayer()) if (Creature* creatureTarget = unitTarget->ToCreature()) if (creatureTarget->isWorldBoss() || creatureTarget->IsDungeonBoss()) return; @@ -4431,9 +4516,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); } @@ -4682,13 +4765,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_FIRE; 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 = m_caster->GetMap()->GetCreature(unitCaster->m_SummonSlot[slot]); if (totem && totem->IsTotem()) { uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); @@ -4696,7 +4782,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(); } @@ -4706,7 +4792,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); } } @@ -4773,10 +4859,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) @@ -4784,6 +4870,9 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; + if (!unitCaster) + return; + uint32 name_id = m_spellInfo->Effects[effIndex].MiscValue; GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(name_id); @@ -4801,9 +4890,9 @@ 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); - fo = m_caster->GetOrientation(); + float dis = m_spellInfo->Effects[effIndex].CalcRadius(unitCaster); + unitCaster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); + fo = unitCaster->GetOrientation(); } else { @@ -4812,14 +4901,14 @@ 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); - fo = m_caster->GetOrientation(); + unitCaster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); + fo = unitCaster->GetOrientation(); } - 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, fo); + unitCaster->GetPosition(fx, fy, fz, fo); QuaternionData rot = QuaternionData::fromEulerAnglesZYX(fo, 0.f, 0.f); @@ -4839,8 +4928,8 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) { 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) @@ -4858,15 +4947,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_SPELLCASTER: case GAMEOBJECT_TYPE_GUILD_BANK: @@ -4885,9 +4974,9 @@ 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); @@ -4903,9 +4992,9 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) PhasingHandler::InheritPhaseShift(linkedTrap, m_caster); 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); } @@ -5283,8 +5372,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*/) @@ -5295,15 +5387,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*/) @@ -5322,11 +5410,10 @@ 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::EffectRenamePet(SpellEffIndex /*effIndex*/) @@ -5452,16 +5539,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; @@ -5475,14 +5562,14 @@ void Spell::EffectCastButtons(SpellEffIndex effIndex) if (!spellInfo) continue; - if (!p_caster->HasSpell(spell_id) || p_caster->GetSpellHistory()->HasCooldown(spell_id)) + if (!player->HasSpell(spell_id) || player->GetSpellHistory()->HasCooldown(spell_id)) continue; if (!spellInfo->HasAttribute(SPELL_ATTR9_SUMMON_PLAYER_TOTEM)) continue; - int32 cost = spellInfo->CalcPowerCost(m_caster, spellInfo->GetSchoolMask()); - if (m_caster->GetPower(POWER_MANA) < cost) + int32 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); @@ -5559,8 +5646,10 @@ void Spell::EffectSummonRaFFriend(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH) return; - if (Player* target = ObjectAccessor::FindPlayer(m_caster->GetTarget())) - m_caster->CastSpell(nullptr, m_spellInfo->Effects[effIndex].TriggerSpell); + if (m_caster->GetTypeId() != TYPEID_PLAYER || !unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + m_caster->CastSpell(unitTarget, m_spellInfo->Effects[effIndex].TriggerSpell); } void Spell::EffectUnlockGuildVaultTab(SpellEffIndex effIndex) @@ -5658,8 +5747,8 @@ void Spell::EffectResurrectWithAura(SpellEffIndex effIndex) return; // SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER bonus - if (m_caster->IsPlayer() && m_caster->ToPlayer()->GetGuildId() == player->GetGuildId() && !m_caster->IsInCombat()) - AddPct(health, m_caster->GetTotalAuraModifier(SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER)); + if (m_caster->IsPlayer() && m_caster->ToPlayer()->GetGuildId() == player->GetGuildId() && !m_caster->ToPlayer()->IsInCombat()) + AddPct(health, m_caster->ToPlayer()->GetTotalAuraModifier(SPELL_AURA_MOD_RESURRECTED_HEALTH_BY_GUILD_MEMBER)); ExecuteLogEffectResurrect(effIndex, player); player->SetResurrectRequestData(m_caster, health, mana, resurrectAura); @@ -5671,9 +5760,12 @@ void Spell::EffectCreateAreaTrigger(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; + if (!unitCaster || !m_targets.HasDst()) + return; + Position pos; if (!m_targets.HasDst()) - pos = GetCaster()->GetPosition(); + pos = unitCaster->GetPosition(); else pos = destTarget->GetPosition(); @@ -5681,7 +5773,7 @@ void Spell::EffectCreateAreaTrigger(SpellEffIndex effIndex) uint32 triggerEntry = GetSpellInfo()->Effects[effIndex].MiscValue; AreaTrigger* areaTrigger = new AreaTrigger(); - if (!areaTrigger->CreateAreaTrigger(m_caster->GetMap()->GenerateLowGuid(), triggerEntry, GetCaster(), GetSpellInfo(), pos)) + if (!areaTrigger->CreateAreaTrigger(m_caster->GetMap()->GenerateLowGuid(), triggerEntry, unitCaster, GetSpellInfo(), pos)) delete areaTrigger; } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index fa4a523353..203587bc6b 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -423,7 +423,7 @@ bool SpellEffectInfo::IsUnitOwnedAuraEffect() const return IsAreaAuraEffect() || Effect == SPELL_EFFECT_APPLY_AURA || Effect == SPELL_EFFECT_APPLY_AURA_2; } -uint32 SpellEffectInfo::CalcPeriod(Unit* caster, Spell* spell /* = nullptr */) const +uint32 SpellEffectInfo::CalcPeriod(WorldObject const* caster, Spell* spell /* = nullptr */) const { // Default value found in client. uint32 period = AuraPeriod; @@ -435,27 +435,31 @@ uint32 SpellEffectInfo::CalcPeriod(Unit* caster, Spell* spell /* = nullptr */) c if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_ACTIVATION_TIME, period, spell); - if (_spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) && !_spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) - period = int32(period * caster->GetFloatValue(UNIT_MOD_CAST_HASTE)); + if (caster->IsUnit() && _spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) && !_spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) + period = int32(period * caster->ToUnit()->GetFloatValue(UNIT_MOD_CAST_HASTE)); } return period; } -int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const* target) const +int32 SpellEffectInfo::CalcValue(WorldObject const* caster, int32 const* bp, Unit const* target) const { float basePointsPerLevel = RealPointsPerLevel; int32 basePoints = bp ? *bp : BasePoints; float comboDamage = PointsPerComboPoint; + Unit const* casterUnit = nullptr; + if (caster) + casterUnit = caster->ToUnit(); + // base amount modification based on spell lvl vs caster lvl if (Scaling.Coefficient != 0.0f) { uint32 level = _spellInfo->SpellLevel; if (target && _spellInfo->IsPositiveEffect(_effIndex) && (Effect == SPELL_EFFECT_APPLY_AURA || Effect == SPELL_EFFECT_APPLY_AURA_2)) level = target->getLevel(); - else if (caster) - level = caster->getLevel(); + else if (casterUnit) + level = casterUnit->getLevel(); if (_spellInfo->HasAttribute(SPELL_ATTR10_USE_SPELL_BASE_LEVEL_FOR_SCALING)) level = _spellInfo->BaseLevel; @@ -494,9 +498,9 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const } else { - if (caster) + 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)) @@ -528,17 +532,17 @@ 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 (_spellInfo->HasAttribute(SPELL_ATTR1_FINISHING_MOVE_DAMAGE) && caster->IsMovedByClient() && comboDamage) - if (uint8 comboPoints = caster->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints()) + if (_spellInfo->HasAttribute(SPELL_ATTR1_FINISHING_MOVE_DAMAGE) && casterUnit->IsMovedByClient() && comboDamage) + if (uint8 comboPoints = casterUnit->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints()) value += comboDamage * comboPoints; - value = caster->ApplyEffectModifiers(_spellInfo, _effIndex, value); + value = casterUnit->ApplyEffectModifiers(_spellInfo, _effIndex, value); - if (!caster->IsControlledByPlayer() && - _spellInfo->SpellLevel && _spellInfo->SpellLevel != caster->getLevel() && + if (!casterUnit->IsControlledByPlayer() && + _spellInfo->SpellLevel && _spellInfo->SpellLevel != casterUnit->getLevel() && !basePointsPerLevel && _spellInfo->HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL)) { bool canEffectScale = false; @@ -582,12 +586,12 @@ 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; - if (caster->getLevel() > 80) - value *= (caster->getLevel() - 80) * 4.4f; // Cataclysm creatures have a way higher jump in stats than previous expansions so we use this estimated value based on combat log packet research + if (casterUnit->getLevel() > 80) + value *= (casterUnit->getLevel() - 80) * 4.4f; // Cataclysm creatures have a way higher jump in stats than previous expansions so we use this estimated value based on combat log packet research } } } @@ -604,7 +608,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) const { float multiplier = Amplitude; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr)) @@ -612,7 +616,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) const { float multiplierPercent = DamageMultiplier * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr)) @@ -630,7 +634,7 @@ bool SpellEffectInfo::HasMaxRadius() const return MaxRadiusEntry != nullptr; } -float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const +float SpellEffectInfo::CalcRadius(WorldObject* caster, Spell* spell) const { const SpellRadiusEntry* entry = RadiusEntry; if (!HasRadius() && HasMaxRadius()) @@ -642,8 +646,11 @@ float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const float radius = entry->RadiusMin; if (caster) { - radius += entry->RadiusPerLevel * caster->getLevel(); + if (Unit* casterUnit = caster->ToUnit()) + radius += entry->RadiusPerLevel * casterUnit->getLevel(); + radius = std::min(radius, entry->RadiusMax); + if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_RADIUS, radius, spell); } @@ -1116,7 +1123,7 @@ bool SpellInfo::HasOnlyDamageEffects() const return true; } -bool SpellInfo::CanBeInterrupted(Unit* interruptTarget, bool ignoreImmunity /*= false*/) const +bool SpellInfo::CanBeInterrupted(Unit const* interruptTarget, bool ignoreImmunity /*= false*/) const { return HasAttribute(SPELL_ATTR7_CAN_ALWAYS_BE_INTERRUPTED) || HasChannelInterruptFlag(SpellAuraInterruptFlags::Damage | SpellAuraInterruptFlags::EnteringCombat) @@ -1867,7 +1874,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; @@ -1955,7 +1962,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta return SPELL_FAILED_BAD_TARGETS; // Do not allow pet or guardian targets if the spell is a raid buff or may not target pets at all - if ((HasAttribute(SPELL_ATTR7_CONSOLIDATED_RAID_BUFF) || HasAttribute(SPELL_ATTR5_DONT_ALLOW_PET_TARGET)) && (!caster->IsPet() && !caster->IsGuardian())) + if ((HasAttribute(SPELL_ATTR7_CONSOLIDATED_RAID_BUFF) || HasAttribute(SPELL_ATTR5_DONT_ALLOW_PET_TARGET)) && (caster->IsCreature() && !caster->ToCreature()->IsPet() && !caster->ToCreature()->IsGuardian())) if ((unitTarget->IsPet() || unitTarget->IsGuardian())) return SPELL_FAILED_BAD_TARGETS; @@ -1974,7 +1981,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; @@ -1994,13 +2001,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))) @@ -2019,7 +2029,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) const { uint32 neededTargets = GetExplicitTargetMask(); if (!target) @@ -2034,19 +2044,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 (caster->IsValidAssistTarget(unitTarget, this)) - return SPELL_CAST_OK; - if (neededTargets & TARGET_FLAG_UNIT_MINIPET) - if (unitTarget->GetGUID() == caster->GetCritterGUID()) + 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_PASSENGER) - if (unitTarget->IsOnVehicle(caster)) + if ((neededTargets & TARGET_FLAG_UNIT_MINIPET) && unitCaster) + if (unitTarget->GetGUID() == unitCaster->GetCritterGUID()) + return SPELL_CAST_OK; + if ((neededTargets & TARGET_FLAG_UNIT_PASSENGER) && unitCaster) + if (unitTarget->IsOnVehicle(unitCaster)) return SPELL_CAST_OK; return SPELL_FAILED_BAD_TARGETS; } @@ -3335,7 +3346,7 @@ float SpellInfo::GetMinRange(bool positive) const return RangeEntry->RangeMin[0]; } -float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const +float SpellInfo::GetMaxRange(bool positive, WorldObject* caster, Spell* spell) const { if (!RangeEntry) return 0.0f; @@ -3364,7 +3375,7 @@ int32 SpellInfo::GetMaxDuration() const return (DurationEntry->MaxDuration == -1) ? -1 : abs(DurationEntry->MaxDuration); } -int32 SpellInfo::CalcDuration(Unit* caster, Spell* spell) const +int32 SpellInfo::CalcDuration(WorldObject const* caster, Spell* spell) const { if (IsPassive() && !DurationEntry) return -1; @@ -3376,17 +3387,18 @@ int32 SpellInfo::CalcDuration(Unit* caster, Spell* spell) const if (duration == -1) return -1; - if (caster != nullptr) - { - uint32 level = caster->getLevel(); - if (MaxLevel > 0 && level > MaxLevel) - level = MaxLevel; - if (BaseLevel > 0) - level -= BaseLevel; + Unit const* unitCaster = caster ? caster->ToUnit() : nullptr; + if (!unitCaster) + return std::min(GetMaxDuration(), DurationEntry->Duration); - duration = DurationEntry->Duration + DurationEntry->DurationPerLevel * level; - duration = std::min(GetMaxDuration(), duration); - } + uint32 level = unitCaster->getLevel(); + if (MaxLevel > 0 && level > MaxLevel) + level = MaxLevel; + if (BaseLevel > 0) + level -= BaseLevel; + + duration = DurationEntry->Duration + DurationEntry->DurationPerLevel * level; + duration = std::min(GetMaxDuration(), duration); if (duration == -1) return -1; @@ -3394,21 +3406,20 @@ int32 SpellInfo::CalcDuration(Unit* caster, Spell* spell) const // Increase duration based on combo points if (HasAttribute(SPELL_ATTR1_FINISHING_MOVE_DURATION)) { - if (uint8 comboPoints = (caster && caster->IsMovedByClient()) ? caster->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints() : 0) + if (uint8 comboPoints = unitCaster->IsMovedByClient() ? unitCaster->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints() : 0) { if (GetDuration() != GetMaxDuration() && GetDuration() != -1) duration += int32((GetMaxDuration() - GetDuration()) * comboPoints / 5); } } - if (caster != nullptr) - if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, SPELLMOD_DURATION, duration, spell); + if (Player* modOwner = unitCaster->GetSpellModOwner()) + modOwner->ApplySpellMod(Id, SPELLMOD_DURATION, duration, spell); bool hasteAffectsDuration = HasAttribute(SPELL_ATTR8_HASTE_AFFECTS_DURATION); bool spellHasteAffectsPeriodic = HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC); // That aura is not used as of 4.3.4, but just include it, as it still is technically supported by the client, and so should we. - bool hasPeriodicHasteAuras = caster != nullptr && caster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, this); + bool hasPeriodicHasteAuras = unitCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, this); if (spellHasteAffectsPeriodic || hasteAffectsDuration) { @@ -3423,13 +3434,13 @@ int32 SpellInfo::CalcDuration(Unit* caster, Spell* spell) const if ((spellHasteAffectsPeriodic || hasPeriodicHasteAuras) && !HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) { - float hasteValue = caster ? caster->GetFloatValue(UNIT_MOD_CAST_HASTE) : 0.0f; + float hasteValue = unitCaster->GetFloatValue(UNIT_MOD_CAST_HASTE); if (hasteValue > 0.0f) { if (hasteAffectsDuration) return int32(duration * hasteValue); - int32 effectPeriod = Effects[periodicEffectIndex].CalcPeriod(caster, spell); + int32 effectPeriod = Effects[periodicEffectIndex].CalcPeriod(unitCaster, spell); if (effectPeriod > 0) { @@ -3450,10 +3461,10 @@ int32 SpellInfo::CalcDuration(Unit* caster, Spell* spell) const uint32 SpellInfo::CalcCastTime(uint8 level, Spell* spell /*= nullptr*/) const { int32 castTime = 0; - if (!level && spell) - level = spell->GetCaster()->getLevel(); + if (!level && spell && spell->GetCaster()->IsUnit()) + level = spell->GetCaster()->ToUnit()->getLevel(); - // not all spells have cast time index and this is all is pasiive abilities + // not all spells have cast time index and this is all is passive abilities if (level && Scaling.CastTimeMax > 0) { castTime = Scaling.CastTimeMax; @@ -3515,17 +3526,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; @@ -3556,15 +3572,15 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S { // health as power used case POWER_HEALTH: - ressource = caster->GetCreateHealth(); + ressource = unitCaster->GetCreateHealth(); break; case POWER_MANA: - ressource = caster->GetCreateMana(); + ressource = unitCaster->GetCreateMana(); break; case POWER_RAGE: case POWER_FOCUS: case POWER_ENERGY: - ressource = caster->GetMaxPower(Powers(PowerType)); + ressource = unitCaster->GetMaxPower(Powers(PowerType)); break; case POWER_RUNE: case POWER_RUNIC_POWER: @@ -3581,7 +3597,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S // Scaling if (SpellScalingId) - powerCost = int32(ceilf(GetSpellScalingMultiplier(caster, GetSpellScaling(), true) * (double)powerCost)); + powerCost = int32(ceilf(GetSpellScalingMultiplier(unitCaster, GetSpellScaling(), true) * (double)powerCost)); else { uint32 manaCostPerLevel = invertSign ? -int32(ManaCostPerlevel) : ManaCostPerlevel; @@ -3590,7 +3606,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S } // Flat mod from caster auras by spell school and power type - Unit::AuraEffectList const& auras = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL); + Unit::AuraEffectList const& auras = unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL); for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i) { if (!((*i)->GetMiscValue() & schoolMask)) @@ -3604,7 +3620,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S if (HasAttribute(SPELL_ATTR4_SPELL_VS_EXTEND_COST)) { uint32 speed = 0; - if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(caster->GetShapeshiftForm())) + if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(unitCaster->GetShapeshiftForm())) speed = ss->CombatRoundTime; else { @@ -3612,7 +3628,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S if (HasAttribute(SPELL_ATTR3_REQ_OFFHAND)) slot = OFF_ATTACK; - speed = caster->GetAttackTime(slot); + speed = unitCaster->GetAttackTime(slot); } powerCost += speed / 100; @@ -3622,12 +3638,12 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S if (Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost, spell); - if (!caster->IsControlledByPlayer()) + if (!unitCaster->IsControlledByPlayer()) { if (HasAttribute(SPELL_ATTR0_SCALES_WITH_CREATURE_LEVEL)) { 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 && spellScaler->ratio > 0.f && casterScaler && casterScaler->ratio > 0.f) powerCost *= casterScaler->ratio / spellScaler->ratio; } @@ -3637,7 +3653,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S powerCost = -powerCost; // PCT mod from user auras by spell school and power type - Unit::AuraEffectList const& aurasPct = caster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT); + Unit::AuraEffectList const& aurasPct = unitCaster->GetAuraEffectsByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT); for (Unit::AuraEffectList::const_iterator i = aurasPct.begin(); i != aurasPct.end(); ++i) { if (!((*i)->GetMiscValue() & schoolMask)) @@ -3651,16 +3667,16 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S return powerCost; } -float SpellInfo::GetSpellScalingMultiplier(Unit const* caster, SpellScalingEntry const* scalingEntry, bool isPowerCostRelated /*= false*/) const +float SpellInfo::GetSpellScalingMultiplier(WorldObject const* caster, SpellScalingEntry const* scalingEntry, bool isPowerCostRelated /*= false*/) const { - if (!caster || !scalingEntry) + if (!caster || !caster->IsUnit() || !scalingEntry) return 1.f; float multiplier = 1.f; float scalingMultiplier = 1.f; int32 castTimeMaxLevel = scalingEntry->CastTimeMaxLevel; - uint8 casterLevel = caster->getLevel(); + uint8 casterLevel = caster->ToUnit()->getLevel(); if (casterLevel < castTimeMaxLevel && scalingEntry->CastTimeMax) { diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 90e52cee64..01e119f120 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -291,14 +291,14 @@ 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, Unit const* target = 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; bool HasMaxRadius() const; - float CalcRadius(Unit* caster = nullptr, Spell* = nullptr) const; + float CalcRadius(WorldObject* caster = nullptr, Spell* = nullptr) const; uint32 GetProvidedTargetMask() const; uint32 GetMissingTargetMask(bool srcSet = false, bool destSet = false, uint32 mask = 0) const; @@ -306,7 +306,7 @@ public: SpellEffectImplicitTargetTypes GetImplicitTargetType() const; SpellTargetObjectTypes GetUsedTargetObjectType() const; - uint32 CalcPeriod(Unit* caster, Spell* spell = nullptr) const; + uint32 CalcPeriod(WorldObject const* caster, Spell* spell = nullptr) const; private: struct StaticData @@ -493,7 +493,7 @@ class TC_GAME_API SpellInfo inline bool HasAttribute(SpellAttr10 attribute) const { return !!(AttributesEx10 & attribute); } inline bool HasAttribute(SpellCustomAttributes customAttribute) const { return !!(AttributesCu & customAttribute); } - bool CanBeInterrupted(Unit* interruptTarget, bool ignoreImmunity = false) const; + bool CanBeInterrupted(Unit const* interruptTarget, bool ignoreImmunity = false) const; bool HasAnyAuraInterruptFlag() const; bool HasAuraInterruptFlag(SpellAuraInterruptFlags flag) const { return AuraInterruptFlags.HasFlag(flag); } @@ -559,8 +559,8 @@ class TC_GAME_API SpellInfo SpellCastResult CheckShapeshift(uint32 form) const; SpellCastResult CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = nullptr) const; - SpellCastResult CheckTarget(Unit const* caster, WorldObject const* target, bool implicit = true) const; - SpellCastResult CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget = nullptr) const; + SpellCastResult CheckTarget(WorldObject const* caster, WorldObject const* target, bool implicit = true) const; + SpellCastResult CheckExplicitTarget(WorldObject const* caster, WorldObject const* target, Item const* itemTarget = nullptr) const; SpellCastResult CheckVehicle(Unit const* caster) const; bool CheckTargetCreatureType(Unit const* target) const; @@ -578,20 +578,20 @@ 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; - int32 CalcDuration(Unit* caster, Spell* spell = nullptr) const; + int32 CalcDuration(WorldObject const* caster, Spell* spell = nullptr) const; uint32 GetMaxTicks() const; uint32 CalcCastTime(uint8 level = 0, Spell* spell = nullptr) const; uint32 GetRecoveryTime() const; - int32 CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; - float GetSpellScalingMultiplier(Unit const* caster, SpellScalingEntry const* scalingEntry, bool isPowerCostRelated = false) const; + int32 CalcPowerCost(WorldObject const* caster, SpellSchoolMask schoolMask, Spell* spell = nullptr) const; + float GetSpellScalingMultiplier(WorldObject const* caster, SpellScalingEntry const* scalingEntry, bool isPowerCostRelated = false) const; bool IsRanked() const; uint8 GetRank() const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 3998f07dd1..d04b3dd90b 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->DifficultySpellID[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 657ca18a58..b981920256 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -617,8 +617,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 2583919546..d7988820d3 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -186,12 +186,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 @@ -642,7 +647,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 1abbea4ed1..8ec6d1887d 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -613,6 +613,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; @@ -1036,6 +1037,8 @@ class TC_GAME_API AuraScript : public _SpellScript ObjectGuid GetCasterGUID() const; // returns unit which cast the aura or nullptr 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, nullptr otherwise (only for persistent area auras nullptr is returned) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index da79a77af2..85cb7cc8be 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -1736,12 +1736,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 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::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 9c9c9f9ca9..d3c4db460e 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -1313,7 +1313,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()) {