diff --git a/sql/migrations/20190914214129_world.sql b/sql/migrations/20190914214129_world.sql new file mode 100644 index 000000000..ee62512e1 --- /dev/null +++ b/sql/migrations/20190914214129_world.sql @@ -0,0 +1,23 @@ +DROP PROCEDURE IF EXISTS add_migration; +delimiter ?? +CREATE PROCEDURE `add_migration`() +BEGIN +DECLARE v INT DEFAULT 1; +SET v = (SELECT COUNT(*) FROM `migrations` WHERE `id`='20190914214129'); +IF v=0 THEN +INSERT INTO `migrations` VALUES ('20190914214129'); +-- Add your query below. + + +-- Fix target order for Tonk Control Console Trap. +DELETE FROM `spell_scripts` WHERE `id`=24935; +INSERT INTO `spell_scripts` (`id`, `delay`, `command`, `datalong`, `datalong2`, `datalong3`, `datalong4`, `target_param1`, `target_param2`, `target_type`, `data_flags`, `dataint`, `dataint2`, `dataint3`, `dataint4`, `x`, `y`, `z`, `o`, `condition_id`, `comments`) VALUES (24935, 0, 15, 24934, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Cast Summon RC Tank'); +INSERT INTO `spell_scripts` (`id`, `delay`, `command`, `datalong`, `datalong2`, `datalong3`, `datalong4`, `target_param1`, `target_param2`, `target_type`, `data_flags`, `dataint`, `dataint2`, `dataint3`, `dataint4`, `x`, `y`, `z`, `o`, `condition_id`, `comments`) VALUES (24935, 2, 15, 24936, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Cast RC Tank Control'); + + +-- End of migration. +END IF; +END?? +delimiter ; +CALL add_migration(); +DROP PROCEDURE IF EXISTS add_migration; diff --git a/src/game/AI/GameObjectAI.h b/src/game/AI/GameObjectAI.h index 9be60a638..b574b7b0f 100644 --- a/src/game/AI/GameObjectAI.h +++ b/src/game/AI/GameObjectAI.h @@ -15,7 +15,6 @@ class MANGOS_DLL_SPEC GameObjectAI virtual void SetData(uint32 /*id*/, uint32 /*value*/) {} virtual void InformGuid(ObjectGuid guid) {} virtual bool OnUse(Unit* /*user*/) { return false; } - virtual SpellCastResult CheckSpellCast(Unit* /*caster*/, uint32 /*spellId*/) { return SPELL_CAST_OK; } // Called if a temporary summoned of m_creature reach a move point virtual void SummonedMovementInform(Creature* /*summoned*/, uint32 /*motion_type*/, uint32 /*point_id*/) {} diff --git a/src/game/Maps/GridNotifiers.h b/src/game/Maps/GridNotifiers.h index fb2ab4a39..d6246cc9a 100644 --- a/src/game/Maps/GridNotifiers.h +++ b/src/game/Maps/GridNotifiers.h @@ -150,12 +150,12 @@ namespace MaNGOS struct MANGOS_DLL_DECL DynamicObjectUpdater { DynamicObject &i_dynobject; - Unit* i_check; + WorldObject* i_check; bool i_positive; - DynamicObjectUpdater(DynamicObject &dynobject, Unit* caster, bool positive) : i_dynobject(dynobject), i_positive(positive) + DynamicObjectUpdater(DynamicObject &dynobject, WorldObject* caster, bool positive) : i_dynobject(dynobject), i_positive(positive) { i_check = caster; - Unit* owner = i_check->GetOwner(); + Unit* owner = i_check->IsUnit() ? static_cast(i_check)->GetOwner() : nullptr; if(owner) i_check = owner; } @@ -585,8 +585,8 @@ namespace MaNGOS class GameObjectFocusCheck { public: - GameObjectFocusCheck(Unit const* unit,uint32 focusId) : i_unit(unit), i_focusId(focusId) {} - WorldObject const& GetFocusObject() const { return *i_unit; } + GameObjectFocusCheck(WorldObject const* caster, uint32 focusId) : i_caster(caster), i_focusId(focusId) {} + WorldObject const& GetFocusObject() const { return *i_caster; } bool operator()(GameObject* go) const { if(go->GetGOInfo()->type != GAMEOBJECT_TYPE_SPELL_FOCUS) @@ -597,10 +597,10 @@ namespace MaNGOS float dist = (float)go->GetGOInfo()->spellFocus.dist; - return go->IsWithinDistInMap(i_unit, dist); + return go->IsWithinDistInMap(i_caster, dist); } private: - Unit const* i_unit; + WorldObject const* i_caster; uint32 i_focusId; }; diff --git a/src/game/Maps/GridNotifiersImpl.h b/src/game/Maps/GridNotifiersImpl.h index e4cf9c3bb..9c842201c 100644 --- a/src/game/Maps/GridNotifiersImpl.h +++ b/src/game/Maps/GridNotifiersImpl.h @@ -164,9 +164,11 @@ inline void MaNGOS::DynamicObjectUpdater::VisitHelper(Unit* target) if (!i_dynobject.NeedsRefresh(target)) return; + Unit* pUnit = i_check->ToUnit(); + // Negative AoE from non flagged players cannot target other players if (Player *attackedPlayer = target->GetCharmerOrOwnerPlayerOrPlayerItself()) - if (!i_positive && i_check->IsPlayer() && !i_check->IsPvP() && !((Player*)i_check)->IsInDuelWith(attackedPlayer)) + if (pUnit && !i_positive && i_check->IsPlayer() && !pUnit->IsPvP() && !((Player*)i_check)->IsInDuelWith(attackedPlayer)) return; SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(i_dynobject.GetSpellId()); @@ -174,13 +176,13 @@ inline void MaNGOS::DynamicObjectUpdater::VisitHelper(Unit* target) // Mise en combat // Exception : fusee eclairante, piege de givre - if (!i_positive && i_dynobject.GetSpellId() != 1543 && i_dynobject.GetSpellId() != 13810) + if (pUnit && !i_positive && i_dynobject.GetSpellId() != 1543 && i_dynobject.GetSpellId() != 13810) { if (CreatureAI* pAi = target->AI()) - pAi->AttackedBy(i_check); + pAi->AttackedBy(pUnit); - target->SetInCombatWithAggressor(i_check); - i_check->SetInCombatWithVictim(target); + target->SetInCombatWithAggressor(pUnit); + pUnit->SetInCombatWithVictim(target); } // Check target immune to spell or aura if (target->IsImmuneToSpell(spellInfo, false) || target->IsImmuneToSpellEffect(spellInfo, eff_index, false)) @@ -196,7 +198,11 @@ inline void MaNGOS::DynamicObjectUpdater::VisitHelper(Unit* target) holder->SetInUse(true); if (!holder->GetAuraByEffectIndex(eff_index)) { - PersistentAreaAura* Aur = new PersistentAreaAura(spellInfo, eff_index, NULL, holder, target, i_dynobject.GetCaster()); + Unit* pCaster = i_dynobject.GetCaster()->ToUnit(); + if (!pCaster) + pCaster = target; + + PersistentAreaAura* Aur = new PersistentAreaAura(spellInfo, eff_index, NULL, holder, target, pCaster); holder->AddAura(Aur, eff_index); target->AddAuraToModList(Aur); @@ -214,8 +220,12 @@ inline void MaNGOS::DynamicObjectUpdater::VisitHelper(Unit* target) } else { + Unit* pCaster = i_dynobject.GetCaster()->ToUnit(); + if (!pCaster) + pCaster = target; + holder = CreateSpellAuraHolder(spellInfo, target, i_dynobject.GetCaster()); - PersistentAreaAura* Aur = new PersistentAreaAura(spellInfo, eff_index, NULL, holder, target, i_dynobject.GetCaster()); + PersistentAreaAura* Aur = new PersistentAreaAura(spellInfo, eff_index, NULL, holder, target, pCaster); holder->AddAura(Aur, eff_index); // Debuff slots may be full, in which case holder is deleted or holder is not able to @@ -226,7 +236,7 @@ inline void MaNGOS::DynamicObjectUpdater::VisitHelper(Unit* target) if (holder && holder->IsChanneled()) { - if (Unit *caster = i_dynobject.GetCaster()) + if (WorldObject *caster = i_dynobject.GetCaster()) { // Caster is channeling this spell, update current channel spell holders with // the new holder. Don't check channel object, as it might be a spell with diff --git a/src/game/Maps/ScriptCommands.cpp b/src/game/Maps/ScriptCommands.cpp index ad5ecfd03..3154b26a2 100644 --- a/src/game/Maps/ScriptCommands.cpp +++ b/src/game/Maps/ScriptCommands.cpp @@ -566,24 +566,24 @@ bool Map::ScriptCommand_CastSpell(const ScriptInfo& script, WorldObject* source, Unit* pUnitSource = ToUnit(source); Unit* pUnitTarget = ToUnit(target); - if (!pUnitSource) + if (!source) { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL (script id %u) call for a NULL or non-unit source (TypeId: %u), skipping.", script.id, source ? source->GetTypeId() : 0); + sLog.outError("SCRIPT_COMMAND_CAST_SPELL (script id %u) call for a NULL source, skipping.", script.id); return ShouldAbortScript(script); } if (!pUnitTarget) return ShouldAbortScript(script); - if ((script.castSpell.flags & CF_INTERRUPT_PREVIOUS) && pUnitSource->IsNonMeleeSpellCasted(false)) + if ((script.castSpell.flags & CF_INTERRUPT_PREVIOUS) && pUnitSource && pUnitSource->IsNonMeleeSpellCasted(false)) pUnitSource->InterruptNonMeleeSpells(false); - Creature* pCreatureSource = pUnitSource->ToCreature(); + Creature* pCreatureSource = source->ToCreature(); if (pCreatureSource) pCreatureSource->TryToCast(pUnitTarget, script.castSpell.spellId, script.castSpell.flags, 0u); else - pUnitSource->CastSpell(pUnitTarget, script.castSpell.spellId, (script.castSpell.flags & CF_TRIGGERED) != 0); + source->CastSpell(pUnitTarget, script.castSpell.spellId, (script.castSpell.flags & CF_TRIGGERED) != 0); return false; } diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp index a7fffa72f..e84f43cea 100644 --- a/src/game/ObjectGridLoader.cpp +++ b/src/game/ObjectGridLoader.cpp @@ -339,5 +339,14 @@ ObjectGridStoper::Visit(CreatureMapType &m) } } -template void ObjectGridUnloader::Visit(GameObjectMapType &); +void +ObjectGridStoper::Visit(GameObjectMapType &m) +{ + // remove dynobjects created at cast at grid de-activation + for (GameObjectMapType::iterator iter = m.begin(); iter != m.end(); ++iter) + { + iter->getSource()->RemoveAllDynObjects(); + } +} + template void ObjectGridUnloader::Visit(DynamicObjectMapType &); diff --git a/src/game/ObjectGridLoader.h b/src/game/ObjectGridLoader.h index 22765c525..5ad17b611 100644 --- a/src/game/ObjectGridLoader.h +++ b/src/game/ObjectGridLoader.h @@ -102,6 +102,7 @@ class MANGOS_DLL_DECL ObjectGridStoper void Stop(GridType &grid); void Visit(CreatureMapType &m); + void Visit(GameObjectMapType &m); template void Visit(GridRefManager &) {} private: diff --git a/src/game/Objects/Corpse.cpp b/src/game/Objects/Corpse.cpp index 497d03c4e..9352f3c5e 100644 --- a/src/game/Objects/Corpse.cpp +++ b/src/game/Objects/Corpse.cpp @@ -255,31 +255,31 @@ bool Corpse::LoadFromDB(uint32 lowguid, Field *fields) return true; } -bool Corpse::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const +bool Corpse::isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const { - return IsInWorld() && u->IsInWorld() && IsWithinDist(viewPoint, u->GetMap()->GetVisibilityDistance() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) + GetVisibilityModifier(), false); + return IsInWorld() && pDetector->IsInWorld() && IsWithinDist(viewPoint, pDetector->GetMap()->GetVisibilityDistance() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) + GetVisibilityModifier(), false); } -ReputationRank Corpse::GetReactionTo(Unit const* unit) const +ReputationRank Corpse::GetReactionTo(WorldObject const* target) const { if (Player* owner = sObjectMgr.GetPlayer(GetOwnerGuid())) - return owner->GetReactionTo(unit); - else if (unit->IsPlayer() && unit->ToPlayer()->GetGroup() && unit->ToPlayer()->GetGroup()->IsMember(GetOwnerGuid())) + return owner->GetReactionTo(target); + else if (target->IsPlayer() && target->ToPlayer()->GetGroup() && target->ToPlayer()->GetGroup()->IsMember(GetOwnerGuid())) return REP_FRIENDLY; else if (m_faction) - return Unit::GetFactionReactionTo(m_faction, unit); + return WorldObject::GetFactionReactionTo(m_faction, target); else return REP_NEUTRAL; } -bool Corpse::IsHostileTo(Unit const* unit) const +bool Corpse::IsHostileTo(WorldObject const* target) const { - return GetReactionTo(unit) <= REP_HOSTILE; + return GetReactionTo(target) <= REP_HOSTILE; } -bool Corpse::IsFriendlyTo(Unit const* unit) const +bool Corpse::IsFriendlyTo(WorldObject const* target) const { - return GetReactionTo(unit) >= REP_FRIENDLY; + return GetReactionTo(target) >= REP_FRIENDLY; } bool Corpse::IsExpired(time_t t) const @@ -289,3 +289,10 @@ bool Corpse::IsExpired(time_t t) const else return m_time < t - 3 * DAY; } + +uint32 Corpse::getLevel() const +{ + if (Unit* pOwner = ObjectAccessor::GetUnit(*this, GetOwnerGuid())) + return pOwner->getLevel(); + return DEFAULT_MAX_LEVEL; +} \ No newline at end of file diff --git a/src/game/Objects/Corpse.h b/src/game/Objects/Corpse.h index 5a1cdbeb2..0e117aee3 100644 --- a/src/game/Objects/Corpse.h +++ b/src/game/Objects/Corpse.h @@ -74,14 +74,14 @@ class Corpse : public WorldObject void ResetGhostTime() { m_time = time(NULL); } CorpseType GetType() const { return m_type; } - ReputationRank GetReactionTo(Unit const* unit) const; - bool IsHostileTo(Unit const* unit) const; - bool IsFriendlyTo(Unit const* unit) const; + ReputationRank GetReactionTo(WorldObject const* target) const final override; + bool IsHostileTo(WorldObject const* target) const override; + bool IsFriendlyTo(WorldObject const* target) const override; GridPair const& GetGrid() const { return m_grid; } void SetGrid(GridPair const& grid) { m_grid = grid; } - bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const; + bool isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const override; Loot loot; // remove insignia ONLY at BG Player* lootRecipient; @@ -92,6 +92,8 @@ class Corpse : public WorldObject bool IsExpired(time_t t) const; void SetFactionTemplate(FactionTemplateEntry const* entry) { m_faction = entry; } FactionTemplateEntry const* GetFactionTemplate() { return m_faction; } + uint32 getFaction() const final override { return m_faction->ID; } + uint32 getLevel() const final override; private: GridReference m_gridRef; FactionTemplateEntry const* m_faction; diff --git a/src/game/Objects/Creature.cpp b/src/game/Objects/Creature.cpp index 98db7c367..1ca93e82d 100644 --- a/src/game/Objects/Creature.cpp +++ b/src/game/Objects/Creature.cpp @@ -2046,7 +2046,7 @@ bool Creature::IsImmuneToSpellEffect(SpellEntry const *spellInfo, SpellEffectInd return Unit::IsImmuneToSpellEffect(spellInfo, index, castOnSelf); } -bool Creature::IsVisibleInGridForPlayer(Player* pl) const +bool Creature::IsVisibleInGridForPlayer(Player const* pl) const { // gamemaster in GM mode see all, including ghosts if (pl->IsGameMaster()) @@ -2890,19 +2890,6 @@ void Creature::AllLootRemovedFromCorpse() } } -uint32 Creature::GetLevelForTarget(Unit const* target) const -{ - if (!IsWorldBoss()) - return Unit::GetLevelForTarget(target); - - uint32 level = target->getLevel() + sWorld.getConfig(CONFIG_UINT32_WORLD_BOSS_LEVEL_DIFF); - if (level < 1) - return 1; - if (level > 255) - return 255; - return level; -} - std::string Creature::GetAIName() const { return ObjectMgr::GetCreatureTemplate(GetEntry())->ai_name; diff --git a/src/game/Objects/Creature.h b/src/game/Objects/Creature.h index ec59d608c..1a9073ef1 100644 --- a/src/game/Objects/Creature.h +++ b/src/game/Objects/Creature.h @@ -590,8 +590,6 @@ class MANGOS_DLL_SPEC Creature : public Unit return GetCreatureInfo()->rank == CREATURE_ELITE_WORLDBOSS; } - uint32 GetLevelForTarget(Unit const* target) const override; // overwrite Unit::GetLevelForTarget for boss level support - bool IsInEvadeMode() const; bool AIM_Initialize(); @@ -708,7 +706,7 @@ class MANGOS_DLL_SPEC Creature : public Unit Cell const& GetCurrentCell() const { return m_currentCell; } void SetCurrentCell(Cell const& cell) { m_currentCell = cell; } - bool IsVisibleInGridForPlayer(Player* pl) const override; + bool IsVisibleInGridForPlayer(Player const* pl) const override; void RemoveCorpse(); bool IsDeadByDefault() const { return m_isDeadByDefault; }; diff --git a/src/game/Objects/DynamicObject.cpp b/src/game/Objects/DynamicObject.cpp index c60810bf4..614abef8f 100644 --- a/src/game/Objects/DynamicObject.cpp +++ b/src/game/Objects/DynamicObject.cpp @@ -60,7 +60,7 @@ void DynamicObject::RemoveFromWorld() Object::RemoveFromWorld(); } -bool DynamicObject::Create(uint32 guidlow, Unit *caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius, DynamicObjectType type) +bool DynamicObject::Create(uint32 guidlow, WorldObject* caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius, DynamicObjectType type) { WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT); SetMap(caster->GetMap()); @@ -123,17 +123,39 @@ bool DynamicObject::Create(uint32 guidlow, Unit *caster, uint32 spellId, SpellEf return true; } -Unit* DynamicObject::GetCaster() const +WorldObject* DynamicObject::GetCaster() const { + if (ObjectGuid guid = GetCasterGuid()) + { + if (!guid.IsEmpty()) + { + if (guid.IsUnit()) + return ObjectAccessor::GetUnit(*this, guid); + else if (guid.IsGameObject()) + { + return GetMap()->GetGameObject(guid); + } + } + } // can be not found in some cases - return ObjectAccessor::GetUnit(*this, GetCasterGuid()); + return nullptr; +} + +uint32 DynamicObject::getFaction() const +{ + return GetCaster()->getFaction(); +} + +uint32 DynamicObject::getLevel() const +{ + return GetCaster()->getLevel(); } void DynamicObject::Update(uint32 update_diff, uint32 p_time) { WorldObject::Update(update_diff, p_time); // caster can be not in world at time dynamic object update, but dynamic object not yet deleted in Unit destructor - Unit* caster = GetCaster(); + WorldObject* caster = GetCaster(); if (!caster) { Delete(); @@ -152,7 +174,7 @@ void DynamicObject::Update(uint32 update_diff, uint32 p_time) if (m_aliveDuration <= 0) m_aliveDuration = 0; - if (m_aliveDuration == 0 && (!m_channeled || caster->GetChannelObjectGuid() != GetObjectGuid())) + if (m_aliveDuration == 0 && (!m_channeled || (caster->IsUnit() && static_cast(caster)->GetChannelObjectGuid() != GetObjectGuid()))) deleteThis = true; for (AffectedMap::iterator iter = m_affected.begin(); iter != m_affected.end(); ++iter) @@ -229,31 +251,31 @@ void DynamicObject::Delay(int32 delaytime) } } -bool DynamicObject::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const +bool DynamicObject::isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const { - if (!IsInWorld() || !u->IsInWorld()) + if (!IsInWorld() || !pDetector->IsInWorld()) return false; // always seen by owner - if (GetCasterGuid() == u->GetObjectGuid()) + if (GetCasterGuid() == pDetector->GetObjectGuid()) return true; // normal case return IsWithinDistInMap(viewPoint, GetMap()->GetVisibilityDistance() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) + GetVisibilityModifier(), false); } -bool DynamicObject::IsHostileTo(Unit const* unit) const +bool DynamicObject::IsHostileTo(WorldObject const* target) const { - if (Unit* owner = GetCaster()) - return owner->IsHostileTo(unit); + if (WorldObject* owner = GetCaster()) + return owner->IsHostileTo(target); else return false; } -bool DynamicObject::IsFriendlyTo(Unit const* unit) const +bool DynamicObject::IsFriendlyTo(WorldObject const* target) const { - if (Unit* owner = GetCaster()) - return owner->IsFriendlyTo(unit); + if (WorldObject* owner = GetCaster()) + return owner->IsFriendlyTo(target); else return true; } diff --git a/src/game/Objects/DynamicObject.h b/src/game/Objects/DynamicObject.h index 525f09797..47807438b 100644 --- a/src/game/Objects/DynamicObject.h +++ b/src/game/Objects/DynamicObject.h @@ -44,14 +44,14 @@ class DynamicObject : public WorldObject void AddToWorld(); void RemoveFromWorld(); - bool Create(uint32 guidlow, Unit *caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius, DynamicObjectType type); + bool Create(uint32 guidlow, WorldObject* caster, uint32 spellId, SpellEffectIndex effIndex, float x, float y, float z, int32 duration, float radius, DynamicObjectType type); void Update(uint32 update_diff, uint32 p_time) override; void Delete(); uint32 GetSpellId() const { return m_spellId; } SpellEffectIndex GetEffIndex() const { return m_effIndex; } uint32 GetDuration() const { return m_aliveDuration; } ObjectGuid const& GetCasterGuid() const { return GetGuidValue(DYNAMICOBJECT_CASTER); } - Unit* GetCaster() const; + WorldObject* GetCaster() const; float GetRadius() const { return m_radius; } DynamicObjectType GetType() const { return (DynamicObjectType)GetByteValue(DYNAMICOBJECT_BYTES,0); } bool NeedsRefresh(Unit *unit) const; @@ -60,15 +60,17 @@ class DynamicObject : public WorldObject void RemoveAffected(Unit *unit) { m_affected.erase(unit->GetObjectGuid()); } void Delay(int32 delaytime); - bool IsHostileTo(Unit const* unit) const; - bool IsFriendlyTo(Unit const* unit) const; + bool IsHostileTo(WorldObject const* target) const override; + bool IsFriendlyTo(WorldObject const* target) const override; + uint32 getFaction() const final override; + uint32 getLevel() const final override; float GetObjectBoundingRadius() const // overwrite WorldObject version { return 0.0f; // dynamic object not have real interact size } - bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const; + bool isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const override; GridReference &GetGridRef() { return m_gridRef; } diff --git a/src/game/Objects/GameObject.cpp b/src/game/Objects/GameObject.cpp index 4db8a37ed..951e8bcf4 100644 --- a/src/game/Objects/GameObject.cpp +++ b/src/game/Objects/GameObject.cpp @@ -73,8 +73,20 @@ GameObject::GameObject() : WorldObject(), GameObject::~GameObject() { + // set current spells as deletable + for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) + { + if (m_currentSpells[i]) + { + m_currentSpells[i]->SetReferencedFromCurrent(false); + m_currentSpells[i] = nullptr; + } + } + delete i_AI; delete m_model; + + MANGOS_ASSERT(m_dynObjGUIDs.size() == 0); } void GameObject::AddToWorld() @@ -113,6 +125,8 @@ void GameObject::RemoveFromWorld() if (m_zoneScript) m_zoneScript->OnGameObjectRemove(this); + RemoveAllDynObjects(); + // Remove GO from owner if (ObjectGuid owner_guid = GetOwnerGuid()) { @@ -245,6 +259,18 @@ void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) return; } + m_Events.Update(update_diff); + + // remove finished spells from current pointers + for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) + { + if (m_currentSpells[i] && m_currentSpells[i]->getState() == SPELL_STATE_FINISHED) + { + m_currentSpells[i]->SetReferencedFromCurrent(false); + m_currentSpells[i] = nullptr; // remove pointer + } + } + ///- UpdateAI if (i_AI) i_AI->UpdateAI(update_diff); @@ -278,12 +304,10 @@ void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) SetGoState(GO_STATE_ACTIVE); // SetUInt32Value(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN); - - SendForcedObjectUpdate(); - // Play splash sound - PlayDistanceSound(3355); + // Play splash sound + PlayDistanceSound(3355); SendGameObjectCustomAnim(); } @@ -429,9 +453,11 @@ void GameObject::Update(uint32 update_diff, uint32 /*p_time*/) if (ok && (!AI() || !AI()->OnUse(ok))) { - Unit *caster = owner ? owner : ok; + if (owner) + owner->CastSpell(ok, goInfo->trap.spellId, true, nullptr, nullptr, GetObjectGuid()); + else + CastSpell(ok, goInfo->trap.spellId, true, nullptr, nullptr, GetObjectGuid()); - caster->CastSpell(ok, goInfo->trap.spellId, true, NULL, NULL, GetObjectGuid()); // use template cooldown if provided m_cooldownTime = time(NULL) + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4)); @@ -726,6 +752,16 @@ uint32 GameObject::GetUniqueUseCount() return m_UniqueUsers.size(); } +void GameObject::CleanupsBeforeDelete() +{ + if (m_uint32Values) // only for fully created object + { + InterruptNonMeleeSpells(true); + m_Events.KillAllEvents(false); // non-delatable (currently casted spells) will not deleted now but it will deleted at call in Map::RemoveAllObjectsInRemoveList + } + WorldObject::CleanupsBeforeDelete(); +} + void GameObject::Delete() { if (!IsDeleted()) @@ -987,18 +1023,18 @@ void GameObject::SetVisible(bool b) UpdateObjectVisibility(); } -bool GameObject::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const +bool GameObject::isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const { // Not in world - if (!IsInWorld() || !u->IsInWorld()) + if (!IsInWorld() || !pDetector->IsInWorld()) return false; // Transport always visible at this step implementation - if (IsTransport() && IsInMap(u)) + if (IsTransport() && IsInMap(pDetector)) return true; // quick check visibility false cases for non-GM-mode - if (!u->IsGameMaster()) + if (!pDetector->IsPlayer() || !static_cast(pDetector)->IsGameMaster()) { if (!IsVisible()) return false; @@ -1014,14 +1050,18 @@ bool GameObject::isVisibleForInState(Player const* u, WorldObject const* viewPoi if(check stuff here) return false; }*/ - if (GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && IsHostileTo(u)) + if (Unit const* pDetectorUnit = pDetector->ToUnit()) { - if (!(u->m_detectInvisibilityMask & (1 << 3))) // Detection des pieges + if (GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && IsHostileTo(pDetectorUnit)) + { + if (!(pDetectorUnit->m_detectInvisibilityMask & (1 << 3))) // Detection des pieges + return false; + } + // Smuggled Mana Cell required 10 invisibility type detection/state + if (GetEntry() == 187039 && ((pDetectorUnit->m_detectInvisibilityMask | pDetectorUnit->m_invisibilityMask) & (1 << 10)) == 0) return false; } - // Smuggled Mana Cell required 10 invisibility type detection/state - if (GetEntry() == 187039 && ((u->m_detectInvisibilityMask | u->m_invisibilityMask) & (1 << 10)) == 0) - return false; + } // check distance @@ -1340,9 +1380,8 @@ void GameObject::Use(Unit* user) // Currently we do not expect trap code below to be Use() // directly (except from spell effect). Code here will be called by TriggerLinkedGameObject. - // FIXME: when GO casting will be implemented trap must cast spell to target if (uint32 spellId = GetGOInfo()->trap.spellId) - user->CastSpell(user, spellId, true, NULL, NULL, GetObjectGuid()); + CastSpell(user, spellId, true, nullptr, nullptr, GetObjectGuid()); if (uint32 max_charges = GetGOInfo()->GetCharges()) { @@ -1989,18 +2028,21 @@ void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 SetFloatValue(GAMEOBJECT_ROTATION + 3, rotation3); } -bool GameObject::IsHostileTo(Unit const* unit) const +bool GameObject::IsHostileTo(WorldObject const* target) const { // always non-hostile to GM in GM mode - if (unit->GetTypeId() == TYPEID_PLAYER && ((Player const*)unit)->IsGameMaster()) + if (target->GetTypeId() == TYPEID_PLAYER && ((Player const*)target)->IsGameMaster()) return false; // test owner instead if have if (Unit const* owner = GetOwner()) - return owner->IsHostileTo(unit); + return owner->IsHostileTo(target); - if (Unit const* targetOwner = unit->GetCharmerOrOwner()) - return IsHostileTo(targetOwner); + if (Unit const* pUnitTarget = target->ToUnit()) + { + if (Unit const* targetOwner = pUnitTarget->GetCharmerOrOwner()) + return IsHostileTo(targetOwner); + } // for not set faction case (wild object) use hostile case if (!GetGOInfo()->faction) @@ -2008,23 +2050,23 @@ bool GameObject::IsHostileTo(Unit const* unit) const // faction base cases FactionTemplateEntry const*tester_faction = sObjectMgr.GetFactionTemplateEntry(GetGOInfo()->faction); - FactionTemplateEntry const*target_faction = unit->getFactionTemplateEntry(); + FactionTemplateEntry const*target_faction = target->getFactionTemplateEntry(); if (!tester_faction || !target_faction) return false; // GvP forced reaction and reputation case - if (unit->GetTypeId() == TYPEID_PLAYER) + if (target->GetTypeId() == TYPEID_PLAYER) { // forced reaction if (tester_faction->faction) { - if (ReputationRank const* force = ((Player*)unit)->GetReputationMgr().GetForcedRankIfAny(tester_faction)) + if (ReputationRank const* force = ((Player*)target)->GetReputationMgr().GetForcedRankIfAny(tester_faction)) return *force <= REP_HOSTILE; // apply reputation state FactionEntry const* raw_tester_faction = sObjectMgr.GetFactionEntry(tester_faction->faction); if (raw_tester_faction && raw_tester_faction->reputationListID >= 0) - return ((Player const*)unit)->GetReputationMgr().GetRank(raw_tester_faction) <= REP_HOSTILE; + return ((Player const*)target)->GetReputationMgr().GetRank(raw_tester_faction) <= REP_HOSTILE; } } @@ -2032,18 +2074,21 @@ bool GameObject::IsHostileTo(Unit const* unit) const return tester_faction->IsHostileTo(*target_faction); } -bool GameObject::IsFriendlyTo(Unit const* unit) const +bool GameObject::IsFriendlyTo(WorldObject const* target) const { // always friendly to GM in GM mode - if (unit->GetTypeId() == TYPEID_PLAYER && ((Player const*)unit)->IsGameMaster()) + if (target->GetTypeId() == TYPEID_PLAYER && ((Player const*)target)->IsGameMaster()) return true; // test owner instead if have if (Unit const* owner = GetOwner()) - return owner->IsFriendlyTo(unit); + return owner->IsFriendlyTo(target); - if (Unit const* targetOwner = unit->GetCharmerOrOwner()) - return IsFriendlyTo(targetOwner); + if (Unit const* pUnitTarget = target->ToUnit()) + { + if (Unit const* targetOwner = pUnitTarget->GetCharmerOrOwner()) + return IsFriendlyTo(targetOwner); + } // for not set faction case (wild object) use hostile case if (!GetGOInfo()->faction) @@ -2051,23 +2096,23 @@ bool GameObject::IsFriendlyTo(Unit const* unit) const // faction base cases FactionTemplateEntry const*tester_faction = sObjectMgr.GetFactionTemplateEntry(GetGOInfo()->faction); - FactionTemplateEntry const*target_faction = unit->getFactionTemplateEntry(); + FactionTemplateEntry const*target_faction = target->getFactionTemplateEntry(); if (!tester_faction || !target_faction) return false; // GvP forced reaction and reputation case - if (unit->GetTypeId() == TYPEID_PLAYER) + if (target->GetTypeId() == TYPEID_PLAYER) { // forced reaction if (tester_faction->faction) { - if (ReputationRank const* force = ((Player*)unit)->GetReputationMgr().GetForcedRankIfAny(tester_faction)) + if (ReputationRank const* force = ((Player*)target)->GetReputationMgr().GetForcedRankIfAny(tester_faction)) return *force >= REP_FRIENDLY; // apply reputation state if (FactionEntry const* raw_tester_faction = sObjectMgr.GetFactionEntry(tester_faction->faction)) if (raw_tester_faction->reputationListID >= 0) - return ((Player const*)unit)->GetReputationMgr().GetRank(raw_tester_faction) >= REP_FRIENDLY; + return ((Player const*)target)->GetReputationMgr().GetRank(raw_tester_faction) >= REP_FRIENDLY; } } @@ -2322,3 +2367,75 @@ void GameObject::Despawn() else AddObjectToRemoveList(); } + +uint32 GameObject::getLevel() const +{ + uint32 level = 0; + + switch (GetGOInfo()->type) + { + case GAMEOBJECT_TYPE_CHEST: + level = GetGOInfo()->chest.level; + break; + case GAMEOBJECT_TYPE_TRAP: + level = GetGOInfo()->trap.level; + break; + } + + if (!level) + level = GetUInt32Value(GAMEOBJECT_LEVEL); + + return level > 0 ? level : DEFAULT_MAX_LEVEL; +} + +// function based on function Unit::CanAttack from 13850 client +bool GameObject::IsValidAttackTarget(Unit const* target) const +{ + ASSERT(target); + + if (FindMap() != target->FindMap()) + return false; + + // can't attack unattackable units or GMs + if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) + return false; + + // check flags + if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_NON_ATTACKABLE_2)) + return false; + + // CvC case - can attack each other only when one of them is hostile + if (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + return GetReactionTo(target) <= REP_HOSTILE || target->GetReactionTo(this) <= REP_HOSTILE; + + // PvP, PvC, CvP case + // can't attack friendly targets + if (GetReactionTo(target) > REP_NEUTRAL + || target->GetReactionTo(this) > REP_NEUTRAL) + return false; + + Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? target->GetAffectingPlayer() : nullptr; + + // Not all neutral creatures can be attacked + if (GetReactionTo(target) == REP_NEUTRAL && + target->GetReactionTo(this) == REP_NEUTRAL) + { + if (playerAffectingTarget) + { + Player const* player = playerAffectingTarget; + WorldObject const* non_player = this; + + if (FactionTemplateEntry const* factionTemplate = non_player->getFactionTemplateEntry()) + { + if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate))) + if (FactionEntry const* factionEntry = sObjectMgr.GetFactionEntry(factionTemplate->faction)) + if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry)) + if (!(repState->Flags & FACTION_FLAG_AT_WAR)) + return false; + + } + } + } + + return true; +} \ No newline at end of file diff --git a/src/game/Objects/GameObject.h b/src/game/Objects/GameObject.h index fd8378614..81328ecbe 100644 --- a/src/game/Objects/GameObject.h +++ b/src/game/Objects/GameObject.h @@ -624,6 +624,7 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject uint32 ComputeRespawnDelay() const; // Applies dynamic / random respawn timers if needed. void Refresh(); void Delete(); + void CleanupsBeforeDelete() override; // Functions spawn/remove gameobject with DB guid in all loaded map copies (if point grid loaded in map) static void AddToRemoveListInMaps(uint32 db_guid, GameObjectData const* data); @@ -694,15 +695,13 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject // 0 = use `gameobject`.`spawntimesecs` void ResetDoorOrButton(); - bool IsHostileTo(Unit const* unit) const; - bool IsFriendlyTo(Unit const* unit) const; + bool IsHostileTo(WorldObject const* target) const override; + bool IsFriendlyTo(WorldObject const* target) const override; void SummonLinkedTrapIfAny(); void TriggerLinkedGameObject(Unit* target); void RespawnLinkedGameObject(); - - bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const; - + GameObject* LookupFishingHoleAround(float range); GridReference &GetGridRef() { return m_gridRef; } @@ -730,7 +729,11 @@ class MANGOS_DLL_SPEC GameObject : public WorldObject bool IsVisible() const { return m_visible; } void SetVisible(bool b); + bool isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const override; + uint32 getFaction() const final override { return GetGOInfo()->faction; } + uint32 getLevel() const final override; + bool IsValidAttackTarget(Unit const* target) const final override; protected: bool m_visible; uint32 m_spellId; diff --git a/src/game/Objects/Object.cpp b/src/game/Objects/Object.cpp index b5b4c78bc..86fdebd30 100644 --- a/src/game/Objects/Object.cpp +++ b/src/game/Objects/Object.cpp @@ -27,6 +27,7 @@ #include "World.h" #include "Creature.h" #include "Player.h" +#include "Totem.h" #include "ObjectMgr.h" #include "ObjectGuid.h" #include "UpdateData.h" @@ -1632,6 +1633,48 @@ bool WorldObject::IsInRange3d(float x, float y, float z, float minRange, float m return distsq < maxdist * maxdist; } +bool WorldObject::CanReachWithMeleeSpellAttack(Unit const* pVictim, float flat_mod /*= 0.0f*/) const +{ + if (!pVictim || !pVictim->IsInWorld()) + return false; + + float reach = IsUnit() ? static_cast(this)->GetCombatReach(pVictim, true, flat_mod) : ATTACK_DISTANCE; + + // This check is not related to bounding radius + float dx = GetPositionX() - pVictim->GetPositionX(); + float dy = GetPositionY() - pVictim->GetPositionY(); + + // melee spells ignore Z-axis checks + return dx * dx + dy * dy < reach * reach; +} + +float WorldObject::GetLeewayBonusRange(const Unit* target, bool ability) const +{ + if (Player const* pPlayer = ToPlayer()) + { + if (target) + { + if (ability) + return (pPlayer->GetXZFlagBasedSpeed() > LEEWAY_MIN_MOVE_SPEED && target->GetXZFlagBasedSpeed() > LEEWAY_MIN_MOVE_SPEED) ? LEEWAY_BONUS_RANGE : 0.0f; + else // auto attacks do not check speed, only flags + return (IsMovingButNotWalking() && target->IsMovingButNotWalking()) ? LEEWAY_BONUS_RANGE : 0.0f; + } + } + + return 0.0f; +} + +float WorldObject::GetLeewayBonusRadius() const +{ + if (Player const* pPlayer = ToPlayer()) + { + if ((pPlayer->GetXZFlagBasedSpeed() > LEEWAY_MIN_MOVE_SPEED) || pPlayer->m_movementInfo.HasMovementFlag(MOVEFLAG_JUMPING)) + return LEEWAY_BONUS_RANGE; + } + + return 0.0f; +} + float WorldObject::GetAngle(const WorldObject* obj) const { if (!obj) return 0; @@ -2737,6 +2780,17 @@ Pet* Object::ToPet() return IsPet() ? static_cast(this) : nullptr; } +bool WorldObject::IsLikePlayer() const +{ + if (IsPlayer()) + return true; + + if (Pet const* pPet = ToPet()) + return pPet->isControlled() && pPet->GetOwnerGuid().IsPlayer(); + + return false; +} + bool WorldObject::PrintCoordinatesError(float x, float y, float z, char const* descr) const { sLog.outError("%s with invalid %s coordinates: mapid = %uu, x = %f, y = %f, z = %f", GetGuidStr().c_str(), descr, GetMapId(), x, y, z); @@ -3025,3 +3079,2124 @@ void WorldObject::LoadMapCellsAround(float dist) const NULLNotifier notifier = NULLNotifier(); Cell::VisitAllObjects(this, notifier, dist, false); } + +Unit* WorldObject::SelectMagnetTarget(Unit *victim, Spell* spell, SpellEffectIndex eff) +{ + if (!victim) + return nullptr; + + SpellEntry const* pProto = spell->m_spellInfo; + if (!pProto) return nullptr; + // Example spell: Cause Insanity (Hakkar) + if (pProto->AttributesEx & SPELL_ATTR_EX_CANT_BE_REDIRECTED) + return victim; + // Magic case + + if (pProto->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) + return victim; + + if ((pProto->DmgClass == SPELL_DAMAGE_CLASS_MAGIC || pProto->SpellVisual == 7250) && pProto->Dispel != DISPEL_POISON && !(pProto->Attributes & 0x10)) + { + Unit::AuraList const& magnetAuras = victim->GetAurasByType(SPELL_AURA_SPELL_MAGNET); + for (Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr) + { + if (Unit* magnet = (*itr)->GetCaster()) + { + if (magnet->isAlive() && magnet->IsWithinLOSInMap(this) && spell->CheckTarget(magnet, eff)) + { + if (SpellAuraHolder *holder = (*itr)->GetHolder()) + if (holder->DropAuraCharge()) + { + magnet->RemoveAurasDueToSpell(holder->GetId()); // Remove from grounding totem also + victim->RemoveSpellAuraHolder(holder); + } + return magnet; + } + } + } + } + + return victim; +} + +FactionTemplateEntry const* WorldObject::getFactionTemplateEntry() const +{ + FactionTemplateEntry const* entry = sObjectMgr.GetFactionTemplateEntry(getFaction()); + if (!entry) + { + static ObjectGuid guid; // prevent repeating spam same faction problem + + if (GetObjectGuid() != guid) + { + sLog.outError("%s have invalid faction (faction template id) #%u", GetGuidStr().c_str(), getFaction()); + guid = GetObjectGuid(); + } + } + 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 (IsUnit() && target->IsUnit() && + ToUnit()->GetCharmerOrOwnerOrSelf() == target->ToUnit()->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 (selfPlayerOwner->IsGameMaster()) + return REP_NEUTRAL; + if (FactionTemplateEntry const* targetFactionTemplateEntry = target->getFactionTemplateEntry()) + if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) + return *repRank; + } + else if (targetPlayerOwner) + { + if (targetPlayerOwner->IsGameMaster()) + return REP_NEUTRAL; + if (FactionTemplateEntry const* selfFactionTemplateEntry = getFactionTemplateEntry()) + if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(selfFactionTemplateEntry)) + return *repRank; + } + + if (IsUnit() && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) + { + if (target->IsUnit() && 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 && !selfPlayerOwner->duel->finished) + 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); + + // Sanctuary + if (selfPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && targetPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY)) + return REP_FRIENDLY; + + // Nostalrius: Hackfix because UNIT_BYTE2_FLAG_FFA_PVP is not implemented yet. + if (selfPlayerOwner->IsFFAPvP() && targetPlayerOwner->IsFFAPvP()) + return REP_HOSTILE; + } + + // check FFA_PVP - not implemented that way on MaNGOS :/ + /* + if (GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_FFA_PVP + && target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_FFA_PVP) + return REP_HOSTILE; + */ + } + if (selfPlayerOwner) + { + if (FactionTemplateEntry const* targetFactionTemplateEntry = target->getFactionTemplateEntry()) + { + if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) + return *repRank; + if (FactionEntry const* targetFactionEntry = sObjectMgr.GetFactionEntry(targetFactionTemplateEntry->faction)) + { + if (targetFactionEntry->CanHaveReputation()) + { + // check contested flags + if (targetFactionTemplateEntry->factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD + && selfPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) + return REP_HOSTILE; + + // if faction has reputation, hostile state depends only from AtWar state + if (FactionState const* factionState = selfPlayerOwner->GetReputationMgr().GetState(targetFactionEntry)) + if (factionState->Flags & FACTION_FLAG_AT_WAR) + return REP_HOSTILE; + return REP_FRIENDLY; + } + } + } + } + } + // do checks dependant only on our faction + return GetFactionReactionTo(getFactionTemplateEntry(), target); +} + +ReputationRank WorldObject::GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, WorldObject const* target) +{ + // always neutral when no template entry found + if (!factionTemplateEntry) + return REP_NEUTRAL; + + FactionTemplateEntry const* targetFactionTemplateEntry = target->getFactionTemplateEntry(); + if (!targetFactionTemplateEntry) + return REP_NEUTRAL; + + if (Player const* targetPlayerOwner = target->GetAffectingPlayer()) + { + // check contested flags + if (factionTemplateEntry->IsContestedGuardFaction() + && targetPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) + return REP_HOSTILE; + if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry)) + return *repRank; + if (FactionEntry const* factionEntry = sObjectMgr.GetFactionEntry(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 (FactionState const* factionState = targetPlayerOwner->GetReputationMgr().GetState(factionEntry)) + if (factionState->Flags & FACTION_FLAG_AT_WAR) + repRank = std::min(REP_NEUTRAL, repRank); + return repRank; + } + } + } + + // common faction based check + if (factionTemplateEntry->IsHostileTo(*targetFactionTemplateEntry)) + return REP_HOSTILE; + if (factionTemplateEntry->IsFriendlyTo(*targetFactionTemplateEntry)) + return REP_FRIENDLY; + if (targetFactionTemplateEntry->IsFriendlyTo(*factionTemplateEntry)) + return REP_FRIENDLY; + if (factionTemplateEntry->factionFlags & FACTION_TEMPLATE_HOSTILE_BY_DEFAULT) + return REP_HOSTILE; + // neutral by default + return REP_NEUTRAL; +} + +uint32 WorldObject::GetLevelForTarget(WorldObject const* target) const +{ + // Bosses are 3 levels higher than players. + if (Creature const* pCreature = ToCreature()) + { + if (!pCreature->IsWorldBoss() || !target || !target->IsUnit()) + return pCreature->getLevel(); + + uint32 level = static_cast(target)->getLevel() + sWorld.getConfig(CONFIG_UINT32_WORLD_BOSS_LEVEL_DIFF); + if (level < 1) + return 1; + if (level > 255) + return 255; + return level; + } + + if (Unit const* pUnit = ToUnit()) + return pUnit->getLevel(); + + if (GameObject const* pGo = ToGameObject()) + { + uint32 level = 0; + + switch (pGo->GetGOInfo()->type) + { + case GAMEOBJECT_TYPE_CHEST: + level = pGo->GetGOInfo()->chest.level; + break; + case GAMEOBJECT_TYPE_TRAP: + level = pGo->GetGOInfo()->trap.level; + break; + } + + if (!level) + level = pGo->GetUInt32Value(GAMEOBJECT_LEVEL); + + if (level) + return level; + } + + if (Unit const* pUnit = ::ToUnit(target)) + return pUnit->getLevel(); + + return DEFAULT_MAX_LEVEL; +} + +uint32 WorldObject::GetWeaponSkillValue(WeaponAttackType attType, WorldObject const* target) const +{ + if (Player const* pPlayer = ToPlayer()) + { + Item* item = pPlayer->GetWeaponForAttack(attType, true, true); + + // feral or unarmed skill only for base attack + if (attType != BASE_ATTACK && !item) + return 0; + + if (pPlayer->IsInFeralForm()) + return GetSkillMaxForLevel(); // always maximized SKILL_FERAL_COMBAT in fact + + // weapon skill or (unarmed for base attack) + uint32 skill = item ? item->GetSkill() : SKILL_UNARMED; + +// Daemon: pas en preBC ! +// // in PvP use full skill instead current skill value +// value = (target && target->GetTypeId() == TYPEID_PLAYER) +// ? ((Player*)this)->GetSkillMax(skill) +// : ((Player*)this)->GetSkillValue(skill); + return pPlayer->GetSkillValue(skill); + } + + return GetUnitMeleeSkill(target); +} + +uint32 WorldObject::GetDefenseSkillValue(WorldObject const* target) const +{ + if (Player const* pPlayer = ToPlayer()) + { + // in PvP use full skill instead current skill value + uint32 value = (target && target->IsPlayer()) + ? pPlayer->GetSkillMax(SKILL_DEFENSE) + : pPlayer->GetSkillValue(SKILL_DEFENSE); + return value; + } + + return GetUnitMeleeSkill(target); +} + +// 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* pVictim, SpellEntry const* spell, SpellEffectIndex effIndex, bool CanReflect, Spell* spellPtr) +{ + // Return evade for units in evade mode + if (pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) + return SPELL_MISS_EVADE; + + // Check for immune (use charges) + if (pVictim != this && pVictim->IsImmuneToSpell(spell, pVictim == this)) + 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 (spell->IsPositiveSpell(this, pVictim) || spell->IsPositiveEffect(effIndex, this, pVictim)) + return SPELL_MISS_NONE; + + // Check for immune (use charges) + SpellSchoolMask schoolMask; + if (spellPtr) + schoolMask = spellPtr->m_spellSchoolMask; + else + schoolMask = spell->GetSpellSchoolMask(); + + if (pVictim != this && pVictim->IsImmuneToDamage(schoolMask, spell)) + return SPELL_MISS_IMMUNE; + + // Try victim reflect spell + if (CanReflect) + { + int32 reflectchance = pVictim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS); + Unit::AuraList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL); + for (Unit::AuraList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) + if ((*i)->GetModifier()->m_miscvalue & spell->GetSpellSchoolMask()) + reflectchance += (*i)->GetModifier()->m_amount; + if (reflectchance > 0 && roll_chance_i(reflectchance)) + { + // Start triggers for remove charges if need (trigger only for victim, and mark as active spell) + ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT, PROC_EX_REFLECT, 1, BASE_ATTACK, spell); + return SPELL_MISS_REFLECT; + } + } + + switch (spell->DmgClass) + { + case SPELL_DAMAGE_CLASS_NONE: + return SPELL_MISS_NONE; + case SPELL_DAMAGE_CLASS_MAGIC: + return MagicSpellHitResult(pVictim, spell, spellPtr); + case SPELL_DAMAGE_CLASS_MELEE: + case SPELL_DAMAGE_CLASS_RANGED: + return MeleeSpellHitResult(pVictim, spell, spellPtr); + } + return SPELL_MISS_NONE; +} + +void WorldObject::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellEntry const *procSpell, Spell* spell) +{ + if ((pVictim && !IsInMap(pVictim)) || !IsInWorld()) + return; + + ProcTriggeredList procTriggered; + + // Not much to do if no flags are set. + if (procAttacker) + if (Unit* pUnit = ToUnit()) + pUnit->ProcDamageAndSpellFor(false, pVictim, procAttacker, procExtra, attType, procSpell, amount, procTriggered, spell); + + // Now go on with a victim's events'n'auras + // Not much to do if no flags are set or there is no victim + if (pVictim && pVictim->isAlive() && procVictim) + + // http://blue.cardplace.com/cache/wow-paladin/1069149.htm + // "Charges will not generate off auto attacks or npc attacks by trying" + // "to sit down and force a crit. However, ability crits from physical" + // "abilities such as Sinister Strike, Hamstring, Auto-shot, Aimed shot," + // "etc will generate a charge if you're sitting." +#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_7_1 + pVictim->ProcDamageAndSpellFor(true, IsUnit() ? static_cast(this) : pVictim, procVictim, !procSpell && !pVictim->IsStandingUpForProc() ? procExtra & ~PROC_EX_CRITICAL_HIT : procExtra, attType, procSpell, amount, procTriggered, spell); +#else + pVictim->ProcDamageAndSpellFor(true, IsUnit() ? static_cast(this) : pVictim, procVictim, procExtra, attType, procSpell, amount, procTriggered, spell); +#endif + + if (Unit* pUnit = ToUnit()) + pUnit->HandleTriggers(pVictim, procExtra, amount, procSpell, procTriggered); +} + +// Melee based spells can be miss, parry or dodge on this step +// Crit or block - determined on damage calculation phase! (and can be both in some time) +float WorldObject::MeleeSpellMissChance(Unit* pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const* spell, Spell* spellPtr) +{ + if (!pVictim || !pVictim->IsStandingUp()) + return 0.0f; + + // Calculate hit chance (more correct for chance mod) + float hitChance = 0.0f; + float missChance = 0.0f; + + // PvP - PvE melee chances + if (pVictim->GetTypeId() == TYPEID_PLAYER) + missChance = 5.0f - skillDiff * 0.04f; + else if (skillDiff < -10) + missChance = 5.0f - skillDiff * 0.2f; + else + missChance = 5.0f - skillDiff * 0.1f; + + // Low level reduction + if (!pVictim->IsPlayer() && pVictim->getLevel() < 10) + missChance *= pVictim->getLevel() / 10.0f; + + if (Unit* pUnit = ToUnit()) + { + // Spellmod from SPELLMOD_RESIST_MISS_CHANCE + if (Player * modOwner = pUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, hitChance, spellPtr); + + // Bonuses from attacker aura and ratings + if (attType == RANGED_ATTACK) + hitChance += pUnit->m_modRangedHitChance; + else + hitChance += pUnit->m_modMeleeHitChance; + } + + // There is some code in 1.12 that explicitly adds a modifier that causes the first 1% of +hit gained from + // talents or gear to be ignored against monsters with more than 10 Defense Skill above the attacking player’s Weapon Skill. + // https://us.forums.blizzard.com/en/wow/t/bug-hit-tables/185675/33 + if (skillDiff < -10 && hitChance > 0) + hitChance -= 1.0f; + + // Hit chance depends from victim auras + if (attType == RANGED_ATTACK) + hitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE); + else + hitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE); + + missChance -= hitChance; + + // Limit miss chance from 0 to 60% + if (missChance < 0.0f) + return 0.0f; + if (missChance > 60.0f) + return 60.0f; + return missChance; +} + +// Melee based spells hit result calculations +SpellMissInfo WorldObject::MeleeSpellHitResult(Unit* pVictim, SpellEntry const* spell, Spell* spellPtr) +{ + WeaponAttackType attType = BASE_ATTACK; + + if (spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED) + attType = RANGED_ATTACK; + + // Warrior spell Execute (5308) should never dodge, miss, resist ... Only the trigger can (20647) + if (spell->IsFitToFamily() && spell->Id != 20647) + return SPELL_MISS_NONE; + + // bonus from skills is 0.04% per skill Diff + int32 attackerWeaponSkill = (spell->EquippedItemClass == ITEM_CLASS_WEAPON) ? int32(GetWeaponSkillValue(attType, pVictim)) : GetSkillMaxForLevel(); + int32 skillDiff = attackerWeaponSkill - int32(pVictim->GetSkillMaxForLevel(this)); + int32 fullSkillDiff = attackerWeaponSkill - int32(pVictim->GetDefenseSkillValue(this)); + int32 minWeaponSkill = GetSkillMaxForLevel(pVictim) < attackerWeaponSkill ? GetSkillMaxForLevel(pVictim) : attackerWeaponSkill; + int32 cappedSkillDiff = minWeaponSkill - pVictim->GetSkillMaxForLevel(this); + + uint32 roll = urand(0, 9999); + + uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell, spellPtr) * 100.0f); + // Roll miss + uint32 tmp = spell->AttributesEx3 & SPELL_ATTR_EX3_CANT_MISS ? 0 : missChance; + if (roll < tmp) + return SPELL_MISS_MISS; + + // Chance resist mechanic for spell (effect resistance handled later) + int32 resist_mech = 0; + if (spell->Mechanic) + resist_mech = pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, spell->Mechanic) * 100; + // Roll chance + tmp += resist_mech; + if (roll < tmp) + return SPELL_MISS_RESIST; + + bool canDodge = true; + bool canParry = true; + + // Same spells cannot be parry/dodge + if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK) + return SPELL_MISS_NONE; + + // Ranged attack cannot be parry/dodge + if (attType == RANGED_ATTACK) + return SPELL_MISS_NONE; + + bool from_behind = !pVictim->HasInArc(M_PI_F, this); + + // Check for attack from behind + if (from_behind) + { + // Can`t dodge from behind in PvP (but its possible in PvE) + if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) + canDodge = false; + // Can`t parry + canParry = false; + } + // Check creatures flags_extra for disable parry + if (pVictim->GetTypeId() == TYPEID_UNIT) + { + uint32 flagEx = ((Creature*)pVictim)->GetCreatureInfo()->flags_extra; + if (flagEx & CREATURE_FLAG_EXTRA_NO_PARRY) + canParry = false; + } + + // Check if the player can parry + if (pVictim->GetTypeId() == TYPEID_PLAYER) + { + if (!((Player*)pVictim)->CanParry()) + canParry = false; + } + + if (canDodge) + { + // Roll dodge + int32 dodgeModifier = pVictim->IsPlayer() ? skillDiff * 4 : skillDiff * 10; + int32 dodgeChance = int32(pVictim->GetUnitDodgeChance() * 100.0f) - dodgeModifier; + + if (dodgeChance < 0) + dodgeChance = 0; + + // Low level reduction + if (!pVictim->IsPlayer() && pVictim->getLevel() < 10) + dodgeChance *= pVictim->getLevel() / 10.0f; + + tmp += dodgeChance; + if (roll < tmp) + return SPELL_MISS_DODGE; + } + + if (canParry) + { + // Roll parry + int32 parryModifier = pVictim->IsPlayer() ? skillDiff * 4 : cappedSkillDiff < -10 ? 60 * cappedSkillDiff : 20 * cappedSkillDiff; + int32 parryChance = int32(pVictim->GetUnitParryChance() * 100.0f) - parryModifier; + + // Can`t parry from behind + if (parryChance < 0) + parryChance = 0; + + // Low level reduction + if (!pVictim->IsPlayer() && pVictim->getLevel() < 10) + parryChance *= pVictim->getLevel() / 10.0f; + + tmp += parryChance; + if (roll < tmp) + return SPELL_MISS_PARRY; + } + + return SPELL_MISS_NONE; +} + +SpellMissInfo WorldObject::MagicSpellHitResult(Unit* pVictim, SpellEntry const* spell, Spell* spellPtr) +{ + // Can`t miss on dead target (on skinning for example) + if (!pVictim->isAlive()) + return SPELL_MISS_NONE; + + // Spell cannot be resisted (not exist on dbc, custom flag) + if (spell->AttributesEx4 & SPELL_ATTR_EX4_IGNORE_RESISTANCES) + return SPELL_MISS_NONE; + + int32 hitChance = MagicSpellHitChance(pVictim, spell, spellPtr); + int32 missChance = 10000 - hitChance; + int32 rand = irand(0, 10000); + + if ((IsPlayer() && ToPlayer()->HasOption(PLAYER_CHEAT_UNRANDOMIZE)) || + (pVictim->IsPlayer() && pVictim->ToPlayer()->HasOption(PLAYER_CHEAT_UNRANDOMIZE))) + missChance = 0; + + if (rand < missChance) + return SPELL_MISS_RESIST; + + return SPELL_MISS_NONE; +} + +int32 WorldObject::MagicSpellHitChance(Unit* pVictim, SpellEntry const* spell, Spell* spellPtr) +{ + if (spell->AttributesEx3 & SPELL_ATTR_EX3_CANT_MISS) + return 10000; + + SpellSchoolMask schoolMask = spell->GetSpellSchoolMask(); + // PvP - PvE spell misschances per leveldif > 2 + int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 7 : 11; + int32 leveldif = int32(pVictim->GetLevelForTarget(this)) - int32(GetLevelForTarget(pVictim)); + + // Base hit chance from attacker and victim levels + float modHitChance; + if (leveldif < 3) + modHitChance = 96 - leveldif; + else + modHitChance = 94 - (leveldif - 2) * lchance; + + //DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "%s [%u] : Binary [%s]. Base hit chance %f, level diff: %d", spell->SpellName[2].c_str(), spell->Id, spell->IsBinary() ? "YES" : "NO", modHitChance, leveldif); + + // Spellmod from SPELLMOD_RESIST_MISS_CHANCE + if (Unit* pUnit = ToUnit()) + { + if (Player * modOwner = pUnit->GetSpellModOwner()) + { + modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance, spellPtr); + //DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELLMOD_RESIST_MISS_CHANCE : %f", modHitChance); + } + } + + // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras + modHitChance += pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); + //DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE (+ %i) : %f", pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask), modHitChance); + + // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura + if (spell->IsAreaOfEffectSpell()) + { + modHitChance -= pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE); + //DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_AOE_AVOIDANCE (- %i) : %f", pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE), modHitChance); + } + + // Chance resist mechanic for spell (effect resistance handled later) + int32 resist_mech = 0; + if (spell->Mechanic) + resist_mech = pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, spell->Mechanic); + // Apply mod + modHitChance -= resist_mech; + //DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_MECHANIC_RESISTANCE (- %i) : %f", resist_mech, modHitChance); + + // Chance resist debuff + modHitChance -= pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spell->Dispel)); + //DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_DEBUFF_RESISTANCE (- %i) : %f", pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spell->Dispel)), modHitChance); + + // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings + if (Unit* pUnit = ToUnit()) + modHitChance += int32(pUnit->m_modSpellHitChance); + //DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_SPELL_HIT_CHANCE (+ %i) : %f", int32(m_modSpellHitChance), modHitChance); + + // Nostalrius: sorts binaires. + if (spell->IsBinary()) + { + // Get base victim resistance for school + float resistModHitChance = GetSpellResistChance(pVictim, schoolMask, false); + modHitChance *= (1 - resistModHitChance); + //DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "x %f : HitChance = %f", (1 - resistModHitChance), modHitChance); + } + + int32 HitChance = modHitChance * 100; + if (HitChance < 100) HitChance = 100; + if (HitChance > 9900) HitChance = 9900; + return HitChance; +} + +float WorldObject::GetSpellResistChance(Unit const* victim, uint32 schoolMask, bool innateResists) const +{ + Unit const* pUnit = ToUnit(); + // Get base victim resistance for school + float const baseResistance = victim->GetResistance(GetFirstSchoolInMask(SpellSchoolMask(schoolMask))); + // Get attacker spell penetration from SPELL_AURA_MOD_TARGET_RESISTANCE aura + float const selfResistance = pUnit ? pUnit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask) : 0; + + float resistModHitChance = baseResistance + selfResistance; + +#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_8_4 + if ((resistModHitChance < 0.0f) && (baseResistance >= 0.0f)) + resistModHitChance = 0.0f; +#endif + + // Magic vulnerability calculation + if (resistModHitChance < 0.0f) + { + // Victim's level based skill, penalize when calculating for low levels (< 20): + const float skill = std::max(GetSkillMaxForLevel(victim), uint16(100)); + // Convert resistance value to vulnerability percentage through comparision with skill + resistModHitChance = (float(resistModHitChance) / skill) * 100; + return (resistModHitChance * 0.01f); + } + + uint32 const uiLevel = getLevel(); + + // Computing innate resists, resistance bonus when attacking a creature higher level. Not affected by modifiers. + if (innateResists && victim->GetTypeId() == TYPEID_UNIT) + { + int32 leveldiff = int32(victim->GetLevelForTarget(this)) - int32(GetLevelForTarget(victim)); + resistModHitChance += int32((8.0f * leveldiff) * uiLevel / 63.0f); + } + resistModHitChance *= (float)(0.15f / uiLevel); + + if (resistModHitChance < 0.0f) + resistModHitChance = 0.0f; + if (resistModHitChance > 0.75f) + resistModHitChance = 0.75f; + + return resistModHitChance; +} + +void WorldObject::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo) +{ + WorldPacket data(SMSG_SPELLLOGMISS, (4 + 8 + 1 + 4 + 8 + 1)); + data << uint32(spellID); + data << GetObjectGuid(); + data << uint8(0); // unk8 + data << uint32(1); // target count + // for(i = 0; i < target count; ++i) + data << target->GetObjectGuid(); // target GUID + data << uint8(missInfo); + // Nostalrius: + 2 * float if unk8=1 + // end loop + SendObjectMessageToSet(&data, true); +} + +uint32 WorldObject::SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim, Spell* spell) +{ + // Calculate critical bonus + int32 crit_bonus; + switch (spellProto->DmgClass) + { + case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100% + case SPELL_DAMAGE_CLASS_RANGED: + crit_bonus = damage; + break; + default: + crit_bonus = damage / 2; // for spells is 50% + break; + } + + Unit const* pUnit = ToUnit(); + + // adds additional damage to crit_bonus (from talents) + if (pUnit) + { + if (Player* modOwner = pUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus, spell); + } + + + if (!pVictim) + return damage += crit_bonus; + + uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); + + float critPctDamageMod = pUnit ? pUnit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask) : 1; + + damage = (damage + crit_bonus) * critPctDamageMod; + + return damage; +} + +uint32 WorldObject::SpellCriticalHealingBonus(SpellEntry const *spellProto, uint32 damage, Unit const* pVictim) const +{ + // Calculate critical bonus + int32 crit_bonus; + switch (spellProto->DmgClass) + { + case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100% + case SPELL_DAMAGE_CLASS_RANGED: + // TODO: write here full calculation for melee/ranged spells + crit_bonus = damage; + break; + default: + crit_bonus = damage / 2; // for spells is 50% + break; + } + + if (pVictim) + { + if (Unit const* pUnit = ToUnit()) + { + uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); + crit_bonus = int32(crit_bonus * pUnit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask)); + } + } + + if (crit_bonus > 0) + damage += crit_bonus; + + return damage; +} + +int32 WorldObject::DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellProto, bool critical) +{ + Unit* pUnit = ToUnit(); + + // Script Event HealedBy + if (pVictim->AI() && pUnit) + pVictim->AI()->HealedBy(pUnit, addhealth); + + int32 gain = pVictim->ModifyHealth(int32(addhealth)); + + WorldObject* pHealer = this; + + if (IsCreature() && ((Creature*)this)->IsTotem() && ((Totem*)this)->GetTotemType() != TOTEM_STATUE) + pHealer = pUnit->GetOwner(); + + if (IsPlayer() || pVictim->IsPlayer()) + pHealer->SendHealSpellLog(pVictim, spellProto->Id, addhealth, critical); + + return gain; +} + +void WorldObject::SendHealSpellLog(Unit const* pVictim, uint32 SpellID, uint32 Damage, bool critical) const +{ +#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_9_4 + // we guess size + WorldPacket data(SMSG_SPELLHEALLOG, (8 + 8 + 4 + 4 + 1)); + data << pVictim->GetPackGUID(); + data << GetPackGUID(); + data << uint32(SpellID); + data << uint32(Damage); + data << uint8(critical ? 1 : 0); + // data << uint8(0); // [-ZERO] + SendMessageToSet(&data, true); +#endif +} + +void WorldObject::EnergizeBySpell(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype) +{ + SendEnergizeSpellLog(pVictim, SpellID, Damage, powertype); + // needs to be called after sending spell log + pVictim->ModifyPower(powertype, Damage); +} + +void WorldObject::SendEnergizeSpellLog(Unit const* pVictim, uint32 SpellID, uint32 Damage, Powers powertype) const +{ +#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_9_4 + WorldPacket data(SMSG_SPELLENERGIZELOG, (8 + 8 + 4 + 4 + 4 + 1)); + data << pVictim->GetPackGUID(); + data << GetPackGUID(); + data << uint32(SpellID); + data << uint32(powertype); + data << uint32(Damage); + SendMessageToSet(&data, true); +#endif +} + +void WorldObject::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log) +{ + WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG, (16 + 4 + 4 + 1 + 4 + 4 + 1 + 1 + 4 + 4 + 1)); // we guess size +#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_8_4 + data << log->target->GetPackGUID(); + data << log->attacker->GetPackGUID(); +#else + data << log->target->GetGUID(); + data << log->attacker->GetGUID(); +#endif + data << uint32(log->SpellID); + data << uint32(log->damage); // damage amount + data << uint8(log->school); // damage school + data << uint32(log->absorb); // AbsorbedDamage + data << int32(log->resist); // resist + data << uint8(log->periodicLog); // if 1, then client show spell name (example: %s's ranged shot hit %s for %u school or %s suffers %u school damage from %s's spell_name + data << uint8(log->unused); // unused + data << uint32(log->blocked); // blocked + data << uint32(log->HitInfo); + data << uint8(0); // flag to use extend data + SendMessageToSet(&data, true); +} + +void WorldObject::SendSpellNonMeleeDamageLog(Unit* target, uint32 spellID, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 absorbedDamage, int32 resist, bool isPeriodic, uint32 blocked, bool criticalHit, bool split) +{ + SpellNonMeleeDamage log(this, target, spellID, GetFirstSchoolInMask(damageSchoolMask)); + log.damage = damage; + log.damage += (resist < 0 ? uint32(std::abs(resist)) : 0); + log.damage -= (absorbedDamage + (resist > 0 ? uint32(resist) : 0) + blocked); + log.absorb = absorbedDamage; + log.resist = resist; + log.periodicLog = isPeriodic; + log.blocked = blocked; + log.HitInfo = 0; + if (criticalHit) + log.HitInfo |= SPELL_HIT_TYPE_CRIT; + if (split) + log.HitInfo |= SPELL_HIT_TYPE_SPLIT; + SendSpellNonMeleeDamageLog(&log); +} + +SpellSchoolMask WorldObject::GetMeleeDamageSchoolMask() const +{ + return SPELL_SCHOOL_MASK_NORMAL; +} + +uint32 WorldObject::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage) const +{ + uint32 newdamage = 0; + float armor = (float)pVictim->GetArmor(); + Unit const* pUnit = ToUnit(); + + // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura + if (pUnit) + armor += pUnit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL); + + if (armor < 0.0f) + armor = 0.0f; + + float tmpvalue = 0.1f * armor / (8.5f * float(getLevel()) + 40.0f); + tmpvalue = tmpvalue / (1.0f + tmpvalue); + + if (tmpvalue < 0.0f) + tmpvalue = 0.0f; + if (tmpvalue > 0.75f) + tmpvalue = 0.75f; + + newdamage = uint32(roundf(damage - (damage * tmpvalue))); + + return (newdamage > 1) ? newdamage : 1; +} + +int32 WorldObject::CalculateSpellDamage(Unit const* target, SpellEntry const* spellProto, SpellEffectIndex effect_index, int32 const* effBasePoints, Spell* spell) +{ + Unit* pUnit = ToUnit(); + Player* pPlayer = ToPlayer(); + + uint8 comboPoints = pPlayer ? pPlayer->GetComboPoints() : 0; + + int32 level = getLevel(); + + if (level > (int32)spellProto->maxLevel && spellProto->maxLevel > 0) + level = (int32)spellProto->maxLevel; + else if (level < (int32)spellProto->baseLevel) + level = (int32)spellProto->baseLevel; + level -= (int32)spellProto->spellLevel; + + int32 baseDice = int32(spellProto->EffectBaseDice[effect_index]); + float basePointsPerLevel = spellProto->EffectRealPointsPerLevel[effect_index]; + float randomPointsPerLevel = spellProto->EffectDicePerLevel[effect_index]; + int32 basePoints = effBasePoints + ? *effBasePoints - baseDice + : spellProto->EffectBasePoints[effect_index]; + + basePoints += int32(level * basePointsPerLevel); + int32 randomPoints = int32(spellProto->EffectDieSides[effect_index] + level * randomPointsPerLevel); + float comboDamage = spellProto->EffectPointsPerComboPoint[effect_index]; + + switch (randomPoints) + { + case 0: + case 1: + basePoints += baseDice; + break; // range 1..1 + default: + { + // range can have positive (1..rand) and negative (rand..1) values, so order its for irand + int32 randvalue = baseDice >= randomPoints + ? irand(randomPoints, baseDice) + : irand(baseDice, randomPoints); + + basePoints += randvalue; + break; + } + } + + int32 value = basePoints; + + // random damage + if (comboDamage != 0 && pPlayer && target && (target->GetObjectGuid() == pPlayer->GetComboTargetGuid())) + value += (int32)(comboDamage * comboPoints); + + if (pUnit) + { + if (Player* modOwner = pUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_ALL_EFFECTS, value, spell); + } + + if (spellProto->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION && spellProto->spellLevel && + spellProto->Effect[effect_index] != SPELL_EFFECT_WEAPON_PERCENT_DAMAGE && + spellProto->Effect[effect_index] != SPELL_EFFECT_KNOCK_BACK && + (spellProto->Effect[effect_index] != SPELL_EFFECT_APPLY_AURA || spellProto->EffectApplyAuraName[effect_index] != SPELL_AURA_MOD_DECREASE_SPEED)) + value = int32(value * 0.25f * exp(getLevel() * (70 - spellProto->spellLevel) / 1000.0f)); + + return value; +} + +void WorldObject::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType, Spell* spell) +{ + SpellSchoolMask damageSchoolMask = GetSchoolMask(damageInfo->school); + Unit *pVictim = damageInfo->target; + if (!pVictim) + return; + + if (damage < 0) + return; + + if (!pVictim->isAlive()) + return; + + // Check spell crit chance + bool crit = IsSpellCrit(pVictim, spellInfo, damageSchoolMask, attackType, spell); + + // damage bonus (per damage class) + switch (spellInfo->DmgClass) + { + // Melee and Ranged Spells + case SPELL_DAMAGE_CLASS_RANGED: + case SPELL_DAMAGE_CLASS_MELEE: + { + //Calculate damage bonus + damage = MeleeDamageBonusDone(pVictim, damage, attackType, spellInfo, SPELL_DIRECT_DAMAGE, 1, spell); + damage = pVictim->MeleeDamageBonusTaken(this, damage, attackType, spellInfo, SPELL_DIRECT_DAMAGE, 1, spell); + + // if crit add critical bonus + if (crit) + { + damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; + damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim, spell); + } + break; + } + // Magical Attacks + case SPELL_DAMAGE_CLASS_NONE: + case SPELL_DAMAGE_CLASS_MAGIC: + { + // Calculate damage bonus + damage = SpellDamageBonusDone(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE, 1, spell); + damage = pVictim->SpellDamageBonusTaken(this, spellInfo, damage, SPELL_DIRECT_DAMAGE, 1, spell); + + // If crit add critical bonus + if (crit) + { + damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; + damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim, spell); + } + break; + } + } + + // damage mitigation + if (damage > 0) + { + // physical damage => armor + if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL && !(spellInfo->Custom & SPELL_CUSTOM_IGNORE_ARMOR)) + damage = CalcArmorReducedDamage(pVictim, damage); + } + else + damage = 0; + damageInfo->damage = damage; +} + +/** + * Calculates caster part of melee damage bonuses, + * also includes different bonuses dependent from target auras + */ +uint32 WorldObject::MeleeDamageBonusDone(Unit* pVictim, uint32 pdamage, WeaponAttackType attType, SpellEntry const* spellProto, DamageEffectType damagetype, uint32 stack, Spell* spell, bool flat) +{ + if (!pVictim) + return pdamage; + + if (pdamage == 0) + return pdamage; + + // differentiate for weapon damage based spells + bool isWeaponDamageBasedSpell = !(spellProto && (damagetype == DOT || spellProto->HasEffect(SPELL_EFFECT_SCHOOL_DAMAGE))); + Item* pWeapon = GetTypeId() == TYPEID_PLAYER ? ((Player*)this)->GetWeaponForAttack(attType, true, false) : NULL; + uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); + uint32 schoolMask = spellProto ? spellProto->GetSpellSchoolMask() : GetMeleeDamageSchoolMask(); + + // FLAT damage bonus auras + // ======================= + int32 DoneFlat = 0; + int32 APbonus = 0; + + Unit* pUnit = ToUnit(); + + // ..done flat, already included in weapon damage based spells + if (pUnit && !isWeaponDamageBasedSpell) + { + Unit::AuraList const& mModDamageDone = pUnit->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); + for (Unit::AuraList::const_iterator i = mModDamageDone.begin(); i != mModDamageDone.end(); ++i) + { + if ((*i)->GetModifier()->m_miscvalue & schoolMask && // schoolmask has to fit with the intrinsic spell school + ((*i)->GetModifier()->m_miscvalue & GetMeleeDamageSchoolMask() || // AND schoolmask has to fit with weapon damage school (essential for non-physical spells) + spellProto->DmgClass == SPELL_DAMAGE_CLASS_RANGED) && // Some ranged physical attacks use magic damage, ex. Arcane Shot + (((*i)->GetSpellProto()->EquippedItemClass == -1) || // general, weapon independent + (pWeapon && pWeapon->IsFitToSpellRequirements((*i)->GetSpellProto())))) // OR used weapon fits aura requirements + DoneFlat += (*i)->GetModifier()->m_amount; + } + + // Pets just add their bonus damage to their melee damage + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsPet()) + DoneFlat += ((Pet*)this)->GetBonusDamage(); + } + + // ..done flat (by creature type mask) + if (pUnit) + DoneFlat += pUnit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE, creatureTypeMask); + + // ..done flat (base at attack power for marked target and base at attack power for creature type) + if (attType == RANGED_ATTACK) + { + APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS); + if (pUnit) + APbonus += pUnit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS, creatureTypeMask); + } + else + { + APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS); + if (pUnit) + APbonus += pUnit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS, creatureTypeMask); + } + + // PERCENT damage auras + // ==================== + float DonePercent = 1.0f; + + // ..done pct, already included in weapon damage based spells + if (pUnit && !isWeaponDamageBasedSpell) + { + Unit::AuraList const& mModDamagePercentDone = pUnit->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); + for (Unit::AuraList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) + { + if ((*i)->GetModifier()->m_miscvalue & schoolMask && // schoolmask has to fit with the intrinsic spell school + (*i)->GetModifier()->m_miscvalue & GetMeleeDamageSchoolMask() && // AND schoolmask has to fit with weapon damage school (essential for non-physical spells) + (((*i)->GetSpellProto()->EquippedItemClass == -1) || // general, weapon independent + (pWeapon && pWeapon->IsFitToSpellRequirements((*i)->GetSpellProto())))) // OR used weapon fits aura requirements + DonePercent *= ((*i)->GetModifier()->m_amount + 100.0f) / 100.0f; + } + + if (pUnit && attType == OFF_ATTACK) + DonePercent *= pUnit->GetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT); // no school check required + } + + // Pet happiness increases damage of Hunter pet melee spells + if (IsPet() && ((Pet*)this)->getPetType() == HUNTER_PET) + { + if (Pet* pet = ((Pet*)this)) + { + switch (pet->GetHappinessState()) + { + case HAPPY: DonePercent *= 1.25; break; + case CONTENT: break; + case UNHAPPY: DonePercent *= 0.75; break; + } + } + } + + // ..done pct (by creature type mask) + if (pUnit) + DonePercent *= pUnit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS, creatureTypeMask); + + // final calculation + // ================= + + float DoneTotal = 0.0f; + + // scaling of non weapon based spells + if (!isWeaponDamageBasedSpell) + { + // apply ap bonus and benefit affected by spell power implicit coeffs and spell level penalties + DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneFlat, APbonus, damagetype, true, this, spell); + } + // weapon damage based spells + else if (APbonus || DoneFlat) + { + bool normalized = spellProto ? spellProto->HasEffect(SPELL_EFFECT_NORMALIZED_WEAPON_DMG) : false; + DoneTotal += int32(APbonus / 14.0f * GetAPMultiplier(attType, normalized)); + + // for weapon damage based spells we still have to apply damage done percent mods + // (that are already included into pdamage) to not-yet included DoneFlat + // e.g. from doneVersusCreature, apBonusVs... + UnitMods unitMod; + switch (attType) + { + default: + case BASE_ATTACK: + unitMod = UNIT_MOD_DAMAGE_MAINHAND; + break; + case OFF_ATTACK: + unitMod = UNIT_MOD_DAMAGE_OFFHAND; + break; + case RANGED_ATTACK: + unitMod = UNIT_MOD_DAMAGE_RANGED; + break; + } + + DoneTotal += DoneFlat; + + if (pUnit) + DoneTotal *= pUnit->GetModifierValue(unitMod, TOTAL_PCT); + } + + if (!flat) + DoneTotal = 0.0f; + + float tmpDamage = float(int32(pdamage) + DoneTotal * int32(stack)) * DonePercent; + + // apply spellmod to Done damage + if (spellProto && pUnit) + { + if (Player* modOwner = pUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage, spell); + } + + // bonus result can be negative + return tmpDamage > 0 ? uint32(tmpDamage) : 0; +} + +/** + * Calculates caster part of healing spell bonuses, + * also includes different bonuses dependent from target auras + */ +uint32 WorldObject::SpellHealingBonusDone(Unit *pVictim, SpellEntry const *spellProto, int32 healamount, DamageEffectType damagetype, uint32 stack, Spell* spell) +{ + Unit* pUnit = ToUnit(); + + // For totems get healing bonus from owner (statue isn't totem in fact) + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsTotem() && ((Totem*)this)->GetTotemType() != TOTEM_STATUE) + if (Unit* owner = pUnit->GetOwner()) + return owner->SpellHealingBonusDone(pVictim, spellProto, healamount, damagetype, stack, spell); + + // No heal amount for this class spells + if (spellProto->DmgClass == SPELL_DAMAGE_CLASS_NONE || spellProto->Custom & SPELL_CUSTOM_FIXED_DAMAGE) + { + //DEBUG_UNIT(this, DEBUG_SPELLS_DAMAGE, "SpellHealingBonusDone[spell=%u]: has fixed damage (SPELL_DAMAGE_CLASS_NONE)", spellProto->Id); + return healamount < 0 ? 0 : healamount; + } + // Healing Done + // Done total percent damage auras + float DoneTotalMod = 1.0f; + int32 DoneTotal = 0; + + if (pUnit) + { + // Healing done percent + Unit::AuraList const& mHealingDonePct = pUnit->GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT); + for (Unit::AuraList::const_iterator i = mHealingDonePct.begin(); i != mHealingDonePct.end(); ++i) + DoneTotalMod *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f; + + // done scripted mod (take it from owner) + Unit *owner = pUnit->GetOwner(); + if (!owner) + owner = pUnit; + + Unit::AuraList const& mOverrideClassScript = owner->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for (Unit::AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + { + if (!(*i)->isAffectedOnSpell(spellProto)) + continue; + switch ((*i)->GetModifier()->m_miscvalue) + { + case 4415: // Increased Rejuvenation Healing + DoneTotal += (*i)->GetModifier()->m_amount / 4; // 4 ticks + break; + case 3736: // Hateful Totem of the Third Wind / Increased Lesser Healing Wave / Savage Totem of the Third Wind + DoneTotal += (*i)->GetModifier()->m_amount; + break; + default: + break; + } + } + } + + // Done fixed damage bonus auras + int32 DoneAdvertisedBenefit = SpellBaseHealingBonusDone(spellProto->GetSpellSchoolMask()); + + // apply ap bonus and benefit affected by spell power implicit coeffs and spell level penalties + DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true, this, spell); + + // use float as more appropriate for negative values and percent applying + float heal = (healamount + DoneTotal * int32(stack)) * DoneTotalMod; + + if (pUnit) + { + // apply spellmod to Done amount + if (Player* modOwner = pUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal, spell); + } + + //DEBUG_UNIT(this, DEBUG_SPELLS_DAMAGE, "SpellHealingBonusDone[spell=%u]: (base=%u + %i) * %f. HealingPwr=%i", spellProto->Id, healamount, DoneTotal, DoneTotalMod, DoneAdvertisedBenefit); + return heal < 0 ? 0 : uint32(heal); +} + +int32 WorldObject::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask) +{ + int32 AdvertisedBenefit = 0; + + if (Unit* pUnit = ToUnit()) + { + Unit::AuraList const& mHealingDone = pUnit->GetAurasByType(SPELL_AURA_MOD_HEALING_DONE); + for (Unit::AuraList::const_iterator i = mHealingDone.begin(); i != mHealingDone.end(); ++i) + if (((*i)->GetModifier()->m_miscvalue & schoolMask) != 0) + AdvertisedBenefit += (*i)->GetModifier()->m_amount; + + // Healing bonus of spirit, intellect and strength + if (GetTypeId() == TYPEID_PLAYER) + { + // Healing bonus from stats + Unit::AuraList const& mHealingDoneOfStatPercent = pUnit->GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT); + for (Unit::AuraList::const_iterator i = mHealingDoneOfStatPercent.begin(); i != mHealingDoneOfStatPercent.end(); ++i) + { + // 1.12.* have only 1 stat type support + Stats usedStat = STAT_SPIRIT; + AdvertisedBenefit += int32(pUnit->GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f); + } + } + } + + return AdvertisedBenefit; +} + +/** + * Calculates caster part of spell damage bonuses, + * also includes different bonuses dependent from target auras + */ +uint32 WorldObject::SpellDamageBonusDone(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack, Spell* spell) +{ + if (!spellProto || !pVictim || damagetype == DIRECT_DAMAGE) + return pdamage; + + if (spellProto->Custom & SPELL_CUSTOM_FIXED_DAMAGE) + return pdamage; + + // Ignite damage already includes modifiers + if (spellProto->IsFitToFamily()) + return pdamage; + + Unit* pUnit = ToUnit(); + + // For totems get damage bonus from owner (statue isn't totem in fact) + if (pUnit && GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsTotem() && ((Totem*)this)->GetTotemType() != TOTEM_STATUE) + { + if (Unit* owner = pUnit->GetOwner()) + return owner->SpellDamageBonusDone(pVictim, spellProto, pdamage, damagetype, stack, spell); + } + + float DoneTotalMod = 1.0f; + int32 DoneTotal = 0; + Item* pWeapon = GetTypeId() == TYPEID_PLAYER ? ((Player*)this)->GetWeaponForAttack(BASE_ATTACK, true, false) : NULL; + + // Creature damage + if (GetTypeId() == TYPEID_UNIT && !((Creature*)this)->IsPet()) + DoneTotalMod *= ((Creature*)this)->GetSpellDamageMod(((Creature*)this)->GetCreatureInfo()->rank); + + if (pUnit) + { + Unit::AuraList const& mModDamagePercentDone = pUnit->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); + for (Unit::AuraList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) + { + if (((*i)->GetModifier()->m_miscvalue & spellProto->GetSpellSchoolMask()) && + (*i)->GetSpellProto()->EquippedItemClass == -1 && + spellProto->EquippedItemClass == -1 && + // -1 == any item class (not wand then) + (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0) + // 0 == any inventory type (not wand then) + { + DoneTotalMod *= ((*i)->GetModifier()->m_amount + 100.0f) / 100.0f; + } + // Paladin seals benefit from weapon modifiers + else if ((*i)->GetModifier()->m_miscvalue & GetMeleeDamageSchoolMask() && + spellProto->SpellFamilyName == SPELLFAMILY_PALADIN && spellProto->IsFitToFamilyMask() && + (((*i)->GetSpellProto()->EquippedItemClass == -1) || + (pWeapon && pWeapon->IsFitToSpellRequirements((*i)->GetSpellProto())))) + { + DoneTotalMod *= ((*i)->GetModifier()->m_amount + 100.0f) / 100.0f; + } + } + } + + uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); + + // Add pct bonus from spell damage versus + if (pUnit) + DoneTotalMod *= pUnit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS, creatureTypeMask); + + // Add flat bonus from spell damage creature + if (pUnit) + DoneTotal += pUnit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE, creatureTypeMask); + + // done scripted mod (take it from owner) + Unit *owner = pUnit ? pUnit->GetOwner() : nullptr; + if (!owner) owner = pUnit; + if (owner) + { + Unit::AuraList const& mOverrideClassScript = owner->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for (Unit::AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + { + if (!(*i)->isAffectedOnSpell(spellProto)) + continue; + switch ((*i)->GetModifier()->m_miscvalue) + { + case 4418: // Increased Shock Damage + case 4554: // Increased Lightning Damage + case 4555: // Improved Moonfire + { + DoneTotal += (*i)->GetModifier()->m_amount; + break; + } + } + } + } + + // Pet happiness increases damage of Hunter pet spells (e.g. Lightning Breath) + if (IsPet() && ((Pet*)this)->getPetType() == HUNTER_PET) + { + if (Pet* pet = ((Pet*)this)) + { + switch (pet->GetHappinessState()) + { + case HAPPY: DoneTotalMod *= 1.25; break; + case CONTENT: break; + case UNHAPPY: DoneTotalMod *= 0.75; break; + } + } + } + + // Done fixed damage bonus auras + int32 DoneAdvertisedBenefit = SpellBaseDamageBonusDone(spellProto->GetSpellSchoolMask()); + + // Add flat bonus from spell damage versus + if (pUnit) + DoneAdvertisedBenefit += pUnit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask); + + // Pets just add their bonus damage to their spell damage + // note that their spell damage is just gain of their own auras + if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsPet()) + DoneAdvertisedBenefit += ((Pet*)this)->GetBonusDamage(); + + // apply ap bonus and benefit affected by spell power implicit coeffs and spell level penalties + DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true, this, spell); + + float tmpDamage = (int32(pdamage) + DoneTotal * int32(stack)) * DoneTotalMod; + // apply spellmod to Done damage (flat and pct) + if (pUnit) + { + if (Player* modOwner = pUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage, spell); + } + + //DEBUG_UNIT(this, DEBUG_SPELLS_DAMAGE, "SpellDmgBonus[spell=%u]: (base=%u + %i) * %f. SP=%i", spellProto->Id, pdamage, DoneTotal, DoneTotalMod, DoneAdvertisedBenefit); + return tmpDamage > 0 ? uint32(tmpDamage) : 0; +} + +int32 WorldObject::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) +{ + int32 DoneAdvertisedBenefit = 0; + + if (Unit* pUnit = ToUnit()) + { + Unit::AuraList const& mDamageDone = pUnit->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); + for (Unit::AuraList::const_iterator i = mDamageDone.begin(); i != mDamageDone.end(); ++i) + { + if (((*i)->GetModifier()->m_miscvalue & schoolMask) != 0 && + (*i)->GetSpellProto()->EquippedItemClass == -1 && // -1 == any item class (not wand then) + (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0) // 0 == any inventory type (not wand then) + DoneAdvertisedBenefit += (*i)->GetModifier()->m_amount; + } + + if (GetTypeId() == TYPEID_PLAYER) + { + // Damage bonus from stats + Unit::AuraList const& mDamageDoneOfStatPercent = pUnit->GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT); + for (Unit::AuraList::const_iterator i = mDamageDoneOfStatPercent.begin(); i != mDamageDoneOfStatPercent.end(); ++i) + { + if ((*i)->GetModifier()->m_miscvalue & schoolMask) + { + // stat used stored in miscValueB for this aura + Stats usedStat = STAT_SPIRIT; + DoneAdvertisedBenefit += int32(pUnit->GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f); + } + } + } + } + + return DoneAdvertisedBenefit; +} + +int32 WorldObject::SpellBonusWithCoeffs(SpellEntry const *spellProto, int32 total, int32 benefit, int32 ap_benefit, DamageEffectType damagetype, bool donePart, WorldObject* pCaster, Spell* spell) +{ + // Distribute Damage over multiple effects, reduce by AoE + float coeff = 0.0f; + + // Not apply this to creature casted spells + // Daemon: n'importe quoi. Et apres on se demande pourquoi les degats du sceau du croise sont abuses ... + //if (GetTypeId()==TYPEID_UNIT && !((Creature*)this)->IsPet()) + // coeff = 1.0f; + // Check for table values + if (SpellBonusEntry const* bonus = sSpellMgr.GetSpellBonusData(spellProto->Id)) + { + coeff = damagetype == DOT ? bonus->dot_damage : bonus->direct_damage; + + // apply ap bonus at done part calculation only (it flat total mod so common with taken) + if (donePart && (bonus->ap_bonus || bonus->ap_dot_bonus)) + { + float const ap_bonus = damagetype == DOT ? bonus->ap_dot_bonus : bonus->ap_bonus; + float const total_ap = IsUnit() ? static_cast(this)->GetTotalAttackPowerValue(spellProto->IsSpellRequiresRangedAP() ? RANGED_ATTACK : BASE_ATTACK) : 0; + total += int32(ap_bonus * (total_ap + ap_benefit)); + } + } + // Calculate default coefficient + else if (benefit) + coeff = spellProto->CalculateDefaultCoefficient(damagetype); + + if (benefit) + { + // Calculate level penalty + float LvlPenalty = CalculateLevelPenalty(spellProto); + + // Calculate custom coefficient + coeff = spellProto->CalculateCustomCoefficient(pCaster, damagetype, coeff, spell, donePart); + + // Spellmod SpellDamage + if (Unit* pUnit = ToUnit()) + { + if (Player* modOwner = pUnit->GetSpellModOwner()) + { + coeff *= 100.0f; + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_SPELL_BONUS_DAMAGE, coeff, spell); + coeff /= 100.0f; + } + } + + // Nostalrius. + bool bUsePenalty = true; + // Flash of Light + if (spellProto->Id == 19993) + { + bUsePenalty = false; + if (Unit* pUnit = ToUnit()) + { + if (pUnit->HasAura(28853)) total += 53.0f; // Libram of Divinity + if (pUnit->HasAura(28851)) total += 83.0f; // Libram of Light + } + } + + // Dragonbreath Chili + if (spellProto->Id == 15851) + bUsePenalty = false; + + if (bUsePenalty) + total += int32(benefit * coeff * LvlPenalty); + else + total += int32(benefit * coeff); + + } + + return total; +}; + +void WorldObject::DealDamageMods(Unit *victim, uint32 &damage, uint32* absorb) +{ + Unit* pUnit = ToUnit(); + // [Nostalrius] Pas de degats sous esprit de redemption + if (!victim->isAlive() || + victim->IsTaxiFlying() || + (victim->IsCreature() && static_cast(victim)->IsInEvadeMode()) || + (pUnit && pUnit->getClass() == CLASS_PRIEST && pUnit->HasAura(27827))) + { + if (absorb) + *absorb += damage; + damage = 0; + return; + } + + uint32 originalDamage = damage; + + //Script Event damage Deal + if (Creature* pCreature = ToCreature()) + if (pCreature->AI()) + pCreature->AI()->DamageDeal(victim, damage); + + //Script Event damage taken + if (Creature* pCreature = victim->ToCreature()) + if (pCreature->AI() && pUnit) + pCreature->AI()->DamageTaken(pUnit, damage); + + if (absorb && originalDamage > damage) + *absorb += (originalDamage - damage); +} + +float WorldObject::CalculateLevelPenalty(SpellEntry const* spellProto) +{ + // Fix Nostalrius. La formule de MaNGOSZero est BC. + uint32 spellLevel = spellProto->spellLevel; + if (spellLevel <= 0 || spellLevel > 20) + return 1.0f; + return 1 - ((20.0f - spellLevel) * 0.0375f); + + /* + float LvlPenalty = 0.0f; + + if(spellLevel < 20) + LvlPenalty = 20.0f - spellLevel * 3.75f; + float LvlFactor = (float(spellLevel) + 6.0f) / float(getLevel()); + if(LvlFactor > 1.0f) + LvlFactor = 1.0f; + + return (100.0f - LvlPenalty) * LvlFactor / 100.0f; + */ +} + +float WorldObject::GetAPMultiplier(WeaponAttackType attType, bool normalized) const +{ + Unit const* pUnit = ToUnit(); + if (!pUnit) + return 0; + + if (!normalized || GetTypeId() != TYPEID_PLAYER) + return float(pUnit->GetAttackTime(attType)) / 1000.0f; + + Item *Weapon = ((Player*)this)->GetWeaponForAttack(attType, true, false); + if (!Weapon) + return 2.4f; // fist attack + + switch (Weapon->GetProto()->InventoryType) + { + case INVTYPE_2HWEAPON: + return 3.3f; + case INVTYPE_RANGED: + case INVTYPE_RANGEDRIGHT: + case INVTYPE_THROWN: + return 2.8f; + case INVTYPE_WEAPON: + case INVTYPE_WEAPONMAINHAND: + case INVTYPE_WEAPONOFFHAND: + default: + return Weapon->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER ? 1.7f : 2.4f; + } +} + +void WorldObject::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss) +{ + if (!damageInfo) + return; + + Unit *pVictim = damageInfo->target; + + if (!pVictim) + return; + + if (!pVictim->isAlive() || pVictim->IsTaxiFlying() || (pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())) + return; + + SpellEntry const *spellProto = sSpellMgr.GetSpellEntry(damageInfo->SpellID); + if (spellProto == nullptr) + { + sLog.outError("WorldObject::DealSpellDamage have wrong damageInfo->SpellID: %u", damageInfo->SpellID); + return; + } + + // Call default DealDamage (send critical in hit info for threat calculation) + CleanDamage cleanDamage(0, BASE_ATTACK, damageInfo->HitInfo & SPELL_HIT_TYPE_CRIT ? MELEE_HIT_CRIT : MELEE_HIT_NORMAL, damageInfo->absorb, damageInfo->resist); + DealDamage(pVictim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, GetSchoolMask(damageInfo->school), spellProto, durabilityLoss, damageInfo->spell); +} + +uint32 WorldObject::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss, Spell* spell) +{ + // Should never happen since DealDamage is overriden in Unit class. + if (pVictim == this) + return 0; + + return pVictim->DealDamage(pVictim, damage, cleanDamage, damagetype, damageSchoolMask, spellProto, durabilityLoss, spell); +} + +bool WorldObject::CheckAndIncreaseCastCounter() +{ + uint32 maxCasts = sWorld.getConfig(CONFIG_UINT32_MAX_SPELL_CASTS_IN_CHAIN); + + if (maxCasts && m_castCounter >= maxCasts) + return false; + + ++m_castCounter; + return true; +} + +void WorldObject::SetCurrentCastedSpell(Spell * pSpell) +{ + MANGOS_ASSERT(pSpell); // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells + + CurrentSpellTypes CSpellType = pSpell->GetCurrentContainer(); + + if (pSpell == m_currentSpells[CSpellType]) return; // avoid breaking self + // break same type spell if it is not delayed + InterruptSpell(CSpellType, false); + + // special breakage effects: + switch (CSpellType) + { + case CURRENT_GENERIC_SPELL: + { + // generic spells always break channeled not delayed spells + InterruptSpell(CURRENT_CHANNELED_SPELL, false); + + // autorepeat breaking + if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) + { + // break autorepeat if not Auto Shot + if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351) + InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + + if (Unit* pUnit = ToUnit()) + pUnit->m_AutoRepeatFirstCast = true; + } + } + break; + + case CURRENT_CHANNELED_SPELL: + { + // channel spells always break generic non-delayed and any channeled spells + InterruptSpell(CURRENT_GENERIC_SPELL, false); + InterruptSpell(CURRENT_CHANNELED_SPELL); + + // it also does break autorepeat if not Auto Shot + if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351) + InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + } + break; + + case CURRENT_AUTOREPEAT_SPELL: + { + // only Auto Shoot does not break anything + if (pSpell->m_spellInfo->Category == 351) + { + // generic autorepeats break generic non-delayed and channeled non-delayed spells + InterruptSpell(CURRENT_GENERIC_SPELL, false); + InterruptSpell(CURRENT_CHANNELED_SPELL, false); + } + // special action: set first cast flag + if (Unit* pUnit = ToUnit()) + pUnit->m_AutoRepeatFirstCast = true; + } + break; + + default: + { + // other spell types don't break anything now + } break; + } + + // current spell (if it is still here) may be safely deleted now + if (m_currentSpells[CSpellType]) + m_currentSpells[CSpellType]->SetReferencedFromCurrent(false); + + // set new current spell + m_currentSpells[CSpellType] = pSpell; + pSpell->SetReferencedFromCurrent(true); + + pSpell->m_selfContainer = &(m_currentSpells[pSpell->GetCurrentContainer()]); +} + +Spell* WorldObject::FindCurrentSpellBySpellId(uint32 spell_id) const +{ + for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) + if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id == spell_id) + return m_currentSpells[i]; + return nullptr; +} + +bool WorldObject::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skipAutorepeat) const +{ + // We don't do loop here to explicitly show that melee spell is excluded. + // Maybe later some special spells will be excluded too. + + // generic spells are casted when they are not finished and not delayed + if (m_currentSpells[CURRENT_GENERIC_SPELL] && + (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) && + (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED)) + return (true); + + // channeled spells may be delayed, but they are still considered casted + else if (!skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] && + (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED)) + return (true); + + // autorepeat spells may be finished or delayed, but they are still considered casted + else if (!skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) + return (true); + + return (false); +} + +bool WorldObject::IsNextSwingSpellCasted() const +{ + if (m_currentSpells[CURRENT_MELEE_SPELL] && m_currentSpells[CURRENT_MELEE_SPELL]->IsNextMeleeSwingSpell()) + return (true); + + return (false); +} + +bool WorldObject::IsNoMovementSpellCasted() const +{ + if (m_currentSpells[CURRENT_GENERIC_SPELL] && + (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) && + m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED && + m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) + return (true); + else if (m_currentSpells[CURRENT_CHANNELED_SPELL] && + m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED && + m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) + return (true); + // don't need to check for AUTOREPEAT_SPELL + + return (false); +} + +void WorldObject::InterruptSpellsWithInterruptFlags(uint32 flags, uint32 except) +{ + for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; ++i) + if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(i))) + if (spell->getState() == SPELL_STATE_PREPARING && spell->GetCastedTime()) + if (!spell->IsNextMeleeSwingSpell() && !spell->IsAutoRepeat() && !spell->IsTriggered() && (spell->m_spellInfo->InterruptFlags & flags) && spell->m_spellInfo->Id != except) + InterruptSpell(CurrentSpellTypes(i)); +} + + +void WorldObject::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id) +{ + // generic spells are interrupted if they are not finished or delayed + if (m_currentSpells[CURRENT_GENERIC_SPELL] && (!spell_id || m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Id == spell_id)) + InterruptSpell(CURRENT_GENERIC_SPELL, withDelayed); + + // autorepeat spells are interrupted if they are not finished or delayed + if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id == spell_id)) + InterruptSpell(CURRENT_AUTOREPEAT_SPELL, withDelayed); + + // channeled spells are interrupted if they are not finished, even if they are delayed + if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id == spell_id)) + InterruptSpell(CURRENT_CHANNELED_SPELL, true); +} + +void WorldObject::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed) +{ + MANGOS_ASSERT(spellType < CURRENT_MAX_SPELL); + + if (m_currentSpells[spellType] && (withDelayed || m_currentSpells[spellType]->getState() != SPELL_STATE_DELAYED)) + { + // send autorepeat cancel message for autorepeat spells + if (spellType == CURRENT_AUTOREPEAT_SPELL) + { + if (GetTypeId() == TYPEID_PLAYER) + ((Player*)this)->SendAutoRepeatCancel(); + } + + if (m_currentSpells[spellType]->getState() != SPELL_STATE_FINISHED) + m_currentSpells[spellType]->cancel(); + + // cancel can interrupt spell already (caster cancel ->target aura remove -> caster iterrupt) + if (m_currentSpells[spellType]) + { + m_currentSpells[spellType]->SetReferencedFromCurrent(false); + m_currentSpells[spellType] = nullptr; + } + } +} + +void WorldObject::FinishSpell(CurrentSpellTypes spellType, bool ok /*= true*/) +{ + Spell* spell = m_currentSpells[spellType]; + if (!spell) + return; + + if (spellType == CURRENT_CHANNELED_SPELL) + spell->SendChannelUpdate(0); + + spell->finish(ok); +} + +void WorldObject::GetDynObjects(uint32 spellId, SpellEffectIndex effectIndex, std::vector& dynObjsOut) +{ + for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) + { + DynamicObject* dynObj = GetMap()->GetDynamicObject(*i); + if (!dynObj) + { + i = m_dynObjGUIDs.erase(i); + continue; + } + + if (dynObj->GetSpellId() == spellId && dynObj->GetEffIndex() == effectIndex) + dynObjsOut.push_back(dynObj); + ++i; + } +} + +DynamicObject * WorldObject::GetDynObject(uint32 spellId, SpellEffectIndex effIndex) +{ + for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) + { + DynamicObject* dynObj = GetMap()->GetDynamicObject(*i); + if (!dynObj) + { + i = m_dynObjGUIDs.erase(i); + continue; + } + + if (dynObj->GetSpellId() == spellId && dynObj->GetEffIndex() == effIndex) + return dynObj; + ++i; + } + return nullptr; +} + +DynamicObject * WorldObject::GetDynObject(uint32 spellId) +{ + for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) + { + DynamicObject* dynObj = GetMap()->GetDynamicObject(*i); + if (!dynObj) + { + i = m_dynObjGUIDs.erase(i); + continue; + } + + if (dynObj->GetSpellId() == spellId) + return dynObj; + ++i; + } + return nullptr; +} + +void WorldObject::AddDynObject(DynamicObject* dynObj) +{ + m_dynObjGUIDs.push_back(dynObj->GetObjectGuid()); + dynObj->SetWorldMask(GetWorldMask()); // Nostalrius : phasing +} + +void WorldObject::RemoveDynObject(uint32 spellid) +{ + if (m_dynObjGUIDs.empty()) + return; + for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) + { + DynamicObject* dynObj = GetMap()->GetDynamicObject(*i); + if (!dynObj) + i = m_dynObjGUIDs.erase(i); + else if (spellid == 0 || dynObj->GetSpellId() == spellid) + { + dynObj->Delete(); + i = m_dynObjGUIDs.erase(i); + } + else + ++i; + } +} + +void WorldObject::RemoveAllDynObjects() +{ + while (!m_dynObjGUIDs.empty()) + { + if (DynamicObject* dynObj = GetMap()->GetDynamicObject(*m_dynObjGUIDs.begin())) + dynObj->Delete(); + m_dynObjGUIDs.erase(m_dynObjGUIDs.begin()); + } +} + +void WorldObject::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy, SpellEntry const* triggeredByParent) +{ + SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(spellId); + + if (!spellInfo) + { + if (triggeredByAura) + sLog.outError("CastSpell: unknown spell id %i by caster: %s triggered by aura %u (eff %u)", spellId, GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); + else + sLog.outError("CastSpell: unknown spell id %i by caster: %s", spellId, GetGuidStr().c_str()); + return; + } + + CastSpell(Victim, spellInfo, triggered, castItem, triggeredByAura, originalCaster, triggeredBy, triggeredByParent); +} + +void WorldObject::CastSpell(Unit* Victim, SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy, SpellEntry const* triggeredByParent) +{ + if (!spellInfo) + { + if (triggeredByAura) + sLog.outError("CastSpell: unknown spell by caster: %s triggered by aura %u (eff %u)", GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); + else + sLog.outError("CastSpell: unknown spell by caster: %s", GetGuidStr().c_str()); + return; + } + + if (castItem) + DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "WORLD: cast Item spellId - %i", spellInfo->Id); + + if (triggeredByAura) + { + if (!originalCaster) + originalCaster = triggeredByAura->GetCasterGuid(); + + triggeredBy = triggeredByAura->GetSpellProto(); + } + + Spell *spell; + + if (Unit* pUnit = ToUnit()) + spell = new Spell(pUnit, spellInfo, triggered, originalCaster, triggeredBy, NULL, triggeredByParent); + else if (GameObject* pGameObject = ToGameObject()) + spell = new Spell(pGameObject, spellInfo, triggered, originalCaster, triggeredBy, NULL, triggeredByParent); + else + return; + + SpellCastTargets targets; + + // Don't set unit target on destination target based spells, otherwise the spell will cancel + // as soon as the target dies or leaves the area of the effect + if (spellInfo->Targets & TARGET_FLAG_DEST_LOCATION) + targets.setDestination(Victim->GetPositionX(), Victim->GetPositionY(), Victim->GetPositionZ()); + else + targets.setUnitTarget(Victim); + + if (spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) + if (WorldObject* caster = spell->GetCastingObject()) + targets.setSource(caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ()); + + spell->SetCastItem(castItem); + spell->prepare(std::move(targets), triggeredByAura); +} + +void WorldObject::CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy) +{ + SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(spellId); + + if (!spellInfo) + { + if (triggeredByAura) + sLog.outError("CastCustomSpell: unknown spell id %i by caster: %s triggered by aura %u (eff %u)", spellId, GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); + else + sLog.outError("CastCustomSpell: unknown spell id %i by caster: %s", spellId, GetGuidStr().c_str()); + return; + } + + CastCustomSpell(Victim, spellInfo, bp0, bp1, bp2, triggered, castItem, triggeredByAura, originalCaster, triggeredBy); +} + +void WorldObject::CastCustomSpell(Unit* Victim, SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy) +{ + if (!spellInfo) + { + if (triggeredByAura) + sLog.outError("CastCustomSpell: unknown spell by caster: %s triggered by aura %u (eff %u)", GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); + else + sLog.outError("CastCustomSpell: unknown spell by caster: %s", GetGuidStr().c_str()); + return; + } + + if (castItem) + DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "WORLD: cast Item spellId - %i", spellInfo->Id); + + if (triggeredByAura) + { + if (!originalCaster) + originalCaster = triggeredByAura->GetCasterGuid(); + + triggeredBy = triggeredByAura->GetSpellProto(); + } + + Spell *spell; + + if (Unit* pUnit = ToUnit()) + spell = new Spell(pUnit, spellInfo, triggered, originalCaster, triggeredBy); + else if (GameObject* pGameObject = ToGameObject()) + spell = new Spell(pGameObject, spellInfo, triggered, originalCaster, triggeredBy); + else + return; + + if (bp0) + spell->m_currentBasePoints[EFFECT_INDEX_0] = *bp0; + + if (bp1) + spell->m_currentBasePoints[EFFECT_INDEX_1] = *bp1; + + if (bp2) + spell->m_currentBasePoints[EFFECT_INDEX_2] = *bp2; + + SpellCastTargets targets; + targets.setUnitTarget(Victim); + spell->SetCastItem(castItem); + spell->prepare(std::move(targets), triggeredByAura); +} + +// used for scripting +void WorldObject::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy) +{ + SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(spellId); + + if (!spellInfo) + { + if (triggeredByAura) + sLog.outError("CastSpell(x,y,z): unknown spell id %i by caster: %s triggered by aura %u (eff %u)", spellId, GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); + else + sLog.outError("CastSpell(x,y,z): unknown spell id %i by caster: %s", spellId, GetGuidStr().c_str()); + return; + } + + CastSpell(x, y, z, spellInfo, triggered, castItem, triggeredByAura, originalCaster, triggeredBy); +} + +// used for scripting +void WorldObject::CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy) +{ + if (!spellInfo) + { + if (triggeredByAura) + sLog.outError("CastSpell(x,y,z): unknown spell by caster: %s triggered by aura %u (eff %u)", GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); + else + sLog.outError("CastSpell(x,y,z): unknown spell by caster: %s", GetGuidStr().c_str()); + return; + } + + if (castItem) + DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "WORLD: cast Item spellId - %i", spellInfo->Id); + + if (triggeredByAura) + { + if (!originalCaster) + originalCaster = triggeredByAura->GetCasterGuid(); + + triggeredBy = triggeredByAura->GetSpellProto(); + } + + Spell *spell; + + if (Unit* pUnit = ToUnit()) + spell = new Spell(pUnit, spellInfo, triggered, originalCaster, triggeredBy); + else if (GameObject* pGameObject = ToGameObject()) + spell = new Spell(pGameObject, spellInfo, triggered, originalCaster, triggeredBy); + else + return; + + SpellCastTargets targets; + targets.setDestination(x, y, z); + spell->SetCastItem(castItem); + spell->prepare(std::move(targets), triggeredByAura); +} + +bool WorldObject::isVisibleFor(Player const* u, WorldObject const* viewPoint) const +{ + return isVisibleForInState(u, viewPoint, false); +} \ No newline at end of file diff --git a/src/game/Objects/Object.h b/src/game/Objects/Object.h index 7b93a37cb..5b831da84 100644 --- a/src/game/Objects/Object.h +++ b/src/game/Objects/Object.h @@ -29,6 +29,7 @@ #include "ObjectGuid.h" #include "Camera.h" #include "SpellEntry.h" +#include "Utilities/EventProcessor.h" #include #include @@ -81,6 +82,8 @@ class Creature; class Player; class Unit; class Map; +class Item; +class Aura; class UpdateMask; class InstanceData; class TerrainInfo; @@ -634,6 +637,65 @@ class MANGOS_DLL_SPEC Object struct WorldObjectChangeAccumulator; +// At least some values expected fixed and used in auras field, other custom +enum MeleeHitOutcome +{ + MELEE_HIT_EVADE = 0, + MELEE_HIT_MISS = 1, + MELEE_HIT_DODGE = 2, // used as misc in SPELL_AURA_IGNORE_COMBAT_RESULT + MELEE_HIT_BLOCK = 3, // used as misc in SPELL_AURA_IGNORE_COMBAT_RESULT + MELEE_HIT_PARRY = 4, // used as misc in SPELL_AURA_IGNORE_COMBAT_RESULT + MELEE_HIT_GLANCING = 5, + MELEE_HIT_CRIT = 6, + MELEE_HIT_CRUSHING = 7, + MELEE_HIT_NORMAL = 8, + MELEE_HIT_BLOCK_CRIT = 9, +}; + +// Spell damage info structure based on structure sending in SMSG_SPELLNONMELEEDAMAGELOG opcode +struct SpellNonMeleeDamage { + SpellNonMeleeDamage(WorldObject *_attacker, Unit *_target, uint32 _SpellID, SpellSchools _school) + : target(_target), attacker(_attacker), SpellID(_SpellID), damage(0), school(_school), + absorb(0), resist(0), periodicLog(false), unused(false), blocked(0), HitInfo(0), spell(nullptr) + {} + + Unit *target; + WorldObject *attacker; + uint32 SpellID; + uint32 damage; + SpellSchools school; + uint32 absorb; + int32 resist; + bool periodicLog; + bool unused; + uint32 blocked; + uint32 HitInfo; + Spell *spell; +}; + +struct CleanDamage +{ + CleanDamage(uint32 _damage, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome, uint32 _Absorb, int32 _Resist) : + damage(_damage), attackType(_attackType), hitOutCome(_hitOutCome), absorb(_Absorb), resist(_Resist) {} + + uint32 damage; + WeaponAttackType attackType; + MeleeHitOutcome hitOutCome; + uint32 absorb; + int32 resist; +}; + +enum CurrentSpellTypes +{ + CURRENT_MELEE_SPELL = 0, + CURRENT_GENERIC_SPELL = 1, + CURRENT_AUTOREPEAT_SPELL = 2, + CURRENT_CHANNELED_SPELL = 3 +}; + +#define CURRENT_FIRST_NON_MELEE_SPELL 1 +#define CURRENT_MAX_SPELL 4 + class MANGOS_DLL_SPEC WorldObject : public Object { friend struct WorldObjectChangeAccumulator; @@ -650,8 +712,8 @@ class MANGOS_DLL_SPEC WorldObject : public Object void Update(uint32 time_diff) { -m_obj->Update(m_obj->m_updateTracker.timeElapsed(), time_diff); -m_obj->m_updateTracker.Reset(); + m_obj->Update(m_obj->m_updateTracker.timeElapsed(), time_diff); + m_obj->m_updateTracker.Reset(); } void UpdateRealTime(uint32 now, uint32 time_diff) @@ -778,6 +840,10 @@ m_obj->m_updateTracker.Reset(); bool isInFront(WorldObject const* target,float distance, float arc = M_PI) const; bool isInBack(WorldObject const* target, float distance, float arc = M_PI) const; + bool CanReachWithMeleeSpellAttack(Unit const* pVictim, float flat_mod = 0.0f) const; + float GetLeewayBonusRange(const Unit* target, bool ability) const; + float GetLeewayBonusRadius() const; + // Gestion des positions void GetRelativePositions(float fForwardBackward, float fLeftRight, float fUpDown, float &x, float &y, float &z); void GetInCirclePositions(float dist, uint32 curr, uint32 total, float &x, float &y, float &z, float &o); @@ -845,9 +911,17 @@ m_obj->m_updateTracker.Reset(); void SendObjectDeSpawnAnim(ObjectGuid guid) const; - virtual bool IsHostileTo(Unit const* unit) const =0; - virtual bool IsFriendlyTo(Unit const* unit) const =0; + virtual bool IsHostileTo(WorldObject const* target) const =0; + virtual bool IsFriendlyTo(WorldObject const* target) const =0; + virtual uint32 getFaction() const = 0; + FactionTemplateEntry const* getFactionTemplateEntry() const; + virtual ReputationRank GetReactionTo(WorldObject const* target) const; + ReputationRank static GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, WorldObject const* target); + virtual bool IsValidAttackTarget(Unit const* target) const { return false; } + virtual bool isVisibleForOrDetect(WorldObject const* pDetector, WorldObject const* viewPoint, bool detect, bool inVisibleList = false, bool* alert = nullptr) const { return isVisibleForInState(pDetector, viewPoint, inVisibleList); } + bool IsControlledByPlayer() const; + bool IsLikePlayer() const; virtual void SaveRespawnTime() {} void AddObjectToRemoveList(); @@ -859,10 +933,10 @@ m_obj->m_updateTracker.Reset(); // main visibility check function in normal case (ignore grey zone distance check) bool isWithinVisibilityDistanceOf(Unit const* viewer, WorldObject const* viewpoint, bool inVisibleList = false) const; - bool isVisibleFor(Player const* u, WorldObject const* viewPoint) const { return isVisibleForInState(u,viewPoint,false); } + bool isVisibleFor(Player const* u, WorldObject const* viewPoint) const; // low level function for visibility change code, must be define in all main world object subclasses - virtual bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const = 0; + virtual bool isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const = 0; void SetMap(Map * map); Map * GetMap() const; @@ -927,6 +1001,87 @@ m_obj->m_updateTracker.Reset(); uint32 GetCreatureSummonLimit() const { return m_creatureSummonLimit; } void SetCreatureSummonLimit(uint32 limit); + virtual uint32 getLevel() const = 0; + uint32 GetLevelForTarget(WorldObject const* target = nullptr) const; + uint16 GetSkillMaxForLevel(WorldObject const* target = nullptr) const { return GetLevelForTarget(target) * 5; }; + uint32 GetWeaponSkillValue(WeaponAttackType attType, WorldObject const* target = nullptr) const; + uint32 GetUnitMeleeSkill(WorldObject const* target = nullptr) const { return GetLevelForTarget(target) * 5; } + uint32 GetDefenseSkillValue(WorldObject const* target = nullptr) const; + + virtual Player* GetAffectingPlayer() const { return nullptr; } + Unit* SelectMagnetTarget(Unit *victim, Spell* spell = nullptr, SpellEffectIndex eff = EFFECT_INDEX_0); + + void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr, SpellEntry const* triggeredByParent = nullptr); + void CastSpell(Unit* Victim, SpellEntry const *spellInfo, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr, SpellEntry const* triggeredByParent = nullptr); + void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr); + void CastCustomSpell(Unit* Victim, SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr); + void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr); + void CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr); + + void SetCurrentCastedSpell(Spell * pSpell); + Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; } + Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; + bool CheckAndIncreaseCastCounter(); + void DecreaseCastCounter() { if (m_castCounter) --m_castCounter; } + + // set withDelayed to true to account delayed spells as casted + // delayed+channeled spells are always accounted as casted + // we can skip channeled or delayed checks using flags + bool IsNonMeleeSpellCasted(bool withDelayed = false, bool skipChanneled = false, bool skipAutorepeat = false) const; + bool IsNextSwingSpellCasted() const; + // for movement generators, check if current casted spell has movement interrupt flags + bool IsNoMovementSpellCasted() const; + + // set withDelayed to true to interrupt delayed spells too + // delayed+channeled spells are always interrupted + void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0); + void InterruptSpellsWithInterruptFlags(uint32 flags, uint32 except = 0); + void InterruptSpell(CurrentSpellTypes spellType, bool withDelayed = true); + void FinishSpell(CurrentSpellTypes spellType, bool ok = true); + + virtual bool IsSpellCrit(Unit const* pVictim, SpellEntry const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK, Spell* spell = nullptr) const { return false; } + uint32 SpellCriticalHealingBonus(SpellEntry const* spellProto, uint32 damage, Unit const* pVictim) const; + uint32 SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim, Spell* spell = nullptr); + float MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell, Spell* spellPtr = nullptr); + SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell, Spell* spellPtr = nullptr); + SpellMissInfo MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell, Spell* spellPtr = nullptr); + int32 MagicSpellHitChance(Unit *pVictim, SpellEntry const *spell, Spell* spellPtr = nullptr); + float GetSpellResistChance(Unit const* victim, uint32 schoolMask, bool innateResists) const; + SpellMissInfo SpellHitResult(Unit *pVictim, SpellEntry const *spell, SpellEffectIndex effIndex, bool canReflect = false, Spell* spellPtr = nullptr); + void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellEntry const *procSpell = nullptr, Spell* spell = nullptr); + void CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK, Spell* spell = nullptr); + int32 CalculateSpellDamage(Unit const* target, SpellEntry const* spellProto, SpellEffectIndex effect_index, int32 const* basePoints = nullptr, Spell* spell = nullptr); + int32 SpellBonusWithCoeffs(SpellEntry const *spellProto, int32 total, int32 benefit, int32 ap_benefit, DamageEffectType damagetype, bool donePart, WorldObject *pCaster, Spell* spell = nullptr); + static float CalculateLevelPenalty(SpellEntry const* spellProto); + uint32 SpellDamageBonusDone(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1, Spell* spell = nullptr); + int32 SpellBaseDamageBonusDone(SpellSchoolMask schoolMask); + uint32 SpellHealingBonusDone(Unit *pVictim, SpellEntry const *spellProto, int32 healamount, DamageEffectType damagetype, uint32 stack = 1, Spell* spell = nullptr); + int32 SpellBaseHealingBonusDone(SpellSchoolMask schoolMask); + uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage) const; + uint32 MeleeDamageBonusDone(Unit *pVictim, uint32 damage, WeaponAttackType attType, SpellEntry const *spellProto = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, uint32 stack = 1, Spell* spell = nullptr, bool flat = true); + virtual SpellSchoolMask GetMeleeDamageSchoolMask() const; + float GetAPMultiplier(WeaponAttackType attType, bool normalized) const; + virtual uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss, Spell* spell = nullptr); + void DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb); + void DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss); + void SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log); + void SendSpellNonMeleeDamageLog(Unit *target, uint32 spellID, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 absorbedDamage, int32 resist, bool isPeriodic, uint32 blocked, bool criticalHit = false, bool split = false); + void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo); + int32 DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellProto, bool critical = false); + void SendHealSpellLog(Unit const* pVictim, uint32 SpellID, uint32 Damage, bool critical = false) const; + void EnergizeBySpell(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype); + void SendEnergizeSpellLog(Unit const* pVictim, uint32 SpellID, uint32 Damage, Powers powertype) const; + + void GetDynObjects(uint32 spellId, SpellEffectIndex effectIndex, std::vector& dynObjsOut); + DynamicObject* GetDynObject(uint32 spellId, SpellEffectIndex effIndex); + DynamicObject* GetDynObject(uint32 spellId); + void AddDynObject(DynamicObject* dynObj); + void RemoveDynObject(uint32 spellid); + void RemoveDynObjectWithGUID(ObjectGuid guid) { m_dynObjGUIDs.remove(guid); } + void RemoveAllDynObjects(); + + // Event handler + EventProcessor m_Events; protected: explicit WorldObject(); @@ -951,9 +1106,31 @@ m_obj->m_updateTracker.Reset(); float m_lootAndXPRangeModifier; - uint32 m_creatureSummonCount; // Current summon count - uint32 m_creatureSummonLimit; // Hard limit on creature summons - uint32 m_summonLimitAlert; // Timer to alert GMs if a creature is at the summon limit + uint32 m_creatureSummonCount; // Current summon count + uint32 m_creatureSummonLimit; // Hard limit on creature summons + uint32 m_summonLimitAlert; // Timer to alert GMs if a creature is at the summon limit + + typedef std::list DynObjectGUIDs; + DynObjectGUIDs m_dynObjGUIDs; + + std::array m_currentSpells{}; + uint32 m_castCounter = 0; // count casts chain of triggered spells for prevent infinity cast crashes + private: + // Error traps for some wrong args using + // this will catch and prevent build for any cases when all optional args skipped and instead triggered used non boolean type + // no bodies expected for this declarations + template + void CastSpell(Unit* Victim, uint32 spell, TR triggered); + template + void CastSpell(Unit* Victim, SpellEntry const* spell, TR triggered); + template + void CastCustomSpell(Unit* Victim, uint32 spell, int32 const* bp0, int32 const* bp1, int32 const* bp2, TR triggered); + template + void CastCustomSpell(Unit* Victim, SpellEntry const* spell, int32 const* bp0, int32 const* bp1, int32 const* bp2, TR triggered); + template + void CastSpell(float x, float y, float z, uint32 spell, TR triggered); + template + void CastSpell(float x, float y, float z, SpellEntry const* spell, TR triggered); }; // Helper functions to cast between different Object pointers. Useful when unsure that your object* is valid at all. diff --git a/src/game/Objects/Player.cpp b/src/game/Objects/Player.cpp index a4e613ce3..db61810ca 100644 --- a/src/game/Objects/Player.cpp +++ b/src/game/Objects/Player.cpp @@ -2653,7 +2653,7 @@ bool Player::IsAllowedWhisperFrom(ObjectGuid guid) const return false; } -bool Player::IsGroupVisibleFor(Player* p) const +bool Player::IsGroupVisibleFor(Player const* p) const { switch (sWorld.getConfig(CONFIG_UINT32_GROUP_VISIBILITY)) { @@ -18068,7 +18068,7 @@ bool Player::CanJoinToBattleground() const return true; } -bool Player::IsVisibleInGridForPlayer(Player* pl) const +bool Player::IsVisibleInGridForPlayer(Player const* pl) const { // gamemaster in GM mode see all, including ghosts if (pl->IsGameMaster() && GetSession()->GetSecurity() <= pl->GetSession()->GetSecurity()) @@ -20803,30 +20803,6 @@ bool Player::IsImmuneToSpellEffect(SpellEntry const *spellInfo, SpellEffectIndex return Unit::IsImmuneToSpellEffect(spellInfo, index, castOnSelf); } -/* Appeler cette fonction pour memoriser le dernier sort lance par une unite. -byclient = true si le sort est lance depuis le client du jeu. */ -void Player::SetLastCastedSpell(uint32 spell_id, bool byclient) -{ - if (spell_id != 23303 && spell_id != 836) - { - m_lastCastedSpellID = spell_id; - if (byclient) - m_lastFromClientCastedSpellID = spell_id; - } -} - -/* Appeler cette fonction pour obtenir le dernier sort lance par une unite. -byclientonly = true si on veut le dernier sort lance depuis le client, -sinon inclut tous les sorts. */ -uint32 Player::GetLastCastedSpell(bool byclientonly) -{ - if (byclientonly) - return m_lastFromClientCastedSpellID; - else - return m_lastCastedSpellID; -}; - - Item* Player::AddItem(uint32 itemId, uint32 count) { uint32 noSpaceForCount = 0; diff --git a/src/game/Objects/Player.h b/src/game/Objects/Player.h index b02123f4e..7f5d340a4 100644 --- a/src/game/Objects/Player.h +++ b/src/game/Objects/Player.h @@ -1452,9 +1452,6 @@ class MANGOS_DLL_SPEC Player final: public Unit std::vector m_ItemSetEff; - void SetLastCastedSpell(uint32 spell_id, bool byclient); - uint32 GetLastCastedSpell(bool byclientonly); - /*********************************************************/ /*** TALENT SYSTEM ***/ /*********************************************************/ @@ -1823,7 +1820,7 @@ class MANGOS_DLL_SPEC Player final: public Unit bool IsInVisibleList(WorldObject const* u) const; bool IsInVisibleList_Unsafe(WorldObject const* u) const { return this == u || m_visibleGUIDs.find(u->GetObjectGuid()) != m_visibleGUIDs.end(); } - bool IsVisibleInGridForPlayer(Player* pl) const; + bool IsVisibleInGridForPlayer(Player const* pl) const override; bool IsVisibleGloballyFor(Player* pl) const; void UpdateVisibilityOf(WorldObject const* viewPoint, WorldObject* target); template @@ -2452,7 +2449,7 @@ class MANGOS_DLL_SPEC Player final: public Unit Player* GetNextRandomRaidMember(float radius); PartyResult CanUninviteFromGroup(ObjectGuid uninvitedGuid) const; void UpdateGroupLeaderFlag(const bool remove = false); - bool IsGroupVisibleFor(Player* p) const; + bool IsGroupVisibleFor(Player const* p) const; bool IsInSameGroupWith(Player const* p) const; bool IsInSameRaidWith(Player const* p) const { return p == this || (GetGroup() != nullptr && GetGroup() == p->GetGroup()); } void UninviteFromGroup(); diff --git a/src/game/Objects/Unit.cpp b/src/game/Objects/Unit.cpp index 2aa4c5d19..a7c0df578 100644 --- a/src/game/Objects/Unit.cpp +++ b/src/game/Objects/Unit.cpp @@ -130,11 +130,6 @@ Unit::Unit() m_state = 0; m_deathState = ALIVE; - for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) - m_currentSpells[i] = nullptr; - - m_castCounter = 0; - //m_Aura = NULL; //m_AurasCheck = 2000; //m_removeAuraTimer = 4; @@ -193,7 +188,7 @@ Unit::Unit() //m_victimThreat = 0.0f; for (int i = 0; i < MAX_SPELL_SCHOOL; ++i) m_threatModifier[i] = 1.0f; - m_isSorted = true; + for (int i = 0; i < MAX_MOVE_TYPE; ++i) m_speed_rate[i] = 1.0f; @@ -208,9 +203,6 @@ Unit::Unit() // Phasing worldMask = WORLD_DEFAULT_UNIT; - // Nostalrius : Ivina - m_lastCastedSpellID = 0; - m_lastAttackType = MAX_ATTACK; _casterChaseDistance = 0.0f; m_doExtraAttacks = false; @@ -719,31 +711,6 @@ void Unit::DoKillUnit(Unit* victim) DealDamage(victim, victim->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); } -void Unit::DealDamageMods(Unit *victim, uint32 &damage, uint32* absorb) -{ - // [Nostalrius] Pas de degats sous esprit de redemption - if (!victim->isAlive() || victim->IsTaxiFlying() || (victim->GetTypeId() == TYPEID_UNIT && ((Creature*)victim)->IsInEvadeMode()) || - (getClass() == CLASS_PRIEST && HasAura(27827))) - { - if (absorb) - *absorb += damage; - damage = 0; - return; - } - - uint32 originalDamage = damage; - - //Script Event damage Deal - if (GetTypeId() == TYPEID_UNIT && ((Creature *)this)->AI()) - ((Creature *)this)->AI()->DamageDeal(victim, damage); - //Script Event damage taken - if (victim->GetTypeId() == TYPEID_UNIT && ((Creature *)victim)->AI()) - ((Creature *)victim)->AI()->DamageTaken(this, damage); - - if (absorb && originalDamage > damage) - *absorb += (originalDamage - damage); -} - uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss, Spell* spell) { // remove affects from victim (including from 0 damage and DoTs) @@ -1429,165 +1396,15 @@ void Unit::CastStop(uint32 except_spellid) InterruptSpell(CurrentSpellTypes(i), false); } -void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy, SpellEntry const* triggeredByParent) -{ - SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(spellId); - if (!spellInfo) - { - if (triggeredByAura) - sLog.outError("CastSpell: unknown spell id %i by caster: %s triggered by aura %u (eff %u)", spellId, GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); - else - sLog.outError("CastSpell: unknown spell id %i by caster: %s", spellId, GetGuidStr().c_str()); - return; - } - CastSpell(Victim, spellInfo, triggered, castItem, triggeredByAura, originalCaster, triggeredBy, triggeredByParent); -} -void Unit::CastSpell(Unit* Victim, SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy, SpellEntry const* triggeredByParent) -{ - if (!spellInfo) - { - if (triggeredByAura) - sLog.outError("CastSpell: unknown spell by caster: %s triggered by aura %u (eff %u)", GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); - else - sLog.outError("CastSpell: unknown spell by caster: %s", GetGuidStr().c_str()); - return; - } - if (castItem) - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "WORLD: cast Item spellId - %i", spellInfo->Id); - if (triggeredByAura) - { - if (!originalCaster) - originalCaster = triggeredByAura->GetCasterGuid(); - triggeredBy = triggeredByAura->GetSpellProto(); - } - Spell *spell = new Spell(this, spellInfo, triggered, originalCaster, triggeredBy, NULL, triggeredByParent); - SpellCastTargets targets; - // Don't set unit target on destination target based spells, otherwise the spell will cancel - // as soon as the target dies or leaves the area of the effect - if (spellInfo->Targets & TARGET_FLAG_DEST_LOCATION) - targets.setDestination(Victim->GetPositionX(), Victim->GetPositionY(), Victim->GetPositionZ()); - else - targets.setUnitTarget(Victim); - - if (spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) - if (WorldObject* caster = spell->GetCastingObject()) - targets.setSource(caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ()); - - spell->SetCastItem(castItem); - spell->prepare(std::move(targets), triggeredByAura); -} - -void Unit::CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy) -{ - SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(spellId); - - if (!spellInfo) - { - if (triggeredByAura) - sLog.outError("CastCustomSpell: unknown spell id %i by caster: %s triggered by aura %u (eff %u)", spellId, GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); - else - sLog.outError("CastCustomSpell: unknown spell id %i by caster: %s", spellId, GetGuidStr().c_str()); - return; - } - - CastCustomSpell(Victim, spellInfo, bp0, bp1, bp2, triggered, castItem, triggeredByAura, originalCaster, triggeredBy); -} - -void Unit::CastCustomSpell(Unit* Victim, SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy) -{ - if (!spellInfo) - { - if (triggeredByAura) - sLog.outError("CastCustomSpell: unknown spell by caster: %s triggered by aura %u (eff %u)", GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); - else - sLog.outError("CastCustomSpell: unknown spell by caster: %s", GetGuidStr().c_str()); - return; - } - - if (castItem) - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "WORLD: cast Item spellId - %i", spellInfo->Id); - - if (triggeredByAura) - { - if (!originalCaster) - originalCaster = triggeredByAura->GetCasterGuid(); - - triggeredBy = triggeredByAura->GetSpellProto(); - } - - Spell *spell = new Spell(this, spellInfo, triggered, originalCaster, triggeredBy); - - if (bp0) - spell->m_currentBasePoints[EFFECT_INDEX_0] = *bp0; - - if (bp1) - spell->m_currentBasePoints[EFFECT_INDEX_1] = *bp1; - - if (bp2) - spell->m_currentBasePoints[EFFECT_INDEX_2] = *bp2; - - SpellCastTargets targets; - targets.setUnitTarget(Victim); - spell->SetCastItem(castItem); - spell->prepare(std::move(targets), triggeredByAura); -} - -// used for scripting -void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy) -{ - SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(spellId); - - if (!spellInfo) - { - if (triggeredByAura) - sLog.outError("CastSpell(x,y,z): unknown spell id %i by caster: %s triggered by aura %u (eff %u)", spellId, GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); - else - sLog.outError("CastSpell(x,y,z): unknown spell id %i by caster: %s", spellId, GetGuidStr().c_str()); - return; - } - - CastSpell(x, y, z, spellInfo, triggered, castItem, triggeredByAura, originalCaster, triggeredBy); -} - -// used for scripting -void Unit::CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggeredByAura, ObjectGuid originalCaster, SpellEntry const* triggeredBy) -{ - if (!spellInfo) - { - if (triggeredByAura) - sLog.outError("CastSpell(x,y,z): unknown spell by caster: %s triggered by aura %u (eff %u)", GetGuidStr().c_str(), triggeredByAura->GetId(), triggeredByAura->GetEffIndex()); - else - sLog.outError("CastSpell(x,y,z): unknown spell by caster: %s", GetGuidStr().c_str()); - return; - } - - if (castItem) - DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "WORLD: cast Item spellId - %i", spellInfo->Id); - - if (triggeredByAura) - { - if (!originalCaster) - originalCaster = triggeredByAura->GetCasterGuid(); - - triggeredBy = triggeredByAura->GetSpellProto(); - } - - Spell *spell = new Spell(this, spellInfo, triggered, originalCaster, triggeredBy); - - SpellCastTargets targets; - targets.setDestination(x, y, z); - spell->SetCastItem(castItem); - spell->prepare(std::move(targets), triggeredByAura); -} // Obsolete func need remove, here only for comotability vs another patches uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage) @@ -1602,96 +1419,6 @@ uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage return damageInfo.damage; } -void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType, Spell* spell) -{ - SpellSchoolMask damageSchoolMask = GetSchoolMask(damageInfo->school); - Unit *pVictim = damageInfo->target; - if (!pVictim) - return; - - if (damage < 0) - return; - - if (!pVictim->isAlive()) - return; - - // Check spell crit chance - bool crit = IsSpellCrit(pVictim, spellInfo, damageSchoolMask, attackType, spell); - - // damage bonus (per damage class) - switch (spellInfo->DmgClass) - { - // Melee and Ranged Spells - case SPELL_DAMAGE_CLASS_RANGED: - case SPELL_DAMAGE_CLASS_MELEE: - { - //Calculate damage bonus - damage = MeleeDamageBonusDone(pVictim, damage, attackType, spellInfo, SPELL_DIRECT_DAMAGE, 1, spell); - damage = pVictim->MeleeDamageBonusTaken(this, damage, attackType, spellInfo, SPELL_DIRECT_DAMAGE, 1, spell); - - // if crit add critical bonus - if (crit) - { - damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; - damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim, spell); - } - break; - } - // Magical Attacks - case SPELL_DAMAGE_CLASS_NONE: - case SPELL_DAMAGE_CLASS_MAGIC: - { - // Calculate damage bonus - damage = SpellDamageBonusDone(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE, 1, spell); - damage = pVictim->SpellDamageBonusTaken(this, spellInfo, damage, SPELL_DIRECT_DAMAGE, 1, spell); - - // If crit add critical bonus - if (crit) - { - damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; - damage = SpellCriticalDamageBonus(spellInfo, damage, pVictim, spell); - } - break; - } - } - - // damage mitigation - if (damage > 0) - { - // physical damage => armor - if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL && !(spellInfo->Custom & SPELL_CUSTOM_IGNORE_ARMOR)) - damage = CalcArmorReducedDamage(pVictim, damage); - } - else - damage = 0; - damageInfo->damage = damage; -} - -void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss) -{ - if (!damageInfo) - return; - - Unit *pVictim = damageInfo->target; - - if (!pVictim) - return; - - if (!pVictim->isAlive() || pVictim->IsTaxiFlying() || (pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())) - return; - - SpellEntry const *spellProto = sSpellMgr.GetSpellEntry(damageInfo->SpellID); - if (spellProto == nullptr) - { - sLog.outError("Unit::DealSpellDamage have wrong damageInfo->SpellID: %u", damageInfo->SpellID); - return; - } - - // Call default DealDamage (send critical in hit info for threat calculation) - CleanDamage cleanDamage(0, BASE_ATTACK, damageInfo->HitInfo & SPELL_HIT_TYPE_CRIT ? MELEE_HIT_CRIT : MELEE_HIT_NORMAL, damageInfo->absorb, damageInfo->resist); - DealDamage(pVictim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, GetSchoolMask(damageInfo->school), spellProto, durabilityLoss, damageInfo->spell); -} - //TODO for melee need create structure as in void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType) { @@ -1711,19 +1438,16 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MELEE_HIT; damageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_HIT; damageInfo->HitInfo = HITINFO_NORMALSWING2; - SetLastAttackType(0); // Nostalrius : Ivina (lastAttackType) break; case OFF_ATTACK: damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MELEE_HIT | PROC_FLAG_SUCCESSFUL_OFFHAND_HIT; damageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_HIT;//|PROC_FLAG_TAKEN_OFFHAND_HIT // not used damageInfo->HitInfo = HITINFO_LEFTSWING; - SetLastAttackType(1); break; case RANGED_ATTACK: damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT; damageInfo->procVictim = PROC_FLAG_TAKEN_RANGED_HIT; damageInfo->HitInfo = HITINFO_UNK3; // test (dev note: test what? HitInfo flag possibly not confirmed.) - SetLastAttackType(2); break; default: break; @@ -2177,30 +1901,6 @@ void Unit::HandleEmote(uint32 emote_id) } } -uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage) -{ - uint32 newdamage = 0; - float armor = (float)pVictim->GetArmor(); - - // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura - armor += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL); - - if (armor < 0.0f) - armor = 0.0f; - - float tmpvalue = 0.1f * armor / (8.5f * (float)getLevel() + 40); - tmpvalue = tmpvalue / (1.0f + tmpvalue); - - if (tmpvalue < 0.0f) - tmpvalue = 0.0f; - if (tmpvalue > 0.75f) - tmpvalue = 0.75f; - - newdamage = uint32(roundf(damage - (damage * tmpvalue))); - - return (newdamage > 1) ? newdamage : 1; -} - // NOSTALRIUS: Provient d'un tableau du forum (http://www.hostingpics.net/viewer.php?id=995056Resistances.jpg) struct ResistanceValues { @@ -2247,7 +1947,7 @@ static ResistanceValues resistValues[] = {25, 55, 16, 3, 1, 75} // 300 }; -void Unit::CalculateDamageAbsorbAndResist(Unit *pCaster, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, int32 *resist, SpellEntry const* spellProto, Spell* spell) +void Unit::CalculateDamageAbsorbAndResist(WorldObject* pCaster, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, int32 *resist, SpellEntry const* spellProto, Spell* spell) { if (!pCaster || !isAlive() || !damage) return; @@ -2278,7 +1978,7 @@ void Unit::CalculateDamageAbsorbAndResist(Unit *pCaster, SpellSchoolMask schoolM if (canResist || (resistanceChance < 0)) { - const float multiplier = RollMagicResistanceMultiplierOutcomeAgainst(pCaster, resistanceChance, schoolMask, damagetype, spellProto); + const float multiplier = RollMagicResistanceMultiplierOutcomeAgainst(resistanceChance, schoolMask, damagetype, spellProto); *resist = int32(int64(damage) * multiplier); RemainingDamage -= *resist; } @@ -2446,7 +2146,7 @@ void Unit::CalculateDamageAbsorbAndResist(Unit *pCaster, SpellSchoolMask schoolM *absorb = damage - RemainingDamage - *resist; } -void Unit::CalculateAbsorbResistBlock(Unit *pCaster, SpellNonMeleeDamage *damageInfo, SpellEntry const* spellProto, WeaponAttackType attType, Spell* spell) +void Unit::CalculateAbsorbResistBlock(WorldObject* pCaster, SpellNonMeleeDamage *damageInfo, SpellEntry const* spellProto, WeaponAttackType attType, Spell* spell) { bool blocked = false; // Get blocked status @@ -2800,27 +2500,6 @@ uint32 Unit::CalculateDamage(WeaponAttackType attType, bool normalized, uint8 in return urand((uint32)min_damage, (uint32)max_damage); } -float Unit::CalculateLevelPenalty(SpellEntry const* spellProto) const -{ - // Fix Nostalrius. La formule de MaNGOSZero est BC. - uint32 spellLevel = spellProto->spellLevel; - if (spellLevel <= 0 || spellLevel > 20) - return 1.0f; - return 1 - ((20.0f - spellLevel) * 0.0375f); - - /* - float LvlPenalty = 0.0f; - - if(spellLevel < 20) - LvlPenalty = 20.0f - spellLevel * 3.75f; - float LvlFactor = (float(spellLevel) + 6.0f) / float(getLevel()); - if(LvlFactor > 1.0f) - LvlFactor = 1.0f; - - return (100.0f - LvlPenalty) * LvlFactor / 100.0f; - */ -} - void Unit::SendMeleeAttackStart(Unit* pVictim) { WorldPacket data(SMSG_ATTACKSTART, 8 + 8); @@ -2852,7 +2531,7 @@ void Unit::SendMeleeAttackStop(Unit* victim) ((Creature*)victim)->AI().EnterEvadeMode(this);*/ } -bool Unit::IsSpellBlocked(Unit *pCaster, Unit *pVictim, SpellEntry const *spellEntry, WeaponAttackType attackType) +bool Unit::IsSpellBlocked(WorldObject* pCaster, Unit *pVictim, SpellEntry const *spellEntry, WeaponAttackType attackType) { if (!HasInArc(M_PI_F, pCaster)) return false; @@ -2890,199 +2569,7 @@ bool Unit::IsSpellBlocked(Unit *pCaster, Unit *pVictim, SpellEntry const *spellE return roll_chance_f(blockChance); } -// Melee based spells can be miss, parry or dodge on this step -// Crit or block - determined on damage calculation phase! (and can be both in some time) -float Unit::MeleeSpellMissChance(Unit* pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const* spell, Spell* spellPtr) -{ - if (!pVictim || !pVictim->IsStandingUp()) - return 0.0f; - - // Calculate hit chance (more correct for chance mod) - float hitChance = 0.0f; - float missChance = 0.0f; - - // PvP - PvE melee chances - if (pVictim->GetTypeId() == TYPEID_PLAYER) - missChance = 5.0f - skillDiff * 0.04f; - else if (skillDiff < -10) - missChance = 5.0f - skillDiff * 0.2f; - else - missChance = 5.0f - skillDiff * 0.1f; - - // Low level reduction - if (!pVictim->IsPlayer() && pVictim->getLevel() < 10) - missChance *= pVictim->getLevel() / 10.0f; - - // Spellmod from SPELLMOD_RESIST_MISS_CHANCE - if (Player * modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, hitChance, spellPtr); - - // Bonuses from attacker aura and ratings - if (attType == RANGED_ATTACK) - hitChance += m_modRangedHitChance; - else - hitChance += m_modMeleeHitChance; - - // There is some code in 1.12 that explicitly adds a modifier that causes the first 1% of +hit gained from - // talents or gear to be ignored against monsters with more than 10 Defense Skill above the attacking player’s Weapon Skill. - // https://us.forums.blizzard.com/en/wow/t/bug-hit-tables/185675/33 - if (skillDiff < -10 && hitChance > 0) - hitChance -= 1.0f; - - // Hit chance depends from victim auras - if (attType == RANGED_ATTACK) - hitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE); - else - hitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE); - - missChance -= hitChance; - - // Limit miss chance from 0 to 60% - if (missChance < 0.0f) - return 0.0f; - if (missChance > 60.0f) - return 60.0f; - return missChance; -} - -// Melee based spells hit result calculations -SpellMissInfo Unit::MeleeSpellHitResult(Unit* pVictim, SpellEntry const* spell, Spell* spellPtr) -{ - WeaponAttackType attType = BASE_ATTACK; - - if (spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED) - attType = RANGED_ATTACK; - - // Warrior spell Execute (5308) should never dodge, miss, resist ... Only the trigger can (20647) - if (spell->IsFitToFamily() && spell->Id != 20647) - return SPELL_MISS_NONE; - - // bonus from skills is 0.04% per skill Diff - int32 attackerWeaponSkill = (spell->EquippedItemClass == ITEM_CLASS_WEAPON) ? int32(GetWeaponSkillValue(attType, pVictim)) : GetSkillMaxForLevel(); - int32 skillDiff = attackerWeaponSkill - int32(pVictim->GetSkillMaxForLevel(this)); - int32 fullSkillDiff = attackerWeaponSkill - int32(pVictim->GetDefenseSkillValue(this)); - int32 minWeaponSkill = GetSkillMaxForLevel(pVictim) < attackerWeaponSkill ? GetSkillMaxForLevel(pVictim) : attackerWeaponSkill; - int32 cappedSkillDiff = minWeaponSkill - pVictim->GetSkillMaxForLevel(this); - - uint32 roll = urand(0, 9999); - - uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell, spellPtr) * 100.0f); - // Roll miss - uint32 tmp = spell->AttributesEx3 & SPELL_ATTR_EX3_CANT_MISS ? 0 : missChance; - if (roll < tmp) - return SPELL_MISS_MISS; - - // Chance resist mechanic for spell (effect resistance handled later) - int32 resist_mech = 0; - if (spell->Mechanic) - resist_mech = pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, spell->Mechanic) * 100; - // Roll chance - tmp += resist_mech; - if (roll < tmp) - return SPELL_MISS_RESIST; - - bool canDodge = true; - bool canParry = true; - - // Same spells cannot be parry/dodge - if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK) - return SPELL_MISS_NONE; - - // Ranged attack cannot be parry/dodge - if (attType == RANGED_ATTACK) - return SPELL_MISS_NONE; - - bool from_behind = !pVictim->HasInArc(M_PI_F, this); - - // Check for attack from behind - if (from_behind) - { - // Can`t dodge from behind in PvP (but its possible in PvE) - if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER) - canDodge = false; - // Can`t parry - canParry = false; - } - // Check creatures flags_extra for disable parry - if (pVictim->GetTypeId() == TYPEID_UNIT) - { - uint32 flagEx = ((Creature*)pVictim)->GetCreatureInfo()->flags_extra; - if (flagEx & CREATURE_FLAG_EXTRA_NO_PARRY) - canParry = false; - } - - // Check if the player can parry - if (pVictim->GetTypeId() == TYPEID_PLAYER) - { - if (!((Player*)pVictim)->CanParry()) - canParry = false; - } - - if (canDodge) - { - // Roll dodge - int32 dodgeModifier = pVictim->IsPlayer() ? skillDiff * 4 : skillDiff * 10; - int32 dodgeChance = int32(pVictim->GetUnitDodgeChance() * 100.0f) - dodgeModifier; - - if (dodgeChance < 0) - dodgeChance = 0; - - // Low level reduction - if (!pVictim->IsPlayer() && pVictim->getLevel() < 10) - dodgeChance *= pVictim->getLevel() / 10.0f; - - tmp += dodgeChance; - if (roll < tmp) - return SPELL_MISS_DODGE; - } - - if (canParry) - { - // Roll parry - int32 parryModifier = pVictim->IsPlayer() ? skillDiff * 4 : cappedSkillDiff < -10 ? 60 * cappedSkillDiff : 20 * cappedSkillDiff; - int32 parryChance = int32(pVictim->GetUnitParryChance() * 100.0f) - parryModifier; - - // Can`t parry from behind - if (parryChance < 0) - parryChance = 0; - - // Low level reduction - if (!pVictim->IsPlayer() && pVictim->getLevel() < 10) - parryChance *= pVictim->getLevel() / 10.0f; - - tmp += parryChance; - if (roll < tmp) - return SPELL_MISS_PARRY; - } - - return SPELL_MISS_NONE; -} - -SpellMissInfo Unit::MagicSpellHitResult(Unit* pVictim, SpellEntry const* spell, Spell* spellPtr) -{ - // Can`t miss on dead target (on skinning for example) - if (!pVictim->isAlive()) - return SPELL_MISS_NONE; - - // Spell cannot be resisted (not exist on dbc, custom flag) - if (spell->AttributesEx4 & SPELL_ATTR_EX4_IGNORE_RESISTANCES) - return SPELL_MISS_NONE; - - int32 hitChance = MagicSpellHitChance(pVictim, spell, spellPtr); - int32 missChance = 10000 - hitChance; - int32 rand = irand(0, 10000); - - if ((IsPlayer() && ToPlayer()->HasOption(PLAYER_CHEAT_UNRANDOMIZE)) || - (pVictim->IsPlayer() && pVictim->ToPlayer()->HasOption(PLAYER_CHEAT_UNRANDOMIZE))) - missChance = 0; - - if (rand < missChance) - return SPELL_MISS_RESIST; - - return SPELL_MISS_NONE; -} - -float Unit::RollMagicResistanceMultiplierOutcomeAgainst(const Unit* pCaster, float resistanceChance, SpellSchoolMask schoolMask, DamageEffectType damagetype, SpellEntry const* spellProto) const +float Unit::RollMagicResistanceMultiplierOutcomeAgainst(float resistanceChance, SpellSchoolMask schoolMask, DamageEffectType damagetype, SpellEntry const* spellProto) const { // Magic vulnerability instead of magic resistance: if (resistanceChance < 0) @@ -3148,174 +2635,6 @@ float Unit::RollMagicResistanceMultiplierOutcomeAgainst(const Unit* pCaster, flo return resistCnt; } -float Unit::GetSpellResistChance(Unit const* victim, uint32 schoolMask, bool innateResists) const -{ - // Get base victim resistance for school - float const baseResistance = victim->GetResistance(GetFirstSchoolInMask(SpellSchoolMask(schoolMask))); - // Get attacker spell penetration from SPELL_AURA_MOD_TARGET_RESISTANCE aura - float const selfResistance = GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask); - - float resistModHitChance = baseResistance + selfResistance; - -#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_8_4 - if ((resistModHitChance < 0.0f) && (baseResistance >= 0.0f)) - resistModHitChance = 0.0f; -#endif - - // Magic vulnerability calculation - if (resistModHitChance < 0.0f) - { - // Victim's level based skill, penalize when calculating for low levels (< 20): - const float skill = std::max(GetSkillMaxForLevel(victim), uint16(100)); - // Convert resistance value to vulnerability percentage through comparision with skill - resistModHitChance = (float(resistModHitChance) / skill) * 100; - return (resistModHitChance * 0.01f); - } - - // Computing innate resists, resistance bonus when attacking a creature higher level. Not affected by modifiers. - if (innateResists && victim->GetTypeId() == TYPEID_UNIT) - { - int32 leveldiff = int32(victim->GetLevelForTarget(this)) - int32(GetLevelForTarget(victim)); - resistModHitChance += int32((8.0f * leveldiff) * getLevel() / 63.0f); - } - resistModHitChance *= (float)(0.15f / getLevel()); - - if (resistModHitChance < 0.0f) - resistModHitChance = 0.0f; - if (resistModHitChance > 0.75f) - resistModHitChance = 0.75f; - - return resistModHitChance; -} - -int32 Unit::MagicSpellHitChance(Unit* pVictim, SpellEntry const* spell, Spell* spellPtr) -{ - if (spell->AttributesEx3 & SPELL_ATTR_EX3_CANT_MISS) - return 10000; - - SpellSchoolMask schoolMask = spell->GetSpellSchoolMask(); - // PvP - PvE spell misschances per leveldif > 2 - int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 7 : 11; - int32 leveldif = int32(pVictim->GetLevelForTarget(this)) - int32(GetLevelForTarget(pVictim)); - - // Base hit chance from attacker and victim levels - float modHitChance; - if (leveldif < 3) - modHitChance = 96 - leveldif; - else - modHitChance = 94 - (leveldif - 2) * lchance; - - DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "%s [%u] : Binary [%s]. Base hit chance %f, level diff: %d", spell->SpellName[2].c_str(), spell->Id, spell->IsBinary() ? "YES" : "NO", modHitChance, leveldif); - - // Spellmod from SPELLMOD_RESIST_MISS_CHANCE - if (Player * modOwner = GetSpellModOwner()) - { - modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance, spellPtr); - DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELLMOD_RESIST_MISS_CHANCE : %f", modHitChance); - } - // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras - modHitChance += pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask); - DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE (+ %i) : %f", pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask), modHitChance); - - // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura - if (spell->IsAreaOfEffectSpell()) - { - modHitChance -= pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE); - DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_AOE_AVOIDANCE (- %i) : %f", pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE), modHitChance); - } - - // Chance resist mechanic for spell (effect resistance handled later) - int32 resist_mech = 0; - if (spell->Mechanic) - resist_mech = pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, spell->Mechanic); - // Apply mod - modHitChance -= resist_mech; - DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_MECHANIC_RESISTANCE (- %i) : %f", resist_mech, modHitChance); - - // Chance resist debuff - modHitChance -= pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spell->Dispel)); - DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_DEBUFF_RESISTANCE (- %i) : %f", pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spell->Dispel)), modHitChance); - - // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings - modHitChance += int32(m_modSpellHitChance); - DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "SPELL_AURA_MOD_SPELL_HIT_CHANCE (+ %i) : %f", int32(m_modSpellHitChance), modHitChance); - - // Nostalrius: sorts binaires. - if (spell->IsBinary()) - { - // Get base victim resistance for school - float resistModHitChance = GetSpellResistChance(pVictim, schoolMask, false); - modHitChance *= (1 - resistModHitChance); - DEBUG_UNIT(this, DEBUG_SPELL_COMPUTE_RESISTS, "x %f : HitChance = %f", (1 - resistModHitChance), modHitChance); - } - - int32 HitChance = modHitChance * 100; - if (HitChance < 100) HitChance = 100; - if (HitChance > 9900) HitChance = 9900; - return HitChance; -} -// 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* pVictim, SpellEntry const* spell, SpellEffectIndex effIndex, bool CanReflect, Spell* spellPtr) -{ - // Return evade for units in evade mode - if (pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode()) - return SPELL_MISS_EVADE; - - // Check for immune (use charges) - if (pVictim != this && pVictim->IsImmuneToSpell(spell, pVictim == this)) - 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 (spell->IsPositiveSpell(this, pVictim) || spell->IsPositiveEffect(effIndex, this, pVictim)) - return SPELL_MISS_NONE; - - // Check for immune (use charges) - SpellSchoolMask schoolMask; - if (spellPtr) - schoolMask = spellPtr->m_spellSchoolMask; - else - schoolMask = spell->GetSpellSchoolMask(); - - if (pVictim != this && pVictim->IsImmuneToDamage(schoolMask, spell)) - return SPELL_MISS_IMMUNE; - - // Try victim reflect spell - if (CanReflect) - { - int32 reflectchance = pVictim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS); - Unit::AuraList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL); - for (Unit::AuraList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue & spell->GetSpellSchoolMask()) - reflectchance += (*i)->GetModifier()->m_amount; - if (reflectchance > 0 && roll_chance_i(reflectchance)) - { - // Start triggers for remove charges if need (trigger only for victim, and mark as active spell) - ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT, PROC_EX_REFLECT, 1, BASE_ATTACK, spell); - return SPELL_MISS_REFLECT; - } - } - - switch (spell->DmgClass) - { - case SPELL_DAMAGE_CLASS_NONE: - return SPELL_MISS_NONE; - case SPELL_DAMAGE_CLASS_MAGIC: - return MagicSpellHitResult(pVictim, spell, spellPtr); - case SPELL_DAMAGE_CLASS_MELEE: - case SPELL_DAMAGE_CLASS_RANGED: - return MeleeSpellHitResult(pVictim, spell, spellPtr); - } - return SPELL_MISS_NONE; -} - bool Unit::IsEffectResist(SpellEntry const* spell, int eff) { // Chance resist mechanic @@ -3398,20 +2717,6 @@ float Unit::MeleeMissChanceCalc(const Unit* pVictim, WeaponAttackType attType) c return missChance; } -uint32 Unit::GetDefenseSkillValue(Unit const* target) const -{ - if (GetTypeId() == TYPEID_PLAYER) - { - // in PvP use full skill instead current skill value - uint32 value = (target && target->GetTypeId() == TYPEID_PLAYER) - ? ((Player*)this)->GetSkillMax(SKILL_DEFENSE) - : ((Player*)this)->GetSkillValue(SKILL_DEFENSE); - return value; - } - else - return GetUnitMeleeSkill(target); -} - float Unit::GetUnitDodgeChance() const { if (IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_PENDING_STUNNED)) @@ -3540,35 +2845,6 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* pVict return crit; } -uint32 Unit::GetWeaponSkillValue(WeaponAttackType attType, Unit const* target) const -{ - uint32 value = 0; - if (GetTypeId() == TYPEID_PLAYER) - { - Item* item = ((Player*)this)->GetWeaponForAttack(attType, true, true); - - // feral or unarmed skill only for base attack - if (attType != BASE_ATTACK && !item) - return 0; - - if (IsInFeralForm()) - return GetSkillMaxForLevel(); // always maximized SKILL_FERAL_COMBAT in fact - - // weapon skill or (unarmed for base attack) - uint32 skill = item ? item->GetSkill() : SKILL_UNARMED; - -// Daemon: pas en preBC ! -// // in PvP use full skill instead current skill value -// value = (target && target->GetTypeId() == TYPEID_PLAYER) -// ? ((Player*)this)->GetSkillMax(skill) -// : ((Player*)this)->GetSkillValue(skill); - value = ToPlayer()->GetSkillValue(skill); - } - else - value = GetUnitMeleeSkill(target); - return value; -} - void Unit::_UpdateSpells(uint32 time) { if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) @@ -3676,198 +2952,6 @@ void Unit::_UpdateAutoRepeatSpell() } } -void Unit::SetCurrentCastedSpell(Spell * pSpell) -{ - MANGOS_ASSERT(pSpell); // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells - - CurrentSpellTypes CSpellType = pSpell->GetCurrentContainer(); - - if (pSpell == m_currentSpells[CSpellType]) return; // avoid breaking self - // break same type spell if it is not delayed - InterruptSpell(CSpellType, false); - - // special breakage effects: - switch (CSpellType) - { - case CURRENT_GENERIC_SPELL: - { - // generic spells always break channeled not delayed spells - InterruptSpell(CURRENT_CHANNELED_SPELL, false); - - // autorepeat breaking - if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) - { - // break autorepeat if not Auto Shot - if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351) - InterruptSpell(CURRENT_AUTOREPEAT_SPELL); - m_AutoRepeatFirstCast = true; - } - } - break; - - case CURRENT_CHANNELED_SPELL: - { - // channel spells always break generic non-delayed and any channeled spells - InterruptSpell(CURRENT_GENERIC_SPELL, false); - InterruptSpell(CURRENT_CHANNELED_SPELL); - - // it also does break autorepeat if not Auto Shot - if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && - m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351) - InterruptSpell(CURRENT_AUTOREPEAT_SPELL); - } - break; - - case CURRENT_AUTOREPEAT_SPELL: - { - // only Auto Shoot does not break anything - if (pSpell->m_spellInfo->Category == 351) - { - // generic autorepeats break generic non-delayed and channeled non-delayed spells - InterruptSpell(CURRENT_GENERIC_SPELL, false); - InterruptSpell(CURRENT_CHANNELED_SPELL, false); - } - // special action: set first cast flag - m_AutoRepeatFirstCast = true; - } - break; - - default: - { - // other spell types don't break anything now - } break; - } - - // current spell (if it is still here) may be safely deleted now - if (m_currentSpells[CSpellType]) - m_currentSpells[CSpellType]->SetReferencedFromCurrent(false); - - // set new current spell - m_currentSpells[CSpellType] = pSpell; - pSpell->SetReferencedFromCurrent(true); - - pSpell->m_selfContainer = &(m_currentSpells[pSpell->GetCurrentContainer()]); -} - -void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed) -{ - MANGOS_ASSERT(spellType < CURRENT_MAX_SPELL); - - if (m_currentSpells[spellType] && (withDelayed || m_currentSpells[spellType]->getState() != SPELL_STATE_DELAYED)) - { - // send autorepeat cancel message for autorepeat spells - if (spellType == CURRENT_AUTOREPEAT_SPELL) - { - if (GetTypeId() == TYPEID_PLAYER) - ((Player*)this)->SendAutoRepeatCancel(); - } - - if (m_currentSpells[spellType]->getState() != SPELL_STATE_FINISHED) - m_currentSpells[spellType]->cancel(); - - // cancel can interrupt spell already (caster cancel ->target aura remove -> caster iterrupt) - if (m_currentSpells[spellType]) - { - m_currentSpells[spellType]->SetReferencedFromCurrent(false); - m_currentSpells[spellType] = nullptr; - } - } -} - -void Unit::FinishSpell(CurrentSpellTypes spellType, bool ok /*= true*/) -{ - Spell* spell = m_currentSpells[spellType]; - if (!spell) - return; - - if (spellType == CURRENT_CHANNELED_SPELL) - spell->SendChannelUpdate(0); - - spell->finish(ok); -} - - -bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skipAutorepeat) const -{ - // We don't do loop here to explicitly show that melee spell is excluded. - // Maybe later some special spells will be excluded too. - - // generic spells are casted when they are not finished and not delayed - if (m_currentSpells[CURRENT_GENERIC_SPELL] && - (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) && - (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED)) - return (true); - - // channeled spells may be delayed, but they are still considered casted - else if (!skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] && - (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED)) - return (true); - - // autorepeat spells may be finished or delayed, but they are still considered casted - else if (!skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) - return (true); - - return (false); -} - -bool Unit::IsNextSwingSpellCasted() const -{ - if (m_currentSpells[CURRENT_MELEE_SPELL] && m_currentSpells[CURRENT_MELEE_SPELL]->IsNextMeleeSwingSpell()) - return (true); - - return (false); -} - -bool Unit::IsNoMovementSpellCasted() const -{ - if (m_currentSpells[CURRENT_GENERIC_SPELL] && - (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) && - m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED && - m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) - return (true); - else if (m_currentSpells[CURRENT_CHANNELED_SPELL] && - m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED && - m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) - return (true); - // don't need to check for AUTOREPEAT_SPELL - - return (false); -} - -void Unit::InterruptSpellsWithInterruptFlags(uint32 flags, uint32 except) -{ - for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; ++i) - if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(i))) - if (spell->getState() == SPELL_STATE_PREPARING && spell->GetCastedTime()) - if (!spell->IsNextMeleeSwingSpell() && !spell->IsAutoRepeat() && !spell->IsTriggered() && (spell->m_spellInfo->InterruptFlags & flags) && spell->m_spellInfo->Id != except) - InterruptSpell(CurrentSpellTypes(i)); -} - - -void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id) -{ - // generic spells are interrupted if they are not finished or delayed - if (m_currentSpells[CURRENT_GENERIC_SPELL] && (!spell_id || m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Id == spell_id)) - InterruptSpell(CURRENT_GENERIC_SPELL, withDelayed); - - // autorepeat spells are interrupted if they are not finished or delayed - if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id == spell_id)) - InterruptSpell(CURRENT_AUTOREPEAT_SPELL, withDelayed); - - // channeled spells are interrupted if they are not finished, even if they are delayed - if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id == spell_id)) - InterruptSpell(CURRENT_CHANNELED_SPELL, true); -} - - -Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const -{ - for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i) - if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id == spell_id) - return m_currentSpells[i]; - return nullptr; -} - void Unit::SetInFront(Unit const* pTarget) { SetOrientation(GetAngle(pTarget)); @@ -4669,7 +3753,7 @@ void Unit::RemoveSingleAuraFromSpellAuraHolder(uint32 spellId, SpellEffectIndex } } -void Unit::RemoveAuraHolderDueToSpellByDispel(uint32 spellId, uint32 stackAmount, ObjectGuid casterGuid, Unit *dispeller) +void Unit::RemoveAuraHolderDueToSpellByDispel(uint32 spellId, uint32 stackAmount, ObjectGuid casterGuid) { RemoveAuraHolderFromStack(spellId, stackAmount, casterGuid, AURA_REMOVE_BY_DISPEL); } @@ -5162,94 +4246,6 @@ bool Unit::HasAura(uint32 spellId, SpellEffectIndex effIndex) const return false; } -void Unit::AddDynObject(DynamicObject* dynObj) -{ - m_dynObjGUIDs.push_back(dynObj->GetObjectGuid()); - dynObj->SetWorldMask(GetWorldMask()); // Nostalrius : phasing -} - -void Unit::RemoveDynObject(uint32 spellid) -{ - if (m_dynObjGUIDs.empty()) - return; - for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) - { - DynamicObject* dynObj = GetMap()->GetDynamicObject(*i); - if (!dynObj) - i = m_dynObjGUIDs.erase(i); - else if (spellid == 0 || dynObj->GetSpellId() == spellid) - { - dynObj->Delete(); - i = m_dynObjGUIDs.erase(i); - } - else - ++i; - } -} - -void Unit::RemoveAllDynObjects() -{ - while (!m_dynObjGUIDs.empty()) - { - if (DynamicObject* dynObj = GetMap()->GetDynamicObject(*m_dynObjGUIDs.begin())) - dynObj->Delete(); - m_dynObjGUIDs.erase(m_dynObjGUIDs.begin()); - } -} - -void Unit::GetDynObjects(uint32 spellId, SpellEffectIndex effectIndex, std::vector& dynObjsOut) -{ - for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) - { - DynamicObject* dynObj = GetMap()->GetDynamicObject(*i); - if (!dynObj) - { - i = m_dynObjGUIDs.erase(i); - continue; - } - - if (dynObj->GetSpellId() == spellId && dynObj->GetEffIndex() == effectIndex) - dynObjsOut.push_back(dynObj); - ++i; - } -} - -DynamicObject * Unit::GetDynObject(uint32 spellId, SpellEffectIndex effIndex) -{ - for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) - { - DynamicObject* dynObj = GetMap()->GetDynamicObject(*i); - if (!dynObj) - { - i = m_dynObjGUIDs.erase(i); - continue; - } - - if (dynObj->GetSpellId() == spellId && dynObj->GetEffIndex() == effIndex) - return dynObj; - ++i; - } - return nullptr; -} - -DynamicObject * Unit::GetDynObject(uint32 spellId) -{ - for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) - { - DynamicObject* dynObj = GetMap()->GetDynamicObject(*i); - if (!dynObj) - { - i = m_dynObjGUIDs.erase(i); - continue; - } - - if (dynObj->GetSpellId() == spellId) - return dynObj; - ++i; - } - return nullptr; -} - GameObject* Unit::GetGameObject(uint32 spellId) const { for (GameObjectList::const_iterator i = m_gameObj.begin(); i != m_gameObj.end(); ++i) @@ -5340,47 +4336,6 @@ void Unit::RemoveAllGameObjects() } } -void Unit::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log) -{ - WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG, (16 + 4 + 4 + 1 + 4 + 4 + 1 + 1 + 4 + 4 + 1)); // we guess size -#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_8_4 - data << log->target->GetPackGUID(); - data << log->attacker->GetPackGUID(); -#else - data << log->target->GetGUID(); - data << log->attacker->GetGUID(); -#endif - data << uint32(log->SpellID); - data << uint32(log->damage); // damage amount - data << uint8(log->school); // damage school - data << uint32(log->absorb); // AbsorbedDamage - data << int32(log->resist); // resist - data << uint8(log->periodicLog); // if 1, then client show spell name (example: %s's ranged shot hit %s for %u school or %s suffers %u school damage from %s's spell_name - data << uint8(log->unused); // unused - data << uint32(log->blocked); // blocked - data << uint32(log->HitInfo); - data << uint8(0); // flag to use extend data - SendMessageToSet(&data, true); -} - -void Unit::SendSpellNonMeleeDamageLog(Unit* target, uint32 spellID, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 absorbedDamage, int32 resist, bool isPeriodic, uint32 blocked, bool criticalHit, bool split) -{ - SpellNonMeleeDamage log(this, target, spellID, GetFirstSchoolInMask(damageSchoolMask)); - log.damage = damage; - log.damage += (resist < 0 ? uint32(std::abs(resist)) : 0); - log.damage -= (absorbedDamage + (resist > 0 ? uint32(resist) : 0) + blocked); - log.absorb = absorbedDamage; - log.resist = resist; - log.periodicLog = isPeriodic; - log.blocked = blocked; - log.HitInfo = 0; - if (criticalHit) - log.HitInfo |= SPELL_HIT_TYPE_CRIT; - if (split) - log.HitInfo |= SPELL_HIT_TYPE_SPLIT; - SendSpellNonMeleeDamageLog(&log); -} - void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo *pInfo, AuraType auraTypeOverride) { Aura *aura = pInfo->aura; @@ -5449,35 +4404,6 @@ public: typedef std::list RemoveSpellList; -void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellEntry const *procSpell, Spell* spell) -{ - if ((pVictim && !IsInMap(pVictim)) || !IsInWorld()) - return; - - ProcTriggeredList procTriggered; - - // Not much to do if no flags are set. - if (procAttacker) - ProcDamageAndSpellFor(false, pVictim, procAttacker, procExtra, attType, procSpell, amount, procTriggered, spell); - - // Now go on with a victim's events'n'auras - // Not much to do if no flags are set or there is no victim - if (pVictim && pVictim->isAlive() && procVictim) - - // http://blue.cardplace.com/cache/wow-paladin/1069149.htm - // "Charges will not generate off auto attacks or npc attacks by trying" - // "to sit down and force a crit. However, ability crits from physical" - // "abilities such as Sinister Strike, Hamstring, Auto-shot, Aimed shot," - // "etc will generate a charge if you're sitting." -#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_7_1 - pVictim->ProcDamageAndSpellFor(true, this, procVictim, !procSpell && !pVictim->IsStandingUpForProc() ? procExtra & ~PROC_EX_CRITICAL_HIT : procExtra, attType, procSpell, amount, procTriggered, spell); -#else - pVictim->ProcDamageAndSpellFor(true, this, procVictim, procExtra, attType, procSpell, amount, procTriggered, spell); -#endif - - HandleTriggers(pVictim, procExtra, amount, procSpell, procTriggered); -} - void Unit::HandleTriggers(Unit *pVictim, uint32 procExtra, uint32 amount, SpellEntry const *procSpell, ProcTriggeredList const& procTriggered) { RemoveSpellList removedSpells; @@ -5577,21 +4503,6 @@ void Unit::HandleTriggers(Unit *pVictim, uint32 procExtra, uint32 amount, SpellE } } -void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo) -{ - WorldPacket data(SMSG_SPELLLOGMISS, (4 + 8 + 1 + 4 + 8 + 1)); - data << uint32(spellID); - data << GetObjectGuid(); - data << uint8(0); // unk8 - data << uint32(1); // target count - // for(i = 0; i < target count; ++i) - data << target->GetObjectGuid(); // target GUID - data << uint8(missInfo); - // Nostalrius: + 2 * float if unk8=1 - // end loop - SendObjectMessageToSet(&data, true); -} - void Unit::SendAttackStateUpdate(CalcDamageInfo *damageInfo) { DEBUG_FILTER_LOG(LOG_FILTER_COMBAT, "WORLD: Sending SMSG_ATTACKERSTATEUPDATE"); @@ -5727,172 +4638,14 @@ void Unit::setPowerType(Powers new_powertype) } } -FactionTemplateEntry const* Unit::getFactionTemplateEntry() const +bool Unit::IsHostileTo(WorldObject const* target) const { - FactionTemplateEntry const* entry = sObjectMgr.GetFactionTemplateEntry(getFaction()); - if (!entry) - { - static ObjectGuid guid; // prevent repeating spam same faction problem - - if (GetObjectGuid() != guid) - { - sLog.outError("%s have invalid faction (faction template id) #%u", GetGuidStr().c_str(), getFaction()); - guid = GetObjectGuid(); - } - } - return entry; + return GetReactionTo(target) <= REP_HOSTILE; } -// function based on function Unit::UnitReaction from 13850 client -ReputationRank Unit::GetReactionTo(Unit const* target) const +bool Unit::IsFriendlyTo(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 (selfPlayerOwner->IsGameMaster()) - return REP_NEUTRAL; - if (FactionTemplateEntry const* targetFactionTemplateEntry = target->getFactionTemplateEntry()) - if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) - return *repRank; - } - else if (targetPlayerOwner) - { - if (targetPlayerOwner->IsGameMaster()) - return REP_NEUTRAL; - 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 && !selfPlayerOwner->duel->finished) - 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); - - // Sanctuary - if (selfPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && targetPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY)) - return REP_FRIENDLY; - - // Nostalrius: Hackfix because UNIT_BYTE2_FLAG_FFA_PVP is not implemented yet. - if (selfPlayerOwner->IsFFAPvP() && targetPlayerOwner->IsFFAPvP()) - return REP_HOSTILE; - } - - // check FFA_PVP - not implemented that way on MaNGOS :/ - /* - if (GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_FFA_PVP - && target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_FFA_PVP) - return REP_HOSTILE; - */ - } - if (selfPlayerOwner) - { - if (FactionTemplateEntry const* targetFactionTemplateEntry = target->getFactionTemplateEntry()) - { - if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry)) - return *repRank; - if (FactionEntry const* targetFactionEntry = sObjectMgr.GetFactionEntry(targetFactionTemplateEntry->faction)) - { - if (targetFactionEntry->CanHaveReputation()) - { - // check contested flags - if (targetFactionTemplateEntry->factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD - && selfPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) - return REP_HOSTILE; - - // if faction has reputation, hostile state depends only from AtWar state - if (FactionState const* factionState = selfPlayerOwner->GetReputationMgr().GetState(targetFactionEntry)) - if (factionState->Flags & FACTION_FLAG_AT_WAR) - 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 (!targetFactionTemplateEntry) - return REP_NEUTRAL; - - if (Player const* targetPlayerOwner = target->GetAffectingPlayer()) - { - // check contested flags - if (factionTemplateEntry->IsContestedGuardFaction() - && targetPlayerOwner->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) - return REP_HOSTILE; - if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry)) - return *repRank; - if (FactionEntry const* factionEntry = sObjectMgr.GetFactionEntry(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 (FactionState const* factionState = targetPlayerOwner->GetReputationMgr().GetState(factionEntry)) - if (factionState->Flags & FACTION_FLAG_AT_WAR) - repRank = std::min(REP_NEUTRAL, repRank); - return repRank; - } - } - } - - // common faction based check - if (factionTemplateEntry->IsHostileTo(*targetFactionTemplateEntry)) - return REP_HOSTILE; - if (factionTemplateEntry->IsFriendlyTo(*targetFactionTemplateEntry)) - return REP_FRIENDLY; - if (targetFactionTemplateEntry->IsFriendlyTo(*factionTemplateEntry)) - return REP_FRIENDLY; - if (factionTemplateEntry->factionFlags & FACTION_TEMPLATE_HOSTILE_BY_DEFAULT) - return REP_HOSTILE; - // neutral by default - return REP_NEUTRAL; -} - -bool Unit::IsHostileTo(Unit const* unit) const -{ - return GetReactionTo(unit) <= REP_HOSTILE; -} - -bool Unit::IsFriendlyTo(Unit const* unit) const -{ - return GetReactionTo(unit) >= REP_FRIENDLY; + return GetReactionTo(target) >= REP_FRIENDLY; } bool Unit::IsHostileToPlayers() const @@ -6285,27 +5038,6 @@ void Unit::RemoveCharmAuras() RemoveSpellsCausingAura(SPELL_AURA_AOE_CHARM); } -float Unit::GetLeewayBonusRange(const Unit* target, bool ability) const -{ - if (IsPlayer() && target) - { - if (ability) - return (GetXZFlagBasedSpeed() > LEEWAY_MIN_MOVE_SPEED && target->GetXZFlagBasedSpeed() > LEEWAY_MIN_MOVE_SPEED) ? LEEWAY_BONUS_RANGE : 0.0f; - else // auto attacks do not check speed, only flags - return (IsMovingButNotWalking() && target->IsMovingButNotWalking()) ? LEEWAY_BONUS_RANGE : 0.0f; - } - - return 0.0f; -} - -float Unit::GetLeewayBonusRadius() const -{ - if (IsPlayer() && (GetXZFlagBasedSpeed() > LEEWAY_MIN_MOVE_SPEED || ToPlayer()->m_movementInfo.HasMovementFlag(MOVEFLAG_JUMPING))) - return LEEWAY_BONUS_RANGE; - - return 0.0f; -} - void Unit::SetPet(Pet* pet) { SetPetGuid(pet ? pet->GetObjectGuid() : ObjectGuid()); @@ -6488,102 +5220,6 @@ bool Unit::UnsummonOldPetBeforeNewSummon(uint32 newPetEntry) return true; } -int32 Unit::DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellProto, bool critical) -{ - // Script Event HealedBy - if (pVictim->AI()) - pVictim->AI()->HealedBy(this, addhealth); - - int32 gain = pVictim->ModifyHealth(int32(addhealth)); - - Unit* unit = this; - - if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsTotem() && ((Totem*)this)->GetTotemType() != TOTEM_STATUE) - unit = GetOwner(); - - if (!unit) - return 0; - - if (unit->GetTypeId() == TYPEID_PLAYER || pVictim->GetTypeId() == TYPEID_PLAYER) - unit->SendHealSpellLog(pVictim, spellProto->Id, addhealth, critical); - - return gain; -} - -Unit* Unit::SelectMagnetTarget(Unit *victim, Spell* spell, SpellEffectIndex eff) -{ - if (!victim) - return nullptr; - - SpellEntry const* pProto = spell->m_spellInfo; - if (!pProto) return nullptr; - // Example spell: Cause Insanity (Hakkar) - if (pProto->AttributesEx & SPELL_ATTR_EX_CANT_BE_REDIRECTED) - return victim; - // Magic case - - if (pProto->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) - return victim; - - if ((pProto->DmgClass == SPELL_DAMAGE_CLASS_MAGIC || pProto->SpellVisual == 7250) && pProto->Dispel != DISPEL_POISON && !(pProto->Attributes & 0x10)) - { - Unit::AuraList const& magnetAuras = victim->GetAurasByType(SPELL_AURA_SPELL_MAGNET); - for (Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr) - { - if (Unit* magnet = (*itr)->GetCaster()) - { - if (magnet->isAlive() && magnet->IsWithinLOSInMap(this) && spell->CheckTarget(magnet, eff)) - { - if (SpellAuraHolder *holder = (*itr)->GetHolder()) - if (holder->DropAuraCharge()) - { - magnet->RemoveAurasDueToSpell(holder->GetId()); // Remove from grounding totem also - victim->RemoveSpellAuraHolder(holder); - } - return magnet; - } - } - } - } - - return victim; -} - -void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical) -{ -#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_9_4 - // we guess size - WorldPacket data(SMSG_SPELLHEALLOG, (8 + 8 + 4 + 4 + 1)); - data << pVictim->GetPackGUID(); - data << GetPackGUID(); - data << uint32(SpellID); - data << uint32(Damage); - data << uint8(critical ? 1 : 0); - // data << uint8(0); // [-ZERO] - SendMessageToSet(&data, true); -#endif -} - -void Unit::SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype) -{ -#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_9_4 - WorldPacket data(SMSG_SPELLENERGIZELOG, (8 + 8 + 4 + 4 + 4 + 1)); - data << pVictim->GetPackGUID(); - data << GetPackGUID(); - data << uint32(SpellID); - data << uint32(powertype); - data << uint32(Damage); - SendMessageToSet(&data, true); -#endif -} - -void Unit::EnergizeBySpell(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype) -{ - SendEnergizeSpellLog(pVictim, SpellID, Damage, powertype); - // needs to be called after sending spell log - pVictim->ModifyPower(powertype, Damage); -} - void Unit::SendEnvironmentalDamageLog(uint8 type, uint32 damage, uint32 absorb, int32 resist) const { WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (8 + 1 + 4 + 4 + 4)); @@ -6595,194 +5231,11 @@ void Unit::SendEnvironmentalDamageLog(uint8 type, uint32 damage, uint32 absorb, SendMessageToSet(&data, true); } -int32 Unit::SpellBonusWithCoeffs(SpellEntry const *spellProto, int32 total, int32 benefit, int32 ap_benefit, DamageEffectType damagetype, bool donePart, Unit *pCaster, Spell* spell) -{ - // Distribute Damage over multiple effects, reduce by AoE - float coeff = 0.0f; - - // Not apply this to creature casted spells - // Daemon: n'importe quoi. Et apres on se demande pourquoi les degats du sceau du croise sont abuses ... - //if (GetTypeId()==TYPEID_UNIT && !((Creature*)this)->IsPet()) - // coeff = 1.0f; - // Check for table values - if (SpellBonusEntry const* bonus = sSpellMgr.GetSpellBonusData(spellProto->Id)) - { - coeff = damagetype == DOT ? bonus->dot_damage : bonus->direct_damage; - - // apply ap bonus at done part calculation only (it flat total mod so common with taken) - if (donePart && (bonus->ap_bonus || bonus->ap_dot_bonus)) - { - float ap_bonus = damagetype == DOT ? bonus->ap_dot_bonus : bonus->ap_bonus; - - total += int32(ap_bonus * (GetTotalAttackPowerValue(spellProto->IsSpellRequiresRangedAP() ? RANGED_ATTACK : BASE_ATTACK) + ap_benefit)); - } - } - // Calculate default coefficient - else if (benefit) - coeff = spellProto->CalculateDefaultCoefficient(damagetype); - - if (benefit) - { - // Calculate level penalty - float LvlPenalty = CalculateLevelPenalty(spellProto); - - // Calculate custom coefficient - coeff = spellProto->CalculateCustomCoefficient(pCaster, damagetype, coeff, spell, donePart); - - // Spellmod SpellDamage - if (Player* modOwner = GetSpellModOwner()) - { - coeff *= 100.0f; - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_SPELL_BONUS_DAMAGE, coeff, spell); - coeff /= 100.0f; - } - // Nostalrius. - bool bUsePenalty = true; - // Flash of Light - if (spellProto->Id == 19993) - { - bUsePenalty = false; - if (HasAura(28853)) total += 53.0f; // Libram of Divinity - if (HasAura(28851)) total += 83.0f; // Libram of Light - } - - // Dragonbreath Chili - if (spellProto->Id == 15851) - bUsePenalty = false; - - if (bUsePenalty) - total += int32(benefit * coeff * LvlPenalty); - else - total += int32(benefit * coeff); - - } - - return total; -}; - -/** - * Calculates caster part of spell damage bonuses, - * also includes different bonuses dependent from target auras - */ -uint32 Unit::SpellDamageBonusDone(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack, Spell* spell) -{ - if (!spellProto || !pVictim || damagetype == DIRECT_DAMAGE) - return pdamage; - - if (spellProto->Custom & SPELL_CUSTOM_FIXED_DAMAGE) - return pdamage; - - // Ignite damage already includes modifiers - if (spellProto->IsFitToFamily()) - return pdamage; - - // For totems get damage bonus from owner (statue isn't totem in fact) - if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsTotem() && ((Totem*)this)->GetTotemType() != TOTEM_STATUE) - { - if (Unit* owner = GetOwner()) - return owner->SpellDamageBonusDone(pVictim, spellProto, pdamage, damagetype, stack, spell); - } - - float DoneTotalMod = 1.0f; - int32 DoneTotal = 0; - Item* pWeapon = GetTypeId() == TYPEID_PLAYER ? ((Player*)this)->GetWeaponForAttack(BASE_ATTACK, true, false) : NULL; - - // Creature damage - if (GetTypeId() == TYPEID_UNIT && !((Creature*)this)->IsPet()) - DoneTotalMod *= ((Creature*)this)->GetSpellDamageMod(((Creature*)this)->GetCreatureInfo()->rank); - - AuraList const& mModDamagePercentDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) - { - if (((*i)->GetModifier()->m_miscvalue & spellProto->GetSpellSchoolMask()) && - (*i)->GetSpellProto()->EquippedItemClass == -1 && - spellProto->EquippedItemClass == -1 && - // -1 == any item class (not wand then) - (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0) - // 0 == any inventory type (not wand then) - { - DoneTotalMod *= ((*i)->GetModifier()->m_amount + 100.0f) / 100.0f; - } - // Paladin seals benefit from weapon modifiers - else if ((*i)->GetModifier()->m_miscvalue & GetMeleeDamageSchoolMask() && - spellProto->SpellFamilyName == SPELLFAMILY_PALADIN && spellProto->IsFitToFamilyMask() && - (((*i)->GetSpellProto()->EquippedItemClass == -1) || - (pWeapon && pWeapon->IsFitToSpellRequirements((*i)->GetSpellProto())))) - { - DoneTotalMod *= ((*i)->GetModifier()->m_amount + 100.0f) / 100.0f; - } - } - - uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); - - // Add pct bonus from spell damage versus - DoneTotalMod *= GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS, creatureTypeMask); - - // Add flat bonus from spell damage creature - DoneTotal += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE, creatureTypeMask); - - // done scripted mod (take it from owner) - Unit *owner = GetOwner(); - if (!owner) owner = this; - AuraList const& mOverrideClassScript = owner->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for (AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) - { - if (!(*i)->isAffectedOnSpell(spellProto)) - continue; - switch ((*i)->GetModifier()->m_miscvalue) - { - case 4418: // Increased Shock Damage - case 4554: // Increased Lightning Damage - case 4555: // Improved Moonfire - { - DoneTotal += (*i)->GetModifier()->m_amount; - break; - } - } - } - - // Pet happiness increases damage of Hunter pet spells (e.g. Lightning Breath) - if (IsPet() && ((Pet*)this)->getPetType() == HUNTER_PET) - { - if (Pet* pet = ((Pet*)this)) - { - switch (pet->GetHappinessState()) - { - case HAPPY: DoneTotalMod *= 1.25; break; - case CONTENT: break; - case UNHAPPY: DoneTotalMod *= 0.75; break; - } - } - } - - // Done fixed damage bonus auras - int32 DoneAdvertisedBenefit = SpellBaseDamageBonusDone(spellProto->GetSpellSchoolMask()); - - // Add flat bonus from spell damage versus - DoneAdvertisedBenefit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask); - - // Pets just add their bonus damage to their spell damage - // note that their spell damage is just gain of their own auras - if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsPet()) - DoneAdvertisedBenefit += ((Pet*)this)->GetBonusDamage(); - - // apply ap bonus and benefit affected by spell power implicit coeffs and spell level penalties - DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true, this, spell); - - float tmpDamage = (int32(pdamage) + DoneTotal * int32(stack)) * DoneTotalMod; - // apply spellmod to Done damage (flat and pct) - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage, spell); - - DEBUG_UNIT(this, DEBUG_SPELLS_DAMAGE, "SpellDmgBonus[spell=%u]: (base=%u + %i) * %f. SP=%i", spellProto->Id, pdamage, DoneTotal, DoneTotalMod, DoneAdvertisedBenefit); - return tmpDamage > 0 ? uint32(tmpDamage) : 0; -} - /** * Calculates target part of spell damage bonuses, * will be called on each tick for periodic damage over time auras */ -uint32 Unit::SpellDamageBonusTaken(Unit *pCaster, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack, Spell* spell) +uint32 Unit::SpellDamageBonusTaken(WorldObject* pCaster, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack, Spell* spell) { if (!spellProto || !pCaster || damagetype == DIRECT_DAMAGE) return pdamage; @@ -6807,37 +5260,6 @@ uint32 Unit::SpellDamageBonusTaken(Unit *pCaster, SpellEntry const *spellProto, return tmpDamage > 0 ? uint32(tmpDamage) : 0; } -int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) -{ - int32 DoneAdvertisedBenefit = 0; - - // ..done - AuraList const& mDamageDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); - for (AuraList::const_iterator i = mDamageDone.begin(); i != mDamageDone.end(); ++i) - { - if (((*i)->GetModifier()->m_miscvalue & schoolMask) != 0 && - (*i)->GetSpellProto()->EquippedItemClass == -1 && // -1 == any item class (not wand then) - (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0) // 0 == any inventory type (not wand then) - DoneAdvertisedBenefit += (*i)->GetModifier()->m_amount; - } - - if (GetTypeId() == TYPEID_PLAYER) - { - // Damage bonus from stats - AuraList const& mDamageDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT); - for (AuraList::const_iterator i = mDamageDoneOfStatPercent.begin(); i != mDamageDoneOfStatPercent.end(); ++i) - { - if ((*i)->GetModifier()->m_miscvalue & schoolMask) - { - // stat used stored in miscValueB for this aura - Stats usedStat = STAT_SPIRIT; - DoneAdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f); - } - } - } - return DoneAdvertisedBenefit; -} - int32 Unit::SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask) { int32 TakenAdvertisedBenefit = 0; @@ -6853,7 +5275,7 @@ int32 Unit::SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask) return TakenAdvertisedBenefit; } -bool Unit::IsSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType, Spell* spell) +bool Unit::IsSpellCrit(Unit const* pVictim, SpellEntry const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType, Spell* spell) const { ASSERT(pVictim); // Les mobs ne peuvent pas critiquer avec les sorts. Mais totems et pets peuvent. @@ -6957,135 +5379,11 @@ bool Unit::IsSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM return false; } -uint32 Unit::SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim, Spell* spell) -{ - // Calculate critical bonus - int32 crit_bonus; - switch (spellProto->DmgClass) - { - case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100% - case SPELL_DAMAGE_CLASS_RANGED: - crit_bonus = damage; - break; - default: - crit_bonus = damage / 2; // for spells is 50% - break; - } - - // adds additional damage to crit_bonus (from talents) - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus, spell); - - if (!pVictim) - return damage += crit_bonus; - - uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); - - float critPctDamageMod = GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask); - - damage = (damage + crit_bonus) * critPctDamageMod; - - return damage; -} - -uint32 Unit::SpellCriticalHealingBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim) -{ - // Calculate critical bonus - int32 crit_bonus; - switch (spellProto->DmgClass) - { - case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100% - case SPELL_DAMAGE_CLASS_RANGED: - // TODO: write here full calculation for melee/ranged spells - crit_bonus = damage; - break; - default: - crit_bonus = damage / 2; // for spells is 50% - break; - } - - if (pVictim) - { - uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); - crit_bonus = int32(crit_bonus * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask)); - } - - if (crit_bonus > 0) - damage += crit_bonus; - - return damage; -} - -/** - * Calculates caster part of healing spell bonuses, - * also includes different bonuses dependent from target auras - */ -uint32 Unit::SpellHealingBonusDone(Unit *pVictim, SpellEntry const *spellProto, int32 healamount, DamageEffectType damagetype, uint32 stack, Spell* spell) -{ - // For totems get healing bonus from owner (statue isn't totem in fact) - if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsTotem() && ((Totem*)this)->GetTotemType() != TOTEM_STATUE) - if (Unit* owner = GetOwner()) - return owner->SpellHealingBonusDone(pVictim, spellProto, healamount, damagetype, stack, spell); - - // No heal amount for this class spells - if (spellProto->DmgClass == SPELL_DAMAGE_CLASS_NONE || spellProto->Custom & SPELL_CUSTOM_FIXED_DAMAGE) - { - DEBUG_UNIT(this, DEBUG_SPELLS_DAMAGE, "SpellHealingBonusDone[spell=%u]: has fixed damage (SPELL_DAMAGE_CLASS_NONE)", spellProto->Id); - return healamount < 0 ? 0 : healamount; - } - // Healing Done - // Done total percent damage auras - float DoneTotalMod = 1.0f; - int32 DoneTotal = 0; - - // Healing done percent - AuraList const& mHealingDonePct = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT); - for (AuraList::const_iterator i = mHealingDonePct.begin(); i != mHealingDonePct.end(); ++i) - DoneTotalMod *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f; - - // done scripted mod (take it from owner) - Unit *owner = GetOwner(); - if (!owner) - owner = this; - AuraList const& mOverrideClassScript = owner->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for (AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) - { - if (!(*i)->isAffectedOnSpell(spellProto)) - continue; - switch ((*i)->GetModifier()->m_miscvalue) - { - case 4415: // Increased Rejuvenation Healing - DoneTotal += (*i)->GetModifier()->m_amount / 4; // 4 ticks - break; - case 3736: // Hateful Totem of the Third Wind / Increased Lesser Healing Wave / Savage Totem of the Third Wind - DoneTotal += (*i)->GetModifier()->m_amount; - break; - default: - break; - } - } - - // Done fixed damage bonus auras - int32 DoneAdvertisedBenefit = SpellBaseHealingBonusDone(spellProto->GetSpellSchoolMask()); - - // apply ap bonus and benefit affected by spell power implicit coeffs and spell level penalties - DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true, this, spell); - - // use float as more appropriate for negative values and percent applying - float heal = (healamount + DoneTotal * int32(stack)) * DoneTotalMod; - // apply spellmod to Done amount - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal, spell); - - DEBUG_UNIT(this, DEBUG_SPELLS_DAMAGE, "SpellHealingBonusDone[spell=%u]: (base=%u + %i) * %f. HealingPwr=%i", spellProto->Id, healamount, DoneTotal, DoneTotalMod, DoneAdvertisedBenefit); - return heal < 0 ? 0 : uint32(heal); -} - /** * Calculates target part of healing spell bonuses, * will be called on each tick for periodic damage over time auras */ -uint32 Unit::SpellHealingBonusTaken(Unit *pCaster, SpellEntry const *spellProto, int32 healamount, DamageEffectType damagetype, uint32 stack, Spell* spell) +uint32 Unit::SpellHealingBonusTaken(WorldObject* pCaster, SpellEntry const* spellProto, int32 healamount, DamageEffectType damagetype, uint32 stack, Spell* spell) { float TakenTotalMod = 1.0f; @@ -7150,30 +5448,6 @@ uint32 Unit::SpellHealingBonusTaken(Unit *pCaster, SpellEntry const *spellProto, return heal < 0 ? 0 : uint32(heal); } -int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask) -{ - int32 AdvertisedBenefit = 0; - - AuraList const& mHealingDone = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE); - for (AuraList::const_iterator i = mHealingDone.begin(); i != mHealingDone.end(); ++i) - if (((*i)->GetModifier()->m_miscvalue & schoolMask) != 0) - AdvertisedBenefit += (*i)->GetModifier()->m_amount; - - // Healing bonus of spirit, intellect and strength - if (GetTypeId() == TYPEID_PLAYER) - { - // Healing bonus from stats - AuraList const& mHealingDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT); - for (AuraList::const_iterator i = mHealingDoneOfStatPercent.begin(); i != mHealingDoneOfStatPercent.end(); ++i) - { - // 1.12.* have only 1 stat type support - Stats usedStat = STAT_SPIRIT; - AdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f); - } - } - return AdvertisedBenefit; -} - int32 Unit::SpellBaseHealingBonusTaken(SpellSchoolMask schoolMask) { int32 AdvertisedBenefit = 0; @@ -7286,168 +5560,11 @@ bool Unit::IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex i return false; } -/** - * Calculates caster part of melee damage bonuses, - * also includes different bonuses dependent from target auras - */ -uint32 Unit::MeleeDamageBonusDone(Unit* pVictim, uint32 pdamage, WeaponAttackType attType, SpellEntry const* spellProto, DamageEffectType damagetype, uint32 stack, Spell* spell, bool flat) -{ - if (!pVictim) - return pdamage; - - if (pdamage == 0) - return pdamage; - - // differentiate for weapon damage based spells - bool isWeaponDamageBasedSpell = !(spellProto && (damagetype == DOT || spellProto->HasEffect(SPELL_EFFECT_SCHOOL_DAMAGE))); - Item* pWeapon = GetTypeId() == TYPEID_PLAYER ? ((Player*)this)->GetWeaponForAttack(attType, true, false) : NULL; - uint32 creatureTypeMask = pVictim->GetCreatureTypeMask(); - uint32 schoolMask = spellProto ? spellProto->GetSpellSchoolMask() : GetMeleeDamageSchoolMask(); - - // FLAT damage bonus auras - // ======================= - int32 DoneFlat = 0; - int32 APbonus = 0; - - // ..done flat, already included in weapon damage based spells - if (!isWeaponDamageBasedSpell) - { - AuraList const& mModDamageDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE); - for (AuraList::const_iterator i = mModDamageDone.begin(); i != mModDamageDone.end(); ++i) - { - if ((*i)->GetModifier()->m_miscvalue & schoolMask && // schoolmask has to fit with the intrinsic spell school - ((*i)->GetModifier()->m_miscvalue & GetMeleeDamageSchoolMask() || // AND schoolmask has to fit with weapon damage school (essential for non-physical spells) - spellProto->DmgClass == SPELL_DAMAGE_CLASS_RANGED) && // Some ranged physical attacks use magic damage, ex. Arcane Shot - (((*i)->GetSpellProto()->EquippedItemClass == -1) || // general, weapon independent - (pWeapon && pWeapon->IsFitToSpellRequirements((*i)->GetSpellProto())))) // OR used weapon fits aura requirements - DoneFlat += (*i)->GetModifier()->m_amount; - } - - // Pets just add their bonus damage to their melee damage - if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsPet()) - DoneFlat += ((Pet*)this)->GetBonusDamage(); - } - - // ..done flat (by creature type mask) - DoneFlat += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE, creatureTypeMask); - - // ..done flat (base at attack power for marked target and base at attack power for creature type) - if (attType == RANGED_ATTACK) - { - APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS); - APbonus += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS, creatureTypeMask); - } - else - { - APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS); - APbonus += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS, creatureTypeMask); - } - - // PERCENT damage auras - // ==================== - float DonePercent = 1.0f; - - // ..done pct, already included in weapon damage based spells - if (!isWeaponDamageBasedSpell) - { - AuraList const& mModDamagePercentDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - for (AuraList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i) - { - if ((*i)->GetModifier()->m_miscvalue & schoolMask && // schoolmask has to fit with the intrinsic spell school - (*i)->GetModifier()->m_miscvalue & GetMeleeDamageSchoolMask() && // AND schoolmask has to fit with weapon damage school (essential for non-physical spells) - (((*i)->GetSpellProto()->EquippedItemClass == -1) || // general, weapon independent - (pWeapon && pWeapon->IsFitToSpellRequirements((*i)->GetSpellProto())))) // OR used weapon fits aura requirements - DonePercent *= ((*i)->GetModifier()->m_amount + 100.0f) / 100.0f; - } - - if (attType == OFF_ATTACK) - DonePercent *= GetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT); // no school check required - } - - // Pet happiness increases damage of Hunter pet melee spells - if (IsPet() && ((Pet*)this)->getPetType() == HUNTER_PET) - { - if (Pet* pet = ((Pet*)this)) - { - switch (pet->GetHappinessState()) - { - case HAPPY: DonePercent *= 1.25; break; - case CONTENT: break; - case UNHAPPY: DonePercent *= 0.75; break; - } - } - } - - // ..done pct (by creature type mask) - DonePercent *= GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS, creatureTypeMask); - - // special dummys/class scripts and other effects - // ============================================= - Unit *owner = GetOwner(); - if (!owner) - owner = this; - - // final calculation - // ================= - - float DoneTotal = 0.0f; - - // scaling of non weapon based spells - if (!isWeaponDamageBasedSpell) - { - // apply ap bonus and benefit affected by spell power implicit coeffs and spell level penalties - DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneFlat, APbonus, damagetype, true, this, spell); - } - // weapon damage based spells - else if (APbonus || DoneFlat) - { - bool normalized = spellProto ? spellProto->HasEffect(SPELL_EFFECT_NORMALIZED_WEAPON_DMG) : false; - DoneTotal += int32(APbonus / 14.0f * GetAPMultiplier(attType, normalized)); - - // for weapon damage based spells we still have to apply damage done percent mods - // (that are already included into pdamage) to not-yet included DoneFlat - // e.g. from doneVersusCreature, apBonusVs... - UnitMods unitMod; - switch (attType) - { - default: - case BASE_ATTACK: - unitMod = UNIT_MOD_DAMAGE_MAINHAND; - break; - case OFF_ATTACK: - unitMod = UNIT_MOD_DAMAGE_OFFHAND; - break; - case RANGED_ATTACK: - unitMod = UNIT_MOD_DAMAGE_RANGED; - break; - } - - DoneTotal += DoneFlat; - - DoneTotal *= GetModifierValue(unitMod, TOTAL_PCT); - } - - if (!flat) - DoneTotal = 0.0f; - - float tmpDamage = float(int32(pdamage) + DoneTotal * int32(stack)) * DonePercent; - - // apply spellmod to Done damage - if (spellProto) - { - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage, spell); - } - - // bonus result can be negative - return tmpDamage > 0 ? uint32(tmpDamage) : 0; -} - /** * Calculates target part of melee damage bonuses, * will be called on each tick for periodic damage over time auras */ -uint32 Unit::MeleeDamageBonusTaken(Unit* pCaster, uint32 pdamage, WeaponAttackType attType, SpellEntry const* spellProto, DamageEffectType damagetype, uint32 stack, Spell* spell, bool flat) +uint32 Unit::MeleeDamageBonusTaken(WorldObject* pCaster, uint32 pdamage, WeaponAttackType attType, SpellEntry const* spellProto, DamageEffectType damagetype, uint32 stack, Spell* spell, bool flat) { if (!pCaster) return pdamage; @@ -8012,66 +6129,66 @@ float Unit::GetRegenMPPerSpirit() const return addvalue; } -bool Unit::isVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, bool detect, bool /*inVisibleList*/, bool* alert) const +bool Unit::isVisibleForOrDetect(WorldObject const* pDetector, WorldObject const* viewPoint, bool detect, bool /*inVisibleList*/, bool* alert) const { - if (!u || !IsInMap(u)) + if (!pDetector || !IsInMap(pDetector)) return false; // Always can see self - if (u == this) + if (pDetector == this) return true; - bool at_same_transport = GetTransport() && GetTransport() == u->GetTransport(); + bool at_same_transport = GetTransport() && GetTransport() == pDetector->GetTransport(); // not in world - if (!at_same_transport && (!IsInWorld() || !u->IsInWorld())) + if (!at_same_transport && (!IsInWorld() || !pDetector->IsInWorld())) return false; // forbidden to seen (at GM respawn command) if (m_Visibility == VISIBILITY_RESPAWN) return false; - Map& _map = *u->GetMap(); - bool isTargetPlayer = u->GetTypeId() == TYPEID_PLAYER; - auto pPlayerTarget = isTargetPlayer ? (Player*)u : nullptr; + Map& _map = *pDetector->GetMap(); + Unit const* pDetectorUnit = pDetector->ToUnit(); + Player const* pDetectorPlayer = pDetector->ToPlayer(); // Grid dead/alive checks - if (isTargetPlayer) + if (pDetectorPlayer) { // non visible at grid for any stealth state - if (!IsVisibleInGridForPlayer(pPlayerTarget)) + if (!IsVisibleInGridForPlayer(pDetectorPlayer)) return false; // if player is dead then he can't detect anyone in any cases - if (!u->isAlive()) + if (!pDetectorPlayer->isAlive()) detect = false; } else { // all dead creatures/players not visible for any creatures - if (!u->isAlive() || !isAlive()) + if ((pDetectorUnit && !pDetectorUnit->isAlive()) || !isAlive()) return false; } // always seen by owner - if (GetCharmerOrOwnerGuid() == u->GetObjectGuid()) + if (GetCharmerOrOwnerGuid() == pDetector->GetObjectGuid()) return true; // isInvisibleForAlive() those units can only be seen by dead or if other // unit is also invisible for alive.. if an isinvisibleforalive unit dies we // should be able to see it too - if (u->isAlive() && isAlive() && isInvisibleForAlive() != u->isInvisibleForAlive()) - if (!isTargetPlayer || !pPlayerTarget->IsGameMaster()) + if (pDetectorUnit && pDetectorUnit->isAlive() && isAlive() && isInvisibleForAlive() != pDetectorUnit->isInvisibleForAlive()) + if (!pDetectorPlayer || !pDetectorPlayer->IsGameMaster()) return false; // redundant phasing - //if (!u->CanSeeInWorld(this)) + //if (!pDetector->CanSeeInWorld(this)) // return false; if (Creature const* pCreature = ToCreature()) { if ((pCreature->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_ONLY_VISIBLE_TO_FRIENDLY) && - !pCreature->IsFriendlyTo(u)) + !pCreature->IsFriendlyTo(pDetector)) return false; } @@ -8080,10 +6197,10 @@ bool Unit::isVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo return true; // GMs see any players, not higher GMs and all units - if (isTargetPlayer && pPlayerTarget->IsGameMaster()) + if (pDetectorPlayer && pDetectorPlayer->IsGameMaster()) { if (GetTypeId() == TYPEID_PLAYER) - return ToPlayer()->GetGMInvisibilityLevel() <= uint8(pPlayerTarget->GetSession()->GetSecurity()); + return ToPlayer()->GetGMInvisibilityLevel() <= uint8(pDetectorPlayer->GetSession()->GetSecurity()); return true; } @@ -8095,25 +6212,25 @@ bool Unit::isVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo bool invisible = m_invisibilityMask != 0; // Detectable invisibility case - if (invisible && ( + if (invisible && pDetectorUnit && ( // Invisible units are always visible for units under same // invisibility type - (m_invisibilityMask & u->m_invisibilityMask) || + (m_invisibilityMask & pDetectorUnit->m_invisibilityMask) || // Invisible units are always visible for units that can detect the // appropriate invisibility level - u->canDetectInvisibilityOf(this))) + pDetectorUnit->canDetectInvisibilityOf(this))) invisible = false; // special cases for always overwrite invisibility/stealth if (invisible || m_Visibility == VISIBILITY_GROUP_STEALTH) { // non-hostile case - if (!u->IsHostileTo(this)) + if (!pDetector->IsHostileTo(this)) { // player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting) - if (GetTypeId() == TYPEID_PLAYER && isTargetPlayer) + if (GetTypeId() == TYPEID_PLAYER && pDetectorPlayer) { - if (((Player*)this)->IsGroupVisibleFor(pPlayerTarget)) + if (((Player*)this)->IsGroupVisibleFor(pDetectorPlayer)) return true; // else apply same rules as for hostile case (detecting check for stealth) @@ -8141,10 +6258,10 @@ bool Unit::isVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo //players detect players only in Player::HandleStealthedUnitsDetection() // Units detect Units only in Units::HandleStealthedUnitsDetection() if (!detect) - return isTargetPlayer ? pPlayerTarget->IsInVisibleList(this) : false; + return pDetectorPlayer ? pDetectorPlayer->IsInVisibleList(this) : false; // Special cases - if (!u->canDetectStealthOf(this, GetDistance(viewPoint), alert)) + if (pDetectorUnit && !pDetectorUnit->canDetectStealthOf(this, GetDistance(viewPoint), alert)) return false; // Now check if target is visible with LoS @@ -9162,66 +7279,6 @@ bool Unit::SelectHostileTarget() //====================================================================== //====================================================================== -int32 Unit::CalculateSpellDamage(Unit const* target, SpellEntry const* spellProto, SpellEffectIndex effect_index, int32 const* effBasePoints, Spell* spell) -{ - Player* unitPlayer = (GetTypeId() == TYPEID_PLAYER) ? (Player*)this : NULL; - - uint8 comboPoints = unitPlayer ? unitPlayer->GetComboPoints() : 0; - - int32 level = int32(getLevel()); - if (level > (int32)spellProto->maxLevel && spellProto->maxLevel > 0) - level = (int32)spellProto->maxLevel; - else if (level < (int32)spellProto->baseLevel) - level = (int32)spellProto->baseLevel; - level -= (int32)spellProto->spellLevel; - - int32 baseDice = int32(spellProto->EffectBaseDice[effect_index]); - float basePointsPerLevel = spellProto->EffectRealPointsPerLevel[effect_index]; - float randomPointsPerLevel = spellProto->EffectDicePerLevel[effect_index]; - int32 basePoints = effBasePoints - ? *effBasePoints - baseDice - : spellProto->EffectBasePoints[effect_index]; - - basePoints += int32(level * basePointsPerLevel); - int32 randomPoints = int32(spellProto->EffectDieSides[effect_index] + level * randomPointsPerLevel); - float comboDamage = spellProto->EffectPointsPerComboPoint[effect_index]; - - switch (randomPoints) - { - case 0: - case 1: - basePoints += baseDice; - break; // range 1..1 - default: - { - // range can have positive (1..rand) and negative (rand..1) values, so order its for irand - int32 randvalue = baseDice >= randomPoints - ? irand(randomPoints, baseDice) - : irand(baseDice, randomPoints); - - basePoints += randvalue; - break; - } - } - - int32 value = basePoints; - - // random damage - if (comboDamage != 0 && unitPlayer && target && (target->GetObjectGuid() == unitPlayer->GetComboTargetGuid())) - value += (int32)(comboDamage * comboPoints); - - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_ALL_EFFECTS, value, spell); - - if (spellProto->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION && spellProto->spellLevel && - spellProto->Effect[effect_index] != SPELL_EFFECT_WEAPON_PERCENT_DAMAGE && - spellProto->Effect[effect_index] != SPELL_EFFECT_KNOCK_BACK && - (spellProto->Effect[effect_index] != SPELL_EFFECT_APPLY_AURA || spellProto->EffectApplyAuraName[effect_index] != SPELL_AURA_MOD_DECREASE_SPEED)) - value = int32(value * 0.25f * exp(getLevel() * (70 - spellProto->spellLevel) / 1000.0f)); - - return value; -} - DiminishingLevels Unit::GetDiminishing(DiminishingGroup group) { for (Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i) @@ -9262,20 +7319,7 @@ void Unit::IncrDiminishing(DiminishingGroup group) m_Diminishing.push_back(DiminishingReturn(group, WorldTimer::getMSTime(), DIMINISHING_LEVEL_2)); } -bool Unit::IsLikePlayer() const -{ - if (IsPlayer()) - return true; - if (!IsPet()) - return false; - // Pet creature. Reste a savoir si ce machin appartient a un joueur. - Pet* pet = ((Pet*)this); - if (!pet->isControlled()) - return false; - return pet->GetOwnerGuid().IsPlayer(); -} - -void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, Unit* caster, DiminishingLevels Level, bool isReflected) +void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, WorldObject const* caster, DiminishingLevels Level, bool isReflected) { if (duration == -1 || group == DIMINISHING_NONE || (!isReflected && caster->IsFriendlyTo(this))) return; @@ -9314,9 +7358,9 @@ void Unit::ApplyDiminishingAura(DiminishingGroup group, bool apply) } } -bool Unit::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const +bool Unit::isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const { - return isVisibleForOrDetect(u, viewPoint, false, inVisibleList); + return isVisibleForOrDetect(pDetector, viewPoint, false, inVisibleList); } /// returns true if creature can't be seen by alive units @@ -10384,11 +8428,6 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* pTarget, uint32 procFlag, } } -SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const -{ - return SPELL_SCHOOL_MASK_NORMAL; -} - Player* Unit::GetSpellModOwner() const { if (GetTypeId() == TYPEID_PLAYER) @@ -11112,31 +9151,6 @@ void Unit::UpdateAuraForGroup(uint8 slot) } } -float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized) const -{ - if (!normalized || GetTypeId() != TYPEID_PLAYER) - return float(GetAttackTime(attType)) / 1000.0f; - - Item *Weapon = ((Player*)this)->GetWeaponForAttack(attType, true, false); - if (!Weapon) - return 2.4f; // fist attack - - switch (Weapon->GetProto()->InventoryType) - { - case INVTYPE_2HWEAPON: - return 3.3f; - case INVTYPE_RANGED: - case INVTYPE_RANGEDRIGHT: - case INVTYPE_THROWN: - return 2.8f; - case INVTYPE_WEAPON: - case INVTYPE_WEAPONMAINHAND: - case INVTYPE_WEAPONOFFHAND: - default: - return Weapon->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER ? 1.7f : 2.4f; - } -} - Aura* Unit::GetDummyAura(uint32 spell_id) const { Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY); @@ -11435,17 +9449,6 @@ void Unit::CleanupDeletedAuras() m_deletedAuras.clear(); } -bool Unit::CheckAndIncreaseCastCounter() -{ - uint32 maxCasts = sWorld.getConfig(CONFIG_UINT32_MAX_SPELL_CASTS_IN_CHAIN); - - if (maxCasts && m_castCounter >= maxCasts) - return false; - - ++m_castCounter; - return true; -} - SpellAuraHolder* Unit::GetSpellAuraHolder(uint32 spellid) const { SpellAuraHolderMap::const_iterator itr = m_spellAuraHolders.find(spellid); @@ -11804,21 +9807,6 @@ bool Unit::CanReachWithMeleeAutoAttackAtPosition(Unit const* pVictim, float x, f return (dx * dx + dy * dy < reach * reach) && ((dz * dz) < zReach); } -bool Unit::CanReachWithMeleeSpellAttack(Unit const* pVictim, float flat_mod /*= 0.0f*/) const -{ - if (!pVictim || !pVictim->IsInWorld()) - return false; - - float reach = GetCombatReach(pVictim, true, flat_mod); - - // This check is not related to bounding radius - float dx = GetPositionX() - pVictim->GetPositionX(); - float dy = GetPositionY() - pVictim->GetPositionY(); - - // melee spells ignore Z-axis checks - return dx * dx + dy * dy < reach * reach; -} - Unit* Unit::GetUnit(WorldObject &obj, uint64 const &Guid) { if (obj.IsInWorld()) @@ -12061,31 +10049,6 @@ Aura* Unit::GetMostImportantAuraAfter(Aura const* like, Aura const* except) return currMostImportant; } -// Nostalrius : Ivina -void Unit::SetLastCastedSpell(uint32 spell_id, bool byclient) -{ - if (spell_id != 23303 && spell_id != 836) - m_lastCastedSpellID = spell_id; -} - -uint32 Unit::GetLastCastedSpell(bool byclientonly) -{ - if (byclientonly) - return 0; - else - return m_lastCastedSpellID; -} - -uint32 Unit::GetLastAttackType() -{ - return m_lastAttackType; -} - -void Unit::SetLastAttackType(uint32 attackType) -{ - m_lastAttackType = attackType; -} - void Unit::UpdateSplineMovement(uint32 t_diff) { if (movespline->Finalized()) diff --git a/src/game/Objects/Unit.h b/src/game/Objects/Unit.h index 81ac7fb02..6d8c539ce 100644 --- a/src/game/Objects/Unit.h +++ b/src/game/Objects/Unit.h @@ -32,7 +32,6 @@ #include "HostileRefManager.h" #include "FollowerReference.h" #include "FollowerRefManager.h" -#include "Utilities/EventProcessor.h" #include "MotionMaster.h" #include "DBCStructure.h" #include "Path.h" @@ -307,7 +306,6 @@ class Aura; class SpellAuraHolder; class Creature; class Spell; -class DynamicObject; class GameObject; class Item; class Pet; @@ -638,33 +636,6 @@ struct DiminishingReturn uint32 hitCount; }; -// At least some values expected fixed and used in auras field, other custom -enum MeleeHitOutcome -{ - MELEE_HIT_EVADE = 0, - MELEE_HIT_MISS = 1, - MELEE_HIT_DODGE = 2, // used as misc in SPELL_AURA_IGNORE_COMBAT_RESULT - MELEE_HIT_BLOCK = 3, // used as misc in SPELL_AURA_IGNORE_COMBAT_RESULT - MELEE_HIT_PARRY = 4, // used as misc in SPELL_AURA_IGNORE_COMBAT_RESULT - MELEE_HIT_GLANCING = 5, - MELEE_HIT_CRIT = 6, - MELEE_HIT_CRUSHING = 7, - MELEE_HIT_NORMAL = 8, - MELEE_HIT_BLOCK_CRIT= 9, -}; - -struct CleanDamage -{ - CleanDamage(uint32 _damage, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome, uint32 _Absorb, int32 _Resist) : - damage(_damage), attackType(_attackType), hitOutCome(_hitOutCome), absorb(_Absorb), resist(_Resist) {} - - uint32 damage; - WeaponAttackType attackType; - MeleeHitOutcome hitOutCome; - uint32 absorb; - int32 resist; -}; - struct SubDamageInfo { SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL; @@ -696,27 +667,6 @@ struct CalcDamageInfo MeleeHitOutcome hitOutCome = MELEE_HIT_EVADE; // TODO: remove this field (need use TargetState) }; -// Spell damage info structure based on structure sending in SMSG_SPELLNONMELEEDAMAGELOG opcode -struct SpellNonMeleeDamage{ - SpellNonMeleeDamage(Unit *_attacker, Unit *_target, uint32 _SpellID, SpellSchools _school) - : target(_target), attacker(_attacker), SpellID(_SpellID), damage(0), school(_school), - absorb(0), resist(0), periodicLog(false), unused(false), blocked(0), HitInfo(0), spell(nullptr) - {} - - Unit *target; - Unit *attacker; - uint32 SpellID; - uint32 damage; - SpellSchools school; - uint32 absorb; - int32 resist; - bool periodicLog; - bool unused; - uint32 blocked; - uint32 HitInfo; - Spell *spell; -}; - struct SpellPeriodicAuraLogInfo { SpellPeriodicAuraLogInfo(Aura *_aura, uint32 _damage, uint32 _absorb, int32 _resist, float _multiplier) @@ -741,16 +691,6 @@ enum SpellAuraProcResult typedef SpellAuraProcResult(Unit::*pAuraProcHandler)(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); extern pAuraProcHandler AuraProcHandler[TOTAL_AURAS]; -enum CurrentSpellTypes -{ - CURRENT_MELEE_SPELL = 0, - CURRENT_GENERIC_SPELL = 1, - CURRENT_AUTOREPEAT_SPELL = 2, - CURRENT_CHANNELED_SPELL = 3 -}; - -#define CURRENT_FIRST_NON_MELEE_SPELL 1 -#define CURRENT_MAX_SPELL 4 #define UNIT_SPELL_UPDATE_TIME_BUFFER 60 struct GlobalCooldown @@ -1076,7 +1016,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject * @param Level The current level of diminishing returns for the group, decides the new duration * @param isReflected Whether the spell was reflected or not, used to determine if we should do any calculations at all. */ - void ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster, DiminishingLevels Level, bool isReflected = false); + void ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, WorldObject const* caster, DiminishingLevels Level, bool isReflected = false); /** * Applies a diminishing return to the given group if apply is true, * otherwise lowers the level by one (?) @@ -1260,8 +1200,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject return !hasUnitState(UNIT_STAT_NO_FREE_MOVE) && !GetOwnerGuid(); } - uint32 getLevel() const { return GetUInt32Value(UNIT_FIELD_LEVEL); } - virtual uint32 GetLevelForTarget(Unit const* /*target*/) const { return getLevel(); } + uint32 getLevel() const final override { return GetUInt32Value(UNIT_FIELD_LEVEL); } void SetLevel(uint32 lvl); uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, 0); } uint32 getRaceMask() const { return getRace() ? 1 << (getRace()-1) : 0x0; } @@ -1325,16 +1264,12 @@ class MANGOS_DLL_SPEC Unit : public WorldObject virtual void SetSheath( SheathState sheathed ) { SetByteValue(UNIT_FIELD_BYTES_2, 0, sheathed); } // faction template id - uint32 getFaction() const { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); } + uint32 getFaction() const final override { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); } void setFaction(uint32 faction) { SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction ); } - FactionTemplateEntry const* getFactionTemplateEntry() const; - ReputationRank GetReactionTo(Unit const* target) const; - ReputationRank static GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target); - - bool IsHostileTo(Unit const* unit) const override; + bool IsHostileTo(WorldObject const* target) const override; bool IsHostileToPlayers() const; - bool IsFriendlyTo(Unit const* unit) const override; + bool IsFriendlyTo(WorldObject const* target) const override; bool IsNeutralToAll() const; bool IsContestedGuard() const { @@ -1366,41 +1301,24 @@ class MANGOS_DLL_SPEC Unit : public WorldObject virtual void Mount(uint32 mount, uint32 spellId = 0); virtual void Unmount(bool from_aura = false); - // Tuer cette unite. - void DoKillUnit(Unit *victim = nullptr); - - uint16 GetSkillMaxForLevel(Unit const* target = nullptr) const { return (target ? GetLevelForTarget(target) : getLevel()) * 5; } - void DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb); - uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss, Spell* spell = nullptr); - // Je viens de tuer quelqu'un. - void Kill(Unit* pVictim, SpellEntry const *spellProto, bool durabilityLoss = true); - int32 DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellProto, bool critical = false); - - void PetOwnerKilledUnit(Unit* pVictim); - - void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellEntry const *procSpell = nullptr, Spell* spell = nullptr); - void ProcDamageAndSpellFor(bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const* procSpell, uint32 damage, ProcTriggeredList& triggeredList, Spell* spell = nullptr); - void HandleTriggers(Unit *pVictim, uint32 procExtra, uint32 amount, SpellEntry const *procSpell, ProcTriggeredList const& procTriggered); - void HandleEmote(uint32 emote_id); // auto-select command/state void HandleEmoteCommand(uint32 emote_id); void HandleEmoteState(uint32 emote_id); - void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool checkLoS = true, bool extra = false ); + // Kills the victim. + void DoKillUnit(Unit *victim = nullptr); + uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss, Spell* spell = nullptr) final override; + // Called after this unit kills someone. + void Kill(Unit* pVictim, SpellEntry const *spellProto, bool durabilityLoss = true); + void PetOwnerKilledUnit(Unit* pVictim); + + void ProcDamageAndSpellFor(bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const* procSpell, uint32 damage, ProcTriggeredList& triggeredList, Spell* spell = nullptr); + void HandleTriggers(Unit *pVictim, uint32 procExtra, uint32 amount, SpellEntry const *procSpell, ProcTriggeredList const& procTriggered); + void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool checkLoS = true, bool extra = false ); float MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const; void CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType = BASE_ATTACK); void DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss); - - void CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK, Spell* spell = nullptr); - void DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss); - - float MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell, Spell* spellPtr = nullptr); - SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell, Spell* spellPtr = nullptr); - SpellMissInfo MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell, Spell* spellPtr = nullptr); - int32 MagicSpellHitChance(Unit *pVictim, SpellEntry const *spell, Spell* spellPtr = nullptr); - SpellMissInfo SpellHitResult(Unit *pVictim, SpellEntry const *spell, SpellEffectIndex effIndex, bool canReflect = false, Spell* spellPtr = nullptr); - // Nostalrius : SPELL_AURA_MOD_MECHANIC_RESISTANCE - bool IsEffectResist(SpellEntry const* spell, int eff); + bool IsEffectResist(SpellEntry const* spell, int eff); // SPELL_AURA_MOD_MECHANIC_RESISTANCE float GetUnitDodgeChance() const; float GetUnitParryChance() const; @@ -1408,9 +1326,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject float GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const; virtual uint32 GetShieldBlockValue() const =0; - uint32 GetUnitMeleeSkill(Unit const* target = nullptr) const { return (target ? GetLevelForTarget(target) : getLevel()) * 5; } - uint32 GetDefenseSkillValue(Unit const* target = nullptr) const; - uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = nullptr) const; float GetPPMProcChance(uint32 WeaponSpeed, float PPM) const; MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType) const; @@ -1439,7 +1354,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject UNIT_NPC_FLAG_SPIRITGUIDE | UNIT_NPC_FLAG_TABARDDESIGNER | UNIT_NPC_FLAG_AUCTIONEER ); } bool isSpiritService() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE ); } - bool IsTaxiFlying() const { return hasUnitState(UNIT_STAT_TAXI_FLIGHT); } bool isInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); } @@ -1457,7 +1371,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool HasAuraTypeByCaster(AuraType auraType, ObjectGuid casterGuid) const; uint32 GetFirstAuraBySpellIconAndVisual(uint32 spellIconId, uint32 spellVisual) const; - uint64 GetAuraApplicationMask() const; uint64 GetNegativeAuraApplicationMask() const; @@ -1477,22 +1390,19 @@ class MANGOS_DLL_SPEC Unit : public WorldObject { return m_spellAuraHolders.find(spellId) != m_spellAuraHolders.end(); } - bool virtual HasSpell(uint32 /*spellID*/) const { return false; } - bool HasStealthAura() const { return HasAuraType(SPELL_AURA_MOD_STEALTH); } bool HasInvisibilityAura() const { return HasAuraType(SPELL_AURA_MOD_INVISIBILITY); } bool isFeared() const { return HasAuraType(SPELL_AURA_MOD_FEAR); } bool isInRoots() const { return HasAuraType(SPELL_AURA_MOD_ROOT); } bool IsPolymorphed() const; bool IsImmuneToSchoolMask(uint32 schoolMask) const; - bool isFrozen() const; void RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage); void RemoveFearEffectsByDamageTaken(uint32 damage, uint32 exceptSpellId, DamageEffectType damagetype); - bool IsValidAttackTarget(Unit const* target) const; + bool IsValidAttackTarget(Unit const* target) const final override; bool isTargetableForAttack(bool inversAlive = false, bool isAttackerPlayer = false) const; bool isAttackableByAOE(bool requireDeadTarget = false, bool isCasterPlayer = false) const; bool isPassiveToHostile() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PASSIVE); } @@ -1501,17 +1411,9 @@ class MANGOS_DLL_SPEC Unit : public WorldObject virtual bool IsUnderWater() const; bool isInAccessablePlaceFor(Creature const* c) const; - void SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical = false); - void SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage,Powers powertype); - void EnergizeBySpell(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype); void SendEnvironmentalDamageLog(uint8 type, uint32 damage, uint32 absorb, int32 resist) const; uint32 SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage); - void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr, SpellEntry const* triggeredByParent = nullptr); - void CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem= nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr, SpellEntry const* triggeredByParent = nullptr); - void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr); - void CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr); - void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr); - void CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem = nullptr, Aura* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid(), SpellEntry const* triggeredBy = nullptr); + // Affiche le visuel d'un sort void SendSpellGo(Unit* target, uint32 spellId); @@ -1519,19 +1421,14 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void SendAttackStateUpdate(CalcDamageInfo *damageInfo); void SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, int32 Resist, VictimState TargetState, uint32 BlockedAmount); - void SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log); - void SendSpellNonMeleeDamageLog(Unit *target, uint32 spellID, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 absorbedDamage, int32 resist, bool isPeriodic, uint32 blocked, bool criticalHit = false, bool split = false); void SendPeriodicAuraLog(SpellPeriodicAuraLogInfo *pInfo, AuraType auraTypeOverride = SPELL_AURA_NONE); - void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo); void NearTeleportTo(float x, float y, float z, float orientation, uint32 teleportOptions = TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); void NearLandTo(float x, float y, float z, float orientation); void TeleportPositionRelocation(float x, float y, float z, float o); void MonsterMoveWithSpeed(float x, float y, float z, float o, float speed, uint32 options); - void MonsterMove(float x, float y, float z); // Utilise vitesse de course + void MonsterMove(float x, float y, float z); - // recommend use MonsterMove/MonsterMoveWithSpeed for most case that correctly work with movegens - // if used additional args in ... part then floats must explicitly casted to double void SendHeartBeat(bool includingSelf = true); virtual void SetFly(bool enable); void SetWalk(bool enable, bool asDefault = true); @@ -1542,8 +1439,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject std::deque m_pendingMovementChanges; std::map m_lastMovementChangeCounterPerType; - - public: std::deque& GetPendingMovementChangesQueue() { return m_pendingMovementChanges; } @@ -1664,9 +1559,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool IsCharmerOrOwnerPlayerOrPlayerItself() const; Player* GetCharmerOrOwnerPlayerOrPlayerItself() const; - Player* GetAffectingPlayer() const; - float GetLeewayBonusRange(const Unit* target, bool ability) const; - float GetLeewayBonusRadius() const; + Player* GetAffectingPlayer() const final override; void SetPet(Pet* pet); void SetCharm(Unit* pet); @@ -1693,6 +1586,9 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void _AddTotem(TotemSlot slot, Totem* totem); // only for call from Totem summon code void _RemoveTotem(Totem* totem); // only for call from Totem class + void UnsummonAllTotems(); + bool UnsummonOldPetBeforeNewSummon(uint32 newPetEntry); + template void CallForAllControlledUnits(Func const& func, uint32 controlledMask); template @@ -1741,7 +1637,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject // removing specific aura FROM stack by diff reasons and selections void RemoveAuraHolderFromStack(uint32 spellId, uint32 stackAmount = 1, ObjectGuid casterGuid = ObjectGuid(), AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT); - void RemoveAuraHolderDueToSpellByDispel(uint32 spellId, uint32 stackAmount, ObjectGuid casterGuid, Unit *dispeller); + void RemoveAuraHolderDueToSpellByDispel(uint32 spellId, uint32 stackAmount, ObjectGuid casterGuid); void DelaySpellAuraHolder(uint32 spellId, int32 delaytime, ObjectGuid casterGuid); @@ -1755,34 +1651,11 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void SetCreateResistance(SpellSchools school, int32 val) { m_createResistances[school] = val; } int32 GetCreateResistance(SpellSchools school) const { return m_createResistances[school]; } - void SetCurrentCastedSpell(Spell * pSpell); bool IsSpellProhibited(SpellEntry const* pSpell); bool HasProhibitedSpell(); /* use this to check if creature is silenced */ typedef std::list ProhibitSpellList; ProhibitSpellList m_prohibitSpell; - void InterruptSpell(CurrentSpellTypes spellType, bool withDelayed = true); - void FinishSpell(CurrentSpellTypes spellType, bool ok = true); - - // set withDelayed to true to account delayed spells as casted - // delayed+channeled spells are always accounted as casted - // we can skip channeled or delayed checks using flags - bool IsNonMeleeSpellCasted(bool withDelayed = false, bool skipChanneled = false, bool skipAutorepeat = false) const; - bool IsNextSwingSpellCasted() const; - // for movement generators, check if current casted spell has movement interrupt flags - bool IsNoMovementSpellCasted() const; - - // set withDelayed to true to interrupt delayed spells too - // delayed+channeled spells are always interrupted - void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0); - void InterruptSpellsWithInterruptFlags(uint32 flags, uint32 except = 0); - - Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; } - Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; - - bool CheckAndIncreaseCastCounter(); - void DecreaseCastCounter() { if (m_castCounter) --m_castCounter; } - ObjectGuid m_ObjectSlotGuid[4]; uint32 m_detectInvisibilityMask; uint32 m_invisibilityMask; @@ -1807,9 +1680,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject float m_modAttackSpeedPct[3]; float m_modRecalcDamagePct[3]; - // Event handler - EventProcessor m_Events; - // stat system bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply); void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) { m_auraModifiersGroup[unitMod][modifierType] = value; } @@ -1851,14 +1721,14 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void UpdateVisibilityAndView() override; // overwrite WorldObject::UpdateVisibilityAndView() // common function for visibility checks for player/creatures with detection code - bool isVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, bool detect, bool inVisibleList = false, bool* alert = nullptr) const; + bool isVisibleForOrDetect(WorldObject const* pDetector, WorldObject const* viewPoint, bool detect, bool inVisibleList = false, bool* alert = nullptr) const final override; bool canDetectInvisibilityOf(Unit const* u) const; bool canDetectStealthOf(Unit const* u, float distance, bool* alert = nullptr) const; // virtual functions for all world objects types - bool isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const override; + bool isVisibleForInState(WorldObject const* pDetector, WorldObject const* viewPoint, bool inVisibleList) const override; // function for low level grid visibility checks in player/creature cases - virtual bool IsVisibleInGridForPlayer(Player* pl) const = 0; + virtual bool IsVisibleInGridForPlayer(Player const* pl) const = 0; bool isInvisibleForAlive() const; bool isVisibleForDead() const; @@ -1917,16 +1787,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void setTransForm(uint32 spellid) { m_transform = spellid;} uint32 getTransForm() const { return m_transform;} float GetCollisionHeight() const { return m_modelCollisionHeight * m_nativeScaleOverride; } - - // at any changes to scale and/or displayId - void UpdateModelData(); - void GetDynObjects(uint32 spellId, SpellEffectIndex effectIndex, std::vector& dynObjsOut); - DynamicObject* GetDynObject(uint32 spellId, SpellEffectIndex effIndex); - DynamicObject* GetDynObject(uint32 spellId); - void AddDynObject(DynamicObject* dynObj); - void RemoveDynObject(uint32 spellid); - void RemoveDynObjectWithGUID(ObjectGuid guid) { m_dynObjGUIDs.remove(guid); } - void RemoveAllDynObjects(); + void UpdateModelData(); // at any changes to scale and/or displayId GameObject* GetGameObject(uint32 spellId) const; void AddGameObject(GameObject* gameObj); @@ -1934,32 +1795,18 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void RemoveGameObject(uint32 spellid, bool del); void RemoveAllGameObjects(); - uint32 CalculateDamage(WeaponAttackType attType, bool normalized, uint8 index = 0); - float GetAPMultiplier(WeaponAttackType attType, bool normalized) const; void ModifyAuraState(AuraState flag, bool apply); bool HasAuraState(AuraState flag) const { return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); } - void UnsummonAllTotems(); - bool UnsummonOldPetBeforeNewSummon(uint32 newPetEntry); - Unit* SelectMagnetTarget(Unit *victim, Spell* spell = nullptr, SpellEffectIndex eff = EFFECT_INDEX_0); - int32 SpellBonusWithCoeffs(SpellEntry const *spellProto, int32 total, int32 benefit, int32 ap_benefit, DamageEffectType damagetype, bool donePart, Unit *pCaster, Spell* spell = nullptr); - int32 SpellBaseDamageBonusDone(SpellSchoolMask schoolMask); + uint32 CalculateDamage(WeaponAttackType attType, bool normalized, uint8 index = 0); int32 SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask); - uint32 SpellDamageBonusDone(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1, Spell* spell = nullptr); - uint32 SpellDamageBonusTaken(Unit *pCaster, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1, Spell* spell = nullptr); - int32 SpellBaseHealingBonusDone(SpellSchoolMask schoolMask); + uint32 SpellDamageBonusTaken(WorldObject* pCaster, SpellEntry const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1, Spell* spell = nullptr); int32 SpellBaseHealingBonusTaken(SpellSchoolMask schoolMask); - uint32 SpellHealingBonusDone(Unit *pVictim, SpellEntry const *spellProto, int32 healamount, DamageEffectType damagetype, uint32 stack = 1, Spell* spell = nullptr); - uint32 SpellHealingBonusTaken(Unit *pCaster, SpellEntry const *spellProto, int32 healamount, DamageEffectType damagetype, uint32 stack = 1, Spell* spell = nullptr); - uint32 MeleeDamageBonusDone(Unit *pVictim, uint32 damage, WeaponAttackType attType, - SpellEntry const *spellProto = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, uint32 stack = 1, Spell* spell = nullptr, bool flat = true); - uint32 MeleeDamageBonusTaken(Unit *pCaster, uint32 pdamage,WeaponAttackType attType, - SpellEntry const *spellProto = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, uint32 stack = 1, Spell* spell = nullptr, bool flat = true); + uint32 SpellHealingBonusTaken(WorldObject* pCaster, SpellEntry const* spellProto, int32 healamount, DamageEffectType damagetype, uint32 stack = 1, Spell* spell = nullptr); + uint32 MeleeDamageBonusTaken(WorldObject* pCaster, uint32 pdamage, WeaponAttackType attType, SpellEntry const *spellProto = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, uint32 stack = 1, Spell* spell = nullptr, bool flat = true); - bool IsSpellBlocked(Unit *pCaster, Unit *pVictim, SpellEntry const *spellProto, WeaponAttackType attackType = BASE_ATTACK); - bool IsSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK, Spell* spell = nullptr); - uint32 SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim, Spell* spell = nullptr); - uint32 SpellCriticalHealingBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim); + bool IsSpellBlocked(WorldObject* pCaster, Unit *pVictim, SpellEntry const *spellProto, WeaponAttackType attackType = BASE_ATTACK); + bool IsSpellCrit(Unit const* pVictim, SpellEntry const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK, Spell* spell = nullptr) const final override; bool IsTriggeredAtSpellProcEvent(Unit *pVictim, SpellAuraHolder* holder, SpellEntry const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const*& spellProcEvent ); // Aura proc handlers @@ -1995,19 +1842,13 @@ class MANGOS_DLL_SPEC Unit : public WorldObject virtual bool IsImmuneToDamage(SpellSchoolMask meleeSchoolMask, SpellEntry const* spellInfo = nullptr); virtual bool IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index, bool castOnSelf) const; - float GetSpellResistChance(Unit const* victim, uint32 schoolMask, bool innateResists) const; - uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage); - void CalculateDamageAbsorbAndResist(Unit *pCaster, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, int32 *resist, SpellEntry const* spellProto = nullptr, Spell* spell = nullptr); - void CalculateAbsorbResistBlock(Unit *pCaster, SpellNonMeleeDamage *damageInfo, SpellEntry const* spellProto, WeaponAttackType attType = BASE_ATTACK, Spell* spell = nullptr); - float RollMagicResistanceMultiplierOutcomeAgainst(const Unit* pCaster, float resistanceChance, SpellSchoolMask schoolMask, DamageEffectType dmgType, SpellEntry const* spellProto) const; + void CalculateDamageAbsorbAndResist(WorldObject* pCaster, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, int32 *resist, SpellEntry const* spellProto = nullptr, Spell* spell = nullptr); + void CalculateAbsorbResistBlock(WorldObject* pCaster, SpellNonMeleeDamage *damageInfo, SpellEntry const* spellProto, WeaponAttackType attType = BASE_ATTACK, Spell* spell = nullptr); + float RollMagicResistanceMultiplierOutcomeAgainst(float resistanceChance, SpellSchoolMask schoolMask, DamageEffectType dmgType, SpellEntry const* spellProto) const; void _RemoveAllAuraMods(); void _ApplyAllAuraMods(); - int32 CalculateSpellDamage(Unit const* target, SpellEntry const* spellProto, SpellEffectIndex effect_index, int32 const* basePoints = nullptr, Spell* spell = nullptr); - - float CalculateLevelPenalty(SpellEntry const* spellProto) const; - void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); } void removeFollower(FollowerReference* /*pRef*/ ) { /* nothing to do yet */ } @@ -2087,7 +1928,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool CanReachWithMeleeAutoAttack(Unit const* pVictim, float flat_mod = 0.0f) const; bool CanReachWithMeleeAutoAttackAtPosition(Unit const* pVictim, float x, float y, float z, float flat_mod = 0.0f) const; - bool CanReachWithMeleeSpellAttack(Unit const* pVictim, float flat_mod = 0.0f) const; // Caster movement float GetMinChaseDistance(Unit* target) const; @@ -2131,15 +1971,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void ProcessRelocationVisibilityUpdates(); bool m_needUpdateVisibility; - uint32 m_lastCastedSpellID; - virtual void SetLastCastedSpell(uint32 spell_id, bool byclient); - virtual uint32 GetLastCastedSpell(bool byclientonly); - uint32 m_lastAttackType; - uint32 GetLastAttackType(); - void SetLastAttackType(uint32 attackType); - // Nostalrius - suis-je considere comme un joueur ? (joueur ou un pet de joueur) - bool IsLikePlayer() const; - // Nostalrius - en fin de CM par exemple void TransferAttackersThreatTo(Unit* unit); void RemoveAttackersThreat(Unit* owner); @@ -2173,9 +2004,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void RemoveSpellCooldown(uint32 spell_id, bool update = false); void RemoveSpellCategoryCooldown(uint32 cat, bool update = false); void WritePetSpellsCooldown(WorldPacket& data); - GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; } - void RemoveAllSpellCooldown(); void setTransformScale(float scale); @@ -2187,12 +2016,9 @@ class MANGOS_DLL_SPEC Unit : public WorldObject explicit Unit (); void _UpdateSpells(uint32 time); - void _UpdateAutoRepeatSpell(); - bool m_AutoRepeatFirstCast; - + uint32 m_attackTimer[MAX_ATTACK]; - float m_createStats[MAX_STATS]; int32 m_createResistances[MAX_SPELL_SCHOOL]; @@ -2207,13 +2033,10 @@ class MANGOS_DLL_SPEC Unit : public WorldObject SpellAuraHolderList m_deletedHolders; SingleCastSpellTargetMap m_singleCastSpellTargets; // casted by unit single per-caster auras - - typedef std::list DynObjectGUIDs; - DynObjectGUIDs m_dynObjGUIDs; - + typedef std::list GameObjectList; GameObjectList m_gameObj; - bool m_isSorted; + uint32 m_transform; float m_modelCollisionHeight; @@ -2229,8 +2052,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject CharmInfo *m_charmInfo; ObjectGuid m_possessorGuid; // Guid of unit possessing this one - virtual SpellSchoolMask GetMeleeDamageSchoolMask() const; - MotionMaster i_motionMaster; uint32 m_reactiveTimer[MAX_REACTIVE]; @@ -2247,6 +2068,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool m_isSpawningLinked; public: + bool m_AutoRepeatFirstCast; void DisableSpline(); void UnitDamaged(ObjectGuid from, uint32 damage) { _damageTakenHistory[from] += damage; _lastDamageTaken = 0; } void SetMeleeZLimit(float newZLimit) { m_meleeZLimit = newZLimit; } @@ -2277,9 +2099,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool m_extraMute; bool m_doExtraAttacks; - Spell* m_currentSpells[CURRENT_MAX_SPELL]; - uint32 m_castCounter; // count casts chain of triggered spells for prevent infinity cast crashes - UnitVisibility m_Visibility; Position m_last_notified_position; bool m_AINotifyScheduled; @@ -2300,22 +2119,6 @@ class MANGOS_DLL_SPEC Unit : public WorldObject float m_meleeZLimit; float m_meleeZReach; - - // Error traps for some wrong args using - // this will catch and prevent build for any cases when all optional args skipped and instead triggered used non boolean type - // no bodies expected for this declarations - template - void CastSpell(Unit* Victim, uint32 spell, TR triggered); - template - void CastSpell(Unit* Victim, SpellEntry const* spell, TR triggered); - template - void CastCustomSpell(Unit* Victim, uint32 spell, int32 const* bp0, int32 const* bp1, int32 const* bp2, TR triggered); - template - void CastCustomSpell(Unit* Victim, SpellEntry const* spell, int32 const* bp0, int32 const* bp1, int32 const* bp2, TR triggered); - template - void CastSpell(float x, float y, float z, uint32 spell, TR triggered); - template - void CastSpell(float x, float y, float z, SpellEntry const* spell, TR triggered); }; template diff --git a/src/game/ScriptMgr.cpp b/src/game/ScriptMgr.cpp index 92c01f0b5..7339bfdee 100644 --- a/src/game/ScriptMgr.cpp +++ b/src/game/ScriptMgr.cpp @@ -1837,7 +1837,7 @@ bool ScriptMgr::OnProcessEvent(uint32 eventId, Object* pSource, Object* pTarget, return pTempScript->pProcessEventId(eventId, pSource, pTarget, isStart); } -bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget) +bool ScriptMgr::OnEffectDummy(WorldObject* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget) { Script* pTempScript = m_scripts[pTarget->GetScriptId()]; @@ -1847,7 +1847,7 @@ bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex ef return pTempScript->pEffectDummyCreature(pCaster, spellId, effIndex, pTarget); } -bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, GameObject* pTarget) +bool ScriptMgr::OnEffectDummy(WorldObject* pCaster, uint32 spellId, SpellEffectIndex effIndex, GameObject* pTarget) { Script* pTempScript = m_scripts[pTarget->GetGOInfo()->ScriptId]; diff --git a/src/game/ScriptMgr.h b/src/game/ScriptMgr.h index 25f7d021a..812dfd15d 100644 --- a/src/game/ScriptMgr.h +++ b/src/game/ScriptMgr.h @@ -1212,7 +1212,7 @@ struct Script pGossipSelectWithCode(nullptr), pGOGossipSelectWithCode(nullptr), pQuestComplete(nullptr), pNPCDialogStatus(nullptr), pGODialogStatus(nullptr), pQuestRewardedNPC(nullptr), pQuestRewardedGO(nullptr), pItemHello(nullptr), pGOHello(nullptr), pAreaTrigger(nullptr), pProcessEventId(nullptr), pItemQuestAccept(nullptr), pGOQuestAccept(nullptr), - pItemUse(nullptr), pEffectDummyCreature(nullptr), pEffectDummyGameObj(nullptr), pEffectDummyItem(nullptr), + pItemUse(nullptr), pEffectDummyCreature(nullptr), pEffectDummyGameObj(nullptr), pEffectAuraDummy(nullptr), GOOpen(nullptr), GOGetAI(nullptr), GetAI(nullptr), GetInstanceData(nullptr) {} @@ -1241,9 +1241,8 @@ struct Script bool (*pGOQuestAccept )(Player*, GameObject*, const Quest*); // bool (*pGOChooseReward )(Player*, GameObject*, const Quest*, uint32); bool (*pItemUse )(Player*, Item*, SpellCastTargets const&); - bool (*pEffectDummyCreature )(Unit*, uint32, SpellEffectIndex, Creature*); - bool (*pEffectDummyGameObj )(Unit*, uint32, SpellEffectIndex, GameObject*); - bool (*pEffectDummyItem )(Unit*, uint32, SpellEffectIndex, Item*); + bool (*pEffectDummyCreature )(WorldObject*, uint32, SpellEffectIndex, Creature*); + bool (*pEffectDummyGameObj )(WorldObject*, uint32, SpellEffectIndex, GameObject*); bool (*pEffectAuraDummy )(const Aura*, bool); bool (*GOOpen )(Player* pUser, GameObject* gobj); GameObjectAI* (*GOGetAI )(GameObject* pGo); @@ -1346,8 +1345,8 @@ class ScriptMgr bool OnGameObjectOpen(Player* pPlayer, GameObject* pGameObject); bool OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* atEntry); bool OnProcessEvent(uint32 eventId, Object* pSource, Object* pTarget, bool isStart); - bool OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget); - bool OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, GameObject* pTarget); + bool OnEffectDummy(WorldObject* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget); + bool OnEffectDummy(WorldObject* pCaster, uint32 spellId, SpellEffectIndex effIndex, GameObject* pTarget); bool OnAuraDummy(Aura const* pAura, bool apply); private: diff --git a/src/game/Spells/Spell.cpp b/src/game/Spells/Spell.cpp index 62dd08e11..d52e9ab9e 100644 --- a/src/game/Spells/Spell.cpp +++ b/src/game/Spells/Spell.cpp @@ -145,23 +145,21 @@ void SpellCastTargets::setCorpseTarget(Corpse* corpse) m_CorpseTargetGUID = corpse->GetObjectGuid(); } -void SpellCastTargets::Update(Unit* caster) +void SpellCastTargets::Update(WorldObject* pCaster) { - m_GOTarget = m_GOTargetGUID ? caster->GetMap()->GetGameObject(m_GOTargetGUID) : NULL; + m_GOTarget = m_GOTargetGUID ? pCaster->GetMap()->GetGameObject(m_GOTargetGUID) : NULL; m_unitTarget = m_unitTargetGUID ? - (m_unitTargetGUID == caster->GetObjectGuid() ? caster : ObjectAccessor::GetUnit(*caster, m_unitTargetGUID)) : + (m_unitTargetGUID == pCaster->GetObjectGuid() ? pCaster->ToUnit() : ObjectAccessor::GetUnit(*pCaster, m_unitTargetGUID)) : NULL; m_itemTarget = nullptr; - if (caster->GetTypeId() == TYPEID_PLAYER) + if (Player* pPlayer = pCaster->ToPlayer()) { - Player *player = ((Player*)caster); - if (m_targetMask & TARGET_FLAG_ITEM) - m_itemTarget = player->GetItemByGuid(m_itemTargetGUID); + m_itemTarget = pPlayer->GetItemByGuid(m_itemTargetGUID); else if (m_targetMask & TARGET_FLAG_TRADE_ITEM) { - if (TradeData* pTrade = player->GetTradeData()) + if (TradeData* pTrade = pPlayer->GetTradeData()) if (m_itemTargetGUID.GetRawValue() < TRADE_SLOT_COUNT) m_itemTarget = pTrade->GetTraderData()->GetItem(TradeSlots(m_itemTargetGUID.GetRawValue())); } @@ -200,7 +198,7 @@ void SpellCastTargets::read(ByteBuffer& data, Unit *caster) data >> m_GOTargetGUID; #endif - if ((m_targetMask & (TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM)) && caster->GetTypeId() == TYPEID_PLAYER) + if ((m_targetMask & (TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM)) && caster->IsPlayer()) #if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_8_4 data >> m_itemTargetGUID.ReadAsPacked(); #else @@ -292,9 +290,9 @@ void SpellCastTargets::write(ByteBuffer& data) const } Spell::Spell(Unit* caster, SpellEntry const *info, bool triggered, ObjectGuid originalCasterGUID, SpellEntry const* triggeredBy, Unit* victim, SpellEntry const* triggeredByParent): - m_immediateHandled(false), m_needSpellLog(false), m_canTrigger(false), m_setCreatureTarget(false) + m_immediateHandled(false), m_needSpellLog(false), m_canTrigger(false), m_setCreatureTarget(false), m_caster(caster), m_casterUnit(caster) { - MANGOS_ASSERT(caster != NULL && info != NULL); + MANGOS_ASSERT(caster != nullptr && info != nullptr); MANGOS_ASSERT(info == sSpellMgr.GetSpellEntry(info->Id) && "`info` must be pointer to a sSpellMgr element"); m_successCast = false; @@ -302,7 +300,6 @@ Spell::Spell(Unit* caster, SpellEntry const *info, bool triggered, ObjectGuid or m_spellInfo = info; m_triggeredBySpellInfo = triggeredBy; m_triggeredByParentSpellInfo = triggeredByParent; - m_caster = caster; m_selfContainer = nullptr; m_referencedFromCurrentSpell = false; m_executeStack = 0; @@ -320,13 +317,93 @@ Spell::Spell(Unit* caster, SpellEntry const *info, bool triggered, ObjectGuid or // wand case if (m_attackType == RANGED_ATTACK) - if (!!(m_caster->getClassMask() & CLASSMASK_WAND_USERS) && m_caster->GetTypeId() == TYPEID_PLAYER) - m_spellSchoolMask = GetSchoolMask(m_caster->GetWeaponDamageSchool(RANGED_ATTACK)); + if (!!(caster->getClassMask() & CLASSMASK_WAND_USERS) && caster->IsPlayer()) + m_spellSchoolMask = GetSchoolMask(caster->GetWeaponDamageSchool(RANGED_ATTACK)); // Set health leech amount to zero m_healthLeech = 0; - m_originalCasterGUID = originalCasterGUID ? originalCasterGUID : m_caster->GetObjectGuid(); + m_originalCasterGUID = originalCasterGUID ? originalCasterGUID : caster->GetObjectGuid(); + + UpdateOriginalCasterPointer(); + + for (int i = 0; i < MAX_EFFECT_INDEX; ++i) + m_currentBasePoints[i] = m_spellInfo->CalculateSimpleValue(SpellEffectIndex(i)); + + m_spellState = SPELL_STATE_NULL; + + m_castPositionX = m_castPositionY = m_castPositionZ = m_castOrientation = 0; + m_TriggerSpells.clear(); + m_preCastSpells.clear(); + m_IsTriggeredSpell = triggered; + //m_AreaAura = false; + m_CastItem = nullptr; + m_IsCastByItem = false; + + unitTarget = nullptr; + itemTarget = nullptr; + corpseTarget = nullptr; + gameObjTarget = nullptr; + focusObject = nullptr; + m_triggeredByAuraSpell = nullptr; + m_triggeredByAuraBasePoints = 0; + + //Auto Shot & Shoot + m_autoRepeat = m_spellInfo->IsAutoRepeatRangedSpell(); + + m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before. + m_casttime = 0; // setup to correct value in Spell::prepare, don't must be used before. + m_timer = 0; // will set to cast time in prepare + m_duration = 0; + + m_channeled = info->IsChanneledSpell(); + + m_needAliveTargetMask = 0; + + // determine reflection + m_canReflect = victim ? m_spellInfo->IsReflectableSpell(caster, victim) : m_spellInfo->IsReflectableSpell(); + + m_isClientStarted = false; + + m_spellAuraHolder = nullptr; + m_delayed = false; + // Must initialize to an element in the list or bad things happen, + // begin changes so use end + m_channeledUpdateIterator = m_channeledHolders.end(); + + CleanupTargetList(); +} + +Spell::Spell(GameObject* caster, SpellEntry const *info, bool triggered, ObjectGuid originalCasterGUID, SpellEntry const* triggeredBy, Unit* victim, SpellEntry const* triggeredByParent): + m_immediateHandled(false), m_needSpellLog(false), m_canTrigger(false), m_setCreatureTarget(false), m_caster(caster), m_casterGo(caster) +{ + MANGOS_ASSERT(caster != nullptr && info != nullptr); + MANGOS_ASSERT(info == sSpellMgr.GetSpellEntry(info->Id) && "`info` must be pointer to a sSpellMgr element"); + + m_successCast = false; + m_destroyed = false; + m_spellInfo = info; + m_triggeredBySpellInfo = triggeredBy; + m_triggeredByParentSpellInfo = triggeredByParent; + m_selfContainer = nullptr; + m_referencedFromCurrentSpell = false; + m_executeStack = 0; + m_delayStart = 0; + m_delayAtDamageCount = 0; + m_isChannelingVisual = false; + + m_applyMultiplierMask = 0; + m_absorbed = 0; + + // Get data for type of attack + m_attackType = m_spellInfo->GetWeaponAttackType(); + + m_spellSchoolMask = info->GetSpellSchoolMask(); // Can be override for some spell (wand shoot for example) + + // Set health leech amount to zero + m_healthLeech = 0; + + m_originalCasterGUID = originalCasterGUID ? originalCasterGUID : caster->GetObjectGuid(); UpdateOriginalCasterPointer(); @@ -385,19 +462,22 @@ Spell::~Spell() template WorldObject* Spell::FindCorpseUsing() { + if (!m_casterUnit) + return nullptr; + // non-standard target selection SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); float max_range = GetSpellMaxRange(srange); WorldObject* result = nullptr; - T u_check(m_caster, max_range); + T u_check(m_casterUnit, max_range); MaNGOS::WorldObjectSearcher searcher(result, u_check); - Cell::VisitGridObjects(m_caster, searcher, max_range); + Cell::VisitGridObjects(m_casterUnit, searcher, max_range); if (!result) - Cell::VisitWorldObjects(m_caster, searcher, max_range); + Cell::VisitWorldObjects(m_casterUnit, searcher, max_range); return result; } @@ -424,8 +504,8 @@ void Spell::FillTargetMap() // TODO: find a way so this is not needed? // for area auras always add caster as target (needed for totems for example) - if (IsAreaAuraEffect(m_spellInfo->Effect[i])) - AddUnitTarget(m_caster, SpellEffectIndex(i)); + if (IsAreaAuraEffect(m_spellInfo->Effect[i]) && m_casterUnit) + AddUnitTarget(m_casterUnit, SpellEffectIndex(i)); // If same target already filled, use it // Example: AoE fear has effects speedup and modfear, with maxtargets = 1 // We dont want to apply speedup to the first, fear to the second. @@ -550,20 +630,10 @@ void Spell::FillTargetMap() switch (m_spellInfo->EffectImplicitTargetB[i]) { case TARGET_ALL_ENEMY_IN_AREA: - // Note: this hack with search required until GO casting not implemented - // environment damage spells already have around enemies targeting but this not help in case nonexistent GO casting support - // currently each enemy selected explicitly and self cast damage - if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) + SetTargetMap(SpellEffectIndex(i), m_spellInfo->EffectImplicitTargetA[i], tmpUnitMap); + SetTargetMap(SpellEffectIndex(i), m_spellInfo->EffectImplicitTargetB[i], tmpUnitMap); + switch (m_spellInfo->Id) { - if (m_targets.getUnitTarget()) - tmpUnitMap.push_back(m_targets.getUnitTarget()); - } - else - { - SetTargetMap(SpellEffectIndex(i), m_spellInfo->EffectImplicitTargetA[i], tmpUnitMap); - SetTargetMap(SpellEffectIndex(i), m_spellInfo->EffectImplicitTargetB[i], tmpUnitMap); - switch (m_spellInfo->Id) - { case 5246: // Exception: Intimidating Shout // The AoE fear does not apply to spell main target (that is stunned by another aura) @@ -595,12 +665,12 @@ void Spell::FillTargetMap() ++itr; } break; - } } break; case 0: SetTargetMap(SpellEffectIndex(i), m_spellInfo->EffectImplicitTargetA[i], tmpUnitMap); - tmpUnitMap.push_back(m_caster); + if (m_casterUnit) + tmpUnitMap.push_back(m_casterUnit); break; default: SetTargetMap(SpellEffectIndex(i), m_spellInfo->EffectImplicitTargetA[i], tmpUnitMap); @@ -970,7 +1040,7 @@ void Spell::AddUnitTarget(Unit* pVictim, SpellEffectIndex effIndex) if (target.missCondition == SPELL_MISS_REFLECT) { // Calculate reflected spell result on caster - target.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, effIndex, m_canReflect, this); + target.reflectResult = m_casterUnit ? m_casterUnit->SpellHitResult(m_casterUnit, m_spellInfo, effIndex, m_canReflect, this) : SPELL_MISS_IMMUNE; if (target.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell target.reflectResult = SPELL_MISS_PARRY; @@ -987,13 +1057,13 @@ void Spell::AddUnitTarget(Unit* pVictim, SpellEffectIndex effIndex) void Spell::AddUnitTarget(ObjectGuid unitGuid, SpellEffectIndex effIndex) { - if (Unit* unit = m_caster->GetObjectGuid() == unitGuid ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGuid)) + if (Unit* unit = m_caster->GetObjectGuid() == unitGuid ? m_casterUnit : ObjectAccessor::GetUnit(*m_caster, unitGuid)) AddUnitTarget(unit, effIndex); } void Spell::CheckAtDelay(TargetInfo* pInf) { - Unit* pTarget = m_caster->GetObjectGuid() == pInf->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, pInf->targetGUID); + Unit* pTarget = m_caster->GetObjectGuid() == pInf->targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_caster, pInf->targetGUID); if (!pTarget) return; @@ -1010,7 +1080,7 @@ void Spell::CheckAtDelay(TargetInfo* pInf) (pTarget->IsImmuneToDamage(m_spellInfo->GetSpellSchoolMask(), m_spellInfo) || pTarget->IsImmuneToSpell(m_spellInfo, pTarget == m_caster))) pInf->missCondition = SPELL_MISS_IMMUNE; - if (pTarget->GetTypeId() == TYPEID_UNIT && ((Creature*)pTarget)->IsInEvadeMode()) + if (pTarget->IsCreature() && ((Creature*)pTarget)->IsInEvadeMode()) pInf->missCondition = SPELL_MISS_EVADE; } @@ -1111,7 +1181,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Get mask of effects for target uint32 mask = target->effectMask; - Unit* unit = m_caster->GetObjectGuid() == target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target->targetGUID); + Unit* unit = m_caster->GetObjectGuid() == target->targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_caster, target->targetGUID); if (!unit) return; @@ -1119,9 +1189,10 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) return; // Get original caster (if exist) and calculate damage/healing from him data - Unit *real_caster = GetAffectiveCaster(); - // FIXME: in case wild GO heal/damage spells will be used target bonuses - Unit *caster = real_caster ? real_caster : m_caster; + WorldObject *pRealCaster = GetAffectiveCasterObject(); + WorldObject *pCaster = pRealCaster ? pRealCaster : m_caster; + Unit* pUnitCaster = pCaster->ToUnit(); + Unit* pRealUnitCaster = pRealCaster->ToUnit(); SpellMissInfo missInfo = target->missCondition; // Need init unitTarget by default unit (can changed in code on reflect) @@ -1191,18 +1262,21 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) DoSpellHitOnUnit(unit, mask); if (missInfo == SPELL_MISS_REFLECT && target->reflectResult == SPELL_MISS_NONE) { - isReflected = true; - DoSpellHitOnUnit(m_caster, mask); - unitTarget = m_caster; + if (m_casterUnit) + { + isReflected = true; + DoSpellHitOnUnit(m_casterUnit, mask); + unitTarget = m_casterUnit; + } } else // in 1.12.1 we need explicit miss info { - if (real_caster && !IsNeedSendToClient()) - real_caster->SendSpellMiss(unit, m_spellInfo->Id, missInfo); + if (pRealCaster && !IsNeedSendToClient()) + pRealCaster->SendSpellMiss(unit, m_spellInfo->Id, missInfo); if (missInfo == SPELL_MISS_MISS || missInfo == SPELL_MISS_RESIST) { - if (real_caster && real_caster != unit) + if (pRealUnitCaster && pRealUnitCaster != unit) { if (!m_spellInfo->IsPositiveSpell() && (m_caster->isVisibleForOrDetect(unit, unit, false) || m_spellInfo->HasAttribute(SPELL_ATTR_EX_IS_PICKPOCKET))) { @@ -1211,7 +1285,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Pickpocket can cause back attack if failed if (m_spellInfo->HasAttribute(SPELL_ATTR_EX_IS_PICKPOCKET)) { - real_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + pRealUnitCaster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); combat = true; } @@ -1220,25 +1294,25 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if (!m_spellInfo->HasAura(SPELL_AURA_MOD_POSSESS)) { if (!unit->isInCombat()) - unit->AttackedBy(real_caster); + unit->AttackedBy(pRealUnitCaster); - unit->AddThreat(real_caster); - unit->SetInCombatWithAggressor(real_caster); + unit->AddThreat(pRealUnitCaster); + unit->SetInCombatWithAggressor(pRealUnitCaster); } - real_caster->SetInCombatWithVictim(unit); + pRealUnitCaster->SetInCombatWithVictim(unit); } else if (m_spellInfo->HasAttribute(SPELL_ATTR_EX3_OUT_OF_COMBAT_ATTACK)) { - unit->SetOutOfCombatWithAggressor(real_caster); - real_caster->SetOutOfCombatWithVictim(unit); + unit->SetOutOfCombatWithAggressor(pRealUnitCaster); + pRealUnitCaster->SetOutOfCombatWithVictim(unit); } } } } } - if (real_caster) + if (pRealUnitCaster) { if (m_spellInfo->AttributesEx & SPELL_ATTR_EX_REFUND_POWER) { @@ -1248,7 +1322,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if ((missInfo == SPELL_MISS_MISS || missInfo == SPELL_MISS_DODGE || missInfo == SPELL_MISS_PARRY) && m_spellInfo->powerType == POWER_ENERGY) { int32 regen = lroundf(m_powerCost * 0.82f); - real_caster->ModifyPower(POWER_ENERGY, regen); + pRealUnitCaster->ModifyPower(POWER_ENERGY, regen); } // Warriors and Druids are refunded 80% of the rage cost on dodge/parry // http://blue.mmo-champion.com/topic/69365-18-02-05-kalgans-response-to-warriors/ @@ -1256,7 +1330,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) else if ((missInfo == SPELL_MISS_PARRY || missInfo == SPELL_MISS_DODGE) && m_spellInfo->powerType == POWER_RAGE) { int32 regen = lroundf(m_powerCost * 0.82f); - real_caster->ModifyPower(POWER_RAGE, regen); + pRealUnitCaster->ModifyPower(POWER_RAGE, regen); } } } @@ -1265,12 +1339,12 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Do healing and triggers if (m_healing) { - bool crit = real_caster && real_caster->IsSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask, BASE_ATTACK, this); + bool crit = pRealCaster && pRealCaster->IsSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask, BASE_ATTACK, this); uint32 addhealth = m_healing; if (crit) { procEx |= PROC_EX_CRITICAL_HIT; - addhealth = caster->SpellCriticalHealingBonus(m_spellInfo, addhealth, nullptr); + addhealth = pCaster->SpellCriticalHealingBonus(m_spellInfo, addhealth, nullptr); #if SUPPORTED_CLIENT_BUILD <= CLIENT_BUILD_1_9_4 // If healing crits, we need to update the execute log data. @@ -1315,21 +1389,21 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) } } - caster->ProcDamageAndSpell(unitTarget, real_caster ? procAttacker : PROC_FLAG_NONE, procVictim, procEx, addhealth, m_attackType, spellInfo, this); + pCaster->ProcDamageAndSpell(unitTarget, pRealCaster ? procAttacker : PROC_FLAG_NONE, procVictim, procEx, addhealth, m_attackType, spellInfo, this); } - int32 gain = caster->DealHeal(unitTarget, addhealth, m_spellInfo, crit); + int32 gain = pCaster->DealHeal(unitTarget, addhealth, m_spellInfo, crit); - float classThreatModifier = caster->getClass() == CLASS_PALADIN ? 0.25f : 0.5f; + float classThreatModifier = pUnitCaster && pUnitCaster->getClass() == CLASS_PALADIN ? 0.25f : 0.5f; - if (real_caster) - unitTarget->getHostileRefManager().threatAssist(real_caster, float(gain) * classThreatModifier * sSpellMgr.GetSpellThreatMultiplier(m_spellInfo), m_spellInfo); + if (pRealUnitCaster) + unitTarget->getHostileRefManager().threatAssist(pRealUnitCaster, float(gain) * classThreatModifier * sSpellMgr.GetSpellThreatMultiplier(m_spellInfo), m_spellInfo); } // Do damage and triggers else if (m_damage) { // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, GetFirstSchoolInMask(m_spellSchoolMask)); + SpellNonMeleeDamage damageInfo(pCaster, unitTarget, m_spellInfo->Id, GetFirstSchoolInMask(m_spellSchoolMask)); damageInfo.spell = this; // World of Warcraft Client Patch 1.11.0 (2006-06-20) @@ -1351,13 +1425,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) // Add bonuses and fill damageInfo struct else { - caster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo, m_attackType, this); + pCaster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo, m_attackType, this); } - unitTarget->CalculateAbsorbResistBlock(caster, &damageInfo, m_spellInfo, BASE_ATTACK, this); + unitTarget->CalculateAbsorbResistBlock(pCaster, &damageInfo, m_spellInfo, BASE_ATTACK, this); m_absorbed = damageInfo.absorb; - caster->DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); + pCaster->DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); // terribly ugly hack for Gluth Decimate to not be affected by any damage modifiers. // SPELL_ATTR_EX3_UNK29 is probably meant to make the spell ignore any damage modifiers, @@ -1368,14 +1442,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) } // Send log damage message to client - caster->SendSpellNonMeleeDamageLog(&damageInfo); + pCaster->SendSpellNonMeleeDamageLog(&damageInfo); procEx = createProcExtendMask(&damageInfo, missInfo); procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE; // (HACK) trigger Vengeance on weapon crits for Paladins // item procs should probably trigger for all classes, e.g. Judgement of Wisdom, but many issues to test for first. Paladin exclusive for now. - if (m_CastItem && m_caster->getClass() == CLASS_PALADIN && (procEx & PROC_EX_CRITICAL_HIT) && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_CANT_TRIGGER_PROC)) + if (m_CastItem && m_casterUnit && m_casterUnit->getClass() == CLASS_PALADIN && (procEx & PROC_EX_CRITICAL_HIT) && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_CANT_TRIGGER_PROC)) m_canTrigger = true; // JoR and JoC: Paladin melee spells trigger melee procs instead of magic if ((m_spellInfo->IsFitToFamilyMask() && m_spellInfo->SpellIconID == 25) || @@ -1386,9 +1460,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) } // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) if (m_canTrigger) - caster->ProcDamageAndSpell(unitTarget, real_caster ? procAttacker : PROC_FLAG_NONE, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, this); + pCaster->ProcDamageAndSpell(unitTarget, pRealCaster ? procAttacker : PROC_FLAG_NONE, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, this); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) { // trigger weapon enchants for weapon based spells; exclude spells that stop attack, because may break CC if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && !(m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET)) @@ -1422,13 +1496,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) } } - caster->DealSpellDamage(&damageInfo, true); + pCaster->DealSpellDamage(&damageInfo, true); // Courroux Naturel a 20% de chance de faire proc WF. - if (m_spellInfo->Id == 17364 && caster->GetTypeId() == TYPEID_PLAYER) + if (m_spellInfo->Id == 17364 && pCaster->IsPlayer()) { - Player* pCaster = caster->ToPlayer(); - Item *item = pCaster->GetWeaponForAttack(BASE_ATTACK, true, true); + Player* pPlayer = pCaster->ToPlayer(); + Item *item = pPlayer->GetWeaponForAttack(BASE_ATTACK, true, true); if (item) { uint32 enchant_id = item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT); @@ -1436,7 +1510,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) if (pEnchant && roll_chance_f(20.0f)) if (SpellEntry const *spellInfo = sSpellMgr.GetSpellEntry(pEnchant->spellid[0])) if (spellInfo->IsFitToFamily()) - pCaster->CastSpell(unitTarget, pEnchant->spellid[0], true, item); + pPlayer->CastSpell(unitTarget, pEnchant->spellid[0], true, item); } } } @@ -1468,11 +1542,11 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) } } // Sunder Armor triggers weapon proc as well as normal procs despite dealing no damage - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->IsFitToFamily() && missInfo == SPELL_MISS_NONE) + if (m_caster->IsPlayer() && m_spellInfo->IsFitToFamily() && missInfo == SPELL_MISS_NONE) ((Player*)m_caster)->CastItemCombatSpell(unitTarget, BASE_ATTACK); // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, GetFirstSchoolInMask(m_spellSchoolMask)); + SpellNonMeleeDamage damageInfo(pCaster, unitTarget, m_spellInfo->Id, GetFirstSchoolInMask(m_spellSchoolMask)); procEx = createProcExtendMask(&damageInfo, missInfo); // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge) uint32 dmg = 0; @@ -1490,7 +1564,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) { dmg = 1; // Override proc flags for offensive dispel - if (caster->IsHostileTo(unitTarget)) + if (pCaster->IsHostileTo(unitTarget)) { procAttacker &= ~(PROC_FLAG_SUCCESSFUL_NONE_POSITIVE_SPELL | PROC_FLAG_SUCCESSFUL_CURE_SPELL_CAST); procAttacker |= PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT; @@ -1500,31 +1574,33 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) } } - caster->ProcDamageAndSpell(unitTarget, real_caster ? procAttacker : PROC_FLAG_NONE, procVictim, procEx, dmg, m_attackType, m_spellInfo, this); + pCaster->ProcDamageAndSpell(unitTarget, pRealCaster ? procAttacker : PROC_FLAG_NONE, procVictim, procEx, dmg, m_attackType, m_spellInfo, this); } if (missInfo != SPELL_MISS_NONE) return; // Call scripted function for AI if this spell is casted upon a creature - if (unit->GetTypeId() == TYPEID_UNIT) + if (unit->IsCreature()) { // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished) // ignore pets or autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if (real_caster && !((Creature*)unit)->IsPet() && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive()) - if (Player* p = real_caster->GetCharmerOrOwnerPlayerOrPlayerItself()) + if (pRealUnitCaster && !((Creature*)unit)->IsPet() && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive()) + if (Player* p = pRealUnitCaster->GetCharmerOrOwnerPlayerOrPlayerItself()) p->RewardPlayerAndGroupAtCast(unit, m_spellInfo->Id); - if (((Creature*)unit)->AI()) - ((Creature*)unit)->AI()->SpellHit(m_caster, m_spellInfo); - - if (ZoneScript* pZoneScript = unit->GetZoneScript()) { - pZoneScript->OnCreatureSpellHit(m_caster, unit->ToCreature(), m_spellInfo); + if (m_casterUnit) + { + if (((Creature*)unit)->AI()) + ((Creature*)unit)->AI()->SpellHit(m_casterUnit, m_spellInfo); + + if (ZoneScript* pZoneScript = unit->GetZoneScript()) + pZoneScript->OnCreatureSpellHit(m_casterUnit, unit->ToCreature(), m_spellInfo); } } // Tell any pets to stop attacking the target on application of breakable crowd control spells - if (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_DAMAGE && unit->HasBreakableByDamageCrowdControlAura(m_caster)) + if (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_DAMAGE && m_casterUnit && unit->HasBreakableByDamageCrowdControlAura(m_casterUnit)) { Unit::AttackerSet attackers = unit->getAttackers(); for (Unit::AttackerSet::iterator itr = attackers.begin(); itr != attackers.end(); ++itr) @@ -1535,7 +1611,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target) } // Call scripted function for AI if this spell is casted by a creature - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) + if (m_caster->IsCreature() && ((Creature*)m_caster)->AI()) ((Creature*)m_caster)->AI()->SpellHitTarget(unit, m_spellInfo); } @@ -1544,12 +1620,13 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) if (!unit) return; - Unit* realCaster = GetAffectiveCaster(); + WorldObject* pRealCaster = GetAffectiveCasterObject(); + Unit* pRealUnitCaster = GetAffectiveCaster(); if (!effectMask) { - if (realCaster && !unit->isInCombat()) - unit->AttackedBy(realCaster); + if (pRealUnitCaster && !unit->isInCombat()) + unit->AttackedBy(pRealUnitCaster); return; } @@ -1557,7 +1634,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) for (int eff = 0; eff < MAX_EFFECT_INDEX; ++eff) if (unit->IsEffectResist(m_spellInfo, eff)) { - if ((realCaster && realCaster->IsPlayer() && realCaster->ToPlayer()->HasOption(PLAYER_CHEAT_UNRANDOMIZE)) || + if ((pRealUnitCaster && pRealUnitCaster->IsPlayer() && pRealUnitCaster->ToPlayer()->HasOption(PLAYER_CHEAT_UNRANDOMIZE)) || (unit->IsPlayer() && unit->ToPlayer()->HasOption(PLAYER_CHEAT_UNRANDOMIZE))) break; effectMask &= ~(1 << eff); @@ -1568,27 +1645,27 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) // Recheck immune (only for delayed spells) if (m_caster != unit && m_spellInfo->speed && ( unit->IsImmuneToDamage(m_spellInfo->GetSpellSchoolMask(), m_spellInfo) || - unit->IsImmuneToSpell(m_spellInfo, unit == realCaster))) + unit->IsImmuneToSpell(m_spellInfo, unit == pRealUnitCaster))) { - if (realCaster) - realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); + if (pRealCaster) + pRealCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); ResetEffectDamageAndHeal(); return; } - if (realCaster && realCaster != unit) + if (pRealCaster && pRealCaster != unit) { // Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells if (unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) && unit->GetCharmerOrOwnerGuid() != m_caster->GetObjectGuid()) { - realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); + pRealCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); ResetEffectDamageAndHeal(); return; } - if (!realCaster->IsFriendlyTo(unit)) + if (!pRealCaster->IsFriendlyTo(unit)) { if (m_damage) // Example: stealth. unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_HITBYSPELL); @@ -1604,7 +1681,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) if (m_delayed && unit == m_targets.getUnitTarget() && !unit->isVisibleForOrDetect(m_caster, m_caster, false)) { - realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); + pRealCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); ResetEffectDamageAndHeal(); return; } @@ -1625,24 +1702,30 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) } // caster can be detected but have stealth aura - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); - m_caster->RemoveNonPassiveSpellsCausingAura(SPELL_AURA_MOD_INVISIBILITY); - - if (!m_spellInfo->HasAura(SPELL_AURA_MOD_POSSESS)) + if (m_casterUnit) { - if (!unit->isInCombat()) - unit->AttackedBy(realCaster); - - unit->AddThreat(realCaster); - unit->SetInCombatWithAggressor(realCaster); + m_casterUnit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + m_casterUnit->RemoveNonPassiveSpellsCausingAura(SPELL_AURA_MOD_INVISIBILITY); } - realCaster->SetInCombatWithVictim(unit); + if (pRealUnitCaster) + { + if (!m_spellInfo->HasAura(SPELL_AURA_MOD_POSSESS)) + { + if (!unit->isInCombat()) + unit->AttackedBy(pRealUnitCaster); + + unit->AddThreat(pRealUnitCaster); + unit->SetInCombatWithAggressor(pRealUnitCaster); + } + + pRealUnitCaster->SetInCombatWithVictim(unit); + } } - else if (m_spellInfo->HasAttribute(SPELL_ATTR_EX3_OUT_OF_COMBAT_ATTACK)) + else if (m_spellInfo->HasAttribute(SPELL_ATTR_EX3_OUT_OF_COMBAT_ATTACK) && pRealUnitCaster) { - unit->SetOutOfCombatWithAggressor(realCaster); - realCaster->SetOutOfCombatWithVictim(unit); + unit->SetOutOfCombatWithAggressor(pRealUnitCaster); + pRealUnitCaster->SetOutOfCombatWithVictim(unit); } } } @@ -1651,7 +1734,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) // for delayed spells ignore negative spells (after duel end) for friendly targets if (m_delayed && !m_spellInfo->IsPositiveSpell()) { - realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); + pRealCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); ResetEffectDamageAndHeal(); return; } @@ -1659,19 +1742,23 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) // World of Warcraft Client Patch (2004-11-07) // - Healing and buffing NPCs will not flag you for PvP unless those // NPCs are in combat. - if (unit->isInCombat()) + if (pRealUnitCaster) { - if (!(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO)) + if (unit->isInCombat()) { - realCaster->SetInCombatWithAssisted(unit); - unit->getHostileRefManager().threatAssist(realCaster, 0.0f, m_spellInfo); + if (!(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO)) + { + pRealUnitCaster->SetInCombatWithAssisted(unit); + unit->getHostileRefManager().threatAssist(pRealUnitCaster, 0.0f, m_spellInfo); + } + } + else if (unit->IsPvP() && unit->IsPlayer()) + { + if (Player* pPlayer = pRealUnitCaster->GetCharmerOrOwnerPlayerOrPlayerItself()) + pPlayer->UpdatePvP(true); } } - else if (unit->IsPvP() && unit->IsPlayer()) - { - if (Player* pPlayer = realCaster->GetCharmerOrOwnerPlayerOrPlayerItself()) - pPlayer->UpdatePvP(true); - } + } } @@ -1679,8 +1766,8 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) m_diminishGroup = m_spellInfo->GetDiminishingReturnsGroup(m_triggeredByAuraSpell); m_diminishLevel = unit->GetDiminishing(m_diminishGroup); - if (realCaster) - DEBUG_UNIT(realCaster, DEBUG_DR, "[%u] Groupe DR : %u. Niveau : %u.", m_spellInfo->Id, m_diminishGroup, m_diminishLevel); + if (pRealUnitCaster) + DEBUG_UNIT(pRealUnitCaster, DEBUG_DR, "[%u] Groupe DR : %u. Niveau : %u.", m_spellInfo->Id, m_diminishGroup, m_diminishLevel); // Increase Diminishing on unit, current informations for actually casts will use values above if ((GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_PLAYER && unit->IsPlayer()) || @@ -1692,7 +1779,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) if (m_spellInfo->IsSpellAppliesAura(effectMask)) { - m_spellAuraHolder = CreateSpellAuraHolder(m_spellInfo, unit, realCaster, m_CastItem); + m_spellAuraHolder = CreateSpellAuraHolder(m_spellInfo, unit, pRealUnitCaster ? pRealUnitCaster : unit, m_CastItem); m_spellAuraHolder->SetTriggered(IsTriggered()); m_spellAuraHolder->setDiminishGroup(m_diminishGroup); m_spellAuraHolder->setDiminishLevel(m_diminishLevel); @@ -1710,8 +1797,8 @@ void Spell::DoSpellHitOnUnit(Unit *unit, uint32 effectMask) // Get multiplier float multiplier = m_spellInfo->DmgMultiplier[effectNumber]; // Apply multiplier mods - if (realCaster) - if (Player* modOwner = realCaster->GetSpellModOwner()) + if (pRealUnitCaster) + if (Player* modOwner = pRealUnitCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier, this); m_damageMultipliers[effectNumber] *= multiplier; } @@ -1793,9 +1880,9 @@ void Spell::DoAllEffectOnTarget(GOTargetInfo *target) // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished) // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if (!IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive()) + if (!IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() && m_casterUnit) { - if (Player* p = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself()) + if (Player* p = m_casterUnit->GetCharmerOrOwnerPlayerOrPlayerItself()) p->RewardPlayerAndGroupAtCast(go, m_spellInfo->Id); } } @@ -1816,14 +1903,13 @@ void Spell::HandleDelayedSpellLaunch(TargetInfo *target) // Get mask of effects for target uint32 mask = target->effectMask; - Unit* unit = m_caster->GetObjectGuid() == target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target->targetGUID); + Unit* unit = m_caster->GetObjectGuid() == target->targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_caster, target->targetGUID); if (!unit) return; // Get original caster (if exist) and calculate damage/healing from him data - Unit *real_caster = GetAffectiveCaster(); - // FIXME: in case wild GO heal/damage spells will be used target bonuses - Unit *caster = real_caster ? real_caster : m_caster; + WorldObject *pRealCaster = GetAffectiveCasterObject(); + WorldObject *pCaster = pRealCaster ? pRealCaster : m_caster; SpellMissInfo missInfo = target->missCondition; // Need init unitTarget by default unit (can changed in code on reflect) @@ -1834,9 +1920,9 @@ void Spell::HandleDelayedSpellLaunch(TargetInfo *target) ResetEffectDamageAndHeal(); // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, GetFirstSchoolInMask(m_spellSchoolMask)); + SpellNonMeleeDamage damageInfo(pCaster, unitTarget, m_spellInfo->Id, GetFirstSchoolInMask(m_spellSchoolMask)); - if (unit && !caster->IsFriendlyTo(unit)) + if (unit && !pCaster->IsFriendlyTo(unit)) unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_HITBYSPELL); if (missInfo == SPELL_MISS_NONE || (missInfo == SPELL_MISS_REFLECT && target->reflectResult == SPELL_MISS_NONE)) { @@ -1850,8 +1936,8 @@ void Spell::HandleDelayedSpellLaunch(TargetInfo *target) // Get multiplier float multiplier = m_spellInfo->DmgMultiplier[effectNumber]; // Apply multiplier mods - if (real_caster) - if (Player* modOwner = real_caster->GetSpellModOwner()) + if (Unit* pRealUnitCaster = ToUnit(pRealCaster)) + if (Player* modOwner = pRealUnitCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier, this); m_damageMultipliers[effectNumber] *= multiplier; } @@ -1859,7 +1945,7 @@ void Spell::HandleDelayedSpellLaunch(TargetInfo *target) } if (m_damage > 0) - caster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo, m_attackType, this); + pCaster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo, m_attackType, this); } target->damage = damageInfo.damage; @@ -1885,9 +1971,9 @@ void Spell::InitializeDamageMultipliers() } } -// : Modification du fonctionnement de cette fonction -// Indique si une canalisation doit s'arreter ou non -// Pour cela, on regarde s'il reste des cibles. +// : Changing the operation of this function +// Indicates whether the cast should stop or not +// For this, we look if there are any targets. bool Spell::HasValidUnitPresentInTargetList() { @@ -1898,11 +1984,11 @@ bool Spell::HasValidUnitPresentInTargetList() if (m_spellInfo->EffectImplicitTargetA[i] != TARGET_SELF) needTargetsMask |= 1 << i; - // Cette canalisation n'a pas besoin de cible: elle se lance sur le lanceur de sort - // Elle ne s'arrete pas, sauf s'il meurt biensur. + // This channel does not need a target: it launches on the caster + // It does not stop unless it dies of course. if (!needTargetsMask) { - if (m_caster->isAlive() == m_spellInfo->IsDeathOnlySpell()) + if (m_casterUnit && m_casterUnit->isAlive() == m_spellInfo->IsDeathOnlySpell()) return false; return true; } @@ -1914,7 +2000,7 @@ bool Spell::HasValidUnitPresentInTargetList() if (ihit->missCondition != SPELL_MISS_NONE) continue; - Unit *unit = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); + Unit *unit = m_caster->GetObjectGuid() == ihit->targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); // either unit is alive and normal spell, or unit dead and deathonly-spell if (unit && (unit->isAlive() != m_spellInfo->IsDeathOnlySpell())) @@ -1941,7 +2027,7 @@ struct ChainHealingOrder { if (Target == MainTarget) return 0; - else if (Target->GetTypeId() == TYPEID_PLAYER && MainTarget->GetTypeId() == TYPEID_PLAYER && + else if (Target->IsPlayer() && MainTarget->IsPlayer() && ((Player const*)Target)->IsInSameRaidWith((Player const*)MainTarget)) { if (Target->GetHealth() == Target->GetMaxHealth()) @@ -1970,10 +2056,10 @@ public: // The spell target is always first unless there is a target at _completely_ the same position (unbelievable case) struct TargetDistanceOrderNear { - const Unit* MainTarget; - TargetDistanceOrderNear(const Unit* Target) : MainTarget(Target) {}; + const WorldObject* MainTarget; + TargetDistanceOrderNear(const WorldObject* Target) : MainTarget(Target) {}; // functor for operator ">" - bool operator()(const Unit* _Left, const Unit* _Right) const + bool operator()(const WorldObject* _Left, const WorldObject* _Right) const { return MainTarget->GetDistanceOrder(_Left, _Right); } @@ -2054,13 +2140,17 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& // Spell states 30yd radius, which you would think is the max radius once its all grown, // however, the visual of the spell goes no further than ~20yd, so lets stop it there. // It will instantly get a 2(?) yd radius, and grow to 20 from there - if (SpellAuraHolder* auraHolder = m_caster->GetSpellAuraHolder(28158)) + if (m_casterUnit) { - const int maxDur = auraHolder->GetAuraMaxDuration(); - const int currTick = maxDur - auraHolder->GetAuraDuration(); - radius = 18.0f / maxDur*currTick + 2; - //radius = 0.5f * (60000 - auraHolder->GetAuraDuration()) * 0.001f; + if (SpellAuraHolder* auraHolder = m_casterUnit->GetSpellAuraHolder(28158)) + { + const int maxDur = auraHolder->GetAuraMaxDuration(); + const int currTick = maxDur - auraHolder->GetAuraDuration(); + radius = 18.0f / maxDur*currTick + 2; + //radius = 0.5f * (60000 - auraHolder->GetAuraDuration()) * 0.001f; + } } + break; } case 29310: // Mana Burn (Heigan, naxxramas) @@ -2108,12 +2198,18 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& switch (targetMode) { + case TARGET_SELF: + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); + else if (m_casterGo) + AddGOTarget(m_casterGo, effIndex); + break; case TARGET_TOTEM_EARTH: case TARGET_TOTEM_WATER: case TARGET_TOTEM_AIR: case TARGET_TOTEM_FIRE: - case TARGET_SELF: - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; case TARGET_RANDOM_ENEMY_CHAIN_IN_AREA: { @@ -2237,7 +2333,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& } case TARGET_PET: { - Pet* tmpUnit = m_caster->GetPet(); + Pet* tmpUnit = m_casterUnit ? m_casterUnit->GetPet() : nullptr; if (!tmpUnit) break; targetUnitMap.push_back(tmpUnit); break; @@ -2413,7 +2509,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& } // exclude caster - targetUnitMap.remove(m_caster); + if (m_casterUnit) + targetUnitMap.remove(m_casterUnit); break; } case TARGET_AREAEFFECT_CUSTOM: @@ -2422,7 +2519,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& break; else if (m_spellInfo->Effect[effIndex] == SPELL_EFFECT_SUMMON || m_spellInfo->Effect[effIndex] == SPELL_EFFECT_SUMMON_OBJECT_WILD) { - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; } @@ -2527,7 +2625,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case SPELL_EFFECT_PERSISTENT_AREA_AURA: break; case SPELL_EFFECT_SUMMON: - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; default: FillAreaTargets(targetUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_AOE_DAMAGE); @@ -2544,10 +2643,12 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case TARGET_ALL_PARTY_AROUND_CASTER: case TARGET_ALL_PARTY_AROUND_CASTER_2: case TARGET_ALL_PARTY: - FillRaidOrPartyTargets(targetUnitMap, m_caster, radius, false, true, true); + if (m_casterUnit) + FillRaidOrPartyTargets(targetUnitMap, m_casterUnit, radius, false, true, true); break; case TARGET_ALL_RAID_AROUND_CASTER: - FillRaidOrPartyTargets(targetUnitMap, m_caster, radius, true, true, false); + if (m_casterUnit) + FillRaidOrPartyTargets(targetUnitMap, m_casterUnit, radius, true, true, false); break; case TARGET_SINGLE_FRIEND: case TARGET_SINGLE_FRIEND_2: @@ -2581,11 +2682,11 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& // Can only be casted on group's members or its pets Group *pGroup = nullptr; - Unit* owner = m_caster->GetCharmerOrOwner(); + Unit* owner = m_casterUnit ? m_casterUnit->GetCharmerOrOwner() : nullptr; Unit *targetOwner = target->GetCharmerOrOwner(); if (owner) { - if (owner->GetTypeId() == TYPEID_PLAYER) + if (owner->IsPlayer()) { if (target == owner) { @@ -2595,9 +2696,9 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& pGroup = ((Player*)owner)->GetGroup(); } } - else if (m_caster->GetTypeId() == TYPEID_PLAYER) + else if (m_caster->IsPlayer()) { - if (targetOwner == m_caster && target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->IsPet()) + if (targetOwner == m_caster && target->IsCreature() && ((Creature*)target)->IsPet()) { targetUnitMap.push_back(target); break; @@ -2610,14 +2711,14 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& // Our target can also be a player's pet who's grouped with us or our pet. But can't be controlled player if (targetOwner) { - if (targetOwner->GetTypeId() == TYPEID_PLAYER && - target->GetTypeId() == TYPEID_UNIT && (((Creature*)target)->IsPet()) && + if (targetOwner->IsPlayer() && + target->IsCreature() && (((Creature*)target)->IsPet()) && target->GetOwnerGuid() == targetOwner->GetObjectGuid() && pGroup->IsMember(((Player*)targetOwner)->GetObjectGuid())) targetUnitMap.push_back(target); } // 1Our target can be a player who is on our group - else if (target->GetTypeId() == TYPEID_PLAYER && pGroup->IsMember(((Player*)target)->GetObjectGuid())) + else if (target->IsPlayer() && pGroup->IsMember(((Player*)target)->GetObjectGuid())) targetUnitMap.push_back(target); } } @@ -2738,7 +2839,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& AddItemTarget(m_targets.getItemTarget(), effIndex); break; case TARGET_MASTER: - if (Unit* owner = m_caster->GetCharmerOrOwner()) + if (Unit* owner = m_casterUnit ? m_casterUnit->GetCharmerOrOwner() : nullptr) targetUnitMap.push_back(owner); break; case TARGET_ALL_ENEMY_IN_AREA_CHANNELED: @@ -2748,16 +2849,17 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& break; case TARGET_AREAEFFECT_PARTY: { - Unit* owner = m_caster->GetCharmerOrOwner(); + Unit* owner = m_casterUnit ? m_casterUnit->GetCharmerOrOwner() : nullptr; Player *pTarget = nullptr; if (owner) { - targetUnitMap.push_back(m_caster); - if (owner->GetTypeId() == TYPEID_PLAYER) + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); + if (owner->IsPlayer()) pTarget = (Player*)owner; } - else if (m_caster->GetTypeId() == TYPEID_PLAYER) + else if (m_caster->IsPlayer()) { if (Unit* target = m_targets.getUnitTarget()) { @@ -2766,7 +2868,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (((Creature*)target)->IsPet()) { Unit *targetOwner = target->GetOwner(); - if (targetOwner->GetTypeId() == TYPEID_PLAYER) + if (targetOwner->IsPlayer()) pTarget = (Player*)targetOwner; } } @@ -2821,7 +2923,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& break; } case TARGET_SELF_FISHING: - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; case TARGET_CHAIN_HEAL: { @@ -2840,8 +2943,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& FillAreaTargets(tempTargetUnitMap, max_range, PUSH_SELF_CENTER, SPELL_TARGETS_FRIENDLY); - if (m_caster != pUnitTarget && std::find(tempTargetUnitMap.begin(), tempTargetUnitMap.end(), m_caster) == tempTargetUnitMap.end()) - tempTargetUnitMap.push_front(m_caster); + if (m_casterUnit && m_casterUnit != pUnitTarget && std::find(tempTargetUnitMap.begin(), tempTargetUnitMap.end(), m_casterUnit) == tempTargetUnitMap.end()) + tempTargetUnitMap.push_front(m_casterUnit); tempTargetUnitMap.sort(TargetDistanceOrderNear(pUnitTarget)); @@ -2899,7 +3002,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& } case TARGET_AREAEFFECT_PARTY_AND_CLASS: { - Player* targetPlayer = m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER + Player* targetPlayer = m_targets.getUnitTarget() && m_targets.getUnitTarget()->IsPlayer() ? (Player*)m_targets.getUnitTarget() : NULL; Group* pGroup = targetPlayer ? targetPlayer->GetGroup() : NULL; @@ -2985,7 +3088,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& m_targets.setDestination(x, y, z); } - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; } case TARGET_EFFECT_SELECT: @@ -3018,7 +3122,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& else { // clear cooldown at fail - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id, true); SendCastResult(SPELL_FAILED_NO_EDIBLE_CORPSES); finish(false); @@ -3053,8 +3157,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (m_targets.getUnitTarget()) targetUnitMap.push_back(m_targets.getUnitTarget()); // Triggered spells have additional spell targets - cast them even if no explicit unit target is given (required for spell 50516 for example) - else if (m_spellInfo->Effect[effIndex] == SPELL_EFFECT_TRIGGER_SPELL) - targetUnitMap.push_back(m_caster); + else if (m_spellInfo->Effect[effIndex] == SPELL_EFFECT_TRIGGER_SPELL && m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; case SPELL_EFFECT_SUMMON_PLAYER: if (m_targets.getUnitTarget()) @@ -3079,7 +3183,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& targetUnitMap.push_back(m_targets.getUnitTarget()); } else */ - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; case SPELL_EFFECT_TRANS_DOOR: { @@ -3089,7 +3194,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& m_caster->GetRandomPoint(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), 5.0f, tmp_x, tmp_y, tmp_z); m_targets.setDestination(tmp_x, tmp_y, tmp_z); } - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; } case SPELL_EFFECT_SUMMON_WILD: @@ -3099,23 +3205,36 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (GameObject* pSummoner = m_caster->GetMap()->GetGameObject(m_originalCasterGUID)) m_targets.setDestination(pSummoner->GetPositionX(), pSummoner->GetPositionY(), pSummoner->GetPositionZ()); } - // no break + + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); + else if (m_casterGo) + AddGOTarget(m_casterGo, effIndex); + break; + } + case SPELL_EFFECT_SUMMON_DEMON: + { + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); + else if (m_casterGo) + AddGOTarget(m_casterGo, effIndex); + break; } case SPELL_EFFECT_SUMMON_CHANGE_ITEM: case SPELL_EFFECT_SUMMON_GUARDIAN: case SPELL_EFFECT_ADD_FARSIGHT: case SPELL_EFFECT_STUCK: case SPELL_EFFECT_DESTROY_ALL_TOTEMS: - case SPELL_EFFECT_SUMMON_DEMON: case SPELL_EFFECT_SKILL: - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; case SPELL_EFFECT_PERSISTENT_AREA_AURA: if (Unit* currentTarget = m_targets.getUnitTarget()) m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ()); break; case SPELL_EFFECT_LEARN_PET_SPELL: - if (Pet* pet = m_caster->GetPet()) + if (Pet* pet = m_casterUnit ? m_casterUnit->GetPet() : nullptr) targetUnitMap.push_back(pet); break; case SPELL_EFFECT_ENCHANT_ITEM: @@ -3130,7 +3249,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& { case SPELL_AURA_ADD_FLAT_MODIFIER: // some spell mods auras have 0 target modes instead expected TARGET_SELF(1) (and present for other ranks for same spell for example) case SPELL_AURA_ADD_PCT_MODIFIER: - targetUnitMap.push_back(m_caster); + if (m_casterUnit) + targetUnitMap.push_back(m_casterUnit); break; default: // apply to target in other case if (m_targets.getUnitTarget()) @@ -3315,7 +3435,7 @@ SpellCastResult Spell::prepare(Aura* triggeredByAura, uint32 chance) } // Fill cost data - m_powerCost = CalculatePowerCost(m_spellInfo, m_caster, this, m_CastItem); + m_powerCost = CalculatePowerCost(m_spellInfo, m_casterUnit, this, m_CastItem); if (Player* pPlayerCaster = m_caster->ToPlayer()) if (pPlayerCaster->HasOption(PLAYER_CHEAT_NO_POWER)) @@ -3385,7 +3505,7 @@ SpellCastResult Spell::prepare(Aura* triggeredByAura, uint32 chance) // ever be Gnomish Cloaking Device, since it's a special case and not removed on // opcode receive if (m_caster->IsPlayer() && m_targets.getGOTarget()) - m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ON_CAST_SPELL); + m_casterUnit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ON_CAST_SPELL); } OnSpellLaunch(); @@ -3395,7 +3515,7 @@ SpellCastResult Spell::prepare(Aura* triggeredByAura, uint32 chance) // [Nostalrius] Stop pets casting channeled spells ! (succubus seduce ...) if (m_timer && channeled) if (m_caster->IsPet()) - m_caster->StopMoving(); + m_casterUnit->StopMoving(); // This is used so that creatures face the target on which they are casting if (m_setCreatureTarget = (m_caster->IsCreature() && (channeled || (!m_IsTriggeredSpell && m_timer)) && m_targets.getUnitTarget() && IsExplicitlySelectedUnitTarget(m_spellInfo->EffectImplicitTargetA[0]) && static_cast(m_caster)->CanHaveTarget())) @@ -3452,7 +3572,7 @@ void Spell::cancel() { case SPELL_STATE_PREPARING: CancelGlobalCooldown(); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) m_caster->ToPlayer()->RestoreSpellMods(this); //(no break) case SPELL_STATE_DELAYED: @@ -3471,7 +3591,7 @@ void Spell::cancel() continue; if (ihit->missCondition == SPELL_MISS_NONE) { - Unit* unit = m_caster->GetObjectGuid() == (*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); + Unit* unit = m_caster->GetObjectGuid() == (*ihit).targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); if (unit && unit->isAlive()) unit->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetObjectGuid()); } @@ -3480,7 +3600,7 @@ void Spell::cancel() SendChannelUpdate(0); SendInterrupted(0); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) { // spell is canceled-take mods and clear list m_caster->ToPlayer()->RemoveSpellMods(this); @@ -3515,7 +3635,8 @@ void Spell::cancel() finish(false); m_caster->RemoveDynObject(m_spellInfo->Id); - m_caster->RemoveGameObject(m_spellInfo->Id, true); + if (m_casterUnit) + m_casterUnit->RemoveGameObject(m_spellInfo->Id, true); } void Spell::cast(bool skipCheck) @@ -3556,11 +3677,14 @@ void Spell::cast(bool skipCheck) return; } - // Ivina : Added the case when caster is charmed and not controlled. - if ((m_caster->GetTypeId() != TYPEID_PLAYER) || ((m_caster->GetCharmerGuid()) && (!m_caster->hasUnitState(UNIT_STAT_POSSESSED)))) + if (m_casterUnit) { - if (m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) - m_caster->SetInFront(m_targets.getUnitTarget()); + // Ivina : Added the case when caster is charmed and not controlled. + if ((!m_caster->IsPlayer()) || ((m_casterUnit->GetCharmerGuid()) && (!m_casterUnit->hasUnitState(UNIT_STAT_POSSESSED)))) + { + if (m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) + m_casterUnit->SetInFront(m_targets.getUnitTarget()); + } } SpellCastResult castResult = SPELL_CAST_OK; @@ -3568,14 +3692,14 @@ void Spell::cast(bool skipCheck) { // Nostalrius - compute power cost once again at cast finished // (in case of mana reduction buff proc while casting) - m_powerCost = CalculatePowerCost(m_spellInfo, m_caster, this, m_CastItem); + m_powerCost = CalculatePowerCost(m_spellInfo, m_casterUnit, this, m_CastItem); castResult = CheckPower(); if (castResult != SPELL_CAST_OK) { SendInterrupted(2); SendCastResult(castResult); //restore spell mods - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) m_caster->ToPlayer()->RestoreSpellMods(this); finish(false); m_caster->DecreaseCastCounter(); @@ -3593,14 +3717,12 @@ void Spell::cast(bool skipCheck) m_caster->DecreaseCastCounter(); SetExecutedCurrently(false); //restore spell mods - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) m_caster->ToPlayer()->RestoreSpellMods(this); return; } } - m_caster->SetLastCastedSpell(m_spellInfo->Id, m_isClientStarted); - // different triggred (for caster) and precast (casted before apply effect to target) cases switch (m_spellInfo->SpellFamilyName) { @@ -3697,15 +3819,15 @@ void Spell::cast(bool skipCheck) { m_caster->DecreaseCastCounter(); SetExecutedCurrently(false); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) m_caster->ToPlayer()->RestoreSpellMods(this); return; } // CAST SPELL // Remove any remaining invis auras on cast completion, should only be gnomish cloaking device - if (!m_IsTriggeredSpell && !m_spellInfo->HasAttribute(SPELL_ATTR_EX_NOT_BREAK_STEALTH)) - m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ON_CAST_SPELL); + if (!m_IsTriggeredSpell && !m_spellInfo->HasAttribute(SPELL_ATTR_EX_NOT_BREAK_STEALTH) && m_casterUnit) + m_casterUnit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ON_CAST_SPELL); SendSpellCooldown(); @@ -3736,10 +3858,10 @@ void Spell::cast(bool skipCheck) } // Shaman totems. Trigger spell cast procs, but not others - if (m_spellInfo->IsTotemSummonSpell() && m_canTrigger) + if (m_spellInfo->IsTotemSummonSpell() && m_canTrigger && m_casterUnit) { uint32 procAttackerFlags = PROC_FLAG_SUCCESSFUL_SPELL_CAST | PROC_FLAG_SUCCESSFUL_MANA_SPELL_CAST; - m_caster->ProcDamageAndSpell(m_caster, procAttackerFlags, PROC_FLAG_NONE, PROC_EX_NORMAL_HIT, 1, m_attackType, m_spellInfo, this); + m_casterUnit->ProcDamageAndSpell(m_casterUnit, procAttackerFlags, PROC_FLAG_NONE, PROC_EX_NORMAL_HIT, 1, m_attackType, m_spellInfo, this); } // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells @@ -3775,11 +3897,11 @@ void Spell::cast(bool skipCheck) } } - if (IsMeleeAttackResetSpell() && !(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS)) + if (m_casterUnit && IsMeleeAttackResetSpell() && !(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS)) { - m_caster->resetAttackTimer(BASE_ATTACK); - if (m_caster->haveOffhandWeapon()) - m_caster->resetAttackTimer(OFF_ATTACK); + m_casterUnit->resetAttackTimer(BASE_ATTACK); + if (m_casterUnit->haveOffhandWeapon()) + m_casterUnit->resetAttackTimer(OFF_ATTACK); } m_caster->DecreaseCastCounter(); @@ -3801,7 +3923,7 @@ void Spell::handle_immediate() { m_spellState = SPELL_STATE_CASTING; SendChannelStart(m_duration); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) m_caster->ToPlayer()->RemoveSpellMods(this); } @@ -3958,6 +4080,9 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { + if (!m_casterUnit) + return; + // (1) have infinity cooldown but set at aura apply, (2) passive cooldown at triggering if (m_spellInfo->Attributes & (SPELL_ATTR_DISABLED_WHILE_ACTIVE | SPELL_ATTR_PASSIVE)) return; @@ -3965,7 +4090,7 @@ void Spell::SendSpellCooldown() if (m_caster->ToPlayer() && m_caster->ToPlayer()->HasOption(PLAYER_CHEAT_NO_COOLDOWN)) return; - m_caster->AddSpellAndCategoryCooldowns(m_spellInfo, m_CastItem ? m_CastItem->GetEntry() : 0, this); + m_casterUnit->AddSpellAndCategoryCooldowns(m_spellInfo, m_CastItem ? m_CastItem->GetEntry() : 0, this); } void Spell::update(uint32 difftime) @@ -3995,7 +4120,7 @@ void Spell::update(uint32 difftime) nowPosZ = m_caster->GetPositionZ(); nowO = m_caster->GetOrientation(); } - if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && + if ((m_caster->IsPlayer() && m_timer != 0) && (fabs(m_castPositionX - nowPosX) > 0.5f || fabs(m_castPositionY - nowPosY) > 0.5f || fabs(m_castPositionZ - nowPosZ) > 0.5f) && (m_spellInfo->Effect[EFFECT_INDEX_0] != SPELL_EFFECT_STUCK || !((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLINGFAR)) && ((m_spellInfo->Id != 24322) && (m_spellInfo->Id != 24323))) @@ -4005,15 +4130,18 @@ void Spell::update(uint32 difftime) { bool bInterrupt = true; - // except if its a self root, since player could have moved a bit before root ack (ravager proc) - for (int i = 0; i < MAX_EFFECT_INDEX; ++i) + if (m_casterUnit) { - if ((m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) && - ((m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_ROOT) || (m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STUN)) && - (m_spellInfo->EffectImplicitTargetA[i] == TARGET_SELF)) + // except if its a self root, since player could have moved a bit before root ack (ravager proc) + for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { - bInterrupt = !(m_caster->IsRooted() || m_caster->HasPendingMovementChange(ROOT)); - break; + if ((m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) && + ((m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_ROOT) || (m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STUN)) && + (m_spellInfo->EffectImplicitTargetA[i] == TARGET_SELF)) + { + bInterrupt = !(m_casterUnit->IsRooted() || m_casterUnit->HasPendingMovementChange(ROOT)); + break; + } } } @@ -4054,11 +4182,9 @@ void Spell::update(uint32 difftime) if (m_timer) { // check for incapacitating states - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - if (m_caster->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) - cancel(); - } + if (m_casterUnit && m_casterUnit->IsPlayer() && + m_casterUnit->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) + cancel(); if (difftime >= m_timer) m_timer = 0; @@ -4080,12 +4206,12 @@ void Spell::update(uint32 difftime) if (m_caster->IsPlayer() || m_caster->IsPet()) { // check for incapacitating player states - if (m_caster->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) + if (m_casterUnit->hasUnitState(UNIT_STAT_CAN_NOT_REACT)) { - if (m_caster->hasUnitState(UNIT_STAT_DIED) || - (m_caster->hasUnitState(UNIT_STAT_STUNNED) && !(m_channeled && m_spellInfo->HasAura(SPELL_AURA_MOD_STUN))) || - (m_caster->hasUnitState(UNIT_STAT_CONFUSED) && !(m_channeled && m_spellInfo->HasAura(SPELL_AURA_MOD_CONFUSE))) || - (m_caster->hasUnitState(UNIT_STAT_FLEEING) && !(m_channeled && m_spellInfo->HasAura(SPELL_AURA_MOD_FEAR)))) + if (m_casterUnit->hasUnitState(UNIT_STAT_DIED) || + (m_casterUnit->hasUnitState(UNIT_STAT_STUNNED) && !(m_channeled && m_spellInfo->HasAura(SPELL_AURA_MOD_STUN))) || + (m_casterUnit->hasUnitState(UNIT_STAT_CONFUSED) && !(m_channeled && m_spellInfo->HasAura(SPELL_AURA_MOD_CONFUSE))) || + (m_casterUnit->hasUnitState(UNIT_STAT_FLEEING) && !(m_channeled && m_spellInfo->HasAura(SPELL_AURA_MOD_FEAR)))) cancel(); } } @@ -4174,7 +4300,7 @@ void Spell::update(uint32 difftime) // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... ) if (!IsAutoRepeat() && !IsNextMeleeSwingSpell()) { - if (Player* p = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself()) + if (Player* p = m_casterUnit ? m_casterUnit->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr) { for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) { @@ -4185,7 +4311,7 @@ void Spell::update(uint32 difftime) if (!target.targetGUID.IsCreature()) continue; - Unit* unit = m_caster->GetObjectGuid() == target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID); + Unit* unit = m_caster->GetObjectGuid() == target.targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_caster, target.targetGUID); if (unit == nullptr) continue; @@ -4220,8 +4346,11 @@ void Spell::update(uint32 difftime) void Spell::HandleAddTargetTriggerAuras() { + if (!m_casterUnit) + return; + // handle SPELL_AURA_ADD_TARGET_TRIGGER auras - auto const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); + auto const& targetTriggers = m_casterUnit->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER); for (auto i = targetTriggers.begin(); i != targetTriggers.end(); ++i) { if (!(*i)->isAffectedOnSpell(m_spellInfo)) @@ -4232,9 +4361,9 @@ void Spell::HandleAddTargetTriggerAuras() continue; Unit* target = nullptr; if (ihit->missCondition == SPELL_MISS_NONE) - target = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); + target = m_casterUnit->GetObjectGuid() == ihit->targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_casterUnit, ihit->targetGUID); else if (ihit->missCondition == SPELL_MISS_REFLECT && ihit->reflectResult == SPELL_MISS_NONE) - target = m_caster; + target = m_casterUnit; if (!target) continue; if (target && target->isAlive()) @@ -4243,9 +4372,9 @@ void Spell::HandleAddTargetTriggerAuras() SpellEffectIndex auraSpellIdx = (*i)->GetEffIndex(); // Calculate chance at that moment (can be depend for example from combo points) int32 auraBasePoints = (*i)->GetBasePoints(); - int32 chance = m_caster->CalculateSpellDamage(target, auraSpellInfo, auraSpellIdx, &auraBasePoints); - if ((m_caster->IsPlayer() && m_caster->ToPlayer()->HasOption(PLAYER_CHEAT_ALWAYS_PROC)) || roll_chance_i(chance)) - m_caster->CastSpell(target, auraSpellInfo->EffectTriggerSpell[auraSpellIdx], true, nullptr, (*i)); + int32 chance = m_casterUnit->CalculateSpellDamage(target, auraSpellInfo, auraSpellIdx, &auraBasePoints); + if ((m_casterUnit->IsPlayer() && m_casterUnit->ToPlayer()->HasOption(PLAYER_CHEAT_ALWAYS_PROC)) || roll_chance_i(chance)) + m_casterUnit->CastSpell(target, auraSpellInfo->EffectTriggerSpell[auraSpellIdx], true, nullptr, (*i)); } } } @@ -4267,18 +4396,21 @@ void Spell::finish(bool ok) if (m_setCreatureTarget) static_cast(m_caster)->ClearCastingTarget(); - if (!ok && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK) + if (Player* pPlayer = m_caster->ToPlayer()) { - // Fix a client problem with ritual of doom, by itself it disables - // the spell during cast and then the spell stays disabled - // Ignore the spell when it's triggered (ritual helper) - if (m_spellInfo->Id == 18540 && !m_IsTriggeredSpell - && !m_caster->HasSpellCooldown(m_spellInfo->Id)) - m_caster->ToPlayer()->SendClearCooldown(18540, m_caster); + if (!ok && pPlayer->getClass() == CLASS_WARLOCK) + { + // Fix a client problem with ritual of doom, by itself it disables + // the spell during cast and then the spell stays disabled + // Ignore the spell when it's triggered (ritual helper) + if (m_spellInfo->Id == 18540 && !m_IsTriggeredSpell + && !pPlayer->HasSpellCooldown(m_spellInfo->Id)) + pPlayer->ToPlayer()->SendClearCooldown(18540, pPlayer); + } } // Restore pet movement after spell (succubus after seduce) - if (CharmInfo* ci = m_caster->GetCharmInfo()) + if (CharmInfo* ci = m_casterUnit ? m_casterUnit->GetCharmInfo() : nullptr) { ci->SetIsAtStay(false); ci->SetIsCommandFollow(false); @@ -4290,7 +4422,7 @@ void Spell::finish(bool ok) if (!ok) { CancelGlobalCooldown(); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) m_caster->ToPlayer()->RestoreSpellMods(this); return; } @@ -4299,14 +4431,14 @@ void Spell::finish(bool ok) HandleAddTargetTriggerAuras(); // Heal caster for all health leech from all targets - if (m_healthLeech) - m_caster->DealHeal(m_caster, uint32(m_healthLeech), m_spellInfo); + if (m_healthLeech && m_casterUnit) + m_casterUnit->DealHeal(m_casterUnit, uint32(m_healthLeech), m_spellInfo); /*if (IsRangedAttackResetSpell()) m_caster->resetAttackTimer(RANGED_ATTACK);*/ // Clear combo at finish state - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->NeedsComboPoints()) + if (m_caster->IsPlayer() && m_spellInfo->NeedsComboPoints()) { // Not drop combopoints if negative spell and if any miss on enemy exist bool needDrop = true; @@ -4343,7 +4475,7 @@ void Spell::finish(bool ok) } } - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) { m_caster->ToPlayer()->RemoveSpellMods(this); @@ -4353,28 +4485,33 @@ void Spell::finish(bool ok) if (m_spellInfo->Id == pGo->GetGOInfo()->summoningRitual.spellId) pGo->FinishRitual(); } - // Stop Attack for some spells - if (m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET) + + if (m_casterUnit) { - m_caster->AttackStop(); - m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); - } - else if ((m_spellInfo->AttributesEx & SPELL_ATTR_EX_MELEE_COMBAT_START)) - { - // Pets should initiate melee combat on spell with this flag. (Growl) - if (Pet* pPet = m_caster->ToPet()) - if (pPet->AI() && pPet->GetCharmInfo()) - if (Unit* const pTarget = m_targets.getUnitTarget()) - { - pPet->GetCharmInfo()->SetIsCommandAttack(true); - pPet->AI()->AttackStart(pTarget); - } + // Stop Attack for some spells + if (m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET) + { + m_casterUnit->AttackStop(); + m_casterUnit->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + } + else if ((m_spellInfo->AttributesEx & SPELL_ATTR_EX_MELEE_COMBAT_START)) + { + // Pets should initiate melee combat on spell with this flag. (Growl) + if (Pet* pPet = m_casterUnit->ToPet()) + if (pPet->AI() && pPet->GetCharmInfo()) + if (Unit* const pTarget = m_targets.getUnitTarget()) + { + pPet->GetCharmInfo()->SetIsCommandAttack(true); + pPet->AI()->AttackStart(pTarget); + } + } } + } void Spell::SendCastResult(SpellCastResult result) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return; if (((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time @@ -4517,7 +4654,7 @@ void Spell::WriteAmmoToPacket(WorldPacket* data) uint32 ammoInventoryType = 0; uint32 ammoDisplayID = 0; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) { Item *pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK); if (pItem) @@ -4552,8 +4689,11 @@ void Spell::WriteAmmoToPacket(WorldPacket* data) switch (m_caster->GetByteValue(UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 0, VIRTUAL_ITEM_INFO_0_OFFSET_SUBCLASS)) { case ITEM_SUBCLASS_WEAPON_THROWN: - ammoDisplayID = m_caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i); - ammoInventoryType = m_caster->GetByteValue(UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 0, VIRTUAL_ITEM_INFO_0_OFFSET_INVENTORYTYPE); + if (m_casterUnit) + { + ammoDisplayID = m_casterUnit->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i); + ammoInventoryType = m_casterUnit->GetByteValue(UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 0, VIRTUAL_ITEM_INFO_0_OFFSET_INVENTORYTYPE); + } break; case ITEM_SUBCLASS_WEAPON_BOW: case ITEM_SUBCLASS_WEAPON_CROSSBOW: @@ -4768,7 +4908,7 @@ void Spell::SendChannelUpdate(uint32 time) if (!time) { // Reset farsight for some possessing auras of possessed summoned (as they might work with different aura types) - if (m_spellInfo->Attributes & SPELL_ATTR_EX_FARSIGHT && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->GetCharmGuid() + if (m_spellInfo->Attributes & SPELL_ATTR_EX_FARSIGHT && m_caster->IsPlayer() && m_casterUnit->GetCharmGuid() && !m_spellInfo->HasAura(SPELL_AURA_MOD_POSSESS) && !m_spellInfo->HasAura(SPELL_AURA_MOD_POSSESS_PET)) { Player* player = (Player*)m_caster; @@ -4790,7 +4930,7 @@ void Spell::SendChannelUpdate(uint32 time) // TODO - Requires more specials for target? // Some possessed might want to despawn? - if (possessed->GetUInt32Value(UNIT_CREATED_BY_SPELL) == m_spellInfo->Id && possessed->GetTypeId() == TYPEID_UNIT) + if (possessed->GetUInt32Value(UNIT_CREATED_BY_SPELL) == m_spellInfo->Id && possessed->IsCreature()) ((Creature*)possessed)->ForcedDespawn(); } } @@ -4817,9 +4957,10 @@ void Spell::SendChannelUpdate(uint32 time) m_channeledUpdateIterator = m_channeledHolders.end(); // Remove single target auras on caster now if they expired - m_caster->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetObjectGuid(), AURA_REMOVE_BY_CHANNEL); + if (m_casterUnit) + m_casterUnit->RemoveAurasByCasterSpell(m_spellInfo->Id, m_casterUnit->GetObjectGuid(), AURA_REMOVE_BY_CHANNEL); - ObjectGuid target_guid = m_caster->GetChannelObjectGuid(); + ObjectGuid target_guid = m_casterUnit ? m_casterUnit->GetChannelObjectGuid() : ObjectGuid(); if (target_guid != m_caster->GetObjectGuid() && target_guid.IsUnit()) { if (Unit* target = ObjectAccessor::GetUnit(*m_caster, target_guid)) @@ -4833,24 +4974,28 @@ void Spell::SendChannelUpdate(uint32 time) // live until the cast has finished. Technically don't need to explicitly remove // but no harm m_caster->RemoveDynObject(m_spellInfo->Id); - m_caster->RemoveGameObject(m_spellInfo->Id, true); + if (m_casterUnit) + m_casterUnit->RemoveGameObject(m_spellInfo->Id, true); } // Only modify/send values if we are the current channeled spell ! if (m_caster->GetCurrentSpell(CURRENT_CHANNELED_SPELL) && m_caster->GetCurrentSpell(CURRENT_CHANNELED_SPELL) != this) return; + if (!m_casterUnit) + return; + if (!time) { // Reset of channel values has to be done after a few delay. // Else, we have some visual bugs (arcane projectile, last tick) - ChannelResetEvent* event = new ChannelResetEvent(m_caster); - m_caster->m_Events.AddEvent(event, m_caster->m_Events.CalculateTime(0)); + ChannelResetEvent* event = new ChannelResetEvent(m_casterUnit); + m_casterUnit->m_Events.AddEvent(event, m_casterUnit->m_Events.CalculateTime(0)); } - else if (m_caster->GetTypeId() == TYPEID_PLAYER) + else if (Player* pPlayer = m_casterUnit->ToPlayer()) { WorldPacket data(MSG_CHANNEL_UPDATE, 4); data << uint32(time); - ((Player*)m_caster)->SendDirectMessage(&data); + pPlayer->SendDirectMessage(&data); } } @@ -4892,7 +5037,7 @@ void Spell::SendChannelStart(uint32 duration) } } - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) { WorldPacket data(MSG_CHANNEL_START, (4 + 4)); data << uint32(m_spellInfo->Id); @@ -4901,9 +5046,12 @@ void Spell::SendChannelStart(uint32 duration) } m_timer = duration; - if (target) - m_caster->SetChannelObjectGuid(target->GetObjectGuid()); - m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); + if (m_casterUnit) + { + if (target) + m_casterUnit->SetChannelObjectGuid(target->GetObjectGuid()); + m_casterUnit->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id); + } } void Spell::SendResurrectRequest(Player* target) @@ -4911,7 +5059,7 @@ void Spell::SendResurrectRequest(Player* target) // Both players and NPCs can resurrect using spells - have a look at creature 28487 for example // However, the packet structure differs slightly - const char* sentName = m_caster->GetTypeId() == TYPEID_PLAYER ? "" : m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex()); + const char* sentName = m_caster->IsPlayer() ? "" : m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex()); WorldPacket data(SMSG_RESURRECT_REQUEST, (8 + 4 + strlen(sentName) + 1 + 1 + 1)); data << m_caster->GetObjectGuid(); @@ -4920,13 +5068,13 @@ void Spell::SendResurrectRequest(Player* target) data << sentName; data << uint8(0); - data << uint8(m_caster->GetTypeId() == TYPEID_PLAYER ? 0 : 1); + data << uint8(m_caster->IsPlayer() ? 0 : 1); target->GetSession()->SendPacket(&data); } void Spell::SendPlaySpellVisual(uint32 SpellID) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return; WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 8 + 4); @@ -4937,7 +5085,7 @@ void Spell::SendPlaySpellVisual(uint32 SpellID) void Spell::TakeCastItem() { - if (!m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_CastItem || !m_caster->IsPlayer()) return; // not remove cast item at triggered spell (equipping, weapon damage, etc) @@ -4997,13 +5145,13 @@ void Spell::TakeCastItem() void Spell::TakePower() { - if (m_CastItem || m_triggeredByAuraSpell || IsChannelingVisual()) + if (m_CastItem || m_triggeredByAuraSpell || IsChannelingVisual() || !m_casterUnit) return; // health as power used if (m_spellInfo->powerType == POWER_HEALTH) { - m_caster->ModifyHealth(-(int32)m_powerCost); + m_casterUnit->ModifyHealth(-(int32)m_powerCost); return; } @@ -5015,16 +5163,16 @@ void Spell::TakePower() Powers powerType = Powers(m_spellInfo->powerType); - m_caster->ModifyPower(powerType, -(int32)m_powerCost); + m_casterUnit->ModifyPower(powerType, -(int32)m_powerCost); // Set the five second timer if (powerType == POWER_MANA && m_powerCost > 0) - m_caster->SetLastManaUse(m_spellInfo->Id); + m_casterUnit->SetLastManaUse(m_spellInfo->Id); } void Spell::TakeReagents() { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return; if (IgnoreItemRequirements()) // reagents used in triggered spell removed by original spell or don't must be removed. @@ -5071,6 +5219,10 @@ void Spell::TakeReagents() void Spell::TakeAmmo() { + Player* pCaster = m_caster->ToPlayer(); + if (!pCaster) + return; + // Some ranged attacks dont take any ammo switch (m_spellInfo->Id) { @@ -5081,43 +5233,34 @@ void Spell::TakeAmmo() return; } - if (m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_attackType == RANGED_ATTACK) { - Item *pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK, true, false); - - // wands don't have ammo - if (!pItem || pItem->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_WAND) - { - m_caster->SetLastAttackType(2); - return; - } - - if ((m_spellInfo->Id == 75) || (m_spellInfo->Id == 2764)) - { - m_caster->SetLastAttackType(2); - } + Item *pItem = pCaster->GetWeaponForAttack(RANGED_ATTACK, true, false); if (pItem->GetProto()->InventoryType == INVTYPE_THROWN) { if (pItem->GetMaxStackCount() == 1) { // decrease durability for non-stackable throw weapon - ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); + pCaster->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); } else { // decrease items amount for stackable throw weapon uint32 count = 1; - ((Player*)m_caster)->DestroyItemCount(pItem, count, true); + pCaster->DestroyItemCount(pItem, count, true); } } - else if (uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID)) - ((Player*)m_caster)->DestroyItemCount(ammo, 1, true); + else if (uint32 ammo = pCaster->GetUInt32Value(PLAYER_AMMO_ID)) + pCaster->DestroyItemCount(ammo, 1, true); } } void Spell::HandleThreatSpells() { + if (!m_casterUnit) + return; + if (m_UniqueTargetInfo.empty()) return; @@ -5128,7 +5271,7 @@ void Spell::HandleThreatSpells() float threat = threatEntry->threat; if (threatEntry->ap_bonus != 0.0f) - threat += threatEntry->ap_bonus * m_caster->GetTotalAttackPowerValue(m_spellInfo->GetWeaponAttackType()); + threat += threatEntry->ap_bonus * m_casterUnit->GetTotalAttackPowerValue(m_spellInfo->GetWeaponAttackType()); bool positive = true; uint8 effectMask = 0; @@ -5153,20 +5296,20 @@ void Spell::HandleThreatSpells() if (ihit->missCondition != SPELL_MISS_NONE) continue; - Unit* target = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID); + Unit* target = m_casterUnit->GetObjectGuid() == ihit->targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_casterUnit, ihit->targetGUID); if (!target) continue; // positive spells distribute threat among all units that are in combat with target, like healing if (positive) - target->getHostileRefManager().threatAssist(m_caster /*real_caster ??*/, threat, m_spellInfo); + target->getHostileRefManager().threatAssist(m_casterUnit /*pRealCaster ??*/, threat, m_spellInfo); // for negative spells threat gets distributed among affected targets else { if (!target->CanHaveThreatList()) continue; - target->AddThreat(m_caster, threat, false, m_spellInfo->GetSpellSchoolMask(), m_spellInfo); + target->AddThreat(m_casterUnit, threat, false, m_spellInfo->GetSpellSchoolMask(), m_spellInfo); } } @@ -5230,7 +5373,7 @@ void Spell::CastTriggerSpells() { for (SpellInfoList::const_iterator si = m_TriggerSpells.begin(); si != m_TriggerSpells.end(); ++si) { - Spell* spell = new Spell(m_caster, (*si), true, m_originalCasterGUID); + Spell* spell = m_casterUnit ? new Spell(m_casterUnit, (*si), true, m_originalCasterGUID) : new Spell(m_casterGo, (*si), true, m_originalCasterGUID); spell->prepare(m_targets); // use original spell original targets } } @@ -5284,7 +5427,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_CAST_OK; // Prevent casting while sitting unless the spell allows it - if (!m_IsTriggeredSpell && !m_caster->IsStandingUp() && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_SITTING)) + if (!m_IsTriggeredSpell && m_casterUnit && !m_casterUnit->IsStandingUp() && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_SITTING)) return SPELL_FAILED_NOT_STANDING; /* Check cooldowns to prevent cheating (ignore passive spells, that client side visual only) @@ -5311,8 +5454,8 @@ SpellCastResult Spell::CheckCast(bool strict) } } - if (m_caster->GetTypeId() == TYPEID_PLAYER && !(m_spellInfo->Attributes & SPELL_ATTR_PASSIVE) - && !m_IsTriggeredSpell && (m_caster->HasSpellCooldown(m_spellInfo->Id) || m_caster->HasSpellCategoryCooldown(spellCat))) + if (m_caster->IsPlayer() && !(m_spellInfo->Attributes & SPELL_ATTR_PASSIVE) + && !m_IsTriggeredSpell && (m_casterUnit->HasSpellCooldown(m_spellInfo->Id) || m_casterUnit->HasSpellCategoryCooldown(spellCat))) { if (m_triggeredByAuraSpell || m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) return SPELL_FAILED_DONT_REPORT; @@ -5321,7 +5464,7 @@ SpellCastResult Spell::CheckCast(bool strict) } // check death state to prevent cheating ("deathbug") - if (!m_caster->isAlive() && !(m_spellInfo->Attributes & SPELL_ATTR_PASSIVE) && !((m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD) || (m_IsTriggeredSpell && !m_triggeredByAuraSpell))) + if (m_casterUnit && !m_casterUnit->isAlive() && !(m_spellInfo->Attributes & SPELL_ATTR_PASSIVE) && !((m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD) || (m_IsTriggeredSpell && !m_triggeredByAuraSpell))) { if (m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; @@ -5342,14 +5485,14 @@ SpellCastResult Spell::CheckCast(bool strict) } // only allow triggered spells if at an ended battleground - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) if (BattleGround * bg = ((Player*)m_caster)->GetBattleGround()) if (bg->GetStatus() == STATUS_WAIT_LEAVE) return SPELL_FAILED_DONT_REPORT; - if (strict) + if (strict && m_casterUnit) { - if (m_caster->isInCombat() && m_spellInfo->IsNonCombatSpell()) + if (m_casterUnit && m_casterUnit->isInCombat() && m_spellInfo->IsNonCombatSpell()) return SPELL_FAILED_AFFECTING_COMBAT; // only check at first call, Stealth auras are already removed at second call @@ -5357,16 +5500,16 @@ SpellCastResult Spell::CheckCast(bool strict) //if (strict) //{ // Cannot be used in this stance/form - SpellCastResult shapeError = m_spellInfo->GetErrorAtShapeshiftedCast(m_caster->GetShapeshiftForm()); + SpellCastResult shapeError = m_spellInfo->GetErrorAtShapeshiftedCast(m_casterUnit->GetShapeshiftForm()); if (shapeError != SPELL_CAST_OK) return shapeError; - if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) + if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_casterUnit->HasStealthAura())) return SPELL_FAILED_ONLY_STEALTHED; } } - if (m_caster->GetTypeId() == TYPEID_PLAYER && !((Player*)m_caster)->IsGameMaster() && + if (m_caster->IsPlayer() && !((Player*)m_caster)->IsGameMaster() && sWorld.getConfig(CONFIG_BOOL_VMAP_INDOOR_CHECK) && VMAP::VMapFactory::createOrGetVMapManager()->isLineOfSightCalcEnabled()) { @@ -5380,10 +5523,10 @@ SpellCastResult Spell::CheckCast(bool strict) } // caster state requirements - if (m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState))) + if (m_spellInfo->CasterAuraState && m_casterUnit && !m_casterUnit->HasAuraState(AuraState(m_spellInfo->CasterAuraState))) return SPELL_FAILED_CASTER_AURASTATE; - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) { // Autorepeat spells, if cast start when moving, are delayed until moving stop in spell update code. if (m_caster->IsMoving()) @@ -5394,31 +5537,31 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_MOVING; } - if (!m_IsTriggeredSpell && m_spellInfo->NeedsComboPoints() && - (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetObjectGuid() != ((Player*)m_caster)->GetComboTargetGuid())) + if (!m_IsTriggeredSpell && m_spellInfo->NeedsComboPoints() && Spells::IsExplicitlySelectedUnitTarget(m_spellInfo->EffectImplicitTargetA[0]) && + (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetObjectGuid() != ((Player*)m_caster)->GetComboTargetGuid())) // warrior not have real combo-points at client side but use this way for mark allow Overpower use - return m_caster->getClass() == CLASS_WARRIOR ? SPELL_FAILED_BAD_TARGETS : SPELL_FAILED_NO_COMBO_POINTS; + return m_casterUnit->getClass() == CLASS_WARRIOR ? SPELL_FAILED_BAD_TARGETS : SPELL_FAILED_NO_COMBO_POINTS; switch (m_spellInfo->Id) { - // Morsure de mangouste + // Mongoose Bite case 1495: case 14269: case 14270: case 14271: - if (m_targets.getUnitTargetGuid() != m_caster->GetReactiveTarget(REACTIVE_DEFENSE)) + if (m_targets.getUnitTargetGuid() != m_casterUnit->GetReactiveTarget(REACTIVE_DEFENSE)) return SPELL_FAILED_BAD_TARGETS; break; - // Contre-attaque + // Counterattack case 19306: case 20909: case 20910: - if (m_targets.getUnitTargetGuid() != m_caster->GetReactiveTarget(REACTIVE_HUNTER_PARRY)) + if (m_targets.getUnitTargetGuid() != m_casterUnit->GetReactiveTarget(REACTIVE_HUNTER_PARRY)) return SPELL_FAILED_BAD_TARGETS; break; // Reindeer Transformation only castable while mounted case 25860: - if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED)) + if (!m_casterUnit->HasAuraType(SPELL_AURA_MOUNTED)) return SPELL_FAILED_ONLY_MOUNTED; break; } @@ -5426,7 +5569,7 @@ SpellCastResult Spell::CheckCast(bool strict) // Loatheb Corrupted Mind spell failed if (!m_IsCastByItem && !m_IsTriggeredSpell) { - Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + Unit::AuraList const& auraClassScripts = m_casterUnit->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); for (auto itr = auraClassScripts.begin(); itr != auraClassScripts.end(); ++itr) { if ((*itr)->GetModifier()->m_miscvalue == 4327) @@ -5449,7 +5592,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (Unit *target = m_targets.getUnitTarget()) { - // Nostalrius - Un sort plus puissant est deja actif + // A more powerful spell is already active if (m_spellInfo->IsSpellAppliesAura() && !m_spellInfo->IsAreaOfEffectSpell() && m_spellInfo->IsPositiveSpell() && target->HasMorePowerfullSpellActive(m_spellInfo)) return SPELL_FAILED_AURA_BOUNCED; @@ -5498,7 +5641,7 @@ SpellCastResult Spell::CheckCast(bool strict) // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode // this case can be triggered if rank not found (too low-level target for first rank) - if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_CastItem && !m_IsTriggeredSpell) + if (m_caster->IsPlayer() && !m_CastItem && !m_IsTriggeredSpell) { // spell expected to be auto-downranking in cast handle, so must be same if (m_spellInfo != sSpellMgr.SelectAuraRankForLevel(m_spellInfo, target->getLevel())) @@ -5521,7 +5664,7 @@ SpellCastResult Spell::CheckCast(bool strict) // Can't help a friend in duel | sorry, not enough time to figure out why this is necessary if (m_spellInfo->IsPositiveSpell() && m_spellInfo->Id != 28697) { - Player* casterOwner = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself(); + Player* casterOwner = m_casterUnit ? m_casterUnit->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr; Player* targetOwner = target->GetCharmerOrOwnerPlayerOrPlayerItself(); if (m_spellInfo->Id == 7266 && targetOwner && targetOwner->duel && casterOwner && !casterOwner->IsInDuelWith(targetOwner)) @@ -5535,7 +5678,7 @@ SpellCastResult Spell::CheckCast(bool strict) } else if (m_caster == target) { - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->IsInWorld()) + if (m_caster->IsPlayer() && m_caster->IsInWorld()) { // Additional check for some spells // If 0 spell effect empty - client not send target data (need use selection) @@ -5567,7 +5710,7 @@ SpellCastResult Spell::CheckCast(bool strict) { if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_PET) { - Pet* pet = m_caster->GetPet(); + Pet* pet = m_casterUnit ? m_casterUnit->GetPet() : nullptr; if (!pet) { if (m_triggeredByAuraSpell) // not report pet not existence for triggered spells @@ -5588,13 +5731,13 @@ SpellCastResult Spell::CheckCast(bool strict) } } - //check creature type - //ignore self casts (including area casts when caster selected as target) + // check creature type + // ignore self casts (including area casts when caster selected as target) if (non_caster_target) { if (!CheckTargetCreatureType(target)) { - if (target->GetTypeId() == TYPEID_PLAYER) + if (target->IsPlayer()) return SPELL_FAILED_TARGET_IS_PLAYER; else return SPELL_FAILED_BAD_TARGETS; @@ -5634,7 +5777,7 @@ SpellCastResult Spell::CheckCast(bool strict) // TODO: this check can be applied and for player to prevent cheating when IsPositiveSpell will return always correct result. // check target for pet/charmed casts (not self targeted), self targeted cast used for area effects and etc // Nostalrius : pas de status positif / negatif pour les sorts de dispel (marche en offensif comme defensif) - if (!explicit_target_mode && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->GetCharmerOrOwnerGuid() && !m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) + if (!explicit_target_mode && m_caster->IsCreature() && m_casterUnit->GetCharmerOrOwnerGuid() && !m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) { // check correctness positive/negative cast target (pet cast real check and cheating check) if (m_spellInfo->IsPositiveSpell()) @@ -5658,7 +5801,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_TARGET_AURASTATE; //Must be behind the target. - if (m_spellInfo->IsFromBehindOnlySpell() && !m_caster->IsBehindTarget(target, strict)) + if (m_spellInfo->IsFromBehindOnlySpell() && m_casterUnit && !m_casterUnit->IsBehindTarget(target, strict)) { SendInterrupted(2); return SPELL_FAILED_NOT_BEHIND; @@ -5673,13 +5816,6 @@ SpellCastResult Spell::CheckCast(bool strict) } else if (GameObject* goTarget = m_targets.getGOTarget()) { - if (goTarget->AI()) - { - SpellCastResult goAIresult = goTarget->AI()->CheckSpellCast(m_caster, m_spellInfo->Id); - if (goAIresult != SPELL_CAST_OK) - return goAIresult; - } - // Require LOS to loot chests // For example, check LoS when opening a gobj (spell 6477). // Prevents abuse with object id 165554 for example. @@ -5696,7 +5832,7 @@ SpellCastResult Spell::CheckCast(bool strict) else if (m_targets.IsEmpty()) { // check LOS for ground targeted AOE spells like Blizzard, Flamestrike ... - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + if (m_caster->IsPlayer() && m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) { if (m_targets.m_destX && m_targets.m_destY && m_targets.m_destZ) { @@ -5705,34 +5841,37 @@ SpellCastResult Spell::CheckCast(bool strict) } } } - - // Nostalrius: impossible to cast spells while banned / feared / confused ... - // Except divine shields, pvp trinkets for example - // Stoneform is explicitly excluded, it should be allowed to cast always - // TODO: This condition allows an antifear item to be used while stuned for example. - if (!m_IsTriggeredSpell && !m_spellInfo->HasAura(SPELL_AURA_SCHOOL_IMMUNITY) && !m_spellInfo->HasAura(SPELL_AURA_MECHANIC_IMMUNITY) && - m_spellInfo->Id != 20594 && // Stoneform - m_caster->hasUnitState(UNIT_STAT_ISOLATED | UNIT_STAT_STUNNED | UNIT_STAT_PENDING_STUNNED | UNIT_STAT_CONFUSED | UNIT_STAT_FLEEING)) - return SPELL_FAILED_DONT_REPORT; - // zone check - SpellCastResult locRes = sSpellMgr.GetSpellAllowedInLocationError(m_spellInfo, m_caster, m_caster->GetCharmerOrOwnerPlayerOrPlayerItself()); - if (locRes != SPELL_CAST_OK) - return locRes; - - // not let players cast spells at mount (and let do it to creatures) - if (m_caster->IsMounted() && m_caster->GetTypeId() == TYPEID_PLAYER && !m_IsTriggeredSpell && - !m_spellInfo->IsPassiveSpell() && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED)) + if (m_casterUnit) { - if (m_caster->IsTaxiFlying()) - return SPELL_FAILED_NOT_ON_TAXI; - else if (m_caster->GetTypeId() == TYPEID_PLAYER) + // Nostalrius: impossible to cast spells while banned / feared / confused ... + // Except divine shields, pvp trinkets for example + // Stoneform is explicitly excluded, it should be allowed to cast always + // TODO: This condition allows an antifear item to be used while stuned for example. + if (!m_IsTriggeredSpell && !m_spellInfo->HasAura(SPELL_AURA_SCHOOL_IMMUNITY) && !m_spellInfo->HasAura(SPELL_AURA_MECHANIC_IMMUNITY) && + m_spellInfo->Id != 20594 && // Stoneform + m_casterUnit->hasUnitState(UNIT_STAT_ISOLATED | UNIT_STAT_STUNNED | UNIT_STAT_PENDING_STUNNED | UNIT_STAT_CONFUSED | UNIT_STAT_FLEEING)) + return SPELL_FAILED_DONT_REPORT; + + // zone check + SpellCastResult locRes = sSpellMgr.GetSpellAllowedInLocationError(m_spellInfo, m_casterUnit, m_casterUnit->GetCharmerOrOwnerPlayerOrPlayerItself()); + if (locRes != SPELL_CAST_OK) + return locRes; + + // not let players cast spells at mount (and let do it to creatures) + if (m_casterUnit->IsMounted() && m_casterUnit->IsPlayer() && !m_IsTriggeredSpell && + !m_spellInfo->IsPassiveSpell() && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED)) { - m_caster->Unmount(); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + if (m_casterUnit->IsTaxiFlying()) + return SPELL_FAILED_NOT_ON_TAXI; + else if (m_casterUnit->IsPlayer()) + { + m_casterUnit->Unmount(); + m_casterUnit->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + } + else + return SPELL_FAILED_NOT_MOUNTED; } - else - return SPELL_FAILED_NOT_MOUNTED; } // always (except passive spells) check items (focus object can be required for any type casts) @@ -5819,7 +5958,7 @@ SpellCastResult Spell::CheckCast(bool strict) // check if explicit target is provided and check it up against database valid target entry/state if (Unit* pTarget = m_targets.getUnitTarget()) { - if (pTarget->GetTypeId() == TYPEID_UNIT && pTarget->GetEntry() == i_spellST->second.targetEntry) + if (pTarget->IsCreature() && pTarget->GetEntry() == i_spellST->second.targetEntry) { if (i_spellST->second.type == SPELL_TARGET_TYPE_DEAD && ((Creature*)pTarget)->IsCorpse()) { @@ -5941,19 +6080,22 @@ SpellCastResult Spell::CheckCast(bool strict) } } - SpellCastResult castResult = CheckPower(); - if (castResult != SPELL_CAST_OK) - return castResult; - - // triggered spell not affected by stun/etc - castResult = CheckCasterAuras(); - if (castResult != SPELL_CAST_OK) - return castResult; - - if (m_caster->hasUnitState(UNIT_STAT_POSSESSED)) + if (m_casterUnit) { - if (m_spellInfo->Category == 21) // Enrage - return SPELL_FAILED_NOT_READY; + SpellCastResult castResult = CheckPower(); + if (castResult != SPELL_CAST_OK) + return castResult; + + // triggered spell not affected by stun/etc + castResult = CheckCasterAuras(); + if (castResult != SPELL_CAST_OK) + return castResult; + + if (m_casterUnit->hasUnitState(UNIT_STAT_POSSESSED)) + { + if (m_spellInfo->Category == 21) // Enrage + return SPELL_FAILED_NOT_READY; + } } } @@ -6021,7 +6163,7 @@ SpellCastResult Spell::CheckCast(bool strict) Unit* caster = GetAffectiveCaster(); if (!caster || caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getUnitTarget() || - m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER) + m_targets.getUnitTarget()->IsPlayer()) return SPELL_FAILED_BAD_TARGETS; Player* plrCaster = (Player*)caster; @@ -6074,7 +6216,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_spellInfo->EffectImplicitTargetA[i] != TARGET_PET) break; - Pet* pet = m_caster->GetPet(); + Pet* pet = m_casterUnit ? m_casterUnit->GetPet() : nullptr; if (!pet) return SPELL_FAILED_NO_PET; @@ -6097,7 +6239,7 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_LEARN_PET_SPELL: { - Pet* pet = m_caster->GetPet(); + Pet* pet = m_casterUnit ? m_casterUnit->GetPet() : nullptr; if (!pet) return SPELL_FAILED_NO_PET; @@ -6120,14 +6262,14 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_FEED_PET: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return SPELL_FAILED_BAD_TARGETS; Item* foodItem = m_targets.getItemTarget(); if (!foodItem) return SPELL_FAILED_BAD_TARGETS; - Pet* pet = m_caster->GetPet(); + Pet* pet = m_casterUnit->GetPet(); if (!pet) return SPELL_FAILED_NO_PET; @@ -6147,7 +6289,7 @@ SpellCastResult Spell::CheckCast(bool strict) case SPELL_EFFECT_POWER_DRAIN: { // Can be area effect, Check only for players and not check if target - caster (spell can have multiply drain/burn effects) - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) if (Unit* target = m_targets.getUnitTarget()) if (target != m_caster && int32(target->getPowerType()) != m_spellInfo->EffectMiscValue[i]) return SPELL_FAILED_BAD_TARGETS; @@ -6155,9 +6297,13 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_CHARGE: { - if (m_caster->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_PENDING_ROOT)) + if (!m_casterUnit) + return SPELL_FAILED_DONT_REPORT; + + if (m_casterUnit->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_PENDING_ROOT)) return SPELL_FAILED_ROOTED; - // Pas de chemin disponible ? + + // No path available? if (strict) { const SpellRangeEntry *spellRange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); @@ -6167,15 +6313,15 @@ SpellCastResult Spell::CheckCast(bool strict) float x, y, z; unitTarget->GetPosition(x, y, z); - if (!m_caster->IsInWater()) + if (!m_casterUnit->IsInWater()) { float posX, posY, posZ; - m_caster->GetPosition(posX, posY, posZ); - float ground_Z = m_caster->GetMap()->GetHeight(posX, posY, posZ, true); + m_casterUnit->GetPosition(posX, posY, posZ); + float ground_Z = m_casterUnit->GetMap()->GetHeight(posX, posY, posZ, true); float z_diff = fabs(ground_Z - posZ); - PathInfo path(m_caster); - path.SetTransport(m_caster->GetTransport()); + PathInfo path(m_casterUnit); + path.SetTransport(m_casterUnit->GetTransport()); path.calculate(x, y, z); // On est sur terre, le chemin doit etre valide. @@ -6191,7 +6337,7 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_SKINNING: { - if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() != TYPEID_UNIT) + if (!m_caster->IsPlayer() || !m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() != TYPEID_UNIT) return SPELL_FAILED_BAD_TARGETS; if (!m_targets.getUnitTarget()->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) @@ -6239,7 +6385,7 @@ SpellCastResult Spell::CheckCast(bool strict) case SPELL_EFFECT_OPEN_LOCK_ITEM: case SPELL_EFFECT_OPEN_LOCK: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) // only players can open locks, gather etc. + if (!m_caster->IsPlayer()) // only players can open locks, gather etc. return SPELL_FAILED_BAD_TARGETS; // we need a go target in case of TARGET_GAMEOBJECT (for other targets acceptable GO and items) @@ -6343,7 +6489,7 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_SUMMON_DEAD_PET: { - Creature *pet = m_caster->GetPet(); + Creature *pet = m_casterUnit ? m_casterUnit->GetPet() : nullptr; if (!pet) return SPELL_FAILED_NO_PET; @@ -6357,29 +6503,32 @@ SpellCastResult Spell::CheckCast(bool strict) case SPELL_EFFECT_SUMMON: case SPELL_EFFECT_SUMMON_PHANTASM: case SPELL_EFFECT_SUMMON_DEMON: - if (m_caster->GetPetGuid()) + if (m_casterUnit && m_casterUnit->GetPetGuid()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; // no break case SPELL_EFFECT_SUMMON_POSSESSED: - if (m_caster->GetCharmGuid()) + if (m_casterUnit && m_casterUnit->GetCharmGuid()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; case SPELL_EFFECT_SUMMON_PET: { // In Vanilla old pets were unsummoned as soon as you began summoning a new one. - if (!m_caster->UnsummonOldPetBeforeNewSummon(m_spellInfo->EffectMiscValue[i])) + if (m_casterUnit && !m_casterUnit->UnsummonOldPetBeforeNewSummon(m_spellInfo->EffectMiscValue[i])) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if (m_caster->GetCharmGuid()) + if (m_casterUnit && m_casterUnit->GetCharmGuid()) return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } case SPELL_EFFECT_TRANS_DOOR: { - if (m_caster->getClass() == CLASS_WARLOCK && m_spellInfo->Id == 698) + if (!m_casterUnit) + return SPELL_FAILED_BAD_TARGETS; + + if (m_casterUnit->getClass() == CLASS_WARLOCK && m_spellInfo->Id == 698) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return SPELL_FAILED_BAD_TARGETS; if (!((Player*)m_caster)->GetSelectionGuid()) return SPELL_FAILED_BAD_TARGETS; @@ -6409,7 +6558,7 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_SUMMON_PLAYER: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return SPELL_FAILED_BAD_TARGETS; if (!m_targets.getUnitTarget()) return SPELL_FAILED_BAD_TARGETS; @@ -6439,7 +6588,7 @@ SpellCastResult Spell::CheckCast(bool strict) case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER: { // not allow use this effect at battleground until battleground start (Nostalrius: Blizzlike) - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) if (BattleGround const *bg = ((Player*)m_caster)->GetBattleGround()) if (bg->GetStatus() != STATUS_IN_PROGRESS) return SPELL_FAILED_TRY_AGAIN; @@ -6457,31 +6606,37 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_EFFECT_SCRIPT_EFFECT: { + if (!m_casterUnit) + break; + // Black Qiraji Battle Tank if (m_spellInfo->Id == 26656) { - if (m_caster->IsMounted()) + if (m_casterUnit->IsMounted()) { - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + m_casterUnit->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); return SPELL_FAILED_DONT_REPORT; } - if (m_caster->IsInWater()) + if (m_casterUnit->IsInWater()) return SPELL_FAILED_ONLY_ABOVEWATER; - if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->GetTransport()) + if (Player* pPlayer = m_casterUnit->ToPlayer()) + { + if (pPlayer->GetTransport()) + return SPELL_FAILED_NO_MOUNTS_ALLOWED; + + if (m_casterUnit->GetMapId() != 531 && !sMapStorage.LookupEntry(m_casterUnit->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell) + return SPELL_FAILED_NO_MOUNTS_ALLOWED; + } + + if (m_casterUnit->GetAreaId() == 35) return SPELL_FAILED_NO_MOUNTS_ALLOWED; - if (m_caster->GetMapId() != 531 && m_caster->GetTypeId() == TYPEID_PLAYER && !sMapStorage.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell) - return SPELL_FAILED_NO_MOUNTS_ALLOWED; - - if (m_caster->GetAreaId() == 35) - return SPELL_FAILED_NO_MOUNTS_ALLOWED; - - if (m_caster->IsInDisallowedMountForm()) + if (m_casterUnit->IsInDisallowedMountForm()) return SPELL_FAILED_NOT_SHAPESHIFT; - if (m_caster->GetMapId() == 531) + if (m_casterUnit->GetMapId() == 531) break; } } @@ -6577,19 +6732,19 @@ SpellCastResult Spell::CheckCast(bool strict) { case SPELL_AURA_MOD_POSSESS: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return SPELL_FAILED_BAD_TARGETS; - if (m_targets.getUnitTarget() == m_caster) + if (m_targets.getUnitTarget() == m_casterUnit) return SPELL_FAILED_BAD_TARGETS; - if (m_caster->GetPetGuid()) + if (m_casterUnit->GetPetGuid()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if (m_caster->GetCharmGuid()) + if (m_casterUnit->GetCharmGuid()) return SPELL_FAILED_ALREADY_HAVE_CHARM; - if (m_caster->GetCharmerGuid()) + if (m_casterUnit->GetCharmerGuid()) #if SUPPORTED_CLIENT_BUILD >= CLIENT_BUILD_1_11_2 return SPELL_FAILED_CHARMED; #else @@ -6614,16 +6769,19 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_AURA_MOD_CHARM: { - if (m_targets.getUnitTarget() == m_caster) + if (!m_casterUnit) return SPELL_FAILED_BAD_TARGETS; - if (m_caster->GetPetGuid()) + if (m_targets.getUnitTarget() == m_casterUnit) + return SPELL_FAILED_BAD_TARGETS; + + if (m_casterUnit->GetPetGuid()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; - if (m_caster->GetCharmGuid()) + if (m_casterUnit->GetCharmGuid()) return SPELL_FAILED_ALREADY_HAVE_CHARM; - if (m_caster->GetCharmerGuid()) + if (m_casterUnit->GetCharmerGuid()) #if SUPPORTED_CLIENT_BUILD >= CLIENT_BUILD_1_11_2 return SPELL_FAILED_CHARMED; #else @@ -6653,20 +6811,20 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_AURA_MOD_POSSESS_PET: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return SPELL_FAILED_BAD_TARGETS; - if (m_caster->GetCharmGuid()) + if (m_casterUnit->GetCharmGuid()) return SPELL_FAILED_ALREADY_HAVE_CHARM; - if (m_caster->GetCharmerGuid()) + if (m_casterUnit->GetCharmerGuid()) #if SUPPORTED_CLIENT_BUILD >= CLIENT_BUILD_1_11_2 return SPELL_FAILED_CHARMED; #else return SPELL_FAILED_FIZZLE; #endif - Pet* pet = m_caster->GetPet(); + Pet* pet = m_casterUnit->GetPet(); if (!pet) return SPELL_FAILED_NO_PET; @@ -6681,10 +6839,13 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_AURA_MOUNTED: { - if (m_caster->IsInWater()) + if (!m_casterUnit) + return SPELL_FAILED_BAD_TARGETS; + + if (m_casterUnit->IsInWater()) return SPELL_FAILED_ONLY_ABOVEWATER; - if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->GetTransport()) + if (m_casterUnit->IsPlayer() && ((Player*)m_casterUnit)->GetTransport()) return SPELL_FAILED_NO_MOUNTS_ALLOWED; /// Specific case for Temple of Ahn'Qiraj mounts as they are usable only in AQ40 and are the only mounts allowed here @@ -6698,7 +6859,7 @@ SpellCastResult Spell::CheckCast(bool strict) case 26054: case 26055: case 26056: - if (m_caster->GetMapId() == 531) + if (m_casterUnit->GetMapId() == 531) { isAQ40Mount = true; break; @@ -6706,19 +6867,19 @@ SpellCastResult Spell::CheckCast(bool strict) else return SPELL_FAILED_NOT_HERE; default: - if ((m_caster->GetMapId() == 531 && m_caster->GetTerrain()->IsOutdoors(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ()))) + if ((m_casterUnit->GetMapId() == 531 && m_casterUnit->GetTerrain()->IsOutdoors(m_casterUnit->GetPositionX(), m_casterUnit->GetPositionY(), m_casterUnit->GetPositionZ()))) isAQ40Mount = true; break; } // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells - if (!isAQ40Mount && m_caster->GetTypeId() == TYPEID_PLAYER && !sMapStorage.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell) //[-ZERO] && !m_spellInfo->AreaId) + if (!isAQ40Mount && m_casterUnit->IsPlayer() && !sMapStorage.LookupEntry(m_casterUnit->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell) //[-ZERO] && !m_spellInfo->AreaId) return SPELL_FAILED_NO_MOUNTS_ALLOWED; - if (m_caster->GetAreaId() == 35) + if (m_casterUnit->GetAreaId() == 35) return SPELL_FAILED_NO_MOUNTS_ALLOWED; - if (m_caster->IsInDisallowedMountForm()) + if (m_casterUnit->IsInDisallowedMountForm()) return SPELL_FAILED_NOT_SHAPESHIFT; break; @@ -6741,7 +6902,7 @@ SpellCastResult Spell::CheckCast(bool strict) !IsAreaEffectTarget(SpellTarget(m_spellInfo->EffectImplicitTargetB[i]))) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if (m_caster->GetTypeId() != TYPEID_PLAYER || m_CastItem) + if (!m_caster->IsPlayer() || m_CastItem) break; if (m_targets.getUnitTarget()->getPowerType() != POWER_MANA) @@ -6751,7 +6912,7 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_AURA_MOD_SHAPESHIFT: { - if (m_caster->HasAura(23397)) // Ustaag : Nefarian Warrior Class Call + if (m_casterUnit && m_casterUnit->HasAura(23397)) // Ustaag : Nefarian Warrior Class Call return SPELL_FAILED_NOT_SHAPESHIFT; break; } @@ -6774,7 +6935,7 @@ SpellCastResult Spell::CheckCast(bool strict) // check trade slot case (last, for allow catch any another cast problems) if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return SPELL_FAILED_NOT_TRADING; Player *pCaster = ((Player*)m_caster); @@ -6802,18 +6963,20 @@ SpellCastResult Spell::CheckCast(bool strict) SpellCastResult Spell::CheckPetCast(Unit* target) { - if (!m_caster->isAlive()) + if (!m_casterUnit->isAlive()) return SPELL_FAILED_CASTER_DEAD; - if (m_caster->IsNonMeleeSpellCasted(false)) //prevent spellcast interruption by another spellcast + //prevent spellcast interruption by another spellcast + if (m_casterUnit->IsNonMeleeSpellCasted(false)) return SPELL_FAILED_SPELL_IN_PROGRESS; - if (m_caster->isInCombat() && m_spellInfo->IsNonCombatSpell()) + + if (m_casterUnit->isInCombat() && m_spellInfo->IsNonCombatSpell()) return SPELL_FAILED_AFFECTING_COMBAT; - if (m_caster->GetTypeId() == TYPEID_UNIT && (((Creature*)m_caster)->IsPet() || m_caster->isCharmed())) + if (m_casterUnit->IsCreature() && (((Creature*)m_casterUnit)->IsPet() || m_casterUnit->isCharmed())) { //dead owner (pets still alive when owners ressed?) - if (m_caster->GetCharmerOrOwner() && !m_caster->GetCharmerOrOwner()->isAlive()) + if (m_casterUnit->GetCharmerOrOwner() && !m_casterUnit->GetCharmerOrOwner()->isAlive()) return SPELL_FAILED_CASTER_DEAD; if (!target && m_targets.getUnitTarget()) @@ -6842,13 +7005,13 @@ SpellCastResult Spell::CheckPetCast(Unit* target) if (_target) //for target dead/target not valid { - if (!_target->isTargetableForAttack(false, m_caster->IsPlayer())) + if (!_target->isTargetableForAttack(false, m_casterUnit->IsPlayer())) return SPELL_FAILED_BAD_TARGETS; // guessed error // SPELL_EFFECT_DISPEL -> Positive or negative depending on the target if (m_spellInfo->IsPositiveSpell() && !m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) { - if (m_caster->IsHostileTo(_target)) + if (m_casterUnit->IsHostileTo(_target)) return SPELL_FAILED_BAD_TARGETS; } else @@ -6861,12 +7024,12 @@ SpellCastResult Spell::CheckPetCast(Unit* target) m_spellInfo->EffectImplicitTargetA[j] == TARGET_CASTER_COORDINATES) checkValidAttackTarget = false; } - if (checkValidAttackTarget && !m_caster->IsValidAttackTarget(_target)) + if (checkValidAttackTarget && !m_casterUnit->IsValidAttackTarget(_target)) return SPELL_FAILED_BAD_TARGETS; } } //cooldown - if (m_caster->HasSpellCooldown(m_spellInfo->Id) || m_caster->HasSpellCategoryCooldown(m_spellInfo->Category)) + if (m_casterUnit->HasSpellCooldown(m_spellInfo->Id) || m_casterUnit->HasSpellCategoryCooldown(m_spellInfo->Category)) return SPELL_FAILED_NOT_READY; } @@ -6875,6 +7038,9 @@ SpellCastResult Spell::CheckPetCast(Unit* target) SpellCastResult Spell::CheckCasterAuras() const { + if (!m_casterUnit) + return SPELL_CAST_OK; + // Flag drop spells totally immuned to caster auras // FIXME: find more nice check for all totally immuned spells // AttributesEx3 & 0x10000000? @@ -6906,7 +7072,7 @@ SpellCastResult Spell::CheckCasterAuras() const // Check whether the cast should be prevented by any state you might have. SpellCastResult prevented_reason = SPELL_CAST_OK; // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out - uint32 unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); // Get unit state + uint32 unitflag = m_casterUnit->GetUInt32Value(UNIT_FIELD_FLAGS); // Get unit state if (unitflag & UNIT_FLAG_STUNNED && !(mechanic_immune & (1 << (MECHANIC_STUN - 1u)))) prevented_reason = SPELL_FAILED_STUNNED; else if (unitflag & UNIT_FLAG_CONFUSED && !(mechanic_immune & CONFUSED_MECHANIC_MASK)) @@ -6915,7 +7081,7 @@ SpellCastResult Spell::CheckCasterAuras() const prevented_reason = SPELL_FAILED_FLEEING; else if (m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && (unitflag & UNIT_FLAG_SILENCED || - m_caster->IsSpellProhibited(m_spellInfo))) // Nostalrius : fix counterspell for mobs. + m_casterUnit->IsSpellProhibited(m_spellInfo))) // Nostalrius : fix counterspell for mobs. prevented_reason = SPELL_FAILED_SILENCED; else if (unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY) prevented_reason = SPELL_FAILED_PACIFIED; @@ -6926,7 +7092,7 @@ SpellCastResult Spell::CheckCasterAuras() const if (school_immune || mechanic_immune || dispel_immune) { //Checking auras is needed now, because you are prevented by some state but the spell grants immunity. - auto const& auras = m_caster->GetSpellAuraHolderMap(); + auto const& auras = m_casterUnit->GetSpellAuraHolderMap(); for (auto itr = auras.begin(); itr != auras.end(); ++itr) { SpellAuraHolder *holder = itr->second; @@ -6985,11 +7151,14 @@ SpellCastResult Spell::CheckCasterAuras() const bool Spell::CanAutoCast(Unit* target) { + if (!m_casterUnit) + return false; + ObjectGuid targetguid = target->GetObjectGuid(); // Nostalrius - par exemple roder ne doit pas se declencher si on envoie le pet attaquer. // Sinon le pet revient a cause de cet attribut : - if (m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET && m_caster->getVictim()) + if (m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET && m_casterUnit->getVictim()) return false; // 2947 - Fire Shield, rank 1 enUS @@ -7040,8 +7209,8 @@ SpellCastResult Spell::CheckRange(bool strict) // Pour 'TARGET_PET', 'getUnitTarget' renvoit le caster >.< for (int i = 0; i < MAX_EFFECT_INDEX; ++i) if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET) - if (m_caster) - if (Pet* pPet = m_caster->GetPet()) + if (m_casterUnit) + if (Pet* pPet = m_casterUnit->GetPet()) target = pPet; // special range cases @@ -7064,10 +7233,13 @@ SpellCastResult Spell::CheckRange(bool strict) float range_mod = 1.0f; - if (Player* modOwner = m_caster->GetSpellModOwner()) + if (m_casterUnit) { - float base = ATTACK_DISTANCE; - range_mod += modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, base, this); + if (Player* modOwner = m_casterUnit->GetSpellModOwner()) + { + float base = ATTACK_DISTANCE; + range_mod += modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, base, this); + } } // with additional 5 dist for non stricted case (some melee spells have delay in apply @@ -7084,8 +7256,11 @@ SpellCastResult Spell::CheckRange(bool strict) float max_range = GetSpellMaxRange(srange); float min_range = GetSpellMinRange(srange); - if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this); + if (m_casterUnit) + { + if (Player* modOwner = m_casterUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this); + } max_range += range_mod; @@ -7106,7 +7281,7 @@ SpellCastResult Spell::CheckRange(bool strict) return SPELL_FAILED_OUT_OF_RANGE; if (min_range && dist < min_range) return SPELL_FAILED_TOO_CLOSE; - if (m_caster->GetTypeId() == TYPEID_PLAYER && + if (m_caster->IsPlayer() && (m_spellInfo->Custom & SPELL_CUSTOM_FACE_TARGET) && !m_caster->HasInArc(M_PI_F, target)) return SPELL_FAILED_UNIT_NOT_INFRONT; } @@ -7125,6 +7300,9 @@ SpellCastResult Spell::CheckRange(bool strict) uint32 Spell::CalculatePowerCost(SpellEntry const* spellInfo, Unit* caster, Spell* spell, Item* castItem) { + if (!caster) + return 0; + // item cast not used power if (castItem) return 0; @@ -7191,13 +7369,13 @@ uint32 Spell::CalculatePowerCost(SpellEntry const* spellInfo, Unit* caster, Spel SpellCastResult Spell::CheckPower() const { // item cast not used power - if (m_CastItem || m_IsTriggeredSpell) + if (m_CastItem || m_IsTriggeredSpell || !m_casterUnit) return SPELL_CAST_OK; // health as power used - need check health amount if (m_spellInfo->powerType == POWER_HEALTH) { - if (m_caster->GetHealth() <= m_powerCost) + if (m_casterUnit->GetHealth() <= m_powerCost) return SPELL_FAILED_CASTER_AURASTATE; return SPELL_CAST_OK; } @@ -7211,7 +7389,7 @@ SpellCastResult Spell::CheckPower() const // Check power amount Powers powerType = Powers(m_spellInfo->powerType); - if (m_caster->GetPower(powerType) < m_powerCost) + if (m_casterUnit->GetPower(powerType) < m_powerCost) return SPELL_FAILED_NO_POWER; return SPELL_CAST_OK; @@ -7255,7 +7433,7 @@ SpellCastResult Spell::CheckItems() } } - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return SPELL_CAST_OK; Player* p_caster = (Player*)m_caster; @@ -7340,7 +7518,7 @@ SpellCastResult Spell::CheckItems() // check target item (for triggered case not report error) if (m_targets.getItemTargetGuid()) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster->IsPlayer()) return m_IsTriggeredSpell && !(m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM) ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_BAD_TARGETS; @@ -7587,7 +7765,7 @@ SpellCastResult Spell::CheckItems() case SPELL_EFFECT_WEAPON_DAMAGE: case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: { - if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER; + if (!m_caster->IsPlayer()) return SPELL_FAILED_TARGET_NOT_PLAYER; if (m_attackType != RANGED_ATTACK) break; Item *pItem = ((Player*)m_caster)->GetWeaponForAttack(m_attackType, true, false); @@ -7655,7 +7833,7 @@ SpellCastResult Spell::CheckItems() void Spell::Delayed() { - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER) + if (!m_caster || !m_caster->IsPlayer()) return; if (m_spellState == SPELL_STATE_DELAYED) @@ -7671,7 +7849,7 @@ void Spell::Delayed() //check resist chance int32 resistChance = 100; //must be initialized to 100 for percent modifiers ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, resistChance, this); - resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; + resistChance += m_casterUnit->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; if (roll_chance_i(resistChance)) return; @@ -7691,19 +7869,19 @@ void Spell::Delayed() data << ObjectGuid(m_caster->GetObjectGuid()); data << uint32(delaytime); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_caster->IsPlayer()) ((Player*)m_caster)->SendDirectMessage(&data); } void Spell::DelayedChannel() { - if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING) + if (!m_caster || !m_caster->IsPlayer() || getState() != SPELL_STATE_CASTING) return; //check resist chance int32 resistChance = 100; //must be initialized to 100 for percent modifiers ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, resistChance, this); - resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; + resistChance += m_casterUnit->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100; if (roll_chance_i(resistChance)) return; @@ -7726,7 +7904,7 @@ void Spell::DelayedChannel() if ((*ihit).missCondition == SPELL_MISS_NONE) { - if (Unit* unit = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID)) + if (Unit* unit = m_caster->GetObjectGuid() == ihit->targetGUID ? m_casterUnit : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID)) unit->DelaySpellAuraHolder(m_spellInfo->Id, delaytime, m_caster->GetObjectGuid()); } } @@ -7747,16 +7925,16 @@ void Spell::DelayedChannel() void Spell::UpdateOriginalCasterPointer() { if (m_originalCasterGUID == m_caster->GetObjectGuid()) - m_originalCaster = m_caster; + m_originalCaster = m_casterUnit; else if (m_originalCasterGUID.IsGameObject()) { GameObject* go = m_caster->IsInWorld() ? m_caster->GetMap()->GetGameObject(m_originalCasterGUID) : NULL; - m_originalCaster = go ? go->GetOwner() : NULL; + m_originalCaster = go ? go->GetOwner() : nullptr; } else { Unit* unit = ObjectAccessor::GetUnit(*m_caster, m_originalCasterGUID); - m_originalCaster = unit && unit->IsInWorld() ? unit : NULL; + m_originalCaster = unit && unit->IsInWorld() ? unit : nullptr; } } @@ -7811,9 +7989,9 @@ CurrentSpellTypes Spell::GetCurrentContainer() const bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) { - if (target != m_caster && m_spellInfo->IsPositiveSpell()) + if (m_casterUnit && target != m_caster && m_spellInfo->IsPositiveSpell()) { - Player* casterOwner = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself(); + Player* casterOwner = m_casterUnit->GetCharmerOrOwnerPlayerOrPlayerItself(); Player* targetOwner = target->GetCharmerOrOwnerPlayerOrPlayerItself(); if (targetOwner && casterOwner != targetOwner && targetOwner->duel && casterOwner && !casterOwner->IsInDuelWith(targetOwner)) return false; @@ -7847,7 +8025,7 @@ bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) } // Check player targets and remove if in GM mode or GM invisibility (for not self casting case) - if (target != m_caster && target->GetTypeId() == TYPEID_PLAYER) + if (target != m_caster && target->IsPlayer()) { if (((Player*)target)->GetVisibility() == VISIBILITY_OFF) return false; @@ -8104,7 +8282,7 @@ SpellCastResult Spell::CanOpenLock(SpellEffectIndex effIndex, uint32 lockId, Ski 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 ? + skillValue = m_CastItem || !m_caster->IsPlayer() ? 0 : ((Player*)m_caster)->GetSkillValue(skillId); skillValue += spellSkillBonus; @@ -8403,12 +8581,15 @@ void Spell::ClearCastItem() bool Spell::HasGlobalCooldown() const { - return m_caster->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo); + return m_casterUnit ? m_casterUnit->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo) : false; } void Spell::TriggerGlobalCooldown() { - if (Player* pPlayer = m_caster->ToPlayer()) + if (!m_casterUnit) + return; + + if (Player* pPlayer = m_casterUnit->ToPlayer()) if (pPlayer->HasOption(PLAYER_CHEAT_NO_COOLDOWN)) return; @@ -8423,9 +8604,9 @@ void Spell::TriggerGlobalCooldown() { // apply haste rating #if SUPPORTED_CLIENT_BUILD >= CLIENT_BUILD_1_12_1 - gcd = int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_SPEED)); + gcd = int32(float(gcd) * m_casterUnit->GetFloatValue(UNIT_MOD_CAST_SPEED)); #else - gcd = int32(float(gcd) * (1.0f + m_caster->GetInt32Value(UNIT_MOD_CAST_SPEED)/100.0f)); + gcd = int32(float(gcd) * (1.0f + m_casterUnit->GetInt32Value(UNIT_MOD_CAST_SPEED)/100.0f)); #endif if (gcd < 1000) @@ -8435,22 +8616,25 @@ void Spell::TriggerGlobalCooldown() } // global cooldown only for player or controlled units - if (m_caster->GetCharmInfo() || m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd); + if (m_casterUnit->GetCharmInfo() || m_casterUnit->IsPlayer()) + m_casterUnit->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd); } void Spell::CancelGlobalCooldown() { + if (!m_casterUnit) + return; + if (!m_spellInfo->StartRecoveryTime) return; // cancel global cooldown when interrupting current cast - if (m_caster->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) + if (m_casterUnit->GetCurrentSpell(CURRENT_GENERIC_SPELL) != this) return; // global cooldown have only player or controlled units - if (m_caster->GetCharmInfo() || m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo); + if (m_casterUnit->GetCharmInfo() || m_casterUnit->IsPlayer()) + m_casterUnit->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo); } void Spell::ResetEffectDamageAndHeal() @@ -8479,7 +8663,7 @@ void Spell::OnSpellLaunch() LockEntry const *lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->GetLockId()); if (lockInfo && lockInfo->Index[1] == LOCKTYPE_SLOW_OPEN) { - Spell *visual = new Spell(m_caster, sSpellMgr.GetSpellEntry(24390), true); + Spell *visual = m_casterUnit ? new Spell(m_casterUnit, sSpellMgr.GetSpellEntry(24390), true) : new Spell(m_casterGo, sSpellMgr.GetSpellEntry(24390), true); visual->prepare(); } } @@ -8496,16 +8680,19 @@ void Spell::OnSpellLaunch() isCharge = true; if (!isCharge) return; - + + if (!m_casterUnit) + return; + // Delay attack, otherwise player makes instant attack after cast - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_casterUnit->IsPlayer()) { - m_caster->setAttackTimer(BASE_ATTACK, m_caster->getAttackTimer(BASE_ATTACK) + 200 + 40 * m_caster->GetDistance(unitTarget)); - m_caster->setAttackTimer(OFF_ATTACK, m_caster->getAttackTimer(OFF_ATTACK) + 200 + 40 * m_caster->GetDistance(unitTarget)); + m_casterUnit->setAttackTimer(BASE_ATTACK, m_casterUnit->getAttackTimer(BASE_ATTACK) + 200 + 40 * m_casterUnit->GetDistance(unitTarget)); + m_casterUnit->setAttackTimer(OFF_ATTACK, m_casterUnit->getAttackTimer(OFF_ATTACK) + 200 + 40 * m_casterUnit->GetDistance(unitTarget)); } - bool triggerAutoAttack = unitTarget != m_caster && !m_spellInfo->IsPositiveSpell() && !(m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET); - m_caster->GetMotionMaster()->MoveCharge(unitTarget, sWorld.getConfig(CONFIG_UINT32_SPELLS_CCDELAY), triggerAutoAttack); + bool triggerAutoAttack = unitTarget != m_casterUnit && !m_spellInfo->IsPositiveSpell() && !(m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET); + m_casterUnit->GetMotionMaster()->MoveCharge(unitTarget, sWorld.getConfig(CONFIG_UINT32_SPELLS_CCDELAY), triggerAutoAttack); } bool Spell::HasModifierApplied(SpellModifier* mod) @@ -8518,33 +8705,36 @@ bool Spell::HasModifierApplied(SpellModifier* mod) void Spell::RemoveStealthAuras() { + if (!m_casterUnit) + return; + // 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 (!m_IsTriggeredSpell && !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_BREAK_STEALTH) && m_caster->GetTypeId() == TYPEID_PLAYER + if (!m_IsTriggeredSpell && !(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_BREAK_STEALTH) && m_casterUnit->IsPlayer() && m_spellInfo->SpellIconID != 250 // Camouflage fufu && m_spellInfo->SpellIconID != 103 // Roder ou Camouflage elfe de la nuit && m_spellInfo->SpellIconID != 252 // Disparition ) { - bool doUnaura = m_caster->HasAuraType(SPELL_AURA_MOD_STEALTH); + bool doUnaura = m_casterUnit->HasAuraType(SPELL_AURA_MOD_STEALTH); - if (doUnaura && m_caster->GetTypeId() == TYPEID_PLAYER + if (doUnaura && m_casterUnit->IsPlayer() && m_spellInfo->SpellIconID == 249 // Assommer ) { // Gestion de assommer ameliore - if (m_caster->HasAura(14076)) // Rang 1 + if (m_casterUnit->HasAura(14076)) // Rang 1 doUnaura = (urand(0, 99) > 30); - else if (m_caster->HasAura(14094)) // Rang 2 + else if (m_casterUnit->HasAura(14094)) // Rang 2 doUnaura = (urand(0, 99) > 60); - else if (m_caster->HasAura(14095)) // Rang 3 + else if (m_casterUnit->HasAura(14095)) // Rang 3 doUnaura = (urand(0, 99) > 90); } if (doUnaura) - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + m_casterUnit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); } if (!m_IsTriggeredSpell) - m_caster->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + m_casterUnit->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); } void Spell::Delete() const diff --git a/src/game/Spells/Spell.h b/src/game/Spells/Spell.h index 75cc164b6..542662b12 100644 --- a/src/game/Spells/Spell.h +++ b/src/game/Spells/Spell.h @@ -162,7 +162,7 @@ class SpellCastTargets bool IsEmpty() const { return !m_GOTargetGUID && !m_unitTargetGUID && !m_itemTarget && !m_CorpseTargetGUID; } - void Update(Unit* caster); + void Update(WorldObject* caster); float m_srcX, m_srcY, m_srcZ; float m_destX, m_destY, m_destZ; @@ -224,7 +224,7 @@ class Spell { friend struct MaNGOS::SpellNotifierPlayer; friend struct MaNGOS::SpellNotifierCreatureAndPlayer; - friend void Unit::SetCurrentCastedSpell( Spell * pSpell ); + friend void WorldObject::SetCurrentCastedSpell(Spell* pSpell); public: void EffectEmpty(SpellEffectIndex eff_idx); @@ -325,6 +325,7 @@ class Spell void HandleAddTargetTriggerAuras(); Spell(Unit* caster, SpellEntry const *info, bool triggered, ObjectGuid originalCasterGUID = ObjectGuid(), SpellEntry const* triggeredBy = NULL, Unit* victim = NULL, SpellEntry const* triggeredByParent = NULL); + Spell(GameObject* caster, SpellEntry const *info, bool triggered, ObjectGuid originalCasterGUID = ObjectGuid(), SpellEntry const* triggeredBy = NULL, Unit* victim = NULL, SpellEntry const* triggeredByParent = NULL); ~Spell(); SpellCastResult prepare(SpellCastTargets targets, Aura* triggeredByAura = nullptr, uint32 chance = 0); @@ -421,7 +422,7 @@ class Spell { return m_spellInfo->Attributes & SPELL_ATTR_RANGED; } - bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; } + bool IsChannelActive() const { return m_casterUnit ? m_casterUnit->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0 : false; } bool IsMeleeAttackResetSpell() const { return !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); } bool IsRangedAttackResetSpell() const { return !m_IsTriggeredSpell && IsRangedSpell() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); } @@ -439,11 +440,11 @@ class Spell // caster types: // formal spell caster, in game source of spell affects cast - Unit* GetCaster() const { return m_caster; } + WorldObject* GetCaster() const { return m_caster; } // real source of cast affects, explicit caster, or DoT/HoT applier, or GO owner, or wild GO itself. Can be NULL WorldObject* GetAffectiveCasterObject() const; // limited version returning NULL in cases wild gameobject caster object, need for Aura (auras currently not support non-Unit caster) - Unit* GetAffectiveCaster() const { return m_originalCasterGUID ? m_originalCaster : m_caster; } + Unit* GetAffectiveCaster() const { return m_originalCasterGUID ? m_originalCaster : m_casterUnit; } // m_originalCasterGUID can store GO guid, and in this case this is visual caster WorldObject* GetCastingObject() const; @@ -502,10 +503,12 @@ class Spell bool IgnoreItemRequirements() const; // some item use spells have unexpected reagent data void UpdateOriginalCasterPointer(); - Unit* m_caster; + WorldObject* const m_caster = nullptr; + Unit* const m_casterUnit = nullptr; + GameObject* const m_casterGo = nullptr; ObjectGuid m_originalCasterGUID; // real source of cast (aura caster/etc), used for spell targets selection - // e.g. damage around area spell trigered by victim aura and da,age emeies of aura caster + // e.g. damage around area spell trigered by victim aura and damage enemies of aura caster Unit* m_originalCaster; // cached pointer for m_originalCaster, updated at Spell::UpdatePointers() Spell** m_selfContainer; // pointer to our spell container (if applicable) diff --git a/src/game/Spells/SpellEffects.cpp b/src/game/Spells/SpellEffects.cpp index 5bd9e9a9d..de5e10b8a 100644 --- a/src/game/Spells/SpellEffects.cpp +++ b/src/game/Spells/SpellEffects.cpp @@ -276,7 +276,7 @@ void Spell::EffectInstaKill(SpellEffectIndex /*eff_idx*/) return; // Demonic Sacrifice - if (m_spellInfo->Id == 18788 && unitTarget->GetTypeId() == TYPEID_UNIT) + if (m_spellInfo->Id == 18788 && unitTarget->IsCreature() && m_casterUnit) { uint32 entry = unitTarget->GetEntry(); uint32 spellID; @@ -299,7 +299,7 @@ void Spell::EffectInstaKill(SpellEffectIndex /*eff_idx*/) return; } - m_caster->CastSpell(m_caster, spellID, true); + m_casterUnit->CastSpell(m_casterUnit, spellID, true); } if (m_caster == unitTarget) // prevent interrupt message @@ -319,19 +319,19 @@ void Spell::EffectInstaKill(SpellEffectIndex /*eff_idx*/) void Spell::EffectEnvironmentalDMG(SpellEffectIndex eff_idx) { - uint32 absorb = 0; - int32 resist = 0; + if (!unitTarget || !unitTarget->isAlive()) + return; - // Note: this hack with damage replace required until GO casting not implemented - // environment damage spells already have around enemies targeting but this not help in case nonexistent GO casting support - // currently each enemy selected explicitly and self cast damage, we prevent apply self casted spell bonuses/etc - damage = m_spellInfo->CalculateSimpleValue(eff_idx); + if (unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->EnvironmentalDamage(DAMAGE_FIRE, damage); + else + { + uint32 absorb = 0; + int32 resist = 0; + unitTarget->CalculateDamageAbsorbAndResist(m_caster, m_spellInfo->GetSpellSchoolMask(), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist, m_spellInfo); + m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_spellInfo->GetSpellSchoolMask(), absorb, resist, false, 0, false); - m_caster->CalculateDamageAbsorbAndResist(m_caster, m_spellInfo->GetSpellSchoolMask(), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist, m_spellInfo); - - m_caster->SendSpellNonMeleeDamageLog(m_caster, m_spellInfo->Id, damage, m_spellInfo->GetSpellSchoolMask(), absorb, resist, false, 0, false); - if (m_caster->GetTypeId() == TYPEID_PLAYER) - ((Player*)m_caster)->EnvironmentalDamage(DAMAGE_FIRE, damage); + } } void Spell::EffectSchoolDMG(SpellEffectIndex effect_idx) @@ -431,20 +431,23 @@ void Spell::EffectSchoolDMG(SpellEffectIndex effect_idx) break; case SPELLFAMILY_WARRIOR: { + if (!m_casterUnit) + break; + // Bloodthirst if (m_spellInfo->SpellIconID == 38 && m_spellInfo->IsFitToFamilyMask()) { - float attackPower = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + float attackPower = m_casterUnit->GetTotalAttackPowerValue(BASE_ATTACK); if (unitTarget) - attackPower += m_caster->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS, unitTarget->GetCreatureTypeMask()); + attackPower += m_casterUnit->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS, unitTarget->GetCreatureTypeMask()); damage = uint32(damage * attackPower / 100); } // Shield Slam else if (m_spellInfo->IsFitToFamilyMask()) - damage += int32(m_caster->GetShieldBlockValue()); + damage += int32(m_casterUnit->GetShieldBlockValue()); // Execute trigger else if (m_spellInfo->Id == 20647) - m_caster->SetPower(POWER_RAGE, 0); + m_casterUnit->SetPower(POWER_RAGE, 0); break; } case SPELLFAMILY_WARLOCK: @@ -472,17 +475,21 @@ void Spell::EffectSchoolDMG(SpellEffectIndex effect_idx) // Ferocious Bite if (m_spellInfo->IsFitToFamilyMask() && m_spellInfo->SpellVisual == 6587) { + Player* pPlayer = m_caster->ToPlayer(); + if (!pPlayer) + break; + #if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_11_2 // World of Warcraft Client Patch 1.12.0 (2006-08-22) // - Ferocious Bite: Book of Ferocious Bite (Rank 5) now drops off The // Beast in Black Rock Spire. In addition, Ferocious Bite now increases // in potency with greater attack power. // ( AP * 3% * combo + energy * 2,7 + damage ) - if (uint32 combo = ((Player*)m_caster)->GetComboPoints()) - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f); + if (uint32 combo = ((Player*)pPlayer)->GetComboPoints()) + damage += int32(pPlayer->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f); #endif - damage += int32(m_caster->GetPower(POWER_ENERGY) * m_spellInfo->DmgMultiplier[effect_idx]); - m_caster->SetPower(POWER_ENERGY, 0); + damage += int32(pPlayer->GetPower(POWER_ENERGY) * m_spellInfo->DmgMultiplier[effect_idx]); + pPlayer->SetPower(POWER_ENERGY, 0); } break; } @@ -493,10 +500,11 @@ void Spell::EffectSchoolDMG(SpellEffectIndex effect_idx) // - Eviscerate: Manual of Eviscerate (Rank 9) now drops off Blackhand // Assassins in Black Rock Spire.In addition, Eviscerate now increases // in potency with greater attack power. - if (m_spellInfo->IsFitToFamilyMask() && m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_spellInfo->IsFitToFamilyMask()) { - if (uint32 combo = ((Player*)m_caster)->GetComboPoints()) - damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f); + if (Player* pPlayer = m_caster->ToPlayer()) + if (uint32 combo = pPlayer->GetComboPoints()) + damage += int32(pPlayer->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f); } #endif break; @@ -590,7 +598,8 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 25716: // Force Self - Bow { - m_caster->HandleEmote(EMOTE_ONESHOT_BOW); + if (m_casterUnit) + m_casterUnit->HandleEmote(EMOTE_ONESHOT_BOW); return; } case 27798: // Nature's Bounty @@ -740,17 +749,18 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) else if (roll < 80) // Polymorph (10% chance, 3% to self, backfire) { spell_id = 13323; - newTarget = m_caster; + newTarget = m_casterUnit; } else if (roll < 95) // Enveloping Winds (15% chance) spell_id = 25189; else // Summon Felhund minion (5% chance) { spell_id = 14642; - newTarget = m_caster; + newTarget = m_casterUnit; } - m_caster->CastSpell(newTarget, spell_id, true, m_CastItem); + if (newTarget) + m_caster->CastSpell(newTarget, spell_id, true, m_CastItem); return; } case 17009: // Voodoo @@ -827,7 +837,8 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 8063: // Deviate Fish { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* pPlayer = m_caster->ToPlayer(); + if (!pPlayer) return; uint32 spell_id = 0; @@ -849,18 +860,19 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) spell_id = 8068; break; // Healthy Spirit } - m_caster->CastSpell(m_caster, spell_id, true, nullptr); + pPlayer->CastSpell(pPlayer, spell_id, true, nullptr); return; } case 8213: // Savory Deviate Delight { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* pPlayer = m_caster->ToPlayer(); + if (!pPlayer) return; uint32 spell_id = 0; uint32 spells[6] = { - (m_caster->getGender() == GENDER_MALE ? 8219u : 8220u), // Flip Out - ninja - (m_caster->getGender() == GENDER_MALE ? 8221u : 8222u), // Yaaarrrr - pirate + (pPlayer->getGender() == GENDER_MALE ? 8219u : 8220u), // Flip Out - ninja + (pPlayer->getGender() == GENDER_MALE ? 8221u : 8222u), // Yaaarrrr - pirate 8223u, // Oops - goo 8215u, // Rapid Cast 8224u, // Cowardice @@ -873,7 +885,7 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) else spell_id = spells[urand(0, 1)]; - m_caster->CastSpell(m_caster, spell_id, true, nullptr); + pPlayer->CastSpell(pPlayer, spell_id, true, nullptr); return; } case 8593: // Symbol of life (restore creature to life) @@ -886,10 +898,10 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 9204: // Hate to Zero { - if (!unitTarget) + if (!unitTarget || !m_casterUnit) return; - m_caster->getThreatManager().modifyThreatPercent(unitTarget, -100); + m_casterUnit->getThreatManager().modifyThreatPercent(unitTarget, -100); return; } case 9976: // Polly Eats the E.C.A.C. @@ -907,16 +919,16 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) case 12850: // (now good common check for this spells) case 12868: { - if (!unitTarget) + if (!unitTarget || !m_casterUnit) return; float damage; // DW should benefit of attack power, damage percent mods etc. // TODO: check if using offhand damage is correct and if it should be divided by 2 - if (m_caster->haveOffhandWeapon() && m_caster->getAttackTimer(BASE_ATTACK) > m_caster->getAttackTimer(OFF_ATTACK)) - damage = (m_caster->GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2; + if (m_casterUnit->haveOffhandWeapon() && m_casterUnit->getAttackTimer(BASE_ATTACK) > m_casterUnit->getAttackTimer(OFF_ATTACK)) + damage = (m_casterUnit->GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + m_casterUnit->GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2; else - damage = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2; + damage = (m_casterUnit->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_casterUnit->GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2; switch (m_spellInfo->Id) { @@ -935,13 +947,16 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) }; int32 deepWoundsDotBasePoints0 = int32(damage / 4); - m_caster->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, nullptr, nullptr, true, nullptr); + m_casterUnit->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, nullptr, nullptr, true, nullptr); return; } case 12975: //Last Stand { - int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth() * 0.3); - m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, nullptr, nullptr, true, nullptr); + if (!m_casterUnit) + return; + + int32 healthModSpellBasePoints0 = int32(m_casterUnit->GetMaxHealth() * 0.3); + m_casterUnit->CastCustomSpell(m_casterUnit, 12976, &healthModSpellBasePoints0, nullptr, nullptr, true, nullptr); return; } case 13120: // net-o-matic @@ -1022,7 +1037,8 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 16589: // Noggenfogger Elixir { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* pPlayer = m_caster->ToPlayer(); + if (!pPlayer) return; uint32 spell_id = 0; @@ -1039,7 +1055,7 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) break; } - m_caster->CastSpell(m_caster, spell_id, true, nullptr); + pPlayer->CastSpell(pPlayer, spell_id, true, nullptr); return; } case 17251: // Spirit Healer Res @@ -1059,6 +1075,9 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 17271: // Test Fetid Skull { + if (!m_casterUnit) + return; + if (!itemTarget && m_caster->GetTypeId() != TYPEID_PLAYER) return; @@ -1066,12 +1085,14 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) ? 17269 // Create Resonating Skull : 17270; // Create Bone Dust - m_caster->CastSpell(m_caster, spell_id, true, nullptr); + + m_casterUnit->CastSpell(m_casterUnit, spell_id, true, nullptr); return; } case 17770: // Wolfshead Helm Energy { - m_caster->CastSpell(m_caster, 29940, true, nullptr); + if (m_casterUnit) + m_casterUnit->CastSpell(m_casterUnit, 29940, true, nullptr); return; } case 17950: @@ -1146,35 +1167,36 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) #endif case 20572: // Blood Fury { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* pPlayer = m_caster->ToPlayer(); + if (!pPlayer) return; #if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_8_4 - m_caster->CastSpell(m_caster, 23230, true); + pPlayer->CastSpell(pPlayer, 23230, true); #endif - damage = damage * (m_caster->GetInt32Value(UNIT_FIELD_ATTACK_POWER)) / 100; + damage = damage * (pPlayer->GetInt32Value(UNIT_FIELD_ATTACK_POWER)) / 100; if (damage > 0) - m_caster->CastCustomSpell(m_caster, 23234, &damage, nullptr, nullptr, true, nullptr); + pPlayer->CastCustomSpell(pPlayer, 23234, &damage, nullptr, nullptr, true, nullptr); return; } case 20577: // Cannibalize { - if (unitTarget || corpseTarget) - m_caster->CastSpell(m_caster, 20578, true, nullptr); + if (m_casterUnit && (unitTarget || corpseTarget)) + m_casterUnit->CastSpell(m_casterUnit, 20578, true, nullptr); return; } case 21147: // Arcane Vacuum { - if (unitTarget) + if (unitTarget && m_casterUnit) { // Spell used by Azuregos to teleport all the players to him // This also resets the target threat - if (m_caster->getThreatManager().getThreat(unitTarget)) - m_caster->getThreatManager().modifyThreatPercent(unitTarget, -100); + if (m_casterUnit->getThreatManager().getThreat(unitTarget)) + m_casterUnit->getThreatManager().modifyThreatPercent(unitTarget, -100); // cast summon player - m_caster->CastSpell(unitTarget, 21150, true); + m_casterUnit->CastSpell(unitTarget, 21150, true); } return; @@ -1221,57 +1243,57 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 23074: // Arcanite Dragonling { - if (!m_CastItem) + if (!m_CastItem || !m_casterUnit) return; - m_caster->CastSpell(m_caster, 19804, true, m_CastItem); + m_casterUnit->CastSpell(m_casterUnit, 19804, true, m_CastItem); return; } case 23075: // Mithril Mechanical Dragonling { - if (!m_CastItem) + if (!m_CastItem || !m_casterUnit) return; - m_caster->CastSpell(m_caster, 12749, true, m_CastItem); + m_casterUnit->CastSpell(m_casterUnit, 12749, true, m_CastItem); return; } case 23076: // Mechanical Dragonling { - if (!m_CastItem) + if (!m_CastItem || !m_casterUnit) return; - m_caster->CastSpell(m_caster, 4073, true, m_CastItem); + m_casterUnit->CastSpell(m_casterUnit, 4073, true, m_CastItem); return; } case 23133: // Gnomish Battle Chicken { - if (!m_CastItem) + if (!m_CastItem || !m_casterUnit) return; - m_caster->CastSpell(m_caster, 13166, true, m_CastItem); + m_casterUnit->CastSpell(m_casterUnit, 13166, true, m_CastItem); return; } case 23134: // Goblin Bomb Dispenser { - if (!m_CastItem) + if (!m_CastItem || !m_casterUnit) return; uint32 roll = urand(0, 99); if (roll < 10) - unitTarget->CastSpell(m_caster, 13261, true); + unitTarget->CastSpell(m_casterUnit, 13261, true); else - m_caster->CastSpell(m_caster, 13258, true, m_CastItem); + m_casterUnit->CastSpell(m_casterUnit, 13258, true, m_CastItem); return; } case 4050: // Explosive Sheep { - Unit* pOwner = m_caster->GetCharmerOrOwner(); + Unit* pOwner = m_casterUnit ? m_casterUnit->GetCharmerOrOwner() : nullptr; if (pOwner && pOwner->GetTypeId() == TYPEID_PLAYER) { - uint32 spellid = m_caster->GetUInt32Value(UNIT_CREATED_BY_SPELL); + uint32 spellid = m_casterUnit->GetUInt32Value(UNIT_CREATED_BY_SPELL); SpellEntry const* spellInfo = sSpellMgr.GetSpellEntry(spellid); if (spellInfo) pOwner->CooldownEvent(spellInfo); @@ -1292,37 +1314,45 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 23448: // Transporter Arrival - Ultrasafe Transporter: Gadgetzan - backfires { + if (!m_casterUnit) + return; + int32 r = irand(0, 119); if (r < 20) // Transporter Malfunction - 1/6 polymorph - m_caster->CastSpell(m_caster, 23444, true); + m_casterUnit->CastSpell(m_casterUnit, 23444, true); else if (r < 100) // Evil Twin - 4/6 evil twin - m_caster->CastSpell(m_caster, 23445, true); + m_casterUnit->CastSpell(m_casterUnit, 23445, true); return; } case 23453: // Gnomish Transporter - Ultrasafe Transporter: Gadgetzan { - uint64 m_playerGUID = m_caster->GetObjectGuid(); + if (!m_casterUnit) + return; + + uint64 m_playerGUID = m_casterUnit->GetObjectGuid(); if (roll_chance_i(50)) // Gadgetzan Transporter - success - m_caster->CastSpell(m_caster, 23441, true); + m_casterUnit->CastSpell(m_casterUnit, 23441, true); else // Gadgetzan Transporter Failure - failure { if (roll_chance_i(50)) - m_caster->CastSpell(m_caster, 23446, true); + m_casterUnit->CastSpell(m_casterUnit, 23446, true); else { - if (Player* pPlayer = m_caster->GetMap()->GetPlayer(m_playerGUID)) + if (Player* pPlayer = m_casterUnit->GetMap()->GetPlayer(m_playerGUID)) pPlayer->TeleportTo(1, -7341.38f, -3908.11f, 150.7f, 0.51f); } } return; } case 23645: // Hourglass Sand - m_caster->RemoveAurasDueToSpell(23170); // Brood Affliction: Bronze + if (m_casterUnit) + m_casterUnit->RemoveAurasDueToSpell(23170); // Brood Affliction: Bronze return; case 23725: // Gift of Life (warrior bwl trinket) - m_caster->CastSpell(m_caster, 23782, true); + if (m_casterUnit) + m_casterUnit->CastSpell(m_casterUnit, 23782, true); return; case 24781: // Dream Fog { @@ -1334,6 +1364,9 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 24930: // Hallow's End Treat { + if (!m_casterUnit) + return; + uint32 spell_id = 0; switch (urand(1, 4)) @@ -1352,61 +1385,64 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) break; // Ghost } - m_caster->CastSpell(m_caster, spell_id, true); + m_casterUnit->CastSpell(m_casterUnit, spell_id, true); return; } case 25860: // Reindeer Transformation { - if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED)) + if (!m_casterUnit) return; - float speed = m_caster->GetSpeedRate(MOVE_RUN); + if (!m_casterUnit->HasAuraType(SPELL_AURA_MOUNTED)) + return; - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + float speed = m_casterUnit->GetSpeedRate(MOVE_RUN); + + m_casterUnit->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); //5 different spells used depending on mounted speed if (speed >= 2.0f) - m_caster->CastSpell(m_caster, 25859, true); //100% ground Reindeer + m_casterUnit->CastSpell(m_casterUnit, 25859, true); //100% ground Reindeer else // Reindeer - m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer + m_casterUnit->CastSpell(m_casterUnit, 25858, true); //60% ground Reindeer return; } case 25938: // Viscidus explode { - if (!m_caster) + if (!m_casterUnit) return; - if (m_caster->GetHealthPercent() < 5.0f) // Only way to kill this boss + if (m_casterUnit->GetHealthPercent() < 5.0f) // Only way to kill this boss { - m_caster->CastSpell(m_caster, 26003, true); + m_casterUnit->CastSpell(m_casterUnit, 26003, true); return; } // Stop combat - m_caster->CombatStop(); + m_casterUnit->CombatStop(); // Fake death - //m_caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FEING_DEATH); - m_caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - m_caster->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); - m_caster->addUnitState(UNIT_STAT_DIED); + //m_casterUnit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FEING_DEATH); + m_casterUnit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_casterUnit->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + m_casterUnit->addUnitState(UNIT_STAT_DIED); // Summon globs - m_caster->CastSpell(m_caster, 25885, true); + m_casterUnit->CastSpell(m_casterUnit, 25885, true); return; } case 26003: // Viscidus suicide { - if (!m_caster) + if (!m_casterUnit) return; - if (!m_caster->isAlive()) + if (!m_casterUnit->isAlive()) return; - m_caster->CastSpell(m_caster, 26002, true); + m_casterUnit->CastSpell(m_casterUnit, 26002, true); return; } case 26074: // Holiday Cheer @@ -1441,13 +1477,16 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) case 28098: // Stalagg Tesla Effect case 28110: // Feugen Tesla Effect { + if (!m_casterUnit) + return; + if (unitTarget->GetTypeId() != TYPEID_UNIT) return; - if (m_caster->getVictim() && !m_caster->IsWithinDistInMap(unitTarget, 60.0f)) + if (m_casterUnit->getVictim() && !m_casterUnit->IsWithinDistInMap(unitTarget, 60.0f)) { // Cast Shock on nearby targets - if (Unit* pTarget = ((Creature*)m_caster)->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) + if (Unit* pTarget = ((Creature*)m_casterUnit)->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0)) unitTarget->CastSpell(pTarget, 28099, false); } else @@ -1457,7 +1496,7 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) unitTarget->DeleteThreatList(); unitTarget->CombatStop(true); // Recast chain (Stalagg Chain or Feugen Chain - unitTarget->CastSpell(m_caster, m_spellInfo->Id == 28098 ? 28096 : 28111, false); + unitTarget->CastSpell(m_casterUnit, m_spellInfo->Id == 28098 ? 28096 : 28111, false); } return; } @@ -1503,9 +1542,9 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 28697: // Forgiveness (SM Ashbringer event) { - if (unitTarget && m_caster) + if (unitTarget && m_casterUnit) { - m_caster->Kill(unitTarget, nullptr); + m_casterUnit->Kill(unitTarget, nullptr); } return; } @@ -1523,11 +1562,11 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } case 26899: // Friendship Bracelet { - if (unitTarget && m_caster && unitTarget->HasAura(26898)) + if (unitTarget && m_casterUnit && unitTarget->HasAura(26898)) { unitTarget->RemoveAurasDueToSpell(26898); // Remove Heartbroken unitTarget->CastSpell(unitTarget, 26921, true); // Create Bracelet - m_caster->CastSpell(m_caster, 26664, true); // Cast The Power of Friendship + m_casterUnit->CastSpell(m_casterUnit, 26664, true); // Cast The Power of Friendship } return; } @@ -1602,7 +1641,10 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) // Berserking (troll racial traits) case 1661: { - uint32 healthPerc = uint32((float(m_caster->GetHealth()) / m_caster->GetMaxHealth()) * 100); + if (!m_casterUnit) + return; + + uint32 healthPerc = uint32((float(m_casterUnit->GetHealth()) / m_casterUnit->GetMaxHealth()) * 100); int32 melee_mod = 10; if (healthPerc <= 40) melee_mod = 30; @@ -1610,8 +1652,8 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) melee_mod = 10 + (100 - healthPerc) / 3; // FIXME: custom spell required this aura state by some unknown reason, we not need remove it anyway - m_caster->ModifyAuraState(AURA_STATE_BERSERKING, true); - m_caster->CastCustomSpell(m_caster, 26635, &melee_mod, &melee_mod, &melee_mod, true, nullptr); + m_casterUnit->ModifyAuraState(AURA_STATE_BERSERKING, true); + m_casterUnit->CastCustomSpell(m_casterUnit, 26635, &melee_mod, &melee_mod, &melee_mod, true, nullptr); return; } } @@ -1649,12 +1691,12 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) // Execute if (m_spellInfo->IsFitToFamilyMask()) { - if (!unitTarget) + if (!unitTarget || !m_casterUnit) return; - int32 basePoints0 = damage + int32(m_caster->GetPower(POWER_RAGE) * m_spellInfo->DmgMultiplier[eff_idx]); - // m_caster->SetPower(POWER_RAGE, 0); // Done in EffectSchoolDMG - spell 20647 - m_caster->CastCustomSpell(unitTarget, 20647, &basePoints0, nullptr, nullptr, true, nullptr); + int32 basePoints0 = damage + int32(m_casterUnit->GetPower(POWER_RAGE) * m_spellInfo->DmgMultiplier[eff_idx]); + // m_casterUnit->SetPower(POWER_RAGE, 0); // Done in EffectSchoolDMG - spell 20647 + m_casterUnit->CastCustomSpell(unitTarget, 20647, &basePoints0, nullptr, nullptr, true, nullptr); return; } if (m_spellInfo->Id == 21977) //Warrior's Wrath @@ -1667,19 +1709,22 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) } if (m_spellInfo->Id == 23424) // Ustaag : Nefarian Class Call Chaman Corrupted Totems { + if (!m_casterUnit) + return; + switch (urand(0, 3)) { case 0: // Corrupted Fire Nova Totem - m_caster->CastSpell(m_caster, 23419, true); + m_casterUnit->CastSpell(m_casterUnit, 23419, true); break; case 1: // Corrupted Stoneskin Totem - m_caster->CastSpell(m_caster, 23420, true); + m_casterUnit->CastSpell(m_casterUnit, 23420, true); break; case 2: // Corrupted Windfury Totem - m_caster->CastSpell(m_caster, 23423, true); + m_casterUnit->CastSpell(m_casterUnit, 23423, true); break; case 3: // Corrupted Healing Stream Totem - m_caster->CastSpell(m_caster, 23422, true); + m_casterUnit->CastSpell(m_casterUnit, 23422, true); break; } return; @@ -1691,22 +1736,25 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) // Life Tap if (m_spellInfo->IsFitToFamilyMask()) { + if (!m_casterUnit) + return; + float cost = m_currentBasePoints[EFFECT_INDEX_0]; - if (Player* modOwner = m_caster->GetSpellModOwner()) + if (Player* modOwner = m_casterUnit->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, cost, this); - int32 dmg = m_caster->SpellDamageBonusDone(m_caster, m_spellInfo, uint32(cost > 0 ? cost : 0), SPELL_DIRECT_DAMAGE); - dmg = m_caster->SpellDamageBonusTaken(m_caster, m_spellInfo, dmg, SPELL_DIRECT_DAMAGE); + int32 dmg = m_casterUnit->SpellDamageBonusDone(m_casterUnit, m_spellInfo, uint32(cost > 0 ? cost : 0), SPELL_DIRECT_DAMAGE); + dmg = m_casterUnit->SpellDamageBonusTaken(m_casterUnit, m_spellInfo, dmg, SPELL_DIRECT_DAMAGE); - if (int32(m_caster->GetHealth()) > dmg) + if (int32(m_casterUnit->GetHealth()) > dmg) { // Shouldn't Appear in Combat Log - m_caster->ModifyHealth(-dmg); + m_casterUnit->ModifyHealth(-dmg); int32 mana = dmg; - Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + Unit::AuraList const& auraDummy = m_casterUnit->GetAurasByType(SPELL_AURA_DUMMY); for (Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr) { // only Imp. Life Tap have this in combination with dummy aura @@ -1714,12 +1762,12 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) mana = ((*itr)->GetModifier()->m_amount + 100) * mana / 100; } - m_caster->CastCustomSpell(m_caster, 31818, &mana, nullptr, nullptr, true, nullptr); + m_casterUnit->CastCustomSpell(m_casterUnit, 31818, &mana, nullptr, nullptr, true, nullptr); // Mana Feed - int32 manaFeedVal = m_caster->CalculateSpellDamage(m_caster, m_spellInfo, EFFECT_INDEX_1); + int32 manaFeedVal = m_casterUnit->CalculateSpellDamage(m_casterUnit, m_spellInfo, EFFECT_INDEX_1); manaFeedVal = manaFeedVal * mana / 100; if (manaFeedVal > 0) - m_caster->CastCustomSpell(m_caster, 32553, &manaFeedVal, nullptr, nullptr, true, nullptr); + m_casterUnit->CastCustomSpell(m_casterUnit, 32553, &manaFeedVal, nullptr, nullptr, true, nullptr); } else SendCastResult(SPELL_FAILED_FIZZLE); @@ -1922,16 +1970,17 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) return; } - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* pPlayer = m_caster->ToPlayer(); + if (!pPlayer) return; for (int j = BASE_ATTACK; j <= OFF_ATTACK; ++j) { - if (Item* item = ((Player*)m_caster)->GetWeaponForAttack(WeaponAttackType(j))) + if (Item* item = pPlayer->GetWeaponForAttack(WeaponAttackType(j))) { if (item->IsFitToSpellRequirements(m_spellInfo)) { - Spell *spell = new Spell(m_caster, spellInfo, true); + Spell *spell = new Spell(pPlayer, spellInfo, true); // enchanting spell selected by calculated damage-per-sec in enchanting effect // at calculation applied affect from Elemental Weapons talent @@ -1972,7 +2021,8 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx) // pet auras if (PetAura const* petSpell = sSpellMgr.GetPetAura(m_spellInfo->Id)) { - m_caster->AddPetAura(petSpell); + if (m_casterUnit) + m_casterUnit->AddPetAura(petSpell); return; } @@ -2093,7 +2143,7 @@ void Spell::EffectTriggerSpell(SpellEffectIndex eff_idx) } // select formal caster for triggered spell - Unit* caster = m_caster; + WorldObject* caster = m_caster; // some triggered spells require specific equipment if (spellInfo->EquippedItemClass >= 0 && m_caster->GetTypeId() == TYPEID_PLAYER) @@ -2191,14 +2241,16 @@ void Spell::EffectTeleportUnits(SpellEffectIndex eff_idx) } case TARGET_EFFECT_SELECT: { - // m_destN filled, but sometimes for wrong dest and does not have TARGET_FLAG_DEST_LOCATION + if (!m_casterUnit) + return; + // m_destN filled, but sometimes for wrong dest and does not have TARGET_FLAG_DEST_LOCATION float x = unitTarget->GetPositionX(); float y = unitTarget->GetPositionY(); float z = unitTarget->GetPositionZ(); float orientation = m_caster->GetOrientation(); - m_caster->NearTeleportTo(x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget == m_caster ? TELE_TO_SPELL : 0)); + m_casterUnit->NearTeleportTo(x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget == m_caster ? TELE_TO_SPELL : 0)); return; } default: @@ -2221,15 +2273,15 @@ void Spell::EffectTeleportUnits(SpellEffectIndex eff_idx) } // post effects for TARGET_TABLE_X_Y_Z_COORDINATES - if (m_spellInfo->Id == 23442) + if (m_spellInfo->Id == 23442 && m_casterUnit) { int32 r = irand(0, 119); if (r >= 70) // 7/12 success { if (r < 100) // 4/12 evil twin - m_caster->CastSpell(m_caster, 23445, true); + m_casterUnit->CastSpell(m_casterUnit, 23445, true); else // 1/12 fire - m_caster->CastSpell(m_caster, 23449, true); + m_casterUnit->CastSpell(m_casterUnit, 23449, true); } } } @@ -2274,7 +2326,7 @@ void Spell::EffectApplyAura(SpellEffectIndex eff_idx) m_currentBasePoints[eff_idx] = eff_idx == EFFECT_INDEX_0 ? int32(urand(600, 1200) * (caster->getLevel() / 60.0f)) * (!urand(0,6) ? 2 : 1) : m_currentBasePoints[EFFECT_INDEX_0] * 0.1249f; // Paladin T3 JoL - else if (m_spellInfo->IsFitToFamilyMask() && m_spellInfo->SpellIconID == 299 && m_caster->HasAura(28775)) + else if (m_spellInfo->IsFitToFamilyMask() && m_spellInfo->SpellIconID == 299 && m_casterUnit && m_casterUnit->HasAura(28775)) m_currentBasePoints[eff_idx] = 20; Aura* aur = CreateAura(m_spellInfo, eff_idx, &m_currentBasePoints[eff_idx], m_spellAuraHolder, unitTarget, caster, m_CastItem); @@ -2323,12 +2375,16 @@ void Spell::EffectPowerDrain(SpellEffectIndex eff_idx) if (manaMultiplier == 0) manaMultiplier = 1; - if (Player *modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, manaMultiplier); + if (m_casterUnit) + { + if (Player *modOwner = m_casterUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, manaMultiplier); + } int32 gain = int32(new_damage * manaMultiplier); - m_caster->ModifyPower(POWER_MANA, gain); + if (m_casterUnit) + m_casterUnit->ModifyPower(POWER_MANA, gain); info.powerDrain.multiplier = manaMultiplier; } @@ -2373,9 +2429,12 @@ void Spell::EffectPowerBurn(SpellEffectIndex eff_idx) unitTarget->ModifyPower(powertype, -new_damage); float multiplier = m_spellInfo->EffectMultipleValue[eff_idx]; - if (Player *modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier); - + if (m_casterUnit) + { + if (Player *modOwner = m_casterUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier); + } + new_damage = int32(new_damage * multiplier); m_damage += new_damage; } @@ -2385,7 +2444,7 @@ void Spell::EffectHeal(SpellEffectIndex eff_idx) if (unitTarget && unitTarget->isAlive() && damage >= 0) { // Try to get original caster - Unit *caster = GetAffectiveCaster(); + WorldObject *caster = GetAffectiveCasterObject(); if (!caster) return; @@ -2460,7 +2519,7 @@ void Spell::EffectHealMechanical(SpellEffectIndex /*eff_idx*/) if (unitTarget && unitTarget->isAlive() && damage >= 0) { // Try to get original caster - Unit *caster = GetAffectiveCaster(); + WorldObject *caster = GetAffectiveCasterObject(); if (!caster) return; @@ -2483,15 +2542,19 @@ void Spell::EffectHealthLeech(SpellEffectIndex effIndex) DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "HealthLeech :%i", damage); float healMultiplier = m_spellInfo->EffectMultipleValue[effIndex]; - if (Player *modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, healMultiplier); + + if (m_casterUnit) + { + if (Player *modOwner = m_casterUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, healMultiplier); + } // get max possible damage, don't count overkill for heal if (damage > static_cast(unitTarget->GetHealth())) damage = unitTarget->GetHealth(); - if (m_caster->isAlive()) - m_caster->DealHeal(m_caster, uint32(damage * healMultiplier), m_spellInfo); + if (m_casterUnit && m_casterUnit->isAlive()) + m_casterUnit->DealHeal(m_casterUnit, uint32(damage * healMultiplier), m_spellInfo); // Non delayed spells bonus damage is added later if (!m_delayed) @@ -2613,15 +2676,18 @@ void Spell::EffectCreateItem(SpellEffectIndex eff_idx) void Spell::EffectPersistentAA(SpellEffectIndex eff_idx) { - Unit* pCaster = GetAffectiveCaster(); - // FIXME: in case wild GO will used wrong affective caster (target in fact) as dynobject owner + WorldObject* pCaster = GetAffectiveCasterObject(); + if (!pCaster) pCaster = m_caster; float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx])); - if (Player* modOwner = pCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius); + if (Unit* pUnit = pCaster->ToUnit()) + { + if (Player* modOwner = pUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius); + } DynamicObject* dynObj = new DynamicObject; if (!dynObj->Create(pCaster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), pCaster, m_spellInfo->Id, @@ -2668,6 +2734,9 @@ void Spell::EffectEnergize(SpellEffectIndex eff_idx) void Spell::SendLoot(ObjectGuid guid, LootType loottype, LockType lockType) { + if (!m_casterUnit) + return; + if (gameObjTarget) { switch (gameObjTarget->GetGoType()) @@ -2677,11 +2746,11 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype, LockType lockType) case GAMEOBJECT_TYPE_QUESTGIVER: case GAMEOBJECT_TYPE_SPELL_FOCUS: case GAMEOBJECT_TYPE_GOOBER: - gameObjTarget->Use(m_caster); + gameObjTarget->Use(m_casterUnit); return; case GAMEOBJECT_TYPE_CHEST: - gameObjTarget->Use(m_caster); + gameObjTarget->Use(m_casterUnit); // Don't return, let loots been taken break; @@ -2781,12 +2850,12 @@ void Spell::EffectOpenLock(SpellEffectIndex eff_idx) SendLoot(guid, LOOT_SKINNING, LockType(m_spellInfo->EffectMiscValue[eff_idx])); - if (gameObjTarget) + if (gameObjTarget && m_casterUnit) { if (player) sScriptMgr.OnGameObjectOpen(player, gameObjTarget); if (gameObjTarget->AI()) - gameObjTarget->AI()->OnUse(m_caster); + gameObjTarget->AI()->OnUse(m_casterUnit); } // not allow use skill grow at item base open @@ -2927,17 +2996,21 @@ void Spell::EffectApplyAreaAura(SpellEffectIndex eff_idx) if (!unitTarget->isAlive()) return; - AreaAura* Aur = new AreaAura(m_spellInfo, eff_idx, &m_currentBasePoints[eff_idx], m_spellAuraHolder, unitTarget, m_caster, m_CastItem); + AreaAura* Aur = new AreaAura(m_spellInfo, eff_idx, &m_currentBasePoints[eff_idx], m_spellAuraHolder, unitTarget, m_casterUnit ? m_casterUnit : unitTarget, m_CastItem); m_spellAuraHolder->AddAura(Aur, eff_idx); } void Spell::EffectSummon(SpellEffectIndex eff_idx) { - if (!m_caster->GetPetGuid().IsEmpty()) + if (!m_casterUnit) + return; + + if (!m_casterUnit->GetPetGuid().IsEmpty()) return; if (!unitTarget) return; + uint32 pet_entry = m_spellInfo->EffectMiscValue[eff_idx]; if (!pet_entry) return; @@ -2949,14 +3022,14 @@ void Spell::EffectSummon(SpellEffectIndex eff_idx) return; } - uint32 level = m_caster->getLevel(); + uint32 level = m_casterUnit->getLevel(); Pet* spawnCreature = new Pet(SUMMON_PET); - if (m_caster->GetTypeId() == TYPEID_PLAYER && spawnCreature->LoadPetFromDB((Player*)m_caster, pet_entry)) + if (m_casterUnit->GetTypeId() == TYPEID_PLAYER && spawnCreature->LoadPetFromDB((Player*)m_casterUnit, pet_entry)) { // Summon in dest location if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - spawnCreature->Relocate(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_caster->GetOrientation()); + spawnCreature->Relocate(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_casterUnit->GetOrientation()); // set timer for unsummon if (m_duration > 0) @@ -2966,12 +3039,12 @@ void Spell::EffectSummon(SpellEffectIndex eff_idx) } // Summon in dest location - CreatureCreatePos pos(m_caster->GetMap(), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_caster->GetOrientation()); + CreatureCreatePos pos(m_casterUnit->GetMap(), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_casterUnit->GetOrientation()); if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) - pos = CreatureCreatePos(m_caster, -m_caster->GetOrientation()); + pos = CreatureCreatePos(m_casterUnit, -m_casterUnit->GetOrientation()); - Map *map = m_caster->GetMap(); + Map *map = m_casterUnit->GetMap(); uint32 pet_number = sObjectMgr.GeneratePetNumber(); if (!spawnCreature->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) { @@ -2986,17 +3059,17 @@ void Spell::EffectSummon(SpellEffectIndex eff_idx) if (m_duration > 0) spawnCreature->SetDuration(m_duration); - spawnCreature->SetOwnerGuid(m_caster->GetObjectGuid()); + spawnCreature->SetOwnerGuid(m_casterUnit->GetObjectGuid()); spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); spawnCreature->SetInitCreaturePowerType(); - spawnCreature->setFaction(m_caster->getFaction()); + spawnCreature->setFaction(m_casterUnit->getFaction()); spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); spawnCreature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); spawnCreature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000); - spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); + spawnCreature->SetCreatorGuid(m_casterUnit->GetObjectGuid()); spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - spawnCreature->InitStatsForLevel(level, m_caster); + spawnCreature->InitStatsForLevel(level, m_casterUnit); spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false); @@ -3009,19 +3082,19 @@ void Spell::EffectSummon(SpellEffectIndex eff_idx) map->Add((Creature*)spawnCreature); - m_caster->SetPet(spawnCreature); + m_casterUnit->SetPet(spawnCreature); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_casterUnit->GetTypeId() == TYPEID_PLAYER) { spawnCreature->SetReactState(REACT_DEFENSIVE); spawnCreature->SavePetToDB(PET_SAVE_AS_CURRENT); - ((Player*)m_caster)->PetSpellInitialize(); + ((Player*)m_casterUnit)->PetSpellInitialize(); } else spawnCreature->SetReactState(REACT_AGGRESSIVE); - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned((Creature*)spawnCreature); + if (m_casterUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_casterUnit)->AI()) + ((Creature*)m_casterUnit)->AI()->JustSummoned((Creature*)spawnCreature); AddExecuteLogInfo(eff_idx, ExecuteLogInfo(spawnCreature->GetObjectGuid())); } @@ -3176,7 +3249,7 @@ void Spell::EffectDispel(SpellEffectIndex eff_idx) { SpellAuraHolder* dispelledHolder = j->first; data << uint32(dispelledHolder->GetId()); // Spell Id - unitTarget->RemoveAuraHolderDueToSpellByDispel(dispelledHolder->GetId(), j->second, dispelledHolder->GetCasterGuid(), m_caster); + unitTarget->RemoveAuraHolderDueToSpellByDispel(dispelledHolder->GetId(), j->second, dispelledHolder->GetCasterGuid()); } m_caster->SendMessageToSet(&data, true); @@ -3184,6 +3257,9 @@ void Spell::EffectDispel(SpellEffectIndex eff_idx) // Devour Magic if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == SPELLCATEGORY_HEALING_SPELL) { + if (!m_casterUnit) + return; + uint32 heal_spell = 0; switch (m_spellInfo->Id) { @@ -3204,7 +3280,7 @@ void Spell::EffectDispel(SpellEffectIndex eff_idx) break; } if (heal_spell) - m_caster->CastSpell(m_caster, heal_spell, true); + m_casterUnit->CastSpell(m_casterUnit, heal_spell, true); } } // Send fail log to client @@ -3377,12 +3453,16 @@ void Spell::EffectSummonWild(SpellEffectIndex eff_idx) summon->SetFactionTemporary(m_caster->getFaction(), TEMPFACTION_NONE); summon->lootForCreator = true; summon->SetCreatorGuid(m_caster->GetObjectGuid()); - summon->SetLootRecipient(m_caster); + if (m_casterUnit) + summon->SetLootRecipient(m_casterUnit); break; // Rockwing Gargoyle case 16381: - if (m_caster->getAttackerForHelper()) - summon->AI()->AttackStart(m_caster->getAttackerForHelper()); + if (m_casterUnit) + { + if (Unit* pTarget = m_casterUnit->getAttackerForHelper()) + summon->AI()->AttackStart(pTarget); + } break; // Chained Essence of Eranikus case 12766: @@ -3411,9 +3491,9 @@ void Spell::EffectSummonWild(SpellEffectIndex eff_idx) EranikusText = -1001019; break; } - summon->MonsterWhisper(EranikusText, m_caster); + if (m_casterUnit) + summon->MonsterWhisper(EranikusText, m_casterUnit); break; - } // UNIT_FIELD_CREATEDBY are not set for these kind of spells. @@ -3429,6 +3509,9 @@ void Spell::EffectSummonWild(SpellEffectIndex eff_idx) // Used for SPELL_EFFECT_SUMMON_GUARDIAN and SPELL_EFFECT_SUMMON_POSSESSED void Spell::EffectSummonGuardian(SpellEffectIndex eff_idx) { + if (!m_casterUnit) + return; + uint32 pet_entry = m_spellInfo->EffectMiscValue[eff_idx]; if (!pet_entry) return; @@ -3441,16 +3524,16 @@ void Spell::EffectSummonGuardian(SpellEffectIndex eff_idx) } // set timer for unsummon - int32 duration = m_spellInfo->CalculateDuration(m_caster); + int32 duration = m_spellInfo->CalculateDuration(m_casterUnit); // second direct cast unsummon guardian(s) (guardians without like functionality have cooldown > spawn time) - if (!m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER) + if (!m_IsTriggeredSpell && m_casterUnit->GetTypeId() == TYPEID_PLAYER) { bool found = false; // including protector - while (Pet* old_summon = m_caster->FindGuardianWithEntry(pet_entry)) + while (Pet* old_summon = m_casterUnit->FindGuardianWithEntry(pet_entry)) { - old_summon->Unsummon(PET_SAVE_AS_DELETED, m_caster); + old_summon->Unsummon(PET_SAVE_AS_DELETED, m_casterUnit); found = true; } @@ -3459,14 +3542,14 @@ void Spell::EffectSummonGuardian(SpellEffectIndex eff_idx) } // Hard cap for NPC summoned guardians - if (m_caster->GetTypeId() != TYPEID_PLAYER && m_caster->GetGuardianCountWithEntry(pet_entry) > 15) + if (m_casterUnit->GetTypeId() != TYPEID_PLAYER && m_casterUnit->GetGuardianCountWithEntry(pet_entry) > 15) return; // in another case summon new - uint32 level = m_caster->getLevel(); + uint32 level = m_casterUnit->getLevel(); // Summoned by unit: use creature level but cap at owner level - if (m_caster->GetTypeId() == TYPEID_UNIT) + if (m_casterUnit->GetTypeId() == TYPEID_UNIT) { uint32 creaturelevel = cInfo->level_min == cInfo->level_max ? cInfo->level_min : urand(cInfo->level_min, cInfo->level_max); if (creaturelevel < level) level = creaturelevel; @@ -3490,7 +3573,7 @@ void Spell::EffectSummonGuardian(SpellEffectIndex eff_idx) ItemPrototype const *proto = m_CastItem->GetProto(); if (proto && proto->RequiredSkill == SKILL_ENGINEERING) { - uint16 engiLevel = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINEERING); + uint16 engiLevel = ((Player*)m_casterUnit)->GetSkillValue(SKILL_ENGINEERING); if (engiLevel) { level = engiLevel / 5; @@ -3499,7 +3582,7 @@ void Spell::EffectSummonGuardian(SpellEffectIndex eff_idx) } // Eye of Kilrog if (m_spellInfo->Effect[eff_idx] == SPELL_EFFECT_SUMMON_POSSESSED) - level = m_caster->getLevel(); + level = m_casterUnit->getLevel(); } // select center of summon position @@ -3517,7 +3600,7 @@ void Spell::EffectSummonGuardian(SpellEffectIndex eff_idx) // If dest location if present // Summon 1 unit in dest location - CreatureCreatePos pos(m_caster->GetMap(), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_caster->GetOrientation()); + CreatureCreatePos pos(m_casterUnit->GetMap(), m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_casterUnit->GetOrientation()); if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) { @@ -3525,15 +3608,15 @@ void Spell::EffectSummonGuardian(SpellEffectIndex eff_idx) if (count > 0) { float x, y, z; - m_caster->GetRandomPoint(center_x, center_y, center_z, radius, x, y, z); - pos = CreatureCreatePos(m_caster->GetMap(), x, y, z, m_caster->GetOrientation()); + m_casterUnit->GetRandomPoint(center_x, center_y, center_z, radius, x, y, z); + pos = CreatureCreatePos(m_casterUnit->GetMap(), x, y, z, m_casterUnit->GetOrientation()); } } // Summon if dest location not present near caster else - pos = CreatureCreatePos(m_caster, m_caster->GetOrientation()); + pos = CreatureCreatePos(m_casterUnit, m_casterUnit->GetOrientation()); - Map *map = m_caster->GetMap(); + Map *map = m_casterUnit->GetMap(); uint32 pet_number = sObjectMgr.GeneratePetNumber(); if (!spawnCreature->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), pos, cInfo, pet_number)) { @@ -3548,26 +3631,26 @@ void Spell::EffectSummonGuardian(SpellEffectIndex eff_idx) spawnCreature->SetDuration(m_duration); //spawnCreature->SetName(""); // generated by client - spawnCreature->SetOwnerGuid(m_caster->GetObjectGuid()); + spawnCreature->SetOwnerGuid(m_casterUnit->GetObjectGuid()); spawnCreature->SetInitCreaturePowerType(); spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, spawnCreature->GetCreatureInfo()->npc_flags); - spawnCreature->setFaction(m_caster->getFaction()); + spawnCreature->setFaction(m_casterUnit->getFaction()); spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, 0); - spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid()); + spawnCreature->SetCreatorGuid(m_casterUnit->GetObjectGuid()); spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - spawnCreature->InitStatsForLevel(level, m_caster); + spawnCreature->InitStatsForLevel(level, m_casterUnit); spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false); spawnCreature->AIM_Initialize(); - m_caster->AddGuardian(spawnCreature); + m_casterUnit->AddGuardian(spawnCreature); map->Add((Creature*)spawnCreature); // Notify Summoner - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(spawnCreature); + if (m_casterUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_casterUnit)->AI()) + ((Creature*)m_casterUnit)->AI()->JustSummoned(spawnCreature); switch (m_spellInfo->Id) { @@ -3878,9 +3961,12 @@ void Spell::EffectTameCreature(SpellEffectIndex /*eff_idx*/) void Spell::EffectSummonPet(SpellEffectIndex eff_idx) { - uint32 petLevel = m_caster->IsPlayer() ? m_caster->getLevel() : std::max(int32(m_caster->getLevel()) + int32(m_spellInfo->EffectMultipleValue[eff_idx]), 1); + if (!m_casterUnit) + return; - ObjectGuid petGuid = m_caster->EffectSummonPet(m_spellInfo->Id, m_spellInfo->EffectMiscValue[eff_idx], petLevel); + uint32 petLevel = m_casterUnit->IsPlayer() ? m_casterUnit->getLevel() : std::max(int32(m_casterUnit->getLevel()) + int32(m_spellInfo->EffectMultipleValue[eff_idx]), 1); + + ObjectGuid petGuid = m_casterUnit->EffectSummonPet(m_spellInfo->Id, m_spellInfo->EffectMiscValue[eff_idx], petLevel); if (petGuid) AddExecuteLogInfo(eff_idx, ExecuteLogInfo(petGuid)); } @@ -4041,7 +4127,7 @@ void Spell::EffectLearnPetSpell(SpellEffectIndex eff_idx) void Spell::EffectTaunt(SpellEffectIndex eff_idx) { - if (!unitTarget) + if (!unitTarget || !m_casterUnit) return; // this effect use before aura Taunt apply for prevent taunt already attacking target @@ -4058,11 +4144,11 @@ void Spell::EffectTaunt(SpellEffectIndex eff_idx) // Also use this effect to set the taunter's threat to the taunted creature's highest value if (unitTarget->CanHaveThreatList() && unitTarget->getThreatManager().getCurrentVictim()) { - unitTarget->getThreatManager().addThreat(m_caster, unitTarget->getThreatManager().getCurrentVictim()->getThreat()); + unitTarget->getThreatManager().addThreat(m_casterUnit, unitTarget->getThreatManager().getCurrentVictim()->getThreat()); // Patch 1.11 notes // https://web.archive.org/web/20061109034626/http://evilempireguild.org:80/guides/kenco2.php - // Recently(1.11.x), the behaviour of Taunt has been buffed slightly.It now does three things : + // Recently(1.11.x), the behaviour of Taunt has been buffed slightly. It now does three things : // Taunt debuff.The mob is forced to attack you for 3 seconds.Later taunts by other players override this. // You are given threat equal to the mob's previous aggro target, permanently. Importantly, you won't necessarily get as much threat @@ -4071,7 +4157,9 @@ void Spell::EffectTaunt(SpellEffectIndex eff_idx) // but a taunt now gives you instant aggro on the mob. // Of course if other people are generating significant threat on the mob, they could exceed your threat by more than 10 % before the taunt debuff wears off, // and will gain aggro as soon as it does.There is no limit to the amount of threat you can gain from Taunt. - unitTarget->getThreatManager().setCurrentVictimIfCan(m_caster); +#if SUPPORTED_CLIENT_BUILD > CLIENT_BUILD_1_10_2 + unitTarget->getThreatManager().setCurrentVictimIfCan(m_casterUnit); +#endif } AddExecuteLogInfo(eff_idx, ExecuteLogInfo(unitTarget->GetObjectGuid())); @@ -4079,6 +4167,8 @@ void Spell::EffectTaunt(SpellEffectIndex eff_idx) void Spell::EffectWeaponDmg(SpellEffectIndex eff_idx) { + if (!m_casterUnit) + return; if (!unitTarget) return; if (!unitTarget->isAlive()) @@ -4086,28 +4176,28 @@ void Spell::EffectWeaponDmg(SpellEffectIndex eff_idx) if (m_spellInfo->Id == 17364) // Courroux naturel { - if (!m_caster->isAlive()) // CalculateMeleeDamage does not work in that case. + if (!m_casterUnit->isAlive()) // CalculateMeleeDamage does not work in that case. return; - m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MELEE_ATTACK); - if (Spell* spell = m_caster->GetCurrentSpell(CURRENT_MELEE_SPELL)) + m_casterUnit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MELEE_ATTACK); + if (Spell* spell = m_casterUnit->GetCurrentSpell(CURRENT_MELEE_SPELL)) spell->cast(); CalcDamageInfo damageInfo; - m_caster->CalculateMeleeDamage(unitTarget, 0, &damageInfo, BASE_ATTACK); + m_casterUnit->CalculateMeleeDamage(unitTarget, 0, &damageInfo, BASE_ATTACK); // Send log damage message to client - for (uint8 i = 0; i < m_caster->GetWeaponDamageCount(BASE_ATTACK); i++) + for (uint8 i = 0; i < m_casterUnit->GetWeaponDamageCount(BASE_ATTACK); i++) { damageInfo.totalDamage -= damageInfo.subDamage[i].damage; - m_caster->DealDamageMods(unitTarget, damageInfo.subDamage[i].damage, &damageInfo.subDamage[i].absorb); + m_casterUnit->DealDamageMods(unitTarget, damageInfo.subDamage[i].damage, &damageInfo.subDamage[i].absorb); damageInfo.totalDamage += damageInfo.subDamage[i].damage; } - m_caster->SendAttackStateUpdate(&damageInfo); - m_caster->ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.totalDamage, damageInfo.attackType); - m_caster->DealMeleeDamage(&damageInfo, true); + m_casterUnit->SendAttackStateUpdate(&damageInfo); + m_casterUnit->ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.totalDamage, damageInfo.attackType); + m_casterUnit->DealMeleeDamage(&damageInfo, true); // if damage unitTarget call AI reaction - unitTarget->AttackedBy(m_caster); + unitTarget->AttackedBy(m_casterUnit); m_damage = 0; return; } @@ -4194,25 +4284,25 @@ void Spell::EffectWeaponDmg(SpellEffectIndex eff_idx) break; } - float weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT); + float weapon_total_pct = m_casterUnit->GetModifierValue(unitMod, TOTAL_PCT); bonus = int32(bonus * weapon_total_pct); } // + weapon damage with applied weapon% dmg to base weapon damage in call - for (uint8 i = 0; i < m_caster->GetWeaponDamageCount(m_attackType); i++) + for (uint8 i = 0; i < m_casterUnit->GetWeaponDamageCount(m_attackType); i++) { - if (unitTarget->IsImmuneToDamage(GetSchoolMask(m_caster->GetWeaponDamageSchool(m_attackType, i)))) + if (unitTarget->IsImmuneToDamage(GetSchoolMask(m_casterUnit->GetWeaponDamageSchool(m_attackType, i)))) continue; - bonus += int32(m_caster->CalculateDamage(m_attackType, normalized, i) * weaponDamagePercentMod); + bonus += int32(m_casterUnit->CalculateDamage(m_attackType, normalized, i) * weaponDamagePercentMod); } // Seal of Command if (m_spellInfo->School == SPELL_SCHOOL_HOLY) { // Add spell gear bonus and spell modifiers - bonus = m_caster->SpellDamageBonusDone(unitTarget, m_spellInfo, bonus, SPELL_DIRECT_DAMAGE); - bonus = unitTarget->SpellDamageBonusTaken(m_caster, m_spellInfo, bonus, SPELL_DIRECT_DAMAGE); + bonus = m_casterUnit->SpellDamageBonusDone(unitTarget, m_spellInfo, bonus, SPELL_DIRECT_DAMAGE); + bonus = unitTarget->SpellDamageBonusTaken(m_casterUnit, m_spellInfo, bonus, SPELL_DIRECT_DAMAGE); } // prevent negative damage @@ -4221,13 +4311,13 @@ void Spell::EffectWeaponDmg(SpellEffectIndex eff_idx) void Spell::EffectThreat(SpellEffectIndex eff_idx) { - if (!unitTarget || !unitTarget->isAlive() || !m_caster->isAlive()) + if (!unitTarget || !unitTarget->isAlive() || !m_casterUnit || !m_casterUnit->isAlive()) return; if (!unitTarget->CanHaveThreatList()) return; - unitTarget->AddThreat(m_caster, float(damage), false, m_spellInfo->GetSpellSchoolMask(), m_spellInfo); + unitTarget->AddThreat(m_casterUnit, float(damage), false, m_spellInfo->GetSpellSchoolMask(), m_spellInfo); AddExecuteLogInfo(eff_idx, ExecuteLogInfo(unitTarget->GetObjectGuid())); } @@ -4238,16 +4328,20 @@ void Spell::EffectHealMaxHealth(SpellEffectIndex eff_idx) return; if (!unitTarget->isAlive()) return; - uint32 heal = m_caster->GetMaxHealth(); + + uint32 heal = m_casterUnit ? m_casterUnit->GetMaxHealth() : unitTarget->GetMaxHealth(); // Healing percent modifiers float DoneTotalMod = 1.0f; float TakenTotalMod = 1.0f; // Healing done percent - std::list const& mHealingDonePct = m_caster->GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT); - for (std::list ::const_iterator i = mHealingDonePct.begin(); i != mHealingDonePct.end(); ++i) - DoneTotalMod *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f; + if (m_casterUnit) + { + std::list const& mHealingDonePct = m_casterUnit->GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT); + for (std::list ::const_iterator i = mHealingDonePct.begin(); i != mHealingDonePct.end(); ++i) + DoneTotalMod *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f; + } heal *= DoneTotalMod; @@ -4447,8 +4541,8 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) } case 29313: // CooldownAll { - if (m_caster) - m_caster->RemoveAllSpellCooldown(); + if (m_casterUnit) + m_casterUnit->RemoveAllSpellCooldown(); return; } #endif @@ -4468,15 +4562,15 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) break; } - m_caster->CastSpell(m_caster, spell_id, true, nullptr); + m_casterUnit->CastSpell(m_casterUnit, spell_id, true, nullptr); return; } case 10101: // Knock Away { - if (!unitTarget) + if (!unitTarget || !m_casterUnit) return; - m_caster->getThreatManager().modifyThreatPercent(unitTarget, -100); + m_casterUnit->getThreatManager().modifyThreatPercent(unitTarget, -100); return; } @@ -4520,7 +4614,7 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) if (m_caster->GetTypeId() != TYPEID_PLAYER) return; - uint8 race = m_caster->getRace(); + uint8 race = m_casterUnit->getRace(); uint32 spellId = 0; switch (m_spellInfo->Id) @@ -4562,7 +4656,7 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) } if (spellId) - m_caster->CastSpell(m_caster, spellId, true); + m_casterUnit->CastSpell(m_casterUnit, spellId, true); return; } @@ -4580,13 +4674,13 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) SPELL_BLOODSIPHON_HEAL = 24322, SPELL_POISONOUS_BLOOD = 24321 }; - if (!unitTarget || !unitTarget->isAlive()) + if (!unitTarget || !unitTarget->isAlive() || !m_casterUnit) return; if (unitTarget->HasAura(SPELL_POISONOUS_BLOOD)) - unitTarget->CastSpell(m_caster, SPELL_BLOODSIPHON_DAMAGE, true); + unitTarget->CastSpell(m_casterUnit, SPELL_BLOODSIPHON_DAMAGE, true); else - unitTarget->CastSpell(m_caster, SPELL_BLOODSIPHON_HEAL, true); + unitTarget->CastSpell(m_casterUnit, SPELL_BLOODSIPHON_HEAL, true); return; } case 24590: // Brittle Armor - need remove one 24575 Brittle Armor aura @@ -4611,7 +4705,7 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) }; spellId = spells[urand(0, 7)]; - m_caster->CastSpell(m_caster, spellId, true); + m_casterUnit->CastSpell(m_casterUnit, spellId, true); return; } @@ -4826,17 +4920,17 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) } case 28408: // Chains of Kel'Thuzad (Naxxramas: Kel'thuzad) { - if (!m_caster) + if (!m_casterUnit) return; // Select maintank + 4 random targets std::vector viableTargets; - const ThreatList& tl = m_caster->getThreatManager().getThreatList(); + const ThreatList& tl = m_casterUnit->getThreatManager().getThreatList(); for (auto it = tl.begin(); it != tl.end(); it++) { if ((*it)->getUnitGuid().IsPlayer()) { - if (Unit* pUnit = m_caster->GetMap()->GetUnit((*it)->getUnitGuid())) + if (Unit* pUnit = m_casterUnit->GetMap()->GetUnit((*it)->getUnitGuid())) { if (pUnit->isAlive()) viableTargets.push_back(pUnit); @@ -4847,14 +4941,14 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) int num_targets = std::min(int(viableTargets.size()), 5)-1; // leaving 1 target not MCed to avoid reset due to all MCed // always MC maintank - if (Unit* maintank = m_caster->getVictim()) + if (Unit* maintank = m_casterUnit->getVictim()) { auto it = std::find(viableTargets.begin(), viableTargets.end(), maintank); if (it != viableTargets.end()) viableTargets.erase(it); num_targets -= 1; maintank->CastSpell(maintank, 28409, true); // modifies scale - m_caster->CastSpell(maintank, 28410, true); // applies dmg and healing mod, as well as the charm itself + m_casterUnit->CastSpell(maintank, 28410, true); // applies dmg and healing mod, as well as the charm itself } for (int i = 0; i < num_targets; i++) @@ -4864,7 +4958,7 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) viableTargets.erase(viableTargets.begin() + rand); target->CastSpell(target, 28409, true); // modifies scale - m_caster->CastSpell(target, 28410, true); // applies dmg and healing mod, as well as the charm itself + m_casterUnit->CastSpell(target, 28410, true); // applies dmg and healing mod, as well as the charm itself } return; } @@ -4931,8 +5025,8 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) case 26678: // Bag of Candies { uint32 candySpells[8] = { 26668, 26670, 26671, 26672, 26673, 26674, 26675, 26676 }; - if (m_caster) - m_caster->CastSpell(m_caster, candySpells[urand(0, 7)], true); + if (m_casterUnit) + m_casterUnit->CastSpell(m_casterUnit, candySpells[urand(0, 7)], true); return; } case 27657: // Valentine End Check @@ -5040,7 +5134,9 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) if (m_spellInfo->Id == 9033) { // remove movement affects - Unit* target = unitTarget ? unitTarget : m_caster; + Unit* target = unitTarget ? unitTarget : m_casterUnit; + if (!target) + return; target->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); Unit::AuraList const& slowingAuras = target->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); for (Unit::AuraList::const_iterator iter = slowingAuras.begin(); iter != slowingAuras.end();) @@ -5081,12 +5177,12 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) } else if (m_spellInfo->IsFitToFamilyMask()) { - if (!unitTarget || !unitTarget->isAlive()) + if (!unitTarget || !unitTarget->isAlive() || !m_casterUnit) return; uint32 spellId2 = 0; // all seals have aura dummy - Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + Unit::AuraList const& m_dummyAuras = m_casterUnit->GetAurasByType(SPELL_AURA_DUMMY); for (Unit::AuraList::const_iterator itr = m_dummyAuras.begin(); itr != m_dummyAuras.end(); ++itr) { SpellEntry const *spellInfo = (*itr)->GetSpellProto(); @@ -5102,7 +5198,7 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) continue; // found, remove seal - m_caster->RemoveAurasDueToSpell((*itr)->GetId()); + m_casterUnit->RemoveAurasDueToSpell((*itr)->GetId()); break; } @@ -5112,11 +5208,11 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) // Seal of Fury proc else if (m_spellInfo->SpellFamilyFlags == 4096) { - if (!unitTarget || !unitTarget->CanHaveThreatList()) + if (!unitTarget || !unitTarget->CanHaveThreatList() || !m_casterUnit) return; - if (unitTarget->getThreatManager().getThreat(m_caster)) - unitTarget->getThreatManager().addThreat(m_caster, damage * m_caster->GetAttackTime(BASE_ATTACK) / 1000); + if (unitTarget->getThreatManager().getThreat(m_casterUnit)) + unitTarget->getThreatManager().addThreat(m_casterUnit, damage * m_casterUnit->GetAttackTime(BASE_ATTACK) / 1000); } break; } @@ -5131,11 +5227,11 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx) case 20870: case 20871: { - if (!unitTarget || !unitTarget->CanHaveThreatList()) + if (!unitTarget || !unitTarget->CanHaveThreatList() || !m_casterUnit) return; - if (unitTarget->getThreatManager().getThreat(m_caster)) - unitTarget->getThreatManager().addThreat(m_caster, damage * m_caster->GetAttackTime(BASE_ATTACK) / 1000); + if (unitTarget->getThreatManager().getThreat(m_casterUnit)) + unitTarget->getThreatManager().addThreat(m_casterUnit, damage * m_casterUnit->GetAttackTime(BASE_ATTACK) / 1000); } } break; @@ -5184,10 +5280,10 @@ void Spell::EffectSanctuary(SpellEffectIndex eff_idx) // Vanish allows to remove all threat and cast regular stealth so other spells can be used if (m_spellInfo->IsFitToFamily()) { - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); + unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); unitTarget->InterruptAttacksOnMe(0.0f, guard_check); - if (auto pPlayer = m_caster->ToPlayer()) + if (Player* pPlayer = m_caster->ToPlayer()) { if (no_guards) pPlayer->SetCannotBeDetectedTimer(1000); @@ -5214,10 +5310,10 @@ void Spell::EffectAddComboPoints(SpellEffectIndex /*eff_idx*/) void Spell::EffectDuel(SpellEffectIndex eff_idx) { - if (!m_caster || !unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER || unitTarget->GetTypeId() != TYPEID_PLAYER) + if (!m_casterUnit || !unitTarget || !m_casterUnit->IsPlayer() || !unitTarget->IsPlayer()) return; - Player *caster = (Player*)m_caster; + Player *caster = (Player*)m_casterUnit; Player *target = (Player*)unitTarget; // if the caster is already in a duel or has issued a challenge @@ -5271,25 +5367,25 @@ void Spell::EffectDuel(SpellEffectIndex eff_idx) uint32 gameobject_id = m_spellInfo->EffectMiscValue[eff_idx]; - Map *map = m_caster->GetMap(); - float x = (m_caster->GetPositionX() + unitTarget->GetPositionX()) * 0.5f; - float y = (m_caster->GetPositionY() + unitTarget->GetPositionY()) * 0.5f; - float z = m_caster->GetPositionZ(); + Map *map = m_casterUnit->GetMap(); + float x = (m_casterUnit->GetPositionX() + unitTarget->GetPositionX()) * 0.5f; + float y = (m_casterUnit->GetPositionY() + unitTarget->GetPositionY()) * 0.5f; + float z = m_casterUnit->GetPositionZ(); if (!pGameObj->Create(map->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map, x, y, z, - m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, GO_ANIMPROGRESS_DEFAULT, GO_STATE_READY)) + m_casterUnit->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, GO_ANIMPROGRESS_DEFAULT, GO_STATE_READY)) { delete pGameObj; return; } - pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction()); - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() + 1); + pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_casterUnit->getFaction()); + pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_casterUnit->getLevel() + 1); int32 duration = m_spellInfo->GetDuration(); pGameObj->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); - m_caster->AddGameObject(pGameObj); + m_casterUnit->AddGameObject(pGameObj); map->Add(pGameObj); //END @@ -5398,7 +5494,8 @@ void Spell::EffectActivateObject(SpellEffectIndex eff_idx) gameObjTarget->SendGameObjectCustomAnim(3); break; case GameObjectActions::Disturb: - gameObjTarget->Use(m_caster); + if (m_casterUnit) + gameObjTarget->Use(m_casterUnit); break; case GameObjectActions::Unlock: gameObjTarget->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); @@ -5407,7 +5504,8 @@ void Spell::EffectActivateObject(SpellEffectIndex eff_idx) gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); break; case GameObjectActions::Open: - gameObjTarget->Use(m_caster); + if (m_casterUnit) + gameObjTarget->Use(m_casterUnit); /* Seems like Open can also trigger traps, cause linked trap to trigger, etc... go figure... gameObjTarget->UseDoorOrButton(0, false, m_caster); */ @@ -5487,6 +5585,9 @@ void Spell::EffectActivateObject(SpellEffectIndex eff_idx) void Spell::EffectSummonTotem(SpellEffectIndex eff_idx) { + if (!m_casterUnit) + return; + int slot = 0; switch (m_spellInfo->Effect[eff_idx]) { @@ -5511,14 +5612,14 @@ void Spell::EffectSummonTotem(SpellEffectIndex eff_idx) // unsummon old totem if (slot < MAX_TOTEM_SLOT) - if (Totem *OldTotem = m_caster->GetTotem(TotemSlot(slot))) + if (Totem *OldTotem = m_casterUnit->GetTotem(TotemSlot(slot))) OldTotem->UnSummon(); // FIXME: Setup near to finish point because GetObjectBoundingRadius set in Create but some Create calls can be dependent from proper position // if totem have creature_template_addon.auras with persistent point for example or script call float angle = slot < MAX_TOTEM_SLOT ? M_PI_F / MAX_TOTEM_SLOT - (slot * 2 * M_PI_F / MAX_TOTEM_SLOT) : 0; - CreatureCreatePos pos(m_caster, m_caster->GetOrientation(), 2.0f, angle); + CreatureCreatePos pos(m_casterUnit, m_casterUnit->GetOrientation(), 2.0f, angle); CreatureInfo const *cinfo = ObjectMgr::GetCreatureTemplate(m_spellInfo->EffectMiscValue[eff_idx]); if (!cinfo) @@ -5529,7 +5630,7 @@ void Spell::EffectSummonTotem(SpellEffectIndex eff_idx) Totem* pTotem = new Totem; - if (!pTotem->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_UNIT), pos, cinfo, m_caster)) + if (!pTotem->Create(m_casterUnit->GetMap()->GenerateLocalLowGuid(HIGHGUID_UNIT), pos, cinfo, m_casterUnit)) { delete pTotem; return; @@ -5538,10 +5639,10 @@ void Spell::EffectSummonTotem(SpellEffectIndex eff_idx) pTotem->SetSummonPoint(pos); if (slot < MAX_TOTEM_SLOT) - m_caster->_AddTotem(TotemSlot(slot), pTotem); + m_casterUnit->_AddTotem(TotemSlot(slot), pTotem); //pTotem->SetName(""); // generated by client - pTotem->SetOwner(m_caster); + pTotem->SetOwner(m_casterUnit); pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initialized pTotem->SetDuration(m_duration); @@ -5554,13 +5655,13 @@ void Spell::EffectSummonTotem(SpellEffectIndex eff_idx) pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_casterUnit->GetTypeId() == TYPEID_PLAYER) pTotem->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - if (m_caster->IsPvP()) + if (m_casterUnit->IsPvP()) pTotem->SetPvP(true); - pTotem->Summon(m_caster); + pTotem->Summon(m_casterUnit); AddExecuteLogInfo(eff_idx, ExecuteLogInfo(pTotem->GetObjectGuid())); } @@ -5681,23 +5782,22 @@ void Spell::EffectInebriate(SpellEffectIndex /*eff_idx*/) void Spell::EffectFeedPet(SpellEffectIndex eff_idx) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* pPlayer = m_caster->ToPlayer(); + if (!pPlayer) return; - Player *_player = (Player*)m_caster; - Item* foodItem = itemTarget; if (!foodItem) return; - Pet *pet = _player->GetPet(); + Pet *pet = pPlayer->GetPet(); if (!pet) return; if (!pet->isAlive()) return; - if (!pet->IsWithinLOSInMap(_player)) + if (!pet->IsWithinLOSInMap(pPlayer)) { SendCastResult(SPELL_FAILED_LINE_OF_SIGHT); return; @@ -5711,30 +5811,34 @@ void Spell::EffectFeedPet(SpellEffectIndex eff_idx) info.feedPet.itemEntry = foodItem->GetProto()->ItemId; uint32 count = 1; - _player->DestroyItemCount(foodItem, count, true); - m_caster->CastCustomSpell(m_caster, m_spellInfo->EffectTriggerSpell[eff_idx], &benefit, nullptr, nullptr, true); + pPlayer->DestroyItemCount(foodItem, count, true); + pPlayer->CastCustomSpell(pPlayer, m_spellInfo->EffectTriggerSpell[eff_idx], &benefit, nullptr, nullptr, true); AddExecuteLogInfo(eff_idx, info); } void Spell::EffectDismissPet(SpellEffectIndex eff_idx) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) + Player* pPlayer = m_caster->ToPlayer(); + if (!pPlayer) return; - Pet* pet = m_caster->GetPet(); + Pet* pet = pPlayer->GetPet(); // not let dismiss dead pet if (!pet || !pet->isAlive()) return; - pet->Unsummon(PET_SAVE_NOT_IN_SLOT, m_caster); + pet->Unsummon(PET_SAVE_NOT_IN_SLOT, pPlayer); AddExecuteLogInfo(eff_idx, ExecuteLogInfo(pet->GetObjectGuid())); } void Spell::EffectSummonObject(SpellEffectIndex eff_idx) { + if (!m_casterUnit) + return; + uint32 go_id = m_spellInfo->EffectMiscValue[eff_idx]; uint8 slot = 0; @@ -5756,11 +5860,11 @@ void Spell::EffectSummonObject(SpellEffectIndex eff_idx) return; } - if (ObjectGuid guid = m_caster->m_ObjectSlotGuid[slot]) + if (ObjectGuid guid = m_casterUnit->m_ObjectSlotGuid[slot]) { - if (GameObject* obj = m_caster ? m_caster->GetMap()->GetGameObject(guid) : NULL) + if (GameObject* obj = m_casterUnit ? m_casterUnit->GetMap()->GetGameObject(guid) : NULL) obj->SetLootState(GO_JUST_DEACTIVATED); - m_caster->m_ObjectSlotGuid[slot].Clear(); + m_casterUnit->m_ObjectSlotGuid[slot].Clear(); } GameObject* pGameObj = new GameObject; @@ -5775,33 +5879,33 @@ void Spell::EffectSummonObject(SpellEffectIndex eff_idx) } // Summon in random point all other units if location present else - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + m_casterUnit->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); - Map *map = m_caster->GetMap(); + Map *map = m_casterUnit->GetMap(); if (!pGameObj->Create(map->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, - x, y, z, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, GO_ANIMPROGRESS_DEFAULT, GO_STATE_READY)) + x, y, z, m_casterUnit->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, GO_ANIMPROGRESS_DEFAULT, GO_STATE_READY)) { delete pGameObj; return; } - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); + pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_casterUnit->getLevel()); int32 duration = m_spellInfo->GetDuration(); pGameObj->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); - m_caster->AddGameObject(pGameObj); + m_casterUnit->AddGameObject(pGameObj); map->Add(pGameObj); WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM, 8); data << ObjectGuid(pGameObj->GetObjectGuid()); - m_caster->SendMessageToSet(&data, true); + m_casterUnit->SendMessageToSet(&data, true); - m_caster->m_ObjectSlotGuid[slot] = pGameObj->GetObjectGuid(); + m_casterUnit->m_ObjectSlotGuid[slot] = pGameObj->GetObjectGuid(); pGameObj->SummonLinkedTrapIfAny(); - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(pGameObj); + if (m_casterUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_casterUnit)->AI()) + ((Creature*)m_casterUnit)->AI()->JustSummoned(pGameObj); AddExecuteLogInfo(eff_idx, ExecuteLogInfo(pGameObj->GetObjectGuid())); } @@ -5824,7 +5928,8 @@ void Spell::EffectResurrect(SpellEffectIndex eff_idx) case 8342: if (roll_chance_i(67)) { - m_caster->CastSpell(m_caster, 8338, true, m_CastItem); + if (m_casterUnit) + m_casterUnit->CastSpell(m_casterUnit, 8338, true, m_CastItem); return; } break; @@ -5832,7 +5937,8 @@ void Spell::EffectResurrect(SpellEffectIndex eff_idx) case 22999: if (roll_chance_i(50)) { - m_caster->CastSpell(m_caster, 23055, true, m_CastItem); + if (m_casterUnit) + m_casterUnit->CastSpell(m_casterUnit, 23055, true, m_CastItem); return; } break; @@ -6204,8 +6310,11 @@ void Spell::EffectSummonDeadPet(SpellEffectIndex /*eff_idx*/) void Spell::EffectDestroyAllTotems(SpellEffectIndex /*eff_idx*/) { + if (!m_casterUnit) + return; + for (int slot = 0; slot < MAX_TOTEM_SLOT; ++slot) - if (Totem* totem = m_caster->GetTotem(TotemSlot(slot))) + if (Totem* totem = m_casterUnit->GetTotem(TotemSlot(slot))) totem->UnSummon(); } @@ -6273,16 +6382,19 @@ void Spell::EffectDurabilityDamagePCT(SpellEffectIndex eff_idx) void Spell::EffectModifyThreatPercent(SpellEffectIndex eff_idx) { - if (!unitTarget) + if (!unitTarget || !m_casterUnit) return; - unitTarget->getThreatManager().modifyThreatPercent(m_caster, damage); + unitTarget->getThreatManager().modifyThreatPercent(m_casterUnit, damage); AddExecuteLogInfo(eff_idx, ExecuteLogInfo(unitTarget->GetObjectGuid())); } void Spell::EffectTransmitted(SpellEffectIndex eff_idx) { + if (!m_casterUnit) + return; + uint32 name_id = m_spellInfo->EffectMiscValue[eff_idx]; GameObjectInfo const* goinfo = ObjectMgr::GetGameObjectInfo(name_id); @@ -6305,11 +6417,11 @@ void Spell::EffectTransmitted(SpellEffectIndex eff_idx) { float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx])); float x, y, z; - m_caster->GetPosition(x, y, z); - fx = x + dis * cos(m_caster->GetOrientation()); - fy = y + dis * sin(m_caster->GetOrientation()); + m_casterUnit->GetPosition(x, y, z); + fx = x + dis * cos(m_casterUnit->GetOrientation()); + fy = y + dis * sin(m_casterUnit->GetOrientation()); fz = z; - m_caster->GetMap()->GetLosHitPosition(x, y, z + 0.5f, fx, fy, fz, -1.5f); + m_casterUnit->GetMap()->GetLosHitPosition(x, y, z + 0.5f, fx, fy, fz, -1.5f); } else { @@ -6318,18 +6430,18 @@ void Spell::EffectTransmitted(SpellEffectIndex eff_idx) float dis = rand_norm_f() * (max_dis - min_dis) + min_dis; float x, y, z; - m_caster->GetPosition(x, y, z); - fx = x + dis * cos(m_caster->GetOrientation()); - fy = y + dis * sin(m_caster->GetOrientation()); + m_casterUnit->GetPosition(x, y, z); + fx = x + dis * cos(m_casterUnit->GetOrientation()); + fy = y + dis * sin(m_casterUnit->GetOrientation()); fz = z; - m_caster->GetMap()->GetLosHitPosition(x, y, z + 0.5f, fx, fy, fz, -1.5f); + m_casterUnit->GetMap()->GetLosHitPosition(x, y, z + 0.5f, fx, fy, fz, -1.5f); } - Map *cMap = m_caster->GetMap(); + Map *cMap = m_casterUnit->GetMap(); if (goinfo->type == GAMEOBJECT_TYPE_FISHINGNODE) { - float waterLevel = m_caster->GetTerrain()->GetWaterLevel(fx, fy, fz); + float waterLevel = m_casterUnit->GetTerrain()->GetWaterLevel(fx, fy, fz); if (waterLevel == VMAP_INVALID_HEIGHT_VALUE) // Hack to prevent fishing bobber from failing to land on fishing hole { // but this is not proper, we really need to ignore not materialized objects @@ -6347,7 +6459,7 @@ void Spell::EffectTransmitted(SpellEffectIndex eff_idx) GameObject* pGameObj = new GameObject; if (!pGameObj->Create(cMap->GenerateLocalLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap, - fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, GO_ANIMPROGRESS_DEFAULT, GO_STATE_READY)) + fx, fy, fz, m_casterUnit->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, GO_ANIMPROGRESS_DEFAULT, GO_STATE_READY)) { delete pGameObj; return; @@ -6359,8 +6471,8 @@ void Spell::EffectTransmitted(SpellEffectIndex eff_idx) { case GAMEOBJECT_TYPE_FISHINGNODE: { - m_caster->SetChannelObjectGuid(pGameObj->GetObjectGuid()); - m_caster->AddGameObject(pGameObj); // will removed at spell cancel + m_casterUnit->SetChannelObjectGuid(pGameObj->GetObjectGuid()); + m_casterUnit->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) @@ -6386,17 +6498,17 @@ void Spell::EffectTransmitted(SpellEffectIndex eff_idx) } case GAMEOBJECT_TYPE_SUMMONING_RITUAL: { - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (m_casterUnit->GetTypeId() == TYPEID_PLAYER) { // Set the summoning target - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->GetSelectionGuid()) - pGameObj->SetSummonTarget(m_caster->ToPlayer()->GetSelectionGuid()); + if (m_casterUnit->GetTypeId() == TYPEID_PLAYER && m_casterUnit->ToPlayer()->GetSelectionGuid()) + pGameObj->SetSummonTarget(m_casterUnit->ToPlayer()->GetSelectionGuid()); // will be removed at Spell::Cancel or GameObject::RemoveUniqueUse if activated - m_caster->AddGameObject(pGameObj); + m_casterUnit->AddGameObject(pGameObj); // the caster becomes a go user too - pGameObj->AddUniqueUse(m_caster->ToPlayer()); + pGameObj->AddUniqueUse(m_casterUnit->ToPlayer()); m_targets.setGOTarget(pGameObj); SetChannelingVisual(true); } @@ -6410,25 +6522,25 @@ void Spell::EffectTransmitted(SpellEffectIndex eff_idx) pGameObj->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); - pGameObj->SetOwnerGuid(m_caster->GetObjectGuid()); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + pGameObj->SetOwnerGuid(m_casterUnit->GetObjectGuid()); + if (m_casterUnit->GetTypeId() == TYPEID_PLAYER) { - if(Group * group = ((Player*)m_caster)->GetGroup()) + if(Group * group = ((Player*)m_casterUnit)->GetGroup()) pGameObj->SetOwnerGroupId(group->GetId()); } - pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); + pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_casterUnit->getLevel()); pGameObj->SetSpellId(m_spellInfo->Id); DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted"); - //m_caster->AddGameObject(pGameObj); + //m_casterUnit->AddGameObject(pGameObj); //m_ObjToDel.push_back(pGameObj); cMap->Add(pGameObj); pGameObj->SummonLinkedTrapIfAny(); - if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI()) - ((Creature*)m_caster)->AI()->JustSummoned(pGameObj); + if (m_casterUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_casterUnit)->AI()) + ((Creature*)m_casterUnit)->AI()->JustSummoned(pGameObj); AddExecuteLogInfo(eff_idx, ExecuteLogInfo(pGameObj->GetObjectGuid())); } diff --git a/src/game/Spells/SpellEntry.cpp b/src/game/Spells/SpellEntry.cpp index 44564653d..63b3d0119 100644 --- a/src/game/Spells/SpellEntry.cpp +++ b/src/game/Spells/SpellEntry.cpp @@ -582,21 +582,25 @@ uint32 SpellEntry::GetCastTime(Spell* spell) const if (spell) { - // Nostalrius: ne pas consommer les SpellModifier si 'castTime' = 0 (Eclat lunaire / Grace de la nature 16886) - if (castTime) - if (Player* modOwner = spell->GetCaster()->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, SPELLMOD_CASTING_TIME, castTime, spell); - - if (!(Attributes & (SPELL_ATTR_IS_ABILITY | SPELL_ATTR_TRADESPELL))) -#if SUPPORTED_CLIENT_BUILD >= CLIENT_BUILD_1_12_1 - castTime = int32(castTime * spell->GetCaster()->GetFloatValue(UNIT_MOD_CAST_SPEED)); -#else - castTime = int32(castTime * (1.0f + spell->GetCaster()->GetInt32Value(UNIT_MOD_CAST_SPEED)/100.0f)); -#endif - else + if (Unit* pUnit = spell->GetCaster()->ToUnit()) { - if (spell->IsRangedSpell() && !spell->IsAutoRepeat()) - castTime = int32(castTime * spell->GetCaster()->m_modAttackSpeedPct[RANGED_ATTACK]); + // Nostalrius: do not consume the Spell Mod if 'castTime' = 0 (Nature's Grace 16886) + if (castTime) + if (Player* modOwner = pUnit->GetSpellModOwner()) + modOwner->ApplySpellMod(Id, SPELLMOD_CASTING_TIME, castTime, spell); + + if (!(Attributes & (SPELL_ATTR_IS_ABILITY | SPELL_ATTR_TRADESPELL))) + { +#if SUPPORTED_CLIENT_BUILD >= CLIENT_BUILD_1_12_1 + castTime = int32(castTime * pUnit->GetFloatValue(UNIT_MOD_CAST_SPEED)); +#else + castTime = int32(castTime * (1.0f + pUnit->GetInt32Value(UNIT_MOD_CAST_SPEED) / 100.0f)); +#endif + } + else if (spell->IsRangedSpell() && !spell->IsAutoRepeat()) + { + castTime = int32(castTime * pUnit->m_modAttackSpeedPct[RANGED_ATTACK]); + } } } @@ -727,7 +731,7 @@ float SpellEntry::CalculateDefaultCoefficient(DamageEffectType const damagetype) return coeff * DotFactor; } -float SpellEntry::CalculateCustomCoefficient(Unit const* caster, DamageEffectType const damageType, float coeff, Spell* spell, bool donePart) const +float SpellEntry::CalculateCustomCoefficient(WorldObject const* caster, DamageEffectType const damageType, float coeff, Spell* spell, bool donePart) const { if (!caster) return coeff; @@ -773,10 +777,13 @@ float SpellEntry::CalculateCustomCoefficient(Unit const* caster, DamageEffectTyp if (spell->GetTargetNum() > 1) { - if (Player* modOwner = caster->GetSpellModOwner()) + if (Unit const* pUnit = caster->ToUnit()) { - // Improved Chain Heal (T2 3/8 bonus) / Gift of the Gathering Storm Chain Lightning Bonus - modOwner->ApplySpellMod(spell->m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier, spell); + if (Player* modOwner = pUnit->GetSpellModOwner()) + { + // Improved Chain Heal (T2 3/8 bonus) / Gift of the Gathering Storm Chain Lightning Bonus + modOwner->ApplySpellMod(spell->m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier, spell); + } } } @@ -808,7 +815,7 @@ int32 SpellEntry::GetMaxDuration() const return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]); } -int32 SpellEntry::CalculateDuration(Unit const* caster) const +int32 SpellEntry::CalculateDuration(WorldObject const* caster) const { int32 duration = GetDuration(); @@ -816,15 +823,19 @@ int32 SpellEntry::CalculateDuration(Unit const* caster) const { int32 maxduration = GetMaxDuration(); - if (duration != maxduration && caster->GetTypeId() == TYPEID_PLAYER) - duration += int32((maxduration - duration) * ((Player*)caster)->GetComboPoints() / 5); + if (duration != maxduration) + if (Player const* pPlayer = caster->ToPlayer()) + duration += int32((maxduration - duration) * pPlayer->GetComboPoints() / 5); - if (Player* modOwner = caster->GetSpellModOwner()) + if (Unit const* pUnit = caster->ToUnit()) { - modOwner->ApplySpellMod(Id, SPELLMOD_DURATION, duration); + if (Player* modOwner = pUnit->GetSpellModOwner()) + { + modOwner->ApplySpellMod(Id, SPELLMOD_DURATION, duration); - if (duration < 0) - duration = 0; + if (duration < 0) + duration = 0; + } } } @@ -857,7 +868,7 @@ uint16 SpellEntry::GetAuraMaxTicks() const return 6; } -bool SpellEntry::IsPositiveSpell(Unit* caster, Unit* victim) const +bool SpellEntry::IsPositiveSpell(WorldObject* caster, Unit* victim) const { if (Attributes & SPELL_ATTR_NEGATIVE) return false; @@ -869,7 +880,7 @@ bool SpellEntry::IsPositiveSpell(Unit* caster, Unit* victim) const return true; } -bool SpellEntry::IsPositiveEffect(SpellEffectIndex effIndex, Unit* caster, Unit* victim) const +bool SpellEntry::IsPositiveEffect(SpellEffectIndex effIndex, WorldObject* caster, Unit* victim) const { // Nostalrius (SpellMod) if (Custom & SPELL_CUSTOM_POSITIVE) diff --git a/src/game/Spells/SpellEntry.h b/src/game/Spells/SpellEntry.h index 4335f4644..91d94bf4d 100644 --- a/src/game/Spells/SpellEntry.h +++ b/src/game/Spells/SpellEntry.h @@ -29,6 +29,7 @@ class Spell; class Unit; +class WorldObject; class SpellEntry; // Custom flags assigned in the db @@ -891,8 +892,8 @@ class SpellEntry return Internal & SPELL_INTERNAL_POSITIVE; } - bool IsPositiveSpell(Unit* caster, Unit* victim) const; - bool IsPositiveEffect(SpellEffectIndex effIndex, Unit* caster = nullptr, Unit* victim = nullptr) const; + bool IsPositiveSpell(WorldObject* caster, Unit* victim) const; + bool IsPositiveEffect(SpellEffectIndex effIndex, WorldObject* caster = nullptr, Unit* victim = nullptr) const; inline bool IsHealSpell() const { @@ -939,7 +940,7 @@ class SpellEntry return Internal & SPELL_INTERNAL_REFLECTABLE; } - inline bool IsReflectableSpell(Unit* caster, Unit* victim) const + inline bool IsReflectableSpell(WorldObject* caster, Unit* victim) const { return DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !HasAttribute(SPELL_ATTR_IS_ABILITY) && !HasAttribute(SPELL_ATTR_EX_CANT_BE_REFLECTED) && !HasAttribute(SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) @@ -1067,13 +1068,13 @@ class SpellEntry int32 GetDuration() const; int32 GetMaxDuration() const; - int32 CalculateDuration(Unit const* caster = nullptr) const; + int32 CalculateDuration(WorldObject const* caster = nullptr) const; uint32 GetCastTime(Spell* spell = nullptr) const; uint32 GetCastTimeForBonus(DamageEffectType damagetype) const; uint16 GetAuraMaxTicks() const; WeaponAttackType GetWeaponAttackType() const; float CalculateDefaultCoefficient(DamageEffectType const damagetype) const; - float CalculateCustomCoefficient(Unit const* caster, DamageEffectType const damageType, float coeff, Spell* spell, bool donePart) const; + float CalculateCustomCoefficient(WorldObject const* caster, DamageEffectType const damageType, float coeff, Spell* spell, bool donePart) const; SpellCastResult GetErrorAtShapeshiftedCast(uint32 form) const; uint32 GetMechanic() const { return Mechanic; } uint32 GetManaCost() const { return manaCost; } diff --git a/src/scripts/eastern_kingdoms/burning_steppes/burning_steppes.cpp b/src/scripts/eastern_kingdoms/burning_steppes/burning_steppes.cpp index 78f6fbf76..dd9f75b47 100644 --- a/src/scripts/eastern_kingdoms/burning_steppes/burning_steppes.cpp +++ b/src/scripts/eastern_kingdoms/burning_steppes/burning_steppes.cpp @@ -492,7 +492,7 @@ bool QuestAccept_npc_grark_lorkrub(Player* pPlayer, Creature* pCreature, const Q return false; } -bool EffectDummyCreature_spell_capture_grark(Unit* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget/*, ObjectGuid*/ /*originalCasterGuid*/) +bool EffectDummyCreature_spell_capture_grark(WorldObject* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget/*, ObjectGuid*/ /*originalCasterGuid*/) { // always check spellid and effectindex if (uiSpellId == SPELL_CAPTURE_GRARK && uiEffIndex == EFFECT_INDEX_0) diff --git a/src/scripts/eastern_kingdoms/dun_morogh/gnomeregan/boss_thermaplugg.cpp b/src/scripts/eastern_kingdoms/dun_morogh/gnomeregan/boss_thermaplugg.cpp index 16a4aeb5f..fb382f005 100644 --- a/src/scripts/eastern_kingdoms/dun_morogh/gnomeregan/boss_thermaplugg.cpp +++ b/src/scripts/eastern_kingdoms/dun_morogh/gnomeregan/boss_thermaplugg.cpp @@ -230,7 +230,7 @@ CreatureAI* GetAI_boss_thermaplugg(Creature* pCreature) return new boss_thermapluggAI(pCreature); } -bool EffectDummyCreature_spell_boss_thermaplugg(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) +bool EffectDummyCreature_spell_boss_thermaplugg(WorldObject* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) { if ((uiSpellId != SPELL_ACTIVATE_BOMB_A && uiSpellId != SPELL_ACTIVATE_BOMB_B) || uiEffIndex != EFFECT_INDEX_0) return false; diff --git a/src/scripts/eastern_kingdoms/eastern_plaguelands/eastern_plaguelands.cpp b/src/scripts/eastern_kingdoms/eastern_plaguelands/eastern_plaguelands.cpp index 52129d366..5519545e5 100644 --- a/src/scripts/eastern_kingdoms/eastern_plaguelands/eastern_plaguelands.cpp +++ b/src/scripts/eastern_kingdoms/eastern_plaguelands/eastern_plaguelands.cpp @@ -1845,19 +1845,22 @@ enum SPELL_PLACING_SMOKEY_S_EXPLOSIVES = 19250, TRIGGER_SCOURGE_STRUCTURE = 12247 }; -bool EffectDummyGameObj_go_mark_of_detonation(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex effIndex, GameObject* pGameObjectTarget) +bool EffectDummyGameObj_go_mark_of_detonation(WorldObject* pCaster, uint32 uiSpellId, SpellEffectIndex effIndex, GameObject* pGameObjectTarget) { //always check spellid and effectindex - if (uiSpellId == SPELL_PLACING_SMOKEY_S_EXPLOSIVES && effIndex == EFFECT_INDEX_0 && pCaster->GetTypeId() == TYPEID_PLAYER) + if (uiSpellId == SPELL_PLACING_SMOKEY_S_EXPLOSIVES && effIndex == EFFECT_INDEX_0) { - if (Creature* pCreature = pGameObjectTarget->FindNearestCreature(TRIGGER_SCOURGE_STRUCTURE, 8.000000, true)) + if (Player* pPlayer = pCaster->ToPlayer()) { - pCaster->ToPlayer()->KilledMonsterCredit(pCreature->GetEntry(), pCreature->GetObjectGuid()); - pCreature->DealDamage(pCreature, pCreature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + if (Creature* pCreature = pGameObjectTarget->FindNearestCreature(TRIGGER_SCOURGE_STRUCTURE, 8.000000, true)) + { + pPlayer->KilledMonsterCredit(pCreature->GetEntry(), pCreature->GetObjectGuid()); + pCreature->DealDamage(pCreature, pCreature->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); + } + //always return true when we are handling this spell and effect + pGameObjectTarget->Despawn(); + return true; } - //always return true when we are handling this spell and effect - pGameObjectTarget->Despawn(); - return true; } return false; } diff --git a/src/scripts/eastern_kingdoms/eastern_plaguelands/naxxramas/boss_gothik.cpp b/src/scripts/eastern_kingdoms/eastern_plaguelands/naxxramas/boss_gothik.cpp index f6cd1a513..5d2edf953 100644 --- a/src/scripts/eastern_kingdoms/eastern_plaguelands/naxxramas/boss_gothik.cpp +++ b/src/scripts/eastern_kingdoms/eastern_plaguelands/naxxramas/boss_gothik.cpp @@ -596,7 +596,7 @@ CreatureAI* GetAI_boss_gothik(Creature* pCreature) return new boss_gothikAI(pCreature); } -bool EffectDummyCreature_spell_anchor(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) +bool EffectDummyCreature_spell_anchor(WorldObject* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) { if (uiEffIndex != EFFECT_INDEX_0 || pCreatureTarget->GetEntry() != NPC_SUB_BOSS_TRIGGER) return true; diff --git a/src/scripts/eastern_kingdoms/stranglethorn_vale/zulgurub/boss_mandokir.cpp b/src/scripts/eastern_kingdoms/stranglethorn_vale/zulgurub/boss_mandokir.cpp index 25173c0a5..dfecf8a24 100644 --- a/src/scripts/eastern_kingdoms/stranglethorn_vale/zulgurub/boss_mandokir.cpp +++ b/src/scripts/eastern_kingdoms/stranglethorn_vale/zulgurub/boss_mandokir.cpp @@ -146,8 +146,8 @@ struct boss_mandokirAI : public ScriptedAI uint64 m_uiTargetToKill; bool m_bTargetMoved; float m_fWatchedTargetAllowedMoveRange; - bool m_bTargetCasted; - bool m_bTargetAttacked; + bool m_bTargetActed; + float m_fTargetThreat; bool m_bFearAfterCharge; bool m_bChargeCasted; @@ -178,8 +178,8 @@ struct boss_mandokirAI : public ScriptedAI m_uiTargetToKill = 0; m_bTargetMoved = false; m_fWatchedTargetAllowedMoveRange = 2.0; - m_bTargetCasted = false; - m_bTargetAttacked = false; + m_bTargetActed = false; + m_fTargetThreat = 0.0f; m_uiPlayerToRez = 0; @@ -301,17 +301,14 @@ struct boss_mandokirAI : public ScriptedAI m_fWatchedTargetAllowedMoveRange = 2.0; } - // Ni attaquer, soigner, ou participer au combat - if ((!m_bTargetCasted) && (pWatchTarget->GetLastCastedSpell(true))) - m_bTargetCasted = true; // Ni attaquer. - if ((!m_bTargetAttacked) && (pWatchTarget->GetLastAttackType() < MAX_ATTACK)) - m_bTargetAttacked = true; + if ((!m_bTargetActed) && (m_creature->getThreatManager().getThreat(pWatchTarget) > m_fTargetThreat)) + m_bTargetActed = true; } // Le debuff est termine else { - if (m_bTargetMoved || m_bTargetCasted || m_bTargetAttacked) + if (m_bTargetMoved || m_bTargetActed) m_uiTargetToKill = m_uiWatchTarget; else m_uiTargetToKill = 0; @@ -399,11 +396,9 @@ struct boss_mandokirAI : public ScriptedAI m_uiWatchTarget = pTarget->GetGUID(); m_fTargetX = pTarget->GetPositionX(); m_fTargetY = pTarget->GetPositionY(); - pTarget->SetLastCastedSpell(0, true); - pTarget->SetLastAttackType(MAX_ATTACK); m_bTargetMoved = false; - m_bTargetCasted = false; - m_bTargetAttacked = false; + m_bTargetActed = false; + m_fTargetThreat = m_creature->getThreatManager().getThreat(pTarget); } } diff --git a/src/scripts/kalimdor/darkshore/darkshore.cpp b/src/scripts/kalimdor/darkshore/darkshore.cpp index 3eb8da1e7..3a40a8a4d 100644 --- a/src/scripts/kalimdor/darkshore/darkshore.cpp +++ b/src/scripts/kalimdor/darkshore/darkshore.cpp @@ -847,20 +847,24 @@ CreatureAI* GetAI_npc_rabid_thistle_bear(Creature* pCreature) return new npc_rabid_thistle_bearAI(pCreature); } -bool EffectDummyCreature_npc_rabid_thistle_bear(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex effIndex, Creature* pCreatureTarget) +bool EffectDummyCreature_npc_rabid_thistle_bear(WorldObject* pCaster, uint32 uiSpellId, SpellEffectIndex effIndex, Creature* pCreatureTarget) { //always check spellid and effectindex - if (uiSpellId == SPELL_TRAPPED_BEAR && effIndex == EFFECT_INDEX_0 && pCaster->GetTypeId() == TYPEID_PLAYER) + if (uiSpellId == SPELL_TRAPPED_BEAR && effIndex == EFFECT_INDEX_0) { - pCreatureTarget->UpdateEntry(NPC_CAPTURED_RABID_THISTLE_BEAR); - pCaster->ToPlayer()->KilledMonsterCredit(pCreatureTarget->GetEntry(), pCreatureTarget->GetObjectGuid()); - if (npc_rabid_thistle_bearAI* pBearAI = dynamic_cast(pCreatureTarget->AI())) + if (Player* pPlayer = pCaster->ToPlayer()) { - pBearAI->EnterEvadeMode(); - pBearAI->StartFollowing(pCaster->ToPlayer()); + pCreatureTarget->UpdateEntry(NPC_CAPTURED_RABID_THISTLE_BEAR); + pPlayer->KilledMonsterCredit(pCreatureTarget->GetEntry(), pCreatureTarget->GetObjectGuid()); + if (npc_rabid_thistle_bearAI* pBearAI = dynamic_cast(pCreatureTarget->AI())) + { + pBearAI->EnterEvadeMode(); + pBearAI->StartFollowing(pPlayer); + } + + //always return true when we are handling this spell and effect + return true; } - //always return true when we are handling this spell and effect - return true; } return false; } diff --git a/src/scripts/kalimdor/durotar/durotar.cpp b/src/scripts/kalimdor/durotar/durotar.cpp index b53838f1a..ec42e2ff4 100644 --- a/src/scripts/kalimdor/durotar/durotar.cpp +++ b/src/scripts/kalimdor/durotar/durotar.cpp @@ -177,14 +177,13 @@ struct LazyPeonAI : public ScriptedAI } }; -bool peon_wake_up(Unit *caster, uint32 spellId, SpellEffectIndex effIndex, Creature *crTarget) +bool peon_wake_up(WorldObject* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature *crTarget) { if (spellId == SPELL_AWAKEN_PEON && crTarget->GetEntry() == LAZY_PEON_ENTRY && crTarget->HasAura(SPELL_BUFF_SLEEP)) { /** Display updated quest status */ - Player* player; - if (player = caster->ToPlayer()) - player->KilledMonsterCredit(crTarget->GetEntry(), crTarget->GetObjectGuid()); + if (Player* pPlayer = pCaster->ToPlayer()) + pPlayer->KilledMonsterCredit(crTarget->GetEntry(), crTarget->GetObjectGuid()); } return true; diff --git a/src/scripts/kalimdor/felwood/felwood.cpp b/src/scripts/kalimdor/felwood/felwood.cpp index 68223a600..f91d033f4 100644 --- a/src/scripts/kalimdor/felwood/felwood.cpp +++ b/src/scripts/kalimdor/felwood/felwood.cpp @@ -107,7 +107,7 @@ CreatureAI* GetAI_npc_kitten(Creature* pCreature) return new npc_kittenAI(pCreature); } -bool EffectDummyCreature_npc_kitten(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex effIndex, Creature* pCreatureTarget) +bool EffectDummyCreature_npc_kitten(WorldObject* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex effIndex, Creature* pCreatureTarget) { //always check spellid and effectindex if (uiSpellId == SPELL_CORRUPT_SABER_VISUAL && effIndex == EFFECT_INDEX_0) diff --git a/src/scripts/kalimdor/feralas/feralas.cpp b/src/scripts/kalimdor/feralas/feralas.cpp index 74540ff72..19bbbff6d 100644 --- a/src/scripts/kalimdor/feralas/feralas.cpp +++ b/src/scripts/kalimdor/feralas/feralas.cpp @@ -247,7 +247,7 @@ bool QuestAccept_npc_shay_leafrunner(Player* pPlayer, Creature* pCreature, const return true; } -bool EffectDummyCreature_npc_shay_leafrunner(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget)/*, ObjectGuid*/ /*originalCasterGuid*//*)*/ +bool EffectDummyCreature_npc_shay_leafrunner(WorldObject* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget)/*, ObjectGuid*/ /*originalCasterGuid*//*)*/ { if (uiSpellId == SPELL_SHAYS_BELL && uiEffIndex == EFFECT_INDEX_0) { diff --git a/src/scripts/kalimdor/moonglade/moonglade.cpp b/src/scripts/kalimdor/moonglade/moonglade.cpp index 89c47918b..a54266a18 100644 --- a/src/scripts/kalimdor/moonglade/moonglade.cpp +++ b/src/scripts/kalimdor/moonglade/moonglade.cpp @@ -1112,7 +1112,7 @@ bool QuestAccept_npc_keeper_remulos(Player* pPlayer, Creature* pCreature, const return true; } -bool EffectDummyCreature_conjure_rift(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) +bool EffectDummyCreature_conjure_rift(WorldObject* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) { //always check spellid and effectindex if (uiSpellId == SPELL_CONJURE_RIFT && uiEffIndex == EFFECT_INDEX_0) diff --git a/src/scripts/kalimdor/the_barrens/razorfen_kraul/razorfen_kraul.cpp b/src/scripts/kalimdor/the_barrens/razorfen_kraul/razorfen_kraul.cpp index 98caec533..21c838453 100644 --- a/src/scripts/kalimdor/the_barrens/razorfen_kraul/razorfen_kraul.cpp +++ b/src/scripts/kalimdor/the_barrens/razorfen_kraul/razorfen_kraul.cpp @@ -304,22 +304,26 @@ CreatureAI* GetAI_npc_snufflenose_gopher(Creature* pCreature) return new npc_snufflenose_gopherAI(pCreature); } -bool EffectDummyCreature_npc_snufflenose_gopher(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) +bool EffectDummyCreature_npc_snufflenose_gopher(WorldObject* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) { + Unit* pUnit = pCaster->ToUnit(); + if (!pUnit) + return false; + // always check spellid and effectindex if (uiSpellId == SPELL_SNUFFLENOSE_COMMAND && uiEffIndex == EFFECT_INDEX_0) { if (pCreatureTarget->GetEntry() == NPC_SNUFFLENOSE_GOPHER) { // Do nothing if player has not targeted gopher - if (pCaster->GetTargetGuid() != pCreatureTarget->GetObjectGuid()) + if (pUnit->GetTargetGuid() != pCreatureTarget->GetObjectGuid()) { // Send Spell_FAILED_BAD_TARGETS pCreatureTarget->SendPetCastFail(uiSpellId, (SpellCastResult)0x0A); return false; } - DoScriptText(SAY_GOPHER_COMMAND, pCreatureTarget, pCaster); + DoScriptText(SAY_GOPHER_COMMAND, pCreatureTarget, pUnit); if (npc_snufflenose_gopherAI* pGopherAI = dynamic_cast(pCreatureTarget->AI())) { diff --git a/src/scripts/world/spell_scripts.cpp b/src/scripts/world/spell_scripts.cpp index 473db6f08..6ff634908 100644 --- a/src/scripts/world/spell_scripts.cpp +++ b/src/scripts/world/spell_scripts.cpp @@ -52,7 +52,7 @@ enum SPELL_BLAZERUNNER_AURA = 13913 }; -bool EffectDummyCreature_spell_dummy_npc(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) +bool EffectDummyCreature_spell_dummy_npc(WorldObject* /*pCaster*/, uint32 uiSpellId, SpellEffectIndex uiEffIndex, Creature* pCreatureTarget) { switch (uiSpellId) {