From d4f3fed660f725c5772b92c37dcca97c4df6c368 Mon Sep 17 00:00:00 2001 From: Dramacydal Date: Mon, 25 Mar 2013 16:31:55 +0300 Subject: [PATCH] [c12546] Fix usage of SpellEquippedItems and SPELL_AURA_MOD_DAMAGE_DONE & SPELL_AURA_MOD_DAMAGE_PERCENT_DONE auras as a result --- src/game/Spell.cpp | 722 ++++++++++++++++++++++++++------------ src/game/SpellAuras.cpp | 4 +- src/game/SpellEffects.cpp | 7 +- src/game/Unit.cpp | 8 +- src/shared/revision_nr.h | 2 +- 5 files changed, 514 insertions(+), 229 deletions(-) diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index b3bd0f431..e3907ed2c 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -37,7 +37,7 @@ #include "MapManager.h" #include "ObjectAccessor.h" #include "CellImpl.h" -#include "Policies/SingletonImp.h" +#include "Policies/Singleton.h" #include "SharedDefines.h" #include "LootMgr.h" #include "VMapFactory.h" @@ -46,6 +46,8 @@ #include "Chat.h" #include "DB2Stores.h" #include "SQLStorages.h" +#include "Vehicle.h" +#include "SQLStorages.h" extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; @@ -429,6 +431,7 @@ Spell::Spell(Unit* caster, SpellEntry const* info, bool triggered, ObjectGuid or m_runesState = 0; m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before. + m_usedHolyPower = 0; 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; @@ -440,7 +443,7 @@ Spell::Spell(Unit* caster, SpellEntry const* info, bool triggered, ObjectGuid or m_spellFlags = SPELL_FLAG_NORMAL; - if (m_spellInfo->GetDmgClass() == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR_EX2_CANT_REFLECTED)) + if (m_spellInfo->GetDmgClass() == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR_EX_CANT_REFLECTED)) { for (int j = 0; j < MAX_EFFECT_INDEX; ++j) { @@ -888,7 +891,7 @@ void Spell::AddUnitTarget(Unit* pVictim, SpellEffectIndex effIndex) return; // Check for effect immune skip if immuned - bool immuned = pVictim->IsImmuneToSpellEffect(m_spellInfo, effIndex); + bool immuned = pVictim->IsImmuneToSpellEffect(m_spellInfo, effIndex, pVictim == m_caster); if (pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsTotem() && (m_spellFlags & SPELL_FLAG_REDIRECTED)) immuned = false; @@ -920,30 +923,25 @@ void Spell::AddUnitTarget(Unit* pVictim, SpellEffectIndex effIndex) // spell fly from visual cast object WorldObject* affectiveObject = GetAffectiveCasterObject(); - // Spell has trajectory - need calculate incoming time - if (affectiveObject && m_targets.GetSpeed() > 0.0f) - { - float dist; - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - dist = affectiveObject->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ); - else - dist = affectiveObject->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ()); - float speed = m_targets.GetSpeed() * cos(m_targets.GetElevation()); - - target.timeDelay = (uint64) floor(dist / speed * 1000.0f); - - // Calculate minimum incoming time - if (m_delayMoment == 0 || m_delayMoment>target.timeDelay) - m_delayMoment = target.timeDelay; - } - // Spell has speed - need calculate incoming time - else if (m_spellInfo->speed > 0.0f && affectiveObject && pVictim != affectiveObject) + // Spell have speed (possible inherited from triggering spell) - need calculate incoming time + float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; + if (speed > 0.0f && affectiveObject && (pVictim != affectiveObject || (m_targets.m_targetMask & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION)))) { // calculate spell incoming interval - float dist = affectiveObject->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ()); + float dist = 0.0f; // distance to impact + if (pVictim == affectiveObject) // Calculate dist to destination target also for self-cast spells + { + if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + dist = affectiveObject->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ); + else // Must have Source Target + dist = affectiveObject->GetDistance(m_targets.m_srcX, m_targets.m_srcY, m_targets.m_srcZ); + } + else // normal unit target, take distance + dist = affectiveObject->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ()); + if (dist < 5.0f) dist = 5.0f; - target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f); + target.timeDelay = (uint64) floor(dist / speed * 1000.0f); // Calculate minimum incoming time if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) @@ -1016,30 +1014,15 @@ void Spell::AddGOTarget(GameObject* pVictim, SpellEffectIndex effIndex) // spell fly from visual cast object WorldObject* affectiveObject = GetAffectiveCasterObject(); - // Spell has trajectory - need calculate incoming time - if (affectiveObject && m_targets.GetSpeed() > 0.0f) - { - float dist; - if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) - dist = affectiveObject->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ); - else - dist = affectiveObject->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ()); - - float speed = m_targets.GetSpeed() * cos(m_targets.GetElevation()); - - target.timeDelay = (uint64) floor(dist / speed * 1000.0f); - - if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) - m_delayMoment = target.timeDelay; - } - // Spell has speed - need calculate incoming time - else if (m_spellInfo->speed > 0.0f && affectiveObject && pVictim != affectiveObject) + // Spell can have speed - need calculate incoming time + float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; + if (speed > 0.0f && affectiveObject && pVictim != affectiveObject) { // calculate spell incoming interval float dist = affectiveObject->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ()); if (dist < 5.0f) dist = 5.0f; - target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f); + target.timeDelay = (uint64) floor(dist / speed * 1000.0f); if (m_delayMoment == 0 || m_delayMoment > target.timeDelay) m_delayMoment = target.timeDelay; } @@ -1121,7 +1104,8 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) procVictim = PROC_FLAG_NONE; } - if (m_spellInfo->speed > 0) + float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; + if (speed > 0.0f) { // mark effects that were already handled in Spell::HandleDelayedSpellLaunch on spell launch as processed for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) @@ -1195,7 +1179,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Fill base damage struct (unitTarget - is real spell target) SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask); - if (m_spellInfo->speed > 0) + if (speed > 0.0f) { damageInfo.damage = m_damage; damageInfo.HitInfo = target->HitInfo; @@ -1292,9 +1276,10 @@ void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) Unit* realCaster = GetAffectiveCaster(); // Recheck immune (only for delayed spells) - if (m_spellInfo->speed && ( + float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; + if (speed && ( unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo)) || - unit->IsImmuneToSpell(m_spellInfo))) + unit->IsImmuneToSpell(m_spellInfo, unit == realCaster))) { if (realCaster) realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE); @@ -1315,7 +1300,7 @@ void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) if (realCaster && realCaster != unit) { // Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells - if (m_spellInfo->speed > 0.0f && + if (speed > 0.0f && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) && unit->GetCharmerOrOwnerGuid() != m_caster->GetObjectGuid()) { @@ -1327,7 +1312,7 @@ void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) if (!realCaster->IsFriendlyTo(unit)) { // for delayed spells ignore not visible explicit target - if (m_spellInfo->speed > 0.0f && unit == m_targets.getUnitTarget() && + if (speed > 0.0f && unit == m_targets.getUnitTarget() && !unit->isVisibleForOrDetect(m_caster, m_caster, false)) { realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); @@ -1367,7 +1352,7 @@ void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) else { // for delayed spells ignore negative spells (after duel end) for friendly targets - if (m_spellInfo->speed > 0.0f && !IsPositiveSpell(m_spellInfo->Id)) + if (speed > 0.0f && !IsPositiveSpell(m_spellInfo->Id)) { realCaster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE); ResetEffectDamageAndHeal(); @@ -1446,7 +1431,7 @@ void Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) } } - duration = unit->CalculateAuraDuration(m_spellInfo, effectMask, duration, m_caster); + duration = unit->CalculateAuraDuration(m_spellInfo, effectMask, duration, m_caster, this); if (duration != originalDuration) { @@ -1674,9 +1659,12 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& SpellEffectEntry const* spellEffect = m_spellInfo->GetSpellEffect(effIndex); SpellClassOptionsEntry const* classOpt = m_spellInfo->GetSpellClassOptions(); + if (!spellEffect) + return; + float radius; - if (spellEffect && spellEffect->EffectRadiusIndex) - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->EffectRadiusIndex)); + if (uint32 radiusIndex = spellEffect->GetRadiusIndex()) + radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); else radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); @@ -1701,24 +1689,30 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& { switch (m_spellInfo->Id) { - case 802: // Mutate Bug - case 804: // Explode Bug - case 23138: // Gate of Shazzrah - case 28560: // Summon Blizzard - case 30769: // Pick Red Riding Hood - case 30835: // Infernal Relay - case 31347: // Doom TODO: exclude top threat target from target selection - case 33711: // Murmur's Touch - case 38794: // Murmur's Touch (h) - case 44869: // Spectral Blast - case 45976: // Open Portal - case 47669: // Awaken Subboss + case 802: // Mutate Bug (AQ40, Emperor Vek'nilash) + case 804: // Explode Bug (AQ40, Emperor Vek'lor) + case 23138: // Gate of Shazzrah (MC, Shazzrah) + case 28560: // Summon Blizzard (Naxx, Sapphiron) + case 30541: // Blaze (Magtheridon) + case 30572: // Quake (Magtheridon) + case 30769: // Pick Red Riding Hood (Karazhan, Big Bad Wolf) + case 30835: // Infernal Relay (Karazhan, Prince Malchezaar) + case 31347: // Doom (Hyjal Summit, Azgalor) + case 33711: // Murmur's Touch (Shadow Labyrinth, Murmur) + case 38794: // Murmur's Touch (h) (Shadow Labyrinth, Murmur) + case 40834: // Agonizing Flames (BT, Illidan Stormrage) + case 41537: // Summon Enslaved Soul (BT, Reliquary of Souls) + case 44869: // Spectral Blast (SWP, Kalecgos) + case 45892: // Sinister Reflection (SWP, Kil'jaeden) + case 45976: // Open Portal (SWP, M'uru) + case 47669: // Awaken Subboss (Utgarde Pinnacle) case 48278: // Paralyze (Utgarde Pinnacle) case 50742: // Ooze Combine (Halls of Stone) case 50988: // Glare of the Tribunal (Halls of Stone) case 51003: // Summon Dark Matter Target (Halls of Stone) case 51146: // Summon Searing Gaze Target (Halls Of Stone) case 52438: // Summon Skittering Swarmer (Azjol Nerub, Krik'thir the Gatewatcher) + case 52449: // Summon Skittering Infector (Azjol Nerub, Krik'thir the Gatewatcher) case 53457: // Impale (Azjol Nerub, Anub'arak) case 54148: // Ritual of the Sword (Utgarde Pinnacle, Svala) case 55479: // Forced Obedience (Naxxramas, Razovius) @@ -1726,18 +1720,21 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case 59870: // Glare of the Tribunal (h) (Halls of Stone) case 62016: // Charge Orb (Ulduar, Thorim) case 62042: // Stormhammer (Ulduar, Thorim) + case 62166: // Stone Grip (Ulduar, Kologarn) case 62301: // Cosmic Smash (Ulduar, Algalon) case 62488: // Activate Construct (Ulduar, Ignis) - case 63018: // Searing Light + case 63018: // Searing Light (Ulduar, XT-002) case 63024: // Gravity Bomb (Ulduar, XT-002) case 63387: // Rapid Burst case 63795: // Psychosis (Ulduar, Yogg-Saron) - case 64218: // Overcharge + case 63820: // Summon Scrap Bot Trigger (Ulduar, Mimiron) use for Scrap Bots, hits npc 33856 + case 64218: // Overcharge (VoA, Emalon) case 64234: // Gravity Bomb (h) (Ulduar, XT-002) + case 64425: // Summon Scrap Bot Trigger (Ulduar, Mimiron) use for Assault Bots, hits npc 33856 case 64531: // Rapid Burst (h) - case 65121: // Searing Light (h) + case 65121: // Searing Light (h) (Ulduar, XT-002) case 65301: // Psychosis (Ulduar, Yogg-Saron) - case 65872: // Pursuing Spikes + case 65872: // Pursuing Spikes (ToCrusader, Anub'arak) case 65950: // Touch of Light (ToCrusader, Val'kyr Twins) case 66001: // Touch of Darkness (ToCrusader, Val'kyr Twins) case 66152: // Bullet Controller Summon Periodic Trigger Light (ToCrusader) @@ -1755,6 +1752,10 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case 68950: // Fear (FoS) case 68912: // Wailing Souls (FoS) case 69048: // Mirrored Soul (FoS) + case 69057: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar) 10 man + case 72088: + case 73142: + case 73144: case 69140: // Coldflame (ICC, Marrowgar) case 69674: // Mutated Infection (ICC, Rotface) case 70450: // Blood Mirror @@ -1771,8 +1772,8 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case 73023: // Mutated Infection (Mode 3) unMaxTargets = 1; break; - case 28542: // Life Drain - case 66013: // Penetrating Cold (10 man) + case 28542: // Life Drain (Naxx, Sapphiron) + case 66013: // Penetrating Cold (10 man) (ToCrusader, Anub'arak) case 67755: // Nerubian Burrower (Mode 1) (ToCrusader, Anub'arak) case 67756: // Nerubian Burrower (Mode 2) (ToCrusader, Anub'arak) case 68509: // Penetrating Cold (10 man heroic) @@ -1783,28 +1784,39 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case 71390: // Pact of the Darkfallen unMaxTargets = 2; break; - case 28796: // Poison Bolt Volley - case 29213: // Curse of the Plaguebringer - case 30004: // Flame Wreath - case 31298: // Sleep + case 28796: // Poison Bolt Volley (Naxx, Faerlina) + case 29213: // Curse of the Plaguebringer (Naxx, Noth the Plaguebringer) + case 30004: // Flame Wreath (Karazhan, Shade of Aran) + case 31298: // Sleep (Hyjal Summit, Anetheron) case 39992: // Needle Spine Targeting (BT, Warlord Najentus) - case 51904: // Limiting the count of Summoned Ghouls - case 54522: + case 41303: // Soul Drain (BT, Reliquary of Souls) + case 41376: // Spite (BT, Reliquary of Souls) + case 51904: // Summon Ghouls On Scarlet Crusade + case 54522: // Summon Ghouls On Scarlet Crusade case 60936: // Surge of Power (h) (Malygos) case 61693: // Arcane Storm (Malygos) + case 63981: // StoneGrip (h) (Ulduar, Kologarn) case 64598: // Cosmic Smash (h) (Ulduar, Algalon) + case 64620: // Summon Fire Bot Trigger (Ulduar, Mimiron) hits npc 33856 case 70814: // Bone Slice (ICC, Lord Marrowgar, heroic) case 72095: // Frozen Orb (h) (Vault of Archavon, Toravon) + case 72089: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar) 25 man + case 70826: + case 73143: + case 73145: unMaxTargets = 3; break; + case 37676: // Insidious Whisper (SSC, Leotheras the Blind) + case 38028: // Watery Grave (SSC, Morogrim Tidewalker) case 67757: // Nerubian Burrower (Mode 3) (ToCrusader, Anub'arak) case 71221: // Gas spore (Mode 1) (ICC, Festergut) unMaxTargets = 4; break; - case 30843: // Enfeeble - case 42005: // Bloodboil + case 30843: // Enfeeble (Karazhan, Prince Malchezaar) + case 40243: // Crushing Shadows (BT, Teron Gorefiend) + case 42005: // Bloodboil (BT, Gurtogg Bloodboil) case 45641: // Fire Bloom (SWP, Kil'jaeden) - case 55665: // Life Drain (h) + case 55665: // Life Drain (h) (Naxx, Sapphiron) case 58917: // Consume Minions case 64604: // Nature Bomb (Ulduar, Freya) case 67076: // Mistress' Kiss (Mode 1) (ToCrusader, Jaraxxus) @@ -1816,11 +1828,14 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case 61694: // Arcane Storm (h) (Malygos) unMaxTargets = 7; break; - case 54098: // Poison Bolt Volley (h) - case 54835: // Curse of the Plaguebringer (h) + case 38054: // Random Rocket Missile + unMaxTargets = 8; + break; + case 54098: // Poison Bolt Volley (h) (Naxx, Faerlina) + case 54835: // Curse of the Plaguebringer (h) (Naxx, Noth the Plaguebringer) unMaxTargets = 10; break; - case 25991: // Poison Bolt Volley (Pincess Huhuran) + case 25991: // Poison Bolt Volley (AQ40, Pincess Huhuran) unMaxTargets = 15; break; case 61916: // Lightning Whirl (Ulduar, Stormcaller Brundir) @@ -1832,6 +1847,24 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case 63482: // Lightning Whirl (h) (Ulduar, Stormcaller Brundir) unMaxTargets = urand(3, 6); break; + case 74452: // Conflagration (Saviana, Ruby Sanctum) + { + if (m_caster) + { + switch (m_caster->GetMap()->GetDifficulty()) + { + case RAID_DIFFICULTY_10MAN_NORMAL: + case RAID_DIFFICULTY_10MAN_HEROIC: + unMaxTargets = 2; + break; + case RAID_DIFFICULTY_25MAN_NORMAL: + case RAID_DIFFICULTY_25MAN_HEROIC: + unMaxTargets = 5; + break; + } + } + break; + } default: break; } @@ -1893,7 +1926,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case 24811: // Draw Spirit (Lethon) { if (effIndex == EFFECT_INDEX_0) // Copy range from EFF_1 to 0 - radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->EffectRadiusIndex)); + radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); break; } case 28241: // Poison (Naxxramas, Grobbulus Cloud) @@ -1915,6 +1948,16 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (Unit* realCaster = GetAffectiveCaster()) radius = radius * realCaster->GetObjectScale(); break; + case 69057: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar encounter, 10N) + case 70826: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar encounter, 25N) + case 72088: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar encounter, 10H) + case 72089: // Bone Spike Graveyard (Icecrown Citadel, Lord Marrowgar encounter, 25H) + case 73142: // Bone Spike Graveyard (during Bone Storm) (Icecrown Citadel, Lord Marrowgar encounter, 10N) + case 73143: // Bone Spike Graveyard (during Bone Storm) (Icecrown Citadel, Lord Marrowgar encounter, 25N) + case 73144: // Bone Spike Graveyard (during Bone Storm) (Icecrown Citadel, Lord Marrowgar encounter, 10H) + case 73145: // Bone Spike Graveyard (during Bone Storm) (Icecrown Citadel, Lord Marrowgar encounter, 25H) + radius = DEFAULT_VISIBILITY_INSTANCE; + break; default: break; } @@ -1924,14 +1967,6 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& break; } - Unit::AuraList const& mod = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS); - for (Unit::AuraList::const_iterator m = mod.begin(); m != mod.end(); ++m) - { - if (!(*m)->isAffectedOnSpell(m_spellInfo)) - continue; - unMaxTargets += (*m)->GetModifier()->m_amount; - } - std::list tempTargetGOList; switch (targetMode) @@ -1966,8 +2001,21 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& float dest_x = m_targets.m_destX + cos(angle) * radius; float dest_y = m_targets.m_destY + sin(angle) * radius; float dest_z = m_caster->GetPositionZ(); - m_caster->UpdateGroundPositionZ(dest_x, dest_y, dest_z); - m_targets.setDestination(dest_x, dest_y, dest_z); + if (!MapManager::IsValidMapCoord(m_caster->GetMapId(), dest_x, dest_y, dest_z)) + { + sLog.outError("Spell::SetTargetMap: invalid map coordinates for spell %u eff_idx %u target mode %u: mapid %u x %f y %f z %f\n" + "spell radius: %f caster position: x %f y %f z %f\n" + "base dest position: x %f y %f z %f", + m_spellInfo->Id, effIndex, targetMode, m_caster->GetMapId(), dest_x, dest_y, dest_z, + radius, m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), + m_targets.m_destX, m_targets.m_destY, m_caster->GetPositionZ()); + m_targets.setDestination(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ()); + } + else + { + m_caster->UpdateGroundPositionZ(dest_x, dest_y, dest_z); + m_targets.setDestination(dest_x, dest_y, dest_z); + } } // This targetMode is often used as 'last' implicitTarget for positive spells, that just require coordinates @@ -2052,7 +2100,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) break; - if (!prev->IsWithinLOSInMap(*next)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) { ++next; continue; @@ -2112,7 +2160,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) break; - if (!prev->IsWithinLOSInMap(*next)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) { ++next; continue; @@ -2185,7 +2233,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) break; - if (!prev->IsWithinLOSInMap(*next)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) { ++next; continue; @@ -2205,30 +2253,53 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& case TARGET_ALL_ENEMY_IN_AREA: FillAreaTargets(targetUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_AOE_DAMAGE); - if (m_spellInfo->Id == 42005) // Bloodboil (spell hits only the 5 furthest away targets) + switch (m_spellInfo->Id) { - if (targetUnitMap.size() > unMaxTargets) + // Do not target current victim + case 30843: // Enfeeble + case 31347: // Doom + case 37676: // Insidious Whisper + case 38028: // Watery Grave + case 40618: // Insignificance + case 41376: // Spite + case 62166: // Stone Grip + case 63981: // Stone Grip (h) { - targetUnitMap.sort(TargetDistanceOrderFarAway(m_caster)); - targetUnitMap.resize(unMaxTargets); + if (Unit* pVictim = m_caster->getVictim()) + targetUnitMap.remove(pVictim); + break; } - } - else if (m_spellInfo->Id == 30843) // Enfeeble (do not target current victim) - { - if (Unit* pVictim = m_caster->getVictim()) - targetUnitMap.remove(pVictim); + // Other special cases + case 42005: // Bloodboil (spell hits only the 5 furthest away targets) + { + if (targetUnitMap.size() > unMaxTargets) + { + targetUnitMap.sort(TargetDistanceOrderFarAway(m_caster)); + targetUnitMap.resize(unMaxTargets); + } + break; + } + default: + break; } break; case TARGET_AREAEFFECT_INSTANT: { SpellTargets targetB = SPELL_TARGETS_AOE_DAMAGE; - - // Select friendly targets for positive effect - if (IsPositiveEffect(m_spellInfo, effIndex)) - targetB = SPELL_TARGETS_FRIENDLY; + switch (spellEffect->Effect) + { + case SPELL_EFFECT_QUEST_COMPLETE: + case SPELL_EFFECT_KILL_CREDIT_PERSONAL: + case SPELL_EFFECT_KILL_CREDIT_GROUP: + targetB = SPELL_TARGETS_ALL; + default: + // Select friendly targets for positive effect + if (IsPositiveEffect(m_spellInfo, effIndex)) + targetB = SPELL_TARGETS_FRIENDLY; + } UnitList tempTargetUnitMap; - SpellScriptTargetBounds bounds = sSpellMgr.GetSpellScriptTargetBounds(m_spellInfo->Id); + SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); // fill real target list if no spell script target defined FillAreaTargets(bounds.first != bounds.second ? tempTargetUnitMap : targetUnitMap, @@ -2241,19 +2312,22 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if ((*iter)->GetTypeId() != TYPEID_UNIT) continue; - for (SpellScriptTarget::const_iterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) + for (SQLMultiStorage::SQLMultiSIterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) { - // only creature entries supported for this target type - if (i_spellST->second.type == SPELL_TARGET_TYPE_GAMEOBJECT) + if (i_spellST->CanNotHitWithSpellEffect(effIndex)) continue; - if ((*iter)->GetEntry() == i_spellST->second.targetEntry) + // only creature entries supported for this target type + if (i_spellST->type == SPELL_TARGET_TYPE_GAMEOBJECT) + continue; + + if ((*iter)->GetEntry() == i_spellST->targetEntry) { - if (i_spellST->second.type == SPELL_TARGET_TYPE_DEAD && ((Creature*)(*iter))->IsCorpse()) + if (i_spellST->type == SPELL_TARGET_TYPE_DEAD && ((Creature*)(*iter))->IsCorpse()) { targetUnitMap.push_back((*iter)); } - else if (i_spellST->second.type == SPELL_TARGET_TYPE_CREATURE && (*iter)->isAlive()) + else if (i_spellST->type == SPELL_TARGET_TYPE_CREATURE && (*iter)->isAlive()) { targetUnitMap.push_back((*iter)); } @@ -2279,7 +2353,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& } UnitList tempTargetUnitMap; - SpellScriptTargetBounds bounds = sSpellMgr.GetSpellScriptTargetBounds(m_spellInfo->Id); + SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); // fill real target list if no spell script target defined FillAreaTargets(bounds.first != bounds.second ? tempTargetUnitMap : targetUnitMap, radius, PUSH_DEST_CENTER, SPELL_TARGETS_ALL); @@ -2290,19 +2364,22 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if ((*iter)->GetTypeId() != TYPEID_UNIT) continue; - for (SpellScriptTarget::const_iterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) + for (SQLMultiStorage::SQLMultiSIterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) { - // only creature entries supported for this target type - if (i_spellST->second.type == SPELL_TARGET_TYPE_GAMEOBJECT) + if (i_spellST->CanNotHitWithSpellEffect(effIndex)) continue; - if ((*iter)->GetEntry() == i_spellST->second.targetEntry) + // only creature entries supported for this target type + if (i_spellST->type == SPELL_TARGET_TYPE_GAMEOBJECT) + continue; + + if ((*iter)->GetEntry() == i_spellST->targetEntry) { - if (i_spellST->second.type == SPELL_TARGET_TYPE_DEAD && ((Creature*)(*iter))->IsCorpse()) + if (i_spellST->type == SPELL_TARGET_TYPE_DEAD && ((Creature*)(*iter))->IsCorpse()) { targetUnitMap.push_back((*iter)); } - else if (i_spellST->second.type == SPELL_TARGET_TYPE_CREATURE && (*iter)->isAlive()) + else if (i_spellST->type == SPELL_TARGET_TYPE_CREATURE && (*iter)->isAlive()) { targetUnitMap.push_back((*iter)); } @@ -2327,36 +2404,63 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& } case TARGET_AREAEFFECT_GO_AROUND_SOURCE: case TARGET_AREAEFFECT_GO_AROUND_DEST: + case TARGET_GO_IN_FRONT_OF_CASTER_90: { float x, y, z; - if (targetMode == TARGET_AREAEFFECT_GO_AROUND_SOURCE) - { - if (m_targets.m_targetMask & TARGET_FLAG_SOURCE_LOCATION) - m_targets.getSource(x, y, z); - else - m_caster->GetPosition(x, y, z); - } - else + + if (targetMode == TARGET_AREAEFFECT_GO_AROUND_SOURCE && (m_targets.m_targetMask & TARGET_FLAG_SOURCE_LOCATION)) + m_targets.getSource(x, y, z); + else if (targetMode == TARGET_AREAEFFECT_GO_AROUND_DEST) m_targets.getDestination(x, y, z); + else // can also happen for GO_AROUND_SOURCE without SOURCE_LOCATION + m_caster->GetPosition(x, y, z); - // It may be possible to fill targets for some spell effects - // automatically (SPELL_EFFECT_WMO_REPAIR(88) for example) but - // for some/most spells we clearly need/want to limit with spell_target_script - - // Some spells untested, for affected GO type 33. May need further adjustments for spells related. - - SpellScriptTargetBounds bounds = sSpellMgr.GetSpellScriptTargetBounds(m_spellInfo->Id); - for (SpellScriptTarget::const_iterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) + bool fixedTargetExist = false; + SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); + for (SQLMultiStorage::SQLMultiSIterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) { - if (i_spellST->second.type == SPELL_TARGET_TYPE_GAMEOBJECT) + if (i_spellST->CanNotHitWithSpellEffect(effIndex)) + continue; + + if (i_spellST->type == SPELL_TARGET_TYPE_GAMEOBJECT) { + fixedTargetExist = true; // search all GO's with entry, within range of m_destN - MaNGOS::GameObjectEntryInPosRangeCheck go_check(*m_caster, i_spellST->second.targetEntry, x, y, z, radius); + MaNGOS::GameObjectEntryInPosRangeCheck go_check(*m_caster, i_spellST->targetEntry, x, y, z, radius); MaNGOS::GameObjectListSearcher checker(tempTargetGOList, go_check); - Cell::VisitGridObjects(m_caster, checker, radius); + Cell::VisitGridObjects(m_caster, checker, radius + GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex))); } } + if (!fixedTargetExist) + { + // Generic handling for spells that require GO-type 33 + if (spellEffect->Effect == SPELL_EFFECT_WMO_DAMAGE || spellEffect->Effect == SPELL_EFFECT_WMO_REPAIR || spellEffect->Effect == SPELL_EFFECT_WMO_CHANGE) + { + MaNGOS::GameObjectTypeInPosRangeCheck go_check(*m_caster, GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING, x, y, z, radius, spellEffect->Effect == SPELL_EFFECT_WMO_DAMAGE, spellEffect->Effect == SPELL_EFFECT_WMO_REPAIR); + MaNGOS::GameObjectListSearcher checker(tempTargetGOList, go_check); + Cell::VisitGridObjects(m_caster, checker, radius + GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex))); + } + } + + // Filter some targets for special target-type + for (std::list::iterator itr = tempTargetGOList.begin(); itr != tempTargetGOList.end();) + { + switch (targetMode) + { + case TARGET_GO_IN_FRONT_OF_CASTER_90: + if (!m_caster->HasInArc(M_PI_F / 2, *itr)) + { + tempTargetGOList.erase(itr++); + continue; + } + // no break here + case TARGET_AREAEFFECT_GO_AROUND_SOURCE: + case TARGET_AREAEFFECT_GO_AROUND_DEST: + default: + ++itr; + } + } break; } case TARGET_ALL_ENEMY_IN_AREA_INSTANT: @@ -2437,6 +2541,22 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->IsPet() && ((Pet*)target)->getPetType() == MINI_PET) targetUnitMap.push_back(target); break; + case TARGET_CONTROLLED_VEHICLE: + if (m_caster->IsBoarded() && m_caster->GetTransportInfo()->IsOnVehicle()) + targetUnitMap.push_back((Unit*)m_caster->GetTransportInfo()->GetTransport()); + break; + case TARGET_VEHICLE_PASSENGER_0: + case TARGET_VEHICLE_PASSENGER_1: + case TARGET_VEHICLE_PASSENGER_2: + case TARGET_VEHICLE_PASSENGER_3: + case TARGET_VEHICLE_PASSENGER_4: + case TARGET_VEHICLE_PASSENGER_5: + case TARGET_VEHICLE_PASSENGER_6: + case TARGET_VEHICLE_PASSENGER_7: + if (m_caster->IsVehicle()) + if (Unit* passenger = m_caster->GetVehicleInfo()->GetPassenger(targetMode - TARGET_VEHICLE_PASSENGER_0)) + targetUnitMap.push_back(passenger); + break; case TARGET_CASTER_COORDINATES: { // Check original caster is GO - set its coordinates as src cast @@ -2571,6 +2691,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& { case 3879: pushType = PUSH_IN_BACK; break; case 7441: pushType = PUSH_IN_FRONT_15; break; + case 8669: pushType = PUSH_IN_FRONT_15; break; } FillAreaTargets(targetUnitMap, radius, pushType, SPELL_TARGETS_AOE_DAMAGE); break; @@ -2751,7 +2872,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) break; - if (!prev->IsWithinLOSInMap(*next)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) { ++next; continue; @@ -2883,7 +3004,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& // "at the base of", in difference to 0 which appear to be "directly in front of". // TODO: some summoned will make caster be half inside summoned object. Need to fix // that in the below code (nearpoint vs closepoint, etc). - if (!spellEffect || spellEffect->EffectRadiusIndex == 0) + if (!spellEffect || spellEffect->GetRadiusIndex() == 0) radius = 0.0f; if (m_spellInfo->Id == 50019) // Hawk Hunting, problematic 50K radius @@ -3396,13 +3517,14 @@ void Spell::cast(bool skipCheck) else if (m_spellInfo->Id == 68992) // Darkflight { AddPrecastSpell(96223); // Run Speed Marker - AddPrecastSpell(97709); // Altered Form + if (m_caster->HasWorgenForm()) + AddPrecastSpell(97709); // Altered Form } else if (m_spellInfo->Id == 68996) // Two Forms { if (m_caster->IsInWorgenForm()) m_caster->RemoveSpellsCausingAura(SPELL_AURA_WORGEN_TRANSFORM); - else + else if (m_caster->HasWorgenForm()) AddPrecastSpell(97709); // Altered Form } // Chaos Bane strength buff @@ -3615,7 +3737,8 @@ void Spell::cast(bool skipCheck) procTarget = m_caster; // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells - if (m_spellInfo->speed > 0.0f) + float speed = m_spellInfo->speed == 0.0f && m_triggeredBySpellInfo ? m_triggeredBySpellInfo->speed : m_spellInfo->speed; + if (speed > 0.0f) { // Remove used for cast item if need (it can be already NULL after TakeReagents call @@ -3818,7 +3941,8 @@ void Spell::update(uint32 difftime) // check if the player caster has moved before the spell finished if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) && - ((spellEffect && spellEffect->Effect != SPELL_EFFECT_STUCK) || !((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLINGFAR))) + ((spellEffect && spellEffect->Effect != SPELL_EFFECT_STUCK) || !((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLINGFAR)) && + !m_caster->HasAffectedAura(SPELL_AURA_ALLOW_CAST_WHILE_MOVING, m_spellInfo)) { // always cancel for channeled spells if (m_spellState == SPELL_STATE_CASTING) @@ -4032,15 +4156,15 @@ void Spell::SendCastResult(SpellCastResult result) SendCastResult((Player*)m_caster, m_spellInfo, m_cast_count, result); } -void Spell::SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 cast_count, SpellCastResult result) +void Spell::SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 cast_count, SpellCastResult result, bool isPetCastResult /*=false*/) { if (result == SPELL_CAST_OK) return; - WorldPacket data(SMSG_CAST_FAILED, (4 + 1 + 1)); + WorldPacket data(isPetCastResult ? SMSG_PET_CAST_FAILED : SMSG_CAST_FAILED, (4 + 1 + 2)); data << uint8(cast_count); // single cast or multi 2.3 (0/1) data << uint32(spellInfo->Id); - data << uint8(result); // problem + data << uint8(!IsPassiveSpell(spellInfo) ? result : SPELL_FAILED_DONT_REPORT); // do not report failed passive spells switch (result) { case SPELL_FAILED_NOT_READY: @@ -4088,7 +4212,7 @@ void Spell::SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 ca case SPELL_FAILED_EQUIPPED_ITEM_CLASS: { SpellEquippedItemsEntry const* eqItems = spellInfo->GetSpellEquippedItems(); - data << uint32(eqItems ? eqItems->EquippedItemClass : 0); + data << uint32(eqItems ? eqItems->EquippedItemClass : -1); data << uint32(eqItems ? eqItems->EquippedItemSubClassMask : 0); //data << uint32(eqItems ? eqItems->EquippedItemInventoryTypeMask : 0); } @@ -4097,7 +4221,7 @@ void Spell::SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 ca case SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND: { SpellEquippedItemsEntry const* eqItems = spellInfo->GetSpellEquippedItems(); - data << uint32(eqItems ? eqItems->EquippedItemClass : 0); + data << uint32(eqItems ? eqItems->EquippedItemClass : -1); data << uint32(eqItems ? eqItems->EquippedItemSubClassMask : 0); break; } @@ -4584,8 +4708,6 @@ void Spell::SendChannelUpdate(uint32 time) } } - - m_caster->RemoveAurasByCasterSpell(m_spellInfo->Id, m_caster->GetObjectGuid()); ObjectGuid target_guid = m_caster->GetChannelObjectGuid(); @@ -4650,7 +4772,7 @@ void Spell::SendChannelStart(uint32 duration) // data << uint32(0); //} data << uint8(0); // unk2 - //if (unk1) + //if (unk2) //{ // data << ObjectGuid().WriteAsPacked(); // data << uint32(0); @@ -4764,6 +4886,29 @@ void Spell::TakePower() if (m_CastItem || m_triggeredByAuraSpell) return; + bool hit = true; + if (m_caster->GetTypeId() == TYPEID_PLAYER) + { + if (m_spellInfo->powerType == POWER_RAGE || m_spellInfo->powerType == POWER_ENERGY || m_spellInfo->powerType == POWER_HOLY_POWER) + { + ObjectGuid targetGuid = m_targets.getUnitTargetGuid(); + if (!targetGuid.IsEmpty()) + for (TargetList::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + if (ihit->targetGUID == targetGuid) + { + if (ihit->missCondition != SPELL_MISS_NONE && ihit->missCondition != SPELL_MISS_MISS) + hit = false; + if (ihit->missCondition != SPELL_MISS_NONE) + { + // lower spell cost on fail (by talent aura) + if (Player* modOwner = ((Player*)m_caster)->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost); + } + break; + } + } + } + // health as power used if (m_spellInfo->powerType == POWER_HEALTH) { @@ -4779,6 +4924,31 @@ void Spell::TakePower() Powers powerType = Powers(m_spellInfo->powerType); + if (powerType == POWER_HOLY_POWER) + { + m_usedHolyPower = m_powerCost; + + // spells consume all holy power when successfully hit + if (hit) + { + // Divine Purpose + if (m_caster->HasAura(90174)) + { + m_usedHolyPower = m_caster->GetMaxPower(POWER_HOLY_POWER); + return; + } + else + m_usedHolyPower = m_caster->GetPower(POWER_HOLY_POWER); + } + + // Zealotry - does not take power + if (m_spellInfo->Id == 85696) + return; + + m_caster->ModifyPower(powerType, -(int32)m_usedHolyPower); + return; + } + if (powerType == POWER_RUNE) { CheckOrTakeRunePower(true); @@ -5074,6 +5244,15 @@ void Spell::CastPreCastSpells(Unit* target) m_caster->CastSpell(target, (*si), true, m_CastItem); } +Unit* Spell::GetPrefilledUnitTargetOrUnitTarget(SpellEffectIndex effIndex) const +{ + for (TargetList::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr) + if (itr->effectMask & (1 << effIndex)) + return m_caster->GetMap()->GetUnit(itr->targetGUID); + + return m_targets.getUnitTarget(); +} + SpellCastResult Spell::CheckCast(bool strict) { // check cooldowns to prevent cheating (ignore passive spells, that client side visual only) @@ -5157,7 +5336,7 @@ SpellCastResult Spell::CheckCast(bool strict) { // cancel autorepeat spells if cast start when moving // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) - if (((Player*)m_caster)->isMoving()) + if (((Player*)m_caster)->isMoving() && !m_caster->HasAffectedAura(SPELL_AURA_ALLOW_CAST_WHILE_MOVING, m_spellInfo)) { // skip stuck spell to allow use it in falling case and apply spell limitations at movement if ((!((Player*)m_caster)->m_movementInfo.HasMovementFlag(MOVEFLAG_FALLINGFAR) || m_spellInfo->GetSpellEffectIdByIndex(EFFECT_INDEX_0) != SPELL_EFFECT_STUCK) && @@ -5172,6 +5351,10 @@ SpellCastResult Spell::CheckCast(bool strict) return m_caster->getClass() == CLASS_WARRIOR ? SPELL_FAILED_CASTER_AURASTATE : SPELL_FAILED_NO_COMBO_POINTS; } + // Spells like Disengage are allowed only in combat + if (!m_caster->isInCombat() && m_spellInfo->HasAttribute(SPELL_ATTR_STOP_ATTACK_TARGET) && m_spellInfo->HasAttribute(SPELL_ATTR_EX2_UNK26)) + return SPELL_FAILED_CASTER_AURASTATE; + SpellClassOptionsEntry const* classOptions = m_spellInfo->GetSpellClassOptions(); if(Unit *target = m_targets.getUnitTarget()) @@ -5195,7 +5378,6 @@ SpellCastResult Spell::CheckCast(bool strict) // Avenging Wrath Marker if (target->HasAura(61987)) return SPELL_FAILED_CASTER_AURASTATE; - } else if (target->HasAura(auraRestrictions->excludeTargetAuraSpell)) return SPELL_FAILED_CASTER_AURASTATE; @@ -5220,9 +5402,20 @@ SpellCastResult Spell::CheckCast(bool strict) // Not allow casting on flying player if (target->IsTaxiFlying()) - return SPELL_FAILED_BAD_TARGETS; + { + switch (m_spellInfo->Id) + { + // Except some spells from Taxi Flying cast + case 36573: // Vision Guide + case 42316: // Alcaz Survey Credit + case 42385: // Alcaz Survey Aura + break; + default: + return SPELL_FAILED_BAD_TARGETS; + } + } - if (!m_IsTriggeredSpell && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) + if (!m_IsTriggeredSpell && !m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) return SPELL_FAILED_LINE_OF_SIGHT; // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode @@ -5233,6 +5426,9 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_spellInfo != sSpellMgr.SelectAuraRankForLevel(m_spellInfo, target->getLevel())) return SPELL_FAILED_LOWLEVEL; } + + if (strict && m_spellInfo->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) && target->GetTypeId() != TYPEID_PLAYER && !IsAreaOfEffectSpell(m_spellInfo)) + return SPELL_FAILED_BAD_TARGETS; } else if (m_caster == target) { @@ -5378,7 +5574,7 @@ SpellCastResult Spell::CheckCast(bool strict) } if (IsPositiveSpell(m_spellInfo->Id)) - if (target->IsImmuneToSpell(m_spellInfo)) + if (target->IsImmuneToSpell(m_spellInfo, target == m_caster)) return SPELL_FAILED_TARGET_AURASTATE; // Must be behind the target. @@ -5426,7 +5622,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_NOT_MOUNTED; } - // always (except passive spells) check items (focus object can be required for any type casts) + // always (except passive spells) check items if (!IsPassiveSpell(m_spellInfo)) { SpellCastResult castResult = CheckItems(); @@ -5434,6 +5630,20 @@ SpellCastResult Spell::CheckCast(bool strict) return castResult; } + // check spell focus object + if (m_spellInfo->GetRequiresSpellFocus()) + { + GameObject* ok = NULL; + MaNGOS::GameObjectFocusCheck go_check(m_caster, m_spellInfo->GetRequiresSpellFocus()); + MaNGOS::GameObjectSearcher checker(ok, go_check); + Cell::VisitGridObjects(m_caster, checker, m_caster->GetMap()->GetVisibilityDistance()); + + if (!ok) + return SPELL_FAILED_REQUIRES_SPELL_FOCUS; + + focusObject = ok; // game object found in range + } + // Database based targets from spell_target_script if (m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example) { @@ -5450,7 +5660,7 @@ SpellCastResult Spell::CheckCast(bool strict) spellEffect->EffectImplicitTargetA == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT) { - SpellScriptTargetBounds bounds = sSpellMgr.GetSpellScriptTargetBounds(m_spellInfo->Id); + SQLMultiStorage::SQLMSIteratorBounds bounds = sSpellScriptTargetStorage.getBounds(m_spellInfo->Id); if (bounds.first == bounds.second) { @@ -5471,17 +5681,20 @@ SpellCastResult Spell::CheckCast(bool strict) Creature* creatureScriptTarget = NULL; GameObject* goScriptTarget = NULL; - for (SpellScriptTarget::const_iterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) + for (SQLMultiStorage::SQLMultiSIterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) { - switch (i_spellST->second.type) + if (i_spellST->CanNotHitWithSpellEffect(SpellEffectIndex(j))) + continue; + + switch (i_spellST->type) { case SPELL_TARGET_TYPE_GAMEOBJECT: { GameObject* p_GameObject = NULL; - if (i_spellST->second.targetEntry) + if (i_spellST->targetEntry) { - MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster, i_spellST->second.targetEntry, range); + MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster, i_spellST->targetEntry, range); MaNGOS::GameObjectLastSearcher checker(p_GameObject, go_check); Cell::VisitGridObjects(m_caster, checker, range); @@ -5514,15 +5727,15 @@ 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->GetTypeId() == TYPEID_UNIT && pTarget->GetEntry() == i_spellST->targetEntry) { - if (i_spellST->second.type == SPELL_TARGET_TYPE_DEAD && ((Creature*)pTarget)->IsCorpse()) + if (i_spellST->type == SPELL_TARGET_TYPE_DEAD && ((Creature*)pTarget)->IsCorpse()) { // always use spellMaxRange, in case GetLastRange returned different in a previous pass if (pTarget->IsWithinDistInMap(m_caster, GetSpellMaxRange(srange))) targetExplicit = (Creature*)pTarget; } - else if (i_spellST->second.type == SPELL_TARGET_TYPE_CREATURE && pTarget->isAlive()) + else if (i_spellST->type == SPELL_TARGET_TYPE_CREATURE && pTarget->isAlive()) { // always use spellMaxRange, in case GetLastRange returned different in a previous pass if (pTarget->IsWithinDistInMap(m_caster, GetSpellMaxRange(srange))) @@ -5534,7 +5747,7 @@ SpellCastResult Spell::CheckCast(bool strict) // no target provided or it was not valid, so use closest in range if (!targetExplicit) { - MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, i_spellST->second.targetEntry, i_spellST->second.type != SPELL_TARGET_TYPE_DEAD, i_spellST->second.type == SPELL_TARGET_TYPE_DEAD, range); + MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, i_spellST->targetEntry, i_spellST->type != SPELL_TARGET_TYPE_DEAD, i_spellST->type == SPELL_TARGET_TYPE_DEAD, range); MaNGOS::CreatureLastSearcher searcher(p_Creature, u_check); // Visit all, need to find also Pet* objects @@ -5663,7 +5876,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (target->GetOwnerGuid() != m_caster->GetObjectGuid()) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - float dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->EffectRadiusIndex)); + float dist = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); if (!target->IsWithinDistInMap(m_caster,dist)) return SPELL_FAILED_OUT_OF_RANGE; @@ -5698,6 +5911,24 @@ SpellCastResult Spell::CheckCast(bool strict) } break; } + case SPELL_EFFECT_DISTRACT: // All nearby enemies must not be in combat + { + if (m_targets.m_targetMask & (TARGET_FLAG_DEST_LOCATION | TARGET_FLAG_SOURCE_LOCATION)) + { + UnitList targetsCombat; + float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); + + FillAreaTargets(targetsCombat, radius, PUSH_DEST_CENTER, SPELL_TARGETS_AOE_DAMAGE); + + if (targetsCombat.empty()) + break; + + for (UnitList::iterator itr = targetsCombat.begin(); itr != targetsCombat.end(); ++itr) + if ((*itr)->isInCombat()) + return SPELL_FAILED_TARGET_IN_COMBAT; + } + break; + } case SPELL_EFFECT_SCHOOL_DAMAGE: { // Hammer of Wrath @@ -6023,11 +6254,11 @@ SpellCastResult Spell::CheckCast(bool strict) case SPELL_EFFECT_LEAP: case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER: { - float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->EffectRadiusIndex)); + float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellEffect->GetRadiusIndex())); float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation()); float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation()); // teleport a bit above terrain level to avoid falling below it - float fz = m_caster->GetTerrain()->GetHeight(fx, fy, m_caster->GetPositionZ(), true); + float fz = m_caster->GetMap()->GetHeight(m_caster->GetPhaseMask(), fx, fy, m_caster->GetPositionZ()); if (fz <= INVALID_HEIGHT) // note: this also will prevent use effect in instances without vmaps height enabled return SPELL_FAILED_TRY_AGAIN; @@ -6059,7 +6290,14 @@ SpellCastResult Spell::CheckCast(bool strict) if(!spellEffect) continue; - switch(spellEffect->EffectApplyAuraName) + // Do not check in case of junk in DBC + if (!IsAuraApplyEffect(m_spellInfo, SpellEffectIndex(i))) + continue; + + // Possible Unit-target for the spell + Unit* expectedTarget = GetPrefilledUnitTargetOrUnitTarget(SpellEffectIndex(i)); + + switch (spellEffect->EffectApplyAuraName) { case SPELL_AURA_DUMMY: { @@ -6084,7 +6322,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_UNKNOWN; - if (m_targets.getUnitTarget() == m_caster) + if (expectedTarget == m_caster) return SPELL_FAILED_BAD_TARGETS; if (m_caster->GetPetGuid()) @@ -6096,20 +6334,20 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_caster->GetCharmerGuid()) return SPELL_FAILED_CHARMED; - if (!m_targets.getUnitTarget()) + if (!expectedTarget) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if (m_targets.getUnitTarget()->GetCharmerGuid()) + if (expectedTarget->GetCharmerGuid()) return SPELL_FAILED_CHARMED; - if (int32(m_targets.getUnitTarget()->getLevel()) > CalculateDamage(SpellEffectIndex(i), m_targets.getUnitTarget())) + if (int32(expectedTarget->getLevel()) > CalculateDamage(SpellEffectIndex(i), expectedTarget)) return SPELL_FAILED_HIGHLEVEL; break; } case SPELL_AURA_MOD_CHARM: { - if (m_targets.getUnitTarget() == m_caster) + if (expectedTarget == m_caster) return SPELL_FAILED_BAD_TARGETS; if (m_caster->GetPetGuid()) @@ -6121,13 +6359,13 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_caster->GetCharmerGuid()) return SPELL_FAILED_CHARMED; - if (!m_targets.getUnitTarget()) + if (!expectedTarget) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if (m_targets.getUnitTarget()->GetCharmerGuid()) + if (expectedTarget->GetCharmerGuid()) return SPELL_FAILED_CHARMED; - if (int32(m_targets.getUnitTarget()->getLevel()) > CalculateDamage(SpellEffectIndex(i), m_targets.getUnitTarget())) + if (int32(expectedTarget->getLevel()) > CalculateDamage(SpellEffectIndex(i), expectedTarget)) return SPELL_FAILED_HIGHLEVEL; break; @@ -6171,11 +6409,11 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS: { - if (!m_targets.getUnitTarget()) + if (!expectedTarget) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; // can be casted at non-friendly unit or own pet/charm - if (m_caster->IsFriendlyTo(m_targets.getUnitTarget())) + if (m_caster->IsFriendlyTo(expectedTarget)) return SPELL_FAILED_TARGET_FRIENDLY; break; @@ -6194,40 +6432,67 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_AURA_PERIODIC_MANA_LEECH: { - if (!m_targets.getUnitTarget()) + if (!expectedTarget) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; if (m_caster->GetTypeId() != TYPEID_PLAYER || m_CastItem) break; - if (m_targets.getUnitTarget()->getPowerType() != POWER_MANA) + if (expectedTarget->getPowerType() != POWER_MANA) + return SPELL_FAILED_BAD_TARGETS; + + break; + } + case SPELL_AURA_CONTROL_VEHICLE: + { + if (m_caster->HasAuraType(SPELL_AURA_MOUNTED)) + return SPELL_FAILED_NOT_MOUNTED; + + if (!expectedTarget || !expectedTarget->IsVehicle()) + return SPELL_FAILED_BAD_TARGETS; + + // It is possible to change between vehicles that are boarded on each other + if (m_caster->IsBoarded() && m_caster->GetTransportInfo()->IsOnVehicle()) + { + // Check if trying to board a vehicle that is boarded on current transport + bool boardedOnEachOther = m_caster->GetTransportInfo()->HasOnBoard(expectedTarget); + // Check if trying to board a vehicle that has the current transport on board + if (!boardedOnEachOther) + boardedOnEachOther = expectedTarget->GetVehicleInfo()->HasOnBoard(m_caster); + + if (!boardedOnEachOther) + return SPELL_FAILED_NOT_ON_TRANSPORT; + } + + if (!expectedTarget->GetVehicleInfo()->CanBoard(m_caster)) return SPELL_FAILED_BAD_TARGETS; break; } case SPELL_AURA_MIRROR_IMAGE: { - Unit* pTarget = m_targets.getUnitTarget(); - - // In case of TARGET_SCRIPT, we have already added a target. Use it here (and find a better solution) - if (m_UniqueTargetInfo.size() == 1) - pTarget = m_caster->GetMap()->GetAnyTypeCreature(m_UniqueTargetInfo.front().targetGUID); - - if (!pTarget) + if (!expectedTarget) return SPELL_FAILED_BAD_TARGETS; - if (pTarget->GetTypeId() != TYPEID_UNIT) // Target must be creature. TODO: Check if target can also be player + // Target must be creature. TODO: Check if target can also be player + if (expectedTarget->GetTypeId() != TYPEID_UNIT) return SPELL_FAILED_BAD_TARGETS; - if (pTarget == m_caster) // Clone self can't be accepted + if (expectedTarget == m_caster) // Clone self can't be accepted return SPELL_FAILED_BAD_TARGETS; // It is assumed that target can not be cloned if already cloned by same or other clone auras - if (pTarget->HasAuraType(SPELL_AURA_MIRROR_IMAGE)) + if (expectedTarget->HasAuraType(SPELL_AURA_MIRROR_IMAGE)) return SPELL_FAILED_BAD_TARGETS; break; } + case SPELL_AURA_WORGEN_TRANSFORM: + { + if (!m_caster->HasWorgenForm()) + return m_IsTriggeredSpell ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; + break; + } default: break; } @@ -6306,7 +6571,8 @@ SpellCastResult Spell::CheckPetCast(Unit* target) Unit* _target = m_targets.getUnitTarget(); - if (_target) // for target dead/target not valid + // for target dead/target not valid + if (_target && m_targets.m_targetMask & TARGET_FLAG_UNIT) { if (!_target->isTargetableForAttack()) return SPELL_FAILED_BAD_TARGETS; // guessed error @@ -6649,12 +6915,21 @@ uint32 Spell::CalculatePowerCost(SpellEntry const* spellInfo, Unit* caster, Spel return 0; } } - SpellSchools school = GetFirstSchoolInMask(spell ? spell->m_spellSchoolMask : GetSpellSchoolMask(spellInfo)); + + SpellSchoolMask schoolMask = spell ? spell->m_spellSchoolMask : GetSpellSchoolMask(spellInfo); // Flat mod from caster auras by spell school - powerCost += caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school); + Unit::AuraList const& pwrCostAuras = caster->GetAurasByType(SPELL_AURA_MOD_POWER_COST_SCHOOL); + for (Unit::AuraList::const_iterator itr = pwrCostAuras.begin(); itr != pwrCostAuras.end(); ++itr) + { + if (((*itr)->GetModifier()->m_miscvalue & schoolMask) && + (!(*itr)->GetSpellEffect()->EffectMiscValueB || (*itr)->GetSpellEffect()->EffectMiscValueB & (1 << spellInfo->powerType))) + powerCost += (*itr)->GetModifier()->m_amount; + } + // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) if (spellInfo->HasAttribute(SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST)) powerCost += caster->GetAttackTime(OFF_ATTACK) / 100; + // Apply cost mod by spell if (spell) if (Player* modOwner = caster->GetSpellModOwner()) @@ -6664,7 +6939,17 @@ uint32 Spell::CalculatePowerCost(SpellEntry const* spellInfo, Unit* caster, Spel powerCost = int32(powerCost / (1.117f * spellInfo->GetSpellLevel() / caster->getLevel() - 0.1327f)); // PCT mod from user auras by school - powerCost = int32(powerCost * (1.0f + caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER + school))); + float pctCostMultiplier = 1.0f; + Unit::AuraList const& pwrCostPctAuras = caster->GetAurasByType(SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT); + for (Unit::AuraList::const_iterator itr = pwrCostPctAuras.begin(); itr != pwrCostPctAuras.end(); ++itr) + { + if (((*itr)->GetModifier()->m_miscvalue & schoolMask) && + (!(*itr)->GetSpellEffect()->EffectMiscValueB || (*itr)->GetSpellEffect()->EffectMiscValueB & (1 << spellInfo->powerType))) + pctCostMultiplier += (*itr)->GetModifier()->m_amount / 100.0f; + } + + // PCT mod from user auras by school + powerCost = int32(powerCost * pctCostMultiplier); if (powerCost < 0) powerCost = 0; return powerCost; @@ -6858,20 +7143,6 @@ SpellCastResult Spell::CheckItems() return m_IsTriggeredSpell ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS; } - // check spell focus object - if(uint32 spellFocus = m_spellInfo->GetRequiresSpellFocus()) - { - GameObject* ok = NULL; - MaNGOS::GameObjectFocusCheck go_check(m_caster, spellFocus); - MaNGOS::GameObjectSearcher checker(ok, go_check); - Cell::VisitGridObjects(m_caster, checker, m_caster->GetMap()->GetVisibilityDistance()); - - if (!ok) - return SPELL_FAILED_REQUIRES_SPELL_FOCUS; - - focusObject = ok; // game object found in range - } - // check reagents (ignore triggered spells with reagents processed by original spell) and special reagent ignore case. if (!IgnoreItemRequirements()) { @@ -7267,7 +7538,7 @@ bool Spell::CheckTargetCreatureType(Unit* target) const spellCreatureTargetMask = 0x7FF; } - // Dismiss Pet and Taming Lesson and Control Roskipped + // Dismiss Pet, Taming Lesson and Control Robot skipped if (m_spellInfo->Id == 2641 || m_spellInfo->Id == 23356 || m_spellInfo->Id == 30009) spellCreatureTargetMask = 0; @@ -7357,7 +7628,7 @@ bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) // fall through case SPELL_EFFECT_RESURRECT_NEW: // player far away, maybe his corpse near? - if (target != m_caster && !target->IsWithinLOSInMap(m_caster)) + if (target != m_caster && !m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !target->IsWithinLOSInMap(m_caster)) { if (!m_targets.getCorpseTargetGuid()) return false; @@ -7369,7 +7640,7 @@ bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) if (target->GetObjectGuid() != corpse->GetOwnerGuid()) return false; - if (!corpse->IsWithinLOSInMap(m_caster)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !corpse->IsWithinLOSInMap(m_caster)) return false; } @@ -7379,17 +7650,28 @@ bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) // Get GO cast coordinates if original caster -> GO if (target != m_caster) if (WorldObject* caster = GetCastingObject()) - if (!target->IsWithinLOSInMap(caster)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !target->IsWithinLOSInMap(caster)) return false; break; } + if (target->GetTypeId() != TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) + && spellEffect->EffectImplicitTargetA != TARGET_SCRIPT && spellEffect->EffectImplicitTargetA != TARGET_SELF) + return false; + switch (m_spellInfo->Id) { case 37433: // Spout (The Lurker Below), only players affected if its not in water if (target->GetTypeId() != TYPEID_PLAYER || target->IsInWater()) return false; - default: break; + break; + case 68921: // Soulstorm (FoS), only targets farer than 10 away + case 69049: // Soulstorm - = - + if (m_caster->IsWithinDist(target, 10.0f, false)) + return false; + break; + default: + break; } return true; diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 05f886519..441cf2aac 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -6508,7 +6508,7 @@ void Aura::HandleModDamageDone(bool apply, bool Real) if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0) { // apply generic physical damage bonuses including wand case - if (equippedItems && (equippedItems->EquippedItemClass == -1 || target->GetTypeId() != TYPEID_PLAYER)) + if (!equippedItems || equippedItems->EquippedItemClass == -1 || target->GetTypeId() != TYPEID_PLAYER) { target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(m_modifier.m_amount), apply); target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(m_modifier.m_amount), apply); @@ -6594,7 +6594,7 @@ void Aura::HandleModDamagePercentDone(bool apply, bool Real) if((m_modifier.m_miscvalue & SPELL_SCHOOL_MASK_NORMAL) != 0) { // apply generic physical damage bonuses including wand case - if (equippedItems && (equippedItems->EquippedItemClass == -1 || target->GetTypeId() != TYPEID_PLAYER)) + if (!equippedItems || equippedItems->EquippedItemClass == -1 || target->GetTypeId() != TYPEID_PLAYER) { target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(m_modifier.m_amount), apply); target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_modifier.m_amount), apply); diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 589349eab..49c80dda3 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -4897,12 +4897,15 @@ void Spell::EffectProficiency(SpellEffectEntry const* /*effect*/) SpellEquippedItemsEntry const* eqItems = m_spellInfo->GetSpellEquippedItems(); - if (eqItems && eqItems->EquippedItemClass == ITEM_CLASS_WEAPON && !(p_target->GetWeaponProficiency() & eqItems->EquippedItemSubClassMask)) + if (!eqItems) + return; + + if (eqItems->EquippedItemClass == ITEM_CLASS_WEAPON && !(p_target->GetWeaponProficiency() & eqItems->EquippedItemSubClassMask)) { p_target->AddWeaponProficiency(eqItems->EquippedItemSubClassMask); p_target->SendProficiency(ITEM_CLASS_WEAPON, p_target->GetWeaponProficiency()); } - if (eqItems && eqItems->EquippedItemClass == ITEM_CLASS_ARMOR && !(p_target->GetArmorProficiency() & eqItems->EquippedItemSubClassMask)) + if (eqItems->EquippedItemClass == ITEM_CLASS_ARMOR && !(p_target->GetArmorProficiency() & eqItems->EquippedItemSubClassMask)) { p_target->AddArmorProficiency(eqItems->EquippedItemSubClassMask); p_target->SendProficiency(ITEM_CLASS_ARMOR, p_target->GetArmorProficiency()); diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 1351278e0..f04e939f7 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -6558,9 +6558,9 @@ uint32 Unit::SpellDamageBonusDone(Unit* pVictim, SpellEntry const* spellProto, u { SpellEquippedItemsEntry const* spellEquip = (*i)->GetSpellProto()->GetSpellEquippedItems(); if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) && - spellEquip && spellEquip->EquippedItemClass == -1 && + (!spellEquip || spellEquip->EquippedItemClass == -1 && // -1 == any item class (not wand then) - spellEquip->EquippedItemInventoryTypeMask == 0 ) + spellEquip->EquippedItemInventoryTypeMask == 0)) // 0 == any inventory type (not wand then) { DoneTotalMod *= ((*i)->GetModifier()->m_amount + 100.0f) / 100.0f; @@ -6943,8 +6943,8 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) { SpellEquippedItemsEntry const* spellEquip = (*i)->GetSpellProto()->GetSpellEquippedItems(); if (((*i)->GetModifier()->m_miscvalue & schoolMask) != 0 && - spellEquip && spellEquip->EquippedItemClass == -1 && // -1 == any item class (not wand then) - spellEquip->EquippedItemInventoryTypeMask == 0) // 0 == any inventory type (not wand then) + (!spellEquip || spellEquip->EquippedItemClass == -1 && // -1 == any item class (not wand then) + spellEquip->EquippedItemInventoryTypeMask == 0)) // 0 == any inventory type (not wand then) DoneAdvertisedBenefit += (*i)->GetModifier()->m_amount; } diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 3ec0531bf..61082ba50 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "12545" + #define REVISION_NR "12546" #endif // __REVISION_NR_H__