diff options
Diffstat (limited to 'src/server/game/Spells/Spell.cpp')
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 508 |
1 files changed, 292 insertions, 216 deletions
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 5ccd5ee6e21..f8f1dc773ff 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2599,7 +2599,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA return SPELL_MISS_EVADE; // For delayed spells immunity may be applied between missile launch and hit - check immunity for that case - if (m_spellInfo->Speed && (unit->IsImmunedToDamage(m_spellInfo) || unit->IsImmunedToSpell(m_spellInfo))) + if (m_spellInfo->Speed && unit->IsImmunedToSpell(m_spellInfo)) return SPELL_MISS_IMMUNE; // disable effects to which unit is immune @@ -3013,7 +3013,8 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered if ((_triggeredCastFlags & TRIGGERED_IGNORE_COMBO_POINTS) || m_CastItem || !m_caster->m_playerMovingMe) m_needComboPoints = false; - SpellCastResult result = CheckCast(true); + uint32 param1 = 0, param2 = 0; + SpellCastResult result = CheckCast(true, ¶m1, ¶m2); // target is checked in too many locations and with different results to handle each of them // handle just the general SPELL_FAILED_BAD_TARGETS result which is the default result for most DBC target checks if (_triggeredCastFlags & TRIGGERED_IGNORE_TARGET_CHECK && result == SPELL_FAILED_BAD_TARGETS) @@ -3038,7 +3039,10 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); } - SendCastResult(result); + if (param1 || param2) + SendCastResult(result, ¶m1, ¶m2); + else + SendCastResult(result); finish(false); return; @@ -3227,10 +3231,11 @@ void Spell::cast(bool skipCheck) // skip check if done already (for instant cast spells for example) if (!skipCheck) { - SpellCastResult castResult = CheckCast(false); + uint32 param1 = 0, param2 = 0; + SpellCastResult castResult = CheckCast(false, ¶m1, ¶m2); if (castResult != SPELL_CAST_OK) { - SendCastResult(castResult); + SendCastResult(castResult, ¶m1, ¶m2); SendInterrupted(0); //restore spell mods if (m_caster->GetTypeId() == TYPEID_PLAYER) @@ -3782,7 +3787,7 @@ void Spell::finish(bool ok) } template<class T> -inline void FillSpellCastFailedArgs(T& packet, ObjectGuid castId, SpellInfo const* spellInfo, SpellCastResult result, SpellCustomErrors customError, uint32* misc, Player* caster) +inline void FillSpellCastFailedArgs(T& packet, ObjectGuid castId, SpellInfo const* spellInfo, SpellCastResult result, SpellCustomErrors customError, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/, Player* caster) { packet.CastID = castId; packet.SpellID = spellInfo->Id; @@ -3791,109 +3796,182 @@ inline void FillSpellCastFailedArgs(T& packet, ObjectGuid castId, SpellInfo cons switch (result) { case SPELL_FAILED_NOT_READY: - packet.FailedArg1 = 0; // unknown (value 1 update cooldowns on client flag) + if (*param1) + packet.FailedArg1 = *param1; + else + packet.FailedArg1 = 0; // unknown (value 1 update cooldowns on client flag) break; case SPELL_FAILED_REQUIRES_SPELL_FOCUS: - packet.FailedArg1 = spellInfo->RequiresSpellFocus; // SpellFocusObject.dbc id + if (param1) + packet.FailedArg1 = *param1; + else + packet.FailedArg1 = spellInfo->RequiresSpellFocus; // SpellFocusObject.dbc id break; - case SPELL_FAILED_REQUIRES_AREA: // AreaTable.dbc id - // hardcode areas limitation case - switch (spellInfo->Id) + case SPELL_FAILED_REQUIRES_AREA: // AreaTable.dbc id + if (param1) + packet.FailedArg1 = *param1; + else { - case 41617: // Cenarion Mana Salve - case 41619: // Cenarion Healing Salve - packet.FailedArg1 = 3905; - break; - case 41618: // Bottled Nethergon Energy - case 41620: // Bottled Nethergon Vapor - packet.FailedArg1 = 3842; - break; - case 45373: // Bloodberry Elixir - packet.FailedArg1 = 4075; - break; - default: // default case (don't must be) - packet.FailedArg1 = 0; - break; + // hardcode areas limitation case + switch (spellInfo->Id) + { + case 41617: // Cenarion Mana Salve + case 41619: // Cenarion Healing Salve + packet.FailedArg1 = 3905; + break; + case 41618: // Bottled Nethergon Energy + case 41620: // Bottled Nethergon Vapor + packet.FailedArg1 = 3842; + break; + case 45373: // Bloodberry Elixir + packet.FailedArg1 = 4075; + break; + default: // default case (don't must be) + packet.FailedArg1 = 0; + break; + } } break; case SPELL_FAILED_TOTEMS: - if (spellInfo->Totem[0]) - packet.FailedArg1 = spellInfo->Totem[0]; - if (spellInfo->Totem[1]) - packet.FailedArg2 = spellInfo->Totem[1]; + if (param1) + { + packet.FailedArg1 = *param1; + if (param2) + packet.FailedArg2 = *param2; + } + else + { + if (spellInfo->Totem[0]) + packet.FailedArg1 = spellInfo->Totem[0]; + if (spellInfo->Totem[1]) + packet.FailedArg2 = spellInfo->Totem[1]; + } break; case SPELL_FAILED_TOTEM_CATEGORY: - if (spellInfo->TotemCategory[0]) - packet.FailedArg1 = spellInfo->TotemCategory[0]; - if (spellInfo->TotemCategory[1]) - packet.FailedArg2 = spellInfo->TotemCategory[1]; + if (param1) + { + packet.FailedArg1 = *param1; + if (param2) + packet.FailedArg2 = *param2; + } + else + { + if (spellInfo->TotemCategory[0]) + packet.FailedArg1 = spellInfo->TotemCategory[0]; + if (spellInfo->TotemCategory[1]) + packet.FailedArg2 = spellInfo->TotemCategory[1]; + } break; case SPELL_FAILED_EQUIPPED_ITEM_CLASS: case SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND: case SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND: - packet.FailedArg1 = spellInfo->EquippedItemClass; - packet.FailedArg2 = spellInfo->EquippedItemSubClassMask; + if (param1 && param2) + { + packet.FailedArg1 = *param1; + packet.FailedArg2 = *param2; + } + else + { + packet.FailedArg1 = spellInfo->EquippedItemClass; + packet.FailedArg2 = spellInfo->EquippedItemSubClassMask; + } break; case SPELL_FAILED_TOO_MANY_OF_ITEM: { - uint32 item = 0; - for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(caster->GetMap()->GetDifficultyID())) - if (effect->ItemType) - item = effect->ItemType; - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item); - if (proto && proto->GetItemLimitCategory()) - packet.FailedArg1 = proto->GetItemLimitCategory(); + if (param1) + packet.FailedArg1 = *param1; + else + { + uint32 item = 0; + for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(caster->GetMap()->GetDifficultyID())) + if (effect->ItemType) + item = effect->ItemType; + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item); + if (proto && proto->GetItemLimitCategory()) + packet.FailedArg1 = proto->GetItemLimitCategory(); + } break; } case SPELL_FAILED_PREVENTED_BY_MECHANIC: - packet.FailedArg1 = spellInfo->GetAllEffectsMechanicMask(); // SpellMechanic.dbc id + if (param1) + packet.FailedArg1 = *param1; + else + packet.FailedArg1 = spellInfo->GetAllEffectsMechanicMask(); // SpellMechanic.dbc id break; case SPELL_FAILED_NEED_EXOTIC_AMMO: - packet.FailedArg1 = spellInfo->EquippedItemSubClassMask; // seems correct... + if (param1) + packet.FailedArg1 = *param1; + else + packet.FailedArg1 = spellInfo->EquippedItemSubClassMask; // seems correct... break; case SPELL_FAILED_NEED_MORE_ITEMS: - packet.FailedArg1 = 0; // Item id - packet.FailedArg2 = 0; // Item count? + if (param1 && param2) + { + packet.FailedArg1 = *param1; + packet.FailedArg2 = *param2; + } + else + { + packet.FailedArg1 = 0; // Item id + packet.FailedArg2 = 0; // Item count? + } break; case SPELL_FAILED_MIN_SKILL: - packet.FailedArg1 = 0; // SkillLine.dbc id - packet.FailedArg2 = 0; // required skill value + if (param1 && param2) + { + packet.FailedArg1 = *param1; + packet.FailedArg2 = *param2; + } + else + { + packet.FailedArg1 = 0; // SkillLine.dbc id + packet.FailedArg2 = 0; // required skill value + } break; case SPELL_FAILED_FISHING_TOO_LOW: - packet.FailedArg1 = 0; // required fishing skill + if (param1) + packet.FailedArg1 = *param1; + else + packet.FailedArg1 = 0; // required fishing skill break; case SPELL_FAILED_CUSTOM_ERROR: packet.FailedArg1 = customError; break; case SPELL_FAILED_SILENCED: - packet.FailedArg1 = 0; // Unknown + if (param1) + packet.FailedArg1 = *param1; + else + packet.FailedArg1 = 0; // Unknown break; case SPELL_FAILED_REAGENTS: { - uint32 missingItem = 0; - for (uint32 i = 0; i < MAX_SPELL_REAGENTS; i++) + if (param1) + packet.FailedArg1 = *param1; + else { - if (spellInfo->Reagent[i] <= 0) - continue; + uint32 missingItem = 0; + for (uint32 i = 0; i < MAX_SPELL_REAGENTS; i++) + { + if (spellInfo->Reagent[i] <= 0) + continue; - uint32 itemid = spellInfo->Reagent[i]; - uint32 itemcount = spellInfo->ReagentCount[i]; + uint32 itemid = spellInfo->Reagent[i]; + uint32 itemcount = spellInfo->ReagentCount[i]; - if (!caster->HasItemCount(itemid, itemcount)) - { - missingItem = itemid; - break; + if (!caster->HasItemCount(itemid, itemcount)) + { + missingItem = itemid; + break; + } } + packet.FailedArg1 = missingItem; // first missing item } - - packet.FailedArg1 = missingItem; // first missing item break; } case SPELL_FAILED_CANT_UNTALENT: { - if (misc) - if (TalentEntry const* talent = sTalentStore.LookupEntry(misc[0])) - packet.FailedArg1 = talent->SpellID; + ASSERT(param1); + packet.FailedArg1 = *param1; break; } // TODO: SPELL_FAILED_NOT_STANDING @@ -3902,7 +3980,7 @@ inline void FillSpellCastFailedArgs(T& packet, ObjectGuid castId, SpellInfo cons } } -void Spell::SendCastResult(SpellCastResult result) +void Spell::SendCastResult(SpellCastResult result, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) const { if (result == SPELL_CAST_OK) return; @@ -3910,7 +3988,7 @@ void Spell::SendCastResult(SpellCastResult result) if (m_caster->GetTypeId() != TYPEID_PLAYER) return; - if (m_caster->ToPlayer()->GetSession()->PlayerLoading()) // don't send cast results at loading time + if (m_caster->ToPlayer()->IsLoading()) // don't send cast results at loading time return; if (_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) @@ -3918,11 +3996,11 @@ void Spell::SendCastResult(SpellCastResult result) WorldPackets::Spells::CastFailed castFailed; castFailed.SpellXSpellVisualID = m_SpellVisual; - FillSpellCastFailedArgs(castFailed, m_castId, m_spellInfo, result, m_customError, m_misc.Raw.Data, m_caster->ToPlayer()); + FillSpellCastFailedArgs(castFailed, m_castId, m_spellInfo, result, m_customError, param1, param2, m_caster->ToPlayer()); m_caster->ToPlayer()->SendDirectMessage(castFailed.Write()); } -void Spell::SendPetCastResult(SpellCastResult result) +void Spell::SendPetCastResult(SpellCastResult result, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) const { if (result == SPELL_CAST_OK) return; @@ -3935,18 +4013,18 @@ void Spell::SendPetCastResult(SpellCastResult result) result = SPELL_FAILED_DONT_REPORT; WorldPackets::Spells::PetCastFailed petCastFailed; - FillSpellCastFailedArgs(petCastFailed, m_castId, m_spellInfo, result, SPELL_CUSTOM_ERROR_NONE, m_misc.Raw.Data, owner->ToPlayer()); + FillSpellCastFailedArgs(petCastFailed, m_castId, m_spellInfo, result, SPELL_CUSTOM_ERROR_NONE, param1, param2, owner->ToPlayer()); owner->ToPlayer()->SendDirectMessage(petCastFailed.Write()); } -void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint32 spellVisual, ObjectGuid cast_count, SpellCastResult result, SpellCustomErrors customError /*= SPELL_CUSTOM_ERROR_NONE*/, uint32* misc /*= nullptr*/) +void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint32 spellVisual, ObjectGuid cast_count, SpellCastResult result, SpellCustomErrors customError /*= SPELL_CUSTOM_ERROR_NONE*/, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) { if (result == SPELL_CAST_OK) return; WorldPackets::Spells::CastFailed packet; packet.SpellXSpellVisualID = spellVisual; - FillSpellCastFailedArgs(packet, cast_count, spellInfo, result, customError, misc, caster); + FillSpellCastFailedArgs(packet, cast_count, spellInfo, result, customError, param1, param2, caster); caster->GetSession()->SendPacket(packet.Write()); } @@ -4605,7 +4683,7 @@ void Spell::TakePower() } } -SpellCastResult Spell::CheckRuneCost() +SpellCastResult Spell::CheckRuneCost() const { int32 runeCost = std::accumulate(m_powerCost.begin(), m_powerCost.end(), 0, [](int32 totalCost, SpellPowerCost const& cost) { @@ -4785,7 +4863,7 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT } } -SpellCastResult Spell::CheckCast(bool strict) +SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) { // check death state if (!m_caster->IsAlive() && !m_spellInfo->IsPassive() && !(m_spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell))) @@ -4848,13 +4926,15 @@ SpellCastResult Spell::CheckCast(bool strict) bool checkForm = true; // Ignore form req aura Unit::AuraEffectList const& ignore = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_SHAPESHIFT); - for (Unit::AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + for (AuraEffect const* aurEff : ignore) { - if (!(*i)->IsAffectingSpell(m_spellInfo)) + if (!aurEff->IsAffectingSpell(m_spellInfo)) continue; + checkForm = false; break; } + if (checkForm) { // Cannot be used in this stance/form @@ -5076,7 +5156,7 @@ SpellCastResult Spell::CheckCast(bool strict) // always (except passive spells) check items (only player related checks) if (!m_spellInfo->IsPassive()) { - castResult = CheckItems(); + castResult = CheckItems(param1, param2); if (castResult != SPELL_CAST_OK) return castResult; } @@ -5096,7 +5176,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURAS)) { - castResult = CheckCasterAuras(); + castResult = CheckCasterAuras(param1); if (castResult != SPELL_CAST_OK) return castResult; } @@ -5567,7 +5647,11 @@ SpellCastResult Spell::CheckCast(bool strict) if (!talent) return SPELL_FAILED_DONT_REPORT; if (m_caster->GetSpellHistory()->HasCooldown(talent->SpellID)) + { + if (param1) + *param1 = talent->SpellID; return SPELL_FAILED_CANT_UNTALENT; + } break; } case SPELL_EFFECT_GIVE_ARTIFACT_POWER: @@ -5780,142 +5864,110 @@ SpellCastResult Spell::CheckPetCast(Unit* target) return CheckCast(true); } -SpellCastResult Spell::CheckCasterAuras() const +SpellCastResult Spell::CheckCasterAuras(uint32* param1) const { // spells totally immuned to caster auras (wsg flag drop, give marks etc) if (m_spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CASTER_AURAS)) return SPELL_CAST_OK; - uint8 school_immune = 0; - uint32 mechanic_immune = 0; - uint32 dispel_immune = 0; - - // Check if the spell grants school or mechanic immunity. - // We use bitmasks so the loop is done only once and not on every aura check below. - if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) + bool usableWhileStunned = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_STUNNED); + bool usableWhileFeared = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_FEARED); + bool usableWhileConfused = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_CONFUSED); + if (m_spellInfo->HasAttribute(SPELL_ATTR7_USABLE_IN_STUN_FEAR_CONFUSION)) { - for (SpellEffectInfo const* effect : GetEffects()) - { - if (!effect) - continue; - - if (effect->ApplyAuraName == SPELL_AURA_SCHOOL_IMMUNITY) - school_immune |= uint32(effect->MiscValue); - else if (effect->ApplyAuraName == SPELL_AURA_MECHANIC_IMMUNITY) - mechanic_immune |= 1 << uint32(effect->MiscValue); - else if (effect->ApplyAuraName == SPELL_AURA_DISPEL_IMMUNITY) - dispel_immune |= SpellInfo::GetDispelMask(DispelType(effect->MiscValue)); - } - // immune movement impairment and loss of control - if (m_spellInfo->Id == 42292 || m_spellInfo->Id == 59752 || m_spellInfo->Id == 19574 || m_spellInfo->Id == 53490) - mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; + usableWhileStunned = true; + usableWhileFeared = true; + usableWhileConfused = true; } - bool usableInStun = m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_STUNNED); - // 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 - if (unitflag & UNIT_FLAG_STUNNED) - { - // spell is usable while stunned, check if caster has allowed stun auras, another stun types must prevent cast spell - if (usableInStun) - { - static uint32 const allowedStunMask = - 1 << MECHANIC_STUN - | 1 << MECHANIC_FREEZE - | 1 << MECHANIC_SAPPED - | 1 << MECHANIC_SLEEP; - - bool foundNotStun = false; - Unit::AuraEffectList const& stunAuras = m_caster->GetAuraEffectsByType(SPELL_AURA_MOD_STUN); - for (Unit::AuraEffectList::const_iterator i = stunAuras.begin(); i != stunAuras.end(); ++i) - { - uint32 mechanicMask = (*i)->GetSpellInfo()->GetAllEffectsMechanicMask(); - if (mechanicMask && !(mechanicMask & allowedStunMask)) - { - foundNotStun = true; - break; - } - } - if (foundNotStun) - prevented_reason = SPELL_FAILED_STUNNED; - } - else - prevented_reason = SPELL_FAILED_STUNNED; - } - else if (unitflag & UNIT_FLAG_CONFUSED && !m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_CONFUSED)) - prevented_reason = SPELL_FAILED_CONFUSED; - else if (unitflag & UNIT_FLAG_FLEEING && !m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_FEARED)) - prevented_reason = SPELL_FAILED_FLEEING; - else if (unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_SILENCE) - prevented_reason = SPELL_FAILED_SILENCED; - else if (unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_PACIFY) - prevented_reason = SPELL_FAILED_PACIFIED; + SpellCastResult result = SPELL_CAST_OK; + + // Get unit state + uint32 const unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); + if (!m_caster->GetCharmerGUID().IsEmpty()) + { + if (Unit* charmer = m_caster->GetCharmer()) + if (charmer->GetUnitBeingMoved() != m_caster && CheckCasterNotImmunedCharmAuras(param1)) + result = SPELL_FAILED_CHARMED; + } + else if (unitflag & UNIT_FLAG_STUNNED && !usableWhileStunned && CheckCasterNotImmunedStunAuras(param1)) + result = SPELL_FAILED_STUNNED; + else if (unitflag & UNIT_FLAG_SILENCED && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_SILENCE && CheckCasterNotImmunedSilenceAuras(param1)) + result = SPELL_FAILED_SILENCED; + else if (unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_PACIFY && CheckCasterNotImmunedPacifyAuras(param1)) + result = SPELL_FAILED_PACIFIED; + else if (unitflag & UNIT_FLAG_FLEEING && !usableWhileFeared && CheckCasterNotImmunedFearAuras(param1)) + result = SPELL_FAILED_FLEEING; + else if (unitflag & UNIT_FLAG_CONFUSED && !usableWhileConfused && CheckCasterNotImmunedDisorientAuras(param1)) + result = SPELL_FAILED_CONFUSED; else if (m_caster->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_NO_ACTIONS) && m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_NO_ACTIONS) - prevented_reason = SPELL_FAILED_NO_ACTIONS; + result = SPELL_FAILED_NO_ACTIONS; // Attr must make flag drop spell totally immune from all effects - if (prevented_reason != SPELL_CAST_OK) - { - if (school_immune || mechanic_immune || dispel_immune) - { - //Checking auras is needed now, because you are prevented by some state but the spell grants immunity. - Unit::AuraApplicationMap const& auras = m_caster->GetAppliedAuras(); - for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - Aura const* aura = itr->second->GetBase(); - SpellInfo const* auraInfo = aura->GetSpellInfo(); - if (auraInfo->GetAllEffectsMechanicMask() & mechanic_immune) - continue; - if (auraInfo->GetSchoolMask() & school_immune && !auraInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)) - continue; - if (auraInfo->GetDispelMask() & dispel_immune) - continue; + if (result != SPELL_CAST_OK) + return (param1 && *param1) ? SPELL_FAILED_PREVENTED_BY_MECHANIC : result; - //Make a second check for spell failed so the right SPELL_FAILED message is returned. - //That is needed when your casting is prevented by multiple states and you are only immune to some of them. - for (SpellEffectInfo const* effect : GetEffects()) - { - if (!effect) - continue; + return SPELL_CAST_OK; +} - if (AuraEffect* part = aura->GetEffect(effect->EffectIndex)) - { - switch (part->GetAuraType()) - { - case SPELL_AURA_MOD_STUN: - if (!usableInStun || !(auraInfo->GetAllEffectsMechanicMask() & (1<<MECHANIC_STUN))) - return SPELL_FAILED_STUNNED; - break; - case SPELL_AURA_MOD_CONFUSE: - if (!m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_CONFUSED)) - return SPELL_FAILED_CONFUSED; - break; - case SPELL_AURA_MOD_FEAR: - if (!m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_FEARED)) - return SPELL_FAILED_FLEEING; - break; - case SPELL_AURA_MOD_SILENCE: - case SPELL_AURA_MOD_PACIFY: - case SPELL_AURA_MOD_PACIFY_SILENCE: - if (m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_PACIFY) - return SPELL_FAILED_PACIFIED; - else if (m_spellInfo->PreventionType & SPELL_PREVENTION_TYPE_SILENCE) - return SPELL_FAILED_SILENCED; - break; - default: break; - } - } - } - } +// based on sub_00804430 from 12340 client +bool Spell::CheckCasterHasNotImmunedAuraType(AuraType auraType, uint32* param1) const +{ + // Checking auras is needed now, because you are prevented by some state but the spell grants immunity. + Unit::AuraEffectList const& auraEffects = m_caster->GetAuraEffectsByType(auraType); + if (auraEffects.empty()) + return false; + + for (AuraEffect const* aurEff : auraEffects) + { + if (m_spellInfo->CanSpellCastOverrideAuraEffect(aurEff)) + continue; + + if (param1) + { + *param1 = aurEff->GetSpellEffectInfo()->Mechanic; + if (!*param1) + *param1 = aurEff->GetSpellInfo()->Mechanic; } - // You are prevented from casting and the spell cast does not grant immunity. Return a failed error. - else - return prevented_reason; + return true; } - return SPELL_CAST_OK; + + return false; +} + +bool Spell::CheckCasterNotImmunedCharmAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_CHARM, param1) || + CheckCasterHasNotImmunedAuraType(SPELL_AURA_AOE_CHARM, param1) || + CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_POSSESS, param1); +} + +bool Spell::CheckCasterNotImmunedStunAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_STUN, param1); +} + +bool Spell::CheckCasterNotImmunedSilenceAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_SILENCE, param1) || + CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_PACIFY_SILENCE, param1); +} + +bool Spell::CheckCasterNotImmunedPacifyAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_PACIFY, param1) || + CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_PACIFY_SILENCE, param1); +} + +bool Spell::CheckCasterNotImmunedFearAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_FEAR, param1); +} + +bool Spell::CheckCasterNotImmunedDisorientAuras(uint32* param1) const +{ + return CheckCasterHasNotImmunedAuraType(SPELL_AURA_MOD_CONFUSE, param1); } SpellCastResult Spell::CheckArenaAndRatedBattlegroundCastRules() @@ -6025,7 +6077,7 @@ void Spell::CheckDst() m_targets.SetDst(*m_caster); } -SpellCastResult Spell::CheckRange(bool strict) +SpellCastResult Spell::CheckRange(bool strict) const { // Don't check for instant cast spells if (!strict && m_casttime == 0) @@ -6064,7 +6116,7 @@ SpellCastResult Spell::CheckRange(bool strict) return SPELL_CAST_OK; } -std::pair<float, float> Spell::GetMinMaxRange(bool strict) +std::pair<float, float> Spell::GetMinMaxRange(bool strict) const { float rangeMod = 0.0f; float minRange = 0.0f; @@ -6110,14 +6162,14 @@ std::pair<float, float> Spell::GetMinMaxRange(bool strict) maxRange *= ranged->GetTemplate()->GetRangedModRange() * 0.01f; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, maxRange, this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, maxRange, const_cast<Spell*>(this)); maxRange += rangeMod; return std::pair<float, float>(minRange, maxRange); } -SpellCastResult Spell::CheckPower() +SpellCastResult Spell::CheckPower() const { // item cast not used power if (m_CastItem) @@ -6155,12 +6207,15 @@ SpellCastResult Spell::CheckPower() return SPELL_CAST_OK; } -SpellCastResult Spell::CheckItems() +SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 /*= nullptr*/) const { Player* player = m_caster->ToPlayer(); if (!player) return SPELL_CAST_OK; + if (m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ITEM_CHECK)) + return SPELL_CAST_OK; + if (!m_CastItem) { if (!m_castItemGUID.IsEmpty()) @@ -6236,10 +6291,11 @@ SpellCastResult Spell::CheckItems() // check target item if (!m_targets.GetItemTargetGUID().IsEmpty()) { - if (!m_targets.GetItemTarget()) + Item* item = m_targets.GetItemTarget(); + if (!item) return SPELL_FAILED_ITEM_GONE; - if (!m_targets.GetItemTarget()->IsFitToSpellRequirements(m_spellInfo)) + if (!item->IsFitToSpellRequirements(m_spellInfo)) return SPELL_FAILED_EQUIPPED_ITEM_CLASS; } // if not item target then required item must be equipped @@ -6289,7 +6345,11 @@ SpellCastResult Spell::CheckItems() } } if (!player->HasItemCount(itemid, itemcount)) + { + if (param1) + *param1 = itemid; return SPELL_FAILED_REAGENTS; + } } } @@ -6496,21 +6556,29 @@ SpellCastResult Spell::CheckItems() } case SPELL_EFFECT_PROSPECTING: { - if (!m_targets.GetItemTarget()) + Item* item = m_targets.GetItemTarget(); + if (!item) return SPELL_FAILED_CANT_BE_PROSPECTED; //ensure item is a prospectable ore - if (!(m_targets.GetItemTarget()->GetTemplate()->GetFlags() & ITEM_FLAG_IS_PROSPECTABLE)) + if (!(item->GetTemplate()->GetFlags() & ITEM_FLAG_IS_PROSPECTABLE)) return SPELL_FAILED_CANT_BE_PROSPECTED; //prevent prospecting in trade slot - if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != m_caster->GetGUID()) return SPELL_FAILED_CANT_BE_PROSPECTED; //Check for enough skill in jewelcrafting - uint32 item_prospectingskilllevel = m_targets.GetItemTarget()->GetTemplate()->GetRequiredSkillRank(); - if (item_prospectingskilllevel >player->GetSkillValue(SKILL_JEWELCRAFTING)) + uint32 item_prospectingskilllevel = item->GetTemplate()->GetRequiredSkillRank(); + if (item_prospectingskilllevel > player->GetSkillValue(SKILL_JEWELCRAFTING)) return SPELL_FAILED_LOW_CASTLEVEL; //make sure the player has the required ores in inventory - if (m_targets.GetItemTarget()->GetCount() < 5) + if (item->GetCount() < 5) + { + if (param1 && param2) + { + *param1 = item->GetEntry(); + *param2 = 5; + } return SPELL_FAILED_NEED_MORE_ITEMS; + } if (!LootTemplates_Prospecting.HaveLootFor(m_targets.GetItemTargetEntry())) return SPELL_FAILED_CANT_BE_PROSPECTED; @@ -6519,21 +6587,29 @@ SpellCastResult Spell::CheckItems() } case SPELL_EFFECT_MILLING: { - if (!m_targets.GetItemTarget()) + Item* item = m_targets.GetItemTarget(); + if (!item) return SPELL_FAILED_CANT_BE_MILLED; //ensure item is a millable herb - if (!(m_targets.GetItemTarget()->GetTemplate()->GetFlags() & ITEM_FLAG_IS_MILLABLE)) + if (!(item->GetTemplate()->GetFlags() & ITEM_FLAG_IS_MILLABLE)) return SPELL_FAILED_CANT_BE_MILLED; //prevent milling in trade slot - if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID()) + if (item->GetOwnerGUID() != m_caster->GetGUID()) return SPELL_FAILED_CANT_BE_MILLED; //Check for enough skill in inscription - uint32 item_millingskilllevel = m_targets.GetItemTarget()->GetTemplate()->GetRequiredSkillRank(); + uint32 item_millingskilllevel = item->GetTemplate()->GetRequiredSkillRank(); if (item_millingskilllevel > player->GetSkillValue(SKILL_INSCRIPTION)) return SPELL_FAILED_LOW_CASTLEVEL; //make sure the player has the required herbs in inventory - if (m_targets.GetItemTarget()->GetCount() < 5) + if (item->GetCount() < 5) + { + if (param1 && param2) + { + *param1 = item->GetEntry(); + *param2 = 5; + } return SPELL_FAILED_NEED_MORE_ITEMS; + } if (!LootTemplates_Milling.HaveLootFor(m_targets.GetItemTargetEntry())) return SPELL_FAILED_CANT_BE_MILLED; |