diff options
Diffstat (limited to 'src/game/SpellEffects.cpp')
-rw-r--r-- | src/game/SpellEffects.cpp | 6090 |
1 files changed, 6090 insertions, 0 deletions
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp new file mode 100644 index 00000000000..fa037fcdb24 --- /dev/null +++ b/src/game/SpellEffects.cpp @@ -0,0 +1,6090 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "SharedDefines.h" +#include "Database/DatabaseEnv.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "Log.h" +#include "UpdateMask.h" +#include "World.h" +#include "ObjectMgr.h" +#include "SpellMgr.h" +#include "Player.h" +#include "SkillExtraItems.h" +#include "Unit.h" +#include "CreatureAI.h" +#include "Spell.h" +#include "DynamicObject.h" +#include "SpellAuras.h" +#include "Group.h" +#include "UpdateData.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "SharedDefines.h" +#include "Pet.h" +#include "GameObject.h" +#include "GossipDef.h" +#include "Creature.h" +#include "Totem.h" +#include "CreatureAI.h" +#include "BattleGround.h" +#include "BattleGroundEY.h" +#include "BattleGroundWS.h" +#include "VMapFactory.h" +#include "Language.h" +#include "SocialMgr.h" +#include "Util.h" + +pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= +{ + &Spell::EffectNULL, // 0 + &Spell::EffectInstaKill, // 1 SPELL_EFFECT_INSTAKILL + &Spell::EffectSchoolDMG, // 2 SPELL_EFFECT_SCHOOL_DAMAGE + &Spell::EffectDummy, // 3 SPELL_EFFECT_DUMMY + &Spell::EffectUnused, // 4 SPELL_EFFECT_PORTAL_TELEPORT unused + &Spell::EffectTeleportUnits, // 5 SPELL_EFFECT_TELEPORT_UNITS + &Spell::EffectApplyAura, // 6 SPELL_EFFECT_APPLY_AURA + &Spell::EffectEnvirinmentalDMG, // 7 SPELL_EFFECT_ENVIRONMENTAL_DAMAGE + &Spell::EffectPowerDrain, // 8 SPELL_EFFECT_POWER_DRAIN + &Spell::EffectHealthLeech, // 9 SPELL_EFFECT_HEALTH_LEECH + &Spell::EffectHeal, // 10 SPELL_EFFECT_HEAL + &Spell::EffectUnused, // 11 SPELL_EFFECT_BIND + &Spell::EffectNULL, // 12 SPELL_EFFECT_PORTAL + &Spell::EffectUnused, // 13 SPELL_EFFECT_RITUAL_BASE unused + &Spell::EffectUnused, // 14 SPELL_EFFECT_RITUAL_SPECIALIZE unused + &Spell::EffectUnused, // 15 SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL unused + &Spell::EffectQuestComplete, // 16 SPELL_EFFECT_QUEST_COMPLETE + &Spell::EffectWeaponDmg, // 17 SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL + &Spell::EffectResurrect, // 18 SPELL_EFFECT_RESURRECT + &Spell::EffectAddExtraAttacks, // 19 SPELL_EFFECT_ADD_EXTRA_ATTACKS + &Spell::EffectUnused, // 20 SPELL_EFFECT_DODGE one spell: Dodge + &Spell::EffectUnused, // 21 SPELL_EFFECT_EVADE one spell: Evade (DND) + &Spell::EffectParry, // 22 SPELL_EFFECT_PARRY + &Spell::EffectUnused, // 23 SPELL_EFFECT_BLOCK one spell: Block + &Spell::EffectCreateItem, // 24 SPELL_EFFECT_CREATE_ITEM + &Spell::EffectUnused, // 25 SPELL_EFFECT_WEAPON + &Spell::EffectUnused, // 26 SPELL_EFFECT_DEFENSE one spell: Defense + &Spell::EffectPersistentAA, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA + &Spell::EffectSummonType, // 28 SPELL_EFFECT_SUMMON + &Spell::EffectMomentMove, // 29 SPELL_EFFECT_LEAP + &Spell::EffectEnergize, // 30 SPELL_EFFECT_ENERGIZE + &Spell::EffectWeaponDmg, // 31 SPELL_EFFECT_WEAPON_PERCENT_DAMAGE + &Spell::EffectTriggerMissileSpell, // 32 SPELL_EFFECT_TRIGGER_MISSILE + &Spell::EffectOpenLock, // 33 SPELL_EFFECT_OPEN_LOCK + &Spell::EffectSummonChangeItem, // 34 SPELL_EFFECT_SUMMON_CHANGE_ITEM + &Spell::EffectApplyAreaAura, // 35 SPELL_EFFECT_APPLY_AREA_AURA_PARTY + &Spell::EffectLearnSpell, // 36 SPELL_EFFECT_LEARN_SPELL + &Spell::EffectUnused, // 37 SPELL_EFFECT_SPELL_DEFENSE one spell: SPELLDEFENSE (DND) + &Spell::EffectDispel, // 38 SPELL_EFFECT_DISPEL + &Spell::EffectUnused, // 39 SPELL_EFFECT_LANGUAGE + &Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD + &Spell::EffectSummonWild, // 41 SPELL_EFFECT_SUMMON_WILD + &Spell::EffectSummonGuardian, // 42 SPELL_EFFECT_SUMMON_GUARDIAN + &Spell::EffectTeleUnitsFaceCaster, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER + &Spell::EffectLearnSkill, // 44 SPELL_EFFECT_SKILL_STEP + &Spell::EffectAddHonor, // 45 SPELL_EFFECT_ADD_HONOR honor/pvp related + &Spell::EffectNULL, // 46 SPELL_EFFECT_SPAWN we must spawn pet there + &Spell::EffectTradeSkill, // 47 SPELL_EFFECT_TRADE_SKILL + &Spell::EffectUnused, // 48 SPELL_EFFECT_STEALTH one spell: Base Stealth + &Spell::EffectUnused, // 49 SPELL_EFFECT_DETECT one spell: Detect + &Spell::EffectTransmitted, // 50 SPELL_EFFECT_TRANS_DOOR + &Spell::EffectUnused, // 51 SPELL_EFFECT_FORCE_CRITICAL_HIT unused + &Spell::EffectUnused, // 52 SPELL_EFFECT_GUARANTEE_HIT one spell: zzOLDCritical Shot + &Spell::EffectEnchantItemPerm, // 53 SPELL_EFFECT_ENCHANT_ITEM + &Spell::EffectEnchantItemTmp, // 54 SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY + &Spell::EffectTameCreature, // 55 SPELL_EFFECT_TAMECREATURE + &Spell::EffectSummonPet, // 56 SPELL_EFFECT_SUMMON_PET + &Spell::EffectLearnPetSpell, // 57 SPELL_EFFECT_LEARN_PET_SPELL + &Spell::EffectWeaponDmg, // 58 SPELL_EFFECT_WEAPON_DAMAGE + &Spell::EffectOpenSecretSafe, // 59 SPELL_EFFECT_OPEN_LOCK_ITEM + &Spell::EffectProficiency, // 60 SPELL_EFFECT_PROFICIENCY + &Spell::EffectSendEvent, // 61 SPELL_EFFECT_SEND_EVENT + &Spell::EffectPowerBurn, // 62 SPELL_EFFECT_POWER_BURN + &Spell::EffectThreat, // 63 SPELL_EFFECT_THREAT + &Spell::EffectTriggerSpell, // 64 SPELL_EFFECT_TRIGGER_SPELL + &Spell::EffectUnused, // 65 SPELL_EFFECT_HEALTH_FUNNEL unused + &Spell::EffectUnused, // 66 SPELL_EFFECT_POWER_FUNNEL unused + &Spell::EffectHealMaxHealth, // 67 SPELL_EFFECT_HEAL_MAX_HEALTH + &Spell::EffectInterruptCast, // 68 SPELL_EFFECT_INTERRUPT_CAST + &Spell::EffectDistract, // 69 SPELL_EFFECT_DISTRACT + &Spell::EffectPull, // 70 SPELL_EFFECT_PULL one spell: Distract Move + &Spell::EffectPickPocket, // 71 SPELL_EFFECT_PICKPOCKET + &Spell::EffectAddFarsight, // 72 SPELL_EFFECT_ADD_FARSIGHT + &Spell::EffectSummonGuardian, // 73 SPELL_EFFECT_SUMMON_POSSESSED + &Spell::EffectSummonTotem, // 74 SPELL_EFFECT_SUMMON_TOTEM + &Spell::EffectHealMechanical, // 75 SPELL_EFFECT_HEAL_MECHANICAL one spell: Mechanical Patch Kit + &Spell::EffectSummonObjectWild, // 76 SPELL_EFFECT_SUMMON_OBJECT_WILD + &Spell::EffectScriptEffect, // 77 SPELL_EFFECT_SCRIPT_EFFECT + &Spell::EffectUnused, // 78 SPELL_EFFECT_ATTACK + &Spell::EffectSanctuary, // 79 SPELL_EFFECT_SANCTUARY + &Spell::EffectAddComboPoints, // 80 SPELL_EFFECT_ADD_COMBO_POINTS + &Spell::EffectUnused, // 81 SPELL_EFFECT_CREATE_HOUSE one spell: Create House (TEST) + &Spell::EffectNULL, // 82 SPELL_EFFECT_BIND_SIGHT + &Spell::EffectDuel, // 83 SPELL_EFFECT_DUEL + &Spell::EffectStuck, // 84 SPELL_EFFECT_STUCK + &Spell::EffectSummonPlayer, // 85 SPELL_EFFECT_SUMMON_PLAYER + &Spell::EffectActivateObject, // 86 SPELL_EFFECT_ACTIVATE_OBJECT + &Spell::EffectSummonTotem, // 87 SPELL_EFFECT_SUMMON_TOTEM_SLOT1 + &Spell::EffectSummonTotem, // 88 SPELL_EFFECT_SUMMON_TOTEM_SLOT2 + &Spell::EffectSummonTotem, // 89 SPELL_EFFECT_SUMMON_TOTEM_SLOT3 + &Spell::EffectSummonTotem, // 90 SPELL_EFFECT_SUMMON_TOTEM_SLOT4 + &Spell::EffectUnused, // 91 SPELL_EFFECT_THREAT_ALL one spell: zzOLDBrainwash + &Spell::EffectEnchantHeldItem, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM + &Spell::EffectUnused, // 93 SPELL_EFFECT_SUMMON_PHANTASM + &Spell::EffectSelfResurrect, // 94 SPELL_EFFECT_SELF_RESURRECT + &Spell::EffectSkinning, // 95 SPELL_EFFECT_SKINNING + &Spell::EffectCharge, // 96 SPELL_EFFECT_CHARGE + &Spell::EffectSummonCritter, // 97 SPELL_EFFECT_SUMMON_CRITTER + &Spell::EffectKnockBack, // 98 SPELL_EFFECT_KNOCK_BACK + &Spell::EffectDisEnchant, // 99 SPELL_EFFECT_DISENCHANT + &Spell::EffectInebriate, //100 SPELL_EFFECT_INEBRIATE + &Spell::EffectFeedPet, //101 SPELL_EFFECT_FEED_PET + &Spell::EffectDismissPet, //102 SPELL_EFFECT_DISMISS_PET + &Spell::EffectReputation, //103 SPELL_EFFECT_REPUTATION + &Spell::EffectSummonObject, //104 SPELL_EFFECT_SUMMON_OBJECT_SLOT1 + &Spell::EffectSummonObject, //105 SPELL_EFFECT_SUMMON_OBJECT_SLOT2 + &Spell::EffectSummonObject, //106 SPELL_EFFECT_SUMMON_OBJECT_SLOT3 + &Spell::EffectSummonObject, //107 SPELL_EFFECT_SUMMON_OBJECT_SLOT4 + &Spell::EffectDispelMechanic, //108 SPELL_EFFECT_DISPEL_MECHANIC + &Spell::EffectSummonDeadPet, //109 SPELL_EFFECT_SUMMON_DEAD_PET + &Spell::EffectDestroyAllTotems, //110 SPELL_EFFECT_DESTROY_ALL_TOTEMS + &Spell::EffectDurabilityDamage, //111 SPELL_EFFECT_DURABILITY_DAMAGE + &Spell::EffectSummonDemon, //112 SPELL_EFFECT_SUMMON_DEMON + &Spell::EffectResurrectNew, //113 SPELL_EFFECT_RESURRECT_NEW + &Spell::EffectTaunt, //114 SPELL_EFFECT_ATTACK_ME + &Spell::EffectDurabilityDamagePCT, //115 SPELL_EFFECT_DURABILITY_DAMAGE_PCT + &Spell::EffectSkinPlayerCorpse, //116 SPELL_EFFECT_SKIN_PLAYER_CORPSE one spell: Remove Insignia, bg usage, required special corpse flags... + &Spell::EffectSpiritHeal, //117 SPELL_EFFECT_SPIRIT_HEAL one spell: Spirit Heal + &Spell::EffectSkill, //118 SPELL_EFFECT_SKILL professions and more + &Spell::EffectApplyAreaAura, //119 SPELL_EFFECT_APPLY_AREA_AURA_PET + &Spell::EffectUnused, //120 SPELL_EFFECT_TELEPORT_GRAVEYARD one spell: Graveyard Teleport Test + &Spell::EffectWeaponDmg, //121 SPELL_EFFECT_NORMALIZED_WEAPON_DMG + &Spell::EffectUnused, //122 SPELL_EFFECT_122 unused + &Spell::EffectSendTaxi, //123 SPELL_EFFECT_SEND_TAXI taxi/flight related (misc value is taxi path id) + &Spell::EffectPlayerPull, //124 SPELL_EFFECT_PLAYER_PULL opposite of knockback effect (pulls player twoard caster) + &Spell::EffectModifyThreatPercent, //125 SPELL_EFFECT_MODIFY_THREAT_PERCENT + &Spell::EffectStealBeneficialBuff, //126 SPELL_EFFECT_STEAL_BENEFICIAL_BUFF spell steal effect? + &Spell::EffectProspecting, //127 SPELL_EFFECT_PROSPECTING Prospecting spell + &Spell::EffectApplyAreaAura, //128 SPELL_EFFECT_APPLY_AREA_AURA_FRIEND + &Spell::EffectApplyAreaAura, //129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY + &Spell::EffectNULL, //130 SPELL_EFFECT_REDIRECT_THREAT + &Spell::EffectUnused, //131 SPELL_EFFECT_131 used in some test spells + &Spell::EffectNULL, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value + &Spell::EffectUnlearnSpecialization, //133 SPELL_EFFECT_UNLEARN_SPECIALIZATION unlearn profession specialization + &Spell::EffectKillCredit, //134 SPELL_EFFECT_KILL_CREDIT misc value is creature entry + &Spell::EffectNULL, //135 SPELL_EFFECT_CALL_PET + &Spell::EffectHealPct, //136 SPELL_EFFECT_HEAL_PCT + &Spell::EffectEnergisePct, //137 SPELL_EFFECT_ENERGIZE_PCT + &Spell::EffectNULL, //138 SPELL_EFFECT_138 Leap + &Spell::EffectUnused, //139 SPELL_EFFECT_139 unused + &Spell::EffectForceCast, //140 SPELL_EFFECT_FORCE_CAST + &Spell::EffectNULL, //141 SPELL_EFFECT_141 damage and reduce speed? + &Spell::EffectTriggerSpellWithValue, //142 SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE + &Spell::EffectApplyAreaAura, //143 SPELL_EFFECT_APPLY_AREA_AURA_OWNER + &Spell::EffectNULL, //144 SPELL_EFFECT_144 Spectral Blast + &Spell::EffectNULL, //145 SPELL_EFFECT_145 Black Hole Effect + &Spell::EffectUnused, //146 SPELL_EFFECT_146 unused + &Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail + &Spell::EffectUnused, //148 SPELL_EFFECT_148 unused + &Spell::EffectNULL, //149 SPELL_EFFECT_149 swoop + &Spell::EffectUnused, //150 SPELL_EFFECT_150 unused + &Spell::EffectTriggerRitualOfSummoning, //151 SPELL_EFFECT_TRIGGER_SPELL_2 + &Spell::EffectNULL, //152 SPELL_EFFECT_152 summon Refer-a-Friend + &Spell::EffectNULL, //153 SPELL_EFFECT_CREATE_PET misc value is creature entry +}; + +void Spell::EffectNULL(uint32 /*i*/) +{ + sLog.outDebug("WORLD: Spell Effect DUMMY"); +} + +void Spell::EffectUnused(uint32 /*i*/) +{ + // NOT USED BY ANY SPELL OR USELESS OR IMPLEMENTED IN DIFFERENT WAY IN MANGOS +} + +void Spell::EffectResurrectNew(uint32 i) +{ + if(!unitTarget || unitTarget->isAlive()) + return; + + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + if(!unitTarget->IsInWorld()) + return; + + Player* pTarget = ((Player*)unitTarget); + + if(pTarget->isRessurectRequested()) // already have one active request + return; + + uint32 health = damage; + uint32 mana = m_spellInfo->EffectMiscValue[i]; + pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana); + SendResurrectRequest(pTarget); +} + +void Spell::EffectInstaKill(uint32 /*i*/) +{ + if( !unitTarget || !unitTarget->isAlive() ) + return; + + // Demonic Sacrifice + if(m_spellInfo->Id==18788 && unitTarget->GetTypeId()==TYPEID_UNIT) + { + uint32 entry = unitTarget->GetEntry(); + uint32 spellID; + switch(entry) + { + case 416: spellID=18789; break; //imp + case 417: spellID=18792; break; //fellhunter + case 1860: spellID=18790; break; //void + case 1863: spellID=18791; break; //succubus + case 17252: spellID=35701; break; //fellguard + default: + sLog.outError("EffectInstaKill: Unhandled creature entry (%u) case.",entry); + return; + } + + m_caster->CastSpell(m_caster,spellID,true); + } + + if(m_caster==unitTarget) // prevent interrupt message + finish(); + + uint32 health = unitTarget->GetHealth(); + m_caster->DealDamage(unitTarget, health, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); +} + +void Spell::EffectEnvirinmentalDMG(uint32 i) +{ + uint32 absorb = 0; + uint32 resist = 0; + + // 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 not existed GO casting support + // currently each enemy selected explicitly and self cast damage, we prevent apply self casted spell bonuses/etc + damage = m_spellInfo->EffectBasePoints[i]+m_spellInfo->EffectBaseDice[i]; + + m_caster->CalcAbsorbResist(m_caster,GetSpellSchoolMask(m_spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist); + + m_caster->SendSpellNonMeleeDamageLog(m_caster, m_spellInfo->Id, damage, GetSpellSchoolMask(m_spellInfo), absorb, resist, false, 0, false); + if(m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->EnvironmentalDamage(m_caster->GetGUID(),DAMAGE_FIRE,damage); +} + +void Spell::EffectSchoolDMG(uint32 effect_idx) +{ + if( unitTarget && unitTarget->isAlive()) + { + switch(m_spellInfo->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + { + //Gore + if(m_spellInfo->SpellIconID == 2269 ) + { + damage+= rand()%2 ? damage : 0; + } + + switch(m_spellInfo->Id) // better way to check unknown + { + // Meteor like spells (divided damage to targets) + case 24340: case 26558: case 28884: // Meteor + case 36837: case 38903: case 41276: // Meteor + case 26789: // Shard of the Fallen Star + case 31436: // Malevolent Cleave + case 35181: // Dive Bomb + case 40810: case 43267: case 43268: // Saber Lash + case 42384: // Brutal Swipe + case 45150: // Meteor Slash + { + uint32 count = 0; + for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + if(ihit->effectMask & (1<<effect_idx)) + ++count; + + damage /= count; // divide to all targets + break; + } + // percent from health with min + case 25599: // Thundercrash + { + damage = unitTarget->GetHealth() / 2; + if(damage < 200) + damage = 200; + break; + } + } + break; + } + + case SPELLFAMILY_MAGE: + { + // Arcane Blast + if(m_spellInfo->SpellFamilyFlags & 0x20000000LL) + { + m_caster->CastSpell(m_caster,36032,true); + } + break; + } + case SPELLFAMILY_WARRIOR: + { + // Bloodthirst + if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL) + { + damage = uint32(damage * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK)) / 100); + } + // Shield Slam + else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL) + damage += int32(m_caster->GetShieldBlockValue()); + // Victory Rush + else if(m_spellInfo->SpellFamilyFlags & 0x10000000000LL) + { + damage = uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); + m_caster->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, false); + } + break; + } + case SPELLFAMILY_WARLOCK: + { + // Incinerate Rank 1 & 2 + if((m_spellInfo->SpellFamilyFlags & 0x00004000000000LL) && m_spellInfo->SpellIconID==2128) + { + // Incinerate does more dmg (dmg*0.25) if the target is Immolated. + if(unitTarget->HasAuraState(AURA_STATE_IMMOLATE)) + damage += int32(damage*0.25); + } + break; + } + case SPELLFAMILY_DRUID: + { + // Ferocious Bite + if((m_spellInfo->SpellFamilyFlags & 0x000800000) && m_spellInfo->SpellVisual==6587) + { + // converts each extra point of energy into ($f1+$AP/630) additional damage + float multiple = m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 630 + m_spellInfo->DmgMultiplier[effect_idx]; + damage += int32(m_caster->GetPower(POWER_ENERGY) * multiple); + m_caster->SetPower(POWER_ENERGY,0); + } + // Rake + else if(m_spellInfo->SpellFamilyFlags & 0x0000000000001000LL) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100); + } + // Swipe + else if(m_spellInfo->SpellFamilyFlags & 0x0010000000000000LL) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.08f); + } + // Starfire + else if ( m_spellInfo->SpellFamilyFlags & 0x0004LL ) + { + Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i) + { + // Starfire Bonus (caster) + switch((*i)->GetModifier()->m_miscvalue) + { + case 5481: // Nordrassil Regalia - bonus + { + Unit::AuraList const& m_periodicDamageAuras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + for(Unit::AuraList::const_iterator itr = m_periodicDamageAuras.begin(); itr != m_periodicDamageAuras.end(); ++itr) + { + // Moonfire or Insect Swarm (target debuff from any casters) + if ( (*itr)->GetSpellProto()->SpellFamilyFlags & 0x00200002LL ) + { + int32 mod = (*i)->GetModifier()->m_amount; + damage += damage*mod/100; + break; + } + } + break; + } + case 5148: //Improved Starfire - Ivory Idol of the Moongoddes Aura + { + damage += (*i)->GetModifier()->m_amount; + break; + } + } + } + } + //Mangle Bonus for the initial damage of Lacerate and Rake + if((m_spellInfo->SpellFamilyFlags==0x0000000000001000LL && m_spellInfo->SpellIconID==494) || + (m_spellInfo->SpellFamilyFlags==0x0000010000000000LL && m_spellInfo->SpellIconID==2246)) + { + Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i) + if((*i)->GetSpellProto()->SpellFamilyFlags & 0x0000044000000000LL && (*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID) + { + damage = int32(damage*(100.0f+(*i)->GetModifier()->m_amount)/100.0f); + break; + } + } + break; + } + case SPELLFAMILY_ROGUE: + { + // Envenom + if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x800000000LL)) + { + // consume from stack dozes not more that have combo-points + if(uint32 combo = ((Player*)m_caster)->GetComboPoints()) + { + // count consumed deadly poison doses at target + uint32 doses = 0; + + // remove consumed poison doses + Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end() && combo;) + { + // Deadly poison (only attacker applied) + if( (*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && ((*itr)->GetSpellProto()->SpellFamilyFlags & 0x10000) && + (*itr)->GetSpellProto()->SpellVisual==5100 && (*itr)->GetCasterGUID()==m_caster->GetGUID() ) + { + --combo; + ++doses; + + unitTarget->RemoveSingleAuraFromStack((*itr)->GetId(), (*itr)->GetEffIndex()); + + itr = auras.begin(); + } + else + ++itr; + } + + damage *= doses; + damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.03f * doses); + + // Eviscerate and Envenom Bonus Damage (item set effect) + if(m_caster->GetDummyAura(37169)) + damage += ((Player*)m_caster)->GetComboPoints()*40; + } + } + // Eviscerate + else if((m_spellInfo->SpellFamilyFlags & 0x00020000LL) && m_caster->GetTypeId()==TYPEID_PLAYER) + { + if(uint32 combo = ((Player*)m_caster)->GetComboPoints()) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f); + + // Eviscerate and Envenom Bonus Damage (item set effect) + if(m_caster->GetDummyAura(37169)) + damage += combo*40; + } + } + break; + } + case SPELLFAMILY_HUNTER: + { + // Mongoose Bite + if((m_spellInfo->SpellFamilyFlags & 0x000000002) && m_spellInfo->SpellVisual==342) + { + damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2); + } + // Arcane Shot + else if((m_spellInfo->SpellFamilyFlags & 0x00000800) && m_spellInfo->maxLevel > 0) + { + damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.15); + } + // Steady Shot + else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL) + { + int32 base = irand((int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE),(int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE)); + damage += int32(float(base)/m_caster->GetAttackTime(RANGED_ATTACK)*2800 + m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.2f); + } + //Explosive Trap Effect + else if(m_spellInfo->SpellFamilyFlags & 0x00000004) + { + damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1); + } + break; + } + case SPELLFAMILY_PALADIN: + { + //Judgement of Vengeance + if((m_spellInfo->SpellFamilyFlags & 0x800000000LL) && m_spellInfo->SpellIconID==2292) + { + uint32 stacks = 0; + Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE); + for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) + if((*itr)->GetId() == 31803 && (*itr)->GetCasterGUID()==m_caster->GetGUID()) + ++stacks; + if(!stacks) + //No damage if the target isn't affected by this + damage = -1; + else + damage *= stacks; + } + break; + } + } + + if(damage >= 0) + { + uint32 finalDamage; + if(m_originalCaster) // m_caster only passive source of cast + finalDamage = m_originalCaster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true); + else + finalDamage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true); + + // post effects + switch(m_spellInfo->SpellFamilyName) + { + case SPELLFAMILY_WARRIOR: + { + // Bloodthirst + if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL) + { + uint32 BTAura = 0; + switch(m_spellInfo->Id) + { + case 23881: BTAura = 23885; break; + case 23892: BTAura = 23886; break; + case 23893: BTAura = 23887; break; + case 23894: BTAura = 23888; break; + case 25251: BTAura = 25252; break; + case 30335: BTAura = 30339; break; + default: + sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id); + break; + } + + if (BTAura) + m_caster->CastSpell(m_caster,BTAura,true); + } + break; + } + case SPELLFAMILY_PRIEST: + { + // Shadow Word: Death + if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000200000000LL) && unitTarget->isAlive()) + // deals damage equal to damage done to caster if victim is not killed + m_caster->SpellNonMeleeDamageLog( m_caster, m_spellInfo->Id, finalDamage, m_IsTriggeredSpell, false); + + break; + } + case SPELLFAMILY_PALADIN: + { + // Judgement of Blood + if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL) && m_spellInfo->SpellIconID==153) + { + int32 damagePoint = finalDamage * 33 / 100; + m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true); + } + break; + } + } + } + } +} + +void Spell::EffectDummy(uint32 i) +{ + if(!unitTarget && !gameObjTarget && !itemTarget) + return; + + // selection by spell family + switch(m_spellInfo->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + // Gnomish Poultryizer trinket + switch(m_spellInfo->Id ) + { + case 8063: // Deviate Fish + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + uint32 spell_id = 0; + switch(urand(1,5)) + { + case 1: spell_id = 8064; break; // Sleepy + case 2: spell_id = 8065; break; // Invigorate + case 3: spell_id = 8066; break; // Shrink + case 4: spell_id = 8067; break; // Party Time! + case 5: spell_id = 8068; break; // Healthy Spirit + } + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } + case 8213: // Savory Deviate Delight + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + uint32 spell_id = 0; + switch(urand(1,2)) + { + // Flip Out - ninja + case 1: spell_id = (m_caster->getGender() == GENDER_MALE ? 8219 : 8220); break; + // Yaaarrrr - pirate + case 2: spell_id = (m_caster->getGender() == GENDER_MALE ? 8221 : 8222); break; + } + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } + case 8593: // Symbol of life (restore creature to life) + case 31225: // Shimmering Vessel (restore creature to life) + { + if(!unitTarget || unitTarget->GetTypeId()!=TYPEID_UNIT) + return; + ((Creature*)unitTarget)->setDeathState(JUST_ALIVED); + return; + } + case 12162: // Deep wounds + case 12850: // (now good common check for this spells) + case 12868: + { + if(!unitTarget) + 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; + else + damage = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE))/2; + + switch (m_spellInfo->Id) + { + case 12850: damage *= 0.2f; break; + case 12162: damage *= 0.4f; break; + case 12868: damage *= 0.6f; break; + default: + sLog.outError("Spell::EffectDummy: Spell %u not handled in DW",m_spellInfo->Id); + return; + }; + + int32 deepWoundsDotBasePoints0 = int32(damage / 4); + m_caster->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, NULL, NULL, true, NULL); + return; + } + case 12975: //Last Stand + { + int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth()*0.3); + m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, NULL, NULL, true, NULL); + return; + } + case 13120: // net-o-matic + { + if(!unitTarget) + return; + + uint32 spell_id = 0; + + uint32 roll = urand(0, 99); + + if(roll < 2) // 2% for 30 sec self root (off-like chance unknown) + spell_id = 16566; + else if(roll < 4) // 2% for 20 sec root, charge to target (off-like chance unknown) + spell_id = 13119; + else // normal root + spell_id = 13099; + + m_caster->CastSpell(unitTarget,spell_id,true,NULL); + return; + } + case 13567: // Dummy Trigger + { + // can be used for different aura triggreing, so select by aura + if(!m_triggeredByAuraSpell || !unitTarget) + return; + + switch(m_triggeredByAuraSpell->Id) + { + case 26467: // Persistent Shield + m_caster->CastCustomSpell(unitTarget, 26470, &damage, NULL, NULL, true); + break; + default: + sLog.outError("EffectDummy: Non-handled case for spell 13567 for triggered aura %u",m_triggeredByAuraSpell->Id); + break; + } + return; + } + case 14185: // Preparation Rogue + { + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + //immediately finishes the cooldown on certain Rogue abilities + const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); + for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + { + uint32 classspell = itr->first; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + + if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & 0x26000000860LL)) + { + ((Player*)m_caster)->RemoveSpellCooldown(classspell); + + WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); + data << uint32(classspell); + data << uint64(m_caster->GetGUID()); + ((Player*)m_caster)->GetSession()->SendPacket(&data); + } + } + return; + } + case 15998: // Capture Worg Pup + case 29435: // Capture Female Kaliri Hatchling + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) + return; + + Creature* creatureTarget = (Creature*)unitTarget; + creatureTarget->setDeathState(JUST_DIED); + creatureTarget->RemoveCorpse(); + creatureTarget->SetHealth(0); // just for nice GM-mode view + return; + } + case 16589: // Noggenfogger Elixir + { + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + uint32 spell_id = 0; + switch(urand(1,3)) + { + case 1: spell_id = 16595; break; + case 2: spell_id = 16593; break; + default:spell_id = 16591; break; + } + + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } + case 17251: // Spirit Healer Res + { + if(!unitTarget || !m_originalCaster) + return; + + if(m_originalCaster->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8); + data << unitTarget->GetGUID(); + ((Player*)m_originalCaster)->GetSession()->SendPacket( &data ); + } + return; + } + case 17271: // Test Fetid Skull + { + if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + uint32 spell_id = roll_chance_i(50) ? 17269 : 17270; + + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } + case 20577: // Cannibalize + if (unitTarget) + m_caster->CastSpell(m_caster,20578,false,NULL); + return; + case 23019: // Crystal Prison Dummy DND + { + if(!unitTarget || !unitTarget->isAlive() || unitTarget->GetTypeId() != TYPEID_UNIT || ((Creature*)unitTarget)->isPet()) + return; + + Creature* creatureTarget = (Creature*)unitTarget; + if(creatureTarget->isPet()) + return; + + creatureTarget->setDeathState(JUST_DIED); + creatureTarget->RemoveCorpse(); + creatureTarget->SetHealth(0); // just for nice GM-mode view + + GameObject* pGameObj = new GameObject; + + Map *map = creatureTarget->GetMap(); + + if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 179644, map, + creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), + creatureTarget->GetOrientation(), 0, 0, 0, 0, 100, 1) ) + { + delete pGameObj; + return; + } + + pGameObj->SetRespawnTime(creatureTarget->GetRespawnTime()-time(NULL)); + pGameObj->SetOwnerGUID(m_caster->GetGUID() ); + pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() ); + pGameObj->SetSpellId(m_spellInfo->Id); + + DEBUG_LOG("AddObject at SpellEfects.cpp EffectDummy\n"); + map->Add(pGameObj); + + WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8); + data << uint64(pGameObj->GetGUID()); + m_caster->SendMessageToSet(&data,true); + + return; + } + case 23074: // Arc. Dragonling + if (!m_CastItem) return; + m_caster->CastSpell(m_caster,19804,true,m_CastItem); + return; + case 23075: // Mithril Mechanical Dragonling + if (!m_CastItem) return; + m_caster->CastSpell(m_caster,12749,true,m_CastItem); + return; + case 23076: // Mechanical Dragonling + if (!m_CastItem) return; + m_caster->CastSpell(m_caster,4073,true,m_CastItem); + return; + case 23133: // Gnomish Battle Chicken + if (!m_CastItem) return; + m_caster->CastSpell(m_caster,13166,true,m_CastItem); + return; + case 23448: // Ultrasafe Transporter: Gadgetzan - backfires + { + int32 r = irand(0, 119); + if ( r < 20 ) // 1/6 polymorph + m_caster->CastSpell(m_caster,23444,true); + else if ( r < 100 ) // 4/6 evil twin + m_caster->CastSpell(m_caster,23445,true); + else // 1/6 miss the target + m_caster->CastSpell(m_caster,36902,true); + return; + } + case 23453: // Ultrasafe Transporter: Gadgetzan + if ( roll_chance_i(50) ) // success + m_caster->CastSpell(m_caster,23441,true); + else // failure + m_caster->CastSpell(m_caster,23446,true); + return; + case 23645: // Hourglass Sand + m_caster->RemoveAurasDueToSpell(23170); + return; + case 23725: // Gift of Life (warrior bwl trinket) + m_caster->CastSpell(m_caster,23782,true); + m_caster->CastSpell(m_caster,23783,true); + return; + case 25860: // Reindeer Transformation + { + if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED)) + return; + + float flyspeed = m_caster->GetSpeedRate(MOVE_FLY); + float speed = m_caster->GetSpeedRate(MOVE_RUN); + + m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + + //5 different spells used depending on mounted speed and if mount can fly or not + if (flyspeed >= 4.1f) + m_caster->CastSpell(m_caster, 44827, true); //310% flying Reindeer + else if (flyspeed >= 3.8f) + m_caster->CastSpell(m_caster, 44825, true); //280% flying Reindeer + else if (flyspeed >= 1.6f) + m_caster->CastSpell(m_caster, 44824, true); //60% flying Reindeer + else if (speed >= 2.0f) + m_caster->CastSpell(m_caster, 25859, true); //100% ground Reindeer + else + m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer + + return; + } + //case 26074: // Holiday Cheer + // return; -- implemented at client side + case 28006: // Arcane Cloaking + { + if( unitTarget->GetTypeId() == TYPEID_PLAYER ) + m_caster->CastSpell(unitTarget,29294,true); + return; + } + case 28730: // Arcane Torrent (Mana) + { + int32 count = 0; + Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i) + if ((*i)->GetId() == 28734) + ++count; + if (count) + { + m_caster->RemoveAurasDueToSpell(28734); + int32 bp = damage * count; + m_caster->CastCustomSpell(m_caster, 28733, &bp, NULL, NULL, true); + } + return; + } + case 29200: // Purify Helboar Meat + { + if( m_caster->GetTypeId() != TYPEID_PLAYER ) + return; + + uint32 spell_id = roll_chance_i(50) ? 29277 : 29278; + + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } + case 29858: // Soulshatter + if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT && unitTarget->IsHostileTo(m_caster)) + m_caster->CastSpell(unitTarget,32835,true); + return; + case 30458: // Nigh Invulnerability + if (!m_CastItem) return; + if(roll_chance_i(86)) // success + m_caster->CastSpell(m_caster, 30456, true, m_CastItem); + else // backfire in 14% casts + m_caster->CastSpell(m_caster, 30457, true, m_CastItem); + return; + case 30507: // Poultryizer + if (!m_CastItem) return; + if(roll_chance_i(80)) // success + m_caster->CastSpell(unitTarget, 30501, true, m_CastItem); + else // backfire 20% + m_caster->CastSpell(unitTarget, 30504, true, m_CastItem); + return; + case 33060: // Make a Wish + { + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + uint32 spell_id = 0; + + switch(urand(1,5)) + { + case 1: spell_id = 33053; break; + case 2: spell_id = 33057; break; + case 3: spell_id = 33059; break; + case 4: spell_id = 33062; break; + case 5: spell_id = 33064; break; + } + + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } + case 35745: + { + uint32 spell_id; + switch(m_caster->GetAreaId()) + { + case 3900: spell_id = 35743; break; + case 3742: spell_id = 35744; break; + default: return; + } + + m_caster->CastSpell(m_caster,spell_id,true); + return; + } + case 37674: // Chaos Blast + if(unitTarget) + m_caster->CastSpell(unitTarget,37675,true); + return; + case 44875: // Complete Raptor Capture + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT) + return; + + Creature* creatureTarget = (Creature*)unitTarget; + + creatureTarget->setDeathState(JUST_DIED); + creatureTarget->RemoveCorpse(); + creatureTarget->SetHealth(0); // just for nice GM-mode view + + //cast spell Raptor Capture Credit + m_caster->CastSpell(m_caster,42337,true,NULL); + return; + } + case 45030: // Impale Emissary + { + // Emissary of Hate Credit + m_caster->CastSpell(m_caster,45088,true); + return; + } + case 50243: // Teach Language + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + // spell has a 1/3 chance to trigger one of the below + if(roll_chance_i(66)) + return; + if(((Player*)m_caster)->GetTeam() == ALLIANCE) + { + // 1000001 - gnomish binary + m_caster->CastSpell(m_caster, 50242, true); + } + else + { + // 01001000 - goblin binary + m_caster->CastSpell(m_caster, 50246, true); + } + + return; + } + case 51582: //Rocket Boots Engaged (Rocket Boots Xtreme and Rocket Boots Xtreme Lite) + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + if(BattleGround* bg = ((Player*)m_caster)->GetBattleGround()) + bg->EventPlayerDroppedFlag((Player*)m_caster); + + m_caster->CastSpell(m_caster, 30452, true, NULL); + return; + } + } + + //All IconID Check in there + switch(m_spellInfo->SpellIconID) + { + // Berserking (troll racial traits) + case 1661: + { + uint32 healthPerc = uint32((float(m_caster->GetHealth())/m_caster->GetMaxHealth())*100); + int32 melee_mod = 10; + if (healthPerc <= 40) + melee_mod = 30; + if (healthPerc < 100 && healthPerc > 40) + melee_mod = 10+(100-healthPerc)/3; + + int32 hasteModBasePoints0 = melee_mod; // (EffectBasePoints[0]+1)-1+(5-melee_mod) = (melee_mod-1+1)-1+5-melee_mod = 5-1 + int32 hasteModBasePoints1 = (5-melee_mod); + int32 hasteModBasePoints2 = 5; + + // 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,&hasteModBasePoints0,&hasteModBasePoints1,&hasteModBasePoints2,true,NULL); + return; + } + } + break; + case SPELLFAMILY_MAGE: + switch(m_spellInfo->Id ) + { + case 11958: // Cold Snap + { + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + // immediately finishes the cooldown on Frost spells + const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); + for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + { + if (itr->second->state == PLAYERSPELL_REMOVED) + continue; + + uint32 classspell = itr->first; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + + if( spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && + (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) && + spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0 ) + { + ((Player*)m_caster)->RemoveSpellCooldown(classspell); + + WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); + data << uint32(classspell); + data << uint64(m_caster->GetGUID()); + ((Player*)m_caster)->GetSession()->SendPacket(&data); + } + } + return; + } + } + break; + case SPELLFAMILY_WARRIOR: + // Charge + if(m_spellInfo->SpellFamilyFlags & 0x1 && m_spellInfo->SpellVisual == 867) + { + int32 chargeBasePoints0 = damage; + m_caster->CastCustomSpell(m_caster,34846,&chargeBasePoints0,NULL,NULL,true); + return; + } + // Execute + if(m_spellInfo->SpellFamilyFlags & 0x20000000) + { + if(!unitTarget) + return; + + int32 basePoints0 = damage+int32(m_caster->GetPower(POWER_RAGE) * m_spellInfo->DmgMultiplier[i]); + m_caster->CastCustomSpell(unitTarget, 20647, &basePoints0, NULL, NULL, true, 0); + m_caster->SetPower(POWER_RAGE,0); + return; + } + if(m_spellInfo->Id==21977) //Warrior's Wrath + { + if(!unitTarget) + return; + + m_caster->CastSpell(unitTarget,21887,true); // spell mod + return; + } + break; + case SPELLFAMILY_WARLOCK: + //Life Tap (only it have this with dummy effect) + if (m_spellInfo->SpellFamilyFlags == 0x40000) + { + float cost = m_currentBasePoints[0]+1; + + if(Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, cost,this); + + int32 dmg = m_caster->SpellDamageBonus(m_caster, m_spellInfo,uint32(cost > 0 ? cost : 0), SPELL_DIRECT_DAMAGE); + + if(int32(m_caster->GetHealth()) > dmg) + { + // Shouldn't Appear in Combat Log + m_caster->ModifyHealth(-dmg); + + int32 mana = dmg; + + Unit::AuraList const& auraDummy = m_caster->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 + if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 208) + mana = ((*itr)->GetModifier()->m_amount + 100)* mana / 100; + } + + m_caster->CastCustomSpell(m_caster,31818,&mana,NULL,NULL,true,NULL); + + // Mana Feed + int32 manaFeedVal = m_caster->CalculateSpellDamage(m_spellInfo,1, m_spellInfo->EffectBasePoints[1],m_caster); + manaFeedVal = manaFeedVal * mana / 100; + if(manaFeedVal > 0) + m_caster->CastCustomSpell(m_caster,32553,&manaFeedVal,NULL,NULL,true,NULL); + } + else + SendCastResult(SPELL_FAILED_FIZZLE); + return; + } + break; + case SPELLFAMILY_PRIEST: + switch(m_spellInfo->Id ) + { + case 28598: // Touch of Weakness triggered spell + { + if(!unitTarget || !m_triggeredByAuraSpell) + return; + + uint32 spellid = 0; + switch(m_triggeredByAuraSpell->Id) + { + case 2652: spellid = 2943; break; // Rank 1 + case 19261: spellid = 19249; break; // Rank 2 + case 19262: spellid = 19251; break; // Rank 3 + case 19264: spellid = 19252; break; // Rank 4 + case 19265: spellid = 19253; break; // Rank 5 + case 19266: spellid = 19254; break; // Rank 6 + case 25461: spellid = 25460; break; // Rank 7 + default: + sLog.outError("Spell::EffectDummy: Spell 28598 triggered by unhandeled spell %u",m_triggeredByAuraSpell->Id); + return; + } + m_caster->CastSpell(unitTarget, spellid, true, NULL); + return; + } + } + break; + case SPELLFAMILY_DRUID: + switch(m_spellInfo->Id ) + { + case 5420: // Tree of Life passive + { + // Tree of Life area effect + int32 health_mod = int32(m_caster->GetStat(STAT_SPIRIT)/4); + m_caster->CastCustomSpell(m_caster,34123,&health_mod,NULL,NULL,true,NULL); + return; + } + } + break; + case SPELLFAMILY_ROGUE: + switch(m_spellInfo->Id ) + { + case 31231: // Cheat Death + { + m_caster->CastSpell(m_caster,45182,true); + return; + } + case 5938: // Shiv + { + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *pCaster = ((Player*)m_caster); + + Item *item = pCaster->GetWeaponForAttack(OFF_ATTACK); + if(!item) + return; + + // all poison enchantments is temporary + uint32 enchant_id = item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT); + if(!enchant_id) + return; + + SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!pEnchant) + return; + + for (int s=0;s<3;s++) + { + if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL) + continue; + + SpellEntry const* combatEntry = sSpellStore.LookupEntry(pEnchant->spellid[s]); + if(!combatEntry || combatEntry->Dispel != DISPEL_POISON) + continue; + + m_caster->CastSpell(unitTarget, combatEntry, true, item); + } + + m_caster->CastSpell(unitTarget, 5940, true); + return; + } + } + break; + case SPELLFAMILY_HUNTER: + // Steady Shot + if(m_spellInfo->SpellFamilyFlags & 0x100000000LL) + { + if( !unitTarget || !unitTarget->isAlive()) + return; + + bool found = false; + + // check dazed affect + Unit::AuraList const& decSpeedList = unitTarget->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); + for(Unit::AuraList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter) + { + if((*iter)->GetSpellProto()->SpellIconID==15 && (*iter)->GetSpellProto()->Dispel==0) + { + found = true; + break; + } + } + + if(found) + m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true); + return; + } + // Kill command + if(m_spellInfo->SpellFamilyFlags & 0x00080000000000LL) + { + if(m_caster->getClass()!=CLASS_HUNTER) + return; + + // clear hunter crit aura state + m_caster->ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE,false); + + // additional damage from pet to pet target + Pet* pet = m_caster->GetPet(); + if(!pet || !pet->getVictim()) + return; + + uint32 spell_id = 0; + switch (m_spellInfo->Id) + { + case 34026: spell_id = 34027; break; // rank 1 + default: + sLog.outError("Spell::EffectDummy: Spell %u not handled in KC",m_spellInfo->Id); + return; + } + + pet->CastSpell(pet->getVictim(), spell_id, true); + return; + } + + switch(m_spellInfo->Id) + { + case 23989: //Readiness talent + { + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + //immediately finishes the cooldown for hunter abilities + const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap(); + for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + { + uint32 classspell = itr->first; + SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell); + + if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 ) + { + ((Player*)m_caster)->RemoveSpellCooldown(classspell); + + WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); + data << uint32(classspell); + data << uint64(m_caster->GetGUID()); + ((Player*)m_caster)->GetSession()->SendPacket(&data); + } + } + return; + } + case 37506: // Scatter Shot + { + if (m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + // break Auto Shot and autohit + m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + m_caster->AttackStop(); + ((Player*)m_caster)->SendAttackSwingCancelAttack(); + return; + } + } + break; + case SPELLFAMILY_PALADIN: + switch(m_spellInfo->SpellIconID) + { + case 156: // Holy Shock + { + if(!unitTarget) + return; + + int hurt = 0; + int heal = 0; + + switch(m_spellInfo->Id) + { + case 20473: hurt = 25912; heal = 25914; break; + case 20929: hurt = 25911; heal = 25913; break; + case 20930: hurt = 25902; heal = 25903; break; + case 27174: hurt = 27176; heal = 27175; break; + case 33072: hurt = 33073; heal = 33074; break; + default: + sLog.outError("Spell::EffectDummy: Spell %u not handled in HS",m_spellInfo->Id); + return; + } + + if(m_caster->IsFriendlyTo(unitTarget)) + m_caster->CastSpell(unitTarget, heal, true, 0); + else + m_caster->CastSpell(unitTarget, hurt, true, 0); + + return; + } + case 561: // Judgement of command + { + if(!unitTarget) + return; + + uint32 spell_id = m_currentBasePoints[i]+1; + SpellEntry const* spell_proto = sSpellStore.LookupEntry(spell_id); + if(!spell_proto) + return; + + if( !unitTarget->hasUnitState(UNIT_STAT_STUNDED) && m_caster->GetTypeId()==TYPEID_PLAYER) + { + // decreased damage (/2) for non-stunned target. + SpellModifier *mod = new SpellModifier; + mod->op = SPELLMOD_DAMAGE; + mod->value = -50; + mod->type = SPELLMOD_PCT; + mod->spellId = m_spellInfo->Id; + mod->effectId = i; + mod->lastAffected = NULL; + mod->mask = 0x0000020000000000LL; + mod->charges = 0; + + ((Player*)m_caster)->AddSpellMod(mod, true); + m_caster->CastSpell(unitTarget,spell_proto,true,NULL); + // mod deleted + ((Player*)m_caster)->AddSpellMod(mod, false); + } + else + m_caster->CastSpell(unitTarget,spell_proto,true,NULL); + + return; + } + } + + switch(m_spellInfo->Id) + { + case 31789: // Righteous Defense (step 1) + { + // 31989 -> dummy effect (step 1) + dummy effect (step 2) -> 31709 (taunt like spell for each target) + + // non-standard cast requirement check + if (!unitTarget || unitTarget->getAttackers().empty()) + { + // clear cooldown at fail + if(m_caster->GetTypeId()==TYPEID_PLAYER) + { + ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id); + + WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); + data << uint32(m_spellInfo->Id); + data << uint64(m_caster->GetGUID()); + ((Player*)m_caster)->GetSession()->SendPacket(&data); + } + + SendCastResult(SPELL_FAILED_TARGET_AFFECTING_COMBAT); + return; + } + + // Righteous Defense (step 2) (in old version 31980 dummy effect) + // Clear targets for eff 1 + for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + ihit->effectMask &= ~(1<<1); + + // not empty (checked) + Unit::AttackerSet const& attackers = unitTarget->getAttackers(); + + // chance to be selected from list + float chance = 100.0f/attackers.size(); + uint32 count=0; + for(Unit::AttackerSet::const_iterator aItr = attackers.begin(); aItr != attackers.end() && count < 3; ++aItr) + { + if(!roll_chance_f(chance)) + continue; + ++count; + AddUnitTarget((*aItr), 1); + } + + // now let next effect cast spell at each target. + return; + } + case 37877: // Blessing of Faith + { + if(!unitTarget) + return; + + uint32 spell_id = 0; + switch(unitTarget->getClass()) + { + case CLASS_DRUID: spell_id = 37878; break; + case CLASS_PALADIN: spell_id = 37879; break; + case CLASS_PRIEST: spell_id = 37880; break; + case CLASS_SHAMAN: spell_id = 37881; break; + default: return; // ignore for not healing classes + } + + m_caster->CastSpell(m_caster,spell_id,true); + return; + } + } + break; + case SPELLFAMILY_SHAMAN: + //Shaman Rockbiter Weapon + if (m_spellInfo->SpellFamilyFlags == 0x400000) + { + uint32 spell_id = 0; + switch(m_spellInfo->Id) + { + case 8017: spell_id = 36494; break; // Rank 1 + case 8018: spell_id = 36750; break; // Rank 2 + case 8019: spell_id = 36755; break; // Rank 3 + case 10399: spell_id = 36759; break; // Rank 4 + case 16314: spell_id = 36763; break; // Rank 5 + case 16315: spell_id = 36766; break; // Rank 6 + case 16316: spell_id = 36771; break; // Rank 7 + case 25479: spell_id = 36775; break; // Rank 8 + case 25485: spell_id = 36499; break; // Rank 9 + default: + sLog.outError("Spell::EffectDummy: Spell %u not handled in RW",m_spellInfo->Id); + return; + } + + SpellEntry const *spellInfo = sSpellStore.LookupEntry( spell_id ); + + if(!spellInfo) + { + sLog.outError("WORLD: unknown spell id %i\n", spell_id); + return; + } + + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + for(int i = BASE_ATTACK; i <= OFF_ATTACK; ++i) + { + if(Item* item = ((Player*)m_caster)->GetWeaponForAttack(WeaponAttackType(i))) + { + if(item->IsFitToSpellRequirements(m_spellInfo)) + { + Spell *spell = new Spell(m_caster, spellInfo, true); + + // enchanting spell selected by calculated damage-per-sec in enchanting effect + // at calculation applied affect from Elemental Weapons talent + // real enchantment damage-1 + spell->m_currentBasePoints[1] = damage-1; + + SpellCastTargets targets; + targets.setItemTarget( item ); + spell->prepare(&targets); + } + } + } + return; + } + + if(m_spellInfo->Id == 39610) // Mana-Tide Totem effect + { + if(!unitTarget || unitTarget->getPowerType() != POWER_MANA) + return; + + // Regenerate 6% of Total Mana Every 3 secs + int32 EffectBasePoints0 = unitTarget->GetMaxPower(POWER_MANA) * damage / 100; + m_caster->CastCustomSpell(unitTarget,39609,&EffectBasePoints0,NULL,NULL,true,NULL,NULL,m_originalCasterGUID); + return; + } + + break; + } + + // pet auras + if(PetAura const* petSpell = spellmgr.GetPetAura(m_spellInfo->Id)) + { + m_caster->AddPetAura(petSpell); + return; + } +} + +void Spell::EffectTriggerSpellWithValue(uint32 i) +{ + uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i]; + + // normal case + SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id ); + + if(!spellInfo) + { + sLog.outError("EffectTriggerSpellWithValue of spell %u: triggering unknown spell id %i\n", m_spellInfo->Id,triggered_spell_id); + return; + } + + int32 bp = damage; + m_caster->CastCustomSpell(unitTarget,triggered_spell_id,&bp,&bp,&bp,true,NULL,NULL,m_originalCasterGUID); +} + +void Spell::EffectTriggerRitualOfSummoning(uint32 i) +{ + uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i]; + SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id ); + + if(!spellInfo) + { + sLog.outError("EffectTriggerRitualOfSummoning of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id); + return; + } + + finish(); + Spell *spell = new Spell(m_caster, spellInfo, true); + + SpellCastTargets targets; + targets.setUnitTarget( unitTarget); + spell->prepare(&targets); + + m_caster->SetCurrentCastedSpell(spell); + spell->m_selfContainer = &(m_caster->m_currentSpells[spell->GetCurrentContainer()]); + +} + +void Spell::EffectForceCast(uint32 i) +{ + if( !unitTarget ) + return; + + uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i]; + + // normal case + SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id ); + + if(!spellInfo) + { + sLog.outError("EffectForceCast of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id); + return; + } + + unitTarget->CastSpell(unitTarget,spellInfo,true,NULL,NULL,m_originalCasterGUID); +} + +void Spell::EffectTriggerSpell(uint32 i) +{ + uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i]; + + // special cases + switch(triggered_spell_id) + { + // Vanish + case 18461: + { + m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); + m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); + m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED); + + // if this spell is given to NPC it must handle rest by it's own AI + if ( m_caster->GetTypeId() != TYPEID_PLAYER ) + return; + + // get highest rank of the Stealth spell + uint32 spellId = 0; + const PlayerSpellMap& sp_list = ((Player*)m_caster)->GetSpellMap(); + for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) + { + // only highest rank is shown in spell book, so simply check if shown in spell book + if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); + if (!spellInfo) + continue; + + if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_STEALTH) + { + spellId = spellInfo->Id; + break; + } + } + + // no Stealth spell found + if (!spellId) + return; + + // reset cooldown on it if needed + if(((Player*)m_caster)->HasSpellCooldown(spellId)) + ((Player*)m_caster)->RemoveSpellCooldown(spellId); + + m_caster->CastSpell(m_caster, spellId, true); + return; + } + // just skip + case 23770: // Sayge's Dark Fortune of * + // not exist, common cooldown can be implemented in scripts if need. + return; + // Brittle Armor - (need add max stack of 24575 Brittle Armor) + case 29284: + { + const SpellEntry *spell = sSpellStore.LookupEntry(24575); + if (!spell) + return; + + for (int i=0; i < spell->StackAmount; ++i) + m_caster->CastSpell(unitTarget,spell->Id, true, m_CastItem, NULL, m_originalCasterGUID); + return; + } + // Mercurial Shield - (need add max stack of 26464 Mercurial Shield) + case 29286: + { + const SpellEntry *spell = sSpellStore.LookupEntry(26464); + if (!spell) + return; + + for (int i=0; i < spell->StackAmount; ++i) + m_caster->CastSpell(unitTarget,spell->Id, true, m_CastItem, NULL, m_originalCasterGUID); + return; + } + // Righteous Defense + case 31980: + { + m_caster->CastSpell(unitTarget, 31790, true,m_CastItem,NULL,m_originalCasterGUID); + return; + } + // Cloak of Shadows + case 35729 : + { + Unit::AuraMap& Auras = m_caster->GetAuras(); + for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end(); ++iter) + { + // remove all harmful spells on you... + if( // ignore positive and passive auras + !iter->second->IsPositive() && !iter->second->IsPassive() && + // ignore physical auras + (GetSpellSchoolMask(iter->second->GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL)==0 && + // ignore immunity persistent spells + !( iter->second->GetSpellProto()->AttributesEx & 0x10000 ) ) + { + m_caster->RemoveAurasDueToSpell(iter->second->GetSpellProto()->Id); + iter = Auras.begin(); + } + } + return; + } + // Priest Shadowfiend (34433) need apply mana gain trigger aura on pet + case 41967: + { + if (Unit *pet = m_caster->GetPet()) + pet->CastSpell(pet, 28305, true); + return; + } + } + + // normal case + SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id ); + + if(!spellInfo) + { + sLog.outError("EffectTriggerSpell of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id); + return; + } + + // some triggered spells require specific equipment + if(spellInfo->EquippedItemClass >=0 && m_caster->GetTypeId()==TYPEID_PLAYER) + { + // main hand weapon required + if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_MAIN_HAND) + { + Item* item = ((Player*)m_caster)->GetWeaponForAttack(BASE_ATTACK); + + // skip spell if no weapon in slot or broken + if(!item || item->IsBroken() ) + return; + + // skip spell if weapon not fit to triggered spell + if(!item->IsFitToSpellRequirements(spellInfo)) + return; + } + + // offhand hand weapon required + if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND) + { + Item* item = ((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK); + + // skip spell if no weapon in slot or broken + if(!item || item->IsBroken() ) + return; + + // skip spell if weapon not fit to triggered spell + if(!item->IsFitToSpellRequirements(spellInfo)) + return; + } + } + + // some triggered spells must be casted instantly (for example, if next effect case instant kill caster) + bool instant = false; + for(uint32 j = i+1; j < 3; ++j) + { + if(m_spellInfo->Effect[j]==SPELL_EFFECT_INSTAKILL && m_spellInfo->EffectImplicitTargetA[j]==TARGET_SELF) + { + instant = true; + break; + } + } + + if(instant) + { + if (unitTarget) + m_caster->CastSpell(unitTarget,spellInfo,true,m_CastItem,NULL,m_originalCasterGUID); + } + else + m_TriggerSpells.push_back(spellInfo); +} + +void Spell::EffectTriggerMissileSpell(uint32 effect_idx) +{ + uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[effect_idx]; + + // normal case + SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id ); + + if(!spellInfo) + { + sLog.outError("EffectTriggerMissileSpell of spell %u: triggering unknown spell id %effect_idx", m_spellInfo->Id,triggered_spell_id); + return; + } + + if (m_CastItem) + DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id); + + Spell *spell = new Spell(m_caster, spellInfo, true, m_originalCasterGUID ); + + SpellCastTargets targets; + targets.setDestination(m_targets.m_destX,m_targets.m_destY,m_targets.m_destZ); + spell->m_CastItem = m_CastItem; + spell->prepare(&targets, NULL); +} + +void Spell::EffectTeleportUnits(uint32 i) +{ + if(!unitTarget || unitTarget->isInFlight()) + return; + + switch (m_spellInfo->EffectImplicitTargetB[i]) + { + case TARGET_INNKEEPER_COORDINATES: + { + // Only players can teleport to innkeeper + if (unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + ((Player*)unitTarget)->TeleportTo(((Player*)unitTarget)->m_homebindMapId,((Player*)unitTarget)->m_homebindX,((Player*)unitTarget)->m_homebindY,((Player*)unitTarget)->m_homebindZ,unitTarget->GetOrientation(),unitTarget==m_caster ? TELE_TO_SPELL : 0); + return; + } + case TARGET_TABLE_X_Y_Z_COORDINATES: + { + // TODO: Only players can teleport? + if (unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + SpellTargetPosition const* st = spellmgr.GetSpellTargetPosition(m_spellInfo->Id); + if(!st) + { + sLog.outError( "Spell::EffectTeleportUnits - unknown Teleport coordinates for spell ID %u\n", m_spellInfo->Id ); + return; + } + ((Player*)unitTarget)->TeleportTo(st->target_mapId,st->target_X,st->target_Y,st->target_Z,st->target_Orientation,unitTarget==m_caster ? TELE_TO_SPELL : 0); + break; + } + case TARGET_BEHIND_VICTIM: + { + // Get selected target for player (or victim for units) + Unit *pTarget = NULL; + if(m_caster->GetTypeId() == TYPEID_PLAYER) + pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection()); + else + pTarget = m_caster->getVictim(); + // No target present - return + if (!pTarget) + return; + // Init dest coordinates + uint32 mapid = m_caster->GetMapId(); + float x = m_targets.m_destX; + float y = m_targets.m_destY; + float z = m_targets.m_destZ; + float orientation = pTarget->GetOrientation(); + // Teleport + if(unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); + else + { + MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation); + WorldPacket data; + unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation); + unitTarget->SendMessageToSet(&data, false); + } + return; + } + default: + { + // If not exist data for dest location - return + if(!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) + { + sLog.outError( "Spell::EffectTeleportUnits - unknown EffectImplicitTargetB[%u] = %u for spell ID %u\n", i, m_spellInfo->EffectImplicitTargetB[i], m_spellInfo->Id ); + return; + } + // Init dest coordinates + uint32 mapid = m_caster->GetMapId(); + float x = m_targets.m_destX; + float y = m_targets.m_destY; + float z = m_targets.m_destZ; + float orientation = unitTarget->GetOrientation(); + // Teleport + if(unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); + else + { + MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation); + WorldPacket data; + unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation); + unitTarget->SendMessageToSet(&data, false); + } + return; + } + } + + // post effects for TARGET_TABLE_X_Y_Z_COORDINATES + switch ( m_spellInfo->Id ) + { + // Dimensional Ripper - Everlook + case 23442: + { + 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); + else // 1/12 fire + m_caster->CastSpell(m_caster,23449,true); + } + return; + } + // Ultrasafe Transporter: Toshley's Station + case 36941: + { + if ( roll_chance_i(50) ) // 50% success + { + int32 rand_eff = urand(1,7); + switch ( rand_eff ) + { + case 1: + // soul split - evil + m_caster->CastSpell(m_caster,36900,true); + break; + case 2: + // soul split - good + m_caster->CastSpell(m_caster,36901,true); + break; + case 3: + // Increase the size + m_caster->CastSpell(m_caster,36895,true); + break; + case 4: + // Decrease the size + m_caster->CastSpell(m_caster,36893,true); + break; + case 5: + // Transform + { + if (((Player*)m_caster)->GetTeam() == ALLIANCE ) + m_caster->CastSpell(m_caster,36897,true); + else + m_caster->CastSpell(m_caster,36899,true); + break; + } + case 6: + // chicken + m_caster->CastSpell(m_caster,36940,true); + break; + case 7: + // evil twin + m_caster->CastSpell(m_caster,23445,true); + break; + } + } + return; + } + // Dimensional Ripper - Area 52 + case 36890: + { + if ( roll_chance_i(50) ) // 50% success + { + int32 rand_eff = urand(1,4); + switch ( rand_eff ) + { + case 1: + // soul split - evil + m_caster->CastSpell(m_caster,36900,true); + break; + case 2: + // soul split - good + m_caster->CastSpell(m_caster,36901,true); + break; + case 3: + // Increase the size + m_caster->CastSpell(m_caster,36895,true); + break; + case 4: + // Transform + { + if (((Player*)m_caster)->GetTeam() == ALLIANCE ) + m_caster->CastSpell(m_caster,36897,true); + else + m_caster->CastSpell(m_caster,36899,true); + break; + } + } + } + return; + } + } +} + +void Spell::EffectApplyAura(uint32 i) +{ + if(!unitTarget) + return; + + SpellImmuneList const& list = unitTarget->m_spellImmune[IMMUNITY_STATE]; + for(SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr) + if(itr->type == m_spellInfo->EffectApplyAuraName[i]) + return; + + // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load) + if( !unitTarget->isAlive() && m_spellInfo->Id != 20584 && m_spellInfo->Id != 8326 && + (unitTarget->GetTypeId()!=TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading()) ) + return; + + Unit* caster = m_originalCasterGUID ? m_originalCaster : m_caster; + if(!caster) + return; + + sLog.outDebug("Spell: Aura is: %u", m_spellInfo->EffectApplyAuraName[i]); + + Aura* Aur = CreateAura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, caster, m_CastItem); + + // Now Reduce spell duration using data received at spell hit + int32 duration = Aur->GetAuraMaxDuration(); + unitTarget->ApplyDiminishingToDuration(m_diminishGroup,duration,m_caster,m_diminishLevel); + Aur->setDiminishGroup(m_diminishGroup); + + // if Aura removed and deleted, do not continue. + if(duration== 0 && !(Aur->IsPermanent())) + { + delete Aur; + return; + } + + if(duration != Aur->GetAuraMaxDuration()) + { + Aur->SetAuraMaxDuration(duration); + Aur->SetAuraDuration(duration); + } + + bool added = unitTarget->AddAura(Aur); + + // Aura not added and deleted in AddAura call; + if (!added) + return; + + // found crash at character loading, broken pointer to Aur... + // Aur was deleted in AddAura()... + if(!Aur) + return; + + // TODO Make a way so it works for every related spell! + if(unitTarget->GetTypeId()==TYPEID_PLAYER) // Negative buff should only be applied on players + { + uint32 spellId = 0; + if(m_spellInfo->CasterAuraStateNot==AURA_STATE_WEAKENED_SOUL || m_spellInfo->TargetAuraStateNot==AURA_STATE_WEAKENED_SOUL) + spellId = 6788; // Weakened Soul + else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_FORBEARANCE || m_spellInfo->TargetAuraStateNot==AURA_STATE_FORBEARANCE) + spellId = 25771; // Forbearance + else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_HYPOTHERMIA) + spellId = 41425; // Hypothermia + else if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages + spellId = 11196; // Recently Bandaged + else if( (m_spellInfo->AttributesEx & 0x20) && (m_spellInfo->AttributesEx2 & 0x20000) ) + spellId = 23230; // Blood Fury - Healing Reduction + + SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(spellId); + if (AdditionalSpellInfo) + { + // applied at target by target + Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, 0, &m_currentBasePoints[0], unitTarget,unitTarget, 0); + unitTarget->AddAura(AdditionalAura); + sLog.outDebug("Spell: Additional Aura is: %u", AdditionalSpellInfo->EffectApplyAuraName[0]); + } + } + + // Prayer of Mending (jump animation), we need formal caster instead original for correct animation + if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags & 0x00002000000000LL)) + m_caster->CastSpell(unitTarget,41637,true,NULL,Aur); +} + +void Spell::EffectUnlearnSpecialization( uint32 i ) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + Player *_player = (Player*)unitTarget; + uint32 spellToUnlearn = m_spellInfo->EffectTriggerSpell[i]; + + _player->removeSpell(spellToUnlearn); + + sLog.outDebug( "Spell: Player %u have unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() ); +} + +void Spell::EffectPowerDrain(uint32 i) +{ + if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) + return; + + Powers drain_power = Powers(m_spellInfo->EffectMiscValue[i]); + + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + if(unitTarget->getPowerType() != drain_power) + return; + if(damage < 0) + return; + + uint32 curPower = unitTarget->GetPower(drain_power); + + //add spell damage bonus + damage=m_caster->SpellDamageBonus(unitTarget,m_spellInfo,uint32(damage),SPELL_DIRECT_DAMAGE); + + // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) + uint32 power = damage; + if ( drain_power == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER ) + power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power); + + int32 new_damage; + if(curPower < power) + new_damage = curPower; + else + new_damage = power; + + unitTarget->ModifyPower(drain_power,-new_damage); + + if(drain_power == POWER_MANA) + { + float manaMultiplier = m_spellInfo->EffectMultipleValue[i]; + if(manaMultiplier==0) + manaMultiplier = 1; + + if(Player *modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, manaMultiplier); + + int32 gain = int32(new_damage*manaMultiplier); + + m_caster->ModifyPower(POWER_MANA,gain); + //send log + m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id,gain,POWER_MANA,false); + } +} + +void Spell::EffectSendEvent(uint32 EffectIndex) +{ + if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->InBattleGround()) + { + BattleGround* bg = ((Player *)m_caster)->GetBattleGround(); + if(bg && bg->GetStatus() == STATUS_IN_PROGRESS) + { + switch(m_spellInfo->Id) + { + case 23333: // Pickup Horde Flag + /*do not uncomment . + if(bg->GetTypeID()==BATTLEGROUND_WS) + bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget); + sLog.outDebug("Send Event Horde Flag Picked Up"); + break; + /* not used : + case 23334: // Drop Horde Flag + if(bg->GetTypeID()==BATTLEGROUND_WS) + bg->EventPlayerDroppedFlag((Player*)m_caster); + sLog.outDebug("Drop Horde Flag"); + break; + */ + case 23335: // Pickup Alliance Flag + /*do not uncomment ... (it will cause crash, because of null targetobject!) anyway this is a bad way to call that event, because it would cause recursion + if(bg->GetTypeID()==BATTLEGROUND_WS) + bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget); + sLog.outDebug("Send Event Alliance Flag Picked Up"); + break; + /* not used : + case 23336: // Drop Alliance Flag + if(bg->GetTypeID()==BATTLEGROUND_WS) + bg->EventPlayerDroppedFlag((Player*)m_caster); + sLog.outDebug("Drop Alliance Flag"); + break; + case 23385: // Alliance Flag Returns + if(bg->GetTypeID()==BATTLEGROUND_WS) + bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget); + sLog.outDebug("Alliance Flag Returned"); + break; + case 23386: // Horde Flag Returns + if(bg->GetTypeID()==BATTLEGROUND_WS) + bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget); + sLog.outDebug("Horde Flag Returned"); + break;*/ + case 34976: + /* + if(bg->GetTypeID()==BATTLEGROUND_EY) + bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget); + */ + break; + default: + sLog.outDebug("Unknown spellid %u in BG event", m_spellInfo->Id); + break; + } + } + } + sLog.outDebug("Spell ScriptStart %u for spellid %u in EffectSendEvent ", m_spellInfo->EffectMiscValue[EffectIndex], m_spellInfo->Id); + sWorld.ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[EffectIndex], m_caster, focusObject); +} + +void Spell::EffectPowerBurn(uint32 i) +{ + if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) + return; + + Powers powertype = Powers(m_spellInfo->EffectMiscValue[i]); + + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + if(unitTarget->getPowerType()!=powertype) + return; + if(damage < 0) + return; + + int32 curPower = int32(unitTarget->GetPower(powertype)); + + // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) + uint32 power = damage; + if ( powertype == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER ) + power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power); + + int32 new_damage = (curPower < power) ? curPower : power; + + unitTarget->ModifyPower(powertype,-new_damage); + float multiplier = m_spellInfo->EffectMultipleValue[i]; + + if(Player *modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier); + + new_damage = int32(new_damage*multiplier); + m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true); +} + +void Spell::EffectHeal( uint32 /*i*/ ) +{ + if( unitTarget && unitTarget->isAlive() && damage >= 0) + { + // Try to get original caster + Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster; + + // Skip if m_originalCaster not available + if (!caster) + return; + + int32 addhealth = damage; + + // Vessel of the Naaru (Vial of the Sunwell trinket) + if (m_spellInfo->Id == 45064) + { + // Amount of heal - depends from stacked Holy Energy + int damageAmount = 0; + Unit::AuraList const& mDummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) + if((*i)->GetId() == 45062) + damageAmount+=(*i)->GetModifier()->m_amount; + if (damageAmount) + m_caster->RemoveAurasDueToSpell(45062); + + addhealth += damageAmount; + } + // Swiftmend - consumes Regrowth or Rejuvenation + else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND)) + { + Unit::AuraList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_HEAL); + // find most short by duration + Aura *targetAura = NULL; + for(Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) + { + if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DRUID + && ((*i)->GetSpellProto()->SpellFamilyFlags == 0x40 || (*i)->GetSpellProto()->SpellFamilyFlags == 0x10) ) + { + if(!targetAura || (*i)->GetAuraDuration() < targetAura->GetAuraDuration()) + targetAura = *i; + } + } + + if(!targetAura) + { + sLog.outError("Target(GUID:" I64FMTD ") has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget->GetGUID()); + return; + } + int idx = 0; + while(idx < 3) + { + if(targetAura->GetSpellProto()->EffectApplyAuraName[idx] == SPELL_AURA_PERIODIC_HEAL) + break; + idx++; + } + + int32 tickheal = caster->SpellHealingBonus(targetAura->GetSpellProto(), targetAura->GetModifier()->m_amount, DOT, unitTarget); + int32 tickcount = GetSpellDuration(targetAura->GetSpellProto()) / targetAura->GetSpellProto()->EffectAmplitude[idx]; + unitTarget->RemoveAurasDueToSpell(targetAura->GetId()); + + addhealth += tickheal * tickcount; + } + else + addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth,HEAL, unitTarget); + + bool crit = caster->isSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask, m_attackType); + if (crit) + addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, unitTarget); + caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, crit); + + int32 gain = unitTarget->ModifyHealth( int32(addhealth) ); + unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo); + + if(caster->GetTypeId()==TYPEID_PLAYER) + if(BattleGround *bg = ((Player*)caster)->GetBattleGround()) + bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain); + + // ignore item heals + if(m_CastItem) + return; + + uint32 procHealer = PROC_FLAG_HEAL; + if (crit) + procHealer |= PROC_FLAG_CRIT_HEAL; + + m_caster->ProcDamageAndSpell(unitTarget,procHealer,PROC_FLAG_HEALED,addhealth,SPELL_SCHOOL_MASK_NONE,m_spellInfo,m_IsTriggeredSpell); + } +} + +void Spell::EffectHealPct( uint32 /*i*/ ) +{ + if( unitTarget && unitTarget->isAlive() && damage >= 0) + { + // Try to get original caster + Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster; + + // Skip if m_originalCaster not available + if (!caster) + return; + + uint32 addhealth = unitTarget->GetMaxHealth() * damage / 100; + caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false); + + int32 gain = unitTarget->ModifyHealth( int32(addhealth) ); + unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo); + + if(caster->GetTypeId()==TYPEID_PLAYER) + if(BattleGround *bg = ((Player*)caster)->GetBattleGround()) + bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain); + } +} + +void Spell::EffectHealMechanical( uint32 /*i*/ ) +{ + // Mechanic creature type should be correctly checked by targetCreatureType field + if( unitTarget && unitTarget->isAlive() && damage >= 0) + { + // Try to get original caster + Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster; + + // Skip if m_originalCaster not available + if (!caster) + return; + + uint32 addhealth = caster->SpellHealingBonus(m_spellInfo, uint32(damage), HEAL, unitTarget); + caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false); + unitTarget->ModifyHealth( int32(damage) ); + } +} + +void Spell::EffectHealthLeech(uint32 i) +{ + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + + if(damage < 0) + return; + + sLog.outDebug("HealthLeech :%i", damage); + + float multiplier = m_spellInfo->EffectMultipleValue[i]; + + if(Player *modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier); + + int32 new_damage = int32(damage*multiplier); + uint32 curHealth = unitTarget->GetHealth(); + new_damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true); + if(curHealth < new_damage) + new_damage = curHealth; + + if(m_caster->isAlive()) + { + new_damage = m_caster->SpellHealingBonus(m_spellInfo, new_damage, HEAL, m_caster); + + m_caster->ModifyHealth(new_damage); + + if(m_caster->GetTypeId() == TYPEID_PLAYER) + m_caster->SendHealSpellLog(m_caster, m_spellInfo->Id, uint32(new_damage)); + } +} + +void Spell::DoCreateItem(uint32 i, uint32 itemtype) +{ + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + Player* player = (Player*)unitTarget; + + uint32 newitemid = itemtype; + ItemPrototype const *pProto = objmgr.GetItemPrototype( newitemid ); + if(!pProto) + { + player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); + return; + } + + uint32 num_to_add; + + // TODO: maybe all this can be replaced by using correct calculated `damage` value + if(pProto->Class != ITEM_CLASS_CONSUMABLE || m_spellInfo->SpellFamilyName != SPELLFAMILY_MAGE) + { + int32 basePoints = m_currentBasePoints[i]; + int32 randomPoints = m_spellInfo->EffectDieSides[i]; + if (randomPoints) + num_to_add = basePoints + irand(1, randomPoints); + else + num_to_add = basePoints + 1; + } + else if (pProto->MaxCount == 1) + num_to_add = 1; + else if(player->getLevel() >= m_spellInfo->spellLevel) + { + int32 basePoints = m_currentBasePoints[i]; + float pointPerLevel = m_spellInfo->EffectRealPointsPerLevel[i]; + num_to_add = basePoints + 1 + uint32((player->getLevel() - m_spellInfo->spellLevel)*pointPerLevel); + } + else + num_to_add = 2; + + if (num_to_add < 1) + num_to_add = 1; + if (num_to_add > pProto->Stackable) + num_to_add = pProto->Stackable; + + // init items_count to 1, since 1 item will be created regardless of specialization + int items_count=1; + // the chance to create additional items + float additionalCreateChance=0.0f; + // the maximum number of created additional items + uint8 additionalMaxNum=0; + // get the chance and maximum number for creating extra items + if ( canCreateExtraItems(player, m_spellInfo->Id, additionalCreateChance, additionalMaxNum) ) + { + // roll with this chance till we roll not to create or we create the max num + while ( roll_chance_f(additionalCreateChance) && items_count<=additionalMaxNum ) + ++items_count; + } + + // really will be created more items + num_to_add *= items_count; + + // can the player store the new item? + ItemPosCountVec dest; + uint32 no_space = 0; + uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, newitemid, num_to_add, &no_space ); + if( msg != EQUIP_ERR_OK ) + { + // convert to possible store amount + if( msg == EQUIP_ERR_INVENTORY_FULL || msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS ) + num_to_add -= no_space; + else + { + // if not created by another reason from full inventory or unique items amount limitation + player->SendEquipError( msg, NULL, NULL ); + return; + } + } + + if(num_to_add) + { + // create the new item and store it + Item* pItem = player->StoreNewItem( dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid)); + + // was it successful? return error if not + if(!pItem) + { + player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); + return; + } + + // set the "Crafted by ..." property of the item + if( pItem->GetProto()->Class != ITEM_CLASS_CONSUMABLE && pItem->GetProto()->Class != ITEM_CLASS_QUEST) + pItem->SetUInt32Value(ITEM_FIELD_CREATOR,player->GetGUIDLow()); + + // send info to the client + if(pItem) + player->SendNewItem(pItem, num_to_add, true, true); + + // we succeeded in creating at least one item, so a levelup is possible + player->UpdateCraftSkill(m_spellInfo->Id); + } + + // for battleground marks send by mail if not add all expected + if(no_space > 0 ) + { + BattleGroundTypeId bgType; + switch(m_spellInfo->Id) + { + case SPELL_AV_MARK_WINNER: + case SPELL_AV_MARK_LOSER: + bgType = BATTLEGROUND_AV; + break; + case SPELL_WS_MARK_WINNER: + case SPELL_WS_MARK_LOSER: + bgType = BATTLEGROUND_WS; + break; + case SPELL_AB_MARK_WINNER: + case SPELL_AB_MARK_LOSER: + bgType = BATTLEGROUND_AB; + break; + default: + return; + } + + if(BattleGround* bg = sBattleGroundMgr.GetBattleGround(bgType)) + bg->SendRewardMarkByMail(player,newitemid,no_space); + } +} + +void Spell::EffectCreateItem(uint32 i) +{ + DoCreateItem(i,m_spellInfo->EffectItemType[i]); +} + +void Spell::EffectPersistentAA(uint32 i) +{ + float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + + if(Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius); + + int32 duration = GetSpellDuration(m_spellInfo); + DynamicObject* dynObj = new DynamicObject; + if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, i, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius)) + { + delete dynObj; + return; + } + dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65); + dynObj->SetUInt32Value(GAMEOBJECT_DISPLAYID, 368003); + dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x01eeeeee); + m_caster->AddDynObject(dynObj); + MapManager::Instance().GetMap(dynObj->GetMapId(), dynObj)->Add(dynObj); +} + +void Spell::EffectEnergize(uint32 i) +{ + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + + if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) + return; + + // Some level depends spells + int multipler = 0; + int level_diff = 0; + switch (m_spellInfo->Id) + { + // Restore Energy + case 9512: + level_diff = m_caster->getLevel() - 40; + multipler = 2; + break; + // Blood Fury + case 24571: + level_diff = m_caster->getLevel() - 60; + multipler = 10; + break; + // Burst of Energy + case 24532: + level_diff = m_caster->getLevel() - 60; + multipler = 4; + break; + default: + break; + } + + if (level_diff > 0) + damage -= multipler * level_diff; + + if(damage < 0) + return; + + Powers power = Powers(m_spellInfo->EffectMiscValue[i]); + + if(unitTarget->GetMaxPower(power) == 0) + return; + + unitTarget->ModifyPower(power,damage); + m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power); + + // Mad Alchemist's Potion + if (m_spellInfo->Id == 45051) + { + // find elixirs on target + uint32 elixir_mask = 0; + Unit::AuraMap& Auras = unitTarget->GetAuras(); + for(Unit::AuraMap::iterator itr = Auras.begin(); itr != Auras.end(); ++itr) + { + uint32 spell_id = itr->second->GetId(); + if(uint32 mask = spellmgr.GetSpellElixirMask(spell_id)) + elixir_mask |= mask; + } + + // get available elixir mask any not active type from battle/guardian (and flask if no any) + elixir_mask = (elixir_mask & ELIXIR_FLASK_MASK) ^ ELIXIR_FLASK_MASK; + + // get all available elixirs by mask and spell level + std::vector<uint32> elixirs; + SpellElixirMap const& m_spellElixirs = spellmgr.GetSpellElixirMap(); + for(SpellElixirMap::const_iterator itr = m_spellElixirs.begin(); itr != m_spellElixirs.end(); ++itr) + { + if (itr->second & elixir_mask) + { + if (itr->second & (ELIXIR_UNSTABLE_MASK | ELIXIR_SHATTRATH_MASK)) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); + if (spellInfo && (spellInfo->spellLevel < m_spellInfo->spellLevel || spellInfo->spellLevel > unitTarget->getLevel())) + continue; + + elixirs.push_back(itr->first); + } + } + + if (!elixirs.empty()) + { + // cast random elixir on target + uint32 rand_spell = urand(0,elixirs.size()-1); + m_caster->CastSpell(unitTarget,elixirs[rand_spell],true,m_CastItem); + } + } +} + +void Spell::EffectEnergisePct(uint32 i) +{ + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + + if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) + return; + + Powers power = Powers(m_spellInfo->EffectMiscValue[i]); + + uint32 maxPower = unitTarget->GetMaxPower(power); + if(maxPower == 0) + return; + + uint32 gain = damage * maxPower / 100; + unitTarget->ModifyPower(power, gain); + m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power); +} + +void Spell::SendLoot(uint64 guid, LootType loottype) +{ + Player* player = (Player*)m_caster; + if (!player) + return; + + if (gameObjTarget) + { + switch (gameObjTarget->GetGoType()) + { + case GAMEOBJECT_TYPE_DOOR: + case GAMEOBJECT_TYPE_BUTTON: + gameObjTarget->UseDoorOrButton(); + sWorld.ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); + return; + + case GAMEOBJECT_TYPE_QUESTGIVER: + // start or end quest + player->PrepareQuestMenu(guid); + player->SendPreparedQuest(guid); + return; + + case GAMEOBJECT_TYPE_SPELL_FOCUS: + // triggering linked GO + if(uint32 trapEntry = gameObjTarget->GetGOInfo()->spellFocus.linkedTrapId) + gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster); + return; + + case GAMEOBJECT_TYPE_GOOBER: + // goober_scripts can be triggered if the player don't have the quest + if (gameObjTarget->GetGOInfo()->goober.eventId) + { + sLog.outDebug("Goober ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->goober.eventId,gameObjTarget->GetDBTableGUIDLow()); + sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->goober.eventId, player, gameObjTarget); + } + + // cast goober spell + if (gameObjTarget->GetGOInfo()->goober.questId) + ///Quest require to be active for GO using + if(player->GetQuestStatus(gameObjTarget->GetGOInfo()->goober.questId) != QUEST_STATUS_INCOMPLETE) + return; + + gameObjTarget->AddUniqueUse(player); + gameObjTarget->SetLootState(GO_JUST_DEACTIVATED); + + //TODO? Objective counting called without spell check but with quest objective check + // if send spell id then this line will duplicate to spell casting call (double counting) + // So we or have this line and not required in quest_template have reqSpellIdN + // or must remove this line and required in DB have data in quest_template have reqSpellIdN for all quest using cases. + player->CastedCreatureOrGO(gameObjTarget->GetEntry(), gameObjTarget->GetGUID(), 0); + + // triggering linked GO + if(uint32 trapEntry = gameObjTarget->GetGOInfo()->goober.linkedTrapId) + gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster); + + return; + + case GAMEOBJECT_TYPE_CHEST: + // TODO: possible must be moved to loot release (in different from linked triggering) + if (gameObjTarget->GetGOInfo()->chest.eventId) + { + sLog.outDebug("Chest ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->chest.eventId,gameObjTarget->GetDBTableGUIDLow()); + sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->chest.eventId, player, gameObjTarget); + } + + // triggering linked GO + if(uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrapId) + gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster); + + // Don't return, let loots been taken + } + } + + // Send loot + player->SendLoot(guid, loottype); +} + +void Spell::EffectOpenLock(uint32 /*i*/) +{ + if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER) + { + sLog.outDebug( "WORLD: Open Lock - No Player Caster!"); + return; + } + + Player* player = (Player*)m_caster; + + LootType loottype = LOOT_CORPSE; + uint32 lockId = 0; + uint64 guid = 0; + + // Get lockId + if(gameObjTarget) + { + GameObjectInfo const* goInfo = gameObjTarget->GetGOInfo(); + // Arathi Basin banner opening ! + if( goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune || + goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK ) + { + if(BattleGround *bg = player->GetBattleGround())// in battleground + { + if( !player->IsMounted() && // not mounted + !player->HasStealthAura() && // not stealthed + !player->HasInvisibilityAura() && // not invisible + player->isAlive() ) // live player + { + // check if it's correct bg + if(bg && bg->GetTypeID() == BATTLEGROUND_AB) + bg->EventPlayerClickedOnFlag(player, gameObjTarget); + + return; + } + } + } + else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND) + { + if(BattleGround *bg = player->GetBattleGround()) + if(bg->GetTypeID() == BATTLEGROUND_EY) + bg->EventPlayerClickedOnFlag(player, gameObjTarget); + return; + } + lockId = gameObjTarget->GetLockId(); + guid = gameObjTarget->GetGUID(); + } + else if(itemTarget) + { + lockId = itemTarget->GetProto()->LockID; + guid = itemTarget->GetGUID(); + } + else + { + sLog.outDebug( "WORLD: Open Lock - No GameObject/Item Target!"); + return; + } + + if(!lockId) // possible case for GO and maybe for items. + { + SendLoot(guid, loottype); + return; + } + + // Get LockInfo + LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); + + if (!lockInfo) + { + sLog.outError( "Spell::EffectOpenLock: %s [guid = %u] has an unknown lockId: %u!", + (gameObjTarget ? "gameobject" : "item"), GUID_LOPART(guid), lockId); + SendCastResult(SPELL_FAILED_BAD_TARGETS); + return; + } + + // check key + for(int i = 0; i < 5; ++i) + { + // type==1 This means lockInfo->key[i] is an item + if(lockInfo->keytype[i]==LOCK_KEY_ITEM && lockInfo->key[i] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[i]) + { + SendLoot(guid, loottype); + return; + } + } + + uint32 SkillId = 0; + // Check and skill-up skill + if( m_spellInfo->Effect[1] == SPELL_EFFECT_SKILL ) + SkillId = m_spellInfo->EffectMiscValue[1]; + // pickpocketing spells + else if( m_spellInfo->EffectMiscValue[0] == LOCKTYPE_PICKLOCK ) + SkillId = SKILL_LOCKPICKING; + + // skill bonus provided by casting spell (mostly item spells) + uint32 spellSkillBonus = uint32(m_currentBasePoints[0]+1); + + uint32 reqSkillValue = lockInfo->requiredminingskill; + + if(lockInfo->requiredlockskill) // required pick lock skill applying + { + if(SkillId != SKILL_LOCKPICKING) // wrong skill (cheating?) + { + SendCastResult(SPELL_FAILED_FIZZLE); + return; + } + + reqSkillValue = lockInfo->requiredlockskill; + } + else if(SkillId == SKILL_LOCKPICKING) // apply picklock skill to wrong target + { + SendCastResult(SPELL_FAILED_BAD_TARGETS); + return; + } + + if ( SkillId ) + { + loottype = LOOT_SKINNING; + if ( player->GetSkillValue(SkillId) + spellSkillBonus < reqSkillValue ) + { + SendCastResult(SPELL_FAILED_LOW_CASTLEVEL); + return; + } + + // update skill if really known + uint32 SkillValue = player->GetPureSkillValue(SkillId); + if(SkillValue) // non only item base skill + { + if(gameObjTarget) + { + // Allow one skill-up until respawned + if ( !gameObjTarget->IsInSkillupList( player->GetGUIDLow() ) && + player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue) ) + gameObjTarget->AddToSkillupList( player->GetGUIDLow() ); + } + else if(itemTarget) + { + // Do one skill-up + uint32 SkillValue = player->GetPureSkillValue(SkillId); + player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue); + } + } + } + + SendLoot(guid, loottype); +} + +void Spell::EffectSummonChangeItem(uint32 i) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *player = (Player*)m_caster; + + // applied only to using item + if(!m_CastItem) + return; + + // ... only to item in own inventory/bank/equip_slot + if(m_CastItem->GetOwnerGUID()!=player->GetGUID()) + return; + + uint32 newitemid = m_spellInfo->EffectItemType[i]; + if(!newitemid) + return; + + uint16 pos = m_CastItem->GetPos(); + + Item *pNewItem = Item::CreateItem( newitemid, 1, player); + if( !pNewItem ) + return; + + for(uint8 i= PERM_ENCHANTMENT_SLOT; i<=TEMP_ENCHANTMENT_SLOT; ++i) + { + if(m_CastItem->GetEnchantmentId(EnchantmentSlot(i))) + pNewItem->SetEnchantment(EnchantmentSlot(i), m_CastItem->GetEnchantmentId(EnchantmentSlot(i)), m_CastItem->GetEnchantmentDuration(EnchantmentSlot(i)), m_CastItem->GetEnchantmentCharges(EnchantmentSlot(i))); + } + + if(m_CastItem->GetUInt32Value(ITEM_FIELD_DURABILITY) < m_CastItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY)) + { + double loosePercent = 1 - m_CastItem->GetUInt32Value(ITEM_FIELD_DURABILITY) / double(m_CastItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY)); + player->DurabilityLoss(pNewItem, loosePercent); + } + + if( player->IsInventoryPos( pos ) ) + { + ItemPosCountVec dest; + uint8 msg = player->CanStoreItem( m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true ); + if( msg == EQUIP_ERR_OK ) + { + player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true); + + // prevent crash at access and unexpected charges counting with item update queue corrupt + if(m_CastItem==m_targets.getItemTarget()) + m_targets.setItemTarget(NULL); + + m_CastItem = NULL; + + player->StoreItem( dest, pNewItem, true); + return; + } + } + else if( player->IsBankPos ( pos ) ) + { + ItemPosCountVec dest; + uint8 msg = player->CanBankItem( m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true ); + if( msg == EQUIP_ERR_OK ) + { + player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true); + + // prevent crash at access and unexpected charges counting with item update queue corrupt + if(m_CastItem==m_targets.getItemTarget()) + m_targets.setItemTarget(NULL); + + m_CastItem = NULL; + + player->BankItem( dest, pNewItem, true); + return; + } + } + else if( player->IsEquipmentPos ( pos ) ) + { + uint16 dest; + uint8 msg = player->CanEquipItem( m_CastItem->GetSlot(), dest, pNewItem, true ); + if( msg == EQUIP_ERR_OK ) + { + player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true); + + // prevent crash at access and unexpected charges counting with item update queue corrupt + if(m_CastItem==m_targets.getItemTarget()) + m_targets.setItemTarget(NULL); + + m_CastItem = NULL; + + player->EquipItem( dest, pNewItem, true); + player->AutoUnequipOffhandIfNeed(); + return; + } + } + + // fail + delete pNewItem; +} + +void Spell::EffectOpenSecretSafe(uint32 i) +{ + EffectOpenLock(i); //no difference for now +} + +void Spell::EffectProficiency(uint32 /*i*/) +{ + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + Player *p_target = (Player*)unitTarget; + + uint32 subClassMask = m_spellInfo->EquippedItemSubClassMask; + if(m_spellInfo->EquippedItemClass == 2 && !(p_target->GetWeaponProficiency() & subClassMask)) + { + p_target->AddWeaponProficiency(subClassMask); + p_target->SendProficiency(uint8(0x02),p_target->GetWeaponProficiency()); + } + if(m_spellInfo->EquippedItemClass == 4 && !(p_target->GetArmorProficiency() & subClassMask)) + { + p_target->AddArmorProficiency(subClassMask); + p_target->SendProficiency(uint8(0x04),p_target->GetArmorProficiency()); + } +} + +void Spell::EffectApplyAreaAura(uint32 i) +{ + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + + AreaAura* Aur = new AreaAura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, m_caster, m_CastItem); + unitTarget->AddAura(Aur); +} + +void Spell::EffectSummonType(uint32 i) +{ + switch(m_spellInfo->EffectMiscValueB[i]) + { + case SUMMON_TYPE_GUARDIAN: + case SUMMON_TYPE_POSESSED: + case SUMMON_TYPE_POSESSED2: + EffectSummonGuardian(i); + break; + case SUMMON_TYPE_WILD: + EffectSummonWild(i); + break; + case SUMMON_TYPE_DEMON: + EffectSummonDemon(i); + break; + case SUMMON_TYPE_SUMMON: + EffectSummon(i); + break; + case SUMMON_TYPE_CRITTER: + case SUMMON_TYPE_CRITTER2: + EffectSummonCritter(i); + break; + case SUMMON_TYPE_TOTEM_SLOT1: + case SUMMON_TYPE_TOTEM_SLOT2: + case SUMMON_TYPE_TOTEM_SLOT3: + case SUMMON_TYPE_TOTEM_SLOT4: + case SUMMON_TYPE_TOTEM: + EffectSummonTotem(i); + break; + case SUMMON_TYPE_UNKNOWN1: + case SUMMON_TYPE_UNKNOWN2: + case SUMMON_TYPE_UNKNOWN3: + case SUMMON_TYPE_UNKNOWN4: + case SUMMON_TYPE_UNKNOWN5: + case SUMMON_TYPE_UNKNOWN6: + break; + default: + sLog.outError("EffectSummonType: Unhandled summon type %u", m_spellInfo->EffectMiscValueB[i]); + break; + } +} + +void Spell::EffectSummon(uint32 i) +{ + if(m_caster->GetPetGUID()) + return; + + if(!unitTarget) + return; + uint32 pet_entry = m_spellInfo->EffectMiscValue[i]; + if(!pet_entry) + return; + uint32 level = m_caster->getLevel(); + Pet* spawnCreature = new Pet(SUMMON_PET); + + if(spawnCreature->LoadPetFromDB(m_caster,pet_entry)) + { + // set timer for unsummon + int32 duration = GetSpellDuration(m_spellInfo); + if(duration > 0) + spawnCreature->SetDuration(duration); + + return; + } + + Map *map = m_caster->GetMap(); + uint32 pet_number = objmgr.GeneratePetNumber(); + if(!spawnCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_PET),map,m_spellInfo->EffectMiscValue[i], pet_number)) + { + sLog.outErrorDb("Spell::EffectSummon: no such creature entry %u",m_spellInfo->EffectMiscValue[i]); + delete spawnCreature; + return; + } + + // Summon in dest location + float x,y,z; + if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + } + else + m_caster->GetClosePoint(x,y,z,spawnCreature->GetObjectSize()); + + spawnCreature->Relocate(x,y,z,-m_caster->GetOrientation()); + + if(!spawnCreature->IsPositionValid()) + { + sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY()); + delete spawnCreature; + return; + } + + // set timer for unsummon + int32 duration = GetSpellDuration(m_spellInfo); + if(duration > 0) + spawnCreature->SetDuration(duration); + + spawnCreature->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID()); + spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS , 0); + spawnCreature->setPowerType(POWER_MANA); + spawnCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction()); + spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS,0); + spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_0,2048); + spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0); + spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0); + spawnCreature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0); + spawnCreature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000); + spawnCreature->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID()); + spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + spawnCreature->InitStatsForLevel(level); + + spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false); + + spawnCreature->AIM_Initialize(); + spawnCreature->InitPetCreateSpells(); + spawnCreature->SetHealth(spawnCreature->GetMaxHealth()); + spawnCreature->SetPower(POWER_MANA, spawnCreature->GetMaxPower(POWER_MANA)); + + std::string name = m_caster->GetName(); + name.append(petTypeSuffix[spawnCreature->getPetType()]); + spawnCreature->SetName( name ); + + map->Add((Creature*)spawnCreature); + + if(m_caster->GetTypeId() == TYPEID_PLAYER) + { + m_caster->SetPet(spawnCreature); + spawnCreature->GetCharmInfo()->SetReactState( REACT_DEFENSIVE ); + spawnCreature->SavePetToDB(PET_SAVE_AS_CURRENT); + ((Player*)m_caster)->PetSpellInitialize(); + } +} + +void Spell::EffectLearnSpell(uint32 i) +{ + if(!unitTarget) + return; + + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + { + if(m_caster->GetTypeId() == TYPEID_PLAYER) + EffectLearnPetSpell(i); + + return; + } + + Player *player = (Player*)unitTarget; + + uint32 spellToLearn = (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) ? damage : m_spellInfo->EffectTriggerSpell[i]; + player->learnSpell(spellToLearn); + + sLog.outDebug( "Spell: Player %u have learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() ); +} + +void Spell::EffectDispel(uint32 i) +{ + if(!unitTarget) + return; + + // Fill possible dispell list + std::vector <Aura *> dispel_list; + + // Create dispel mask by dispel type + uint32 dispel_type = m_spellInfo->EffectMiscValue[i]; + uint32 dispelMask = GetDispellMask( DispelType(dispel_type) ); + Unit::AuraMap const& auras = unitTarget->GetAuras(); + for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + { + Aura *aur = (*itr).second; + if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask) + { + if(aur->GetSpellProto()->Dispel == DISPEL_MAGIC) + { + bool positive = true; + if (!aur->IsPositive()) + positive = false; + else + positive = (aur->GetSpellProto()->AttributesEx & SPELL_ATTR_EX_NEGATIVE)==0; + + // do not remove positive auras if friendly target + // negative auras if non-friendly target + if(positive == unitTarget->IsFriendlyTo(m_caster)) + continue; + } + // Add aura to dispel list + dispel_list.push_back(aur); + } + } + // Ok if exist some buffs for dispel try dispel it + if (!dispel_list.empty()) + { + std::list < std::pair<uint32,uint64> > success_list;// (spell_id,casterGuid) + std::list < uint32 > fail_list; // spell_id + int32 list_size = dispel_list.size(); + // Dispell N = damage buffs (or while exist buffs for dispel) + for (int32 count=0; count < damage && list_size > 0; ++count) + { + // Random select buff for dispel + Aura *aur = dispel_list[urand(0, list_size-1)]; + + SpellEntry const* spellInfo = aur->GetSpellProto(); + // Base dispel chance + // TODO: possible chance depend from spell level?? + int32 miss_chance = 0; + // Apply dispel mod from aura caster + if (Unit *caster = aur->GetCaster()) + { + if ( Player* modOwner = caster->GetSpellModOwner() ) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance, this); + } + // Try dispel + if (roll_chance_i(miss_chance)) + fail_list.push_back(aur->GetId()); + else + success_list.push_back(std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID())); + // Remove buff from list for prevent doubles + for (std::vector<Aura *>::iterator j = dispel_list.begin(); j != dispel_list.end(); ) + { + Aura *dispeled = *j; + if (dispeled->GetId() == aur->GetId() && dispeled->GetCasterGUID() == aur->GetCasterGUID()) + { + j = dispel_list.erase(j); + --list_size; + } + else + ++j; + } + } + // Send success log and really remove auras + if (!success_list.empty()) + { + int32 count = success_list.size(); + WorldPacket data(SMSG_SPELLDISPELLOG, 8+8+4+1+4+count*5); + data.append(unitTarget->GetPackGUID()); // Victim GUID + data.append(m_caster->GetPackGUID()); // Caster GUID + data << uint32(m_spellInfo->Id); // Dispell spell id + data << uint8(0); // not used + data << uint32(count); // count + for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j) + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first); + data << uint32(spellInfo->Id); // Spell Id + data << uint8(0); // 0 - dispeled !=0 cleansed + unitTarget->RemoveAurasDueToSpellByDispel(spellInfo->Id, j->second, m_caster); + } + m_caster->SendMessageToSet(&data, true); + + // On succes dispel + // Devour Magic + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == 12) + { + uint32 heal_spell = 0; + switch (m_spellInfo->Id) + { + case 19505: heal_spell = 19658; break; + case 19731: heal_spell = 19732; break; + case 19734: heal_spell = 19733; break; + case 19736: heal_spell = 19735; break; + case 27276: heal_spell = 27278; break; + case 27277: heal_spell = 27279; break; + default: + sLog.outDebug("Spell for Devour Magic %d not handled in Spell::EffectDispel", m_spellInfo->Id); + break; + } + if (heal_spell) + m_caster->CastSpell(m_caster, heal_spell, true); + } + } + // Send fail log to client + if (!fail_list.empty()) + { + // Failed to dispell + WorldPacket data(SMSG_DISPEL_FAILED, 8+8+4+4*fail_list.size()); + data << uint64(m_caster->GetGUID()); // Caster GUID + data << uint64(unitTarget->GetGUID()); // Victim GUID + data << uint32(m_spellInfo->Id); // Dispell spell id + for (std::list< uint32 >::iterator j = fail_list.begin(); j != fail_list.end(); ++j) + data << uint32(*j); // Spell Id + m_caster->SendMessageToSet(&data, true); + } + } +} + +void Spell::EffectDualWield(uint32 /*i*/) +{ + if (unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->SetCanDualWield(true); +} + +void Spell::EffectPull(uint32 /*i*/) +{ + // TODO: create a proper pull towards distract spell center for distract + sLog.outDebug("WORLD: Spell Effect DUMMY"); +} + +void Spell::EffectDistract(uint32 /*i*/) +{ + // Check for possible target + if (!unitTarget || unitTarget->isInCombat()) + return; + + // target must be OK to do this + if( unitTarget->hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING ) ) + return; + + float angle = unitTarget->GetAngle(m_targets.m_destX, m_targets.m_destY); + + if ( unitTarget->GetTypeId() == TYPEID_PLAYER ) + { + // For players just turn them + WorldPacket data; + ((Player*)unitTarget)->BuildTeleportAckMsg(&data, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), angle); + ((Player*)unitTarget)->GetSession()->SendPacket( &data ); + ((Player*)unitTarget)->SetPosition(unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), angle, false); + } + else + { + // Set creature Distracted, Stop it, And turn it + unitTarget->SetOrientation(angle); + unitTarget->StopMoving(); + unitTarget->GetMotionMaster()->MoveDistract(damage*1000); + } +} + +void Spell::EffectPickPocket(uint32 /*i*/) +{ + if( m_caster->GetTypeId() != TYPEID_PLAYER ) + return; + + // victim must be creature and attackable + if( !unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->IsFriendlyTo(unitTarget) ) + return; + + // victim have to be alive and humanoid or undead + if( unitTarget->isAlive() && (unitTarget->GetCreatureTypeMask() &CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) != 0) + { + int32 chance = 10 + int32(m_caster->getLevel()) - int32(unitTarget->getLevel()); + + if (chance > irand(0, 19)) + { + // Stealing successful + //sLog.outDebug("Sending loot from pickpocket"); + ((Player*)m_caster)->SendLoot(unitTarget->GetGUID(),LOOT_PICKPOCKETING); + } + else + { + // Reveal action + get attack + m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + if (((Creature*)unitTarget)->AI()) + ((Creature*)unitTarget)->AI()->AttackStart(m_caster); + } + } +} + +void Spell::EffectAddFarsight(uint32 i) +{ + float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + int32 duration = GetSpellDuration(m_spellInfo); + DynamicObject* dynObj = new DynamicObject; + if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, i, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius)) + { + delete dynObj; + return; + } + dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65); + dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x80000002); + m_caster->AddDynObject(dynObj); + MapManager::Instance().GetMap(dynObj->GetMapId(), dynObj)->Add(dynObj); + m_caster->SetUInt64Value(PLAYER_FARSIGHT, dynObj->GetGUID()); +} + +void Spell::EffectSummonWild(uint32 i) +{ + uint32 creature_entry = m_spellInfo->EffectMiscValue[i]; + if(!creature_entry) + return; + + uint32 level = m_caster->getLevel(); + + // level of creature summoned using engineering item based at engineering skill level + if(m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem) + { + ItemPrototype const *proto = m_CastItem->GetProto(); + if(proto && proto->RequiredSkill == SKILL_ENGINERING) + { + uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING); + if(skill202) + { + level = skill202/5; + } + } + } + + // select center of summon position + float center_x = m_targets.m_destX; + float center_y = m_targets.m_destY; + float center_z = m_targets.m_destZ; + + float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + + int32 amount = damage > 0 ? damage : 1; + + for(int32 count = 0; count < amount; ++count) + { + float px, py, pz; + // If dest location if present + if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + // Summon 1 unit in dest location + if (count == 0) + { + px = m_targets.m_destX; + py = m_targets.m_destY; + pz = m_targets.m_destZ; + } + // Summon in random point all other units if location present + else + m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz); + } + // Summon if dest location not present near caster + else + m_caster->GetClosePoint(px,py,pz,3.0f); + + int32 duration = GetSpellDuration(m_spellInfo); + + TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN; + + m_caster->SummonCreature(creature_entry,px,py,pz,m_caster->GetOrientation(),summonType,duration); + } +} + +void Spell::EffectSummonGuardian(uint32 i) +{ + uint32 pet_entry = m_spellInfo->EffectMiscValue[i]; + if(!pet_entry) + return; + + // Jewelery statue case (totem like) + if(m_spellInfo->SpellIconID==2056) + { + EffectSummonTotem(i); + return; + } + + // set timer for unsummon + int32 duration = GetSpellDuration(m_spellInfo); + + // Search old Guardian only for players (if casted spell not have duration or cooldown) + // FIXME: some guardians have control spell applied and controlled by player and anyway player can't summon in this time + // so this code hack in fact + if( m_caster->GetTypeId() == TYPEID_PLAYER && (duration <= 0 || GetSpellRecoveryTime(m_spellInfo)==0) ) + if(((Player*)m_caster)->HasGuardianWithEntry(pet_entry)) + return; // find old guardian, ignore summon + + // in another case summon new + uint32 level = m_caster->getLevel(); + + // level of pet summoned using engineering item based at engineering skill level + if(m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem) + { + ItemPrototype const *proto = m_CastItem->GetProto(); + if(proto && proto->RequiredSkill == SKILL_ENGINERING) + { + uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING); + if(skill202) + { + level = skill202/5; + } + } + } + + // select center of summon position + float center_x = m_targets.m_destX; + float center_y = m_targets.m_destY; + float center_z = m_targets.m_destZ; + + float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + + int32 amount = damage > 0 ? damage : 1; + + for(int32 count = 0; count < amount; ++count) + { + Pet* spawnCreature = new Pet(GUARDIAN_PET); + + Map *map = m_caster->GetMap(); + uint32 pet_number = objmgr.GeneratePetNumber(); + if(!spawnCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map,m_spellInfo->EffectMiscValue[i], pet_number)) + { + sLog.outError("no such creature entry %u",m_spellInfo->EffectMiscValue[i]); + delete spawnCreature; + return; + } + + float px, py, pz; + // If dest location if present + if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + // Summon 1 unit in dest location + if (count == 0) + { + px = m_targets.m_destX; + py = m_targets.m_destY; + pz = m_targets.m_destZ; + } + // Summon in random point all other units if location present + else + m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz); + } + // Summon if dest location not present near caster + else + m_caster->GetClosePoint(px,py,pz,spawnCreature->GetObjectSize()); + + spawnCreature->Relocate(px,py,pz,m_caster->GetOrientation()); + + if(!spawnCreature->IsPositionValid()) + { + sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY()); + delete spawnCreature; + return; + } + + if(duration > 0) + spawnCreature->SetDuration(duration); + + spawnCreature->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID()); + spawnCreature->setPowerType(POWER_MANA); + spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS , 0); + spawnCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction()); + spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS,0); + spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0); + spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0); + spawnCreature->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID()); + spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + spawnCreature->InitStatsForLevel(level); + spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false); + + spawnCreature->AIM_Initialize(); + + if(m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->AddGuardian(spawnCreature); + + map->Add((Creature*)spawnCreature); + } +} + +void Spell::EffectTeleUnitsFaceCaster(uint32 i) +{ + if(!unitTarget) + return; + + if(unitTarget->isInFlight()) + return; + + uint32 mapid = m_caster->GetMapId(); + float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + + float fx,fy,fz; + m_caster->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis); + + if(unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, -m_caster->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); + else + MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)m_caster, fx, fy, fz, -m_caster->GetOrientation()); +} + +void Spell::EffectLearnSkill(uint32 i) +{ + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + if(damage < 0) + return; + + uint32 skillid = m_spellInfo->EffectMiscValue[i]; + uint16 skillval = ((Player*)unitTarget)->GetPureSkillValue(skillid); + ((Player*)unitTarget)->SetSkill(skillid, skillval?skillval:1, damage*75); +} + +void Spell::EffectAddHonor(uint32 /*i*/) +{ + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + sLog.outDebug("SpellEffect::AddHonor called for spell_id %u , that rewards %d honor points to player: %u", m_spellInfo->Id, this->damage, ((Player*)unitTarget)->GetGUIDLow()); + + // TODO: find formula for honor reward based on player's level! + + // now fixed only for level 70 players: + if (((Player*)unitTarget)->getLevel() == 70) + ((Player*)unitTarget)->RewardHonor(NULL, 1, this->damage); +} + +void Spell::EffectTradeSkill(uint32 /*i*/) +{ + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + // uint32 skillid = m_spellInfo->EffectMiscValue[i]; + // uint16 skillmax = ((Player*)unitTarget)->(skillid); + // ((Player*)unitTarget)->SetSkill(skillid,skillval?skillval:1,skillmax+75); +} + +void Spell::EffectEnchantItemPerm(uint32 i) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + if (!itemTarget) + return; + + Player* p_caster = (Player*)m_caster; + + p_caster->UpdateCraftSkill(m_spellInfo->Id); + + if (m_spellInfo->EffectMiscValue[i]) + { + uint32 enchant_id = m_spellInfo->EffectMiscValue[i]; + + SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!pEnchant) + return; + + // item can be in trade slot and have owner diff. from caster + Player* item_owner = itemTarget->GetOwner(); + if(!item_owner) + return; + + if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + sLog.outCommand("GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)", + p_caster->GetName(),p_caster->GetSession()->GetAccountId(), + itemTarget->GetProto()->Name1,itemTarget->GetEntry(), + item_owner->GetName(),item_owner->GetSession()->GetAccountId()); + + // remove old enchanting before applying new if equipped + item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,false); + + itemTarget->SetEnchantment(PERM_ENCHANTMENT_SLOT, enchant_id, 0, 0); + + // add new enchanting if equipped + item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,true); + } +} + +void Spell::EffectEnchantItemTmp(uint32 i) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player* p_caster = (Player*)m_caster; + + if(!itemTarget) + return; + + uint32 enchant_id = m_spellInfo->EffectMiscValue[i]; + + // Shaman Rockbiter Weapon + if(i==0 && m_spellInfo->Effect[1]==SPELL_EFFECT_DUMMY) + { + int32 enchnting_damage = m_currentBasePoints[1]+1; + + // enchanting id selected by calculated damage-per-sec stored in Effect[1] base value + // with already applied percent bonus from Elemental Weapons talent + // Note: damage calculated (correctly) with rounding int32(float(v)) but + // RW enchantments applied damage int32(float(v)+0.5), this create 0..1 difference sometime + switch(enchnting_damage) + { + // Rank 1 + case 2: enchant_id = 29; break; // 0% [ 7% == 2, 14% == 2, 20% == 2] + // Rank 2 + case 4: enchant_id = 6; break; // 0% [ 7% == 4, 14% == 4] + case 5: enchant_id = 3025; break; // 20% + // Rank 3 + case 6: enchant_id = 1; break; // 0% [ 7% == 6, 14% == 6] + case 7: enchant_id = 3027; break; // 20% + // Rank 4 + case 9: enchant_id = 3032; break; // 0% [ 7% == 6] + case 10: enchant_id = 503; break; // 14% + case 11: enchant_id = 3031; break; // 20% + // Rank 5 + case 15: enchant_id = 3035; break; // 0% + case 16: enchant_id = 1663; break; // 7% + case 17: enchant_id = 3033; break; // 14% + case 18: enchant_id = 3034; break; // 20% + // Rank 6 + case 28: enchant_id = 3038; break; // 0% + case 29: enchant_id = 683; break; // 7% + case 31: enchant_id = 3036; break; // 14% + case 33: enchant_id = 3037; break; // 20% + // Rank 7 + case 40: enchant_id = 3041; break; // 0% + case 42: enchant_id = 1664; break; // 7% + case 45: enchant_id = 3039; break; // 14% + case 48: enchant_id = 3040; break; // 20% + // Rank 8 + case 49: enchant_id = 3044; break; // 0% + case 52: enchant_id = 2632; break; // 7% + case 55: enchant_id = 3042; break; // 14% + case 58: enchant_id = 3043; break; // 20% + // Rank 9 + case 62: enchant_id = 2633; break; // 0% + case 66: enchant_id = 3018; break; // 7% + case 70: enchant_id = 3019; break; // 14% + case 74: enchant_id = 3020; break; // 20% + default: + sLog.outError("Spell::EffectEnchantItemTmp: Damage %u not handled in S'RW",enchnting_damage); + return; + } + } + + if (!enchant_id) + { + sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have 0 as enchanting id",m_spellInfo->Id,i); + return; + } + + SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!pEnchant) + { + sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have not existed enchanting id %u ",m_spellInfo->Id,i,enchant_id); + return; + } + + // select enchantment duration + uint32 duration; + + // rogue family enchantments exception by duration + if(m_spellInfo->Id==38615) + duration = 1800; // 30 mins + // other rogue family enchantments always 1 hour (some have spell damage=0, but some have wrong data in EffBasePoints) + else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE) + duration = 3600; // 1 hour + // shaman family enchantments + else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_SHAMAN) + duration = 1800; // 30 mins + // other cases with this SpellVisual already selected + else if(m_spellInfo->SpellVisual==215) + duration = 1800; // 30 mins + // some fishing pole bonuses + else if(m_spellInfo->SpellVisual==563) + duration = 600; // 10 mins + // shaman rockbiter enchantments + else if(m_spellInfo->SpellVisual==0) + duration = 1800; // 30 mins + else if(m_spellInfo->Id==29702) + duration = 300; // 5 mins + else if(m_spellInfo->Id==37360) + duration = 300; // 5 mins + // default case + else + duration = 3600; // 1 hour + + // item can be in trade slot and have owner diff. from caster + Player* item_owner = itemTarget->GetOwner(); + if(!item_owner) + return; + + if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + sLog.outCommand("GM %s (Account: %u) enchanting(temp): %s (Entry: %d) for player: %s (Account: %u)", + p_caster->GetName(),p_caster->GetSession()->GetAccountId(), + itemTarget->GetProto()->Name1,itemTarget->GetEntry(), + item_owner->GetName(),item_owner->GetSession()->GetAccountId()); + + // remove old enchanting before applying new if equipped + item_owner->ApplyEnchantment(itemTarget,TEMP_ENCHANTMENT_SLOT,false); + + itemTarget->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, duration*1000, 0); + + // add new enchanting if equipped + item_owner->ApplyEnchantment(itemTarget,TEMP_ENCHANTMENT_SLOT,true); +} + +void Spell::EffectTameCreature(uint32 /*i*/) +{ + if(m_caster->GetPetGUID()) + return; + + if(!unitTarget) + return; + + if(unitTarget->GetTypeId() == TYPEID_PLAYER) + return; + + Creature* creatureTarget = (Creature*)unitTarget; + + if(creatureTarget->isPet()) + return; + + if(m_caster->getClass() == CLASS_HUNTER) + { + // cast finish successfully + //SendChannelUpdate(0); + finish(); + + Pet* pet = new Pet(HUNTER_PET); + + if(!pet->CreateBaseAtCreature(creatureTarget)) + { + delete pet; + return; + } + + creatureTarget->setDeathState(JUST_DIED); + creatureTarget->RemoveCorpse(); + creatureTarget->SetHealth(0); // just for nice GM-mode view + + pet->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_caster->GetGUID()); + pet->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID()); + pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction()); + pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + if(!pet->InitStatsForLevel(creatureTarget->getLevel())) + { + sLog.outError("ERROR: InitStatsForLevel() in EffectTameCreature failed! Pet deleted."); + delete pet; + return; + } + + // prepare visual effect for levelup + pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()-1); + + pet->GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true); + // this enables pet details window (Shift+P) + pet->AIM_Initialize(); + pet->InitPetCreateSpells(); + pet->SetHealth(pet->GetMaxHealth()); + + MapManager::Instance().GetMap(pet->GetMapId(), pet)->Add((Creature*)pet); + + // visual effect for levelup + pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()); + + if(m_caster->GetTypeId() == TYPEID_PLAYER) + { + m_caster->SetPet(pet); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + ((Player*)m_caster)->PetSpellInitialize(); + } + } +} + +void Spell::EffectSummonPet(uint32 i) +{ + uint32 petentry = m_spellInfo->EffectMiscValue[i]; + + Pet *OldSummon = m_caster->GetPet(); + + // if pet requested type already exist + if( OldSummon ) + { + if(petentry == 0 || OldSummon->GetEntry() == petentry) + { + // pet in corpse state can't be summoned + if( OldSummon->isDead() ) + return; + + MapManager::Instance().GetMap(OldSummon->GetMapId(), OldSummon)->Remove((Creature*)OldSummon,false); + OldSummon->SetMapId(m_caster->GetMapId()); + + float px, py, pz; + m_caster->GetClosePoint(px, py, pz, OldSummon->GetObjectSize()); + + OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation()); + MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->Add((Creature*)OldSummon); + + if(m_caster->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled() ) + { + ((Player*)m_caster)->PetSpellInitialize(); + } + return; + } + + if(m_caster->GetTypeId() == TYPEID_PLAYER) + ((Player*)m_caster)->RemovePet(OldSummon,(OldSummon->getPetType()==HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT),false); + else + return; + } + + Pet* NewSummon = new Pet; + + // petentry==0 for hunter "call pet" (current pet summoned if any) + if(NewSummon->LoadPetFromDB(m_caster,petentry)) + { + if(NewSummon->getPetType()==SUMMON_PET) + { + // Remove Demonic Sacrifice auras (known pet) + Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();) + { + if((*itr)->GetModifier()->m_miscvalue==2228) + { + m_caster->RemoveAurasDueToSpell((*itr)->GetId()); + itr = auraClassScripts.begin(); + } + else + ++itr; + } + } + + return; + } + + // not error in case fail hunter call pet + if(!petentry) + { + delete NewSummon; + return; + } + + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(petentry); + + if(!cInfo) + { + sLog.outError("EffectSummonPet: creature entry %u not found.",petentry); + delete NewSummon; + return; + } + + Map *map = m_caster->GetMap(); + uint32 pet_number = objmgr.GeneratePetNumber(); + if(!NewSummon->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map, petentry, pet_number)) + { + delete NewSummon; + return; + } + + float px, py, pz; + m_caster->GetClosePoint(px, py, pz, NewSummon->GetObjectSize()); + + NewSummon->Relocate(px, py, pz, m_caster->GetOrientation()); + + if(!NewSummon->IsPositionValid()) + { + sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", NewSummon->GetGUIDLow(), NewSummon->GetEntry(), NewSummon->GetPositionX(), NewSummon->GetPositionY()); + delete NewSummon; + return; + } + + uint32 petlevel = m_caster->getLevel(); + NewSummon->setPetType(SUMMON_PET); + + uint32 faction = m_caster->getFaction(); + if(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isTotem()) + { + Unit* owner = ((Totem*)m_caster)->GetOwner(); + if(owner) + faction = owner->getFaction(); + NewSummon->GetCharmInfo()->SetReactState(REACT_AGGRESSIVE); + } + + NewSummon->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_caster->GetGUID()); + NewSummon->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID()); + NewSummon->SetUInt32Value(UNIT_NPC_FLAGS , 0); + NewSummon->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction); + NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_0,2048); + NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_1,0); + NewSummon->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,time(NULL)); + NewSummon->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0); + NewSummon->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000); + NewSummon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + NewSummon->GetCharmInfo()->SetPetNumber(pet_number, true); + // this enables pet details window (Shift+P) + + // this enables popup window (pet dismiss, cancel), hunter pet additional flags set later + NewSummon->SetUInt32Value(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE); + + NewSummon->InitStatsForLevel( petlevel); + NewSummon->InitPetCreateSpells(); + + if(NewSummon->getPetType()==SUMMON_PET) + { + // Remove Demonic Sacrifice auras (new pet) + Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();) + { + if((*itr)->GetModifier()->m_miscvalue==2228) + { + m_caster->RemoveAurasDueToSpell((*itr)->GetId()); + itr = auraClassScripts.begin(); + } + else + ++itr; + } + + // generate new name for summon pet + std::string new_name=objmgr.GeneratePetName(petentry); + if(!new_name.empty()) + NewSummon->SetName(new_name); + } + else if(NewSummon->getPetType()==HUNTER_PET) + NewSummon->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED); + + NewSummon->AIM_Initialize(); + NewSummon->SetHealth(NewSummon->GetMaxHealth()); + NewSummon->SetPower(POWER_MANA, NewSummon->GetMaxPower(POWER_MANA)); + + map->Add((Creature*)NewSummon); + + m_caster->SetPet(NewSummon); + sLog.outDebug("New Pet has guid %u", NewSummon->GetGUIDLow()); + + if(m_caster->GetTypeId() == TYPEID_PLAYER) + { + NewSummon->SavePetToDB(PET_SAVE_AS_CURRENT); + ((Player*)m_caster)->PetSpellInitialize(); + } +} + +void Spell::EffectLearnPetSpell(uint32 i) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *_player = (Player*)m_caster; + + Pet *pet = _player->GetPet(); + if(!pet) + return; + if(!pet->isAlive()) + return; + + SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]); + if(!learn_spellproto) + return; + + pet->SetTP(pet->m_TrainingPoints - pet->GetTPForSpell(learn_spellproto->Id)); + pet->learnSpell(learn_spellproto->Id); + + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + _player->PetSpellInitialize(); +} + +void Spell::EffectTaunt(uint32 /*i*/) +{ + // this effect use before aura Taunt apply for prevent taunt already attacking target + // for spell as marked "non effective at already attacking target" + if(unitTarget && unitTarget->GetTypeId() != TYPEID_PLAYER) + { + if(unitTarget->getVictim()==m_caster) + { + SendCastResult(SPELL_FAILED_DONT_REPORT); + return; + } + } + + // 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()); +} + +void Spell::EffectWeaponDmg(uint32 i) +{ + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + + // multiple weapon dmg effect workaround + // execute only the last weapon damage + // and handle all effects at once + for (int j = 0; j < 3; j++) + { + switch(m_spellInfo->Effect[j]) + { + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + if (j < i) // we must calculate only at last weapon effect + return; + break; + } + } + + // some spell specific modifiers + bool customBonusDamagePercentMod = false; + float bonusDamagePercentMod = 1.0f; // applied to fixed effect damage bonus if set customBonusDamagePercentMod + float weaponDamagePercentMod = 1.0f; // applied to weapon damage (and to fixed effect damage bonus if customBonusDamagePercentMod not set + float totalDamagePercentMod = 1.0f; // applied to final bonus+weapon damage + bool normalized = false; + + int32 spell_bonus = 0; // bonus specific for spell + switch(m_spellInfo->SpellFamilyName) + { + case SPELLFAMILY_WARRIOR: + { + // Whirlwind, single only spell with 2 weapon white damage apply if have + if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x00000400000000LL)) + { + if(((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK,true)) + spell_bonus += m_caster->CalculateDamage (OFF_ATTACK, normalized); + } + // Devastate bonus and sunder armor refresh + else if(m_spellInfo->SpellVisual == 671 && m_spellInfo->SpellIconID == 1508) + { + customBonusDamagePercentMod = true; + bonusDamagePercentMod = 0.0f; // only applied if auras found + + Unit::AuraList const& list = unitTarget->GetAurasByType(SPELL_AURA_MOD_RESISTANCE); + for(Unit::AuraList::const_iterator itr=list.begin();itr!=list.end();++itr) + { + SpellEntry const *proto = (*itr)->GetSpellProto(); + if(proto->SpellVisual == 406 && proto->SpellIconID == 565) + { + int32 duration = GetSpellDuration(proto); + (*itr)->SetAuraDuration(duration); + (*itr)->UpdateAuraDuration(); + bonusDamagePercentMod += 1.0f; // +100% + } + } + } + break; + } + case SPELLFAMILY_ROGUE: + { + // Ambush + if(m_spellInfo->SpellFamilyFlags & 0x00000200LL) + { + customBonusDamagePercentMod = true; + bonusDamagePercentMod = 2.5f; // 250% + } + // Mutilate (for each hand) + else if(m_spellInfo->SpellFamilyFlags & 0x600000000LL) + { + bool found = false; + // fast check + if(unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON)) + found = true; + // full aura scan + else + { + Unit::AuraMap const& auras = unitTarget->GetAuras(); + for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr) + { + if(itr->second->GetSpellProto()->Dispel == DISPEL_POISON) + { + found = true; + break; + } + } + } + + if(found) + totalDamagePercentMod *= 1.5f; // 150% if poisoned + } + break; + } + case SPELLFAMILY_PALADIN: + { + // Seal of Command - receive benefit from Spell Damage and Healing + if(m_spellInfo->SpellFamilyFlags & 0x00000002000000LL) + { + spell_bonus += int32(0.20f*m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo))); + spell_bonus += int32(0.29f*m_caster->SpellBaseDamageBonusForVictim(GetSpellSchoolMask(m_spellInfo), unitTarget)); + } + break; + } + case SPELLFAMILY_SHAMAN: + { + // Skyshatter Harness item set bonus + // Stormstrike + if(m_spellInfo->SpellFamilyFlags & 0x001000000000LL) + { + Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i) + { + // Stormstrike AP Buff + if ( (*i)->GetModifier()->m_miscvalue == 5634 ) + { + m_caster->CastSpell(m_caster,38430,true,NULL,*i); + break; + } + } + } + } + } + + int32 fixed_bonus = 0; + for (int j = 0; j < 3; j++) + { + switch(m_spellInfo->Effect[j]) + { + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + fixed_bonus += CalculateDamage(j,unitTarget); + break; + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + fixed_bonus += CalculateDamage(j,unitTarget); + normalized = true; + break; + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + weaponDamagePercentMod *= float(CalculateDamage(j,unitTarget)) / 100.0f; + + // applied only to prev.effects fixed damage + if(customBonusDamagePercentMod) + fixed_bonus = int32(fixed_bonus*bonusDamagePercentMod); + else + fixed_bonus = int32(fixed_bonus*weaponDamagePercentMod); + break; + default: + break; // not weapon damage effect, just skip + } + } + + // non-weapon damage + int32 bonus = spell_bonus + fixed_bonus; + + // apply to non-weapon bonus weapon total pct effect, weapon total flat effect included in weapon damage + if(bonus) + { + UnitMods unitMod; + switch(m_attackType) + { + 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; + } + + float weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT); + bonus = int32(bonus*weapon_total_pct); + } + + // + weapon damage with applied weapon% dmg to base weapon damage in call + bonus += int32(m_caster->CalculateDamage(m_attackType, normalized)*weaponDamagePercentMod); + + // total damage + bonus = int32(bonus*totalDamagePercentMod); + + // prevent negative damage + uint32 eff_damage = uint32(bonus > 0 ? bonus : 0); + + const uint32 nohitMask = HITINFO_ABSORB | HITINFO_RESIST | HITINFO_MISS; + + uint32 hitInfo = 0; + VictimState victimState = VICTIMSTATE_NORMAL; + uint32 blocked_dmg = 0; + uint32 absorbed_dmg = 0; + uint32 resisted_dmg = 0; + CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL ); + + m_caster->DoAttackDamage(unitTarget, &eff_damage, &cleanDamage, &blocked_dmg, m_spellSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, m_attackType, m_spellInfo, m_IsTriggeredSpell); + + if ((hitInfo & nohitMask) && m_attackType != RANGED_ATTACK) // not send ranged miss/etc + m_caster->SendAttackStateUpdate(hitInfo & nohitMask, unitTarget, 1, m_spellSchoolMask, eff_damage, absorbed_dmg, resisted_dmg, VICTIMSTATE_NORMAL, blocked_dmg); + + bool criticalhit = (hitInfo & HITINFO_CRITICALHIT); + m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, eff_damage, m_spellSchoolMask, absorbed_dmg, resisted_dmg, false, blocked_dmg, criticalhit); + + if (eff_damage > (absorbed_dmg + resisted_dmg + blocked_dmg)) + { + eff_damage -= (absorbed_dmg + resisted_dmg + blocked_dmg); + } + else + { + cleanDamage.damage += eff_damage; + eff_damage = 0; + } + + // SPELL_SCHOOL_NORMAL use for weapon-like threat and rage calculation + m_caster->DealDamage(unitTarget, eff_damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, true); + + // Hemorrhage + if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & 0x2000000)) + { + if(m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->AddComboPoints(unitTarget, 1); + } + // Mangle (Cat): CP + if(m_spellInfo->SpellFamilyName==SPELLFAMILY_DRUID && (m_spellInfo->SpellFamilyFlags==0x0000040000000000LL)) + { + if(m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->AddComboPoints(unitTarget,1); + } + + + // take ammo + if(m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER) + { + Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK ); + + // wands don't have ammo + if(!pItem || pItem->IsBroken() || pItem->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_WAND) + return; + + if( pItem->GetProto()->InventoryType == INVTYPE_THROWN ) + { + if(pItem->GetMaxStackCount()==1) + { + // decrease durability for non-stackable throw weapon + ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED); + } + else + { + // decrease items amount for stackable throw weapon + uint32 count = 1; + ((Player*)m_caster)->DestroyItemCount( pItem, count, true); + } + } + else if(uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID)) + ((Player*)m_caster)->DestroyItemCount(ammo, 1, true); + } +} + +void Spell::EffectThreat(uint32 /*i*/) +{ + if(!unitTarget || !unitTarget->isAlive() || !m_caster->isAlive()) + return; + + if(!unitTarget->CanHaveThreatList()) + return; + + unitTarget->AddThreat(m_caster, float(damage)); +} + +void Spell::EffectHealMaxHealth(uint32 /*i*/) +{ + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + + uint32 heal = m_caster->GetMaxHealth(); + + int32 gain = unitTarget->ModifyHealth(heal); + unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo); + + m_caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, heal); +} + +void Spell::EffectInterruptCast(uint32 /*i*/) +{ + if(!unitTarget) + return; + if(!unitTarget->isAlive()) + return; + + // TODO: not all spells that used this effect apply cooldown at school spells + // also exist case: apply cooldown to interrupted cast only and to all spells + for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++) + { + if (unitTarget->m_currentSpells[i]) + { + // check if we can interrupt spell + if ( unitTarget->m_currentSpells[i]->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT && unitTarget->m_currentSpells[i]->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE ) + { + unitTarget->ProhibitSpellScholl(GetSpellSchoolMask(unitTarget->m_currentSpells[i]->m_spellInfo), GetSpellDuration(m_spellInfo)); + unitTarget->InterruptSpell(i,false); + } + } + } +} + +void Spell::EffectSummonObjectWild(uint32 i) +{ + uint32 gameobject_id = m_spellInfo->EffectMiscValue[i]; + + GameObject* pGameObj = new GameObject; + + WorldObject* target = focusObject; + if( !target ) + target = m_caster; + + float x,y,z; + if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + } + else + m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE); + + Map *map = target->GetMap(); + + if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map, + x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1)) + { + delete pGameObj; + return; + } + + int32 duration = GetSpellDuration(m_spellInfo); + pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetSpellId(m_spellInfo->Id); + + if(pGameObj->GetGoType() != GAMEOBJECT_TYPE_FLAGDROP) // make dropped flag clickable for other players (not set owner guid (created by) for this)... + m_caster->AddGameObject(pGameObj); + map->Add(pGameObj); + + if(pGameObj->GetMapId() == 489 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //WS + { + if(m_caster->GetTypeId() == TYPEID_PLAYER) + { + Player *pl = (Player*)m_caster; + BattleGround* bg = ((Player *)m_caster)->GetBattleGround(); + if(bg && bg->GetTypeID()==BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS) + { + uint32 team = ALLIANCE; + + if(pl->GetTeam() == team) + team = HORDE; + + ((BattleGroundWS*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID(),team); + } + } + } + + if(pGameObj->GetMapId() == 566 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //EY + { + if(m_caster->GetTypeId() == TYPEID_PLAYER) + { + BattleGround* bg = ((Player *)m_caster)->GetBattleGround(); + if(bg && bg->GetTypeID()==BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS) + { + ((BattleGroundEY*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID()); + } + } + } + + if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry()) + { + GameObject* linkedGO = new GameObject; + if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, map, + x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1)) + { + linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0); + linkedGO->SetSpellId(m_spellInfo->Id); + + m_caster->AddGameObject(linkedGO); + map->Add(linkedGO); + } + else + { + delete linkedGO; + linkedGO = NULL; + return; + } + } +} + +void Spell::EffectScriptEffect(uint32 effIndex) +{ + // TODO: we must implement hunter pet summon at login there (spell 6962) + + // by spell id + switch(m_spellInfo->Id) + { + // Bending Shinbone + case 8856: + { + if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + + uint32 spell_id = 0; + switch(urand(1,5)) + { + case 1: spell_id = 8854; break; + default: spell_id = 8855; break; + } + + m_caster->CastSpell(m_caster,spell_id,true,NULL); + return; + } + + // Healthstone creating spells + case 6201: + case 6202: + case 5699: + case 11729: + case 11730: + case 27230: + { + uint32 itemtype; + uint32 rank = 0; + Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) + { + if((*i)->GetId() == 18692) + { + rank = 1; + break; + } + else if((*i)->GetId() == 18693) + { + rank = 2; + break; + } + } + + static uint32 const itypes[6][3] = { + { 5512,19004,19005}, // Minor Healthstone + { 5511,19006,19007}, // Lesser Healthstone + { 5509,19008,19009}, // Healthstone + { 5510,19010,19011}, // Greater Healthstone + { 9421,19012,19013}, // Major Healthstone + {22103,22104,22105} // Master Healthstone + }; + + switch(m_spellInfo->Id) + { + case 6201: itemtype=itypes[0][rank];break; // Minor Healthstone + case 6202: itemtype=itypes[1][rank];break; // Lesser Healthstone + case 5699: itemtype=itypes[2][rank];break; // Healthstone + case 11729: itemtype=itypes[3][rank];break; // Greater Healthstone + case 11730: itemtype=itypes[4][rank];break; // Major Healthstone + case 27230: itemtype=itypes[5][rank];break; // Master Healthstone + default: + return; + } + DoCreateItem( effIndex, itemtype ); + return; + } + // Brittle Armor - need remove one 24575 Brittle Armor aura + case 24590: + unitTarget->RemoveSingleAuraFromStack(24575, 0); + unitTarget->RemoveSingleAuraFromStack(24575, 1); + return; + // Mercurial Shield - need remove one 26464 Mercurial Shield aura + case 26465: + unitTarget->RemoveSingleAuraFromStack(26464, 0); + return; + // Orb teleport spells + case 25140: + case 25143: + case 25650: + case 25652: + case 29128: + case 29129: + case 35376: + case 35727: + { + if(!unitTarget) + return; + + uint32 spellid; + switch(m_spellInfo->Id) + { + case 25140: spellid = 32571; break; + case 25143: spellid = 32572; break; + case 25650: spellid = 30140; break; + case 25652: spellid = 30141; break; + case 29128: spellid = 32568; break; + case 29129: spellid = 32569; break; + case 35376: spellid = 25649; break; + case 35727: spellid = 35730; break; + default: + return; + } + + unitTarget->CastSpell(unitTarget,spellid,false); + return; + } + + // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell) + case 22539: + case 22972: + case 22975: + case 22976: + case 22977: + case 22978: + case 22979: + case 22980: + case 22981: + case 22982: + case 22983: + case 22984: + case 22985: + { + if(!unitTarget || !unitTarget->isAlive()) + return; + + // Onyxia Scale Cloak + if(unitTarget->GetDummyAura(22683)) + return; + + // Shadow Flame + m_caster->CastSpell(unitTarget, 22682, true); + return; + } + break; + + // Summon Black Qiraji Battle Tank + case 26656: + { + if(!unitTarget) + return; + + // Prevent stacking of mounts + unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + + // Two separate mounts depending on area id (allows use both in and out of specific instance) + if (unitTarget->GetAreaId() == 3428) + unitTarget->CastSpell(unitTarget, 25863, false); + else + unitTarget->CastSpell(unitTarget, 26655, false); + break; + } + // Piccolo of the Flaming Fire + case 17512: + { + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE); + break; + } + + // Dreaming Glory + case 28698: + { + if(!unitTarget) + return; + unitTarget->CastSpell(unitTarget, 28694, true); + break; + } + + // Netherbloom + case 28702: + { + if(!unitTarget) + return; + // 25% chance of casting a random buff + if(roll_chance_i(75)) + return; + + // triggered spells are 28703 to 28707 + // Note: some sources say, that there was the possibility of + // receiving a debuff. However, this seems to be removed by a patch. + const uint32 spellid = 28703; + + // don't overwrite an existing aura + for(uint8 i=0; i<5; i++) + if(unitTarget->HasAura(spellid+i, 0)) + return; + unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true); + break; + } + + // Nightmare Vine + case 28720: + { + if(!unitTarget) + return; + // 25% chance of casting Nightmare Pollen + if(roll_chance_i(75)) + return; + unitTarget->CastSpell(unitTarget, 28721, true); + break; + } + + // Mirren's Drinking Hat + case 29830: + { + uint32 item = 0; + switch ( urand(1,6) ) + { + case 1: case 2: case 3: item = 23584; break;// Loch Modan Lager + case 4: case 5: item = 23585; break;// Stouthammer Lite + case 6: item = 23586; break;// Aerie Peak Pale Ale + } + if (item) + DoCreateItem(effIndex,item); + break; + } + // Improved Sprint + case 30918: + { + // Removes snares and roots. + uint32 mechanic_mask = (1<<MECHANIC_ROOT) | (1<<MECHANIC_SNARE); + Unit::AuraMap& Auras = unitTarget->GetAuras(); + for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) + { + next = iter; + ++next; + Aura *aur = iter->second; + if (!aur->IsPositive()) //only remove negative spells + { + // check for mechanic mask + if(GetSpellMechanicMask(aur->GetSpellProto(), aur->GetEffIndex()) & mechanic_mask) + { + unitTarget->RemoveAurasDueToSpell(aur->GetId()); + if(Auras.empty()) + break; + else + next = Auras.begin(); + } + } + } + break; + } + case 41126: // Flame Crash + { + if(!unitTarget) + return; + + unitTarget->CastSpell(unitTarget, 41131, true); + break; + } + case 44876: // Force Cast - Portal Effect: Sunwell Isle + { + if(!unitTarget) + return; + + unitTarget->CastSpell(unitTarget, 44870, true); + break; + } + + // Goblin Weather Machine + case 46203: + { + if(!unitTarget) + return; + + uint32 spellId; + switch(rand()%4) + { + case 0: + spellId=46740; + break; + case 1: + spellId=46739; + break; + case 2: + spellId=46738; + break; + case 3: + spellId=46736; + break; + } + unitTarget->CastSpell(unitTarget, spellId, true); + break; + } + } + + if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN ) + { + switch(m_spellInfo->SpellFamilyFlags) + { + // Judgement + case 0x800000: + { + if(!unitTarget || !unitTarget->isAlive()) + return; + uint32 spellId2 = 0; + + // all seals have aura dummy + Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator itr = m_dummyAuras.begin(); itr != m_dummyAuras.end(); ++itr) + { + SpellEntry const *spellInfo = (*itr)->GetSpellProto(); + + // search seal (all seals have judgement's aura dummy spell id in 2 effect + if ( !spellInfo || !IsSealSpell((*itr)->GetSpellProto()) || (*itr)->GetEffIndex() != 2 ) + continue; + + // must be calculated base at raw base points in spell proto, GetModifier()->m_value for S.Righteousness modified by SPELLMOD_DAMAGE + spellId2 = (*itr)->GetSpellProto()->EffectBasePoints[2]+1; + + if(spellId2 <= 1) + continue; + + // found, remove seal + m_caster->RemoveAurasDueToSpell((*itr)->GetId()); + + // Sanctified Judgement + Unit::AuraList const& m_auras = m_caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = m_auras.begin(); i != m_auras.end(); ++i) + { + if ((*i)->GetSpellProto()->SpellIconID == 205 && (*i)->GetSpellProto()->Attributes == 0x01D0LL) + { + int32 chance = (*i)->GetModifier()->m_amount; + if ( roll_chance_i(chance) ) + { + int32 mana = spellInfo->manaCost; + if ( Player* modOwner = m_caster->GetSpellModOwner() ) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, mana); + mana = int32(mana* 0.8f); + m_caster->CastCustomSpell(m_caster,31930,&mana,NULL,NULL,true,NULL,*i); + } + break; + } + } + + break; + } + + m_caster->CastSpell(unitTarget,spellId2,true); + return; + } + } + } + + // normal DB scripted effect + if(!unitTarget) + return; + + sLog.outDebug("Spell ScriptStart spellid %u in EffectScriptEffect ", m_spellInfo->Id); + sWorld.ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget); +} + +void Spell::EffectSanctuary(uint32 /*i*/) +{ + if(!unitTarget) + return; + //unitTarget->CombatStop(); + + unitTarget->CombatStop(); + unitTarget->getHostilRefManager().deleteReferences(); // stop all fighting + // Vanish allows to remove all threat and cast regular stealth so other spells can be used + if(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_VANISH)) + { + ((Player *)m_caster)->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); + } +} + +void Spell::EffectAddComboPoints(uint32 /*i*/) +{ + if(!unitTarget) + return; + + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + if(damage <= 0) + return; + + ((Player*)m_caster)->AddComboPoints(unitTarget, damage); +} + +void Spell::EffectDuel(uint32 i) +{ + if(!m_caster || !unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + Player *caster = (Player*)m_caster; + Player *target = (Player*)unitTarget; + + // caster or target already have requested duel + if( caster->duel || target->duel || target->GetSocial()->HasIgnore(caster->GetGUIDLow()) ) + return; + + // Players can only fight a duel with each other outside (=not inside dungeons and not in capital cities) + // Don't have to check the target's map since you cannot challenge someone across maps + if( caster->GetMapId() != 0 && caster->GetMapId() != 1 && caster->GetMapId() != 530) + { + SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here + return; + } + + AreaTableEntry const* casterAreaEntry = GetAreaEntryByAreaID(caster->GetZoneId()); + if(casterAreaEntry && (casterAreaEntry->flags & AREA_FLAG_CAPITAL) ) + { + SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here + return; + } + + AreaTableEntry const* targetAreaEntry = GetAreaEntryByAreaID(target->GetZoneId()); + if(targetAreaEntry && (targetAreaEntry->flags & AREA_FLAG_CAPITAL) ) + { + SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here + return; + } + + //CREATE DUEL FLAG OBJECT + GameObject* pGameObj = new GameObject; + + uint32 gameobject_id = m_spellInfo->EffectMiscValue[i]; + + Map *map = m_caster->GetMap(); + if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map, + m_caster->GetPositionX()+(unitTarget->GetPositionX()-m_caster->GetPositionX())/2 , + m_caster->GetPositionY()+(unitTarget->GetPositionY()-m_caster->GetPositionY())/2 , + m_caster->GetPositionZ(), + m_caster->GetOrientation(), 0, 0, 0, 0, 0, 1)) + { + delete pGameObj; + return; + } + + pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction() ); + pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()+1 ); + int32 duration = GetSpellDuration(m_spellInfo); + pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetSpellId(m_spellInfo->Id); + + m_caster->AddGameObject(pGameObj); + map->Add(pGameObj); + //END + + // Send request + WorldPacket data(SMSG_DUEL_REQUESTED, 16); + data << pGameObj->GetGUID(); + data << caster->GetGUID(); + caster->GetSession()->SendPacket(&data); + target->GetSession()->SendPacket(&data); + + // create duel-info + DuelInfo *duel = new DuelInfo; + duel->initiator = caster; + duel->opponent = target; + duel->startTime = 0; + duel->startTimer = 0; + caster->duel = duel; + + DuelInfo *duel2 = new DuelInfo; + duel2->initiator = caster; + duel2->opponent = caster; + duel2->startTime = 0; + duel2->startTimer = 0; + target->duel = duel2; + + caster->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID()); + target->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID()); +} + +void Spell::EffectStuck(uint32 /*i*/) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + if(!sWorld.getConfig(CONFIG_CAST_UNSTUCK)) + return; + + Player* pTarget = (Player*)unitTarget; + + sLog.outDebug("Spell Effect: Stuck"); + sLog.outDetail("Player %s (guid %u) used auto-unstuck future at map %u (%f, %f, %f)", pTarget->GetName(), pTarget->GetGUIDLow(), m_caster->GetMapId(), m_caster->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ()); + + if(pTarget->isInFlight()) + return; + + // homebind location is loaded always + pTarget->TeleportTo(pTarget->m_homebindMapId,pTarget->m_homebindX,pTarget->m_homebindY,pTarget->m_homebindZ,pTarget->GetOrientation(), (unitTarget==m_caster ? TELE_TO_SPELL : 0)); + + // Stuck spell trigger Hearthstone cooldown + SpellEntry const *spellInfo = sSpellStore.LookupEntry(8690); + if(!spellInfo) + return; + Spell spell(pTarget,spellInfo,true,0); + spell.SendSpellCooldown(); +} + +void Spell::EffectSummonPlayer(uint32 /*i*/) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + // Evil Twin (ignore player summon, but hide this for summoner) + if(unitTarget->GetDummyAura(23445)) + return; + + float x,y,z; + m_caster->GetClosePoint(x,y,z,unitTarget->GetObjectSize()); + + ((Player*)unitTarget)->SetSummonPoint(m_caster->GetMapId(),x,y,z); + + WorldPacket data(SMSG_SUMMON_REQUEST, 8+4+4); + data << uint64(m_caster->GetGUID()); // summoner guid + data << uint32(m_caster->GetZoneId()); // summoner zone + data << uint32(MAX_PLAYER_SUMMON_DELAY*1000); // auto decline after msecs + ((Player*)unitTarget)->GetSession()->SendPacket(&data); +} + +static ScriptInfo generateActivateCommand() +{ + ScriptInfo si; + si.command = SCRIPT_COMMAND_ACTIVATE_OBJECT; + return si; +} + +void Spell::EffectActivateObject(uint32 effect_idx) +{ + if(!gameObjTarget) + return; + + static ScriptInfo activateCommand = generateActivateCommand(); + + int32 delay_secs = m_spellInfo->EffectMiscValue[effect_idx]; + + sWorld.ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget); +} + +void Spell::EffectSummonTotem(uint32 i) +{ + uint8 slot = 0; + switch(m_spellInfo->EffectMiscValueB[i]) + { + case SUMMON_TYPE_TOTEM_SLOT1: slot = 0; break; + case SUMMON_TYPE_TOTEM_SLOT2: slot = 1; break; + case SUMMON_TYPE_TOTEM_SLOT3: slot = 2; break; + case SUMMON_TYPE_TOTEM_SLOT4: slot = 3; break; + // Battle standard case + case SUMMON_TYPE_TOTEM: slot = 254; break; + // jewelery statue case, like totem without slot + case SUMMON_TYPE_GUARDIAN: slot = 255; break; + default: return; + } + + if(slot < MAX_TOTEM) + { + uint64 guid = m_caster->m_TotemSlot[slot]; + if(guid != 0) + { + Creature *OldTotem = ObjectAccessor::GetCreature(*m_caster, guid); + if(OldTotem && OldTotem->isTotem()) + ((Totem*)OldTotem)->UnSummon(); + } + } + + uint32 team = 0; + if (m_caster->GetTypeId()==TYPEID_PLAYER) + team = ((Player*)m_caster)->GetTeam(); + + Totem* pTotem = new Totem; + + if(!pTotem->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), m_caster->GetMap(), m_spellInfo->EffectMiscValue[i], team )) + { + delete pTotem; + return; + } + + float angle = slot < MAX_TOTEM ? M_PI/MAX_TOTEM - (slot*2*M_PI/MAX_TOTEM) : 0; + + float x,y,z; + m_caster->GetClosePoint(x,y,z,pTotem->GetObjectSize(),2.0f,angle); + + // totem must be at same Z in case swimming caster and etc. + if( fabs( z - m_caster->GetPositionZ() ) > 5 ) + z = m_caster->GetPositionZ(); + + pTotem->Relocate(x, y, z, m_caster->GetOrientation()); + + if(slot < MAX_TOTEM) + m_caster->m_TotemSlot[slot] = pTotem->GetGUID(); + + pTotem->SetOwner(m_caster->GetGUID()); + pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initilized + + int32 duration=GetSpellDuration(m_spellInfo); + if(Player* modOwner = m_caster->GetSpellModOwner()) + modOwner->ApplySpellMod(m_spellInfo->Id,SPELLMOD_DURATION, duration); + pTotem->SetDuration(duration); + + if (damage) // if not spell info, DB values used + { + pTotem->SetMaxHealth(damage); + pTotem->SetHealth(damage); + } + + pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL,m_spellInfo->Id); + pTotem->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE); + + pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_MOD_FEAR,true); + pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_TRANSFORM,true); + + pTotem->Summon(m_caster); + + if(slot < MAX_TOTEM && m_caster->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_TOTEM_CREATED, 1+8+4+4); + data << uint8(slot); + data << uint64(pTotem->GetGUID()); + data << uint32(duration); + data << uint32(m_spellInfo->Id); + ((Player*)m_caster)->SendDirectMessage(&data); + } +} + +void Spell::EffectEnchantHeldItem(uint32 i) +{ + // this is only item spell effect applied to main-hand weapon of target player (players in area) + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + Player* item_owner = (Player*)unitTarget; + Item* item = item_owner->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); + + if(!item ) + return; + + // must be equipped + if(!item ->IsEquipped()) + return; + + if (m_spellInfo->EffectMiscValue[i]) + { + uint32 enchant_id = m_spellInfo->EffectMiscValue[i]; + int32 duration = GetSpellDuration(m_spellInfo); //Try duration index first .. + if(!duration) + duration = m_currentBasePoints[i]+1; //Base points after .. + if(!duration) + duration = 10; //10 seconds for enchants which don't have listed duration + + SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!pEnchant) + return; + + // Always go to temp enchantment slot + EnchantmentSlot slot = TEMP_ENCHANTMENT_SLOT; + + // Enchantment will not be applied if a different one already exists + if(item->GetEnchantmentId(slot) && item->GetEnchantmentId(slot) != enchant_id) + return; + + // Apply the temporary enchantment + item->SetEnchantment(slot, enchant_id, duration*1000, 0); + item_owner->ApplyEnchantment(item,slot,true); + } +} + +void Spell::EffectDisEnchant(uint32 /*i*/) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player* p_caster = (Player*)m_caster; + if(!itemTarget || !itemTarget->GetProto()->DisenchantID) + return; + + p_caster->UpdateCraftSkill(m_spellInfo->Id); + + ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(),LOOT_DISENCHANTING); + + // item will be removed at disenchanting end +} + +void Spell::EffectInebriate(uint32 /*i*/) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + Player *player = (Player*)unitTarget; + uint16 currentDrunk = player->GetDrunkValue(); + uint16 drunkMod = damage * 256; + if (currentDrunk + drunkMod > 0xFFFF) + currentDrunk = 0xFFFF; + else + currentDrunk += drunkMod; + player->SetDrunkValue(currentDrunk, m_CastItem?m_CastItem->GetEntry():0); +} + +void Spell::EffectFeedPet(uint32 i) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *_player = (Player*)m_caster; + + if(!itemTarget) + return; + + Pet *pet = _player->GetPet(); + if(!pet) + return; + + if(!pet->isAlive()) + return; + + int32 benefit = pet->GetCurrentFoodBenefitLevel(itemTarget->GetProto()->ItemLevel); + if(benefit <= 0) + return; + + uint32 count = 1; + _player->DestroyItemCount(itemTarget,count,true); + // TODO: fix crash when a spell has two effects, both pointed at the same item target + + m_caster->CastCustomSpell(m_caster,m_spellInfo->EffectTriggerSpell[i],&benefit,NULL,NULL,true); +} + +void Spell::EffectDismissPet(uint32 /*i*/) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Pet* pet = m_caster->GetPet(); + + // not let dismiss dead pet + if(!pet||!pet->isAlive()) + return; + + ((Player*)m_caster)->RemovePet(pet,PET_SAVE_NOT_IN_SLOT); +} + +void Spell::EffectSummonObject(uint32 i) +{ + uint32 go_id = m_spellInfo->EffectMiscValue[i]; + + uint8 slot = 0; + switch(m_spellInfo->Effect[i]) + { + case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: slot = 0; break; + case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: slot = 1; break; + case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: slot = 2; break; + case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: slot = 3; break; + default: return; + } + + uint64 guid = m_caster->m_ObjectSlot[slot]; + if(guid != 0) + { + GameObject* obj = NULL; + if( m_caster ) + obj = ObjectAccessor::GetGameObject(*m_caster, guid); + + if(obj) obj->Delete(); + m_caster->m_ObjectSlot[slot] = 0; + } + + GameObject* pGameObj = new GameObject; + + float rot2 = sin(m_caster->GetOrientation()/2); + float rot3 = cos(m_caster->GetOrientation()/2); + + float x,y,z; + // If dest location if present + if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + } + // Summon in random point all other units if location present + else + m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE); + + Map *map = m_caster->GetMap(); + if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, x, y, z, m_caster->GetOrientation(), 0, 0, rot2, rot3, 0, 1)) + { + delete pGameObj; + return; + } + + pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL,m_caster->getLevel()); + int32 duration = GetSpellDuration(m_spellInfo); + pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetSpellId(m_spellInfo->Id); + m_caster->AddGameObject(pGameObj); + + map->Add(pGameObj); + WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8); + data << pGameObj->GetGUID(); + m_caster->SendMessageToSet(&data,true); + + m_caster->m_ObjectSlot[slot] = pGameObj->GetGUID(); +} + +void Spell::EffectResurrect(uint32 i) +{ + if(!unitTarget) + return; + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + if(unitTarget->isAlive()) + return; + if(!unitTarget->IsInWorld()) + return; + + switch (m_spellInfo->Id) + { + // Defibrillate (Goblin Jumper Cables) have 33% chance on success + case 8342: + if (roll_chance_i(67)) + { + m_caster->CastSpell(m_caster, 8338, true, m_CastItem); + return; + } + break; + // Defibrillate (Goblin Jumper Cables XL) have 50% chance on success + case 22999: + if (roll_chance_i(50)) + { + m_caster->CastSpell(m_caster, 23055, true, m_CastItem); + return; + } + break; + default: + break; + } + + Player* pTarget = ((Player*)unitTarget); + + if(pTarget->isRessurectRequested()) // already have one active request + return; + + uint32 health = pTarget->GetMaxHealth() * damage / 100; + uint32 mana = pTarget->GetMaxPower(POWER_MANA) * damage / 100; + + pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana); + SendResurrectRequest(pTarget); +} + +void Spell::EffectAddExtraAttacks(uint32 /*i*/) +{ + if(!unitTarget || !unitTarget->isAlive()) + return; + + if( unitTarget->m_extraAttacks ) + return; + + unitTarget->m_extraAttacks = damage; +} + +void Spell::EffectParry(uint32 /*i*/) +{ + if (unitTarget->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)unitTarget)->SetCanParry(true); + } +} + +void Spell::EffectMomentMove(uint32 i) +{ + if(unitTarget->isInFlight()) + return; + + if( m_spellInfo->rangeIndex== 1) //self range + { + uint32 mapid = m_caster->GetMapId(); + float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); + + // before caster + float fx,fy,fz; + unitTarget->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis); + float ox,oy,oz; + unitTarget->GetPosition(ox,oy,oz); + + float fx2,fy2,fz2; // getObjectHitPos overwrite last args in any result case + if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(mapid, ox,oy,oz+0.5, fx,fy,oz+0.5,fx2,fy2,fz2, -0.5)) + { + fx = fx2; + fy = fy2; + fz = fz2; + unitTarget->UpdateGroundPositionZ(fx,fy,fz); + } + + if(unitTarget->GetTypeId() == TYPEID_PLAYER) + ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, unitTarget->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); + else + MapManager::Instance().GetMap(mapid, unitTarget)->CreatureRelocation((Creature*)unitTarget, fx, fy, fz, unitTarget->GetOrientation()); + } +} + +void Spell::EffectReputation(uint32 i) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + Player *_player = (Player*)unitTarget; + + int32 rep_change = m_currentBasePoints[i]+1; // field store reputation change -1 + + uint32 faction_id = m_spellInfo->EffectMiscValue[i]; + + FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id); + + if(!factionEntry) + return; + + _player->ModifyFactionReputation(factionEntry,rep_change); +} + +void Spell::EffectQuestComplete(uint32 i) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player *_player = (Player*)m_caster; + + uint32 quest_id = m_spellInfo->EffectMiscValue[i]; + _player->AreaExploredOrEventHappens(quest_id); +} + +void Spell::EffectSelfResurrect(uint32 i) +{ + if(!unitTarget || unitTarget->isAlive()) + return; + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + if(!unitTarget->IsInWorld()) + return; + + uint32 health = 0; + uint32 mana = 0; + + // flat case + if(damage < 0) + { + health = uint32(-damage); + mana = m_spellInfo->EffectMiscValue[i]; + } + // percent case + else + { + health = uint32(damage/100.0f*unitTarget->GetMaxHealth()); + if(unitTarget->GetMaxPower(POWER_MANA) > 0) + mana = uint32(damage/100.0f*unitTarget->GetMaxPower(POWER_MANA)); + } + + Player *plr = ((Player*)unitTarget); + plr->ResurrectPlayer(0.0f); + + plr->SetHealth( health ); + plr->SetPower(POWER_MANA, mana ); + plr->SetPower(POWER_RAGE, 0 ); + plr->SetPower(POWER_ENERGY, plr->GetMaxPower(POWER_ENERGY) ); + + plr->SpawnCorpseBones(); + + plr->SaveToDB(); +} + +void Spell::EffectSkinning(uint32 /*i*/) +{ + if(unitTarget->GetTypeId() != TYPEID_UNIT ) + return; + if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Creature* creature = (Creature*) unitTarget; + int32 targetLevel = creature->getLevel(); + + uint32 skill; + if(creature->GetCreatureInfo()->flag1 & 256) + skill = SKILL_HERBALISM; // special case + else if(creature->GetCreatureInfo()->flag1 & 512) + skill = SKILL_MINING; // special case + else + skill = SKILL_SKINNING; // normal case + + ((Player*)m_caster)->SendLoot(creature->GetGUID(),LOOT_SKINNING); + creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + + int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel-10)*10 : targetLevel*5; + + int32 skillValue = ((Player*)m_caster)->GetPureSkillValue(skill); + + // Double chances for elites + ((Player*)m_caster)->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1 ); +} + +void Spell::EffectCharge(uint32 /*i*/) +{ + if(!unitTarget || !m_caster) + return; + + float x, y, z; + unitTarget->GetContactPoint(m_caster, x, y, z); + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + ((Creature *)unitTarget)->StopMoving(); + + // Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags + m_caster->SendMonsterMove(x, y, z, 0, MOVEMENTFLAG_WALK_MODE, 1); + + if(m_caster->GetTypeId() != TYPEID_PLAYER) + MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->CreatureRelocation((Creature*)m_caster,x,y,z,m_caster->GetOrientation()); + + // not all charge effects used in negative spells + if ( !IsPositiveSpell(m_spellInfo->Id)) + m_caster->Attack(unitTarget,true); +} + +void Spell::EffectSummonCritter(uint32 i) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + Player* player = (Player*)m_caster; + + uint32 pet_entry = m_spellInfo->EffectMiscValue[i]; + if(!pet_entry) + return; + + Pet* old_critter = player->GetMiniPet(); + + // for same pet just despawn + if(old_critter && old_critter->GetEntry() == pet_entry) + { + player->RemoveMiniPet(); + return; + } + + // despawn old pet before summon new + if(old_critter) + player->RemoveMiniPet(); + + // summon new pet + Pet* critter = new Pet(MINI_PET); + + Map *map = m_caster->GetMap(); + uint32 pet_number = objmgr.GeneratePetNumber(); + if(!critter->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), + map, pet_entry, pet_number)) + { + sLog.outError("Spell::EffectSummonCritter, spellid %u: no such creature entry %u", m_spellInfo->Id, pet_entry); + delete critter; + return; + } + + float x,y,z; + // If dest location if present + if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + } + // Summon if dest location not present near caster + else + m_caster->GetClosePoint(x,y,z,critter->GetObjectSize()); + + critter->Relocate(x,y,z,m_caster->GetOrientation()); + + if(!critter->IsPositionValid()) + { + sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", critter->GetGUIDLow(), critter->GetEntry(), critter->GetPositionX(), critter->GetPositionY()); + delete critter; + return; + } + + critter->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID()); + critter->SetUInt64Value(UNIT_FIELD_CREATEDBY,m_caster->GetGUID()); + critter->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction()); + critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); + + critter->AIM_Initialize(); + critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter... + critter->SetMaxHealth(1); + critter->SetHealth(1); + critter->SetLevel(1); + + // set timer for unsummon + int32 duration = GetSpellDuration(m_spellInfo); + if(duration > 0) + critter->SetDuration(duration); + + std::string name = player->GetName(); + name.append(petTypeSuffix[critter->getPetType()]); + critter->SetName( name ); + player->SetMiniPet(critter); + + map->Add((Creature*)critter); +} + +void Spell::EffectKnockBack(uint32 i) +{ + if(!unitTarget || !m_caster) + return; + + // Effect only works on players + if(unitTarget->GetTypeId()!=TYPEID_PLAYER) + return; + + float vsin = sin(m_caster->GetAngle(unitTarget)); + float vcos = cos(m_caster->GetAngle(unitTarget)); + + WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4)); + data.append(unitTarget->GetPackGUID()); + data << uint32(0); // Sequence + data << float(vcos); // x direction + data << float(vsin); // y direction + data << float(m_spellInfo->EffectMiscValue[i])/10; // Horizontal speed + data << float(damage/-10); // Z Movement speed (vertical) + + ((Player*)unitTarget)->GetSession()->SendPacket(&data); +} + +void Spell::EffectSendTaxi(uint32 i) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(m_spellInfo->EffectMiscValue[i]); + if(!entry) + return; + + std::vector<uint32> nodes; + + nodes.resize(2); + nodes[0] = entry->from; + nodes[1] = entry->to; + + uint32 mountid = 0; + switch(m_spellInfo->Id) + { + case 31606: //Stormcrow Amulet + mountid = 17447; + break; + case 45071: //Quest - Sunwell Daily - Dead Scar Bombing Run + case 45113: //Quest - Sunwell Daily - Ship Bombing Run + case 45353: //Quest - Sunwell Daily - Ship Bombing Run Return + mountid = 22840; + break; + case 34905: //Stealth Flight + mountid = 6851; + break; + } + + ((Player*)unitTarget)->ActivateTaxiPathTo(nodes,mountid); + +} + +void Spell::EffectPlayerPull(uint32 i) +{ + if(!unitTarget || !m_caster) + return; + + // Effect only works on players + if(unitTarget->GetTypeId()!=TYPEID_PLAYER) + return; + + float vsin = sin(unitTarget->GetAngle(m_caster)); + float vcos = cos(unitTarget->GetAngle(m_caster)); + + WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4)); + data.append(unitTarget->GetPackGUID()); + data << uint32(0); // Sequence + data << float(vcos); // x direction + data << float(vsin); // y direction + // Horizontal speed + data << float(damage ? damage : unitTarget->GetDistance2d(m_caster)); + data << float(m_spellInfo->EffectMiscValue[i])/-10; // Z Movement speed + + ((Player*)unitTarget)->GetSession()->SendPacket(&data); +} + +void Spell::EffectDispelMechanic(uint32 i) +{ + if(!unitTarget) + return; + + uint32 mechanic = m_spellInfo->EffectMiscValue[i]; + + Unit::AuraMap& Auras = unitTarget->GetAuras(); + for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next) + { + next = iter; + ++next; + SpellEntry const *spell = sSpellStore.LookupEntry(iter->second->GetSpellProto()->Id); + if(spell->Mechanic == mechanic || spell->EffectMechanic[iter->second->GetEffIndex()] == mechanic) + { + unitTarget->RemoveAurasDueToSpell(spell->Id); + if(Auras.empty()) + break; + else + next = Auras.begin(); + } + } + return; +} + +void Spell::EffectSummonDeadPet(uint32 /*i*/) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + Player *_player = (Player*)m_caster; + Pet *pet = _player->GetPet(); + if(!pet) + return; + if(pet->isAlive()) + return; + if(damage < 0) + return; + pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0); + pet->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + pet->setDeathState( ALIVE ); + pet->clearUnitState(UNIT_STAT_ALL_STATE); + pet->SetHealth( uint32(pet->GetMaxHealth()*(float(damage)/100))); + + pet->AIM_Initialize(); + + _player->PetSpellInitialize(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); +} + +void Spell::EffectDestroyAllTotems(uint32 /*i*/) +{ + float mana = 0; + for(int slot = 0; slot < MAX_TOTEM; ++slot) + { + if(!m_caster->m_TotemSlot[slot]) + continue; + + Creature* totem = ObjectAccessor::GetCreature(*m_caster,m_caster->m_TotemSlot[slot]); + if(totem && totem->isTotem()) + { + uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); + if(spellInfo) + mana += spellInfo->manaCost * damage / 100; + ((Totem*)totem)->UnSummon(); + } + } + + int32 gain = m_caster->ModifyPower(POWER_MANA,int32(mana)); + m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id, gain, POWER_MANA); +} + +void Spell::EffectDurabilityDamage(uint32 i) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + int32 slot = m_spellInfo->EffectMiscValue[i]; + + // FIXME: some spells effects have value -1/-2 + // Possibly its mean -1 all player equipped items and -2 all items + if(slot < 0) + { + ((Player*)unitTarget)->DurabilityPointsLossAll(damage,(slot < -1)); + return; + } + + // invalid slot value + if(slot >= INVENTORY_SLOT_BAG_END) + return; + + if(Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0,slot)) + ((Player*)unitTarget)->DurabilityPointsLoss(item,damage); +} + +void Spell::EffectDurabilityDamagePCT(uint32 i) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + int32 slot = m_spellInfo->EffectMiscValue[i]; + + // FIXME: some spells effects have value -1/-2 + // Possibly its mean -1 all player equipped items and -2 all items + if(slot < 0) + { + ((Player*)unitTarget)->DurabilityLossAll(double(damage)/100.0f,(slot < -1)); + return; + } + + // invalid slot value + if(slot >= INVENTORY_SLOT_BAG_END) + return; + + if(damage <= 0) + return; + + if(Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0,slot)) + ((Player*)unitTarget)->DurabilityLoss(item,double(damage)/100.0f); +} + +void Spell::EffectModifyThreatPercent(uint32 /*effIndex*/) +{ + if(!unitTarget) + return; + + unitTarget->getThreatManager().modifyThreatPercent(m_caster, damage); +} + +void Spell::EffectTransmitted(uint32 effIndex) +{ + uint32 name_id = m_spellInfo->EffectMiscValue[effIndex]; + + GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id); + + if (!goinfo) + { + sLog.outErrorDb("Gameobject (Entry: %u) not exist and not created at spell (ID: %u) cast",name_id, m_spellInfo->Id); + return; + } + + float fx,fy,fz; + + if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + fx = m_targets.m_destX; + fy = m_targets.m_destY; + fz = m_targets.m_destZ; + } + //FIXME: this can be better check for most objects but still hack + else if(m_spellInfo->EffectRadiusIndex[effIndex] && m_spellInfo->speed==0) + { + float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[effIndex])); + m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis); + } + else + { + float min_dis = GetSpellMinRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + float max_dis = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)); + float dis = rand_norm() * (max_dis - min_dis) + min_dis; + + m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis); + } + + Map *cMap = m_caster->GetMap(); + + if(goinfo->type==GAMEOBJECT_TYPE_FISHINGNODE) + { + if ( !cMap->IsInWater(fx,fy,fz-0.5f)) // 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 + SendCastResult(SPELL_FAILED_NOT_HERE); + SendChannelUpdate(0); + return; + } + + // replace by water level in this case + fz = cMap->GetWaterLevel(fx,fy); + } + // if gameobject is summoning object, it should be spawned right on caster's position + else if(goinfo->type==GAMEOBJECT_TYPE_SUMMONING_RITUAL) + { + m_caster->GetPosition(fx,fy,fz); + } + + GameObject* pGameObj = new GameObject; + + if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap, + fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1)) + { + delete pGameObj; + return; + } + + int32 duration = GetSpellDuration(m_spellInfo); + + switch(goinfo->type) + { + case GAMEOBJECT_TYPE_FISHINGNODE: + { + m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,pGameObj->GetGUID()); + // Orientation3 + pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 2, 0.88431775569915771 ); + // Orientation4 + pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 3, -0.4668855369091033 ); + m_caster->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) + int32 lastSec; + switch(urand(0, 3)) + { + case 0: lastSec = 3; break; + case 1: lastSec = 7; break; + case 2: lastSec = 13; break; + case 3: lastSec = 17; break; + } + + duration = duration - lastSec*1000 + FISHING_BOBBER_READY_TIME*1000; + break; + } + case GAMEOBJECT_TYPE_SUMMONING_RITUAL: + { + if(m_caster->GetTypeId()==TYPEID_PLAYER) + { + pGameObj->AddUniqueUse((Player*)m_caster); + m_caster->AddGameObject(pGameObj); // will removed at spell cancel + } + break; + } + case GAMEOBJECT_TYPE_FISHINGHOLE: + case GAMEOBJECT_TYPE_CHEST: + default: + { + break; + } + } + + pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + + pGameObj->SetOwnerGUID(m_caster->GetGUID() ); + + pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() ); + pGameObj->SetSpellId(m_spellInfo->Id); + + DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted\n"); + //m_caster->AddGameObject(pGameObj); + //m_ObjToDel.push_back(pGameObj); + + cMap->Add(pGameObj); + + WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8); + data << uint64(pGameObj->GetGUID()); + m_caster->SendMessageToSet(&data,true); + + if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry()) + { + GameObject* linkedGO = new GameObject; + if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, cMap, + fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1)) + { + linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0); + linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() ); + linkedGO->SetSpellId(m_spellInfo->Id); + linkedGO->SetOwnerGUID(m_caster->GetGUID() ); + + MapManager::Instance().GetMap(linkedGO->GetMapId(), linkedGO)->Add(linkedGO); + } + else + { + delete linkedGO; + linkedGO = NULL; + return; + } + } +} + +void Spell::EffectProspecting(uint32 /*i*/) +{ + if(m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + Player* p_caster = (Player*)m_caster; + if(!itemTarget || !(itemTarget->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP)) + return; + + if(itemTarget->GetCount() < 5) + return; + + if( sWorld.getConfig(CONFIG_SKILL_PROSPECTING)) + { + uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_JEWELCRAFTING); + uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank; + p_caster->UpdateGatherSkill(SKILL_JEWELCRAFTING, SkillValue, reqSkillValue); + } + + ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_PROSPECTING); +} + +void Spell::EffectSkill(uint32 /*i*/) +{ + sLog.outDebug("WORLD: SkillEFFECT"); +} + +void Spell::EffectSummonDemon(uint32 i) +{ + float px = m_targets.m_destX; + float py = m_targets.m_destY; + float pz = m_targets.m_destZ; + + Creature* Charmed = m_caster->SummonCreature(m_spellInfo->EffectMiscValue[i], px, py, pz, m_caster->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,3600000); + if (!Charmed) + return; + + // might not always work correctly, maybe the creature that dies from CoD casts the effect on itself and is therefore the caster? + Charmed->SetLevel(m_caster->getLevel()); + + // TODO: Add damage/mana/hp according to level + + if (m_spellInfo->EffectMiscValue[i] == 89) // Inferno summon + { + // Enslave demon effect, without mana cost and cooldown + m_caster->CastSpell(Charmed, 20882, true); // FIXME: enslave does not scale with level, level 62+ minions cannot be enslaved + + // Inferno effect + Charmed->CastSpell(Charmed, 22703, true, 0); + } +} + +/* There is currently no need for this effect. We handle it in BattleGround.cpp + If we would handle the resurrection here, the spiritguide would instantly disappear as the + player revives, and so we wouldn't see the spirit heal visual effect on the npc. + This is why we use a half sec delay between the visual effect and the resurrection itself */ +void Spell::EffectSpiritHeal(uint32 /*i*/) +{ + /* + if(!unitTarget || unitTarget->isAlive()) + return; + if(unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + if(!unitTarget->IsInWorld()) + return; + + //m_spellInfo->EffectBasePoints[i]; == 99 (percent?) + //((Player*)unitTarget)->setResurrect(m_caster->GetGUID(), unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), unitTarget->GetMaxHealth(), unitTarget->GetMaxPower(POWER_MANA)); + ((Player*)unitTarget)->ResurrectPlayer(1.0f); + ((Player*)unitTarget)->SpawnCorpseBones(); + */ +} + +// remove insignia spell effect +void Spell::EffectSkinPlayerCorpse(uint32 /*i*/) +{ + sLog.outDebug("Effect: SkinPlayerCorpse"); + if ( (m_caster->GetTypeId() != TYPEID_PLAYER) || (unitTarget->GetTypeId() != TYPEID_PLAYER) || (unitTarget->isAlive()) ) + return; + + ((Player*)unitTarget)->RemovedInsignia( (Player*)m_caster ); +} + +void Spell::EffectStealBeneficialBuff(uint32 i) +{ + sLog.outDebug("Effect: StealBeneficialBuff"); + + if(!unitTarget || unitTarget==m_caster) // can't steal from self + return; + + std::vector <Aura *> steal_list; + // Create dispel mask by dispel type + uint32 dispelMask = GetDispellMask( DispelType(m_spellInfo->EffectMiscValue[i]) ); + Unit::AuraMap const& auras = unitTarget->GetAuras(); + for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + { + Aura *aur = (*itr).second; + if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask) + { + // Need check for passive? this + if (aur->IsPositive() && !aur->IsPassive()) + steal_list.push_back(aur); + } + } + // Ok if exist some buffs for dispel try dispel it + if (!steal_list.empty()) + { + std::list < std::pair<uint32,uint64> > success_list; + int32 list_size = steal_list.size(); + // Dispell N = damage buffs (or while exist buffs for dispel) + for (int32 count=0; count < damage && list_size > 0; ++count) + { + // Random select buff for dispel + Aura *aur = steal_list[urand(0, list_size-1)]; + // Not use chance for steal + // TODO possible need do it + success_list.push_back( std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID())); + + // Remove buff from list for prevent doubles + for (std::vector<Aura *>::iterator j = steal_list.begin(); j != steal_list.end(); ) + { + Aura *stealed = *j; + if (stealed->GetId() == aur->GetId() && stealed->GetCasterGUID() == aur->GetCasterGUID()) + { + j = steal_list.erase(j); + --list_size; + } + else + ++j; + } + } + // Really try steal and send log + if (!success_list.empty()) + { + int32 count = success_list.size(); + WorldPacket data(SMSG_SPELLSTEALLOG, 8+8+4+1+4+count*5); + data.append(unitTarget->GetPackGUID()); // Victim GUID + data.append(m_caster->GetPackGUID()); // Caster GUID + data << uint32(m_spellInfo->Id); // Dispell spell id + data << uint8(0); // not used + data << uint32(count); // count + for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j) + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first); + data << uint32(spellInfo->Id); // Spell Id + data << uint8(0); // 0 - steals !=0 transfers + unitTarget->RemoveAurasDueToSpellBySteal(spellInfo->Id, j->second, m_caster); + } + m_caster->SendMessageToSet(&data, true); + } + } +} + +void Spell::EffectKillCredit(uint32 i) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + ((Player*)unitTarget)->KilledMonster(m_spellInfo->EffectMiscValue[i], 0); +} + +void Spell::EffectQuestFail(uint32 i) +{ + if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) + return; + + ((Player*)unitTarget)->FailQuest(m_spellInfo->EffectMiscValue[i]); +} |