/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* 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, see .
*/
#include "Spell.h"
#include "AccountMgr.h"
#include "AreaTrigger.h"
#include "AzeriteEmpoweredItem.h"
#include "AzeriteItem.h"
#include "Battleground.h"
#include "BattlegroundMgr.h"
#include "BattlePetMgr.h"
#include "CellImpl.h"
#include "CombatLogPackets.h"
#include "CombatPackets.h"
#include "Common.h"
#include "Conversation.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "CreatureTextMgr.h"
#include "DatabaseEnv.h"
#include "DB2Stores.h"
#include "DuelPackets.h"
#include "DynamicObject.h"
#include "GameEventSender.h"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "GameTime.h"
#include "Garrison.h"
#include "GossipDef.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
#include "Guild.h"
#include "InstanceScript.h"
#include "Item.h"
#include "Language.h"
#include "Log.h"
#include "Loot.h"
#include "LootMgr.h"
#include "Map.h"
#include "MiscPackets.h"
#include "MotionMaster.h"
#include "MoveSpline.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "OutdoorPvPMgr.h"
#include "PathGenerator.h"
#include "Pet.h"
#include "PhasingHandler.h"
#include "Player.h"
#include "ReputationMgr.h"
#include "RestMgr.h"
#include "SceneObject.h"
#include "ScriptMgr.h"
#include "SharedDefines.h"
#include "SkillExtraItems.h"
#include "SocialMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "SpellHistory.h"
#include "SpellMgr.h"
#include "SpellPackets.h"
#include "TalentPackets.h"
#include "TemporarySummon.h"
#include "Totem.h"
#include "TraitMgr.h"
#include "TraitPacketsCommon.h"
#include "Unit.h"
#include "Util.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
NonDefaultConstructible SpellEffectHandlers[TOTAL_SPELL_EFFECTS] =
{
&Spell::EffectNULL, // 0
&Spell::EffectInstaKill, // 1 SPELL_EFFECT_INSTAKILL
&Spell::EffectSchoolDMG, // 2 SPELL_EFFECT_SCHOOL_DAMAGE
&Spell::EffectDummy, // 3 SPELL_EFFECT_DUMMY
&Spell::EffectUnused, // 4 SPELL_EFFECT_PORTAL_TELEPORT unused
&Spell::EffectNULL, // 5 SPELL_EFFECT_5
&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::EffectNULL, // 12 SPELL_EFFECT_PORTAL
&Spell::EffectTeleportToReturnPoint, // 13 SPELL_EFFECT_TELEPORT_TO_RETURN_POINT
&Spell::EffectIncreaseCurrencyCap, // 14 SPELL_EFFECT_INCREASE_CURRENCY_CAP
&Spell::EffectTeleportUnitsWithVisualLoadingScreen, // 15 SPELL_EFFECT_TELEPORT_WITH_SPELL_VISUAL_KIT_LOADING_SCREEN
&Spell::EffectQuestComplete, // 16 SPELL_EFFECT_QUEST_COMPLETE
&Spell::EffectWeaponDmg, // 17 SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL
&Spell::EffectResurrect, // 18 SPELL_EFFECT_RESURRECT
&Spell::EffectAddExtraAttacks, // 19 SPELL_EFFECT_ADD_EXTRA_ATTACKS
&Spell::EffectUnused, // 20 SPELL_EFFECT_DODGE one spell: Dodge
&Spell::EffectUnused, // 21 SPELL_EFFECT_EVADE one spell: Evade (DND)
&Spell::EffectParry, // 22 SPELL_EFFECT_PARRY
&Spell::EffectBlock, // 23 SPELL_EFFECT_BLOCK one spell: Block
&Spell::EffectCreateItem, // 24 SPELL_EFFECT_CREATE_ITEM
&Spell::EffectUnused, // 25 SPELL_EFFECT_WEAPON
&Spell::EffectUnused, // 26 SPELL_EFFECT_DEFENSE one spell: Defense
&Spell::EffectPersistentAA, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA
&Spell::EffectSummonType, // 28 SPELL_EFFECT_SUMMON
&Spell::EffectLeap, // 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::EffectUnused, // 35 SPELL_EFFECT_APPLY_AREA_AURA_PARTY
&Spell::EffectLearnSpell, // 36 SPELL_EFFECT_LEARN_SPELL
&Spell::EffectUnused, // 37 SPELL_EFFECT_SPELL_DEFENSE one spell: SPELLDEFENSE (DND)
&Spell::EffectDispel, // 38 SPELL_EFFECT_DISPEL
&Spell::EffectUnused, // 39 SPELL_EFFECT_LANGUAGE
&Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD
&Spell::EffectJump, // 41 SPELL_EFFECT_JUMP
&Spell::EffectJumpDest, // 42 SPELL_EFFECT_JUMP_DEST
&Spell::EffectTeleUnitsFaceCaster, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER
&Spell::EffectLearnSkill, // 44 SPELL_EFFECT_SKILL_STEP
&Spell::EffectPlayMovie, // 45 SPELL_EFFECT_PLAY_MOVIE
&Spell::EffectUnused, // 46 SPELL_EFFECT_SPAWN clientside, unit appears as if it was just spawned
&Spell::EffectTradeSkill, // 47 SPELL_EFFECT_TRADE_SKILL
&Spell::EffectUnused, // 48 SPELL_EFFECT_STEALTH one spell: Base Stealth
&Spell::EffectUnused, // 49 SPELL_EFFECT_DETECT one spell: Detect
&Spell::EffectTransmitted, // 50 SPELL_EFFECT_TRANS_DOOR
&Spell::EffectUnused, // 51 SPELL_EFFECT_FORCE_CRITICAL_HIT unused
&Spell::EffectNULL, // 52 SPELL_EFFECT_SET_MAX_BATTLE_PET_COUNT
&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::EffectUnused, // 65 SPELL_EFFECT_APPLY_AREA_AURA_RAID
&Spell::EffectRechargeItem, // 66 SPELL_EFFECT_RECHARGE_ITEM
&Spell::EffectHealMaxHealth, // 67 SPELL_EFFECT_HEAL_MAX_HEALTH
&Spell::EffectInterruptCast, // 68 SPELL_EFFECT_INTERRUPT_CAST
&Spell::EffectDistract, // 69 SPELL_EFFECT_DISTRACT
&Spell::EffectNULL, // 70 SPELL_EFFECT_COMPLETE_AND_REWARD_WORLD_QUEST
&Spell::EffectPickPocket, // 71 SPELL_EFFECT_PICKPOCKET
&Spell::EffectAddFarsight, // 72 SPELL_EFFECT_ADD_FARSIGHT
&Spell::EffectUntrainTalents, // 73 SPELL_EFFECT_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::EffectNULL, // 80 SPELL_EFFECT_MODIFY_FOLLOWER_ITEM_LEVEL
&Spell::EffectNULL, // 81 SPELL_EFFECT_PUSH_ABILITY_TO_ACTION_BAR
&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::EffectGameObjectDamage, // 87 SPELL_EFFECT_GAMEOBJECT_DAMAGE
&Spell::EffectGameObjectRepair, // 88 SPELL_EFFECT_GAMEOBJECT_REPAIR
&Spell::EffectGameObjectSetDestructionState, // 89 SPELL_EFFECT_GAMEOBJECT_SET_DESTRUCTION_STATE
&Spell::EffectKillCreditPersonal, // 90 SPELL_EFFECT_KILL_CREDIT Kill credit but only for single person
&Spell::EffectNULL, // 91 SPELL_EFFECT_THREAT_ALL
&Spell::EffectEnchantHeldItem, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM
&Spell::EffectForceDeselect, // 93 SPELL_EFFECT_FORCE_DESELECT
&Spell::EffectSelfResurrect, // 94 SPELL_EFFECT_SELF_RESURRECT
&Spell::EffectSkinning, // 95 SPELL_EFFECT_SKINNING
&Spell::EffectCharge, // 96 SPELL_EFFECT_CHARGE
&Spell::EffectCastButtons, // 97 SPELL_EFFECT_CAST_BUTTON (totem bar since 3.2.2a)
&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::EffectNULL, //105 SPELL_EFFECT_SURVEY
&Spell::EffectChangeRaidMarker, //106 SPELL_EFFECT_CHANGE_RAID_MARKER
&Spell::EffectNULL, //107 SPELL_EFFECT_SHOW_CORPSE_LOOT
&Spell::EffectDispelMechanic, //108 SPELL_EFFECT_DISPEL_MECHANIC
&Spell::EffectResurrectPet, //109 SPELL_EFFECT_RESURRECT_PET
&Spell::EffectDestroyAllTotems, //110 SPELL_EFFECT_DESTROY_ALL_TOTEMS
&Spell::EffectDurabilityDamage, //111 SPELL_EFFECT_DURABILITY_DAMAGE
&Spell::EffectNULL, //112 SPELL_EFFECT_112
&Spell::EffectCancelConversation, //113 SPELL_EFFECT_CANCEL_CONVERSATION
&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::EffectUnused, //119 SPELL_EFFECT_APPLY_AREA_AURA_PET
&Spell::EffectTeleportGraveyard, //120 SPELL_EFFECT_TELEPORT_GRAVEYARD
&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::EffectPullTowards, //124 SPELL_EFFECT_PULL_TOWARDS
&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::EffectUnused, //128 SPELL_EFFECT_APPLY_AREA_AURA_FRIEND
&Spell::EffectUnused, //129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY
&Spell::EffectRedirectThreat, //130 SPELL_EFFECT_REDIRECT_THREAT
&Spell::EffectPlaySound, //131 SPELL_EFFECT_PLAY_SOUND sound id in misc value (SoundEntries.dbc)
&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::EffectEnergizePct, //137 SPELL_EFFECT_ENERGIZE_PCT
&Spell::EffectLeapBack, //138 SPELL_EFFECT_LEAP_BACK Leap back
&Spell::EffectQuestClear, //139 SPELL_EFFECT_CLEAR_QUEST Reset quest status (miscValue - quest ID)
&Spell::EffectForceCast, //140 SPELL_EFFECT_FORCE_CAST
&Spell::EffectForceCast, //141 SPELL_EFFECT_FORCE_CAST_WITH_VALUE
&Spell::EffectTriggerSpell, //142 SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE
&Spell::EffectUnused, //143 SPELL_EFFECT_APPLY_AREA_AURA_OWNER
&Spell::EffectKnockBack, //144 SPELL_EFFECT_KNOCK_BACK_DEST
&Spell::EffectPullTowardsDest, //145 SPELL_EFFECT_PULL_TOWARDS_DEST Black Hole Effect
&Spell::EffectNULL, //146 SPELL_EFFECT_RESTORE_GARRISON_TROOP_VITALITY
&Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail
&Spell::EffectTriggerMissileSpell, //148 SPELL_EFFECT_TRIGGER_MISSILE_SPELL_WITH_VALUE
&Spell::EffectChargeDest, //149 SPELL_EFFECT_CHARGE_DEST
&Spell::EffectQuestStart, //150 SPELL_EFFECT_QUEST_START
&Spell::EffectTriggerRitualOfSummoning, //151 SPELL_EFFECT_TRIGGER_SPELL_2
&Spell::EffectSummonRaFFriend, //152 SPELL_EFFECT_SUMMON_RAF_FRIEND summon Refer-a-Friend
&Spell::EffectCreateTamedPet, //153 SPELL_EFFECT_CREATE_TAMED_PET misc value is creature entry
&Spell::EffectDiscoverTaxi, //154 SPELL_EFFECT_DISCOVER_TAXI
&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::EffectForceCast, //160 SPELL_EFFECT_FORCE_CAST_2
&Spell::EffectNULL, //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_EFFECT_OBLITERATE_ITEM
&Spell::EffectRemoveAura, //164 SPELL_EFFECT_REMOVE_AURA
&Spell::EffectDamageFromMaxHealthPCT, //165 SPELL_EFFECT_DAMAGE_FROM_MAX_HEALTH_PCT
&Spell::EffectGiveCurrency, //166 SPELL_EFFECT_GIVE_CURRENCY
&Spell::EffectUpdatePlayerPhase, //167 SPELL_EFFECT_UPDATE_PLAYER_PHASE
&Spell::EffectNULL, //168 SPELL_EFFECT_ALLOW_CONTROL_PET
&Spell::EffectDestroyItem, //169 SPELL_EFFECT_DESTROY_ITEM
&Spell::EffectUpdateZoneAurasAndPhases, //170 SPELL_EFFECT_UPDATE_ZONE_AURAS_AND_PHASES
&Spell::EffectSummonPersonalGameObject, //171 SPELL_EFFECT_SUMMON_PERSONAL_GAMEOBJECT
&Spell::EffectResurrectWithAura, //172 SPELL_EFFECT_RESURRECT_WITH_AURA
&Spell::EffectUnlockGuildVaultTab, //173 SPELL_EFFECT_UNLOCK_GUILD_VAULT_TAB
&Spell::EffectApplyAura, //174 SPELL_EFFECT_APPLY_AURA_ON_PET
&Spell::EffectNULL, //175 SPELL_EFFECT_175
&Spell::EffectSanctuary, //176 SPELL_EFFECT_SANCTUARY_2
&Spell::EffectNULL, //177 SPELL_EFFECT_DESPAWN_PERSISTENT_AREA_AURA
&Spell::EffectUnused, //178 SPELL_EFFECT_178 unused
&Spell::EffectCreateAreaTrigger, //179 SPELL_EFFECT_CREATE_AREATRIGGER
&Spell::EffectNULL, //180 SPELL_EFFECT_UPDATE_AREATRIGGER
&Spell::EffectRemoveTalent, //181 SPELL_EFFECT_REMOVE_TALENT
&Spell::EffectNULL, //182 SPELL_EFFECT_DESPAWN_AREATRIGGER
&Spell::EffectNULL, //183 SPELL_EFFECT_183
&Spell::EffectNULL, //184 SPELL_EFFECT_REPUTATION
&Spell::EffectNULL, //185 SPELL_EFFECT_185
&Spell::EffectNULL, //186 SPELL_EFFECT_186
&Spell::EffectNULL, //187 SPELL_EFFECT_RANDOMIZE_ARCHAEOLOGY_DIGSITES
&Spell::EffectNULL, //188 SPELL_EFFECT_SUMMON_STABLED_PET_AS_GUARDIAN
&Spell::EffectNULL, //189 SPELL_EFFECT_LOOT
&Spell::EffectNULL, //190 SPELL_EFFECT_CHANGE_PARTY_MEMBERS
&Spell::EffectNULL, //191 SPELL_EFFECT_TELEPORT_TO_DIGSITE
&Spell::EffectUncageBattlePet, //192 SPELL_EFFECT_UNCAGE_BATTLEPET
&Spell::EffectNULL, //193 SPELL_EFFECT_START_PET_BATTLE
&Spell::EffectUnused, //194 SPELL_EFFECT_194
&Spell::EffectPlaySceneScriptPackage, //195 SPELL_EFFECT_PLAY_SCENE_SCRIPT_PACKAGE
&Spell::EffectCreateSceneObject, //196 SPELL_EFFECT_CREATE_SCENE_OBJECT
&Spell::EffectCreatePrivateSceneObject, //197 SPELL_EFFECT_CREATE_PERSONAL_SCENE_OBJECT
&Spell::EffectPlayScene, //198 SPELL_EFFECT_PLAY_SCENE
&Spell::EffectNULL, //199 SPELL_EFFECT_DESPAWN_SUMMON
&Spell::EffectHealBattlePetPct, //200 SPELL_EFFECT_HEAL_BATTLEPET_PCT
&Spell::EffectEnableBattlePets, //201 SPELL_EFFECT_ENABLE_BATTLE_PETS
&Spell::EffectUnused, //202 SPELL_EFFECT_APPLY_AREA_AURA_SUMMONS
&Spell::EffectRemoveAura, //203 SPELL_EFFECT_REMOVE_AURA_2
&Spell::EffectChangeBattlePetQuality, //204 SPELL_EFFECT_CHANGE_BATTLEPET_QUALITY
&Spell::EffectLaunchQuestChoice, //205 SPELL_EFFECT_LAUNCH_QUEST_CHOICE
&Spell::EffectNULL, //206 SPELL_EFFECT_ALTER_ITEM
&Spell::EffectNULL, //207 SPELL_EFFECT_LAUNCH_QUEST_TASK
&Spell::EffectNULL, //208 SPELL_EFFECT_SET_REPUTATION
&Spell::EffectUnused, //209 SPELL_EFFECT_209
&Spell::EffectLearnGarrisonBuilding, //210 SPELL_EFFECT_LEARN_GARRISON_BUILDING
&Spell::EffectNULL, //211 SPELL_EFFECT_LEARN_GARRISON_SPECIALIZATION
&Spell::EffectRemoveAuraBySpellLabel, //212 SPELL_EFFECT_REMOVE_AURA_BY_SPELL_LABEL
&Spell::EffectJumpDest, //213 SPELL_EFFECT_JUMP_DEST_2
&Spell::EffectCreateGarrison, //214 SPELL_EFFECT_CREATE_GARRISON
&Spell::EffectNULL, //215 SPELL_EFFECT_UPGRADE_CHARACTER_SPELLS
&Spell::EffectNULL, //216 SPELL_EFFECT_CREATE_SHIPMENT
&Spell::EffectNULL, //217 SPELL_EFFECT_UPGRADE_GARRISON
&Spell::EffectNULL, //218 SPELL_EFFECT_218
&Spell::EffectCreateConversation, //219 SPELL_EFFECT_CREATE_CONVERSATION
&Spell::EffectAddGarrisonFollower, //220 SPELL_EFFECT_ADD_GARRISON_FOLLOWER
&Spell::EffectNULL, //221 SPELL_EFFECT_ADD_GARRISON_MISSION
&Spell::EffectCreateHeirloomItem, //222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM
&Spell::EffectNULL, //223 SPELL_EFFECT_CHANGE_ITEM_BONUSES
&Spell::EffectActivateGarrisonBuilding, //224 SPELL_EFFECT_ACTIVATE_GARRISON_BUILDING
&Spell::EffectGrantBattlePetLevel, //225 SPELL_EFFECT_GRANT_BATTLEPET_LEVEL
&Spell::EffectNULL, //226 SPELL_EFFECT_TRIGGER_ACTION_SET
&Spell::EffectNULL, //227 SPELL_EFFECT_TELEPORT_TO_LFG_DUNGEON
&Spell::EffectNULL, //228 SPELL_EFFECT_228
&Spell::EffectNULL, //229 SPELL_EFFECT_SET_FOLLOWER_QUALITY
&Spell::EffectNULL, //230 SPELL_EFFECT_230
&Spell::EffectNULL, //231 SPELL_EFFECT_INCREASE_FOLLOWER_EXPERIENCE
&Spell::EffectNULL, //232 SPELL_EFFECT_REMOVE_PHASE
&Spell::EffectNULL, //233 SPELL_EFFECT_RANDOMIZE_FOLLOWER_ABILITIES
&Spell::EffectNULL, //234 SPELL_EFFECT_234
&Spell::EffectUnused, //235 SPELL_EFFECT_235
&Spell::EffectGiveExperience, //236 SPELL_EFFECT_GIVE_EXPERIENCE
&Spell::EffectGiveRestedExperience, //237 SPELL_EFFECT_GIVE_RESTED_EXPERIENCE_BONUS
&Spell::EffectNULL, //238 SPELL_EFFECT_INCREASE_SKILL
&Spell::EffectNULL, //239 SPELL_EFFECT_END_GARRISON_BUILDING_CONSTRUCTION
&Spell::EffectGiveArtifactPower, //240 SPELL_EFFECT_GIVE_ARTIFACT_POWER
&Spell::EffectUnused, //241 SPELL_EFFECT_241
&Spell::EffectGiveArtifactPowerNoBonus, //242 SPELL_EFFECT_GIVE_ARTIFACT_POWER_NO_BONUS
&Spell::EffectApplyEnchantIllusion, //243 SPELL_EFFECT_APPLY_ENCHANT_ILLUSION
&Spell::EffectNULL, //244 SPELL_EFFECT_LEARN_FOLLOWER_ABILITY
&Spell::EffectUpgradeHeirloom, //245 SPELL_EFFECT_UPGRADE_HEIRLOOM
&Spell::EffectNULL, //246 SPELL_EFFECT_FINISH_GARRISON_MISSION
&Spell::EffectNULL, //247 SPELL_EFFECT_ADD_GARRISON_MISSION_SET
&Spell::EffectNULL, //248 SPELL_EFFECT_FINISH_SHIPMENT
&Spell::EffectNULL, //249 SPELL_EFFECT_FORCE_EQUIP_ITEM
&Spell::EffectNULL, //250 SPELL_EFFECT_TAKE_SCREENSHOT
&Spell::EffectNULL, //251 SPELL_EFFECT_SET_GARRISON_CACHE_SIZE
&Spell::EffectTeleportUnits, //252 SPELL_EFFECT_TELEPORT_UNITS
&Spell::EffectGiveHonor, //253 SPELL_EFFECT_GIVE_HONOR
&Spell::EffectJumpCharge, //254 SPELL_EFFECT_JUMP_CHARGE
&Spell::EffectLearnTransmogSet, //255 SPELL_EFFECT_LEARN_TRANSMOG_SET
&Spell::EffectUnused, //256 SPELL_EFFECT_256
&Spell::EffectUnused, //257 SPELL_EFFECT_257
&Spell::EffectNULL, //258 SPELL_EFFECT_MODIFY_KEYSTONE
&Spell::EffectRespecAzeriteEmpoweredItem, //259 SPELL_EFFECT_RESPEC_AZERITE_EMPOWERED_ITEM
&Spell::EffectNULL, //260 SPELL_EFFECT_SUMMON_STABLED_PET
&Spell::EffectNULL, //261 SPELL_EFFECT_SCRAP_ITEM
&Spell::EffectUnused, //262 SPELL_EFFECT_262
&Spell::EffectNULL, //263 SPELL_EFFECT_REPAIR_ITEM
&Spell::EffectNULL, //264 SPELL_EFFECT_REMOVE_GEM
&Spell::EffectLearnAzeriteEssencePower, //265 SPELL_EFFECT_LEARN_AZERITE_ESSENCE_POWER
&Spell::EffectNULL, //266 SPELL_EFFECT_SET_ITEM_BONUS_LIST_GROUP_ENTRY
&Spell::EffectCreatePrivateConversation, //267 SPELL_EFFECT_CREATE_PRIVATE_CONVERSATION
&Spell::EffectNULL, //268 SPELL_EFFECT_APPLY_MOUNT_EQUIPMENT
&Spell::EffectNULL, //269 SPELL_EFFECT_INCREASE_ITEM_BONUS_LIST_GROUP_STEP
&Spell::EffectNULL, //270 SPELL_EFFECT_270
&Spell::EffectUnused, //271 SPELL_EFFECT_APPLY_AREA_AURA_PARTY_NONRANDOM
&Spell::EffectNULL, //272 SPELL_EFFECT_SET_COVENANT
&Spell::EffectNULL, //273 SPELL_EFFECT_CRAFT_RUNEFORGE_LEGENDARY
&Spell::EffectUnused, //274 SPELL_EFFECT_274
&Spell::EffectUnused, //275 SPELL_EFFECT_275
&Spell::EffectLearnTransmogIllusion, //276 SPELL_EFFECT_LEARN_TRANSMOG_ILLUSION
&Spell::EffectNULL, //277 SPELL_EFFECT_SET_CHROMIE_TIME
&Spell::EffectNULL, //278 SPELL_EFFECT_278
&Spell::EffectNULL, //279 SPELL_EFFECT_LEARN_GARR_TALENT
&Spell::EffectUnused, //280 SPELL_EFFECT_280
&Spell::EffectNULL, //281 SPELL_EFFECT_LEARN_SOULBIND_CONDUIT
&Spell::EffectNULL, //282 SPELL_EFFECT_CONVERT_ITEMS_TO_CURRENCY
&Spell::EffectNULL, //283 SPELL_EFFECT_COMPLETE_CAMPAIGN
&Spell::EffectSendChatMessage, //284 SPELL_EFFECT_SEND_CHAT_MESSAGE
&Spell::EffectNULL, //285 SPELL_EFFECT_MODIFY_KEYSTONE_2
&Spell::EffectGrantBattlePetExperience, //286 SPELL_EFFECT_GRANT_BATTLEPET_EXPERIENCE
&Spell::EffectNULL, //287 SPELL_EFFECT_SET_GARRISON_FOLLOWER_LEVEL
&Spell::EffectNULL, //288 SPELL_EFFECT_CRAFT_ITEM
&Spell::EffectModifyAuraStacks, //289 SPELL_EFFECT_MODIFY_AURA_STACKS
&Spell::EffectModifyCooldown, //290 SPELL_EFFECT_MODIFY_COOLDOWN
&Spell::EffectModifyCooldowns, //291 SPELL_EFFECT_MODIFY_COOLDOWNS
&Spell::EffectModifyCooldownsByCategory, //292 SPELL_EFFECT_MODIFY_COOLDOWNS_BY_CATEGORY
&Spell::EffectModifySpellCharges, //293 SPELL_EFFECT_MODIFY_CHARGES
&Spell::EffectNULL, //294 SPELL_EFFECT_CRAFT_LOOT
&Spell::EffectNULL, //295 SPELL_EFFECT_SALVAGE_ITEM
&Spell::EffectNULL, //296 SPELL_EFFECT_CRAFT_SALVAGE_ITEM
&Spell::EffectNULL, //297 SPELL_EFFECT_RECRAFT_ITEM
&Spell::EffectNULL, //298 SPELL_EFFECT_CANCEL_ALL_PRIVATE_CONVERSATIONS
&Spell::EffectNULL, //299 SPELL_EFFECT_299
&Spell::EffectUnused, //300 SPELL_EFFECT_300
&Spell::EffectNULL, //301 SPELL_EFFECT_CRAFT_ENCHANT
&Spell::EffectNULL, //302 SPELL_EFFECT_GATHERING
&Spell::EffectCreateTraitTreeConfig, //303 SPELL_EFFECT_CREATE_TRAIT_TREE_CONFIG
&Spell::EffectChangeActiveCombatTraitConfig, //304 SPELL_EFFECT_CHANGE_ACTIVE_COMBAT_TRAIT_CONFIG
&Spell::EffectNULL, //305 SPELL_EFFECT_305
&Spell::EffectNULL, //306 SPELL_EFFECT_306
&Spell::EffectNULL, //307 SPELL_EFFECT_307
&Spell::EffectNULL, //308 SPELL_EFFECT_CANCEL_PRELOAD_WORLD
&Spell::EffectNULL, //309 SPELL_EFFECT_PRELOAD_WORLD
&Spell::EffectNULL, //310 SPELL_EFFECT_310
&Spell::EffectNULL, //311 SPELL_EFFECT_ENSURE_WORLD_LOADED
&Spell::EffectNULL, //312 SPELL_EFFECT_312
&Spell::EffectNULL, //313 SPELL_EFFECT_CHANGE_ITEM_BONUSES_2
&Spell::EffectNULL, //314 SPELL_EFFECT_ADD_SOCKET_BONUS
};
void Spell::EffectNULL()
{
TC_LOG_DEBUG("spells", "WORLD: Spell Effect DUMMY");
}
void Spell::EffectUnused()
{
// NOT USED BY ANY SPELL OR USELESS OR IMPLEMENTED IN DIFFERENT WAY IN TRINITY
}
void Spell::EffectResurrectNew()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!m_corpseTarget && !unitTarget)
return;
Player* player = nullptr;
if (m_corpseTarget)
player = ObjectAccessor::FindPlayer(m_corpseTarget->GetOwnerGUID());
else if (unitTarget)
player = unitTarget->ToPlayer();
if (!player || player->IsAlive() || !player->IsInWorld())
return;
if (player->IsResurrectRequested()) // already have one active request
return;
uint32 health = damage;
uint32 mana = effectInfo->MiscValue;
ExecuteLogEffectResurrect(SpellEffectName(effectInfo->Effect), player);
player->SetResurrectRequestData(m_caster, health, mana, 0);
SendResurrectRequest(player);
}
void Spell::EffectInstaKill()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsAlive())
return;
if (unitTarget->GetTypeId() == TYPEID_PLAYER)
if (unitTarget->ToPlayer()->GetCommandStatus(CHEAT_GOD))
return;
if (m_caster == unitTarget) // prevent interrupt message
finish();
WorldPackets::CombatLog::SpellInstakillLog data;
data.Target = unitTarget->GetGUID();
data.Caster = m_caster->GetGUID();
data.SpellID = m_spellInfo->Id;
m_caster->SendMessageToSet(data.Write(), true);
Unit::Kill(GetUnitCasterForEffectHandlers(), unitTarget, false);
}
void Spell::EffectEnvironmentalDMG()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsAlive())
return;
// CalcAbsorbResist already in Player::EnvironmentalDamage
if (unitTarget->GetTypeId() == TYPEID_PLAYER)
unitTarget->ToPlayer()->EnvironmentalDamage(DAMAGE_FIRE, damage);
else
{
Unit* unitCaster = GetUnitCasterForEffectHandlers();
DamageInfo damageInfo(unitCaster, unitTarget, damage, m_spellInfo, m_spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE, BASE_ATTACK);
Unit::CalcAbsorbResist(damageInfo);
SpellNonMeleeDamage log(unitCaster, unitTarget, m_spellInfo, m_SpellVisual, m_spellInfo->GetSchoolMask(), m_castId);
log.damage = damageInfo.GetDamage();
log.originalDamage = damage;
log.absorb = damageInfo.GetAbsorb();
log.resist = damageInfo.GetResist();
if (unitCaster)
unitCaster->SendSpellNonMeleeDamageLog(&log);
}
}
void Spell::EffectSchoolDMG()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
return;
if (unitTarget && unitTarget->IsAlive())
{
bool apply_direct_bonus = true;
// Meteor like spells (divided damage to targets)
if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_SHARE_DAMAGE))
{
// divide to all targets
if (int64 count = GetUnitTargetCountForEffect(SpellEffIndex(effectInfo->EffectIndex)))
damage /= count;
}
Unit* unitCaster = GetUnitCasterForEffectHandlers();
switch (m_spellInfo->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
{
break;
}
case SPELLFAMILY_WARRIOR:
{
break;
}
case SPELLFAMILY_WARLOCK:
{
break;
}
case SPELLFAMILY_PRIEST:
{
break;
}
case SPELLFAMILY_DRUID:
{
break;
}
case SPELLFAMILY_DEATHKNIGHT:
{
break;
}
}
if (unitCaster && apply_direct_bonus)
{
uint32 bonus = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, *effectInfo);
damage = bonus + uint32(bonus * variance);
damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE);
}
m_damage += damage;
}
}
void Spell::EffectDummy()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget && !gameObjTarget && !itemTarget && !m_corpseTarget)
return;
// pet auras
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
if (PetAura const* petSpell = sSpellMgr->GetPetAura(m_spellInfo->Id, effectInfo->EffectIndex))
{
m_caster->ToPlayer()->AddPetAura(petSpell);
return;
}
}
// normal DB scripted effect
TC_LOG_DEBUG("spells", "Spell ScriptStart spellid {} in EffectDummy({})", m_spellInfo->Id, effectInfo->EffectIndex);
m_caster->GetMap()->ScriptsStart(sSpellScripts, uint32(m_spellInfo->Id | (effectInfo->EffectIndex << 24)), m_caster, unitTarget);
}
void Spell::EffectTriggerSpell()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET
&& effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH)
return;
uint32 triggered_spell_id = effectInfo->TriggerSpell;
/// @todo move those to spell scripts
if (effectInfo->Effect == SPELL_EFFECT_TRIGGER_SPELL
&& effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
{
// special cases
switch (triggered_spell_id)
{
// Demonic Empowerment -- succubus
case 54437:
{
unitTarget->RemoveMovementImpairingAuras(true);
unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STALKED);
unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STUN);
// Cast Lesser Invisibility
unitTarget->CastSpell(unitTarget, 7870, this);
return;
}
// Brittle Armor - (need add max stack of 24575 Brittle Armor)
case 29284:
{
// Brittle Armor
SpellInfo const* spell = sSpellMgr->GetSpellInfo(24575, GetCastDifficulty());
if (!spell)
return;
for (uint32 j = 0; j < spell->StackAmount; ++j)
m_caster->CastSpell(unitTarget, spell->Id, this);
return;
}
// Mercurial Shield - (need add max stack of 26464 Mercurial Shield)
case 29286:
{
// Mercurial Shield
SpellInfo const* spell = sSpellMgr->GetSpellInfo(26464, GetCastDifficulty());
if (!spell)
return;
for (uint32 j = 0; j < spell->StackAmount; ++j)
m_caster->CastSpell(unitTarget, spell->Id, this);
return;
}
}
}
if (triggered_spell_id == 0)
{
TC_LOG_WARN("spells.effect.nospell", "Spell::EffectTriggerSpell: Spell {} [EffectIndex: {}] does not have triggered spell.", m_spellInfo->Id, effectInfo->EffectIndex);
return;
}
// normal case
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(triggered_spell_id, GetCastDifficulty());
if (!spellInfo)
{
TC_LOG_ERROR("spells.effect.nospell", "Spell::EffectTriggerSpell spell {} tried to trigger unknown spell {}", m_spellInfo->Id, triggered_spell_id);
return;
}
SpellCastTargets targets;
if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
{
if (!spellInfo->NeedsToBeTriggeredByCaster(m_spellInfo))
return;
targets.SetUnitTarget(unitTarget);
}
else //if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH)
{
if (spellInfo->NeedsToBeTriggeredByCaster(m_spellInfo) && (effectInfo->GetProvidedTargetMask() & TARGET_FLAG_UNIT_MASK))
return;
if (spellInfo->GetExplicitTargetMask() & TARGET_FLAG_DEST_LOCATION)
targets.SetDst(m_targets);
if (Unit* target = m_targets.GetUnitTarget())
targets.SetUnitTarget(target);
else
{
if (Unit* unit = m_caster->ToUnit())
targets.SetUnitTarget(unit);
else if (GameObject* go = m_caster->ToGameObject())
targets.SetGOTarget(go);
}
}
Milliseconds delay = 0ms;
if (effectInfo->Effect == SPELL_EFFECT_TRIGGER_SPELL)
delay = Milliseconds(effectInfo->MiscValue);
m_caster->m_Events.AddEventAtOffset([caster = m_caster, targets, originalCaster = m_originalCasterGUID, castItemGuid = m_castItemGUID, originalCastId = m_castId,
spellEffectInfo = effectInfo, value = damage, itemLevel = m_castItemLevel]() mutable
{
targets.Update(caster); // refresh pointers stored in targets
// original caster guid only for GO cast
CastSpellExtraArgs args(TRIGGERED_FULL_MASK);
args.SetOriginalCaster(originalCaster);
args.OriginalCastId = originalCastId;
args.OriginalCastItemLevel = itemLevel;
if (!castItemGuid.IsEmpty() && sSpellMgr->AssertSpellInfo(spellEffectInfo->TriggerSpell, caster->GetMap()->GetDifficultyID())->HasAttribute(SPELL_ATTR2_RETAIN_ITEM_CAST))
if (Player const* triggeringAuraCaster = Object::ToPlayer(caster))
args.CastItem = triggeringAuraCaster->GetItemByGuid(castItemGuid);
// set basepoints for trigger with value effect
if (spellEffectInfo->Effect == SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE)
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), value);
caster->CastSpell(std::move(targets), spellEffectInfo->TriggerSpell, args);
}, delay);
}
void Spell::EffectTriggerMissileSpell()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET
&& effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
uint32 triggered_spell_id = effectInfo->TriggerSpell;
if (triggered_spell_id == 0)
{
TC_LOG_WARN("spells.effect.nospell", "Spell::EffectTriggerMissileSpell: Spell {} [EffectIndex: {}] does not have triggered spell.", m_spellInfo->Id, effectInfo->EffectIndex);
return;
}
// normal case
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(triggered_spell_id, GetCastDifficulty());
if (!spellInfo)
{
TC_LOG_ERROR("spells.effect.nospell", "Spell::EffectTriggerMissileSpell spell {} tried to trigger unknown spell {}.", m_spellInfo->Id, triggered_spell_id);
return;
}
SpellCastTargets targets;
if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET)
{
if (!spellInfo->NeedsToBeTriggeredByCaster(m_spellInfo))
return;
targets.SetUnitTarget(unitTarget);
}
else //if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT)
{
if (spellInfo->NeedsToBeTriggeredByCaster(m_spellInfo) && (effectInfo->GetProvidedTargetMask() & TARGET_FLAG_UNIT_MASK))
return;
if (spellInfo->GetExplicitTargetMask() & TARGET_FLAG_DEST_LOCATION)
targets.SetDst(m_targets);
if (Unit* unit = m_caster->ToUnit())
targets.SetUnitTarget(unit);
else if (GameObject* go = m_caster->ToGameObject())
targets.SetGOTarget(go);
}
CastSpellExtraArgs args(TRIGGERED_FULL_MASK);
args.SetOriginalCaster(m_originalCasterGUID);
args.SetTriggeringSpell(this);
// set basepoints for trigger with value effect
if (effectInfo->Effect == SPELL_EFFECT_TRIGGER_MISSILE_SPELL_WITH_VALUE)
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), damage);
// original caster guid only for GO cast
m_caster->CastSpell(std::move(targets), spellInfo->Id, args);
}
void Spell::EffectForceCast()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
uint32 triggered_spell_id = effectInfo->TriggerSpell;
if (triggered_spell_id == 0)
{
TC_LOG_WARN("spells.effect.nospell", "Spell::EffectForceCast: Spell {} [EffectIndex: {}] does not have triggered spell.", m_spellInfo->Id, effectInfo->EffectIndex);
return;
}
// normal case
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(triggered_spell_id, GetCastDifficulty());
if (!spellInfo)
{
TC_LOG_ERROR("spells.effect.nospell", "Spell::EffectForceCast of spell {}: triggering unknown spell id {}.", m_spellInfo->Id, triggered_spell_id);
return;
}
if (effectInfo->Effect == SPELL_EFFECT_FORCE_CAST && damage)
{
switch (m_spellInfo->Id)
{
case 52588: // Skeletal Gryphon Escape
case 48598: // Ride Flamebringer Cue
unitTarget->RemoveAura(damage);
break;
case 52463: // Hide In Mine Car
case 52349: // Overtake
{
CastSpellExtraArgs args(TRIGGERED_FULL_MASK);
args.SetOriginalCaster(m_originalCasterGUID);
args.SetTriggeringSpell(this);
args.AddSpellMod(SPELLVALUE_BASE_POINT0, damage);
unitTarget->CastSpell(unitTarget, spellInfo->Id, args);
return;
}
}
}
switch (spellInfo->Id)
{
case 72298: // Malleable Goo Summon
unitTarget->CastSpell(unitTarget, spellInfo->Id, CastSpellExtraArgs(TRIGGERED_FULL_MASK)
.SetOriginalCaster(m_originalCasterGUID)
.SetTriggeringSpell(this));
return;
}
CastSpellExtraArgs args(TRIGGERED_FULL_MASK);
args.SetTriggeringSpell(this);
if (effectInfo->Effect == SPELL_EFFECT_FORCE_CAST_WITH_VALUE)
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), damage);
unitTarget->CastSpell(m_caster, spellInfo->Id, args);
}
void Spell::EffectTriggerRitualOfSummoning()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
uint32 triggered_spell_id = effectInfo->TriggerSpell;
if (triggered_spell_id == 0)
{
TC_LOG_WARN("spells.effect.nospell", "Spell::EffectTriggerRitualOfSummoning: Spell {} [EffectIndex: {}] does not have triggered spell.", m_spellInfo->Id, effectInfo->EffectIndex);
return;
}
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(triggered_spell_id, GetCastDifficulty());
if (!spellInfo)
{
TC_LOG_ERROR("spells.effect.nospell", "EffectTriggerRitualOfSummoning of spell {}: triggering unknown spell id {}.", m_spellInfo->Id, triggered_spell_id);
return;
}
finish();
m_caster->CastSpell(nullptr, spellInfo->Id, CastSpellExtraArgs()
.SetTriggeringSpell(this));
}
void Spell::CalculateJumpSpeeds(SpellEffectInfo const* effInfo, float dist, float& speedXY, float& speedZ)
{
Unit* unitCaster = GetUnitCasterForEffectHandlers();
ASSERT(unitCaster);
float multiplier = effInfo->Amplitude;
if (multiplier <= 0.0f)
multiplier = 1.0f;
float minHeight = effInfo->MiscValue ? effInfo->MiscValue / 10.0f : 0.5f; // Lower bound is blizzlike
float maxHeight = effInfo->MiscValueB ? effInfo->MiscValueB / 10.0f : 1000.0f; // Upper bound is unknown
unitCaster->GetMotionMaster()->CalculateJumpSpeeds(dist, MOVE_RUN, multiplier, minHeight, maxHeight, speedXY, speedZ);
}
void Spell::EffectJump()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (unitCaster->IsInFlight())
return;
if (!unitTarget)
return;
float speedXY, speedZ;
CalculateJumpSpeeds(effectInfo, unitCaster->GetExactDist2d(unitTarget), speedXY, speedZ);
JumpArrivalCastArgs arrivalCast;
arrivalCast.SpellId = effectInfo->TriggerSpell;
arrivalCast.Target = unitTarget->GetGUID();
unitCaster->GetMotionMaster()->MoveJump(*unitTarget, speedXY, speedZ, EVENT_JUMP, false, &arrivalCast);
}
void Spell::EffectJumpDest()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (unitCaster->IsInFlight())
return;
if (!m_targets.HasDst())
return;
float speedXY, speedZ;
CalculateJumpSpeeds(effectInfo, unitCaster->GetExactDist2d(destTarget), speedXY, speedZ);
JumpArrivalCastArgs arrivalCast;
arrivalCast.SpellId = effectInfo->TriggerSpell;
unitCaster->GetMotionMaster()->MoveJump(*destTarget, speedXY, speedZ, EVENT_JUMP, !m_targets.GetObjectTargetGUID().IsEmpty(), &arrivalCast);
}
TeleportToOptions GetTeleportOptions(WorldObject const* caster, Unit const* unitTarget, SpellDestination const& targetDest)
{
TeleportToOptions options = TELE_TO_NONE;
if (caster == unitTarget)
options |= TELE_TO_SPELL;
if (targetDest._position.GetMapId() == unitTarget->GetMapId())
{
options |= TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET;
if (unitTarget->GetTransGUID() == targetDest._transportGUID)
options |= TELE_TO_NOT_LEAVE_TRANSPORT;
}
return options;
}
void Spell::EffectTeleportUnits()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->IsInFlight())
return;
// If not exist data for dest location - return
if (!m_targets.HasDst())
{
TC_LOG_ERROR("spells", "Spell::EffectTeleportUnits - does not have a destination for spellId {}.", m_spellInfo->Id);
return;
}
// Init dest coordinates
WorldLocation targetDest(*destTarget);
if (targetDest.GetMapId() == MAPID_INVALID)
targetDest.m_mapId = unitTarget->GetMapId();
if (!targetDest.GetOrientation() && m_targets.GetUnitTarget())
targetDest.SetOrientation(m_targets.GetUnitTarget()->GetOrientation());
Player* player = unitTarget->ToPlayer();
// Custom loading screen
if (player)
{
if (uint32 customLoadingScreenId = effectInfo->MiscValue)
player->SendDirectMessage(WorldPackets::Spells::CustomLoadScreen(m_spellInfo->Id, customLoadingScreenId).Write());
TeleportToOptions options = GetTeleportOptions(m_caster, unitTarget, m_destTargets[effectInfo->EffectIndex]);
player->TeleportTo(targetDest, options);
}
else if (targetDest.GetMapId() == unitTarget->GetMapId())
unitTarget->NearTeleportTo(targetDest, unitTarget == m_caster);
else
TC_LOG_ERROR("spells", "Spell::EffectTeleportUnits - spellId {} attempted to teleport creature to a different map.", m_spellInfo->Id);
}
class DelayedSpellTeleportEvent : public BasicEvent
{
public:
explicit DelayedSpellTeleportEvent(Unit* target, WorldLocation const& targetDest, TeleportToOptions options, uint32 spellId)
: _target(target), _targetDest(targetDest), _options(options), _spellId(spellId){ }
bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override
{
if (Player* player = _target->ToPlayer())
player->TeleportTo(_targetDest, _options);
else if (_targetDest.GetMapId() == _target->GetMapId())
_target->NearTeleportTo(_targetDest, (_options & TELE_TO_SPELL) != TELE_TO_NONE);
else
TC_LOG_ERROR("spells", "Spell::EffectTeleportUnitsWithVisualLoadingScreen - spellId {} attempted to teleport creature to a different map.", _spellId);
return true;
}
private:
Unit* _target;
WorldLocation _targetDest;
TeleportToOptions _options;
uint32 _spellId;
};
void Spell::EffectTeleportUnitsWithVisualLoadingScreen()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
// If not exist data for dest location - return
if (!m_targets.HasDst())
{
TC_LOG_ERROR("spells", "Spell::EffectTeleportUnitsWithVisualLoadingScreen - does not have a destination for spellId {}.", m_spellInfo->Id);
return;
}
// Init dest coordinates
WorldLocation targetDest(*destTarget);
if (targetDest.GetMapId() == MAPID_INVALID)
targetDest.m_mapId = unitTarget->GetMapId();
if (!targetDest.GetOrientation() && m_targets.GetUnitTarget())
targetDest.SetOrientation(m_targets.GetUnitTarget()->GetOrientation());
if (effectInfo->MiscValueB)
if (Player* playerTarget = unitTarget->ToPlayer())
playerTarget->SendDirectMessage(WorldPackets::Spells::SpellVisualLoadScreen(effectInfo->MiscValueB, effectInfo->MiscValue).Write());
TeleportToOptions options = GetTeleportOptions(m_caster, unitTarget, m_destTargets[effectInfo->EffectIndex]);
unitTarget->m_Events.AddEventAtOffset(new DelayedSpellTeleportEvent(unitTarget, targetDest, options, m_spellInfo->Id),
Milliseconds(effectInfo->MiscValue));
}
void Spell::EffectApplyAura()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!_spellAura || !unitTarget)
return;
// register target/effect on aura
AuraApplication* aurApp = _spellAura->GetApplicationOfTarget(unitTarget->GetGUID());
if (!aurApp)
aurApp = unitTarget->_CreateAuraApplication(_spellAura, 1 << effectInfo->EffectIndex);
else
aurApp->UpdateApplyEffectMask(aurApp->GetEffectsToApply() | 1 << effectInfo->EffectIndex, false);
}
void Spell::EffectUnlearnSpecialization()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
uint32 spellToUnlearn = effectInfo->TriggerSpell;
player->RemoveSpell(spellToUnlearn);
TC_LOG_DEBUG("spells", "Spell: Player {} has unlearned spell {} from Npc {}", player->GetGUID().ToString(), spellToUnlearn, m_caster->GetGUID().ToString());
}
void Spell::EffectPowerDrain()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (effectInfo->MiscValue < 0 || effectInfo->MiscValue >= int8(MAX_POWERS))
return;
Powers powerType = Powers(effectInfo->MiscValue);
if (!unitTarget || !unitTarget->IsAlive() || unitTarget->GetPowerType() != powerType || damage < 0)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
// add spell damage bonus
if (unitCaster)
{
uint32 bonus = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, *effectInfo);
damage = bonus + uint32(bonus * variance);
damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE);
}
int32 newDamage = -(unitTarget->ModifyPower(powerType, -damage));
// Don't restore from self drain
float gainMultiplier = 0.f;
if (unitCaster && unitCaster != unitTarget)
{
gainMultiplier = effectInfo->CalcValueMultiplier(unitCaster, this);
int32 const gain = int32(newDamage * gainMultiplier);
unitCaster->EnergizeBySpell(unitCaster, m_spellInfo, gain, powerType);
}
ExecuteLogEffectTakeTargetPower(SpellEffectName(effectInfo->Effect), unitTarget, powerType, newDamage, gainMultiplier);
}
void Spell::EffectSendEvent()
{
// we do not handle a flag dropping or clicking on flag in battleground by sendevent system
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET
&& effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
WorldObject* target = nullptr;
// call events for object target if present
if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET)
{
if (unitTarget)
target = unitTarget;
else if (gameObjTarget)
target = gameObjTarget;
else if (m_corpseTarget)
target = m_corpseTarget;
}
else // if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT)
{
// let's prevent executing effect handler twice in case when spell effect is capable of targeting an object
// this check was requested by scripters, but it has some downsides:
// now it's impossible to script (using sEventScripts) a cast which misses all targets
// or to have an ability to script the moment spell hits dest (in a case when there are object targets present)
if (effectInfo->GetProvidedTargetMask() & (TARGET_FLAG_UNIT_MASK | TARGET_FLAG_GAMEOBJECT_MASK))
return;
// some spells have no target entries in dbc and they use focus target
if (focusObject)
target = focusObject;
/// @todo there should be a possibility to pass dest target to event script
}
TC_LOG_DEBUG("spells", "Spell ScriptStart {} for spellid {} in EffectSendEvent ", effectInfo->MiscValue, m_spellInfo->Id);
GameEvents::Trigger(effectInfo->MiscValue, m_caster, target);
}
void Spell::EffectPowerBurn()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (effectInfo->MiscValue < 0 || effectInfo->MiscValue >= int8(MAX_POWERS))
return;
Powers powerType = Powers(effectInfo->MiscValue);
if (!unitTarget || !unitTarget->IsAlive() || unitTarget->GetPowerType() != powerType || damage < 0)
return;
int32 newDamage = -(unitTarget->ModifyPower(powerType, -damage));
// NO - Not a typo - EffectPowerBurn uses effect value multiplier - not effect damage multiplier
float dmgMultiplier = effectInfo->CalcValueMultiplier(GetUnitCasterForEffectHandlers(), this);
// add log data before multiplication (need power amount, not damage)
ExecuteLogEffectTakeTargetPower(SpellEffectName(effectInfo->Effect), unitTarget, powerType, newDamage, 0.0f);
newDamage = int32(newDamage * dmgMultiplier);
m_damage += newDamage;
}
void Spell::EffectHeal()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
return;
if (!unitTarget || !unitTarget->IsAlive() || damage < 0)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
// Skip if m_originalCaster not available
if (!unitCaster)
return;
int32 addhealth = damage;
// Vessel of the Naaru (Vial of the Sunwell trinket)
///@todo: move this to scripts
if (m_spellInfo->Id == 45064)
{
// Amount of heal - depends from stacked Holy Energy
int32 damageAmount = 0;
if (AuraEffect const* aurEff = unitCaster->GetAuraEffect(45062, 0))
{
damageAmount += aurEff->GetAmount();
unitCaster->RemoveAurasDueToSpell(45062);
}
addhealth += damageAmount;
}
// Death Pact - return pct of max health to caster
else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00080000)
addhealth = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, int32(unitCaster->CountPctFromMaxHealth(damage)), HEAL, *effectInfo);
else
{
uint32 bonus = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, *effectInfo);
addhealth = bonus + uint32(bonus * variance);
}
addhealth = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, addhealth, HEAL);
// Remove Grievious bite if fully healed
if (unitTarget->HasAura(48920) && (unitTarget->GetHealth() + addhealth >= unitTarget->GetMaxHealth()))
unitTarget->RemoveAura(48920);
m_healing += addhealth;
}
void Spell::EffectHealPct()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsAlive() || damage < 0)
return;
uint32 heal = unitTarget->CountPctFromMaxHealth(damage);
if (Unit* unitCaster = GetUnitCasterForEffectHandlers())
{
heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, *effectInfo);
heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL);
}
m_healing += heal;
}
void Spell::EffectHealMechanical()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsAlive() || damage < 0)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
uint32 heal = damage;
if (unitCaster)
heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, *effectInfo);
heal += uint32(heal * variance);
if (unitCaster)
heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL);
m_healing += heal;
}
void Spell::EffectHealthLeech()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsAlive() || damage < 0)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
uint32 bonus = 0;
if (unitCaster)
bonus = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, *effectInfo);
damage = bonus + uint32(bonus * variance);
if (unitCaster)
damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE);
TC_LOG_DEBUG("spells", "HealthLeech :{}", damage);
float healMultiplier = effectInfo->CalcValueMultiplier(unitCaster, this);
m_damage += damage;
DamageInfo damageInfo(unitCaster, unitTarget, damage, m_spellInfo, m_spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE, BASE_ATTACK);
Unit::CalcAbsorbResist(damageInfo);
uint32 const absorb = damageInfo.GetAbsorb();
damage -= absorb;
// get max possible damage, don't count overkill for heal
uint32 healthGain = uint32(-unitTarget->GetHealthGain(-damage) * healMultiplier);
if (unitCaster && unitCaster->IsAlive())
{
healthGain = unitCaster->SpellHealingBonusDone(unitCaster, m_spellInfo, healthGain, HEAL, *effectInfo);
healthGain = unitCaster->SpellHealingBonusTaken(unitCaster, m_spellInfo, healthGain, HEAL);
HealInfo healInfo(unitCaster, unitCaster, healthGain, m_spellInfo, m_spellSchoolMask);
unitCaster->HealBySpell(healInfo);
}
}
void Spell::DoCreateItem(uint32 itemId, ItemContext context /*= ItemContext::NONE*/, std::vector const& bonusListIDs /*= std::vector()*/)
{
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
uint32 newitemid = itemId;
ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(newitemid);
if (!pProto)
{
player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr);
return;
}
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();
/* == gem perfection handling == */
// this is bad, should be done using spell_loot_template (and conditions)
// the chance of getting a perfect result
float perfectCreateChance = 0.0f;
// the resulting perfect item if successful
uint32 perfectItemType = itemId;
// get perfection capability and chance
if (CanCreatePerfectItem(player, m_spellInfo->Id, perfectCreateChance, perfectItemType))
if (roll_chance_f(perfectCreateChance)) // if the roll succeeds...
newitemid = perfectItemType; // the perfect item replaces the regular one
/* == gem perfection handling over == */
/* == profession specialization handling == */
// 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;
/* == profession specialization handling over == */
// can the player store the new item?
ItemPosCountVec dest;
uint32 no_space = 0;
InventoryResult 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_INV_FULL || msg == EQUIP_ERR_ITEM_MAX_COUNT)
num_to_add -= no_space;
else
{
// if not created by another reason from full inventory or unique items amount limitation
player->SendEquipError(msg, nullptr, nullptr, newitemid);
return;
}
}
if (num_to_add)
{
// create the new item and store it
Item* pItem = player->StoreNewItem(dest, newitemid, true, GenerateItemRandomBonusListId(newitemid), GuidSet(), context, bonusListIDs);
// was it successful? return error if not
if (!pItem)
{
player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr);
return;
}
// set the "Crafted by ..." property of the item
if (pItem->GetTemplate()->HasSignature())
pItem->SetCreator(player->GetGUID());
// send info to the client
player->SendNewItem(pItem, num_to_add, true, true);
if (pItem->GetQuality() > ITEM_QUALITY_EPIC || (pItem->GetQuality() == ITEM_QUALITY_EPIC && pItem->GetItemLevel(player) >= MinNewsItemLevel))
if (Guild* guild = player->GetGuild())
guild->AddGuildNews(GUILD_NEWS_ITEM_CRAFTED, player->GetGUID(), 0, pProto->GetId());
// we succeeded in creating at least one item, so a levelup is possible
player->UpdateCraftSkill(m_spellInfo);
}
}
void Spell::EffectCreateItem()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
DoCreateItem(effectInfo->ItemType, m_spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) ? ItemContext::Trade_Skill : ItemContext::NONE);
ExecuteLogEffectCreateItem(SpellEffectName(effectInfo->Effect), effectInfo->ItemType);
}
void Spell::EffectCreateItem2()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
ItemContext context = m_spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) ? ItemContext::Trade_Skill : ItemContext::NONE;
// Pick a random item from spell_loot_template
if (m_spellInfo->IsLootCrafting())
{
player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell, context, false, true);
player->UpdateCraftSkill(m_spellInfo);
}
else // If there's no random loot entries for this spell, pick the item associated with this spell
{
uint32 item_id = effectInfo->ItemType;
if (item_id)
DoCreateItem(item_id, context);
}
/// @todo ExecuteLogEffectCreateItem(i, m_spellInfo->Effects[i].ItemType);
}
void Spell::EffectCreateRandomItem()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
// create some random items
player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell, m_spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) ? ItemContext::Trade_Skill : ItemContext::NONE);
/// @todo ExecuteLogEffectCreateItem(i, m_spellInfo->Effects[i].ItemType);
}
void Spell::EffectPersistentAA()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
// only handle at last effect
for (size_t i = effectInfo->EffectIndex + 1; i < m_spellInfo->GetEffects().size(); ++i)
if (m_spellInfo->GetEffect(SpellEffIndex(i)).IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA))
return;
ASSERT(!_dynObjAura);
float radius = effectInfo->CalcRadius(unitCaster);
// Caster not in world, might be spell triggered from aura removal
if (!unitCaster->IsInWorld())
return;
DynamicObject* dynObj = new DynamicObject(false);
if (!dynObj->CreateDynamicObject(unitCaster->GetMap()->GenerateLowGuid(), unitCaster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL, m_SpellVisual))
{
delete dynObj;
return;
}
AuraCreateInfo createInfo(m_castId, m_spellInfo, GetCastDifficulty(), MAX_EFFECT_MASK, dynObj);
createInfo
.SetCaster(unitCaster)
.SetBaseAmount(m_spellValue->EffectBasePoints)
.SetCastItem(m_castItemGUID, m_castItemEntry, m_castItemLevel);
if (Aura* aura = Aura::TryCreate(createInfo))
{
_dynObjAura = aura->ToDynObjAura();
_dynObjAura->_RegisterForTargets();
}
else
return;
ASSERT(_dynObjAura->GetDynobjOwner());
_dynObjAura->_ApplyEffectForTargets(effectInfo->EffectIndex);
}
void Spell::EffectEnergize()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !unitTarget)
return;
if (!unitTarget->IsAlive())
return;
if (effectInfo->MiscValue < 0 || effectInfo->MiscValue >= int8(MAX_POWERS))
return;
Powers power = Powers(effectInfo->MiscValue);
if (unitTarget->GetMaxPower(power) == 0)
return;
// Some level depends spells
switch (m_spellInfo->Id)
{
case 24571: // Blood Fury
// Instantly increases your rage by ${(300-10*$max(0,$PL-60))/10}.
damage -= 10 * std::max(0, std::min(30, unitCaster->GetLevel() - 60));
break;
case 24532: // Burst of Energy
// Instantly increases your energy by ${60-4*$max(0,$min(15,$PL-60))}.
damage -= 4 * std::max(0, std::min(15, unitCaster->GetLevel() - 60));
break;
case 67490: // Runic Mana Injector (mana gain increased by 25% for engineers - 3.2.0 patch change)
{
if (Player* player = unitCaster->ToPlayer())
if (player->HasSkill(SKILL_ENGINEERING))
AddPct(damage, 25);
break;
}
default:
break;
}
unitCaster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power);
}
void Spell::EffectEnergizePct()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !unitTarget)
return;
if (!unitTarget->IsAlive())
return;
if (effectInfo->MiscValue < 0 || effectInfo->MiscValue >= int8(MAX_POWERS))
return;
Powers power = Powers(effectInfo->MiscValue);
uint32 maxPower = unitTarget->GetMaxPower(power);
if (!maxPower)
return;
uint32 const gain = CalculatePct(maxPower, damage);
unitCaster->EnergizeBySpell(unitTarget, m_spellInfo, gain, power);
}
void Spell::EffectOpenLock()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (m_caster->GetTypeId() != TYPEID_PLAYER)
{
TC_LOG_DEBUG("spells", "WORLD: Open Lock - No Player Caster!");
return;
}
Player* player = m_caster->ToPlayer();
uint32 lockId = 0;
ObjectGuid guid;
// Get lockId
if (gameObjTarget)
{
GameObjectTemplate const* goInfo = gameObjTarget->GetGOInfo();
if (goInfo->GetNoDamageImmune() && player->HasUnitFlag(UNIT_FLAG_IMMUNE))
return;
// Arathi Basin banner opening. /// @todo Verify correctness of this check
if ((goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune) ||
(goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.requireLOS))
{
//CanUseBattlegroundObject() already called in CheckCast()
// in battleground check
if (Battleground* bg = player->GetBattleground())
{
bg->EventPlayerClickedOnFlag(player, gameObjTarget);
return;
}
}
else if (goInfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT)
{
gameObjTarget->AssaultCapturePoint(player);
return;
}
else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND)
{
//CanUseBattlegroundObject() already called in CheckCast()
// in battleground check
if (Battleground* bg = player->GetBattleground())
{
if (bg->GetTypeID(true) == BATTLEGROUND_EY)
bg->EventPlayerClickedOnFlag(player, gameObjTarget);
return;
}
}
else if (goInfo->type == GAMEOBJECT_TYPE_NEW_FLAG)
{
gameObjTarget->Use(player);
return;
}
else if (m_spellInfo->Id == 1842 && gameObjTarget->GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && gameObjTarget->GetOwner())
{
gameObjTarget->SetLootState(GO_JUST_DEACTIVATED);
return;
}
/// @todo Add script for spell 41920 - Filling, becouse server it freze when use this spell
// handle outdoor pvp object opening, return true if go was registered for handling
// these objects must have been spawned by outdoorpvp!
else if (gameObjTarget->GetGOInfo()->type == GAMEOBJECT_TYPE_GOOBER && sOutdoorPvPMgr->HandleOpenGo(player, gameObjTarget))
return;
lockId = goInfo->GetLockId();
guid = gameObjTarget->GetGUID();
}
else if (itemTarget)
{
lockId = itemTarget->GetTemplate()->GetLockID();
guid = itemTarget->GetGUID();
}
else
{
TC_LOG_DEBUG("spells", "WORLD: Open Lock - No GameObject/Item Target!");
return;
}
SkillType skillId = SKILL_NONE;
int32 reqSkillValue = 0;
int32 skillValue;
SpellCastResult res = CanOpenLock(*effectInfo, lockId, skillId, reqSkillValue, skillValue);
if (res != SPELL_CAST_OK)
{
SendCastResult(res);
return;
}
if (gameObjTarget)
gameObjTarget->Use(player);
else if (itemTarget)
{
itemTarget->SetItemFlag(ITEM_FIELD_FLAG_UNLOCKED);
itemTarget->SetState(ITEM_CHANGED, itemTarget->GetOwner());
}
// 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->GetGUID()) &&
player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue, 1, gameObjTarget))
gameObjTarget->AddToSkillupList(player->GetGUID());
}
else if (itemTarget)
{
// Do one skill-up
player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue);
}
}
}
ExecuteLogEffectOpenLock(SpellEffectName(effectInfo->Effect), gameObjTarget ? (Object*)gameObjTarget : (Object*)itemTarget);
}
void Spell::EffectSummonChangeItem()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = m_caster->ToPlayer();
// 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 = effectInfo->ItemType;
if (!newitemid)
return;
uint16 pos = m_CastItem->GetPos();
Item* pNewItem = Item::CreateItem(newitemid, 1, m_CastItem->GetContext(), 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->m_itemData->Durability < *m_CastItem->m_itemData->MaxDurability)
{
double lossPercent = 1 - *m_CastItem->m_itemData->Durability / double(m_CastItem->m_itemData->MaxDurability);
player->DurabilityLoss(pNewItem, lossPercent);
}
if (player->IsInventoryPos(pos))
{
ItemPosCountVec dest;
InventoryResult 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(nullptr);
m_CastItem = nullptr;
m_castItemGUID.Clear();
m_castItemEntry = 0;
m_castItemLevel = -1;
player->StoreItem(dest, pNewItem, true);
player->SendNewItem(pNewItem, 1, true, false);
player->ItemAddedQuestCheck(newitemid, 1);
return;
}
}
else if (player->IsBankPos(pos))
{
ItemPosCountVec dest;
if (player->CanBankItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true) == 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(nullptr);
m_CastItem = nullptr;
m_castItemGUID.Clear();
m_castItemEntry = 0;
m_castItemLevel = -1;
player->BankItem(dest, pNewItem, true);
return;
}
}
else if (player->IsEquipmentPos(pos))
{
uint16 dest;
player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), true);
InventoryResult msg = player->CanEquipItem(m_CastItem->GetSlot(), dest, pNewItem, true);
if (msg == EQUIP_ERR_OK || msg == EQUIP_ERR_CLIENT_LOCKED_OUT)
{
if (msg == EQUIP_ERR_CLIENT_LOCKED_OUT) dest = EQUIPMENT_SLOT_MAINHAND;
// prevent crash at access and unexpected charges counting with item update queue corrupt
if (m_CastItem == m_targets.GetItemTarget())
m_targets.SetItemTarget(nullptr);
m_CastItem = nullptr;
m_castItemGUID.Clear();
m_castItemEntry = 0;
m_castItemLevel = -1;
player->EquipItem(dest, pNewItem, true);
player->AutoUnequipOffhandIfNeed();
player->SendNewItem(pNewItem, 1, true, false);
player->ItemAddedQuestCheck(newitemid, 1);
return;
}
}
// fail
delete pNewItem;
}
void Spell::EffectProficiency()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
Player* p_target = m_caster->ToPlayer();
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::EffectSummonType()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
uint32 entry = effectInfo->MiscValue;
if (!entry)
return;
SummonPropertiesEntry const* properties = sSummonPropertiesStore.LookupEntry(effectInfo->MiscValueB);
if (!properties)
{
TC_LOG_ERROR("spells", "EffectSummonType: Unhandled summon type {}.", effectInfo->MiscValueB);
return;
}
WorldObject* caster = m_caster;
if (m_originalCaster)
caster = m_originalCaster;
ObjectGuid privateObjectOwner = [&]()
{
if (!(properties->GetFlags().HasFlag(SummonPropertiesFlags::OnlyVisibleToSummoner | SummonPropertiesFlags::OnlyVisibleToSummonerGroup)))
return ObjectGuid::Empty;
if (caster->IsPrivateObject())
return caster->GetPrivateObjectOwner();
if (properties->GetFlags().HasFlag(SummonPropertiesFlags::OnlyVisibleToSummonerGroup))
if (caster->IsPlayer() && m_originalCaster->ToPlayer()->GetGroup())
return caster->ToPlayer()->GetGroup()->GetGUID();
return caster->GetGUID();
}();
int32 duration = m_spellInfo->CalcDuration(caster);
Unit* unitCaster = GetUnitCasterForEffectHandlers();
TempSummon* summon = nullptr;
// determine how many units should be summoned
uint32 numSummons;
// some spells need to summon many units, for those spells number of summons is stored in effect value
// however so far noone found a generic check to find all of those (there's no related data in summonproperties.dbc
// and in spell attributes, possibly we need to add a table for those)
// so here's a list of MiscValueB values, which is currently most generic check
switch (effectInfo->MiscValueB)
{
case 64:
case 61:
case 1101:
case 66:
case 648:
case 2301:
case 1061:
case 1261:
case 629:
case 181:
case 715:
case 1562:
case 833:
case 1161:
case 713:
numSummons = (damage > 0) ? damage : 1;
break;
default:
numSummons = 1;
break;
}
switch (properties->Control)
{
case SUMMON_CATEGORY_WILD:
case SUMMON_CATEGORY_ALLY:
case SUMMON_CATEGORY_UNK:
{
if (properties->GetFlags().HasFlag(SummonPropertiesFlags::JoinSummonerSpawnGroup))
{
SummonGuardian(effectInfo, entry, properties, numSummons, privateObjectOwner);
break;
}
switch (SummonTitle(properties->Title))
{
case SummonTitle::Pet:
case SummonTitle::Guardian:
case SummonTitle::Runeblade:
case SummonTitle::Minion:
SummonGuardian(effectInfo, entry, properties, numSummons, privateObjectOwner);
break;
// Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE)
case SummonTitle::Vehicle:
case SummonTitle::Mount:
{
if (!unitCaster)
return;
summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id);
break;
}
case SummonTitle::Lightwell:
case SummonTitle::Totem:
{
if (!unitCaster)
return;
summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner);
if (!summon || !summon->IsTotem())
return;
if (damage) // if not spell info, DB values used
{
summon->SetMaxHealth(damage);
summon->SetHealth(damage);
}
break;
}
case SummonTitle::Companion:
{
if (!unitCaster)
return;
summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner);
if (!summon || !summon->HasUnitTypeMask(UNIT_MASK_MINION))
return;
summon->SetImmuneToAll(true);
break;
}
default:
{
float radius = effectInfo->CalcRadius();
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
for (uint32 count = 0; count < numSummons; ++count)
{
Position pos;
if (count == 0)
pos = *destTarget;
else
// randomize position for multiple summons
pos = caster->GetRandomPoint(*destTarget, radius);
summon = caster->GetMap()->SummonCreature(entry, pos, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner);
if (!summon)
continue;
summon->SetTempSummonType(summonType);
if (properties->Control == SUMMON_CATEGORY_ALLY)
summon->SetOwnerGUID(caster->GetGUID());
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), summon);
}
return;
}
}
break;
}
case SUMMON_CATEGORY_PET:
SummonGuardian(effectInfo, entry, properties, numSummons, privateObjectOwner);
break;
case SUMMON_CATEGORY_PUPPET:
{
if (!unitCaster)
return;
summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner);
break;
}
case SUMMON_CATEGORY_VEHICLE:
{
if (!unitCaster)
return;
// Summoning spells (usually triggered by npc_spellclick) that spawn a vehicle and that cause the clicker
// to cast a ride vehicle spell on the summoned unit.
summon = unitCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, unitCaster, m_spellInfo->Id);
if (!summon || !summon->IsVehicle())
return;
// The spell that this effect will trigger. It has SPELL_AURA_CONTROL_VEHICLE
uint32 spellId = VEHICLE_SPELL_RIDE_HARDCODED;
int32 basePoints = effectInfo->CalcValue();
if (basePoints > MAX_VEHICLE_SEATS)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(basePoints, GetCastDifficulty());
if (spellInfo && spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
spellId = spellInfo->Id;
}
CastSpellExtraArgs args(TRIGGERED_FULL_MASK);
args.SetTriggeringSpell(this);
// if we have small value, it indicates seat position
if (basePoints > 0 && basePoints < MAX_VEHICLE_SEATS)
args.AddSpellMod(SPELLVALUE_BASE_POINT0, basePoints);
unitCaster->CastSpell(summon, spellId, args);
break;
}
}
if (summon)
{
summon->SetCreatorGUID(caster->GetGUID());
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), summon);
}
}
void Spell::EffectLearnSpell()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
{
if (unitTarget->ToPet())
EffectLearnPetSpell();
return;
}
Player* player = unitTarget->ToPlayer();
if (m_CastItem && !effectInfo->TriggerSpell)
{
for (ItemEffectEntry const* itemEffect : m_CastItem->GetEffects())
{
if (itemEffect->TriggerType != ITEM_SPELLTRIGGER_ON_LEARN)
continue;
bool dependent = false;
if (BattlePetSpeciesEntry const* speciesEntry = BattlePets::BattlePetMgr::GetBattlePetSpeciesBySpell(uint32(itemEffect->SpellID)))
{
player->GetSession()->GetBattlePetMgr()->AddPet(speciesEntry->ID, BattlePets::BattlePetMgr::SelectPetDisplay(speciesEntry),
BattlePets::BattlePetMgr::RollPetBreed(speciesEntry->ID), BattlePets::BattlePetMgr::GetDefaultPetQuality(speciesEntry->ID));
// If the spell summons a battle pet, we fake that it has been learned and the battle pet is added
// marking as dependent prevents saving the spell to database (intended)
dependent = true;
}
player->LearnSpell(itemEffect->SpellID, dependent);
}
}
if (effectInfo->TriggerSpell)
{
player->LearnSpell(effectInfo->TriggerSpell, false);
TC_LOG_DEBUG("spells", "Spell: Player {} has learned spell {} from Npc {}", player->GetGUID().ToString(), effectInfo->TriggerSpell, m_caster->GetGUID().ToString());
}
}
void Spell::EffectDispel()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
// Create dispel mask by dispel type
uint32 dispel_type = effectInfo->MiscValue;
uint32 dispelMask = SpellInfo::GetDispelMask(DispelType(dispel_type));
DispelChargesList dispelList;
unitTarget->GetDispellableAuraList(m_caster, dispelMask, dispelList, targetMissInfo == SPELL_MISS_REFLECT);
if (dispelList.empty())
return;
size_t remaining = dispelList.size();
// Ok if exist some buffs for dispel try dispel it
DispelChargesList successList;
successList.reserve(damage);
WorldPackets::Spells::DispelFailed dispelFailed;
dispelFailed.CasterGUID = m_caster->GetGUID();
dispelFailed.VictimGUID = unitTarget->GetGUID();
dispelFailed.SpellID = m_spellInfo->Id;
// dispel N = damage buffs (or while exist buffs for dispel)
for (int32 count = 0; count < damage && remaining > 0;)
{
// Random select buff for dispel
auto itr = dispelList.begin();
std::advance(itr, urand(0, remaining - 1));
if (itr->RollDispel())
{
auto successItr = std::find_if(successList.begin(), successList.end(), [&itr](DispelableAura& dispelAura) -> bool
{
if (dispelAura.GetAura()->GetId() == itr->GetAura()->GetId() && dispelAura.GetAura()->GetCaster() == itr->GetAura()->GetCaster())
return true;
return false;
});
uint8 dispelledCharges = 1;
if (itr->GetAura()->GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_ALL_STACKS))
dispelledCharges = itr->GetDispelCharges();
if (successItr == successList.end())
successList.emplace_back(itr->GetAura(), 0, dispelledCharges);
else
successItr->IncrementCharges();
if (!itr->DecrementCharge(dispelledCharges))
{
--remaining;
std::swap(*itr, dispelList[remaining]);
}
}
else
{
dispelFailed.FailedSpells.push_back(int32(itr->GetAura()->GetId()));
}
++count;
}
if (!dispelFailed.FailedSpells.empty())
m_caster->SendMessageToSet(dispelFailed.Write(), true);
if (successList.empty())
return;
WorldPackets::CombatLog::SpellDispellLog spellDispellLog;
spellDispellLog.IsBreak = false; // TODO: use me
spellDispellLog.IsSteal = false;
spellDispellLog.TargetGUID = unitTarget->GetGUID();
spellDispellLog.CasterGUID = m_caster->GetGUID();
spellDispellLog.DispelledBySpellID = m_spellInfo->Id;
for (DispelableAura const& dispelableAura : successList)
{
WorldPackets::CombatLog::SpellDispellData dispellData;
dispellData.SpellID = dispelableAura.GetAura()->GetId();
dispellData.Harmful = false; // TODO: use me
unitTarget->RemoveAurasDueToSpellByDispel(dispelableAura.GetAura()->GetId(), m_spellInfo->Id, dispelableAura.GetAura()->GetCasterGUID(), m_caster, dispelableAura.GetDispelCharges());
spellDispellLog.DispellData.emplace_back(dispellData);
}
m_caster->SendMessageToSet(spellDispellLog.Write(), true);
CallScriptSuccessfulDispel(SpellEffIndex(effectInfo->EffectIndex));
m_hitMask |= PROC_HIT_DISPEL;
}
void Spell::EffectDualWield()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
unitTarget->SetCanDualWield(true);
}
void Spell::EffectDistract()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
// Check for possible target
if (!unitTarget || unitTarget->IsEngaged())
return;
// target must be OK to do this
if (unitTarget->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING))
return;
unitTarget->GetMotionMaster()->MoveDistract(damage * IN_MILLISECONDS, unitTarget->GetAbsoluteAngle(destTarget));
}
void Spell::EffectPickPocket()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* player = m_caster->ToPlayer();
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
Creature* creature = Object::ToCreature(unitTarget);
if (!creature)
return;
if (creature->CanGeneratePickPocketLoot())
{
creature->StartPickPocketRefillTimer();
creature->m_loot.reset(new Loot(creature->GetMap(), creature->GetGUID(), LOOT_PICKPOCKETING, nullptr));
if (uint32 lootid = creature->GetCreatureDifficulty()->PickPocketLootID)
creature->m_loot->FillLoot(lootid, LootTemplates_Pickpocketing, player, true);
// Generate extra money for pick pocket loot
const uint32 a = urand(0, creature->GetLevel() / 2);
const uint32 b = urand(0, player->GetLevel() / 2);
creature->m_loot->gold = uint32(10 * (a + b) * sWorld->getRate(RATE_DROP_MONEY));
}
else if (creature->m_loot)
{
if (creature->m_loot->loot_type == LOOT_PICKPOCKETING && creature->m_loot->isLooted())
player->SendLootError(creature->m_loot->GetGUID(), creature->GetGUID(), LOOT_ERROR_ALREADY_PICKPOCKETED);
return;
}
player->SendLoot(*creature->m_loot);
}
void Spell::EffectAddFarsight()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
float radius = effectInfo->CalcRadius();
int32 duration = m_spellInfo->CalcDuration(m_caster);
// Caster not in world, might be spell triggered from aura removal
if (!player->IsInWorld())
return;
DynamicObject* dynObj = new DynamicObject(true);
if (!dynObj->CreateDynamicObject(player->GetMap()->GenerateLowGuid(), player, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS, m_SpellVisual))
{
delete dynObj;
return;
}
dynObj->SetDuration(duration);
dynObj->SetCasterViewpoint();
}
void Spell::EffectUntrainTalents()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || m_caster->GetTypeId() == TYPEID_PLAYER)
return;
unitTarget->ToPlayer()->SendRespecWipeConfirm(m_caster->GetGUID(), sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST) ? 0 : unitTarget->ToPlayer()->GetNextResetTalentsCost(), SPEC_RESET_TALENTS);
}
void Spell::EffectTeleUnitsFaceCaster()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
if (unitTarget->IsInFlight())
return;
if (m_targets.HasDst())
unitTarget->NearTeleportTo(destTarget->GetPositionX(), destTarget->GetPositionY(), destTarget->GetPositionZ(), destTarget->GetAbsoluteAngle(m_caster), unitTarget == m_caster);
}
void Spell::EffectLearnSkill()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (damage < 1)
return;
uint32 skillid = effectInfo->MiscValue;
SkillRaceClassInfoEntry const* rcEntry = sDB2Manager.GetSkillRaceClassInfo(skillid, unitTarget->GetRace(), unitTarget->GetClass());
if (!rcEntry)
return;
SkillTiersEntry const* tier = sObjectMgr->GetSkillTier(rcEntry->SkillTierID);
if (!tier)
return;
uint16 skillval = unitTarget->ToPlayer()->GetPureSkillValue(skillid);
unitTarget->ToPlayer()->SetSkill(skillid, damage, std::max(skillval, 1), tier->Value[damage - 1]);
}
void Spell::EffectPlayMovie()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
uint32 movieId = effectInfo->MiscValue;
if (!sMovieStore.LookupEntry(movieId))
return;
unitTarget->ToPlayer()->SendMovieStart(movieId);
}
void Spell::EffectTradeSkill()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
// uint32 skillid = m_spellInfo->Effects[i].MiscValue;
// uint16 skillmax = unitTarget->ToPlayer()->(skillid);
// m_caster->ToPlayer()->SetSkill(skillid, skillval?skillval:1, skillmax+75);
}
void Spell::EffectEnchantItemPerm()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!itemTarget)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
// Handle vellums
if (itemTarget->IsVellum())
{
// destroy one vellum from stack
uint32 count = 1;
player->DestroyItemCount(itemTarget, count, true);
unitTarget = player;
// and add a scroll
damage = 1;
DoCreateItem(effectInfo->ItemType, m_spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) ? ItemContext::Trade_Skill : ItemContext::NONE);
itemTarget = nullptr;
m_targets.SetItemTarget(nullptr);
}
else
{
// do not increase skill if vellum used
if (!(m_CastItem && m_CastItem->GetTemplate()->HasFlag(ITEM_FLAG_NO_REAGENT_COST)))
player->UpdateCraftSkill(m_spellInfo);
uint32 enchant_id = effectInfo->MiscValue;
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 != player && player->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE))
{
sLog->OutCommand(player->GetSession()->GetAccountId(), "GM {} (Account: {}) enchanting(perm): {} (Entry: {}) for player: {} (Account: {})",
player->GetName(), player->GetSession()->GetAccountId(),
itemTarget->GetTemplate()->GetDefaultLocaleName(), 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, m_caster->GetGUID());
// add new enchanting if equipped
item_owner->ApplyEnchantment(itemTarget, PERM_ENCHANTMENT_SLOT, true);
item_owner->RemoveTradeableItem(itemTarget);
itemTarget->ClearSoulboundTradeable(item_owner);
}
}
void Spell::EffectEnchantItemPrismatic()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!itemTarget)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
uint32 enchantId = effectInfo->MiscValue;
if (!enchantId)
return;
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
if (!enchant)
return;
// support only enchantings with add socket in this slot
{
bool add_socket = false;
for (uint8 i = 0; i < MAX_ITEM_ENCHANTMENT_EFFECTS; ++i)
{
if (enchant->Effect[i] == ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET)
{
add_socket = true;
break;
}
}
if (!add_socket)
{
TC_LOG_ERROR("spells", "Spell::EffectEnchantItemPrismatic: attempt to apply the enchant spell {} with SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC ({}), but without ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET ({}), not supported 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 != player && player->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE))
{
sLog->OutCommand(player->GetSession()->GetAccountId(), "GM {} (Account: {}) enchanting(perm): {} (Entry: {}) for player: {} (Account: {})",
player->GetName(), player->GetSession()->GetAccountId(),
itemTarget->GetTemplate()->GetDefaultLocaleName(), 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, enchantId, 0, 0, m_caster->GetGUID());
// add new enchanting if equipped
item_owner->ApplyEnchantment(itemTarget, PRISMATIC_ENCHANTMENT_SLOT, true);
item_owner->RemoveTradeableItem(itemTarget);
itemTarget->ClearSoulboundTradeable(item_owner);
}
void Spell::EffectEnchantItemTmp()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!itemTarget)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
uint32 enchant_id = effectInfo->MiscValue;
if (!enchant_id)
{
TC_LOG_ERROR("spells", "Spell {} Effect {} (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) has enchanting id 0.", m_spellInfo->Id, effectInfo->EffectIndex);
return;
}
SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
if (!pEnchant)
{
TC_LOG_ERROR("spells", "Spell {} Effect {} (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) has a non-existing enchanting id {} ", m_spellInfo->Id, effectInfo->EffectIndex, enchant_id);
return;
}
// select enchantment duration
uint32 duration = pEnchant->Duration;
// item can be in trade slot and have owner diff. from caster
Player* item_owner = itemTarget->GetOwner();
if (!item_owner)
return;
if (item_owner != player && player->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE))
{
sLog->OutCommand(player->GetSession()->GetAccountId(), "GM {} (Account: {}) enchanting(temp): {} (Entry: {}) for player: {} (Account: {})",
player->GetName(), player->GetSession()->GetAccountId(),
itemTarget->GetTemplate()->GetDefaultLocaleName(), 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, m_caster->GetGUID());
// add new enchanting if equipped
item_owner->ApplyEnchantment(itemTarget, TEMP_ENCHANTMENT_SLOT, true);
}
void Spell::EffectTameCreature()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !unitCaster->GetPetGUID().IsEmpty())
return;
if (!unitTarget)
return;
if (unitTarget->GetTypeId() != TYPEID_UNIT)
return;
Creature* creatureTarget = unitTarget->ToCreature();
if (creatureTarget->IsPet())
return;
if (unitCaster->GetClass() != CLASS_HUNTER)
return;
// cast finish successfully
//SendChannelUpdate(0);
finish();
Pet* pet = unitCaster->CreateTamedPetFrom(creatureTarget, m_spellInfo->Id);
if (!pet) // in very specific state like near world end/etc.
return;
// "kill" original creature
creatureTarget->DespawnOrUnsummon();
uint8 level = (creatureTarget->GetLevelForTarget(m_caster) < (m_caster->GetLevelForTarget(creatureTarget) - 5)) ? (m_caster->GetLevelForTarget(creatureTarget) - 5) : creatureTarget->GetLevelForTarget(m_caster);
// prepare visual effect for levelup
pet->SetLevel(level - 1);
// add to world
pet->GetMap()->AddToMap(pet->ToCreature());
// visual effect for levelup
pet->SetLevel(level);
// caster have pet now
unitCaster->SetMinion(pet, true);
if (unitCaster->GetTypeId() == TYPEID_PLAYER)
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
unitCaster->ToPlayer()->PetSpellInitialize();
}
}
void Spell::EffectSummonPet()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Player* owner = nullptr;
if (Unit* unitCaster = GetUnitCasterForEffectHandlers())
{
owner = unitCaster->ToPlayer();
if (!owner && unitCaster->IsTotem())
owner = unitCaster->GetCharmerOrOwnerPlayerOrPlayerItself();
}
uint32 petentry = effectInfo->MiscValue;
if (!owner)
{
SummonPropertiesEntry const* properties = sSummonPropertiesStore.LookupEntry(67);
if (properties)
SummonGuardian(effectInfo, petentry, properties, 1, ObjectGuid::Empty);
return;
}
Pet* OldSummon = owner->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;
ASSERT(OldSummon->GetMap() == owner->GetMap());
//OldSummon->GetMap()->Remove(OldSummon->ToCreature(), false);
float px, py, pz;
owner->GetClosePoint(px, py, pz, OldSummon->GetCombatReach());
OldSummon->NearTeleportTo(px, py, pz, OldSummon->GetOrientation());
//OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation());
//OldSummon->SetMap(owner->GetMap());
//owner->GetMap()->Add(OldSummon->ToCreature());
if (owner->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled())
owner->ToPlayer()->PetSpellInitialize();
return;
}
if (owner->GetTypeId() == TYPEID_PLAYER)
owner->ToPlayer()->RemovePet(OldSummon, PET_SAVE_NOT_IN_SLOT, false);
else
return;
}
Optional petSlot;
if (!petentry)
petSlot = PetSaveMode(damage);
float x, y, z;
owner->GetClosePoint(x, y, z, owner->GetCombatReach());
bool isNew = false;
Pet* pet = owner->SummonPet(petentry, petSlot, x, y, z, owner->GetOrientation(), 0, &isNew);
if (!pet)
return;
if (isNew)
{
if (m_caster->GetTypeId() == TYPEID_UNIT)
{
if (m_caster->ToCreature()->IsTotem())
pet->SetReactState(REACT_AGGRESSIVE);
else
pet->SetReactState(REACT_DEFENSIVE);
}
pet->SetCreatedBySpell(m_spellInfo->Id);
// generate new name for summon pet
std::string new_name = sObjectMgr->GeneratePetName(petentry);
if (!new_name.empty())
pet->SetName(new_name);
}
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), pet);
}
void Spell::EffectLearnPetSpell()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
if (unitTarget->ToPlayer())
{
EffectLearnSpell();
return;
}
Pet* pet = unitTarget->ToPet();
if (!pet)
return;
SpellInfo const* learn_spellproto = sSpellMgr->GetSpellInfo(effectInfo->TriggerSpell, DIFFICULTY_NONE);
if (!learn_spellproto)
return;
pet->learnSpell(learn_spellproto->Id);
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
pet->GetOwner()->PetSpellInitialize();
}
void Spell::EffectTaunt()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
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 || unitTarget->IsTotem())
{
SendCastResult(SPELL_FAILED_DONT_REPORT);
return;
}
// Hand of Reckoning can hit some entities that can't have a threat list (including players' pets)
if (m_spellInfo->Id == 62124)
if (unitTarget->GetTypeId() != TYPEID_PLAYER && unitTarget->GetTarget() != unitCaster->GetGUID())
unitCaster->CastSpell(unitTarget, 67485, true);
if (!unitTarget->CanHaveThreatList())
{
SendCastResult(SPELL_FAILED_DONT_REPORT);
return;
}
ThreatManager& mgr = unitTarget->GetThreatManager();
if (mgr.GetCurrentVictim() == unitCaster)
{
SendCastResult(SPELL_FAILED_DONT_REPORT);
return;
}
if (!mgr.IsThreatListEmpty())
// Set threat equal to highest threat currently on target
mgr.MatchUnitThreatToHighestThreat(unitCaster);
}
void Spell::EffectWeaponDmg()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (!unitTarget || !unitTarget->IsAlive())
return;
// multiple weapon dmg effect workaround
// execute only the last weapon damage
// and handle all effects at once
for (size_t j = effectInfo->EffectIndex + 1; j < m_spellInfo->GetEffects().size(); ++j)
{
switch (m_spellInfo->GetEffect(SpellEffIndex(j)).Effect)
{
case SPELL_EFFECT_WEAPON_DAMAGE:
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
return; // we must calculate only at last weapon effect
default:
break;
}
}
// some spell specific modifiers
float totalDamagePercentMod = 1.0f; // applied to final bonus+weapon damage
int32 fixed_bonus = 0;
int32 spell_bonus = 0; // bonus specific for spell
switch (m_spellInfo->SpellFamilyName)
{
case SPELLFAMILY_SHAMAN:
{
// Skyshatter Harness item set bonus
// Stormstrike
if (AuraEffect* aurEff = unitCaster->IsScriptOverriden(m_spellInfo, 5634))
unitCaster->CastSpell(nullptr, 38430, aurEff);
break;
}
}
bool normalized = false;
float weaponDamagePercentMod = 1.0f;
for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
{
switch (spellEffectInfo.Effect)
{
case SPELL_EFFECT_WEAPON_DAMAGE:
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
fixed_bonus += CalculateDamage(spellEffectInfo, unitTarget);
break;
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
fixed_bonus += CalculateDamage(spellEffectInfo, unitTarget);
normalized = true;
break;
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
ApplyPct(weaponDamagePercentMod, CalculateDamage(spellEffectInfo, unitTarget));
break;
default:
break; // not weapon damage effect, just skip
}
}
// if (addPctMods) { percent mods are added in Unit::CalculateDamage } else { percent mods are added in Unit::MeleeDamageBonusDone }
// this distinction is neccessary to properly inform the client about his autoattack damage values from Script_UnitDamage
bool const addPctMods = !m_spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CASTER_DAMAGE_MODIFIERS) && (m_spellSchoolMask & SPELL_SCHOOL_MASK_NORMAL);
if (addPctMods)
{
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 = unitCaster->GetPctModifierValue(unitMod, TOTAL_PCT);
if (fixed_bonus)
fixed_bonus = int32(fixed_bonus * weapon_total_pct);
if (spell_bonus)
spell_bonus = int32(spell_bonus * weapon_total_pct);
}
int32 weaponDamage = unitCaster->CalculateDamage(m_attackType, normalized, addPctMods);
// Sequence is important
for (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects())
{
// We assume that a spell have at most one fixed_bonus
// and at most one weaponDamagePercentMod
switch (spellEffectInfo.Effect)
{
case SPELL_EFFECT_WEAPON_DAMAGE:
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
weaponDamage += fixed_bonus;
break;
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
weaponDamage = int32(weaponDamage * weaponDamagePercentMod);
break;
default:
break; // not weapon damage effect, just skip
}
}
weaponDamage += spell_bonus;
weaponDamage = int32(weaponDamage * totalDamagePercentMod);
// prevent negative damage
weaponDamage = std::max(weaponDamage, 0);
// Add melee damage bonuses (also check for negative)
weaponDamage = unitCaster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, SPELL_DIRECT_DAMAGE, m_spellInfo, effectInfo);
m_damage += unitTarget->MeleeDamageBonusTaken(unitCaster, weaponDamage, m_attackType, SPELL_DIRECT_DAMAGE, m_spellInfo);
}
void Spell::EffectThreat()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !unitCaster->IsAlive())
return;
if (!unitTarget)
return;
if (!unitTarget->CanHaveThreatList())
return;
unitTarget->GetThreatManager().AddThreat(unitCaster, float(damage), m_spellInfo, true);
}
void Spell::EffectHealMaxHealth()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (!unitTarget || !unitTarget->IsAlive())
return;
int32 addhealth = 0;
// damage == 0 - heal for caster max health
if (damage == 0)
addhealth = unitCaster->GetMaxHealth();
else
addhealth = unitTarget->GetMaxHealth() - unitTarget->GetHealth();
m_healing += addhealth;
}
void Spell::EffectInterruptCast()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
return;
if (!unitTarget || !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
// there is no CURRENT_AUTOREPEAT_SPELL spells that can be interrupted
for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_AUTOREPEAT_SPELL; ++i)
{
if (Spell* spell = unitTarget->GetCurrentSpell(CurrentSpellTypes(i)))
{
SpellInfo const* curSpellInfo = spell->m_spellInfo;
// check if we can interrupt spell
if ((spell->getState() == SPELL_STATE_CASTING
|| (spell->getState() == SPELL_STATE_PREPARING && spell->GetCastTime() > 0.0f))
&& curSpellInfo->CanBeInterrupted(m_caster, unitTarget))
{
int32 duration = m_spellInfo->GetDuration();
duration = unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effectInfo->EffectIndex);
unitTarget->GetSpellHistory()->LockSpellSchool(curSpellInfo->GetSchoolMask(), Milliseconds(duration));
m_hitMask |= PROC_HIT_INTERRUPT;
SendSpellInterruptLog(unitTarget, curSpellInfo->Id);
unitTarget->InterruptSpell(CurrentSpellTypes(i), false);
}
}
}
}
void Spell::EffectSummonObjectWild()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
WorldObject* target = focusObject;
if (!target)
target = m_caster;
float x, y, z, o;
if (m_targets.HasDst())
destTarget->GetPosition(x, y, z, o);
else
{
m_caster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS);
o = target->GetOrientation();
}
Map* map = target->GetMap();
Position pos = Position(x, y, z, o);
QuaternionData rot = QuaternionData::fromEulerAnglesZYX(o, 0.f, 0.f);
GameObject* go = GameObject::CreateGameObject(effectInfo->MiscValue, map, pos, rot, 255, GO_STATE_READY);
if (!go)
return;
PhasingHandler::InheritPhaseShift(go, m_caster);
int32 duration = m_spellInfo->CalcDuration(m_caster);
go->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
go->SetSpellId(m_spellInfo->Id);
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), go);
// Wild object not have owner and check clickable by players
map->AddToMap(go);
if (go->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP)
if (Player* player = m_caster->ToPlayer())
if (Battleground* bg = player->GetBattleground())
bg->SetDroppedFlagGUID(go->GetGUID(), bg->GetPlayerTeam(player->GetGUID()) == ALLIANCE ? TEAM_HORDE: TEAM_ALLIANCE);
if (GameObject* linkedTrap = go->GetLinkedTrap())
{
PhasingHandler::InheritPhaseShift(linkedTrap , m_caster);
linkedTrap->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0);
linkedTrap->SetSpellId(m_spellInfo->Id);
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), linkedTrap);
}
}
void Spell::EffectScriptEffect()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
/// @todo we must implement hunter pet summon at login there (spell 6962)
/// @todo: move this to scripts
switch (m_spellInfo->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
{
switch (m_spellInfo->Id)
{
// Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell)
case 22539:
case 22972:
case 22975:
case 22976:
case 22977:
case 22978:
case 22979:
case 22980:
case 22981:
case 22982:
case 22983:
case 22984:
case 22985:
{
if (!unitTarget || !unitTarget->IsAlive())
return;
// Onyxia Scale Cloak
if (unitTarget->HasAura(22683))
return;
// Shadow Flame
m_caster->CastSpell(unitTarget, 22682, this);
return;
}
// Mug Transformation
case 41931:
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
uint8 bag = 19;
uint8 slot = 0;
Item* item = nullptr;
while (bag) // 256 = 0 due to var type
{
item = m_caster->ToPlayer()->GetItemByPos(bag, slot);
if (item && item->GetEntry() == 38587)
break;
++slot;
if (slot == 39)
{
slot = 0;
++bag;
}
}
if (bag)
{
if (m_caster->ToPlayer()->GetItemByPos(bag, slot)->GetCount() == 1) m_caster->ToPlayer()->RemoveItem(bag, slot, true);
else m_caster->ToPlayer()->GetItemByPos(bag, slot)->SetCount(m_caster->ToPlayer()->GetItemByPos(bag, slot)->GetCount()-1);
// Spell 42518 (Braufest - Gratisprobe des Braufest herstellen)
m_caster->CastSpell(m_caster, 42518, this);
return;
}
break;
}
// Brutallus - Burn
case 45141:
case 45151:
{
//Workaround for Range ... should be global for every ScriptEffect
float radius = effectInfo->CalcRadius();
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->GetDistance(m_caster) >= radius && !unitTarget->HasAura(46394) && unitTarget != m_caster)
unitTarget->CastSpell(unitTarget, 46394, this);
break;
}
// Summon Ghouls On Scarlet Crusade
case 51904:
{
if (!m_targets.HasDst())
return;
float radius = effectInfo->CalcRadius();
for (uint8 i = 0; i < 15; ++i)
m_caster->CastSpell(m_caster->GetRandomPoint(*destTarget, radius), 54522, this);
break;
}
case 52173: // Coyote Spirit Despawn
case 60243: // Blood Parrot Despawn
if (unitTarget->GetTypeId() == TYPEID_UNIT && unitTarget->IsSummon())
unitTarget->ToTempSummon()->UnSummon();
return;
case 57347: // Retrieving (Wintergrasp RP-GG pickup spell)
{
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER)
return;
unitTarget->ToCreature()->DespawnOrUnsummon();
return;
}
case 57349: // Drop RP-GG (Wintergrasp RP-GG at death drop spell)
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
// Delete item from inventory at death
m_caster->ToPlayer()->DestroyItemCount(damage, 5, true);
return;
}
case 62482: // Grab Crate
{
if (!unitCaster)
return;
if (unitTarget)
{
if (Unit* seat = unitCaster->GetVehicleBase())
{
if (Unit* parent = seat->GetVehicleBase())
{
/// @todo a hack, range = 11, should after some time cast, otherwise too far
unitCaster->CastSpell(parent, 62496, this);
unitTarget->CastSpell(parent, damage, CastSpellExtraArgs()
.SetTriggeringSpell(this)); // DIFFICULTY_NONE, so effect always valid
}
}
}
return;
}
}
break;
}
}
// normal DB scripted effect
TC_LOG_DEBUG("spells", "Spell ScriptStart spellid {} in EffectScriptEffect({})", m_spellInfo->Id, effectInfo->EffectIndex);
m_caster->GetMap()->ScriptsStart(sSpellScripts, uint32(m_spellInfo->Id | (effectInfo->EffectIndex << 24)), m_caster, unitTarget);
}
void Spell::EffectSanctuary()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
if (unitTarget->GetTypeId() == TYPEID_PLAYER && !unitTarget->GetMap()->IsDungeon())
{
// stop all pve combat for players outside dungeons, suppress pvp combat
unitTarget->CombatStop(false, false);
}
else
{
// in dungeons (or for nonplayers), reset this unit on all enemies' threat lists
for (auto const& pair : unitTarget->GetThreatManager().GetThreatenedByMeList())
pair.second->ScaleThreat(0.0f);
}
// makes spells cast before this time fizzle
unitTarget->m_lastSanctuaryTime = GameTime::GetGameTimeMS();
}
void Spell::EffectDuel()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* caster = m_caster->ToPlayer();
Player* target = unitTarget->ToPlayer();
// caster or target already have requested duel
if (caster->duel || target->duel || !target->GetSocial() || target->GetSocial()->HasIgnore(caster->GetGUID(), caster->GetSession()->GetAccountGUID()))
return;
// Players can only fight a duel in zones with this flag
AreaTableEntry const* casterAreaEntry = sAreaTableStore.LookupEntry(caster->GetAreaId());
if (casterAreaEntry && !(casterAreaEntry->GetFlags().HasFlag(AreaFlags::AllowDueling)))
{
SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
return;
}
AreaTableEntry const* targetAreaEntry = sAreaTableStore.LookupEntry(target->GetAreaId());
if (targetAreaEntry && !(targetAreaEntry->GetFlags().HasFlag(AreaFlags::AllowDueling)))
{
SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
return;
}
//CREATE DUEL FLAG OBJECT
Map* map = caster->GetMap();
Position const pos =
{
caster->GetPositionX() + (unitTarget->GetPositionX() - caster->GetPositionX()) / 2,
caster->GetPositionY() + (unitTarget->GetPositionY() - caster->GetPositionY()) / 2,
caster->GetPositionZ(),
caster->GetOrientation()
};
QuaternionData rot = QuaternionData::fromEulerAnglesZYX(pos.GetOrientation(), 0.f, 0.f);
GameObject* go = GameObject::CreateGameObject(effectInfo->MiscValue, map, pos, rot, 0, GO_STATE_READY);
if (!go)
return;
PhasingHandler::InheritPhaseShift(go, caster);
go->SetFaction(caster->GetFaction());
go->SetLevel(caster->GetLevel() + 1);
int32 duration = m_spellInfo->CalcDuration(caster);
go->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
go->SetSpellId(m_spellInfo->Id);
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), go);
caster->AddGameObject(go);
map->AddToMap(go);
//END
// Send request
WorldPackets::Duel::DuelRequested packet;
packet.ArbiterGUID = go->GetGUID();
packet.RequestedByGUID = caster->GetGUID();
packet.RequestedByWowAccount = caster->GetSession()->GetAccountGUID();
WorldPacket const* worldPacket = packet.Write();
caster->SendDirectMessage(worldPacket);
target->SendDirectMessage(worldPacket);
// create duel-info
bool isMounted = (GetSpellInfo()->Id == 62875);
caster->duel = std::make_unique(target, caster, isMounted);
target->duel = std::make_unique(caster, caster, isMounted);
caster->SetDuelArbiter(go->GetGUID());
target->SetDuelArbiter(go->GetGUID());
sScriptMgr->OnPlayerDuelRequest(target, caster);
}
void Spell::EffectStuck()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (!sWorld->getBoolConfig(CONFIG_CAST_UNSTUCK))
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
TC_LOG_DEBUG("spells", "Spell Effect: Stuck");
TC_LOG_DEBUG("spells", "Player {} {} used the auto-unstuck feature at map {} ({}, {}, {}).", player->GetName(), player->GetGUID().ToString(), player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
if (player->IsInFlight())
return;
// if player is dead without death timer is teleported to graveyard, otherwise not apply the effect
if (player->isDead())
{
if (!player->GetDeathTimer())
player->RepopAtGraveyard();
return;
}
// the player dies if hearthstone is in cooldown, else the player is teleported to home
if (player->GetSpellHistory()->HasCooldown(8690))
{
player->KillSelf();
return;
}
player->TeleportTo(player->m_homebind, TELE_TO_SPELL);
// Stuck spell trigger Hearthstone cooldown
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(8690, GetCastDifficulty());
if (!spellInfo)
return;
Spell spell(player, spellInfo, TRIGGERED_FULL_MASK);
spell.SendSpellCooldown();
}
void Spell::EffectSummonPlayer()
{
// workaround - this effect should not use target map
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
unitTarget->ToPlayer()->SendSummonRequestFrom(unitCaster);
}
void Spell::EffectActivateObject()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!gameObjTarget)
return;
GameObjectActions action = GameObjectActions(effectInfo->MiscValue);
gameObjTarget->ActivateObject(action, effectInfo->MiscValueB, m_caster, m_spellInfo->Id, int32(effectInfo->EffectIndex));
}
void Spell::EffectApplyGlyph()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
std::vector& glyphs = player->GetGlyphs(player->GetActiveTalentGroup());
std::size_t replacedGlyph = glyphs.size();
for (std::size_t i = 0; i < glyphs.size(); ++i)
{
if (std::vector const* activeGlyphBindableSpells = sDB2Manager.GetGlyphBindableSpells(glyphs[i]))
{
if (std::find(activeGlyphBindableSpells->begin(), activeGlyphBindableSpells->end(), m_misc.SpellId) != activeGlyphBindableSpells->end())
{
replacedGlyph = i;
player->RemoveAurasDueToSpell(sGlyphPropertiesStore.AssertEntry(glyphs[i])->SpellID);
break;
}
}
}
uint32 glyphId = effectInfo->MiscValue;
if (replacedGlyph < glyphs.size())
{
if (glyphId)
glyphs[replacedGlyph] = glyphId;
else
glyphs.erase(glyphs.begin() + replacedGlyph);
}
else if (glyphId)
glyphs.push_back(glyphId);
player->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2::ChangeGlyph);
if (GlyphPropertiesEntry const* glyphProperties = sGlyphPropertiesStore.LookupEntry(glyphId))
player->CastSpell(player, glyphProperties->SpellID, this);
WorldPackets::Talent::ActiveGlyphs activeGlyphs;
activeGlyphs.Glyphs.emplace_back(m_misc.SpellId, uint16(glyphId));
activeGlyphs.IsFullUpdate = false;
player->SendDirectMessage(activeGlyphs.Write());
}
void Spell::EffectEnchantHeldItem()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
// 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 = unitTarget->ToPlayer();
Item* item = item_owner->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
if (!item)
return;
// must be equipped
if (!item->IsEquipped())
return;
if (effectInfo->MiscValue)
{
uint32 enchant_id = effectInfo->MiscValue;
int32 duration = m_spellInfo->GetDuration(); //Try duration index first ..
if (!duration)
duration = damage;//+1; //Base points after ..
if (!duration)
duration = 10 * IN_MILLISECONDS; //10 seconds for enchants which don't have listed duration
if (m_spellInfo->Id == 14792) // Venomhide Poison
duration = 5 * MINUTE * IN_MILLISECONDS;
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, 0, m_caster->GetGUID());
item_owner->ApplyEnchantment(item, slot, true);
}
}
void Spell::EffectDisEnchant()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (Player* caster = m_caster->ToPlayer())
{
caster->UpdateCraftSkill(m_spellInfo);
itemTarget->m_loot.reset(new Loot(caster->GetMap(), itemTarget->GetGUID(), LOOT_DISENCHANTING, nullptr));
itemTarget->m_loot->FillLoot(ASSERT_NOTNULL(itemTarget->GetDisenchantLoot(caster))->ID, LootTemplates_Disenchant, caster, true);
caster->SendLoot(*itemTarget->m_loot);
}
// item will be removed at disenchanting end
}
void Spell::EffectInebriate()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
uint8 currentDrunk = player->GetDrunkValue();
uint8 drunkMod = damage;
if (currentDrunk + drunkMod > 100)
{
currentDrunk = 100;
if (rand_chance() < 25.0f)
player->CastSpell(player, 67468, CastSpellExtraArgs()
.SetTriggeringSpell(this)); // Drunken Vomit
}
else
currentDrunk += drunkMod;
player->SetDrunkValue(currentDrunk, m_CastItem ? m_CastItem->GetEntry() : 0);
}
void Spell::EffectFeedPet()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
Item* foodItem = itemTarget;
if (!foodItem)
return;
Pet* pet = player->GetPet();
if (!pet)
return;
if (!pet->IsAlive())
return;
ExecuteLogEffectDestroyItem(SpellEffectName(effectInfo->Effect), foodItem->GetEntry());
int32 pct;
int32 levelDiff = int32(pet->GetLevel()) - int32(foodItem->GetTemplate()->GetBaseItemLevel());
if (levelDiff >= 30)
return;
else if (levelDiff >= 20)
pct = int32(12.5); // we can't pass double so keeping the cast here for future references
else if (levelDiff >= 10)
pct = 25;
else
pct = 50;
uint32 count = 1;
player->DestroyItemCount(foodItem, count, true);
/// @todo fix crash when a spell has two effects, both pointed at the same item target
CastSpellExtraArgs args(TRIGGERED_FULL_MASK);
args.SetTriggeringSpell(this);
args.AddSpellMod(SPELLVALUE_BASE_POINT0, pct);
m_caster->CastSpell(pet, effectInfo->TriggerSpell, args);
}
void Spell::EffectDismissPet()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsPet())
return;
Pet* pet = unitTarget->ToPet();
ExecuteLogEffectUnsummonObject(SpellEffectName(effectInfo->Effect), pet);
pet->Remove(PET_SAVE_NOT_IN_SLOT);
}
void Spell::EffectSummonObject()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
uint8 slot = effectInfo->Effect - SPELL_EFFECT_SUMMON_OBJECT_SLOT1;
ObjectGuid guid = unitCaster->m_ObjectSlot[slot];
if (!guid.IsEmpty())
{
if (GameObject* obj = unitCaster->GetMap()->GetGameObject(guid))
{
// Recast case - null spell id to make auras not be removed on object remove from world
if (m_spellInfo->Id == obj->GetSpellId())
obj->SetSpellId(0);
unitCaster->RemoveGameObject(obj, true);
}
unitCaster->m_ObjectSlot[slot].Clear();
}
float x, y, z, o;
// If dest location if present
if (m_targets.HasDst())
destTarget->GetPosition(x, y, z, o);
// Summon in random point all other units if location present
else
{
unitCaster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS);
o = unitCaster->GetOrientation();
}
Map* map = m_caster->GetMap();
Position pos = Position(x, y, z, o);
QuaternionData rot = QuaternionData::fromEulerAnglesZYX(o, 0.f, 0.f);
GameObject* go = GameObject::CreateGameObject(effectInfo->MiscValue, map, pos, rot, 255, GO_STATE_READY);
if (!go)
return;
PhasingHandler::InheritPhaseShift(go, m_caster);
go->SetFaction(unitCaster->GetFaction());
go->SetLevel(unitCaster->GetLevel());
int32 duration = m_spellInfo->CalcDuration(m_caster);
go->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0);
go->SetSpellId(m_spellInfo->Id);
unitCaster->AddGameObject(go);
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), go);
map->AddToMap(go);
unitCaster->m_ObjectSlot[slot] = go->GetGUID();
}
void Spell::EffectResurrect()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!m_corpseTarget && !unitTarget)
return;
Player* player = nullptr;
if (m_corpseTarget)
player = ObjectAccessor::FindPlayer(m_corpseTarget->GetOwnerGUID());
else if (unitTarget)
player = unitTarget->ToPlayer();
if (!player || player->IsAlive() || !player->IsInWorld())
return;
if (player->IsResurrectRequested()) // already have one active request
return;
uint32 health = player->CountPctFromMaxHealth(damage);
uint32 mana = CalculatePct(player->GetMaxPower(POWER_MANA), damage);
ExecuteLogEffectResurrect(SpellEffectName(effectInfo->Effect), player);
player->SetResurrectRequestData(m_caster, health, mana, 0);
SendResurrectRequest(player);
}
void Spell::EffectAddExtraAttacks()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsAlive())
return;
unitTarget->AddExtraAttacks(damage);
ExecuteLogEffectExtraAttacks(SpellEffectName(effectInfo->Effect), unitTarget, damage);
}
void Spell::EffectParry()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetCanParry(true);
}
void Spell::EffectBlock()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetCanBlock(true);
}
void Spell::EffectLeap()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->IsInFlight())
return;
if (!m_targets.HasDst())
return;
Position pos = destTarget->GetPosition();
unitTarget->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), unitTarget == m_caster);
}
void Spell::EffectReputation()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
int32 repChange = damage;
uint32 factionId = effectInfo->MiscValue;
FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
if (!factionEntry)
return;
repChange = player->CalculateReputationGain(REPUTATION_SOURCE_SPELL, 0, repChange, factionId);
player->GetReputationMgr().ModifyReputation(factionEntry, repChange);
}
void Spell::EffectQuestComplete()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
uint32 questId = effectInfo->MiscValue;
if (questId)
{
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
return;
uint16 logSlot = player->FindQuestSlot(questId);
if (logSlot < MAX_QUEST_LOG_SIZE)
player->AreaExploredOrEventHappens(questId);
else if (quest->HasFlag(QUEST_FLAGS_TRACKING_EVENT)) // Check if the quest is used as a serverside flag.
player->SetRewardedQuest(questId); // If so, set status to rewarded without broadcasting it to client.
}
}
void Spell::EffectForceDeselect()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
float dist = unitCaster->GetVisibilityRange();
// clear focus
Trinity::PacketSenderOwning breakTarget;
breakTarget.Data.UnitGUID = m_caster->GetGUID();
breakTarget.Data.Write();
Trinity::MessageDistDelivererToHostile> notifierBreak(unitCaster, breakTarget, dist);
Cell::VisitWorldObjects(m_caster, notifierBreak, dist);
// and selection
Trinity::PacketSenderOwning clearTarget;
clearTarget.Data.Guid = m_caster->GetGUID();
clearTarget.Data.Write();
Trinity::MessageDistDelivererToHostile> notifierClear(unitCaster, clearTarget, dist);
Cell::VisitWorldObjects(m_caster, notifierClear, dist);
// we should also force pets to remove us from current target
Unit::AttackerSet attackerSet;
for (Unit::AttackerSet::const_iterator itr = unitCaster->getAttackers().begin(); itr != unitCaster->getAttackers().end(); ++itr)
if ((*itr)->GetTypeId() == TYPEID_UNIT && !(*itr)->CanHaveThreatList())
attackerSet.insert(*itr);
for (Unit::AttackerSet::const_iterator itr = attackerSet.begin(); itr != attackerSet.end(); ++itr)
(*itr)->AttackStop();
}
void Spell::EffectSelfResurrect()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Player* player = m_caster->ToPlayer();
if (!player || !player->IsInWorld() || player->IsAlive())
return;
uint32 health = 0;
uint32 mana = 0;
// flat case
if (damage < 0)
{
health = uint32(-damage);
mana = effectInfo->MiscValue;
}
// percent case
else
{
health = player->CountPctFromMaxHealth(damage);
if (player->GetMaxPower(POWER_MANA) > 0)
mana = CalculatePct(player->GetMaxPower(POWER_MANA), damage);
}
player->ResurrectPlayer(0.0f);
player->SetHealth(health);
player->SetPower(POWER_MANA, mana);
player->SetPower(POWER_RAGE, 0);
player->SetFullPower(POWER_ENERGY);
player->SetPower(POWER_FOCUS, 0);
player->SpawnCorpseBones();
}
void Spell::EffectSkinning()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (unitTarget->GetTypeId() != TYPEID_UNIT)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
Creature* creature = unitTarget->ToCreature();
int32 targetLevel = creature->GetLevelForTarget(m_caster);
uint32 skill = creature->GetCreatureDifficulty()->GetRequiredLootSkill();
creature->SetUnitFlag3(UNIT_FLAG3_ALREADY_SKINNED);
creature->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
Loot* loot = new Loot(creature->GetMap(), creature->GetGUID(), LOOT_SKINNING, nullptr);
creature->m_personalLoot[player->GetGUID()].reset(loot);
loot->FillLoot(creature->GetCreatureDifficulty()->SkinLootID, LootTemplates_Skinning, player, true);
player->SendLoot(*loot);
if (!IsPartOfSkillLine(skill, m_spellInfo->Id))
return;
// Skill gain for skinning
// This formula is still used (10.0.5.48526)
if (skill == SKILL_SKINNING)
{
int32 reqValue;
if (targetLevel <= 10)
reqValue = 1;
else if (targetLevel < 20)
reqValue = (targetLevel - 10) * 10;
else if (targetLevel <= 73)
reqValue = targetLevel * 5;
else if (targetLevel < 80)
reqValue = targetLevel * 10 - 365;
else if (targetLevel <= 84)
reqValue = targetLevel * 5 + 35;
else if (targetLevel <= 87)
reqValue = targetLevel * 15 - 805;
else if (targetLevel <= 92)
reqValue = (targetLevel - 62) * 20;
else if (targetLevel <= 104)
reqValue = targetLevel * 5 + 175;
else if (targetLevel <= 107)
reqValue = targetLevel * 15 - 905;
else if (targetLevel <= 112)
reqValue = (targetLevel - 72) * 20;
else if (targetLevel <= 122)
reqValue = (targetLevel - 32) * 10;
else
reqValue = 900;
ContentTuningEntry const* contentTuning = sContentTuningStore.LookupEntry(creature->GetContentTuning());
if (!contentTuning)
return;
uint32 skinningSkill = player->GetProfessionSkillForExp(skill, contentTuning->ExpansionID);
if (!skinningSkill)
return;
if (uint32 pureSkillValue = player->GetPureSkillValue(skinningSkill))
{
// Double chances for elites
player->UpdateGatherSkill(skinningSkill, pureSkillValue, reqValue, creature->isElite() ? 2 : 1);
}
}
}
void Spell::EffectCharge()
{
if (!unitTarget)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
{
// charge changes fall time
if (unitCaster->GetTypeId() == TYPEID_PLAYER)
unitCaster->ToPlayer()->SetFallInformation(0, unitCaster->GetPositionZ());
float speed = G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) ? m_spellInfo->Speed : SPEED_CHARGE;
Optional spellEffectExtraData;
if (effectInfo->MiscValueB)
{
spellEffectExtraData.emplace();
spellEffectExtraData->Target = unitTarget->GetGUID();
spellEffectExtraData->SpellVisualId = effectInfo->MiscValueB;
}
// Spell is not using explicit target - no generated path
if (!m_preGeneratedPath)
{
Position pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetCombatReach(), unitTarget->GetRelativeAngle(m_caster));
m_preGeneratedPath = std::make_unique(unitCaster);
m_preGeneratedPath->CalculatePath(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
}
if (G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) && m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
speed = m_preGeneratedPath->GetPathLength() / speed;
unitCaster->GetMotionMaster()->MoveCharge(*m_preGeneratedPath, speed, unitTarget, spellEffectExtraData ? &*spellEffectExtraData : nullptr);
// abuse implementation detail of MoveCharge accepting PathGenerator argument (instantly started spline)
UpdateDelayMomentForUnitTarget(unitTarget, unitCaster->movespline->Duration());
}
if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET)
{
// not all charge effects used in negative spells
if (!m_spellInfo->IsPositive() && m_caster->GetTypeId() == TYPEID_PLAYER)
unitCaster->Attack(unitTarget, true);
if (effectInfo->TriggerSpell)
m_caster->CastSpell(unitTarget, effectInfo->TriggerSpell, CastSpellExtraArgs(TRIGGERED_FULL_MASK)
.SetOriginalCaster(m_originalCasterGUID)
.SetTriggeringSpell(this));
}
}
void Spell::EffectChargeDest()
{
if (!destTarget)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH)
{
Position pos = destTarget->GetPosition();
if (!unitCaster->IsWithinLOS(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()))
{
float angle = unitCaster->GetRelativeAngle(pos.GetPositionX(), pos.GetPositionY());
float dist = unitCaster->GetDistance(pos);
pos = unitCaster->GetFirstCollisionPosition(dist, angle);
}
PathGenerator path(unitCaster);
path.CalculatePath(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
float speed = G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) ? m_spellInfo->Speed : SPEED_CHARGE;
if (G3D::fuzzyGt(m_spellInfo->Speed, 0.0f) && m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION))
speed = path.GetPathLength() / speed;
unitCaster->GetMotionMaster()->MoveCharge(path, speed);
// abuse implementation detail of MoveCharge accepting PathGenerator argument (instantly started spline)
UpdateDelayMomentForDst(unitCaster->movespline->Duration());
}
else if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT)
{
if (effectInfo->TriggerSpell)
m_caster->CastSpell(*destTarget, effectInfo->TriggerSpell, CastSpellExtraArgs(TRIGGERED_FULL_MASK)
.SetOriginalCaster(m_originalCasterGUID)
.SetTriggeringSpell(this));
}
}
void Spell::EffectKnockBack()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
if (m_caster->GetAffectingPlayer())
if (Creature* creatureTarget = unitTarget->ToCreature())
if (creatureTarget->isWorldBoss() || creatureTarget->IsDungeonBoss())
return;
// Spells with SPELL_EFFECT_KNOCK_BACK (like Thunderstorm) can't knockback target if target has ROOT/STUN
if (unitTarget->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
return;
// Instantly interrupt non melee spells being cast
if (unitTarget->IsNonMeleeSpellCast(true))
unitTarget->InterruptNonMeleeSpells(true);
float ratio = 0.1f;
float speedxy = float(effectInfo->MiscValue) * ratio;
float speedz = float(damage) * ratio;
if (speedxy < 0.01f && speedz < 0.01f)
return;
Position origin;
if (effectInfo->Effect == SPELL_EFFECT_KNOCK_BACK_DEST)
{
if (m_targets.HasDst())
origin = destTarget->GetPosition();
else
return;
}
else //if (effectInfo->Effect == SPELL_EFFECT_KNOCK_BACK)
origin = m_caster->GetPosition();
unitTarget->KnockbackFrom(origin, speedxy, speedz);
Unit::ProcSkillsAndAuras(GetUnitCasterForEffectHandlers(), unitTarget, PROC_FLAG_NONE, { PROC_FLAG_NONE, PROC_FLAG_2_KNOCKBACK },
PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_HIT, PROC_HIT_NONE, nullptr, nullptr, nullptr);
}
void Spell::EffectLeapBack()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
return;
if (!unitTarget)
return;
float speedxy = effectInfo->MiscValue / 10.f;
float speedz = damage / 10.f;
// Disengage
unitTarget->JumpTo(speedxy, speedz, effectInfo->PositionFacing);
// changes fall time
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetFallInformation(0, m_caster->GetPositionZ());
}
void Spell::EffectQuestClear()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
uint32 quest_id = effectInfo->MiscValue;
Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
if (!quest)
return;
QuestStatus oldStatus = player->GetQuestStatus(quest_id);
// Player has never done this quest
if (oldStatus == QUEST_STATUS_NONE)
return;
// remove all quest entries for 'entry' from quest log
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 logQuest = player->GetQuestSlotQuestId(slot);
if (logQuest == quest_id)
{
player->SetQuestSlot(slot, 0);
// we ignore unequippable quest items in this case, it's still be equipped
player->TakeQuestSourceItem(logQuest, false);
if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
{
player->pvpInfo.IsHostile = player->pvpInfo.IsInHostileArea || player->HasPvPForcingQuest();
player->UpdatePvPState();
}
}
}
player->RemoveActiveQuest(quest_id, false);
player->RemoveRewardedQuest(quest_id);
sScriptMgr->OnQuestStatusChange(player, quest_id);
sScriptMgr->OnQuestStatusChange(player, quest, oldStatus, QUEST_STATUS_NONE);
}
void Spell::EffectSendTaxi()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
unitTarget->ToPlayer()->ActivateTaxiPathTo(effectInfo->MiscValue, m_spellInfo->Id);
}
void Spell::EffectPullTowards()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
Position pos = m_caster->GetFirstCollisionPosition(m_caster->GetCombatReach(), m_caster->GetRelativeAngle(unitTarget));
// This is a blizzlike mistake: this should be 2D distance according to projectile motion formulas, but Blizzard erroneously used 3D distance.
float distXY = unitTarget->GetExactDist(pos);
// Avoid division by 0
if (distXY < 0.001)
return;
float distZ = pos.GetPositionZ() - unitTarget->GetPositionZ();
float speedXY = effectInfo->MiscValue ? effectInfo->MiscValue / 10.0f : 30.0f;
float speedZ = (2 * speedXY * speedXY * distZ + Movement::gravity * distXY * distXY) / (2 * speedXY * distXY);
if (!std::isfinite(speedZ))
{
TC_LOG_ERROR("spells", "Spell {} with SPELL_EFFECT_PULL_TOWARDS called with invalid speedZ. {}", m_spellInfo->Id, GetDebugInfo());
return;
}
unitTarget->JumpTo(speedXY, speedZ, 0.0f, pos);
}
void Spell::EffectPullTowardsDest()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
if (!m_targets.HasDst())
{
TC_LOG_ERROR("spells", "Spell {} with SPELL_EFFECT_PULL_TOWARDS_DEST has no dest target", m_spellInfo->Id);
return;
}
Position const* pos = m_targets.GetDstPos();
// This is a blizzlike mistake: this should be 2D distance according to projectile motion formulas, but Blizzard erroneously used 3D distance
float distXY = unitTarget->GetExactDist(pos);
// Avoid division by 0
if (distXY < 0.001)
return;
float distZ = pos->GetPositionZ() - unitTarget->GetPositionZ();
float speedXY = effectInfo->MiscValue ? effectInfo->MiscValue / 10.0f : 30.0f;
float speedZ = (2 * speedXY * speedXY * distZ + Movement::gravity * distXY * distXY) / (2 * speedXY * distXY);
if (!std::isfinite(speedZ))
{
TC_LOG_ERROR("spells", "Spell {} with SPELL_EFFECT_PULL_TOWARDS_DEST called with invalid speedZ. {}", m_spellInfo->Id, GetDebugInfo());
return;
}
unitTarget->JumpTo(speedXY, speedZ, 0.0f, *pos);
}
void Spell::EffectChangeRaidMarker()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Player* player = m_caster->ToPlayer();
if (!player || !m_targets.HasDst())
return;
Group* group = player->GetGroup();
if (!group || (group->isRaidGroup() && !group->IsLeader(player->GetGUID()) && !group->IsAssistant(player->GetGUID())))
return;
float x, y, z;
destTarget->GetPosition(x, y, z);
group->AddRaidMarker(damage, player->GetMapId(), x, y, z);
}
void Spell::EffectDispelMechanic()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
uint32 mechanic = effectInfo->MiscValue;
DispelList dispel_list;
Unit::AuraMap const& auras = unitTarget->GetOwnedAuras();
for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
{
Aura* aura = itr->second;
if (!aura->GetApplicationOfTarget(unitTarget->GetGUID()))
continue;
if (roll_chance_i(aura->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster))))
if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & (UI64LIT(1) << mechanic)))
dispel_list.emplace_back(aura->GetId(), aura->GetCasterGUID());
}
if (dispel_list.empty())
return;
for (auto itr = dispel_list.begin(); itr != dispel_list.end(); ++itr)
unitTarget->RemoveAura(itr->first, itr->second, 0, AURA_REMOVE_BY_ENEMY_SPELL);
m_hitMask |= PROC_HIT_DISPEL;
}
void Spell::EffectResurrectPet()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (damage < 0)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
// Maybe player dismissed dead pet or pet despawned?
bool hadPet = true;
if (!player->GetPet())
{
PetStable const* petStable = player->GetPetStable();
auto deadPetItr = std::find_if(petStable->ActivePets.begin(), petStable->ActivePets.end(), [](Optional const& petInfo)
{
return petInfo && !petInfo->Health;
});
PetSaveMode slot = PetSaveMode(std::distance(petStable->ActivePets.begin(), deadPetItr));
// Position passed to SummonPet is irrelevant with current implementation,
// pet will be relocated without using these coords in Pet::LoadPetFromDB
player->SummonPet(0, slot, 0.0f, 0.0f, 0.0f, 0.0f, 0);
hadPet = false;
}
// TODO: Better to fail Hunter's "Revive Pet" at cast instead of here when casting ends
Pet* pet = player->GetPet(); // Attempt to get current pet
if (!pet || pet->IsAlive())
return;
// If player did have a pet before reviving, teleport it
if (hadPet)
{
// Reposition the pet's corpse before reviving so as not to grab aggro
// We can use a different, more accurate version of GetClosePoint() since we have a pet
float x, y, z; // Will be used later to reposition the pet if we have one
player->GetClosePoint(x, y, z, pet->GetCombatReach(), PET_FOLLOW_DIST, pet->GetFollowAngle());
pet->NearTeleportTo(x, y, z, player->GetOrientation());
pet->Relocate(x, y, z, player->GetOrientation()); // This is needed so SaveStayPosition() will get the proper coords.
}
pet->ReplaceAllDynamicFlags(UNIT_DYNFLAG_NONE);
pet->RemoveUnitFlag(UNIT_FLAG_SKINNABLE);
pet->setDeathState(ALIVE);
pet->ClearUnitState(UNIT_STATE_ALL_ERASABLE);
pet->SetHealth(pet->CountPctFromMaxHealth(damage));
// Reset things for when the AI to takes over
CharmInfo *ci = pet->GetCharmInfo();
if (ci)
{
// In case the pet was at stay, we don't want it running back
ci->SaveStayPosition();
ci->SetIsAtStay(ci->HasCommandState(COMMAND_STAY));
ci->SetIsFollowing(false);
ci->SetIsCommandAttack(false);
ci->SetIsCommandFollow(false);
ci->SetIsReturning(false);
}
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
}
void Spell::EffectDestroyAllTotems()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
int32 mana = 0;
for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot)
{
if (!unitCaster->m_SummonSlot[slot])
continue;
Creature* totem = unitCaster->GetMap()->GetCreature(unitCaster->m_SummonSlot[slot]);
if (totem && totem->IsTotem())
{
uint32 spell_id = totem->m_unitData->CreatedBySpell;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id, GetCastDifficulty());
if (spellInfo)
{
std::vector costs = spellInfo->CalcPowerCost(unitCaster, spellInfo->GetSchoolMask());
auto m = std::find_if(costs.begin(), costs.end(), [](SpellPowerCost const& cost) { return cost.Power == POWER_MANA; });
if (m != costs.end())
mana += m->Amount;
}
totem->ToTotem()->UnSummon();
}
}
ApplyPct(mana, damage);
if (mana)
{
CastSpellExtraArgs args(TRIGGERED_FULL_MASK);
args.SetTriggeringSpell(this);
args.AddSpellMod(SPELLVALUE_BASE_POINT0, mana);
unitCaster->CastSpell(unitCaster, 39104, args);
}
}
void Spell::EffectDurabilityDamage()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
int32 slot = effectInfo->MiscValue;
// -1 means all player equipped items and -2 all items
if (slot < 0)
{
unitTarget->ToPlayer()->DurabilityPointsLossAll(damage, (slot < -1));
ExecuteLogEffectDurabilityDamage(SpellEffectName(effectInfo->Effect), unitTarget, -1, -1);
return;
}
// invalid slot value
if (slot >= INVENTORY_SLOT_BAG_END)
return;
if (Item* item = unitTarget->ToPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
{
unitTarget->ToPlayer()->DurabilityPointsLoss(item, damage);
ExecuteLogEffectDurabilityDamage(SpellEffectName(effectInfo->Effect), unitTarget, item->GetEntry(), slot);
}
}
void Spell::EffectDurabilityDamagePCT()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
int32 slot = effectInfo->MiscValue;
// FIXME: some spells effects have value -1/-2
// Possibly its mean -1 all player equipped items and -2 all items
if (slot < 0)
{
unitTarget->ToPlayer()->DurabilityLossAll(float(damage) / 100.0f, (slot < -1));
return;
}
// invalid slot value
if (slot >= INVENTORY_SLOT_BAG_END)
return;
if (damage <= 0)
return;
if (Item* item = unitTarget->ToPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
unitTarget->ToPlayer()->DurabilityLoss(item, float(damage) / 100.0f);
}
void Spell::EffectModifyThreatPercent()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !unitTarget)
return;
unitTarget->GetThreatManager().ModifyThreatByPercent(unitCaster, damage);
}
void Spell::EffectTransmitted()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
uint32 name_id = effectInfo->MiscValue;
Unit::AuraEffectList const& overrideSummonedGameObjects = unitCaster->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_SUMMONED_OBJECT);
for (AuraEffect const* aurEff : overrideSummonedGameObjects)
{
if (uint32(aurEff->GetMiscValue()) == name_id)
{
name_id = uint32(aurEff->GetMiscValueB());
break;
}
}
GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(name_id);
if (!goinfo)
{
TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {}) does not exist and is not created by spell (ID: {}) cast.", name_id, m_spellInfo->Id);
return;
}
float fx, fy, fz, fo;
if (m_targets.HasDst())
destTarget->GetPosition(fx, fy, fz, fo);
//FIXME: this can be better check for most objects but still hack
else if (effectInfo->HasRadius() && m_spellInfo->Speed == 0)
{
float dis = effectInfo->CalcRadius(unitCaster);
unitCaster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis);
fo = unitCaster->GetOrientation();
}
else
{
//GO is always friendly to it's creator, get range for friends
float min_dis = m_spellInfo->GetMinRange(true);
float max_dis = m_spellInfo->GetMaxRange(true);
float dis = (float)rand_norm() * (max_dis - min_dis) + min_dis;
unitCaster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis);
fo = unitCaster->GetOrientation();
}
Map* cMap = unitCaster->GetMap();
// if gameobject is summoning object, it should be spawned right on caster's position
if (goinfo->type == GAMEOBJECT_TYPE_RITUAL)
unitCaster->GetPosition(fx, fy, fz, fo);
Position pos = { fx, fy, fz, fo };
QuaternionData rot = QuaternionData::fromEulerAnglesZYX(fo, 0.f, 0.f);
GameObject* go = GameObject::CreateGameObject(name_id, cMap, pos, rot, 255, GO_STATE_READY);
if (!go)
return;
PhasingHandler::InheritPhaseShift(go, m_caster);
int32 duration = m_spellInfo->CalcDuration(m_caster);
switch (goinfo->type)
{
case GAMEOBJECT_TYPE_FISHINGNODE:
{
go->SetFaction(unitCaster->GetFaction());
ObjectGuid bobberGuid = go->GetGUID();
// client requires fishing bobber guid in channel object slot 0 to be usable
unitCaster->SetChannelObject(0, bobberGuid);
unitCaster->AddGameObject(go); // 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, 2))
{
case 0: lastSec = 3; break;
case 1: lastSec = 7; break;
case 2: lastSec = 13; break;
}
// Duration of the fishing bobber can't be higher than the Fishing channeling duration
duration = std::min(duration, duration - lastSec*IN_MILLISECONDS + FISHING_BOBBER_READY_TIME*IN_MILLISECONDS);
break;
}
case GAMEOBJECT_TYPE_RITUAL:
{
if (unitCaster->GetTypeId() == TYPEID_PLAYER)
{
go->AddUniqueUse(unitCaster->ToPlayer());
unitCaster->AddGameObject(go); // will be removed at spell cancel
}
break;
}
case GAMEOBJECT_TYPE_DUEL_ARBITER: // 52991
unitCaster->AddGameObject(go);
break;
case GAMEOBJECT_TYPE_FISHINGHOLE:
case GAMEOBJECT_TYPE_CHEST:
default:
break;
}
go->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0);
go->SetOwnerGUID(unitCaster->GetGUID());
//go->SetLevel(unitCaster->getLevel());
go->SetSpellId(m_spellInfo->Id);
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), go);
TC_LOG_DEBUG("spells", "AddObject at SpellEfects.cpp EffectTransmitted");
//unitCaster->AddGameObject(go);
//m_ObjToDel.push_back(go);
cMap->AddToMap(go);
if (GameObject* linkedTrap = go->GetLinkedTrap())
{
PhasingHandler::InheritPhaseShift(linkedTrap, m_caster);
linkedTrap->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0);
//linkedTrap->SetLevel(unitCaster->getLevel());
linkedTrap->SetSpellId(m_spellInfo->Id);
linkedTrap->SetOwnerGUID(unitCaster->GetGUID());
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), linkedTrap);
}
}
void Spell::EffectProspecting()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
if (!itemTarget || !itemTarget->GetTemplate()->HasFlag(ITEM_FLAG_IS_PROSPECTABLE))
return;
if (itemTarget->GetCount() < 5)
return;
if (sWorld->getBoolConfig(CONFIG_SKILL_PROSPECTING))
{
uint32 SkillValue = player->GetPureSkillValue(SKILL_JEWELCRAFTING);
uint32 reqSkillValue = itemTarget->GetTemplate()->GetRequiredSkillRank();
player->UpdateGatherSkill(SKILL_JEWELCRAFTING, SkillValue, reqSkillValue);
}
itemTarget->m_loot.reset(new Loot(player->GetMap(), itemTarget->GetGUID(), LOOT_PROSPECTING, nullptr));
itemTarget->m_loot->FillLoot(itemTarget->GetEntry(), LootTemplates_Prospecting, player, true);
player->SendLoot(*itemTarget->m_loot);
}
void Spell::EffectMilling()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
if (!itemTarget || !itemTarget->GetTemplate()->HasFlag(ITEM_FLAG_IS_MILLABLE))
return;
if (itemTarget->GetCount() < 5)
return;
if (sWorld->getBoolConfig(CONFIG_SKILL_MILLING))
{
uint32 SkillValue = player->GetPureSkillValue(SKILL_INSCRIPTION);
uint32 reqSkillValue = itemTarget->GetTemplate()->GetRequiredSkillRank();
player->UpdateGatherSkill(SKILL_INSCRIPTION, SkillValue, reqSkillValue);
}
itemTarget->m_loot.reset(new Loot(player->GetMap(), itemTarget->GetGUID(), LOOT_MILLING, nullptr));
itemTarget->m_loot->FillLoot(itemTarget->GetEntry(), LootTemplates_Milling, player, true);
player->SendLoot(*itemTarget->m_loot);
}
void Spell::EffectSkill()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
TC_LOG_DEBUG("spells", "WORLD: SkillEFFECT");
}
void Spell::EffectSpiritHeal()
{
Unit* caster = GetCaster()->ToUnit();
if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT)
caster->CastSpell(nullptr, SPELL_RESURRECTION_VISUAL, true);
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (Player* playerTarget = unitTarget->ToPlayer())
{
if (!playerTarget->IsInWorld())
return;
// skip if player does not want to live
if (!playerTarget->CanAcceptAreaSpiritHealFrom(caster))
return;
playerTarget->ResurrectPlayer(1.0f);
playerTarget->CastSpell(playerTarget, SPELL_PET_SUMMONED, true);
playerTarget->CastSpell(playerTarget, SPELL_SPIRIT_HEAL_MANA, true);
playerTarget->SpawnCorpseBones(false);
}
}
// remove insignia spell effect
void Spell::EffectSkinPlayerCorpse()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
TC_LOG_DEBUG("spells", "Effect: SkinPlayerCorpse");
Player* player = m_caster->ToPlayer();
Player* target = nullptr;
if (unitTarget)
target = unitTarget->ToPlayer();
else if (m_corpseTarget)
target = ObjectAccessor::FindPlayer(m_corpseTarget->GetOwnerGUID());
if (!player || !target || target->IsAlive())
return;
target->RemovedInsignia(player);
}
void Spell::EffectStealBeneficialBuff()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
TC_LOG_DEBUG("spells", "Effect: StealBeneficialBuff");
if (!unitTarget || unitTarget == m_caster) // can't steal from self
return;
DispelChargesList stealList;
// Create dispel mask by dispel type
uint32 dispelMask = SpellInfo::GetDispelMask(DispelType(effectInfo->MiscValue));
Unit::AuraMap const& auras = unitTarget->GetOwnedAuras();
for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
{
Aura* aura = itr->second;
AuraApplication const* aurApp = aura->GetApplicationOfTarget(unitTarget->GetGUID());
if (!aurApp)
continue;
if ((aura->GetSpellInfo()->GetDispelMask()) & dispelMask)
{
// Need check for passive? this
if (!aurApp->IsPositive() || aura->IsPassive() || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_CANNOT_BE_STOLEN))
continue;
// 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance."
int32 chance = aura->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster));
if (!chance)
continue;
// The charges / stack amounts don't count towards the total number of auras that can be dispelled.
// Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell
// Polymorph instead of 1 / (5 + 1) -> 16%.
bool dispelCharges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES);
uint8 charges = dispelCharges ? aura->GetCharges() : aura->GetStackAmount();
if (charges > 0)
stealList.emplace_back(aura, chance, charges);
}
}
if (stealList.empty())
return;
size_t remaining = stealList.size();
// Ok if exist some buffs for dispel try dispel it
std::vector> successList;
successList.reserve(damage);
WorldPackets::Spells::DispelFailed dispelFailed;
dispelFailed.CasterGUID = m_caster->GetGUID();
dispelFailed.VictimGUID = unitTarget->GetGUID();
dispelFailed.SpellID = m_spellInfo->Id;
// dispel N = damage buffs (or while exist buffs for dispel)
for (int32 count = 0; count < damage && remaining > 0;)
{
// Random select buff for dispel
DispelChargesList::iterator itr = stealList.begin();
std::advance(itr, urand(0, remaining - 1));
if (itr->RollDispel())
{
uint8 stolenCharges = 1;
if (itr->GetAura()->GetSpellInfo()->HasAttribute(SPELL_ATTR1_DISPEL_ALL_STACKS))
stolenCharges = itr->GetDispelCharges();
successList.emplace_back(itr->GetAura()->GetId(), itr->GetAura()->GetCasterGUID(), int32(stolenCharges));
if (!itr->DecrementCharge(stolenCharges))
{
--remaining;
std::swap(*itr, stealList[remaining]);
}
}
else
{
dispelFailed.FailedSpells.push_back(int32(itr->GetAura()->GetId()));
}
++count;
}
if (!dispelFailed.FailedSpells.empty())
m_caster->SendMessageToSet(dispelFailed.Write(), true);
if (successList.empty())
return;
WorldPackets::CombatLog::SpellDispellLog spellDispellLog;
spellDispellLog.IsBreak = false; // TODO: use me
spellDispellLog.IsSteal = true;
spellDispellLog.TargetGUID = unitTarget->GetGUID();
spellDispellLog.CasterGUID = m_caster->GetGUID();
spellDispellLog.DispelledBySpellID = m_spellInfo->Id;
for (auto const& [spellId, auraCaster, stolenCharges] : successList)
{
WorldPackets::CombatLog::SpellDispellData dispellData;
dispellData.SpellID = spellId;
dispellData.Harmful = false; // TODO: use me
unitTarget->RemoveAurasDueToSpellBySteal(spellId, auraCaster, m_caster, stolenCharges);
spellDispellLog.DispellData.emplace_back(dispellData);
}
m_caster->SendMessageToSet(spellDispellLog.Write(), true);
m_hitMask |= PROC_HIT_DISPEL;
}
void Spell::EffectKillCreditPersonal()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
unitTarget->ToPlayer()->KilledMonsterCredit(effectInfo->MiscValue);
}
void Spell::EffectKillCredit()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (int32 creatureEntry = effectInfo->MiscValue)
unitTarget->ToPlayer()->RewardPlayerAndGroupAtEvent(creatureEntry, unitTarget);
}
void Spell::EffectQuestFail()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
unitTarget->ToPlayer()->FailQuest(effectInfo->MiscValue);
}
void Spell::EffectQuestStart()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
Player* player = unitTarget->ToPlayer();
if (!player)
return;
if (Quest const* quest = sObjectMgr->GetQuestTemplate(effectInfo->MiscValue))
{
if (!player->CanTakeQuest(quest, false))
return;
if (quest->IsAutoAccept() && player->CanAddQuest(quest, false))
{
player->AddQuestAndCheckCompletion(quest, player);
player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, player->GetGUID(), true, true);
}
else
player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, player->GetGUID(), true, false);
}
}
void Spell::EffectCreateTamedPet()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || !unitTarget->GetPetGUID().IsEmpty() || unitTarget->GetClass() != CLASS_HUNTER)
return;
uint32 creatureEntry = effectInfo->MiscValue;
Pet* pet = unitTarget->CreateTamedPetFrom(creatureEntry, m_spellInfo->Id);
if (!pet)
return;
// relocate
float px, py, pz;
unitTarget->GetClosePoint(px, py, pz, pet->GetCombatReach(), PET_FOLLOW_DIST, pet->GetFollowAngle());
pet->Relocate(px, py, pz, unitTarget->GetOrientation());
// add to world
pet->GetMap()->AddToMap(pet->ToCreature());
// unitTarget has pet now
unitTarget->SetMinion(pet, true);
if (unitTarget->GetTypeId() == TYPEID_PLAYER)
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
unitTarget->ToPlayer()->PetSpellInitialize();
}
}
void Spell::EffectDiscoverTaxi()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
uint32 nodeid = effectInfo->MiscValue;
if (sTaxiNodesStore.LookupEntry(nodeid))
unitTarget->ToPlayer()->GetSession()->SendDiscoverNewTaxiNode(nodeid);
}
void Spell::EffectTitanGrip()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->SetCanTitanGrip(true, uint32(effectInfo->MiscValue));
}
void Spell::EffectRedirectThreat()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (unitTarget)
unitCaster->GetThreatManager().RegisterRedirectThreat(m_spellInfo->Id, unitTarget->GetGUID(), uint32(damage));
}
void Spell::EffectGameObjectDamage()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!gameObjTarget)
return;
FactionTemplateEntry const* casterFaction = m_caster->GetFactionTemplateEntry();
FactionTemplateEntry const* targetFaction = sFactionTemplateStore.LookupEntry(gameObjTarget->GetFaction());
// Do not allow to damage GO's of friendly factions (ie: Wintergrasp Walls/Ulduar Storm Beacons)
if (!targetFaction || (casterFaction && !casterFaction->IsFriendlyTo(targetFaction)))
gameObjTarget->ModifyHealth(-damage, m_caster, GetSpellInfo()->Id);
}
void Spell::EffectGameObjectRepair()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!gameObjTarget)
return;
gameObjTarget->ModifyHealth(damage, m_caster);
}
void Spell::EffectGameObjectSetDestructionState()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!gameObjTarget)
return;
gameObjTarget->SetDestructibleState(GameObjectDestructibleState(effectInfo->MiscValue), m_caster, true);
}
void Spell::SummonGuardian(SpellEffectInfo const* effect, uint32 entry, SummonPropertiesEntry const* properties, uint32 numGuardians, ObjectGuid privateObjectOwner)
{
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (unitCaster->IsTotem())
unitCaster = unitCaster->ToTotem()->GetOwner();
// in another case summon new
float radius = 5.0f;
int32 duration = m_spellInfo->CalcDuration(m_originalCaster);
//TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
Map* map = unitCaster->GetMap();
for (uint32 count = 0; count < numGuardians; ++count)
{
Position pos;
if (count == 0)
pos = *destTarget;
else
// randomize position for multiple summons
pos = unitCaster->GetRandomPoint(*destTarget, radius);
TempSummon* summon = map->SummonCreature(entry, pos, properties, duration, unitCaster, m_spellInfo->Id, 0, privateObjectOwner);
if (!summon)
return;
if (summon->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
{
uint8 level = summon->GetLevel();
if (properties && !properties->GetFlags().HasFlag(SummonPropertiesFlags::UseCreatureLevel))
level = unitCaster->GetLevel();
// level of pet summoned using engineering item based at engineering skill level
if (m_CastItem && unitCaster->GetTypeId() == TYPEID_PLAYER)
if (ItemTemplate const* proto = m_CastItem->GetTemplate())
if (proto->GetRequiredSkill() == SKILL_ENGINEERING)
if (uint16 skill202 = unitCaster->ToPlayer()->GetSkillValue(SKILL_ENGINEERING))
level = skill202 / 5;
((Guardian*)summon)->InitStatsForLevel(level);
}
if (summon->HasUnitTypeMask(UNIT_MASK_MINION) && m_targets.HasDst())
((Minion*)summon)->SetFollowAngle(unitCaster->GetAbsoluteAngle(summon));
if (summon->GetEntry() == 27893)
{
UF::VisibleItem const& weapon = m_caster->ToPlayer()->m_playerData->VisibleItems[EQUIPMENT_SLOT_MAINHAND];
if (weapon.ItemID)
{
summon->SetDisplayId(11686); // modelid2
summon->SetVirtualItem(0, weapon.ItemID, weapon.ItemAppearanceModID, weapon.ItemVisual);
}
else
summon->SetDisplayId(1126); // modelid1
}
ExecuteLogEffectSummonObject(SpellEffectName(effect->Effect), summon);
}
}
void Spell::EffectRenamePet()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT ||
!unitTarget->IsPet() || ((Pet*)unitTarget)->getPetType() != HUNTER_PET)
return;
unitTarget->SetPetFlag(UNIT_PET_FLAG_CAN_BE_RENAMED);
}
void Spell::EffectPlayMusic()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
uint32 soundid = effectInfo->MiscValue;
if (!sSoundKitStore.LookupEntry(soundid))
{
TC_LOG_ERROR("spells", "EffectPlayMusic: Sound (Id: {}) does not exist in spell {}.", soundid, m_spellInfo->Id);
return;
}
unitTarget->ToPlayer()->SendDirectMessage(WorldPackets::Misc::PlayMusic(soundid).Write());
}
void Spell::EffectActivateSpec()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
uint32 specID = m_misc.SpecializationId;
ChrSpecializationEntry const* spec = sChrSpecializationStore.AssertEntry(specID);
// Safety checks done in Spell::CheckCast
if (!spec->IsPetSpecialization())
player->ActivateTalentGroup(spec);
else
player->GetPet()->SetSpecialization(specID);
}
void Spell::EffectPlaySound()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
Player* player = unitTarget->ToPlayer();
if (!player)
return;
switch (m_spellInfo->Id)
{
case 91604: // Restricted Flight Area
player->GetSession()->SendNotification(LANG_ZONE_NOFLYZONE);
break;
default:
break;
}
uint32 soundId = effectInfo->MiscValue;
if (!sSoundKitStore.LookupEntry(soundId))
{
TC_LOG_ERROR("spells", "EffectPlaySound: Sound (Id: {}) does not exist in spell {}.", soundId, m_spellInfo->Id);
return;
}
player->PlayDirectSound(soundId, player);
}
void Spell::EffectRemoveAura()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
// there may be need of specifying casterguid of removed auras
unitTarget->RemoveAurasDueToSpell(effectInfo->TriggerSpell);
}
void Spell::EffectDamageFromMaxHealthPCT()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
m_damage += unitTarget->CountPctFromMaxHealth(damage);
}
void Spell::EffectGiveCurrency()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (!sCurrencyTypesStore.LookupEntry(effectInfo->MiscValue))
return;
unitTarget->ToPlayer()->ModifyCurrency(effectInfo->MiscValue, damage, CurrencyGainSource::Spell, CurrencyDestroyReason::Spell);
}
void Spell::EffectCastButtons()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
uint32 button_id = effectInfo->MiscValue + 132;
uint32 n_buttons = effectInfo->MiscValueB;
for (; n_buttons; --n_buttons, ++button_id)
{
ActionButton const* ab = player->GetActionButton(button_id);
if (!ab || ab->GetType() != ACTION_BUTTON_SPELL)
continue;
//! Action button data is unverified when it's set so it can be "hacked"
//! to contain invalid spells, so filter here.
uint32 spell_id = ab->GetAction();
if (!spell_id)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id, GetCastDifficulty());
if (!spellInfo)
continue;
if (!player->HasSpell(spell_id) || player->GetSpellHistory()->HasCooldown(spell_id))
continue;
if (!spellInfo->HasAttribute(SPELL_ATTR9_SUMMON_PLAYER_TOTEM))
continue;
CastSpellExtraArgs args;
args.TriggerFlags = TRIGGERED_IGNORE_GCD | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY | TRIGGERED_DONT_REPORT_CAST_ERROR;
args.OriginalCastId = m_castId;
args.CastDifficulty = GetCastDifficulty();
m_caster->CastSpell(m_caster, spellInfo->Id, args);
}
}
void Spell::EffectRechargeItem()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
Player* player = unitTarget->ToPlayer();
if (!player)
return;
if (Item* item = player->GetItemByEntry(effectInfo->ItemType))
{
for (ItemEffectEntry const* itemEffect : item->GetEffects())
if (itemEffect->LegacySlotIndex <= item->m_itemData->SpellCharges.size())
item->SetSpellCharges(itemEffect->LegacySlotIndex, itemEffect->Charges);
item->SetState(ITEM_CHANGED, player);
}
}
void Spell::EffectBind()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
WorldLocation homeLoc;
uint32 areaId = player->GetAreaId();
if (effectInfo->MiscValue)
areaId = effectInfo->MiscValue;
if (m_targets.HasDst())
homeLoc.WorldRelocate(*destTarget);
else
homeLoc = player->GetWorldLocation();
player->SetHomebind(homeLoc, areaId);
player->SendBindPointUpdate();
TC_LOG_DEBUG("spells", "EffectBind: New homebind X: {}, Y: {}, Z: {} O: {}, MapId: {}, AreaId: {}",
homeLoc.GetPositionX(), homeLoc.GetPositionY(), homeLoc.GetPositionZ(), homeLoc.GetOrientation(), homeLoc.GetMapId(), areaId);
// zone update
player->SendPlayerBound(m_caster->GetGUID(), areaId);
}
void Spell::EffectTeleportToReturnPoint()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (Player* player = unitTarget->ToPlayer())
if (WorldLocation const* dest = player->GetStoredAuraTeleportLocation(effectInfo->MiscValue))
player->TeleportTo(*dest, unitTarget == m_caster ? TELE_TO_SPELL | TELE_TO_NOT_LEAVE_COMBAT : TELE_TO_NONE);
}
void Spell::EffectIncreaseCurrencyCap()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (damage <= 0)
return;
if (Player* player = unitTarget->ToPlayer())
player->IncreaseCurrencyCap(effectInfo->MiscValue, damage);
}
void Spell::EffectSummonRaFFriend()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (m_caster->GetTypeId() != TYPEID_PLAYER || !unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
m_caster->CastSpell(unitTarget, effectInfo->TriggerSpell, this);
}
void Spell::EffectUnlockGuildVaultTab()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
// Safety checks done in Spell::CheckCast
Player* caster = m_caster->ToPlayer();
if (Guild* guild = caster->GetGuild())
guild->HandleBuyBankTab(caster->GetSession(), damage - 1); // Bank tabs start at zero internally
}
void Spell::EffectSummonPersonalGameObject()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
uint32 goId = effectInfo->MiscValue;
if (!goId)
return;
float x, y, z, o;
if (m_targets.HasDst())
destTarget->GetPosition(x, y, z, o);
else
{
m_caster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS);
o = m_caster->GetOrientation();
}
Map* map = m_caster->GetMap();
Position pos = Position(x, y, z, o);
QuaternionData rot = QuaternionData::fromEulerAnglesZYX(o, 0.f, 0.f);
GameObject* go = GameObject::CreateGameObject(goId, map, pos, rot, 255, GO_STATE_READY);
if (!go)
{
TC_LOG_WARN("spells", "SpellEffect Failed to summon personal gameobject. SpellId {}, effect {}", m_spellInfo->Id, effectInfo->EffectIndex);
return;
}
PhasingHandler::InheritPhaseShift(go, m_caster);
int32 duration = m_spellInfo->CalcDuration(m_caster);
go->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0);
go->SetSpellId(m_spellInfo->Id);
go->SetPrivateObjectOwner(m_caster->GetGUID());
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), go);
map->AddToMap(go);
if (GameObject* linkedTrap = go->GetLinkedTrap())
{
PhasingHandler::InheritPhaseShift(linkedTrap, m_caster);
linkedTrap->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0);
linkedTrap->SetSpellId(m_spellInfo->Id);
ExecuteLogEffectSummonObject(SpellEffectName(effectInfo->Effect), linkedTrap);
}
}
void Spell::EffectResurrectWithAura()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsInWorld())
return;
Player* target = unitTarget->ToPlayer();
if (!target)
return;
if (unitTarget->IsAlive())
return;
if (target->IsResurrectRequested()) // already have one active request
return;
uint32 health = target->CountPctFromMaxHealth(damage);
uint32 mana = CalculatePct(target->GetMaxPower(POWER_MANA), damage);
uint32 resurrectAura = 0;
if (sSpellMgr->GetSpellInfo(effectInfo->TriggerSpell, DIFFICULTY_NONE))
resurrectAura = effectInfo->TriggerSpell;
if (resurrectAura && target->HasAura(resurrectAura))
return;
ExecuteLogEffectResurrect(SpellEffectName(effectInfo->Effect), target);
target->SetResurrectRequestData(m_caster, health, mana, resurrectAura);
SendResurrectRequest(target);
}
void Spell::EffectCreateAreaTrigger()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !m_targets.HasDst())
return;
int32 duration = GetSpellInfo()->CalcDuration(GetCaster());
AreaTrigger::CreateAreaTrigger(effectInfo->MiscValue, unitCaster, nullptr, GetSpellInfo(), destTarget->GetPosition(), duration, m_SpellVisual, m_castId);
}
void Spell::EffectRemoveTalent()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
TalentEntry const* talent = sTalentStore.LookupEntry(m_misc.TalentId);
if (!talent)
return;
Player* player = unitTarget ? unitTarget->ToPlayer() : nullptr;
if (!player)
return;
player->RemoveTalent(talent);
player->SendTalentsInfoData();
}
void Spell::EffectDestroyItem()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = unitTarget->ToPlayer();
if (Item* item = player->GetItemByEntry(effectInfo->ItemType))
player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
}
void Spell::EffectLearnGarrisonBuilding()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (Garrison* garrison = unitTarget->ToPlayer()->GetGarrison())
garrison->LearnBlueprint(effectInfo->MiscValue);
}
void Spell::EffectRemoveAuraBySpellLabel()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
unitTarget->RemoveAppliedAuras([&](AuraApplication const* aurApp)
{
return aurApp->GetBase()->GetSpellInfo()->HasLabel(effectInfo->MiscValue);
});
}
void Spell::EffectCreateGarrison()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
unitTarget->ToPlayer()->CreateGarrison(effectInfo->MiscValue);
}
void Spell::EffectCreateConversation()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !m_targets.HasDst())
return;
Conversation::CreateConversation(effectInfo->MiscValue, unitCaster, destTarget->GetPosition(), ObjectGuid::Empty, GetSpellInfo());
}
void Spell::EffectCancelConversation()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget)
return;
std::vector objs;
Trinity::ObjectEntryAndPrivateOwnerIfExistsCheck check(unitTarget->GetGUID(), effectInfo->MiscValue);
Trinity::WorldObjectListSearcher checker(unitTarget, objs, check, GRID_MAP_TYPE_MASK_CONVERSATION);
Cell::VisitGridObjects(unitTarget, checker, 100.0f);
for (WorldObject* obj : objs)
{
if (Conversation* convo = obj->ToConversation())
convo->Remove();
}
}
void Spell::EffectAddGarrisonFollower()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (Garrison* garrison = unitTarget->ToPlayer()->GetGarrison())
garrison->AddFollower(effectInfo->MiscValue);
}
void Spell::EffectCreateHeirloomItem()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* player = m_caster->ToPlayer();
if (!player)
return;
CollectionMgr* collectionMgr = player->GetSession()->GetCollectionMgr();
if (!collectionMgr)
return;
std::vector bonusList;
bonusList.push_back(collectionMgr->GetHeirloomBonus(m_misc.Raw.Data[0]));
DoCreateItem(m_misc.Raw.Data[0], ItemContext::NONE, bonusList);
ExecuteLogEffectCreateItem(SpellEffectName(effectInfo->Effect), m_misc.Raw.Data[0]);
}
void Spell::EffectActivateGarrisonBuilding()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (Garrison* garrison = unitTarget->ToPlayer()->GetGarrison())
garrison->ActivateBuilding(effectInfo->MiscValue);
}
void Spell::EffectGrantBattlePetLevel()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* playerCaster = m_caster->ToPlayer();
if (!playerCaster)
return;
if (!unitTarget || !unitTarget->IsCreature())
return;
playerCaster->GetSession()->GetBattlePetMgr()->GrantBattlePetLevel(unitTarget->GetBattlePetCompanionGUID(), damage);
}
void Spell::EffectGiveExperience()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* playerTarget = Object::ToPlayer(unitTarget);
if (!playerTarget)
return;
uint32 xp = Quest::XPValue(playerTarget, effectInfo->MiscValue, effectInfo->MiscValueB);
playerTarget->GiveXP(xp, nullptr);
}
void Spell::EffectGiveRestedExperience()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* playerTarget = Object::ToPlayer(unitTarget);
if (!playerTarget)
return;
// effect value is number of resting hours
playerTarget->GetRestMgr().AddRestBonus(REST_TYPE_XP, damage * HOUR * playerTarget->GetRestMgr().CalcExtraPerSec(REST_TYPE_XP, 0.125f));
}
void Spell::EffectHealBattlePetPct()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (BattlePets::BattlePetMgr* battlePetMgr = unitTarget->ToPlayer()->GetSession()->GetBattlePetMgr())
battlePetMgr->HealBattlePetsPct(damage);
}
void Spell::EffectEnableBattlePets()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsPlayer())
return;
Player* player = unitTarget->ToPlayer();
player->SetPlayerFlag(PLAYER_FLAGS_PET_BATTLES_UNLOCKED);
player->GetSession()->GetBattlePetMgr()->UnlockSlot(BattlePets::BattlePetSlot::Slot0);
}
void Spell::EffectChangeBattlePetQuality()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* playerCaster = m_caster->ToPlayer();
if (!playerCaster)
return;
if (!unitTarget || !unitTarget->IsCreature())
return;
auto qualityItr = std::lower_bound(sBattlePetBreedQualityStore.begin(), sBattlePetBreedQualityStore.end(), damage, [](BattlePetBreedQualityEntry const* a1, int32 selector)
{
return a1->MaxQualityRoll < selector;
});
BattlePets::BattlePetBreedQuality quality = BattlePets::BattlePetBreedQuality::Poor;
if (qualityItr != sBattlePetBreedQualityStore.end())
quality = BattlePets::BattlePetBreedQuality(qualityItr->QualityEnum);
playerCaster->GetSession()->GetBattlePetMgr()->ChangeBattlePetQuality(unitTarget->GetBattlePetCompanionGUID(), quality);
}
void Spell::EffectLaunchQuestChoice()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || !unitTarget->IsPlayer())
return;
unitTarget->ToPlayer()->SendPlayerChoice(GetCaster()->GetGUID(), effectInfo->MiscValue);
}
void Spell::EffectUncageBattlePet()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (!m_CastItem || !m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
return;
uint32 speciesId = m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID);
uint16 breed = m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA) & 0xFFFFFF;
uint8 quality = (m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA) >> 24) & 0xFF;
uint16 level = m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL);
uint32 displayId = m_CastItem->GetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID);
BattlePetSpeciesEntry const* speciesEntry = sBattlePetSpeciesStore.LookupEntry(speciesId);
if (!speciesEntry)
return;
Player* player = m_caster->ToPlayer();
BattlePets::BattlePetMgr* battlePetMgr = player->GetSession()->GetBattlePetMgr();
if (!battlePetMgr)
return;
if (battlePetMgr->GetMaxPetLevel() < level)
{
battlePetMgr->SendError(BattlePets::BattlePetError::TooHighLevelToUncage, speciesEntry->CreatureID);
SendCastResult(SPELL_FAILED_CANT_ADD_BATTLE_PET);
return;
}
if (battlePetMgr->HasMaxPetCount(speciesEntry, player->GetGUID()))
{
battlePetMgr->SendError(BattlePets::BattlePetError::CantHaveMorePetsOfType, speciesEntry->CreatureID);
SendCastResult(SPELL_FAILED_CANT_ADD_BATTLE_PET);
return;
}
battlePetMgr->AddPet(speciesId, displayId, breed, BattlePets::BattlePetBreedQuality(quality), level);
player->SendPlaySpellVisual(player, BattlePets::SPELL_VISUAL_UNCAGE_PET, 0, 0, 0.f, false);
player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), true);
m_CastItem = nullptr;
}
void Spell::EffectUpgradeHeirloom()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (Player* player = m_caster->ToPlayer())
if (CollectionMgr* collectionMgr = player->GetSession()->GetCollectionMgr())
collectionMgr->UpgradeHeirloom(m_misc.Raw.Data[0], int32(m_castItemEntry));
}
void Spell::EffectApplyEnchantIllusion()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!itemTarget)
return;
Player* player = m_caster->ToPlayer();
if (!player || player->GetGUID() != itemTarget->GetOwnerGUID())
return;
itemTarget->SetState(ITEM_CHANGED, player);
itemTarget->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, effectInfo->MiscValue);
if (itemTarget->IsEquipped())
player->SetVisibleItemSlot(itemTarget->GetSlot(), itemTarget);
player->RemoveTradeableItem(itemTarget);
itemTarget->ClearSoulboundTradeable(player);
}
void Spell::EffectUpdatePlayerPhase()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
PhasingHandler::OnConditionChange(unitTarget);
}
void Spell::EffectUpdateZoneAurasAndPhases()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
unitTarget->ToPlayer()->UpdateAreaDependentAuras(unitTarget->GetAreaId());
}
void Spell::EffectGiveArtifactPower()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
return;
Player* playerCaster = m_caster->ToPlayer();
if (!playerCaster)
return;
if (Aura* artifactAura = playerCaster->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE))
if (Item* artifact = playerCaster->GetItemByGuid(artifactAura->GetCastItemGUID()))
artifact->GiveArtifactXp(damage, m_CastItem, uint32(effectInfo->MiscValue));
}
void Spell::EffectGiveArtifactPowerNoBonus()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
if (Aura* artifactAura = unitTarget->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE))
if (Item* artifact = unitTarget->ToPlayer()->GetItemByGuid(artifactAura->GetCastItemGUID()))
artifact->GiveArtifactXp(damage, m_CastItem, 0);
}
void Spell::EffectPlaySceneScriptPackage()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
m_caster->ToPlayer()->GetSceneMgr().PlaySceneByPackageId(effectInfo->MiscValue, SceneFlag::PlayerNonInteractablePhased, destTarget);
}
template
bool IsUnitTargetSceneObjectAura(Spell const* spell, TargetInfo const& target)
{
if (target.TargetGUID != spell->GetCaster()->GetGUID())
return false;
for (SpellEffectInfo const& spellEffectInfo : spell->GetSpellInfo()->GetEffects())
if (target.EffectMask & (1 << spellEffectInfo.EffectIndex) && spellEffectInfo.IsUnitOwnedAuraEffect())
return true;
return false;
}
void Spell::EffectCreateSceneObject()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !m_targets.HasDst())
return;
if (SceneObject* sceneObject = SceneObject::CreateSceneObject(effectInfo->MiscValue, unitCaster, destTarget->GetPosition(), ObjectGuid::Empty))
{
bool hasAuraTargetingCaster = std::find_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [this](TargetInfo const& target)
{
return IsUnitTargetSceneObjectAura(this, target);
}) != m_UniqueTargetInfo.end();
if (hasAuraTargetingCaster)
sceneObject->SetCreatedBySpellCast(m_castId);
}
}
void Spell::EffectCreatePrivateSceneObject()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || !m_targets.HasDst())
return;
if (SceneObject* sceneObject = SceneObject::CreateSceneObject(effectInfo->MiscValue, unitCaster, destTarget->GetPosition(), unitCaster->GetGUID()))
{
bool hasAuraTargetingCaster = std::find_if(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [this](TargetInfo const& target)
{
return IsUnitTargetSceneObjectAura(this, target);
}) != m_UniqueTargetInfo.end();
if (hasAuraTargetingCaster)
sceneObject->SetCreatedBySpellCast(m_castId);
}
}
void Spell::EffectPlayScene()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
m_caster->ToPlayer()->GetSceneMgr().PlayScene(effectInfo->MiscValue, destTarget);
}
void Spell::EffectGiveHonor()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
WorldPackets::Combat::PvPCredit packet;
packet.Honor = damage;
packet.OriginalHonor = damage;
Player* playerTarget = unitTarget->ToPlayer();
playerTarget->AddHonorXP(damage);
playerTarget->SendDirectMessage(packet.Write());
}
void Spell::EffectJumpCharge()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
if (unitCaster->IsInFlight())
return;
JumpChargeParams const* params = sObjectMgr->GetJumpChargeParams(effectInfo->MiscValue);
if (!params)
return;
float speed = params->Speed;
if (params->TreatSpeedAsMoveTimeSeconds)
speed = unitCaster->GetExactDist(destTarget) / params->MoveTimeInSec;
Optional arrivalCast;
if (effectInfo->TriggerSpell)
{
arrivalCast.emplace();
arrivalCast->SpellId = effectInfo->TriggerSpell;
}
Optional effectExtra;
if (params->SpellVisualId || params->ProgressCurveId || params->ParabolicCurveId)
{
effectExtra.emplace();
if (params->SpellVisualId)
effectExtra->SpellVisualId = *params->SpellVisualId;
if (params->ProgressCurveId)
effectExtra->ProgressCurveId = *params->ProgressCurveId;
if (params->ParabolicCurveId)
effectExtra->ParabolicCurveId = *params->ParabolicCurveId;
}
unitCaster->GetMotionMaster()->MoveJumpWithGravity(*destTarget, speed, params->JumpGravity, EVENT_JUMP, false,
arrivalCast ? &*arrivalCast : nullptr,
effectExtra ? &*effectExtra : nullptr);
}
void Spell::EffectLearnTransmogSet()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
unitTarget->ToPlayer()->GetSession()->GetCollectionMgr()->AddTransmogSet(effectInfo->MiscValue);
}
void Spell::EffectRespecAzeriteEmpoweredItem()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
if (!itemTarget || !itemTarget->IsAzeriteEmpoweredItem())
return;
Player* owner = m_caster->ToPlayer();
if (!owner)
return;
AzeriteEmpoweredItem* azeriteEmpoweredItem = itemTarget->ToAzeriteEmpoweredItem();
owner->ModifyMoney(-azeriteEmpoweredItem->GetRespecCost());
// reapply all item mods - item level change affects stats and auras
if (azeriteEmpoweredItem->IsEquipped())
owner->_ApplyItemMods(azeriteEmpoweredItem, azeriteEmpoweredItem->GetSlot(), false);
azeriteEmpoweredItem->ClearSelectedAzeritePowers();
if (azeriteEmpoweredItem->IsEquipped())
owner->_ApplyItemMods(azeriteEmpoweredItem, azeriteEmpoweredItem->GetSlot(), true);
azeriteEmpoweredItem->SetState(ITEM_CHANGED, owner);
owner->SetNumRespecs(owner->GetNumRespecs() + 1);
}
void Spell::EffectLearnAzeriteEssencePower()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* playerTarget = unitTarget ? unitTarget->ToPlayer() : nullptr;
if (!playerTarget)
return;
Item* heartOfAzeroth = playerTarget->GetItemByEntry(ITEM_ID_HEART_OF_AZEROTH, ItemSearchLocation::Everywhere);
if (!heartOfAzeroth)
return;
AzeriteItem* azeriteItem = heartOfAzeroth->ToAzeriteItem();
if (!azeriteItem)
return;
// remove old rank and apply new one
if (azeriteItem->IsEquipped())
{
if (UF::SelectedAzeriteEssences const* selectedEssences = azeriteItem->GetSelectedAzeriteEssences())
{
for (int32 slot = 0; slot < MAX_AZERITE_ESSENCE_SLOT; ++slot)
{
if (selectedEssences->AzeriteEssenceID[slot] == uint32(effectInfo->MiscValue))
{
bool major = AzeriteItemMilestoneType(sDB2Manager.GetAzeriteItemMilestonePower(slot)->Type) == AzeriteItemMilestoneType::MajorEssence;
playerTarget->ApplyAzeriteEssence(azeriteItem, effectInfo->MiscValue, MAX_AZERITE_ESSENCE_RANK, major, false);
playerTarget->ApplyAzeriteEssence(azeriteItem, effectInfo->MiscValue, effectInfo->MiscValueB, major, false);
break;
}
}
}
}
azeriteItem->SetEssenceRank(effectInfo->MiscValue, effectInfo->MiscValueB);
azeriteItem->SetState(ITEM_CHANGED, playerTarget);
}
void Spell::EffectCreatePrivateConversation()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster || unitCaster->GetTypeId() != TYPEID_PLAYER)
return;
Conversation::CreateConversation(effectInfo->MiscValue, unitCaster, destTarget->GetPosition(), unitCaster->GetGUID(), GetSpellInfo());
}
void Spell::EffectSendChatMessage()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Unit* unitCaster = GetUnitCasterForEffectHandlers();
if (!unitCaster)
return;
uint32 broadcastTextId = effectInfo->MiscValue;
if (!sBroadcastTextStore.LookupEntry(broadcastTextId))
return;
ChatMsg chatType = ChatMsg(effectInfo->MiscValueB);
unitCaster->Talk(broadcastTextId, chatType, CreatureTextMgr::GetRangeForChatType(chatType), unitTarget);
}
void Spell::EffectGrantBattlePetExperience()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* playerCaster = m_caster->ToPlayer();
if (!playerCaster)
return;
if (!unitTarget || !unitTarget->IsCreature())
return;
playerCaster->GetSession()->GetBattlePetMgr()->GrantBattlePetExperience(unitTarget->GetBattlePetCompanionGUID(), damage, BattlePets::BattlePetXpSource::SpellEffect);
}
void Spell::EffectLearnTransmogIllusion()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* player = Object::ToPlayer(unitTarget);
if (!player)
return;
uint32 illusionId = effectInfo->MiscValue;
if (!sTransmogIllusionStore.LookupEntry(illusionId))
return;
player->GetSession()->GetCollectionMgr()->AddTransmogIllusion(illusionId);
}
void Spell::EffectModifyAuraStacks()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Aura* targetAura = unitTarget->GetAura(effectInfo->TriggerSpell);
if (!targetAura)
return;
switch (effectInfo->MiscValue)
{
case 0:
targetAura->ModStackAmount(damage);
break;
case 1:
targetAura->SetStackAmount(damage);
break;
default:
break;
}
}
void Spell::EffectModifyCooldown()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
unitTarget->GetSpellHistory()->ModifyCooldown(effectInfo->TriggerSpell, Milliseconds(damage));
}
void Spell::EffectModifyCooldowns()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
unitTarget->GetSpellHistory()->ModifyCoooldowns([this](SpellHistory::CooldownStorageType::iterator itr)
{
SpellInfo const* spellOnCooldown = sSpellMgr->AssertSpellInfo(itr->first, DIFFICULTY_NONE);
if (spellOnCooldown->SpellFamilyName != uint32(effectInfo->MiscValue))
return false;
int32 bitIndex = effectInfo->MiscValueB - 1;
if (bitIndex < 0 || uint32(bitIndex) >= sizeof(flag128) * 8)
return false;
flag128 reqFlag;
reqFlag[bitIndex / 32] = 1u << (bitIndex % 32);
return bool(spellOnCooldown->SpellFamilyFlags & reqFlag);
}, Milliseconds(damage));
}
void Spell::EffectModifyCooldownsByCategory()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
unitTarget->GetSpellHistory()->ModifyCoooldowns([this](SpellHistory::CooldownStorageType::iterator itr)
{
return sSpellMgr->AssertSpellInfo(itr->first, DIFFICULTY_NONE)->CategoryId == uint32(effectInfo->MiscValue);
}, Milliseconds(damage));
}
void Spell::EffectModifySpellCharges()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
for (int32 i = 0; i < damage; ++i)
unitTarget->GetSpellHistory()->RestoreCharge(effectInfo->MiscValue);
}
void Spell::EffectCreateTraitTreeConfig()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* target = Object::ToPlayer(unitTarget);
if (!target)
return;
WorldPackets::Traits::TraitConfig newConfig;
newConfig.Type = TraitMgr::GetConfigTypeForTree(effectInfo->MiscValue);
if (newConfig.Type != TraitConfigType::Generic)
return;
newConfig.TraitSystemID = sTraitTreeStore.AssertEntry(effectInfo->MiscValue)->TraitSystemID;
target->CreateTraitConfig(newConfig);
}
void Spell::EffectChangeActiveCombatTraitConfig()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* target = Object::ToPlayer(unitTarget);
if (!target)
return;
WorldPackets::Traits::TraitConfig* traitConfig = std::any_cast(&m_customArg);
if (!traitConfig)
return;
target->UpdateTraitConfig(std::move(*traitConfig), damage, false);
}
void Spell::EffectTeleportGraveyard()
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
Player* target = Object::ToPlayer(unitTarget);
if (!target)
return;
target->RepopAtGraveyard();
}