mirror of
https://github.com/mangosfour/server.git
synced 2025-12-15 01:37:00 +00:00
In addition, dummy effect of spell 51866 and 51872 and dummy aura effect of spell 51870. Dummy effect of 51866 might need additional work (see comments). Code expect additional spell cast by AI (summon dummy at death of related quest target creatures). Signed-off-by: NoFantasy <nofantasy@nf.no>
7778 lines
310 KiB
C++
7778 lines
310 KiB
C++
/*
|
|
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* 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 "Database/DatabaseEnv.h"
|
|
#include "WorldPacket.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 "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 "BattleGroundMgr.h"
|
|
#include "BattleGround.h"
|
|
#include "BattleGroundEY.h"
|
|
#include "BattleGroundWS.h"
|
|
#include "VMapFactory.h"
|
|
#include "Language.h"
|
|
#include "SocialMgr.h"
|
|
#include "Util.h"
|
|
#include "TemporarySummon.h"
|
|
#include "ScriptCalls.h"
|
|
#include "SkillDiscovery.h"
|
|
#include "Formulas.h"
|
|
#include "GridNotifiers.h"
|
|
#include "GridNotifiersImpl.h"
|
|
#include "CellImpl.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 from pre-1.2.1
|
|
&Spell::EffectTeleportUnits, // 5 SPELL_EFFECT_TELEPORT_UNITS
|
|
&Spell::EffectApplyAura, // 6 SPELL_EFFECT_APPLY_AURA
|
|
&Spell::EffectEnvironmentalDMG, // 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::EffectBind, // 11 SPELL_EFFECT_BIND
|
|
&Spell::EffectUnused, // 12 SPELL_EFFECT_PORTAL unused from pre-1.2.1, exist 2 spell, but not exist any data about its real usage
|
|
&Spell::EffectUnused, // 13 SPELL_EFFECT_RITUAL_BASE unused from pre-1.2.1
|
|
&Spell::EffectUnused, // 14 SPELL_EFFECT_RITUAL_SPECIALIZE unused from pre-1.2.1
|
|
&Spell::EffectUnused, // 15 SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL unused from pre-1.2.1
|
|
&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::EffectEmpty, // 20 SPELL_EFFECT_DODGE one spell: Dodge
|
|
&Spell::EffectEmpty, // 21 SPELL_EFFECT_EVADE one spell: Evade (DND)
|
|
&Spell::EffectParry, // 22 SPELL_EFFECT_PARRY
|
|
&Spell::EffectBlock, // 23 SPELL_EFFECT_BLOCK one spell: Block
|
|
&Spell::EffectCreateItem, // 24 SPELL_EFFECT_CREATE_ITEM
|
|
&Spell::EffectEmpty, // 25 SPELL_EFFECT_WEAPON spell per weapon type, in ItemSubclassmask store mask that can be used for usability check at equip, but current way using skill also work.
|
|
&Spell::EffectEmpty, // 26 SPELL_EFFECT_DEFENSE one spell: Defense
|
|
&Spell::EffectPersistentAA, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA
|
|
&Spell::EffectSummonType, // 28 SPELL_EFFECT_SUMMON
|
|
&Spell::EffectLeapForward, // 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::EffectEmpty, // 37 SPELL_EFFECT_SPELL_DEFENSE one spell: SPELLDEFENSE (DND)
|
|
&Spell::EffectDispel, // 38 SPELL_EFFECT_DISPEL
|
|
&Spell::EffectEmpty, // 39 SPELL_EFFECT_LANGUAGE misc store lang id
|
|
&Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD
|
|
&Spell::EffectJump, // 41 SPELL_EFFECT_JUMP
|
|
&Spell::EffectJump, // 42 SPELL_EFFECT_JUMP2
|
|
&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 spawn/login animation, expected by spawn unit cast, also base points store some dynflags
|
|
&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 from pre-1.2.1
|
|
&Spell::EffectUnused, // 52 SPELL_EFFECT_GUARANTEE_HIT unused from pre-1.2.1
|
|
&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::EffectCreateRandomItem, // 59 SPELL_EFFECT_CREATE_RANDOM_ITEM create item base at spell specific loot
|
|
&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::EffectApplyAreaAura, // 65 SPELL_EFFECT_APPLY_AREA_AURA_RAID
|
|
&Spell::EffectRestoreItemCharges, // 66 SPELL_EFFECT_RESTORE_ITEM_CHARGES itemtype - is affected item ID
|
|
&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::EffectNULL, // 73 SPELL_EFFECT_UNTRAIN_TALENTS one spell: Trainer: Untrain Talents
|
|
&Spell::EffectApplyGlyph, // 74 SPELL_EFFECT_APPLY_GLYPH
|
|
&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::EffectNULL, // 87 SPELL_EFFECT_WMO_DAMAGE (57 spells in 3.3.2)
|
|
&Spell::EffectNULL, // 88 SPELL_EFFECT_WMO_REPAIR (2 spells in 3.3.2)
|
|
&Spell::EffectNULL, // 89 SPELL_EFFECT_WMO_CHANGE (7 spells in 3.3.2)
|
|
&Spell::EffectKillCreditPersonal, // 90 SPELL_EFFECT_KILL_CREDIT Kill credit but only for single person
|
|
&Spell::EffectUnused, // 91 SPELL_EFFECT_THREAT_ALL one spell: zzOLDBrainwash
|
|
&Spell::EffectEnchantHeldItem, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM
|
|
&Spell::EffectBreakPlayerTargeting, // 93 SPELL_EFFECT_BREAK_PLAYER_TARGETING
|
|
&Spell::EffectSelfResurrect, // 94 SPELL_EFFECT_SELF_RESURRECT
|
|
&Spell::EffectSkinning, // 95 SPELL_EFFECT_SKINNING
|
|
&Spell::EffectCharge, // 96 SPELL_EFFECT_CHARGE
|
|
&Spell::EffectSummonAllTotems, // 97 SPELL_EFFECT_SUMMON_ALL_TOTEMS
|
|
&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::EffectUnused, //112 SPELL_EFFECT_112 (old 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::EffectPlayMusic, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value (SoundEntries.dbc)
|
|
&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::EffectLeapBack, //138 SPELL_EFFECT_LEAP_BACK Leap back
|
|
&Spell::EffectNULL, //139 SPELL_EFFECT_CLEAR_QUEST (misc - is quest ID)
|
|
&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::EffectActivateRune, //146 SPELL_EFFECT_ACTIVATE_RUNE
|
|
&Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail
|
|
&Spell::EffectNULL, //148 SPELL_EFFECT_148 single spell: Inflicts Fire damage to an enemy.
|
|
&Spell::EffectCharge2, //149 SPELL_EFFECT_CHARGE2 swoop
|
|
&Spell::EffectNULL, //150 SPELL_EFFECT_150 2 spells in 3.3.2
|
|
&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
|
|
&Spell::EffectTeachTaxiNode, //154 SPELL_EFFECT_TEACH_TAXI_NODE single spell: Teach River's Heart Taxi Path
|
|
&Spell::EffectTitanGrip, //155 SPELL_EFFECT_TITAN_GRIP Allows you to equip two-handed axes, maces and swords in one hand, but you attack $49152s1% slower than normal.
|
|
&Spell::EffectEnchantItemPrismatic, //156 SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC
|
|
&Spell::EffectCreateItem2, //157 SPELL_EFFECT_CREATE_ITEM_2 create item or create item template and replace by some randon spell loot item
|
|
&Spell::EffectMilling, //158 SPELL_EFFECT_MILLING milling
|
|
&Spell::EffectRenamePet, //159 SPELL_EFFECT_ALLOW_RENAME_PET allow rename pet once again
|
|
&Spell::EffectNULL, //160 SPELL_EFFECT_160 single spell: Nerub'ar Web Random Unit
|
|
&Spell::EffectSpecCount, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert)
|
|
&Spell::EffectActivateSpec, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec
|
|
&Spell::EffectNULL, //163
|
|
&Spell::EffectNULL, //164 cancel's some aura...
|
|
};
|
|
|
|
void Spell::EffectEmpty(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
// NOT NEED ANY IMPLEMENTATION CODE, EFFECT POSISBLE USED AS MARKER OR CLIENT INFORM
|
|
}
|
|
|
|
void Spell::EffectNULL(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
DEBUG_LOG("WORLD: Spell Effect DUMMY");
|
|
}
|
|
|
|
void Spell::EffectUnused(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
// NOT USED BY ANY SPELL OR USELESS OR IMPLEMENTED IN DIFFERENT WAY IN MANGOS
|
|
}
|
|
|
|
void Spell::EffectResurrectNew(SpellEffectIndex eff_idx)
|
|
{
|
|
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[eff_idx];
|
|
pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
|
|
SendResurrectRequest(pTarget);
|
|
}
|
|
|
|
void Spell::EffectInstaKill(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if( !unitTarget || !unitTarget->isAlive() )
|
|
return;
|
|
|
|
if(m_caster == unitTarget) // prevent interrupt message
|
|
finish();
|
|
|
|
m_caster->DealDamage(unitTarget, unitTarget->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
|
|
}
|
|
|
|
void Spell::EffectEnvironmentalDMG(SpellEffectIndex eff_idx)
|
|
{
|
|
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->CalculateSimpleValue(eff_idx);
|
|
|
|
m_caster->CalculateAbsorbAndResist(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(DAMAGE_FIRE, damage);
|
|
}
|
|
|
|
void Spell::EffectSchoolDMG(SpellEffectIndex effect_idx)
|
|
{
|
|
if( unitTarget && unitTarget->isAlive())
|
|
{
|
|
switch(m_spellInfo->SpellFamilyName)
|
|
{
|
|
case SPELLFAMILY_GENERIC:
|
|
{
|
|
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 57467: // 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
|
|
case 64422: case 64688: // Sonic Screech
|
|
case 70492: case 72505: // Ooze Eruption
|
|
case 71904: // Chaos Bane
|
|
case 72624: case 72625: // Ooze Eruption
|
|
{
|
|
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;
|
|
}
|
|
// Intercept (warrior spell trigger)
|
|
case 20253:
|
|
case 61491:
|
|
{
|
|
damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.12f);
|
|
break;
|
|
}
|
|
// percent max target health
|
|
case 29142: // Eyesore Blaster
|
|
case 35139: // Throw Boom's Doom
|
|
case 49882: // Leviroth Self-Impale
|
|
{
|
|
damage = damage * unitTarget->GetMaxHealth() / 100;
|
|
break;
|
|
}
|
|
// Cataclysmic Bolt
|
|
case 38441:
|
|
{
|
|
damage = unitTarget->GetMaxHealth() / 2;
|
|
break;
|
|
}
|
|
// Tympanic Tantrum
|
|
case 62775:
|
|
{
|
|
damage = unitTarget->GetMaxHealth() / 10;
|
|
break;
|
|
}
|
|
// Hand of Rekoning (name not have typos ;) )
|
|
case 67485:
|
|
damage += uint32(0.5f * m_caster->GetTotalAttackPowerValue(BASE_ATTACK));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_MAGE:
|
|
// remove Arcane Blast buffs at any non-Arcane Blast arcane damage spell.
|
|
// NOTE: it removed at hit instead cast because currently spell done-damage calculated at hit instead cast
|
|
if ((m_spellInfo->SchoolMask & SPELL_SCHOOL_MASK_ARCANE) && !(m_spellInfo->SpellFamilyFlags & UI64LIT(0x20000000)))
|
|
m_caster->RemoveAurasDueToSpell(36032); // Arcane Blast buff
|
|
break;
|
|
case SPELLFAMILY_WARRIOR:
|
|
{
|
|
// Bloodthirst
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x40000000000))
|
|
{
|
|
damage = uint32(damage * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK)) / 100);
|
|
}
|
|
// Shield Slam
|
|
else if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000020000000000)) && m_spellInfo->Category==1209)
|
|
damage += int32(m_caster->GetShieldBlockValue());
|
|
// Victory Rush
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x10000000000))
|
|
{
|
|
damage = uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
|
|
m_caster->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, false);
|
|
}
|
|
// Revenge ${$m1+$AP*0.310} to ${$M1+$AP*0.310}
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000400))
|
|
damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.310f);
|
|
// Heroic Throw ${$m1+$AP*.50}
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000100000000))
|
|
damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.5f);
|
|
// Shattering Throw ${$m1+$AP*.50}
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0040000000000000))
|
|
damage+= uint32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.5f);
|
|
// Shockwave ${$m3/100*$AP}
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000800000000000))
|
|
{
|
|
int32 pct = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_INDEX_2);
|
|
if (pct > 0)
|
|
damage+= int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * pct / 100);
|
|
break;
|
|
}
|
|
// Thunder Clap
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000080))
|
|
{
|
|
damage+=int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 12 / 100);
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_WARLOCK:
|
|
{
|
|
// Incinerate Rank 1 & 2
|
|
if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x00004000000000)) && m_spellInfo->SpellIconID==2128)
|
|
{
|
|
// Incinerate does more dmg (dmg*0.25) if the target have Immolate debuff.
|
|
// Check aura state for speed but aura state set not only for Immolate spell
|
|
if(unitTarget->HasAuraState(AURA_STATE_CONFLAGRATE))
|
|
{
|
|
Unit::AuraList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
|
|
for(Unit::AuraList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i)
|
|
{
|
|
// Immolate
|
|
if((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK &&
|
|
((*i)->GetSpellProto()->SpellFamilyFlags & UI64LIT(0x00000000000004)))
|
|
{
|
|
damage += damage/4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Shadowflame
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0001000000000000))
|
|
{
|
|
// Apply DOT part
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 47897: m_caster->CastSpell(unitTarget, 47960, true); break;
|
|
case 61290: m_caster->CastSpell(unitTarget, 61291, true); break;
|
|
default:
|
|
sLog.outError("Spell::EffectDummy: Unhandeled Shadowflame spell rank %u",m_spellInfo->Id);
|
|
break;
|
|
}
|
|
}
|
|
// Shadow Bite
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0040000000000000))
|
|
{
|
|
Unit *owner = m_caster->GetOwner();
|
|
if (!owner)
|
|
break;
|
|
|
|
uint32 counter = 0;
|
|
Unit::AuraList const& dotAuras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
|
|
for(Unit::AuraList::const_iterator itr = dotAuras.begin(); itr!=dotAuras.end(); ++itr)
|
|
if ((*itr)->GetCasterGUID() == owner->GetGUID())
|
|
++counter;
|
|
|
|
if (counter)
|
|
damage += (counter * owner->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_INDEX_2) * damage) / 100.0f;
|
|
}
|
|
// Conflagrate - consumes Immolate or Shadowflame
|
|
else if (m_spellInfo->TargetAuraState == AURA_STATE_CONFLAGRATE)
|
|
{
|
|
Aura const* aura = NULL; // found req. aura for damage calculation
|
|
|
|
Unit::AuraList const &mPeriodic = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
|
|
for(Unit::AuraList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i)
|
|
{
|
|
// for caster applied auras only
|
|
if ((*i)->GetSpellProto()->SpellFamilyName != SPELLFAMILY_WARLOCK ||
|
|
(*i)->GetCasterGUID()!=m_caster->GetGUID())
|
|
continue;
|
|
|
|
// Immolate
|
|
if ((*i)->GetSpellProto()->SpellFamilyFlags & UI64LIT(0x0000000000000004))
|
|
{
|
|
aura = *i; // it selected always if exist
|
|
break;
|
|
}
|
|
|
|
// Shadowflame
|
|
if ((*i)->GetSpellProto()->SpellFamilyFlags2 & 0x00000002)
|
|
aura = *i; // remember but wait possible Immolate as primary priority
|
|
}
|
|
|
|
// found Immolate or Shadowflame
|
|
if (aura)
|
|
{
|
|
int32 damagetick = aura->GetModifier()->m_amount;
|
|
damage += damagetick * 4;
|
|
|
|
// Glyph of Conflagrate
|
|
if (!m_caster->HasAura(56235))
|
|
unitTarget->RemoveAurasByCasterSpell(aura->GetId(), m_caster->GetGUID());
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_PRIEST:
|
|
{
|
|
// Shadow Word: Death - deals damage equal to damage done to caster
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000200000000))
|
|
m_caster->CastCustomSpell(m_caster, 32409, &damage, 0, 0, true);
|
|
// Improved Mind Blast (Mind Blast in shadow form bonus)
|
|
else if (m_caster->m_form == FORM_SHADOW && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x00002000)))
|
|
{
|
|
Unit::AuraList const& ImprMindBlast = m_caster->GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER);
|
|
for(Unit::AuraList::const_iterator i = ImprMindBlast.begin(); i != ImprMindBlast.end(); ++i)
|
|
{
|
|
if ((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_PRIEST &&
|
|
((*i)->GetSpellProto()->SpellIconID == 95))
|
|
{
|
|
int chance = (*i)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_1);
|
|
if (roll_chance_i(chance))
|
|
// Mind Trauma
|
|
m_caster->CastSpell(unitTarget, 48301, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_DRUID:
|
|
{
|
|
// Ferocious Bite
|
|
if (m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x000800000)) && m_spellInfo->SpellVisual[0]==6587)
|
|
{
|
|
// converts up to 30 points of energy into ($f1+$AP/410) additional damage
|
|
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
float multiple = ap / 410 + m_spellInfo->DmgMultiplier[effect_idx];
|
|
damage += int32(((Player*)m_caster)->GetComboPoints() * ap * 7 / 100);
|
|
uint32 energy = m_caster->GetPower(POWER_ENERGY);
|
|
uint32 used_energy = energy > 30 ? 30 : energy;
|
|
damage += int32(used_energy * multiple);
|
|
m_caster->SetPower(POWER_ENERGY,energy-used_energy);
|
|
}
|
|
// Rake
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000001000) && m_spellInfo->Effect[EFFECT_INDEX_2] == SPELL_EFFECT_ADD_COMBO_POINTS)
|
|
{
|
|
// $AP*0.01 bonus
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
|
|
}
|
|
// Swipe
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0010000000000000))
|
|
{
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.08f);
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_ROGUE:
|
|
{
|
|
// Envenom
|
|
if (m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x800000000)))
|
|
{
|
|
// consume from stack dozes not more that have combo-points
|
|
if(uint32 combo = ((Player*)m_caster)->GetComboPoints())
|
|
{
|
|
Aura *poison = 0;
|
|
// Lookup for Deadly poison (only attacker applied)
|
|
Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
|
|
for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
|
|
if( (*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE &&
|
|
((*itr)->GetSpellProto()->SpellFamilyFlags & UI64LIT(0x10000)) &&
|
|
(*itr)->GetCasterGUID()==m_caster->GetGUID() )
|
|
{
|
|
poison = *itr;
|
|
break;
|
|
}
|
|
// count consumed deadly poison doses at target
|
|
if (poison)
|
|
{
|
|
bool needConsume = true;
|
|
uint32 spellId = poison->GetId();
|
|
uint32 doses = poison->GetStackAmount();
|
|
if (doses > combo)
|
|
doses = combo;
|
|
|
|
// Master Poisoner
|
|
Unit::AuraList const& auraList = ((Player*)m_caster)->GetAurasByType(SPELL_AURA_MOD_DURATION_OF_EFFECTS_BY_DISPEL);
|
|
for(Unit::AuraList::const_iterator iter = auraList.begin(); iter!=auraList.end(); ++iter)
|
|
{
|
|
if ((*iter)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_ROGUE && (*iter)->GetSpellProto()->SpellIconID == 1960)
|
|
{
|
|
if (int32 chance = (*iter)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_2))
|
|
if (roll_chance_i(chance))
|
|
needConsume = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(needConsume)
|
|
for (uint32 i = 0; i < doses; ++i)
|
|
unitTarget->RemoveSingleSpellAurasFromStack(spellId);
|
|
|
|
damage *= doses;
|
|
damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.09f * 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 & UI64LIT(0x00020000)) && m_caster->GetTypeId()==TYPEID_PLAYER)
|
|
{
|
|
if(uint32 combo = ((Player*)m_caster)->GetComboPoints())
|
|
{
|
|
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
damage += irand(int32(ap * combo * 0.03f), int32(ap * combo * 0.07f));
|
|
|
|
// Eviscerate and Envenom Bonus Damage (item set effect)
|
|
if(m_caster->GetDummyAura(37169))
|
|
damage += combo*40;
|
|
}
|
|
}
|
|
// Gouge
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000008))
|
|
{
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.21f);
|
|
}
|
|
// Instant Poison
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000002000))
|
|
{
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.10f);
|
|
}
|
|
// Wound Poison
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000010000000))
|
|
{
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.04f);
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_HUNTER:
|
|
{
|
|
//Gore
|
|
if (m_spellInfo->SpellIconID == 1578)
|
|
{
|
|
if (m_caster->HasAura(57627)) // Charge 6 sec post-affect
|
|
damage *= 2;
|
|
}
|
|
// Mongoose Bite
|
|
else if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x000000002)) && m_spellInfo->SpellVisual[0]==342)
|
|
{
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f);
|
|
}
|
|
// Counterattack
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0008000000000000))
|
|
{
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f);
|
|
}
|
|
// Arcane Shot
|
|
else if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x00000800)) && m_spellInfo->maxLevel > 0)
|
|
{
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.15f);
|
|
}
|
|
// Steady Shot
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x100000000))
|
|
{
|
|
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.1f);
|
|
}
|
|
// Explosive Trap Effect
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x00000004))
|
|
{
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1f);
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_PALADIN:
|
|
{
|
|
// Judgement of Righteousness - receive benefit from Spell Damage and Attack power
|
|
if (m_spellInfo->Id == 20187)
|
|
{
|
|
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo));
|
|
if (holy < 0)
|
|
holy = 0;
|
|
damage += int32(ap * 0.2f) + int32(holy * 32 / 100);
|
|
}
|
|
// Judgement of Vengeance/Corruption ${1+0.22*$SPH+0.14*$AP} + 10% for each application of Holy Vengeance/Blood Corruption on the target
|
|
else if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x800000000)) && m_spellInfo->SpellIconID==2292)
|
|
{
|
|
uint32 debuf_id;
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 53733: debuf_id = 53742; break;// Judgement of Corruption -> Blood Corruption
|
|
case 31804: debuf_id = 31803; break;// Judgement of Vengeance -> Holy Vengeance
|
|
default: return;
|
|
}
|
|
|
|
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo));
|
|
if (holy < 0)
|
|
holy = 0;
|
|
damage+=int32(ap * 0.14f) + int32(holy * 22 / 100);
|
|
// Get stack of Holy Vengeance on the target added by caster
|
|
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() == debuf_id) && (*itr)->GetCasterGUID()==m_caster->GetGUID())
|
|
{
|
|
stacks = (*itr)->GetStackAmount();
|
|
break;
|
|
}
|
|
}
|
|
// + 10% for each application of Holy Vengeance on the target
|
|
if(stacks)
|
|
damage += damage * stacks * 10 /100;
|
|
}
|
|
// Avenger's Shield ($m1+0.07*$SPH+0.07*$AP) - ranged sdb for future
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000004000))
|
|
{
|
|
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo));
|
|
if (holy < 0)
|
|
holy = 0;
|
|
damage += int32(ap * 0.07f) + int32(holy * 7 / 100);
|
|
}
|
|
// Hammer of Wrath ($m1+0.15*$SPH+0.15*$AP) - ranged type sdb future fix
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000008000000000))
|
|
{
|
|
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo));
|
|
if (holy < 0)
|
|
holy = 0;
|
|
damage += int32(ap * 0.15f) + int32(holy * 15 / 100);
|
|
}
|
|
// Hammer of the Righteous
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0004000000000000))
|
|
{
|
|
// Add main hand dps * effect[2] amount
|
|
float average = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2;
|
|
int32 count = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_INDEX_2);
|
|
damage += count * int32(average * IN_MILLISECONDS) / m_caster->GetAttackTime(BASE_ATTACK);
|
|
}
|
|
// Shield of Righteousness
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0010000000000000))
|
|
{
|
|
damage+=int32(m_caster->GetShieldBlockValue());
|
|
}
|
|
// Judgement
|
|
else if (m_spellInfo->Id == 54158)
|
|
{
|
|
// [1 + 0.25 * SPH + 0.16 * AP]
|
|
damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * 0.16f);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(damage >= 0)
|
|
m_damage += damage;
|
|
}
|
|
}
|
|
|
|
void Spell::EffectDummy(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!unitTarget && !gameObjTarget && !itemTarget)
|
|
return;
|
|
|
|
// selection by spell family
|
|
switch(m_spellInfo->SpellFamilyName)
|
|
{
|
|
case SPELLFAMILY_GENERIC:
|
|
{
|
|
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 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 triggering, 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 15998: // Capture Worg Pup
|
|
case 29435: // Capture Female Kaliri Hatchling
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
Creature* creatureTarget = (Creature*)unitTarget;
|
|
|
|
creatureTarget->ForcedDespawn();
|
|
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)
|
|
return;
|
|
|
|
Unit* caster = GetAffectiveCaster();
|
|
|
|
if (caster && caster->GetTypeId() == TYPEID_PLAYER)
|
|
{
|
|
WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8);
|
|
data << uint64(unitTarget->GetGUID());
|
|
((Player*)caster)->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 // Create Resonating Skull
|
|
: 17270; // Create Bone Dust
|
|
|
|
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;
|
|
|
|
GameObject* pGameObj = new GameObject;
|
|
|
|
Map *map = creatureTarget->GetMap();
|
|
|
|
// create before death for get proper coordinates
|
|
if (!pGameObj->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 179644, map, m_caster->GetPhaseMask(),
|
|
creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(),
|
|
creatureTarget->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY) )
|
|
{
|
|
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);
|
|
|
|
creatureTarget->ForcedDespawn();
|
|
|
|
DEBUG_LOG("AddObject at SpellEfects.cpp EffectDummy");
|
|
map->Add(pGameObj);
|
|
|
|
return;
|
|
}
|
|
case 23074: // Arcanite 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: // Transporter Arrival - Ultrasafe Transporter: Gadgetzan - backfires
|
|
{
|
|
int32 r = irand(0, 119);
|
|
if (r < 20) // Transporter Malfunction - 1/6 polymorph
|
|
m_caster->CastSpell(m_caster, 23444, true);
|
|
else if (r < 100) // Evil Twin - 4/6 evil twin
|
|
m_caster->CastSpell(m_caster, 23445, true);
|
|
else // Transporter Malfunction - 1/6 miss the target
|
|
m_caster->CastSpell(m_caster, 36902, true);
|
|
|
|
return;
|
|
}
|
|
case 23453: // Gnomish Transporter - Ultrasafe Transporter: Gadgetzan
|
|
{
|
|
if (roll_chance_i(50)) // Gadgetzan Transporter - success
|
|
m_caster->CastSpell(m_caster, 23441, true);
|
|
else // Gadgetzan Transporter Failure - failure
|
|
m_caster->CastSpell(m_caster, 23446, true);
|
|
|
|
return;
|
|
}
|
|
case 23645: // Hourglass Sand
|
|
m_caster->RemoveAurasDueToSpell(23170); // Brood Affliction: Bronze
|
|
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_FLIGHT);
|
|
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)
|
|
// Flying Reindeer
|
|
m_caster->CastSpell(m_caster, 44827, true); //310% flying Reindeer
|
|
else if (flyspeed >= 3.8f)
|
|
// Flying Reindeer
|
|
m_caster->CastSpell(m_caster, 44825, true); //280% flying Reindeer
|
|
else if (flyspeed >= 1.6f)
|
|
// Flying Reindeer
|
|
m_caster->CastSpell(m_caster, 44824, true); //60% flying Reindeer
|
|
else if (speed >= 2.0f)
|
|
// Reindeer
|
|
m_caster->CastSpell(m_caster, 25859, true); //100% ground Reindeer
|
|
else
|
|
// Reindeer
|
|
m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer
|
|
|
|
return;
|
|
}
|
|
case 26074: // Holiday Cheer
|
|
// implemented at client side
|
|
return;
|
|
case 28006: // Arcane Cloaking
|
|
{
|
|
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER )
|
|
// Naxxramas Entry Flag Effect DND
|
|
m_caster->CastSpell(unitTarget, 29294, true);
|
|
|
|
return;
|
|
}
|
|
case 29200: // Purify Helboar Meat
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
uint32 spell_id = roll_chance_i(50)
|
|
? 29277 // Summon Purified Helboar Meat
|
|
: 29278; // Summon Toxic Helboar Meat
|
|
|
|
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)) // Nigh-Invulnerability - success
|
|
m_caster->CastSpell(m_caster, 30456, true, m_CastItem);
|
|
else // Complete Vulnerability - 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)) // Poultryized! - success
|
|
m_caster->CastSpell(unitTarget, 30501, true, m_CastItem);
|
|
else // Poultryized! - 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; // Mr Pinchy's Blessing
|
|
case 2: spell_id = 33057; break; // Summon Mighty Mr. Pinchy
|
|
case 3: spell_id = 33059; break; // Summon Furious Mr. Pinchy
|
|
case 4: spell_id = 33062; break; // Tiny Magical Crawdad
|
|
case 5: spell_id = 33064; break; // Mr. Pinchy's Gift
|
|
}
|
|
|
|
m_caster->CastSpell(m_caster, spell_id, true, NULL);
|
|
return;
|
|
}
|
|
case 35745: // Socrethar's Stone
|
|
{
|
|
uint32 spell_id;
|
|
switch(m_caster->GetAreaId())
|
|
{
|
|
case 3900: spell_id = 35743; break; // Socrethar Portal
|
|
case 3742: spell_id = 35744; break; // Socrethar Portal
|
|
default: return;
|
|
}
|
|
|
|
m_caster->CastSpell(m_caster, spell_id, true);
|
|
return;
|
|
}
|
|
case 37674: // Chaos Blast
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
int32 basepoints0 = 100;
|
|
m_caster->CastCustomSpell(unitTarget, 37675, &basepoints0, NULL, NULL, true);
|
|
return;
|
|
}
|
|
case 40802: // Mingo's Fortune Generator (Mingo's Fortune Giblets)
|
|
{
|
|
// selecting one from Bloodstained Fortune item
|
|
uint32 newitemid;
|
|
switch(urand(1, 20))
|
|
{
|
|
case 1: newitemid = 32688; break;
|
|
case 2: newitemid = 32689; break;
|
|
case 3: newitemid = 32690; break;
|
|
case 4: newitemid = 32691; break;
|
|
case 5: newitemid = 32692; break;
|
|
case 6: newitemid = 32693; break;
|
|
case 7: newitemid = 32700; break;
|
|
case 8: newitemid = 32701; break;
|
|
case 9: newitemid = 32702; break;
|
|
case 10: newitemid = 32703; break;
|
|
case 11: newitemid = 32704; break;
|
|
case 12: newitemid = 32705; break;
|
|
case 13: newitemid = 32706; break;
|
|
case 14: newitemid = 32707; break;
|
|
case 15: newitemid = 32708; break;
|
|
case 16: newitemid = 32709; break;
|
|
case 17: newitemid = 32710; break;
|
|
case 18: newitemid = 32711; break;
|
|
case 19: newitemid = 32712; break;
|
|
case 20: newitemid = 32713; break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
DoCreateItem(eff_idx, newitemid);
|
|
return;
|
|
}
|
|
case 42287: // Salvage Wreckage
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if (roll_chance_i(66))
|
|
m_caster->CastSpell(m_caster, 42289, true, m_CastItem);
|
|
else
|
|
m_caster->CastSpell(m_caster, 42288, true);
|
|
|
|
return;
|
|
}
|
|
case 43036: // Dismembering Corpse
|
|
{
|
|
if (!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if (unitTarget->HasAura(43059, EFFECT_INDEX_0))
|
|
return;
|
|
|
|
unitTarget->CastSpell(m_caster, 43037, true);
|
|
unitTarget->CastSpell(unitTarget, 43059, true);
|
|
return;
|
|
}
|
|
// Demon Broiled Surprise
|
|
/* FIX ME: Required for correct work implementing implicit target 7 (in pair (22,7))
|
|
case 43723:
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
((Player*)m_caster)->CastSpell(unitTarget, 43753, true);
|
|
return;
|
|
}
|
|
*/
|
|
case 43882: // Scourging Crystal Controller Dummy
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
// see spell dummy 50133
|
|
unitTarget->RemoveAurasDueToSpell(43874);
|
|
return;
|
|
}
|
|
case 44454: // Tasty Reef Fish
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
m_caster->CastSpell(unitTarget, 44455, true, m_CastItem);
|
|
return;
|
|
}
|
|
case 44875: // Complete Raptor Capture
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
Creature* creatureTarget = (Creature*)unitTarget;
|
|
|
|
creatureTarget->ForcedDespawn();
|
|
|
|
//cast spell Raptor Capture Credit
|
|
m_caster->CastSpell(m_caster, 42337, true, NULL);
|
|
return;
|
|
}
|
|
case 44997: // Converting Sentry
|
|
{
|
|
//Converted Sentry Credit
|
|
m_caster->CastSpell(m_caster, 45009, true);
|
|
return;
|
|
}
|
|
case 45030: // Impale Emissary
|
|
{
|
|
// Emissary of Hate Credit
|
|
m_caster->CastSpell(m_caster, 45088, true);
|
|
return;
|
|
}
|
|
case 45980: // Re-Cursive Transmatter Injection
|
|
{
|
|
if (m_caster->GetTypeId() == TYPEID_PLAYER && unitTarget)
|
|
{
|
|
if (const SpellEntry *pSpell = sSpellStore.LookupEntry(46022))
|
|
{
|
|
m_caster->CastSpell(unitTarget, pSpell, true);
|
|
((Player*)m_caster)->KilledMonsterCredit(pSpell->EffectMiscValue[EFFECT_INDEX_0], 0);
|
|
}
|
|
|
|
if (unitTarget->GetTypeId() == TYPEID_UNIT)
|
|
((Creature*)unitTarget)->ForcedDespawn();
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 45685: // Magnataur On Death 2
|
|
{
|
|
m_caster->RemoveAurasDueToSpell(45673);
|
|
m_caster->RemoveAurasDueToSpell(45672);
|
|
m_caster->RemoveAurasDueToSpell(45677);
|
|
m_caster->RemoveAurasDueToSpell(45681);
|
|
m_caster->RemoveAurasDueToSpell(45683);
|
|
return;
|
|
}
|
|
case 45990: // Collect Oil
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
if (const SpellEntry *pSpell = sSpellStore.LookupEntry(45991))
|
|
{
|
|
unitTarget->CastSpell(unitTarget, pSpell, true);
|
|
((Creature*)unitTarget)->ForcedDespawn(GetSpellDuration(pSpell) + 1);
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 46167: // Planning for the Future: Create Snowfall Glade Pup Cover
|
|
case 50926: // Gluttonous Lurkers: Create Zul'Drak Rat Cover
|
|
case 51026: // Create Drakkari Medallion Cover
|
|
case 51592: // Pickup Primordial Hatchling
|
|
case 51961: // Captured Chicken Cover
|
|
case 55364: // Create Ghoul Drool Cover
|
|
case 61832: // Rifle the Bodies: Create Magehunter Personal Effects Cover
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
uint32 spellId = 0;
|
|
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 46167: spellId = 46773; break;
|
|
case 50926: spellId = 50927; break;
|
|
case 51026: spellId = 50737; break;
|
|
case 51592: spellId = 51593; break;
|
|
case 51961: spellId = 51037; break;
|
|
case 55364: spellId = 55363; break;
|
|
case 61832: spellId = 47096; break;
|
|
}
|
|
|
|
if (const SpellEntry *pSpell = sSpellStore.LookupEntry(spellId))
|
|
{
|
|
unitTarget->CastSpell(m_caster, spellId, true);
|
|
|
|
Creature* creatureTarget = (Creature*)unitTarget;
|
|
|
|
if (const SpellCastTimesEntry *pCastTime = sSpellCastTimesStore.LookupEntry(pSpell->CastingTimeIndex))
|
|
creatureTarget->ForcedDespawn(pCastTime->CastTime + 1);
|
|
}
|
|
return;
|
|
}
|
|
case 46485: // Greatmother's Soulcatcher
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
if (const SpellEntry *pSpell = sSpellStore.LookupEntry(46486))
|
|
{
|
|
m_caster->CastSpell(unitTarget, pSpell, true);
|
|
|
|
if (const SpellEntry *pSpellCredit = sSpellStore.LookupEntry(pSpell->EffectMiscValue[EFFECT_INDEX_0]))
|
|
((Player*)m_caster)->KilledMonsterCredit(pSpellCredit->EffectMiscValue[EFFECT_INDEX_0], 0);
|
|
|
|
((Creature*)unitTarget)->ForcedDespawn();
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 46606: // Plague Canister Dummy
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
unitTarget->CastSpell(m_caster, 43160, true);
|
|
unitTarget->setDeathState(JUST_DIED);
|
|
unitTarget->SetHealth(0);
|
|
return;
|
|
}
|
|
case 46797: // Quest - Borean Tundra - Set Explosives Cart
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// Quest - Borean Tundra - Summon Explosives Cart
|
|
unitTarget->CastSpell(unitTarget,46798,true,m_CastItem,NULL,m_originalCasterGUID);
|
|
break;
|
|
}
|
|
case 49357: // Brewfest Mount Transformation
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
|
|
return;
|
|
|
|
m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
|
|
|
|
// Ram for Alliance, Kodo for Horde
|
|
if (((Player *)m_caster)->GetTeam() == ALLIANCE)
|
|
{
|
|
if (m_caster->GetSpeedRate(MOVE_RUN) >= 2.0f)
|
|
// 100% Ram
|
|
m_caster->CastSpell(m_caster, 43900, true);
|
|
else
|
|
// 60% Ram
|
|
m_caster->CastSpell(m_caster, 43899, true);
|
|
}
|
|
else
|
|
{
|
|
if (((Player *)m_caster)->GetSpeedRate(MOVE_RUN) >= 2.0f)
|
|
// 100% Kodo
|
|
m_caster->CastSpell(m_caster, 49379, true);
|
|
else
|
|
// 60% Kodo
|
|
m_caster->CastSpell(m_caster, 49378, true);
|
|
}
|
|
return;
|
|
}
|
|
case 50133: // Scourging Crystal Controller
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
if (unitTarget->HasAura(43874))
|
|
{
|
|
// someone else is already channeling target
|
|
if (unitTarget->HasAura(43878))
|
|
return;
|
|
|
|
m_caster->CastSpell(unitTarget, 43878, true, m_CastItem);
|
|
}
|
|
|
|
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 50546: // Ley Line Focus Control Ring Effect
|
|
case 50547: // Ley Line Focus Control Amulet Effect
|
|
case 50548: // Ley Line Focus Control Talisman Effect
|
|
{
|
|
if (!m_originalCaster || !unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 50546: unitTarget->CastSpell(m_originalCaster, 47390, true); break;
|
|
case 50547: unitTarget->CastSpell(m_originalCaster, 47472, true); break;
|
|
case 50548: unitTarget->CastSpell(m_originalCaster, 47635, true); break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 51276: // Incinerate Corpse
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
unitTarget->CastSpell(unitTarget, 51278, true);
|
|
unitTarget->CastSpell(m_caster, 51279, true);
|
|
|
|
unitTarget->setDeathState(JUST_DIED);
|
|
return;
|
|
}
|
|
case 51330: // Shoot RJR
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// guessed chances
|
|
if (roll_chance_i(75))
|
|
m_caster->CastSpell(unitTarget, roll_chance_i(50) ? 51332 : 51366, true, m_CastItem);
|
|
else
|
|
m_caster->CastSpell(unitTarget, 51331, true, m_CastItem);
|
|
|
|
return;
|
|
}
|
|
case 51333: // Dig For Treasure
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
if (roll_chance_i(75))
|
|
m_caster->CastSpell(unitTarget, 51370, true, m_CastItem);
|
|
else
|
|
m_caster->CastSpell(m_caster, 51345, 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;
|
|
}
|
|
case 51840: // Despawn Fruit Tosser
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
if (roll_chance_i(20))
|
|
{
|
|
// summon NPC, or...
|
|
unitTarget->CastSpell(m_caster, 52070, true);
|
|
}
|
|
else
|
|
{
|
|
// ...drop banana, orange or papaya
|
|
switch(urand(0,2))
|
|
{
|
|
case 0: unitTarget->CastSpell(m_caster, 51836, true); break;
|
|
case 1: unitTarget->CastSpell(m_caster, 51837, true); break;
|
|
case 2: unitTarget->CastSpell(m_caster, 51839, true); break;
|
|
}
|
|
}
|
|
|
|
((Creature*)unitTarget)->ForcedDespawn();
|
|
return;
|
|
}
|
|
case 51866: // Kick Nass
|
|
{
|
|
// It is possible that Nass Heartbeat (spell id 61438) is involved in this
|
|
// If so, unclear how it should work and using the below instead (even though it could be a bit hack-ish)
|
|
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
// Only own guardian pet
|
|
if (m_caster != unitTarget->GetOwner())
|
|
return;
|
|
|
|
// This means we already set state (see below) and need to wait.
|
|
if (unitTarget->hasUnitState(UNIT_STAT_ROOT))
|
|
return;
|
|
|
|
// Expecting pTargetDummy to be summoned by AI at death of target creatures.
|
|
|
|
Creature* pTargetDummy = NULL;
|
|
float fRange = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
|
|
|
|
MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, 28523, true, fRange*2);
|
|
MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, pTargetDummy, u_check);
|
|
|
|
Cell::VisitGridObjects(m_caster, searcher, fRange*2);
|
|
|
|
if (pTargetDummy)
|
|
{
|
|
if (unitTarget->hasUnitState(UNIT_STAT_FOLLOW | UNIT_STAT_FOLLOW_MOVE))
|
|
unitTarget->GetMotionMaster()->MovementExpired();
|
|
|
|
unitTarget->MonsterMove(pTargetDummy->GetPositionX(), pTargetDummy->GetPositionY(), pTargetDummy->GetPositionZ(), IN_MILLISECONDS);
|
|
|
|
// Add state to temporarily prevent follow
|
|
unitTarget->addUnitState(UNIT_STAT_ROOT);
|
|
|
|
// Collect Hair Sample
|
|
unitTarget->CastSpell(pTargetDummy, 51870, true);
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 51872: // Hair Sample Collected
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
// clear state to allow follow again
|
|
m_caster->clearUnitState(UNIT_STAT_ROOT);
|
|
|
|
// Nass Kill Credit
|
|
m_caster->CastSpell(m_caster, 51871, true);
|
|
|
|
// Despawn dummy creature
|
|
((Creature*)unitTarget)->ForcedDespawn();
|
|
|
|
return;
|
|
}
|
|
case 51964: // Tormentor's Incense
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
// This might not be the best way, and effect may need some adjustment. Removal of any aura from surrounding dummy creatures?
|
|
if (((Creature*)unitTarget)->AI())
|
|
((Creature*)unitTarget)->AI()->AttackStart(m_caster);
|
|
|
|
return;
|
|
}
|
|
case 52308: // Take Sputum Sample
|
|
{
|
|
switch(eff_idx)
|
|
{
|
|
case EFFECT_INDEX_0:
|
|
{
|
|
uint32 spellID = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_0);
|
|
uint32 reqAuraID = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1);
|
|
|
|
if (m_caster->HasAura(reqAuraID, EFFECT_INDEX_0))
|
|
m_caster->CastSpell(m_caster, spellID, true, NULL);
|
|
return;
|
|
}
|
|
case EFFECT_INDEX_1:
|
|
return; // additional data for dummy[0]
|
|
}
|
|
return;
|
|
}
|
|
case 52759: // Ancestral Awakening
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
m_caster->CastCustomSpell(unitTarget, 52752, &damage, NULL, NULL, true);
|
|
return;
|
|
}
|
|
case 52845: // Brewfest Mount Transformation (Faction Swap)
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
|
|
return;
|
|
|
|
m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
|
|
|
|
// Ram for Horde, Kodo for Alliance
|
|
if (((Player *)m_caster)->GetTeam() == HORDE)
|
|
{
|
|
if (m_caster->GetSpeedRate(MOVE_RUN) >= 2.0f)
|
|
// Swift Brewfest Ram, 100% Ram
|
|
m_caster->CastSpell(m_caster, 43900, true);
|
|
else
|
|
// Brewfest Ram, 60% Ram
|
|
m_caster->CastSpell(m_caster, 43899, true);
|
|
}
|
|
else
|
|
{
|
|
if (((Player *)m_caster)->GetSpeedRate(MOVE_RUN) >= 2.0f)
|
|
// Great Brewfest Kodo, 100% Kodo
|
|
m_caster->CastSpell(m_caster, 49379, true);
|
|
else
|
|
// Brewfest Riding Kodo, 60% Kodo
|
|
m_caster->CastSpell(m_caster, 49378, true);
|
|
}
|
|
return;
|
|
}
|
|
case 53341: // Rune of Cinderglacier
|
|
case 53343: // Rune of Razorice
|
|
{
|
|
// Runeforging Credit
|
|
m_caster->CastSpell(m_caster, 54586, true);
|
|
return;
|
|
}
|
|
case 53808: // Pygmy Oil
|
|
{
|
|
const SpellEntry *pSpellShrink = sSpellStore.LookupEntry(53805);
|
|
const SpellEntry *pSpellTransf = sSpellStore.LookupEntry(53806);
|
|
|
|
if (!pSpellTransf || !pSpellShrink)
|
|
return;
|
|
|
|
if (Aura* pAura = m_caster->GetAura(pSpellShrink->Id, EFFECT_INDEX_0))
|
|
{
|
|
uint8 stackNum = pAura->GetStackAmount();
|
|
|
|
// chance to become pygmified (5, 10, 15 etc)
|
|
if (roll_chance_i(stackNum*5))
|
|
{
|
|
m_caster->RemoveAurasDueToSpell(pSpellShrink->Id);
|
|
m_caster->CastSpell(m_caster, pSpellTransf, true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m_caster->HasAura(pSpellTransf->Id, EFFECT_INDEX_0))
|
|
return;
|
|
|
|
m_caster->CastSpell(m_caster, pSpellShrink, true);
|
|
return;
|
|
}
|
|
case 55004: // Nitro Boosts
|
|
{
|
|
if (!m_CastItem)
|
|
return;
|
|
|
|
if (roll_chance_i(95)) // Nitro Boosts - success
|
|
m_caster->CastSpell(m_caster, 54861, true, m_CastItem);
|
|
else // Knocked Up - backfire 5%
|
|
m_caster->CastSpell(m_caster, 46014, true, m_CastItem);
|
|
|
|
return;
|
|
}
|
|
case 55818: // Hurl Boulder
|
|
{
|
|
// unclear how many summon min/max random, best guess below
|
|
uint32 random = urand(3,5);
|
|
|
|
for(uint32 i = 0; i < random; ++i)
|
|
m_caster->CastSpell(m_caster, 55528, true);
|
|
|
|
return;
|
|
}
|
|
case 58418: // Portal to Orgrimmar
|
|
case 58420: // Portal to Stormwind
|
|
return; // implemented in EffectScript[0]
|
|
case 58601: // Remove Flight Auras
|
|
{
|
|
m_caster->RemoveSpellsCausingAura(SPELL_AURA_FLY);
|
|
m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED);
|
|
return;
|
|
}
|
|
case 59640: // Underbelly Elixir
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
uint32 spell_id = 0;
|
|
switch(urand(1,3))
|
|
{
|
|
case 1: spell_id = 59645; break;
|
|
case 2: spell_id = 59831; break;
|
|
case 3: spell_id = 59843; break;
|
|
}
|
|
|
|
m_caster->CastSpell(m_caster,spell_id,true,NULL);
|
|
return;
|
|
}
|
|
case 60932: // Disengage (one from creature versions)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
m_caster->CastSpell(unitTarget,60934,true,NULL);
|
|
return;
|
|
}
|
|
case 67019: // Flask of the North
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
uint32 spell_id = 0;
|
|
switch(m_caster->getClass())
|
|
{
|
|
case CLASS_WARRIOR:
|
|
case CLASS_DEATH_KNIGHT:
|
|
spell_id = 67018; // STR for Warriors, Death Knights
|
|
break;
|
|
case CLASS_ROGUE:
|
|
case CLASS_HUNTER:
|
|
spell_id = 67017; // AP for Rogues, Hunters
|
|
break;
|
|
case CLASS_PRIEST:
|
|
case CLASS_MAGE:
|
|
case CLASS_WARLOCK:
|
|
spell_id = 67016; // SPD for Priests, Mages, Warlocks
|
|
break;
|
|
case CLASS_SHAMAN:
|
|
// random (SPD, AP)
|
|
spell_id = roll_chance_i(50) ? 67016 : 67017;
|
|
break;
|
|
case CLASS_PALADIN:
|
|
case CLASS_DRUID:
|
|
default:
|
|
// random (SPD, STR)
|
|
spell_id = roll_chance_i(50) ? 67016 : 67018;
|
|
break;
|
|
}
|
|
m_caster->CastSpell(m_caster, spell_id, true);
|
|
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 SpellCooldowns& cm = ((Player *)m_caster)->GetSpellCooldownMap();
|
|
for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
|
|
{
|
|
SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
|
|
|
|
if (spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
|
|
(GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) &&
|
|
spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0)
|
|
{
|
|
((Player*)m_caster)->RemoveSpellCooldown((itr++)->first, true);
|
|
}
|
|
else
|
|
++itr;
|
|
}
|
|
return;
|
|
}
|
|
case 31687: // Summon Water Elemental
|
|
{
|
|
if (m_caster->HasAura(70937)) // Glyph of Eternal Water (permanent limited by known spells version)
|
|
m_caster->CastSpell(m_caster, 70908, true);
|
|
else // temporary version
|
|
m_caster->CastSpell(m_caster, 70907, true);
|
|
|
|
return;
|
|
}
|
|
case 32826: // Polymorph Cast Visual
|
|
{
|
|
if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT)
|
|
{
|
|
//Polymorph Cast Visual Rank 1
|
|
const uint32 spell_list[6] =
|
|
{
|
|
32813, // Squirrel Form
|
|
32816, // Giraffe Form
|
|
32817, // Serpent Form
|
|
32818, // Dragonhawk Form
|
|
32819, // Worgen Form
|
|
32820 // Sheep Form
|
|
};
|
|
unitTarget->CastSpell( unitTarget, spell_list[urand(0, 5)], true);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Conjure Mana Gem
|
|
if (eff_idx == EFFECT_INDEX_1 && m_spellInfo->Effect[EFFECT_INDEX_0] == SPELL_EFFECT_CREATE_ITEM)
|
|
{
|
|
if (m_caster->GetTypeId()!=TYPEID_PLAYER)
|
|
return;
|
|
|
|
// checked in create item check, avoid unexpected
|
|
if (Item* item = ((Player*)m_caster)->GetItemByLimitedCategory(ITEM_LIMIT_CATEGORY_MANA_GEM))
|
|
if (item->HasMaxCharges())
|
|
return;
|
|
|
|
unitTarget->CastSpell( unitTarget, m_spellInfo->CalculateSimpleValue(eff_idx), true, m_CastItem);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_WARRIOR:
|
|
{
|
|
// Charge
|
|
if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x1)) && m_spellInfo->SpellVisual[0] == 867)
|
|
{
|
|
int32 chargeBasePoints0 = damage;
|
|
m_caster->CastCustomSpell(m_caster, 34846, &chargeBasePoints0, NULL, NULL, true);
|
|
return;
|
|
}
|
|
// Execute
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x20000000))
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
uint32 rage = m_caster->GetPower(POWER_RAGE);
|
|
|
|
// up to max 30 rage cost
|
|
if (rage > 300)
|
|
rage = 300;
|
|
|
|
// Glyph of Execution bonus
|
|
uint32 rage_modified = rage;
|
|
|
|
if (Aura *aura = m_caster->GetDummyAura(58367))
|
|
rage_modified += aura->GetModifier()->m_amount*10;
|
|
|
|
int32 basePoints0 = damage+int32(rage_modified * m_spellInfo->DmgMultiplier[eff_idx] +
|
|
m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f);
|
|
|
|
m_caster->CastCustomSpell(unitTarget, 20647, &basePoints0, NULL, NULL, true, 0);
|
|
|
|
// Sudden Death
|
|
if (m_caster->HasAura(52437))
|
|
{
|
|
Unit::AuraList const& auras = m_caster->GetAurasByType(SPELL_AURA_PROC_TRIGGER_SPELL);
|
|
for (Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
|
|
{
|
|
// Only Sudden Death have this SpellIconID with SPELL_AURA_PROC_TRIGGER_SPELL
|
|
if ((*itr)->GetSpellProto()->SpellIconID == 1989)
|
|
{
|
|
// saved rage top stored in next affect
|
|
uint32 lastrage = (*itr)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_1)*10;
|
|
if(lastrage < rage)
|
|
rage -= lastrage;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_caster->SetPower(POWER_RAGE,m_caster->GetPower(POWER_RAGE)-rage);
|
|
return;
|
|
}
|
|
// Slam
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000200000))
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
|
|
// dummy cast itself ignored by client in logs
|
|
m_caster->CastCustomSpell(unitTarget,50782,&damage,NULL,NULL,true);
|
|
return;
|
|
}
|
|
// Concussion Blow
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000004000000))
|
|
{
|
|
m_damage+= uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
|
|
return;
|
|
}
|
|
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
// Warrior's Wrath
|
|
case 21977:
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
m_caster->CastSpell(unitTarget, 21887, true);// spell mod
|
|
return;
|
|
}
|
|
// Last Stand
|
|
case 12975:
|
|
{
|
|
int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth()*0.3);
|
|
m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, NULL, NULL, true, NULL);
|
|
return;
|
|
}
|
|
// Bloodthirst
|
|
case 23881:
|
|
{
|
|
m_caster->CastCustomSpell(unitTarget, 23885, &damage, NULL, NULL, true, NULL);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_WARLOCK:
|
|
{
|
|
// Life Tap
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000040000))
|
|
{
|
|
if (unitTarget && (int32(unitTarget->GetHealth()) > damage))
|
|
{
|
|
// Shouldn't Appear in Combat Log
|
|
unitTarget->ModifyHealth(-damage);
|
|
|
|
int32 spell_power = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo));
|
|
int32 mana = damage + spell_power / 2;
|
|
|
|
// Improved Life Tap mod
|
|
Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
|
|
for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr)
|
|
if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 208)
|
|
mana = ((*itr)->GetModifier()->m_amount + 100)* mana / 100;
|
|
|
|
m_caster->CastCustomSpell(unitTarget, 31818, &mana, NULL, NULL, true);
|
|
|
|
// Mana Feed
|
|
int32 manaFeedVal = 0;
|
|
Unit::AuraList const& mod = m_caster->GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER);
|
|
for(Unit::AuraList::const_iterator itr = mod.begin(); itr != mod.end(); ++itr)
|
|
{
|
|
if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 1982)
|
|
manaFeedVal+= (*itr)->GetModifier()->m_amount;
|
|
}
|
|
if (manaFeedVal > 0)
|
|
{
|
|
manaFeedVal = manaFeedVal * mana / 100;
|
|
m_caster->CastCustomSpell(m_caster, 32553, &manaFeedVal, NULL, NULL, true, NULL);
|
|
}
|
|
}
|
|
else
|
|
SendCastResult(SPELL_FAILED_FIZZLE);
|
|
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_PRIEST:
|
|
{
|
|
// Penance
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0080000000000000))
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
int hurt = 0;
|
|
int heal = 0;
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 47540: hurt = 47758; heal = 47757; break;
|
|
case 53005: hurt = 53001; heal = 52986; break;
|
|
case 53006: hurt = 53002; heal = 52987; break;
|
|
case 53007: hurt = 53003; heal = 52988; break;
|
|
default:
|
|
sLog.outError("Spell::EffectDummy: Spell %u Penance need set correct heal/damage spell", m_spellInfo->Id);
|
|
return;
|
|
}
|
|
|
|
// prevent interrupted message for main spell
|
|
finish(true);
|
|
|
|
// replace cast by selected spell, this also make it interruptible including target death case
|
|
if (m_caster->IsFriendlyTo(unitTarget))
|
|
m_caster->CastSpell(unitTarget, heal, false);
|
|
else
|
|
m_caster->CastSpell(unitTarget, hurt, false);
|
|
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_DRUID:
|
|
{
|
|
// Starfall
|
|
if (m_spellInfo->SpellFamilyFlags2 & 0x00000100)
|
|
{
|
|
//Shapeshifting into an animal form or mounting cancels the effect.
|
|
if(m_caster->GetCreatureType() == CREATURE_TYPE_BEAST || m_caster->IsMounted())
|
|
{
|
|
if(m_triggeredByAuraSpell)
|
|
m_caster->RemoveAurasDueToSpell(m_triggeredByAuraSpell->Id);
|
|
return;
|
|
}
|
|
|
|
//Any effect which causes you to lose control of your character will supress the starfall effect.
|
|
if (m_caster->hasUnitState(UNIT_STAT_NO_FREE_MOVE))
|
|
return;
|
|
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 50286: m_caster->CastSpell(unitTarget, 50288, true); return;
|
|
case 53196: m_caster->CastSpell(unitTarget, 53191, true); return;
|
|
case 53197: m_caster->CastSpell(unitTarget, 53194, true); return;
|
|
case 53198: m_caster->CastSpell(unitTarget, 53195, true); return;
|
|
default:
|
|
sLog.outError("Spell::EffectDummy: Unhandeled Starfall spell rank %u",m_spellInfo->Id);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_ROGUE:
|
|
{
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
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;
|
|
}
|
|
case 14185: // Preparation
|
|
{
|
|
if (m_caster->GetTypeId()!=TYPEID_PLAYER)
|
|
return;
|
|
|
|
//immediately finishes the cooldown on certain Rogue abilities
|
|
const SpellCooldowns& cm = ((Player *)m_caster)->GetSpellCooldownMap();
|
|
for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
|
|
{
|
|
SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
|
|
|
|
if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & UI64LIT(0x0000024000000860)))
|
|
((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true);
|
|
else
|
|
++itr;
|
|
}
|
|
return;
|
|
}
|
|
case 31231: // Cheat Death
|
|
{
|
|
m_caster->CastSpell(m_caster, 45182, true);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_HUNTER:
|
|
{
|
|
// Steady Shot
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x100000000))
|
|
{
|
|
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_damage+= damage;
|
|
return;
|
|
}
|
|
|
|
// Disengage
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000400000000000))
|
|
{
|
|
Unit* target = unitTarget;
|
|
uint32 spellid;
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 57635: spellid = 57636; break; // one from creature cases
|
|
case 61507: spellid = 61508; break; // one from creature cases
|
|
default:
|
|
sLog.outError("Spell %u not handled propertly in EffectDummy(Disengage)",m_spellInfo->Id);
|
|
return;
|
|
}
|
|
if (!target || !target->isAlive())
|
|
return;
|
|
m_caster->CastSpell(target,spellid,true,NULL);
|
|
}
|
|
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 23989: // Readiness talent
|
|
{
|
|
if (m_caster->GetTypeId()!=TYPEID_PLAYER)
|
|
return;
|
|
|
|
//immediately finishes the cooldown for hunter abilities
|
|
const SpellCooldowns& cm = ((Player*)m_caster)->GetSpellCooldownMap();
|
|
for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
|
|
{
|
|
SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
|
|
|
|
if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 )
|
|
((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true);
|
|
else
|
|
++itr;
|
|
}
|
|
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;
|
|
}
|
|
// Last Stand
|
|
case 53478:
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
int32 healthModSpellBasePoints0 = int32(unitTarget->GetMaxHealth() * 0.3);
|
|
unitTarget->CastCustomSpell(unitTarget, 53479, &healthModSpellBasePoints0, NULL, NULL, true, NULL);
|
|
return;
|
|
}
|
|
// Master's Call
|
|
case 53271:
|
|
{
|
|
Pet* pet = m_caster->GetPet();
|
|
if (!pet || !unitTarget)
|
|
return;
|
|
|
|
pet->CastSpell(unitTarget, m_spellInfo->CalculateSimpleValue(eff_idx), true);
|
|
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;
|
|
case 48824: hurt = 48822; heal = 48820; break;
|
|
case 48825: hurt = 48823; heal = 48821; 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);
|
|
else
|
|
m_caster->CastSpell(unitTarget, hurt, true);
|
|
|
|
return;
|
|
}
|
|
case 561: // Judgement of command
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
uint32 spell_id = m_currentBasePoints[eff_idx];
|
|
SpellEntry const* spell_proto = sSpellStore.LookupEntry(spell_id);
|
|
if (!spell_proto)
|
|
return;
|
|
|
|
m_caster->CastSpell(unitTarget, spell_proto, true, NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 31789: // Righteous Defense (step 1)
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
{
|
|
SendCastResult(SPELL_FAILED_TARGET_AFFECTING_COMBAT);
|
|
return;
|
|
}
|
|
|
|
// 31989 -> dummy effect (step 1) + dummy effect (step 2) -> 31709 (taunt like spell for each target)
|
|
Unit* friendTarget = !unitTarget || unitTarget->IsFriendlyTo(m_caster) ? unitTarget : unitTarget->getVictim();
|
|
if (friendTarget)
|
|
{
|
|
Player* player = friendTarget->GetCharmerOrOwnerPlayerOrPlayerItself();
|
|
if (!player || !player->IsInSameRaidWith((Player*)m_caster))
|
|
friendTarget = NULL;
|
|
}
|
|
|
|
// non-standard cast requirement check
|
|
if (!friendTarget || friendTarget->getAttackers().empty())
|
|
{
|
|
((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id,true);
|
|
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), copy
|
|
Unit::AttackerSet attackers = friendTarget->getAttackers();
|
|
|
|
// selected from list 3
|
|
for(uint32 i = 0; i < std::min(size_t(3),attackers.size()); ++i)
|
|
{
|
|
Unit::AttackerSet::iterator aItr = attackers.begin();
|
|
std::advance(aItr, rand() % attackers.size());
|
|
AddUnitTarget((*aItr), EFFECT_INDEX_1);
|
|
attackers.erase(aItr);
|
|
}
|
|
|
|
// 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:
|
|
{
|
|
// Cleansing Totem
|
|
if ((m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000004000000)) && m_spellInfo->SpellIconID==1673)
|
|
{
|
|
if (unitTarget)
|
|
m_caster->CastSpell(unitTarget, 52025, true);
|
|
return;
|
|
}
|
|
// Healing Stream Totem
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000002000))
|
|
{
|
|
if (unitTarget)
|
|
{
|
|
if (Unit *owner = m_caster->GetOwner())
|
|
{
|
|
// Restorative Totems
|
|
Unit::AuraList const& mDummyAuras = owner->GetAurasByType(SPELL_AURA_DUMMY);
|
|
for(Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
|
|
// only its have dummy with specific icon
|
|
if ((*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_SHAMAN && (*i)->GetSpellProto()->SpellIconID == 338)
|
|
damage += (*i)->GetModifier()->m_amount * damage / 100;
|
|
|
|
// Glyph of Healing Stream Totem
|
|
if (Aura *dummy = owner->GetDummyAura(55456))
|
|
damage += dummy->GetModifier()->m_amount * damage / 100;
|
|
}
|
|
m_caster->CastCustomSpell(unitTarget, 52042, &damage, NULL, NULL, true, 0, 0, m_originalCasterGUID);
|
|
}
|
|
return;
|
|
}
|
|
// Mana Spring Totem
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000004000))
|
|
{
|
|
if (!unitTarget || unitTarget->getPowerType()!=POWER_MANA)
|
|
return;
|
|
m_caster->CastCustomSpell(unitTarget, 52032, &damage, 0, 0, true, 0, 0, m_originalCasterGUID);
|
|
return;
|
|
}
|
|
if (m_spellInfo->Id == 39610) // Mana Tide Totem effect
|
|
{
|
|
if (!unitTarget || unitTarget->getPowerType() != POWER_MANA)
|
|
return;
|
|
// Glyph of Mana Tide
|
|
if (Unit *owner = m_caster->GetOwner())
|
|
if (Aura *dummy = owner->GetDummyAura(55441))
|
|
damage+=dummy->GetModifier()->m_amount;
|
|
// 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;
|
|
}
|
|
// Lava Lash
|
|
if (m_spellInfo->SpellFamilyFlags2 & 0x00000004)
|
|
{
|
|
if (m_caster->GetTypeId()!=TYPEID_PLAYER)
|
|
return;
|
|
Item *item = ((Player*)m_caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
|
|
if (item)
|
|
{
|
|
// Damage is increased if your off-hand weapon is enchanted with Flametongue.
|
|
Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
|
|
for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr)
|
|
{
|
|
if ((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_SHAMAN &&
|
|
((*itr)->GetSpellProto()->SpellFamilyFlags & UI64LIT(0x0000000000200000)) &&
|
|
(*itr)->GetCastItemGUID() == item->GetGUID())
|
|
{
|
|
m_damage += m_damage * damage / 100;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// Fire Nova
|
|
if (m_spellInfo->SpellIconID == 33)
|
|
{
|
|
// fire totems slot
|
|
Totem* totem = m_caster->GetTotem(TOTEM_SLOT_FIRE);
|
|
if (!totem)
|
|
return;
|
|
|
|
uint32 triggered_spell_id;
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 1535: triggered_spell_id = 8349; break;
|
|
case 8498: triggered_spell_id = 8502; break;
|
|
case 8499: triggered_spell_id = 8503; break;
|
|
case 11314: triggered_spell_id = 11306; break;
|
|
case 11315: triggered_spell_id = 11307; break;
|
|
case 25546: triggered_spell_id = 25535; break;
|
|
case 25547: triggered_spell_id = 25537; break;
|
|
case 61649: triggered_spell_id = 61650; break;
|
|
case 61657: triggered_spell_id = 61654; break;
|
|
default: return;
|
|
}
|
|
|
|
totem->CastSpell(totem, triggered_spell_id, true, NULL, NULL, m_caster->GetGUID());
|
|
|
|
// Fire Nova Visual
|
|
totem->CastSpell(totem, 19823, true, NULL, NULL, m_caster->GetGUID());
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_DEATHKNIGHT:
|
|
{
|
|
// Death Coil
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x002000))
|
|
{
|
|
if (m_caster->IsFriendlyTo(unitTarget))
|
|
{
|
|
if (unitTarget->GetCreatureType() != CREATURE_TYPE_UNDEAD)
|
|
return;
|
|
|
|
int32 bp = int32(damage * 1.5f);
|
|
m_caster->CastCustomSpell(unitTarget, 47633, &bp, NULL, NULL, true);
|
|
}
|
|
else
|
|
{
|
|
int32 bp = damage;
|
|
m_caster->CastCustomSpell(unitTarget, 47632, &bp, NULL, NULL, true);
|
|
}
|
|
return;
|
|
}
|
|
// Hungering Cold
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000100000000000))
|
|
{
|
|
m_caster->CastSpell(m_caster, 51209, true);
|
|
return;
|
|
}
|
|
// Death Strike
|
|
else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000010))
|
|
{
|
|
uint32 count = 0;
|
|
Unit::AuraMap const& auras = unitTarget->GetAuras();
|
|
for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
|
|
{
|
|
if (itr->second->GetSpellProto()->Dispel == DISPEL_DISEASE &&
|
|
itr->second->GetCasterGUID() == m_caster->GetGUID() &&
|
|
IsSpellLastAuraEffect(itr->second->GetSpellProto(), itr->second->GetEffIndex()))
|
|
{
|
|
++count;
|
|
// max. 15%
|
|
if (count == 3)
|
|
break;
|
|
}
|
|
}
|
|
|
|
int32 bp = int32(count * m_caster->GetMaxHealth() * m_spellInfo->DmgMultiplier[EFFECT_INDEX_0] / 100);
|
|
|
|
// Improved Death Strike (percent stored in not existed EFFECT_INDEX_2 effect base points)
|
|
Unit::AuraList const& auraMod = m_caster->GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER);
|
|
for(Unit::AuraList::const_iterator iter = auraMod.begin(); iter != auraMod.end(); ++iter)
|
|
{
|
|
// only required spell have spellicon for SPELL_AURA_ADD_FLAT_MODIFIER
|
|
if ((*iter)->GetSpellProto()->SpellIconID == 2751 && (*iter)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT)
|
|
{
|
|
bp += (*iter)->GetSpellProto()->CalculateSimpleValue(EFFECT_INDEX_2) * bp / 100;
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_caster->CastCustomSpell(m_caster, 45470, &bp, NULL, NULL, true);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// pet auras
|
|
if (PetAura const* petSpell = sSpellMgr.GetPetAura(m_spellInfo->Id, eff_idx))
|
|
{
|
|
m_caster->AddPetAura(petSpell);
|
|
return;
|
|
}
|
|
|
|
// Script based implementation. Must be used only for not good for implementation in core spell effects
|
|
// So called only for not processed cases
|
|
if (gameObjTarget)
|
|
Script->EffectDummyGameObj(m_caster, m_spellInfo->Id, eff_idx, gameObjTarget);
|
|
else if (unitTarget && unitTarget->GetTypeId()==TYPEID_UNIT)
|
|
Script->EffectDummyCreature(m_caster, m_spellInfo->Id, eff_idx, (Creature*)unitTarget);
|
|
else if (itemTarget)
|
|
Script->EffectDummyItem(m_caster, m_spellInfo->Id, eff_idx, itemTarget);
|
|
}
|
|
|
|
void Spell::EffectTriggerSpellWithValue(SpellEffectIndex eff_idx)
|
|
{
|
|
uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[eff_idx];
|
|
|
|
// normal case
|
|
SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
|
|
|
|
if(!spellInfo)
|
|
{
|
|
sLog.outError("EffectTriggerSpellWithValue of spell %u: triggering unknown spell id %i", 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(SpellEffectIndex eff_idx)
|
|
{
|
|
uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[eff_idx];
|
|
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();
|
|
|
|
m_caster->CastSpell(unitTarget,spellInfo,false);
|
|
}
|
|
|
|
void Spell::EffectForceCast(SpellEffectIndex eff_idx)
|
|
{
|
|
if( !unitTarget )
|
|
return;
|
|
|
|
uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[eff_idx];
|
|
|
|
// 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(SpellEffectIndex effIndex)
|
|
{
|
|
// only unit case known
|
|
if (!unitTarget)
|
|
{
|
|
if(gameObjTarget || itemTarget)
|
|
sLog.outError("Spell::EffectTriggerSpell (Spell: %u): Unsupported non-unit case!",m_spellInfo->Id);
|
|
return;
|
|
}
|
|
|
|
uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[effIndex];
|
|
|
|
// special cases
|
|
switch(triggered_spell_id)
|
|
{
|
|
// Vanish (not exist)
|
|
case 18461:
|
|
{
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED);
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED);
|
|
|
|
// if this spell is given to NPC it must handle rest by it's own AI
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
uint32 spellId = 1784;
|
|
// reset cooldown on it if needed
|
|
if (((Player*)unitTarget)->HasSpellCooldown(spellId))
|
|
((Player*)unitTarget)->RemoveSpellCooldown(spellId);
|
|
|
|
m_caster->CastSpell(unitTarget, 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:
|
|
{
|
|
// Brittle Armor
|
|
SpellEntry const* spell = sSpellStore.LookupEntry(24575);
|
|
if (!spell)
|
|
return;
|
|
|
|
for (uint32 j=0; j < spell->StackAmount; ++j)
|
|
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:
|
|
{
|
|
// Mercurial Shield
|
|
SpellEntry const* spell = sSpellStore.LookupEntry(26464);
|
|
if (!spell)
|
|
return;
|
|
|
|
for (uint32 j=0; j < spell->StackAmount; ++j)
|
|
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 = unitTarget->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 )
|
|
{
|
|
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 = unitTarget->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;
|
|
}
|
|
|
|
// select formal caster for triggered spell
|
|
Unit* caster = m_caster;
|
|
|
|
// 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, true, false);
|
|
|
|
// skip spell if no weapon in slot or broken
|
|
if (!item)
|
|
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, true, false);
|
|
|
|
// skip spell if no weapon in slot or broken
|
|
if (!item)
|
|
return;
|
|
|
|
// skip spell if weapon not fit to triggered spell
|
|
if (!item->IsFitToSpellRequirements(spellInfo))
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Note: not exist spells with weapon req. and IsSpellHaveCasterSourceTargets == true
|
|
// so this just for speedup places in else
|
|
caster = IsSpellWithCasterSourceTargetsOnly(spellInfo) ? unitTarget : m_caster;
|
|
}
|
|
|
|
caster->CastSpell(unitTarget,spellInfo,true,NULL,NULL,m_originalCasterGUID);
|
|
}
|
|
|
|
void Spell::EffectTriggerMissileSpell(SpellEffectIndex 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 (eff: %u): triggering unknown spell id %u",
|
|
m_spellInfo->Id,effect_idx,triggered_spell_id);
|
|
return;
|
|
}
|
|
|
|
if (m_CastItem)
|
|
DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
|
|
|
|
m_caster->CastSpell(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, spellInfo, true, m_CastItem, 0, m_originalCasterGUID);
|
|
}
|
|
|
|
void Spell::EffectJump(SpellEffectIndex eff_idx)
|
|
{
|
|
if(m_caster->isInFlight())
|
|
return;
|
|
|
|
// Init dest coordinates
|
|
float x,y,z,o;
|
|
if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
|
|
{
|
|
x = m_targets.m_destX;
|
|
y = m_targets.m_destY;
|
|
z = m_targets.m_destZ;
|
|
|
|
if(m_spellInfo->EffectImplicitTargetA[eff_idx] == TARGET_BEHIND_VICTIM)
|
|
{
|
|
// explicit cast data from client or server-side cast
|
|
// some spell at client send caster
|
|
Unit* pTarget = NULL;
|
|
if(m_targets.getUnitTarget() && m_targets.getUnitTarget()!=m_caster)
|
|
pTarget = m_targets.getUnitTarget();
|
|
else if(unitTarget->getVictim())
|
|
pTarget = m_caster->getVictim();
|
|
else if(m_caster->GetTypeId() == TYPEID_PLAYER)
|
|
pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
|
|
|
|
o = pTarget ? pTarget->GetOrientation() : m_caster->GetOrientation();
|
|
}
|
|
else
|
|
o = m_caster->GetOrientation();
|
|
}
|
|
else if(unitTarget)
|
|
{
|
|
unitTarget->GetContactPoint(m_caster,x,y,z,CONTACT_DISTANCE);
|
|
o = m_caster->GetOrientation();
|
|
}
|
|
else if(gameObjTarget)
|
|
{
|
|
gameObjTarget->GetContactPoint(m_caster,x,y,z,CONTACT_DISTANCE);
|
|
o = m_caster->GetOrientation();
|
|
}
|
|
else
|
|
{
|
|
sLog.outError( "Spell::EffectJump - unsupported target mode for spell ID %u", m_spellInfo->Id );
|
|
return;
|
|
}
|
|
|
|
m_caster->NearTeleportTo(x, y, z, o, true);
|
|
}
|
|
|
|
void Spell::EffectTeleportUnits(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->isInFlight())
|
|
return;
|
|
|
|
switch (m_spellInfo->EffectImplicitTargetB[eff_idx])
|
|
{
|
|
case TARGET_INNKEEPER_COORDINATES:
|
|
{
|
|
// Only players can teleport to innkeeper
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
((Player*)unitTarget)->TeleportToHomebind(unitTarget==m_caster ? TELE_TO_SPELL : 0);
|
|
return;
|
|
}
|
|
case TARGET_AREAEFFECT_INSTANT: // in all cases first TARGET_TABLE_X_Y_Z_COORDINATES
|
|
case TARGET_TABLE_X_Y_Z_COORDINATES:
|
|
{
|
|
SpellTargetPosition const* st = sSpellMgr.GetSpellTargetPosition(m_spellInfo->Id);
|
|
if(!st)
|
|
{
|
|
sLog.outError( "Spell::EffectTeleportUnits - unknown Teleport coordinates for spell ID %u", m_spellInfo->Id );
|
|
return;
|
|
}
|
|
|
|
if(st->target_mapId==unitTarget->GetMapId())
|
|
unitTarget->NearTeleportTo(st->target_X,st->target_Y,st->target_Z,st->target_Orientation,unitTarget==m_caster);
|
|
else if(unitTarget->GetTypeId()==TYPEID_PLAYER)
|
|
((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:
|
|
{
|
|
Unit *pTarget = NULL;
|
|
|
|
// explicit cast data from client or server-side cast
|
|
// some spell at client send caster
|
|
if(m_targets.getUnitTarget() && m_targets.getUnitTarget()!=unitTarget)
|
|
pTarget = m_targets.getUnitTarget();
|
|
else if(unitTarget->getVictim())
|
|
pTarget = unitTarget->getVictim();
|
|
else if(unitTarget->GetTypeId() == TYPEID_PLAYER)
|
|
pTarget = ObjectAccessor::GetUnit(*unitTarget, ((Player*)unitTarget)->GetSelection());
|
|
|
|
// Init dest coordinates
|
|
float x = m_targets.m_destX;
|
|
float y = m_targets.m_destY;
|
|
float z = m_targets.m_destZ;
|
|
float orientation = pTarget ? pTarget->GetOrientation() : unitTarget->GetOrientation();
|
|
unitTarget->NearTeleportTo(x,y,z,orientation,unitTarget==m_caster);
|
|
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", eff_idx, m_spellInfo->EffectImplicitTargetB[eff_idx], m_spellInfo->Id );
|
|
return;
|
|
}
|
|
// Init dest coordinates
|
|
float x = m_targets.m_destX;
|
|
float y = m_targets.m_destY;
|
|
float z = m_targets.m_destZ;
|
|
float orientation = unitTarget->GetOrientation();
|
|
// Teleport
|
|
unitTarget->NearTeleportTo(x,y,z,orientation,unitTarget==m_caster);
|
|
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(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
|
|
// ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
|
|
if ( (!unitTarget->isAlive() && !(IsDeathOnlySpell(m_spellInfo) || IsDeathPersistentSpell(m_spellInfo))) &&
|
|
(unitTarget->GetTypeId() != TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading()) )
|
|
return;
|
|
|
|
Unit* caster = GetAffectiveCaster();
|
|
if(!caster)
|
|
{
|
|
// FIXME: currently we can't have auras applied explIcitly by gameobjects
|
|
// so for auras from wild gameobjects (no owner) target used
|
|
if (m_originalCasterGUID.IsGameobject())
|
|
caster = unitTarget;
|
|
else
|
|
return;
|
|
}
|
|
|
|
DEBUG_LOG("Spell: Aura is: %u", m_spellInfo->EffectApplyAuraName[eff_idx]);
|
|
|
|
Aura* Aur = CreateAura(m_spellInfo, eff_idx, &m_currentBasePoints[eff_idx], unitTarget, caster, m_CastItem);
|
|
|
|
// Now Reduce spell duration using data received at spell hit
|
|
int32 duration = Aur->GetAuraMaxDuration();
|
|
int32 limitduration = GetDiminishingReturnsLimitDuration(m_diminishGroup,m_spellInfo);
|
|
unitTarget->ApplyDiminishingToDuration(m_diminishGroup, duration, m_caster, m_diminishLevel,limitduration);
|
|
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);
|
|
}
|
|
|
|
unitTarget->AddAura(Aur);
|
|
}
|
|
|
|
void Spell::EffectUnlearnSpecialization(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player *_player = (Player*)unitTarget;
|
|
uint32 spellToUnlearn = m_spellInfo->EffectTriggerSpell[eff_idx];
|
|
|
|
_player->removeSpell(spellToUnlearn);
|
|
|
|
DEBUG_LOG( "Spell: Player %u has unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() );
|
|
}
|
|
|
|
void Spell::EffectPowerDrain(SpellEffectIndex eff_idx)
|
|
{
|
|
if(m_spellInfo->EffectMiscValue[eff_idx] < 0 || m_spellInfo->EffectMiscValue[eff_idx] >= MAX_POWERS)
|
|
return;
|
|
|
|
Powers drain_power = Powers(m_spellInfo->EffectMiscValue[eff_idx]);
|
|
|
|
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->SpellDamageBonusDone(unitTarget,m_spellInfo,uint32(damage),SPELL_DIRECT_DAMAGE);
|
|
damage = unitTarget->SpellDamageBonusTaken(m_caster, 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)
|
|
power -= unitTarget->GetSpellCritDamageReduction(power);
|
|
|
|
int32 new_damage;
|
|
if(curPower < power)
|
|
new_damage = curPower;
|
|
else
|
|
new_damage = power;
|
|
|
|
unitTarget->ModifyPower(drain_power,-new_damage);
|
|
|
|
// Don`t restore from self drain
|
|
if(drain_power == POWER_MANA && m_caster != unitTarget)
|
|
{
|
|
float manaMultiplier = m_spellInfo->EffectMultipleValue[eff_idx];
|
|
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->EnergizeBySpell(m_caster, m_spellInfo->Id, gain, POWER_MANA);
|
|
}
|
|
}
|
|
|
|
void Spell::EffectSendEvent(SpellEffectIndex effectIndex)
|
|
{
|
|
/*
|
|
we do not handle a flag dropping or clicking on flag in battleground by sendevent system
|
|
*/
|
|
DEBUG_LOG("Spell ScriptStart %u for spellid %u in EffectSendEvent ", m_spellInfo->EffectMiscValue[effectIndex], m_spellInfo->Id);
|
|
m_caster->GetMap()->ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[effectIndex], m_caster, focusObject);
|
|
}
|
|
|
|
void Spell::EffectPowerBurn(SpellEffectIndex eff_idx)
|
|
{
|
|
if (m_spellInfo->EffectMiscValue[eff_idx] < 0 || m_spellInfo->EffectMiscValue[eff_idx] >= MAX_POWERS)
|
|
return;
|
|
|
|
Powers powertype = Powers(m_spellInfo->EffectMiscValue[eff_idx]);
|
|
|
|
if (!unitTarget)
|
|
return;
|
|
if (!unitTarget->isAlive())
|
|
return;
|
|
if (unitTarget->getPowerType()!=powertype)
|
|
return;
|
|
if (damage < 0)
|
|
return;
|
|
|
|
// burn x% of target's mana, up to maximum of 2x% of caster's mana (Mana Burn)
|
|
if (m_spellInfo->ManaCostPercentage)
|
|
{
|
|
int32 maxdamage = m_caster->GetMaxPower(powertype) * damage * 2 / 100;
|
|
damage = unitTarget->GetMaxPower(powertype) * damage / 100;
|
|
if(damage > maxdamage)
|
|
damage = maxdamage;
|
|
}
|
|
|
|
int32 curPower = int32(unitTarget->GetPower(powertype));
|
|
|
|
// resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
|
|
int32 power = damage;
|
|
if (powertype == POWER_MANA)
|
|
power -= unitTarget->GetSpellCritDamageReduction(power);
|
|
|
|
int32 new_damage = (curPower < power) ? curPower : power;
|
|
|
|
unitTarget->ModifyPower(powertype, -new_damage);
|
|
float multiplier = m_spellInfo->EffectMultipleValue[eff_idx];
|
|
|
|
if (Player *modOwner = m_caster->GetSpellModOwner())
|
|
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
|
|
|
|
new_damage = int32(new_damage * multiplier);
|
|
m_damage += new_damage;
|
|
}
|
|
|
|
void Spell::EffectHeal(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (unitTarget && unitTarget->isAlive() && damage >= 0)
|
|
{
|
|
// Try to get original caster
|
|
Unit *caster = GetAffectiveCaster();
|
|
if (!caster)
|
|
return;
|
|
|
|
int32 addhealth = damage;
|
|
|
|
// Seal of Light proc
|
|
if (m_spellInfo->Id == 20167)
|
|
{
|
|
float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
int32 holy = caster->SpellBaseHealingBonusDone(GetSpellSchoolMask(m_spellInfo));
|
|
if (holy < 0)
|
|
holy = 0;
|
|
addhealth += int32(ap * 0.15) + int32(holy * 15 / 100);
|
|
}
|
|
// Vessel of the Naaru (Vial of the Sunwell trinket)
|
|
else 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;
|
|
}
|
|
// Death Pact (percent heal)
|
|
else if (m_spellInfo->Id==48743)
|
|
addhealth = addhealth * unitTarget->GetMaxHealth() / 100;
|
|
// 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 &&
|
|
// Regrowth or Rejuvenation 0x40 | 0x10
|
|
((*i)->GetSpellProto()->SpellFamilyFlags & UI64LIT(0x0000000000000050)))
|
|
{
|
|
if (!targetAura || (*i)->GetAuraDuration() < targetAura->GetAuraDuration())
|
|
targetAura = *i;
|
|
}
|
|
}
|
|
|
|
if (!targetAura)
|
|
{
|
|
sLog.outError("Target (GUID: %u TypeId: %u) has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget->GetGUIDLow(), unitTarget->GetTypeId());
|
|
return;
|
|
}
|
|
int idx = 0;
|
|
while(idx < 3)
|
|
{
|
|
if(targetAura->GetSpellProto()->EffectApplyAuraName[idx] == SPELL_AURA_PERIODIC_HEAL)
|
|
break;
|
|
idx++;
|
|
}
|
|
|
|
int32 tickheal = targetAura->GetModifier()->m_amount;
|
|
int32 tickcount = GetSpellDuration(targetAura->GetSpellProto()) / targetAura->GetSpellProto()->EffectAmplitude[idx] - 1;
|
|
|
|
// Glyph of Swiftmend
|
|
if (!caster->HasAura(54824))
|
|
unitTarget->RemoveAurasDueToSpell(targetAura->GetId());
|
|
|
|
addhealth += tickheal * tickcount;
|
|
}
|
|
|
|
// Chain Healing
|
|
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000100))
|
|
{
|
|
if (unitTarget == m_targets.getUnitTarget())
|
|
{
|
|
// check for Riptide
|
|
Aura* riptide = unitTarget->GetAura(SPELL_AURA_PERIODIC_HEAL, SPELLFAMILY_SHAMAN, 0, 0x00000010, caster->GetGUID());
|
|
if (riptide)
|
|
{
|
|
addhealth += addhealth/4;
|
|
unitTarget->RemoveAura(riptide);
|
|
}
|
|
}
|
|
}
|
|
|
|
addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL);
|
|
addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL);
|
|
|
|
m_healing += addhealth;
|
|
}
|
|
}
|
|
|
|
void Spell::EffectHealPct(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (unitTarget && unitTarget->isAlive() && damage >= 0)
|
|
{
|
|
// Try to get original caster
|
|
Unit *caster = GetAffectiveCaster();
|
|
if (!caster)
|
|
return;
|
|
|
|
uint32 addhealth = unitTarget->GetMaxHealth() * damage / 100;
|
|
|
|
addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL);
|
|
addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL);
|
|
|
|
int32 gain = caster->DealHeal(unitTarget, addhealth, m_spellInfo);
|
|
unitTarget->getHostileRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
|
|
}
|
|
}
|
|
|
|
void Spell::EffectHealMechanical(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
// Mechanic creature type should be correctly checked by targetCreatureType field
|
|
if (unitTarget && unitTarget->isAlive() && damage >= 0)
|
|
{
|
|
// Try to get original caster
|
|
Unit *caster = GetAffectiveCaster();
|
|
if (!caster)
|
|
return;
|
|
|
|
uint32 addhealth = caster->SpellHealingBonusDone(unitTarget, m_spellInfo, damage, HEAL);
|
|
addhealth = unitTarget->SpellHealingBonusTaken(caster, m_spellInfo, addhealth, HEAL);
|
|
|
|
caster->DealHeal(unitTarget, addhealth, m_spellInfo);
|
|
}
|
|
}
|
|
|
|
void Spell::EffectHealthLeech(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
if (!unitTarget->isAlive())
|
|
return;
|
|
|
|
if (damage < 0)
|
|
return;
|
|
|
|
DEBUG_LOG("HealthLeech :%i", damage);
|
|
|
|
uint32 curHealth = unitTarget->GetHealth();
|
|
damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage );
|
|
if ((int32)curHealth < damage)
|
|
damage = curHealth;
|
|
|
|
float multiplier = m_spellInfo->EffectMultipleValue[eff_idx];
|
|
|
|
if (Player *modOwner = m_caster->GetSpellModOwner())
|
|
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
|
|
|
|
int32 heal = int32(damage*multiplier);
|
|
if (m_caster->isAlive())
|
|
{
|
|
heal = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, heal, HEAL);
|
|
|
|
m_caster->DealHeal(m_caster, heal, m_spellInfo);
|
|
}
|
|
}
|
|
|
|
void Spell::DoCreateItem(SpellEffectIndex eff_idx, uint32 itemtype)
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player* player = (Player*)unitTarget;
|
|
|
|
uint32 newitemid = itemtype;
|
|
ItemPrototype const *pProto = ObjectMgr::GetItemPrototype( newitemid );
|
|
if(!pProto)
|
|
{
|
|
player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
|
|
return;
|
|
}
|
|
|
|
// bg reward have some special in code work
|
|
bool bg_mark = false;
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case SPELL_WG_MARK_VICTORY:
|
|
case SPELL_WG_MARK_DEFEAT:
|
|
bg_mark = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uint32 num_to_add = damage;
|
|
|
|
if (num_to_add < 1)
|
|
num_to_add = 1;
|
|
if (num_to_add > pProto->GetMaxStackSize())
|
|
num_to_add = pProto->GetMaxStackSize();
|
|
|
|
// 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
|
|
{
|
|
// ignore mana gem case (next effect will recharge existed example)
|
|
if (eff_idx == EFFECT_INDEX_0 && m_spellInfo->Effect[EFFECT_INDEX_1] == SPELL_EFFECT_DUMMY )
|
|
return;
|
|
|
|
// if not created by another reason from full inventory or unique items amount limitation
|
|
player->SendEquipError( msg, NULL, NULL, newitemid );
|
|
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, !bg_mark);
|
|
|
|
// we succeeded in creating at least one item, so a levelup is possible
|
|
if(!bg_mark)
|
|
player->UpdateCraftSkill(m_spellInfo->Id);
|
|
}
|
|
|
|
// for battleground marks send by mail if not add all expected
|
|
// FIXME: single existed bg marks for outfield bg and we not have it..
|
|
/*
|
|
if(no_space > 0 && bg_mark)
|
|
{
|
|
if(BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(BattleGroundTypeId(bgType)))
|
|
bg->SendRewardMarkByMail(player, newitemid, no_space);
|
|
}
|
|
*/
|
|
}
|
|
|
|
void Spell::EffectCreateItem(SpellEffectIndex eff_idx)
|
|
{
|
|
DoCreateItem(eff_idx,m_spellInfo->EffectItemType[eff_idx]);
|
|
}
|
|
|
|
void Spell::EffectCreateItem2(SpellEffectIndex eff_idx)
|
|
{
|
|
if (m_caster->GetTypeId()!=TYPEID_PLAYER)
|
|
return;
|
|
Player* player = (Player*)m_caster;
|
|
|
|
// explicit item (possible fake)
|
|
uint32 item_id = m_spellInfo->EffectItemType[eff_idx];
|
|
|
|
if (item_id)
|
|
DoCreateItem(eff_idx, item_id);
|
|
|
|
// not explicit loot (with fake item drop if need)
|
|
if (IsLootCraftingSpell(m_spellInfo))
|
|
{
|
|
if(item_id)
|
|
{
|
|
if (!player->HasItemCount(item_id, 1))
|
|
return;
|
|
|
|
// remove reagent
|
|
uint32 count = 1;
|
|
player->DestroyItemCount(item_id, count, true);
|
|
}
|
|
|
|
// create some random items
|
|
player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell);
|
|
}
|
|
}
|
|
|
|
void Spell::EffectCreateRandomItem(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (m_caster->GetTypeId()!=TYPEID_PLAYER)
|
|
return;
|
|
Player* player = (Player*)m_caster;
|
|
|
|
// create some random items
|
|
player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell);
|
|
}
|
|
|
|
void Spell::EffectPersistentAA(SpellEffectIndex eff_idx)
|
|
{
|
|
float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx]));
|
|
|
|
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(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, eff_idx, 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);
|
|
m_caster->GetMap()->Add(dynObj);
|
|
}
|
|
|
|
void Spell::EffectEnergize(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
if(!unitTarget->isAlive())
|
|
return;
|
|
|
|
if(m_spellInfo->EffectMiscValue[eff_idx] < 0 || m_spellInfo->EffectMiscValue[eff_idx] >= MAX_POWERS)
|
|
return;
|
|
|
|
Powers power = Powers(m_spellInfo->EffectMiscValue[eff_idx]);
|
|
|
|
// Some level depends spells
|
|
int level_multiplier = 0;
|
|
int level_diff = 0;
|
|
switch (m_spellInfo->Id)
|
|
{
|
|
case 9512: // Restore Energy
|
|
level_diff = m_caster->getLevel() - 40;
|
|
level_multiplier = 2;
|
|
break;
|
|
case 24571: // Blood Fury
|
|
level_diff = m_caster->getLevel() - 60;
|
|
level_multiplier = 10;
|
|
break;
|
|
case 24532: // Burst of Energy
|
|
level_diff = m_caster->getLevel() - 60;
|
|
level_multiplier = 4;
|
|
break;
|
|
case 31930: // Judgements of the Wise
|
|
case 48542: // Revitalize (mana restore case)
|
|
case 63375: // Improved Stormstrike
|
|
case 68082: // Glyph of Seal of Command
|
|
damage = damage * unitTarget->GetCreateMana() / 100;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (level_diff > 0)
|
|
damage -= level_multiplier * level_diff;
|
|
|
|
if(damage < 0)
|
|
return;
|
|
|
|
if(unitTarget->GetMaxPower(power) == 0)
|
|
return;
|
|
|
|
m_caster->EnergizeBySpell(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 = sSpellMgr.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 = sSpellMgr.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(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
if (!unitTarget->isAlive())
|
|
return;
|
|
|
|
if (m_spellInfo->EffectMiscValue[eff_idx] < 0 || m_spellInfo->EffectMiscValue[eff_idx] >= MAX_POWERS)
|
|
return;
|
|
|
|
Powers power = Powers(m_spellInfo->EffectMiscValue[eff_idx]);
|
|
|
|
uint32 maxPower = unitTarget->GetMaxPower(power);
|
|
if (maxPower == 0)
|
|
return;
|
|
|
|
uint32 gain = damage * maxPower / 100;
|
|
m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, gain, power);
|
|
}
|
|
|
|
void Spell::SendLoot(uint64 guid, LootType loottype)
|
|
{
|
|
if (gameObjTarget)
|
|
{
|
|
switch (gameObjTarget->GetGoType())
|
|
{
|
|
case GAMEOBJECT_TYPE_DOOR:
|
|
case GAMEOBJECT_TYPE_BUTTON:
|
|
case GAMEOBJECT_TYPE_QUESTGIVER:
|
|
case GAMEOBJECT_TYPE_SPELL_FOCUS:
|
|
case GAMEOBJECT_TYPE_GOOBER:
|
|
gameObjTarget->Use(m_caster);
|
|
return;
|
|
|
|
case GAMEOBJECT_TYPE_CHEST:
|
|
gameObjTarget->Use(m_caster);
|
|
// Don't return, let loots been taken
|
|
break;
|
|
|
|
default:
|
|
sLog.outError("Spell::SendLoot unhandled GameObject type %u (entry %u).", gameObjTarget->GetGoType(), gameObjTarget->GetEntry());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
// Send loot
|
|
((Player*)m_caster)->SendLoot(guid, loottype);
|
|
}
|
|
|
|
void Spell::EffectOpenLock(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
{
|
|
DEBUG_LOG( "WORLD: Open Lock - No Player Caster!");
|
|
return;
|
|
}
|
|
|
|
Player* player = (Player*)m_caster;
|
|
|
|
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)
|
|
{
|
|
//CanUseBattleGroundObject() already called in CheckCast()
|
|
// in battleground check
|
|
if (BattleGround *bg = player->GetBattleGround())
|
|
{
|
|
// check if it's correct bg
|
|
if (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_AV)
|
|
bg->EventPlayerClickedOnFlag(player, gameObjTarget);
|
|
return;
|
|
}
|
|
}
|
|
else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND)
|
|
{
|
|
//CanUseBattleGroundObject() already called in CheckCast()
|
|
// in battleground check
|
|
if (BattleGround *bg = player->GetBattleGround())
|
|
{
|
|
if (bg->GetTypeID() == BATTLEGROUND_EY)
|
|
bg->EventPlayerClickedOnFlag(player, gameObjTarget);
|
|
return;
|
|
}
|
|
}
|
|
lockId = goInfo->GetLockId();
|
|
guid = gameObjTarget->GetGUID();
|
|
}
|
|
else if (itemTarget)
|
|
{
|
|
lockId = itemTarget->GetProto()->LockID;
|
|
guid = itemTarget->GetGUID();
|
|
}
|
|
else
|
|
{
|
|
DEBUG_LOG( "WORLD: Open Lock - No GameObject/Item Target!");
|
|
return;
|
|
}
|
|
|
|
SkillType skillId = SKILL_NONE;
|
|
int32 reqSkillValue = 0;
|
|
int32 skillValue;
|
|
|
|
SpellCastResult res = CanOpenLock(eff_idx, lockId, skillId, reqSkillValue, skillValue);
|
|
if (res != SPELL_CAST_OK)
|
|
{
|
|
SendCastResult(res);
|
|
return;
|
|
}
|
|
|
|
SendLoot(guid, LOOT_SKINNING);
|
|
|
|
// not allow use skill grow at item base open
|
|
if (!m_CastItem && skillId != SKILL_NONE)
|
|
{
|
|
// update skill if really known
|
|
if (uint32 pureSkillValue = player->GetPureSkillValue(skillId))
|
|
{
|
|
if (gameObjTarget)
|
|
{
|
|
// Allow one skill-up until respawned
|
|
if (!gameObjTarget->IsInSkillupList(player->GetGUIDLow()) &&
|
|
player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue))
|
|
gameObjTarget->AddToSkillupList(player->GetGUIDLow());
|
|
}
|
|
else if (itemTarget)
|
|
{
|
|
// Do one skill-up
|
|
player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spell::EffectSummonChangeItem(SpellEffectIndex eff_idx)
|
|
{
|
|
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[eff_idx];
|
|
if (!newitemid)
|
|
return;
|
|
|
|
uint16 pos = m_CastItem->GetPos();
|
|
|
|
Item *pNewItem = Item::CreateItem( newitemid, 1, player);
|
|
if (!pNewItem)
|
|
return;
|
|
|
|
for(uint8 j= PERM_ENCHANTMENT_SLOT; j<=TEMP_ENCHANTMENT_SLOT; ++j)
|
|
{
|
|
if (m_CastItem->GetEnchantmentId(EnchantmentSlot(j)))
|
|
pNewItem->SetEnchantment(EnchantmentSlot(j), m_CastItem->GetEnchantmentId(EnchantmentSlot(j)), m_CastItem->GetEnchantmentDuration(EnchantmentSlot(j)), m_CastItem->GetEnchantmentCharges(EnchantmentSlot(j)));
|
|
}
|
|
|
|
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::EffectProficiency(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
Player *p_target = (Player*)unitTarget;
|
|
|
|
uint32 subClassMask = m_spellInfo->EquippedItemSubClassMask;
|
|
if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && !(p_target->GetWeaponProficiency() & subClassMask))
|
|
{
|
|
p_target->AddWeaponProficiency(subClassMask);
|
|
p_target->SendProficiency(ITEM_CLASS_WEAPON, p_target->GetWeaponProficiency());
|
|
}
|
|
if (m_spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR && !(p_target->GetArmorProficiency() & subClassMask))
|
|
{
|
|
p_target->AddArmorProficiency(subClassMask);
|
|
p_target->SendProficiency(ITEM_CLASS_ARMOR, p_target->GetArmorProficiency());
|
|
}
|
|
}
|
|
|
|
void Spell::EffectApplyAreaAura(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
if (!unitTarget->isAlive())
|
|
return;
|
|
|
|
AreaAura* Aur = new AreaAura(m_spellInfo, eff_idx, &m_currentBasePoints[eff_idx], unitTarget, m_caster, m_CastItem);
|
|
unitTarget->AddAura(Aur);
|
|
}
|
|
|
|
void Spell::EffectSummonType(SpellEffectIndex eff_idx)
|
|
{
|
|
uint32 prop_id = m_spellInfo->EffectMiscValueB[eff_idx];
|
|
SummonPropertiesEntry const *summon_prop = sSummonPropertiesStore.LookupEntry(prop_id);
|
|
if(!summon_prop)
|
|
{
|
|
sLog.outError("EffectSummonType: Unhandled summon type %u", prop_id);
|
|
return;
|
|
}
|
|
|
|
switch(summon_prop->Group)
|
|
{
|
|
// faction handled later on, or loaded from template
|
|
case SUMMON_PROP_GROUP_WILD:
|
|
case SUMMON_PROP_GROUP_FRIENDLY:
|
|
{
|
|
switch(summon_prop->Type)
|
|
{
|
|
case SUMMON_PROP_TYPE_OTHER:
|
|
{
|
|
// those are classical totems - effectbasepoints is their hp and not summon ammount!
|
|
//SUMMON_TYPE_TOTEM = 121: 23035, battlestands
|
|
//SUMMON_TYPE_TOTEM2 = 647: 52893, Anti-Magic Zone (npc used)
|
|
if(prop_id == 121 || prop_id == 647)
|
|
DoSummonTotem(eff_idx);
|
|
else
|
|
DoSummonWild(eff_idx, summon_prop->FactionId);
|
|
break;
|
|
}
|
|
case SUMMON_PROP_TYPE_SUMMON:
|
|
case SUMMON_PROP_TYPE_GUARDIAN:
|
|
case SUMMON_PROP_TYPE_ARMY:
|
|
case SUMMON_PROP_TYPE_DK:
|
|
case SUMMON_PROP_TYPE_CONSTRUCT:
|
|
{
|
|
// JC golems - 32804, etc -- fits much better totem AI
|
|
if(m_spellInfo->SpellIconID == 2056)
|
|
DoSummonTotem(eff_idx);
|
|
if(prop_id == 832) // scrapbot
|
|
DoSummonWild(eff_idx, summon_prop->FactionId);
|
|
else
|
|
DoSummonGuardian(eff_idx, summon_prop->FactionId);
|
|
break;
|
|
}
|
|
case SUMMON_PROP_TYPE_TOTEM:
|
|
DoSummonTotem(eff_idx, summon_prop->Slot);
|
|
break;
|
|
case SUMMON_PROP_TYPE_CRITTER:
|
|
DoSummonCritter(eff_idx, summon_prop->FactionId);
|
|
break;
|
|
case SUMMON_PROP_TYPE_PHASING:
|
|
case SUMMON_PROP_TYPE_LIGHTWELL:
|
|
case SUMMON_PROP_TYPE_REPAIR_BOT:
|
|
DoSummonWild(eff_idx, summon_prop->FactionId);
|
|
break;
|
|
case SUMMON_PROP_TYPE_SIEGE_VEH:
|
|
case SUMMON_PROP_TYPE_DRAKE_VEH:
|
|
// TODO
|
|
// EffectSummonVehicle(i);
|
|
break;
|
|
default:
|
|
sLog.outError("EffectSummonType: Unhandled summon type %u", summon_prop->Type);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case SUMMON_PROP_GROUP_PETS:
|
|
{
|
|
// FIXME : multiple summons - not yet supported as pet
|
|
//1562 - force of nature - sid 33831
|
|
//1161 - feral spirit - sid 51533
|
|
if(prop_id == 1562) // 3 uncontrolable instead of one controllable :/
|
|
DoSummonGuardian(eff_idx, summon_prop->FactionId);
|
|
else
|
|
DoSummon(eff_idx);
|
|
break;
|
|
}
|
|
case SUMMON_PROP_GROUP_CONTROLLABLE:
|
|
{
|
|
// no type here
|
|
// maybe wrong - but thats the handler currently used for those
|
|
DoSummonGuardian(eff_idx, summon_prop->FactionId);
|
|
break;
|
|
}
|
|
case SUMMON_PROP_GROUP_VEHICLE:
|
|
{
|
|
// TODO
|
|
// EffectSummonVehicle(i);
|
|
break;
|
|
}
|
|
default:
|
|
sLog.outError("EffectSummonType: Unhandled summon group type %u", summon_prop->Group);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Spell::DoSummon(SpellEffectIndex eff_idx)
|
|
{
|
|
if (m_caster->GetPetGUID())
|
|
return;
|
|
|
|
if (!unitTarget)
|
|
return;
|
|
uint32 pet_entry = m_spellInfo->EffectMiscValue[eff_idx];
|
|
if (!pet_entry)
|
|
return;
|
|
uint32 level = m_caster->getLevel();
|
|
Pet* spawnCreature = new Pet(SUMMON_PET);
|
|
|
|
int32 duration = GetSpellDuration(m_spellInfo);
|
|
if(Player* modOwner = m_caster->GetSpellModOwner())
|
|
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
|
|
|
|
if (m_caster->GetTypeId()==TYPEID_PLAYER && spawnCreature->LoadPetFromDB((Player*)m_caster,pet_entry))
|
|
{
|
|
// 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;
|
|
spawnCreature->Relocate(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, -m_caster->GetOrientation());
|
|
}
|
|
|
|
// set timer for unsummon
|
|
if (duration > 0)
|
|
spawnCreature->SetDuration(duration);
|
|
|
|
return;
|
|
}
|
|
|
|
Map *map = m_caster->GetMap();
|
|
uint32 pet_number = sObjectMgr.GeneratePetNumber();
|
|
if (!spawnCreature->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), map, m_caster->GetPhaseMask(),
|
|
m_spellInfo->EffectMiscValue[eff_idx], pet_number))
|
|
{
|
|
sLog.outErrorDb("Spell::EffectSummon: no such creature entry %u",m_spellInfo->EffectMiscValue[eff_idx]);
|
|
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());
|
|
spawnCreature->SetSummonPoint(x, y, z, -m_caster->GetOrientation());
|
|
|
|
if (!spawnCreature->IsPositionValid())
|
|
{
|
|
sLog.outError("Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)",
|
|
spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY());
|
|
delete spawnCreature;
|
|
return;
|
|
}
|
|
|
|
// set timer for unsummon
|
|
if (duration > 0)
|
|
spawnCreature->SetDuration(duration);
|
|
|
|
spawnCreature->SetOwnerGUID(m_caster->GetGUID());
|
|
spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
|
|
spawnCreature->setPowerType(POWER_MANA);
|
|
spawnCreature->setFaction(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->SetCreatorGUID(m_caster->GetGUID());
|
|
spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
|
|
|
|
spawnCreature->InitStatsForLevel(level, m_caster);
|
|
|
|
spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false);
|
|
|
|
spawnCreature->UpdateWalkMode(m_caster);
|
|
|
|
spawnCreature->AIM_Initialize();
|
|
spawnCreature->InitPetCreateSpells();
|
|
spawnCreature->InitLevelupSpellsForLevel();
|
|
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);
|
|
|
|
m_caster->SetPet(spawnCreature);
|
|
|
|
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
|
{
|
|
spawnCreature->GetCharmInfo()->SetReactState( REACT_DEFENSIVE );
|
|
spawnCreature->SavePetToDB(PET_SAVE_AS_CURRENT);
|
|
((Player*)m_caster)->PetSpellInitialize();
|
|
}
|
|
|
|
if (m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->AI())
|
|
((Creature*)m_caster)->AI()->JustSummoned((Creature*)spawnCreature);
|
|
}
|
|
|
|
void Spell::EffectLearnSpell(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
{
|
|
if (m_caster->GetTypeId() == TYPEID_PLAYER)
|
|
EffectLearnPetSpell(eff_idx);
|
|
|
|
return;
|
|
}
|
|
|
|
Player *player = (Player*)unitTarget;
|
|
|
|
uint32 spellToLearn = ((m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) || (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN_PET)) ? damage : m_spellInfo->EffectTriggerSpell[eff_idx];
|
|
player->learnSpell(spellToLearn, false);
|
|
|
|
DEBUG_LOG( "Spell: Player %u has learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() );
|
|
}
|
|
|
|
void Spell::EffectDispel(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// Fill possible dispell list
|
|
std::vector <Aura *> dispel_list;
|
|
|
|
// Create dispel mask by dispel type
|
|
uint32 dispel_type = m_spellInfo->EffectMiscValue[eff_idx];
|
|
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 (all stack cases)
|
|
for(int k = 0; k < aur->GetStackAmount(); ++k)
|
|
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
|
|
|
|
// some spells have effect value = 0 and all from its by meaning expect 1
|
|
if(!damage)
|
|
damage = 1;
|
|
|
|
// Dispell N = damage buffs (or while exist buffs for dispel)
|
|
for (int32 count=0; count < damage && !dispel_list.empty(); ++count)
|
|
{
|
|
// Random select buff for dispel
|
|
std::vector<Aura*>::iterator dispel_itr = dispel_list.begin();
|
|
std::advance(dispel_itr,urand(0, dispel_list.size()-1));
|
|
|
|
Aura *aur = *dispel_itr;
|
|
|
|
// remove entry from dispel_list
|
|
dispel_list.erase(dispel_itr);
|
|
|
|
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(spellInfo->Id);
|
|
else
|
|
success_list.push_back(std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID()));
|
|
}
|
|
// 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 << unitTarget->GetPackGUID(); // Victim GUID
|
|
data << m_caster->GetPackGUID(); // Caster GUID
|
|
data << uint32(m_spellInfo->Id); // Dispel 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->RemoveSingleAuraDueToSpellByDispel(spellInfo->Id, j->second, m_caster);
|
|
}
|
|
m_caster->SendMessageToSet(&data, true);
|
|
|
|
// On success dispel
|
|
// Devour Magic
|
|
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == SPELLCATEGORY_DEVOUR_MAGIC)
|
|
{
|
|
int32 heal_amount = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1);
|
|
m_caster->CastCustomSpell(m_caster, 19658, &heal_amount, NULL, NULL, 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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER)
|
|
((Player*)unitTarget)->SetCanDualWield(true);
|
|
}
|
|
|
|
void Spell::EffectPull(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
// TODO: create a proper pull towards distract spell center for distract
|
|
DEBUG_LOG("WORLD: Spell Effect DUMMY");
|
|
}
|
|
|
|
void Spell::EffectDistract(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
// Check for possible target
|
|
if (!unitTarget || unitTarget->isInCombat())
|
|
return;
|
|
|
|
// target must be OK to do this
|
|
if( unitTarget->hasUnitState(UNIT_STAT_CAN_NOT_REACT) )
|
|
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 * IN_MILLISECONDS);
|
|
}
|
|
}
|
|
|
|
void Spell::EffectPickPocket(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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
|
|
//DEBUG_LOG("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()->AttackedBy(m_caster);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spell::EffectAddFarsight(SpellEffectIndex eff_idx)
|
|
{
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
int32 duration = GetSpellDuration(m_spellInfo);
|
|
DynamicObject* dynObj = new DynamicObject;
|
|
|
|
// set radius to 0: spell not expected to work as persistent aura
|
|
if(!dynObj->Create(m_caster->GetMap()->GenerateLocalLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, eff_idx, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, 0))
|
|
{
|
|
delete dynObj;
|
|
return;
|
|
}
|
|
dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65);
|
|
dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x80000002);
|
|
m_caster->AddDynObject(dynObj);
|
|
m_caster->GetMap()->Add(dynObj);
|
|
((Player*)m_caster)->SetFarSightGUID(dynObj->GetGUID());
|
|
}
|
|
|
|
void Spell::DoSummonWild(SpellEffectIndex eff_idx, uint32 forceFaction)
|
|
{
|
|
uint32 creature_entry = m_spellInfo->EffectMiscValue[eff_idx];
|
|
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[eff_idx]));
|
|
int32 duration = GetSpellDuration(m_spellInfo);
|
|
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN;
|
|
|
|
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);
|
|
|
|
if(Creature *summon = m_caster->SummonCreature(creature_entry, px, py, pz, m_caster->GetOrientation(), summonType, duration))
|
|
{
|
|
summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
|
|
summon->SetCreatorGUID(m_caster->GetGUID());
|
|
|
|
if(forceFaction)
|
|
summon->setFaction(forceFaction);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spell::DoSummonGuardian(SpellEffectIndex eff_idx, uint32 forceFaction)
|
|
{
|
|
uint32 pet_entry = m_spellInfo->EffectMiscValue[eff_idx];
|
|
if (!pet_entry)
|
|
return;
|
|
|
|
// 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[eff_idx]));
|
|
int32 duration = GetSpellDuration(m_spellInfo);
|
|
if(Player* modOwner = m_caster->GetSpellModOwner())
|
|
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
|
|
|
|
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 = sObjectMgr.GeneratePetNumber();
|
|
if (!spawnCreature->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), map,m_caster->GetPhaseMask(),
|
|
m_spellInfo->EffectMiscValue[eff_idx], pet_number))
|
|
{
|
|
sLog.outError("no such creature entry %u", m_spellInfo->EffectMiscValue[eff_idx]);
|
|
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());
|
|
spawnCreature->SetSummonPoint(px, py, pz, m_caster->GetOrientation());
|
|
|
|
if (!spawnCreature->IsPositionValid())
|
|
{
|
|
sLog.outError("Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)",
|
|
spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY());
|
|
delete spawnCreature;
|
|
return;
|
|
}
|
|
|
|
if (duration > 0)
|
|
spawnCreature->SetDuration(duration);
|
|
|
|
spawnCreature->SetOwnerGUID(m_caster->GetGUID());
|
|
spawnCreature->setPowerType(POWER_MANA);
|
|
spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS, spawnCreature->GetCreatureInfo()->npcflag);
|
|
spawnCreature->setFaction(forceFaction ? forceFaction : 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->SetCreatorGUID(m_caster->GetGUID());
|
|
spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
|
|
|
|
spawnCreature->InitStatsForLevel(level, m_caster);
|
|
spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false);
|
|
|
|
spawnCreature->AIM_Initialize();
|
|
|
|
m_caster->AddGuardian(spawnCreature);
|
|
|
|
map->Add((Creature*)spawnCreature);
|
|
}
|
|
}
|
|
|
|
void Spell::EffectTeleUnitsFaceCaster(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
if (unitTarget->isInFlight())
|
|
return;
|
|
|
|
float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx]));
|
|
|
|
float fx, fy, fz;
|
|
m_caster->GetClosePoint(fx, fy, fz, unitTarget->GetObjectSize(), dis);
|
|
|
|
unitTarget->NearTeleportTo(fx, fy, fz, -m_caster->GetOrientation(), unitTarget==m_caster);
|
|
}
|
|
|
|
void Spell::EffectLearnSkill(SpellEffectIndex eff_idx)
|
|
{
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if (damage < 0)
|
|
return;
|
|
|
|
uint32 skillid = m_spellInfo->EffectMiscValue[eff_idx];
|
|
uint16 skillval = ((Player*)unitTarget)->GetPureSkillValue(skillid);
|
|
((Player*)unitTarget)->SetSkill(skillid, skillval ? skillval : 1, damage * 75, damage);
|
|
}
|
|
|
|
void Spell::EffectAddHonor(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
// not scale value for item based reward (/10 value expected)
|
|
if (m_CastItem)
|
|
{
|
|
((Player*)unitTarget)->RewardHonor(NULL, 1, float(damage / 10));
|
|
DEBUG_LOG("SpellEffect::AddHonor (spell_id %u) rewards %d honor points (item %u) for player: %u", m_spellInfo->Id, damage/10, m_CastItem->GetEntry(),((Player*)unitTarget)->GetGUIDLow());
|
|
return;
|
|
}
|
|
|
|
// do not allow to add too many honor for player (50 * 21) = 1040 at level 70, or (50 * 31) = 1550 at level 80
|
|
if (damage <= 50)
|
|
{
|
|
float honor_reward = MaNGOS::Honor::hk_honor_at_level(unitTarget->getLevel(), damage);
|
|
((Player*)unitTarget)->RewardHonor(NULL, 1, honor_reward);
|
|
DEBUG_LOG("SpellEffect::AddHonor (spell_id %u) rewards %f honor points (scale) to player: %u", m_spellInfo->Id, honor_reward, ((Player*)unitTarget)->GetGUIDLow());
|
|
}
|
|
else
|
|
{
|
|
//maybe we have correct honor_gain in damage already
|
|
((Player*)unitTarget)->RewardHonor(NULL, 1, (float)damage);
|
|
sLog.outError("SpellEffect::AddHonor (spell_id %u) rewards %u honor points (non scale) for player: %u", m_spellInfo->Id, damage, ((Player*)unitTarget)->GetGUIDLow());
|
|
}
|
|
}
|
|
|
|
void Spell::EffectTradeSkill(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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(SpellEffectIndex eff_idx)
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
if (!itemTarget)
|
|
return;
|
|
|
|
Player* p_caster = (Player*)m_caster;
|
|
|
|
// not grow at item use at item case
|
|
p_caster->UpdateCraftSkill(m_spellInfo->Id);
|
|
|
|
uint32 enchant_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
if (!enchant_id)
|
|
return;
|
|
|
|
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_BOOL_GM_LOG_TRADE) )
|
|
{
|
|
sLog.outCommand(p_caster->GetSession()->GetAccountId(),"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);
|
|
|
|
// update trade window for show enchantment for caster in trade window
|
|
if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM)
|
|
p_caster->GetSession()->SendUpdateTrade();
|
|
}
|
|
|
|
void Spell::EffectEnchantItemPrismatic(SpellEffectIndex eff_idx)
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
if (!itemTarget)
|
|
return;
|
|
|
|
Player* p_caster = (Player*)m_caster;
|
|
|
|
uint32 enchant_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
if (!enchant_id)
|
|
return;
|
|
|
|
SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
|
|
if (!pEnchant)
|
|
return;
|
|
|
|
// support only enchantings with add socket in this slot
|
|
{
|
|
bool add_socket = false;
|
|
for(int i = 0; i < 3; ++i)
|
|
{
|
|
if (pEnchant->type[i]==ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET)
|
|
{
|
|
add_socket = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!add_socket)
|
|
{
|
|
sLog.outError("Spell::EffectEnchantItemPrismatic: attempt apply enchant spell %u with SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC (%u) but without ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET (%u), not suppoted yet.",
|
|
m_spellInfo->Id,SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC,ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET);
|
|
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_BOOL_GM_LOG_TRADE) )
|
|
{
|
|
sLog.outCommand(p_caster->GetSession()->GetAccountId(),"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,PRISMATIC_ENCHANTMENT_SLOT,false);
|
|
|
|
itemTarget->SetEnchantment(PRISMATIC_ENCHANTMENT_SLOT, enchant_id, 0, 0);
|
|
|
|
// add new enchanting if equipped
|
|
item_owner->ApplyEnchantment(itemTarget,PRISMATIC_ENCHANTMENT_SLOT,true);
|
|
|
|
// update trade window for show enchantment for caster in trade window
|
|
if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM)
|
|
p_caster->GetSession()->SendUpdateTrade();
|
|
}
|
|
|
|
void Spell::EffectEnchantItemTmp(SpellEffectIndex eff_idx)
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player* p_caster = (Player*)m_caster;
|
|
|
|
// Rockbiter Weapon apply to both weapon
|
|
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000400000))
|
|
{
|
|
uint32 spell_id = 0;
|
|
|
|
// enchanting spell selected by calculated damage-per-sec stored in Effect[1] base value
|
|
// 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(damage)
|
|
{
|
|
// Rank 1
|
|
case 2: spell_id = 36744; break; // 0% [ 7% == 2, 14% == 2, 20% == 2]
|
|
// Rank 2
|
|
case 4: spell_id = 36753; break; // 0% [ 7% == 4, 14% == 4]
|
|
case 5: spell_id = 36751; break; // 20%
|
|
// Rank 3
|
|
case 6: spell_id = 36754; break; // 0% [ 7% == 6, 14% == 6]
|
|
case 7: spell_id = 36755; break; // 20%
|
|
// Rank 4
|
|
case 9: spell_id = 36761; break; // 0% [ 7% == 6]
|
|
case 10: spell_id = 36758; break; // 14%
|
|
case 11: spell_id = 36760; break; // 20%
|
|
default:
|
|
sLog.outError("Spell::EffectEnchantItemTmp: Damage %u not handled in S'RW",damage);
|
|
return;
|
|
}
|
|
|
|
|
|
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
|
|
if (!spellInfo)
|
|
{
|
|
sLog.outError("Spell::EffectEnchantItemTmp: unknown spell id %i", spell_id);
|
|
return;
|
|
}
|
|
|
|
for(int j = BASE_ATTACK; j <= OFF_ATTACK; ++j)
|
|
{
|
|
if (Item* item = p_caster->GetWeaponForAttack(WeaponAttackType(j)))
|
|
{
|
|
if (item->IsFitToSpellRequirements(m_spellInfo))
|
|
{
|
|
Spell *spell = new Spell(m_caster, spellInfo, true);
|
|
SpellCastTargets targets;
|
|
targets.setItemTarget( item );
|
|
spell->prepare(&targets);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!itemTarget)
|
|
return;
|
|
|
|
uint32 enchant_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
if (!enchant_id)
|
|
{
|
|
sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have 0 as enchanting id",m_spellInfo->Id,eff_idx);
|
|
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,eff_idx,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[0] == 215)
|
|
duration = 1800; // 30 mins
|
|
// some fishing pole bonuses
|
|
else if(m_spellInfo->SpellVisual[0] == 563)
|
|
duration = 600; // 10 mins
|
|
// shaman rockbiter enchantments
|
|
else if(m_spellInfo->SpellVisual[0] == 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_BOOL_GM_LOG_TRADE) )
|
|
{
|
|
sLog.outCommand(p_caster->GetSession()->GetAccountId(),"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);
|
|
|
|
// update trade window for show enchantment for caster in trade window
|
|
if (m_targets.m_targetMask & TARGET_FLAG_TRADE_ITEM)
|
|
p_caster->GetSession()->SendUpdateTrade();
|
|
}
|
|
|
|
void Spell::EffectTameCreature(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
// Caster must be player, checked in Spell::CheckCast
|
|
// Spell can be triggered, we need to check original caster prior to caster
|
|
Player* plr = (Player*)GetAffectiveCaster();
|
|
|
|
Creature* creatureTarget = (Creature*)unitTarget;
|
|
|
|
// cast finish successfully
|
|
//SendChannelUpdate(0);
|
|
finish();
|
|
|
|
Pet* pet = plr->CreateTamedPetFrom(creatureTarget, m_spellInfo->Id);
|
|
if(!pet) // in versy specific state like near world end/etc.
|
|
return;
|
|
|
|
// "kill" original creature
|
|
creatureTarget->ForcedDespawn();
|
|
|
|
uint32 level = (creatureTarget->getLevel() < (plr->getLevel() - 5)) ? (plr->getLevel() - 5) : creatureTarget->getLevel();
|
|
|
|
// prepare visual effect for levelup
|
|
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1);
|
|
|
|
// add to world
|
|
pet->GetMap()->Add((Creature*)pet);
|
|
|
|
// visual effect for levelup
|
|
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level);
|
|
|
|
// caster have pet now
|
|
plr->SetPet(pet);
|
|
|
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
|
plr->PetSpellInitialize();
|
|
}
|
|
|
|
void Spell::EffectSummonPet(SpellEffectIndex eff_idx)
|
|
{
|
|
uint32 petentry = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
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;
|
|
|
|
OldSummon->GetMap()->Remove((Creature*)OldSummon,false);
|
|
|
|
float px, py, pz;
|
|
m_caster->GetClosePoint(px, py, pz, OldSummon->GetObjectSize());
|
|
|
|
OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation());
|
|
m_caster->GetMap()->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(m_caster->GetTypeId() == TYPEID_PLAYER && NewSummon->LoadPetFromDB((Player*)m_caster, petentry))
|
|
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 = sObjectMgr.GeneratePetNumber();
|
|
if(!NewSummon->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), map, m_caster->GetPhaseMask(),
|
|
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("Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)",
|
|
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)
|
|
{
|
|
if ( ((Creature*)m_caster)->isTotem() )
|
|
NewSummon->GetCharmInfo()->SetReactState(REACT_AGGRESSIVE);
|
|
else
|
|
NewSummon->GetCharmInfo()->SetReactState(REACT_DEFENSIVE);
|
|
}
|
|
|
|
NewSummon->SetOwnerGUID(m_caster->GetGUID());
|
|
NewSummon->SetCreatorGUID(m_caster->GetGUID());
|
|
NewSummon->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
|
|
NewSummon->setFaction(faction);
|
|
NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_0, 2048);
|
|
NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
|
|
NewSummon->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(NULL)));
|
|
NewSummon->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
|
|
NewSummon->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
|
|
NewSummon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
|
|
|
|
NewSummon->UpdateWalkMode(m_caster);
|
|
|
|
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
|
|
if(m_caster->GetTypeId() == TYPEID_PLAYER)
|
|
NewSummon->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
|
|
|
|
if(m_caster->IsPvP())
|
|
NewSummon->SetPvP(true);
|
|
|
|
if(m_caster->IsFFAPvP())
|
|
NewSummon->SetFFAPvP(true);
|
|
|
|
NewSummon->InitStatsForLevel(petlevel, m_caster);
|
|
NewSummon->InitPetCreateSpells();
|
|
NewSummon->InitLevelupSpellsForLevel();
|
|
NewSummon->InitTalentForLevel();
|
|
|
|
if(NewSummon->getPetType() == SUMMON_PET)
|
|
{
|
|
// generate new name for summon pet
|
|
std::string new_name = sObjectMgr.GeneratePetName(petentry);
|
|
if(!new_name.empty())
|
|
NewSummon->SetName(new_name);
|
|
}
|
|
else if(NewSummon->getPetType() == HUNTER_PET)
|
|
{
|
|
NewSummon->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
|
|
NewSummon->SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_ABANDONED);
|
|
}
|
|
|
|
NewSummon->AIM_Initialize();
|
|
NewSummon->SetHealth(NewSummon->GetMaxHealth());
|
|
NewSummon->SetPower(POWER_MANA, NewSummon->GetMaxPower(POWER_MANA));
|
|
|
|
map->Add((Creature*)NewSummon);
|
|
|
|
m_caster->SetPet(NewSummon);
|
|
DEBUG_LOG("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(SpellEffectIndex eff_idx)
|
|
{
|
|
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[eff_idx]);
|
|
if(!learn_spellproto)
|
|
return;
|
|
|
|
pet->learnSpell(learn_spellproto->Id);
|
|
|
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
|
_player->PetSpellInitialize();
|
|
}
|
|
|
|
void Spell::EffectTaunt(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// 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->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(SpellEffectIndex eff_idx)
|
|
{
|
|
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 < MAX_EFFECT_INDEX; ++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 < int(eff_idx)) // we must calculate only at last weapon effect
|
|
return;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// some spell specific modifiers
|
|
bool spellBonusNeedWeaponDamagePercentMod = false; // if set applied weapon damage percent mode to spell bonus
|
|
|
|
float weaponDamagePercentMod = 1.0f; // applied to weapon damage and to fixed effect damage bonus
|
|
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_GENERIC:
|
|
{
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
// for spells with divided damage to targets
|
|
case 66765: case 66809: case 67331: // Meteor Fists
|
|
case 67333: // Meteor Fists
|
|
case 69055: // Bone Slice
|
|
case 71021: // Saber Lash
|
|
{
|
|
uint32 count = 0;
|
|
for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
|
|
if(ihit->effectMask & (1<<eff_idx))
|
|
++count;
|
|
|
|
totalDamagePercentMod /= float(count); // divide to all targets
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_WARRIOR:
|
|
{
|
|
// Devastate bonus and sunder armor refresh
|
|
if(m_spellInfo->SpellVisual[0] == 12295 && m_spellInfo->SpellIconID == 1508)
|
|
{
|
|
uint32 stack = 0;
|
|
// Need refresh all Sunder Armor auras from this caster
|
|
Unit::AuraMap& suAuras = unitTarget->GetAuras();
|
|
SpellEntry const *spellInfo;
|
|
for(Unit::AuraMap::iterator itr = suAuras.begin(); itr != suAuras.end(); ++itr)
|
|
{
|
|
spellInfo = (*itr).second->GetSpellProto();
|
|
if( spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR &&
|
|
(spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000004000)) &&
|
|
(*itr).second->GetCasterGUID() == m_caster->GetGUID())
|
|
{
|
|
(*itr).second->RefreshAura();
|
|
stack = (*itr).second->GetStackAmount();
|
|
break;
|
|
}
|
|
}
|
|
if (stack)
|
|
spell_bonus += stack * CalculateDamage(EFFECT_INDEX_2, unitTarget);
|
|
if (!stack || stack < spellInfo->StackAmount)
|
|
// Devastate causing Sunder Armor Effect
|
|
// and no need to cast over max stack amount
|
|
m_caster->CastSpell(unitTarget, 58567, true);
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_ROGUE:
|
|
{
|
|
// Mutilate (for each hand)
|
|
if(m_spellInfo->SpellFamilyFlags & UI64LIT(0x600000000))
|
|
{
|
|
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.2f; // 120% if poisoned
|
|
}
|
|
// Fan of Knives
|
|
else if (m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0004000000000000)))
|
|
{
|
|
Item* weapon = ((Player*)m_caster)->GetWeaponForAttack(m_attackType,true,true);
|
|
if (weapon && weapon->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER)
|
|
totalDamagePercentMod *= 1.5f; // 150% to daggers
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_PALADIN:
|
|
{
|
|
// Judgement of Command - receive benefit from Spell Damage and Attack Power
|
|
if(m_spellInfo->SpellFamilyFlags & UI64LIT(0x00020000000000))
|
|
{
|
|
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
|
|
int32 holy = m_caster->SpellBaseDamageBonusDone(GetSpellSchoolMask(m_spellInfo));
|
|
if (holy < 0)
|
|
holy = 0;
|
|
spell_bonus += int32(ap * 0.08f) + int32(holy * 13 / 100);
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_HUNTER:
|
|
{
|
|
// Kill Shot
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x80000000000000))
|
|
{
|
|
// 0.4*RAP added to damage (that is 0.2 if we apply PercentMod (200%) to spell_bonus, too)
|
|
spellBonusNeedWeaponDamagePercentMod = true;
|
|
spell_bonus += int32( 0.2f * m_caster->GetTotalAttackPowerValue(RANGED_ATTACK) );
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_SHAMAN:
|
|
{
|
|
// Skyshatter Harness item set bonus
|
|
// Stormstrike
|
|
if(m_spellInfo->SpellFamilyFlags & UI64LIT(0x001000000000))
|
|
{
|
|
Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
|
|
for(Unit::AuraList::const_iterator citr = m_OverrideClassScript.begin(); citr != m_OverrideClassScript.end(); ++citr)
|
|
{
|
|
// Stormstrike AP Buff
|
|
if ( (*citr)->GetModifier()->m_miscvalue == 5634 )
|
|
{
|
|
m_caster->CastSpell(m_caster, 38430, true, NULL, *citr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_DEATHKNIGHT:
|
|
{
|
|
// Blood Strike, Heart Strike, Obliterate
|
|
// Blood-Caked Strike
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0002000001400000) ||
|
|
m_spellInfo->SpellIconID == 1736)
|
|
{
|
|
uint32 count = 0;
|
|
Unit::AuraMap const& auras = unitTarget->GetAuras();
|
|
for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
|
|
{
|
|
if(itr->second->GetSpellProto()->Dispel == DISPEL_DISEASE &&
|
|
itr->second->GetCasterGUID() == m_caster->GetGUID() &&
|
|
IsSpellLastAuraEffect(itr->second->GetSpellProto(), itr->second->GetEffIndex()))
|
|
++count;
|
|
}
|
|
|
|
if (count)
|
|
{
|
|
// Effect 1(for Blood-Caked Strike)/3(other) damage is bonus
|
|
float bonus = count * CalculateDamage(m_spellInfo->SpellIconID == 1736 ? EFFECT_INDEX_0 : EFFECT_INDEX_2, unitTarget) / 100.0f;
|
|
// Blood Strike, Blood-Caked Strike and Obliterate store bonus*2
|
|
if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0002000000400000) ||
|
|
m_spellInfo->SpellIconID == 1736)
|
|
bonus /= 2.0f;
|
|
|
|
totalDamagePercentMod *= 1.0f + bonus;
|
|
}
|
|
|
|
// Heart Strike secondary target
|
|
if (m_spellInfo->SpellIconID == 3145)
|
|
if (m_targets.getUnitTarget() != unitTarget)
|
|
weaponDamagePercentMod /= 2.0f;
|
|
}
|
|
// Glyph of Blood Strike
|
|
if( m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000400000) &&
|
|
m_caster->HasAura(59332) &&
|
|
unitTarget->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED))
|
|
{
|
|
totalDamagePercentMod *= 1.2f; // 120% if snared
|
|
}
|
|
// Glyph of Death Strike
|
|
if( m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000010) &&
|
|
m_caster->HasAura(59336))
|
|
{
|
|
int32 rp = m_caster->GetPower(POWER_RUNIC_POWER) / 10;
|
|
if(rp > 25)
|
|
rp = 25;
|
|
totalDamagePercentMod *= 1.0f + rp / 100.0f;
|
|
}
|
|
// Glyph of Plague Strike
|
|
if( m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000001) &&
|
|
m_caster->HasAura(58657) )
|
|
{
|
|
totalDamagePercentMod *= 1.2f;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int32 fixed_bonus = 0;
|
|
for (int j = 0; j < MAX_EFFECT_INDEX; ++j)
|
|
{
|
|
switch(m_spellInfo->Effect[j])
|
|
{
|
|
case SPELL_EFFECT_WEAPON_DAMAGE:
|
|
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
|
|
fixed_bonus += CalculateDamage(SpellEffectIndex(j), unitTarget);
|
|
break;
|
|
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
|
|
fixed_bonus += CalculateDamage(SpellEffectIndex(j), unitTarget);
|
|
normalized = true;
|
|
break;
|
|
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
|
|
weaponDamagePercentMod *= float(CalculateDamage(SpellEffectIndex(j), unitTarget)) / 100.0f;
|
|
|
|
// applied only to prev.effects fixed damage
|
|
fixed_bonus = int32(fixed_bonus*weaponDamagePercentMod);
|
|
break;
|
|
default:
|
|
break; // not weapon damage effect, just skip
|
|
}
|
|
}
|
|
|
|
// apply weaponDamagePercentMod to spell bonus also
|
|
if(spellBonusNeedWeaponDamagePercentMod)
|
|
spell_bonus = int32(spell_bonus*weaponDamagePercentMod);
|
|
|
|
// 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
|
|
m_damage+= uint32(bonus > 0 ? bonus : 0);
|
|
|
|
// Hemorrhage
|
|
if (m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x2000000)))
|
|
{
|
|
if(m_caster->GetTypeId()==TYPEID_PLAYER)
|
|
((Player*)m_caster)->AddComboPoints(unitTarget, 1);
|
|
}
|
|
// Mangle (Cat): CP
|
|
else if (m_spellInfo->SpellFamilyName==SPELLFAMILY_DRUID && (m_spellInfo->SpellFamilyFlags==UI64LIT(0x0000040000000000)))
|
|
{
|
|
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, true, false);
|
|
|
|
// wands don't have ammo
|
|
if (!pItem || 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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if(!unitTarget || !unitTarget->isAlive() || !m_caster->isAlive())
|
|
return;
|
|
|
|
if(!unitTarget->CanHaveThreatList())
|
|
return;
|
|
|
|
unitTarget->AddThreat(m_caster, float(damage), false, GetSpellSchoolMask(m_spellInfo), m_spellInfo);
|
|
}
|
|
|
|
void Spell::EffectHealMaxHealth(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
if(!unitTarget->isAlive())
|
|
return;
|
|
|
|
uint32 heal = m_caster->GetMaxHealth();
|
|
|
|
m_healing += heal;
|
|
}
|
|
|
|
void Spell::EffectInterruptCast(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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 (Spell* spell = unitTarget->GetCurrentSpell(CurrentSpellTypes(i)))
|
|
{
|
|
SpellEntry const* curSpellInfo = spell->m_spellInfo;
|
|
// check if we can interrupt spell
|
|
if ((curSpellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT) && curSpellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE )
|
|
{
|
|
unitTarget->ProhibitSpellSchool(GetSpellSchoolMask(curSpellInfo), GetSpellDuration(m_spellInfo));
|
|
unitTarget->InterruptSpell(CurrentSpellTypes(i),false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spell::EffectSummonObjectWild(SpellEffectIndex eff_idx)
|
|
{
|
|
uint32 gameobject_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
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(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map,
|
|
m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
|
|
{
|
|
delete pGameObj;
|
|
return;
|
|
}
|
|
|
|
int32 duration = GetSpellDuration(m_spellInfo);
|
|
|
|
pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
|
|
pGameObj->SetSpellId(m_spellInfo->Id);
|
|
|
|
// Wild object not have owner and check clickable by players
|
|
map->Add(pGameObj);
|
|
|
|
if(pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP && m_caster->GetTypeId() == TYPEID_PLAYER)
|
|
{
|
|
Player *pl = (Player*)m_caster;
|
|
BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
|
|
|
|
switch(pGameObj->GetMapId())
|
|
{
|
|
case 489: //WS
|
|
{
|
|
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);
|
|
}
|
|
break;
|
|
}
|
|
case 566: //EY
|
|
{
|
|
if(bg && bg->GetTypeID()==BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS)
|
|
{
|
|
((BattleGroundEY*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pGameObj->SummonLinkedTrapIfAny();
|
|
}
|
|
|
|
void Spell::EffectScriptEffect(SpellEffectIndex eff_idx)
|
|
{
|
|
// TODO: we must implement hunter pet summon at login there (spell 6962)
|
|
|
|
switch(m_spellInfo->SpellFamilyName)
|
|
{
|
|
case SPELLFAMILY_GENERIC:
|
|
{
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 8856: // Bending Shinbone
|
|
{
|
|
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;
|
|
}
|
|
case 17512: // Piccolo of the Flaming Fire
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE);
|
|
return;
|
|
}
|
|
case 20589: // Escape artist
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED);
|
|
return;
|
|
}
|
|
case 24590: // Brittle Armor - need remove one 24575 Brittle Armor aura
|
|
unitTarget->RemoveSingleSpellAurasFromStack(24575);
|
|
return;
|
|
case 26275: // PX-238 Winter Wondervolt TRAP
|
|
{
|
|
uint32 spells[4] = { 26272, 26157, 26273, 26274 };
|
|
|
|
// check presence
|
|
for(int j = 0; j < 4; ++j)
|
|
if (unitTarget->HasAura(spells[j], EFFECT_INDEX_0))
|
|
return;
|
|
|
|
// select spell
|
|
uint32 iTmpSpellId = spells[urand(0,3)];
|
|
|
|
// cast
|
|
unitTarget->CastSpell(unitTarget, iTmpSpellId, true);
|
|
return;
|
|
}
|
|
case 26465: // Mercurial Shield - need remove one 26464 Mercurial Shield aura
|
|
unitTarget->RemoveSingleSpellAurasFromStack(26464);
|
|
return;
|
|
case 25140: // Orb teleport spells
|
|
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;
|
|
}
|
|
case 22539: // Shadow Flame (All script effects, not just end ones to
|
|
case 22972: // prevent player from dodging the last triggered spell)
|
|
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;
|
|
}
|
|
case 26656: // Summon Black Qiraji Battle Tank
|
|
{
|
|
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);
|
|
|
|
return;
|
|
}
|
|
case 29830: // Mirren's Drinking Hat
|
|
{
|
|
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(eff_idx,item);
|
|
|
|
break;
|
|
}
|
|
case 30918: // Improved Sprint
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// Removes snares and roots.
|
|
unitTarget->RemoveAurasAtMechanicImmunity(IMMUNE_TO_ROOT_AND_SNARE_MASK,30918,true);
|
|
break;
|
|
}
|
|
case 41126: // Flame Crash
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
unitTarget->CastSpell(unitTarget, 41131, true);
|
|
break;
|
|
}
|
|
case 44455: // Character Script Effect Reverse Cast
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
Creature* pTarget = (Creature*)unitTarget;
|
|
|
|
if (const SpellEntry *pSpell = sSpellStore.LookupEntry(m_spellInfo->CalculateSimpleValue(eff_idx)))
|
|
{
|
|
// if we used item at least once...
|
|
if (pTarget->isTemporarySummon() && pTarget->GetEntry() == pSpell->EffectMiscValue[eff_idx])
|
|
{
|
|
TemporarySummon* pSummon = (TemporarySummon*)pTarget;
|
|
|
|
// can only affect "own" summoned
|
|
if (pSummon->GetSummonerGuid() == m_caster->GetObjectGuid())
|
|
{
|
|
if (pTarget->hasUnitState(UNIT_STAT_ROAMING | UNIT_STAT_ROAMING_MOVE))
|
|
pTarget->GetMotionMaster()->MovementExpired();
|
|
|
|
// trigger cast of quest complete script (see code for this spell below)
|
|
pTarget->CastSpell(pTarget, 44462, true);
|
|
|
|
pTarget->GetMotionMaster()->MovePoint(0, m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// or if it is first time used item, cast summon and despawn the target
|
|
m_caster->CastSpell(pTarget, pSpell, true);
|
|
pTarget->ForcedDespawn();
|
|
|
|
// TODO: here we should get pointer to the just summoned and make it move.
|
|
// without, it will be one extra use of quest item
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 44462: // Cast Quest Complete on Master
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
Creature* pQuestCow = NULL;
|
|
|
|
float range = 20.0f;
|
|
|
|
// search for a reef cow nearby
|
|
MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster, 24797, true, range);
|
|
MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, pQuestCow, u_check);
|
|
|
|
Cell::VisitGridObjects(m_caster, searcher, range);
|
|
|
|
// no cows found, so return
|
|
if (!pQuestCow)
|
|
return;
|
|
|
|
if (!((Creature*)m_caster)->isTemporarySummon())
|
|
return;
|
|
|
|
if (const SpellEntry *pSpell = sSpellStore.LookupEntry(m_spellInfo->CalculateSimpleValue(eff_idx)))
|
|
{
|
|
TemporarySummon* pSummon = (TemporarySummon*)m_caster;
|
|
|
|
// all ok, so make summoner cast the quest complete
|
|
if (Unit* pSummoner = pSummon->GetSummoner())
|
|
pSummoner->CastSpell(pSummoner, pSpell, true);
|
|
}
|
|
|
|
return;
|
|
}
|
|
case 44876: // Force Cast - Portal Effect: Sunwell Isle
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
unitTarget->CastSpell(unitTarget, 44870, true);
|
|
break;
|
|
}
|
|
case 45668: // Ultra-Advanced Proto-Typical Shortening Blaster
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
if (roll_chance_i(25)) // chance unknown, using 25
|
|
return;
|
|
|
|
static uint32 const spellPlayer[5] =
|
|
{
|
|
45674, // Bigger!
|
|
45675, // Shrunk
|
|
45678, // Yellow
|
|
45682, // Ghost
|
|
45684 // Polymorph
|
|
};
|
|
|
|
static uint32 const spellTarget[5] =
|
|
{
|
|
45673, // Bigger!
|
|
45672, // Shrunk
|
|
45677, // Yellow
|
|
45681, // Ghost
|
|
45683 // Polymorph
|
|
};
|
|
|
|
m_caster->CastSpell(m_caster, spellPlayer[urand(0,4)], true);
|
|
unitTarget->CastSpell(unitTarget, spellTarget[urand(0,4)], true);
|
|
|
|
return;
|
|
}
|
|
case 45691: // Magnataur On Death 1
|
|
{
|
|
// assuming caster is creature, if not, then return
|
|
if (m_caster->GetTypeId() != TYPEID_UNIT)
|
|
return;
|
|
|
|
Player* pPlayer = ((Creature*)m_caster)->GetLootRecipient();
|
|
|
|
if (!pPlayer)
|
|
return;
|
|
|
|
if (pPlayer->HasAura(45674) || pPlayer->HasAura(45675) || pPlayer->HasAura(45678) || pPlayer->HasAura(45682) || pPlayer->HasAura(45684))
|
|
pPlayer->CastSpell(pPlayer, 45686, true);
|
|
|
|
m_caster->CastSpell(m_caster, 45685, true);
|
|
|
|
return;
|
|
}
|
|
case 46203: // Goblin Weather Machine
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
uint32 spellId = 0;
|
|
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;
|
|
}
|
|
case 46642: //5,000 Gold
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
((Player*)unitTarget)->ModifyMoney(50000000);
|
|
break;
|
|
}
|
|
case 47097: // Surge Needle Teleporter
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if (unitTarget->GetAreaId() == 4156)
|
|
unitTarget->CastSpell(unitTarget, 47324, true);
|
|
else if (unitTarget->GetAreaId() == 4157)
|
|
unitTarget->CastSpell(unitTarget, 47325, true);
|
|
|
|
break;
|
|
}
|
|
case 47393: // The Focus on the Beach: Quest Completion Script
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
if (unitTarget->HasAura(47391, EFFECT_INDEX_0))
|
|
unitTarget->RemoveAurasDueToSpell(47391);
|
|
|
|
return;
|
|
}
|
|
case 47615: // Atop the Woodlands: Quest Completion Script
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
if (unitTarget->HasAura(47473, EFFECT_INDEX_0))
|
|
unitTarget->RemoveAurasDueToSpell(47473);
|
|
|
|
return;
|
|
}
|
|
case 47638: // The End of the Line: Quest Completion Script
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
if (unitTarget->HasAura(47636, EFFECT_INDEX_0))
|
|
unitTarget->RemoveAurasDueToSpell(47636);
|
|
|
|
return;
|
|
}
|
|
case 48603: // High Executor's Branding Iron
|
|
// Torture the Torturer: High Executor's Branding Iron Impact
|
|
unitTarget->CastSpell(unitTarget, 48614, true);
|
|
return;
|
|
case 51770: // Emblazon Runeblade
|
|
{
|
|
Unit* caster = GetAffectiveCaster();
|
|
if (!caster)
|
|
return;
|
|
|
|
caster->CastSpell(caster, damage, false);
|
|
break;
|
|
}
|
|
case 51864: // Player Summon Nass
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
// Summon Nass
|
|
if (const SpellEntry* pSpell = sSpellStore.LookupEntry(51865))
|
|
{
|
|
// Only if he is not already there
|
|
if (!m_caster->FindGuardianWithEntry(pSpell->EffectMiscValue[EFFECT_INDEX_0]))
|
|
{
|
|
m_caster->CastSpell(m_caster, pSpell, true);
|
|
|
|
if (Pet* pPet = m_caster->FindGuardianWithEntry(pSpell->EffectMiscValue[EFFECT_INDEX_0]))
|
|
{
|
|
// Nass Periodic Say aura
|
|
pPet->CastSpell(pPet, 51868, true);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
case 51889: // Quest Accept Summon Nass
|
|
{
|
|
// This is clearly for quest accept, is spell 51864 then for gossip and does pretty much the same thing?
|
|
// Just "jumping" to what may be the "gossip-spell" for now, doing the same thing
|
|
m_caster->CastSpell(m_caster, 51864, true);
|
|
return;
|
|
}
|
|
case 51910: // Kickin' Nass: Quest Completion
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if (const SpellEntry* pSpell = sSpellStore.LookupEntry(51865))
|
|
{
|
|
// Is this all to be done at completion?
|
|
if (Pet* pPet = m_caster->FindGuardianWithEntry(pSpell->EffectMiscValue[EFFECT_INDEX_0]))
|
|
((Player*)m_caster)->RemovePet(pPet, PET_SAVE_NOT_IN_SLOT);
|
|
}
|
|
return;
|
|
}
|
|
case 52751: // Death Gate
|
|
{
|
|
if (!unitTarget || unitTarget->getClass() != CLASS_DEATH_KNIGHT)
|
|
return;
|
|
|
|
// triggered spell is stored in m_spellInfo->EffectBasePoints[0]
|
|
unitTarget->CastSpell(unitTarget, damage, false);
|
|
break;
|
|
}
|
|
case 52941: // Song of Cleansing
|
|
{
|
|
uint32 spellId = 0;
|
|
|
|
switch(m_caster->GetAreaId())
|
|
{
|
|
case 4385: spellId = 52954; break; // Bittertide Lake
|
|
case 4290: spellId = 52958; break; // River's Heart
|
|
case 4388: spellId = 52959; break; // Wintergrasp River
|
|
}
|
|
|
|
if (spellId)
|
|
m_caster->CastSpell(m_caster, spellId, true);
|
|
|
|
break;
|
|
}
|
|
case 54182: // An End to the Suffering: Quest Completion Script
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// Remove aura given at quest accept / gossip
|
|
if (unitTarget->HasAura(51967))
|
|
unitTarget->RemoveAurasDueToSpell(51967);
|
|
|
|
return;
|
|
}
|
|
case 54729: // Winged Steed of the Ebon Blade
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
// Prevent stacking of mounts
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
|
|
|
|
// Triggered spell id dependent of riding skill
|
|
if (uint16 skillval = ((Player*)unitTarget)->GetSkillValue(SKILL_RIDING))
|
|
{
|
|
if (skillval >= 300)
|
|
unitTarget->CastSpell(unitTarget, 54727, true);
|
|
else
|
|
unitTarget->CastSpell(unitTarget, 54726, true);
|
|
}
|
|
return;
|
|
}
|
|
case 54436: // Demonic Empowerment (succubus Vanish effect)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED);
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED);
|
|
unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_STUN);
|
|
return;
|
|
}
|
|
case 55693: // Remove Collapsing Cave Aura
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
unitTarget->RemoveAurasDueToSpell(m_spellInfo->CalculateSimpleValue(eff_idx));
|
|
break;
|
|
}
|
|
case 58418: // Portal to Orgrimmar
|
|
case 58420: // Portal to Stormwind
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || eff_idx != EFFECT_INDEX_0)
|
|
return;
|
|
|
|
uint32 spellID = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_0);
|
|
uint32 questID = m_spellInfo->CalculateSimpleValue(EFFECT_INDEX_1);
|
|
|
|
if (((Player*)unitTarget)->GetQuestStatus(questID) == QUEST_STATUS_COMPLETE && !((Player*)unitTarget)->GetQuestRewardStatus (questID))
|
|
unitTarget->CastSpell(unitTarget, spellID, true);
|
|
|
|
return;
|
|
}
|
|
case 59317: // Teleporting
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
// return from top
|
|
if (((Player*)unitTarget)->GetAreaId() == 4637)
|
|
unitTarget->CastSpell(unitTarget, 59316, true);
|
|
// teleport atop
|
|
else
|
|
unitTarget->CastSpell(unitTarget, 59314, true);
|
|
|
|
return;
|
|
} // random spell learn instead placeholder
|
|
case 60893: // Northrend Alchemy Research
|
|
case 61177: // Northrend Inscription Research
|
|
case 61288: // Minor Inscription Research
|
|
case 61756: // Northrend Inscription Research (FAST QA VERSION)
|
|
case 64323: // Book of Glyph Mastery
|
|
{
|
|
if (m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
// learn random explicit discovery recipe (if any)
|
|
if (uint32 discoveredSpell = GetExplicitDiscoverySpell(m_spellInfo->Id, (Player*)m_caster))
|
|
((Player*)m_caster)->learnSpell(discoveredSpell, false);
|
|
|
|
return;
|
|
}
|
|
case 69377: // Fortitude
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
m_caster->CastSpell(unitTarget, 72590, true);
|
|
return;
|
|
}
|
|
case 69378: // Blessing of Forgotten Kings
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
m_caster->CastSpell(unitTarget, 72586, true);
|
|
return;
|
|
}
|
|
case 69381: // Gift of the Wild
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
m_caster->CastSpell(unitTarget, 72588, true);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_WARLOCK:
|
|
{
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 6201: // Healthstone creating spells
|
|
case 6202:
|
|
case 5699:
|
|
case 11729:
|
|
case 11730:
|
|
case 27230:
|
|
case 47871:
|
|
case 47878:
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
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[8][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
|
|
{36889, 36890, 36891}, // Demonic Healthstone
|
|
{36892, 36893, 36894} // Fel 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
|
|
case 47871:
|
|
itemtype=itypes[6][rank];break; // Demonic Healthstone
|
|
case 47878:
|
|
itemtype=itypes[7][rank];break; // Fel Healthstone
|
|
default:
|
|
return;
|
|
}
|
|
DoCreateItem( eff_idx, itemtype );
|
|
return;
|
|
}
|
|
case 47193: // Demonic Empowerment
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
uint32 entry = unitTarget->GetEntry();
|
|
uint32 spellID;
|
|
switch(entry)
|
|
{
|
|
case 416: spellID = 54444; break; // imp
|
|
case 417: spellID = 54509; break; // fellhunter
|
|
case 1860: spellID = 54443; break; // void
|
|
case 1863: spellID = 54435; break; // succubus
|
|
case 17252: spellID = 54508; break; // fellguard
|
|
default:
|
|
return;
|
|
}
|
|
unitTarget->CastSpell(unitTarget, spellID, true);
|
|
return;
|
|
}
|
|
case 47422: // Everlasting Affliction
|
|
{
|
|
// Need refresh caster corruption auras on target
|
|
Unit::AuraMap& suAuras = unitTarget->GetAuras();
|
|
for(Unit::AuraMap::iterator itr = suAuras.begin(); itr != suAuras.end(); ++itr)
|
|
{
|
|
SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
|
|
if(spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK &&
|
|
(spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000002)) &&
|
|
(*itr).second->GetCasterGUID() == m_caster->GetGUID())
|
|
(*itr).second->RefreshAura();
|
|
}
|
|
return;
|
|
}
|
|
case 63521: // Guarded by The Light (Paladin spell with SPELLFAMILY_WARLOCK)
|
|
{
|
|
// Divine Plea, refresh on target (3 aura slots)
|
|
if (Aura* aura = unitTarget->GetAura(54428, EFFECT_INDEX_0))
|
|
aura->RefreshAura();
|
|
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_PRIEST:
|
|
{
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 47948: // Pain and Suffering
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// Refresh Shadow Word: Pain on target
|
|
Unit::AuraMap& auras = unitTarget->GetAuras();
|
|
for(Unit::AuraMap::iterator itr = auras.begin(); itr != auras.end(); ++itr)
|
|
{
|
|
SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
|
|
if (spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST &&
|
|
(spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000008000)) &&
|
|
(*itr).second->GetCasterGUID() == m_caster->GetGUID())
|
|
{
|
|
(*itr).second->RefreshAura();
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_HUNTER:
|
|
{
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 53209: // Chimera Shot
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
uint32 spellId = 0;
|
|
int32 basePoint = 0;
|
|
Unit* target = unitTarget;
|
|
Unit::AuraMap& Auras = unitTarget->GetAuras();
|
|
for(Unit::AuraMap::iterator i = Auras.begin(); i != Auras.end(); ++i)
|
|
{
|
|
Aura *aura = (*i).second;
|
|
if (aura->GetCasterGUID() != m_caster->GetGUID())
|
|
continue;
|
|
|
|
// Search only Serpent Sting, Viper Sting, Scorpid Sting auras
|
|
uint64 familyFlag = aura->GetSpellProto()->SpellFamilyFlags;
|
|
if (!(familyFlag & UI64LIT(0x000000800000C000)))
|
|
continue;
|
|
|
|
// Refresh aura duration
|
|
aura->RefreshAura();
|
|
|
|
// Serpent Sting - Instantly deals 40% of the damage done by your Serpent Sting.
|
|
if ((familyFlag & UI64LIT(0x0000000000004000)) && aura->GetEffIndex() == EFFECT_INDEX_0)
|
|
{
|
|
// m_amount already include RAP bonus
|
|
basePoint = aura->GetModifier()->m_amount * aura->GetAuraMaxTicks() * 40 / 100;
|
|
spellId = 53353; // Chimera Shot - Serpent
|
|
}
|
|
|
|
// Viper Sting - Instantly restores mana to you equal to 60% of the total amount drained by your Viper Sting.
|
|
if ((familyFlag & UI64LIT(0x0000008000000000)) && aura->GetEffIndex() == EFFECT_INDEX_0)
|
|
{
|
|
uint32 target_max_mana = unitTarget->GetMaxPower(POWER_MANA);
|
|
if (!target_max_mana)
|
|
continue;
|
|
|
|
// ignore non positive values (can be result apply spellmods to aura damage
|
|
uint32 pdamage = aura->GetModifier()->m_amount > 0 ? aura->GetModifier()->m_amount : 0;
|
|
|
|
// Special case: draining x% of mana (up to a maximum of 2*x% of the caster's maximum mana)
|
|
uint32 maxmana = m_caster->GetMaxPower(POWER_MANA) * pdamage * 2 / 100;
|
|
|
|
pdamage = target_max_mana * pdamage / 100;
|
|
if (pdamage > maxmana)
|
|
pdamage = maxmana;
|
|
|
|
pdamage *= 4; // total aura damage
|
|
basePoint = pdamage * 60 / 100;
|
|
spellId = 53358; // Chimera Shot - Viper
|
|
target = m_caster;
|
|
}
|
|
|
|
// Scorpid Sting - Attempts to Disarm the target for 10 sec. This effect cannot occur more than once per 1 minute.
|
|
if (familyFlag & UI64LIT(0x0000000000008000))
|
|
spellId = 53359; // Chimera Shot - Scorpid
|
|
// ?? nothing say in spell desc (possibly need addition check)
|
|
//if ((familyFlag & UI64LIT(0x0000010000000000)) || // dot
|
|
// (familyFlag & UI64LIT(0x0000100000000000))) // stun
|
|
//{
|
|
// spellId = 53366; // 53366 Chimera Shot - Wyvern
|
|
//}
|
|
}
|
|
|
|
if (spellId)
|
|
m_caster->CastCustomSpell(target, spellId, &basePoint, 0, 0, false);
|
|
|
|
return;
|
|
}
|
|
case 53412: // Invigoration (pet triggered script, master targeted)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY);
|
|
for(Unit::AuraList::const_iterator i = auras.begin();i != auras.end(); ++i)
|
|
{
|
|
// Invigoration (master talent)
|
|
if ((*i)->GetModifier()->m_miscvalue == 8 && (*i)->GetSpellProto()->SpellIconID == 3487)
|
|
{
|
|
if (roll_chance_i((*i)->GetModifier()->m_amount))
|
|
{
|
|
unitTarget->CastSpell(unitTarget, 53398, true, NULL, (*i), m_caster->GetGUID());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
case 53271: // Master's Call
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// script effect have in value, but this outdated removed part
|
|
unitTarget->CastSpell(unitTarget, 62305, true);
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_PALADIN:
|
|
{
|
|
// Judgement (seal trigger)
|
|
if (m_spellInfo->Category == SPELLCATEGORY_JUDGEMENT)
|
|
{
|
|
if (!unitTarget || !unitTarget->isAlive())
|
|
return;
|
|
|
|
uint32 spellId1 = 0;
|
|
uint32 spellId2 = 0;
|
|
|
|
// Judgement self add switch
|
|
switch (m_spellInfo->Id)
|
|
{
|
|
case 53407: spellId1 = 20184; break; // Judgement of Justice
|
|
case 20271: // Judgement of Light
|
|
case 57774: spellId1 = 20185; break; // Judgement of Light
|
|
case 53408: spellId1 = 20186; break; // Judgement of Wisdom
|
|
default:
|
|
sLog.outError("Unsupported Judgement (seal trigger) spell (Id: %u) in Spell::EffectScriptEffect",m_spellInfo->Id);
|
|
return;
|
|
}
|
|
|
|
// offensive seals have aura dummy in 2 effect
|
|
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)
|
|
{
|
|
// search seal (offensive seals have judgement's aura dummy spell id in 2 effect
|
|
if ((*itr)->GetEffIndex() != EFFECT_INDEX_2 || !IsSealSpell((*itr)->GetSpellProto()))
|
|
continue;
|
|
spellId2 = (*itr)->GetModifier()->m_amount;
|
|
SpellEntry const *judge = sSpellStore.LookupEntry(spellId2);
|
|
if (!judge)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
// if there were no offensive seals than there is seal with proc trigger aura
|
|
if (!spellId2)
|
|
{
|
|
Unit::AuraList const& procTriggerAuras = m_caster->GetAurasByType(SPELL_AURA_PROC_TRIGGER_SPELL);
|
|
for(Unit::AuraList::const_iterator itr = procTriggerAuras.begin(); itr != procTriggerAuras.end(); ++itr)
|
|
{
|
|
if ((*itr)->GetEffIndex() != EFFECT_INDEX_0 || !IsSealSpell((*itr)->GetSpellProto()))
|
|
continue;
|
|
spellId2 = 54158;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (spellId1)
|
|
m_caster->CastSpell(unitTarget, spellId1, true);
|
|
|
|
if (spellId2)
|
|
m_caster->CastSpell(unitTarget, spellId2, true);
|
|
|
|
return;
|
|
}
|
|
}
|
|
case SPELLFAMILY_POTION:
|
|
{
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 28698: // Dreaming Glory
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
unitTarget->CastSpell(unitTarget, 28694, true);
|
|
break;
|
|
}
|
|
case 28702: // Netherbloom
|
|
{
|
|
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, EFFECT_INDEX_0))
|
|
return;
|
|
|
|
unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true);
|
|
break;
|
|
}
|
|
case 28720: // Nightmare Vine
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
// 25% chance of casting Nightmare Pollen
|
|
if (roll_chance_i(75))
|
|
return;
|
|
|
|
unitTarget->CastSpell(unitTarget, 28721, true);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case SPELLFAMILY_DEATHKNIGHT:
|
|
{
|
|
switch(m_spellInfo->Id)
|
|
{
|
|
case 50842: // Pestilence
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
Unit* mainTarget = m_targets.getUnitTarget();
|
|
if (!mainTarget)
|
|
return;
|
|
|
|
// do only refresh diseases on main target if caster has Glyph of Disease
|
|
if (mainTarget == unitTarget && !m_caster->HasAura(63334))
|
|
return;
|
|
|
|
// Blood Plague
|
|
if (mainTarget->HasAura(55078))
|
|
m_caster->CastSpell(unitTarget, 55078, true);
|
|
|
|
// Frost Fever
|
|
if (mainTarget->HasAura(55095))
|
|
m_caster->CastSpell(unitTarget, 55095, true);
|
|
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// normal DB scripted effect
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
DEBUG_LOG("Spell ScriptStart spellid %u in EffectScriptEffect ", m_spellInfo->Id);
|
|
m_caster->GetMap()->ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget);
|
|
}
|
|
|
|
void Spell::EffectSanctuary(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
//unitTarget->CombatStop();
|
|
|
|
unitTarget->CombatStop();
|
|
unitTarget->getHostileRefManager().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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if(damage <= 0)
|
|
return;
|
|
|
|
((Player*)m_caster)->AddComboPoints(unitTarget, damage);
|
|
}
|
|
|
|
void Spell::EffectDuel(SpellEffectIndex eff_idx)
|
|
{
|
|
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() || 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
|
|
uint32 mapid = caster->GetMapId();
|
|
if( mapid != 0 && mapid != 1 && mapid != 530 && mapid != 571 && mapid != 609)
|
|
{
|
|
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[eff_idx];
|
|
|
|
Map *map = m_caster->GetMap();
|
|
if(!pGameObj->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id,
|
|
map, m_caster->GetPhaseMask(),
|
|
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.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
|
|
{
|
|
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/IN_MILLISECONDS : 0);
|
|
pGameObj->SetSpellId(m_spellInfo->Id);
|
|
|
|
m_caster->AddGameObject(pGameObj);
|
|
map->Add(pGameObj);
|
|
//END
|
|
|
|
// Send request
|
|
WorldPacket data(SMSG_DUEL_REQUESTED, 8 + 8);
|
|
data << uint64(pGameObj->GetGUID());
|
|
data << uint64(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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
if(!sWorld.getConfig(CONFIG_BOOL_CAST_UNSTUCK))
|
|
return;
|
|
|
|
Player* pTarget = (Player*)unitTarget;
|
|
|
|
DEBUG_LOG("Spell Effect: Stuck");
|
|
DETAIL_LOG("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->TeleportToHomebind(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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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*IN_MILLISECONDS); // 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(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!gameObjTarget)
|
|
return;
|
|
|
|
static ScriptInfo activateCommand = generateActivateCommand();
|
|
|
|
int32 delay_secs = m_spellInfo->CalculateSimpleValue(eff_idx);
|
|
|
|
gameObjTarget->GetMap()->ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget);
|
|
}
|
|
|
|
void Spell::EffectApplyGlyph(SpellEffectIndex eff_idx)
|
|
{
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player *player = (Player*)m_caster;
|
|
|
|
// apply new one
|
|
if(uint32 glyph = m_spellInfo->EffectMiscValue[eff_idx])
|
|
{
|
|
if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph))
|
|
{
|
|
if(GlyphSlotEntry const *gs = sGlyphSlotStore.LookupEntry(player->GetGlyphSlot(m_glyphIndex)))
|
|
{
|
|
if(gp->TypeFlags != gs->TypeFlags)
|
|
{
|
|
SendCastResult(SPELL_FAILED_INVALID_GLYPH);
|
|
return; // glyph slot mismatch
|
|
}
|
|
}
|
|
|
|
// remove old glyph
|
|
player->ApplyGlyph(m_glyphIndex, false);
|
|
player->SetGlyph(m_glyphIndex, glyph);
|
|
player->ApplyGlyph(m_glyphIndex, true);
|
|
player->SendTalentsInfoData(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spell::DoSummonTotem(SpellEffectIndex eff_idx, uint8 slot_dbc)
|
|
{
|
|
// DBC store slots starting from 1, with no slot 0 value)
|
|
int slot = slot_dbc ? slot_dbc - 1 : TOTEM_SLOT_NONE;
|
|
|
|
// unsummon old totem
|
|
if(slot < MAX_TOTEM_SLOT)
|
|
if (Totem *OldTotem = m_caster->GetTotem(TotemSlot(slot)))
|
|
OldTotem->UnSummon();
|
|
|
|
uint32 team = 0;
|
|
if (m_caster->GetTypeId()==TYPEID_PLAYER)
|
|
team = ((Player*)m_caster)->GetTeam();
|
|
|
|
Totem* pTotem = new Totem;
|
|
|
|
if(!pTotem->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_UNIT), m_caster->GetMap(), m_caster->GetPhaseMask(),
|
|
m_spellInfo->EffectMiscValue[eff_idx], team ))
|
|
{
|
|
delete pTotem;
|
|
return;
|
|
}
|
|
|
|
float angle = slot < MAX_TOTEM_SLOT ? M_PI_F/MAX_TOTEM_SLOT - (slot*2*M_PI_F/MAX_TOTEM_SLOT) : 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());
|
|
pTotem->SetSummonPoint(x, y, z, m_caster->GetOrientation());
|
|
|
|
if (slot < MAX_TOTEM_SLOT)
|
|
m_caster->_AddTotem(TotemSlot(slot),pTotem);
|
|
|
|
pTotem->SetOwner(m_caster->GetGUID());
|
|
pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initialized
|
|
|
|
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);
|
|
|
|
if(m_caster->GetTypeId() == TYPEID_PLAYER)
|
|
pTotem->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
|
|
|
|
if(m_caster->IsPvP())
|
|
pTotem->SetPvP(true);
|
|
|
|
if(m_caster->IsFFAPvP())
|
|
pTotem->SetFFAPvP(true);
|
|
|
|
pTotem->Summon(m_caster);
|
|
|
|
if (slot < MAX_TOTEM_SLOT && 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(SpellEffectIndex eff_idx)
|
|
{
|
|
// 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[eff_idx])
|
|
{
|
|
uint32 enchant_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
int32 duration = GetSpellDuration(m_spellInfo); // Try duration index first...
|
|
if(!duration)
|
|
duration = m_currentBasePoints[eff_idx]; // 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*IN_MILLISECONDS, 0);
|
|
item_owner->ApplyEnchantment(item, slot, true);
|
|
}
|
|
}
|
|
|
|
void Spell::EffectDisEnchant(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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(SpellEffectIndex eff_idx)
|
|
{
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player *_player = (Player*)m_caster;
|
|
|
|
Item* foodItem = m_targets.getItemTarget();
|
|
if(!foodItem)
|
|
return;
|
|
|
|
Pet *pet = _player->GetPet();
|
|
if(!pet)
|
|
return;
|
|
|
|
if(!pet->isAlive())
|
|
return;
|
|
|
|
int32 benefit = pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel);
|
|
if(benefit <= 0)
|
|
return;
|
|
|
|
uint32 count = 1;
|
|
_player->DestroyItemCount(foodItem,count,true);
|
|
// TODO: fix crash when a spell has two effects, both pointed at the same item target
|
|
|
|
m_caster->CastCustomSpell(pet, m_spellInfo->EffectTriggerSpell[eff_idx], &benefit, NULL, NULL, true);
|
|
}
|
|
|
|
void Spell::EffectDismissPet(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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(SpellEffectIndex eff_idx)
|
|
{
|
|
uint32 go_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
uint8 slot = 0;
|
|
switch(m_spellInfo->Effect[eff_idx])
|
|
{
|
|
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;
|
|
}
|
|
|
|
if(uint64 guid = m_caster->m_ObjectSlot[slot])
|
|
{
|
|
if(GameObject* obj = m_caster ? m_caster->GetMap()->GetGameObject(guid) : NULL)
|
|
obj->SetLootState(GO_JUST_DEACTIVATED);
|
|
m_caster->m_ObjectSlot[slot] = 0;
|
|
}
|
|
|
|
GameObject* pGameObj = new GameObject;
|
|
|
|
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(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), go_id, map,
|
|
m_caster->GetPhaseMask(), x, y, z, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
|
|
{
|
|
delete pGameObj;
|
|
return;
|
|
}
|
|
|
|
pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL,m_caster->getLevel());
|
|
int32 duration = GetSpellDuration(m_spellInfo);
|
|
pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
|
|
pGameObj->SetSpellId(m_spellInfo->Id);
|
|
m_caster->AddGameObject(pGameObj);
|
|
|
|
map->Add(pGameObj);
|
|
|
|
m_caster->m_ObjectSlot[slot] = pGameObj->GetGUID();
|
|
|
|
pGameObj->SummonLinkedTrapIfAny();
|
|
}
|
|
|
|
void Spell::EffectResurrect(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if(!unitTarget || !unitTarget->isAlive())
|
|
return;
|
|
|
|
if( unitTarget->m_extraAttacks )
|
|
return;
|
|
|
|
unitTarget->m_extraAttacks = damage;
|
|
}
|
|
|
|
void Spell::EffectParry(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER)
|
|
((Player*)unitTarget)->SetCanParry(true);
|
|
}
|
|
|
|
void Spell::EffectBlock(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER)
|
|
((Player*)unitTarget)->SetCanBlock(true);
|
|
}
|
|
|
|
void Spell::EffectLeapForward(SpellEffectIndex eff_idx)
|
|
{
|
|
if(unitTarget->isInFlight())
|
|
return;
|
|
|
|
if( m_spellInfo->rangeIndex == 1) //self range
|
|
{
|
|
float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx]));
|
|
|
|
// 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(unitTarget->GetMapId(), ox,oy,oz+0.5f, fx,fy,oz+0.5f,fx2,fy2,fz2, -0.5f))
|
|
{
|
|
fx = fx2;
|
|
fy = fy2;
|
|
fz = fz2;
|
|
unitTarget->UpdateGroundPositionZ(fx, fy, fz);
|
|
}
|
|
|
|
unitTarget->NearTeleportTo(fx, fy, fz, unitTarget->GetOrientation(), unitTarget == m_caster);
|
|
}
|
|
}
|
|
|
|
void Spell::EffectLeapBack(SpellEffectIndex eff_idx)
|
|
{
|
|
if(unitTarget->isInFlight())
|
|
return;
|
|
|
|
m_caster->KnockBackFrom(unitTarget,float(m_spellInfo->EffectMiscValue[eff_idx])/10,float(damage)/10);
|
|
}
|
|
|
|
void Spell::EffectReputation(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player *_player = (Player*)unitTarget;
|
|
|
|
int32 rep_change = m_currentBasePoints[eff_idx];
|
|
|
|
uint32 faction_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
|
|
|
|
if(!factionEntry)
|
|
return;
|
|
|
|
_player->GetReputationMgr().ModifyReputation(factionEntry, rep_change);
|
|
}
|
|
|
|
void Spell::EffectQuestComplete(SpellEffectIndex eff_idx)
|
|
{
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player *_player = (Player*)m_caster;
|
|
|
|
uint32 quest_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
_player->AreaExploredOrEventHappens(quest_id);
|
|
}
|
|
|
|
void Spell::EffectSelfResurrect(SpellEffectIndex eff_idx)
|
|
{
|
|
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[eff_idx];
|
|
}
|
|
// 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();
|
|
}
|
|
|
|
void Spell::EffectSkinning(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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 = creature->GetCreatureInfo()->GetRequiredLootSkill();
|
|
|
|
((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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
//TODO: research more ContactPoint/attack distance.
|
|
//3.666666 instead of ATTACK_DISTANCE(5.0f) in below seem to give more accurate result.
|
|
float x, y, z;
|
|
unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f);
|
|
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
((Creature *)unitTarget)->StopMoving();
|
|
|
|
// Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags
|
|
m_caster->MonsterMove(x, y, z, 1);
|
|
|
|
// not all charge effects used in negative spells
|
|
if (unitTarget != m_caster && !IsPositiveSpell(m_spellInfo->Id))
|
|
m_caster->Attack(unitTarget, true);
|
|
}
|
|
|
|
void Spell::EffectCharge2(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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;
|
|
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
((Creature *)unitTarget)->StopMoving();
|
|
}
|
|
else if (unitTarget && unitTarget != m_caster)
|
|
unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f);
|
|
else
|
|
return;
|
|
|
|
// Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags
|
|
m_caster->MonsterMove(x, y, z, 1);
|
|
|
|
// not all charge effects used in negative spells
|
|
if (unitTarget && unitTarget != m_caster && !IsPositiveSpell(m_spellInfo->Id))
|
|
m_caster->Attack(unitTarget, true);
|
|
}
|
|
|
|
void Spell::DoSummonCritter(SpellEffectIndex eff_idx, uint32 forceFaction)
|
|
{
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
Player* player = (Player*)m_caster;
|
|
|
|
uint32 pet_entry = m_spellInfo->EffectMiscValue[eff_idx];
|
|
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 = sObjectMgr.GeneratePetNumber();
|
|
if(!critter->Create(map->GenerateLocalLowGuid(HIGHGUID_PET), map, m_caster->GetPhaseMask(),
|
|
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());
|
|
critter->SetSummonPoint(x, y, z, m_caster->GetOrientation());
|
|
|
|
if(!critter->IsPositionValid())
|
|
{
|
|
sLog.outError("Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)",
|
|
critter->GetGUIDLow(), critter->GetEntry(), critter->GetPositionX(), critter->GetPositionY());
|
|
delete critter;
|
|
return;
|
|
}
|
|
|
|
critter->SetOwnerGUID(m_caster->GetGUID());
|
|
critter->SetCreatorGUID(m_caster->GetGUID());
|
|
|
|
critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
|
|
critter->setFaction(forceFaction ? forceFaction : m_caster->getFaction());
|
|
critter->AIM_Initialize();
|
|
critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter...
|
|
//critter->InitLevelupSpellsForLevel(); // none?
|
|
critter->SelectLevel(critter->GetCreatureInfo()); // some summoned creaters have different from 1 DB data for level/hp
|
|
critter->SetUInt32Value(UNIT_NPC_FLAGS, critter->GetCreatureInfo()->npcflag);
|
|
// some mini-pets have quests
|
|
|
|
// 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(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
|
|
unitTarget->KnockBackFrom(m_caster,float(m_spellInfo->EffectMiscValue[eff_idx])/10,float(damage)/10);
|
|
}
|
|
|
|
void Spell::EffectSendTaxi(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
((Player*)unitTarget)->ActivateTaxiPathTo(m_spellInfo->EffectMiscValue[eff_idx],m_spellInfo->Id);
|
|
}
|
|
|
|
void Spell::EffectPlayerPull(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
|
|
float dist = unitTarget->GetDistance2d(m_caster);
|
|
if (damage && dist > damage)
|
|
dist = float(damage);
|
|
|
|
unitTarget->KnockBackFrom(m_caster,-dist,float(m_spellInfo->EffectMiscValue[eff_idx])/10);
|
|
}
|
|
|
|
void Spell::EffectDispelMechanic(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
|
|
uint32 mechanic = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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(); -- action bar not removed at death and not required send at revive
|
|
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
|
|
}
|
|
|
|
void Spell::EffectSummonAllTotems(SpellEffectIndex eff_idx)
|
|
{
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
int32 start_button = ACTION_BUTTON_SHAMAN_TOTEMS_BAR + m_spellInfo->EffectMiscValue[eff_idx];
|
|
int32 amount_buttons = m_spellInfo->EffectMiscValueB[eff_idx];
|
|
|
|
for(int32 slot = 0; slot < amount_buttons; ++slot)
|
|
if (ActionButton const* actionButton = ((Player*)m_caster)->GetActionButton(start_button+slot))
|
|
if (actionButton->GetType()==ACTION_BUTTON_SPELL)
|
|
if (uint32 spell_id = actionButton->GetAction())
|
|
m_caster->CastSpell(unitTarget,spell_id,true);
|
|
}
|
|
|
|
void Spell::EffectDestroyAllTotems(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
int32 mana = 0;
|
|
for(int slot = 0; slot < MAX_TOTEM_SLOT; ++slot)
|
|
{
|
|
if (Totem* totem = m_caster->GetTotem(TotemSlot(slot)))
|
|
{
|
|
uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
|
|
if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id))
|
|
{
|
|
uint32 manacost = m_caster->GetCreateMana() * spellInfo->ManaCostPercentage / 100;
|
|
mana += manacost * damage / 100;
|
|
}
|
|
totem->UnSummon();
|
|
}
|
|
}
|
|
|
|
if (mana)
|
|
m_caster->CastCustomSpell(m_caster, 39104, &mana, NULL, NULL, true);
|
|
}
|
|
|
|
void Spell::EffectBreakPlayerTargeting (SpellEffectIndex /* eff_idx */)
|
|
{
|
|
if (!unitTarget)
|
|
return;
|
|
|
|
WorldPacket data(SMSG_CLEAR_TARGET, 8);
|
|
data << unitTarget->GetObjectGuid();
|
|
unitTarget->SendMessageToSet(&data, false);
|
|
}
|
|
|
|
void Spell::EffectDurabilityDamage(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
int32 slot = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
// 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(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
int32 slot = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
// 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(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if(!unitTarget)
|
|
return;
|
|
|
|
unitTarget->getThreatManager().modifyThreatPercent(m_caster, damage);
|
|
}
|
|
|
|
void Spell::EffectTransmitted(SpellEffectIndex eff_idx)
|
|
{
|
|
uint32 name_id = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
GameObjectInfo const* goinfo = ObjectMgr::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[eff_idx] && m_spellInfo->speed==0)
|
|
{
|
|
float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[eff_idx]));
|
|
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_f() * (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(sObjectMgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap,
|
|
m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
|
|
{
|
|
delete pGameObj;
|
|
return;
|
|
}
|
|
|
|
int32 duration = GetSpellDuration(m_spellInfo);
|
|
|
|
switch(goinfo->type)
|
|
{
|
|
case GAMEOBJECT_TYPE_FISHINGNODE:
|
|
{
|
|
m_caster->SetChannelObjectGUID(pGameObj->GetGUID());
|
|
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 = 0;
|
|
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*IN_MILLISECONDS + FISHING_BOBBER_READY_TIME*IN_MILLISECONDS;
|
|
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/IN_MILLISECONDS : 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");
|
|
//m_caster->AddGameObject(pGameObj);
|
|
//m_ObjToDel.push_back(pGameObj);
|
|
|
|
cMap->Add(pGameObj);
|
|
|
|
pGameObj->SummonLinkedTrapIfAny();
|
|
}
|
|
|
|
void Spell::EffectProspecting(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
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_BOOL_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::EffectMilling(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player* p_caster = (Player*)m_caster;
|
|
if(!itemTarget || !(itemTarget->GetProto()->BagFamily & BAG_FAMILY_MASK_HERBS))
|
|
return;
|
|
|
|
if(itemTarget->GetCount() < 5)
|
|
return;
|
|
|
|
if( sWorld.getConfig(CONFIG_BOOL_SKILL_MILLING))
|
|
{
|
|
uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_INSCRIPTION);
|
|
uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank;
|
|
p_caster->UpdateGatherSkill(SKILL_INSCRIPTION, SkillValue, reqSkillValue);
|
|
}
|
|
|
|
((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_MILLING);
|
|
}
|
|
|
|
void Spell::EffectSkill(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
DEBUG_LOG("WORLD: SkillEFFECT");
|
|
}
|
|
|
|
void Spell::EffectSpiritHeal(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
// TODO player can't see the heal-animation - he should respawn some ticks later
|
|
if (!unitTarget || unitTarget->isAlive())
|
|
return;
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
if (!unitTarget->IsInWorld())
|
|
return;
|
|
if (m_spellInfo->Id == 22012 && !unitTarget->HasAura(2584))
|
|
return;
|
|
|
|
((Player*)unitTarget)->ResurrectPlayer(1.0f);
|
|
((Player*)unitTarget)->SpawnCorpseBones();
|
|
}
|
|
|
|
// remove insignia spell effect
|
|
void Spell::EffectSkinPlayerCorpse(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
DEBUG_LOG("Effect: SkinPlayerCorpse");
|
|
if ( (m_caster->GetTypeId() != TYPEID_PLAYER) || (unitTarget->GetTypeId() != TYPEID_PLAYER) || (unitTarget->isAlive()) )
|
|
return;
|
|
|
|
((Player*)unitTarget)->RemovedInsignia( (Player*)m_caster );
|
|
}
|
|
|
|
void Spell::EffectStealBeneficialBuff(SpellEffectIndex eff_idx)
|
|
{
|
|
DEBUG_LOG("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[eff_idx]) );
|
|
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() && !(aur->GetSpellProto()->AttributesEx4 & SPELL_ATTR_EX4_NOT_STEALABLE))
|
|
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 << unitTarget->GetPackGUID(); // Victim GUID
|
|
data << 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::EffectKillCreditPersonal(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
((Player*)unitTarget)->KilledMonsterCredit(m_spellInfo->EffectMiscValue[eff_idx], 0);
|
|
}
|
|
|
|
void Spell::EffectKillCredit(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
((Player*)unitTarget)->RewardPlayerAndGroupAtEvent(m_spellInfo->EffectMiscValue[eff_idx], unitTarget);
|
|
}
|
|
|
|
void Spell::EffectQuestFail(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
((Player*)unitTarget)->FailQuest(m_spellInfo->EffectMiscValue[eff_idx]);
|
|
}
|
|
|
|
void Spell::EffectActivateRune(SpellEffectIndex eff_idx)
|
|
{
|
|
if(m_caster->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player *plr = (Player*)m_caster;
|
|
|
|
if(plr->getClass() != CLASS_DEATH_KNIGHT)
|
|
return;
|
|
|
|
for(uint32 j = 0; j < MAX_RUNES; ++j)
|
|
{
|
|
if(plr->GetRuneCooldown(j) && plr->GetCurrentRune(j) == RuneType(m_spellInfo->EffectMiscValue[eff_idx]))
|
|
{
|
|
plr->SetRuneCooldown(j, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spell::EffectTitanGrip(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER)
|
|
((Player*)unitTarget)->SetCanTitanGrip(true);
|
|
}
|
|
|
|
void Spell::EffectRenamePet(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT ||
|
|
!((Creature*)unitTarget)->isPet() || ((Pet*)unitTarget)->getPetType() != HUNTER_PET)
|
|
return;
|
|
|
|
unitTarget->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
|
|
}
|
|
|
|
void Spell::EffectPlayMusic(SpellEffectIndex eff_idx)
|
|
{
|
|
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
uint32 soundid = m_spellInfo->EffectMiscValue[eff_idx];
|
|
|
|
if (!sSoundEntriesStore.LookupEntry(soundid))
|
|
{
|
|
sLog.outError("EffectPlayMusic: Sound (Id: %u) not exist in spell %u.",soundid,m_spellInfo->Id);
|
|
return;
|
|
}
|
|
|
|
WorldPacket data(SMSG_PLAY_MUSIC, 4);
|
|
data << uint32(soundid);
|
|
((Player*)unitTarget)->GetSession()->SendPacket(&data);
|
|
}
|
|
|
|
void Spell::EffectSpecCount(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
((Player*)unitTarget)->UpdateSpecCount(damage);
|
|
}
|
|
|
|
void Spell::EffectActivateSpec(SpellEffectIndex /*eff_idx*/)
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
uint32 spec = damage-1;
|
|
|
|
((Player*)unitTarget)->ActivateSpec(spec);
|
|
}
|
|
|
|
void Spell::EffectBind(SpellEffectIndex eff_idx)
|
|
{
|
|
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player* player = (Player*)unitTarget;
|
|
|
|
uint32 area_id;
|
|
WorldLocation loc;
|
|
if (m_spellInfo->EffectImplicitTargetA[eff_idx] == TARGET_TABLE_X_Y_Z_COORDINATES ||
|
|
m_spellInfo->EffectImplicitTargetB[eff_idx] == TARGET_TABLE_X_Y_Z_COORDINATES)
|
|
{
|
|
SpellTargetPosition const* st = sSpellMgr.GetSpellTargetPosition(m_spellInfo->Id);
|
|
if (!st)
|
|
{
|
|
sLog.outError( "Spell::EffectBind - unknown Teleport coordinates for spell ID %u", m_spellInfo->Id );
|
|
return;
|
|
}
|
|
|
|
loc.mapid = st->target_mapId;
|
|
loc.coord_x = st->target_X;
|
|
loc.coord_y = st->target_Y;
|
|
loc.coord_z = st->target_Y;
|
|
loc.orientation = st->target_Orientation;
|
|
area_id = sMapMgr.GetAreaId(loc.mapid, loc.coord_x, loc.coord_y, loc.coord_z);
|
|
}
|
|
else
|
|
{
|
|
player->GetPosition(loc);
|
|
area_id = player->GetAreaId();
|
|
}
|
|
|
|
player->SetHomebindToLocation(loc,area_id);
|
|
|
|
// binding
|
|
WorldPacket data( SMSG_BINDPOINTUPDATE, (4+4+4+4+4) );
|
|
data << float(loc.coord_x);
|
|
data << float(loc.coord_y);
|
|
data << float(loc.coord_z);
|
|
data << uint32(loc.mapid);
|
|
data << uint32(area_id);
|
|
player->SendDirectMessage( &data );
|
|
|
|
DEBUG_LOG("New Home Position X is %f", loc.coord_x);
|
|
DEBUG_LOG("New Home Position Y is %f", loc.coord_y);
|
|
DEBUG_LOG("New Home Position Z is %f", loc.coord_z);
|
|
DEBUG_LOG("New Home MapId is %u", loc.mapid);
|
|
DEBUG_LOG("New Home AreaId is %u", area_id);
|
|
|
|
// zone update
|
|
data.Initialize(SMSG_PLAYERBOUND, 8+4);
|
|
data << uint64(player->GetGUID());
|
|
data << uint32(area_id);
|
|
player->SendDirectMessage( &data );
|
|
}
|
|
|
|
void Spell::EffectRestoreItemCharges( SpellEffectIndex eff_idx )
|
|
{
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player* player = (Player*)unitTarget;
|
|
|
|
ItemPrototype const* itemProto = ObjectMgr::GetItemPrototype(m_spellInfo->EffectItemType[eff_idx]);
|
|
if (!itemProto)
|
|
return;
|
|
|
|
// In case item from limited category recharge any from category, is this valid checked early in spell checks
|
|
Item* item;
|
|
if (itemProto->ItemLimitCategory)
|
|
item = player->GetItemByLimitedCategory(itemProto->ItemLimitCategory);
|
|
else
|
|
item = player->GetItemByEntry(m_spellInfo->EffectItemType[eff_idx]);
|
|
|
|
if (!item)
|
|
return;
|
|
|
|
item->RestoreCharges();
|
|
}
|
|
|
|
void Spell::EffectTeachTaxiNode( SpellEffectIndex eff_idx )
|
|
{
|
|
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
|
|
return;
|
|
|
|
Player* player = (Player*)unitTarget;
|
|
|
|
uint32 taxiNodeId = m_spellInfo->EffectMiscValue[eff_idx];
|
|
if (!sTaxiNodesStore.LookupEntry(taxiNodeId))
|
|
return;
|
|
|
|
if (player->m_taxi.SetTaximaskNode(taxiNodeId))
|
|
{
|
|
WorldPacket data(SMSG_NEW_TAXI_PATH, 0);
|
|
player->SendDirectMessage( &data );
|
|
|
|
data.Initialize( SMSG_TAXINODE_STATUS, 9 );
|
|
data << uint64( m_caster->GetGUID() );
|
|
data << uint8( 1 );
|
|
player->SendDirectMessage( &data );
|
|
}
|
|
}
|